Retire the old build spec, and point every reference at the architecture (M0.1)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m22s

Development Plan M0.1. Every section reference in code, tests, specs/,
the example config, the tuning guide, and the CLAUDE.md and README files
now cites docs/Architecture.md (P, L, D, O, X, Q) instead of the old
spec's A-H, and docs/Architecture-Build-Spec.md is deleted (git keeps
it). The plan's old-to-new table goes with it.

Mostly mechanical, with a per-site choice where one old section split:
A4 became L2 where it meant stop/abandon/the loop and L3 where it meant
approvals; A10 became L6 in the context-budget code and L7 elsewhere; B2
became D4 for outbound Gitea and publishing and D5 for review intake;
'A3 step N' became the matching step of L8's boot sequence. Comments
describing behaviour the architecture drops (planning always escalating,
thresholds tuned at an old build step) now describe the current code and
point at the Development Plan.

docs/Architecture.md gains one line the rewrite missed: intake outages
delay but never fail work (old B2), now in L5, where the poller and
Matrix code cite it.

Comments and docs only; no behaviour change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
This commit is contained in:
dongarandClaude Opus 5.5 committed 2026-10-01 00:17:48 +00:00
1 parent 890708594b
commit 1ad0b2147d
73 files changed
+259 -1786

No files matched your search

