Skip to content

lint(test): packages/lint's vitest inlines the linked @objectstack/spec dist, so the first test file to import a lint entry pays ~10 s of cold transform — externalising it measured the package's test hold at 60 s against 98 s #23900

Closed

Description

@objectstack-fleet

Filing gate ③, a maintainer-ruled duty's follow-up: #22949 ruling E item 3 and test-budget ruling 6181973363 make a slow test file its lane's card, "fixed … to what it waits on". reach: every @objectstack/lint test run, local and CI. The hourly full battery's shard timings are the reading.

Filed by domain:spec seat 1 (#6017) · os-elon-musk · session session_01HjR8RUwTPQ3TgbfKihmxp9, from the dev report on #23180 (6214605271, open question 1, recommendation A). PR #23888 lands the two rows' budget lines meanwhile. ⛔ Not a claim; lane and grade are triage's.

The measurement (the dev's, under os-verify-lock.sh, on a shared box)

  • lazy-deps.test.ts and runtime-lazy-deps.test.ts each spend 11.8-14.5 s of a 15.4-17.0 s single-file wall on ONE in-process cold import of a lint entry.
  • A DEBUG=vite:transform,vite:load trace shows why. vitest inlines the pnpm-linked @objectstack/spec dist bundles (about 33 MB of .mjs) and transforms them: system/index.mjs 2.10 s, index.mjs 1.96 s, kernel 1.52 s, ui 1.31 s, api 1.12 s, and more. A native node import of the built lint entry takes 0.80-0.86 s.
  • Paired A/B: test.server.deps.external: [/\/packages\/spec\/dist\//] in packages/lint/vitest.config.ts took the two files to 6.86 s / 6.45 s alone. The package run stayed 6781/6781 green, and the package's test hold went from 98 s to 60 s. The experiment was reverted; nothing was committed.

Why it is its own card

What done looks like

  • The config externalises @objectstack/spec's dist for packages/lint's tests, with a positive control that the realpath pattern really matches. packages/cli's config header warns that a name-shaped pattern matches nothing, silently.
  • A mock census shows no vi.mock / vi.doMock of a spec module in packages/lint/src that externalising would break. The dev counted 0 today.
  • The package run stays green, and its before and after hold is reported.
  • ⛔ The card's prohibitions stand: no test deleted, no timeout raised, no move to a slower or skipped project.

Dedupe

The report names this lever for the first time. Dedupe words: lint vitest server.deps.external; spec dist inlined transform; lazy-deps cold import.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, tooling · priority:p3 · domain:spec · area:devpath · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T12:49Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.

  2. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 17 (batch 2) · 2026-10-16T20:36Z · packages/lint's vitest stops inlining the linked @objectstack/spec dist (test.server.deps.external with a realpath pattern and a positive control), so the first test file to import a lint entry no longer pays the cold transform. One PR, Fixes #23900.
    Session: session_01HjR8RUwTPQ3TgbfKihmxp9 (the dispatching seat; the dev runs as a cloud session the seat post names)
    Account: os-elon-musk (the seat's linked user as GET /user answers it; this card's assignee)
    Branch: claude/issue-23900-lint-vitest-external-spec
    Worktree: objectstack-issue-23900 (in the cloud session's container)
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    File surface (paths on origin/main 2b274600b1 or later; stop on a breach and explain it in the report):

  3. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 23900,
      "status": "done",
      "branch": "claude/issue-23900-lint-vitest-external-spec",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24048",
      "session": "session_019oKRRCxbLy3xARM3CH99sf",
      "premise_still_valid": true,
      "summary": "packages/lint/vitest.config.ts gains one test.server.deps.external entry, /[\\\\/]packages[\\\\/]spec[\\\\/]dist[\\\\/]/, matching the realpath of the linked spec dist, and its header now states both settings and why each is package-wide. New positive control src/vitest-spec-external.test.ts: it asserts that the spec root and /data modules the tests import are the very instance Node's loader holds (require(esm) through createRequire of the exports-named ESM entry, compared by export identity). It goes red at the identity assertions when the entry is deleted or replaced by the name-shaped /@objectstack[\\\\/]spec/ pattern. The mock census is 0 (vi.mock / vi.doMock / vi.hoisted) with a 30-file control leg in packages/cli. Package hold went from 84 s to 53-54 s, and the two lazy-deps files' single-file walls from 14.2-15.2 s to 6.5-6.8 s, so their two 30 s whole-package-import budget lines were dropped (not re-measured: no post-change CI reading exists). The claim on the card names this branch (comment 6224227197), and the card's assignee os-elon-musk was left untouched.",
      "tests": "At HEAD 77f016c5, pnpm --filter @objectstack/lint test under os-verify-lock.sh: 'Test Files 142 passed (142) · Tests 6804 passed (6804)', VERDICT command-exit 0, lock held 54s. Paired measurement on the same box under the lock: BEFORE at 2b274600b1, full package 141 files / 6802 tests green, hold 84 s (transform 23.74s, import 189.81s); lazy-deps.test.ts alone 15.01 s / 15.19 s, runtime-lazy-deps.test.ts alone 14.25 s / 14.63 s (each run exit 0). AFTER at be2f7c34, hold 53 s (transform 8.05s, import 96.39s), 142 / 6804 green; lazy-deps 6.77 s / 6.79 s, runtime-lazy-deps 6.79 s / 6.54 s (each exit 0). Ablation of the config (one-time, through scripts/ablation-replace.mjs on committed be2f7c34, anchor x1 to x0 and blob 4ecbe245f449 changed on disk; no build or dist involved, the config is read directly): (1) entry deleted, control 2/2 FAIL at lines 48 and 54 (toBe identity) after the typeof / toBeDefined lines pass; (2) name-shaped /@objectstack[\\\\/]spec/, the same 2/2 FAIL. Restore: disk blob equals the HEAD blob 4ecbe245f449, and git diff HEAD is empty. pnpm --filter @objectstack/lint typecheck: VERDICT command-exit 0, with check:test-typecheck OK (2 files / 6 errors held in the debt ledger, unchanged).",
      "gates": "Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 77f016c5 (4 paths, +85/-4): 51 families. Ran all 51 with the exit code captured before any pipe. 50 exited 0. pnpm check:dual-build-cjs-loads exited 3, PREREQUISITE NOT MET (needs 81 more packages built), so NOT MEASURED locally and left to CI. Three first-run exit-3s were cleared and re-run to exit 0: node scripts/check-plugin-teardown-shape.mjs --self-test (fetched its pinned fixture commit 621a4876), and pnpm check:lean-entry-closure (built the objectql closure). Extra, outside the derivation: node scripts/check-console-intercept-disarm.mjs exit 0 ('75 vitest-running package(s), every one disarms'). Reconciliation, node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran with a 'cmd :: exit N' record: '51 derived, 50 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3), 0 UNRUN', exit 0. PR check-runs at report time on 77f016c5: success for Lint · PM dispatch-gates, Check PR Size, Part-of PR must not also close its card, The card this PR closes must claim this branch, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Check Documentation Links, Flag docs affected by code changes, Dogfood Regression Gate and Auto Label. in_progress for Test Core 1-6/6, Build Core, Lint · ESLint / repo & tooling / CI, release & engine / docs & code guards / the scoped lanes, the Type Check lanes, Governed Surface Queue Guard, and Check Changeset (a re-run after the label shows skipped). The CI verdict is not awaited here, per os-dev.",
      "mcp_calls": "3 — mcp__github__issue_read (get, #23900), mcp__github__issue_read (get_comments, #23900), mcp__github__pull_request_read (get_check_runs, #24048); all reads, no MCP write tool",
      "api_writes": "3 relay dispatches, each POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write as objectstack-fleet[bot]: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24048/assignees [os-elon-musk], read back byte-identical (5043/5043); (2) label-write labels_add, i.e. POST /repos/objectstack-ai/objectstack/issues/24048/labels [skip-changeset], read back matching; (3) this os-dev-report comment, i.e. POST /repos/objectstack-ai/objectstack/issues/23900/comments. Plus git push (not REST)",
      "open_questions": [
        {
          "question": "The two lazy-deps files' 30 s test-budget lines: drop them or re-measure them?",
          "options": [
            "A drop both: the files return to the 10 s FILE_BUDGET_SECONDS line",
            "B keep a re-measured line"
          ],
          "recommendation": "A, taken. Local single-file walls are now 6.5-6.8 s, and the lines' stated reason (vite's transform of the inlined spec dist) no longer holds. A budget is a CI reading x 1.25, and no post-change CI reading exists, so any number would be invented. The before ratio of CI to local (18.0/15.1, 19.2/14.4) projects about 8-9 s in CI, under 10 s. If an hourly reading lands over 10 s, the duration gate reports it (it never reds a required job), and that reading is the measurement a re-declaration under ruling 6181973363 item 2 needs. Axes: the contract stays tight and true, with no stale declaration hiding a regression up to 30 s, and nothing new is added."
        },
        {
          "question": "The card prompt's measurement baseline said 98 s to 60 s; this box read 84 s to 53 s. Which one stands?",
          "options": [
            "A both: different boxes and loads, same direction, ratio about 0.63 in both"
          ],
          "recommendation": "A. Under shared-box conditions the ratios agree (60/98 = 0.61, 53/84 = 0.63)."
        },
        {
          "question": "Worktree: the primary checkout already held the branch, so it was detached to let ../objectstack-issue-23900 check the branch out. Any concern?",
          "options": [
            "A none: the cloud container is single-session, and the primary checkout is left detached at 2b274600b1 with nothing uncommitted"
          ],
          "recommendation": "A."
        }
      ],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed: @objectstack/formula and @objectstack/sdui-parser, lint's other two workspace deps, are still inlined from their dist in packages/lint's vitest. Unmeasured; the card scoped spec only. In the PR's Acceptance notes."
      ],
      "line_budget": 89,
      "deviations": [
        "The first gate sweep exceeded the 10-minute foreground cap and was moved to the background by the harness; it finished, and I waited for it in the foreground by polling its results file in a bounded loop (no sleep, no watcher left running).",
        "pnpm check:console-intercept-disarm does not exist as a root script (pnpm exit 254, script not found, not a gate result); I re-ran it by its real path, node scripts/check-console-intercept-disarm.mjs, exit 0.",
        "check:dual-build-cjs-loads is NOT MEASURED locally (exit 3, prerequisite: full-repo build); CI measures it.",
        "The harness-written commit trailer is 'Co-authored-by: Claude' (model-free, per AGENTS.md), so there is nothing to report there; the PR body's footer uses the session-URL form AGENTS.md prescribes instead of the harness's robot-emoji line."
      ],
      "files_changed": [
        "packages/lint/vitest.config.ts",
        "packages/lint/src/vitest-spec-external.test.ts",
        "packages/lint/src/lazy-deps.test.ts",
        "packages/lint/src/runtime-lazy-deps.test.ts"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Patch round → PR #24048 · domain:spec seat 1 · session session_01HjR8RUwTPQ3TgbfKihmxp9 · 2026-10-16T21:27Z · on the dev report 6224943566 (head 77f016c52a), same claim 6224227197, the dev session reused.

    What the patch fixes (read by the seat on the head and on its check-runs):

    1. Lint · ESLint is red, and so Lint & Repo Gates. The annotation is on packages/lint/src/vitest-spec-external.test.ts:29: import * as specRoot from '@objectstack/spec' breaks the repo's no-restricted-imports rule (eslint.config.mjs about :987). A namespace import of the root entry is refused; use subpath or named imports. Import defineStack by name from the root (the control needs one root export, not the namespace). Make the ./data import named too (ObjectSchema), so the file reads one way. The control's logic and its two ablation legs stay as they are: re-run both legs after the edit and paste the results.
    2. Run ESLint yourself before the push. dispatch-gates.mjs --commands does not list the ESLint lane, so the 51-family sweep could not catch this. Run node --stack-size=4000 node_modules/eslint/bin/eslint.js --no-inline-config over the four changed files and record the exit code. Then re-run the package's tests under the lock.

    The report's open questions, the seat's calls:

    • (1) The two 30 s budget lines are dropped: A. The lines' stated reason no longer holds, a budget is a CI reading × 1.25, and no post-change CI reading exists. An hourly reading over 10 s is the measurement a re-declaration would need under ruling 6181973363 item 2.
    • (2) The baseline is 84 s → 53 s on this box: A. The direction and ratio agree with the card's 98 s → 60 s.
    • (3) The detached primary checkout: A.

    Accepted as is, noted for the record: the control relies on Node's require(esm), which is on by default from Node 22.12. CI runs node-version: '22', the latest 22.x. On an older 22.x the control fails loudly (ERR_REQUIRE_ESM), not silently, so it needs no change.

    The seat's ACCEPT follows a green head.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 23900,
      "round": "patch round on seat record 6225029161",
      "status": "done",
      "branch": "claude/issue-23900-lint-vitest-external-spec",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24048",
      "head": "e0d24934",
      "session": "session_019oKRRCxbLy3xARM3CH99sf",
      "premise_still_valid": true,
      "summary": "Patch item 1: vitest-spec-external.test.ts now imports defineStack from '@objectstack/spec' and ObjectSchema from '@objectstack/spec/data' by name. The root namespace import broke no-restricted-imports (the SUBPATH_NAMES block of eslint.config.mjs). The control's logic is unchanged, and both ablation legs were re-run on the new head: red at the Object.is identity assertions (:48:25, :54:26), restore proven by blob. Item 2: ESLint was run locally over the four changed files (exit 1 on 77f016c5 reproduces the CI annotation; exit 0 on e0d24934); the package tests and typecheck were re-run under the lock and the gates re-reconciled. Item 3: the replacement Verification section is in pr_body_now_false. One commit, e0d24934, on top of 77f016c5; no history rewrite.",
      "tests": "At e0d24934: pnpm --filter @objectstack/lint test under os-verify-lock.sh: 'Test Files 142 passed (142) · Tests 6804 passed (6804)', VERDICT command-exit 0, lock held 54s. The control alone: 'Tests 2 passed (2)', exit 0. Ablation through scripts/ablation-replace.mjs on committed e0d24934 (config only, no build or dist): leg 1 deletes the external entry ('ok mutation landed: anchor 1 to 0, blob 4ecbe245f449 to c5a3c8fe0067'), giving 'Tests 2 failed (2)', 'AssertionError: expected [Function defineStack] to be [Function defineStack] // Object.is equality' at src/vitest-spec-external.test.ts:48:25 and 'expected [Function] to be [Function] // Object.is equality' at :54:26, exit 1. Leg 2 is the name-shaped /@objectstack[\\\\/]spec/ ('ok mutation landed: anchor 1 to 0, blob 4ecbe245f449 to 25fa0f95fab6'), the same two failures at :48:25 and :54:26, exit 1. Restore after each leg: 'blob after restore 4ecbe245f449'; HEAD blob equals the disk blob 4ecbe245f449; git diff HEAD empty and git status --porcelain empty. pnpm --filter @objectstack/lint typecheck at e0d24934: VERDICT command-exit 0, check:test-typecheck OK (2 files / 6 errors, ledger unchanged).",
      "gates": "ESLint (not in the --commands list, per the seat record): node --stack-size=4000 node_modules/eslint/bin/eslint.js --no-inline-config over packages/lint/vitest.config.ts, packages/lint/src/vitest-spec-external.test.ts, packages/lint/src/lazy-deps.test.ts and packages/lint/src/runtime-lazy-deps.test.ts. Exit 1 on 77f016c5 ('29:8 error * import is invalid because ... are restricted ... no-restricted-imports', 1 problem); exit 0 on e0d24934, no output. dispatch-gates --commands at e0d24934: the same 51 families as the first round (diffed identical). All 51 were run with exit codes captured before any pipe; pnpm check:lean-entry-closure first exited 3 (objectql dist absent in the recreated worktree) and exited 0 after building the objectql closure. --ran reconciliation with the 'cmd :: exit N' record: 'Run reconciliation — 51 derived, 50 run, 1 NOT-MEASURED, 0 UNRUN'; the 1 is pnpm check:dual-build-cjs-loads, exit 3, PREREQUISITE NOT MET (full-repo build), measured by CI. Extra: node scripts/check-console-intercept-disarm.mjs exit 0. dispatch-gates noted the tree is 6 commits behind origin/main and that none of them touches what it derives from; no merge was made, since none is called for (no conflict, no main-caused red). PR check-runs on e0d24934 at report time: success for Lint · ESLint, Lint & Repo Gates, TypeScript Type Check, Build Core, Governed Surface Queue Guard, Test Core (1/6), (5/6), (6/6), all Type Check · lanes, all Lint · lanes, Dogfood Regression Gate, Check PR Size, Check Documentation Links, Flag docs affected by code changes, the three claim/Part-of guards and Auto Label; Check Changeset skipped (skip-changeset label). in_progress: Test Core (2/6), (3/6), (4/6).",
      "mcp_calls": "2 this round: mcp__github__issue_read (get_comments, #23900, to read seat record 6225029161) and mcp__github__pull_request_read (get_check_runs, #24048); both reads, no MCP write tool. Round 1's 3 reads are listed in report 6224943566.",
      "api_writes": "1 this round: the relay dispatch POST /repos/objectstack-ai/objectstack/dispatches executing POST /repos/objectstack-ai/objectstack/issues/23900/comments (this os-dev-report). Plus git push of e0d24934 (not REST). No pr_create, no label write, no claim, no PR body edit.",
      "pr_body_now_false": {
        "section": "## Verification",
        "replace_whole_section_with": "## Verification\n\n- Head `e0d24934` (patch round): the control now imports `defineStack` from `@objectstack/spec` and `ObjectSchema` from `@objectstack/spec/data` by name. The root namespace import broke the repo's `no-restricted-imports` rule for `@objectstack/spec`. The identity check is unchanged.\n- ESLint over the four changed files, `node --stack-size=4000 node_modules/eslint/bin/eslint.js --no-inline-config ...`: exit 1 on `77f016c5` (one `no-restricted-imports` error at `vitest-spec-external.test.ts:29:8`), exit 0 on `e0d24934`.\n- Ablation legs re-run on `e0d24934` through `scripts/ablation-replace.mjs`. With the `external` entry deleted, and with the name-shaped `/@objectstack[\\\\/]spec/`, the control is 2/2 red at `:48:25` and `:54:26` (`Object.is` identity). Restore: the disk blob equals the HEAD blob `4ecbe245f449`, and `git diff HEAD` is empty.\n- `pnpm --filter @objectstack/lint test` at `e0d24934`: 142/142 files, 6804/6804 tests (`VERDICT command-exit 0`, lock held 54 s).\n- `pnpm --filter @objectstack/lint typecheck` (including `check:test-typecheck`) at `e0d24934`: `VERDICT command-exit 0`; the debt ledger is unchanged (2 files / 6 errors).\n- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ...` at `e0d24934`: 51 derived, 50 run with exit 0, 1 NOT-MEASURED (`pnpm check:dual-build-cjs-loads` exited 3 because it needs a full-repo build; CI measures it), 0 unrun. Also ran `node scripts/check-console-intercept-disarm.mjs`: exit 0.\n- No publish: `@objectstack/lint`'s `files[]` is `dist`, README, CHANGELOG. None of the changed text is in the built `dist/` (0 hits), while a shipped symbol (`validateStackExpressions`) has 8 hits as the positive control.\n"
      },
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed: dispatch-gates --commands does not derive the ESLint lane for a changed .ts file, so the 51-family sweep cannot catch a lint red; the seat record names it, and the gap was covered this round by running ESLint directly. Not a defect class a/b/c under os-dev rule 3; recorded only."
      ],
      "line_budget": 89,
      "deviations": [
        "The worktree had been removed after round 1 and was recreated from origin's branch tip 77f016c5, so the lint closure (spec, formula, sdui-parser, lint) and the objectql closure were rebuilt under the lock before measuring.",
        "The gate sweep ran as a harness background command and was waited on in the foreground with a bounded until-loop on a sentinel file; nothing is left running."
      ],
      "files_changed": [
        "packages/lint/vitest.config.ts",
        "packages/lint/src/vitest-spec-external.test.ts",
        "packages/lint/src/lazy-deps.test.ts",
        "packages/lint/src/runtime-lazy-deps.test.ts"
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24048 comment 6225804072 · domain:spec seat 1 · session session_01HjR8RUwTPQ3TgbfKihmxp9 · 2026-10-16T22:22Z · seat review only (test config and tests, no contract face), after a patch round (6225029161, an ESLint no-restricted-imports red in the new control test); the head e0d249340c is green and goes to the queue in this act.

    packages/lint's vitest now loads the linked @objectstack/spec dist with Node instead of transforming it. A positive control goes red the moment the pattern stops matching. The package hold went from 84 s to 53 s, and the two lazy-deps files' 30 s budget lines are dropped because both files now run in under 7 s. The cloud dev session is archived at this ACCEPT.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · PR #24048 → c91af8ed64 (Fixes #23900, packages/lint's vitest loads the linked spec dist with Node) · domain:spec seat 1 · os-elon-musk · session session_01HjR8RUwTPQ3TgbfKihmxp9 · 2026-10-16T22:52Z

    probe reading
    PR object merged 2026-10-16T22:51Z out of the merge queue (entered 22:23:13Z); merge commit c91af8ed64
    origin/main git merge-base --is-ancestor c91af8ed64 origin/main → exit 0
    merged vs reviewed head all 4 changed files have the same blobs at c91af8ed64 as at the reviewed head e0d249340c (ACCEPT 6225804072)
    queue branch no gh-readonly-queue/main/pr-24048-* ref remains
    mis-close scan Fixes #23900 is the body's only closing keyword, and the squash message carries no other; this card closed by that PR, as intended, and nothing else did

    For whoever reads the next test-budget refresh list (#23180): lazy-deps.test.ts and runtime-lazy-deps.test.ts no longer declare a 30 s budget. Both ran in 6.5-6.8 s alone after the change (before: 14.3-15.2 s), and the CI-to-local ratio from their old readings projects about 8-9 s in CI. If an hourly reading after c91af8ed64 puts either file over 10 s, that reading is the measurement a re-declaration needs under ruling 6181973363 item 2. Readings from before c91af8ed64 measure the old, inlined config and are not evidence either way.

    pm:dispatched is cleared in this act. The cloud dev session was archived at the seat's ACCEPT.


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions