Skip to content

refactor(daemon): cut daemon test edges into root data fixtures and fakes [11a-3] - #3517

Open
thymikee wants to merge 14 commits into
refactor/daemon-test-fakesfrom
refactor/daemon-test-data-fakes
Open

thymikee wants to merge 14 commits into
refactor/daemon-test-fakesfrom
refactor/daemon-test-data-fakes

Conversation

@thymikee

@thymikee thymikee commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Plan step 11a-3: daemon test files stop importing the root src/__tests__/test-utils/ data fixtures, and the review's copied-fake findings are resolved with one owner each.

Targeted root modules (scan edges now 0): device-inventory-gateways, replay-script-source, android-ui-hierarchy-fixtures, android-snapshot-capture, plugin.fixtures. Total daemon→root scan residue after the cut: 23 edges, all into the platform-runtime cluster owned by step 11a-2 (the only remaining 11a-1 residue).

Review resolutions:

  1. Inventory fake — one owner: packages/device-selection/src/inventory-gateways.fixtures.ts, exported as @agent-device/device-selection/inventory-gateways-fixtures, with findInstalledApp in options, createTestDeviceInventoryGateways exported, and the nullable-provider bridge (withTestDeviceInventoryProvider) extracted once. Daemon (37 files under src/daemon), root composition, device-selection's own tests, and test/integration all import it. The root wrapper and its pin are deleted: the wrapper's fail-closed pin duplicated the real composer's pin already at src/platform-runtime-device-inventory.test.ts, and the real adapter has its own direct tests. test-kit was not viable: the fake needs the root composition's createDeviceInventoryContext (device-selection's production entry), which would make test-kit→device-selection while device-selection's tests need test-kit — a tsc -b project-reference cycle — so the package's own test-only subpath (the fallback) is the one owner.
  2. Replay collector — no second implementation: the real client collector moved into replay-port (script-source-bundle, source-discovery, byte-pure moves with their own test suites), and the bundle helpers live once in replay-port/src/daemon-port/replay-script-source.fixtures.ts (./replay-script-source-fixtures), typed on DaemonWireRequest so it serves both trees without a production seam. Both test trees exercise the same globs, size cap, not-found AppErrors and ranking through it. The root src/__tests__/test-utils/replay-script-source.ts is deleted (its exports move with it; nothing suppressed via .fallowrc).
  3. Android captures: snapshot-capture-fixtures and ui-hierarchy-fixtures are test-only entries on @agent-device/platform-android (the Apple runner requests-fixtures pattern), with the archived raw JSONs under packages/platform-android/src/fixtures/ where own:package-capture owns them. Contract comments kept to one line.
  4. plugin.fixtures stays in @agent-device/test-kit/plugin.fixtures (package vocabulary over the filesystem install, no platform adapters).
  5. createTestDeviceInventoryGatewaysFromProvider is used (lock-policy test + integration harness), so it stays exported; the daemon-side copy that fallow flagged is deleted. pnpm format run repo-wide.
  6. Base delta is test-side only: the moved collector modules are byte-identical and the only production root edit is the two import lines in src/commands/replay/index.ts; everything else is test/fixture moves, import retargets, and gate wiring. No limrun/CONTRIBUTING/coverage/website files in the delta against the current base.

Gates (on the pushed commit, under the host gate lock): check:affected --run clean across all selected lanes; check:quick, check:layering, check:di-seams, check:gate-manifest, check:fallow, the eager-closure budgets table (three APPROVED_OVER_CEILING rows + the mechanics-facet exception for the Android fixture entries), the R13 export-pin tests, and the test-file-size ratchet green. Targeted full runs: src/daemon, src/__tests__, packages, scripts with zero failures; vitest list src parity vs base is exact modulo the moved test files' path keys and one intentionally deleted duplicate pin (the root inventory wrapper's fail-closed case, already owned root-side).

Risk: no production behavior change (CLI startup import-closure guard green through the replay move). Eager-require weight: daemon suites now pull replay-port's real collector instead of the local test twin.

NO DOCS — test topology and fixture ownership only; no user-facing behavior or CLI surface changes.

Follow-up commit (bd49bf8, second review round): assertProviderOutcome/assertSourceFamily moved from the root composer into @agent-device/contracts/device; the fake now calls the shared assertProviderOutcome instead of restating the flow (a malformed provider outcome fails closed with provider_inventory_contract_violation in both paths, pinned by a new fixture test). The fake's local path keeps its per-family filter because the local option is a flat whole-inventory discoverer fanned out per family — that filter is the adapter to the per-family source contract, not a restated assertion site.

