E5 — One real attempt: implementation plan¶
Companion to the accepted design spec
docs/superpowers/specs/2026-08-20-e5-real-attempt-design.md.
Goal: Ship satyrn-engine attempt CONTRACT and make /implement CONTRACT
run that command inside E3 delivery, using E4 for every write.
Architecture: Python prepares one frozen contract and revision map, starts one hermetic Pi print-mode child, preserves transcript and Git patch artifacts, and returns a typed result. The TypeScript slash command owns only command construction, transport conversion, and receipt display. E3 owns isolation, timeout, cleanup, commit, and publication. satyrn-evals remains an executable consumer and imports no engine code.
Scope guard: no packaging, Windows proof, file creation, shell, subagent, validation, grading, retry, batching, or performance claim.
Correction, 2026-08-21¶
Implement the accepted design with its 2026-08-21 correction. Artifact checks
cover every registered worktree plus Git administrative/common roots using
filesystem identity, and publication is descriptor-relative after pinning the
parent during preparation; all result and exception paths close each acquired
descriptor exactly once. Revision enumeration follows no symlink. All
engine-owned temporary and artifact cleanup failures have typed precedence and
retained-path evidence;
secondary cleanup exceptions preserve the primary exception identity. Inner
and outer argv use --model=VALUE and literal -- boundaries. Delivery
cancellation reports completion only after E3 has torn down the attempt group,
closed, and cleaned or retained its worktree. The default tier injects these
seams and proves descriptor-relative parent swaps on the real filesystem; the
marked integration tier proves cross-worktree and Git-administration identity,
tracked-symlink, and delayed-descendant cases with successful siblings.
Stream read errors, diagnostic callback failures, and filesystem-canonical,
component-boundary contract classification are also typed adapter behavior
rather than uncaught Node exceptions.
The cancellation proof also owns a POSIX outer delivery process group. A
deadline before spawn waits for the spawn event; afterward the first refusal
sends group SIGTERM and settles only after direct-child close and a group
probe reporting ESRCH. The adapter does not SIGKILL that outer group because
E3’s separately-sessioned attempt and worktree cleanup remain E3’s ownership;
an arbitrary outer grace period cannot prove those inner resources are safe.
Windows keeps the previous direct-child fallback and is not part of the E5
platform proof. Keep the one-shot Spawner stable and add the PID/spawn-event/
detached options only to DeliverySpawner.
Task 1: Freeze the public command and typed result¶
Files:
Create
src/satyrn_engine/attempt.pyModify
src/satyrn_engine/exits.pyModify
src/satyrn_engine/cli.pyCreate
tests/test_attempt.pyModify
tests/test_exits.pyModify
tests/test_delivery.py
Steps:
Add
ExitCode.ATTEMPT_FAILED = 10without renumbering existing exits.Add
AttemptCode, frozen/slottedAttemptResult,AttemptContext, andAttemptArtifacts, plus an explicit exhaustive code-to-exit map.Add
attempt [--model MODEL] CONTRACT; resolve the model from the flag, thenSATYRN_MODEL, otherwise return usage 2.Keep
deliver’s literal--parser unchanged and add parser siblings for attempt help, model precedence, malformed arguments, and refusal output.Prove every detailed result maps to one stable process exit and exit 1 is still unassigned.
Commit after the tests pass.
Task 2: Prepare one immutable mutation context¶
Files:
Modify
src/satyrn_engine/attempt.pyModify
tests/test_attempt.pyCreate
tests/test_integration_attempt.py
Steps:
Check
cwdand the contract through E1; require a real Git worktree and capture exactHEADonce.Copy accepted contract bytes to an engine-owned temporary file outside the worktree and point E4 at that copy.
Enumerate tracked paths with NUL-safe Git output, normalize safe POSIX names, reject every symlink component, apply E4’s
fnmatchrule, and hash exact bytes.Refuse before Pi when no existing regular tracked file is writable.
Build the version-1 mutation context and deterministic prompt from the sorted path list.
Give every Git refusal a successful real-Git sibling. Include spaces, control characters where supported, a contract path inside the repo, an external contract, and an attempted contract self-edit.
Commit after default tests and focused integration pass.
Task 3: Run one Pi child and preserve artifacts¶
Files:
Modify
src/satyrn_engine/attempt.pyModify
tests/test_attempt.pyModify
tests/test_integration_attempt.pyCreate
tests/fixtures/attempt/fake_pi.py
Steps:
Define the
PiRunnerprotocol and production subprocess implementation.Build exact Pi argv with print JSON, no session, explicit E3.5/E4 extensions, all discovery disabled, no project approval, and only
read,edit.Sanitize Git routing variables,
VIRTUAL_ENV, andSSH_AUTH_SOCK; set the frozen mutation context and explicit engine checkout.Spool Pi stdout to a safe regular temporary file, forward stderr, reap the child, export exact transcript bytes, and replay them to stdout.
Produce a raw binary-capable Git diff against captured HEAD and atomically export it only when non-empty.
On nonzero Pi exit, preserve both artifacts first, then return
ATTEMPT_FAILED. On any artifact failure, that failure takes precedence.Cover start failure, nonzero, empty output, no diff, binary-like diff, artifacts in any registered worktree or Git administrative root, case-alias containment, pre-existing/symlink paths, parent replacement, path redirection after descriptor acquisition, partial descriptor acquisition, write/cleanup failures, exactly-once close, and primary/secondary exception precedence with success siblings.
Commit after default and integration evidence passes.
Task 4: Turn /implement into E3 around E5¶
Files:
Modify
packages/engine/orchestrator.tsModify
packages/engine/package.jsononly if the final extension split needs itModify
tools/replay_orchestrator.mjsModify
tests/test_mutator.mjsonly for shared transport typesCreate or modify
tests/test_orchestrator.mjsModify
tests/test_integration_protocol.pyor add a focused integration file
Steps:
Keep the one-shot E2/E4 exchange helper used by
mutator.ts.Add typed E3 receipt parsing and a delivery-spawn seam with stdout, stderr, spawn, close, error, deadline, and synchronous termination observation. On POSIX start the outer delivery in a detached process group, hold a refusal that precedes
spawnunless a spawn error proves no child exists, then send groupSIGTERM. A timeout or transport refusal is not returned before both the delivery child closes and signal-0 observesESRCH. Keep present/unknown group states pending, and do not use outerSIGKILLas a substitute for E3’s inner teardown. Retain the existing direct-child TERM/KILL/close fallback on Windows.Resolve an in-repository contract to a worktree-relative inner argument; retain an external contract as absolute.
Build the exact outer E3 command and inner E5 command with
SATYRN_ENGINE_REPOandSATYRN_MODELexplicit,--model=VALUE, and a literal--before a potentially dash-leading contract.Replace the check-only
/implementhandler with delivery. Notify success with candidate ref/commit and refusal with exact receipt or adapter code.Contain missing model/repo, start failure, timeout, malformed/extra output, nonzero without receipt, asynchronous stdin/stdout/stderr errors, diagnostic-sink failures, and handler exceptions. Prove timeout teardown has completed before returning, including deadline-before-spawn and synchronous close during signaling. Preserve each close-time refusal code while waiting for the POSIX group to disappear.
Replay the real package through an isolated fake engine, then exercise real E3 delivery around the fake Pi attempt.
Commit after Node behavior/coverage and Python integration pass.
Task 5: Prove the complete slice¶
Files:
Modify
tests/test_integration_attempt.pyAdd only the minimal fixture contract/repository data needed
Modify
tools/only for a reusable live-gated command if necessary
Steps:
Start fake Pi in a real detached E3 worktree and make its one edit travel through the shipped TypeScript mutator and Python protocol.
Assert candidate parent, tree, declared changed path, transcript bytes, receipt, and unchanged caller HEAD/index/status.
Add a refusal sibling where E4 rejects a stale/undeclared replacement and E3 publishes no candidate.
Run a separately gated real model with an exact model string; record Pi version, base, contract, candidate ref, receipt, and transcript evidence in
docs/sdd.mdwithout committing model-generated project output.
Commit after the recorded proof is reproducible.
Task 6: Close E5 documentation and gates¶
Files:
Modify
README.mdModify
ROADMAP.mdModify
docs/architecture.mdModify
docs/contributing.mdModify
docs/glossary.mdonly if one E5 term earns its placeModify
docs/index.mdModify
docs/sdd.mdModify
docs/usage.mdModify
src/satyrn_engine/__init__.py
Steps:
Document direct-attempt danger and
/implementsafety without implying container isolation or grading.Show exact model configuration, source-checkout command, artifact seam, candidate inspection, and recovery/refusal behavior.
Mark E5 complete and E6 current only after live evidence exists.
Run and record measured commands, not copied counts:
uv run pytest -q
uv run pytest -m integration -q
uv run pytest -m '' --cov --cov-report=term -q
node --experimental-strip-types --test --experimental-test-coverage ...
uv run ruff check .
uv run pyrefly check
uv run sphinx-build -W -b html docs docs/_build/html
git diff --check
Require 100% Python statement/branch coverage and 100% lines/branches/ functions for E5-owned TypeScript.
Split the final history into design, implementation, and documentation branches so each stacked PR is understandable and independently checked.