Skip to content

refactor(daemon): make the layer folders the declaration (PR 10) - #3520

Draft
thymikee wants to merge 7 commits into
refactor/daemon-packagefrom
refactor/daemon-layer-folders
Draft

thymikee wants to merge 7 commits into
refactor/daemon-packagefrom
refactor/daemon-layer-folders

Conversation

@thymikee

@thymikee thymikee commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

What

Every daemon file in packages/daemon/src moves under a layer folder — {core,resources,execution,sessions,server}/ — keeping its sub-structure inside the layer (e.g. device/device-claim-admission.ts -> resources/device/device-claim-admission.ts). Tests and test support follow their source module one-to-one. R81 stops reading the manifest and derives the layer from the first path segment under the daemon root; scripts/layering/daemon-layer-manifest.json and its drift check are deleted in the same PR. src/daemon.ts and the dist/src/internal/daemon.js output are unchanged.

Re-run on 11b tip 28949e4a8 (refactor/daemon-package, #3546) per coordinator: reset onto the tip, re-ran the move codemod with daemon root packages/daemon/src, no hand-resolved moved files. R81 derives its root from 11b's DAEMON_SOURCE_ROOT constant (scripts/layering/tracked-sources.ts) — no packages/daemon/src/ or src/daemon/ literal filters remain in the rule or its caller — and refuses an empty input set, so a stale scan filter fails loudly instead of passing vacuously. The teardown-closure probe keeps 11b's constant-derived daemonRoot with only the sessions/ hop added.

Mapping

669 tracked files under the package src root, 669 mapped, 10 identity (cross-layer __tests__/support/ leaves), 659 moved: core 117, resources 49, execution 138, sessions 229, server 107, root __tests__/ 19.

  • Test leaves move beside their source module's new path. Owner layer picked deterministically: exact basename -> dash-prefix (ties by vi.mock targets, then physical colocation) -> single-layer direct-import closure -> highest-rank direct layer. Two tests with zero daemon imports land in core/__tests__/.
  • Cross-layer shared test support stays in the top-level __tests__/support/ (10 identity files). R81's scan treats test-tree files as layerless (isProductionSourceFile excludes __tests__/); planting shared support into core/ would claim a layer it does not own. The root __tests__/ is test-only, so the folder-derivation gate still rejects any unlayered production path at the daemon root.
  • Sweep rule: only paths of files that exist at head are retargeted, so historical text (ADRs citing retired files, history notes) keeps its original spelling. Covers md files (87 hits over 16 files incl. AGENTS.md, ADRs, depgraph README, device-evidence checklist) and repo-path string literals, including sentence-final-period comment paths. The depgraph affected/model in-memory fixture graphs are retargeted by hand to stay self-consistent (their internal relative specs describe fake trees, not real paths, so the codemod cannot resolve them).

Line counts

  • Codemod-only move commit: 946 files (+3,669/-3,177; includes the repository-wide format sweep the rewritten lines require). 659 git mv, 2,906 relative-spec rewrites in 585 files (from, import(), require(), vi.mock), 640 package-subpath rewrites (@agent-device/daemon[/testing]/…), 16 exports-map keys moved with their files, 534 literal rewrites, 87 markdown hits over 16 files. The ./testing/* wildcard needs no change: it maps ./src/*.ts and follows every file automatically.
  • Hand-written commits (6): R81 folder derivation + manifest deletion (255-line rule, 303-line manifest JSON gone), fallow/audit-scope retargets, source-reading probe hops, self-review settle, teardown closure probe, docs(adr) owner pointers. 25 files, +282/-677.

Evidence

  • Gates measured at this head: check:quick (lint + typecheck), check:layering (R81 over the moved graph; 9/9 rule tests), check:di-seams, check:gate-manifest, check:production-exports, check:agent-guidance (docs/agents 39,963 <= 40,000 bytes, no trim needed), eager-closure-budgets + test-file-size ratchet (886 cases), depgraph suites (34) — all green. Heavy gates under the host lock: check:affected --run, check:fallow, check:package — green.
  • Parity: dist/src/internal/daemon.js is byte-identical to the base build at 28949e4a8 (baseline rebuilt from the clean base and from the moved tree; both builds deterministic and equal). Per-file test names: all 659 moved files, extracted test(...)/it(...) names identical to the base tree (static extraction over git show base vs head).
  • R81 bite proofs (temporary probes, not committed): upward core -> sessions static import = 1 violation; production file at the daemon root = 1 violation; empty input set = 1 violation. All three caught; the registered test file pins all three permanently.
  • Literals: no tracked file names a moved-away daemon path at head except one intentional merge-base spelling in session-authority-overlay.ts (the R75 ratchet measures the pre-move tree through that constant pair). All non-glob .fallowrc.json file entries name tracked paths (#3550s pruning absorbed the session-test-harness exemption, and the react-native split entry follows its file into the execution layer); no glob for the retired src/__tests__/test-utils/replay-script-source.ts (the file no longer exists in this lineage).

Owner review items applied

  • .device-evidence/CHECKLIST.md: the press/consumed-no-capture pointer now names src/__tests__/daemon-composition/interaction/interaction-capture-disclosure.test.ts — that test's live home at this base (11b moved it out of the package; the earlier suggestion execution/interaction/internal/__tests__/interaction-capture-disclosure.test.ts was its home on the old base).
  • docs/adr/0023-end-state-hop-trace.md row 23 names packages/daemon/src/resources/device/device-claim-admission.ts; ADR 0022's R75 handler-surface sentence names the live {sessions,execution}/handlers/** roots. Other src/daemon/ citations in the ADRs name files the tree no longer carries at all and stay historical.

@thymikee

Copy link
Copy Markdown
Member Author

The PR is ready at 21794fe. The code looks right to me.

Not blocking: the new .fallowrc.json entry exempts every export of src/__tests__/test-utils/replay-script-source.ts, but maestroScriptSourceBundleFor, noMaestroIncludeSources and withClientReplayScriptSources have no importers (callers use the copy under src/daemon/__tests__/support/), so deleting them and dropping the file from the glob would be cleaner. The path-literal rewrite also skipped docs/adr, scripts/depgraph/README.md, .device-evidence/CHECKLIST.md and src/commands/interaction/runtime/offscreen-double-check.test.ts:14, which still name files that no longer exist. R81's caller in scripts/layering/check.ts:588 filters on the literal 'src/daemon/' instead of DAEMON_ROOT, so the planned retarget to packages/daemon/src would hand it an empty list and pass without checking anything. You can take or leave these.

I did not run the layering, fallow or unit suites or pnpm build, and I did not reproduce the R81 bite proofs. The 309 node:test cases, the 889-file affected run and the dist/src/internal/daemon.js byte parity are unverified on my side.

No checks have reported on this head, so nothing has run against the gates this PR changes. The PR has merge conflicts with main, and the head does not descend from the base tip, so the diff also shows unrelated main commits such as the limrun #3504 changes. Before merge, rebase or retarget the base so CI runs the layering, fallow and unit gates, then remove the dead replay-script-source exports instead of suppressing them.

@thymikee
thymikee force-pushed the refactor/daemon-layer-folders branch from 21794fe to ab48059 Compare October 12, 2026 20:33
@thymikee
thymikee changed the base branch from refactor/daemon-test-data-fakes to refactor/daemon-package October 12, 2026 20:34
@thymikee

Copy link
Copy Markdown
Member Author

Base retargeted to refactor/daemon-package (#3546) per coordinator: reset onto the 11b tip and re-ran the move codemod with daemon root packages/daemon/src, then re-applied the hand-written commits with the review findings (R81 caller filter + empty-set guard, stale doc/test literals incl. codemod coverage for md files, dropped test-utils fallow exemption) folded in. Head: ab48059.

@thymikee

Copy link
Copy Markdown
Member Author

Final head 7937155ef (+1 retarget squashed into the probes commit: .device-evidence/CHECKLIST.md named a replay-disclosure test renamed before this stack — pointed at the live find-target-activation-disclosure.test.ts). Evidence in the body was measured at the previous head; the delta is comment-only in a checklist doc.

@thymikee
thymikee force-pushed the refactor/daemon-layer-folders branch from ab48059 to 7937155 Compare October 12, 2026 20:36
@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.18 MB 5.18 MB +47 B
Package (unpacked) 5.18 MB 5.18 MB +47 B
Package (download) 1.56 MB 1.56 MB -94 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 28.2 ms 27.8 ms -0.4 ms
CLI --help 76.6 ms 76.1 ms -0.5 ms

@thymikee

Copy link
Copy Markdown
Member Author

This PR is ready at 7937155, and the earlier conflict from 21794fe is fixed. Not blocking, and you can take or leave these: the retargeted line at .device-evidence/CHECKLIST.md:104 should point at execution/interaction/internal/tests/interaction-capture-disclosure.test.ts, where the press/no-capture test lives, and row 23 of docs/adr/0023-end-state-hop-trace.md:92 should name packages/daemon/src/resources/device/device-claim-admission.ts, since the old path was already stale at base.

The code looks right to me, but I did not build or run the tests, so the byte-identical daemon bundle and the unit-core and test-name parity claims are unverified. Required evidence is in place, and there are no conflicts.

The CI failures look unrelated to this PR. The Repo Guards R11 failure lists the same 63 escape specifiers in 39 files at base 87969a2 and at head (by my own scan of relative specifiers, which can miss multi-line imports), so this PR moves files but adds no escapes. The Mutants jobs fail on code this diff does not touch. The Ownership budget fails because docs/agents is already 40002 bytes at base, over the 40000 cap, and this PR adds 15 bytes through two path retargets. I saw no Typecheck & Package log, so I cannot confirm the PR body's claim that its failure is inherited from 11b.

Before merge, the base branch (refactor/daemon-package) needs to bring docs/agents back under 40000 bytes with about 17 bytes of headroom for this PR, and clear its R11 and typecheck failures.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 12, 2026
@thymikee
thymikee force-pushed the refactor/daemon-layer-folders branch from 7937155 to 5e88063 Compare October 13, 2026 02:08
@thymikee

Copy link
Copy Markdown
Member Author

Re-ran on the current 11b tip (reset to refactor/daemon-package head, codemod re-applied with root packages/daemon/src; no hand-resolved moves). Head 5e880636a. Review pointers applied:

  • CHECKLIST pointer: the press/consumed-no-capture test is src/__tests__/daemon-composition/interaction/interaction-capture-disclosure.test.ts at this base — 11b moved it out of the package, so execution/interaction/internal/__tests__/interaction-capture-disclosure.test.ts (your suggested path, correct on the old base) does not exist here; it was split at 11b and the press-side assertion lives in the root-composition file. The checklist names the live path.
  • ADR 0023 row 23 now names packages/daemon/src/resources/device/device-claim-admission.ts. Remaining src/daemon/ citations in the ADRs name files deleted before this stack and keep their historical spelling (same rule as 11b: only paths that exist at head get retargeted; sweep covers md).
  • R81: root derived from DAEMON_SOURCE_ROOT (11b's constant), no literal root filters in the rule or check.ts caller; empty input set returns a violation. Bite proofs re-run against this head: upward import / production file at daemon root / empty set, 1 violation each; all three are permanent tests in daemon-layers.test.ts (9/9 pass).
  • Manifest JSON + drift check deleted; exports map (16 production keys) moved with its files, ./testing/* wildcard follows automatically; no .fallowrc.json glob for the retired replay-script-source.ts (file is gone in this lineage).
  • Evidence: dist/src/internal/daemon.js byte-identical to the base build; per-file test names identical across all 656 moved files; check:quick, check:layering, check:di-seams, check:gate-manifest, check:production-exports, check:agent-guidance (docs/agents 39,963 ≤ 40,000 — 11b's trim left headroom, no extra trim needed), eager-closure-budgets (883) and test-file-size ratchet green; check:fallow/check:package/check:affected --run green under the host lock. Counts in the body are measured at this head.

@thymikee thymikee removed the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 13, 2026
@thymikee
thymikee force-pushed the refactor/daemon-layer-folders branch from 5e88063 to 75d7641 Compare October 13, 2026 03:41
@thymikee

Copy link
Copy Markdown
Member Author

Re-run on 11b 88f6e4234 per coordinator: reset onto the tip, codemod re-applied with root packages/daemon/src, hand commits replayed (teardown-closure probe merged with 11b's DAEMON_SOURCE_ROOT derivation; the oxlint override stays — its target file exists in this lineage, unlike the old base). Head 75d764166. Body counts and evidence are measured at this head: codemod 939 files / 656 renames; hand-written 6 commits, 25 files. Green at this head: check:quick, check:layering, check:di-seams, check:gate-manifest, check:production-exports, check:agent-guidance, eager-closure + test-file-size (886), depgraph (34), and under the host lock check:affected --run, check:fallow, check:package. Bundle byte-identical, 656/656 test-name parity, all three R81 bite proofs caught. Fresh-context self-review applied: depgraph in-memory fixture graphs retargeted by hand to stay self-consistent (this was the earlier affected failure), dead core/handlers/__tests__ scan root dropped, ADR 0022 handler glob retargeted to the live {sessions,execution}/handlers/**; R10 retired-path regexes intentionally keep their stale spelling (retargeting them would make them reject live files). One known base-ahead trigger: #3544 review fixes will move 11b again — I will re-run the codemod on the next tip.

@thymikee

Copy link
Copy Markdown
Member Author

Re-run on 11b base b03b59e2f: codemod-only move (941 files, 655 renames) plus the six hand commits replayed with zero conflicts; head 43a8978ef. Green at this head: check:quick, layering, di-seams, gate-manifest, production-exports, agent-guidance (39,963 ≤ 40,000), eager-closure + size ratchet (886), depgraph (34), and under the host lock check:affected --run, check:fallow, check:package. Bundle byte-identical vs the clean-base rebuild, 655/655 test-name parity, all three R81 bite proofs caught.

@thymikee
thymikee added this pull request to stack #3564 October 13, 2026 06:02
Daemon-root-relative mapping applied by codemod (git mv plus every
importer repo-wide, package-subpath and vi.mock specifiers, the
package exports map, markdown docs, and repo-path literals
included), with the repository-wide format sweep the rewritten lines
require. The folder under the daemon root becomes the layer
declaration; the manifest still carries the claim until the gate
commit retires it.

659 moves over 669 tracked package files (10 cross-layer test-support
leaves stay identity in the top-level __tests__/support/), 2,906
relative-spec rewrites in 585 files, 534 repo-path literal rewrites,
640 package-subpath rewrites, 16 exports-map keys, and markdown
retargets confined to paths whose files exist at head.
The five layer folders now carry the claim the manifest carried: the
first path segment under the daemon root is the file's layer, an
unknown segment or a production file at the zone root fails, and the
edge rule over core < resources < execution < sessions < server is
unchanged. The manifest JSON and its drift check retire with this
commit — the folder is the declaration, so ownership moves with the
file and cannot decay into fiction separately from the tree. The rule
derives its root from the shared DAEMON_SOURCE_ROOT constant and
refuses an empty input set, so a stale scan filter cannot pass it
silently.
The row cited src/daemon/device-claim-admission.ts, stale since the
package extraction; it now names the file's home in the resources
layer. The other src/daemon/ citations in the ADRs name files the
tree no longer carries at all and keep their historical spelling.

(cherry picked from commit 563b382)
…tale

The probe walked src/daemon/session-teardown.ts, a path the package
extraction had already orphaned; it now resolves through
packages/daemon/src/sessions/, where the move put the teardown owner.
The non-vacuity pins (closure size, materialized-path-registry
membership) carry over unchanged.

(cherry picked from commit 7937155)
(cherry picked from commit 5d943ca)

# Conflicts:
#	src/__tests__/daemon-composition/session-teardown-import-closure.test.ts
…audit scope

The route-handler and interaction-facade fallow entries follow their
files into the sessions/execution layers. The atomic-publish
ownership probe reads its judged sources through new URL(...)
relative to the test, so its specifiers follow the move by hand.
The daemon-shaped test-support file the move brought into the audit
diff joins the per-file test-support exemption: every consumer is
test-tree code --production analysis cannot see.

(cherry picked from commit 687b53d)
(cherry picked from commit 977fbd7)
Ownership probes and eager-closure walks that read daemon modules by
path follow them into their layer folders: the replay ownership scan's
synthetic probe locations move one folder deeper, and the daemon-
shaped test-support fallow entry settles on the one file the move
actually brought into the audit diff.

(cherry picked from commit 41fa23b,
minus the lease.test.ts plugin-manifest hop: the 11b lineage removed
that probe's file body, so nothing follows the move there)
(cherry picked from commit 665850c)
Dead oxlint override for a file deleted before this stack, the
vacuous device-lane fixture pin (now asserts the fixture exists, at
its path in this tree), the R81 helpers that never needed to leave
their module, and the closure-probe hops that follow the interaction
routes and replay probes into their layer folders.

(cherry picked from commit baebba2;
the ime-capture fixture pin tracks its 11b home under
packages/platform-android/src/fixtures/)
(cherry picked from commit 5e88063)

# Conflicts:
#	oxlint.config.ts
@thymikee

Copy link
Copy Markdown
Member Author

The code is in good shape at 43a8978. The earlier clean review at 7937155 still holds, and the layer folders now declare the layers. The 41 checks at this head show none failing. The PR conflicts with its base, refactor/daemon-package. I did not build or run any gate, so the byte-identical bundle, the 655/655 test-name parity, the R81 bite proofs and the green gate list are your claims, and I did not read the codemod commit 49e3d0f in full.

The base branch refactor/daemon-package was rewritten to 28949e4, and it adds three production daemon files outside the layer folders: handlers/session-device-status-response.ts, settle-observation-capture.ts and device/device-claim-conflict.ts. Please re-stack onto 28949e4 by re-running the layer codemod, so these files land in layer folders before R81 runs again. The conflicts in .device-evidence/CHECKLIST.md, .fallowrc.json, AGENTS.md, docs/adr and device-claim-conflict.ts also need to be resolved in that re-stack. Then please share a green R81 run on the new head.

Not blocking: the R81 success message in report() at scripts/layering/check.ts:440 hardcodes 'core < resources < execution < sessions < server' (the header comment at check.ts:57-61 does the same). You could print DAEMON_LAYER_ORDER.join(' < ') instead, and re-wrap the broken 'and no / static edge' line in the header. Take it or leave it.

One question on simplicity. The layer folder is now the declaration inside the package, and daemon-layers.ts owns it well. But the 16 named facade subpaths now include the folder, for example @agent-device/daemon/core/daemon-request, so about 33 importer sites outside the package encode an internal layer choice. Moving one file between layers, which R81 now invites, would then rewrite importers outside the package. Could the named exports keys stay layer-free (./daemon-request -> ./src/core/daemon-request.ts), with only the ./testing/* pattern following the layout, as ADR 0033's facade-not-reach policy suggests?

@thymikee
thymikee force-pushed the refactor/daemon-layer-folders branch from 43a8978 to a9cdd53 Compare October 13, 2026 07:35
@thymikee

Copy link
Copy Markdown
Member Author

Re-run on 11b base 28949e4a8 (stack on current main, #3550 fallow pruning absorbed): codemod-only move (946 files, 659 renames) + six hand commits, head a9cdd5369. Green at this head: check:quick, layering, di-seams, gate-manifest, production-exports, agent-guidance (39,963 ≤ 40,000), eager-closure + size ratchet (886), depgraph (34), and via the host lock check:affected --run, check:fallow, check:package. Bundle byte-identical vs the clean-base rebuild, 659/659 test-name parity, R81 bites 3/3.

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