E3 — Delivery Implementation Plan¶
For agentic workers: implement this plan task by task. Do not import the old
harness/package or copy its code. The old repository is evidence for behavior and incidents, not source.
Goal: Deliver
satyrn-engine deliver --repo REPO [--timeout SECONDS] CONTRACT -- COMMAND [ARG ...]: run one synchronous command in a detached worktree pinned to the
repository’s exact HEAD, then emit one exact receipt without modifying the
caller’s working tree, index, branch, or HEAD. A successful changed tree also
publishes one reviewable candidate ref; every other handled attempt publishes
none.
Architecture: delivery.py owns the delivery state machine and its frozen
receipt. The existing check() seam still parses the contract and performs
the E1 repository-path check. A small internal Git boundary invokes real Git;
it is not injectable, and its real Git subprocess behavior is exercised by the
marked integration tier.
The command runs without a shell in a new POSIX process group. The worktree is
removed before the candidate ref is created atomically. cli.py owns the
literal -- split and renders the receipt as the only stdout record.
Tech stack: Python 3.14 standard library, Git, pytest, pytest-cov,
ruff, pyrefly, and the existing Sphinx documentation stack. No new runtime
dependency.
Spec:
docs/superpowers/specs/2026-08-18-e3-delivery-design.md
Correction — 2026-08-19: The accepted plan originally prescribed direct pipe forwarding, an engine-owned empty hooks directory, and setting the cleanup guard only after confirmed worktree registration. Review replaced those steps with a temporary-file spool and synchronous process-group teardown,
core.hooksPath=/dev/nullpluscore.fsmonitor=falseon every engine-owned Git call, and an uncertain guard set before the mutatinggit worktree add. These changes prevent descendant-held descriptor blocking, suppress publication and fsmonitor hooks for the full operation, and cover interruption after Git registers a worktree but before Python sees the return. Production still exposes no injected Git runner or failure hook; marked integration tests exercise real Git and process behavior, while default-tier failure tests monkeypatch private production functions only for destructive or non-portable abnormal branches. The current tasks below are normative.
Phase-size decision¶
Keep E3 as one phase, implemented in six reviewable tasks below. Although the Git and process details are substantial, every task enforces one user-visible promise: one attempt always becomes a receipt while the caller’s checkout remains untouched, and only a successful changed tree also becomes a candidate ref.
Split or defer work immediately if implementation requires any of the following: a platform-neutral process abstraction, a persistent supervisor, model or Pi knowledge, writable-path policy, validation policy, a second public command, or a generalized candidate-management API. Those are not supporting details of E3; they belong to E4–E6 or a separately proposed phase.
Global constraints¶
The supported implementation and evidence target is POSIX, a normal non-bare repository, and a synchronous trusted command. Windows packaging is E6.
Worktree isolation is not a security sandbox. Do not add a container, namespace, filesystem interposer, or claim about adversarial commands.
The command is invoked once, with its exact argv,
shell=False,DEVNULLstdin, the isolated worktree ascwd, and a new POSIX session/process group.On timeout, signal the process group with
SIGTERM, allow five seconds, then signal any remaining group withSIGKILLand reap the direct child before Git cleanup.candidate-created,discarded, andrefusedremain the only receipt outcomes. The specificcode, not a fourth outcome, carries the cause.All handled non-candidate results after CLI acceptance exit
NO_CANDIDATE = 8; inherited E1 refusals keep their existing values.The candidate ref is
refs/satyrn/candidates/<contract-id>/head; ids are rejected, never normalized. Publication is create-if-absent withgit update-ref --no-deref REF COMMIT "".The default test tier starts no process and opens no network connection. Real Git and command behavior live only in
@pytest.mark.integrationtests.A refusal test always has a sibling success. Integration tests use temporary local repositories and the production CLI/library seams, never a fake Git runner or injected failure backend.
The earlier
local-ai-pirepository is consulted at immutable revision8588ba4, especiallyharness/candidate.py,harness/processes.py, the two-repository rewrite, andHARVEST-INDEX.md. No implementation is copied.
Task 1: Freeze the public receipt and CLI boundary¶
Files:
Modify:
src/satyrn_engine/exits.pyModify:
src/satyrn_engine/cli.pyModify:
tests/test_exits.pyCreate:
src/satyrn_engine/delivery.pyCreate:
tests/test_delivery.pyCreate:
tests/fixtures/delivery/receipt-ok.jsonCreate:
tests/fixtures/delivery/receipt-repo-dirty.jsonCreate:
tests/fixtures/delivery/receipt-command-failed.jsonCreate:
tests/fixtures/delivery/receipt-candidate-exists.jsonCreate:
tests/fixtures/delivery/receipt-cleanup-failed.json
Produces:
ExitCode.NO_CANDIDATE = 8, without renumbering any existing member.A frozen
DeliveryReceiptwith the exact fields and nullability from the spec, plus one deterministic compact-JSON renderer.Closed
DeliveryCodeandDeliveryOutcomeenums, a typed JSON payload, and complete code-to-outcome and code-to-exit mappings.outcomeis derived, rather than accepted independently by the receipt constructor.Pure timeout validation accepting finite positive values only.
A CLI parser that requires a literal
--, preserves every command token after it, and reports missing separator/command as argparseUSAGE = 2without producing a receipt.
Steps:
Add failing default-tier tests for the complete exit table, all five exact receipt fixtures, valid/default timeout, invalid timeout values, a missing separator, an empty command, and exact argv preservation.
Add
NO_CANDIDATEand implement only the frozen receipt plus pure parsing helpers. Do not start Git or a command from these tests.Make
main()dispatch shape explicit while keeping delivery execution unwired until Task 2. Existingcheckandprotocolbehavior must remain byte-for-byte compatible.Run the default suite, ruff, and pyrefly. Record one success fixture and one refusal fixture by name.
Evidence: parsing deliver --repo . contract.yaml -- tool --flag returns
the command tuple ("tool", "--flag"); parsing without -- fails with exit
2. The committed OK and COMMAND_FAILED receipt bytes match the renderer.
Commit: feat(delivery): freeze the E3 receipt and CLI boundary
Task 2: Refuse before creating a worktree¶
Files:
Modify:
src/satyrn_engine/check.pyModify:
src/satyrn_engine/delivery.pyModify:
src/satyrn_engine/cli.pyModify:
tests/test_check.pyCreate:
tests/test_integration_delivery.py
Produces:
deliver(repo, contract_path, command, timeout) -> DeliveryReceiptas the library seam.Contract-first E1 refusals with
contract_idpopulated whenever parsing succeeded, includingREPO_UNAVAILABLE.Git-root resolution, exact
HEAD^{commit}capture, full dirty-state refusal, candidate-id validation, and early existing-ref refusal.A sanitized engine/command environment that removes repository-local routing variables reported by
git rev-parse --local-env-vars, plusGIT_NAMESPACE, while preserving ordinary configuration andPATH.
Steps:
Add a sibling test showing
check()retains the parsed contract on a repository-path refusal; this is required by the receipt’s nullability rule.Add integration helpers that initialize a temporary local Git repository, create one base commit, invoke the real CLI, and parse exactly one stdout receipt.
Add failing integration pairs for a clean repository versus dirty tracked, deleted, and untracked state; repository root versus a subdirectory; ordinary root versus linked worktree root; valid id versus
/and Git-invalid ids; absent versus existing ordinary, symbolic, and dangling-symbolic refs.Implement preflight using byte-oriented Git output and
--no-optional-locks status --porcelain=v1 -z --untracked-files=all --ignore-submodules=none. The input repository shown in the receipt is an absolute normalized path without resolving its symlink spelling; Git operations use the canonical root.Wire the CLI to print exactly one receipt and return the receipt’s exit code.
Run the default suite plus the focused integration tests. Confirm every refusal leaves the source status and
HEADunchanged and creates no engine-owned worktree.
Evidence: one clean-root invocation reaches the later delivery boundary;
REPO_DIRTY and INVALID_CANDIDATE_ID refuse before worktree registration and
match their receipt fields exactly.
Commit: feat(delivery): refuse unsafe repositories and candidate ids
Task 3: Run one isolated attempt and create one candidate¶
Files:
Modify:
src/satyrn_engine/delivery.pyModify:
tests/test_integration_delivery.py
Produces:
A unique temporary parent verified outside every registered worktree, with a detached linked worktree at the captured base commit.
One exact command invocation whose stdout/stderr are spooled outside engine memory and forwarded in chunks to stderr while stdout remains receipt-only.
POSIX process-group timeout teardown.
Explicit typed states for uncertain Git registration and process cleanup, with a minimal process protocol and an immutable teardown result.
Staging, explicit tree/commit creation, deterministic changed-path extraction, worktree removal, and create-if-absent candidate publication.
Steps:
Add a successful fixture command that modifies, adds, and deletes files. Assert the candidate commit has exactly
BASEas parent, contains the three changes, is reachable by the expected ref, and leaves the caller’s tree, index, branch, andHEADunchanged.Add sibling failure fixtures for no change, nonzero exit, missing executable, timeout, and a command that moves the isolated
HEAD. Assert exact codes, no candidate ref, and no registered worktree.Add the timeout descendant proof: the command starts a same-process-group child that would write a delayed sentinel, then exceeds the timeout. Assert
COMMAND_TIMEOUT, absence of the sentinel after its delay, and cleanup.Implement registration-aware cleanup. Set the cleanup guard to uncertain immediately before the mutating worktree-add call, refine it after Git’s registration query, and clear it only after Git confirms absence. This covers interruption after Git mutates shared metadata but before Python sees the return. Do not use a temporary-directory context manager that deletes a retained or uncertain registered worktree after cleanup failure.
Create the commit with
write-treeandcommit-tree, explicit parent, fixed engine identity, disabled signing, and a message containing candidate id and base SHA. Derive final paths fromdiff-tree -z, not pre-commit filesystem observation.Remove the worktree before publishing the ref. If command or Git handling fails, route through the same cleanup and apply
CLEANUP_FAILEDprecedence.Run the focused integration tests repeatedly to demonstrate that no delayed process writes after timeout.
Evidence: the success receipt names a readable candidate commit/ref and three canonical paths; the timeout receipt names no candidate and its delayed sentinel never appears.
Commit: feat(delivery): create candidates in isolated worktrees
Task 4: Prove atomic publication and cleanup precedence¶
Files:
Modify:
src/satyrn_engine/delivery.pyModify:
tests/test_integration_delivery.py
Produces:
Race-safe create-if-absent publication with diagnostic re-read after a failed compare-and-swap.
Correct differentiation of
CANDIDATE_EXISTSfromGIT_FAILEDwithout claiming retry safety.Visible retained-path reporting when a locked worktree cannot be removed.
Unexpected-exception cleanup through the same registered-worktree guard.
Steps:
Add two real CLI processes synchronized inside their attempt commands so both target the same absent candidate id. Assert exactly one
OK, exactly oneCANDIDATE_EXISTS, one published ref, and an unchanged caller checkout.Add an ancestor-ref collision whose sibling candidate id succeeds. Assert the collision is
GIT_FAILED, not mislabeled as candidate existence.Add a command that locks its own worktree before returning. Assert
CLEANUP_FAILEDsupersedes the pending result, reports the retained path, and publishes no candidate. The test must explicitly unlock/remove/prune in its own teardown.Exercise a real failure after worktree registration and confirm the common cleanup path. Do not add an injected Git runner solely to manufacture the failure.
Implement
update-ref --no-derefwith an empty expected-old object id. After failure, inspect symbolic and ordinary state; an existing ref isCANDIDATE_EXISTS, every absent or unreadable result isGIT_FAILED.Run focused integration tests several times so the race proof is not an ordering accident.
Evidence: the two-process fixture always produces one winner and one loser; the locked-worktree fixture retains a real path and proves result precedence.
Commit: fix(delivery): make publication and cleanup outcomes atomic
Task 5: Close the supported Git and environment edges¶
Files:
Modify:
src/satyrn_engine/delivery.pyModify:
tests/test_integration_delivery.py
Produces:
Canonical UTF-8 byte ordering for receipt paths and refusal of non-UTF-8 candidate paths before publication.
Engine Git operations that cannot be redirected by inherited local Git routing variables or execute repository, reference-transaction, or external fsmonitor hooks.
Engine Git operations that ignore inherited author and committer dates while the trusted command continues to receive them.
Preserved ordinary Git configuration and filters.
Steps:
Add a UTF-8 success fixture whose path ordering differs between locale/text sorting and raw UTF-8 byte sorting. Assert the specified byte order.
On POSIX, create a non-UTF-8 path through a trusted fixture command. Assert
GIT_FAILED, no publication, and cleanup; pair it with the UTF-8 success.Configure a hook sentinel and a harmless repository filter. Assert the hook never fires during engine Git operations while the filter still participates successfully in checkout/staging.
Invoke delivery with inherited
GIT_DIR,GIT_WORK_TREE, andGIT_NAMESPACEpointing elsewhere. Assert Git still discovers the isolated worktree and publishes in the intended repository.Confirm all Git subprocesses use the canonical cwd split from the spec and byte-oriented capture where paths are involved.
Evidence: the UTF-8 candidate succeeds in byte order; the non-UTF-8 sibling refuses without a ref; the hook sentinel remains absent while the filter-backed candidate succeeds.
Commit: fix(delivery): harden Git environment and path handling
Task 6: Document the behavior and record the phase¶
Files:
Modify:
README.mdModify:
ROADMAP.mdModify:
CLAUDE.mdModify:
docs/architecture.mdModify:
docs/usage.mdModify:
docs/glossary.mdModify:
docs/contributing.mdModify:
docs/index.mdModify:
docs/superpowers/index.mdModify:
pyproject.tomlModify:
src/satyrn_engine/__init__.py
Produces:
Contributor-facing documentation of why the design uses a separate worktree, detached
HEAD, a pinned base commit, one synchronous attempt, and a dedicated non-branch ref.An explicit trust boundary: worktree isolation is not a sandbox; unrestricted absolute paths and shared Git mutation remain trusted until E4/E5 constrain the model tool surface.
Honest prior-art framing: worktree isolation exists elsewhere; E3 applies it to single-attempt safety and a dedicated candidate-ref namespace.
Correct provenance instructions pointing at
local-ai-pi@8588ba4; a visible correction inCLAUDE.mdfor its stale commit/guard-copy wording.candidate,receipt, andworktree isolationdefinitions added to the concept budget and glossary only now that E3 implements them.Integration-marker help that describes local Git/process requirements rather than claiming every integration test needs network access.
Steps:
Add CLI usage examples for candidate creation, inspection with
git show, and explicit deletion withgit update-ref -d. Do not document merge or automatic apply behavior that E3 does not have.Add an architecture section explaining the snapshot-isolation analogy and each design choice’s reason. Cite the old evidence and verified external prior art; do not copy unverified claims from a PR comment.
Update the roadmap and SDD record with the commands and named success/failure fixtures actually run. Preserve corrections rather than rewriting them away.
Correct stale repository instructions before they can direct a later worker to the wrong provenance or claim the guards were already copied.
Run the full default suite, the E3 integration file, ruff, pyrefly, strict Sphinx build, combined statement/branch coverage, and
git diff --check.Review the final diff against the phase-size decision. Move anything about Pi, validation, mutation policy, model quality, candidate management, or Windows to its named later phase.
Evidence: one documented success command creates a candidate ref and one documented failure command emits a stable receipt; strict docs and all code checks pass from a clean checkout.
Commit: docs: explain and record E3 delivery
Completion commands¶
uv run pytest
uv run pytest -m integration tests/test_integration_delivery.py -v
uv run pytest -m "" --cov
uv run ruff check .
uv run pyrefly check
uv run --group docs sphinx-build -W -b html docs docs/_build/html
git diff --check
The final report names the exact candidate-created fixture, at least one discarded fixture, at least one refused fixture, the two-process race result, and the process-group descendant sentinel result. It does not claim model improvement, sandboxing, validation, Windows support, or packaging.