+19 -19
View File
@@ -1,4 +1,4 @@
"""Delegation harness (spec A13).
"""Delegation harness (architecture L9).
Escalation is delegation, not consultation: a frontier model receives a scoped
sub-task, works autonomously with full tool access, and hands back. The local
@@ -17,7 +17,7 @@ resuming on top of a half-edited tree.
**Checkpointing as it happens.** Every tool call, result, and assistant turn
is written to the canonical log when it occurs, never batched at handback.
That is what makes a delegation resumable after a crash (A3).
That is what makes a delegation resumable after a crash (L8).
Scope is bounded by construction: a delegation is handed a sub-task the
approved plan already contains. It is smart, not authorised to widen the work.
@@ -144,7 +144,7 @@ class DelegationResult:
The work on the branch may be perfectly good, so it is preserved and
the intent left pending: the next boot treats it exactly like a crash
mid-delegation (A3) and resumes if the cut was clean. Discarding here
mid-delegation (L8) and resumes if the cut was clean. Discarding here
would throw away work because the budget ran out.
"""
return self.status == "interrupted"
@@ -152,7 +152,7 @@ class DelegationResult:
@property
def usable(self) -> bool:
"""Only a successful handback may be built upon. A cap breach, a
failure or an operator's stop leaves a half-edited tree, which A13
failure or an operator's stop leaves a half-edited tree, which L9
says must gate rather than be resumed."""
return self.status == "handback" and self.handback is not None
@@ -190,7 +190,7 @@ class Delegation:
started_at = self._now()
base_branch = self._git.current_branch()
# Recorded before the branch exists, so a crash anywhere after this
# leaves a durable statement of what was in flight (A2/A3).
# leaves a durable statement of what was in flight (L8).
intent_id = self._store.begin_intent(
DELEGATION_INTENT,
{
@@ -234,12 +234,12 @@ class Delegation:
)
if result.interrupted:
# Leave the branch and the intent alone: this delegation is not
# finished, it was cut off, and A3's resume rule is exactly the
# finished, it was cut off, and L8's resume rule is exactly the
# machinery for picking it back up.
return result
if not result.usable:
# Abort discards the branch: the caller must never resume on top
# of a partially-edited tree (A13).
# of a partially-edited tree (L9).
self._abandon(branch, base_branch)
# Settled either way: an aborted delegation is a *finished* one, and
# leaving the intent pending would make the next boot think work was
@@ -248,12 +248,12 @@ class Delegation:
return result
def resume(self, intent: Intent) -> DelegationResult:
"""Continue a delegation the process died in the middle of (A3 step 5).
"""Continue a delegation the process died in the middle of (L8 boot step 5).
The transcript is already in the canonical log, so the loop re-renders
it and carries on. No new branch and no second ``delegation_started``:
this is the same delegation, which is why its vendor-opaque reasoning
may still be replayed (A10 defines a session as the delegation, not the
may still be replayed (L7 defines a session as the delegation, not the
process lifetime).
"""
payload = intent.payload
@@ -284,7 +284,7 @@ class Delegation:
return result
def recover(self, notify: Callable[[str], None]) -> DelegationResult | None:
"""Sweep pending delegation intents at boot (A3 step 5).
"""Sweep pending delegation intents at boot (L8 boot step 5).
Settles what is already finished, discards what never started, resumes
a clean cut — announcing it with a *notice*, not a question, because a
@@ -363,7 +363,7 @@ class Delegation:
tokens=spent,
)
# Metered from what the provider reported, not estimated. The cap
# is a scope limit (A13), distinct from the money cap A5 keeps on
# is a scope limit (L9), distinct from the money cap L9 keeps on
# the LiteLLM key — but an unbounded delegation can still exhaust
# a daily budget and take external models away until it resets.
usage = getattr(self._client, "last_usage", {}) or {}
@@ -380,7 +380,7 @@ class Delegation:
calls: list[ToolCall] = []
for event in produced:
# Checkpoint as it arrives, never batched at handback (A13):
# Checkpoint as it arrives, never batched at handback (L9):
# this is what makes the delegation resumable after a crash.
self._store.append_event(event)
if isinstance(event, ToolCall):
@@ -475,7 +475,7 @@ class Delegation:
# Commit each turn's work onto the delegation branch, so that
# discarding the branch really does discard the work, and so a
# crash leaves the branch at a known commit (A3).
# crash leaves the branch at a known commit (L8).
self._commit_turn(turn)
return DelegationResult(
@@ -504,7 +504,7 @@ class Delegation:
def _parse_handback(arguments: dict[str, Any]) -> Handback | None:
"""Validated locally rather than trusted: A14's rule for prompt-level
"""Validated locally rather than trusted: L9's rule for prompt-level
structured output applies to the handback too."""
required = ("changed", "now_true", "next")
if any(not str(arguments.get(key, "")).strip() for key in required):
@@ -521,10 +521,10 @@ def _parse_handback(arguments: dict[str, Any]) -> Handback | None:
def delegation_reconciler(store: StateStore, git_repo: GitRepo) -> Reconciler:
"""A3 step 5: decide whether an interrupted delegation may be resumed.
"""L8 boot step 5: decide whether an interrupted delegation may be resumed.
The judgement is an observable check, never model self-assessment — the
same reasoning as A12. Returns:
same reasoning as L9. Returns:
- ``True`` the delegation is settled or cleanly resumable
- ``False`` it never really started, so there is nothing to resume
@@ -534,7 +534,7 @@ def delegation_reconciler(store: StateStore, git_repo: GitRepo) -> Reconciler:
existing and carrying the delegation's commits: per-turn shas are not
recorded in the log today, so a branch rewritten out from under us would
not be detected. Stated rather than glossed, because the check is weaker
than A3's wording implies.
than L8's wording implies.
"""
def check(intent: Intent) -> bool | None:
@@ -549,7 +549,7 @@ def delegation_reconciler(store: StateStore, git_repo: GitRepo) -> Reconciler:
def delegation_verdict(
store: StateStore, git_repo: GitRepo, intent: Intent
) -> Literal["resume", "settled", "absent", "gate"]:
"""The A3 step 5 decision, separated from the Reconciler contract because
"""The L8 boot step 5 decision, separated from the Reconciler contract because
boot must *act* on it — resuming is not the same as judging settled."""
branch = intent.payload.get("branch")
if not branch:
@@ -564,7 +564,7 @@ def delegation_verdict(
last = events[-1].event
if isinstance(last, ToolCall):
# A call with no recorded result: its effect is unknown and cannot be
# verified by inspection, so gate (A3).
# verified by inspection, so gate (L8).
return "gate"
if not isinstance(last, AssistantText | ToolResult):
return "gate"