~bigbes/sr-ht-spec

ref: f82d90a4317a0f36bb56eb4ba402f0c0c6d09950 sr-ht-spec/core/comment_test.go -rw-r--r-- 6.6 KiB
e183a11e — Eugene Blikh 24 days ago
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