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,10 +1,10 @@
|
||||
"""Local executor loop (spec A12 escalation triggers, A15 accommodations).
|
||||
"""Local executor loop (architecture L9 escalation triggers, L9 accommodations).
|
||||
|
||||
Runs the approved plan with the executor role — the cheap local model doing
|
||||
mechanical work. Every tool call and result is checkpointed to the canonical
|
||||
log as it happens (A13), so a crash mid-milestone leaves a resumable record.
|
||||
log as it happens (L9), so a crash mid-milestone leaves a resumable record.
|
||||
|
||||
Escalation is decided from outside the model (A12): consecutive command
|
||||
Escalation is decided from outside the model (L9): consecutive command
|
||||
failures and hard caps on turns, never the model's own claim that it is stuck
|
||||
or finished-enough. Until delegation exists (step 7), an escalation gates to
|
||||
the human with the failures attached rather than engaging a frontier model.
|
||||
@@ -115,7 +115,7 @@ class LLMExecutor:
|
||||
def _escalate(
|
||||
self, milestone: int, failures: int, last_failure: str
|
||||
) -> ExecutorOutcome | None:
|
||||
"""Hand the sub-task to a stronger model, per A12.
|
||||
"""Hand the sub-task to a stronger model, per L9.
|
||||
|
||||
Returns None when the delegation handed back usable work and the local
|
||||
loop should carry on; an ExecutorOutcome when the caller must stop.
|
||||
@@ -124,7 +124,7 @@ class LLMExecutor:
|
||||
if self._delegate is None:
|
||||
return ExecutorOutcome("escalate", note)
|
||||
|
||||
# A12 says the failures go in the brief: the delegate must not have to
|
||||
# L9 says the failures go in the brief: the delegate must not have to
|
||||
# rediscover what the local model already learned the hard way.
|
||||
brief = (
|
||||
f"Milestone {milestone} is stuck. The local executor tried and its "
|
||||
@@ -140,14 +140,14 @@ class LLMExecutor:
|
||||
return None
|
||||
if result.status == "stopped":
|
||||
return ExecutorOutcome("stopped", f"during a delegation: {result.note}")
|
||||
# A13: a cap breach or failure leaves a half-edited tree, so the local
|
||||
# L9: a cap breach or failure leaves a half-edited tree, so the local
|
||||
# loop must not resume on top of it.
|
||||
return ExecutorOutcome(
|
||||
"escalate", f"{note}; delegation {result.status}: {result.note}"
|
||||
)
|
||||
|
||||
def _tools(self) -> list[dict[str, Any]]:
|
||||
# A12: ask_expert exists in addition to the observable escalation
|
||||
# L9: ask_expert exists in addition to the observable escalation
|
||||
# triggers, never as a replacement for them.
|
||||
if self._expert is None:
|
||||
return [*EXECUTOR_TOOLS, FINISH_TOOL]
|
||||
@@ -203,7 +203,7 @@ class LLMExecutor:
|
||||
prose_nudged = False
|
||||
|
||||
for turn in range(self._max_turns):
|
||||
# A4: `stop` takes effect at the next tool boundary. The start of
|
||||
# L2: `stop` takes effect at the next tool boundary. The start of
|
||||
# a turn is one, and so is the gap between two tool calls below.
|
||||
if self._cancel.requested:
|
||||
return ExecutorOutcome("stopped", f"before turn {turn + 1}")
|
||||
@@ -267,7 +267,7 @@ class LLMExecutor:
|
||||
elif call.tool not in offered:
|
||||
# A model inventing tools is not doing the work, and with
|
||||
# no server enforcement nothing else would stop it, so it
|
||||
# counts toward escalation like a failed command (A12).
|
||||
# counts toward escalation like a failed command (L9).
|
||||
outcome = ToolOutcome(
|
||||
f"no such tool: {call.tool}; the tools are "
|
||||
+ ", ".join(sorted(offered)),
|
||||
@@ -294,7 +294,7 @@ class LLMExecutor:
|
||||
0 if outcome.success else consecutive_failures + 1
|
||||
)
|
||||
if consecutive_failures >= self._failure_limit:
|
||||
# A12: this is the trigger delegation exists for.
|
||||
# L9: this is the trigger delegation exists for.
|
||||
# Escalating to the human is the fallback when there
|
||||
# is no delegate, not the intended path.
|
||||
escalation = self._escalate(
|
||||
|
||||
Reference in new issue
Block a user