Skip to content

docs(tooling): rewrite service-automation's DEBT note to its measured composition - #10721

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-10174-service-automation-note
Aug 21, 2026
Merged

docs(tooling): rewrite service-automation's DEBT note to its measured composition#10721
os-zhuang merged 1 commit into
mainfrom
claude/issue-10174-service-automation-note

Conversation

@claude

@claudeclaudeBot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Closes#10174

DEBT['@objectstack/service-automation'] recorded errors: 3 while its own note
narrated 5 — it opened 'code-tier 5.', closed 'Re-measured 5 at 5ab08428.',
and itemised 2 + 3. The two it itemised first were TS2741 in engine.test.ts, a
file tsc no longer reports anything for. The entry was evidently lowered 5 -> 3
without the note being rewritten alongside it.

Prose only. errors: 3 is unchanged — the count is correct and no count moves.

Measured, not adopted

Re-derived with the gate's own invocation (tscErrorCount()'s), from the repo root
against a built dependency closure, at base 53a48c93f4:

$ ./node_modules/.bin/tsc --noEmit --pretty false -p packages/services/service-automation/tsconfig.json
packages/services/service-automation/src/nested-region-parity.test.ts(95,25): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
packages/services/service-automation/src/nested-region-parity.test.ts(151,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
packages/services/service-automation/src/nested-region-parity.test.ts(180,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.

3, all TS2341, all in one file — matching the field exactly, and matching what
the card measured at an earlier tip.

The measurement is valid, not a silent zero: the output carries none of the
codes in the gate's own TSC_SETUP_ERROR set (TS5058/5083/6053/18003/5012/2688),
and tsc's exit 2 came with three recognisable diagnostics, so the gate's
"refusing to record 0" guard does not fire.

The graduation is real, not an exclusion artifact.--listFiles on the same
project lists src/engine.test.ts in the compiled program, and it reports nothing.
That file now builds its pausing fixtures through a single defineActionDescriptor
helper that declares resumeAuthority (its docblock cites #5561), where the note's
TS2741 had been "both literals omit both".

What the note says now

The measured composition first — 3 x TS2341 at nested-region-parity.test.ts
95/151/180, dot-reading the private engine.flows (the as any casts on two of
those lines sit on .config, not on the engine, so they do not suppress it) —
then the 5 -> 3 graduation, then the residue restated as the one decision it
actually is: whether tests may read private state at all.

The entry keeps its warrant as the specimen #5278 cites for composition drift, now
stated honestly: it has drifted both ways — 2 -> 5 by acquiring a second file,
then 5 -> 3 by graduating the first.

Deliberately not in this PR

The file already states the general rule at :167-173 and cites this very entry
as its worked example, but speaks only about counts going up; this entry drifted
the other way. Per triage, extending that wording or adding a note-vs-field
consistency check is a new finding rather than a rider, so neither is here. The
consistency-check proposal is filed separately and named in the dev report.

Gates

Derived with node scripts/pm/dispatch-gates.mjs (no paths — merge base 53a48c93f),
then run at 5757b74a35:

GateResult
check:type-check-coverageOK — 64/77 packages type-checked, 13 in DEBT; self-test 23+24+25+28+9 cases hold
check:type-check-debt (--re-measure)OK — 33 ledger entries re-measured in 271.2s, 1912 raw errors, none above its recorded number
check:cross-package-test-inputsOK — 13 packages read outside themselves, all declared; 90 self-test cases passed
check:nul-bytesOK — 6222 text files scanned, no raw ASCII control bytes

The re-measure half first refused correctly on a partial closure ("would silently
measure a DIFFERENT WORLD"); it was re-run after
turbo run build --filter='./packages/*' --filter='./packages/*/*' (70/70 successful).

Its one informational line is @objectstack/plugin-auth TEST_DEBT 109 recorded vs 97
measured — pre-existing, unrelated to this card, explicitly not an error, and already
owned by #6376.

No changeset: gate-ledger prose publishes nothing, so this carries skip-changeset.


Generated by Claude Code

… composition
`DEBT['@objectstack/service-automation']` recorded `errors: 3` while its own
note narrated 5 -- opening "code-tier 5.", closing "Re-measured 5 at 5ab0842",
and itemising 2 + 3. The two it itemised first were TS2741 in `engine.test.ts`,
a file tsc no longer reports anything for. The entry was lowered 5 -> 3 without
the note being rewritten alongside it.
Re-measured with the gate's own invocation at 53a48c9, from the repo root
against a built dependency closure:
./node_modules/.bin/tsc --noEmit --pretty false \
-p packages/services/service-automation/tsconfig.json
src/nested-region-parity.test.ts(95,25): error TS2341: ...
src/nested-region-parity.test.ts(151,20): error TS2341: ...
src/nested-region-parity.test.ts(180,20): error TS2341: ...
3, all TS2341, all in one file. `--listFiles` confirms `engine.test.ts` is
still in the program, so its silence is a graduation and not an exclusion.
Prose only -- `errors: 3` is unchanged and correct, and no count moves. This is
the mirror image of the rule the same file already states at :167-173, which
cites this very entry as its worked example but speaks only about counts going
UP; this entry drifted the other way.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 21, 2026
@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown
ContributorAuthor

PM review — verified against the diff, not the report. Approving.

The count did not move, and I checked that rather than trusting it

errors: 3 is byte-identical; the diff is 11 insertions / 9 deletions entirely inside the note: string. No ratchet event, which is what this card authorised and nothing more.

⭐ The distinction that makes the graduation claim real

A file that was fixed and a file that quietly dropped out of the project produce the identical observation — zero diagnostics. Asserting a graduation from "tsc reports nothing there any more" would have been the plausible-but-unfounded version.

You closed it with --listFiles: engine.test.ts is still in the project (line 355 of the file list) and reports nothing, and the reason is visible in the source — that file now builds its pausing fixtures through a single defineActionDescriptor helper declaring resumeAuthority (#5561), 5 call sites over 1 construction site. The note says exactly that, so the next reader does not have to re-derive it.

The .config-vs-engine detail is the same instinct one level down: the two as any casts on those lines sit on .config, not on the engine, so they do not suppress the TS2341. Without that sentence the note would read as though the casts were doing something.

⭐ The validity check is the part I want to name

"output contains none of the gate's TSC_SETUP_ERROR codes (TS5058/5083/6053/18003/5012/2688), and tsc's exit 2 came with 3 recognisable diagnostics, so the gate's own 'exited non-zero but printed no recognisable diagnostics — refusing to record 0' guard does not fire."

You did not just run the measurement, you established it was a measurement and not a setup failure wearing one's clothes — using the gate's own definition of that distinction. Writing a note from a number that never ran is precisely the defect class this ledger exists to prevent, and it is how a --re-measure returns a confident zero.

And the heavy gate failing first is evidence, not noise.check:type-check-debt --re-measure exited 1 on a partial closure with "measuring now would not fail, it would silently measure a DIFFERENT WORLD" — then green after a full 70-task build. Reporting that first red rather than only the green is what shows the number in the note was taken against a real closure.

Mutation confirmed on disk with anchor counts both ways, byte delta, and post-write greps — including that 'Re-measured 5 at 5ab08428' went 3 → 2 repo-wide with the two survivors identified as legitimately consistent (connector-mcp, connector-openapi, both errors: 5). That is the right way to prove a string edit hit one entry and not a neighbour.

skip-changeset read back at 09:49:36Z, ~4 minutes after the write and well past the bot window — the #10703 race did not bite. Checking rather than assuming is the correct handling now that we know the window is ordering-dependent.

#10722 is the more valuable output of this card

You went past the instance to the mechanism: nothing reads a DEBT/TEST_DEBT note, and lowerLedgerEntries() at :2229 rewrites only the errors: digits — so --lower, which the gate's own surplus output recommends, desynchronises an entry's prose by construction and mints a fresh instance of this class every time it runs.

That reframes #10174 from "someone forgot" to "the sanctioned path does this." The bounded check design (tier itemisations must sum to errors) and the reason a naive scan over every Re-measured N would false-positive on objectql are both the right level of care for a proposal.

Respecting triage's boundary — not touching :167-173 and not adding the check here — was correct; it belongs in its own card, and now it has one.

#10723 I have routed (it arrived unlabelled, so it was invisible to every board): a second live instance in DEBT['@objectstack/metadata']errors: 89 against a note itemising 34+24+34 = 92 and claiming "Re-measured 92". Your confirmation that it is stale rather than historical is the load-bearing bit: tonight's full --re-measure reported exactly one surplus entry (plugin-auth), so metadata measures its recorded 89 exactly.

And correctly not re-filing the plugin-auth TEST_DEBT drift — #10615 already owns it at 98, now 97. Recording a known-tracked observation instead of minting a duplicate is the right call.

Arming.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 21, 2026 10:19
@os-zhuang
os-zhuang added this pull request to the merge queueAug 21, 2026
Merged via the queue into main with commit fb3b0f7Aug 21, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-10174-service-automation-note branch August 21, 2026 10:29
os-zhuang pushed a commit that referenced this pull request Aug 21, 2026
`DEBT['@objectstack/metadata']` recorded `errors: 89` while its own note
itemised 34 + 24 + 34 = 92 and stated "Re-measured 92 at 5ab0842". The
entry was evidently lowered 92 -> 89 without the note being rewritten
alongside it, the same class PR #10721 just repaired for
`@objectstack/service-automation`.
Prose only. `errors: 89` is unchanged -- re-measured at 4b84834 against
a built dependency closure, the package reports exactly 89.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
os-zhuang pushed a commit that referenced this pull request Aug 21, 2026
…er minting the drift
`DEBT`/`TEST_DEBT` pair a measured `errors` with free prose, and nothing read
the prose: a note could narrate any count at all, including one its own field
contradicted, with every gate green. Four hand repairs of that shape are on
record (#7038, #8982, #10721, #10775), the last two on the same day. None of
them closed the class, because `--lower` MINTS it -- its regex capture ends at
the digits, so the sanctioned one-command way to close a surplus is also the way
to desynchronise a note.
Both halves land together, because neither is safe alone. A check without the
tool fix leaves the ledger RED after the next `--lower` run, in whoever's PR ran
it, breaking the "closing an entry is FREE" invariant #5278/#6376 paid for. The
tool fix without the check leaves today's instances undetected.
COMPOSITION reads exactly one thing -- the house-convention tier itemisation the
notes already open with -- and abstains, silently, everywhere else. A tier
counted twice or a further tier count later in the note means the note quotes
its own history and the entry is skipped rather than guessed at. Per-code
tallies (`TS2835 x67, ...`) are deliberately NOT summed: they are partial by
construction, and plugin-rest's sums to 147 while saying "composition as counted
at 153".
`--lower` now plans a `compositionAt` declaration alongside each lowering that
would strand an itemisation, and writes it as a sibling field beside the number
it already locates -- no prose surgery, no invented composition. The staleness
becomes a declared fact the check reads and the green summary reports, instead
of a silent one nothing reads at all. `compositionAt` may only ever declare a
pile LARGER than the field, so it cannot launder a raise.
Measured: over all 33 entries on main, 11 are checked and 0 fire. Over the tree
before #10721 landed, exactly the two known instances fire and nothing else.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude