Skip to content

refactor: consolidate architecture ownership and client results#1210

Merged
thymikee merged 6 commits into
mainfrom
devin/1783749457-architecture-consolidation
Jul 11, 2026
Merged

refactor: consolidate architecture ownership and client results#1210
thymikee merged 6 commits into
mainfrom
devin/1783749457-architecture-consolidation

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Consolidates #1182, #1183, #1184, and #1187 by moving shared contracts below their consumers and eliminating the full 36-identity target-spine back-edge baseline without introducing compatibility barrels:

contracts | request | selectors | platforms -> core -> commands | cli-schema
                                             -> client | daemon/server -> daemon/client -> cli
  • Moves selector resolution, request cancellation/progress, CLI schema support, result serialization, platform capabilities, and shared command contracts to focused owners; the CLI flag registry is split by command family.
  • Narrows the closed-result Node client batch (doctor, diff, replay, test, record, trace) to canonical exact contracts while preserving runtime payloads and documenting the 0.20 minor-version policy; dynamic interaction families remain deliberately broad.
  • Preserves public ios/macos wire values, command behavior, descriptor semantics, fast paths, and centralized interaction guarantees—including the selector-resolution disclosure added on main.
  • Strengthens the architecture guard with ranked neutral zones and synthetic forbidden-edge tests, and pins selector bundling to stable sdk-selectors.js with a clean-build regression that rejects auto-numbered fallback chunks.
  • Updates moved-code breadcrumbs and enforcement pointers so ADRs, Swift parity comments, Fallow, package exports, and interaction-guarantee runtime ownership resolve to real modules.

Link to Devin session: https://app.devin.ai/sessions/b1c3093bf2b248ca82a1a5df89152d02
Requested by: @thymikee

@thymikee thymikee self-assigned this Jul 11, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.7 MB 1.7 MB +1.6 kB
JS gzip 530.8 kB 530.9 kB +57 B
npm tarball 638.7 kB 640.1 kB +1.4 kB
npm unpacked 2.3 MB 2.3 MB +6.0 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 25.8 ms 26.1 ms +0.3 ms
CLI --help 54.5 ms 53.7 ms -0.7 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/agent-device-client.js +65.6 kB +20.3 kB
dist/src/sdk-selectors.js +15.7 kB +5.4 kB
dist/src/registry.js -3.9 kB -1.2 kB
dist/src/server.js +1.9 kB +510 B
dist/src/tv-remote.js -27 B +12 B

@thymikee

Copy link
Copy Markdown
Member

Review found one small cleanup before readiness: two source-of-truth breadcrumbs are stale after the ownership moves. docs/adr/0012-interactive-replay.md still points to deleted src/cli/parser/cli-flags.ts, and RunnerTests+ScrollGesture.swift still points to old src/core/scroll-gesture.ts. Update both to the new owning modules and verify no other references to the deleted paths remain. Runtime/architecture review otherwise found no blocking issue; assigning a focused fixer.

@devin-ai-integration

devin-ai-integration Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Addressed in 8427ca7a and 492468497: updated all stale breadcrumbs, expanded and synthetically tested the new neutral-zone layering ranks, and fixed the selector chunk predicate with a production-build assertion for stable sdk-selectors.js (and no selectors2.js). CI is green (19/19), including Swift compatibility and unit compile.

@thymikee

Copy link
Copy Markdown
Member

Follow-up re-review at 8427ca7a0 found three remaining items:

  1. P2: selector chunk grouping is still ineffective: exact-head build emits selectors2.js (~15.1 kB), with no sdk-selectors.js/.d.ts. Fix the grouping predicate and add a build-output assertion for the intended stable chunk.
  2. P2: the new contracts/request/selectors/cli-schema layering ranks are untested. Removing them leaves model tests and the empty baseline green. Add synthetic prohibited import edges proving each new zone is enforced.
  3. P3: RunnerTests+ScrollGesture.swift still references deleted src/core/__tests__/scroll-gesture.test.ts; update it to src/contracts/scroll-gesture.test.ts.

All exact-head CI is green, but the two P2s keep this follow-up from readiness. Assigning a focused fixer.

@devin-ai-integration

devin-ai-integration Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up re-review items are fixed in 492468497: selector grouping now emits stable sdk-selectors.js and rejects selectors2.js in the production-build test; synthetic tests pin all four new layering-zone ranks; the remaining Swift breadcrumb now points to src/contracts/scroll-gesture.test.ts. CI is green (19/19), including the Swift checks.

thymikee and others added 4 commits July 11, 2026 07:11
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@thymikee

Copy link
Copy Markdown
Member

Re-review at 492468497: the three architecture cleanup findings are resolved. The selector chunk now builds as sdk-selectors.js with a clean-build regression, all four new neutral ownership zones have synthetic forbidden-edge coverage, and the stale Swift breadcrumb is corrected. The independent GPT-5.5 pass found no remaining issues, and all 22 checks are green.

Because this is a broad architecture refactor, the configured high-risk route still calls for an independently authorized Claude pass. I am withholding ready-for-human until that cross-vendor evidence exists.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration devin-ai-integration Bot force-pushed the devin/1783749457-architecture-consolidation branch from 4924684 to e731982 Compare July 11, 2026 07:13
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Independent Claude/Opus cross-vendor high-risk pass

Reviewed exact head e73198268 against base main c93dcdbc9, tracing real code (not summarizing prior GPT/Devin passes). Re-ran gates locally: format:check, lint, typecheck, check:layering (787 files, 0 back-edges), check:mcp-metadata, the clean-build owner-file/selector-chunk test, and targeted vitest for selectors/request/contracts/cli-schema/commands/cli-grammar/client (all green). CI at head: 18 passed / 1 pending (Smoke).

Findings

LOW — stale ADR breadcrumbs to a deleted module (actionable)
Three source-of-truth pointers still reference src/daemon/selectors-resolve.ts, which this PR moved to src/selectors/resolve.ts:

  • docs/adr/0010-error-system.md:33selectorFailureHint, STALE_REF_HINT in src/daemon/selectors-resolve.ts
  • docs/adr/0012-interactive-replay.md:59src/daemon/selectors-resolve.ts:153-204
  • docs/adr/0012-interactive-replay.md:132selectorFailureHint, src/daemon/selectors-resolve.ts:84-97

All four symbols now live in src/selectors/resolve.ts (selectorFailureHint, STALE_REF_HINT, accumulateDisambiguationCandidate/compareDisambiguationCandidates), so both the paths and the cited line ranges are stale. This is the same breadcrumb class the earlier review required fixing in 0012 (the cli-flags.ts pointer) and the class the final commit fixed in interaction-guarantees.ts — the ADR prose was just missed. No gate covers doc breadcrumbs, so it won't fail CI. Suggest repointing to src/selectors/resolve.ts (with refreshed line refs) before human handoff.

INFO — one back-edge cleared by zone reclassification, not dependency inversion (non-blocking)
The client -> daemon-client identity was removed by relocating src/client/client.tssrc/agent-device-client.ts (root (root) zone, which TARGET_DAG_RANK intentionally leaves unranked), not by inverting the edge — src/agent-device-client.ts:1 still statically imports ./daemon/client/daemon-client.ts. This is consistent with the other root composition entrypoints (cli.ts, backend.ts) and is a legitimate design choice, but the headline "removes all 36 target-spine back-edge identities" is achieved for that one identity via reclassification rather than removing the underlying dependency. Flagging only for transparency.

Verified clean (no findings)

  • (1) Back-edges / ranks: back-edge-baseline.json is {}; check.ts reports zero drift; new ranks contracts/request/selectors=1, cli-schema=3 are exercised by the synthetic forbidden-edge test (scripts/layering/model.test.ts, 7/7). uniqueStrings moved to src/kernel/collections.ts (rank 0) and action-utils/selectors/request/cli-flags/client-shared consumers now resolve down-only.
  • (2) Selector ownership + feat: disclose selector resolution in interaction responses #1193 disclosure: resolutionDisclosure/disambiguation/errorTaxonomy cells preserved; via pointers updated to src/selectors/resolve.ts#... and are runtime-validated (interaction-guarantees.test.ts dynamically imports each module and asserts symbol in mod; 8/8 green — this is why the final commit was required).
  • (3) sdk-selectors chunk: owner-files-no-leak.test.ts clean build (via --config-loader native) asserts sdk-selectors.js exists and selectors2.js does not — passes locally; chunk predicate now anchors src/selectors/index.ts.
  • (4) Node-client narrowing: doctor/diff/replay/test/record/trace narrowed to CommandResult<K>; producers are typed (satisfies DoctorCommandResult, Promise<DiffSnapshotCommandResult>, satisfies ReplayCommandResult/TraceCommandResult/RecordingCommandResult), so runtime-shape parity is typechecker-enforced. Result shapes moved verbatim (e.g. mode:'snapshot' unchanged); daemon/types.ts re-exports the contract types (single source of truth); type-equality parity tests + semver rationale in docs/node-client-result-types.md (0.20 minor).
  • (5) CLI grammar split: flag count identical (141 → 141) across flag-definitions-{connection,target,action,workflow}.ts aggregated by flag-registry.ts; deleted src/cli/parser/cli-flags.ts has no remaining code importers.
  • (6) MCP: additive output schemas for the six new closed results; command-tools.ts is import-only; sync-mcp-metadata --check passes.
  • (7) Wire / fast paths: apple-os-capabilities.tsplatforms/apple/capabilities.ts is a pure (R100) rename; shouldUseHostMacFastPath moved verbatim and is still shared by both platforms/apple/plugin.ts and core/platform-inventory.ts; no ios/macos wire collapse; Integration + Coverage (apple-leak guard) green.
  • (8) Bundle/exports: package.json and tsdown entry unchanged; buildFlags moved verbatim into commands/command-flags.ts (now private); Bundle Size chunk deltas are the expected relocations.

Readiness

No blocking issues. Runtime semantics, layering, selector ownership, client narrowing, MCP, and platform wire are all sound. Recommend fixing the LOW stale-breadcrumb finding (docs-only) and confirming the pending Smoke check before marking ready-for-human. Not approving/merging or labeling, per scope.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Independent Claude/Opus cross-vendor pass — verdict at 605934492

Verified only the breadcrumb fix (605934492) against my earlier LOW finding. Scope is docs-only (docs/adr/0010-error-system.md, docs/adr/0012-interactive-replay.md, 3 lines); no code touched.

  • 0010:33 → now src/selectors/resolve.ts
  • 0012:59 → now src/selectors/resolve.ts:181-285 (covers accumulateDisambiguationCandidate @181 / compareDisambiguationCandidates @260) ✔
  • 0012:132 → now selectorFailureHint, src/selectors/resolve.ts:110-113 (@110), thrown at resolution.ts:213-217 (@216) ✔
  • Repo-wide grep for daemon/selectors-resolve across docs/CONTEXT/AGENTS/skills/website/apple-runner: none remaining

The paths and the refreshed line ranges are accurate. My LOW finding is resolved; the earlier INFO observation (root-zone reclassification of the client→daemon-client edge) was non-blocking and unchanged.

Verdict: No blocking issues. Runtime semantics, layering, selector ownership, client narrowing, MCP, and platform wire remain sound; the sole actionable finding is fixed. From the independent cross-vendor pass, 605934492 is ready-for-human pending the in-flight CI run (Typecheck, Lint & Format, Layering, Fallow already green at this head; remaining jobs are docs-irrelevant re-runs). Not approving/merging or labeling, per scope.

@devin-ai-integration devin-ai-integration Bot added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Jul 11, 2026
@thymikee thymikee merged commit 0a8ea3a into main Jul 11, 2026
22 checks passed
@thymikee thymikee deleted the devin/1783749457-architecture-consolidation branch July 11, 2026 07:40
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-07-11 07:40 UTC

thymikee added a commit that referenced this pull request Jul 11, 2026
…lution guard + step-5 resume wiring

Rebuilt onto current main (#1210 selector relocation, #1211 resume + --update
retirement, #1213, #1215) as one coherent commit — the prior 4-commit history
collided structurally with #1211's own session-replay-runtime split.

Step 4 (decision 3, enforcement): every annotated resolved-target replay/test
action is verified against a fresh pre-action snapshot BEFORE dispatch
(verifyReplayActionTarget → classifyReplayTarget, decision 3's six paths). Only
a verified outcome sends the action; selector-miss / identity-mismatch /
identity-unverifiable each return a complete target-binding REPLAY_DIVERGENCE
built from its own capture, with targetBinding {classification==kind,
matchCount per the presence rule, recorded/observed, mismatches, candidates}
through #1211's shared failure-response + bounding/sanitizer pipeline.

Post-resolution guard: a verified outcome mints internal.replayTargetGuard
(the verified member's identity); dispatch's own resolution (occlusion/
visibility guards verification does not replicate) must land on the SAME
element or assertExpectedResolvedTarget refuses pre-action, which the loop
converts to an identity-mismatch divergence. Guarded dispatch skips the
direct-iOS and native-ref fast paths so a tree node exists to check.

Step-5 resume wiring (this rebase): the target-binding divergence resume stub
is replaced with #1211's buildReplayDivergenceResume — a pre-action divergence
resumes AT the failed step (from = step index, real SHA-256 planDigest, allowed
unless preflight rejects the skipped range). buildTargetBindingDivergenceResponse
is the single resume site; planActions/planDigest are threaded from the loop.

--update healing is retired by #1211, so the in-branch "verify healed action"
work is dropped (its module and test removed as moot).

Tests: classification (13), guard (4), token (5), runtime end-to-end (9, incl.
old-script pass-through, guard threading + mismatch conversion, and a real
computed resume assertion), plus target-binding wire tests in divergence.test.ts
(classification==kind, matchCount presence, overflow-fallback targetBinding,
computed-resume-not-stub, text report). Also repoints
daemon-http-disconnect.test.ts already handled upstream by #1215.
thymikee added a commit that referenced this pull request Jul 11, 2026
… enforcement (#1209)

* feat(replay): ADR 0012 step 4 target-binding verification + post-resolution guard + step-5 resume wiring

Rebuilt onto current main (#1210 selector relocation, #1211 resume + --update
retirement, #1213, #1215) as one coherent commit — the prior 4-commit history
collided structurally with #1211's own session-replay-runtime split.

Step 4 (decision 3, enforcement): every annotated resolved-target replay/test
action is verified against a fresh pre-action snapshot BEFORE dispatch
(verifyReplayActionTarget → classifyReplayTarget, decision 3's six paths). Only
a verified outcome sends the action; selector-miss / identity-mismatch /
identity-unverifiable each return a complete target-binding REPLAY_DIVERGENCE
built from its own capture, with targetBinding {classification==kind,
matchCount per the presence rule, recorded/observed, mismatches, candidates}
through #1211's shared failure-response + bounding/sanitizer pipeline.

Post-resolution guard: a verified outcome mints internal.replayTargetGuard
(the verified member's identity); dispatch's own resolution (occlusion/
visibility guards verification does not replicate) must land on the SAME
element or assertExpectedResolvedTarget refuses pre-action, which the loop
converts to an identity-mismatch divergence. Guarded dispatch skips the
direct-iOS and native-ref fast paths so a tree node exists to check.

Step-5 resume wiring (this rebase): the target-binding divergence resume stub
is replaced with #1211's buildReplayDivergenceResume — a pre-action divergence
resumes AT the failed step (from = step index, real SHA-256 planDigest, allowed
unless preflight rejects the skipped range). buildTargetBindingDivergenceResponse
is the single resume site; planActions/planDigest are threaded from the loop.

--update healing is retired by #1211, so the in-branch "verify healed action"
work is dropped (its module and test removed as moot).

Tests: classification (13), guard (4), token (5), runtime end-to-end (9, incl.
old-script pass-through, guard threading + mismatch conversion, and a real
computed resume assertion), plus target-binding wire tests in divergence.test.ts
(classification==kind, matchCount presence, overflow-fallback targetBinding,
computed-resume-not-stub, text report). Also repoints
daemon-http-disconnect.test.ts already handled upstream by #1215.

* test(replay): assert real computed resume on identity-mismatch and guard-mismatch paths

Non-blocking review follow-up: only the selector-miss runtime test asserted
the computed resume {allowed, from, planDigest}. All three target-binding
paths funnel through buildTargetBindingDivergenceResponse (one shared resume
site), so this is coverage not a bug — but pin resume on the identity-mismatch
and guard-mismatch tests too so no path can regress its resume wiring silently.

* fix(replay): carry structural denotation into the post-resolution guard (P1)

The guard carried only LOCAL identity {id, role, label}. ADR path 6 isolates
ONE member among several SAME-local-identity nodes via sibling / region-scoped
viewportOrder — a discriminator lost at the guard. So if verification denoted
duplicate A but dispatch's occlusion/visibility filter selected duplicate B
(identical id/role/label), the guard passed and SENT the action on the wrong
element — the exact verified-but-taps-different-node mis-binding step 4 exists
to prevent.

Fix: replayTargetGuard now carries a structural denotation of the verified
member — its pre-order document index (decision 3's canonical total order,
`node.index` after the shared reindex pipeline) plus its same-parent sibling
ordinal. `assertExpectedResolvedTarget` compares BOTH local identity AND
structural denotation against dispatch's pre-promotion capture, refusing
pre-action when EITHER differs. Two distinct duplicates always differ in
document order, so a different-member resolution can no longer pass. The
promotion exemption is preserved (comparison is on the verified LEAF, and
duplicates are distinct leaves). New helpers live in target-identity-node.ts
(replay zone) so writer/classifier/guard share one sibling-ordinal
implementation; computeSiblingOrdinal now delegates to it.

The guard-mismatch divergence surfaces a `position:` mismatch line (from the
structural denotations) so `mismatches` is never empty when local identity is
identical.

END-TO-END regression (session-replay-target-guard.test.ts): two "Save"
buttons with IDENTICAL id/role/label, A covered+deeper (verification's winner),
B visible (dispatch's winner) — driven through the real interaction runtime
(`device.interactions.press`), the guard refuses pre-action with ZERO backend
taps AND zero native tapTargets, the refusal driven purely by the structural
denotation (both observedStructural/expectedStructural asserted). Plus a
same-structural passing case, a runtime-level `position:`-mismatch divergence
test, and updated guard-threading assertions for the {identity, structural}
shape.

Wire contract unchanged: replayTargetGuard is DaemonRequestInternal (never on
the wire); decision 4's targetBinding {recorded, observed} stay LOCAL identity
per decision 3's Identity tier — the structural denotation is an internal
guard concern, surfaced only as a `mismatches` string. No ADR change needed.
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