Skip to content

<objective> Land the shared backoff schedule and the BridgePort contract that the Anda bridge, the AgentOS supervisor, and the controller all build on. No live I/O in this plan.

Purpose: Unblock every retry loop in the phase (D-59, D-62) and the controller bridge dependency (D-64) with pure, fully injected code. Output: One pure utility, one interface addition, and the backoff test. </objective>

<execution_context> @~/.claude/gsd-core/workflows/execute-plan.md @~/.claude/gsd-core/templates/summary.md </execution_context>

<context> @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/03-container-runtime-and-bridges/03-CONTEXT.md @.planning/phases/03-container-runtime-and-bridges/03-PATTERNS.md </context>

<tasks>

<task type="tdd"> <name>Task 1: Bounded backoff with full jitter</name> <files>src/core/backoff.ts, test/core/backoff.test.ts</files> <read_first> - src/core/timeout.ts (one exported function, no imports; backoff follows the same shape) - test/core/timeout.test.ts (liveTimers helper and QUAL-01 title style) </read_first> <behavior> - Test: delay for attempt n equals random() * min(cap, base * 2^n) with injected random returning 0 and 1 for the jitter bounds - Test: delays never exceed cap across many attempts; zero jitter boundary when random returns 0 - Test titles cite ANDA-03 and RT-04 </behavior> <action>Write the failing test first per behavior, then implement computeBackoffDelayMs(attempt, baseMs, capMs, random) in src/core/backoff.ts per D-49 and the pattern map section 1 row 1: pure function, random source passed as a parameter, imports nothing from outside core, full jitter formula random() * min(cap, base * 2^attempt). Callers pass their own base and cap (Anda 500/30000 per D-62, AgentOS 500/8000 per D-59); this module holds no defaults beyond its signature. Sleep stays with the caller, never here.</action> <acceptance_criteria> - test/core/backoff.test.ts asserts the jitter bounds with injected random, the cap, and the zero-jitter boundary - src/core/backoff.ts imports nothing </acceptance_criteria> <verify> <automated>npx tsc -p tsconfig.json && node --test dist/test/core/backoff.test.js</automated> <fails_when>non-zero exit, or the summary shows 0 passed</fails_when> </verify> <done>Backoff schedule green with injected randomness and no imports.</done> </task>

<task type="auto"> <name>Task 2: BridgePort beside ExecutionService</name> <files>src/core/ports.ts</files> <read_first> - src/core/ports.ts (ExecutionService lines 16-22, frozen per D-39) - src/core/state-machine.ts (StateSnapshot.lastTransitionAt shape that BridgeEvent.at must match per F-P3-04) </read_first> <action>Add the BridgePort interface and BridgeEvent type to src/core/ports.ts per D-49 and pattern-map F-P3-04: BridgeEvent is { kind: up | down | mismatch, code?: ErrorCode, at: string } where at matches StateSnapshot.lastTransitionAt; BridgePort has connect(deadlineMs), current snapshot, subscribe, and close. ExecutionService keeps its exact D-39 signatures with no environment field. Interface only, no runtime values. BridgeEvent lives in this module, never in the RealmEvent union in src/core/events.ts.</action> <acceptance_criteria> - src/core/ports.ts exports BridgePort and BridgeEvent with the exact D-64/D-65 member shapes - ExecutionService text is byte-identical to before except surrounding context </acceptance_criteria> <verify> <automated>npx tsc -p tsconfig.json && node --test dist/test/core/backoff.test.js</automated> <fails_when>non-zero exit, or typecheck reports any error in ports.ts consumers</fails_when> </verify> <done>BridgePort exists beside the untouched ExecutionService with typecheck green.</done> </task>

</tasks>

<threat_model>

Trust Boundaries ​

BoundaryDescription
caller seed -> backoffAttempt counters cross into delay computation; overflow must clamp at cap, never NaN or negative

STRIDE Threat Register ​

Threat IDCategoryComponentSeverityDispositionMitigation Plan
T-03-01D (Denial of Service)src/core/backoff.tslowmitigatePure clamped formula; injected random bounded to [0,1) by contract documented in the function header; cap enforced with min()
T-03-SCT (Tampering)npm installshighmitigateNo package installs in this plan or any Phase 3 plan; node:http and node:https only; new-dependency gate never triggers
</threat_model>

<verification> npx tsc -p tsconfig.json && node --test dist/test/core/backoff.test.js; grep the test file for ANDA-03 and RT-04. </verification>

<success_criteria> Backoff delays stay bounded with injected jitter, and the controller can depend on BridgePort without any ExecutionService change. </success_criteria>

<output> Create .planning/phases/03-container-runtime-and-bridges/03-01-SUMMARY.md when done </output>

AGPL-3.0-only. Copyright (C) 2026 Spectrum Web Co/swcstudiospace.