You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[finding] check:skill-examples: extraction's body-close regex and the fence walk's closer disagree on where a fence ends #11690
Found while implementing #11355 (packages/spec/scripts/check-skill-examples.ts).
The gap
#11355 gave both loops in extractFromFile one shared notion of fence ownership
(fenceOwners()). The body boundary is still computed twice, by two patterns that do not
agree on what closes a fence:
extraction body end ^```\s*$ (exactly three backticks, column 0)
fence walk close ^ {0,3}`{run,}[ \t]*$ (run-length aware, up to three spaces indent)
Both directions diverge:
A closing line indented up to three spaces, or written with four or more backticks,
closes the fence for the walk but not for extraction. Extraction's body then runs past the
real end of the block — to the next bare three-backtick line, or to EOF.
Latent, no occurrence. Measured on the corpus while landing #11355: 257 marked blocks
across 99 files extract identically with the ownership fix in place, and no file in any
scanned root uses CRLF (checked skills/, content/docs/, packages/spec/src, packages/client-react/src, packages/client/src — zero files containing CR).
Worth recording rather than leaving buried because it is the same family as #11355: two
notions of one fact living in one file, agreeing today. The per-surface vacuous-green guard
would catch a whole surface dropping to zero blocks, but a single file's tail going quiet is
invisible to it.
Suggested shape
Un-adjudicated:
Take the body end from the same walk — fenceOwners() already computes each span's end, so
extraction could read it instead of re-deriving one. Re-measure corpus extraction identity
either way; the 257 blocks / 99 files reading above is the baseline to compare against.
Or state the divergence deliberately in the file's header comment, so the next author knows
which closer governs which half.
Deliberately not folded into #11355's PR: that card's ruling scoped the change to
fence-awareness of extraction's recognition, and this is a different question (where a block ends) whose fix has to be re-measured against the corpus on its own.
Concentrated triage batch: finding → pm:queue, Bug, S (domain:spec stands) — the two fence-close notions in check-skill-examples.ts disagree in both directions (indent/run-length closes for the walk but not extraction; CRLF closes for extraction but not the walk), so extraction can run a body past its real fence end. Fix: one shared closer predicate for both loops (the fenceOwners() precedent shows the shape), with self-test cases for the four divergent spellings the body enumerates.
Claim: PM loop round R6 (wave 2)
Session: session_01NDGG54XF5gbTLdQzCtnaVV
Branch: claude/issue-11690-fence-closer-predicate
Worktree: objectstack-issue-11690
Domain: domain:spec
File surface: packages/spec/scripts/check-skill-examples.ts (one shared fence-closer predicate for both loops, fenceOwners() precedent) + its self-tests (the four divergent spellings the body enumerates) (stop on breach; explain in the report)
Container & model: S mechanical, mode:subagent, model: sonnet — dispatch-gates 2026-08-25 @8619f95: "no path-derived mandate … The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable)"; correctness is pinned by the script's own self-test cases (gate-farm-judged) ⇒ sonnet floor applies
Clause-②: no (spec tooling face; no contract surface)
Serial constraints cleared: fold-or-serial ANSWERED for the check-skill-examples family — #12051 and #12048 share this file but gate ① fails (three different defect forms: closer disagreement / semantic-pass degradation / module resolution), so SERIAL: they hold until this PR lands; this card (Bug) goes first by the selection order. Sole writer of the file this batch. No pin assertions on sibling behavior.
{
"issue": 11690,
"status": "done",
"branch": "claude/issue-11690-fence-closer-predicate",
"pr": "https://github.com/objectstack-ai/objectstack/pull/12175",
"premise_still_valid": true,
"summary": "check-skill-examples.ts's fenceOwners() walk and extractFromFile()'s extraction loop re-derived a fenced block's body end with two independent regexes that disagreed on an indented, an over-long, and a CR-trailing closing line. fenceOwners() now returns each opener's closeLine alongside owners, and extraction reads it directly instead of re-deriving one -- one shared closer predicate, per the fenceOwners() precedent already used correctly in scripts/check-role-word.mjs. Added 4 self-test fixtures (indented, four-or-more-backtick, combined indent+over-long, CR-trailing) pinning the previously-divergent spellings, reverse-verified via an ablation that reintroduced the old two-closer logic and confirmed all 4 fail with the expected messages before restoring the fix.",
"tests": "pnpm --filter @objectstack/spec check:skill-examples: self-test green (14 fixtures incl. the 4 new ones) + real corpus '256 prose examples type-check across 3 surface(s)', IDENTICAL before (origin/main@577fabf, hand-run without self-test: '256 marked example(s) across 99 file(s), 3 surface(s)') and after -- 0 divergence on legitimate corpus, confirming mechanism assumption #2. Ablation: committed the fix, reverted only the closer-derivation line back to the old FENCE_CLOSE_RE scan while keeping the new fixtures, re-ran self-test -> 7 failures across exactly the 4 new fixtures with the predicted messages (e.g. 'extracted 1 block(s), expected 2'), then git checkout HEAD -- <path> restored the fix and self-test went green again. Also green: pnpm check:doc-authoring (389+38 files); pnpm --filter @objectstack/spec check:liveness / check:empty-state / check:variant-docs / check:strictness-ledger / check:scripts-typecheck; pnpm check:merge-driver / check:published-files / check:slot-lookup / check:test-source-alias / check:type-source-resolution; node scripts/check-dev-prereqs.mjs --self-test (the CI-required half; the real non-self-test half needs a full 67-package pnpm build this worktree hasn't done, unrelated to this diff -- lint.yml wires only --self-test); node scripts/check-plugin-teardown-shape.mjs; node scripts/docs-audit/check-affected-docs.mjs; node scripts/docs-audit/check-drift-comment.mjs. node scripts/check-nul-bytes.mjs also clean. All at 90d510d.",
"open_questions": [],
"out_of_scope_findings": []
}
Checklist conclusion (verified against GitHub + local diff): draft ✓ · base main ✓ · Fixes #11690 first line ✓ · exactly 1 file (+229/−10) = the claimed surface to the letter — shared closeLine from fenceOwners(), the second FENCE_CLOSE_RE retired with an in-file tombstone explaining both divergence directions ✓ · 4 new self-test fixtures pinning the enumerated spellings, reverse-verified (old closer logic reintroduced → all 4 red with predicted messages → restored green) ✓ · real-corpus before/after IDENTICAL (256 examples / 99 files / 3 surfaces) — zero legitimate-corpus divergence, which is exactly the mechanism assumption the dispatch asked to prove ✓ · skip-changeset justified and label read back (scripts/** not in the publish allowlist, check:published-files green) ✓ · #12051/#12048 serial honored — untouched ✓ · no spec/src path, no governed path ⇒ Clause-② does not fire, no review chain needed.
Process note for the record: this dev stalled once mid-run (background-monitor wait) and was reset with a foreground-posture message — recovery clean, delivery complete on the first reset. Sonnet-floor dispatch validated: the self-test farm judged correctness exactly as the tiering intends.
Next: ready + merge queue once EVERY check is green (CI started 11:54, in progress — flip rides the next check-in). On MERGE: the serial queue behind this file opens — #12051 dispatches next, then #12048.
Activity
claude commentedon Aug 25, 2026
Concentrated triage batch:
finding→pm:queue, Bug, S (domain:specstands) — the two fence-close notions incheck-skill-examples.tsdisagree in both directions (indent/run-length closes for the walk but not extraction; CRLF closes for extraction but not the walk), so extraction can run a body past its real fence end. Fix: one shared closer predicate for both loops (thefenceOwners()precedent shows the shape), with self-test cases for the four divergent spellings the body enumerates.Generated by Claude Code
os-litant commentedon Aug 25, 2026
Claim: PM loop round R6 (wave 2)
Session:
session_01NDGG54XF5gbTLdQzCtnaVVBranch:
claude/issue-11690-fence-closer-predicateWorktree:
objectstack-issue-11690Domain:
domain:specFile surface:
packages/spec/scripts/check-skill-examples.ts(one shared fence-closer predicate for both loops,fenceOwners()precedent) + its self-tests (the four divergent spellings the body enumerates) (stop on breach; explain in the report)Container & model: S mechanical,
mode:subagent,model: sonnet— dispatch-gates 2026-08-25 @8619f95: "no path-derived mandate … The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable)"; correctness is pinned by the script's own self-test cases (gate-farm-judged) ⇒ sonnet floor appliesClause-②: no (spec tooling face; no contract surface)
Serial constraints cleared: fold-or-serial ANSWERED for the check-skill-examples family — #12051 and #12048 share this file but gate ① fails (three different defect forms: closer disagreement / semantic-pass degradation / module resolution), so SERIAL: they hold until this PR lands; this card (Bug) goes first by the selection order. Sole writer of the file this batch. No pin assertions on sibling behavior.
Generated by Claude Code
os-litant commentedon Aug 25, 2026
os-dev-report
{ "issue": 11690, "status": "done", "branch": "claude/issue-11690-fence-closer-predicate", "pr": "https://github.com/objectstack-ai/objectstack/pull/12175", "premise_still_valid": true, "summary": "check-skill-examples.ts's fenceOwners() walk and extractFromFile()'s extraction loop re-derived a fenced block's body end with two independent regexes that disagreed on an indented, an over-long, and a CR-trailing closing line. fenceOwners() now returns each opener's closeLine alongside owners, and extraction reads it directly instead of re-deriving one -- one shared closer predicate, per the fenceOwners() precedent already used correctly in scripts/check-role-word.mjs. Added 4 self-test fixtures (indented, four-or-more-backtick, combined indent+over-long, CR-trailing) pinning the previously-divergent spellings, reverse-verified via an ablation that reintroduced the old two-closer logic and confirmed all 4 fail with the expected messages before restoring the fix.", "tests": "pnpm --filter @objectstack/spec check:skill-examples: self-test green (14 fixtures incl. the 4 new ones) + real corpus '256 prose examples type-check across 3 surface(s)', IDENTICAL before (origin/main@577fabf, hand-run without self-test: '256 marked example(s) across 99 file(s), 3 surface(s)') and after -- 0 divergence on legitimate corpus, confirming mechanism assumption #2. Ablation: committed the fix, reverted only the closer-derivation line back to the old FENCE_CLOSE_RE scan while keeping the new fixtures, re-ran self-test -> 7 failures across exactly the 4 new fixtures with the predicted messages (e.g. 'extracted 1 block(s), expected 2'), then git checkout HEAD -- <path> restored the fix and self-test went green again. Also green: pnpm check:doc-authoring (389+38 files); pnpm --filter @objectstack/spec check:liveness / check:empty-state / check:variant-docs / check:strictness-ledger / check:scripts-typecheck; pnpm check:merge-driver / check:published-files / check:slot-lookup / check:test-source-alias / check:type-source-resolution; node scripts/check-dev-prereqs.mjs --self-test (the CI-required half; the real non-self-test half needs a full 67-package pnpm build this worktree hasn't done, unrelated to this diff -- lint.yml wires only --self-test); node scripts/check-plugin-teardown-shape.mjs; node scripts/docs-audit/check-affected-docs.mjs; node scripts/docs-audit/check-drift-comment.mjs. node scripts/check-nul-bytes.mjs also clean. All at 90d510d.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
os-litant commentedon Aug 25, 2026
ACCEPT — PR #12175 (R6 W2, reviewer of record: spec seat, session
session_01NDGG54XF5gbTLdQzCtnaVV).Checklist conclusion (verified against GitHub + local diff): draft ✓ · base
main✓ ·Fixes #11690first line ✓ · exactly 1 file (+229/−10) = the claimed surface to the letter — sharedcloseLinefromfenceOwners(), the secondFENCE_CLOSE_REretired with an in-file tombstone explaining both divergence directions ✓ · 4 new self-test fixtures pinning the enumerated spellings, reverse-verified (old closer logic reintroduced → all 4 red with predicted messages → restored green) ✓ · real-corpus before/after IDENTICAL (256 examples / 99 files / 3 surfaces) — zero legitimate-corpus divergence, which is exactly the mechanism assumption the dispatch asked to prove ✓ ·skip-changesetjustified and label read back (scripts/** not in the publish allowlist,check:published-filesgreen) ✓ · #12051/#12048 serial honored — untouched ✓ · no spec/src path, no governed path ⇒ Clause-② does not fire, no review chain needed.Process note for the record: this dev stalled once mid-run (background-monitor wait) and was reset with a foreground-posture message — recovery clean, delivery complete on the first reset. Sonnet-floor dispatch validated: the self-test farm judged correctness exactly as the tiering intends.
Next: ready + merge queue once EVERY check is green (CI started 11:54, in progress — flip rides the next check-in). On MERGE: the serial queue behind this file opens — #12051 dispatches next, then #12048.
Generated by Claude Code
fix(spec): the scoped-page guard must skip a root whose dir is absent
fix(spec): give the client-SDK surface a page-scoped docs root so SDK…