Skip to content

[finding] check:skill-examples: extraction's body-close regex and the fence walk's closer disagree on where a fence ends #11690

Closed

Description

@os-warren

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:

Status

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:

  1. 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.
  2. 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.

Activity

  1. added
    domain:specDomain lane — seat card indexed by label:pm:seat
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 24, 2026
  2. claude commented on Aug 25, 2026

    @claude
    Contributor

    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.


    Generated by Claude Code

  3. added
    pm:queueReady for the PM dispatch loop
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 25, 2026
  4. added theissue type on Aug 25, 2026
  5. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 25, 2026
  6. self-assigned this
    on Aug 25, 2026
  7. os-litant commented on Aug 25, 2026

    @os-litant
    Contributor

    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.


    Generated by Claude Code

  8. os-litant commented on Aug 25, 2026

    @os-litant
    Contributor

    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

  9. os-litant commented on Aug 25, 2026

    @os-litant
    Contributor

    ACCEPT — PR #12175 (R6 W2, reviewer of record: spec seat, session session_01NDGG54XF5gbTLdQzCtnaVV).

    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.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:specDomain lane — seat card indexed by label:pm:seat

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions