Skip to content

check-skill-examples.mjs hardening after #7427: the bare-any assertion, a bound on root-devDependency resolution, and widening SCAN_ROOTS to .claude/skills/** #7463

Description

@os-litant

Filed by the skills lane seat (session session_01LraLgQVGq8egUwfYZpbYt1) from the #7359 flight's follow_ups_recorded_not_taken (PR #7427, comment 5519160893). Three hardening items on one gate, none of which the landing flight was funded for. Unassigned, bare, for triage; landing surface scripts/check-skill-examples.mjs + its spec and self-test (not governed) and, for item 3, the .claude/skills/** markers (governed).

The three items

  1. Bare any inside a marked block. objectstack's check-skill-examples.ts carries a third assertion — a marked example may not use a bare any — that the port deliberately left out as "a new assertion rather than the convention". Add it, with a self-test leg and a --measure reading of how many of the 17 marked ts fences would red today (re-point or unmark by row, never mechanically). Landed in PR feat(scripts): assert no bare any in a marked skill example, and scan .claude/skills too #7490 (ac8d523, 10:21Z).
  2. Bound root-devDependency resolution. The imported harness (check-doc-snippet-types.mjs's derivePackageTypePaths / deriveDeclaredDependencyPaths) lets a bare specifier the repository ROOT declares resolve through pnpm's root node_modules symlinks (in the marked population: vitest, @playwright/test); the inherited UNDECLARED control bounds transitives, which is a different leak. The gate prints an Unmapped specifiers: line on every run rather than hiding it. Bound it the way that control bounds transitives — and since the harness is SHARED, check-doc-snippet-types.mjs carries the same edge (its control proves resolution did not widen to transitives and says nothing about root-declared specifiers); decide whether the bound lives in the harness or per gate. Measured and escalated by the feat(scripts): assert no bare any in a marked skill example, and scan .claude/skills too #7490 flight: a bound in the shared harness reds exactly one docs snippet (content/docs/guide/objectos-integration.mdx:638); the home of the bound is objectstack#14909 item 1 (recommendation A). Re-dispatched at that ruling.
  3. Widen SCAN_ROOTS to .claude/skills/** the way check-skills-paths.mjs was widened by check-skills-paths lost 55 of its 93 assertions when the contributor guides moved out of skills/ — widen SCAN_ROOT to cover .claude/skills #7358, so the internal skills' fences can opt in with the same marker. Landed in PR feat(scripts): assert no bare any in a marked skill example, and scan .claude/skills too #7490: 18 → 20 guides, 112 → 121 ts candidates, 0 new marked fences.

Item 4 (cosmetic; added 11:2xZ from the #7494 flight, PR #7496 report 5524854506, finding 4)

--measure's would-be-population list prints the FINDING's line (fence line plus offset — plugin-development.md:103 for the surviving row) while a baseline row is keyed on the FENCE's line (:92). The per-finding [bare-any] diagnostic prints the verbatim row, so the correct spelling exists; only the summary can mislead someone declaring a row from it, and rows have been declared from that output. Print the row key beside each summary line. Lands with item 2's flight or on its own if item 2 waits long.

Constraints

Verified

Refs: #7359 · PR #7427 · #7358 · PR #7490 · objectstack#14909 · objectstack packages/spec/scripts/check-skill-examples.ts.

Activity

  1. self-assigned this
    on Sep 3, 2026
  2. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    Claim: skills lane seat, session session_01LraLgQVGq8egUwfYZpbYt1. Graded by the seat at 09:12Z under the vacant-triage precedent (#7359): domain:skills, priority:p3, pm:queue; claimed at 09:13Z with assignee set and pm:queue → pm:dispatched in one write; read-back at the seat post refresh. Branch claude/issue-7463-skill-examples-hardening, worktree ../objectui-7463, one PR, first line Fixes #7463.

    Brief in one paragraph: the three items in the card's order, each measured under --measure before any assertion turns on, zero new red on day one, exit-2 PRECONDITION NOT MET semantics unchanged, the marked-population shrink-only ratchet (#7359 step 3) not taken. Item 1 ports objectstack's bare-any rule with its scope (the annotation must be any in a checking-erasing position; nested any allowed); a would-be red row is listed and either baselined in a declared shrink-only list in the shape check-skill-eval-tokens.mjs landed, or escalated as needs_decision when it needs a per-row skills judgement — never unmarked mechanically. Item 2 measures which bare specifiers resolve only through the repository root's devDependencies, then argues the bound's home (shared harness vs per gate) on the decision frame and stops with needs_decision if the two readings move check-doc-snippet-types' own verdicts differently. Item 3 widens SCAN_ROOTS to .claude/skills opt-in only, measuring the candidate population and adding no marker there (the surface owner's step), so day one adds zero marked fences. Reverse verification per assertion; the governed-queue guard's verdict over the final path list decides the posture, and the PR stays draft until the seat flips it either way.


    Generated by Claude Code

  3. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    os-dev-report

    Session: session_01LraLgQVGq8egUwfYZpbYt1
    PR: #7490 (draft)
    Head sha: 2df07b7
    Branch: claude/issue-7463-skill-examples-hardening

    Items 1 and 3 implemented; item 2 measured and escalated (see open_questions).

    {
      "issue": 7463,
      "status": "needs_decision",
      "branch": "claude/issue-7463-skill-examples-hardening",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7490",
      "premise_still_valid": true,
      "summary": "Items 1 and 3 are implemented and pushed as draft PR #7490 (head 2df07b7, 3 files, +477/-32). Item 1 ports objectstack's bare-`any` assertion with its scope intact (the annotation must BE `any` in a checking-erasing position; nested `any` allowed), with one deliberate divergence stated in the header: the walk parses as TSX because the shared harness compiles every block as TSX, so an angle-bracket assertion is a parse failure one exit code earlier and its arm is unreachable-but-kept. Measured before arming: 4 findings in 3 marked fences, 10 over all candidates, ZERO new red on day one — all four declared verbatim in KNOWN_BARE_ANY_EXAMPLES, a shrink-only ratchet whose rows fail as STALE when their red goes away. None was unmarked or re-pointed; one of them (plugin-development.md:92) is faithful prose restating a platform type that really is `any` at packages/types/src/base.ts:550, so unmarking it would have been the gate weakening a guide to suit itself. Item 3 widened SCAN_ROOTS to `.claude/skills` (it does exist here, 4 md files): 18 to 20 guides, 112 to 121 ts fences, MARKED unchanged at 56, and no marker added under `.claude/skills`. Item 2 is MEASURED AND ESCALATED, not guessed: the harness is shared, and a bound in compileSnippets newly reds exactly ONE of check-doc-snippet-types' own 432 compiled snippets (content/docs/guide/objectos-integration.mdx:638, importing @playwright/test) — the brief's explicit stop condition, so the item carries no code and the three options are costed on the four axes in open_questions and in the PR body. Invariants held: exit 2 PRECONDITION NOT MET semantics unchanged, --build-filter output byte-identical, marked-population ratchet not taken, existing 17 ts + 39 json marked fences unaffected.",
      "tests": "All exit codes captured by redirect BEFORE any pipe; verdicts quoted from each gate's own printed line. Head 2df07b7. || check-skill-examples.mjs (after the filtered build it derives, under the objectstack lock, slot issue-7463): EXIT 0 — 'Every marked skill example holds up against the built types.' + 'Bare `any`: 4 finding(s) across 3 selected fence(s); 4 declared in KNOWN_BARE_ANY_EXAMPLES (SHRINK-ONLY, 4 row(s)), 0 NOT declared, 0 declared row(s) no longer red.' || --self-test: EXIT 0 — '42 cases pass' (21 new legs: 10 in-scope positions found, 6 nested `any`s not found, TSX/JSX, no-throw, row-key shape, baseline both directions). || --measure: EXIT 0 — 'Starting population — ts: 18/121 pass; json: 39/56 pass.' + 'Bare `any` would-be population — 10 finding(s) over every candidate, of which 4 sit in a MARKED fence.' || --build-filter: EXIT 0, output diffed byte-identical against the pre-change run, so the workflow's build step is unmoved. || vitest scripts/__tests__ (--maxWorkers=2, under the lock): EXIT 1 — 'Test Files 2 failed, 98 passed (100)', 'Tests 3 failed, 2883 passed (2886)'. The 3 failures are PRE-EXISTING and proven so, not asserted (see the ablation below). check-skill-examples.test.ts alone: 65 passed, EXIT 0. || tsc -p tsconfig.scripts.json --noEmit: EXIT 0. || eslint scripts/check-skill-examples.mjs --format json: EXIT 0, 1 file linted, 0 errors 0 warnings (the .md is 'File ignored because no matching configuration was supplied', so the lintable population of this diff is exactly 1 file, read from eslint's own config). || pnpm lint REPO-WIDE: EXIT 0 — 'Tasks: 47 successful, 47 total', 4220 files linted, 0 errors (11832 pre-existing warnings). No narrowing was claimed; the full farm ran. || check-pre-install-import-graph.mjs: EXIT 0. || check-changeset-presence.mjs: EXIT 0 — 'No source or published contract of a released package changed in this range, so no changeset is owed.' || check-governed-queue-guard.mjs --test over the final path list (content/docs/guide/ci-cd-pipeline.md, scripts/check-skill-examples.mjs): EXIT 0 — 'NOT GOVERNED — 2 path(s) checked against 5 governed surface(s); none matched.' Expected: no file under skills/ or .claude/skills/ is modified. PR stays draft. || Control-byte scan (grep -naP for C0/C1 and DEL) over the changed files: no matches. || REVERSE VERIFICATION — four legs, each committed first, mutated under a trap restoring ABSOLUTE paths, mutation proven on disk by grep count BEFORE any verdict was read, restore proven by git hash-object equal to the HEAD blob plus empty git diff HEAD. LEG A (an undeclared bare `any` must red): mutated skills/objectui/guides/i18n.md marked fence; injected-text count 0 to 1, blob 7c2f01fe differs from HEAD fc0d4ad3; gate EXIT 1 with '[bare-any] ... i18n.md:44:35 variable' and '1 NOT declared'; restored blob back to fc0d4ad3, injected count 0, git diff HEAD empty. LEG B (the ratchet only shrinks): mutated testing.md so a DECLARED row's red goes away; removed-text 1 to 0 and injected-text 0 to 1, blob a88336f9 differs from HEAD 601d98fd; gate EXIT 1 with 'testing.md:60 ... [stale-baseline] this row is no longer red'; restored to 601d98fd, removed-text back to 1, git diff HEAD empty. LEG C (the widened root is real coverage): appended a marked bare-`any` fence to .claude/skills/verify/SKILL.md; injected 0 to 1, blob 38816807 differs from HEAD 68b48b7e; gate EXIT 1 with '[bare-any] .claude/skills/verify/SKILL.md:50:42 parameter v' and 'Marked: 18 ts fence(s)' up from 17 — plus the load-bearing CONTROL: the same probe against a copy of the script with SCAN_ROOTS narrowed back to skills alone sees 0 .claude/skills guides and 0 findings, so the widening is what made it reach; restored to 68b48b7e, injected 0, git diff HEAD empty. LEG D (classifying the 3 red tests): reverted all three changed files to merge-base e307c98, proven on disk (narrow SCAN_ROOTS line present, findBareAny absent), re-ran both suites — the SAME 3 tests failed identically; neither suite references check-skill-examples; restored, all three blobs equal their HEAD blobs, git diff HEAD and git status --porcelain both empty. || NOT MEASURED: the BODY of check-skill-examples.mjs is not type-checked — tsconfig.scripts.json sets allowJs true with checkJs FALSE deliberately. Confirmed with --listFiles that the file IS in the program (so the .ts tests' use of findBareAny / bareAnyRowKey / KNOWN_BARE_ANY_EXAMPLES is checked), but errors inside the .mjs are not reported: a clean tsc here is a statement about the callers, not the module body. Also not measured: item 2's bound is not written, so nothing proves a bound works in either direction; and CI convergence, reported at draft-PR time per the dispatch contract.",
      "mcp_calls": "4",
      "open_questions": [
        {
          "question": "Item 2 — where does the bound on root-devDependency resolution live? The harness (compileSnippets in check-doc-snippet-types.mjs) is shared by exactly two production consumers: the docs gate itself and check-skill-examples.mjs. Measured: the marked population's unmapped specifiers are exactly @playwright/test and vitest, both root-declared and resolving out of /node_modules; and a bound placed in compileSnippets newly reds exactly ONE of the docs gate's own 432 compiled snippets — content/docs/guide/objectos-integration.mdx:638, which imports @playwright/test. That is a verdict move on a gate this flight does not own, over a docs surface with its own owner, which is the brief's explicit stop condition.",
          "options": [
            "A — bound inside compileSnippets, unconditionally, for both gates. Real business need: genuine but narrow (one leaking docs snippet, two skills specifiers); a green resting on this workspace's devDependencies is not a claim about what a reader installs. Long-term soundness: STRONGEST — one resolution regime and one answer to 'how far does resolution reach', stated once in the harness that already owns the syntax/semantics split and the four self-controls; the harness's guarantee stops being 'narrow, except for whatever the root happens to declare'. Preventing AI-authored mistakes: strongest — declared equals enforced; a guide cannot rest on a package the reader was never told to install, exactly the class an AI copying a fence propagates. Startup scope discipline: cheapest surface — no flag, no second regime, no staged window; costs one docs snippet that must be fixed or declared in the same change.",
            "B — bound per gate, as a parameter compileSnippets accepts, default off. Real business need: same pull, and it lands the skills half without touching the docs corpus. Long-term soundness: WEAKEST — an opt-in leniency flag in shared code is two resolution regimes behind one function, with the docs gate permanently on the lenient one; the harness would answer 'did resolution stay narrow' with 'depends who is asking', which is the consumer-tolerance shape the frame names. Preventing AI-authored mistakes: weaker — a gate whose strictness is a caller-side argument has a guarantee that must be re-read at every call site. Startup scope discipline: looks cheaper and is not; it adds a permanent configuration surface to avoid one docs edit.",
            "C — do nothing; keep printing the Unmapped specifiers line every run. Real business need: the line already stops the leak being silent, which is what the landing flight chose deliberately. Long-term soundness: weakest — a known-unenforced property a reader must remember to read. Preventing AI-authored mistakes: weakest; a printed line is not a gate. Startup scope discipline: strongest short-term, and it is the status quo."
          ],
          "recommendation": "A, led by long-term soundness (weighted at least 50% per the 2026-09-01 ruling): it is the only option that leaves one answer to one question, and the frame's contract-first and declared-equals-enforced axes select it too. The one docs snippet it reds is a real finding rather than collateral — that snippet's green rests on this workspace's devDependencies, not on anything its reader installs. A does need one call this flight cannot make: whether that snippet is fixed or declared, and by whom, since it is a docs surface with its own owner. B is not recommended at any weighting — it buys one avoided docs edit with a permanent second resolution regime in shared code, which is precisely the consumer-tolerance the frame rejects. C is the honest fallback if the maintainer judges the pull too small to spend a docs edit on, and it should then be recorded as a declared non-enforcement rather than left as a follow-up that reads like pending work."
        },
        {
          "question": "Item 1 — the four declared bare-`any` rows each need a per-row SKILLS judgement that a gate-hardening PR is not the place to make. They are carried in the shrink-only KNOWN_BARE_ANY_EXAMPLES rather than unmarked, so day one adds zero red, but the list is only honest if the rows are worked. Row 1 (plugin-development.md:92, property defaultValue) is the odd one: the guide is FAITHFUL — ComponentInput.defaultValue really is `any` at packages/types/src/base.ts:550 — so fixing the guide alone would make it lie about the contract it restates.",
          "options": [
            "Leave all four as declared debt and let the shrink-only direction force the work when someone next touches those guides. Cheapest; the stale check stops a row being parked silently, but nothing schedules the fix.",
            "File one card per row (or one card for the three testing.md rows plus one for row 1), so the per-row skills judgement is scheduled rather than latent. Row 1's card is really a platform-type card: tighten ComponentInput.defaultValue to unknown, which is the contract-first fix, and update the guide with it.",
            "Fix the three testing.md rows now by teaching the honest idiom (as unknown as T, or ts-expect-error) and leave only row 1 declared. Lands the debt reduction immediately but decides testing-guide idiom without the guide owner."
          ],
          "recommendation": "Option 2 — file the cards, and file row 1's against packages/types rather than against the guide. The seat dedups and files per the flight rules, so I filed nothing myself. Option 3 is tempting and I deliberately did not take it: choosing what the testing guide teaches is a skills judgement, and making it inside a gate-hardening PR is exactly the 'gate weakens the guide to suit itself' move the shrink-only list exists to prevent."
        }
      ],
      "out_of_scope_findings": [
        "NOT filed by me — the seat dedups and files. scripts/__tests__/check-sdui-registration-pins.test.ts:146 — the pin asserts sources.get('mcp:connect-agent') is 'packages/app-shell/src/console/connect/ConnectAgentWidget.tsx' but gets 'packages/app-shell/dist/console/connect/ConnectAgentWidget.js' on a BUILT tree: the derivation prefers dist once it exists, so the pin passes only on an unbuilt checkout. Proven pre-existing at merge-base e307c98 by the Leg D ablation.",
        "NOT filed by me. scripts/__tests__/check-readme-exports.test.ts:899 and :950 — 'hides ONLY omissions, and the tree says so in BOTH build states' and 'finds no fabricated or wrong-path import when built' both fail on a built tree (census.excerptsNotJudged is 1, expected 3). Same built-tree-sensitivity class as the finding above, same Leg D proof. Relevant because check-skill-examples requires a built tree, so its own CI job's tree state surfaces these.",
        "NOT filed by me. packages/types/src/base.ts:550 — 'defaultValue?: any;' in the exported ComponentInput interface. Not a guide defect: it is the reason baseline row 1 exists, and it is why unmarking that fence would have been wrong. Tightening it to unknown is the contract-first fix and a platform-type change with its own consumers, so it wants its own card rather than a rider on this PR."
      ]
    }

    Generated by Claude Code

  4. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    ACCEPT — in-seat review of PR #7490 at 2df07b7 (items 1 and 3 delivered; item 2 measured and escalated). Reviewed in-seat at the contract-review tier (served model unchanged from the 05:33Z reading). No needs:contract-review label is owed: the diff changes a gate, not a contract or an operator-semantics claim.

    What the seat verified itself, not from the report:

    • Posture. node scripts/check-governed-queue-guard.mjs --test over all three changed paths (the report ran it over two): NOT GOVERNED, 3 paths checked, none matched (09:53Z). Non-governed, so this PR ends at the seat's own terminal.
    • Port fidelity. objectstack's position table at packages/spec/scripts/check-skill-examples.ts:885-894 is line for line the port's; the TSX divergence is stated in the header and pinned in both self-test and vitest.
    • The boundary, probed by the seat on the exported findBareAny in a compare worktree at the same head (09:57Z, ten shapes): parameter, optional parameter and catch variable are found; a generic default, a hook type argument, a tuple member, a record value, an array element and a promise argument are not; the angle-bracket assertion is a parse failure under TSX, as documented. Two inherited observations, neither present in a marked fence today, filed as #14910 in objectstack after a dedup at 10:00Z: an index signature's any is flagged under the label return type (isFunctionLike matches it), and a function type's return any is flagged even inside a type argument. The origin has both; not this PR's to fix.
    • Zero new red on day one. --measure reports 4 findings in 3 marked fences, all four declared verbatim in KNOWN_BARE_ANY_EXAMPLES; the shrink-only direction is exercised by the self-test (a ghost row reports STALE) and by the dev's leg B on a real guide. The MARKED population is unchanged at 56 after the root widening, and --build-filter is byte-identical, so the workflow's build step is unmoved.
    • CI on 2df07b7 at 10:03Z: 25 of 29 checks green, including Skill Example Check (09:51:37Z), Doc Snippet Type Check, Type Check, Lint and Governed Surface Queue Guard; the four Test shards are still running (started 09:47:56Z). The three vitest failures the dev reports on a BUILT tree are the two already-open cards finding(test-infra): check-sdui-registration-pins.test.ts fails on any tree where packages/app-shell/dist exists #6893 and finding(scripts): check-readme-exports.test.ts reds on a HALF-built tree — and another gate’s own printed build command is what produces one #7460, proven pre-existing by the dev's leg D at the merge base; CI's unbuilt shards do not see them.
    • Card shape. The PR's first line is now Part of #7463 (changed by the dev at the seat's request and read back); item 2 stays open on this card and the card returns to pm:queue with the pointer after the merge, per the #14297 precedent. The report's Fixes wording predates that change.

    Routing done by the seat: item 2's fork is item 1 of decision batch 5, #14909 in objectstack (recommendation A, as the dev argued on the four axes); the dev's open question 2 is #7494 (the three testing.md rows; domain:skills p3, pm:queue at this PR's merge) and #7493 (ComponentInput.defaultValue?: any at packages/types/src/base.ts:550, bare for triage); findings 1 and 2 of the report are #6893 and #7460 already and are dropped.

    Terminal: ready flip and auto-merge (SQUASH) when the four shards are green; the read-back follows in its own comment; MERGED is witnessed only by the merge patrol.


    Generated by Claude Code

  5. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    Read-back for PR #7490 (branch claude/issue-7463-skill-examples-hardening, head 2df07b7): all 29 checks green by 10:01:25Z (the last, Test (shard 3/4), completed then); flipped ready and auto-merge armed (SQUASH) at 10:07Z; list_pull_requests at 10:08Z reads state open, draft: false, not merged. MERGED is witnessed only by a same-minute git log --grep '(#7490)' at the next merge patrol; at that reading this card goes back to pm:queue with the pointer to #14909 item 1 (the #14297 precedent), and #7494 takes pm:queue.


    Generated by Claude Code

  6. removed their assignment
    on Sep 3, 2026
  7. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    Merge patrol reading 2026-09-03T10:48Z (git fetch origin main, then git log origin/main --grep '(#7490)' in the same minute): PR #7490 merged as 5c09fd5 at 10:21:54Z — the bare-any assertion with its four declared rows and the widened scan root are on main. ⚠️ The seat's 10:46Z patrol loop printed no match for the same pattern although its own fetched head was that very commit; the re-run two minutes later matched. Cause not found; recorded here and in the seat post, and the patrol now prints the top subjects beside the grep.

    State: pm:dispatched → pm:queue and the assignee cleared at 10:47Z (the #14297 precedent — the remaining item sits on a decision card). Item 2, the bound on root-devDependency resolution, is #14909 item 1 (recommendation A); this card is re-dispatched at that ruling on a new branch. Read-back at the seat post refresh.


    Generated by Claude Code

  8. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    Correction to the 10:48Z reading above. At 10:57Z (git fetch origin +main:refs/remotes/origin/main, git log --all --grep '(#7490)', git cat-file -t): the commit on main carrying PR #7490 is ac8d523 (committed 10:07:42Z, subject = the PR title "feat(scripts): assert no bare any in a marked skill example, and scan .claude/skills too"); the sha 5c09fd5 the 10:48Z reading reported (10:21:54Z, a different subject) is not an object in this checkout at all and appears in no ref or reflog, and main's remote-tracking reflog shows only fast-forwards. The API's merged_at is 10:21:54Z. The seat cannot explain where the 10:48Z sha came from and records both readings rather than choosing; what is verified either way is the content — the four KNOWN_BARE_ANY_EXAMPLES rows and SCAN_ROOTS = ['skills', '.claude/skills'] are on main (read at 10:53Z from 20cb8db). State on this card unchanged: pm:queue, assignee cleared, item 2 → #14909.


    Generated by Claude Code

  9. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    Claim: PM loop round 3 — second flight, item 2 (ruled A on objectstack #14909 item 1)
    Session: session_019RfFHiRCSs3JXLK4cwcfox
    Branch: claude/issue-7463-root-devdep-bound
    Worktree: objectui-issue-7463-bound
    Domain: domain:skills
    File surface: scripts/check-doc-snippet-types.mjs (compileSnippets and its self-controls), scripts/check-skill-examples.mjs (the Unmapped specifiers report and its self-test), scripts/__tests__/check-doc-snippet-types.test.ts + scripts/__tests__/check-skill-examples.test.ts (pins), and the one docs snippet the bound turns red — content/docs/guide/objectos-integration.mdx (the Integration Tests fence importing @playwright/test) — fixed or explicitly declared in the same PR (stop on breach; explain in the report).
    Container & model: S code, mode:subagent, model: opus (objectui carries no path-derived tier tool; the surface is gate scripts plus one docs fence — opus default, the seat reviews at the contract tier)
    Clause-②: no
    Serial constraints cleared: neither gate is on the seat post's hot-file queue; the last merges on them are a27d153 14:30Z (#7496) and ac8d523 10:07Z (#7490 — this card's items 1 and 3); no open PR touches them; no in-flight claim names them. Not governed (scripts + content/docs), no .claude/** or skills/** in the surface ⇒ pure-code landing: in-seat review → ready → auto-merge (SQUASH). skip-changeset.

    Decision re-read (20:25Z): maintainer ruling recorded on objectstack #14909 (comment 5528585035, 2026-09-03 16:11Z, verbatim 「同意」 adopting 1A · 2A): the bound lives in the shared harness — compileSnippets refuses a bare specifier that resolves only through the root package.json's devDependencies, unconditionally for both gates, with its own self-test in both directions; ⛔ not a parameter defaulting to off; the one docs snippet it reds is fixed or explicitly declared in the same PR and check-doc-snippet-types's pins move with it; the Unmapped specifiers line stays as the report of what the bound refused. PR body: Part of objectui#7463 — this card closes by hand when the maintainer's item-2 card (the floors) also lands, per the ruling's "this objectstack card closes when both objectui PRs land"; the seat closes this card at that reading.


    Generated by Claude Code

  10. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    os-dev-report

    Session: session_019RfFHiRCSs3JXLK4cwcfox (skills lane seat, os-steve)
    PR: #7554 (draft)
    Head sha: 58737af
    Branch: claude/issue-7463-root-devdep-bound

    Item 2 implemented per the 2026-09-03 ruling (objectstack#14909 item 1, option A). One judgement beyond the ruling's letter is flagged in open_questions: the four MARKED skill fences the bound refuses are carried as declared, shrink-only debt in the gate script rather than unmarked in the governed guide.

    {
      "issue": 7463,
      "status": "done",
      "branch": "claude/issue-7463-root-devdep-bound",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7554",
      "premise_still_valid": true,
      "summary": "Item 2 implemented per the ruling (objectstack#14909 item 1, option A) and pushed as draft PR #7554 (head 58737af, 5 files, +864/-39). compileSnippets in the SHARED harness now refuses a bare specifier that resolves only through the repository root package.json — one exported predicate (resolvesOnlyThroughRootManifest), unconditional, no parameter — enforced at two points: the resolver (the regime, scoped to the virtual probe dir) and a per-block AST walk (the attribution), with a new ROOT-DECLARED control that fails the run when the bound is not applied. A refused block is kept OUT of the semantic program the way an unparseable one is, so the coverage line states what was judged instead of counting it as checked. Re-measured at my branch point: the bound reds EXACTLY ONE docs snippet, content/docs/guide/objectos-integration.mdx:638 importing @playwright/test — the ruling's premise confirmed, not inherited. That fence is DECLARED through check-doc-snippet-types' own fragment mechanism with the measured diagnostic, and the guide now tells the reader to install @playwright/test, which is the reproducible form the ruling asked for; the example is kept intact. ONE JUDGEMENT BEYOND THE RULING'S LETTER, flagged for the seat: the bound also refuses FOUR MARKED fences in skills/objectui/guides/testing.md (:60, :94, :218 on vitest; :258 on @playwright/test) — the two specifiers the ruling names as the marked population, but it rules only on the docs snippet. Since skills/** is governed and outside this PR's file surface, and the card's own constraint is zero new red on day one, they are carried in a new shrink-only KNOWN_ROOT_DEVDEP_EXAMPLES in the gate script, mirroring KNOWN_BARE_ANY_EXAMPLES one flight earlier. A declared row is uncovered debt, never a green, and every line of the run says so (Root bound counts refusals, Semantic phase drops 17 to 13, a NOTE spells it out, and the success sentence now says the 4 declared rows were NOT reached and it does not speak for them). Also landed with the bound: ONE exemption, the JSX factory module, because the compiler imports react/jsx-runtime on the author's behalf — measured, react is mapped in the docs corpus only because @object-ui/layout declares it as a real dependency and the skills corpus imports no package that does, so without the exemption the same JSX tag is bounded in one gate and not the other, over a line nobody wrote; exempted at both enforcement points so they cannot disagree. Invariants held: exit-2 PRECONDITION NOT MET semantics unchanged, --build-filter byte-identical to the pre-change run, the four inherited harness self-controls green, the Unmapped specifiers line kept as the report of what the bound refuses, no skills/** or .claude/skills/** file touched, no other docs page touched. PM correction received mid-flight and honoured: no skip-changeset label was applied in objectui (none had been applied at any point — the PR was not yet open when the correction arrived); check-changeset-presence.mjs's own verdict says no changeset is owed, so nothing was applied.",
      "tests": "All exit codes captured by redirect BEFORE any pipe; every verdict quoted from the gate's own printed line. Union re-run AFTER the final commit, at head 58737af (git status clean at that run). || pnpm check:doc-snippets: EXIT 0 — 'Every covered documentation snippet compiles against the built types.' + 'Covered blocks: 613 — 455 to compile, 158 declared fragment(s).' + 'Root bound: no block imports a specifier that resolves only through this repository's ROOT manifest.' + 'Semantic phase: 455 of 455 block(s) judged, 0 failed.' || pnpm check:skill-examples: EXIT 0 — 'Every marked skill example the root bound could reach holds up against the built types — 4 declared row(s) in KNOWN_ROOT_DEVDEP_EXAMPLES were NOT reached, and this line does not speak for them.' + 'Root bound: 4 fence(s) refused (@playwright/test, vitest) and therefore NOT type-checked; 4 row(s) declared in KNOWN_ROOT_DEVDEP_EXAMPLES (SHRINK-ONLY, 4 row(s)), 0 NOT declared, 0 declared row(s) no longer refused.' + 'Semantic phase: 13 of 17 ts fence(s) judged, 0 failed.' || --self-test: EXIT 0 — '52 cases pass' (42 before; 10 new legs for the bound). || --measure: EXIT 0 — 'Starting population — ts: 14/121 pass; json: 39/56 pass.' (20/121 before the bound, measured by running the BASE copies of the three script files against this same built tree) + 'Root-bound would-be population — 18 refusal(s) over every candidate, of which 4 sit in a MARKED fence.' || --build-filter: EXIT 0, output diffed BYTE-IDENTICAL against the pre-change run, so the workflow's build step is unmoved. || BEFORE/AFTER on the two known specifiers: before, skills gate judged 17 of 17 marked ts fences with 'Unmapped specifiers: @playwright/test, vitest' and docs judged 456 of 456; after, skills judges 13 of 17 (4 refused, all declared) and docs 455 of 455 (one block newly declared a fragment). With the corpus UNFIXED, the bound refused exactly one docs block: '[bound] content/docs/guide/objectos-integration.mdx:638 imports @playwright/test' and nothing else across 456. || pnpm check:doc-fences EXIT 0 — 'every TypeScript block in 227 document(s) is fenced ts/tsx/typescript, except 80 declared file(s) carrying 90 block(s) ... (SHRINK-ONLY)'. || pnpm check:skills-paths EXIT 0 — 'check-skills-paths: OK (88/89 stated path(s) resolve across 20 guide file(s); 1 baselined).' || pnpm check:skill-eval-tokens EXIT 0 — 'Every must_contain token is taught by its own skill bundle.' || pnpm check:doc-types EXIT 0 — 'Every documented component type is registered.' || pnpm check:control-bytes EXIT 0 — 'check-control-bytes: OK (scanned 6224 tracked text file(s); skipped 85 binary)'; plus a manual C0/C1/DEL scan over the five changed files, no matches. NOTE: an early edit did land a raw NUL in a template-literal cache key in check-doc-snippet-types.mjs; the manual scan caught it before the first commit and it was replaced with a printable separator, so no commit ever carried it. || pnpm check:pre-install-import-graph EXIT 0. || check-changeset-presence.mjs EXIT 0 — 'No source or published contract of a released package changed in this range, so no changeset is owed.' || check-governed-queue-guard.mjs --test over all 5 changed paths: EXIT 0 — 'NOT GOVERNED — 5 path(s) checked against 5 governed surface(s); none matched.' PR stays draft for the seat. || tsc -p tsconfig.scripts.json --noEmit: EXIT 0. || vitest run over the 11 suites that reference either script (the two own suites plus every other test file git grep finds naming them): EXIT 0 — 'Test Files 11 passed (11)', 'Tests 394 passed (394)'. The two own suites grew by 15 and 8 cases, to 67 and 73. || pnpm lint REPO-WIDE: EXIT 0 — 'Tasks: 47 successful, 47 total', 0 errors (pre-existing warnings only). No narrowing claimed; the full farm ran. || REVERSE VERIFICATION — three legs, fix committed FIRST, each mutation under a trap restoring ABSOLUTE paths, mutation proven on disk by grep counts BEFORE any verdict was read, restore proven by git hash-object equal to the HEAD blob plus empty git diff HEAD. No dist/ is involved: both gates run from source and the packages they resolve against were unchanged, so there is no rebuild leg to report. LEG A (the bound is what reds the docs snippet): removed the new fragment declaration from the mdx, declaration count 1 to 0, blob 3115b38e to a6c30eab; docs gate EXIT 1 with '[bound] content/docs/guide/objectos-integration.mdx:644 imports @playwright/test' and 'Semantic phase: 455 of 456'; restored to 3115b38e, count back to 1, git diff HEAD empty. LEG B (the bound is applied, not merely described): replaced the predicate's verdict with a constant false, removed-text 1 to 0 and injected-text 0 to 1, blob 99bf34d2 to d959bc4d; BOTH gates EXIT 2 (couldNotRun) with the ROOT-DECLARED control at 'produced 0 diagnostic(s)' and 'a specifier only the repository ROOT declares still resolves — the bound is not being applied' — with the bound off the gates refuse to render a verdict rather than going quietly green; restored to 99bf34d2, git diff HEAD empty. LEG C (the debt list only shrinks, both directions): deleted the :258 row, count 1 to 0, blob 8c5c5ec2 to 82e6826d, EXIT 1 with '[bound] ... testing.md:258' and '1 NOT declared'; then added a ghost row testing.md:9999 vitest, count 0 to 1, blob 617553e3, EXIT 1 with '[stale-baseline] this row is no longer refused'; restored both times to 8c5c5ec2, git diff HEAD and git status --porcelain both empty. The standing NEGATIVE control needs no ablation: 455 of 455 docs blocks are still judged and 0 failed, so the bound demonstrably refuses the root set rather than everything. || NOT MEASURED, stated rather than implied: tsconfig.scripts.json sets allowJs with checkJs FALSE deliberately, so errors inside the two .mjs BODIES are not reported — --listFiles confirms both modules AND both edited test files are in the program (2 hits), which makes the clean tsc a statement about the callers, not about the module bodies. Also not measured: CI convergence, reported at draft-PR time per the dispatch contract.",
      "mcp_calls": "5 — create_pull_request, pull_request_read (the mandated PR body read-back), search_issues (the dedup for the finding below), issue_write (filing it), add_issue_comment (this report). Every bulk read went through the zero-quota page payload: objectui#7463's body and comments, and objectstack#14909's body and ruling comment 5528585035.",
      "open_questions": [
        {
          "question": "The four MARKED skill fences the bound reds are carried as declared debt in the gate script rather than fixed. The ruling rules only on the ONE docs snippet, and names vitest and @playwright/test as the marked population it closes without saying what happens to the fences that import them. Confirming the flight's reading before it becomes precedent: a declared row is the right home for a pre-existing refusal, and the fence itself is the guide owner's call.",
          "options": [
            "A (taken) — carry the four in a shrink-only KNOWN_ROOT_DEVDEP_EXAMPLES in check-skill-examples.mjs, with the reason written beside them and the coverage cost printed on every run. Zero new red on day one, which is the card's own stated constraint; no governed file touched, which is this PR's stated file surface; the debt is named, dated and can only shrink. Cost: four marked fences are no longer type-checked, and the run says so in four separate places rather than hiding it.",
            "B — unmark the four fences in skills/objectui/guides/testing.md. Removes the debt list entirely, but it is a governed edit outside this PR's file surface, it flips the PR into the human-merge posture, and it is exactly the 'gate weakens the guide to suit itself' move the shrink-only lists exist to prevent.",
            "C — let them red and land the bound anyway. Honest, and it makes the work visible immediately, but it breaks the card's zero-new-red constraint and leaves main red until a governed PR lands, which no other item on this card did."
          ],
          "recommendation": "A, taken. On the frame: real pull — the four fences are correct documentation about test runners the reader installs, so nothing is broken today and nothing needs an emergency edit. Long-term soundness — the harness now has ONE resolution regime, and the consumer records which refusals predate it; that is the same shape UNGATED_DOCS and KNOWN_BARE_ANY_EXAMPLES already have here, not a second regime. Preventing AI-authored mistakes — a declared row is a named, shrink-only, reviewable debt whose stale direction fails loudly, whereas unmarking is a one-line deletion no gate would notice, which is precisely the cheapest wrong move an agent facing a red gate can make. Startup scope — no new configuration surface; one Set and one classifier. What the seat may want to schedule (I filed nothing for it, since it is a judgement about this card): a skills-lane card to retire the four rows by giving those examples imports the reader provably has, or by the guide owner deciding they should not be gated."
        }
      ],
      "out_of_scope_findings": [
        "filed as objectui#7555: `importedSpecifiers` in both snippet gates is a regex, so an import-shaped line inside a template literal is read as an import — measured at content/docs/plugins/plugin-markdown.mdx:195, where `npm install project-name` inside a README sample reports `project-name` as an imported specifier. Nothing is red because of it (the false name is not a workspace package, so the build filter is unmoved), but it can put a specifier no fence imports into check-skill-examples' `Unmapped specifiers` line, which is now the report of what the bound refuses — so the line and the refusals can disagree. The fix already exists in the tree: the exported AST walk `moduleSpecifiersOf` this PR added, which is pinned against exactly this shape. Bare, unassigned, unlabelled, for triage."
      ]
    }

    Generated by Claude Code

  11. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    ACCEPT (item 2 — second flight) — skills seat (session session_019RfFHiRCSs3JXLK4cwcfox), reviewer of record at the contract tier, 2026-09-03T21:12Z, verified against GitHub and the PR head over git, not against the report.

    PR #7554 @ 58737af — draft, base main, first line Part of #7463 (no closing keyword beside any card — this card closes by hand when the floors PR also lands), five files exactly as claimed (check-doc-snippet-types.mjs, check-skill-examples.mjs, their two suites, content/docs/guide/objectos-integration.mdx), +864/-39, no skills/** or .claude/** path in the diff, no model name, one footer; check-governed-queue-guard --test NOT GOVERNED (dev's reading over all five paths). No skip-changeset — right, per the seat's correction; check-changeset-presence says none is owed.

    Content against the ruling (objectstack #14909 item 1, A): one exported predicate in the shared harness (resolvesOnlyThroughRootManifest), unconditional, enforced at the resolver and at a per-block AST walk, with a new ROOT-DECLARED control that makes both gates refuse to render a verdict (exit 2) when the bound is not applied — the seat read the ablation legs: with the predicate forced false, both gates go couldNotRun, which is the right failure. The bound reds exactly one docs snippet at the branch point (the ruling's premise re-measured, not inherited); that fence is declared through the docs gate's own fragment mechanism and the guide now tells the reader to install @playwright/test — the reproducible form the ruling asked for. --build-filter byte-identical; the Unmapped specifiers line kept as the report of the refusals. The JSX factory exemption (react/jsx-runtime, imported by the compiler on the author's behalf) is measured and applied at both enforcement points so they cannot disagree — accepted.

    Design call beyond the ruling's letter, A accepted: the four marked fences in skills/objectui/guides/testing.md the bound now refuses are carried as a shrink-only KNOWN_ROOT_DEVDEP_EXAMPLES with the coverage cost printed on every run (13 of 17 ts fences judged … 4 declared rows NOT reached). Zero new red, no governed edit, a named debt that can only shrink — the same shape as KNOWN_BARE_ANY_EXAMPLES. The seat files the follow-up the dev proposed: a skills-lane card to retire the four rows (give those examples imports the reader provably has, or the guide owner decides they are not gated). Finding #7555 (regex import reader inside template literals) is real and is triaged by this seat.

    Deviations: none against the brief; the early raw-NUL slip was caught by the dev's own scan before any commit — good run. Report mcp_calls: 6 (the report's own copy says 5; the sixth was the mandated read-back of the report) — within contract.

    CI at 21:10Z on 58737af: 29 check runs, 22 success + 3 skipped, four Test shards in_progress; nothing red.

    Landing: pure code ⇒ the seat flips ready and arms auto-merge (SQUASH) once every check is green; PR #7553 (the floors, same gate file) is being flipped first — if this PR reads dirty after that merge, the dev merges main before the flip. objectstack #14909 closes by hand when both are MERGED; this card closes at the same reading.


    Generated by Claude Code

  12. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    Addendum to the os-dev-report above (comment 5532159291) — merge round on PR #7554, new head 3ca73aa. PR #7553 (objectui#7550's per-category MARKED_FLOOR ratchet) landed on the same two files and left this branch mergeable_state: dirty; merged origin/main at 34ea56d into the branch as a merge commit (no rebase, no force, no stash), with exactly one conflict — the import list in scripts/__tests__/check-skill-examples.test.ts, resolved by keeping both sides' symbols (KNOWN_ROOT_DEVDEP_EXAMPLES and MARKED_FLOOR, rootDevDepRowKey and reconcileFloors); scripts/check-skill-examples.mjs auto-merged and both behaviours are whole, the summary now carrying Marked: 17 ts fence(s) (floor 17), 39 json fence(s) (floor 39) — the floor is SHRINK-ONLY. beside Root bound: 4 fence(s) refused (@playwright/test, vitest) and therefore NOT type-checked; 4 row(s) declared in KNOWN_ROOT_DEVDEP_EXAMPLES (SHRINK-ONLY, 4 row(s)), 0 NOT declared, 0 declared row(s) no longer refused., and the exit path reading both the floor-breach check and the undeclared/stale rows. One thing neither PR could state alone is now written in both docblocks: MARKED_FLOOR counts MARKS, not verifications, so a fence the root bound refuses stays inside the floor while being type-checked by nothing — and unmarking one to silence a refusal would breach that floor; two ratchets, counting different things, neither substituting for the other. Union re-run at 3ca73aa, all EXIT 0: pnpm check:skill-examples (Every marked skill example the root bound could reach holds up against the built types — 4 declared row(s) in KNOWN_ROOT_DEVDEP_EXAMPLES were NOT reached, and this line does not speak for them.), pnpm check:doc-snippets (Every covered documentation snippet compiles against the built types. · Semantic phase: 455 of 455 block(s) judged, 0 failed.), --self-test (52 cases pass), both suites plus the 9 other files that reference either script (Test Files 11 passed (11) · Tests 402 passed (402), up from 394 by objectui#7550's 8 floor pins), pnpm lint repo-wide (Tasks: 47 successful, 47 total, 0 errors), plus check:doc-fences, check:skills-paths, check:skill-eval-tokens, check:control-bytes, tsc -p tsconfig.scripts.json --noEmit, check-changeset-presence.mjs (no changeset is owed, now against 34ea56d) and check-governed-queue-guard.mjs --test (NOT GOVERNED — 5 path(s)). PR body PATCHed with the new head and a merge section, and read back to its end intact. On auto_merge: I cannot assert it either way from a field reading — pull_request_read get returns no auto_merge member at all for this PR, so its absence is UNKNOWN, not "cleared". What the timeline does show: the seat marked the PR ready at 21:38:21Z and enabled auto-merge at 21:38:37Z, both AFTER the conflict appeared at 21:27Z, and no disabled auto-merge event follows; my push resolved the conflict rather than creating one, and mergeable_state has moved from dirty to blocked (checks pending), so the enqueue blocker the seat named is gone. Since this repository's own AGENTS.md records that auto-merge is dropped silently in the conflicting and draft windows, the seat should read autoMergeRequest directly before trusting it, and re-arm if it is null. Two further states are the seat's, not mine, and I have not touched them: the PR is no longer draft (flipped ready at 21:38:21Z), which makes the body's closing "Draft, for the seat to review and land" sentence stale, and the tests label was applied by something other than me.


    Generated by Claude Code

  13. claude commented on Sep 3, 2026

    @claude
    Contributor

    Closed by hand — skills seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve), 2026-09-03T22:44Z: all three items are on main — items 1 and 3 in PR #7490 (ac8d523, 10:07Z), item 2 in PR #7554 (Part of, MERGED 22:31:10Z after the maintainer's ruling on objectstack #14909 item 1). The four KNOWN_ROOT_DEVDEP_EXAMPLES rows the bound left behind are #7557's, filed separately. pm:dispatched removed with the close.


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions