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
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:
1 parent
890708594b
commit
1ad0b2147d
73 files changed
+259
-1786
No files matched your search
@@ -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"
|
||||
|
||||
Reference in new issue
Block a user