Skip to content

build(typecheck): graduate cloud-connection, observability and hono from the DEBT ledger (#20800) - #21038

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-20800-typecheck-graduation
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-20800-typecheck-graduation

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #20800
Clause-②: no

The last three packages of the #4311 type-check ledger, @objectstack/cloud-connection, @objectstack/observability and @objectstack/hono, now type-check their own test layer. Each gets a sibling tsconfig.test.json named by a new typecheck script, every code-tier error is repaired at its cause inside the test layer, and the three DEBT entries are deleted rather than lowered. DEBT in scripts/check-type-check-coverage.mjs now holds @objectstack/spec-monorepo alone; @objectstack/console stays EXEMPT, as the card says.

What changed

Path Change
packages/{cloud-connection,observability,adapters/hono}/tsconfig.test.json new: extends the package's build config, noEmit, module: esnext / moduleResolution: bundler, lib ES2022 (hono keeps its DOM libs). Strictness, paths, rootDir and types are inherited and not re-declared. Each header carries its own measured readings.
packages/{cloud-connection,observability,adapters/hono}/package.json typecheck = tsc --noEmit && tsc --noEmit -p tsconfig.test.json, the zero-residue shape metadata-fs and trigger-record-change use: no per-package ledger, no new dependency, no lockfile change.
test files (listed below) the code-tier repairs. No cast, no @ts-expect-error, no src signature widened.
scripts/check-type-check-coverage.mjs the three DEBT entries deleted, plus one graduation paragraph in the same style as the ones above it. No gate logic moved.

Both readings, before any fix

Taken at f8178ffece (origin/main) with the workspace closure built first, once per config: tsc --noEmit --pretty false --listFiles -p tsconfig.json (the build config) and the same with the new tsconfig.test.json.

package files in the test program (own tests) build config test config config tier code tier
@objectstack/cloud-connection 923 (30) 13 = TS2493 x11 + TS2550 x2 11 = TS2493 x11 2 11
@objectstack/observability 401 (7) 11 = TS2554 x10 + TS2552 x1 11 = same 0 11
@objectstack/hono 858 (5) 3 = TS2769 x2 + TS18046 x1 3 = same 0 3, and 4 more once those cleared (below)

The build-config column reproduces the ledger exactly (13 / 11 / 3). All three build configs already included the tests and never excluded them, so the program that reads them always existed; no script ever ran it. That is the service-automation shape, not an exclude-and-compensate one.

After: every config of every package reads 0, and typecheck exits 0 for all three.

Each error repaired at its cause

Package / file Errors Cause Repair
cloud-connection: cloud-connection-plugin.test.ts x3, connection-credential-store.test.ts x4 (7 declarations) TS2493 x11 vi.fn(async () => new Response(...)) declared with no parameters, so vitest types mock.calls as empty tuples, and the test indexes calls[0][0] / [1] the mock is typed to the call it stubs: vi.fn(async (_url: string, _init?: RequestInit) => ...), the shape fetch(url, init) is always called with in the plugin
cloud-connection: marketplace-install-local-bundle.test.ts x2 TS2550 x2 (config tier) Array#at under the build config's lib ES2020 rewritten to vitest's mock.lastCall, not a widening of the shared build config (the service-storage call recorded in the ledger prose). The test config also carries ES2022, but the first leg of typecheck reads these files under ES2020 too, so the file itself had to compile under both
observability: error-exporters.test.ts, loggers.test.ts, metrics-exporters.test.ts TS2554 x10 the Noop classes declare zero-parameter methods (legal narrowing), so calls on the concrete class type have arity 0 while the contract consumers call through has the real one the instance is declared as the contract it is used through (ErrorReporter, Logger, MetricsRegistry). The source signatures were not widened
observability: otlp-http.test.ts TS2552 fake fetch parameter named RequestInfo, a DOM-lib name this program does not have typed as the union of string, URL and Request, the call site it stubs
hono: hono.test.ts TS2769 x2, TS18046 a bare new Hono() declares no Variables, so c.get('objectStack') takes never and returns unknown the two tests that read the variable back build their app with a KernelEnv type argument declaring objectStack (the middleware does c.set('objectStack', kernel))
hono: src/__mocks__/runtime.ts TS2883 x4, latent see the next section the four vi.fn() fields are annotated Mock

A finding for the reviewer: hono's "3" was not its whole pile

With the three semantic errors repaired, the hono test config reported four more: TS2883 on the four vi.fn() class fields of src/__mocks__/runtime.ts (the inferred type cannot be named without a reference to Procedure inside the pnpm store). tsc withholds declaration-emit diagnostics while any semantic error stands, so the first reading could not show them, and the build config never shows them at all (it exits 0 on that file; only the bundler resolution reports it). Observed in two states: state A (original files) reads 3 errors and no TS2883; state B (hono.test.ts repaired, mock not yet) reads exactly 4 TS2883 and nothing else. The repair is a test-support annotation; the file is a vitest alias target (vitest.config.ts), never built or published.

Nothing was hiding behind the other two: for cloud-connection and observability both configs read 0 once the listed errors were repaired.

Acceptance, measured

All at head 5f4aa15801 unless stated (the branch is two commits plus a merge of origin/main at 9b0de7de73, taken so the derived gate list was not read from a stale tree; closure rebuilt after the merge).

  • pnpm --filter PKG run typecheck exits 0 for all three; each run echoes the typecheck script name.
  • pnpm check:type-check-coverage: OK, "79/80 workspace packages type-checked (plus the root), 1 in the DEBT ledger (26 frozen raw errors), 1 exempt". pnpm check:type-check-debt: OK, "1 ledger entr(ies) re-measured ... none above its recorded number", surplus none.
  • The three suites do not regress: cloud-connection 30 files / 397 tests, observability 7 / 85, hono 5 / 122, all passing; the diff touches no it(, test( or describe( line.
  • The new scripts can fail. Planted through scripts/ablation-replace.mjs (anchor must hit once, blob change proven, restore proven) in one test file per package that this PR does not otherwise edit: the copyright line of marketplace-public-url.test.ts, perf-timing.test.ts and hono-wildcard-fallthrough.test.ts replaced by a line assigning a string to a number. Each typecheck exited 2, naming that file (TS2322 and TS6133), and each restore left the blob equal to HEAD with git diff HEAD empty. Direction observed: turns red, as expected.
  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 82 families; all 82 were run one by one with the exit code captured before any pipe, and --ran reconciles "82 derived, 82 run, 0 NOT-MEASURED, 0 UNRUN". check:pm-dispatch-gates needed the detached form its own header prescribes (about 17 minutes on this shared box; its first two foreground attempts were cut by my own timeout and are not counted).
  • pnpm lint exits 0 over the whole repo at 5f4aa15801.

Changeset

None written. The diff changes no file the three packages ship: files[] is dist, README.md and CHANGELOG.md; the dist build is from src/index.ts and reaches no test file or __mocks__; and after building the three packages, a grep of their dist for every symbol this PR introduced (KernelEnv, lastCall, the _init parameter, type Mock) found nothing, while a positive control (CloudConnectionPlugin, InMemoryErrorReporter, createHonoApp) hit in each. The only shipped-manifest change is the scripts block of each package.json. The seat applies skip-changeset.

Acceptance notes

  • @objectstack/spec-monorepo (root entry, 26) is untouched, as the card says. Its compositionAt staleness notice is the same one main prints.
  • Not run locally: the repository-wide CI jobs (pnpm test for the whole repo, the full dogfood set); they are CI's.

Generated by Claude Code

claude added 3 commits October 1, 2026 01:44
…nection, observability, hono

WIP: sibling tsconfig.test.json files, the typecheck wiring and the
test-layer repairs. The hono header, the DEBT entry deletions and the
re-measured numbers follow.

Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB
Co-authored-by: Claude <noreply@anthropic.com>
…rom the DEBT ledger

The hono test-config header, the three DEBT entry deletions and the
graduation record in check-type-check-coverage.mjs.

Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m dependencies Pull requests that update a dependency file tests labels Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 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

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 9c8b65aa23781f2c1d2aa0abe92fc11e0e71b3a1 → packageMentionDocs.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5f4aa158019f6642640b4605c40e1610ff76990a
Local-runs: none

① Derived judgments

Inputs: card #20800 body, claim 5922890409, os-dev-report 5923961384; PR #21038 body, 16-file list, labels (dependencies, size/m, tests, skip-changeset); the net diff git diff 9b0de7de73699771b69649bf7b507fbd2a842260 refs/review/pr-21038 (16 files, +273 / −36, equal to the PR's file list); precedents 8cf806f690, 45a72b0cc1, b103baf07e, ebb5550917; the gate and its parser at the head (scripts/check-type-check-coverage.mjs, scripts/typecheck-configs.mjs); the three manifests, the three build tsconfig.json, hono vitest.config.ts, root tsup.config.ts and root tsconfig.json, all read at the head sha; AGENTS.md. Nothing built, run or checked out.

(a) Per package.

  • Program + script: all three gain tsconfig.test.json (extends ./tsconfig.json; re-declares only noEmit, module esnext, moduleResolution bundler, lib; include src/**/*) and typecheck = tsc --noEmit && tsc --noEmit -p tsconfig.test.json. hono 75 lines, cloud-connection 72, observability 66. Strictness, paths, rootDir, types are inherited, none re-declared — the AGENTS.md line (module semantics, never its own strictness) holds. hono's sibling keeps DOM / DOM.Iterable in lib because its build config declares them and a child lib replaces the parent's; that is lib semantics, not strictness.
  • Accepted shape: the gate's own parser scripts/typecheck-configs.mjs configsNamedByTypecheck pins this exact string in its self-test battery ("an explicitly named sibling config counts" and "the prescribed sibling route names TWO programs", both tsc --noEmit && tsc --noEmit -p tsconfig.test.json, both expecting tsconfig.json plus tsconfig.test.json). The gate's own prose on the plugin-security graduation: at zero residue "a bare tsc --noEmit -p tsconfig.test.json is the stronger gate, since any error is red immediately with no ledger to be added to", naming metadata-core, metadata-fs, trigger-record-change — and the origin/main manifest census confirms those three carry the identical string. The check:test-typecheck form of 45a72b0cc1 / 8cf806f690 / b103baf07e is the residue-carrying route; both are precedented, and the residue here is zero. Accepted.
  • RECONCILED: each package now declares typecheck and has no DEBT row, so neither direction fires ("declares typecheck but still has a DEBT entry" and "entry names no workspace package"). COVERED and REAL hold (the script invokes tsc). TESTS_COVERED: each build tsconfig.json includes src with no test exclusion, and the sibling is NAMED, so no file is hidden. On this head, CI: Type Check · source gates (the job that runs pnpm check:type-check-coverage) success; Type Check · debt ledger (pnpm check:type-check-debt, the re-measure) success; Type Check · workspace (turbo run typecheck over packages/*, packages/*/*, apps/*, which executes the three new scripts, both legs each) success. The "after: 0 and 0" is CI-evidenced, not only the dev's local reading.

(b) Code-tier repairs, file by file. Banned-form grep over the diff's ADDED lines: as unknown as 0; the word as on any added non-comment line 0 (the as any on mockKernel, the ctx as any calls and the as unknown as typeof fetch in otlp-http.test.ts are untouched context, pre-existing); angle-bracket cast 0 (the only angle brackets added are the type argument on the Hono constructor, a generic argument, not an assertion); @ts-expect-error 4 hits, every one inside comment prose that says "no @ts-expect-error" (the three sibling headers and the gate paragraph), 0 directives; @ts-ignore 0; any on added non-comment lines 0. The diff holds no non-test src/** file except hono's __mocks__/runtime.ts (see c), so no source signature moved.

  • cloud-connection/src/cloud-connection-plugin.test.ts (3 declarations) and connection-credential-store.test.ts (4): vi.fn over a zero-parameter async arrow → vi.fn over (_url: string, _init?: RequestInit). At cause: the stub is vi.stubGlobal('fetch', fetchSpy), every non-test fetch( in the package (13 sites in cloud-connection-plugin.ts, marketplace-install-local-plugin.ts, marketplace-proxy-plugin.ts) passes a string URL and an init object, and the tests index mock.calls[0]![0] / [1], which the zero-arity declaration typed as an empty tuple (TS2493 x11, the deleted row's own note). Mock typed to the call it stubs; Response bodies byte-identical.
  • observability/src/__tests__/error-exporters.test.ts, loggers.test.ts, metrics-exporters.test.ts: the instance declared as its contract (ErrorReporter, Logger, MetricsRegistry, type-only import from ../contracts.js). At cause: contracts.ts declares captureException(error, context?), counter(name, labels?, value?), histogram(name, value, labels?), gauge(name, value, labels?); the Noop classes implement them with zero parameters (captureException(): void { }, debug(): void { }, counter(): void { }), a legal narrowing that gives the class type arity 0 (TS2554 x10). The test now calls through the contract a consumer uses; the Noop signatures in src/*.ts are untouched (no such file in the diff).
  • observability/src/__tests__/otlp-http.test.ts: fake-fetch parameter RequestInfo | URL → string | URL | Request. At cause: the program has types: ["node"] and no DOM lib, so RequestInfo is not a name in scope (TS2552, the deleted row's own code); the union is the call-site shape. The pre-existing as unknown as typeof fetch on the return is unchanged context.
  • hono/src/hono.test.ts: a KernelEnv type declaring Variables: { objectStack: { name: string } }, passed as the type argument to the Hono constructor in the two tests that read c.get('objectStack'). At cause: src/index.ts:277 does c.set('objectStack', kernel) and the test reads kernel.name; objectStackMiddleware's (c: any, next: any) at index.ts:276 is untouched. A declared context variable, no cast.
  • hono/src/__mocks__/runtime.ts: four fields annotated Mock (type-only import). vi.fn() with no implementation already infers Mock of Procedure, so the annotation names the inferred type rather than widening it.
    Every repair is at its cause; zero banned forms in code.

(c) Deviation — packages/adapters/hono/src/__mocks__/runtime.ts. Verified at the head: files[] is dist, README.md, CHANGELOG.md; the build is the root tsup.config.ts with entry: ['src/index.ts'] (hono has no tsup config of its own), so dist and its .d.ts are the bundle of src/index.ts and its import graph; src/index.ts imports hono, hono/cors, @objectstack/plugin-hono-server, @objectstack/runtime and nothing under __mocks__ — git grep __mocks__ on the head over packages/adapters/hono/ hits only vitest.config.ts:44, the alias that maps @objectstack/runtime to src/__mocks__/runtime.ts for the vitest run alone. So dist cannot contain it. The build tsconfig.json includes src/**/*, so both legs of typecheck read it, which is exactly why it had to compile. Judgment: test layer by every functional test — not shipped, not built, vitest-only, imports vitest, __mocks__ convention — and under src/ by path alone. The claim's exclusion reads "any source change in src/**"; the direction's concern is a widened source signature to make a test compile, and this is an annotation on a test double that names its inferred type. Declared in the report and the PR; accepted, with the note that the claim's path wording, not the dev, was the blunt instrument.

(d) Deviation — Array#at → mock.lastCall, marketplace-install-local-bundle.test.ts lines 81 and 106. register = vi.fn(); mock.calls.at(-1) is the last element of mock.calls, vitest's mock.lastCall is the arguments of the last call — the same value when called, undefined in both when never called, the same ! and [0] either way, the same any element type. The comment above line 81 ("the LAST register call is the installed package") is the assertion's meaning and is unchanged. The build config's lib is the root's ES2020 and the first typecheck leg reads the tests under it, so .at would have kept that leg red; the shared build config was not widened — the service-storage call in ebb5550917 (TS2550 rewritten rather than lib widened). Meaning identical.

(e) scripts/check-type-check-coverage.mjs. One hunk (@@ -805,19 +805,32 @@). Non-comment changed lines: exactly the 12 deleted lines of the three DEBT rows (cloud-connection 13, hono 3, observability 11) — deleted, not lowered; every added line is a // comment. No function, constant, regex or import moved. The spec-monorepo row (errors 26, compositionAt 80, note) lies outside the hunk and is byte-identical. The graduation paragraph, clause by clause: "entries 13 / 11 / 3" equals the deleted rows; "no typecheck script at all" equals the base manifests; "BUILD tsconfig.json does NOT exclude tests" equals the three configs (include src, exclude node_modules / dist only); "13 vs 11, 11 vs 11, 3 vs 3" is the dev's reading and is consistent with the deleted notes (cloud-connection "code-tier 11 + 2 config-tier", observability "TS2554 x10, TS2552", hono "TS2769/TS18046"); the repair list matches (b); "TS2883" is the code this repo already uses for the cannot-be-named family (nine i18n-extract.config.ts headers, packages/spec/scripts/check-entry-nameability.ts). Accurate.

(f) Test lines. Changed lines matching it( / test( / describe(: 0. Changed lines matching expect(: one pair, marketplace-install-local-bundle.test.ts:106, calls.at(-1)! → lastCall!, meaning-identical per (d). So the report's "touches no it/test/describe line" is true, and it does touch one assertion line, which the report's own Array#at deviation names. Larger hunks spot-checked: the 7 fetchSpy hunks change only the arrow's parameter list; the hono tests' bodies at the head (lines 36-70) are unchanged apart from the constructor's type argument. The tests still test what they did; CI Test Core (all six shards and the aggregate) success on this head.

② Semver level

skip-changeset holds; a patch is not owed. Manifest delta: the scripts block of three package.json gains one key; dependencies, devDependencies, exports, files untouched; no pnpm-lock.yaml change; nothing under files[] (dist, README.md, CHANGELOG.md) changes, and dist is built from src/index.ts, which no test or __mocks__ file reaches. All three packages are public (no private, publishConfig.access: public, members of the Changesets fixed group — which check-changeset-fixed.mjs reports spans all 69 public packages, so a patch here would version every public package for a dev-script key). AGENTS.md owes a changeset for "anything that publishes — feature, functional improvement or fix" and reserves skip-changeset for "a diff that publishes nothing from any released package"; nothing a consumer installs or runs changes — scripts.typecheck is a workspace dev script, and package.json reaching the tarball with one more script key is not a fix, feature or functional change. Precedents: 45a72b0cc1 (verify, public, scripts-only manifest delta) and 8cf806f690 (seven public packages, scripts-only manifest deltas) both landed with no changeset; ebb5550917 (service-storage) wrote a patch and gave its own reason — "the published package.json gaining typecheck / check:test-typecheck scripts and a tsx devDependency" plus a lockfile move — and the devDependency and lockfile are what distinguish it; b103baf07e (http-conformance) is private: true and says nothing about a public package. This PR's delta is the verify / seven-package shape. Check Changeset: failure on the run before the label, skipped on the re-run after it — the workflow's documented author opt-out, and the re-run is the converged latest. The dev's deviation names the patch alternative honestly; declining it is right.

③ Boundary flags

Deviations, each judged:

  1. origin/main (9b0de7de73) merged mid-task, merge commit 5f4aa15801. The net diff against 9b0de7de73 is the 16 files and nothing else. git merge-tree --write-tree origin/main refs/review/pr-21038 (origin/main 2f2fa11d75, the PR's base) exits 0 and writes tree c1ec068fbc; none of the 16 paths is merge=os-regen routed (git check-attr merge reads unspecified for all 16), so the local reading is not driver-flattered. Clean.
  2. __mocks__/runtime.ts under src/: accepted as test layer, ① (c).
  3. Zero-residue bare typecheck form: accepted shape, ① (a); no tsx devDependency and no lockfile change follow from it, which is the point.
  4. Array#at → mock.lastCall: meaning identical, ① (d).
  5. No changeset: ②.
  6. Model-free commit trailers: a repo rule, not a boundary; noted.
  7. Post-report cleanup: housekeeping.

Out-of-scope observation (TS2883 withheld while semantic errors stand). The DEBT doc-block says every entry "was measured by running the package's own tsc --noEmit", and MEASURED reds only when a re-measure EXCEEDS the recorded number. If tsc withholds declaration-emit diagnostics until the semantic set is empty, a recorded count is a floor, not a census, and the surplus surfaces exactly when an author pays the entry down — which the ratchet already anticipates (AGENTS.md: "when a re-measure forces a count up, rewrite the entry's note too"). A trap for the next author, not a defect of the gate: growth is red by MEASURED and nothing lands green through it. On spec-monorepo: the root tsconfig.json carries declaration: true, so the same withholding can sit behind its 26; the row is card-excluded, untouched, and its note already says the count moves with scripts/. On the method: the observation now lives where the next author reads, the DEBT doc-block above the ledger and the hono sibling header — the acceptance-notes branch of Prime Directive #10, not the file-an-issue branch (no reproducible defect: the gate did what it documents). "carrier: none" is right.

Check-runs on the head, collapsed latest per name, converged (35 names, 0 pending at the final read): required seven all success — Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard. Also success: Type Check · source gates, Type Check · debt ledger, Type Check · workspace, Type Check · consumer gates, Dogfood Verify CLI, all six Test Core shards, all three Dogfood Regression Gate shards, the four claim/card guards, Check Documentation Links, Flag docs affected by code changes, filter. skipped: Check Changeset (label opt-out, re-run after the label), Auto Label, Check PR Size, Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in). One failure, advisory and inherited: Validate Package Dependencies (job 110199998672) — the OSV-scanner step on pnpm-lock.yaml (dompurify 3.4.13, next 16.3.3; every earlier step of that job passed), this PR touches no lockfile, and the identical check is failure on the origin/main head 2f2fa11d75 (job 110199937081); not in the required set. Merge-tree onto origin/main: clean (above). The PR is draft with mergeable_state: blocked, which is the expected state for a Tier-free diff awaiting the seat.

Implemented-by: claude/issue-20800-typecheck-graduation
Reviewed-by: session_01VvcEokUG1tvVxkceYfR5XB

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Queue hold: Validate Package Dependencies is red on this head for advisories in main's lockfile, not for this diff

domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-10-01T03:44Z


Generated by Claude Code

…pecheck-graduation

Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 07:55
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 1, 2026 07:55
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit 3c3a05c Oct 1, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20800-typecheck-graduation branch October 1, 2026 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

type-check coverage: graduate the last three DEBT packages (cloud-connection, hono, observability) — the remainder of #4311

2 participants