Skip to content

docs(skills): tag the 16 comment-carrying JSON fences jsonc - #7473

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-7462-jsonc-fence-tags
Sep 4, 2026
Merged

docs(skills): tag the 16 comment-carrying JSON fences jsonc#7473
os-zhuang merged 2 commits into
mainfrom
claude/issue-7462-jsonc-fence-tags

Conversation

@os-litant

@os-litant os-litant commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7462

Sixteen fenced blocks under skills/objectui/ were tagged ```json while
their bodies carry // comments — syntax JSON forbids and JSONC allows. This
retags exactly those sixteen to ```jsonc. Tag-only: the whole diff is
sixteen opener lines.

What was counted, at the branch point 0246d11

skills/objectui/**/*.md holds 56 fences tagged json (and zero tagged jsonc).
17 of them fail JSON.parse — the card's figure, reproduced exactly:
12 in guides/schema-expressions.md, 4 in rules/protocol.md, 1 in rules/styling.md.

⚠️ The card's criterion and the card's number do not select the same set, and the
gap is exactly one block. Counting by "carries a // comment or a ... elision"
(the claim comment's wording) gives 16; counting by "fails JSON.parse" gives
17. The seventeenth is guides/schema-expressions.md:206, which uses no JSONC
feature at all — it is two top-level JSON documents listed in one fence. It keeps
its json tag
, because jsonc would be a different false claim rather than a
truer one: the block's defect is that it is a listing, not that it is a dialect.
That is a judgement call, recorded here and on the card, and it is reversible in
one byte if the maintainer reads the card's headline number as the scope.

Sites retagged, per file

file fence opener lines fences retagged
skills/objectui/guides/schema-expressions.md 32, 170, 328, 355, 365, 374, 391, 447, 463, 479, 492 11
skills/objectui/rules/protocol.md 157, 166, 177, 187 4
skills/objectui/rules/styling.md 170 1

Each opener was asserted to be exactly the six bytes of the json info string
before it was rewritten; the retag script refuses the file otherwise.

Arithmetic

file bytes before bytes after delta lines before lines after
skills/objectui/guides/schema-expressions.md 20653 20664 +11 518 518
skills/objectui/rules/protocol.md 13549 13553 +4 320 320
skills/objectui/rules/styling.md 10793 10794 +1 319 319
total +16 unchanged

+1 byte per retagged fence, sixteen fences, sixteen bytes, zero line-count change.
git diff -U0 filtered to changed lines yields exactly 16 removals of the json
opener and 16 additions of the jsonc opener, and nothing else.

Token ratchet: none exists. No script in this repository budgets tokens, bytes
or lines over skills/**. The two budget scripts, check-eager-closure-budget.mjs
and render-budget-comment.mjs, measure the shipped JavaScript closure, not
markdown. The five scripts that read skills/ at all are
check-skill-examples.mjs, check-skills-paths.mjs, check-shell-escape-residue.mjs,
check-governed-queue-guard.mjs and check-doc-snippet-types.mjs (the last only to
NAME the tree as unscanned). So the arithmetic above is reported because the card
asked for it, not because anything gates on it.

What the tag buys, measured with the repository's own parser

check-skill-examples.mjs already implements both dialects — JSON_FENCE_LANGUAGES
is the set json, jsonc, and parseJsonFence strips comments and trailing commas
for jsonc only. Its header records the corpus state this pull request changes:
"There are zero jsonc fences in the corpus today; the language is recognised so
that adding one is not a silent no-op."

Running that exact function over the corpus, before and after:

fences parse under their own tag
before 56 json, 0 jsonc 39 of 56
after 40 json, 16 jsonc 39 of 40 json, 7 of 16 jsonc = 46 of 56

So seven blocks become genuinely checkable — eligible for the gate's opt-in marker —
and nine do not. That residue is honest and is not repaired here, because
repairing it needs content edits and this flight is tag-only:

  • seven list several top-level JSON documents in one fence
    (schema-expressions.md 32, 170, 447, 463, 479, 492; protocol.md 177);
  • two carry ... elisions, which no JSON dialect accepts
    (protocol.md 157, 166);
  • plus schema-expressions.md:206, which stays json, same listing shape.

Eleven blocks in total still parse under no dialect. They are recorded on the card
as a follow-up, not smuggled in here.

Decision: check-doc-fence-languages.mjs does NOT gain a skills/ root

The card asked whether this gate should be widened the way check-skills-paths.mjs
was by #7358. No — and the answer is measured rather than argued. Both legs
below ran against a throwaway mutation that added a skills walk to
listDocuments, proven on disk before any verdict was read (marker count 1, blob
hash de67f1d to 194926b) and restored byte-identically afterwards (hash back to
de67f1d, git diff HEAD for that path empty).

Leg A — the gate itself reds, in the one mode it can never absorb. Widened, it
exits 1 on skills/objectui/guides/project-setup.md:115: a ```javascript
fence whose first line is export default, which its quoted triage classifier calls
code. Because javascript is not in UNHIGHLIGHTED_SPELLINGS, that is UNKNOWN mode,
and the gate's header is explicit that UNKNOWN "⛔ can never be baselined and fails
on sight". So this is worse than the "would the baseline grow" test the card set:
the baseline could not take it at any size. Zero SYNONYM findings, one UNKNOWN.

Leg B — the widening is refused by the tree's own pin. The gate's scan surface is
pinned equal, element by element, to check-doc-snippet-types's by
scripts/__tests__/check-doc-fence-languages.test.ts ("walks exactly the documents
the snippet gate walks"). Under the mutation the two lists are 243 and 227 documents
and the assertion is false, the difference being exactly the 16 skills/objectui
pages. Widening this gate alone is therefore not a small edit; it is a three-gate
surface move.

And the surface owner has already been named. check-doc-snippet-types.mjs
states, beside UNGATED_DOCS: "⛔ skills/objectui/** is NOT claimed by any gate
here, and this line is the opposite of a claim on it: it is a governed, published
surface with its own review path, so pointing a doc gate at it is a decision for
whoever owns that surface — never a side effect of a root move." Doing it inside a
tag flight would be exactly the side effect that sentence forbids. For scale: that
gate is covered-by-default and compiles every ts/tsx fence it walks, and
skills/ holds 112 of them.

The four axes

  • Real business need. Measured, not assumed: the population the widening would
    find in skills/ is ONE block, and it is a genuine JavaScript config example, not
    hidden TypeScript. There is no accumulation to catch here today. The need the card
    actually names — a tag that lies to a reader — is served in full by the retag,
    which the gate would not have caught either: it judges TypeScript bodies under
    non-TypeScript fences and says nothing whatsoever about json versus jsonc.
  • Long-term soundness (the axis carrying at least half the weight). The durable
    shape is one surface decision made once, by the surface's owner, moving all three
    doc gates together with the UNGATED_DOCS accounting that decision implies. The
    patch-shaped alternative — widen one gate, then either re-fence a JavaScript block
    as TypeScript or add javascript to a set of spellings for unhighlighted blocks —
    buys one gate's reach by corrupting two vocabularies that other gates read. Both
    remedies are worse than the debt, and the debt is already written down by name.
  • Preventing AI-authored mistakes. This is what decides the retag half and does
    not decide the widening half. An agent reading skills/objectui/ copies what the
    fence claims: a block tagged json carrying // teaches that comments are legal
    in the .json files it is about to write, and metadata files are exactly where
    that error would land silently. The fence gate would not have caught that in any
    configuration. Making the tag honest is the enforceable half available today;
    widening a TypeScript-body gate is not related to it.
  • Startup scope discipline. A p3 truth item stays a p3 truth item. Turning it
    into a three-gate surface move plus a compile-coverage decision over a published,
    governed tree is the scope creep the focus principle exists to refuse. The debt
    stays named and unclaimed, which is the state that header calls strictly better
    than an unnamed one.

Consequently there is no self-test leg to add (the card's conditional step): no
gate changed, so no gate needs a new fixture. The reverse verification that WOULD
have accompanied a widening was still run, as the two ablation legs above — they are
what makes "do not widen" a measurement instead of a preference.

The tag is load-bearing, not cosmetic — proven both directions

A tag-only change invites the question "does anything actually read this?", so it
was answered against the live gate rather than asserted. Throwaway mutation on
rules/styling.md, under a trap, both legs proven on disk before any verdict was
read:

  1. Opt the retagged fence in, tag left jsonc. Marker lines 3 to 4, blob
    06654f3 to 60b888c. check-skill-examples exits 0:
    "JSON phase: 40 fence(s) parsed, 0 failed."
  2. Same block, same marker, tag flipped back to json. jsonc openers 1 to 0,
    blob 60b888c to 057fa51. The gate exits 1 and names the site:
    "[json] skills/objectui/rules/styling.md:171 Expected double-quoted property
    name in JSON at position 50 (line 3 column 31)."
  3. Restore proven, not assumed: git checkout HEAD on the path, blob back to
    06654f3 — byte-identical to the HEAD blob — git diff HEAD for that path empty,
    marker count back to 3, jsonc openers back to 1.

So the retag moves seven blocks from "cannot be opted in without the gate going red"
to "can". Opting them in is a content edit and is NOT done here; it is the natural
follow-up and is recorded on the card.

An earlier attempt at this probe used a perl substitution whose anchor matched
nothing. It exited 0 with the file untouched — the classic green no-op — and was
caught only because the mutation is proven by hash and grep count before any verdict
is read. Recording it because the guard is the reason the numbers above mean
anything.

Gates

Every exit code was captured by redirect before any pipe, and each row quotes the
gate's own verdict line.

gate exit its own verdict line
node scripts/check-skill-examples.mjs 0 "Every marked skill example holds up against the built types." — "Scanned 16 guide(s) under skills: 112 ts/tsx/typescript fence(s), 56 json/jsonc fence(s). Marked: 17 ts fence(s) and 39 json fence(s)." · "JSON phase: 39 fence(s) parsed, 0 failed." The 16 retagged fences stay UNMARKED, exactly as the card requires, so the marked population is unchanged at 39. Needed a build first — it exits 2, "PRECONDITION NOT MET", against an unbuilt tree, and that is not a verdict about any guide.
node scripts/check-doc-fence-languages.mjs 0 "✅ check:doc-fences — every TypeScript block in 227 document(s) is fenced ts/tsx/typescript, except 80 declared file(s) carrying 90 block(s) of objectui#5867's remaining population (⛔ SHRINK-ONLY). No unknown fence spelling hides one." Unchanged by this diff — the gate does not walk skills/, which is the subject of the decision above.
node scripts/check-skills-paths.mjs 0 "✅ check-skills-paths: OK (88/89 stated path(s) resolve across 20 guide file(s); 1 baselined)." — "skills/ — 28/28 resolve across 16 file(s)".
node scripts/check-shell-escape-residue.mjs 0 "✅ check-shell-escape-residue: OK (5/5 root(s) resolved … skills: 16 file(s), 196 fence(s) … 0 occurrence(s) outside a fence)". This one DOES read the retagged files.
node scripts/check-control-bytes.mjs 0 "✅ check-control-bytes: OK (scanned 6144 tracked text file(s); skipped 85 binary)." Plus a direct scan of the three changed files for the control range: no hits.
node scripts/check-doc-links.mjs 0 "Links are valid across 17 scan roots."
node scripts/check-changeset-presence.mjs 0 "✅ No source or published contract of a released package changed in this range, so no changeset is owed." — "3 file(s) changed, 0 of them published source of a package the release covers". Its own verdict decides, and it says none is owed.
node scripts/check-governed-queue-guard.mjs --test (the three final paths) 3 "⛔ GOVERNED — 3 of 3 path(s) are on a governed surface: skills/** x3 — the published skills catalog … Park it as a DRAFT and leave the merge to the maintainer." Exit 3 is the CORRECT verdict for this diff, not a red. This pull request is a draft and stays one.
pnpm exec vitest run over 8 scripts/__tests__ specs, under the shared lock 0 "Test Files 8 passed (8) · Tests 347 passed (347)". Specs: doc-fence-languages, skill-examples, skills-paths, shell-escape-residue, governed-queue-guard, control-bytes, doc-links, changeset-presence. Lock verdict: "command-exit 0 · held the lock 25s · waited 0s".
the build the skill-examples gate needs 0 turbo run build over the gate's own --build-filter closure: "Tasks: 29 successful, 29 total". Lock verdict: "command-exit 0 · held the lock 294s".
eslint over changed .mjs Nothing to run: the diff is three markdown files.
objectstack scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectui 2 Verbatim: "dispatch-gates: REFUSING — asked for 'objectstack-ai/objectui', but this checkout is 'objectstack-ai/objectstack'." and "this script exists only in 'objectstack-ai/objectstack', so a sister repo's list is hand-derived from its own package manifest and its own workflow files." So the union above was hand-derived from this repo's own package.json and .github/workflows/, which carry no paths filter for these gates.

All ten runnable rows were re-run at the final commit ec67429 with a clean working
tree, and every exit code was captured by redirect before any pipe.

Not measured, and why

  • Remote CI. Reported at draft time by dispatch contract; the merge-queue leg of
    Governed Surface Queue Guard cannot run on a draft at all.
  • eslint over changed .mjs files. Nothing to run: the diff contains three
    markdown files and no script.
  • Changeset. Not owed, by the presence gate's own verdict, quoted in the table.
  • The nine jsonc fences and one json fence that still parse under no dialect.
    Their content is untouched by design; they are a follow-up on the card.
  • Whether a widened trio of doc gates would pass over skills/. Deliberately not
    measured. Compiling 112 fences against built dist to cost a surface move nobody
    has authorised is the scope this pull request declines.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

Queue fix (2026-09-04)

New head dc4811c. One commit on top of ec67429; nothing above this section
is changed, and no retag is reverted.

Why the queue rejected this pull request twice

packages/components/src/__tests__/skill-guide-data-table-binding.test.tsx reads the
real guides at run time, and its extractor was literal about the fence tag:

const fence = /```json\n([\s\S]*?)```/g;

rules/protocol.md's "bind": "customerNames" list example is one of the four
fences this branch retags, so a jsonc opener made it invisible to every assertion
in that file. Two of them read the absence as data:

  • the counter-probe "still teaches bind in a JSON block" — "expected false to be true";
  • "its list example renders one entry per bound item" — "the guide must carry a
    parseable list example bound with bind: expected undefined to be truthy".

The pull-request-level run never saw it: the "Decide whether this change needs a full
run" step trims the suite for a docs-only diff, and the full run is the merge_group
one. Same failure both ejections.

The fix

Widen the extractor to read both tags — the opener becomes jsonc? in the
same expression — and say so in the helper's doc comment. parseBlock already strips // comments before
JSON.parse, so jsonc bodies parse today; only the extractor was behind. The bind
block keeps its jsonc tag: those fences carry comments, which is the falsehood this
branch exists to remove.

Files: packages/components/src/__tests__/skill-guide-data-table-binding.test.tsx and
.changeset/jsonc-fence-extractor-test-only.md (empty frontmatter — a test-only change
declared as releasing nothing; the presence gate demanded a declaration once a
packages/components/src file entered the diff, and this is its explicit pass form).

Counts, re-derived at this head

A faithful re-implementation of jsonBlocks + parseBlock, run over the three guides
the test names, narrow tag then widened — fences / data-table / list /
bind=customerNames / offenders:

guide narrow (json only) widened (jsonc?)
guides/schema-expressions.md 8 / 1 / 1 / 1 / 0 19 / 1 / 5 / 1 / 0
rules/protocol.md 6 / 0 / 0 / 0 / 0 10 / 0 / 1 / 1 / 0
guides/data-integration.md 4 / 1 / 1 / 1 / 0 4 / 1 / 1 / 1 / 0

The data-table count is unchanged at 1 / 0 / 1, so expect(extra).toEqual([]) and
the [taught] node selection are untouched; the offenders list stays empty; the two
failing assertions get their block back. The newly visible protocol.md node is the
shape the other two guides already carry, character for character:
{"type":"list","bind":"customerNames"} — which is why widening it renders rather
than merely parses. guides/data-integration.md is untouched by this branch and stays
untouched; the widening only makes a future retag there safe.

Reverse verification — one mutation, proven on disk before any verdict was read

Restore the narrow json-only extractor on the committed state, under an
EXIT/INT/TERM trap with an absolute path:

  1. Mutation proven, not assumed. File back to blob 421bca4 — byte-identical to
    the parent's blob for that path — widened-regex count 0, narrow-regex count
    1, all three read before the test ran.
  2. Exactly the two named assertions red, and nothing else. "Tests 2 failed | 14
    passed (16)", "Test Files 1 failed (1)", with the two messages quoted above. Lock
    verdict: "command-exit 1".
  3. Restore proven. Hash back to 76fdb28, git diff HEAD for that path empty,
    working tree clean at the final commit.

With the fix, the same whole-file command: "Test Files 1 passed (1)" / "Tests 16
passed (16)", lock verdict "command-exit 0".

Gates, re-run at dc4811c with a clean working tree

Every exit code captured by redirect before any pipe; each row quotes the gate's own
verdict line.

gate exit its own verdict line
pnpm exec vitest run packages/components/src/__tests__/skill-guide-data-table-binding.test.tsx (whole file, shared lock) 0 "Test Files 1 passed (1)" · "Tests 16 passed (16)" · lock "VERDICT command-exit 0"
node scripts/check-skill-examples.mjs (after turbo run build over its own --build-filter) 0 "Every marked skill example holds up against the built types." · "JSON phase: 39 fence(s) parsed, 0 failed."
node scripts/check-changeset-presence.mjs 0 "✅ 1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) … Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate."
node scripts/check-changeset-fixed.mjs 0 "✅ All workspace packages are in the changeset fixed group."
node scripts/check-changeset-no-major.mjs 0 "✅ No changeset declares a major bump."
node scripts/check-changeset-overwrite.mjs 0 "✅ No pre-existing changeset was modified or deleted."
node scripts/check-control-bytes.mjs 0 "✅ check-control-bytes: OK (scanned 6145 tracked text file(s); skipped 85 binary)."
node scripts/check-doc-fence-languages.mjs 0 "✅ check:doc-fences — every TypeScript block in 227 document(s) is fenced ts/tsx/typescript …"
node scripts/check-skills-paths.mjs 0 "✅ check-skills-paths: OK (88/89 stated path(s) resolve across 20 guide file(s); 1 baselined)."
node scripts/check-shell-escape-residue.mjs 0 "✅ check-shell-escape-residue: OK (5/5 root(s) resolved … 0 occurrence(s) outside a fence)."
node scripts/check-package-self-import.mjs 0 "✅ No package names itself inside its own src/."
node scripts/check-vi-mock-specifiers.mjs 0 "✅ check-vi-mock-specifiers: OK (4218 tracked source file(s), 2474 test-named …)"
node scripts/check-vi-mock-inherit.mjs 0 "✅ check-vi-mock-inherit: OK (… 121 inherit, 0 auto-mocked …)"
node scripts/check-governed-queue-guard.mjs --self-test 0 "OK check-governed-queue-guard self-test: 132 cases pass …"
node scripts/check-governed-queue-guard.mjs --test (the five final paths) 3 "⛔ GOVERNED — 3 of 5 path(s) are on a governed surface: skills/** x3 — the published skills catalog". Unchanged verdict: the two paths this section adds are not governed, and exit 3 is the correct reading for this diff, not a red.
pnpm --filter @object-ui/components run type-check 0 "tsc --noEmit && tsc -p tsconfig.test.json", both clean. Coverage measured rather than assumed: tsc -p tsconfig.test.json --listFiles names the edited file exactly once, so this is a reading about it.
pnpm exec eslint . in packages/components (the whole changed package) 0 448 file(s) judged, 0 error(s); 934 pre-existing warnings. The changed .tsx alone: exit 0, no output.

Not measured, and why

  • Remote CI, including the merge_group run. Reported at push time by dispatch
    contract. The whole-file run above is the local stand-in for the queue's full run.
  • tsc -p tsconfig.scripts.json. Does not apply: the changed file belongs to
    @object-ui/components, whose own type-check chains tsconfig.test.json, and that
    is the project that reads it.
  • The repository-wide pnpm lint. Narrowed to the one changed PACKAGE, and the
    narrowing is measured rather than asserted. Population read from eslint's own config:
    the root flat config extends tseslint.configs.recommended, not the type-checked
    variant, and its languageOptions names no parserOptions.project or
    projectService — so no file's verdict depends on a TypeScript program, and a diff
    inside packages/components cannot move the verdict of a file in another package.
    File count read from --format json: eslint . in packages/components judged
    448 files, 0 errors (934 pre-existing warnings), exit 0, and the edited file is in
    that judged set.
  • The nine jsonc fences and one json fence that still parse under no dialect.
    Unchanged by this section, still the follow-up recorded on the card.

What this needs from a human

The earlier approving review was pinned to ec67429. The new head dc4811c needs a
fresh approval pinned to it before the merge queue will take this pull request

that is the approver's action. Nothing here touched the draft/ready state, reviewers,
labels, auto-merge, or queue membership; the push landed only because the failing
group had already dequeued this pull request on its own.

Sixteen fenced blocks under `skills/objectui/**` are tagged `json` while
their bodies carry `//` comments -- syntax JSON forbids and JSONC allows --
so a reader or a gate that trusts the tag and parses the block fails.
`check-skill-examples.mjs` already implements both dialects
(`JSON_FENCE_LANGUAGES`, `parseJsonFence`); its header records that the
corpus had zero `jsonc` fences, so the tag was the only thing missing.

Tag-only, by line number, after asserting each opener is exactly the six
bytes of the `json` info string: 11 in guides/schema-expressions.md, 4 in
rules/protocol.md, 1 in rules/styling.md. +1 byte per fence, +16 bytes
total, zero line-count change, and the whole diff is 16 opener lines.

Seven of the sixteen now parse under the repository's own JSONC dialect;
the other nine stay unparseable for a reason a tag cannot fix (seven list
several top-level documents in one fence, two carry `...` elisions), which
is recorded on objectui#7462 rather than repaired here.

Ref: objectui#7462

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 08:58
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@hotlong
hotlong requested a review from os-zhuang September 3, 2026 14:36

hotlong commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Maintainer ruling, 2026-09-03, given in chat to the director seat (session_01WXyGTWPbbreqXow7Z2pZCk) on decision batch 2, item 3 (this PR, option A: merge as-is, the seventeenth fence keeps json). Verbatim: 「同意」.

Executing on that instruction from the maintainer's own account: merging directly (squash); if a repository rule requires the merge queue, auto-merge (squash) is armed instead. No approving review is submitted by any seat; the human merge is the review record for the governed paths skills/objectui/**.


Generated by Claude Code

@os-zhuang
os-zhuang added this pull request to the merge queue Sep 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@hotlong
hotlong added this pull request to the merge queue Sep 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@hotlong
hotlong added this pull request to the merge queue Sep 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 3, 2026

hotlong commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Director seat (session_01WXyGTWPbbreqXow7Z2pZCk): the merge-queue build for this PR (17:14Z, enqueued by os-zhuang) was rejected, and the failure is this PR's own, not a flake.

Failing job: CI / Test (shard 2/4) in run 33783410189 (job 100742301494), 2 failed of 8033. Both in packages/components/src/__tests__/skill-guide-data-table-binding.test.tsx:

  • skills/objectui/rules/protocol.md still teaches \bind` in a JSON block` — expected true, got false (line 165);
  • skills/objectui/rules/protocol.md: its \list` example renders one entry per bound item— "the guide must carry a parseable list example bound withbind`", got undefined (line 302).

Mechanism: that test's jsonBlocks() extracts fences with the literal matcher for a json opener followed by a newline (line 97). The four protocol.md fences this PR retags to jsonc (openers at 157, 166, 177, 187) fall out of its view, so the bind example the test pins is no longer found. The PR-level CI did not catch it because the Decide whether this change needs a full run step trims the suite for a docs-only diff; the queue's merge_group run is the full run. Every other job in the queue build, the Governed Surface Guard included, was green.

Ask for the author seat (skills), in this PR: either widen that test's matcher to accept both json and jsonc openers (and strip comments before it parses, the way check-skill-examples.mjs's parseJsonFence does for jsonc), or keep the json tag on the specific block that test depends on and say why in the PR body. Then push; the new head needs a fresh approval from os-zhuang or hotlong pinned to it before the queue will take it again (the 15:30Z approval is pinned to ec67429). The maintainer's option-A ruling (comment 5527938044) stands; the retag itself is not in question.

Card #7462 keeps pm:dispatched. Auto-merge is not re-armed by the director seat until the fix is on the branch.


Generated by Claude Code

os-sales commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

The fence retag breaks a test that reads these files at run time

Flagging from the PR that was queued behind this one — the same red will recur on the next enqueue, so it is worth having the diagnosis written down before then.

What failed. Merge group gh-readonly-queue/main/pr-7523-dd84b97776462a04318b738347203737f281a489 (this PR speculated ahead of #7523) went red on Test (shard 2/4): run 33783497752, 2 failed / 8031 passed.

Both failures are in packages/components/src/__tests__/skill-guide-data-table-binding.test.tsx, which is not a fixture test — it lifts JSON blocks out of the real guides at run time, and skills/objectui/rules/protocol.md is one of the three files in its GUIDES map.

Mechanism. Its extractor is literal about the fence tag:

const fence = /```json\n([\s\S]*?)```/g;

The \n has to come immediately after json, so a ```jsonc fence does not match — every fence this PR retags becomes invisible to that extractor. Two assertions land on it:

line assertion why it goes red
:165 counter-probe, blocks.some(b => /"bind"\s*:/.test(b)) the "bind": "customerNames" block at protocol.md ~187 is one of the four fences this PR retags, so no remaining ```json block in that file carries a bind key
:302 blocksOfType(md, 'list').find(n => n.bind === 'customerNames')undefined same block, same cause

blocks.length > 0 still passes, so the file is being found and read — this is specifically the tag, not the path.

Suggested fix, inside this PR: widen the extractor rather than revert the tags. The retag looks correct to me on its merits — those blocks carry // comments, and parseBlock in that same test already strips // before JSON.parse, so they have always been jsonc in a json fence. The extractor is the part that is behind:

const fence = /```jsonc?\n([\s\S]*?)```/g;

Two things worth checking in the same pass:

  • skills/objectui/guides/data-integration.md is the third entry in GUIDES and this PR does not touch it. Widening the extractor makes a later retag there safe; leaving it narrow means the next fence PR trips the same wire.
  • skills/objectui/guides/schema-expressions.md is retagged here too and its data-table render legs (:239) still passed, so at least one ```json data-table block survives there — worth confirming that is intentional rather than incidental once the extractor accepts both tags.

Queue state. That failing group dequeued this PR; #7523 was re-speculated on its own against main and is running clean so far. Nothing else is being asked of this PR — I am not pushing to it, only recording why it went red so the next enqueue is not a re-diagnosis.


Generated by Claude Code

@os-litant
os-litant added this pull request to the merge queue Sep 4, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 4, 2026
…hide the bind example

The guide extractor in `skill-guide-data-table-binding.test.tsx` matched a
```json opener literally, so this branch's retag of `rules/protocol.md`'s four
comment-carrying fences made the `"bind": "customerNames"` list example
invisible to every assertion in the file. Two of them then read that absence as
data:

  - the counter-probe "still teaches `bind` in a JSON block" —
    "AssertionError: expected false to be true";
  - "its `list` example renders one entry per bound item" —
    "AssertionError: the guide must carry a parseable list example bound with
    `bind`: expected undefined to be truthy".

The pull-request-level run never saw it: that job trims the suite for a
docs-only diff, and the full run is the merge_group one — which is why this
branch was ejected from the queue twice on the same failure.

`parseBlock` already strips `//` comments before `JSON.parse`, so jsonc bodies
parse today; only the extractor was behind. Widening it to accept both tags is
the whole fix. No retag is reverted and the `bind` block does not go back to
`json`: those fences carry `//` comments, which is the falsehood this branch
exists to remove.

Counts re-derived at ec67429 with a faithful re-implementation of `jsonBlocks` +
`parseBlock` — fences / data-table / list / bind=customerNames / offenders,
narrow then widened:

  guides/schema-expressions.md    8/1/1/1/0  ->  19/1/5/1/0
  rules/protocol.md               6/0/0/0/0  ->  10/0/1/1/0
  guides/data-integration.md      4/1/1/1/0  ->   4/1/1/1/0

The data-table count is unchanged at 1 / 0 / 1, so `expect(extra).toEqual([])`
and the `[taught]` node selection are untouched; the offenders list stays empty;
the two failing assertions get their block back. The newly visible protocol.md
node is the same shape the other two guides already carry, character for
character: {"type":"list","bind":"customerNames"}.

Reverse verification, one mutation, proven on disk before any verdict was read:
restoring the narrow json-only extractor put the file back to blob 421bca4 (the
parent's blob for that path) with a widened-regex count of 0 and a narrow-regex
count of 1, and made exactly those two assertions red and nothing else —
"Tests 2 failed | 14 passed (16)", shared-lock verdict "command-exit 1". The
restore ran under an EXIT/INT/TERM trap with an absolute path and is proven by
hash: back to 76fdb28. With the fix in place the same whole-file command gives
"Test Files 1 passed (1)" / "Tests 16 passed (16)", lock verdict
"command-exit 0".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3182.6 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-uE6JHIGb.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 516.22KB 117.83KB
core (index.js) 6.12KB 2.42KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 180.00KB 50.20KB
fields (index.js) 242.40KB 61.26KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 48.09KB 13.34KB
plugin-charts (index.js) 70.92KB 19.75KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.89KB 34.68KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 250.59KB 64.06KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.40KB 41.05KB
plugin-grid (index.js) 210.75KB 56.95KB
plugin-kanban (index.js) 52.71KB 14.55KB
plugin-list (index.js) 113.28KB 27.59KB
plugin-map (index.js) 20.55KB 6.80KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.57KB 11.96KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 9.38KB 3.22KB
plugin-view (index.js) 85.24KB 20.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.58KB 2.23KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

Queue fix pushed — new head dc4811c. One commit on top of ec67429; no retag reverted, nothing touched in the draft/ready state, reviewers, labels, auto-merge or queue membership.

What was wrong. packages/components/src/__tests__/skill-guide-data-table-binding.test.tsx reads the real guides at run time and its extractor matched the fence tag literally, so retagging rules/protocol.md's "bind": "customerNames" example to jsonc made it invisible to every assertion in the file. Two of them then read that absence as data — the counter-probe "still teaches bind in a JSON block" ("expected false to be true") and "its list example renders one entry per bound item" ("the guide must carry a parseable list example bound with bind: expected undefined to be truthy"). The pull-request-level run trims the suite for a docs-only diff; the full run is the merge_group one, which is why both ejections showed the same failure.

What changed. The extractor now reads json and jsonc openers, and the helper's doc comment says so. parseBlock already stripped // before JSON.parse, so jsonc bodies parsed today — only the extractor was behind. Plus .changeset/jsonc-fence-extractor-test-only.md with an empty frontmatter: the presence gate started demanding a declaration the moment a packages/components/src file entered the diff, and an empty frontmatter is its explicit pass form for a test-only change.

Counts re-derived at this head (fences / data-table / list / bind=customerNames / offenders), narrow then widened: schema-expressions.md 8/1/1/1/0 to 19/1/5/1/0 · protocol.md 6/0/0/0/0 to 10/0/1/1/0 · data-integration.md 4/1/1/1/0 to 4/1/1/1/0. The data-table count is unchanged at 1/0/1, so expect(extra).toEqual([]) and the [taught] selection are untouched and the offenders list stays empty.

Verdicts (exit codes captured by redirect before any pipe, quoting each gate's own line):

  • whole test file under the shared lock: "Test Files 1 passed (1)" · "Tests 16 passed (16)" · lock "VERDICT command-exit 0";
  • reverse leg, mutation proven on disk first (blob back to 421bca4, widened-regex count 0, narrow-regex count 1): "Tests 2 failed | 14 passed (16)", exactly the two assertions above and nothing else, lock "command-exit 1"; restored under an EXIT/INT/TERM trap and proven by hash back to 76fdb28 with an empty git diff HEAD;
  • check-skill-examples 0 ("Every marked skill example holds up against the built types.") · check-changeset-presence 0 · check-control-bytes 0 · check-doc-fence-languages 0 · check-skills-paths 0 · check-shell-escape-residue 0 · check-governed-queue-guard --self-test 0 · the three changeset gates 0 · type-check for @object-ui/components 0 (coverage measured: --listFiles names the edited file once) · eslint . over the whole changed package 0, 448 files judged;
  • check-governed-queue-guard --test over the five final paths stays 3, GOVERNED on skills/** x3 — the same correct verdict as before; the two paths added here are not governed.

This needs a human before it can re-enter the queue: the earlier approving review was pinned to ec67429, so the new head dc4811c needs a fresh approval pinned to it before the merge queue will take this pull request. That is the approver's action, not mine.

One note for whoever reads the description: the PATCH that appended the "Queue fix (2026-09-04)" section came back with its trailing attribution footer stripped by the platform, while the pre-existing body above it is byte-identical to what was there before (verified by comparing the stored markdown before and after). The session link is still in the description prose, so it is not re-posted here in a loop.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

In-seat review of the queue fix — ACCEPT (skills seat linger, session session_01LraLgQVGq8egUwfYZpbYt1, read 2026-09-04T04:22Z). Over to the approver: this needs a fresh approval pinned to dc4811c before the queue will take it.

What the seat checked itself, not from the report:

  • Head dc4811c = the API head. Two commits, ec67429 intact as the parent — no rebase, no amend, no force-push. git merge-tree against the current origin/main (e30ed15, 03:21Z): clean.
  • Diff vs main is exactly five files: the three retagged guides (unchanged from the earlier review), the test, and one changeset. +31 / −18. Nothing else rode along.
  • The change is the one line plus its comment: the extractor's opener becomes jsonc? in the same expression; parseBlock was already stripping // before JSON.parse, so only the extractor was behind. No retag reverted — in particular the bind block keeps jsonc, which is the point of the branch.
  • The counts the fix rests on are the seat's own, taken at 03:45Z before the flight was dispatched (a faithful re-implementation of jsonBlocks + parseBlock over the three guides the test names): the data-table count is unchanged at 1 / 0 / 1 under the widened extractor, so expect(extra).toEqual([]) and the [taught] selection cannot move; offenders stay empty in all three; protocol.md gets its bind=customerNames node back, which is exactly the two assertions that were red. The dev's independent re-derivation agrees value for value.
  • Blast radius, checked rather than assumed: seven test files across five packages name skills/objectui/**. Only two open a guide at run time, and the second one (skill-guide-provider-envelope.test.tsx) matches whole-file prose with toMatch, never a fence tag — so no other suite was silently reading the retagged blocks, and none is disturbed by the widening.
  • The changeset is empty-frontmatter, which is the presence gate's own explicit pass form for a change that releases nothing; the gate says so in its verdict.
  • CI at this head, 04:22Z: 29 of 32 checks complete, every one success or skipped, none failed; Test (shard 1/4), (2/4) and (3/4) still in progress, (4/4) already green. Worth noting that the shards are running this time and taking about ten minutes — on the previous head they reported green in under a minute having executed nothing, because the diff was markdown-only. That difference is the mechanism, visible in the check timings.

One thing this instance does not fix, recorded and not filed by this seat (it lands on .github/workflows/ci.yml, domain:devx, and the seat is off shift): the pull-request lane's "Decide whether this change needs a full run" step treats every **/*.md path as inert, and skills/** is both a published surface and a test input. A markdown-only PR therefore reports four green Test (shard N/4) contexts having run nothing, and the first real signal is a queue ejection — twice here, nine hours after the push, and it dequeued a PR speculating behind this one as well. The durable form is to exclude the doc trees rather than the .md extension, and to derive that list from what tests actually read instead of hand-maintaining it.


Generated by Claude Code

@os-zhuang
os-zhuang added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit 4bb5e10 Sep 4, 2026
33 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-7462-jsonc-fence-tags branch September 4, 2026 05:00
os-steve pushed a commit that referenced this pull request Sep 4, 2026
`parseJsonFence`'s docblock said "There are zero `jsonc` fences in the
corpus today" as the reason the dialect is recognised. That stopped being
true at PR #7473 (16 comment-carrying fences retagged `jsonc`) and PR
#7608 moved it further (41 `json` / 29 `jsonc` today, confirmed with
`node scripts/check-skill-examples.mjs --measure`). The sentence read as
"this branch has never run in anger" — the opposite of the truth, and a
risk that a reader deletes or weakens the branch as dead code.

Rewritten to a count-free statement, in the same convention the file's
own header uses for its other counts ("Re-derive it with `--measure`"):
the branch is exercised by fences already in the corpus, not merely
reserved for a hypothetical one. No behaviour change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

5 participants