Skip to content

fix(tooling): drop the derivation gate's export filter — rule 2 as a one-liner, rule 1 scoped - #7274

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-6291-derivation-export-filter-split
Sep 2, 2026
Merged

yinlianghui merged 2 commits into
mainfrom
claude/issue-6291-derivation-export-filter-split

Conversation

@yinlianghui

Copy link
Copy Markdown
Contributor

Fixes #6291

Ruling A (split), implemented in the order it sets: half 1 is rule 2's filter drop, half 2 is the two structural narrowings, then rule 1's filter drop, then a mechanically regenerated DEBT block, then the reasoned ALLOW entries. One branch, two commits, each independently green.

Every figure below is re-taken on 3612110ff against @objectstack/spec@17.2.0. PR #6284's census is the evidence base, not a list to transcribe — and it had moved.

The population, re-taken

PR #6284 (a76b18cf2) this commit (3612110ff)
rule 1, filter dropped, no narrowings +29 +25
rule 1, filter dropped, narrowings on (projected) +19
rule 2, filter dropped +2 +1

Four rule-1 sites left the population in the interval: ActionParam twice (b84dc18, "one authority for ActionSchema and the Breadcrumb pair", consolidated the pair that already disagreed with itself), SelectOption, and EXPLAIN_BATCH_MAX_RECORD_IDS — the one whose own comment said it existed only until the spec pin exported the constant (#6286). Rule 2's second site was CURATED_CAPABILITY_LABELS; #6285 replaced its prose claim with a two-directional parity test, so the burn-down this rule exists to provoke took the site out of the population before this change could reach it.

Two sites the census classified as real mirrors — the FlowNode / FlowEdge pair in FlowNodeInspector.tsx — became pure aliases in the interval, so the narrowing retires them rather than the ledger carrying them.

Where the 19 went

4 different-concept collisions → reasoned ALLOW entries. Each read at its site; where the spec's symbol is a schema, probed with safeParse on this commit rather than judged by name.

symbol why it is not a mirror
app-shell:SearchResult A ROW is not a RESPONSE. The spec's is what ISearchService.search() resolves to — {hits, totalHits, processingTimeMs?, facets?}. This is one rendered result row {id, label, href, type, description?}, built from the navigation tree. The two share no key.
app-shell:Dimension The spec's DimensionSchema is the semantic-layer cube dimension and REQUIRES sql. Probed: the local instance fails with invalid_type on sql and unrecognized_keys: ["field","dateGranularity"] — the two keys carrying this shape's whole meaning are the two the spec rejects. The spec exports no DatasetSchema to bind against.
app-shell:ModelConfig A model SELECTION against a model REGISTRY RECORD. Probed: the local instance fails on all five of the spec's required keys (id, name, version, capabilities, limits), and model/temperature/maxTokens are not registry keys at all.
app-shell:DashboardWidget Already derived one hop out and deliberately wider: DashboardWidgetSchema & { id: string }, where @object-ui/types' interface is declared extends Omit of Partial-of-SpecDashboardWidget (minus four re-typed keys). The intersection restores id to required. Deliberately NOT covered by isPureAlias — an intersection that widens a spec shape is exactly what this map exists to make someone write a reason for.

15 findings under 14 names → DEBT, regenerated by --ledger, never hand-written. @object-ui/app-shell (AdminScope, AppLike, FlowEdge, FlowRuntimeState, ObjectLike ×2 sites, RemoteTable), @object-ui/core (CONTEXT_TOKEN_SUGGESTIONS, isContextToken), @object-ui/types (UserFilterFieldSchema, UserFiltersSchema), @object-ui/components (SortDirection), @object-ui/data-objectstack (normalizeFilterOperator), @object-ui/plugin-detail (RecordAlertProps), @object-ui/plugin-tree (TreeConfig).

Rule 2's one addition, CONTEXT_TOKEN_SUGGESTIONS, went to CLAIM_DEBT the same way.

The DEBT anchor is a new issue, and that is deliberate

DEBT_ISSUE was 4115. That block had been empty since #3162 and objectstack#4115 was itself retired by objectstack#6883, so the gate's own stale-entry message — "delete them … (and close #N once the ledger is empty)" — pointed at an issue nobody can act on. #6291 could not serve either, being the card this PR retires. #7265 is the open burn-down anchor, with the seeded population and its provenance in the body.

Noted there and not changed here: CLAIM_DEBT_ISSUE = 4592 has the same dead-anchor defect with a live 19-entry block. Rule 2's ledger is not what this card widened, so it is filed rather than ridden.

The narrowings, and one deviation from the ruling's literal wording

The ruling says "extend isRendererLike to rule 1". Implemented as a new, narrower predicate, rendersJsx, and the reason is measured rather than stylistic.

Rule 2 can afford isRendererLike because it drops function and class declarations one line earlier (if (!nameNode) continue), so that predicate only ever judges a const initialised with a call or a function. Rule 1 records every function with derived: false. Reusing isRendererLike verbatim would therefore skip every spec-named function in the tree — and measured, that is four declarations, not two: the two local React Field components the ruling names, plus isContextToken and normalizeFilterOperator, both classified real mirrors by the census, both DEBT entries today. normalizeFilterOperator is a second normalizer over the spec's own, and two normalizers disagreeing about filter operators is the silent over-fetch class of objectstack#3948. A blanket "functions are renderers" would have made those two invisible instead of tracked — silently, and for good.

So rendersJsx tests the judgement the header actually states — "a component that RENDERS the spec's shape is not a second declaration of it" — literally: the declaration must contain JSX. A predicate returning boolean does not; a component returning a JSX element does. This retires exactly the sites the ruling predicted and no others.

isPureAlias is the narrowest possible reading of the second narrowing: a bare TypeReferenceNode, one identifier, no type arguments, no qualified name. A generic reference A of B, a union, and A & { … } are all still forks. It generalises the judgement scanFile already makes for barrels — "whatever it points at gets judged at its own declaration site".

Two ALLOW entries were DELETED — the one place this exceeded the ruling

@object-ui/auth:AuthProvider and @object-ui/plugin-list:UserFilters stopped matching anything once rendersJsx made their judgement structurally, and ratchet 3 requires deleting an entry that excuses nothing (a stale entry reserves the name for a future fork under it). So the narrowing shrank the hand-written part of the map while the collisions grew it: 16 declared dialects → 18.

Both deletions are documented in place with what each entry carried beyond the waiver — auth-spec-parity.test.ts still fails if the spec's AuthProvider stops being an enum of provider ids, and AuthProviderConfig is still renamed rather than excused, because that one really does name the spec's OAuth registration shape.

@object-ui/plugin-list:ListView survived the same narrowing, and the asymmetry is documented rather than tidied: the finding it now matches is not the component but the bare export { ListView, … } in packages/plugin-list/src/index.tsx. scanFile's barrel skip fires only when the re-export carries a module specifier, so a bare export { X } over a local import is recorded at the barrel. That hole predates this card; it is filed, not fixed here.

The named pin was rewritten, not deleted

scripts/__tests__/check-spec-symbol-derivation.test.ts › "an unexported declaration — it publishes no surface to be mistaken for the spec" now asserts the inverse, on the same fixture — which makes the point better than a new one could, because that fixture re-exported the declaration on the very next line (export type { InternalNavigationConfig }) and so always did publish a surface. hasExportModifier never saw it, because the export was a separate statement. The pin's own fixture contradicted the pin's own name.

Beside it, the half the pin was really holding: rule 2 stays quiet on module-local shapes for its OWN reasons — renderer skip, tie test, absent claim — never because of an export keyword.

scanFile is now exported (as scanFileForClaims already was) so the narrowings carry a discrimination proof: one red per widening, one green per narrowing, and the red near neighbour beside each — a non-rendering function is still a fork, an alias that widens is still a fork. Those two are what keep rendersJsx from decaying into isRendererLike and isPureAlias into "type aliases are exempt". 35 cases → 40.

Ablation evidence

Each ablation ran against the committed implementation, with the mutation confirmed on disk by injected-marker count before the run, an EXIT-trapped restore, and the restore proven by comparing git hash-object against the HEAD blob (not by a trap firing).

ablated gate/tests what turned red
rendersJsx skip removed gate exit 1 exactly 4: Field ×2 (the retired false positives) plus AuthProvider and UserFilters — which is also the independent confirmation that the two deleted ALLOW entries are accounted for by the narrowing and nothing else
isPureAlias removed gate exit 1 FlowNode at FlowNodeInspector.tsx:87 and FlowPreview.tsx:57. The two FlowEdge aliases stay green because FlowEdge is already a DEBT-declared name for that package — the ledger absorbing them is correct behaviour, not a miss
both export filters restored tests exit 1 4 failed / 36 passed — the rewritten rule-2 pin, rule 1's widening pin, and both red near neighbours. The green narrowing cases stay green, as they must: they are green under the filter too

Marker counts, exit codes and restore proofs: on-disk injected-marker count: 1 / 1 / 2 (expect 2); RESTORE_OK after each, with git diff HEAD --name-only empty.

Verification — all on 3612110ff

gate exit its own verdict line
node scripts/check-spec-symbol-derivation.mjs before (67dadd602) 0 ✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; 16 declared dialects, 0 untriaged collisions in 0 packages. / ✅ spec alignment claims: 2 declared deliberate copies, 18 unbacked claims in 5 packages.
pnpm check:spec-symbols after 0 ✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; 18 declared dialects, 14 untriaged collisions in 7 packages. / ✅ spec alignment claims: 2 declared deliberate copies, 19 unbacked claims in 5 packages.
pnpm exec vitest run scripts/__tests__/check-spec-symbol-derivation.test.ts scripts/__tests__/check-doc-component-types.test.ts --maxWorkers=2 0 Test Files 2 passed (2) / Tests 89 passed (89) — the second file is the only other suite in scripts/__tests__ naming this script
pnpm type-check:scripts 0 tsc -p tsconfig.scripts.json, no diagnostics
pnpm lint:root (unnarrowed) 0 ✖ 29 problems (0 errors, 29 warnings) — all pre-existing, none in either edited file (verified by name against the report)
pnpm check:control-bytes 0 ✅ check-control-bytes: OK (scanned 5996 tracked text file(s); skipped 85 binary).
own control-byte scan of both edited files — grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' → no hits
pnpm check:entry-guard 0 ✓ check:entry-guard: 58 scripts/ file(s) — no entry guard outside the baseline; 0 file(s) still hand-type one …; 52 export bindings, 52 of them inert on import (0 known-unsafe, ⛔ SHRINK-ONLY). — the new scanFile export is inert on import
pnpm check:esm-specifiers 0 Specifier leg: no un-ledgered package emits an extensionless relative specifier.
pnpm check:shell-escape-residue 0 ✅ check-shell-escape-residue: OK (4/4 root(s) resolved … 0 occurrence(s) outside a fence).
pnpm check:self-import 0 ✅ No package names itself inside its own src/.
pnpm check:node-esm-load 1 NOT MEASURED, not a red. Its build leg replayed turbo cache entries from a sibling agent's worktree on this container (@object-ui/example-byo-backend-console:build: cache hit … /home/user/objectui-issue-6606/…), and it then reported ✗ @object-ui/console: no-build-output. This diff is scripts/-only — 2 files, 0 under packages/— and that gate grades built package artifacts, which this change cannot reach. Filed separately; CI's clean checkout is the authority.

Every exit code was captured by redirect before any pipe.

Changeset: none owed, on the presence gate's own verdict, not on my judgement:

Compared the working tree with 67dadd602 (merge-base with origin/main): 2 file(s) changed,
0 of them published source of a package the release covers, 0 of them a manifest whose
published contract moved, 0 under a package changesets ignores, 0 changeset(s) added.
✅  No source or published contract of a released package changed in this range, so no
changeset is owed.

There is no skip-changeset label in this repo — confirmed by PR #6284 and not created here.

Scope fence

Two files, both under scripts/: check-spec-symbol-derivation.mjs and check-spec-symbol-derivation.test.ts. Zero package-source edits — none of the 14 mirror sites was fixed; they are ledger entries, per the ruling. #6286 and #6287 remain open on their own merits and are untouched by this branch.

Heavy runs were serialised through the container-global lock at /home/user/objectstack/scripts/pm/os-verify-lock.sh (objectui carries no copy), slot dev-6291.


🤖 Generated with Claude Code

https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b


Generated by Claude Code

…al declarations

A spec-alignment claim is read by the next agent editing the FILE, not by an
importer, so the `export` modifier was never what made it dangerous. The three
module-local mirrors in `packages/react/src/spec-bridge/bridges/form-view.ts`
(objectui#5652) had drifted on three keys and inverted one arm with this gate
green throughout.

Half 1 of the split ruled on objectui#6291, re-measured on this commit against
`@objectstack/spec@17.2.0` rather than transcribed from PR #6284's census:

  rule 2   18 unbacked claims -> 19   (+1 module-local declaration)

One, not the two measured at a76b18c — objectui#6285 fixed
`CURATED_CAPABILITY_LABELS` in the interval by replacing its prose claim with a
parity test. The one that remains, `CONTEXT_TOKEN_SUGGESTIONS`, is a real
mirror and goes to the mechanically regenerated CLAIM_DEBT block, with a note
recording why a shrink-only block may grow on a jurisdiction widening.

The named pin "an unexported declaration — it publishes no surface to be
mistaken for the spec" is rewritten, not deleted: it now asserts the inverse,
on the same fixture, which turns out to re-export the declaration on the next
line and so always did publish a surface. A second case beside it proves the
precision rules — renderer skip, tie test, absent claim — are what keep
module-local shapes green.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
…tions, scoped

Half 2 of the split ruled on objectui#6291, in the order the ruling set: the two
structural narrowings first, then the filter drop, then a mechanically
regenerated DEBT block, then the reasoned ALLOW entries.

Re-measured on this commit against `@objectstack/spec@17.2.0` rather than
transcribed from PR #6284's census:

  rule 1, filter dropped, NO narrowings   0 untriaged -> 25   (+25)
  rule 1, filter dropped, narrowings on   0 untriaged -> 19   (+19)

25, not the 29 at a76b18c — `ActionParam` twice (b84dc18 consolidated the
disagreeing pair), `SelectOption`, and `EXPLAIN_BATCH_MAX_RECORD_IDS`
(objectui#6286) left the population in the interval.

The narrowings are `rendersJsx` and `isPureAlias`. `rendersJsx` is deliberately
NOT rule 2's `isRendererLike`: rule 2 drops function and class declarations one
line before it, rule 1 records every one of them, so reusing it verbatim would
have silenced `isContextToken` and `normalizeFilterOperator` — both real
mirrors, both classified as such by the census. It tests the judgement the
header actually states, that a component RENDERS, by requiring JSX in the
declaration.

The remaining 19 split 4 / 15. Four different-concept collisions got reasoned
ALLOW entries — `SearchResult`, `Dimension`, `ModelConfig`, `DashboardWidget`,
each read at its site and, where the spec's symbol is a schema, probed with
`safeParse` on this commit. Fifteen findings under fourteen names are real
mirrors and were seeded into DEBT by `--ledger`, re-anchored on objectui#7265:
the old anchor (objectstack#4115) is no longer open, and this card's own number
cannot serve as one either, since the PR retires it.

Two ALLOW entries were DELETED, which the ruling did not predict:
`@object-ui/auth:AuthProvider` and `@object-ui/plugin-list:UserFilters` stopped
matching anything once `rendersJsx` made their judgement structurally, and
ratchet 3 requires deleting an entry that excuses nothing. Both deletions are
documented in place with what each entry carried beyond the waiver.

`scanFile` is exported so the narrowings carry a discrimination proof: one red
per widening, one green per narrowing, and the RED near neighbour beside each —
a non-rendering function is still a fork, and an alias that widens is still a
fork.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BGMDbrVa8JjZcCQ7DWYH1b
@github-actions github-actions Bot added the tests label Sep 2, 2026
@yinlianghui
yinlianghui marked this pull request as ready for review September 2, 2026 02:59
@yinlianghui
yinlianghui added this pull request to the merge queue Sep 2, 2026
Merged via the queue into main with commit 9207c53 Sep 2, 2026
29 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-6291-derivation-export-filter-split branch September 2, 2026 03:14
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Oct 7, 2026
…ectstack-ai#6286 release note (objectui#9528) (objectstack-ai#10970)

Fixes objectstack-ai#9528

Clause-②: no — release-note prose; no contract, accept set or public
surface changes (a contract review is still owed: the diff is
`.changeset` prose)

## What changes

One sentence in the **pending**
`.changeset/6286-explain-batch-cap-from-spec.md`. That body publishes
verbatim into `@object-ui/plugin-grid`'s CHANGELOG at the next release,
and the release PR objectui#5400 regenerates from `main`, so this only
helps if it lands before that PR merges. The frontmatter is untouched:
`check-changeset-overwrite.mjs` prints `declared at base:
@object-ui/plugin-grid: patch` and `declares now:
@object-ui/plugin-grid: patch`.

Before:

> … is exactly the drift `scripts/check-spec-symbol-derivation.mjs`
argues about — and could not catch here, because both of its scanners
skip non-exported declarations and this const was module-local
(objectui#5899).

After:

> … is exactly the drift `scripts/check-spec-symbol-derivation.mjs`
argues about — and could not catch here: when this change landed, both
of its scanners skipped non-exported declarations, and this const was
module-local (objectui#5899). objectui#6291 has since removed that
filter from both scanners.

## Why this wording, and why the history stays

The sentence was **true when written and went false later**. It is not a
false sentence from the start. So the fix keeps the explanation, puts it
in the past tense and dates it. Deleting it would lose true history.

**1. True when the changeset landed.** `c38162d7c9` (objectstack-ai#6333, 2026-08-25)
added the changeset. At that commit, both scanners in the gate open with
the same filter line, once in `scanFileForClaims` (rule 2) and once in
`scanFile` (rule 1):

```
if (!hasExportModifier(stmt)) continue;
```

The table below counts that line and two related markers in each
revision of the gate file (`git show
REV:scripts/check-spec-symbol-derivation.mjs | grep -c …`):

| revision | `if (!hasExportModifier(stmt)) continue;` | `const
hasExportModifier` | `// No export filter` |
|---|---|---|---|
| `c38162d7c9` (objectstack-ai#6333 lands) | 2 | 1 | 0 |
| `9207c53e45^` | 2 | 1 | 0 |
| `9207c53e45` (objectstack-ai#7274, objectui#6291) | 0 | 1 | 2 |
| `b120b66077` (this PR's base) | 0 | 0 | 3 (two code comments plus one
header mention of them) |

**2. False since `9207c53e45`** (objectstack-ai#7274, objectui#6291, 2026-09-02). On
the base, the two scanners now carry `// No export filter: a
module-local declaration carrying a spec-alignment` in
`scanFileForClaims` and `// No export filter (objectui#6291). A
module-local declaration under a spec` in `scanFile`. The gate's own
header says the same thing: "⛔ HISTORY, NOT BEHAVIOUR. Neither scanner
has an export filter today."

The order of the two commits was checked on a shallow checkout, so it
needs a control leg:
- `git merge-base --is-ancestor c38162d 9207c53` exits 0.
- The reverse check exits 1.
- Control leg: `git merge-base --is-ancestor 62311b6 c38162d`
exits 0. `62311b6419` is the gate's first commit (2026-07-30), older
than both.

**3. Which rule would catch this const, then and now.** I measured this
with a 2×2 probe, not read it off the header:
- **Fixture A** is the pre-objectstack-ai#6333 hook source, `git show
c38162d^:packages/plugin-grid/src/hooks/useRecordCrudVerdicts.ts`. In
it, `const EXPLAIN_BATCH_MAX_RECORD_IDS = 200;` is module-local.
- **Fixture B** is the same file with `export` added. It is the control.
- Both fixtures were fed to the scanners of both gate versions, using
today's spec export set (5085 names; `EXPLAIN_BATCH_MAX_RECORD_IDS` maps
to `@objectstack/spec/security`).
- The only change to the `c38162d7c9` copy was exporting its `scanFile`.
The anchor count was checked on disk: 1 plain before and 0 after; 0
exported before and 1 after.

| fixture | gate at `c38162d7c9`: rule 1 | rule 2 | gate at
`b120b66077`: rule 1 | rule 2 |
|---|---|---|---|---|
| A — module-local (verbatim) | none | none | `const` finding | none |
| B — `export` added (control) | `const` finding | none | `const`
finding | none |

What the probe shows:
- **When objectstack-ai#6286 landed, the export filter is what hid the const from rule
1.** The control proves the old rule 1 flags the const as soon as it is
exported.
- **Today rule 1 (the name match) reports it.** `DEBT` is empty and no
`ALLOW` key names it, so it would be a fresh finding and the gate would
exit 1.
- **Rule 2 reports it in no cell, and no filter is involved.**
`findClaim` returns `null` on the const's doc comment. The comment names
`@objectstack/spec@17.0.0-rc.6` but contains none of the
`CLAIM_PATTERNS` phrases.
- So for this const, the card is right that rule 1 is the operative
rule. The claim note's point is also true: both scanners dropped the
filter. But that matters for rule 2 only in general, not for this const.

The new sentence therefore states only history: the filter existed when
the change landed, and objectui#6291 removed it from both scanners. It
asserts no live capability of the gate, so a later change to the gate
cannot make it false the way the old sentence became false (AGENTS.md
objectstack-ai#9).

## The other pending changesets that name the gate

`git grep -l check-spec-symbol-derivation b120b66 -- .changeset`
lists **seven** files. As a positive control, the same form of search
for `plugin-grid` in the 6286 file counts 1. I widened the search to
every spelling of the gate (`spec-symbol`, `check:spec-symbols`), which
gives **17** files, and read all 17 in full. I also grepped every
pending body for `non-exported|unexported|not exported|export
filter|hasExportModifier|export modifier|exported only` and for
`module-local`. **Only the 6286 file states the export-filter
property.** The other 16 say nothing about it:

- `7265-core-context-token-mirrors`,
`7265-data-objectstack-filter-operator-rename`,
`7265-components-sort-direction-bound`,
`7265-plugin-detail-record-alert-renderer-props`,
`spec-symbol-mirrors-app-shell-7265`,
`7265-types-user-filter-field-derives`,
`9596-debt-issue-anchor-truthiness`: these describe module-local mirrors
that the rule-1 `DEBT` ledger carried, which fits today's behaviour.
- `console-form-container-specs-one-declaration-5596`: says derivation
is the gate's sanctioned form.
- `listview-comment-pair-4559`: says rule 2 reads the *attached* comment
block.
- `9542-result-dialog-i18nlabel`: names the rule-2 failure class.
- `3162-types-ledger-batch8-verdicts`, `spec-pin-17-1-0-5328`,
`spec-refresh-17-2-0-5668`: ledger and pin history.
- `ci-decide-markdown-trace-9241`: says the gate opens
`content/docs/**`.
- `8841-tree-view-config-derive` and
`date-range-default-range-binding-4984`: explain a miss by **name
matching** (and, in 4984, rule 2's tie test). That is a different
property, which I did not re-measure here.

## Gates (all read at HEAD `25b8b6eeb2`, the only commit on this branch)

| command | exit | verdict line |
|---|---|---|
| `pnpm check:changeset-claims` | 0 | `✅ No pending changeset names a
file this change touches.`; self-contradiction: `Every package declared
across those 1 body(ies) is either not negated in its own prose, …`;
born-false: `Line addresses read in them: 0.` |
| `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.` |
| `node scripts/check-changeset-overwrite.mjs` | 0 (report-only) | `1
modified` — case 2, "CORRECTING a declaration on purpose … prose that no
longer matches the change"; the declared package set is unchanged |
| `node scripts/check-changeset-no-major.mjs` | 0 | ✅ No changeset
declares a major bump. |
| `node scripts/check-changeset-fixed.mjs` | 0 | `✅ All workspace
packages are in the changeset fixed group.` |
| `pnpm check:control-bytes` | 0 | `✅ check-control-bytes: OK (scanned
9240 tracked text file(s); skipped 85 binary).` |
| `pnpm check:new-line-citations` | 0 | `VERDICT
new-cross-file-line-citations: 0 new citation(s), enforcement
report-only -> exit 0` |
| `pnpm exec vitest run --maxWorkers=2` on the 24
`scripts/__tests__/*.test.ts` that mention `.changeset` (via
`os-verify-lock.sh`) | 0 | `Test Files 24 passed (24)` / `Tests 914
passed (914)` / `VERDICT command-exit 0` |

The 24-file set is a superset of what `node
scripts/markdown-test-inputs.mjs --changed -` returns for this file.
That helper records `.changeset/**` as the input of
`check-changeset-no-major.test.ts`; the positive control is
`packages/plugin-dashboard/README.md`, which it echoes back. No package
source, gate or test is touched, so nothing else is owed. `node
scripts/check-governed-queue-guard.mjs --test` on the file answers `✅
NOT GOVERNED`.

## Acceptance notes

- `spec-symbol-mirrors-app-shell-7265` reads "held six declarations …
Four are now bound … and three were renamed". That is 4 + 3 against six.
Its commit (`14a1b92035`, objectstack-ai#9465) removes six app-shell names from
`DEBT`, so one name may have held two declarations. **I did not measure
this.** That changeset has empty frontmatter, so it publishes nothing.
Nobody is carrying this; it is only noted here.
- The 24-file test run printed one `fatal: Invalid symmetric difference
expression 0000…` line on stderr while every test passed. The SHA in it
is all zeros; `check-changeset-overwrite.test.ts` and
`docs-build-trigger.test.ts` are the two files in the set that spell
such a SHA. I did not trace it further. It is not a failure.

## Contract review

A contract review is owed and the PM seat commissions it. Everything
above is meant to be re-runnable: every quoted code string, commit and
command reproduces on this repo. This PR touches no gate, adds no
checker, and leaves the frontmatter and bump level unchanged.

Session: `https://claude.ai/code/session_01EBx9rvB7dufCz4at53x35U`

---

_Generated by [Claude
Code](https://claude.ai/code/session_01EBx9rvB7dufCz4at53x35U)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Split the derivation gate's export-filter change: rule 2 one-liner now, rule 1 scoped (narrowings → DEBT block → 4 ALLOW entries)

2 participants