ci: reconcile cross-workspace test consumers with selected proof inventories
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- bun, typescript
Research direction
Start with scripts/ci-plan.ts:99-112 and scripts/ci-proof.ts:33-43, then reproduce the reported production planChecks, workspaces, and unitFiles inventory for packages/ui-server/src/brain/application.ts. Audit whether selected categories execute packages/ui-react/tests/share-draft-runtime.test.ts and retain the exact receipts. Done means a measured correction preserves discovery, shard coverage, worker metadata, refusal guards, and passes the specified consumer scenarios.
Written by the indexing model from the issue text.
Description
Context
Hosted ready-candidate unit proof can omit real cross-workspace test consumers.
affectedWorkspaces(scripts/ci-plan.ts:99-113) computes the reverse closure from manifest edges only (workspaces,scripts/ci-plan.ts:86-96: dependencies, devDependencies, peerDependencies, optionalDependencies).- For a package-code change without an explicit inventory,
unitFiles(scripts/ci-proof.ts:36-48) discovers roottests/plus the test roots of the affected packages. - So a test in workspace X that imports workspace Y (by package name or by a relative
../../<y>/tests/helpers/...path), where X's manifest does not declare Y, is not selected when only Y changes.
Local check:pr also runs bun run test --changed=<merge-base> as import-graph feedback (localChecks, scripts/check-pr.ts:16-19). That step is documented as feedback only; complete hosted discovery is the required proof (docs/process/testing.md, "Required proof for a PR"). Local feedback can therefore run a consumer that hosted proof leaves out. That is how this gap was found (#2401, #1665).
Policy context:
docs/decisions/ci-utility.md("Local feedback and authoritative proof") promises "affected workspaces and every reverse dependant, plus root tooling tests". It does not say whether "dependant" means manifest edges or import edges.- The browser, layout, endurance and captures categories already select from static-import discovery plus validated declarations (#2125 ruling, 2026-10-16, ci-utility "Browser proof input ownership";
selectBrowserClosures,scripts/ci-plan.ts:296). That ruling's rule is "Do not silently omit an input because it is absent from the import graph." - Root
tests/**,scripts/**,experiments/**andwebsite/**changes use a conservative reverse reference closure (rootConsumers,scripts/ci-plan.ts:135). - No ruling covers unit consumers of package changes.
Evidence (main e16b8407f, 2026-10-19)
-
Production
planChecks([packages/ui-server/src/brain/application.ts], workspaces(root), root)thenunitFilesgivesaffected: ["ui-server"]and 404 files.packages/ui-react/tests/share-draft-runtime.test.tsis absent. It imports@schlessera/brain-ui-serverand two../../ui-server/tests/helpers/*fixtures (lines 7-9), andpackages/ui-react/package.jsondeclares neither. -
A scan of
packages/*/tests/**finds 20 undeclared cross-workspace test edges:Edge Files ui-react → ui-server 25 ui-kit → ui-react 10 ui-react → module-diagrams 9 ui-sdk → ui-server 5 ui-backend-claude → ui-server 3 ui-react → core 3 ui-react → geo 3 ui-sdk → ui-backend-claude 3 The other 12 edges have one file each, including ui-server → module-jobs, ui-server → ui-react, ui-backend-pi → ui-backend-claude and module-travel → module-speaking.
-
For 10 sampled edges (changing
packages/<target>/src/index.ts), the planner missed 9. Only geo →ui-react/tests/track-uploads.test.tswas covered, and only transitively. Table: comment. -
In-memory comparison, declaring all 20 edges in the workspace snapshot (no manifest edits):
Changed workspace Unit files now With the 20 edges declared ui-server 404 1082 core 680 1082 module-jobs 171 1082 ui-backend-claude 483 1082 For module-jobs, browser proof also switches from unselected to selected. A
commonchange already selects 1068 files today.
Open decision (needs: decision)
Which mechanism makes complete affected unit proof include cross-workspace test consumers?
- A. Declare the edges as devDependencies, plus a lint that every cross-workspace import under
packages/*/tests/**is declared.- Uses the existing planner unchanged and is simple to audit.
- Measured above, it pushes almost every package change to about 1082 files and widens browser selection and forward builds (
selectBuildPackages,scripts/publishable-packages.ts:95-117). That works against #1849/#1326. - It adds dev cycles (ui-react ↔ ui-server, ui-kit ↔ ui-react). Publish order ignores devDeps, so the cycles are harmless there.
- One global bun.lock PR.
- B. Have the planner add reverse test-import consumers to the unit inventory.
- For package changes, add every test file anywhere under
packages/*/testsortests/whose static import closure (package specifiers and relative paths, through shared helpers) reaches a changed file. Add these files on top of manifest-closure discovery, never instead of it. - Reuse the static-import resolution behind #2125 (
scripts/browser-input-closure.ts) or therootConsumersreference scan. - Unresolvable or unsupported edges fall back to the existing broad or global selection.
- Selects only the actual consumers (for example, about 25 ui-react files for a ui-server change instead of all 580 in the workspace).
- Unit-category analogue of #2125 option D.
- For package changes, add every test file anywhere under
- C. Reuse
bun test --changedin hosted CI.- Contradicts its documented feedback-only status. It misses spawned and file-read inputs.
- Not verified to produce a listable inventory that
unitFilescan check against discovery and shard.
Recommendation: B. Optionally add A's lint as a report-only check, without requiring manifest declarations. B keeps the existing closure as the floor and adds exact consumers. It follows the import-discovery rule the maintainer already chose for browser categories, and it avoids A's measured near-global widening. Recommendation only; not approved.
Scope (after the ruling)
- Implement the chosen mechanism in
scripts/ci-plan.ts/scripts/ci-proof.ts(or the manifests plus lint, if A). - Update
docs/decisions/ci-utility.md("Local feedback and authoritative proof", plus "Independent Bun test-only changes" if affected) in the same PR. Keep anyfile:linecitations into moved lines correct. - Cover both package-specifier and relative cross-workspace test imports, including imports through shared test helpers.
Out of scope
- Removing local import-graph feedback.
- Narrowing existing manifest-closure discovery, shard coverage or category selection.
- Relaxing guards.
- Adding a global-everything fallback without measured relevance.
- Treating a manual isolated pass as a selection fix.
- Changing #1665's activation scope or claiming a product regression.
- The stopped #2284 investigation.
- Cross-category equivalence for browser-category specs: #2125 already owns those closures.
Acceptance criteria
- Failing first. A unit test in
tests/ci-plan.test.ts(or an equivalent production-planner test) asserts thatunitFiles(planChecks([<ws>/src change], workspaces(root), root), root)contains a cross-workspace consumer. It covers at leastui-server→packages/ui-react/tests/share-draft-runtime.test.ts(package and relative import) andmodule-jobs→packages/ui-server/tests/jobs-board-selection-http.test.ts. It fails on current main for the missing file and passes after the fix. - Mutation receipt. Disabling the new consumer edge (or removing the declaration, if A) turns that test red on the named assertion. Restoring it turns it green.
- Whole class covered. A repository-level check lists every cross-workspace import under
packages/*/tests/**and fails if the planner would not select the importer for a change to the imported workspace. The 20 edges above all pass. - No loss of existing proof. For every sampled change, existing selections are a subset of the new ones. Global and unknown inputs still expand to complete discovery. Duplicate, missing or empty inventories still refuse (
unitFiles). Shard balancing and test-worker metadata are unchanged. - Unresolvable edges fail open. An unresolvable or dynamic cross-workspace import falls back to the broad selection rather than being dropped (controlled test).
- Measured widening. The PR records unit-file counts before and after for ui-server, core, module-jobs and ui-backend-claude changes.
- Policy updated.
docs/decisions/ci-utility.mdstates the chosen rule.
Verification
bun run test tests/ci-plan.test.ts(plus any new test file).- Production planner diagnostic:
planChecks(changedFiles(...), workspaces(root), root)andunitFilesfor the sampled edges, before and after. bun run check:pr --base origin/main.- Explicit ready-candidate CI with the complete selected inventory and actual head/base/checkout in the aggregate.
Contract impact
None. Repository CI tooling only (scripts/ci-plan.ts header: "Product CLI/MCP contracts are unaffected"). Option A would edit package manifests' devDependencies only, which do not ship to consumers.
History
- 2026-10-18 (filing): the #1665 candidate
13601e76668b5801b4cc0aa5259a2df5a50ae42bon base775eb17aab740fa759c2182f306c38fc17acbcbegaveaffected: [ui-server],global: falseand 404 hosted unit files, withoutshare-draft-runtime.test.tsand withclaude-cancellation.test.ts. Local import-graph feedback selected it and produced the three initial-confirmation failures recorded in #2401. Later scoped runs pass but do not diagnose the original combined failure. - 2026-10-18, second pass: production planChecks, workspaces, unitFiles and categoryCommands at
775eb17agave 403 files without the share consumer. The diagnostic exits 1 on the missing-consumer assertion. - 2026-10-18, third pass: adding only an in-memory ui-react → ui-server edge selects the consumer (403 → 580 files; affected module-diagrams, ui-react, ui-server). This confirms that manifest closure is the cause.
- 2026-10-19, class reproduction at
09eb1f78e: about 20 undeclared edges; the planner missed 9 of 10 sampled.needs: reproremoved. - #2413 records the ui-server → module-jobs instance. #2399 notes that its own SDK change widened the normal closure and did not modify selection.
Triage 2026-10-19: re-verified at e16b8407f (404 files, 20 edges). Corrected citations (affectedWorkspaces 99-113, unitFiles 36-48, share-draft imports 7-9). Recorded that no ruling covers unit-consumer selection and that local --changed is deliberately feedback-only. Measured option A in memory. Added the decision question with options and a recommendation (B). Labels: needs: decision, priority: p1. Attached to #1849.
- Dominant language
- TypeScript
- Stars
- 1
- Forks
- 0
- Avg merge
- 4h 41m
- Merged PRs (30d)
- 954
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from schlessera/brain-kit
-
agent-ready area: ui-sdk priority: p3 type: chore
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
schlessera/brain-kit#2534 ·
Maintainers usually reply within 1 day
-
agent-ready area: ci priority: p3 type: chore
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
schlessera/brain-kit#2509 ·
Maintainers usually reply within 1 day
-
agent-ready area: core priority: p3 type: fix
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
schlessera/brain-kit#2503 ·
Maintainers usually reply within 1 day
-
agent-ready area: modules priority: p2 type: fix
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
schlessera/brain-kit#2500 ·
Maintainers usually reply within 1 day
-
agent-ready area: modules priority: p3 type: fix
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
schlessera/brain-kit#2499 ·
Maintainers usually reply within 1 day
All issues in schlessera/brain-kit
Similar issues
-
bug P3 UI/UX
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
StellarState/SS-backend#712 ·
Maintainers usually reply within 1 day
-
area: backend bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
snapotter-hq/SnapOtter#3473 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
gitroomhq/postiz-app#2304 ·
Maintainers usually reply within 1 day
-
harlan-agent-ready-to-implement routine:ci-review
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
harlan-zw/unlighthouse#454 · 2 comments ·
Maintainers usually reply within 1 day