Skip to content

[finding] No plugin package has a tsc program that compiles its tests — plugin-approvals alone hides 345 type errors, and 0 of 14 are in the test-typecheck ledger #14062

Closed

Description

@claude

Found while working #13568 (approvals record-delete linkage). Out of that card's scope — recorded, not fixed. Same class as #13676 (packages/objectql) and #12542 (packages/rest, closed); this instance is the packages/plugins/** family, which no card names yet.

Measurement

packages/plugins/plugin-approvals/tsconfig.json ends with:

"include": ["src/**/*"],
"exclude": ["dist", "node_modules", "**/*.test.ts"]

and the package's typecheck script is tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json — no test project. So no tsc program in this package compiles a single test file; the only thing that touches them is esbuild type-stripping under vitest, which type-checks nothing.

Measured on origin/main at e612dd0f5, by copying the package tsconfig with "exclude" reduced to ["dist", "node_modules"] and running tsc --noEmit -p against it:

345 errors

Overwhelmingly one class — openNodeRequest returns ApprovalRequestRow | ApprovalNodeAutoOutcome and test callers read .id / .pending_approvers straight off the union:

src/admin-exemption-retired.test.ts(215,16): error TS2339: Property 'pending_approvers' does not exist on type
  'ApprovalRequestRow | ApprovalNodeAutoOutcome'.

Why it is worth a card rather than a shrug

The count is not the point; the channel is. AGENTS.md and #12542 name this exact shape as how a compile-time pin becomes a phantom check: a @ts-expect-error in a file no tsc program compiles asserts nothing, and deleting it is invisible. This package has real compile-time pins — exec-context-annotation.pin.ts, manager-org-screen-parity.contract.test.ts — living in that untyped zone.

Scope: the family, not this one package

The gap is not specific to approvals:

measure count
packages under packages/plugins/ 14
…with a tsconfig.test.json 1
…with a check:test-typecheck script 0

packages/spec and packages/client both run check:test-typecheck against a tsconfig.test.json with the shrink-only test-typecheck-debt.json ledger. No plugin package is inside that instrument at all — so the ledger's own "shrink-only" guarantee says nothing about a third of the repo's runtime surface.

What this card is NOT asking for

⛔ Not "add 345 errors to test-typecheck-debt.json". That ledger is an EXACT, shrink-only ratchet and expanding it is maintainer-only (#5286). The decision this card wants is the same one #12542 answered for rest: whether the plugin family gets a checked test zone, and if so on what schedule and with what starting ledger — or whether it is deliberately left out, in which case the compile-time pins living there should be re-sited somewhere that actually compiles them.

No repro of a live defect — nothing is currently broken by this; it is a missing instrument, which is why it is filed as an observation rather than a bug.


Generated by Claude Code

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Sep 1, 2026
  2. huangyiirene commented on Sep 1, 2026

    @huangyiirene
    Collaborator

    裁决:全家族收编 —— packages/plugins/** 进 test-typecheck 仪器族(维护者 2026-09-01,总监批 #26)

    项目总监席 · session session_01KGtaLpkW1mycWgkbSb3H6t · 维护者对本批逐字:「同意」。

    1. 收编:14 个插件包全部进入 check:test-typecheck 仪器族 —— 每包 tsconfig.test.json + 起始台账条目按各包实测数入册(shrink-only,plugin-approvals 从 345 起);
    2. @ts-expect-error 退役 pin 在 packages/spec 里是幽灵检查:tsconfig 把 **/*.test.ts 排除出唯一的 tsc --noEmit #5286 要求的维护者授权由本裁决携带:台账扩张的成本判断(起始台账 + 永久义务 + 14 包量级)已呈维护者并获准 —— 分诊席推荐的更小一支(只声明范围 + 迁两个钉)如实入册为被考虑过的备选,未采:仪器对运行时面三分之一保持沉默的空洞,比 14 份 tsconfig 的机械成本更贵;
    3. 实施序:plugin-approvals 先行(345 的实测基线),其余 13 包机械跟进,可分批 PR;⛔ 不是「修 345 个错误」—— 是让棘轮开始看见它们,修复照棘轮自身的只减纪律走;
    4. 既有编译期钉(exec-context-annotation.pin.ts、manager-org-screen-parity.contract.test.ts)随收编自动变真,⛔ 不迁址;
    5. 相邻不合卡维持:[finding] packages/objectql's tsc program excludes **/*.test.ts, so its whole test corpus is type-exercised only by esbuild type-stripping — the same shape #12542 closed for packages/rest #13676(objectql 同类)各走各的;「12 包缺 typecheck 脚本」的结转项是不同缺口,⛔ 不并入。

    状态转移(同笔)

    needs-user-decision → pm:queue;priority:p2 domain:devx finding 不动。


    Generated by Claude Code

  3. added
    pm:queueReady for the PM dispatch loop
    and removed
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Sep 1, 2026
  4. huangyiirene commented on Sep 2, 2026

    @huangyiirene
    Collaborator

    Triage bookkeeping (R+90, triage seat, session session_019kDRpB7D2XzVzkaLp57T5D), two label writes, ⛔ no re-grading:

    1. pm:blocking applied — derived from the reverse index, ⛔ not hand-judged: [finding] service-cluster tests compile under no tsc program — a TS2322 sits in memory.contract.test.ts, the package's contract witness (services-family instance of the #14062 class) #14181 (packages/services/** instance of this class) carries Blocked-by: #14062 and is open, so this card has an open downstream dependant and the derived cache should say so. It raises this card's position in the lane's selection order; the grade (p2) is untouched.
    2. finding removed — this card was graded (pm:queue + domain:devx + priority:p2) rounds ago and the label should have come off then; grading is leaving the box.

    The class decision this card carries is unchanged and is what #14181 and #14173 wait on: checked test zone vs. deliberate exclusion with pins re-sited.


    Generated by Claude Code

  5. added
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Sep 2, 2026
  6. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 2, 2026
  7. claude commented on Sep 2, 2026

    @claude
    ContributorAuthor

    Claim: domain:devx execution seat (#6023), R1 wave 7.
    Session: session_01WLJQhde67SeTccsmnBVarV (subagent dev shares this ID)
    Branch: claude/issue-14062-plugins-test-typecheck
    Worktree: ../objectstack-14062
    Domain: domain:devx · pm:queue → pm:dispatched + assignee in one label write, read back and matching. pm:blocking kept (derived cache, #14181 waits on this card).
    File surface: packages/plugins/*/tsconfig.test.json (14 new), packages/plugins/*/package.json (check:test-typecheck + gen:test-typecheck-debt + typecheck wiring), packages/plugins/*/test-typecheck-debt.json (generated by scripts/check-test-typecheck.mts --update, EXACT per-signature), and scripts/check-type-check-coverage.mjs only if its declared package population must name the new programs. ⛔ Not scripts/check-test-typecheck.mts itself unless a self-test needs a plugin-family case.
    Container & model: os-dev subagent, opus. Tier readout (dispatch-gates.mjs --tier, derived on origin/main@1dcb995f2): no path-derived mandate — floor sonnet · default opus · ceiling fable. Clause ②: NO — no contract accept/reject change, no public surface widened; the ledger expansion is maintainer-only (#5286) and the director ruling of 2026-09-01 (comment 5494659007, 「同意」) carries that authority — PR body quotes it.
    Serial constraints: none on the hot-file queue. #14173 (packages/lint, same class) and #14181 (packages/services/**) wait on this card and are ⛔ not folded in (ruling item 5).


    Generated by Claude Code

  8. claude commented on Sep 2, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 14062,
      "status": "done",
      "branch": "claude/issue-14062-plugins-test-typecheck",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/14420",
      "premise_still_valid": true,
      "summary": "All 14 packages/plugins/** packages are in the check:test-typecheck instrument: each has a tsconfig.test.json its typecheck script names, and the four measuring non-zero carry generated EXACT shrink-only starting ledgers (plugin-approvals 324/8 files, plugin-auth 94/10, plugin-sharing 3/2, knowledge-ragflow 3/1). The ten measuring zero deliberately carry NO ledger: verified reading — check:test-typecheck treats a missing ledger as empty entries and prints '0 file(s) / 0 error(s)', under which any error is red immediately with no entry to be added to, strictly stronger than a ledger holding nothing (the call plugin-security recorded for itself in #13176); gen:test-typecheck-debt is wired only where a ledger exists, so no package owns a generator whose only output would be an empty file. No test file is edited (ruling item 3). PREMISE: the card's load-bearing measurement reproduces EXACTLY — 345 by the card's own recipe at today's HEAD; the ledger reads 324 because vitest module semantics subtract 21 config-tier diagnostics (20xTS2550 lib, 1xTS1470 import.meta), both numbers recorded in the config header and the ledger _note. TWO MEASURED CORRECTIONS to the card, both in the PR body: (1) the headline 'no plugin package has a tsc program that compiles its tests' is over-general — 9 of the 14 never excluded their tests and tsc --noEmit already compiled them at zero errors; the real gap is 3 packages hiding tests behind **/*.test.ts (approvals 35, auth 87, sharing 30) plus knowledge-ragflow, silent the other way (tests in the build program but NO typecheck script existed, so nothing ran it) = 153 test files newly under a running type check; (2) exec-context-annotation.pin.ts was never a phantom pin — it is a .pin.ts, which **/*.test.ts never matched, so it was in the build program all along (control measured in ablation B: deleting a directive reds the BUILD config too). The pin ruling item 4 genuinely makes real is manager-org-screen-parity.contract.test.ts. Repo-wide sweep: NO @ts-expect-error in packages/plugins/** was actually phantom before this PR. SIDE EFFECTS, all forced by gates and documented in place: 4 graduated entries deleted from check-type-check-coverage.mjs (approvals/auth/sharing from TEST_DEBT, knowledge-ragflow from DEBT) with a block recording that none was PAID DOWN — the same errors moved to a finer per-file/per-signature instrument; a #11490 re-baseline in check-type-source-resolution.mjs meeting all three of its doc-block conditions with before/after stated in place (98 -> 111 programs, 54 -> 55 entries, 242 -> 269 pairs at merge base 79b6a22a5); 4 NOT_DRIVER_MANAGED rows in regen-artifacts.mjs; knowledge-ragflow's build tsconfig now excludes tests, because check:type-check-coverage refuses a typecheck that never invokes tsc and this card is not chartered to repair its 3 errors — ⛔ that is NOT the repo-wide 'packages missing a typecheck script' carry-over the ruling keeps separate in item 5. plugin-hono-server's config omits the lib override, measured: pinning lib ES2022 strips the DOM default its non-root-extending tsconfig relies on and manufactures 34 errors, 26 inside node_modules .d.ts.",
      "tests": "Union run at b7413a964 (git rev-parse --short HEAD from that run), workspace closure built first via `pnpm exec turbo run build --concurrency=2 --filter='./packages/*' --filter='./packages/*/*'` under the shared verify lock (VERDICT command-exit 0). ALL 14 packages `pnpm --filter @objectstack/PKG typecheck` EXIT=0, each printing its own gate verdict line, e.g. 'check:test-typecheck: OK - @objectstack/plugin-approvals's test layer compiles under packages/plugins/plugin-approvals/tsconfig.test.json; 8 file(s) / 324 error(s) / 27 pinned signature(s)'. Derived family (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, re-derived from the FINAL diff, 38 paths vs merge base 79b6a22a5, 25 families) all EXIT=0: agent-test-spelling, bash32-floor, changeset-gate-self-tests, cli-command-ids, cross-package-test-inputs, doc-authoring, dual-build-cjs-loads, entry-guard, merge-driver, objectui-changeset, override-consistency, page-declaration-shape, parse-guard, pm-half-states, pnpm-filter-targets, published-files, ratchet-remedy-authority, service-providers, slot-lookup, test-source-alias, turbo-task-graph, type-check-coverage, type-check-debt, type-source-resolution, watch-hint-literal; plus the filter-scoped `--filter @objectstack/spec run check:llms-txt`, the convention-triggered check:i18n / check:i18n-stale-fill, check:nul-bytes, check:declared-population-live. Key verdict lines quoted, not inferred from a bare exit code: 'check-type-check-coverage: OK - 68/78 workspace packages type-checked'; 'check-type-check-coverage --re-measure: OK - 23 ledger entr(ies) re-measured in 287.7s, 771 raw tsc error(s) total, none above its recorded number' with 'surplus: none - every entry sits exactly at its measurement, so any new error is red'; 'check-type-source-resolution OK - 111 tsc program(s) across 77 packages scanned; 55 registered'. dual-build-cjs-loads and type-check-debt exit 3 = PREREQUISITE NOT MET without the whole repo's dist/ - read as NOT MEASURED, then re-run green after the closure build (both reported above). Every gate's exit code captured by redirect-then-read (cmd > file 2>&1; EXIT=$?), never through a pipe. ABLATION A (the load-bearing one - a file in NO tsc program on main): target packages/plugins/plugin-approvals/src/manager-org-screen-parity.contract.test.ts, run from a COMMITTED implementation. Mutation confirmed ON DISK before any reading, anchored on the injected text: 'injected marker occurrences: 1', pre-mutation hash a010631b3d220db32d6eaa36a1ea5d23c266650c == HEAD blob, post-mutation hash d6470da16c16cb3111954d23295874471b9b781b (different, non-empty). MUTATED EXIT=1 with the exact everyday verdict: 'src/manager-org-screen-parity.contract.test.ts: 2 type error(s) in a file the ledger does not cover ... MAINTAINER-ONLY'. RESTORE leg proven by observed state, not exit code: `git checkout HEAD -- ABSPATH` (HEAD named explicitly, never a bare checkout, and the trap path absolute from git rev-parse --show-toplevel), restored hash a010631b3d220db32d6eaa36a1ea5d23c266650c == HEAD blob, `git diff HEAD -- path` EMPTY, marker occurrences 0, RESTORED EXIT=0 back at 8 files / 324 errors / 27 signatures. NO REBUILD was needed for either leg and that is stated rather than assumed: the mutated file is the package's OWN source compiled from src by this program, not a dependency reached through dist/ exports, so no dist/ marker can survive into the next run. ABLATION B (the PM-requested pin deletion, with its control): deleting a @ts-expect-error in exec-context-annotation.pin.ts - directives before 1, after 0, post hash 0f45c8617a91937fbaa8479efb1d1aabc337714c != HEAD blob 502cebae33222e7b3f7d451c9e9217ebc80b4d95 - MUTATED EXIT=1, and the CONTROL shows the same deletion also reds the BUILD config (EXIT=2, TS2322 at 135,39), which is measurement correction 2. Restored hash == HEAD blob, git diff HEAD empty, directives back to 1. LINT, narrowed with the narrowing MEASURED (all three pieces): (1) the population is ESLint's own, not my guess - passing all 38 changed paths to `eslint --no-inline-config --format json`, 35 come back carrying 'File ignored because no matching configuration was supplied.'; (2) the count is read from that JSON - the 3 files ESLint does judge (the edited scripts/*.mjs) report errorCount 0, warningCount 0; (3) invariance for untouched files - this repo runs one eslint.config.mjs which, in its own words, 'never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file', so no untouched file's verdict is a function of this diff. vitest: @objectstack/knowledge-ragflow (the only package whose BUILD tsconfig this PR edits) 1 file / 9 tests passed; no runtime source is edited anywhere in the PR, so no other suite is in the change's radius. Script self-tests for the three edited scripts, all green: check:type-check-coverage --self-test (47+65+43+28+19+18 cases), check:type-source-resolution --self-test, and check:merge-driver (= git-merge-regen --self-test, the reconciliation that reads regen-artifacts.mjs). git grep over *.test.* finds no vitest suite that references any of the three as code.",
      "mcp_calls": "0 — the whole run used REST via curl (issue body, comments, PR create, PR read-back); zero MCP GitHub tool calls",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  9. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Sep 2, 2026
  10. claude commented on Sep 2, 2026

    @claude
    ContributorAuthor

    ACCEPT — #14062 landed. PR #14420 (head b7413a96) merged to main as ad54eb342 via the merge queue (auto-merge armed, zero rework; the auto_merge: null reading at ~06:40Z was the PR mid-queue, not disarmed).

    Close-out probes executed on origin/main (fresh fetch, throwaway worktree — not the shared checkout; ad54eb342 confirmed as an ancestor of origin/main):

    • packages/plugins/plugin-approvals/package.json:17 "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json && pnpm check:test-typecheck" and :19 the check:test-typecheck script — the test-typecheck leg is now part of the package's own typecheck, not a separate opt-in.
    • scripts/check-type-check-coverage.mjs carries the graduation block: plugin-approvals (345), plugin-auth (94) and plugin-sharing (3) left the TEST_DEBT ledger on 2026-09-02; knowledge-ragflow (4) left DEBT; the per-package test-typecheck-debt.json ratchets read 324 / 94 / 3 / 3. Control: @objectstack/runtime still 1 hit in the ledger, so the file is read and the removals are the diff.
    • Merge touched 38 files: 14 tsconfig.test.json, scripts/check-type-source-resolution.mjs, scripts/regen-artifacts.mjs, the per-package debt files, and .changeset/plugins-test-typecheck-onboarding.md.

    pm:dispatched stripped in this same step (pm:blocking / priority:p2 / domain:devx kept — ownership is not state; pm:blocking is the triage sweep's cache and is cleared by that sweep, not here). Downstream cards that carried Blocked-by: #14062 — #14386, #14173, #14181 — are now unlocked for the next selection pass (their file surfaces get re-verified on ad54eb342 before dispatch).

    Seat: session_01WLJQhde67SeTccsmnBVarV (domain:devx, #6023).


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:devxDomain lane — seat card indexed by label:pm:seatpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions