5 Commits

Author SHA1 Message Date
Michał Pierzchała b2b084d2e1 docs: fix phantom specifiers, the duplicate ADR 0019, and the Node floor (#2533)
AGENTS.md routed request cancellation/progress and diagnostics to
`@agent-device/capture-kit` subpaths that no package exports; both live in
`@agent-device/host-kit/request` and `@agent-device/host-kit/diagnostics`. It also
named `@agent-device/contracts` as an importable seam although that package
publishes no root export, and claimed `src/daemon/handlers/session.ts` was over
budget after that extraction already landed at 242 lines.

Two ADRs carried number 0019. The hop trace has its own claims to make, so it now
numbers 0023, joins the index, and keeps the links from ADR 0019 and ADR 0022.

The Node floor split was undocumented: `engines.node` stays at 22.12 because CI
installs the published tarball on that floor, while contributors need 22.13 for the
pinned pnpm. CONTRIBUTING now says so, and installation.md names the 22.12 floor and
the web backend's Node 24 requirement.

Extend the agent-guidance contract to resolve every `@agent-device/*` specifier
AGENTS.md names against the owning package's `exports`, root included, so neither a
phantom subpath nor a phantom package root can route an agent to a module that does
not exist.
2026-09-13 10:15:57 +02:00
Michał Pierzchała 342e98cff9 refactor(daemon): separate open-target policy from platform mechanics (#2416)
* refactor(daemon): separate open-target policy from Android mechanics

Move resolveAndroidPackageForOpen/inferAndroidPackageAfterOpen behind
the Android owning seam in packages/platform-android. resolveSessionAppBundleIdForTarget
now lazily reaches Android mechanics itself instead of taking an
injected resolver function, so open-prepare and selector-dispatch
import only the neutral open plan/result surface from
platform-runtime-open-target.ts. Reclassifies the two R74 inventory
edges to daemon-policy-essential and updates ADR 0022.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0167UVzrdzVMCZqXgxtzWoTD

* refactor: address adversarial review findings on open-target seam

Restore try/catch around the Android-mechanics lazy load so a module
load failure still resolves to undefined instead of throwing. Rename
the unrelated private resolveAndroidPackageForOpen in app-lifecycle.ts
to requireAndroidPackageForOpen to remove the naming collision with
the new exported function. Add a planted-violation regression test
for reintroducing Android mechanics on the selector-dispatch edge.
Tighten ADR/inventory wording that overstated which files consume the
neutral resolver.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0167UVzrdzVMCZqXgxtzWoTD

* fix(platform-android): keep the mechanics facade lazy for the new open-target exports

resolveAndroidPackageForOpen/inferAndroidPackageAfterOpen were re-exported
statically from mechanics.ts, which eagerly evaluates open-target-resolution.ts
on import and tripped the eager-closure-budgets gate (177 -> 178 modules).
Wrap them as lazy async functions, matching the existing pattern used for
listAndroidAppsWithAdb/captureAndroidLogcatWithAdb in the same file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0167UVzrdzVMCZqXgxtzWoTD

* fix(android-tools): keep inferOpenedAppBundleId best-effort on a mechanics load failure

Loading Android mechanics moved from the near-infallible root
platform-runtime-open-target.ts to the real adb-backed mechanics
module, but the wrapper call stayed unguarded. A loader failure now
throws instead of leaving the app-bundle identity unset, even for a
targetless open that never needed the loaded module. Wrap the load
and delegate in try/catch so it degrades to the current bundle id,
matching the pre-refactor behavior, and add a regression test with
the loader rejecting on a targetless open.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0167UVzrdzVMCZqXgxtzWoTD

* perf(android-tools): skip loading Android mechanics when app-bundle identity is known

inferOpenedAppBundleId always loaded Android mechanics before
delegating, even when currentAppBundleId already made the delegate's
own fast-return a no-op. Check it first so the load is skipped
entirely once the identity is already known, and add a regression
test asserting the loader is never called in that case.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0167UVzrdzVMCZqXgxtzWoTD

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-09 17:35:12 +02:00
Michał Pierzchała 871ce1b755 refactor(daemon): normalize lifecycle participation of platform resource owners (#2415)
* refactor(daemon): typed lifecycle participation for platform resource owners

daemon-runtime.ts no longer imports the Apple runner owner, the Android
snapshot-helper/Web orphan cleanups, or the app-log legacy marker recovery
directly. Those are now behind PlatformOwnerLifecycle, a typed
startup/shutdown surface owned by the daemon; the root composition module
platform-runtime-daemon-lifecycle.ts is the sole place that wires the
concrete platform owners into it.

Reclassifies the R76 daemon-platform-runtime-inventory edges accordingly:
the leaked apple-runner-owner and operation-host edges are removed, and
resource-cleanup/daemon-lifecycle stay as composition-essential.

Closes #2333

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017oNQ8THHYFcK7NX1s78kW5

* style: apply oxfmt formatting to R76 inventory test fixtures

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017oNQ8THHYFcK7NX1s78kW5

* test(daemon): update source-ordering assertion for the renamed call site

daemon-runtime.ts now calls platformDaemonLifecycleOwners.recoverLegacyAppLogMarkers
instead of the old recoverLegacyAppLogMarkersAfterDaemonLock dynamic import; the
text-based ordering guard needs to look for the new call site.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017oNQ8THHYFcK7NX1s78kW5

* docs(adr): record #2333 as landed in ADR 0022

The registry (DAEMON_PLATFORM_RUNTIME_EDGES) already reflects the
retired apple-runner-owner/operation-host edges; update the ADR prose
to match instead of leaving #2333 listed as outstanding work.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017oNQ8THHYFcK7NX1s78kW5

* fix(daemon): configure the Apple runner owner only after the daemon lock is held

configureForDaemonLock ran before acquireDaemonLock, which meant a
process that lost the lock race briefly published a global runner-owner
state dir and claim-authority probe it didn't own. Move the call inside
the post-lock try block (verified nothing reads the runner-owner state
before request time — runner-host.ts only captures getter closures) and
drop the now-unreachable clear call on the lock-failure branch. Adds a
source-order regression test pinning the new sequence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017oNQ8THHYFcK7NX1s78kW5

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-09 15:54:43 +02:00
Ahmad Al-Faqih ef5a459294 refactor(daemon): isolate Apple session observations (#2405)
* refactor(daemon): consume a semantic Apple session observation port

* chore(gates): retire direct daemon observation imports

---------

Co-authored-by: PLASMA-FR <173463847+PLASMA-FR@users.noreply.github.com>
2026-09-09 08:28:06 +02:00
Michał Pierzchała 31b01a2fe8 docs(adr): record the #2278 coupling audit and re-trace entry-to-platform hops (#2355)
* docs(adr): record the #2278 coupling audit and re-trace entry-to-platform hops (ADR 0022)

* docs(adr): re-trace hop tables with spine side-calls excluded from hop counts

* docs(adr): renumber R74 references to R76 after the main merge
2026-09-08 13:21:29 +02:00