Design answers at e4eee6b (review follow-up round):

  1. Composer home (Q1): yes, moved. The composed-gateways flow now lives in @agent-device/device-selection/composed-inventory-gateways with the installed-app probe injected instead of hardcoded; src/platform-runtime-device-inventory.ts stays as the production wiring that attaches the Apple probe (function-scoped import keeps the composition root's eager closure flat; the hub no-growth gate is green). The test fake composes through the real composer with fake registry sources — the root, daemon-composition, and integration consumers exercise the production flow, not a restatement. No root-side wrapper is needed anymore: the one owner is the package entry both trees build on.
  2. Collector home (Q2): moved out of the daemon port. script-source-bundle, source-discovery, and the request fixture now sit at packages/replay-port/src/ (client-side replay vocabulary, like session-replay-vars), outside the daemon-port/ engine-prefix zone; the ./script-source-bundle, ./source-discovery, and ./replay-script-source-fixtures subpaths keep their names. Caller filesystem reads stay out of the daemon's dispatch vocabulary.

@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.20 MB 5.20 MB +282 B
Package (unpacked) 5.20 MB 5.20 MB +282 B
Package (download) 1.56 MB 1.56 MB +170 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 26.6 ms 26.4 ms -0.2 ms
CLI --help 77.5 ms 76.1 ms -1.4 ms

@thymikee

Copy link
Copy Markdown
Member Author

I reviewed e51dff5 and found two problems to fix before merge. Both are copied test fakes, and one shared owner should replace each.

The new device-inventory-gateways.ts is about 110 lines copied from the existing device-selection fake. It also re-states the discover/providerFirst flow of the real createComposedDeviceInventoryGateways, without its assertProviderOutcome and assertSourceFamily checks. There are now three versions of this flow. The 43 migrated daemon tests no longer cover the real composition, so a change to provider-outcome checks, family assertions or scoping will not reach them. The copy also drops findInstalledApp, and the inline bridge in withTestDeviceInventoryProvider duplicates createTestDeviceInventoryGatewaysFromProvider in the same file. The rule: one fake owns this logic, and every test tree imports it. The daemon helper can re-export it, or the package fake can move behind a single importable test-support owner. Please add findInstalledApp to its options and delete the inline bridge.

The new replay-script-source.ts copies replayScriptSourceBundleFor, maestroScriptSourceBundleFor, noMaestroIncludeSources, withClientReplayScriptSources and withFlags from src/__tests__/test-utils/replay-script-source.ts, which still has 8 or more root importers. It also re-implements loadReplayScriptSourceBundle and the file discovery in a weaker form: no glob support, no bundle size cap, no not-found AppError, and no extension-ranked ordering. Two helpers now define what a request looks like when it reaches the daemon, and they can drift apart. A change in the real client collector would not fail the daemon or replay-suite tests. The rule: one helper module owns the bundle helpers, and the collector and discovery functions are passed in. Root passes the real ones. Alternatively, give the real collector a package-owned home that both trees import, so no second implementation exists.

Not blocking, and you can take or leave these: the Android archived captures and walker builders in android-ui-hierarchy-fixtures.ts are platform fixtures, and they have several non-daemon consumers (core interactor tests, snapshot-chrome tests, src/__tests__, scripts/check-affected/device-lanes.test.ts), so a platform-android test-fixtures subpath may fit better if the eager-closure rule allows it; and the header comments in the new support files narrate the root modules they replace, so please drop them or keep one line stating the contract.

Could the daemon avoid new fakes altogether? Export the existing inventory fake through one test-support owner, or move it into test-kit with an injected context wrapper, and point the 43 daemon importers and the root importers at it. That removes about 150 lines. Keep one replay-source helper module with the collector passed in. Keep plugin.fixtures in test-kit, as you have it. Put the Android captures with platform-android if the eager-closure rule allows, or leave them at the root and accept those few edges explicitly instead of inverting core to daemon. The first decision is which module owns the inventory fake: a device-selection test-support subpath or a test-kit dependency edge. The test-kit rejection from ADR-0019 applies only to the Android pieces.

The changes are test-only, so I ran no tests or gates here. I relied on your reported 433 files and 3305 tests green, and on the green layering, DI-seams and eager-closure checks. Smoke Tests is still queued, not failed, and this diff does not touch the runtime code that run exercises. There are no conflicts. The next step before merge is to replace the two copied fakes with one shared owner each.

@thymikee

Copy link
Copy Markdown
Member Author

The code still has two copied test fakes and two failing checks at 93356dc, so it is not ready to merge. The earlier review at e51dff5 raised the same two copies. The inline bridge is now an in-file helper, but both copies are still there, and the Required evidence is satisfied.

First, src/daemon/tests/support/device-inventory-gateways.ts is still a roughly 110-line copy of the fake in packages/device-selection/src/__tests__/test-utils/device-inventory-gateways.ts. It re-states the discover/providerFirst flow without the assertProviderOutcome and assertSourceFamily checks from the real createComposedDeviceInventoryGateways, and it has no findInstalledApp. So there are now three versions of this flow, and the 43 migrated daemon tests no longer reach the real composition checks. A change to provider-outcome checks or family assertions will not fail them. The rule: one fake owns the inventory-gateway logic and every test tree imports it. Please expose the device-selection fake through a test-support subpath, or move it to test-kit with an injected context wrapper. Then point the daemon and root importers at it and delete this copy. If you keep a daemon wrapper, add findInstalledApp.

Second, src/daemon/tests/support/replay-script-source.ts is a second implementation of the bundle helpers and collector. It has no glob support, no bundle size cap, no not-found AppError and no extension-ranked ordering. The root src/__tests__/test-utils/replay-script-source.ts still has the real client collector, and this file was not touched in the delta. The daemon and replay-suite tests now build bundles with a weaker collector, so drift in the real one goes unnoticed. The rule: one helper module owns the bundle helpers and takes the collector and discovery functions as parameters. Root passes the real ones. Alternatively, give the real collector a package-owned home that both trees import.

Third, createTestDeviceInventoryGatewaysFromProvider is exported but has no outside consumer. Fallow reports it as an unused export, so the Compatibility & Provenance (fallow) check fails. Routing the bridge through it in this delta created that state. Fixing the first point removes the copy and the export with it. Otherwise, please remove the export.

Fourth, oxfmt --check flags provider-device-admission-fixture.ts, which is new in this delta, and it also names src/daemon/handlers/__tests__/lease.test.ts. The delta stat shows 212 lines deleted from that second file, so I could not tell whether it is still at head. The Lint & Format check fails because of this, while oxlint itself reported 0 errors. Please run pnpm format repository-wide and commit the result.

Not blocking, and you can take or leave these: the delta stat lists limrun, CONTRIBUTING, coverage and website-docs changes, which are probably base movement from the rebase, so please confirm the diff against 2c7a8d0 is test-side only (the PR body says 83 files and I count 84), and the header comments in the new support files narrate the root modules they replace, so drop them or keep one line stating the contract.

Could both daemon copies be deleted, with the device-selection fake exposed through one test-support subpath or moved to test-kit, and a single replay-source helper that takes the collector as a parameter? That would remove about 250 lines and keep the edge count at 0 for src/daemon. You would first need to decide which module owns the inventory fake: a device-selection test-support subpath or a test-kit dependency edge. The ADR-0019 eager-closure rule that rejected test-kit applies only to the Android pieces.

On checks, the fallow and Lint & Format failures look related to this diff. I did not read the Coverage failure log, and Smoke Tests is still queued. I did not run tests or gates, and I did not grep the head to confirm 0 src/daemon edges to the five root modules. After the two fakes have one owner each and pnpm format is committed, the checks need to pass before merge.

thymikee added a commit that referenced this pull request Oct 12, 2026
…y source collector

Owner review on #3517: the two daemon-side fakes restated logic that already
had owners. Both are replaced by single importable owners:

- The device-selection test fake becomes the one inventory-gateways fixture:
  moved to packages/device-selection/src/inventory-gateways.fixtures.ts and
  exported as ./inventory-gateways-fixtures (with findInstalledApp already in
  its options and createTestDeviceInventoryGatewaysFromProvider now exported).
  The daemon copy, the root fake, and its pin (the real composer's fail-closed
  contract is pinned in platform-runtime-device-inventory.test.ts) are deleted;
  daemon, root, device-selection, and integration consumers all import the
  owner.
- The real client collector moves into its package: script-source-bundle and
  source-discovery move from src/commands/replay into replay-port (own exports
  ./script-source-bundle, ./source-discovery). The bundle helpers live once in
  packages/replay-port/src/daemon-port/replay-script-source.fixtures.ts
  (./replay-script-source-fixtures), typed on DaemonWireRequest; both test
  trees import it and run the same globs, size cap, AppErrors and ranking.
- The Android capture fixtures move to platform-android test-only entries
  (./snapshot-capture-fixtures, ./ui-hierarchy-fixtures) so no test tree
  reaches sideways; the archived raw JSONs move with them.
@thymikee
thymikee force-pushed the refactor/daemon-test-data-fakes branch from 93356dc to c61535e Compare October 12, 2026 19:56
@thymikee

Copy link
Copy Markdown
Member Author

Rework pushed at c61535e (rebased onto the refreshed base 57b6b9b; findings → fixes):

  • Copied inventory fake → one owner: @agent-device/device-selection/inventory-gateways-fixtures (the moved device-selection fake, findInstalledApp in options, provider bridge extracted once). test-kit was impossible without a test-kit↔device-selection project-reference cycle, so the package subpath (the stated fallback) is the owner. The daemon copy, the root wrapper, and its pin are deleted — the wrapper's fail-closed pin duplicated the real composer's pin already at src/__tests__/platform-runtime-device-inventory.test.ts:104. createTestDeviceInventoryGatewaysFromProvider stays exported: it has two consumers (lock-policy test, integration harness), and the copy fallow flagged is gone.
  • Copied replay collector → your preferred option: the real script-source-bundle/source-discovery moved into replay-port byte-pure (own subpaths + their suites); one helper module replay-port/src/daemon-port/replay-script-source.fixtures.ts (./replay-script-source-fixtures) typed on DaemonWireRequest. Both trees exercise the same globs, size cap, not-found AppErrors and ranking. Root test-utils/replay-script-source.ts deleted outright — no .fallowrc suppression, no unused exports left behind.
  • Android captures → test-only entries ./snapshot-capture-fixtures and ./ui-hierarchy-fixtures on @agent-device/platform-android (eager-closure allows it with the mechanics-facet exception + three approved budget rows); archived JSONs live under packages/platform-android/src/fixtures/ where own:package-capture owns them. Header comments are one-line contract statements.
  • Format: pnpm format run repo-wide (the two files you saw were already fixed by the base refresh); base delta is test-side only — the collector moves are byte-identical and the only production root edit is two import lines in src/commands/replay/index.ts; the limrun/CONTRIBUTING/coverage/website noise is gone against the refreshed base and the body's file count now matches git diff --name-only origin/refactor/daemon-test-fakes...HEAD (124).
  • Parity: vitest list src vs base differs only by the two moved test files' path keys and the one intentionally deleted duplicate pin. Gates green on the pushed commit (check:affected --run, layering, di-seams, gate-manifest, fallow, eager-closure, ratchet).

@thymikee

Copy link
Copy Markdown
Member Author

The PR is ready at c61535e. Both earlier problems from the 93356dc review are fixed: the fallow findings sat on the copied fakes, and the Lint & Format failure sat on the unformatted fixture, so both are gone or formatted. All 36 checks pass, and the review found no code problems. Not blocking, and you can take or leave these: the single fake in packages/device-selection/src/inventory-gateways.fixtures.ts (https://github.com/callstack/agent-device/blob/c61535e/packages/device-selection/src/inventory-gateways.fixtures.ts#L77) restates createComposedDeviceInventoryGateways without assertProviderOutcome and assertSourceFamily, so a malformed provider outcome in root tests would fail with a TypeError or pass instead of the typed provider_inventory_contract_violation, and the composer pin already owns that contract; the three new APPROVED_OVER_CEILING rows and the mechanics-facet exceptions in scripts/tests/eager-closure-budgets.ts (https://github.com/callstack/agent-device/blob/c61535e/scripts/__tests__/eager-closure-budgets.ts#L135) could be dropped once 11a-2 settles and the Android fixture subpaths can live in test-kit; and the PR body names the inventory pin as src/tests/platform-runtime-device-inventory.test.ts when the file is src/platform-runtime-device-inventory.test.ts. I did not run tests or gates myself, so the parity and gate results rest on the PR body and green CI. I also did not check that moving the collector adds no CLI startup import-closure weight beyond the passing guard, or that the 43-file count in the body matches the 37 files I counted under src/daemon. The total of 60 importers does confirm one owner. Nothing else needs to happen before merge.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 12, 2026
@thymikee

Copy link
Copy Markdown
Member Author

Non-blocking items taken; pushed as bd49bf8 (normal push):

  • Fake restating the composer flow: assertProviderOutcome/assertSourceFamily moved from src/platform-runtime-device-inventory.ts into @agent-device/contracts/device (both sides already import that subpath; the fake can't import the root composer). The fake's provider path now calls the shared assertProviderOutcome, so a malformed outcome fails closed with provider_inventory_contract_violation in both paths — pinned by a new fixture test. Composing through createComposedDeviceInventoryGateways itself wasn't possible: it needs the root's platform-module registry/host and hardcodes the Apple findInstalledApp probe, which the fake must supply or omit. The fake's local per-family filter stays: local is a flat whole-inventory discoverer fanned out per family, so filtering there is the adapter to the per-family source contract, not a restated assertion (asserting instead broke three tests that deliberately hand the fake mixed-family inventories).
  • PR body: pin path corrected to src/platform-runtime-device-inventory.test.ts; the 43 was all daemon+daemon-composition importers — corrected to the 37 under src/daemon.
  • APPROVED_OVER_CEILING rows left as-is for the post-11a-2 follow-up.

Gates: 11,030 tests green across src/daemon/src/__tests__/packages, typecheck and layering clean, check:affected --run fully green under the gate lock.

thymikee added a commit that referenced this pull request Oct 12, 2026
…y source collector

Owner review on #3517: the two daemon-side fakes restated logic that already
had owners. Both are replaced by single importable owners:

- The device-selection test fake becomes the one inventory-gateways fixture:
  moved to packages/device-selection/src/inventory-gateways.fixtures.ts and
  exported as ./inventory-gateways-fixtures (with findInstalledApp already in
  its options and createTestDeviceInventoryGatewaysFromProvider now exported).
  The daemon copy, the root fake, and its pin (the real composer's fail-closed
  contract is pinned in platform-runtime-device-inventory.test.ts) are deleted;
  daemon, root, device-selection, and integration consumers all import the
  owner.
- The real client collector moves into its package: script-source-bundle and
  source-discovery move from src/commands/replay into replay-port (own exports
  ./script-source-bundle, ./source-discovery). The bundle helpers live once in
  packages/replay-port/src/daemon-port/replay-script-source.fixtures.ts
  (./replay-script-source-fixtures), typed on DaemonWireRequest; both test
  trees import it and run the same globs, size cap, AppErrors and ranking.
- The Android capture fixtures move to platform-android test-only entries
  (./snapshot-capture-fixtures, ./ui-hierarchy-fixtures) so no test tree
  reaches sideways; the archived raw JSONs move with them.
@thymikee
thymikee force-pushed the refactor/daemon-test-data-fakes branch from bd49bf8 to 1159838 Compare October 12, 2026 23:53
@thymikee

Copy link
Copy Markdown
Member Author

Coordinator: rebased onto the current tip of the PR below (stack sync, no content changes).

@thymikee

Copy link
Copy Markdown
Member Author

This PR is ready at 1159838. The earlier clean review at c61535e still stands, and the new commit adds a contracts export, device-selection fixture, root composer and one daemon test change that I found correct when reading the diff. All 36 checks are green, and they cover those routes. I did not run the tests locally, and I did not execute a check that the new fixture test fails without assertProviderOutcome; I reasoned that from the code. I also did not confirm beyond green CI that the export-pin and eager-closure gates account for the two new contracts facade exports. Not blocking, and you can take or leave these: the rebase comment says "stack sync, no content changes" and the body cites bd49bf8, but this head adds a real contracts export and test changes, so a corrected comment and hash would help; and the web-close teardown test change in daemon-runtime-web-close-teardown.test.ts (installFakeManagedAgentBrowser plus a stderr assertion) is a separate concern from the contracts inventory work, so it is worth keeping out of the stated scope. No conflicts are known. The PR is still a draft with the ready-for-human label, so nothing else stands between this delta and merge.

@thymikee

Copy link
Copy Markdown
Member Author

Coordinator correction to my earlier sync comment: the sync itself changed nothing. It only rebased the branch's existing commits onto #3519's new tip, 773f78b. 1159838 is bd49bf8 (the worker's contracts-export and inventory-assertion commit) rebased; the hash changed but the content didn't. The web-close teardown test change (installFakeManagedAgentBrowser plus the stderr assertion) belongs to #3519 (773f78b), not this PR. It only appears here when the diff is taken against an older #3519 head.

@thymikee
thymikee added this pull request to stack #3564 October 13, 2026 06:02
thymikee added a commit that referenced this pull request Oct 13, 2026
…y source collector

Owner review on #3517: the two daemon-side fakes restated logic that already
had owners. Both are replaced by single importable owners:

- The device-selection test fake becomes the one inventory-gateways fixture:
  moved to packages/device-selection/src/inventory-gateways.fixtures.ts and
  exported as ./inventory-gateways-fixtures (with findInstalledApp already in
  its options and createTestDeviceInventoryGatewaysFromProvider now exported).
  The daemon copy, the root fake, and its pin (the real composer's fail-closed
  contract is pinned in platform-runtime-device-inventory.test.ts) are deleted;
  daemon, root, device-selection, and integration consumers all import the
  owner.
- The real client collector moves into its package: script-source-bundle and
  source-discovery move from src/commands/replay into replay-port (own exports
  ./script-source-bundle, ./source-discovery). The bundle helpers live once in
  packages/replay-port/src/daemon-port/replay-script-source.fixtures.ts
  (./replay-script-source-fixtures), typed on DaemonWireRequest; both test
  trees import it and run the same globs, size cap, AppErrors and ranking.
- The Android capture fixtures move to platform-android test-only entries
  (./snapshot-capture-fixtures, ./ui-hierarchy-fixtures) so no test tree
  reaches sideways; the archived raw JSONs move with them.
@thymikee
thymikee force-pushed the refactor/daemon-test-data-fakes branch from 1159838 to 2354fd0 Compare October 13, 2026 06:24
@thymikee

Copy link
Copy Markdown
Member Author

Coordinator: rebased onto #3519's new head 8f501eb (#3519 was rebased onto main; see its comment). The rebase applied cleanly and none of this PR's commits changed. check:quick passes.

@thymikee

Copy link
Copy Markdown
Member Author

The earlier review at 1159838 found no problems, and the rebase onto the new #3519 head (2354fd0) changed only the base. Your patch is the same apart from hunk offsets, and all 41 checks pass. Not blocking, take or leave: createTestDeviceInventoryGateways in https://github.com/callstack/agent-device/blob/2354fd0/packages/device-selection/src/inventory-gateways.fixtures.ts#L88 restates the real createComposedDeviceInventoryGateways in https://github.com/callstack/agent-device/blob/2354fd0/src/platform-runtime-device-inventory.ts#L38-L125 (provider-first discovery, declined-to-local fallback, provider projection, and per-family fan-out that swallows errors). The deleted root helper built its gateways through the real composer, and about 57 root, daemon-composition and integration test files now use the copy, so they could pass while production behavior drifts. A root-side wrapper over the real composer for src/__tests__ and test/integration would keep those tests honest, and the package fake could stay for the daemon tree only.

Two questions. First, the fake exists only because the daemon test tree cannot import the root module. Could the composer move into @agent-device/device-selection, with findInstalledApp and the host loader injected, so the fixture builds on the real one? Second, source-discovery.ts says it runs on the caller (#1802: the daemon opens no caller path), yet it now sits in packages/replay-port/src/daemon-port, and nothing stops daemon code from importing it. Is there a client-owned home the fixture could import, such as a client subpath, so caller filesystem reads stay out of the daemon port?

I did not run the tests or check:quick locally and relied on the CI results. I judged the 70-file delta from the range-diff and a full-diff comparison, not line by line, since it is almost all upstream main and #3519 content. No other reviewer has looked at this head, and there are no review threads. There are no conflicts. The two design questions above are the open item for this head.

@thymikee thymikee removed the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 13, 2026
thymikee added a commit that referenced this pull request Oct 13, 2026
…y source collector

Owner review on #3517: the two daemon-side fakes restated logic that already
had owners. Both are replaced by single importable owners:

- The device-selection test fake becomes the one inventory-gateways fixture:
  moved to packages/device-selection/src/inventory-gateways.fixtures.ts and
  exported as ./inventory-gateways-fixtures (with findInstalledApp already in
  its options and createTestDeviceInventoryGatewaysFromProvider now exported).
  The daemon copy, the root fake, and its pin (the real composer's fail-closed
  contract is pinned in platform-runtime-device-inventory.test.ts) are deleted;
  daemon, root, device-selection, and integration consumers all import the
  owner.
- The real client collector moves into its package: script-source-bundle and
  source-discovery move from src/commands/replay into replay-port (own exports
  ./script-source-bundle, ./source-discovery). The bundle helpers live once in
  packages/replay-port/src/daemon-port/replay-script-source.fixtures.ts
  (./replay-script-source-fixtures), typed on DaemonWireRequest; both test
  trees import it and run the same globs, size cap, AppErrors and ranking.
- The Android capture fixtures move to platform-android test-only entries
  (./snapshot-capture-fixtures, ./ui-hierarchy-fixtures) so no test tree
  reaches sideways; the archived raw JSONs move with them.
@thymikee
thymikee force-pushed the refactor/daemon-test-data-fakes branch from 2354fd0 to e4eee6b Compare October 13, 2026 09:50
@thymikee

Copy link
Copy Markdown
Member Author

Both design questions taken; pushed at e4eee6b (stack of two commits on the fresh 11a-2f base — force-with-lease was needed because the base moved under the previous push; content of the earlier commits unchanged):

  • Q1 (composer home): yes — the flow moved into @agent-device/device-selection/composed-inventory-gateways with the findInstalledApp probe injected instead of hardcoded, as a production subpath. src/platform-runtime-device-inventory.ts keeps its path as the production wiring that attaches the Apple simulator probe; the composer hop is function-scoped so the composition-root hub stays flat (hub no-growth gate green). The fake now builds its gateways through the real composer with fake registry sources — the restated flow is deleted, so root/daemon/integration consumers exercise production behavior, which also retires the wrapper suggestion: the one owner is the package entry both trees build on. The probe-attachment pin stays root-side (platform-runtime-device-inventory.test.ts, mocking the tool-provider as before); the package suite pins the injected-probe path.
  • Q2 (collector home): moved — script-source-bundle, source-discovery, and the request fixture now sit at packages/replay-port/src/ (client-side replay vocabulary, next to session-replay-vars), outside the daemon-port/ engine zone; subpath names unchanged, so consumers retarget by path only. Caller filesystem reads are out of the daemon dispatch port.

Gates on the pushed head: typecheck, layering (incl. enumeration + eager-closure hub budgets), quick, di-seams, gate-manifest, fallow green; full src scripts packages unit sweep 14,230 tests green pre-rebase, targeted clusters green post-rebase.

thymikee added a commit that referenced this pull request Oct 13, 2026
…y source collector

Owner review on #3517: the two daemon-side fakes restated logic that already
had owners. Both are replaced by single importable owners:

- The device-selection test fake becomes the one inventory-gateways fixture:
  moved to packages/device-selection/src/inventory-gateways.fixtures.ts and
  exported as ./inventory-gateways-fixtures (with findInstalledApp already in
  its options and createTestDeviceInventoryGatewaysFromProvider now exported).
  The daemon copy, the root fake, and its pin (the real composer's fail-closed
  contract is pinned in platform-runtime-device-inventory.test.ts) are deleted;
  daemon, root, device-selection, and integration consumers all import the
  owner.
- The real client collector moves into its package: script-source-bundle and
  source-discovery move from src/commands/replay into replay-port (own exports
  ./script-source-bundle, ./source-discovery). The bundle helpers live once in
  packages/replay-port/src/daemon-port/replay-script-source.fixtures.ts
  (./replay-script-source-fixtures), typed on DaemonWireRequest; both test
  trees import it and run the same globs, size cap, AppErrors and ranking.
- The Android capture fixtures move to platform-android test-only entries
  (./snapshot-capture-fixtures, ./ui-hierarchy-fixtures) so no test tree
  reaches sideways; the archived raw JSONs move with them.
@thymikee
thymikee force-pushed the refactor/daemon-test-data-fakes branch from 77c1009 to e615de3 Compare October 13, 2026 15:13
@thymikee

Copy link
Copy Markdown
Member Author

Coordinator: rebased onto #3519's new head df0af2a13, which now sits on current main. This PR's 9 commits replayed without conflicts and their content is unchanged. check:quick passes. New head e615de39d.

thymikee added a commit that referenced this pull request Oct 13, 2026
…y source collector

Owner review on #3517: the two daemon-side fakes restated logic that already
had owners. Both are replaced by single importable owners:

- The device-selection test fake becomes the one inventory-gateways fixture:
  moved to packages/device-selection/src/inventory-gateways.fixtures.ts and
  exported as ./inventory-gateways-fixtures (with findInstalledApp already in
  its options and createTestDeviceInventoryGatewaysFromProvider now exported).
  The daemon copy, the root fake, and its pin (the real composer's fail-closed
  contract is pinned in platform-runtime-device-inventory.test.ts) are deleted;
  daemon, root, device-selection, and integration consumers all import the
  owner.
- The real client collector moves into its package: script-source-bundle and
  source-discovery move from src/commands/replay into replay-port (own exports
  ./script-source-bundle, ./source-discovery). The bundle helpers live once in
  packages/replay-port/src/daemon-port/replay-script-source.fixtures.ts
  (./replay-script-source-fixtures), typed on DaemonWireRequest; both test
  trees import it and run the same globs, size cap, AppErrors and ranking.
- The Android capture fixtures move to platform-android test-only entries
  (./snapshot-capture-fixtures, ./ui-hierarchy-fixtures) so no test tree
  reaches sideways; the archived raw JSONs move with them.
@thymikee
thymikee force-pushed the refactor/daemon-test-data-fakes branch from e615de3 to f5ed49c Compare October 13, 2026 15:31
@thymikee

Copy link
Copy Markdown
Member Author

Rebased onto the rewritten refactor/daemon-test-fakes tip (df0af2a, 11a-2a..2g re-push) — conflict resolved. range-diff confirms all 9 PR commits replayed identical (zero content change); typecheck, layering, 5,742 targeted daemon/root/package tests and the full affected sweep (14,829 tests, one unrelated fuzz-harness timeout that passes in isolation under suite contention) green pre-push. #3544's stack was resynced onto the new head the same way. Remote head: f5ed49c.

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed f5ed49c. The code looks good, and I found no defects. Before the label, please answer the design questions below. I ran no tests or gates locally, and CI status comes from the check list only.

The earlier points from 77c1009 are fixed. The test fake now composes through the real composer, and the consumer-gate and fixture-path items are addressed at this head. The only checks not passing are the Mutants shards for kernel-errors, selectors and interaction-settle. They are queued or in progress, not failed. This diff touches nothing under packages/selectors or packages/kernel, and nothing on the settle or errors routes, so they look unrelated. Smoke Tests, which covers the daemon startup route through the dynamic import of the package composer, is no longer in the not-passing list. I did not re-review the base move from c13c1bd to df0af2a, because the base commits for 11a-2a..2g belong to PR #3519. I know of no conflicts.

Not blocking, and you can take or leave it: assertSourceFamily and assertProviderOutcome were exported from the public contracts device facade so the fake could share them. The fake now uses the real composer, so the only consumer left is composed-inventory-gateways.ts. The docstrings also carry review-history narration, and the facade gained an AppError import only for these helpers. Moving them back as private functions would drop those imports from contracts.

The new concept is sound: device-selection owns the composed inventory flow, and the root keeps only the host-specific Apple probe. Can the two helpers above go back to being private in composed-inventory-gateways.ts? Can the wrapper in src/platform-runtime-device-inventory.ts be folded into createPlatformDeviceInventoryGateways in platform-runtime.ts, or renamed for what it does, for example attachAppleInstalledAppProbe? It is async and shares its name with the sync package function it wraps, and it carries a comment about where the flow moved. A smaller gap: the Android fixture entries have a consumer-gate pin in platform-package-policy.ts. Should the new test-only subpaths device-selection/inventory-gateways-fixtures and replay-port/replay-script-source-fixtures get the same?

Of the earlier inline threads, the four from cubic-dev-ai are fixed at this head and can be resolved: the test-only subpath check order, the Android IME fixture path, the R71 retired path, and the consumer accept and deny test.

Once the design questions above are answered, this can follow base PR #3519.

@thymikee

Copy link
Copy Markdown
Member Author

All three design questions are addressed in new commits on top of f5ed49c; no history rewrite, so #3544's rebase target is just the new tip.

1. Helpers back to private — done (5964aad1c). assertSourceFamily and assertProviderOutcome are private functions in packages/device-selection/src/composed-inventory-gateways.ts, their only consumer now that the fake composes through the real composer. The contracts device facade drops both re-exports, the AppError import, and the now-false ProviderDeviceInventorySource import; the review-history docstrings went with them (the surviving comments state the invariant, not its history). packages/contracts/src/device-inventory.ts no longer imports AppError or Platform at all.

2. Wrapper — renamed, not folded (2de5aad5e). It is now attachAppleInstalledAppProbe in src/platform-runtime-device-inventory.ts, async like what it promises, and the where-the-flow-moved comment is cut to one line naming the probe. I evaluated folding it into createPlatformDeviceInventoryGateways in platform-runtime.ts and chose against it: the dynamic import('@agent-device/device-selection/composed-inventory-gateways') would land in the composition root itself, where platform-composition-policy.ts's import allowlist is category-based (contracts, platform façades, named host adapters). A fold does not retire the special cases, it moves them: platform-composition-policy.ts would have to name either the wrapper call or widen the allowlist with a non-platform package specifier — a looser gate than the wrapper file the zones already carry. Renaming is the simpler result; the root-module-zones.ts and platform-composition-policy.ts entries stay as reviewed.

3. Consumer-gate pins — added (adb27488e). One placement note: R13's ANDROID_TEST_ONLY_SUBPATHS lives in the platform-package rule, which is defined over the six packages/platform-* declarations and cannot make a claim about device-selection or replay-port without changing what R13 governs. The same claim now lives where non-platform cross-package specifier edges are already owned: TEST_ONLY_FIXTURE_SUBPATHS in scripts/layering/package-boundaries.ts (R11) rejects both subpaths from any production consumer in both the package-internal and root scans, with the same non-production predicate as R13. The accept/deny test mirrors the Android one: __tests__, .test.ts, and test/ consumers pass; the same import from src/commands/... or a package src/ production file is denied. Bite proven: a planted production import of both subpaths under src/daemon/ produced exactly the two R11 violations, then was removed.

Evidence at the new head: check:quick, check:layering, check:di-seams, check:gate-manifest, the eager-closure-budgets suite (854), the test-file-size ratchet, and the targeted lanes (device-selection, replay-port, root wiring, platform-android, full src/daemon, daemon-composition, 4.9k tests) locally green; the full check:affected --run sweep under the host gate lock: all runnable checks passed.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 9 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread scripts/layering/package-boundaries.test.ts Outdated
Comment thread scripts/layering/package-boundaries.ts Outdated
…akes

Daemon tests and fixtures no longer import the root test-utils and plugin
fixture modules:

- device-inventory-gateways: daemon policy tests get a support fake at the
  ComposedDeviceInventoryGateways port composed from contracts projection and
  provider-request scoping only; root composition tests keep the real helper.
- replay-script-source: daemon tests get a support fake that builds client
  replay source bundles through the package replay-backend registry and a
  deterministic suite walk, with no root command-layer imports.
- android-snapshot-capture, android-ui-hierarchy-fixtures (plus the archived
  raw capture JSON) move under src/daemon/__tests__/support/ and are imported
  from the root test trees that share them (root -> daemon test support is an
  allowed direction); plugin.fixtures moves to @agent-device/test-kit as a
  data-only fixture over package vocabulary.
…y source collector

Owner review on #3517: the two daemon-side fakes restated logic that already
had owners. Both are replaced by single importable owners:

- The device-selection test fake becomes the one inventory-gateways fixture:
  moved to packages/device-selection/src/inventory-gateways.fixtures.ts and
  exported as ./inventory-gateways-fixtures (with findInstalledApp already in
  its options and createTestDeviceInventoryGatewaysFromProvider now exported).
  The daemon copy, the root fake, and its pin (the real composer's fail-closed
  contract is pinned in platform-runtime-device-inventory.test.ts) are deleted;
  daemon, root, device-selection, and integration consumers all import the
  owner.
- The real client collector moves into its package: script-source-bundle and
  source-discovery move from src/commands/replay into replay-port (own exports
  ./script-source-bundle, ./source-discovery). The bundle helpers live once in
  packages/replay-port/src/daemon-port/replay-script-source.fixtures.ts
  (./replay-script-source-fixtures), typed on DaemonWireRequest; both test
  trees import it and run the same globs, size cap, AppErrors and ranking.
- The Android capture fixtures move to platform-android test-only entries
  (./snapshot-capture-fixtures, ./ui-hierarchy-fixtures) so no test tree
  reaches sideways; the archived raw JSONs move with them.
…the test fake

Move assertProviderOutcome and assertSourceFamily from
src/platform-runtime-device-inventory.ts into @agent-device/contracts/device
(device-inventory.ts, exported through ./device). The test fake in
device-selection previously restated the provider flow without the outcome
assertion, so a malformed provider outcome surfaced as a TypeError or fell
through instead of the provider_inventory_contract_violation AppError. Both
the production composer and the fake now call the same validators, and a
fixture test pins the fail-closed behavior on the fake.

The fake's local path keeps its per-family filter: the local option is a
flat whole-inventory discoverer fanned out per family, so filtering there is
the adapter to the per-family source contract rather than a contract the
fake would assert against itself.
…on port

The replay source bundle loader and caller-side path discovery run on the
client (#1802: the daemon opens no caller path), yet they sat under
packages/replay-port/src/daemon-port/, where nothing stopped daemon code
from importing them. Move script-source-bundle, source-discovery, and the
request fixture to the replay-port source root, which stays outside the
engine-prefix policy and the daemon-port zone; the subpath names are
unchanged, so consumers and the CLI closure guard retarget by path only.
…package

The composed-gateways flow (provider-first discovery, declined-to-local
fallback, request projection, per-family fan-out) moves from the root
composition into @agent-device/device-selection/composed-inventory-gateways
with the installed-app probe injected rather than hardcoded, exported as a
production subpath. The test fake now builds its gateways through that real
composer with fake registry sources instead of restating the flow, so the
root, daemon-composition and integration consumers exercise production
behavior rather than a copy that could drift from it.

src/platform-runtime-device-inventory.ts keeps its path as the production
wiring that attaches the Apple simulator probe, importing the package
composer function-scoped so the composition root's eager closure does not
grow, and its root-side suite pins that factory composition.
- The relocated archived-capture pin in the affected-lanes test names its real
  path: packages/platform-android/src/fixtures/android-ime-capture.raw.json.
- The two Android test-only fixture subpaths keep export validation but leave
  the general mechanics-facet consumer allowlist: only non-production files may
  import them, so production code (src/commands/ style) importing a fixture now
  trips R13 instead of riding the facet seam.
- R71's guidance and owner list follow the caller collector to
  packages/replay-port/src/; the retired src/commands/replay/ path was deleted
  by the collector move and left the policy naming a stranded owner.
Mirror the mechanics/adb-host facet tests: a test or integration file importing
@agent-device/platform-android/snapshot-capture-fixtures passes R13, while the
same import from production files (src/commands/ and src/core/interactors/,
the latter proving production files ride no fixture seam) is denied. Guards
the ANDROID_TEST_ONLY_SUBPATHS predicate added for the review's P2.
The test fake now composes through the real composer, so
composed-inventory-gateways.ts is the only consumer of assertSourceFamily
and assertProviderOutcome. They go back as private functions there, and the
contracts device facade drops them along with the AppError import that only
served the re-export. Answers the review's first simplicity question.
createComposedDeviceInventoryGateways at the root shared its name with the
sync package composer it wraps while being an async wrapper that attaches
the Apple installed-app probe; it is now attachAppleInstalledAppProbe and
the where-the-flow-moved narration is cut to one line naming the probe.
Folding it into platform-runtime.ts was evaluated and rejected: the
composition import allowlist is category-based (contracts, platform
facades, host adapters), so the fold would widen it for one dynamic
import of the package composer — a looser gate than the wrapper file the
zones and composition policy already carry.
The new device-selection/inventory-gateways-fixtures and
replay-port/replay-script-source-fixtures subpaths belong to non-platform
packages, so R13's ANDROID_TEST_ONLY_SUBPATHS predicate cannot reach them;
TEST_ONLY_FIXTURE_SUBPATHS makes the same non-production-consumer claim at
R11, in both the package-internal and root scans, and a planted production
import in src/daemon/ was confirmed to fire before it was removed. Mirrors
the Android fixture accept/deny test.
- The R11 accept/deny test now pins TEST_ONLY_FIXTURE_SUBPATHS against an
  independent expectation before iterating it, so deleting an entry from the
  checker's set can no longer take its production-denial coverage with it.
- The non-production consumer predicate recognizes package-local test trees
  (packages/*/test/), not just the root test/ directory: a package harness
  importing a fixture subpath is a test consumer, matching how R13's file
  vocabulary treats harness trees. Pin extends the accept list accordingly.
thymikee added a commit that referenced this pull request Oct 13, 2026
…y source collector

Owner review on #3517: the two daemon-side fakes restated logic that already
had owners. Both are replaced by single importable owners:

- The device-selection test fake becomes the one inventory-gateways fixture:
  moved to packages/device-selection/src/inventory-gateways.fixtures.ts and
  exported as ./inventory-gateways-fixtures (with findInstalledApp already in
  its options and createTestDeviceInventoryGatewaysFromProvider now exported).
  The daemon copy, the root fake, and its pin (the real composer's fail-closed
  contract is pinned in platform-runtime-device-inventory.test.ts) are deleted;
  daemon, root, device-selection, and integration consumers all import the
  owner.
- The real client collector moves into its package: script-source-bundle and
  source-discovery move from src/commands/replay into replay-port (own exports
  ./script-source-bundle, ./source-discovery). The bundle helpers live once in
  packages/replay-port/src/daemon-port/replay-script-source.fixtures.ts
  (./replay-script-source-fixtures), typed on DaemonWireRequest; both test
  trees import it and run the same globs, size cap, AppErrors and ranking.
- The Android capture fixtures move to platform-android test-only entries
  (./snapshot-capture-fixtures, ./ui-hierarchy-fixtures) so no test tree
  reaches sideways; the archived raw JSONs move with them.
@thymikee

Copy link
Copy Markdown
Member Author

Coordinator: rebased onto #3519's new head af6925d20 (#3519 was rebased onto main again). This PR's 13 commits, including the design-question answers at 9dc0bb5cb, replayed without conflicts and their content is unchanged. Verified: check:quick passes; the device-selection, replay-port, platform-android, contracts, daemon, composition and layering lanes pass (678 files, 5044 tests). New head 3bdeef3fe.

@thymikee
thymikee force-pushed the refactor/daemon-test-data-fakes branch 2 times, most recently from 3bdeef3 to 6b4b1d9 Compare October 13, 2026 17:59
thymikee added a commit that referenced this pull request Oct 13, 2026
…ize tripwire

Unblocks the stack: 9dc0bb5 (the design-question round on #3517) grew
package-boundaries.test.ts from 961 to 1005 lines, crossing the 1000-line
test-file tripwire, so the size ratchet fails unit-core on #3517 itself and
every branch stacked on it. The ratchet's own message prescribes the repair:
split along the mirrored source. Pure move — the test body is byte-identical;
the in-memory parser fixtures it shares with the R11 suite move to
package-boundaries.fixtures.ts so the split files parse one pair.
package-boundaries.test.ts is over the 1,000-line tripwire (961 at the
merge-base), so the size ratchet rejects the consumer-gate pins these
branches add to it. Tests mirror source topology: the test-only fixture
subpath inventory (R11's TEST_ONLY_FIXTURE_SUBPATHS accept/deny gate and
the platform-android manifest's fixture export entries, both owned by
package-boundaries.ts) moves with its rule into
scripts/layering/test-only-fixture-subpaths.test.ts, mirroring the
R13-side pin in platform-package-policy.test.ts. Move-only: the accept/
deny test is byte-identical and the manifest assertion keeps its full
sorted key list; it reads the tree itself instead of sharing the real-
tree test's package list. package-boundaries.test.ts ends at 955 lines,
under its base length.
@thymikee

Copy link
Copy Markdown
Member Author

Coverage was red on the size ratchet: scripts/layering/package-boundaries.test.ts is over the 1,000-line tripwire (961 at the merge-base), and this PR's consumer-gate pins grew it to 1,005. Fixed at the cause in 1ae90cfd8 — the fixture-subpath pins moved to their own test module instead of trimming anything:

  • New scripts/layering/test-only-fixture-subpaths.test.ts holds the test-only fixture subpath inventory owned by package-boundaries.ts: R11's TEST_ONLY_FIXTURE_SUBPATHS accept/deny gate (moved byte-identical, incl. the independent set pin and the package-test/ accept case) plus the platform-android manifest's fixture export entries, which were two lines added inside the real-tree key-list test. The new file mirrors the R13-side pin's placement next to its owner (platform-package-policy.test.ts), following the facade-surface.test.ts split precedent.
  • The manifest assertion keeps its full sorted key list; it now calls readWorkspacePackages itself rather than riding the real-tree test's package list — same assertion, same tree.
  • package-boundaries.test.ts ends at 955 lines (≤ 961); nothing else was touched.

Verified: ratchet test green, check:layering green (R11/R13 messages unchanged), check:quick (lint+typecheck) green, format:check green, and the three affected test modules pass (46 tests). New head: 1ae90cfd8.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant