Repository navigation
Description
Found while implementing a tree-scoped absence pin (#14878, PR #15085) and reading the two in-tree precedents for that shape. Recording only — no severity asserted, unassigned.
The mismatch, at origin/main 89eb997d8
packages/core/src/security/operation-private-keys.pin.test.ts walks the repo with git ls-files and filters the candidate set with:
const isScannedSource = (path: string) => /\.tsx?$/.test(path) && !path.endsWith('.d.ts');/\.tsx?$/ matches .ts and .tsx. Its declared radius in scripts/cross-package-test-inputs.mjs is:
'@objectstack/core': {
globs: ['packages/**/*.ts'],
},packages/**/*.ts does not cover .tsx. So the pin judges a population strictly wider than the population turbo re-runs it for.
Why that is the exact defect the declaration table exists to prevent
Both scoping layers check-cross-package-test-inputs drives are keyed on the declared globs: Layer A unions the package into the shard when the diff touches them, Layer B puts them in @objectstack/core#test's turbo inputs so the cache hash moves. A .tsx file under packages/ that declares its own OPERATION_PRIVATE_KEY_PREFIX or withoutOperationPrivateKeys would therefore be scanned by the pin and invisible to both layers — a fourth hand-copy landing on main with every PR reporting green, which is the #7802 shape the table was written to close.
git ls-files/git grep, which the roster's literal collector deliberately cannot name (its header states that trade). The correspondence between scanner and glob is therefore prose-held here, and prose held a radius exactly once before, in #9763.
The sibling that got it right, and why it is the model
packages/types/src/driver-error-classification.callers.test.ts has the identical shape and the identical glob, and it narrows its scanner to .ts rather than widening the glob — with the reason written out:
⚠️ The EXTENSION boundary is.tsalone, and unlike the tree above that one is not free — it is a deliberate trade with a second gate. […] this package's declared radius is INHERITED as watch hints bycheck:cross-package-test-inputs, and the dispatch-gates self-test pins that no hint of that family reaches therealtime-hooks.test.tsxfile inpackages/client-react[…] A glob here that covers.tsxmakes that case fail.
⇒ Widening @objectstack/core's glob to .tsx is probably the wrong repair — it is the one the types header says breaks a scripts/pm/ self-test specimen owned by another lane. Narrowing isScannedSource to .ts is the cheap half; whoever takes it should measure the .tsx population under packages/ first (the types header measured 8 tracked .tsx files there, all with zero mentions of its own subject) and record what is lost, rather than assuming.
Measurement a fixer should redo rather than trust
git ls-files 'packages/**/*.tsx' | wc -l
git grep --files-with-matches --fixed-strings -e OPERATION_PRIVATE_KEY_PREFIX -e withoutOperationPrivateKeys -- packages | grep '\.tsx$'
Negative claims here should be paired with a positive control on the same command shape, the way that pin's own anti-vacuity tests are.
Dedup
One targeted search_issues pass. It was live rather than silently zero — the query returned the ancestor card #7284 (withoutOperationPrivateKeys is now hand-copied into three packages) as a hit. Nearest neighbour by shape is #8113 (a detector seeing only one idiom of the thing it pins); neither is this subject.
Refs: #7284 (the convergence the pin guards), #7802 and #9763 (the declaration table and the day prose held a radius), #14878 / PR #15085 (where this was noticed).
Generated by Claude Code
Activity
os-zhuang commentedon Sep 4, 2026
Triage (R+148): lands in
packages/core/src/security/operation-private-keys.pin.test.ts⇒domain:engine. GradedBug·priority:p3.Routed on where the likely repair lands, not on where the mismatch is visible: the card's own reading is that narrowing⚠️ ⛔ Widening the glob in
isScannedSourceto.tsis the cheap half, and that file ispackages/core.scripts/cross-package-test-inputs.mjsis probably the wrong repair — see below — but if the measurement says otherwise, that limb isdomain:devxand the card comes back for a designation.Bug: the pin judges a population strictly wider than the population turbo re-runs it for./\.tsx?$/matches.tsand.tsx; the declared radius ispackages/**/*.ts.priority:p3— no instance exists today, and the card asserts none. The exposure is a.tsxfile underpackages/declaring its ownOPERATION_PRIVATE_KEY_PREFIXorwithoutOperationPrivateKeys: scanned by the pin, invisible to both scoping layers, landing onmainwith every PR green. ⇒ That is the #7802 shape the declaration table exists to close, one extension wide.⭐ The sibling already solved this, and its reasoning is the fence
packages/types/src/driver-error-classification.callers.test.tshas the identical shape and the identical glob, and narrows its scanner rather than widening its glob — with the reason written out:⇒ Widening
@objectstack/core's glob would break ascripts/pm/self-test specimen owned by another lane (the dispatch-gates self-test pins that no hint of that family reachesrealtime-hooks.test.tsxinpackages/client-react). ⛔ Do not take the symmetric-looking repair without reading that header first.typesheader measured 8 tracked.tsxfiles underpackages/, all with zero mentions of its own subject. ⇒ Re-run the card's two commands for this subject and record what coverage is given up, pairing every negative with a positive control on the same command shape — the way that pin's own anti-vacuity tests already do.git ls-files/git grep, which the roster's literal collector deliberately cannot name — its header states that trade. ⇒ The scanner-to-glob correspondence is prose-held, and #9763 is the day prose held a radius and lost. Whatever lands should say so at both ends.origin/main89eb997d8; locateisScannedSourceand the@objectstack/coreroster entry by symbol.Generated by Claude Code
claude commentedon Sep 4, 2026
Claim —
domain:engineexecution seatSession
https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ· branchclaude/issue-15090-core-private-keys-scanner-extension.comment is the claim, and the comments were re-read after writing it. Triage
5544065007(17:18:53Z today) is the operative and only ruling on this card; it was read now rather than
recalled, because a card's operative ruling is its most recent one at claim time.
Why this one, in the total order. Six cards carry
domain:engine+pm:queue. Five are notdispatchable, which is a different thing from not being next:
packages/metadata-protocol/src/protocol.tsprotocol.ts, plusobjectql/src/engine.tsandspec/src/data/field.zod.tsplatform-objectszh-CN.*.generated.ts⭐ I re-derived that map by content rather than trusting it: I have had it wrong once in this
session, and the fix for that is not to be more careful, it is to read the PR's file list again.
⇒ #15090 is the only dispatchable card, and it is taken because it is dispatchable, not because
p3 outranks p1.
Clause-② prediction (the dev's declaration governs, not this line). Path limb: the expected
⚠️ If the diff reaches a contract file the declaration limb is what stops it, not this
diff touches
packages/core/src/security/*.pin.test.tsand possiblyscripts/, and notpackages/spec/src/**. Content limb: narrowing a test scanner neither changes contractaccept/reject behaviour nor widens a published surface. ⇒ predicted
Clause-②: no, dispatched atopus.prediction.
⛔ The routing fence I am carrying forward verbatim from triage, because it is the one thing
this card can get wrong:
⇒ If the measurement points at the glob, the dev stops and reports. This seat ⛔ never produces
domain:*labels and ⛔ never re-grades; a designation is triage's to make.Generated by Claude Code
claude commentedon Sep 4, 2026
os-dev-report
{ "issue": 15090, "status": "done", "clause_2": "Clause-②: no", "branch": "claude/issue-15090-core-private-keys-scanner-extension", "pr": "https://github.com/objectstack-ai/objectstack/pull/15499", "premise_still_valid": true, "routing_fence_fired": false, "summary": "Clause-②: no. Narrowed `isScannedSource` in packages/core/src/security/operation-private-keys.pin.test.ts from /\\.tsx?$/ to `.ts` only, so the pin judges exactly the population its declared radius (a packages/** subtree glob ending in .ts) makes turbo re-run it for. Added one executable case that turns the previously prose-held scanner/glob correspondence into three assertions (the .tsx exclusion is real and non-empty; the filter drops them; no excluded .tsx file declares either guarded symbol), and wrote the reason at BOTH ends — the pin's header and the @objectstack/core entry in scripts/cross-package-test-inputs.mjs (comment only, the glob is byte-unchanged). Diff is 3 files: the pin test, that comment, and a patch changeset. No packages/spec, no published symbol added/removed/re-signed, so the declaration is `no`. NOT governed (measured, with a firing positive control). Draft PR #15499 is open and was NOT flipped ready; auto-merge not armed.", "tests": "All at branch head 0d795f7be, worktree /home/user/objectstack-15090 off origin/main b548e438d. Heavy runs went through scripts/pm/os-verify-lock.sh (slot issue-15090-core-pin); verdicts read from its VERDICT line, not from a bare $?. (1) pnpm --filter '@objectstack/core^...' build -> VERDICT command-exit 0; 3 packages actually built (spec, types, metadata-core), so the filter matched rather than exiting 0 having run nothing. (2) pnpm --filter @objectstack/core typecheck -> VERDICT command-exit 0, including check:test-typecheck. MEASURED on the edited file, not assumed: tsc -p tsconfig.test.json --listFiles counts operation-private-keys.pin.test.ts 1 time (the main program counts it 0). (3) pnpm --filter @objectstack/core exec vitest run --maxWorkers=2 -> VERDICT command-exit 0, 49 files / 1190 tests passed (the pin file alone: 5 passed, up from 4). (4) ABLATION, two legs, both in one locked run of a script carrying trap RESTORE_FN EXIT INT TERM with ABSOLUTE paths. Precondition asserted first: on-disk blob == HEAD blob (1aa9592225a9f...). Leg A, re-widen isScannedSource back to /\\.tsx?$/: mutation CONFIRMED ON DISK before running — old-text grep count 1->0, new-text count 2, blob hash 1aa9592225a9f... -> a4d0ea50d1a3c... . Result 1 failed | 4 passed, red exactly on the new case ('The scan surface has grown `.tsx` files back', expected [ ...(8) ] to deeply equal []) while the offender assertion stayed GREEN — the predicted direction, and the whole point: a wider scanner reads as coverage CI never runs. Leg B, plant an untracked .tsx under packages/ declaring OPERATION_PRIVATE_KEY_PREFIX: presence confirmed on disk (112 bytes) and confirmed visible to git as untracked (count 1) before running. Result 1 failed | 4 passed, red on 'These `.tsx` files declare their own copy...'. RESTORE proven on disk, not by exit code, after each leg: git hash-object == the HEAD blob, git diff HEAD empty, git status --porcelain empty, planted file gone. No build/dist leg is involved — vitest executes this pin from source, and it imports only node builtins plus vitest, so there is no exports-resolved dist for a stale artifact to hide in. (5) THE FENCE MEASUREMENT (see routing_fence_fired). Same mutate/observe/restore discipline against scripts/cross-package-test-inputs.mjs: precondition blob == HEAD blob 75ee0619f09d8..., mutation confirmed on disk (old-anchor 2->1, new-text 0->1, hash -> a9cbc5031edbf...), probe re-derived check:cross-package-test-inputs' watch hints from the tree via dispatch-gates' own discoverFamilies/hintCovers. BASELINE: 106 hints, case 'nor a .tsx test file inside it' = true, covering .tsx hints = []. MUTATED: 107 hints, that case = FALSE, covering hint = the added `.tsx` glob itself. Restored and proven: hash back to 75ee0619f09d8..., git diff HEAD empty, anchor counts back to 2/0. (6) GATES: 57 derived families, all 57 exit 0 — see the gates array. Exit codes captured BEFORE any pipe (cmd > log 2>&1; EXIT=$?), never after one. (7) node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list -> exit 0, '57 derived famil(ies) accounted for — 57 run, 0 NOT-MEASURED', asserted against the Reconciliation line's own total, not against a count of matched families. Derived AFTER the final commit. (8) Governed surfaces: node scripts/pm/check-governed-merges.mjs --test on the final 3-file list -> exit 0, NOT governed; positive control --test docs/adr/0094-x.md -> exit 3, GOVERNED. The gate can say no.", "gates": [ "exit 0 | node packages/lint/scripts/check-reference-carrier-shape.mjs", "exit 0 | node packages/lint/scripts/check-reference-carrier-shape.mjs --self-test", "exit 0 | node scripts/check-adr-0087-registration.mjs --self-test", "exit 0 | node scripts/check-changeset-no-major.mjs --self-test", "exit 0 | node scripts/check-ci-filter-parity.mjs", "exit 0 | node scripts/check-closing-keyword-parity.mjs", "exit 0 | node scripts/check-closing-keyword-parity.mjs --self-test", "exit 0 | node scripts/check-comment-mask-adoption.mjs", "exit 0 | node scripts/check-comment-mask-adoption.mjs --self-test", "exit 0 | node scripts/check-comment-mask-corpus.mjs", "exit 0 | node scripts/check-empty-changeset.mjs --self-test", "exit 0 | node scripts/check-keyed-text-bounds.mjs", "exit 0 | node scripts/check-keyed-text-bounds.mjs --self-test", "exit 0 | node scripts/check-plugin-teardown-shape.mjs", "exit 0 | node scripts/check-plugin-teardown-shape.mjs --self-test", "exit 0 | node scripts/check-self-test-wired.mjs", "exit 0 | node scripts/check-self-test-wired.mjs --self-test", "exit 0 | node scripts/check-system-context-census.mjs", "exit 0 | node scripts/check-system-context-census.mjs --self-test", "exit 0 | node scripts/check-undeclared-dep-imports.mjs", "exit 0 | node scripts/check-undeclared-dep-imports.mjs --self-test", "exit 0 | node scripts/check-whole-set-label-write.mjs", "exit 0 | node scripts/check-whole-set-label-write.mjs --self-test", "exit 0 | node scripts/docs-audit/check-affected-docs.mjs", "exit 0 | node scripts/docs-audit/check-drift-comment.mjs", "exit 0 | node scripts/pm/release-rehearsal-clone.mjs --self-test", "exit 0 | pnpm check:agent-test-spelling", "exit 0 | pnpm check:bash32-floor", "exit 0 | pnpm check:changeset-gate-self-tests", "exit 0 | pnpm check:cli-command-ids", "exit 0 | pnpm check:cross-package-test-inputs", "exit 0 | pnpm check:doc-authoring", "exit 0 | pnpm check:driver-memory-census", "exit 3 -> re-run to exit 0 after building the closure | pnpm check:dual-build-cjs-loads", "exit 0 | pnpm check:engine-double-contract", "exit 0 | pnpm check:entry-guard", "exit 0 | pnpm check:kernel-hook-pairs", "exit 0 | pnpm check:logger-receiver-detach", "exit 0 | pnpm check:nul-bytes", "exit 0 | pnpm check:objectql-double-limit", "exit 0 | pnpm check:objectui-changeset", "exit 0 | pnpm check:org-identifier", "exit 0 | pnpm check:page-declaration-shape", "exit 0 | pnpm check:parse-guard", "exit 0 | pnpm check:pm-half-states", "exit 0 | pnpm check:pnpm-filter-targets", "exit 0 | pnpm check:published-files", "exit 0 | pnpm check:query-options-erasure", "exit 0 | pnpm check:ratchet-remedy-authority", "exit 0 | pnpm check:refd-timer-probe", "exit 0 | pnpm check:slot-lookup", "exit 0 | pnpm check:test-source-alias", "exit 0 | pnpm check:type-check-coverage", "exit 3 -> re-run to exit 0 after building the closure | pnpm check:type-check-debt", "exit 0 | pnpm check:type-source-resolution", "exit 0 | pnpm check:watch-hint-literal", "exit 0 | pnpm check:where-matcher" ], "gates_notes": "MEASUREMENTS vs merely exit 0: the two that first returned exit 3 (PREREQUISITE NOT MET = NOT MEASURED) were NOT reported as passes — the closure was built (turbo run build --filter='./packages/*' --filter='./packages/*/*', locked, VERDICT command-exit 0) and both re-ran to a real green: check:dual-build-cjs-loads reports 103 require entry points across 66 packages loading and 619 emitted CJS files parsing; check:type-check-debt --re-measure reports 13 ledger entries re-measured, 143 raw tsc errors, none above its recorded number. check:cross-package-test-inputs is the family this card is about and it is a real measurement on the final tree: 117 self-test cases pass and 26 packages read outside themselves, all declared. check:nul-bytes exit 0, plus an independent control-character self-scan of both edited files (grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' -> no hits) AND a Python sweep for invisible characters, which caught and removed two U+200B zero-width spaces I had introduced while dodging a `*/` sequence inside a JSDoc block. The `--self-test` invocations in the list are measurements of the GATE's logic, not of my tree; the paired bare invocation beside each is the one that read the tree. NOT claimed as clearances: dispatch-gates printed 37 artifact-roster families whose `silent` verdict is a fact about a list rather than about my paths (11 of them keep a roster in a directory one of my paths is in). They were not run and their silence is reported as silence.", "tsx_population": "8 .tsx files under packages/ at b548e438d, tracked and also over the pin's real surface (git ls-files --cached --others --exclude-standard), all 8 in packages/client-react; 0 of them mention OPERATION_PRIVATE_KEY_PREFIX or withoutOperationPrivateKeys. FIRING POSITIVE CONTROL on the same command shape (git grep --files-with-matches -z --untracked --text --fixed-strings -e ... -- packages, then the same .tsx filter): substituting a string that really is in a .tsx file returns 2 .tsx hits (client-react/src/index.tsx, client-react/src/realtime-hooks.tsx), both in the single-pattern and the two-pattern form; a string present nowhere exits 1 with no output. So the command shape, the pathspec and the .tsx filter can all produce hits — the zero is about the tree, not about the query. NOTE, and it is worth a moment: my FIRST attempt at a positive control used a symbol that does not exist (useRealtimeSubscription) and returned 0 .tsx hits — a NOT-MEASURED that reads exactly like the pass it was meant to validate. It was replaced, not rationalised. WHAT NARROWING LOSES, stated rather than asserted-away: the pin gives up naming a .tsx redeclaration as an offender. That loss is empty today and the loss is now RE-MEASURED on every run by the new case rather than transcribed. It also loses nothing CI ever acted on: a .tsx-only diff never re-runs this pin under either scoping layer, so the pre-change 'coverage' could only ever fire in some later, unrelated PR.", "zone2_falsified": [ "NONE of claims 1-5 was falsified; all five were re-measured at today's HEAD b548e438d rather than at the card's 89eb997d8, and all five held.", "Claim 1 (the mismatch still exists) HOLDS, both halves read at b548e438d: the pin had `const isScannedSource = (path: string) => /\\.tsx?$/.test(path) && !path.endsWith('.d.ts');` and the roster entry had `globs: ['packages/**/*.ts'],` with the comment 'walks `git ls-files` over the whole repo and reads every matching source file'.", "Claim 2 (the types sibling is the model) HOLDS — read the header myself; its load-bearing sentence is 'A glob here that covers `.tsx` makes that case fail', and the roster entry beside it records that `packages/**` was the only entry in the table covering a .tsx file and turned that case red.", "Claim 3 (redo the measurement) DONE with a firing positive control — see tsx_population.", "Claim 4 (no instance exists today) HOLDS: 0 .tsx files mention either symbol, so the priority:p3 grading premise is NOT falsified.", "Claim 5 (Clause-② prediction `no`) HOLDS and is declared as `Clause-②: no`.", "ONE CORRECTION, not a falsification, and it cuts in the safe direction: the card's suggested command `git ls-files 'packages/**/*.tsx'` sees only TRACKED files, while the pin's own surface is tracked PLUS untracked (--others --exclude-standard, deliberate per its #7706 header). Both readings are 8 here, so the card's number is right — but the two commands are not the same question, and the pin's own surface is the one this card is about. The new case scans the pin's surface, not git ls-files' default." ], "mcp_calls": "1 — create_pull_request, and nothing else. Every READ (issue body, both comments, PR body read-back) and the WRITE of this report comment went through repo-scoped REST curl, which was probed live in this container first (200 on the issue endpoint, 201 on the comment POST). No search_issues call was made; no MCP list or read call was made at all. An earlier revision of this comment said 2 on the assumption the comment would need MCP — it did not, and the number is corrected here rather than left standing.", "open_questions": [], "out_of_scope_findings": [ "None filed. I swept for the same defect class elsewhere before concluding that: `git grep -n 'tsx?'` over packages/**/*.ts and scripts/*.mjs returns 26 hits, and none is a second instance. The in-package scanners that use a `tsx?` pattern (client/src/envelope-caller-census.test.ts, spec/scripts/check-error-code-provenance.ts, spec/scripts/check-skill-examples.ts) all use it to EXCLUDE test files, where a wider pattern is conservative rather than radius-widening; the rest live in root scripts/ gates, which declare no radius in the cross-package table at all. The only other roster entry with an extension-keyed glob is @objectstack/types, which is the sibling that already narrowed. The one thing I did fix in place, and it is declared here rather than smuggled: the @objectstack/core roster comment claimed the pin 'walks `git ls-files` over the whole repo'; its pathspec is `packages`. Same file, same defect class (the scanner/glob correspondence prose), mechanical, no new verification surface — corrected in the same comment that now carries the correspondence, and named in the PR body." ] }Generated by Claude Code