Skip to content

Guard against silent anonymous-block graph collisions on push#2

Merged
kurtcagle merged 1 commit into
mainfrom
fix/anonymous-block-graph-collision
Jul 8, 2026
Merged

Guard against silent anonymous-block graph collisions on push#2
kurtcagle merged 1 commit into
mainfrom
fix/anonymous-block-graph-collision

Conversation

@kurtcagle

Copy link
Copy Markdown
Owner

Follow-up to #1 (validate.js), addressing the root cause Ben flagged in review:

a blank line between a <!-- databook:id: ... --> annotation and its code fence makes the block anonymous, and anonymous blocks get PUT to the default graph sequentially — each replacing the last

Bit the UN-GGCE QA pass twice (satellite-orbits in June, capability-maturity this week).

Two changes — one closes the specific trigger, the other closes the class of bug:

  1. lib/parser.js — the pre-fence annotation walk now tolerates a single blank line before giving up. There's no ambiguity in this case (nothing else could plausibly own the annotation), so this is a safe, low-risk relaxation.

  2. commands/push.js (the structural fix) — before executing any push, refuse (rather than silently overwrite) when two or more pushable blocks resolve to the same graph IRI under PUT semantics. This is deliberately independent of Fix databook validate: ESM require crash, silent engine fallback, error masking #1: it catches the collision regardless of why a block ended up anonymous — blank line, malformed annotation, missing frontmatter id, or anything else — rather than only patching this one trigger. --merge (POST, additive) or giving each block a unique id opts out.

Verified locally against three cases before pushing:

  • A blank-line-separated annotation now resolves to its block id correctly.
  • Two anonymous blocks under default (PUT) semantics now die with a clear message before any network call, rather than silently clobbering each other.
  • Named blocks and --merge mode are unaffected — no false positives.

Rationale for doing both rather than just the parser fix: the parser fix alone would have given a false sense that this class of bug was closed, when in fact any other path to an anonymous block (typo in the annotation key, missing id in frontmatter, etc.) would still silently corrupt data on push.

Follow-up to the validate.js fix (PR #1), addressing the root cause Ben
flagged: a blank line between a <!-- databook:id: ... --> annotation and
its code fence makes the block anonymous, and anonymous blocks resolving
to the default graph get PUT sequentially — each replacing the last, with
no error or warning. Bit the UN-GGCE QA pass twice (satellite-orbits in
June, capability-maturity this week).

Two changes, addressing both the trigger and the underlying hazard:

1. lib/parser.js — tolerate a single blank line in the pre-fence backward
   walk. There's no ambiguity in this case (nothing else could plausibly
   own the annotation), so this closes the specific trigger at no cost.

2. commands/push.js (the structural fix) — refuse to push, rather than
   silently overwrite, when two or more pushable blocks resolve to the
   same graph IRI under PUT semantics. This catches the collision
   regardless of *why* a block ended up anonymous (blank line, malformed
   annotation, missing frontmatter id, etc.), not just this one trigger.
   --merge (POST, additive) or unique per-block ids opt out.

Verified locally:
- Blank-line annotation now resolves to its intended block id.
- Two anonymous blocks under default PUT semantics now die with a clear
  message before any network call is attempted.
- Named blocks and --merge mode are unaffected (no false positives).
@kurtcagle kurtcagle merged commit 4b71d19 into main Jul 8, 2026
kosmika pushed a commit to kosmika/w3c-cg_holon that referenced this pull request Jul 8, 2026
Follow-up to the fix in kurtcagle/databook (PR kurtcagle/databook#2),
adapted for this repo's older, divergent implementations/js/ copy of the CLI.

This copy's lib/parser.js predates pre-fence <!-- databook:id: ... --> annotations
entirely (inline-only), so the blank-line trigger Ben hit doesn't apply here — no
parser change needed.

But the same class of bug exists via a different path: this push.js's
resolveGraphIri() falls back to `${docId}#${block.id ?? block.label}` for blocks
with no id, so two or more anonymous blocks sharing a fence label (e.g. two
'turtle' blocks) resolve to the identical graph IRI and silently clobber each
other under PUT.

Adds the same guard as the canonical fix: refuse to push, rather than silently
overwrite, when 2+ pushable blocks resolve to the same graph IRI under PUT
semantics. --merge (POST, additive) or a unique id per block opts out.

Verified locally:
- Two anonymous same-label blocks now die with a clear message before any
  network call (caught via the #turtle fragment collision, not a default-graph
  collision — the correct path for this implementation's fallback logic).
- Named (inline-annotated) blocks and --merge mode are unaffected.
kosmika pushed a commit to kosmika/w3c-cg_holon that referenced this pull request Jul 8, 2026
Follow-up to Paul Trevithick's pre-fence annotation support (PR w3c-cg#1, merged
2026-07-08), which introduced the same class of bug this whole thread has
been chasing: the buffer reset on ANY non-comment line included blank
lines, so <!-- databook:id: ... --> annotations separated from their fence
by a blank line silently become anonymous — the exact trigger that bit the
UN-GGCE QA pass twice in the canonical repo (see kurtcagle/databook#2).

This keeps Paul's forward-scan design intact and just makes the reset
condition tolerate one blank line before giving up, rather than switching
to the canonical repo's backward-walk approach — no reason to rewrite a
working, just-merged contribution when a small, targeted change closes the
gap.

Verified with four cases: single blank line (tolerated), no blank line
(control), two consecutive blank lines (still resets — deliberately not
loosened further), and a real content line between annotation and fence
(still resets, correctly, since that's never ambiguous).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant