fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid - #15152

Merged
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck
Sep 4, 2026
Merged

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid#15152
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck

Conversation

@os-sales

@os-salesos-sales commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes#15048

packages/services/service-automation had no typecheck script at all (its scripts were build and test), so no tsc program anywhere read this package. Turbo/CI typecheck lanes skipped it silently, because a zero-matching filter run exits 0. tsup transpiles with esbuild and vitest runs through esbuild type-stripping; neither type-checks.

Clause-②: no

The route, and which exemplar it was copied from

Copied from plugin-webhooks / service-cluster (#14181, PR #15032), not from plugin-auth / plugin-sharing / core, because it is the structural match. The deciding property is what the BUILD config does with tests:

packagetsconfig.json excludes tests?ledger filefits here
plugin-auth, plugin-sharingyes (**/*.test.ts in exclude)debt ledger presentno
core (#14916)yes (**/*.spec.ts, **/*.test.ts)test-typecheck-debt.json, 4 residualno
plugin-webhooks, service-clusternononeyes
service-automationno

service-automation's tsconfig.json includes the tests and always did (include: ["src"], no test exclusion), so the program that would have read them already existed and was simply never invoked. It is therefore not a package that needs an exclusion compensated for, and AGENTS.md is explicit in the other direction: "Never exclude*.test.ts / *.spec.ts from a package's tsconfig.json". So tsconfig.json is untouched, and the sibling tsconfig.test.json is the family's uniform instrument over the same files.

The sibling changes module semantics only (module: esnext, moduleResolution: bundler, lib: ES2022, matching how vitest actually executes these files). Strictness is inherited and untouched, and it declares no paths.

Measured error count, before and after

Dependency closure built first (pnpm --filter '@objectstack/service-automation^...' build), measured at 2cc4610304 (origin/main):

programbeforeafter
BUILD semantics (tsc --noEmit -p tsconfig.json, which already included the tests)30
test layer (tsc --noEmit -p tsconfig.test.json)30

555 files in the program, covering all 103 of the package's src/**/*.test.ts.

The two readings agree, and that agreement is the load-bearing result — same as service-cluster's own graduation, and unlike @objectstack/core (#14916: 98 undivided → 4 after the split, nearly all TS7006 cascading from one unresolved import). This package carried no config-tier pile at all; the 3 were genuinely code-tier from the start, well inside the dispatch's "a handful, fix them properly" branch, so no stop-and-report was owed.

The three errors, and why the fix is correct rather than convenient

All three were TS2341 ("Property 'flows' is private and only accessible within class 'AutomationEngine'"), all in src/nested-region-parity.test.ts (lines 95/151/180):

src/nested-region-parity.test.ts(95,25): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(151,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(180,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.

Three tests dot-read the class's privateflows map (engine.flows.get(name)) directly, instead of going through its own public accessor:

// src/engine.tsasyncgetFlow(name: string): Promise<FlowParsed|null>{returnthis.flows.get(name)??null;}

That accessor already exists, and it is already the idiom every other test file in this package uses (await engine.getFlow(name) — 10+ call sites across engine.test.ts, canonicalize-stored-flow.test.ts, flow-cold-boot-bind.test.ts, flow-load-conversion.test.ts, etc.). The fix is therefore not a workaround: it replaces three private-internals reads with the public surface the class already offers, matching the rest of the suite — no source signature widened, no cast, no bracket-notation trick.

- const flow = engine.flows.get('repro')!;+ const flow = (await engine.getFlow('repro'))!;
- expect((engine.flows.get('callout')!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');+ expect(((await engine.getFlow('callout'))!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');
- expect((engine.flows.get('purge')!.nodes[1]!.config as any).body.nodes[0].config)+ expect(((await engine.getFlow('purge'))!.nodes[1]!.config as any).body.nodes[0].config)

The two test bodies that were not already async were made async to await the accessor. Re-ran the file after the fix: 22/22 tests pass, unchanged behavior — the assertions test the same thing, only the read reaches it through the public door.

Ledger consequences

  • DEBT entry deleted, not lowered.check-type-check-coverage.mjs carried '@objectstack/service-automation': { errors: 3, note: 'code-tier 3 (TS2341 x3)...' }. That gate's own invariant is "covered packages must not also sit in the ledger". The entry is deleted and the graduation recorded in the file's prose. Gate now reads 5 in DEBT where it read 6 (104 frozen errors, down from 114), on the merged tree.
  • No test-typecheck-debt.json is created, and its absence is the zero: any error in any file here is red immediately, with no entry to be added to. ⛔ No ledger was grown in either direction.

⚠️check:type-source-resolution — a re-baseline, please review it as one

This gate goes red on this diff as expected (the card's own ⚠️ block flags this), and the remedy its failure text names (paths) is measurably the wrong one here too — same finding as service-cluster's own re-baseline, reproduced at larger scale because this package pulls 9 workspace deps instead of 2.

Provenance, measured four ways on one checkout, varying only what typecheck names:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

Row 2 is the load-bearing one: tsconfig.json is always a counted program per this gate's own design (programConfigsFor's doc-block), and it already includes every test file — yet it measures clean, with zero dist-resolved workspace type imports. So the exposure is only reachable through the onboarded tsconfig.test.json program, not merely first seen there. This package also had zero pre-existing programs (no typecheck script at all), so there is no program a dep could be laundered through — the same clean case service-cluster reported.

Numbers stated: before 59/78 packages, 120 programs, 293 pairs, 19 clean; after 60/78, 121, 302, 18 clean — +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else.

Why the entry and not paths, measured: redirecting the 9 deps to source takes this package's test layer from 0 errors to 648 (647 TS6059 "not under rootDir" + 1 TS6133) — all 647 of the TS6059 land in another package's source, zero in this package's own src/: packages/spec/src/** 379, packages/core/src/** 62, packages/plugins/plugin-security/src/** 60, packages/objectql/src/** 49, packages/services/service-messaging/src/** 41, packages/metadata-core/src/** 29, packages/formula/src/** 15, packages/services/service-job/src/** 6, packages/drivers/driver-sql/src/** 6. Billed to packages that cannot pay them down — the same service-cluster (#14181: 0 → 435) and PR #12570 finding, reproduced at a larger scale because this package pulls more workspace deps.

Gates

All measured figures in this section are the post-merge re-run on head c6d25b73 (merge commit 8aff199c, merge base 919beca43); the pre-merge readings they replace were taken at 6a703bf42d, and the merge-round comment on this PR records both sets side by side.

Package-level, on the merged head c6d25b73:

  • pnpm --filter @objectstack/service-automation typecheckexit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s).
  • pnpm --filter @objectstack/service-automation testexit 0: 103 test files / 1225 tests passed.
  • pnpm check:type-check-coverageexit 0: check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root), 5 in the DEBT ledger (104 frozen raw errors), 1 exempt.
  • pnpm check:type-source-resolutionexit 0 after the re-baseline: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned; 60 registered as still resolving a workspace dep's types through dist/.
  • pnpm check:nul-bytes — exit 0.

Dispatch-derived gate list (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff, final head c6d25b73), each exit code captured by redirect-then-read, never across a pipe: 64 commands — 62 exit 0, 2 exit 3 (NOT MEASURED), 0 red.

NOT MEASURED, quoting each gate's own verdict:

  • check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. ⛔ This is NOT a pass: nothing was measured."
  • check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk. … ⛔ This is NOT a pass and NOT a finding: nothing was measured."

Both read the same missing prerequisite — a full-workspace pnpm build, which is CI's job, not a per-package build closure. Three commands (check:pm-dispatch-gates, check:query-options-erasure, check:slot-lookup) needed longer than a 90s probe budget and passed on a second, unhurried run — each does a real full-tree scan, not a hang.

Both edited gate scripts' own --self-test are in that union and hold.

Declined, with reasons

Changeset

.changeset/service-automation-test-tsc-program.md, patch on @objectstack/service-automation. src/** is otherwise byte-identical (only the three private-read call sites in one test file changed, same assertions), so no shipped behaviour moves; the level reflects the published package.json gaining typecheck / check:test-typecheck scripts and a tsx devDependency.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

@github-actionsgithub-actionsBot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Sep 4, 2026
@github-actions

github-actionsBot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 919beca43b2901320cea39afd912b7f662ddd3b1packageMentionDocs.

@os-sales
os-sales marked this pull request as ready for review September 4, 2026 04:14
@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to a conflict with the base branch Sep 4, 2026
…rvice-automation-typecheck
# Conflicts:
#	scripts/check-type-check-coverage.mjs
… tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
@os-warrenClaude

Copy link
Copy Markdown
Collaborator

Merge round — conflicted against main, resolved by keeping every entry

main moved to 919beca43b: sibling PR #15147 (card #15049) landed as 8422acd, graduating @objectstack/service-knowledge out of the same two ledgers this PR edits, which put this PR in mergeable_state: dirty. Nothing about the reviewed change was reopened; the body above stands except for the figures this comment supersedes.

Merge, not rebase.git merge origin/main → merge commit 8aff199c, new head c6d25b73. No rebase, no amend, no force-push.

Conflicts, and why each resolution is "keep every entry"

One file conflicted — scripts/check-type-check-coverage.mjs, two hunks. The two sides do not disagree: each deletes a different DEBT key and records a different graduation.

  1. Graduation prose.service-knowledge's paragraph (from main) and service-automation's (from this branch) are independent paragraphs about different packages. Both kept, service-knowledge first since it landed first, separated by the file's usual bare comment line. Every earlier graduation paragraph (metadata, service-cluster) is untouched and still above them, so the two blocks' "above" references still resolve.
  2. The DEBT map.main deleted @objectstack/service-knowledge; this branch deleted @objectstack/service-automation; git presented the two deletions as one conflict because they were adjacent. Both entries are gone in the resolution, and cloud-connection, hono, observability, service-storage and spec-monorepo are byte-identical to main.

scripts/check-type-source-resolution.mjs auto-merged — the two new registry entries sit in different rows — and both @objectstack/service-automation and @objectstack/service-knowledge are present in the merged file, alongside service-cluster and service-i18n.

pnpm-lock.yaml was not hand-merged. Git's union of the two tsx devDependency additions was checked against the repo's own tooling: pnpm install on the merged manifest set left the file untouched (git status --porcelain -- pnpm-lock.yaml empty afterwards), and pnpm install --frozen-lockfile then exits 0.

Recomputed numbers — these supersede the pre-merge figures in the body above

The service-knowledge graduation moved every absolute this PR's provenance block quoted, and a merge does not reconcile prose. Re-measured with --list on the merged tree, varying only what the typecheck script names. Each mutation was proven on disk by comparing git hash-object against the HEAD blob before the run, and each restore by git checkout HEAD -- ... plus an empty git diff HEAD:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

before 59/78 packages, 120 programs, 293 pairs, 19 clean → after 60/78, 121, 302, 18 clean, i.e. +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else — the deltas the block actually claims are unchanged; only the absolutes moved. Commit c6d25b73 writes exactly these into this entry's own doc-block and says which merge moved them. The service-cluster, service-i18n and service-knowledge blocks keep their own historical readings untouched.

The DEBT ledger summary line moved for the same reason — two packages graduated where the reviewed round had one:

check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root),
5 in the DEBT ledger (104 frozen raw errors, …), 1 exempt.

(The body above reads 6 in DEBT / 114 frozen; that was true of the pre-merge tree.)

Gates re-run on the merged head c6d25b73

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff (7 paths, merge base 919beca43) → 64 commands. Every exit code captured redirect-then-read, never across a pipe; verdict lines read rather than bare status. 62 exit 0, 2 NOT MEASURED (exit 3), 0 findings.

  • pnpm check:type-check-coverage — exit 0, quoted above; its --self-test leg reports 48 semantic + 68 observation + 45 re-measure + 28 built-closure + 19 auto-lowering + 18 exit-code cases holding.
  • pnpm check:type-source-resolution — exit 0: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned …; 60 registered as still resolving a workspace dep's types through dist/. (self-test leg OK.)
  • pnpm --filter @objectstack/service-automation typecheck — exit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s) / 0 pinned signature(s).
  • pnpm --filter @objectstack/service-automation test — exit 0: 103 test files / 1225 tests passed, identical to the reviewed round.
  • pnpm check:nul-bytes — exit 0, plus a hand scan of both edited scripts for raw control bytes: no matches.

NOT MEASURED, quoting each gate's own verdict rather than a bare status:

  • pnpm check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. This is NOT a pass: nothing was measured."
  • pnpm check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk … Build the closure first, exactly as lint.yml does before this step." Its --self-test leg ran and passed before the refusal.

Both want a full-workspace build, which is CI's job rather than a per-package dependency closure — the same two NOT MEASURED results the reviewed round recorded, unchanged in kind by the merge.

One further note for the record: node scripts/check-plugin-teardown-shape.mjs --self-test exited 1 once inside the batch, refusing with "cannot read the positive control" and declining to print a verdict. Re-run unhurried on the same tree it exits 0 with all 47 cases passing, and the clone is not shallow and does hold the pinned fixture commit — a transient read under load, not a finding.


Generated by Claude Code

@os-warren
os-warren added this pull request to the merge queueSep 4, 2026
Merged via the queue into main with commit 460134aSep 4, 2026
41 checks passed
@os-warren
os-warren deleted the claude/issue-15048-service-automation-typecheck branch September 4, 2026 07:38
os-warren pushed a commit that referenced this pull request Sep 4, 2026
Resolves the third and last conflict of the #14062 family. Both siblings
have landed on main (#15147 service-knowledge, #15152 service-automation)
and all three touch the same two gate scripts.
Resolution is "keep every entry" -- the three cards do not disagree:
* scripts/check-type-check-coverage.mjs: all three graduation paragraphs
kept above `DEBT` (service-storage first, so the service-knowledge
paragraph's "the paragraph above ... for `metadata` and `service-storage`"
back-reference still resolves), and all three DEBT entries deleted. The
ledger summary line is computed at runtime from DEBT/TEST_DEBT, so no
hand-edited total exists there to reconcile.
* scripts/check-type-source-resolution.mjs: auto-merged; all three new
registry entries present, service-cluster / service-i18n untouched.
* pnpm-lock.yaml: not hand-merged. `pnpm install` over the merged manifest
set leaves the auto-merged file byte-identical (hash 000bb7d...) and
`pnpm install --frozen-lockfile` exits 0.
The diff against main is unchanged from the reviewed one: same 20 files,
same +307/-133.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
zhuangjianguo pushed a commit that referenced this pull request Sep 4, 2026
…the TS2341 x3 it hid (#15152)
* wip: onboard service-automation typecheck, fix TS2341 residue
* wip: onboarding gate registry entry + changeset
* fix(scripts): re-measure this entry's provenance totals on the merged tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tooling(services): service-automation has no typecheck script — its test layer is compiled by no tsc program (#14062 family, sibling of #14181)

3 participants

@os-sales@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid - #15152

Merged
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck
Sep 4, 2026
Merged

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid#15152
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck

Conversation

@os-sales

@os-salesos-sales commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes#15048

packages/services/service-automation had no typecheck script at all (its scripts were build and test), so no tsc program anywhere read this package. Turbo/CI typecheck lanes skipped it silently, because a zero-matching filter run exits 0. tsup transpiles with esbuild and vitest runs through esbuild type-stripping; neither type-checks.

Clause-②: no

The route, and which exemplar it was copied from

Copied from plugin-webhooks / service-cluster (#14181, PR #15032), not from plugin-auth / plugin-sharing / core, because it is the structural match. The deciding property is what the BUILD config does with tests:

packagetsconfig.json excludes tests?ledger filefits here
plugin-auth, plugin-sharingyes (**/*.test.ts in exclude)debt ledger presentno
core (#14916)yes (**/*.spec.ts, **/*.test.ts)test-typecheck-debt.json, 4 residualno
plugin-webhooks, service-clusternononeyes
service-automationno

service-automation's tsconfig.json includes the tests and always did (include: ["src"], no test exclusion), so the program that would have read them already existed and was simply never invoked. It is therefore not a package that needs an exclusion compensated for, and AGENTS.md is explicit in the other direction: "Never exclude*.test.ts / *.spec.ts from a package's tsconfig.json". So tsconfig.json is untouched, and the sibling tsconfig.test.json is the family's uniform instrument over the same files.

The sibling changes module semantics only (module: esnext, moduleResolution: bundler, lib: ES2022, matching how vitest actually executes these files). Strictness is inherited and untouched, and it declares no paths.

Measured error count, before and after

Dependency closure built first (pnpm --filter '@objectstack/service-automation^...' build), measured at 2cc4610304 (origin/main):

programbeforeafter
BUILD semantics (tsc --noEmit -p tsconfig.json, which already included the tests)30
test layer (tsc --noEmit -p tsconfig.test.json)30

555 files in the program, covering all 103 of the package's src/**/*.test.ts.

The two readings agree, and that agreement is the load-bearing result — same as service-cluster's own graduation, and unlike @objectstack/core (#14916: 98 undivided → 4 after the split, nearly all TS7006 cascading from one unresolved import). This package carried no config-tier pile at all; the 3 were genuinely code-tier from the start, well inside the dispatch's "a handful, fix them properly" branch, so no stop-and-report was owed.

The three errors, and why the fix is correct rather than convenient

All three were TS2341 ("Property 'flows' is private and only accessible within class 'AutomationEngine'"), all in src/nested-region-parity.test.ts (lines 95/151/180):

src/nested-region-parity.test.ts(95,25): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(151,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(180,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.

Three tests dot-read the class's privateflows map (engine.flows.get(name)) directly, instead of going through its own public accessor:

// src/engine.tsasyncgetFlow(name: string): Promise<FlowParsed|null>{returnthis.flows.get(name)??null;}

That accessor already exists, and it is already the idiom every other test file in this package uses (await engine.getFlow(name) — 10+ call sites across engine.test.ts, canonicalize-stored-flow.test.ts, flow-cold-boot-bind.test.ts, flow-load-conversion.test.ts, etc.). The fix is therefore not a workaround: it replaces three private-internals reads with the public surface the class already offers, matching the rest of the suite — no source signature widened, no cast, no bracket-notation trick.

- const flow = engine.flows.get('repro')!;+ const flow = (await engine.getFlow('repro'))!;
- expect((engine.flows.get('callout')!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');+ expect(((await engine.getFlow('callout'))!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');
- expect((engine.flows.get('purge')!.nodes[1]!.config as any).body.nodes[0].config)+ expect(((await engine.getFlow('purge'))!.nodes[1]!.config as any).body.nodes[0].config)

The two test bodies that were not already async were made async to await the accessor. Re-ran the file after the fix: 22/22 tests pass, unchanged behavior — the assertions test the same thing, only the read reaches it through the public door.

Ledger consequences

  • DEBT entry deleted, not lowered.check-type-check-coverage.mjs carried '@objectstack/service-automation': { errors: 3, note: 'code-tier 3 (TS2341 x3)...' }. That gate's own invariant is "covered packages must not also sit in the ledger". The entry is deleted and the graduation recorded in the file's prose. Gate now reads 5 in DEBT where it read 6 (104 frozen errors, down from 114), on the merged tree.
  • No test-typecheck-debt.json is created, and its absence is the zero: any error in any file here is red immediately, with no entry to be added to. ⛔ No ledger was grown in either direction.

⚠️check:type-source-resolution — a re-baseline, please review it as one

This gate goes red on this diff as expected (the card's own ⚠️ block flags this), and the remedy its failure text names (paths) is measurably the wrong one here too — same finding as service-cluster's own re-baseline, reproduced at larger scale because this package pulls 9 workspace deps instead of 2.

Provenance, measured four ways on one checkout, varying only what typecheck names:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

Row 2 is the load-bearing one: tsconfig.json is always a counted program per this gate's own design (programConfigsFor's doc-block), and it already includes every test file — yet it measures clean, with zero dist-resolved workspace type imports. So the exposure is only reachable through the onboarded tsconfig.test.json program, not merely first seen there. This package also had zero pre-existing programs (no typecheck script at all), so there is no program a dep could be laundered through — the same clean case service-cluster reported.

Numbers stated: before 59/78 packages, 120 programs, 293 pairs, 19 clean; after 60/78, 121, 302, 18 clean — +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else.

Why the entry and not paths, measured: redirecting the 9 deps to source takes this package's test layer from 0 errors to 648 (647 TS6059 "not under rootDir" + 1 TS6133) — all 647 of the TS6059 land in another package's source, zero in this package's own src/: packages/spec/src/** 379, packages/core/src/** 62, packages/plugins/plugin-security/src/** 60, packages/objectql/src/** 49, packages/services/service-messaging/src/** 41, packages/metadata-core/src/** 29, packages/formula/src/** 15, packages/services/service-job/src/** 6, packages/drivers/driver-sql/src/** 6. Billed to packages that cannot pay them down — the same service-cluster (#14181: 0 → 435) and PR #12570 finding, reproduced at a larger scale because this package pulls more workspace deps.

Gates

All measured figures in this section are the post-merge re-run on head c6d25b73 (merge commit 8aff199c, merge base 919beca43); the pre-merge readings they replace were taken at 6a703bf42d, and the merge-round comment on this PR records both sets side by side.

Package-level, on the merged head c6d25b73:

  • pnpm --filter @objectstack/service-automation typecheckexit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s).
  • pnpm --filter @objectstack/service-automation testexit 0: 103 test files / 1225 tests passed.
  • pnpm check:type-check-coverageexit 0: check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root), 5 in the DEBT ledger (104 frozen raw errors), 1 exempt.
  • pnpm check:type-source-resolutionexit 0 after the re-baseline: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned; 60 registered as still resolving a workspace dep's types through dist/.
  • pnpm check:nul-bytes — exit 0.

Dispatch-derived gate list (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff, final head c6d25b73), each exit code captured by redirect-then-read, never across a pipe: 64 commands — 62 exit 0, 2 exit 3 (NOT MEASURED), 0 red.

NOT MEASURED, quoting each gate's own verdict:

  • check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. ⛔ This is NOT a pass: nothing was measured."
  • check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk. … ⛔ This is NOT a pass and NOT a finding: nothing was measured."

Both read the same missing prerequisite — a full-workspace pnpm build, which is CI's job, not a per-package build closure. Three commands (check:pm-dispatch-gates, check:query-options-erasure, check:slot-lookup) needed longer than a 90s probe budget and passed on a second, unhurried run — each does a real full-tree scan, not a hang.

Both edited gate scripts' own --self-test are in that union and hold.

Declined, with reasons

Changeset

.changeset/service-automation-test-tsc-program.md, patch on @objectstack/service-automation. src/** is otherwise byte-identical (only the three private-read call sites in one test file changed, same assertions), so no shipped behaviour moves; the level reflects the published package.json gaining typecheck / check:test-typecheck scripts and a tsx devDependency.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

@github-actionsgithub-actionsBot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Sep 4, 2026
@github-actions

github-actionsBot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 919beca43b2901320cea39afd912b7f662ddd3b1packageMentionDocs.

@os-sales
os-sales marked this pull request as ready for review September 4, 2026 04:14
@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to a conflict with the base branch Sep 4, 2026
…rvice-automation-typecheck
# Conflicts:
#	scripts/check-type-check-coverage.mjs
… tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
@os-warrenClaude

Copy link
Copy Markdown
Collaborator

Merge round — conflicted against main, resolved by keeping every entry

main moved to 919beca43b: sibling PR #15147 (card #15049) landed as 8422acd, graduating @objectstack/service-knowledge out of the same two ledgers this PR edits, which put this PR in mergeable_state: dirty. Nothing about the reviewed change was reopened; the body above stands except for the figures this comment supersedes.

Merge, not rebase.git merge origin/main → merge commit 8aff199c, new head c6d25b73. No rebase, no amend, no force-push.

Conflicts, and why each resolution is "keep every entry"

One file conflicted — scripts/check-type-check-coverage.mjs, two hunks. The two sides do not disagree: each deletes a different DEBT key and records a different graduation.

  1. Graduation prose.service-knowledge's paragraph (from main) and service-automation's (from this branch) are independent paragraphs about different packages. Both kept, service-knowledge first since it landed first, separated by the file's usual bare comment line. Every earlier graduation paragraph (metadata, service-cluster) is untouched and still above them, so the two blocks' "above" references still resolve.
  2. The DEBT map.main deleted @objectstack/service-knowledge; this branch deleted @objectstack/service-automation; git presented the two deletions as one conflict because they were adjacent. Both entries are gone in the resolution, and cloud-connection, hono, observability, service-storage and spec-monorepo are byte-identical to main.

scripts/check-type-source-resolution.mjs auto-merged — the two new registry entries sit in different rows — and both @objectstack/service-automation and @objectstack/service-knowledge are present in the merged file, alongside service-cluster and service-i18n.

pnpm-lock.yaml was not hand-merged. Git's union of the two tsx devDependency additions was checked against the repo's own tooling: pnpm install on the merged manifest set left the file untouched (git status --porcelain -- pnpm-lock.yaml empty afterwards), and pnpm install --frozen-lockfile then exits 0.

Recomputed numbers — these supersede the pre-merge figures in the body above

The service-knowledge graduation moved every absolute this PR's provenance block quoted, and a merge does not reconcile prose. Re-measured with --list on the merged tree, varying only what the typecheck script names. Each mutation was proven on disk by comparing git hash-object against the HEAD blob before the run, and each restore by git checkout HEAD -- ... plus an empty git diff HEAD:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

before 59/78 packages, 120 programs, 293 pairs, 19 clean → after 60/78, 121, 302, 18 clean, i.e. +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else — the deltas the block actually claims are unchanged; only the absolutes moved. Commit c6d25b73 writes exactly these into this entry's own doc-block and says which merge moved them. The service-cluster, service-i18n and service-knowledge blocks keep their own historical readings untouched.

The DEBT ledger summary line moved for the same reason — two packages graduated where the reviewed round had one:

check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root),
5 in the DEBT ledger (104 frozen raw errors, …), 1 exempt.

(The body above reads 6 in DEBT / 114 frozen; that was true of the pre-merge tree.)

Gates re-run on the merged head c6d25b73

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff (7 paths, merge base 919beca43) → 64 commands. Every exit code captured redirect-then-read, never across a pipe; verdict lines read rather than bare status. 62 exit 0, 2 NOT MEASURED (exit 3), 0 findings.

  • pnpm check:type-check-coverage — exit 0, quoted above; its --self-test leg reports 48 semantic + 68 observation + 45 re-measure + 28 built-closure + 19 auto-lowering + 18 exit-code cases holding.
  • pnpm check:type-source-resolution — exit 0: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned …; 60 registered as still resolving a workspace dep's types through dist/. (self-test leg OK.)
  • pnpm --filter @objectstack/service-automation typecheck — exit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s) / 0 pinned signature(s).
  • pnpm --filter @objectstack/service-automation test — exit 0: 103 test files / 1225 tests passed, identical to the reviewed round.
  • pnpm check:nul-bytes — exit 0, plus a hand scan of both edited scripts for raw control bytes: no matches.

NOT MEASURED, quoting each gate's own verdict rather than a bare status:

  • pnpm check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. This is NOT a pass: nothing was measured."
  • pnpm check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk … Build the closure first, exactly as lint.yml does before this step." Its --self-test leg ran and passed before the refusal.

Both want a full-workspace build, which is CI's job rather than a per-package dependency closure — the same two NOT MEASURED results the reviewed round recorded, unchanged in kind by the merge.

One further note for the record: node scripts/check-plugin-teardown-shape.mjs --self-test exited 1 once inside the batch, refusing with "cannot read the positive control" and declining to print a verdict. Re-run unhurried on the same tree it exits 0 with all 47 cases passing, and the clone is not shallow and does hold the pinned fixture commit — a transient read under load, not a finding.


Generated by Claude Code

@os-warren
os-warren added this pull request to the merge queueSep 4, 2026
Merged via the queue into main with commit 460134aSep 4, 2026
41 checks passed
@os-warren
os-warren deleted the claude/issue-15048-service-automation-typecheck branch September 4, 2026 07:38
os-warren pushed a commit that referenced this pull request Sep 4, 2026
Resolves the third and last conflict of the #14062 family. Both siblings
have landed on main (#15147 service-knowledge, #15152 service-automation)
and all three touch the same two gate scripts.
Resolution is "keep every entry" -- the three cards do not disagree:
* scripts/check-type-check-coverage.mjs: all three graduation paragraphs
kept above `DEBT` (service-storage first, so the service-knowledge
paragraph's "the paragraph above ... for `metadata` and `service-storage`"
back-reference still resolves), and all three DEBT entries deleted. The
ledger summary line is computed at runtime from DEBT/TEST_DEBT, so no
hand-edited total exists there to reconcile.
* scripts/check-type-source-resolution.mjs: auto-merged; all three new
registry entries present, service-cluster / service-i18n untouched.
* pnpm-lock.yaml: not hand-merged. `pnpm install` over the merged manifest
set leaves the auto-merged file byte-identical (hash 000bb7d...) and
`pnpm install --frozen-lockfile` exits 0.
The diff against main is unchanged from the reviewed one: same 20 files,
same +307/-133.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
zhuangjianguo pushed a commit that referenced this pull request Sep 4, 2026
…the TS2341 x3 it hid (#15152)
* wip: onboard service-automation typecheck, fix TS2341 residue
* wip: onboarding gate registry entry + changeset
* fix(scripts): re-measure this entry's provenance totals on the merged tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tooling(services): service-automation has no typecheck script — its test layer is compiled by no tsc program (#14062 family, sibling of #14181)

3 participants

@os-sales@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid - #15152

Merged
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck
Sep 4, 2026
Merged

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid#15152
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck

Conversation

@os-sales

@os-salesos-sales commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes#15048

packages/services/service-automation had no typecheck script at all (its scripts were build and test), so no tsc program anywhere read this package. Turbo/CI typecheck lanes skipped it silently, because a zero-matching filter run exits 0. tsup transpiles with esbuild and vitest runs through esbuild type-stripping; neither type-checks.

Clause-②: no

The route, and which exemplar it was copied from

Copied from plugin-webhooks / service-cluster (#14181, PR #15032), not from plugin-auth / plugin-sharing / core, because it is the structural match. The deciding property is what the BUILD config does with tests:

packagetsconfig.json excludes tests?ledger filefits here
plugin-auth, plugin-sharingyes (**/*.test.ts in exclude)debt ledger presentno
core (#14916)yes (**/*.spec.ts, **/*.test.ts)test-typecheck-debt.json, 4 residualno
plugin-webhooks, service-clusternononeyes
service-automationno

service-automation's tsconfig.json includes the tests and always did (include: ["src"], no test exclusion), so the program that would have read them already existed and was simply never invoked. It is therefore not a package that needs an exclusion compensated for, and AGENTS.md is explicit in the other direction: "Never exclude*.test.ts / *.spec.ts from a package's tsconfig.json". So tsconfig.json is untouched, and the sibling tsconfig.test.json is the family's uniform instrument over the same files.

The sibling changes module semantics only (module: esnext, moduleResolution: bundler, lib: ES2022, matching how vitest actually executes these files). Strictness is inherited and untouched, and it declares no paths.

Measured error count, before and after

Dependency closure built first (pnpm --filter '@objectstack/service-automation^...' build), measured at 2cc4610304 (origin/main):

programbeforeafter
BUILD semantics (tsc --noEmit -p tsconfig.json, which already included the tests)30
test layer (tsc --noEmit -p tsconfig.test.json)30

555 files in the program, covering all 103 of the package's src/**/*.test.ts.

The two readings agree, and that agreement is the load-bearing result — same as service-cluster's own graduation, and unlike @objectstack/core (#14916: 98 undivided → 4 after the split, nearly all TS7006 cascading from one unresolved import). This package carried no config-tier pile at all; the 3 were genuinely code-tier from the start, well inside the dispatch's "a handful, fix them properly" branch, so no stop-and-report was owed.

The three errors, and why the fix is correct rather than convenient

All three were TS2341 ("Property 'flows' is private and only accessible within class 'AutomationEngine'"), all in src/nested-region-parity.test.ts (lines 95/151/180):

src/nested-region-parity.test.ts(95,25): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(151,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(180,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.

Three tests dot-read the class's privateflows map (engine.flows.get(name)) directly, instead of going through its own public accessor:

// src/engine.tsasyncgetFlow(name: string): Promise<FlowParsed|null>{returnthis.flows.get(name)??null;}

That accessor already exists, and it is already the idiom every other test file in this package uses (await engine.getFlow(name) — 10+ call sites across engine.test.ts, canonicalize-stored-flow.test.ts, flow-cold-boot-bind.test.ts, flow-load-conversion.test.ts, etc.). The fix is therefore not a workaround: it replaces three private-internals reads with the public surface the class already offers, matching the rest of the suite — no source signature widened, no cast, no bracket-notation trick.

- const flow = engine.flows.get('repro')!;+ const flow = (await engine.getFlow('repro'))!;
- expect((engine.flows.get('callout')!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');+ expect(((await engine.getFlow('callout'))!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');
- expect((engine.flows.get('purge')!.nodes[1]!.config as any).body.nodes[0].config)+ expect(((await engine.getFlow('purge'))!.nodes[1]!.config as any).body.nodes[0].config)

The two test bodies that were not already async were made async to await the accessor. Re-ran the file after the fix: 22/22 tests pass, unchanged behavior — the assertions test the same thing, only the read reaches it through the public door.

Ledger consequences

  • DEBT entry deleted, not lowered.check-type-check-coverage.mjs carried '@objectstack/service-automation': { errors: 3, note: 'code-tier 3 (TS2341 x3)...' }. That gate's own invariant is "covered packages must not also sit in the ledger". The entry is deleted and the graduation recorded in the file's prose. Gate now reads 5 in DEBT where it read 6 (104 frozen errors, down from 114), on the merged tree.
  • No test-typecheck-debt.json is created, and its absence is the zero: any error in any file here is red immediately, with no entry to be added to. ⛔ No ledger was grown in either direction.

⚠️check:type-source-resolution — a re-baseline, please review it as one

This gate goes red on this diff as expected (the card's own ⚠️ block flags this), and the remedy its failure text names (paths) is measurably the wrong one here too — same finding as service-cluster's own re-baseline, reproduced at larger scale because this package pulls 9 workspace deps instead of 2.

Provenance, measured four ways on one checkout, varying only what typecheck names:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

Row 2 is the load-bearing one: tsconfig.json is always a counted program per this gate's own design (programConfigsFor's doc-block), and it already includes every test file — yet it measures clean, with zero dist-resolved workspace type imports. So the exposure is only reachable through the onboarded tsconfig.test.json program, not merely first seen there. This package also had zero pre-existing programs (no typecheck script at all), so there is no program a dep could be laundered through — the same clean case service-cluster reported.

Numbers stated: before 59/78 packages, 120 programs, 293 pairs, 19 clean; after 60/78, 121, 302, 18 clean — +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else.

Why the entry and not paths, measured: redirecting the 9 deps to source takes this package's test layer from 0 errors to 648 (647 TS6059 "not under rootDir" + 1 TS6133) — all 647 of the TS6059 land in another package's source, zero in this package's own src/: packages/spec/src/** 379, packages/core/src/** 62, packages/plugins/plugin-security/src/** 60, packages/objectql/src/** 49, packages/services/service-messaging/src/** 41, packages/metadata-core/src/** 29, packages/formula/src/** 15, packages/services/service-job/src/** 6, packages/drivers/driver-sql/src/** 6. Billed to packages that cannot pay them down — the same service-cluster (#14181: 0 → 435) and PR #12570 finding, reproduced at a larger scale because this package pulls more workspace deps.

Gates

All measured figures in this section are the post-merge re-run on head c6d25b73 (merge commit 8aff199c, merge base 919beca43); the pre-merge readings they replace were taken at 6a703bf42d, and the merge-round comment on this PR records both sets side by side.

Package-level, on the merged head c6d25b73:

  • pnpm --filter @objectstack/service-automation typecheckexit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s).
  • pnpm --filter @objectstack/service-automation testexit 0: 103 test files / 1225 tests passed.
  • pnpm check:type-check-coverageexit 0: check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root), 5 in the DEBT ledger (104 frozen raw errors), 1 exempt.
  • pnpm check:type-source-resolutionexit 0 after the re-baseline: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned; 60 registered as still resolving a workspace dep's types through dist/.
  • pnpm check:nul-bytes — exit 0.

Dispatch-derived gate list (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff, final head c6d25b73), each exit code captured by redirect-then-read, never across a pipe: 64 commands — 62 exit 0, 2 exit 3 (NOT MEASURED), 0 red.

NOT MEASURED, quoting each gate's own verdict:

  • check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. ⛔ This is NOT a pass: nothing was measured."
  • check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk. … ⛔ This is NOT a pass and NOT a finding: nothing was measured."

Both read the same missing prerequisite — a full-workspace pnpm build, which is CI's job, not a per-package build closure. Three commands (check:pm-dispatch-gates, check:query-options-erasure, check:slot-lookup) needed longer than a 90s probe budget and passed on a second, unhurried run — each does a real full-tree scan, not a hang.

Both edited gate scripts' own --self-test are in that union and hold.

Declined, with reasons

Changeset

.changeset/service-automation-test-tsc-program.md, patch on @objectstack/service-automation. src/** is otherwise byte-identical (only the three private-read call sites in one test file changed, same assertions), so no shipped behaviour moves; the level reflects the published package.json gaining typecheck / check:test-typecheck scripts and a tsx devDependency.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

@github-actionsgithub-actionsBot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Sep 4, 2026
@github-actions

github-actionsBot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 919beca43b2901320cea39afd912b7f662ddd3b1packageMentionDocs.

@os-sales
os-sales marked this pull request as ready for review September 4, 2026 04:14
@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to a conflict with the base branch Sep 4, 2026
…rvice-automation-typecheck
# Conflicts:
#	scripts/check-type-check-coverage.mjs
… tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
@os-warrenClaude

Copy link
Copy Markdown
Collaborator

Merge round — conflicted against main, resolved by keeping every entry

main moved to 919beca43b: sibling PR #15147 (card #15049) landed as 8422acd, graduating @objectstack/service-knowledge out of the same two ledgers this PR edits, which put this PR in mergeable_state: dirty. Nothing about the reviewed change was reopened; the body above stands except for the figures this comment supersedes.

Merge, not rebase.git merge origin/main → merge commit 8aff199c, new head c6d25b73. No rebase, no amend, no force-push.

Conflicts, and why each resolution is "keep every entry"

One file conflicted — scripts/check-type-check-coverage.mjs, two hunks. The two sides do not disagree: each deletes a different DEBT key and records a different graduation.

  1. Graduation prose.service-knowledge's paragraph (from main) and service-automation's (from this branch) are independent paragraphs about different packages. Both kept, service-knowledge first since it landed first, separated by the file's usual bare comment line. Every earlier graduation paragraph (metadata, service-cluster) is untouched and still above them, so the two blocks' "above" references still resolve.
  2. The DEBT map.main deleted @objectstack/service-knowledge; this branch deleted @objectstack/service-automation; git presented the two deletions as one conflict because they were adjacent. Both entries are gone in the resolution, and cloud-connection, hono, observability, service-storage and spec-monorepo are byte-identical to main.

scripts/check-type-source-resolution.mjs auto-merged — the two new registry entries sit in different rows — and both @objectstack/service-automation and @objectstack/service-knowledge are present in the merged file, alongside service-cluster and service-i18n.

pnpm-lock.yaml was not hand-merged. Git's union of the two tsx devDependency additions was checked against the repo's own tooling: pnpm install on the merged manifest set left the file untouched (git status --porcelain -- pnpm-lock.yaml empty afterwards), and pnpm install --frozen-lockfile then exits 0.

Recomputed numbers — these supersede the pre-merge figures in the body above

The service-knowledge graduation moved every absolute this PR's provenance block quoted, and a merge does not reconcile prose. Re-measured with --list on the merged tree, varying only what the typecheck script names. Each mutation was proven on disk by comparing git hash-object against the HEAD blob before the run, and each restore by git checkout HEAD -- ... plus an empty git diff HEAD:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

before 59/78 packages, 120 programs, 293 pairs, 19 clean → after 60/78, 121, 302, 18 clean, i.e. +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else — the deltas the block actually claims are unchanged; only the absolutes moved. Commit c6d25b73 writes exactly these into this entry's own doc-block and says which merge moved them. The service-cluster, service-i18n and service-knowledge blocks keep their own historical readings untouched.

The DEBT ledger summary line moved for the same reason — two packages graduated where the reviewed round had one:

check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root),
5 in the DEBT ledger (104 frozen raw errors, …), 1 exempt.

(The body above reads 6 in DEBT / 114 frozen; that was true of the pre-merge tree.)

Gates re-run on the merged head c6d25b73

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff (7 paths, merge base 919beca43) → 64 commands. Every exit code captured redirect-then-read, never across a pipe; verdict lines read rather than bare status. 62 exit 0, 2 NOT MEASURED (exit 3), 0 findings.

  • pnpm check:type-check-coverage — exit 0, quoted above; its --self-test leg reports 48 semantic + 68 observation + 45 re-measure + 28 built-closure + 19 auto-lowering + 18 exit-code cases holding.
  • pnpm check:type-source-resolution — exit 0: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned …; 60 registered as still resolving a workspace dep's types through dist/. (self-test leg OK.)
  • pnpm --filter @objectstack/service-automation typecheck — exit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s) / 0 pinned signature(s).
  • pnpm --filter @objectstack/service-automation test — exit 0: 103 test files / 1225 tests passed, identical to the reviewed round.
  • pnpm check:nul-bytes — exit 0, plus a hand scan of both edited scripts for raw control bytes: no matches.

NOT MEASURED, quoting each gate's own verdict rather than a bare status:

  • pnpm check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. This is NOT a pass: nothing was measured."
  • pnpm check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk … Build the closure first, exactly as lint.yml does before this step." Its --self-test leg ran and passed before the refusal.

Both want a full-workspace build, which is CI's job rather than a per-package dependency closure — the same two NOT MEASURED results the reviewed round recorded, unchanged in kind by the merge.

One further note for the record: node scripts/check-plugin-teardown-shape.mjs --self-test exited 1 once inside the batch, refusing with "cannot read the positive control" and declining to print a verdict. Re-run unhurried on the same tree it exits 0 with all 47 cases passing, and the clone is not shallow and does hold the pinned fixture commit — a transient read under load, not a finding.


Generated by Claude Code

@os-warren
os-warren added this pull request to the merge queueSep 4, 2026
Merged via the queue into main with commit 460134aSep 4, 2026
41 checks passed
@os-warren
os-warren deleted the claude/issue-15048-service-automation-typecheck branch September 4, 2026 07:38
os-warren pushed a commit that referenced this pull request Sep 4, 2026
Resolves the third and last conflict of the #14062 family. Both siblings
have landed on main (#15147 service-knowledge, #15152 service-automation)
and all three touch the same two gate scripts.
Resolution is "keep every entry" -- the three cards do not disagree:
* scripts/check-type-check-coverage.mjs: all three graduation paragraphs
kept above `DEBT` (service-storage first, so the service-knowledge
paragraph's "the paragraph above ... for `metadata` and `service-storage`"
back-reference still resolves), and all three DEBT entries deleted. The
ledger summary line is computed at runtime from DEBT/TEST_DEBT, so no
hand-edited total exists there to reconcile.
* scripts/check-type-source-resolution.mjs: auto-merged; all three new
registry entries present, service-cluster / service-i18n untouched.
* pnpm-lock.yaml: not hand-merged. `pnpm install` over the merged manifest
set leaves the auto-merged file byte-identical (hash 000bb7d...) and
`pnpm install --frozen-lockfile` exits 0.
The diff against main is unchanged from the reviewed one: same 20 files,
same +307/-133.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
zhuangjianguo pushed a commit that referenced this pull request Sep 4, 2026
…the TS2341 x3 it hid (#15152)
* wip: onboard service-automation typecheck, fix TS2341 residue
* wip: onboarding gate registry entry + changeset
* fix(scripts): re-measure this entry's provenance totals on the merged tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tooling(services): service-automation has no typecheck script — its test layer is compiled by no tsc program (#14062 family, sibling of #14181)

3 participants

@os-sales@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid - #15152

Merged
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck
Sep 4, 2026
Merged

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid#15152
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck

Conversation

@os-sales

@os-salesos-sales commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes#15048

packages/services/service-automation had no typecheck script at all (its scripts were build and test), so no tsc program anywhere read this package. Turbo/CI typecheck lanes skipped it silently, because a zero-matching filter run exits 0. tsup transpiles with esbuild and vitest runs through esbuild type-stripping; neither type-checks.

Clause-②: no

The route, and which exemplar it was copied from

Copied from plugin-webhooks / service-cluster (#14181, PR #15032), not from plugin-auth / plugin-sharing / core, because it is the structural match. The deciding property is what the BUILD config does with tests:

packagetsconfig.json excludes tests?ledger filefits here
plugin-auth, plugin-sharingyes (**/*.test.ts in exclude)debt ledger presentno
core (#14916)yes (**/*.spec.ts, **/*.test.ts)test-typecheck-debt.json, 4 residualno
plugin-webhooks, service-clusternononeyes
service-automationno

service-automation's tsconfig.json includes the tests and always did (include: ["src"], no test exclusion), so the program that would have read them already existed and was simply never invoked. It is therefore not a package that needs an exclusion compensated for, and AGENTS.md is explicit in the other direction: "Never exclude*.test.ts / *.spec.ts from a package's tsconfig.json". So tsconfig.json is untouched, and the sibling tsconfig.test.json is the family's uniform instrument over the same files.

The sibling changes module semantics only (module: esnext, moduleResolution: bundler, lib: ES2022, matching how vitest actually executes these files). Strictness is inherited and untouched, and it declares no paths.

Measured error count, before and after

Dependency closure built first (pnpm --filter '@objectstack/service-automation^...' build), measured at 2cc4610304 (origin/main):

programbeforeafter
BUILD semantics (tsc --noEmit -p tsconfig.json, which already included the tests)30
test layer (tsc --noEmit -p tsconfig.test.json)30

555 files in the program, covering all 103 of the package's src/**/*.test.ts.

The two readings agree, and that agreement is the load-bearing result — same as service-cluster's own graduation, and unlike @objectstack/core (#14916: 98 undivided → 4 after the split, nearly all TS7006 cascading from one unresolved import). This package carried no config-tier pile at all; the 3 were genuinely code-tier from the start, well inside the dispatch's "a handful, fix them properly" branch, so no stop-and-report was owed.

The three errors, and why the fix is correct rather than convenient

All three were TS2341 ("Property 'flows' is private and only accessible within class 'AutomationEngine'"), all in src/nested-region-parity.test.ts (lines 95/151/180):

src/nested-region-parity.test.ts(95,25): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(151,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(180,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.

Three tests dot-read the class's privateflows map (engine.flows.get(name)) directly, instead of going through its own public accessor:

// src/engine.tsasyncgetFlow(name: string): Promise<FlowParsed|null>{returnthis.flows.get(name)??null;}

That accessor already exists, and it is already the idiom every other test file in this package uses (await engine.getFlow(name) — 10+ call sites across engine.test.ts, canonicalize-stored-flow.test.ts, flow-cold-boot-bind.test.ts, flow-load-conversion.test.ts, etc.). The fix is therefore not a workaround: it replaces three private-internals reads with the public surface the class already offers, matching the rest of the suite — no source signature widened, no cast, no bracket-notation trick.

- const flow = engine.flows.get('repro')!;+ const flow = (await engine.getFlow('repro'))!;
- expect((engine.flows.get('callout')!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');+ expect(((await engine.getFlow('callout'))!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');
- expect((engine.flows.get('purge')!.nodes[1]!.config as any).body.nodes[0].config)+ expect(((await engine.getFlow('purge'))!.nodes[1]!.config as any).body.nodes[0].config)

The two test bodies that were not already async were made async to await the accessor. Re-ran the file after the fix: 22/22 tests pass, unchanged behavior — the assertions test the same thing, only the read reaches it through the public door.

Ledger consequences

  • DEBT entry deleted, not lowered.check-type-check-coverage.mjs carried '@objectstack/service-automation': { errors: 3, note: 'code-tier 3 (TS2341 x3)...' }. That gate's own invariant is "covered packages must not also sit in the ledger". The entry is deleted and the graduation recorded in the file's prose. Gate now reads 5 in DEBT where it read 6 (104 frozen errors, down from 114), on the merged tree.
  • No test-typecheck-debt.json is created, and its absence is the zero: any error in any file here is red immediately, with no entry to be added to. ⛔ No ledger was grown in either direction.

⚠️check:type-source-resolution — a re-baseline, please review it as one

This gate goes red on this diff as expected (the card's own ⚠️ block flags this), and the remedy its failure text names (paths) is measurably the wrong one here too — same finding as service-cluster's own re-baseline, reproduced at larger scale because this package pulls 9 workspace deps instead of 2.

Provenance, measured four ways on one checkout, varying only what typecheck names:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

Row 2 is the load-bearing one: tsconfig.json is always a counted program per this gate's own design (programConfigsFor's doc-block), and it already includes every test file — yet it measures clean, with zero dist-resolved workspace type imports. So the exposure is only reachable through the onboarded tsconfig.test.json program, not merely first seen there. This package also had zero pre-existing programs (no typecheck script at all), so there is no program a dep could be laundered through — the same clean case service-cluster reported.

Numbers stated: before 59/78 packages, 120 programs, 293 pairs, 19 clean; after 60/78, 121, 302, 18 clean — +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else.

Why the entry and not paths, measured: redirecting the 9 deps to source takes this package's test layer from 0 errors to 648 (647 TS6059 "not under rootDir" + 1 TS6133) — all 647 of the TS6059 land in another package's source, zero in this package's own src/: packages/spec/src/** 379, packages/core/src/** 62, packages/plugins/plugin-security/src/** 60, packages/objectql/src/** 49, packages/services/service-messaging/src/** 41, packages/metadata-core/src/** 29, packages/formula/src/** 15, packages/services/service-job/src/** 6, packages/drivers/driver-sql/src/** 6. Billed to packages that cannot pay them down — the same service-cluster (#14181: 0 → 435) and PR #12570 finding, reproduced at a larger scale because this package pulls more workspace deps.

Gates

All measured figures in this section are the post-merge re-run on head c6d25b73 (merge commit 8aff199c, merge base 919beca43); the pre-merge readings they replace were taken at 6a703bf42d, and the merge-round comment on this PR records both sets side by side.

Package-level, on the merged head c6d25b73:

  • pnpm --filter @objectstack/service-automation typecheckexit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s).
  • pnpm --filter @objectstack/service-automation testexit 0: 103 test files / 1225 tests passed.
  • pnpm check:type-check-coverageexit 0: check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root), 5 in the DEBT ledger (104 frozen raw errors), 1 exempt.
  • pnpm check:type-source-resolutionexit 0 after the re-baseline: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned; 60 registered as still resolving a workspace dep's types through dist/.
  • pnpm check:nul-bytes — exit 0.

Dispatch-derived gate list (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff, final head c6d25b73), each exit code captured by redirect-then-read, never across a pipe: 64 commands — 62 exit 0, 2 exit 3 (NOT MEASURED), 0 red.

NOT MEASURED, quoting each gate's own verdict:

  • check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. ⛔ This is NOT a pass: nothing was measured."
  • check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk. … ⛔ This is NOT a pass and NOT a finding: nothing was measured."

Both read the same missing prerequisite — a full-workspace pnpm build, which is CI's job, not a per-package build closure. Three commands (check:pm-dispatch-gates, check:query-options-erasure, check:slot-lookup) needed longer than a 90s probe budget and passed on a second, unhurried run — each does a real full-tree scan, not a hang.

Both edited gate scripts' own --self-test are in that union and hold.

Declined, with reasons

Changeset

.changeset/service-automation-test-tsc-program.md, patch on @objectstack/service-automation. src/** is otherwise byte-identical (only the three private-read call sites in one test file changed, same assertions), so no shipped behaviour moves; the level reflects the published package.json gaining typecheck / check:test-typecheck scripts and a tsx devDependency.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

@github-actionsgithub-actionsBot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Sep 4, 2026
@github-actions

github-actionsBot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 919beca43b2901320cea39afd912b7f662ddd3b1packageMentionDocs.

@os-sales
os-sales marked this pull request as ready for review September 4, 2026 04:14
@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to a conflict with the base branch Sep 4, 2026
…rvice-automation-typecheck
# Conflicts:
#	scripts/check-type-check-coverage.mjs
… tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
@os-warrenClaude

Copy link
Copy Markdown
Collaborator

Merge round — conflicted against main, resolved by keeping every entry

main moved to 919beca43b: sibling PR #15147 (card #15049) landed as 8422acd, graduating @objectstack/service-knowledge out of the same two ledgers this PR edits, which put this PR in mergeable_state: dirty. Nothing about the reviewed change was reopened; the body above stands except for the figures this comment supersedes.

Merge, not rebase.git merge origin/main → merge commit 8aff199c, new head c6d25b73. No rebase, no amend, no force-push.

Conflicts, and why each resolution is "keep every entry"

One file conflicted — scripts/check-type-check-coverage.mjs, two hunks. The two sides do not disagree: each deletes a different DEBT key and records a different graduation.

  1. Graduation prose.service-knowledge's paragraph (from main) and service-automation's (from this branch) are independent paragraphs about different packages. Both kept, service-knowledge first since it landed first, separated by the file's usual bare comment line. Every earlier graduation paragraph (metadata, service-cluster) is untouched and still above them, so the two blocks' "above" references still resolve.
  2. The DEBT map.main deleted @objectstack/service-knowledge; this branch deleted @objectstack/service-automation; git presented the two deletions as one conflict because they were adjacent. Both entries are gone in the resolution, and cloud-connection, hono, observability, service-storage and spec-monorepo are byte-identical to main.

scripts/check-type-source-resolution.mjs auto-merged — the two new registry entries sit in different rows — and both @objectstack/service-automation and @objectstack/service-knowledge are present in the merged file, alongside service-cluster and service-i18n.

pnpm-lock.yaml was not hand-merged. Git's union of the two tsx devDependency additions was checked against the repo's own tooling: pnpm install on the merged manifest set left the file untouched (git status --porcelain -- pnpm-lock.yaml empty afterwards), and pnpm install --frozen-lockfile then exits 0.

Recomputed numbers — these supersede the pre-merge figures in the body above

The service-knowledge graduation moved every absolute this PR's provenance block quoted, and a merge does not reconcile prose. Re-measured with --list on the merged tree, varying only what the typecheck script names. Each mutation was proven on disk by comparing git hash-object against the HEAD blob before the run, and each restore by git checkout HEAD -- ... plus an empty git diff HEAD:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

before 59/78 packages, 120 programs, 293 pairs, 19 clean → after 60/78, 121, 302, 18 clean, i.e. +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else — the deltas the block actually claims are unchanged; only the absolutes moved. Commit c6d25b73 writes exactly these into this entry's own doc-block and says which merge moved them. The service-cluster, service-i18n and service-knowledge blocks keep their own historical readings untouched.

The DEBT ledger summary line moved for the same reason — two packages graduated where the reviewed round had one:

check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root),
5 in the DEBT ledger (104 frozen raw errors, …), 1 exempt.

(The body above reads 6 in DEBT / 114 frozen; that was true of the pre-merge tree.)

Gates re-run on the merged head c6d25b73

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff (7 paths, merge base 919beca43) → 64 commands. Every exit code captured redirect-then-read, never across a pipe; verdict lines read rather than bare status. 62 exit 0, 2 NOT MEASURED (exit 3), 0 findings.

  • pnpm check:type-check-coverage — exit 0, quoted above; its --self-test leg reports 48 semantic + 68 observation + 45 re-measure + 28 built-closure + 19 auto-lowering + 18 exit-code cases holding.
  • pnpm check:type-source-resolution — exit 0: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned …; 60 registered as still resolving a workspace dep's types through dist/. (self-test leg OK.)
  • pnpm --filter @objectstack/service-automation typecheck — exit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s) / 0 pinned signature(s).
  • pnpm --filter @objectstack/service-automation test — exit 0: 103 test files / 1225 tests passed, identical to the reviewed round.
  • pnpm check:nul-bytes — exit 0, plus a hand scan of both edited scripts for raw control bytes: no matches.

NOT MEASURED, quoting each gate's own verdict rather than a bare status:

  • pnpm check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. This is NOT a pass: nothing was measured."
  • pnpm check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk … Build the closure first, exactly as lint.yml does before this step." Its --self-test leg ran and passed before the refusal.

Both want a full-workspace build, which is CI's job rather than a per-package dependency closure — the same two NOT MEASURED results the reviewed round recorded, unchanged in kind by the merge.

One further note for the record: node scripts/check-plugin-teardown-shape.mjs --self-test exited 1 once inside the batch, refusing with "cannot read the positive control" and declining to print a verdict. Re-run unhurried on the same tree it exits 0 with all 47 cases passing, and the clone is not shallow and does hold the pinned fixture commit — a transient read under load, not a finding.


Generated by Claude Code

@os-warren
os-warren added this pull request to the merge queueSep 4, 2026
Merged via the queue into main with commit 460134aSep 4, 2026
41 checks passed
@os-warren
os-warren deleted the claude/issue-15048-service-automation-typecheck branch September 4, 2026 07:38
os-warren pushed a commit that referenced this pull request Sep 4, 2026
Resolves the third and last conflict of the #14062 family. Both siblings
have landed on main (#15147 service-knowledge, #15152 service-automation)
and all three touch the same two gate scripts.
Resolution is "keep every entry" -- the three cards do not disagree:
* scripts/check-type-check-coverage.mjs: all three graduation paragraphs
kept above `DEBT` (service-storage first, so the service-knowledge
paragraph's "the paragraph above ... for `metadata` and `service-storage`"
back-reference still resolves), and all three DEBT entries deleted. The
ledger summary line is computed at runtime from DEBT/TEST_DEBT, so no
hand-edited total exists there to reconcile.
* scripts/check-type-source-resolution.mjs: auto-merged; all three new
registry entries present, service-cluster / service-i18n untouched.
* pnpm-lock.yaml: not hand-merged. `pnpm install` over the merged manifest
set leaves the auto-merged file byte-identical (hash 000bb7d...) and
`pnpm install --frozen-lockfile` exits 0.
The diff against main is unchanged from the reviewed one: same 20 files,
same +307/-133.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
zhuangjianguo pushed a commit that referenced this pull request Sep 4, 2026
…the TS2341 x3 it hid (#15152)
* wip: onboard service-automation typecheck, fix TS2341 residue
* wip: onboarding gate registry entry + changeset
* fix(scripts): re-measure this entry's provenance totals on the merged tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tooling(services): service-automation has no typecheck script — its test layer is compiled by no tsc program (#14062 family, sibling of #14181)

3 participants

@os-sales@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid - #15152

Merged
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck
Sep 4, 2026
Merged

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid#15152
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck

Conversation

@os-sales

@os-salesos-sales commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes#15048

packages/services/service-automation had no typecheck script at all (its scripts were build and test), so no tsc program anywhere read this package. Turbo/CI typecheck lanes skipped it silently, because a zero-matching filter run exits 0. tsup transpiles with esbuild and vitest runs through esbuild type-stripping; neither type-checks.

Clause-②: no

The route, and which exemplar it was copied from

Copied from plugin-webhooks / service-cluster (#14181, PR #15032), not from plugin-auth / plugin-sharing / core, because it is the structural match. The deciding property is what the BUILD config does with tests:

packagetsconfig.json excludes tests?ledger filefits here
plugin-auth, plugin-sharingyes (**/*.test.ts in exclude)debt ledger presentno
core (#14916)yes (**/*.spec.ts, **/*.test.ts)test-typecheck-debt.json, 4 residualno
plugin-webhooks, service-clusternononeyes
service-automationno

service-automation's tsconfig.json includes the tests and always did (include: ["src"], no test exclusion), so the program that would have read them already existed and was simply never invoked. It is therefore not a package that needs an exclusion compensated for, and AGENTS.md is explicit in the other direction: "Never exclude*.test.ts / *.spec.ts from a package's tsconfig.json". So tsconfig.json is untouched, and the sibling tsconfig.test.json is the family's uniform instrument over the same files.

The sibling changes module semantics only (module: esnext, moduleResolution: bundler, lib: ES2022, matching how vitest actually executes these files). Strictness is inherited and untouched, and it declares no paths.

Measured error count, before and after

Dependency closure built first (pnpm --filter '@objectstack/service-automation^...' build), measured at 2cc4610304 (origin/main):

programbeforeafter
BUILD semantics (tsc --noEmit -p tsconfig.json, which already included the tests)30
test layer (tsc --noEmit -p tsconfig.test.json)30

555 files in the program, covering all 103 of the package's src/**/*.test.ts.

The two readings agree, and that agreement is the load-bearing result — same as service-cluster's own graduation, and unlike @objectstack/core (#14916: 98 undivided → 4 after the split, nearly all TS7006 cascading from one unresolved import). This package carried no config-tier pile at all; the 3 were genuinely code-tier from the start, well inside the dispatch's "a handful, fix them properly" branch, so no stop-and-report was owed.

The three errors, and why the fix is correct rather than convenient

All three were TS2341 ("Property 'flows' is private and only accessible within class 'AutomationEngine'"), all in src/nested-region-parity.test.ts (lines 95/151/180):

src/nested-region-parity.test.ts(95,25): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(151,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(180,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.

Three tests dot-read the class's privateflows map (engine.flows.get(name)) directly, instead of going through its own public accessor:

// src/engine.tsasyncgetFlow(name: string): Promise<FlowParsed|null>{returnthis.flows.get(name)??null;}

That accessor already exists, and it is already the idiom every other test file in this package uses (await engine.getFlow(name) — 10+ call sites across engine.test.ts, canonicalize-stored-flow.test.ts, flow-cold-boot-bind.test.ts, flow-load-conversion.test.ts, etc.). The fix is therefore not a workaround: it replaces three private-internals reads with the public surface the class already offers, matching the rest of the suite — no source signature widened, no cast, no bracket-notation trick.

- const flow = engine.flows.get('repro')!;+ const flow = (await engine.getFlow('repro'))!;
- expect((engine.flows.get('callout')!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');+ expect(((await engine.getFlow('callout'))!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');
- expect((engine.flows.get('purge')!.nodes[1]!.config as any).body.nodes[0].config)+ expect(((await engine.getFlow('purge'))!.nodes[1]!.config as any).body.nodes[0].config)

The two test bodies that were not already async were made async to await the accessor. Re-ran the file after the fix: 22/22 tests pass, unchanged behavior — the assertions test the same thing, only the read reaches it through the public door.

Ledger consequences

  • DEBT entry deleted, not lowered.check-type-check-coverage.mjs carried '@objectstack/service-automation': { errors: 3, note: 'code-tier 3 (TS2341 x3)...' }. That gate's own invariant is "covered packages must not also sit in the ledger". The entry is deleted and the graduation recorded in the file's prose. Gate now reads 5 in DEBT where it read 6 (104 frozen errors, down from 114), on the merged tree.
  • No test-typecheck-debt.json is created, and its absence is the zero: any error in any file here is red immediately, with no entry to be added to. ⛔ No ledger was grown in either direction.

⚠️check:type-source-resolution — a re-baseline, please review it as one

This gate goes red on this diff as expected (the card's own ⚠️ block flags this), and the remedy its failure text names (paths) is measurably the wrong one here too — same finding as service-cluster's own re-baseline, reproduced at larger scale because this package pulls 9 workspace deps instead of 2.

Provenance, measured four ways on one checkout, varying only what typecheck names:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

Row 2 is the load-bearing one: tsconfig.json is always a counted program per this gate's own design (programConfigsFor's doc-block), and it already includes every test file — yet it measures clean, with zero dist-resolved workspace type imports. So the exposure is only reachable through the onboarded tsconfig.test.json program, not merely first seen there. This package also had zero pre-existing programs (no typecheck script at all), so there is no program a dep could be laundered through — the same clean case service-cluster reported.

Numbers stated: before 59/78 packages, 120 programs, 293 pairs, 19 clean; after 60/78, 121, 302, 18 clean — +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else.

Why the entry and not paths, measured: redirecting the 9 deps to source takes this package's test layer from 0 errors to 648 (647 TS6059 "not under rootDir" + 1 TS6133) — all 647 of the TS6059 land in another package's source, zero in this package's own src/: packages/spec/src/** 379, packages/core/src/** 62, packages/plugins/plugin-security/src/** 60, packages/objectql/src/** 49, packages/services/service-messaging/src/** 41, packages/metadata-core/src/** 29, packages/formula/src/** 15, packages/services/service-job/src/** 6, packages/drivers/driver-sql/src/** 6. Billed to packages that cannot pay them down — the same service-cluster (#14181: 0 → 435) and PR #12570 finding, reproduced at a larger scale because this package pulls more workspace deps.

Gates

All measured figures in this section are the post-merge re-run on head c6d25b73 (merge commit 8aff199c, merge base 919beca43); the pre-merge readings they replace were taken at 6a703bf42d, and the merge-round comment on this PR records both sets side by side.

Package-level, on the merged head c6d25b73:

  • pnpm --filter @objectstack/service-automation typecheckexit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s).
  • pnpm --filter @objectstack/service-automation testexit 0: 103 test files / 1225 tests passed.
  • pnpm check:type-check-coverageexit 0: check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root), 5 in the DEBT ledger (104 frozen raw errors), 1 exempt.
  • pnpm check:type-source-resolutionexit 0 after the re-baseline: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned; 60 registered as still resolving a workspace dep's types through dist/.
  • pnpm check:nul-bytes — exit 0.

Dispatch-derived gate list (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff, final head c6d25b73), each exit code captured by redirect-then-read, never across a pipe: 64 commands — 62 exit 0, 2 exit 3 (NOT MEASURED), 0 red.

NOT MEASURED, quoting each gate's own verdict:

  • check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. ⛔ This is NOT a pass: nothing was measured."
  • check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk. … ⛔ This is NOT a pass and NOT a finding: nothing was measured."

Both read the same missing prerequisite — a full-workspace pnpm build, which is CI's job, not a per-package build closure. Three commands (check:pm-dispatch-gates, check:query-options-erasure, check:slot-lookup) needed longer than a 90s probe budget and passed on a second, unhurried run — each does a real full-tree scan, not a hang.

Both edited gate scripts' own --self-test are in that union and hold.

Declined, with reasons

Changeset

.changeset/service-automation-test-tsc-program.md, patch on @objectstack/service-automation. src/** is otherwise byte-identical (only the three private-read call sites in one test file changed, same assertions), so no shipped behaviour moves; the level reflects the published package.json gaining typecheck / check:test-typecheck scripts and a tsx devDependency.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

@github-actionsgithub-actionsBot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Sep 4, 2026
@github-actions

github-actionsBot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 919beca43b2901320cea39afd912b7f662ddd3b1packageMentionDocs.

@os-sales
os-sales marked this pull request as ready for review September 4, 2026 04:14
@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to a conflict with the base branch Sep 4, 2026
…rvice-automation-typecheck
# Conflicts:
#	scripts/check-type-check-coverage.mjs
… tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
@os-warrenClaude

Copy link
Copy Markdown
Collaborator

Merge round — conflicted against main, resolved by keeping every entry

main moved to 919beca43b: sibling PR #15147 (card #15049) landed as 8422acd, graduating @objectstack/service-knowledge out of the same two ledgers this PR edits, which put this PR in mergeable_state: dirty. Nothing about the reviewed change was reopened; the body above stands except for the figures this comment supersedes.

Merge, not rebase.git merge origin/main → merge commit 8aff199c, new head c6d25b73. No rebase, no amend, no force-push.

Conflicts, and why each resolution is "keep every entry"

One file conflicted — scripts/check-type-check-coverage.mjs, two hunks. The two sides do not disagree: each deletes a different DEBT key and records a different graduation.

  1. Graduation prose.service-knowledge's paragraph (from main) and service-automation's (from this branch) are independent paragraphs about different packages. Both kept, service-knowledge first since it landed first, separated by the file's usual bare comment line. Every earlier graduation paragraph (metadata, service-cluster) is untouched and still above them, so the two blocks' "above" references still resolve.
  2. The DEBT map.main deleted @objectstack/service-knowledge; this branch deleted @objectstack/service-automation; git presented the two deletions as one conflict because they were adjacent. Both entries are gone in the resolution, and cloud-connection, hono, observability, service-storage and spec-monorepo are byte-identical to main.

scripts/check-type-source-resolution.mjs auto-merged — the two new registry entries sit in different rows — and both @objectstack/service-automation and @objectstack/service-knowledge are present in the merged file, alongside service-cluster and service-i18n.

pnpm-lock.yaml was not hand-merged. Git's union of the two tsx devDependency additions was checked against the repo's own tooling: pnpm install on the merged manifest set left the file untouched (git status --porcelain -- pnpm-lock.yaml empty afterwards), and pnpm install --frozen-lockfile then exits 0.

Recomputed numbers — these supersede the pre-merge figures in the body above

The service-knowledge graduation moved every absolute this PR's provenance block quoted, and a merge does not reconcile prose. Re-measured with --list on the merged tree, varying only what the typecheck script names. Each mutation was proven on disk by comparing git hash-object against the HEAD blob before the run, and each restore by git checkout HEAD -- ... plus an empty git diff HEAD:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

before 59/78 packages, 120 programs, 293 pairs, 19 clean → after 60/78, 121, 302, 18 clean, i.e. +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else — the deltas the block actually claims are unchanged; only the absolutes moved. Commit c6d25b73 writes exactly these into this entry's own doc-block and says which merge moved them. The service-cluster, service-i18n and service-knowledge blocks keep their own historical readings untouched.

The DEBT ledger summary line moved for the same reason — two packages graduated where the reviewed round had one:

check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root),
5 in the DEBT ledger (104 frozen raw errors, …), 1 exempt.

(The body above reads 6 in DEBT / 114 frozen; that was true of the pre-merge tree.)

Gates re-run on the merged head c6d25b73

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff (7 paths, merge base 919beca43) → 64 commands. Every exit code captured redirect-then-read, never across a pipe; verdict lines read rather than bare status. 62 exit 0, 2 NOT MEASURED (exit 3), 0 findings.

  • pnpm check:type-check-coverage — exit 0, quoted above; its --self-test leg reports 48 semantic + 68 observation + 45 re-measure + 28 built-closure + 19 auto-lowering + 18 exit-code cases holding.
  • pnpm check:type-source-resolution — exit 0: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned …; 60 registered as still resolving a workspace dep's types through dist/. (self-test leg OK.)
  • pnpm --filter @objectstack/service-automation typecheck — exit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s) / 0 pinned signature(s).
  • pnpm --filter @objectstack/service-automation test — exit 0: 103 test files / 1225 tests passed, identical to the reviewed round.
  • pnpm check:nul-bytes — exit 0, plus a hand scan of both edited scripts for raw control bytes: no matches.

NOT MEASURED, quoting each gate's own verdict rather than a bare status:

  • pnpm check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. This is NOT a pass: nothing was measured."
  • pnpm check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk … Build the closure first, exactly as lint.yml does before this step." Its --self-test leg ran and passed before the refusal.

Both want a full-workspace build, which is CI's job rather than a per-package dependency closure — the same two NOT MEASURED results the reviewed round recorded, unchanged in kind by the merge.

One further note for the record: node scripts/check-plugin-teardown-shape.mjs --self-test exited 1 once inside the batch, refusing with "cannot read the positive control" and declining to print a verdict. Re-run unhurried on the same tree it exits 0 with all 47 cases passing, and the clone is not shallow and does hold the pinned fixture commit — a transient read under load, not a finding.


Generated by Claude Code

@os-warren
os-warren added this pull request to the merge queueSep 4, 2026
Merged via the queue into main with commit 460134aSep 4, 2026
41 checks passed
@os-warren
os-warren deleted the claude/issue-15048-service-automation-typecheck branch September 4, 2026 07:38
os-warren pushed a commit that referenced this pull request Sep 4, 2026
Resolves the third and last conflict of the #14062 family. Both siblings
have landed on main (#15147 service-knowledge, #15152 service-automation)
and all three touch the same two gate scripts.
Resolution is "keep every entry" -- the three cards do not disagree:
* scripts/check-type-check-coverage.mjs: all three graduation paragraphs
kept above `DEBT` (service-storage first, so the service-knowledge
paragraph's "the paragraph above ... for `metadata` and `service-storage`"
back-reference still resolves), and all three DEBT entries deleted. The
ledger summary line is computed at runtime from DEBT/TEST_DEBT, so no
hand-edited total exists there to reconcile.
* scripts/check-type-source-resolution.mjs: auto-merged; all three new
registry entries present, service-cluster / service-i18n untouched.
* pnpm-lock.yaml: not hand-merged. `pnpm install` over the merged manifest
set leaves the auto-merged file byte-identical (hash 000bb7d...) and
`pnpm install --frozen-lockfile` exits 0.
The diff against main is unchanged from the reviewed one: same 20 files,
same +307/-133.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
zhuangjianguo pushed a commit that referenced this pull request Sep 4, 2026
…the TS2341 x3 it hid (#15152)
* wip: onboard service-automation typecheck, fix TS2341 residue
* wip: onboarding gate registry entry + changeset
* fix(scripts): re-measure this entry's provenance totals on the merged tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tooling(services): service-automation has no typecheck script — its test layer is compiled by no tsc program (#14062 family, sibling of #14181)

3 participants

@os-sales@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid - #15152

Merged
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck
Sep 4, 2026
Merged

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid#15152
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck

Conversation

@os-sales

@os-salesos-sales commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes#15048

packages/services/service-automation had no typecheck script at all (its scripts were build and test), so no tsc program anywhere read this package. Turbo/CI typecheck lanes skipped it silently, because a zero-matching filter run exits 0. tsup transpiles with esbuild and vitest runs through esbuild type-stripping; neither type-checks.

Clause-②: no

The route, and which exemplar it was copied from

Copied from plugin-webhooks / service-cluster (#14181, PR #15032), not from plugin-auth / plugin-sharing / core, because it is the structural match. The deciding property is what the BUILD config does with tests:

packagetsconfig.json excludes tests?ledger filefits here
plugin-auth, plugin-sharingyes (**/*.test.ts in exclude)debt ledger presentno
core (#14916)yes (**/*.spec.ts, **/*.test.ts)test-typecheck-debt.json, 4 residualno
plugin-webhooks, service-clusternononeyes
service-automationno

service-automation's tsconfig.json includes the tests and always did (include: ["src"], no test exclusion), so the program that would have read them already existed and was simply never invoked. It is therefore not a package that needs an exclusion compensated for, and AGENTS.md is explicit in the other direction: "Never exclude*.test.ts / *.spec.ts from a package's tsconfig.json". So tsconfig.json is untouched, and the sibling tsconfig.test.json is the family's uniform instrument over the same files.

The sibling changes module semantics only (module: esnext, moduleResolution: bundler, lib: ES2022, matching how vitest actually executes these files). Strictness is inherited and untouched, and it declares no paths.

Measured error count, before and after

Dependency closure built first (pnpm --filter '@objectstack/service-automation^...' build), measured at 2cc4610304 (origin/main):

programbeforeafter
BUILD semantics (tsc --noEmit -p tsconfig.json, which already included the tests)30
test layer (tsc --noEmit -p tsconfig.test.json)30

555 files in the program, covering all 103 of the package's src/**/*.test.ts.

The two readings agree, and that agreement is the load-bearing result — same as service-cluster's own graduation, and unlike @objectstack/core (#14916: 98 undivided → 4 after the split, nearly all TS7006 cascading from one unresolved import). This package carried no config-tier pile at all; the 3 were genuinely code-tier from the start, well inside the dispatch's "a handful, fix them properly" branch, so no stop-and-report was owed.

The three errors, and why the fix is correct rather than convenient

All three were TS2341 ("Property 'flows' is private and only accessible within class 'AutomationEngine'"), all in src/nested-region-parity.test.ts (lines 95/151/180):

src/nested-region-parity.test.ts(95,25): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(151,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(180,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.

Three tests dot-read the class's privateflows map (engine.flows.get(name)) directly, instead of going through its own public accessor:

// src/engine.tsasyncgetFlow(name: string): Promise<FlowParsed|null>{returnthis.flows.get(name)??null;}

That accessor already exists, and it is already the idiom every other test file in this package uses (await engine.getFlow(name) — 10+ call sites across engine.test.ts, canonicalize-stored-flow.test.ts, flow-cold-boot-bind.test.ts, flow-load-conversion.test.ts, etc.). The fix is therefore not a workaround: it replaces three private-internals reads with the public surface the class already offers, matching the rest of the suite — no source signature widened, no cast, no bracket-notation trick.

- const flow = engine.flows.get('repro')!;+ const flow = (await engine.getFlow('repro'))!;
- expect((engine.flows.get('callout')!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');+ expect(((await engine.getFlow('callout'))!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');
- expect((engine.flows.get('purge')!.nodes[1]!.config as any).body.nodes[0].config)+ expect(((await engine.getFlow('purge'))!.nodes[1]!.config as any).body.nodes[0].config)

The two test bodies that were not already async were made async to await the accessor. Re-ran the file after the fix: 22/22 tests pass, unchanged behavior — the assertions test the same thing, only the read reaches it through the public door.

Ledger consequences

  • DEBT entry deleted, not lowered.check-type-check-coverage.mjs carried '@objectstack/service-automation': { errors: 3, note: 'code-tier 3 (TS2341 x3)...' }. That gate's own invariant is "covered packages must not also sit in the ledger". The entry is deleted and the graduation recorded in the file's prose. Gate now reads 5 in DEBT where it read 6 (104 frozen errors, down from 114), on the merged tree.
  • No test-typecheck-debt.json is created, and its absence is the zero: any error in any file here is red immediately, with no entry to be added to. ⛔ No ledger was grown in either direction.

⚠️check:type-source-resolution — a re-baseline, please review it as one

This gate goes red on this diff as expected (the card's own ⚠️ block flags this), and the remedy its failure text names (paths) is measurably the wrong one here too — same finding as service-cluster's own re-baseline, reproduced at larger scale because this package pulls 9 workspace deps instead of 2.

Provenance, measured four ways on one checkout, varying only what typecheck names:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

Row 2 is the load-bearing one: tsconfig.json is always a counted program per this gate's own design (programConfigsFor's doc-block), and it already includes every test file — yet it measures clean, with zero dist-resolved workspace type imports. So the exposure is only reachable through the onboarded tsconfig.test.json program, not merely first seen there. This package also had zero pre-existing programs (no typecheck script at all), so there is no program a dep could be laundered through — the same clean case service-cluster reported.

Numbers stated: before 59/78 packages, 120 programs, 293 pairs, 19 clean; after 60/78, 121, 302, 18 clean — +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else.

Why the entry and not paths, measured: redirecting the 9 deps to source takes this package's test layer from 0 errors to 648 (647 TS6059 "not under rootDir" + 1 TS6133) — all 647 of the TS6059 land in another package's source, zero in this package's own src/: packages/spec/src/** 379, packages/core/src/** 62, packages/plugins/plugin-security/src/** 60, packages/objectql/src/** 49, packages/services/service-messaging/src/** 41, packages/metadata-core/src/** 29, packages/formula/src/** 15, packages/services/service-job/src/** 6, packages/drivers/driver-sql/src/** 6. Billed to packages that cannot pay them down — the same service-cluster (#14181: 0 → 435) and PR #12570 finding, reproduced at a larger scale because this package pulls more workspace deps.

Gates

All measured figures in this section are the post-merge re-run on head c6d25b73 (merge commit 8aff199c, merge base 919beca43); the pre-merge readings they replace were taken at 6a703bf42d, and the merge-round comment on this PR records both sets side by side.

Package-level, on the merged head c6d25b73:

  • pnpm --filter @objectstack/service-automation typecheckexit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s).
  • pnpm --filter @objectstack/service-automation testexit 0: 103 test files / 1225 tests passed.
  • pnpm check:type-check-coverageexit 0: check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root), 5 in the DEBT ledger (104 frozen raw errors), 1 exempt.
  • pnpm check:type-source-resolutionexit 0 after the re-baseline: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned; 60 registered as still resolving a workspace dep's types through dist/.
  • pnpm check:nul-bytes — exit 0.

Dispatch-derived gate list (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff, final head c6d25b73), each exit code captured by redirect-then-read, never across a pipe: 64 commands — 62 exit 0, 2 exit 3 (NOT MEASURED), 0 red.

NOT MEASURED, quoting each gate's own verdict:

  • check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. ⛔ This is NOT a pass: nothing was measured."
  • check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk. … ⛔ This is NOT a pass and NOT a finding: nothing was measured."

Both read the same missing prerequisite — a full-workspace pnpm build, which is CI's job, not a per-package build closure. Three commands (check:pm-dispatch-gates, check:query-options-erasure, check:slot-lookup) needed longer than a 90s probe budget and passed on a second, unhurried run — each does a real full-tree scan, not a hang.

Both edited gate scripts' own --self-test are in that union and hold.

Declined, with reasons

Changeset

.changeset/service-automation-test-tsc-program.md, patch on @objectstack/service-automation. src/** is otherwise byte-identical (only the three private-read call sites in one test file changed, same assertions), so no shipped behaviour moves; the level reflects the published package.json gaining typecheck / check:test-typecheck scripts and a tsx devDependency.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

@github-actionsgithub-actionsBot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Sep 4, 2026
@github-actions

github-actionsBot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 919beca43b2901320cea39afd912b7f662ddd3b1packageMentionDocs.

@os-sales
os-sales marked this pull request as ready for review September 4, 2026 04:14
@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to a conflict with the base branch Sep 4, 2026
…rvice-automation-typecheck
# Conflicts:
#	scripts/check-type-check-coverage.mjs
… tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
@os-warrenClaude

Copy link
Copy Markdown
Collaborator

Merge round — conflicted against main, resolved by keeping every entry

main moved to 919beca43b: sibling PR #15147 (card #15049) landed as 8422acd, graduating @objectstack/service-knowledge out of the same two ledgers this PR edits, which put this PR in mergeable_state: dirty. Nothing about the reviewed change was reopened; the body above stands except for the figures this comment supersedes.

Merge, not rebase.git merge origin/main → merge commit 8aff199c, new head c6d25b73. No rebase, no amend, no force-push.

Conflicts, and why each resolution is "keep every entry"

One file conflicted — scripts/check-type-check-coverage.mjs, two hunks. The two sides do not disagree: each deletes a different DEBT key and records a different graduation.

  1. Graduation prose.service-knowledge's paragraph (from main) and service-automation's (from this branch) are independent paragraphs about different packages. Both kept, service-knowledge first since it landed first, separated by the file's usual bare comment line. Every earlier graduation paragraph (metadata, service-cluster) is untouched and still above them, so the two blocks' "above" references still resolve.
  2. The DEBT map.main deleted @objectstack/service-knowledge; this branch deleted @objectstack/service-automation; git presented the two deletions as one conflict because they were adjacent. Both entries are gone in the resolution, and cloud-connection, hono, observability, service-storage and spec-monorepo are byte-identical to main.

scripts/check-type-source-resolution.mjs auto-merged — the two new registry entries sit in different rows — and both @objectstack/service-automation and @objectstack/service-knowledge are present in the merged file, alongside service-cluster and service-i18n.

pnpm-lock.yaml was not hand-merged. Git's union of the two tsx devDependency additions was checked against the repo's own tooling: pnpm install on the merged manifest set left the file untouched (git status --porcelain -- pnpm-lock.yaml empty afterwards), and pnpm install --frozen-lockfile then exits 0.

Recomputed numbers — these supersede the pre-merge figures in the body above

The service-knowledge graduation moved every absolute this PR's provenance block quoted, and a merge does not reconcile prose. Re-measured with --list on the merged tree, varying only what the typecheck script names. Each mutation was proven on disk by comparing git hash-object against the HEAD blob before the run, and each restore by git checkout HEAD -- ... plus an empty git diff HEAD:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

before 59/78 packages, 120 programs, 293 pairs, 19 clean → after 60/78, 121, 302, 18 clean, i.e. +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else — the deltas the block actually claims are unchanged; only the absolutes moved. Commit c6d25b73 writes exactly these into this entry's own doc-block and says which merge moved them. The service-cluster, service-i18n and service-knowledge blocks keep their own historical readings untouched.

The DEBT ledger summary line moved for the same reason — two packages graduated where the reviewed round had one:

check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root),
5 in the DEBT ledger (104 frozen raw errors, …), 1 exempt.

(The body above reads 6 in DEBT / 114 frozen; that was true of the pre-merge tree.)

Gates re-run on the merged head c6d25b73

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff (7 paths, merge base 919beca43) → 64 commands. Every exit code captured redirect-then-read, never across a pipe; verdict lines read rather than bare status. 62 exit 0, 2 NOT MEASURED (exit 3), 0 findings.

  • pnpm check:type-check-coverage — exit 0, quoted above; its --self-test leg reports 48 semantic + 68 observation + 45 re-measure + 28 built-closure + 19 auto-lowering + 18 exit-code cases holding.
  • pnpm check:type-source-resolution — exit 0: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned …; 60 registered as still resolving a workspace dep's types through dist/. (self-test leg OK.)
  • pnpm --filter @objectstack/service-automation typecheck — exit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s) / 0 pinned signature(s).
  • pnpm --filter @objectstack/service-automation test — exit 0: 103 test files / 1225 tests passed, identical to the reviewed round.
  • pnpm check:nul-bytes — exit 0, plus a hand scan of both edited scripts for raw control bytes: no matches.

NOT MEASURED, quoting each gate's own verdict rather than a bare status:

  • pnpm check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. This is NOT a pass: nothing was measured."
  • pnpm check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk … Build the closure first, exactly as lint.yml does before this step." Its --self-test leg ran and passed before the refusal.

Both want a full-workspace build, which is CI's job rather than a per-package dependency closure — the same two NOT MEASURED results the reviewed round recorded, unchanged in kind by the merge.

One further note for the record: node scripts/check-plugin-teardown-shape.mjs --self-test exited 1 once inside the batch, refusing with "cannot read the positive control" and declining to print a verdict. Re-run unhurried on the same tree it exits 0 with all 47 cases passing, and the clone is not shallow and does hold the pinned fixture commit — a transient read under load, not a finding.


Generated by Claude Code

@os-warren
os-warren added this pull request to the merge queueSep 4, 2026
Merged via the queue into main with commit 460134aSep 4, 2026
41 checks passed
@os-warren
os-warren deleted the claude/issue-15048-service-automation-typecheck branch September 4, 2026 07:38
os-warren pushed a commit that referenced this pull request Sep 4, 2026
Resolves the third and last conflict of the #14062 family. Both siblings
have landed on main (#15147 service-knowledge, #15152 service-automation)
and all three touch the same two gate scripts.
Resolution is "keep every entry" -- the three cards do not disagree:
* scripts/check-type-check-coverage.mjs: all three graduation paragraphs
kept above `DEBT` (service-storage first, so the service-knowledge
paragraph's "the paragraph above ... for `metadata` and `service-storage`"
back-reference still resolves), and all three DEBT entries deleted. The
ledger summary line is computed at runtime from DEBT/TEST_DEBT, so no
hand-edited total exists there to reconcile.
* scripts/check-type-source-resolution.mjs: auto-merged; all three new
registry entries present, service-cluster / service-i18n untouched.
* pnpm-lock.yaml: not hand-merged. `pnpm install` over the merged manifest
set leaves the auto-merged file byte-identical (hash 000bb7d...) and
`pnpm install --frozen-lockfile` exits 0.
The diff against main is unchanged from the reviewed one: same 20 files,
same +307/-133.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
zhuangjianguo pushed a commit that referenced this pull request Sep 4, 2026
…the TS2341 x3 it hid (#15152)
* wip: onboard service-automation typecheck, fix TS2341 residue
* wip: onboarding gate registry entry + changeset
* fix(scripts): re-measure this entry's provenance totals on the merged tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tooling(services): service-automation has no typecheck script — its test layer is compiled by no tsc program (#14062 family, sibling of #14181)

3 participants

@os-sales@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid - #15152

Merged
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck
Sep 4, 2026
Merged

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid#15152
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck

Conversation

@os-sales

@os-salesos-sales commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes#15048

packages/services/service-automation had no typecheck script at all (its scripts were build and test), so no tsc program anywhere read this package. Turbo/CI typecheck lanes skipped it silently, because a zero-matching filter run exits 0. tsup transpiles with esbuild and vitest runs through esbuild type-stripping; neither type-checks.

Clause-②: no

The route, and which exemplar it was copied from

Copied from plugin-webhooks / service-cluster (#14181, PR #15032), not from plugin-auth / plugin-sharing / core, because it is the structural match. The deciding property is what the BUILD config does with tests:

packagetsconfig.json excludes tests?ledger filefits here
plugin-auth, plugin-sharingyes (**/*.test.ts in exclude)debt ledger presentno
core (#14916)yes (**/*.spec.ts, **/*.test.ts)test-typecheck-debt.json, 4 residualno
plugin-webhooks, service-clusternononeyes
service-automationno

service-automation's tsconfig.json includes the tests and always did (include: ["src"], no test exclusion), so the program that would have read them already existed and was simply never invoked. It is therefore not a package that needs an exclusion compensated for, and AGENTS.md is explicit in the other direction: "Never exclude*.test.ts / *.spec.ts from a package's tsconfig.json". So tsconfig.json is untouched, and the sibling tsconfig.test.json is the family's uniform instrument over the same files.

The sibling changes module semantics only (module: esnext, moduleResolution: bundler, lib: ES2022, matching how vitest actually executes these files). Strictness is inherited and untouched, and it declares no paths.

Measured error count, before and after

Dependency closure built first (pnpm --filter '@objectstack/service-automation^...' build), measured at 2cc4610304 (origin/main):

programbeforeafter
BUILD semantics (tsc --noEmit -p tsconfig.json, which already included the tests)30
test layer (tsc --noEmit -p tsconfig.test.json)30

555 files in the program, covering all 103 of the package's src/**/*.test.ts.

The two readings agree, and that agreement is the load-bearing result — same as service-cluster's own graduation, and unlike @objectstack/core (#14916: 98 undivided → 4 after the split, nearly all TS7006 cascading from one unresolved import). This package carried no config-tier pile at all; the 3 were genuinely code-tier from the start, well inside the dispatch's "a handful, fix them properly" branch, so no stop-and-report was owed.

The three errors, and why the fix is correct rather than convenient

All three were TS2341 ("Property 'flows' is private and only accessible within class 'AutomationEngine'"), all in src/nested-region-parity.test.ts (lines 95/151/180):

src/nested-region-parity.test.ts(95,25): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(151,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(180,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.

Three tests dot-read the class's privateflows map (engine.flows.get(name)) directly, instead of going through its own public accessor:

// src/engine.tsasyncgetFlow(name: string): Promise<FlowParsed|null>{returnthis.flows.get(name)??null;}

That accessor already exists, and it is already the idiom every other test file in this package uses (await engine.getFlow(name) — 10+ call sites across engine.test.ts, canonicalize-stored-flow.test.ts, flow-cold-boot-bind.test.ts, flow-load-conversion.test.ts, etc.). The fix is therefore not a workaround: it replaces three private-internals reads with the public surface the class already offers, matching the rest of the suite — no source signature widened, no cast, no bracket-notation trick.

- const flow = engine.flows.get('repro')!;+ const flow = (await engine.getFlow('repro'))!;
- expect((engine.flows.get('callout')!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');+ expect(((await engine.getFlow('callout'))!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');
- expect((engine.flows.get('purge')!.nodes[1]!.config as any).body.nodes[0].config)+ expect(((await engine.getFlow('purge'))!.nodes[1]!.config as any).body.nodes[0].config)

The two test bodies that were not already async were made async to await the accessor. Re-ran the file after the fix: 22/22 tests pass, unchanged behavior — the assertions test the same thing, only the read reaches it through the public door.

Ledger consequences

  • DEBT entry deleted, not lowered.check-type-check-coverage.mjs carried '@objectstack/service-automation': { errors: 3, note: 'code-tier 3 (TS2341 x3)...' }. That gate's own invariant is "covered packages must not also sit in the ledger". The entry is deleted and the graduation recorded in the file's prose. Gate now reads 5 in DEBT where it read 6 (104 frozen errors, down from 114), on the merged tree.
  • No test-typecheck-debt.json is created, and its absence is the zero: any error in any file here is red immediately, with no entry to be added to. ⛔ No ledger was grown in either direction.

⚠️check:type-source-resolution — a re-baseline, please review it as one

This gate goes red on this diff as expected (the card's own ⚠️ block flags this), and the remedy its failure text names (paths) is measurably the wrong one here too — same finding as service-cluster's own re-baseline, reproduced at larger scale because this package pulls 9 workspace deps instead of 2.

Provenance, measured four ways on one checkout, varying only what typecheck names:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

Row 2 is the load-bearing one: tsconfig.json is always a counted program per this gate's own design (programConfigsFor's doc-block), and it already includes every test file — yet it measures clean, with zero dist-resolved workspace type imports. So the exposure is only reachable through the onboarded tsconfig.test.json program, not merely first seen there. This package also had zero pre-existing programs (no typecheck script at all), so there is no program a dep could be laundered through — the same clean case service-cluster reported.

Numbers stated: before 59/78 packages, 120 programs, 293 pairs, 19 clean; after 60/78, 121, 302, 18 clean — +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else.

Why the entry and not paths, measured: redirecting the 9 deps to source takes this package's test layer from 0 errors to 648 (647 TS6059 "not under rootDir" + 1 TS6133) — all 647 of the TS6059 land in another package's source, zero in this package's own src/: packages/spec/src/** 379, packages/core/src/** 62, packages/plugins/plugin-security/src/** 60, packages/objectql/src/** 49, packages/services/service-messaging/src/** 41, packages/metadata-core/src/** 29, packages/formula/src/** 15, packages/services/service-job/src/** 6, packages/drivers/driver-sql/src/** 6. Billed to packages that cannot pay them down — the same service-cluster (#14181: 0 → 435) and PR #12570 finding, reproduced at a larger scale because this package pulls more workspace deps.

Gates

All measured figures in this section are the post-merge re-run on head c6d25b73 (merge commit 8aff199c, merge base 919beca43); the pre-merge readings they replace were taken at 6a703bf42d, and the merge-round comment on this PR records both sets side by side.

Package-level, on the merged head c6d25b73:

  • pnpm --filter @objectstack/service-automation typecheckexit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s).
  • pnpm --filter @objectstack/service-automation testexit 0: 103 test files / 1225 tests passed.
  • pnpm check:type-check-coverageexit 0: check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root), 5 in the DEBT ledger (104 frozen raw errors), 1 exempt.
  • pnpm check:type-source-resolutionexit 0 after the re-baseline: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned; 60 registered as still resolving a workspace dep's types through dist/.
  • pnpm check:nul-bytes — exit 0.

Dispatch-derived gate list (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff, final head c6d25b73), each exit code captured by redirect-then-read, never across a pipe: 64 commands — 62 exit 0, 2 exit 3 (NOT MEASURED), 0 red.

NOT MEASURED, quoting each gate's own verdict:

  • check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. ⛔ This is NOT a pass: nothing was measured."
  • check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk. … ⛔ This is NOT a pass and NOT a finding: nothing was measured."

Both read the same missing prerequisite — a full-workspace pnpm build, which is CI's job, not a per-package build closure. Three commands (check:pm-dispatch-gates, check:query-options-erasure, check:slot-lookup) needed longer than a 90s probe budget and passed on a second, unhurried run — each does a real full-tree scan, not a hang.

Both edited gate scripts' own --self-test are in that union and hold.

Declined, with reasons

Changeset

.changeset/service-automation-test-tsc-program.md, patch on @objectstack/service-automation. src/** is otherwise byte-identical (only the three private-read call sites in one test file changed, same assertions), so no shipped behaviour moves; the level reflects the published package.json gaining typecheck / check:test-typecheck scripts and a tsx devDependency.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

@github-actionsgithub-actionsBot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Sep 4, 2026
@github-actions

github-actionsBot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 919beca43b2901320cea39afd912b7f662ddd3b1packageMentionDocs.

@os-sales
os-sales marked this pull request as ready for review September 4, 2026 04:14
@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to a conflict with the base branch Sep 4, 2026
…rvice-automation-typecheck
# Conflicts:
#	scripts/check-type-check-coverage.mjs
… tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
@os-warrenClaude

Copy link
Copy Markdown
Collaborator

Merge round — conflicted against main, resolved by keeping every entry

main moved to 919beca43b: sibling PR #15147 (card #15049) landed as 8422acd, graduating @objectstack/service-knowledge out of the same two ledgers this PR edits, which put this PR in mergeable_state: dirty. Nothing about the reviewed change was reopened; the body above stands except for the figures this comment supersedes.

Merge, not rebase.git merge origin/main → merge commit 8aff199c, new head c6d25b73. No rebase, no amend, no force-push.

Conflicts, and why each resolution is "keep every entry"

One file conflicted — scripts/check-type-check-coverage.mjs, two hunks. The two sides do not disagree: each deletes a different DEBT key and records a different graduation.

  1. Graduation prose.service-knowledge's paragraph (from main) and service-automation's (from this branch) are independent paragraphs about different packages. Both kept, service-knowledge first since it landed first, separated by the file's usual bare comment line. Every earlier graduation paragraph (metadata, service-cluster) is untouched and still above them, so the two blocks' "above" references still resolve.
  2. The DEBT map.main deleted @objectstack/service-knowledge; this branch deleted @objectstack/service-automation; git presented the two deletions as one conflict because they were adjacent. Both entries are gone in the resolution, and cloud-connection, hono, observability, service-storage and spec-monorepo are byte-identical to main.

scripts/check-type-source-resolution.mjs auto-merged — the two new registry entries sit in different rows — and both @objectstack/service-automation and @objectstack/service-knowledge are present in the merged file, alongside service-cluster and service-i18n.

pnpm-lock.yaml was not hand-merged. Git's union of the two tsx devDependency additions was checked against the repo's own tooling: pnpm install on the merged manifest set left the file untouched (git status --porcelain -- pnpm-lock.yaml empty afterwards), and pnpm install --frozen-lockfile then exits 0.

Recomputed numbers — these supersede the pre-merge figures in the body above

The service-knowledge graduation moved every absolute this PR's provenance block quoted, and a merge does not reconcile prose. Re-measured with --list on the merged tree, varying only what the typecheck script names. Each mutation was proven on disk by comparing git hash-object against the HEAD blob before the run, and each restore by git checkout HEAD -- ... plus an empty git diff HEAD:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

before 59/78 packages, 120 programs, 293 pairs, 19 clean → after 60/78, 121, 302, 18 clean, i.e. +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else — the deltas the block actually claims are unchanged; only the absolutes moved. Commit c6d25b73 writes exactly these into this entry's own doc-block and says which merge moved them. The service-cluster, service-i18n and service-knowledge blocks keep their own historical readings untouched.

The DEBT ledger summary line moved for the same reason — two packages graduated where the reviewed round had one:

check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root),
5 in the DEBT ledger (104 frozen raw errors, …), 1 exempt.

(The body above reads 6 in DEBT / 114 frozen; that was true of the pre-merge tree.)

Gates re-run on the merged head c6d25b73

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff (7 paths, merge base 919beca43) → 64 commands. Every exit code captured redirect-then-read, never across a pipe; verdict lines read rather than bare status. 62 exit 0, 2 NOT MEASURED (exit 3), 0 findings.

  • pnpm check:type-check-coverage — exit 0, quoted above; its --self-test leg reports 48 semantic + 68 observation + 45 re-measure + 28 built-closure + 19 auto-lowering + 18 exit-code cases holding.
  • pnpm check:type-source-resolution — exit 0: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned …; 60 registered as still resolving a workspace dep's types through dist/. (self-test leg OK.)
  • pnpm --filter @objectstack/service-automation typecheck — exit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s) / 0 pinned signature(s).
  • pnpm --filter @objectstack/service-automation test — exit 0: 103 test files / 1225 tests passed, identical to the reviewed round.
  • pnpm check:nul-bytes — exit 0, plus a hand scan of both edited scripts for raw control bytes: no matches.

NOT MEASURED, quoting each gate's own verdict rather than a bare status:

  • pnpm check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. This is NOT a pass: nothing was measured."
  • pnpm check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk … Build the closure first, exactly as lint.yml does before this step." Its --self-test leg ran and passed before the refusal.

Both want a full-workspace build, which is CI's job rather than a per-package dependency closure — the same two NOT MEASURED results the reviewed round recorded, unchanged in kind by the merge.

One further note for the record: node scripts/check-plugin-teardown-shape.mjs --self-test exited 1 once inside the batch, refusing with "cannot read the positive control" and declining to print a verdict. Re-run unhurried on the same tree it exits 0 with all 47 cases passing, and the clone is not shallow and does hold the pinned fixture commit — a transient read under load, not a finding.


Generated by Claude Code

@os-warren
os-warren added this pull request to the merge queueSep 4, 2026
Merged via the queue into main with commit 460134aSep 4, 2026
41 checks passed
@os-warren
os-warren deleted the claude/issue-15048-service-automation-typecheck branch September 4, 2026 07:38
os-warren pushed a commit that referenced this pull request Sep 4, 2026
Resolves the third and last conflict of the #14062 family. Both siblings
have landed on main (#15147 service-knowledge, #15152 service-automation)
and all three touch the same two gate scripts.
Resolution is "keep every entry" -- the three cards do not disagree:
* scripts/check-type-check-coverage.mjs: all three graduation paragraphs
kept above `DEBT` (service-storage first, so the service-knowledge
paragraph's "the paragraph above ... for `metadata` and `service-storage`"
back-reference still resolves), and all three DEBT entries deleted. The
ledger summary line is computed at runtime from DEBT/TEST_DEBT, so no
hand-edited total exists there to reconcile.
* scripts/check-type-source-resolution.mjs: auto-merged; all three new
registry entries present, service-cluster / service-i18n untouched.
* pnpm-lock.yaml: not hand-merged. `pnpm install` over the merged manifest
set leaves the auto-merged file byte-identical (hash 000bb7d...) and
`pnpm install --frozen-lockfile` exits 0.
The diff against main is unchanged from the reviewed one: same 20 files,
same +307/-133.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
zhuangjianguo pushed a commit that referenced this pull request Sep 4, 2026
…the TS2341 x3 it hid (#15152)
* wip: onboard service-automation typecheck, fix TS2341 residue
* wip: onboarding gate registry entry + changeset
* fix(scripts): re-measure this entry's provenance totals on the merged tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tooling(services): service-automation has no typecheck script — its test layer is compiled by no tsc program (#14062 family, sibling of #14181)

3 participants

@os-sales@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid - #15152

Merged
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck
Sep 4, 2026
Merged

fix(service-automation): compile the test layer with tsc, and repair the TS2341 x3 it hid#15152
os-warren merged 5 commits into
mainfrom
claude/issue-15048-service-automation-typecheck

Conversation

@os-sales

@os-salesos-sales commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes#15048

packages/services/service-automation had no typecheck script at all (its scripts were build and test), so no tsc program anywhere read this package. Turbo/CI typecheck lanes skipped it silently, because a zero-matching filter run exits 0. tsup transpiles with esbuild and vitest runs through esbuild type-stripping; neither type-checks.

Clause-②: no

The route, and which exemplar it was copied from

Copied from plugin-webhooks / service-cluster (#14181, PR #15032), not from plugin-auth / plugin-sharing / core, because it is the structural match. The deciding property is what the BUILD config does with tests:

packagetsconfig.json excludes tests?ledger filefits here
plugin-auth, plugin-sharingyes (**/*.test.ts in exclude)debt ledger presentno
core (#14916)yes (**/*.spec.ts, **/*.test.ts)test-typecheck-debt.json, 4 residualno
plugin-webhooks, service-clusternononeyes
service-automationno

service-automation's tsconfig.json includes the tests and always did (include: ["src"], no test exclusion), so the program that would have read them already existed and was simply never invoked. It is therefore not a package that needs an exclusion compensated for, and AGENTS.md is explicit in the other direction: "Never exclude*.test.ts / *.spec.ts from a package's tsconfig.json". So tsconfig.json is untouched, and the sibling tsconfig.test.json is the family's uniform instrument over the same files.

The sibling changes module semantics only (module: esnext, moduleResolution: bundler, lib: ES2022, matching how vitest actually executes these files). Strictness is inherited and untouched, and it declares no paths.

Measured error count, before and after

Dependency closure built first (pnpm --filter '@objectstack/service-automation^...' build), measured at 2cc4610304 (origin/main):

programbeforeafter
BUILD semantics (tsc --noEmit -p tsconfig.json, which already included the tests)30
test layer (tsc --noEmit -p tsconfig.test.json)30

555 files in the program, covering all 103 of the package's src/**/*.test.ts.

The two readings agree, and that agreement is the load-bearing result — same as service-cluster's own graduation, and unlike @objectstack/core (#14916: 98 undivided → 4 after the split, nearly all TS7006 cascading from one unresolved import). This package carried no config-tier pile at all; the 3 were genuinely code-tier from the start, well inside the dispatch's "a handful, fix them properly" branch, so no stop-and-report was owed.

The three errors, and why the fix is correct rather than convenient

All three were TS2341 ("Property 'flows' is private and only accessible within class 'AutomationEngine'"), all in src/nested-region-parity.test.ts (lines 95/151/180):

src/nested-region-parity.test.ts(95,25): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(151,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.
src/nested-region-parity.test.ts(180,20): error TS2341: Property 'flows' is private and only accessible within class 'AutomationEngine'.

Three tests dot-read the class's privateflows map (engine.flows.get(name)) directly, instead of going through its own public accessor:

// src/engine.tsasyncgetFlow(name: string): Promise<FlowParsed|null>{returnthis.flows.get(name)??null;}

That accessor already exists, and it is already the idiom every other test file in this package uses (await engine.getFlow(name) — 10+ call sites across engine.test.ts, canonicalize-stored-flow.test.ts, flow-cold-boot-bind.test.ts, flow-load-conversion.test.ts, etc.). The fix is therefore not a workaround: it replaces three private-internals reads with the public surface the class already offers, matching the rest of the suite — no source signature widened, no cast, no bracket-notation trick.

- const flow = engine.flows.get('repro')!;+ const flow = (await engine.getFlow('repro'))!;
- expect((engine.flows.get('callout')!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');+ expect(((await engine.getFlow('callout'))!.nodes[1]!.config as any).body.nodes[0].type).toBe('http');
- expect((engine.flows.get('purge')!.nodes[1]!.config as any).body.nodes[0].config)+ expect(((await engine.getFlow('purge'))!.nodes[1]!.config as any).body.nodes[0].config)

The two test bodies that were not already async were made async to await the accessor. Re-ran the file after the fix: 22/22 tests pass, unchanged behavior — the assertions test the same thing, only the read reaches it through the public door.

Ledger consequences

  • DEBT entry deleted, not lowered.check-type-check-coverage.mjs carried '@objectstack/service-automation': { errors: 3, note: 'code-tier 3 (TS2341 x3)...' }. That gate's own invariant is "covered packages must not also sit in the ledger". The entry is deleted and the graduation recorded in the file's prose. Gate now reads 5 in DEBT where it read 6 (104 frozen errors, down from 114), on the merged tree.
  • No test-typecheck-debt.json is created, and its absence is the zero: any error in any file here is red immediately, with no entry to be added to. ⛔ No ledger was grown in either direction.

⚠️check:type-source-resolution — a re-baseline, please review it as one

This gate goes red on this diff as expected (the card's own ⚠️ block flags this), and the remedy its failure text names (paths) is measurably the wrong one here too — same finding as service-cluster's own re-baseline, reproduced at larger scale because this package pulls 9 workspace deps instead of 2.

Provenance, measured four ways on one checkout, varying only what typecheck names:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

Row 2 is the load-bearing one: tsconfig.json is always a counted program per this gate's own design (programConfigsFor's doc-block), and it already includes every test file — yet it measures clean, with zero dist-resolved workspace type imports. So the exposure is only reachable through the onboarded tsconfig.test.json program, not merely first seen there. This package also had zero pre-existing programs (no typecheck script at all), so there is no program a dep could be laundered through — the same clean case service-cluster reported.

Numbers stated: before 59/78 packages, 120 programs, 293 pairs, 19 clean; after 60/78, 121, 302, 18 clean — +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else.

Why the entry and not paths, measured: redirecting the 9 deps to source takes this package's test layer from 0 errors to 648 (647 TS6059 "not under rootDir" + 1 TS6133) — all 647 of the TS6059 land in another package's source, zero in this package's own src/: packages/spec/src/** 379, packages/core/src/** 62, packages/plugins/plugin-security/src/** 60, packages/objectql/src/** 49, packages/services/service-messaging/src/** 41, packages/metadata-core/src/** 29, packages/formula/src/** 15, packages/services/service-job/src/** 6, packages/drivers/driver-sql/src/** 6. Billed to packages that cannot pay them down — the same service-cluster (#14181: 0 → 435) and PR #12570 finding, reproduced at a larger scale because this package pulls more workspace deps.

Gates

All measured figures in this section are the post-merge re-run on head c6d25b73 (merge commit 8aff199c, merge base 919beca43); the pre-merge readings they replace were taken at 6a703bf42d, and the merge-round comment on this PR records both sets side by side.

Package-level, on the merged head c6d25b73:

  • pnpm --filter @objectstack/service-automation typecheckexit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s).
  • pnpm --filter @objectstack/service-automation testexit 0: 103 test files / 1225 tests passed.
  • pnpm check:type-check-coverageexit 0: check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root), 5 in the DEBT ledger (104 frozen raw errors), 1 exempt.
  • pnpm check:type-source-resolutionexit 0 after the re-baseline: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned; 60 registered as still resolving a workspace dep's types through dist/.
  • pnpm check:nul-bytes — exit 0.

Dispatch-derived gate list (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff, final head c6d25b73), each exit code captured by redirect-then-read, never across a pipe: 64 commands — 62 exit 0, 2 exit 3 (NOT MEASURED), 0 red.

NOT MEASURED, quoting each gate's own verdict:

  • check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. ⛔ This is NOT a pass: nothing was measured."
  • check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk. … ⛔ This is NOT a pass and NOT a finding: nothing was measured."

Both read the same missing prerequisite — a full-workspace pnpm build, which is CI's job, not a per-package build closure. Three commands (check:pm-dispatch-gates, check:query-options-erasure, check:slot-lookup) needed longer than a 90s probe budget and passed on a second, unhurried run — each does a real full-tree scan, not a hang.

Both edited gate scripts' own --self-test are in that union and hold.

Declined, with reasons

Changeset

.changeset/service-automation-test-tsc-program.md, patch on @objectstack/service-automation. src/** is otherwise byte-identical (only the three private-read call sites in one test file changed, same assertions), so no shipped behaviour moves; the level reflects the published package.json gaining typecheck / check:test-typecheck scripts and a tsx devDependency.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

@github-actionsgithub-actionsBot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Sep 4, 2026
@github-actions

github-actionsBot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 919beca43b2901320cea39afd912b7f662ddd3b1packageMentionDocs.

@os-sales
os-sales marked this pull request as ready for review September 4, 2026 04:14
@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to a conflict with the base branch Sep 4, 2026
…rvice-automation-typecheck
# Conflicts:
#	scripts/check-type-check-coverage.mjs
… tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
@os-warrenClaude

Copy link
Copy Markdown
Collaborator

Merge round — conflicted against main, resolved by keeping every entry

main moved to 919beca43b: sibling PR #15147 (card #15049) landed as 8422acd, graduating @objectstack/service-knowledge out of the same two ledgers this PR edits, which put this PR in mergeable_state: dirty. Nothing about the reviewed change was reopened; the body above stands except for the figures this comment supersedes.

Merge, not rebase.git merge origin/main → merge commit 8aff199c, new head c6d25b73. No rebase, no amend, no force-push.

Conflicts, and why each resolution is "keep every entry"

One file conflicted — scripts/check-type-check-coverage.mjs, two hunks. The two sides do not disagree: each deletes a different DEBT key and records a different graduation.

  1. Graduation prose.service-knowledge's paragraph (from main) and service-automation's (from this branch) are independent paragraphs about different packages. Both kept, service-knowledge first since it landed first, separated by the file's usual bare comment line. Every earlier graduation paragraph (metadata, service-cluster) is untouched and still above them, so the two blocks' "above" references still resolve.
  2. The DEBT map.main deleted @objectstack/service-knowledge; this branch deleted @objectstack/service-automation; git presented the two deletions as one conflict because they were adjacent. Both entries are gone in the resolution, and cloud-connection, hono, observability, service-storage and spec-monorepo are byte-identical to main.

scripts/check-type-source-resolution.mjs auto-merged — the two new registry entries sit in different rows — and both @objectstack/service-automation and @objectstack/service-knowledge are present in the merged file, alongside service-cluster and service-i18n.

pnpm-lock.yaml was not hand-merged. Git's union of the two tsx devDependency additions was checked against the repo's own tooling: pnpm install on the merged manifest set left the file untouched (git status --porcelain -- pnpm-lock.yaml empty afterwards), and pnpm install --frozen-lockfile then exits 0.

Recomputed numbers — these supersede the pre-merge figures in the body above

The service-knowledge graduation moved every absolute this PR's provenance block quoted, and a merge does not reconcile prose. Re-measured with --list on the merged tree, varying only what the typecheck script names. Each mutation was proven on disk by comparing git hash-object against the HEAD blob before the run, and each restore by git checkout HEAD -- ... plus an empty git diff HEAD:

typecheck namesentry--list totals
nothing (origin/main)absent120 programs / 293 pairs (59/78 packages, 19 clean)
tsconfig.json onlyabsent120 programs / 293 pairs (59/78, 19 clean)
tsconfig.test.json onlyPRESENT121 programs / 302 pairs (60/78, 18 clean)
both (this PR)PRESENT121 programs / 302 pairs (60/78, 18 clean)

before 59/78 packages, 120 programs, 293 pairs, 19 clean → after 60/78, 121, 302, 18 clean, i.e. +1 package, +1 program, +9 pairs (one per dep), this entry and nothing else — the deltas the block actually claims are unchanged; only the absolutes moved. Commit c6d25b73 writes exactly these into this entry's own doc-block and says which merge moved them. The service-cluster, service-i18n and service-knowledge blocks keep their own historical readings untouched.

The DEBT ledger summary line moved for the same reason — two packages graduated where the reviewed round had one:

check-type-check-coverage: OK — 74/79 workspace packages type-checked (plus the root),
5 in the DEBT ledger (104 frozen raw errors, …), 1 exempt.

(The body above reads 6 in DEBT / 114 frozen; that was true of the pre-merge tree.)

Gates re-run on the merged head c6d25b73

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived against the post-merge diff (7 paths, merge base 919beca43) → 64 commands. Every exit code captured redirect-then-read, never across a pipe; verdict lines read rather than bare status. 62 exit 0, 2 NOT MEASURED (exit 3), 0 findings.

  • pnpm check:type-check-coverage — exit 0, quoted above; its --self-test leg reports 48 semantic + 68 observation + 45 re-measure + 28 built-closure + 19 auto-lowering + 18 exit-code cases holding.
  • pnpm check:type-source-resolution — exit 0: check-type-source-resolution OK — 121 tsc program(s) across 78 packages scanned …; 60 registered as still resolving a workspace dep's types through dist/. (self-test leg OK.)
  • pnpm --filter @objectstack/service-automation typecheck — exit 0: check:test-typecheck: OK — @objectstack/service-automation's test layer compiles under packages/services/service-automation/tsconfig.test.json; 0 file(s) / 0 error(s) / 0 pinned signature(s).
  • pnpm --filter @objectstack/service-automation test — exit 0: 103 test files / 1225 tests passed, identical to the reviewed round.
  • pnpm check:nul-bytes — exit 0, plus a hand scan of both edited scripts for raw control bytes: no matches.

NOT MEASURED, quoting each gate's own verdict rather than a bare status:

  • pnpm check:dual-build-cjs-loads — exit 3: "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … Run pnpm build first. This is NOT a pass: nothing was measured."
  • pnpm check:type-check-debt — exit 3: "--re-measure cannot run: 13 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk … Build the closure first, exactly as lint.yml does before this step." Its --self-test leg ran and passed before the refusal.

Both want a full-workspace build, which is CI's job rather than a per-package dependency closure — the same two NOT MEASURED results the reviewed round recorded, unchanged in kind by the merge.

One further note for the record: node scripts/check-plugin-teardown-shape.mjs --self-test exited 1 once inside the batch, refusing with "cannot read the positive control" and declining to print a verdict. Re-run unhurried on the same tree it exits 0 with all 47 cases passing, and the clone is not shallow and does hold the pinned fixture commit — a transient read under load, not a finding.


Generated by Claude Code

@os-warren
os-warren added this pull request to the merge queueSep 4, 2026
Merged via the queue into main with commit 460134aSep 4, 2026
41 checks passed
@os-warren
os-warren deleted the claude/issue-15048-service-automation-typecheck branch September 4, 2026 07:38
os-warren pushed a commit that referenced this pull request Sep 4, 2026
Resolves the third and last conflict of the #14062 family. Both siblings
have landed on main (#15147 service-knowledge, #15152 service-automation)
and all three touch the same two gate scripts.
Resolution is "keep every entry" -- the three cards do not disagree:
* scripts/check-type-check-coverage.mjs: all three graduation paragraphs
kept above `DEBT` (service-storage first, so the service-knowledge
paragraph's "the paragraph above ... for `metadata` and `service-storage`"
back-reference still resolves), and all three DEBT entries deleted. The
ledger summary line is computed at runtime from DEBT/TEST_DEBT, so no
hand-edited total exists there to reconcile.
* scripts/check-type-source-resolution.mjs: auto-merged; all three new
registry entries present, service-cluster / service-i18n untouched.
* pnpm-lock.yaml: not hand-merged. `pnpm install` over the merged manifest
set leaves the auto-merged file byte-identical (hash 000bb7d...) and
`pnpm install --frozen-lockfile` exits 0.
The diff against main is unchanged from the reviewed one: same 20 files,
same +307/-133.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
zhuangjianguo pushed a commit that referenced this pull request Sep 4, 2026
…the TS2341 x3 it hid (#15152)
* wip: onboard service-automation typecheck, fix TS2341 residue
* wip: onboarding gate registry entry + changeset
* fix(scripts): re-measure this entry's provenance totals on the merged tree
The `service-knowledge` onboarding landed on `main` between this entry's first
reading and this merge, so every absolute in its provenance block (programs,
pairs, packages, clean count) was a number about a tree that no longer exists.
Re-taken with `--list` on the merge commit itself, all four rows plus the
before/after pair, by varying only what the `typecheck` script names:
no `typecheck` script absent 120 programs / 293 pairs
names tsconfig.json absent 120 programs / 293 pairs
names tsconfig.test PRESENT 121 programs / 302 pairs
names both (the card) PRESENT 121 programs / 302 pairs
before 59 of 78 packages, 120 programs, 293 pairs, 19 clean
after 60 of 78 packages, 121 programs, 302 pairs, 18 clean
The deltas this block actually claims (+1 package, +1 program, +9 pairs, one
per dep) are unchanged; only the absolutes moved, and the block now says which
merge moved them. The sibling entries' own blocks keep their own historical
readings untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tooling(services): service-automation has no typecheck script — its test layer is compiled by no tsc program (#14062 family, sibling of #14181)

3 participants

@os-sales@os-warren@claude