Plan S1 / spec §11.5.1 / §21.5.
apps/daemon/src/plugins/atoms/handoff.ts gains the on-disk bridge
runHandoffAtom({ cwd, manifest, exportTarget?, deployTarget? })
that reads the canonical state previous atoms wrote and returns
the updated manifest with the right handoffKind / exportTargets[]
attached.
Promotion ladder (spec §11.5.1):
decision='reject' → 'design-only'
decision='accept'/'partial' AND no build-test report → 'implementation-plan'
+ build.passing OR tests.passing → 'patch'
+ build.passing AND tests.passing AND docker/cli export → 'deployable-app'
Inputs read from cwd:
<cwd>/review/decision.json — diff-review atom output
<cwd>/critique/build-test.json — build-test atom signals
Both files are optional; missing → the bridge skips the matching
ladder rung. Monotonicity enforced via recordHandoff() — a manifest
that already carries handoffKind='patch' won't demote to
'design-only' even if a follow-up reject arrives (the reject path
documents the rollback escape hatch via enforceMonotonicHandoff:
false).
Returns:
{ manifest, changed, signals: {
decision?, buildPassing?, testsPassing?, deployable
} }
Caller (the pipeline runner / od plugin export / docker tools-pack
hook) is responsible for persisting the updated manifest. Calls
are idempotent: re-running across already-recorded targets is a
no-op via the recordHandoff() append-only contract.
Daemon tests: 1629 → 1639 (+10 cases on plugins-handoff-pipeline:
all four ladder rungs (reject → design-only, accept-noBT →
implementation-plan, accept+oneSignal → patch, accept+bothSignals
+docker/cli → deployable-app), partial decision behaves like
accept, missing decision file leaves manifest alone, append-only
exportTargets[] dedupe across re-runs, refuses to demote across
the monotonic invariant, signals forwarding).
Co-authored-by: Tom Huang <1043269994@qq.com>