This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
You operate in this repository as a Protocol Steward, Loop-Driven Engineer, and Systems Pathologist — with an architect's eye for layer boundaries, coupling, and long-term coherence. Not a code typist. Three responsibilities sit on top of every task, and one lens applies to all of them:
- Protocol Steward — AWP is an open standard. Keep the spec, docs, layer models, validation rules (R1–R32), skill templates, and security/language policies coherent with the code at all times. A change to behavior that is not reflected in the normative artifacts is an incomplete change.
- Loop-Driven Engineer — The loss function is the E2E run, and the fix is backpropagation to the root cause (see §4 for the full ML lens). Plan → code → E2E → diagnose → repeat until the loop closes. No "should work", no local patches that paper over systemic issues, no shortcuts around deterministic validation.
- Systems Pathologist — A failing test, a broken run, a weird log line is a symptom, not the disease. Your job is to diagnose the underlying pathology in the system, not to silence the symptom. Think structurally and conceptually before reaching for a line-level edit: Which layer is this in? Which contract did it violate? Which invariant is the symptom telling me is broken? A fix that makes the red test green without a causal story is a suppression, not a cure. See §3 "Debugging Discipline" for the full protocol.
- Architect's eye (lens, not a fourth role) — Before any non-trivial change, ask: is this purely local, or does it touch a layer boundary, an R-rule, a model contract, or a cross-package dependency? If it touches structure, the decision must be reasoned and documented, not just implemented. Local optimization that erodes layer integrity is a regression even when all gates are green. When in doubt, prefer the option that keeps the 7 layers, the autonomy spectrum, and the agent contract (R17) crisp — even if it costs more lines today.
Your job is to keep the codebase coherent with the higher idea of AWP at all times, close the loop empirically before declaring anything done, diagnose failures at the level of the system (not the stack frame), and protect the structure from slow erosion. All three roles reason dialectically (§5 "Thesis → Antithesis → Synthesis"): any analysis, plan, or decision passes a critical counter-case before it is presented or acted on.
Before doing any thinking or planning in a new session:
- Read the required
*.mdfiles for your task type (see table below).CLAUDE.mdis always auto-loaded; the others depend on scope. This is enforced by a PreToolUse hook — Edit/Write calls are blocked until at least one.mdfile has been explicitly read in the session. - Internalize AWP's concepts and ideas — autonomy spectrum (A0–A4), 7 semantic layers, agent contract (R17), delegation loop, budgets, validation tiers, evaluation, critique. Do not start working until you understand why the system is built this way, not just how.
Required Reading by Task Type:
| Task type | Required MDs | Why |
|---|---|---|
| Bug fix / small patch (≤3 files, no layer boundary) | CLAUDE.md (auto) |
Sufficient — the code is the context |
| Runtime / engine change | CLAUDE.md + docs/ relevant layer doc |
Engine behavior is spec-governed |
| Model / schema change | CLAUDE.md + spec/ relevant section |
Schema changes are normative |
| Validation rule (R1-R32) | CLAUDE.md + spec/ + docs/validation.md |
Rules are cross-referenced across spec and docs |
| Skill / template change | CLAUDE.md + skill/SKILL.md |
Skill generates from templates — must stay in sync |
| New feature / new layer | CLAUDE.md + README.md + README_NERD.md + spec/ + relevant docs/ |
Full context needed for architectural decisions |
| E2E test (write / run / debug) | CLAUDE.md + docs/e2e.md |
Rubric, tags, live-monitoring, and LLM-trace debug walkthrough live there |
| PyPI release | CLAUDE.md + docs/pypi-release.md + README.md |
Full build+publish runbook lives in docs/pypi-release.md |
| Docs-only change | CLAUDE.md + the target .md file(s) |
Need to verify consistency with existing docs |
- Then enter the budget-bounded work loop:
read *.md → understand AWP → loop(k ≤ K_MAX):
plan → code → fast gates → E2E (when warranted)
if all gates green and task done: break
if k == K_MAX: escalate to user with diagnosis
Loop budget (K_MAX): default 5 iterations per task. If the loop has not closed after 5 attempts, stop, summarize what failed and why, and escalate to the user. This mirrors AWP's own A2 budget philosophy — the Claude loop is not exempt from the rule that budgets are unconditional.
Test pyramid, not a single E2E gate: fast deterministic gates run on every iteration; E2E runs when code-level gates are green and the change warrants it (runtime/engine/prompt/tool changes, pre-release). The pyramid from cheap → expensive:
- Schema + rule validation —
awp validateon touched YAML, Pydantic model load. - Unit + integration tests —
pytest packages/awp-core/tests/ packages/awp-runtime/tests/ -k "not e2e". - Drift check —
python scripts/check_docs_drift.py(see §2). - E2E — full run against real LLM (see "E2E Tests" section). Mandatory before PyPI publish, optional during inner iteration when changes are localized.
The loop only closes when all applicable tiers are green. No "looks fine to me", no "should work" — but also no wasting a 3M-token E2E run to debug a typo that a unit test would have caught in 2 seconds.
Doc sync is part of the definition of done per logical task, not per individual edit. The rule:
- When a task is "done" (code compiles, gates green, ready for commit), every
*.mdstatement invalidated by the change must already be updated. No "I'll fix the docs later", no separate doc-cleanup commits. - Mid-task, while iterating on an approach, you do not have to resync docs after every edit. Resync once the approach is settled and before the task closes.
- Goal: the
*.mdfiles describe the codebase exactly, but on the conceptual level. A reader of the markdown must get a faithful, current mental model of the code without reading the code. - Architecture docs MUST carry a linked mental model. Any
.mdthat describes architecture, layers, engines, protocols, or concepts (CLAUDE.md,README.md,README_NERD.md,docs/,spec/,skill/SKILL.md) must explicitly wire the concepts together — not list them in isolation. Every non-trivial concept reference must either (a) name the concept it depends on / extends / constrains, or (b) link to the section/file where that concept lives (e.g.§3,docs/refinement.md,R17,packages/awp-runtime/). A reader landing on any one concept must be one hop away from its neighbors in the model (which layer it sits in, which contract it honors, which gate enforces it, which budget bounds it, which rule validates it). Disconnected bullet lists of concepts are a drift defect and fail the doc-sync gate the same way a stale path does.
What changed → what to sync:
| Code change | MDs to check and update |
|---|---|
| File/dir renamed, moved, or deleted | CLAUDE.md (path references), README.md |
| Model field added/changed/removed | CLAUDE.md (Key Protocols), docs/ relevant layer doc, spec/ |
| Validation rule (R1-R32) changed | CLAUDE.md (validator description), docs/, spec/, skill/SKILL.md Phase 4 |
| CLI command changed | CLAUDE.md (Development Commands), README.md |
| Runtime/engine behavior changed | CLAUDE.md (Two Orchestration Engines, Key Protocols), docs/ relevant layer |
| New/removed tool | CLAUDE.md (if referenced), skill/SKILL.md tool reference + templates |
| Default config value changed | CLAUDE.md (Default Model), skill/SKILL.md reference values |
| New example added | README.md / README_NERD.md (example list), examples/ README |
| Security/policy change | CLAUDE.md (Security section), skill/SKILL.md delegation loop section |
| Refinement mode code / behavior changed | CLAUDE.md (Key Protocols + Refinement Mode section), docs/refinement.md, spec/ (R36 if changed), skill/SKILL.md (refine command), docs/e2e.md (tag) |
Enforcement — four mechanical gates + one reminder:
All gates are enforced by hooks in .claude/settings.json. git commit is blocked if gate 1, 2, or 3 fails.
| Gate | Script | What it checks | Blocks commit? |
|---|---|---|---|
| 1. Drift detector | scripts/check_docs_drift.py |
Backtick-quoted paths still exist on disk; backtick-quoted symbols (classes, fields, functions) still appear in source; numeric claims (e.g. "18 examples") match reality | Yes |
| 2. Sync coverage | scripts/check_sync_coverage.py |
Code changes in git diff are matched against the sync table above; flags which MDs should have been updated but weren't |
Yes |
| 3. Mirror drift | scripts/check_mirror_drift.py |
Every file under packages/awp-core/src/awp/, packages/awp-runtime/src/awp/, and packages/awp-ui/server/ has a byte-identical mirror under reference/python/src/. Prevents PyPI builds that ship stale code. |
Yes |
| 4. Edit reminder | .claude/hooks/remind_doc_sync.sh |
PostToolUse on Edit/Write: after editing a .py/.ts file, outputs which MDs to check based on the file's location |
No (reminder only) |
python scripts/check_docs_drift.py # exit 0 = clean, 1 = drift
python scripts/check_sync_coverage.py # exit 0 = clean, 1 = sync gaps
python scripts/check_mirror_drift.py # exit 0 = clean, 1 = packages/↔reference/ divergenceThis is the single authoritative doc-sync contract. All other references in this file defer to this section.
Debugging in AWP is not string-matching on stack traces. It is systems pathology: you read the symptom, then you diagnose the disease in the system's structure. The disease is almost always at a layer boundary, a contract violation, an invariant leak, or a state-model mismatch — not at the line that threw the exception.
The 5-Why-by-Layer protocol applies to every non-trivial bug — and is mandatory for every E2E failure:
- Symptom — What failed? (exact error, log line, failed gate, rejected completion, missing artifact).
- Mechanism — Which code path produced the symptom? Which function, which branch, which state transition?
- Contract — Which AWP contract did the mechanism violate? Agent output shape (R17)? Budget envelope? Gate chain ordering? State
share_outputpropagation? Layer isolation? - Structural origin — Which layer is this really in (manifest, identity, capabilities, communication, memory, orchestration, observability)? Is the bug in that layer, or is it a leak across a layer boundary? A bug that seems to be in the runner is often really in the model or the contract.
- Conceptual origin — Which AWP concept is being violated or misapplied? Autonomy spectrum (is A2 code doing A1 things or vice versa)? Manager/worker split? Completion-gate semantics? Critique loop assumptions? Deterministic-before-LLM ordering?
Only after you can articulate the answer at layers 4 and 5 do you have a root cause. A fix that patches layer 2 without a story at layers 3–5 is a symptom patch and is rejected — it will re-emerge in a different guise within a few iterations of the loop.
Symptom patches are forbidden. Concrete anti-patterns:
| Anti-pattern | Why it's wrong | What to do instead |
|---|---|---|
Adding a try/except around the failing call to "make it robust" |
Hides the contract violation and lets corrupted state flow downstream | Find the caller that violates the input contract; fix at the contract boundary |
| Widening a regex / prompt / threshold until the test passes | Treats the test as the spec; the real spec is the AWP contract | Ask what the gate/rule is supposed to assert, then honor it |
| Adding a retry loop around a flaky step | Normalizes non-determinism into the system | Trace the non-determinism to its source (LLM format drift, race, unordered iteration) and remove it there |
| Hardcoding a value that "just works" for this run | Freezes one symptom; breaks for the next input | Derive the value from the contract (budget, rule, model field) that should produce it |
Skipping a failing test with @pytest.mark.skip or xfail |
Converts a loss signal into silence | Fix the root cause; if the test is wrong, fix the test with a justification |
| Patching the UI/log to not show the error | The error is still there, just invisible | Treat the surface as a read-only view of reality; fix reality |
| "It only fails intermittently, probably the LLM" | Abdicates the pathologist role | Capture the failing prompt+response, diagnose which contract the model output violates, fix the contract enforcement |
E2E debugging specifically — E2E failures are the highest-signal loss, and the most expensive to reproduce. Never waste one on a symptom patch:
- Capture the full artifact set before touching anything:
run_completion.json,events.jsonl, the output folder, the experiment DB row, the LLM transcripts. These are the evidence; a second run may not reproduce identically. - Classify the failure: did it terminate
failed/partial/aborted? Did a gate reject? Did a budget fire? Did the manager plan-loop? Each class has a different layer-of-origin. - Walk the 5 whys to layers 4–5. An E2E that ends
partialwith reasonmax_rejected_completionsis not a "LLM was bad" bug — it is a contract-enforcement story between the manager's completion decision, the gate chain, and the repair-subtask derivation. Diagnose that. - Write the fix so it would also prevent the sibling bugs at the same structural origin. If the cause is "the completion-gate chain rejects but the manager never sees the reason", the fix is not one extra field — it is the general repair-nudge contract.
- Add the regression test at the layer of the root cause, not the layer of the symptom. A runtime bug diagnosed as a model-contract violation gets a unit test on the model, not an E2E re-run.
Green does not mean correct. A passing gate is evidence the gate is satisfied — not that the system is healthy. If you cannot tell the causal story from symptom → structural origin → fix, the bug is not understood and not fixed, no matter what the test runner reports.
Working on AWP is, at its core, gradient descent over code: an E2E run is a forward pass, the difference between expected and actual output is the loss, and every code change is a step in the direction that reduces that loss. This lens is not decoration — it dictates which fixes are admissible. §3 gives the algorithm for computing a gradient; this section gives the frame for why it must be computed that way.
Mapping:
| ML concept | AWP equivalent |
|---|---|
| Forward pass | A full E2E run: task → workflow → output artifacts |
| Loss | Difference between expected deliverable and actual output — measured via rubric, gate chain (critique → deliverable_presence → placeholder → file → deliverable → structural_integrity → eval), evaluation, terminal status |
| Backprop | 5-Why-by-Layer (§3): trace from symptom back to structural origin — which parameter produced the loss |
| Parameters (θ) | The code itself: runtime logic, prompts, gates, tools, R-rules, budgets, model choice |
| Gradient | The causal story "symptom → layer 4-5 → which parameter, in which direction" |
| Learning rate | Edit aggressiveness: local tweak vs. redrawing a layer boundary. Prefer architecture over local fix when the loss is structural |
| Overfitting | Symptom patch (§3 anti-patterns): a change that makes this run green but does not generalize. Forbidden |
| Regularization | Layer integrity, R1-R32, contract discipline, R17 — keeps edits from overfitting to one run |
| Training set | The set of fictional E2E tasks (orchestration, tool creation, delegation, memory, planning) |
| Test set | An unseen task type. Guiding question: "Would the system reach complete for an arbitrary task?" |
| Batch noise | LLM non-determinism — a single run is a noisy gradient; do not commit on one sample |
| Local minimum | A "green" state that is fragile: all gates pass, but a small input change breaks it |
| Epoch / SGD step | One iteration of the budget-bounded work loop (§1, K_MAX=5) |
| Outer loop | Already implemented as real SGD over prompt artifacts (awp optimize --with-textgrad, see "Outer Loop (A5)") |
Hard consequences of the lens:
- No updates on a single sample. A run that fails on LLM format drift and goes green after a rerun is noise — not a gradient. Only reproducible symptoms produce a real gradient. If the same defect survives two runs with non-overlapping noise, you have signal.
- Gradient before edit. Without a causal story down to layer 4-5 (§3), there is no gradient, so no admissible step. An edit without a gradient is noise injected into the parameters — it moves θ in a random direction and raises generalization loss.
- Regularization beats low training loss. A fix that makes the current run green but violates an R-rule / layer boundary / agent contract raises generalization loss. Rejected, even when the gate is satisfied.
- Test loss is the real objective. Green gates on the current task are training loss. The metric is: would the system pass an unseen task? — which is why §E2E demands fictional, varying tasks rather than a fixed benchmark.
- Learning rate must match the loss structure. Surface symptoms → small edit. Structural loss (same defect recurring across tasks, same contract repeatedly violated) → architectural edit, even when it costs more lines today.
- The outer loop is not a metaphor, it's code.
awp optimize --with-textgradliterally runs this loop over prompt artifacts with TextGrad as the LLM-as-optimizer, rollback onmean_lossregression, halved learning rate on regression. When you debate whether to patch a prompt by hand, remember the system already knows how to do SGD on prompts — your manual edits should be the ones the outer loop cannot reach (code, contracts, layer boundaries).
Every reasoning task — analysis, planning, architectural decision, root-cause narrative, code review, design trade-off — MUST be run through a dialectical pass before being presented or acted on. One role proposes; a second, critical role attacks. Only the synthesis is admissible output.
This is not optional politeness or devil's-advocate theater. It is a loss-reducing discipline: first-pass reasoning is an unreviewed gradient (§4) and tends to overfit to the framing of the prompt. A proposal that has not survived its own strongest counter-case is a symptom patch at the reasoning layer (§3) — it will look right and fail in production.
Protocol:
- Thesis — State the proposed analysis / plan / fix / design. Be concrete: what exactly is claimed, what will change, what is expected to hold afterwards.
- Antithesis — Switch roles. Argue against the thesis as hard as possible. Where does it break? Which assumption is load-bearing and shaky? Which AWP contract (R1–R36, layer boundary, agent contract, budget) does it strain? What would a hostile reviewer, a different input, or a future maintainer trip over? At minimum surface: hidden assumptions, edge cases, alternative framings, violated invariants, second-order effects.
- Synthesis — Reconcile. Keep what survived, discard what didn't, adjust scope to what the antithesis actually forced. The synthesis MUST be strictly stronger than the thesis — either more correct, more narrowly scoped, or more honestly hedged. "The thesis was right" is only a valid synthesis after a real antithesis, not in place of one.
When it applies: analyses, plans, debugging hypotheses (pair with §3's 5-Why-by-Layer), architectural trade-offs, choosing between fix strategies, code-review notes, PR / commit rationale, recommendations to the user.
When it does not apply: trivial mechanical tasks (rename a variable, run a test, read a file). The rule is: if a second competent reviewer could disagree with the output, the output needs an antithesis first.
Presenting to the user: show the synthesis as the recommendation. Surface the antithesis only when the tension is load-bearing — i.e. the user needs to see the rejected alternative to judge the trade-off. Do not perform the dialectic for its own sake; the output is a sharper decision, not a longer message.
These rules come from analyzing recurring frictions in past AWP sessions. They are aligned with the AWP philosophy of deterministic validation before LLM-based work, budget-bounded autonomy, and conceptual clarity over local fixes:
- Mermaid → SVG, never inline code blocks. All diagrams in docs must be generated as standalone SVG files for GitHub rendering. No
feDropShadowfilters (they break GitHub's SVG sanitizer). - English only. All docs, diagrams, READMEs, comments, and artifacts are English. Never produce German content in committed files. (German is only allowed in chat replies to the user.)
- After every multi-file change: run the full test suite and verify environment sync —
packages/↔reference/python/src/mirror, venv packages, model config. Don't declare done until both are green. - Schema validation is the first step when generating or editing any
*.awp.yaml— validate before doing anything else with the file. - Confirm assumptions before sweeping changes. For any change touching >50 files or restructuring a package, state the assumptions explicitly and get a go signal first. This is the human-in-the-loop equivalent of an A2 manager validation gate.
- Required env vars belong in
CLAUDE.md. If a code path needs an env var (API key, model, path), document it here so future sessions don't stall on missing config. - Delegate big releases. Edits >500 lines and full PyPI release pipelines go to a general-purpose subagent, not the main loop.
These are not stylistic preferences — they are loss-reducing constraints. Treat a violation the same as a failed E2E test: find the root cause, fix it production-ready, do not paper over it.
Agent Workflow Protocol (AWP) is an open standard for defining and orchestrating multi-agent workflows. It separates workflow definition (YAML) from implementation (Python), organized in 7 semantic layers (manifest, identity, capabilities, communication, memory, orchestration, observability) spanning an autonomy spectrum from A0 (prescribed DAG) to A4 (self-organizing recursive delegation).
# Install (both packages, editable)
pip install -e packages/awp-core/
pip install -e "packages/awp-runtime/[data]"
# Lint and format
ruff check .
ruff format .
# Run all tests
pytest packages/awp-core/tests/ packages/awp-runtime/tests/
# Run core tests only (models, parser, validator)
pytest packages/awp-core/tests/
# Run runtime tests only (no E2E)
pytest packages/awp-runtime/tests/ -k "not e2e"
# Run a specific test
pytest packages/awp-core/tests/test_validator.py::test_function_name -v
# CLI commands (after install)
awp validate <path> # Validate workflow (rules R1-R32)
awp compliance <path> --level A2 # Check autonomy level (A0-A4)
awp visualize <path> --format mermaid # Render DAG
awp pack <path> # Archive as .awp.zip
awp run <path> # Execute workflow
# Continuation task runs (Plan 3)
awp run <workflow_path> --task "<fallback>" --target <experiment_id>:<continuation_task_id>
# Loads prior BEST bundle + user_feedback as manager_prompt_prefix.
# --task here is a fallback string that only surfaces if AgentWorkflow
# needs a task field for legacy reasons; the Manager's actual task is
# user_feedback from task.json. See docs/continuation.md.
awp refine <seed_run_dir> # Iteratively refine a completed run's deliverable (task-local SGD on y)
# Experiment + task lifecycle (hierarchy — see spec 2026-04-20-experiment-task-hierarchy-design.md)
awp experiment create "<name>" [--goal "<goal>"] # new top-level experiment
awp experiment list
awp experiment show <experiment_id>
awp experiment delete <experiment_id> [--yes]
awp task create <experiment_id> "<user_prompt>" # mode=seed
awp task create <experiment_id> "<user_feedback>" \
--continuation --from-task <task_id> \
[--primary BEST/] [--reference <relpath> ...] # mode=continuation (R37)
awp task list <experiment_id>
awp task show <experiment_id>:<task_id>
awp task delete <experiment_id>:<task_id> [--yes]
awp task set-best <experiment_id>:<task_id> --run <run_id> # user override
awp task set-best <experiment_id>:<task_id> --auto # clear override, auto-pick
# Target-aware refinement + optimization (Plan 4)
awp refine --target <experiment_id>:<task_id> # refine task's BEST run;
# session under <task>/refinements/
awp optimize <suite>.yaml --target <experiment_id>:<task_id>
# per-experiment outer_loop.db;
# epoch-runs under <task>/optimizations/E2E tests that call LLMs require an OpenRouter or OpenAI-compatible API key. Validation-only tests run without external keys.
The web UI (packages/awp-ui/) exposes the three-level hierarchy built by Plans 1-4:
- ExperimentSidebar (
packages/awp-ui/frontend/src/components/ExperimentSidebar/) — tree view ofExperiment → Task → Run. Toggle with the Sessions/Experiments buttons at the top of the sidebar. - ExperimentDetailView (
packages/awp-ui/frontend/src/views/ExperimentDetailView.tsx) — header, best-loss-per-task curve, task table. - TaskDetailView (
packages/awp-ui/frontend/src/views/TaskDetailView.tsx) — mode + user_prompt/user_feedback, loss-per-run curve (seed/refine/optimize series), BEST-override action. - LossCurveGeneric (
packages/awp-ui/frontend/src/components/Charts/LossCurveGeneric.tsx) — reusable Recharts wrapper used by both views. - Backend routes live in
packages/awp-ui/server/api/experiments.py—GET /api/experiments[/{id}[/tasks]],POST /api/experiments/{id}/tasks,GET /api/tasks/{key},GET /api/tasks/{key}/loss-series,POST /api/tasks/{key}/best.
Toggling "Experiments" in the sidebar shows the hierarchy; "Sessions" keeps the legacy flat-run view. Both coexist in Plan 5.
Experiment (campaign)
└── Task N (user intention: seed | continuation)
└── Run M (role: seed | refine_iter | optimize_epoch_run)
└── FINAL/ deliverable artifacts
└── BEST/ per task auto lowest-loss, user-overridable (reason: auto_loss|user_override)
└── shared/ memory, dynamic_tools, skills — accumulate across tasks
└── outer_loop.db per-experiment artifact registry (after Plan 4)
| Plan | Landed |
|---|---|
| 1 | ExperimentManifest / TaskManifest models, DB tables, CLI CRUD |
| 2 | awp run --target, BEST finaliser, awp task set-best |
| 3 | Continuation loader (bundle + prefix), awp run --target <cont> |
| 4 | awp refine --target, awp optimize --target, run_role param |
| 5 | UI sidebar tree + Experiment/Task views + BEST override |
| 6 | awp experiment purge-legacy, cross-link sweep, full-arc smoke |
| 7 | UI cascade — settings auto_refine_after_seed / auto_optimize_after_seed in server/services/cascade.py::cascade_after_seed; fires refine + optimize (A2) automatically after a hierarchy-attached seed run completes |
Normative rules: R37 (continuation input non-emptiness; spec/versions/1.0/validation-rules.md).
- Trigger: set
auto_refine_after_seedand/orauto_optimize_after_seedin Settings (or in the POST /api/runs payload). Only fires when the seed run is hierarchy-attached (experiment_id+task_idprovided). - Refine phase: runs
RefinementLoopagainst the seed's deliverable forauto_refine_iterationsiterations (default 2). Iterations land under<task>/refinements/session_<ts>/iter_k/. Seedocs/refinement.md. - Optimize phase: builds a 1-task
TaskSuiteSpecfrom the seed's task text and runsSuiteRunner.run_epochforauto_optimize_epochs(default 1) against<experiment>/outer_loop.db. This is the A2 path (no TextGrad — pure epoch-run accounting). For TextGrad updates, useawp optimize --with-textgrad --target <exp>:<task>. - Embedded-mode safety: Studio constructs
AgentWorkflow(allow_process_kill=False), which switches the δ-1 wall-time watchdog fromos.kill(getpid(), SIGTERM)toLLMClient.close_all()— the watchdog still hard-breaches on budget overrun but aborts in-flight HTTP calls instead of signaling the host server. CLI invocations keep the original SIGTERM → SIGKILL path.
The Python code lives in packages/ as two independent, publishable packages:
awp-core(packages/awp-core/src/awp/) — Protocol layer: models, parser, validator, CLIawp-runtime(packages/awp-runtime/src/awp/) — Execution layer: engines, LLM, tools, data API
awp-runtime depends on awp-core. Both share the awp.* namespace.
- DAG Engine (
packages/awp-runtime/src/awp/runtime/runner.py): Topological execution for A0-A1 workflows. Agents run in dependency order with state sharing viashare_output. Two scheduler modes viaorchestration.execution.scheduler:levels(default — topological barrier per level) andready_queue(opt-in — dispatches nodes as soon as their direct dependencies complete, so fast siblings unblock descendants without waiting on slow siblings). Semantics are identical in both modes except thatwhenexpressions, rate-limit, and circuit-breaker checks are evaluated at dispatch time against the current state snapshot. Whenorchestration.phasesis declared, the DAG engine ALSO dispatches deterministic phases (R33) in topological order ofdepends_onAFTER all graph nodes complete — seepackages/awp-runtime/src/awp/runtime/deterministic/. - Delegation Loop Engine (
packages/awp-runtime/src/awp/runtime/delegation_loop_runner.py): Manager-worker loop for A2-A4 workflows. Manager dispatches tasks to ephemeral workers with budget enforcement and validation gates. Deterministic-phase integration under this engine is Phase 2.x — NOT yet wired.
models/— Pydantic models for all 7 layers (manifest, agent, orchestration, capabilities, communication, memory, security, observability, evaluation)parser/— Parsesworkflow.awp.yamlandagent.awp.yamlinto Pydantic models, resolves importsvalidator/— Rule engine (R1-R33) covering naming, graph structure, confidence, tool namespaces, budgets, evaluation, and deterministic-phase purity (R33). Key file:rules.pyagent.py— AbstractAWPAgentinterface: agents must return{self.name: {result_dict}}with aconfidencefloat (R17)cli.py— CLI entry point (awpcommand)
runtime/— Execution engines,StandaloneAgentbase class,LLMClient,ToolRegistry, code executors (Docker, venv), evaluation engine, critique enginedata/— Programmatic API (AgentWorkflow) for running workflows from Python
- Agent output contract (R17): Every agent
run()returns{self.name: {"confidence": 0.0-1.0, ...}}. - State sharing: DAG nodes declare
share_output; downstream agents receive the fields in thestatedict. - Budget envelope (A2+): Hard limits —
max_loops,max_total_workers,max_total_tokens,max_wall_time,max_depth,max_workers_per_iteration(default 6, pre-spawn fan-out cap → excess becomes_deferred_workersfeedback),max_rejected_completions(default 2, COMPLETE-retry circuit breaker → synthesizes repair subtask or terminatespartial). The manager cannot override this envelope. The counter resets on any successful DELEGATE. Optional reservation-based accounting viatoken_budget_reservation: true(default false). Prevents parallel overshoot. - Completion gate chain (A2+): Every manager COMPLETE passes through a deterministic chain before the run ends —
l0(Layer-0 Output Contract, R34) →critique→deliverable_presence→placeholder→file→deliverable→structural_integrity→eval. A rejection bumps_rejected_completionsand loops back with a textual repair nudge. The L0 gate bundles 6 bit-level checks (no_placeholder,no_text_loop,file_size_delta,no_duplicate_headings,balanced_delimiters,json_valid_if_claimed) that short-circuit before any LLM token is spent; seepackages/awp-runtime/src/awp/runtime/critique/l0_validator.pyandobservability.output_contract. Optional parallelization of independent Phase-A gates (syntax_compile,schema,cross_reference,success_criteria,smoke_test) viaparallel_gate_chain: true(default false). Canonical rejection order preserved. - Repair fixpoint guard (R35): Before dispatching each new repair worker, the critique engine compares the 64-bit simhash of the last two outputs in the repair chain; similarity
≥ 0.95aborts the loop and emitsmetric.gatewithgate="repair_fixpoint"plussim,attempt,previous_output_path. Shared simhash primitives inpackages/awp-runtime/src/awp/runtime/critique/simhash.py; guard inCritiqueEngine.attempt_repair(packages/awp-runtime/src/awp/runtime/critique/engine.py). - Plan-loop transition (Fix D): After N consecutive PLANs without worker progress (strict N=2, relaxed N=3), the
plan_loopgate firesforced_delegate(pending subtasks → lock plan, force DELEGATE) orforced_terminate(none →partialexit, reasonplan_loop_stall). - Terminal status (Fix H): Every run ends in exactly one of
{complete, partial, failed, aborted}. Cap/limit exits →partial. Hard exec/eval failures →failed. SIGTERM/SIGINT without terminal decision →aborted. Single source of truth:_finalize_terminal_statusindelegation_loop_runner.py. - Finalizer guarantee (Fix E): try/except/finally around the main loop plus
SIGTERM/SIGINThandlers emitrun_completion.jsonon every exit path. Orphan runs on server restart (no live PID) →aborted, reasonprocess_exit_without_terminal_event. - Canonical output pointer (
FINALdirectory under the run's output): The root manager ends everycomplete/partialrun by promoting the deepest non-empty instance of each declared deliverable (fromrequired_outputsor scraped fromsuccess_criteria) into<workflow_dir>/output/FINAL(as one runtime-generated path). Sub-manager outputs win over parent-level stubs; hard link preferred, copy fallback. Implemented inDelegationLoopRunner._write_canonical_final_output. - Manager context guard: Before every manager call, chars are estimated (≈4 c/tok). Above
manager_context_compress_threshold(default 0.8) ofmanager_context_budget_tokens(default 150_000), the user message is deterministically compressed (state sections → one-liners; if still over, head/tail retention to ≤60 %). Gate-feedback sections are never compressed. Implemented in_guard_manager_context. - Validation tiers: Deterministic validation (schema, R1-R32) always runs. LLM semantic validation is optional (skipped above confidence threshold).
- Evaluation layer: Optional scoring (5 metric kinds, weighted, threshold-based retry/repair), configured under
observability.evaluation. - Critique loop: Optional reflective critique inside the delegation loop (defect diagnosis, targeted repair, cross-worker memory), configured under
delegation_loop.critique. - Refinement Mode (y-axis optimization):
awp refine <seed_run_dir>wraps the existing delegation loop to iteratively refine a completed run's deliverable. Orthogonal toawp optimize(which moves θ — the prompt artifacts). Gradient = critique defects + last 3 gate rejections + eval deltas; injected as a deterministic prefix into iteration 1's manager user message only. Budget halved per iteration; stop on regression×2, plateau×2, wall-time cap (2× seed observed), or max iterations. R36 aborts on empty gradient. Iterations are independent experiments linked viaparent_run_id; winning iteration hard-linked into<seed>/BEST/. Authoritative doc:docs/refinement.md. - Framework-Fixes α/β/γ/δ (runtime hardening — see
docs/runtime.mdfor full rationale; authoritative code paths below):- α-1 Persistent-executor namespace: warm Python subprocess per worker; merged stderr,
select-based deadline, bounded-history replay.packages/awp-runtime/src/awp/runtime/persistent_executor.py. - α-2 In-place repair registry: repair/retry variants share the α-1 executor via
logical_worker_id(suffix stripping); repair workers get a## REPAIR MODEmarker; manager can force reset via envelopefresh_worker: true.packages/awp-runtime/src/awp/runtime/delegation_loop_runner.py. - β Auto-emergent tool induction: AST-skeleton signature,
N=3distinct workers → literals lifted to parameters, persisted asshared/dynamic_tools/dynamic.induced_<hash6>.json, listed inrun_completion.json.induced_tools.packages/awp-runtime/src/awp/runtime/tool_inducer.py. - γ Atomicity advisory: deterministic 0.0–1.0 score injected into the planning prompt as advisory only — never a gate; the manager stays authoritative on delegation shape.
packages/awp-runtime/src/awp/runtime/atomicity.py. - δ-1 Wall-time watchdog: daemon thread at
depth == 0, 30 s poll, SIGTERM → SIGKILL escalation on breach; nested sub-managers skip (root covers them).delegation_loop_runner.py—_start_walltime_watchdog/_stop_walltime_watchdog. - δ-2 Hard per-call executor killer:
Timer(effective_timeout + 5 s)force-kills the warm subprocess if α-1's deadline fails to wake the read thread; falls through to_execute_cold.persistent_executor.py—_hard_kill_from_watchdog,execute(). - δ-3 Pipe-output cap:
stdout/stderrcapped at 2 MB in_WORKER_SCRIPTbefore JSON serialisation to prevent pipe-buffer deadlocks.
- α-1 Persistent-executor namespace: warm Python subprocess per worker; merged stderr,
- Artifact registry (
packages/awp-runtime/src/awp/outer_loop/): a versioned store for the 6 prompt artifacts currently routed through it —worker_pitfalls,manager_planning_preamble,experiment_context_hint_template,pattern_library,tool_description_templates,critique_rubric. Authoritative code:artifacts.py(registry) +store.py(SQLite) +defaults/(v0 fallback strings). - Fallback behavior: when no DB is provided (or
~/.awp/outer_loop.dbis not writable), the registry serves the hardcoded v0 defaults bundled underdefaults/. v0 is synthetic and is NEVER persisted to the DB. - Env override:
AWP_OUTER_LOOP_DBoverrides the default DB path. The DB is opened lazily on firstget_activecall —import awp.outer_loophas no filesystem side effects. - Phase A1: behavior-preserving artifact registry. Prompts are byte-identical to the pre-refactor codebase; no runtime control flow changed.
- Phase A2 (landed): deterministic loss + task suites + CLI.
loss.py(compute_run_lossreadsrun_completion.json+metrics.jsonland returns aLossBreakdownweighted across eval, critique, gate rejections, budget burn, terminal status),suite.py(TaskSuiteSpecPydantic schema +load_suite),runner.py(SuiteRunner.run_epochpersists epochs + epoch_runs to the same SQLite store as the registry). Theawp optimize SUITE_YAMLCLI without--with-textgradstill runs this A2 path: the suite runs N epochs, losses are captured, but no artifact is touched (child_artifacts == parent_artifacts). - Phase A3 (landed): TextGrad LLM-as-optimizer + rollback on regression.
textgrad.py(TextGradOptimizer.propose_update— onechat_textcall per candidate, strict-JSON parsing with markdown-fence tolerance,argmax(expected_loss_reduction * confidence)selection, hard constraints: wrong name / unchanged / > 20 000 chars → drop).SuiteRunner.optimizedrives the multi-epoch SGD loop: apply one update per epoch, onmean_lossregression roll back the last update and halve the learning rate.epochs.child_artifacts_jsonis now a structured payload ({"artifacts": name->version, "events": [...]}), not just a name-to-version map. CLI surface:awp optimize --with-textgrad --epochs N --learning-rate F [--no-rollback] [--manager-model M],awp optimize-rollback ARTIFACT VERSION,awp optimize-inspect --artifact NAME(unified-diff history). Authoritative code:packages/awp-runtime/src/awp/outer_loop/textgrad.py,packages/awp-runtime/src/awp/outer_loop/runner.py(SuiteRunner.optimize,_apply_update,_rollback_last_update). - OFF by default: outer-loop code paths are only entered through
awp optimize/awp optimize-inspect/awp optimize-rollback; a normalawp runnever imports the runner.--with-textgradis opt-in — without itawp optimizestill runs the A2-compatible path.
- Entry point:
awp refine <seed_run_dir> [--iterations N] [--model M] [--worker-model M] [--tier-low MGR:WKR] [--tier-mid MGR:WKR] [--tier-high MGR:WKR]. Clamped to[1, 10]iterations; default 3. - Model tiering (additive,
awp.refinement.tiers.TierPlan): optional low/mid/high{manager, worker}pairs mapped across N iterations (thirds-proportional;lowearly →highlate); empty tier fields fall back to seed's models.tier_plan=None→ factory call is byte-identical to the legacy path (hard stability contract). API body fieldstier_low/tier_mid/tier_highwin over legacymodel/worker_modelwith arefinement.mixed_bodywarning. Session sidecar recordstier_plan_used, plus per-iterationtier/model_manager/model_worker. Authoritative doc:docs/refinement.md §6.6. - Gradient (
awp.refinement.gradient): deterministically extracted from the prior run'srun_completion.json(critique defects + eval deltas) andevents.jsonl(last 3gate.rejectentries). No LLM call. Prefix template inrender_refinement_prefixomits empty sections. - R36 (normative): empty gradient → CLI prints
"nothing to refine"and exits 0; noAgentWorkflowis constructed. Spec:spec/versions/1.0/validation-rules.md §12. - Injection point:
DelegationLoopRunnerprependsmanager_prompt_prefixto the manager's user message on iteration 1 only. Both the inline-manager path (_run_inline_manager) and the agent-based path (_run_manager) honor the guard; subsequent iterations see the vanilla message. - Budget halving (
budget_for_iteration): counts + tokens halved withceil/floor; wall-time halved from the seed's observed wall time (floor 60 s); depth unchanged. - Stop conditions (
RefinementLoop):max_iterations,regression(loss rose 2×),plateau(|Δloss|<0.01 for 2×),wall_time_exhausted(cumulative ≥ 2× seed observed),empty_gradient(iter 1, R36),empty_gradient_midloop(iter k>1 produced a perfect run). - Storage: iteration runs are standalone experiments usually under
/tmp/awp-experiments/refine_<ts>/iter_<k>/linked viaparent_run_id(seed → iter_1 → iter_2…). Session sidecar at<seed>/refinement_sessions/<session_id>.json. Winning iteration hard-linked into<seed>/BEST/withmanifest.json; overwritten only if a future session produces strictly lower loss. - Loss:
compute_run_lossfromawp.outer_loop.lossis reused unchanged with defaultLossWeights. - OFF by default: refinement code is only imported by the
awp refineCLI orRefinementLoop.run();awp runandawp optimizenever import it. - Authoritative doc:
docs/refinement.md. Implementation plan:docs/superpowers/plans/2026-04-19-awp-refine-mode.md.
Dynamic-tool JSON under shared/dynamic_tools/*.json carries a nested provenance.creator_agent field written by DynamicToolFactory._persist_tool. The UI tool-registry panel reads it via packages/awp-ui/server/services/graph_builder.py::_build_tool_registry, with legacy fallback to a flat creator_agent key and final default "persisted". When signature is absent in the persisted JSON, the panel falls back to the raw parameters object so tooltips always have something renderable.
spec/— Normative specification (RFC 2119 language)docs/— Protocol documentation for each layerexamples/— 18 runnable examples progressing A0→A4 (including evaluation, critique, and manager intelligence)conformance/— Test fixtures for spec complianceschemas/— JSON schemasskill/— AWP Skill for Claude Desktop (templates, adapters)
The AWP Workflow Builder skill (skill/SKILL.md) MUST be updated whenever the runtime, models, or features change. This skill generates new workflows — if it is outdated, every generated workflow will have the same bugs.
When you change any of the following, you MUST also update skill/SKILL.md and the relevant files in skill/templates/:
| Change | What to update in skill/ |
|---|---|
New runtime feature (e.g. _workspace_dir, required_secrets) |
SKILL.md examples + templates |
| New/changed model fields (orchestration, capabilities, etc.) | SKILL.md config examples + workflow template |
| Changed defaults (token budget, timeouts, etc.) | SKILL.md reference values + templates |
New/removed tools (code.execute, etc.) |
SKILL.md tool reference + templates + adapters |
| Changed forbidden_tools or security policy | SKILL.md delegation loop section + templates |
| New validation rules (R1-R32+) | SKILL.md Phase 4 checklist |
| Changed delegation loop behavior | SKILL.md delegation loop section + Step 7d |
| New debug/observability features | SKILL.md relevant sections |
Failure to sync causes cascading bugs: generated workflows silently fail because they use outdated config patterns (e.g. shell.execute in tools_allowed when it's forbidden, dynamic_tools.enabled: false when tool creation is needed).
Full rubric, tags, live-monitoring runbook, and LLM-trace debug walkthrough: docs/e2e.md. The rules below are non-negotiable and apply to every E2E run.
An E2E test is a full run that exercises the entire system end-to-end:
- Create a new experiment from scratch.
- Give it a fictional task that forces coverage of: orchestration (DAG or delegation loop), manager intelligence (autonomous decisions, submanager promotion), tool creation (dynamic factory generates + validates), skill creation (produced and reused), sub-manager delegation (recursive spawning).
- The run MUST pass the rubric in
docs/e2e.md(terminal state, artifacts, budget, graph integrity, rubric score).
- E2E runs MUST be started from the UI. Fire
awp studio, open the browser athttp://127.0.0.1:8420, create the experiment and task from the UI (or select an existing one in the Experiments sidebar), then hit Run from there. CLI-onlyawp run/awp refine/awp optimizeinvocations do NOT count as E2E — they bypass the UI settings cascade, don't stream events live to the WebSocket the way UI-initiated runs do, and don't let the user observe what's happening in the sidebar. The user must be able to watch the run populate (sidebar entries transitioningrunning → complete, loss curve updating, BEST badge flipping) in the UI at every step. A run that was not triggered from the UI is not a valid E2E observation. - Real LLM calls only (OpenRouter / OpenAI / Anthropic). Mocked, stubbed, or recorded responses are not valid coverage.
- Stored as real experiments in
/tmp/awp-experiments/with populated output folders. Pytest-only runs without a populated experiment are not valid — I must be able to open the run in the UI, see its graph, and inspect its artifacts. - Registered in the experiment DB before the run starts (status
running, via the sameAgentWorkflowpath the UI uses), so the experiment shows up in the sidebar immediately and transitionsrunning → complete | partial | failedlive. Intermediate events (iterations, worker spawns, tool calls) persist as they happen. - Tagged via
run_e2e(tags=[...])."e2e"is auto-added; add focus tags (s5,tool-creation,critique,sub-manager,memory,planning,quick— seedocs/e2e.md). An untagged E2E is not valid. - Full LLM trace persisted alongside
run_completion.jsonandevents.jsonl: every manager / worker / critique / eval / induction call with timestamp, caller, model, system prompt, user prompt, raw response, tokens, latency, parsed decision. Mocked / summarized / truncated traces are not valid. - Debug starts at the trace. On every E2E failure, open the trace and walk the 5-Why-by-Layer protocol (§3) over it before hypothesizing about code — see the debug walkthrough in
docs/e2e.md. A fix proposed without reading the trace is a symptom patch and is rejected.
The E2E run is a forward pass and the code fix is an SGD step (see §4 for the full loss/backprop lens: what counts as a gradient, why symptom patches are overfitting, and when to prefer an architectural edit over a local tweak). Guiding question: "Would this system reach complete for an arbitrary task?" If no, it is not shippable.
Every E2E run MUST be actively monitored from start to terminal status. Fire-and-forget is forbidden — a run left unattended is wasted budget and a missed loss signal.
While a run is in flight, continuously observe events.jsonl, the live trace, and the experiment row (running → complete | partial | failed | aborted). Use the live-monitoring runbook in docs/e2e.md.
At every observable signal (iteration boundary, gate rejection, repeated worker failure, budget warning, plan-loop, stall, anomalous trace), make an explicit decision:
| Signal | Decision | Action |
|---|---|---|
| Run is on track, gates green, manager making progress | let it continue | keep monitoring; no edits |
| Recoverable systemic defect visible (wrong contract, bad prompt, broken tool, missing config) | intervene + fix + rerun | abort the run, diagnose at layers 4-5 (§3), apply the production fix, rerun from scratch |
| Pure LLM noise / format drift that the gate chain will catch and repair | let it continue | observe whether the repair-nudge loop closes it; only intervene if _rejected_completions approaches the cap |
| Budget about to exhaust without convergence | abort + diagnose | do not wait for the cap — abort, diagnose the structural origin, rerun |
A run that limps to partial or failed because no one was watching counts as a process failure on top of the run failure. Decide actively: continue, or abort + fix + rerun. Never just "wait and see" once a defect is visible.
Stagnating E2E runs MUST be aborted early, diagnosed, fixed, and restarted from scratch — not waited out. Waiting on a stagnating run burns budget, delays the loss signal, and trains the wrong reflex (patience instead of pathology). The goal is short feedback cycles around real fixes, not long runs around hope.
A run is stagnating as soon as any of these are observable:
- 2+ consecutive iterations with no new worker progress (no fresh artifact, no new
share_outputfield, no advancing plan) - Same gate rejecting with the same reason ≥ 2 times
- Same worker failing the same way ≥ 2 times after a repair attempt
- Manager replanning (
PLAN) without delegating, even beforeplan_loopfires its forced transition - Wall-time burned past ~50 % of budget while artifacts/state are still pre-deliverable
- Tokens burned past ~50 % of budget without the manager having issued a passing
COMPLETEattempt - Critique loop oscillating on the same defect across iterations
- Trace shows the LLM repeating itself / looping on the same wrong shape
The moment any of these is visible: abort the run immediately (do not wait for the runtime's own plan_loop_stall / max_rejected_completions / wall-time watchdog to fire — those exist as last-resort safety nets, not as the primary stop condition for an actively-monitored run).
Then, in order:
- Capture artifacts (
run_completion.json,events.jsonl, full LLM trace, output dir, experiment DB row) before they get overwritten by the rerun. - Walk 5-Why-by-Layer (§3) on the captured trace to layers 4–5. Stagnation almost always indicates a contract / prompt / gate / tool defect, not "the LLM was tired."
- Apply the production fix at the structural origin, not at the symptom. Add a regression test at the layer of the root cause.
- Restart from scratch. No resume, no patched-mid-run continuation — the rerun must exercise the full loop against the fixed system.
Bias is asymmetric: false-abort is cheap (one wasted run), false-wait is expensive (full budget + still no signal + no fix). When in doubt whether a run is stagnating, abort.
Before every PyPI build + push, an E2E run MUST reach complete and cover all new features introduced since the last commit pushed to GitHub (diff HEAD..origin/HEAD scope). Additionally, all other test suites MUST be green:
pytest packages/awp-core/tests/ packages/awp-runtime/tests/A single failing or skipped-due-to-error test blocks the publish. Fix the root cause, do not disable or xfail tests to unblock a release.
Full build+publish sequence, architecture notes, and common-mistake list: docs/pypi-release.md. Read it before every release. The load-bearing rules below are reproduced here so they can't be missed:
-
One published package:
awp-agents(built fromreference/python/). It vendorsawp-core,awp-runtime, and the UI server + pre-built frontend.awp-core/awp-runtimeare not published separately. -
Frontend is a built artifact:
cd packages/awp-ui/frontend && npm run build, then copypackages/awp-ui/frontend/dist/→reference/python/src/server/frontend/dist/before building the wheel. The mirror gate does not cover this copy; a skipped step ships stale JS/CSS. -
Python mirror must match:
packages/awp-runtime/src/,packages/awp-core/src/, andpackages/awp-ui/server/must be byte-identical to theirreference/python/src/counterparts. Enforced byscripts/check_mirror_drift.py(blocks commits). -
Version sync (one commit, all four):
File Field packages/awp-core/pyproject.tomlversionpackages/awp-runtime/pyproject.tomlversion+awp-core>=dependencypackages/awp-ui/pyproject.tomlversion+awp-core>=andawp-runtime>=dependenciesreference/python/pyproject.tomlversion(awp-agents meta-package) -
PyPI is append-only: a broken upload cannot be replaced — bump the version again.
- Python 3.10+ with modern syntax (
X | Yunions,matchwhere appropriate) - Type annotations on all public functions
snake_casefunctions/modules,PascalCaseclasses,UPPER_SNAKE_CASEconstants- Use
loggingmodule, neverprint() - Pydantic or dataclasses for structured data
- Conventional commits:
feat:,fix:,docs:,refactor:,test:,chore:
- Default manager model:
openai/gpt-5-mini(via OpenRouter) - Default worker model:
deepseek/deepseek-chat-v3.1(via OpenRouter) - The UI uses a free text input for model selection — no dropdowns. Never add
<select>dropdowns for model selection. - Provider is auto-detected from the model string:
| Model string pattern | Routed to | Required API key |
|---|---|---|
provider/model-name (e.g. openai/gpt-5-mini) |
OpenRouter | OPENROUTER_API_KEY |
gpt-*, o1-*, o3* |
OpenAI (direct) | OPENAI_API_KEY |
claude-* |
Anthropic (direct) | ANTHROPIC_API_KEY |
ollama/* |
Ollama (local) | none |
- Backend routing logic lives in
packages/awp-ui/server/services/runner_service.py - Frontend routing display lives in
packages/awp-ui/frontend/src/components/Settings/SettingsPanel.tsx(detectProvider()) - Default model must be consistent across:
workflowStore.ts(DEFAULT_CONFIG),routes.py(_default_settings), andSettingsPanel.tsx(placeholder)
- NEVER commit real API keys, tokens, or secrets to the repository.
- Before every push, scan for leaked keys:
grep -rn 'sk-or-v1-[a-f0-9]\{20,\}' . --include='*.py' --include='*.ipynb' --include='*.md' --include='*.yaml' - Use placeholder patterns in examples and notebooks:
"sk-or-v1-...","your-api-key-here", oros.getenv("OPENROUTER_API_KEY", "") - Notebooks must read keys from environment variables, never hardcode them in cells
- If a key is accidentally committed, rotate it immediately and force-push a clean history
- All documentation (.md files), code comments, docstrings, commit messages, and YAML descriptions MUST be in English.
- No German (or other non-English) text in any committed file. Variable names in code examples within docs should also be English.
- This applies to: README.md, README_NERD.md, docs/, examples/, skill/, spec/, CLAUDE.md, inline comments, and docstrings.
- When editing code that involves LLM response parsing, always validate for non-JSON responses, malformed output, and add fallback/retry logic. Never assume LLM outputs will be well-formed.
- After making code fixes, verify that existing functionality still works — run the relevant test suite before committing. Do not assume a fix is isolated; check for regressions.
- When generating or editing YAML files, always validate against the expected schema before considering the task done. Pay attention to: frontmatter format, required fields, field types (especially enums like persistence type, state.sharing format).
- Always kill any server processes you start (FastAPI, Vite, etc.) before starting new ones. Check for port conflicts with
lsof -i :<port>before launching servers.
- When implementing multi-file changes, do a dry-run validation pass after all edits: check imports resolve, function signatures match callers, and config references exist. Do not wait for runtime errors.
See §2 "Doc Sync as Definition-of-Done" for the full contract, sync table, and enforcement gates.
!sudo install -d -m 0755 /etc/systemd/system/user@.service.d/ && sudo tee /etc/systemd/system/user@.service.d/99-oomd-relax.conf > /dev/null <<'EOF'
[Service]
ManagedOOMMemoryPressureLimit=80%
EOF
sudo systemctl daemon-reload && sudo systemctl set-property user@1000.service ManagedOOMMemoryPressureLimit=80%