feat(core,db): comment anchors and the comment table (spec-by6.3.1)
DESIGN.md held the comment schema back until the review UI existed, because a
schema is the expensive thing to get wrong here. The UI exists, so this lands
the anchoring model it specifies: (doc_id, heading_path, block_index,
block_hash), resolved content-first — the block's hash wherever it moved to,
then position under the same headings, and outdated when neither matches. A
comment that lost its place says so rather than being relocated to a best
guess, because a comment on the wrong paragraph reads as authoritative.
Two refinements the specified model left open:
block_index is the block's position WITHIN its heading path, not within the
document. prosediff numbers blocks document-globally, but the positional
fallback exists precisely for blocks whose content changed, and a global index
is destroyed by any insertion above it — so a global index would fail exactly
when it is needed.
A hash match is not unique. Documents repeat themselves ("TBD" under half the
headings), so the comment's own section wins outright and the nearest index
breaks what that does not. Without it, which duplicate a comment landed on
would depend on document order.
Anchor state is derived, never stored. A comment is not outdated in general, it
is outdated at a revision, and the proposal branch moves under it as the agent
revises; a column would cache a function of a moving input.
The table encodes the rest as constraints: a root carries the whole anchor and
a reply carries none of it (one predicate over all six columns, so a
half-written anchor is unwritable), agent provenance on proposal's rule, and
resolution only on a thread root.
Verified against a real Postgres 16, which caught what Go-level validation did
not: pq.Array sends a nil slice as SQL NULL, so a comment on a block before the
first heading tripped ck_comment_anchor and every document preamble was
uncommentable.
schema.sql keeps the comment table last, matching migration order — the
agreement test compares the two statement for statement. Its absent-list, which
required a design change rather than a quiet migration to add this table, is
the gate this commit passes through.
spec-by6.3.1
refactor(doc): one route from a revision to an Archive (spec-wcr #2, #4)
doc.Scan/DocumentSource were production-dead after service.Archive landed —
their only consumer was doc's own test fixture — and they were the seam that
made web's layering violation writable: a surface could reach past service/
into gitx and build its own archive. Deleted, so doc/ now owns no way to read
a revision and service.Archive is the single route from rev to Archive. The
fixture reads through ListDocuments + FromDocuments instead.
TestScanReportsGitErrors covered an unknown revision failing rather than
walking empty, which gitx did not test itself. Relocated there as
TestWalkOfAnUnknownRevisionFails rather than dropped.
linkHierarchy passed path.Dir(p.Path) where every other call site passes DirOf.
Unobservable today: the two differ only at the space root, and there the
section-proximity step is subsumed by the same-directory step above it, so "."
only ever skipped a lookup that had already answered. Verified by reverting and
re-running. Changed anyway — it stays unobservable only by coincidence of two
ranking rules — with a test pinning the invariant that `parent:` resolves to
whatever the same wikilink in the body resolves to.
spec-wcr
chore(beads): enable Dolt auto-push to dolt.srht.bigb.es
Beads changes were only reaching the remote on a manual bd dolt push, so
issue state drifted from the tracker between sessions. bd has native
auto-push (debounced, pushes to the configured origin remote); it was
simply never enabled — sync.remote was set but dolt.auto-push was unset.
Enable it with an explicit 5m debounce rather than relying on the implicit
default. Verified end-to-end: a write fires "dolt auto-push: pushed
successfully", and a fresh dolt clone of the remote shows this session's
closed beads (spec-jjo, spec-mfm, spec-ejq.1) as closed.
Also commits the JSONL exports, which were stale from this session's work.
chore(beads): track the spec.sr.ht issue backlog
Seven issues under the epic: Phases 3-5 (write plane -> review UI ->
deferred pile, chained by blocks), the loose-ends bucket of latent bugs,
and two ops tasks (nav restart, stale apk pins). Exported to a
git-tracked JSONL so the backlog is durable and portable, not only in the
local embedded Dolt DB.