CLAUDE.md
July 5, 2026 · View on GitHub
Claude Code integration for the Agentic Engineering Framework.
For the provider-neutral framework guide, see FRAMEWORK.md.
This file is auto-loaded by Claude Code. It contains the full operating guide plus Claude Code-specific integration notes.
Project Overview
The Agentic Engineering Framework is a governance framework for systematizing how AI agents work within engineering projects. This is not a traditional code library—it's a set of structural rules, patterns, and enforcement mechanisms for agentic workflows.
Primary Environment: Any file-based, CLI-capable AI agent (see FRAMEWORK.md) Claude Code Integration: This file is auto-loaded by Claude Code.
Core Principle
Nothing gets done without a task. This is enforced structurally by the framework, not by agent discipline.
Four Constitutional Directives (Priority Order)
All architectural decisions must trace back to these directives:
- Antifragility — System strengthens under stress; failures are learning events
- Reliability — Predictable, observable, auditable execution; no silent failures
- Usability — Joy to use/extend/debug; sensible defaults; actionable errors
- Portability — No provider/language/environment lock-in; prefer standards (MCP, LSP, OpenAPI)
Authority Model
Human → SOVEREIGNTY → Can override anything, is accountable
Framework → AUTHORITY → Enforces rules, checks gates, logs everything
Agent → INITIATIVE → Can propose, request, suggest — never decides
Initiative ≠ Authority. Broad directives ("proceed as you see fit") delegate initiative, not authority. When a structural gate blocks an action, the gate wins — always ask, never force. See §Autonomous Mode Boundaries.
Instruction Precedence
When multiple instruction sources conflict (CLAUDE.md, plugins, skills, user messages), this resolution order applies:
- Framework rules (this file) — Core Principle, Authority Model, Enforcement Tiers, and Task System rules take absolute precedence. No plugin or skill can override "Nothing gets done without a task."
- User instructions — Direct human instructions can override framework rules via Tier 2 (situational authorization with logging).
- Skills/plugins — Apply AFTER framework gates are satisfied. A skill that says "invoke before any response" means: after verifying an active task exists. Skills enhance workflows; they do not replace framework governance.
The practical rule: Before following ANY skill or plugin workflow, first ensure a task exists and focus is set. If a skill's instructions conflict with creating a task first, the task wins.
Why this matters: Third-party plugins are not aware of project-specific governance. They will issue instructions like "implement now" or "code first, test first" without checking for task context. The agent must apply framework rules as a pre-filter before deferring to skill workflows.
Watchtower Port (read this FIRST, before any curl localhost:3000)
Watchtower's port is per-project, not hard-coded to 3000. Two consumer projects on one host would collide if the framework assumed 3000 everywhere.
Resolution order (T-885, T-1287, T-1376):
.context/working/watchtower.url— triple-file source of truth, written bybin/watchtower.shon start. Read this file, don't guess.bin/fw config get PORT— per-projectFW_PORTconfig when no Watchtower is currently running.3000— default ONLY when neither of the above is available (fresh project, no config, no running instance).
How to check:
- Current port:
bin/fw watchtower port(triple-file → fw_config → 3000 fallback) - Current URL:
bin/fw watchtower url(triple-file → fw_config →http://localhost:PORTfallback) - Raw files:
cat .context/working/watchtower.port/cat .context/working/watchtower.url - Diagnostics:
bin/fw doctor(surfaces all three triple-file states)
How NOT to check:
- Do not write
curl http://localhost:3000/...in instructions, verification examples, or cron scripts. That literal hard-code is an anti-pattern (T-1376) and has caused agents to kill or mis-target live sessions across projects. - Fallback to
3000is fine in shell whenfw_config PORTand the triple file both return empty — that is a defensive default, not a hard-code.
Task System
File Structure
.tasks/
active/ # In-progress tasks (e.g., T-042-add-oauth.md)
completed/ # Finished tasks
templates/ # Task templates by workflow type
Task File Format
Tasks are Markdown with YAML frontmatter. Use default.md as template.
Required frontmatter fields:
id,name,description,status,workflow_type,horizon,owner,created,last_update
Optional frontmatter fields:
arc_id(T-1849) — single arc reference. Accepts either form:- slug form:
arc_id: dispatch-safety(filename stem of.context/arcs/<slug>.yaml) - arc-NNN form:
arc_id: arc-001(canonical immutable id from T-1848 D-Immutability axiom) - Empty/missing/null → task is unassigned to any arc (allowed; arcs are optional)
- PreToolUse hook
check-arc-id(Write|Edit on.tasks/{active,completed}/T-*.md) refuses saves under agent control whenarc_idis non-empty and does not resolve to an existing arc YAML. Override:FW_ALLOW_ARC_ID_DRIFT=1(logged Tier-2 in.gate-bypass-log.yaml). - Coexists with the legacy
tags: [arc:<slug>, ...]form (T-NEW-3 / T-1850 will migratearc:*tags intoarc_id).
- slug form:
- Inception GO-scope traceability (T-1984, G-066 prevention) — Two optional fields that make inception GO scope machine-readable and gate-enforced:
inception_decisions:(onworkflow_type: inception) — list of{id: <kebab-slug>, text: <one-liner>, ships_in: <referent>}entries. Eachships_in:accepts five shapes: (1)path/to/file.ext(file exists at PROJECT_ROOT), (2)module.function(symbol grepped in lib/agents/bin), (3)tests/path/test.py::test_func(file+function), (4)T-XXX(task in.tasks/completed/), (5)deferred:T-YYYY(task in.tasks/{active,completed}/). PreToolUse hookcheck-inception-decisionsblocks Write|Edit when entries are structurally invalid. Close gate inupdate-task.sh --status work-completedvalidates reachability of everyships_in:referent. Override:--skip-inception-scope-trace "rationale"(direct invocations) orFW_SKIP_INCEPTION_SCOPE_TRACE=1(git/wrapper invocations) — both log Tier-2. Grandfathered: empty/missing field skips the gate entirely.unlocks_inception_decision:(onworkflow_type: build) — list ofT-XXX:decision-idstrings declaring which inception decisions this build child ships. PreToolUse hook validates each reference (inception exists + has that decision id). Example:unlocks_inception_decision: [T-1983:my-decision]. Override:FW_ALLOW_INCEPTION_DECISIONS_DRIFT=1.
bvp_scores,bvp_scores_proposed,cost_estimate(T-1918, arc-006) — Business Value Points scoring fields.bvp_scores:(map) — confirmed per-driver scores 0-5, set only byfw bvp confirm(T-1924). Sovereignty boundary. Shape{D1: <0-5>, D2: <0-5>, D3: <0-5>, D4: <0-5>, [<free-driver-id>: <0-5>]…}.bvp_scores_proposed:(list of timestamped entries) — estimator-proposed scores written by the TermLink BVP estimator worker (T-1922). Persists only when the proposal differs frombvp_scores:by ≥2 on any driver (M3 v2-delta semantics).cost_estimate:(map) — F8 composite0.6×blast_radius + 0.3×tier + 0.1×effort. Q2 fallback: T-shirtS/M/L/XLmapped to2/4/6/8whenblast_radiusis not computable yet. Read by auto-promote (T-1931) andfw bvp rank(T-1919).- Audit (
fw audit) treats unknown frontmatter fields as silent additions (A2). Documented inpolicy/value-drivers.yamlanddocs/reports/T-1915-bvp-inception.md.
Horizon (Priority Scheduling)
The horizon field controls when a task should be considered for work:
| Value | Meaning | Handover behavior |
|---|---|---|
now | Ready to work on (default) | Appears first in Work in Progress, eligible for Suggested First Action |
next | Ready after current work | Appears in Work in Progress, eligible for Suggested First Action |
later | Parked/backlog — not yet | Appears last in Work in Progress, excluded from Suggested First Action |
Rules:
- Default horizon is
now(tasks created viafw work-onorfw task create) - Use
--horizon laterfor tasks captured for future reference - Use
fw task update T-XXX --horizon nowto promote a backlog task - The handover agent sorts tasks by horizon and instructs the enricher to skip
latertasks in suggestions
Invariants (T-1068): update-task.sh enforces consistency between status and horizon:
--status started-work→ auto-setshorizon: now(starting work means it's active now)--horizon next/lateron astarted-worktask → auto-demotesstatus: captured(shelving means you stopped)- Both produce an info message. No override needed — if you want to start work, it's
now; if you shelve it, it's notstarted-work.
Body sections:
- Context (brief, link to design docs for substantial tasks)
- Acceptance Criteria (checkboxes — completion gate P-010)
- Verification (shell commands — verification gate P-011, see below)
- Decisions (only when choosing between alternatives; most tasks have none)
- Updates (auto-populated by git mining at completion; manual entries optional)
Verification Gate (P-011)
The ## Verification section contains shell commands that must pass before work-completed is allowed. This is a structural gate — the framework runs the commands mechanically, not the agent self-assessing.
How it works:
- Agent writes verification commands in
## Verificationwhile working (knows what to check) - On
fw task update T-XXX --status work-completed, update-task.sh extracts and runs each command - If any command exits non-zero → completion is blocked (same as unchecked AC)
--forcebypasses the gate (with warning, logged)- Tasks without
## Verificationpass through (backward compatible)
What to verify:
- YAML/JSON files parse correctly:
python3 -c "import yaml; yaml.safe_load(open('file'))" - Web pages load:
curl -sf "$(bin/fw watchtower url)/page"(never hard-code:3000— see §Watchtower Port above) - Commands succeed:
fw doctor - Output contains expected content:
grep -q "expected" output.txt
Rules:
- Lines starting with
#are comments (skipped) - Empty lines are ignored
- Each non-comment line is executed as a shell command
- First 5 lines of failure output are shown for debugging
Toolchain build commands (L-291, T-1501): If your task touched compileable artifacts, the matching build command MUST be in ## Verification. The framework is toolchain-agnostic by design — it runs only what you write, so a forgotten dotnet build ships broken DLLs to master (origin: 003-NTB-ATC-Plugin T-077, 5 days undetected).
| You edited | Add to ## Verification |
|---|---|
*.vbproj / *.csproj / *.xaml | dotnet build |
*.go | go build ./... |
Cargo.toml / *.rs | cargo check |
tsconfig.json / *.ts | tsc --noEmit |
pom.xml / *.java | mvn -q compile |
If the toolchain is not installed on the gate-running host, scope the command (e.g. command -v dotnet >/dev/null && dotnet build). Don't omit the check.
Cron-touching tasks (L-364, T-1771, T-1942, T-1943): If your task edited .context/cron-registry.yaml, anything under .context/cron/, or any cron generator code (bin/fw cron generate|install, agents/audit/audit.sh cron schedule helpers), ## Verification MUST include:
out=$(bin/fw doctor 2>&1); echo "$out" | grep -q "Cron registry in sync" && ! echo "$out" | grep -q "Cron registry edited but not generated"
The chain is registry → generated → deployed — three transitions, three drift classes (T-1942):
- registry → generated —
fw cron generatenot run after registry edit. Doctor emitsWARN Cron registry edited but not generated. Audit emits FAIL (T-1943). - generated → deployed —
fw cron installnot run after regenerate. Doctor emitsWARN Cron registry drift. Audit emits FAIL (T-1771). - deployed → executable — cron line is on disk but fails at exec time (cwd / env / missing module). L-365 advisory; no automated gate yet.
Each transition is a separate state — "wired" is not "deployed", "deployed" is not "executable". The verification command above checks BOTH the "in sync" PASS (generated↔deployed) AND the absence of the new "edited but not generated" WARN (registry↔generated). Without both clauses, a registry-edited-but-never-regenerated state passes the gate because the in-sync line still fires (origin: T-1935 — 3+ days of silent drift before T-1941 cleared it).
T-1942 + T-1943 close the registry→generated leg at both surfaces (doctor WARN, audit FAIL). T-1771 covers generated→deployed. Task-close is the earliest gate; audit's daily cron catches anything that slips past.
Tool-set.yaml-touching tasks (T-2290, arc-010): If your task edited policy/capability-overlay/tool-set.yaml or agents/mcp/manifest.py, ## Verification MUST include:
out=$(bin/fw doctor 2>&1); echo "$out" | grep -q "framework MCP " && ! echo "$out" | grep -q "manifest stale relative to tool-set.yaml"
The chain is tool-set.yaml → framework-mcp-manifest.json — one transition (tool catalogue → emitted manifest). Drift surface:
- tool-set → manifest —
fw mcp emit-manifestnot run after a content-changing edit totool-set.yaml(or to the emitter). Doctor emitsWARN framework MCP manifest stale relative to tool-set.yaml. Sibling to the cron registry→generated leg above.
The verification command above checks BOTH the OK line (any tool count) AND the absence of the stale-WARN. T-2290 hardened doctor's stale-check to use content compare (md5 of manifest-show vs on-disk) instead of raw mtime — so touch/git checkout/fw vendor self no longer fire false positives, and only real content drift surfaces the WARN. The Verification command is therefore an authoritative content gate, not an mtime gate.
If you edited tool-set.yaml without touching agents/mcp/manifest.py, regenerate before close: bin/fw mcp emit-manifest. If you edited the emitter, manifest content may shift even when tool-set.yaml is unchanged — same drill.
Task Lifecycle
Captured → Started Work ↔ Issues → Work Completed
Workflow Types
| Type | Purpose | Typical Agent |
|---|---|---|
| Specification | Define what to build | Specification Agent |
| Design | Determine how to build | Design Agent |
| Build | Create implementation | Coder Agent |
| Test | Verify correctness | Test Agent |
| Refactor | Improve existing code | Coder Agent |
| Decommission | Remove obsolete code | Deployment Agent |
| Inception | Explore problem, validate assumptions, go/no-go | Human / Any Agent |
Task Sizing Rules
- One task = one deliverable. If a task has multiple independent spikes or deliverables, decompose it.
- One bug = one task. Never compound multiple independent bugs into a single ticket. Each bug has its own root cause, fix, and regression test. Compounding destroys causality traceability and dilutes episodic memory.
- One inception = one question. An inception task should explore one problem and produce one go/no-go decision. "Umbrella inceptions" that bundle independent explorations create all-or-nothing decisions and coarse progress tracking.
- Target: fits in one session. If a task's time-box exceeds 4 hours or requires 3+ sessions, it should be split.
- Decomposition signal: 3+ spikes in an exploration plan, or 3+ independent problem domains, means the task is too big.
Enforcement Tiers
| Tier | Description | Bypass | Implementation |
|---|---|---|---|
| 0 | Consequential actions (force push, hard reset, rm -rf /, DROP TABLE) | Human approval via fw tier0 approve | PreToolUse hook on Bash (check-tier0.sh) |
| 1 | All standard operations (default) | Create task or escalate to Tier 2 | PreToolUse hook on Write/Edit (check-active-task.sh) |
| 2 | Human situational authorization | Single-use, mandatory logging | Partial (git --no-verify + bypass log) |
| 3 | Pre-approved categories (health checks, status queries, git-status) | Configured | Spec only |
Working with Tasks
When starting work (BEFORE reading code, editing files, or invoking skills):
- Check for existing task or create new one following
zzz-default.mdtemplate - Set status to
started-work - Set focus:
fw context focus T-XXX - THEN proceed with implementation (skills, code changes, etc.)
- Record decisions in Decisions section ONLY when choosing between alternatives
- Updates section is auto-populated at completion — manual entries optional
When encountering errors or unexpected behavior (NEVER silently work around them):
- STOP and investigate — do not switch to an alternative path without understanding WHY the error occurred
- Report the error and your investigation findings to the user
- If the error is in framework tooling: fix it (this is higher priority than the current task)
- If the error is environmental: document it and inform the user
- Only after investigation may you proceed with an alternative approach
- If the error seems minor but you cannot explain it: that is a signal, not noise — investigate anyway
When discovering structural flaws (bugs in framework tooling, spec-reality gaps):
- Register first, fix second. Add the flaw to
concerns.yamlBEFORE or alongside the fix - Gaps persist in the register (visible in Watchtower, checked by audit); completed tasks archive and become invisible
- Each independent bug gets its own task (see Task Sizing Rules: "One bug = one task")
When encountering task-level issues:
- Set status to
issues - Log error reference and healing loop suggestions
- Record resolution when fixed for pattern learning
When completing:
- Verify all acceptance criteria met
- If source files were changed: run
fw fabric blast-radius HEADto understand downstream impact - Record any design choices in the task's
## Decisionssection (auto-captured to context fabric on completion) - Set status to
work-completed - Framework auto-generates episodic summary and captures decisions for future reference
Context Integration
Tasks feed three memory types:
- Working Memory — Active task status and pending actions
- Project Memory — Patterns across all tasks (failure modes, effective approaches)
- Episodic Memory — Completed task histories for future reference
Error Escalation Ladder
Graduated response from tactical to structural:
- A — Don't repeat the same failure
- B — Improve technique
- C — Improve tooling
- D — Change ways of working
Proactive Level D: Operational Reflection
Not all improvement comes from failures. When you notice a practice repeating ad-hoc across 3+ tasks, consider codifying it:
- Mine episodic memory for evidence of the pattern (how often, what worked, what broke)
- Assess codification value — use inception go/no-go criteria
- Codify if warranted: protocol in CLAUDE.md, templates in agents/, guidelines
- Record as learning + decision + workflow pattern
Trigger: An organic question about "how we do X" + 3+ instances in episodic memory.
Canonical example: T-097 analyzed sub-agent dispatching across 96 tasks → discovered the real problem (result management, not agent specialization) → produced dispatch protocol (T-098) and prompt templates (T-099). The framework used its own episodic memory as the evidence base for an architectural decision.
Gap Homing (T-1333)
A gap belongs in the register where the FIX lives, not where it was HIT. When a concern's root cause is in another repo or project, file it there — filing it locally creates a zombie entry nobody who could fix it will ever read. If cross-cutting, cross-link, but home the entry where the fix lands. Worked example: G-045 (fleet cert co-rotation) — framework hit the symptom, TermLink owns the fix, so the register entry here points at TermLink's T-1054 rather than describing the fix ourselves.
fw CLI (Primary Interface)
fw is the single entry point for all framework operations — it resolves paths, sets env vars, and routes to agents. Discover commands via fw help, fw <cmd> --help, or the Quick Reference section below.
Path resolution: fw finds the framework via bin/fw's location (inside framework repo) or via .framework.yaml in the project root (shared tooling mode).
fw upgrade (canonical onboarding): Syncs governance to a consumer project — refreshes shims (bin/fw, .claude/settings.json hooks), updates the version pin in .framework.yaml, syncs vendored scripts. It does NOT copy framework source into the consumer — agents and libraries stay in the framework repo, accessed via the shim. Run from the framework repo (fw upgrade /path/to/project) or from inside the consumer (fw upgrade).
Agents
Agents are mechanical scripts in agents/<name>/ paired with an AGENT.md intelligence file. Invoke via fw (see Quick Reference) or directly via ./agents/<name>/<name>.sh. Most agents have a one-liner already catalogued in Quick Reference — this section only describes behavior and when to invoke.
- task-create — Before ANY new work.
fw work-on "name" --type buildis the one-step entry.fw task update --status issuesauto-triggers healing diagnosis;--status work-completedauto-finalizes (date_finished, move to completed/, generate episodic). - audit — Periodic compliance check. Exit codes: 0=pass, 1=warnings, 2=failures. Run after completing work or on suspected drift.
- session-capture — MANDATORY before ending any session or switching context. Review
agents/session-capture/AGENT.md: all work captured as tasks, all decisions recorded, all learnings stored, all open questions tracked. - git — Enforces task traceability (P-002). Every commit must reference
T-XXX.fw git install-hooksinstalls commit-msg, post-commit, pre-push. Use--no-verifyonly for emergencies and log via Tier 2. - handover — MANDATORY at end of every session.
fw handover --commitgenerates, commits, and pushes. Never end a session with unpushed commits. - context — Context Fabric management (working/project/episodic memory). Session init via
fw context init; focus viafw context focus T-XXX; capture viafw context add-learning|add-pattern|add-decision|generate-episodic. - healing — Triggered on
status: issues. Antifragile loop: classify failure → lookup matching patterns → suggest recovery via Error Escalation Ladder → log resolution for future learning. - resume — Post-compaction recovery.
fw resume statusfor full synthesis (handover + working memory + git + tasks);syncto fix stale working memory;quickfor one-line summary.
Component Fabric
The Component Fabric (.fabric/) is a structural topology map of every significant file — each component has a YAML card in .fabric/components/ with id, name, type, subsystem, location, purpose, interfaces, depends_on, depended_by.
When to use: before modifying a file → fw fabric deps <path>; before committing → fw fabric blast-radius [ref]; after creating a new file → fw fabric register <path>; periodic health → fw fabric drift (detects unregistered/orphaned/stale). Also: fw fabric overview for the subsystem summary, fw fabric impact <path> for the full downstream chain, fw fabric search <keyword> to find by tag/name/purpose.
Watchtower /fabric surfaces the subsystem overview, filterable component table, dependency graph, and per-component detail pages.
Context Budget Management (P-009)
Context is a finite, non-renewable resource within a session. Treat it like a battery gauge.
Commit Cadence Rule
- Commit after every meaningful unit of work (not just at session end)
- A "meaningful unit" = completing a subtask, finishing a file, or making a decision
- Each commit is a checkpoint: if context runs out, work up to the last commit is safe
- Target: at least one commit every 15-20 minutes of active work
Handover Timing Rule
- Generate handover AFTER work is done, not before
- Never generate a skeleton handover "to fill in later" — the session may not survive to fill it
- When generating handover: fill in ALL [TODO] sections immediately in the same operation
- For mid-session checkpoints:
fw handover --checkpoint
Agent Output Discipline
- When using Task/Agent tools, request concise output (summaries, not raw data)
- See Sub-Agent Dispatch Protocol below for detailed rules on managing sub-agent results
- Prefer
fw resume quickoverfw resume statusfor routine checks - Prefer
git log --oneline -5overgit log -5
Work Proposal Rule
- Before proposing the next unit of work, check context budget (
checkpoint.sh status) - Below 75% (<225K tokens): proceed normally
- 75-85% (225K-255K): propose only small, bounded tasks; commit first
- Above 85% (255K+): propose only wrap-up actions (commit, learnings, handover)
- Above 95% (285K+): handover immediately, no new work
- This applies especially in autonomous mode — without a human to catch the mistake, proposing work that can't complete in remaining context risks losing all uncommitted work
Automated Monitoring and Critical Protocol
- Primary enforcement: PreToolUse
budget-gate.shreads actual token usage from the session JSONL and blocks Write/Edit/Bash at critical (exit code 2). Fallback: PostToolUsecheckpoint.sh(warnings + auto-handover, T-136). - Escalation ladder: 225K ok→warn, 255K warn→urgent, 285K urgent→critical (BLOCK). Context window 300K default (
FW_CONTEXT_WINDOW). - At critical (or if you see a SESSION WRAPPING UP block): only wrap-up is allowed. Allowed: git commit/add,
fw handover,fw task update, reading files, Write/Edit to.context/.tasks/.claude/. Blocked: Write/Edit to source files, general Bash. - Status cached in
.context/working/.budget-status(JSON: level, tokens, timestamp). Check via./agents/context/checkpoint.sh status. If no transcript available, fails open (PostToolUse fallback handles it). - Wrap up calmly — task files already carry all essential state from continuous capture.
Configuration (T-819, T-891)
4-tier resolution: explicit CLI flag > FW_* env var > .framework.yaml > hardcoded default. Persistent per-project config: fw config set KEY VALUE writes to .framework.yaml.
Agent-relevant settings:
FW_CONTEXT_WINDOW(300000) — budget enforcement ceilingFW_PORT(3000) — Watchtower listen port (also resolved via triple-file; see Watchtower Port section)FW_SAFE_MODE(0) — bypass task gate (escape hatch)FW_DISPATCH_LIMIT(2) — Agent tool cap before TermLink gateFW_NTFY_URL/ configNTFY_URL(empty) — T-2439: ntfy server base URL for push notifications. Empty = let the skills-manager dispatcher use its own (possibly host-local) default. Set it per-install (fw config set NTFY_URL <url>) so the framework publishes to the chosen ntfy instance and can never silently fall back to a host's local server — the dispatcher runs locally on whichever host callsfw_notify, and inferring the target from another host's config shipped pushes to a decommissioned server (origin).fw notify status/fw notify testprint the resolved server up front. Surfaces in Watchtower/config.FW_BRANCH_BEHIND_WARN(50) — T-100143: branch-hygiene behind-threshold.fw doctorWARNs on live branches more than N commits behind origin/master (shared with the T-100144 handover nudge).FW_STALE_ARC_DAYS(30) — T-1855: stale-arc audit WARN threshold. In-progress arcs whose constituent tasks (matched byarc_id:) have no commit in the last N days surface a WARN. Silent on draft/closed/abandoned arcs and on zero-population arcs.FW_ALLOW_ARC_ID_DRIFT(0) — T-1849: single-use bypass for thearc_id:validation hook when the field doesn't resolve to an existing arc YAML. Logged Tier-2 to.context/working/.gate-bypass-log.yaml.FW_RETIRE_WHEN_ADVISORY(1) — T-2169: audit retire_when advisory rail. For each ACTIVEfree_drivers[]entry inpolicy/value-drivers.yamlwithretire_when:text, audit runs a per-driver recognition heuristic against the corpus and emits a WARN when the condition appears recognisably met (F-RECALL: 4/4 signals; F-ORCH: T-1643 cleanly in completed/ OR G-064 closed). Drivers withretire_when:text but no dedicated heuristic emit an INFO (not WARN). WARN-only, never blocks. Set to0to silence the section entirely.
Full reference (handover timeouts, bash timeout, stale-task days, call-level fallbacks, inception commit limit, etc.): fw config list, env | grep FW_, or Watchtower /config. fw doctor warns on out-of-range values.
Sub-Agent Dispatch Protocol
When using Claude Code's Task tool to dispatch sub-agents (Explore, Plan, Code, etc.), follow these rules. Based on evidence from 96 tasks where 8 used sub-agents.
Result Management Rules
Content generators (enrichment, file creation, report writing):
- Sub-agent MUST write output to disk (Write tool), NOT return full content
- Return only: file path + one-line summary (e.g., "Wrote .context/episodic/T-073.yaml — enriched from skeleton")
- This prevents context explosion (T-073: 9 agents returning full YAML spiked context by 30K+ tokens)
Investigators/researchers (codebase exploration, root cause analysis):
- Return structured summaries with findings, NOT raw file contents
- Format: numbered findings with file:line references
- Keep return under 2K tokens per agent
Auditors/reviewers (compliance checks, code review):
- Write detailed report to file if >1K tokens
- Return summary + file path to orchestrator
- Include pass/warn/fail counts in summary
Dispatch Guidelines
| Factor | Rule |
|---|---|
| Max parallel agents | 5 (T-073 used 9 → context explosion; T-061 used 4, T-086 used 5 → fine) |
| Token headroom | Leave 40K tokens free for result ingestion before dispatching |
| When parallel | Tasks are independent, no shared files, no sequential dependency |
| When sequential | Tasks depend on prior results, or editing same files |
| Background agents | Use run_in_background: true for agents >2K tokens expected output |
Task Tool vs TermLink Dispatch
The Task tool and TermLink dispatch are two different mechanisms for parallel work. Choose based on the work type:
| Factor | Task tool agent | TermLink dispatch (fw termlink dispatch) |
|---|---|---|
| Edit/Write tools | Yes (sub-agent) | Yes (spawns full claude -p worker) |
| Context isolation | No (shares parent context window) | Yes (independent process, zero context cost) |
| Max parallel | 5 (hard limit) | Unlimited (real OS processes) |
| Observable from outside | No | Yes (attach, stream, output) |
| Survives context pressure | No (compressed with parent) | Yes (independent session) |
Use Task tool agents when:
- Quick research or codebase exploration (<2K token results)
- Single-file edits within the current session
- Lightweight sub-tasks that benefit from shared context
Use TermLink dispatch when:
- Heavy parallel work (>3 agents, or agents doing multi-file edits)
- E2E testing or command execution requiring terminal isolation
- Work that should survive parent context compaction
- Tasks where you want to observe progress remotely
The rule: If you're about to dispatch 3+ Task tool agents that will each produce >1K tokens or edit files, use TermLink dispatch instead. The context savings are significant — Task agents share the parent's context budget, TermLink workers do not.
Evidence: T-522 session — 3 evaluation agents dispatched via Task tool consumed ~25K parent context tokens. Same work via TermLink dispatch would cost zero parent context.
TermLink output rule (T-818): TermLink workers MUST write directly to target files in the repo (e.g., docs/reports/T-XXX-analysis.md), NOT to /tmp/fw-agent-*. If the parent session hits budget critical before integrating /tmp/ results, outputs are lost. The /tmp/ convention is for Task tool agents only (consumed immediately). See agents/dispatch/preamble.md for full instructions.
Prompt Template Structure
When dispatching sub-agents, include in the prompt:
- Scope: Exactly what to investigate/produce (one clear deliverable)
- Framework context: Relevant framework structure (task format, episodic template, etc.)
- Output format: How to return results (write to file vs. return summary)
- Constraints: Don't modify files outside scope, don't return raw data
- Token hint: "Keep your response concise — the orchestrator has limited context budget"
Result Ledger (fw bus)
The result ledger formalizes the "write to disk, return path + summary" convention into a protocol with typed YAML envelopes and automatic size gating. Use it for sub-agent dispatch:
# Sub-agent posts result (instead of returning full content)
fw bus post --task T-XXX --agent explore --summary "Found 3 issues" --result "inline data"
fw bus post --task T-XXX --agent code --summary "Wrote file" --blob /path/to/output
# Orchestrator reads manifest (5 lines instead of 25KB)
fw bus manifest T-XXX
# Orchestrator reads specific result if needed
fw bus read T-XXX R-001
# Cleanup after task completion
fw bus clear T-XXX
Size gating: Payloads < 2KB are inline. Payloads >= 2KB are auto-moved to .context/bus/blobs/ and referenced. This prevents T-073-class context explosions (~97% context savings in simulation).
Cross-Machine Dispatch (fw dispatch)
For cross-machine communication without TermLink, use SSH-based dispatch:
# Send result to remote machine
fw dispatch send --host dev-server --task T-XXX --agent explore --summary "Found issues"
# Or use --remote flag with bus post
fw bus post --remote dev-server --task T-XXX --agent explore --summary "Found issues"
# List available SSH hosts
fw dispatch hosts
Requirements:
- SSH access to remote host (configured in
~/.ssh/config) - Agentic Framework installed on remote host
- Remote user has write access to
.context/bus/
How it works: The local machine serializes the bus envelope as JSON and pipes it via SSH to fw bus receive on the remote machine, which stores it in the remote bus channel.
Dispatch Patterns
- Parallel investigation / audit / enrichment: 3-5 Task agents scan independent aspects; each writes findings to disk, returns path + summary. Cap at 5 parallel. Use
fw bus postfor formal tracking. - Sequential TDD: Fresh agent per implementation task with review between.
- TermLink parallel workers: Spawn TermLink sessions for isolated heavy work.
termlink interact --jsonfor sync commands,termlink pty inject/outputfor interactive control. Cleanup withtermlink signal SIGTERM+termlink clean. Preferred over Task agents when context isolation matters.
Agent Behavioral Rules
These rules govern agent behavior during work. They are structural expectations, not suggestions.
Choice Presentation
Always present choices as a numbered or lettered list so the user can reply with just the identifier (e.g., "1" or "b"). Never present options as prose paragraphs.
Autonomous Mode Boundaries
When the human says "proceed as you see fit", "go ahead", "do what you think is best", or similar broad directives, this delegates initiative (choosing what to work on), NOT authority (approving, completing, or bypassing). Specifically:
Delegated (agent may do autonomously):
- Choose which task to work on next
- Choose implementation approach within a task
- Run verification, tests, audits
- Commit completed work and report back
NOT delegated (requires explicit human approval per action):
- Completing human-owned tasks (
owner: human) - Using
--forceto bypass any gate (sovereignty, AC, verification) - Suggesting
--forceor batch-completion of human-owned tasks without first listing each task's unchecked Human ACs - Changing task ownership away from human
- Destructive actions (Tier 0)
- Any action the sovereignty gate or structural enforcement blocks
The rule: If a structural gate blocks you, that gate exists precisely for moments like this. A broad directive does not override structural enforcement. Stop and ask.
Pickup Message Handling (G-020, T-469)
Pickup messages from other sessions are PROPOSALS, not build instructions. A detailed spec with file lists and implementation steps is a suggestion, not authorization.
Before acting on a pickup message:
- Assess scope — if it describes >3 new files, a new subsystem, a new CLI route, or a new Watchtower page, create an inception task (not build)
- Write real ACs before editing any source file — the build readiness gate (G-020) will block tasks with placeholder ACs
- Never treat detailed specs as authorization to skip scoping — the more detailed a pickup message is, the more likely it needs inception, not less
Why this rule exists: A pickup message from session 010-termlink caused complete governance bypass. The agent created a build task with placeholder ACs and immediately started editing bin/fw and building a Watchtower page. Human intervened 3 times. Root cause: imperative tone ("Create a PR", "Files to Include") caused the agent to internalize the spec as a build instruction rather than a proposal requiring scoping.
Human Task Completion Rule (T-372, T-373)
Human ACs represent real verification steps. Unvalidated deliverables carry downstream risk. A clean task list is not progress — validated deliverables are progress.
You MAY suggest closing a human-owned task IF you provide evidence that the Human ACs are already satisfied:
- Cite specific evidence (file exists, endpoint responds, output matches expected, config is in place)
- Explain why no further human action is needed
- Example: "T-371 hook is registered in settings.json:100-108 and running (tool counter = 3). Evidence suggests the AC is met — want to close it?"
You MAY suggest the human prioritize verification when there's a reason:
- "T-289 pipeline setup is blocking deployment. Could you test it? Here are the steps..."
- "T-366 articles are building on each other — quality review now catches tone issues before they compound."
You MUST NOT suggest closing without evidence:
- No "batch-close stale tasks" — each task needs individual evidence
- No "just use
--force" — that skips the verification the AC exists to perform - No treating Human ACs as administrative overhead — they catch real problems
Use fw task verify to see what Human ACs are unchecked before suggesting anything.
The test: "Can I cite specific evidence that this task's Human ACs are satisfied?" If yes, suggest closing with that evidence. If no, either help the human execute the verification steps, or move on.
Commit Cadence and Check-In
After every commit, briefly report what was done and ask if the user wants to continue. Do not chain multiple commits without user interaction.
Structural enforcement (T-139, T-478, T-596): The budget-gate.sh PreToolUse hook reads actual token usage from the session transcript and blocks Write/Edit/Bash tool calls when context reaches critical level (>=285K tokens, ~95% of 300K window). At critical, only git commit, fw handover, and read operations are allowed. The hook writes .context/working/.budget-status with current level (ok/warn/urgent/critical) for fast caching. PostToolUse checkpoint.sh remains as fallback for warnings and auto-handover. Context window size is configurable via FW_CONTEXT_WINDOW env var (default: 300K).
Copy-Pasteable Commands (T-609, T-1257)
When giving the human a command to run (Tier 0 approvals, inception decisions, verification steps, Human AC instructions), the command MUST be:
- Single-line, copy-pasteable — works when pasted into any terminal, from any directory
- Prefixed with
cd— always includecd /path/to/project &&so directory context is explicit - Use the right
fwpath for the project's context (T-1257):- Framework repo (contains
FRAMEWORK.md+bin/fwat its root — e.g./opt/999-Agentic-Engineering-Framework): usebin/fw - Consumer project (contains
.agentic-framework/bin/fw— any project initialised viafw init): use.agentic-framework/bin/fw - Bare
fwonly when you know the shim at~/.local/bin/fwis installed and routing works — otherwise it may resolve to a stale global install
- Framework repo (contains
- Verify before suggesting — before pasting a
fwcommand into chat, confirm the chosen path exists in the target project (check forbin/fwOR.agentic-framework/bin/fw). Don't apply the framework-repo pattern to a consumer. - No bare multi-line — if multiple commands are needed, chain with
&&on one line. Never rely on the human copy-pasting multiple separate lines (line breaks cause partial execution errors)
Good — framework repo:
cd /opt/999-Agentic-Engineering-Framework && bin/fw tier0 approve && bin/fw inception decide T-608 go --rationale "approved"
Good — consumer project:
cd /003-NTB-ATC-Plugin && .agentic-framework/bin/fw inception decide T-006 go --rationale "approved"
Bad — applies framework-repo pattern to a consumer project:
cd /003-NTB-ATC-Plugin && bin/fw inception decide T-006 go
# → bash: bin/fw: No such file or directory (consumer has NO bin/ at root)
Bad — bare, no cd:
fw tier0 approve
fw inception decide T-608 go --rationale "approved"
Why this exists:
- T-609: User ran
fw inception decidefrom/home/dimitri-mint-dev/— got "No framework project detected". Three errors from one missingcd. - T-1257: Agent on
/003-NTB-ATC-Plugin(consumer) told user to runbin/fw— consumer has nobin/at root, only.agentic-framework/bin/fw. The generic rule "usebin/fw" was correct for the framework repo but wrong for consumers.
Inception Discipline
When the active task has workflow_type: inception:
- State the phase — Say "This is an inception/exploration task" before doing any work
- Present the filled template for review before executing any spikes or prototypes
- Do not write build artifacts (production code, full apps) before
fw inception decide T-XXX go - The commit-msg hook enforces this — after 2 exploration commits, further commits are blocked until a decision is recorded
- After a GO decision, create separate build tasks for implementation — do not continue building under the inception task ID
- Research artifact first (C-001) — When starting inception work, create
docs/reports/T-XXX-*.mdBEFORE conducting research. Update the file incrementally as dialogue produces findings. Commit after each dialogue segment. The thinking trail IS the artifact — conversations are ephemeral, files are permanent. (Origin: T-194 — 7 existing controls for research persistence all failed because none enforced capture at the point research happens.) - Dialogue log (C-001 extension) — For phases involving human dialogue, include a
## Dialogue Logsection in the research artifact. Record: questions the human posed, answers given, course corrections ("we are not doing X, we are doing Y"), and the outcome/decision that resulted. Structured findings capture WHAT was decided; the dialogue log captures WHY and HOW the reasoning evolved. (Origin: T-194 experiment — 95% of findings captured but conversational reasoning only captured when explicitly logged in Phase 2a.) - Exploratory Conversation Guard (C-002) — If a substantive conversation on an untracked topic reaches 3+ exchanges without an active task, STOP and:
- Create an inception task for the topic
- Invoke
/captureto save the prior dialogue to disk - Continue the conversation under the new task Trigger: 3 substantive exchanges (exclude greetings, one-word replies, status checks). Enforcement: agent self-governs; no hook coverage (G-005).
Web App Startup
When building a web application:
- Check port availability before starting (
ss -tlnp | grep :PORT) - Start the app and report the URL to the user
- Report access options — localhost, LAN IP (for other devices), internet (if applicable)
- Never leave a built web app unstarted without informing the user
Constraint Discovery
For tasks involving hardware APIs (microphone, camera, GPS, Bluetooth):
- Research platform constraints first before building (e.g., getUserMedia requires HTTPS or localhost)
- List constraints in the exploration plan before writing code
- Test the API access path in a minimal spike before building the full app
Agent/Human AC Split (T-193)
Tasks may have ### Agent and ### Human sections under ## Acceptance Criteria:
- Agent ACs: Criteria the agent can verify (code, tests, commands). P-010 gates on these.
- Human ACs: Criteria requiring human verification (UI behavior, subjective quality). Not blocking.
- NEVER check a
### HumanAC. Only the human may verify and check these boxes. - When agent ACs pass but human ACs remain unchecked, the task enters partial-complete: stays in
active/withowner: human. - The human finalizes by checking their ACs and running
fw task update T-XXX --status work-completed.
AC Classification Guidance (T-954)
When writing acceptance criteria, use this risk matrix to decide Human vs Agent:
Make it a Human AC if ANY apply:
- Strategic authority — go/no-go decisions, architecture choices, priority calls
- Subjective judgment — quality, tone, UX feel, "is this good enough?"
- Irreversible external action — publishing, deploying to production, sending communications
- Cross-project blast radius — changes affecting multiple consumer projects or external users
- Touches a rendering surface —
web/templates/,web/static/(CSS/JS),web/blueprints/,web/shared.py,web/app.py. Visual layout/typography/spacing cannot be settled by curl/grep; the render-surface gate (T-1766, P-013) refuses--status work-completedon a render-touching build task without at least one[REVIEW]Human AC. Bypass:--skip-render-review "rationale"(logged Tier-2). Origin: T-1763/T-1764/T-1765 — three render fixes shipped with zero Human ACs. - Subject of the judgment is human experience — the AC's question is asked about the operator's perception (does the layout read clean for the human, does the docs section flow right for the operator, does the handover URL make sense to someone reading it cold). If the AC's audience is agents (stderr prose targeted at agents, internal CLI output read by agents, framework gate wording that agents trip and re-read) — route to
### Agent(self-eval) instead, regardless of how subjective the judgment sounds. Test: read the AC's wording and ask "who is the answer for?" If "the operator" — Human AC. If "the agent that trips this gate" / "the agent that reads this output" — Agent AC. Origin: T-2143 RCA (T-2139's V1 keystone gate-message recursed 4 rounds because tone-of-stderr was routed[REVIEW]even though the audience was agents, not the operator).
Make it an Agent AC (with verification command) if ALL apply:
- Deterministic outcome — binary pass/fail with clear expected result
- Reversible — can be undone if wrong (git revert, config change)
- Internal scope — affects only development tooling, not external users
- Mechanical execution — no judgment needed, just "run X, check Y"
When in doubt, make it Human — false negatives (missing a broken thing) are worse than false positives (asking the human unnecessarily).
RUBBER-STAMP conversion rule: If a Human AC has [RUBBER-STAMP] prefix and its Steps section contains only deterministic shell commands with clear expected output, it SHOULD be an Agent AC with verification commands in ## Verification instead. The machine is more reliable than a human for pass/fail checks.
Author-time default (T-1878): When you are writing a Human AC and your Expected clause is grep-able / file-exists / structural (deterministic shell check), default to [REVIEWER] and move the AC to ### Agent with the reviewer command in ## Verification. Only keep [REVIEW] if verification genuinely needs human taste (tone, layout rhythm, blast-radius judgment). T-1878 found a 412:7 [REVIEW]:[REVIEWER] adoption gap and 13% mis-classification rate on partial-completes — the prefix existed, the author-time nudge didn't. T-1894 manually re-classed 4 such ACs across arc-grooming; T-1896 (the static-scan catch) closes the gap structurally for the next round. Reasoning: docs/reports/T-1878-routing-default-bias.md.
Three Human-AC prefixes (T-1811): The classification above is binary at the Agent/Human level, but Human ACs themselves have three sub-classes by who can verify them:
| Prefix | When to use | Verifier |
|---|---|---|
[RUBBER-STAMP] | Mechanical step with deterministic shell-command verification | Shell (Tier 1/2/3) — convert to Agent AC + ## Verification |
[REVIEWER] | Pattern / wording / convention check that the static-scan reviewer agent handles (anti-pattern detection, block-message conformance, naming consistency) | fw reviewer T-XXX — convert to Agent AC + Verification command bin/fw reviewer T-XXX 2>&1 | grep -q "Overall:.*PASS" |
[REVIEW] | Genuine human judgment — quality, tone, UX feel, strategic call, blast-radius assessment | Human only |
Audience axis (T-2143): [REVIEW] and [REVIEWER] both presume the operator is the verifier. If the AC subject is agent experience — the tone of stderr prose agents read, the wording of an internal CLI output, the rhythm of a framework gate message that agents trip and re-read — the audience is wrong for any Human prefix. Route to ### Agent (self-eval) instead. Author-time test before reaching for a Human prefix: "If I read this AC as an operator I have never seen the system before, am I the one who can answer it?" If the answer requires having tripped an agent gate / read framework stderr / been mid-handoff — that's the agent's job, not the operator's. Origin: T-2143 RCA (T-2139's V1 keystone task recursed the same Human AC four rounds because the agent's single-axis routing heuristic — is the answer subjective? → Human — has no audience check; subjective + agent-audience routes wrong every time).
REVIEWER conversion rule (T-1811): If a Human AC has [REVIEW] prefix but its check would be satisfied by fw reviewer T-XXX returning PASS + needs_human=no, it SHOULD be re-classified as [REVIEWER] and converted to an Agent AC with the reviewer command in ## Verification. The reviewer agent (T-1443) is a static-scan surface — pattern conformance, anti-pattern detection, block-message presence — that complements but does not replace [REVIEW] for taste/judgment calls.
Test: "Could a deterministic static scan of the task file (or referenced source) answer the AC's yes/no?" If yes → [REVIEWER]. If no → [REVIEW].
[REVIEWER] does not cover prose quality (T-1947, L-409): The reviewer agent checks structural patterns only — it does not read prose for tone, clarity, or rhythm. If your AC asks "does this read clearly?" or "is the tone right?", use [REVIEW], not [REVIEWER]. Otherwise the scanner skips your AC silently and still emits a verdict on the task's other ACs — which looks identical to success.
Signal vocabulary (any of these in the Expected clause means [REVIEW], not [REVIEWER]): reads clearly, tone, voice, rhythm, intuitive, feels right, cohesive, cleanly, unambiguous, actionable, layout reads clean.
The detector reviewer-prose-mismatch (T-1947) flags [REVIEWER] ACs that contain this vocabulary and emits CONCERN at task close.
Worked example (origin: T-1811): The Human AC "Confirm focus-drift block message is actionable" on T-1730 was filed as [REVIEW]. The Reviewer agent's static scan (fw reviewer T-1730) returned CONCERN on mock-only-integration — a finding the AC text would never have caught. The AC should have been [REVIEWER] with fw reviewer T-1730 in Verification, plus a separate [REVIEW] AC only if the wording's feel genuinely needed human judgment.
Worked example — audience mismatch (origin: T-2143 / T-2139 round 4): T-2139 (the V1 keystone blocking gate) recursed the same Human AC across 4 author rounds. Round 4 reached:
- [ ] [REVIEW] Block-message stderr reads cleanly to a tripping agent(paraphrased)Steps: Trigger the gate. Read stderr. Verify wording reads clear, actionable, names the bypass mechanisms. Expected: stderr makes the agent unblock itself without operator help.
The agent reflexively reached for [REVIEW] because the question (is the wording good?) is subjective. The static-scan T-1947 prose-mismatch detector would have agreed — its job is to push prose-tone questions toward [REVIEW]. But the audience disqualifies any Human prefix at all — the operator has no operational context for what reading framework stderr feels like mid-handoff. The correct routing was ### Agent (agent self-eval against the prose) — not any Human prefix. The fix (T-2143 leg A) deleted the AC entirely, since the gate's effectiveness is verifiable by integration test, not by reading stderr.
Routing-discipline ladder (T-2143): Four rules now compose into the author-time discipline:
- T-1878 — default-bias for grep-able Expected →
[REVIEWER]not[REVIEW] - T-1947 — prose vocabulary in Expected (reads clearly, tone, voice, rhythm) →
[REVIEW]not[REVIEWER] - T-2143 (this rule) — agent-audience subject →
### Agentnot any Human prefix - T-2147 — reviewer detector flags
[REVIEW]/[REVIEWER]ACs whose subject is agent experience
Read together: T-1878 routes between Human prefixes by check-shape; T-1947 routes between [REVIEW] and [REVIEWER] by Expected vocabulary; T-2143 routes out of Human prefixes entirely when the audience is agents; T-2147 catches author-time misses. The first three are author-time; T-2147 is reviewer-time backstop.
Reviewer auto-tick (v1.5, T-1985): [REVIEWER]-prefixed Agent ACs are automatically ticked by the reviewer scanner when all five evidence conditions hold: (1) overall verdict PASS, (2) zero per-AC findings for that AC's index, (3) AC is currently unticked, (4) no active suppress override targets that AC index, (5) [REVIEWER] prefix present. The tick and verdict block are written in a single atomic os.replace pass. Sovereignty rail: every auto-tick writes a digest-keyed entry auto_tick:<task_id>:<ac_index>:<digest> to .context/working/feedback-stream.yaml. If a human manually un-ticks the AC (changes [x] back to [ ]), the feedback-stream entry blocks re-ticking — the reviewer will NOT re-tick it unless the AC text itself changes (new digest = fresh consent). Note: wildcard pattern overrides (ac_index=None) suppress findings for a pattern but do NOT block auto-tick of unrelated ACs. Auto-tick only runs on active/ tasks; completed/ files are never mutated.
Verification tiers for Agent ACs:
- Tier 1 (programmatic): Shell commands, curl, grep, file checks — for deterministic, reversible checks
- Tier 2 (TermLink E2E): Spawn process, inject commands, check output — for integration/CLI workflows
- Tier 3 (Playwright): Browser automation via
tests/playwright/— for interactive UI verification. Run withfw test playwright.
UI AC split: When verifying UI features, split into two criteria:
- Agent AC for functional check: "page returns HTTP 200 and contains key element" (Tier 1)
- Human AC only if aesthetic judgment is genuinely needed: "layout is clean and intuitive" (
[REVIEW])
Playwright test generation rule (T-971): When writing an Agent AC for a UI feature (Tier 3), also write or update a Playwright test in tests/playwright/. The AC verifies the feature once; the test guards it forever. Pattern:
| AC pattern | Test pattern |
|---|---|
curl -sf URL → 200 | page.goto(url) + assert resp.status == 200 |
curl URL | grep -q "X" | page.goto(url) + assert "X" in page.content() |
| "page shows element X" | expect(page.locator("X")).to_be_visible() |
| "click X, verify Y appears" | page.click("X") + expect(page.locator("Y")).to_be_visible() |
[RUBBER-STAMP] with deterministic steps | Convert to Agent AC + Playwright test |
[REVIEWER] with reviewer-verifiable pattern | Convert to Agent AC + fw reviewer T-XXX in Verification |
[REVIEW] with subjective judgment | Keep as Human AC (not automatable) |
Human AC Format Requirements (T-325)
When writing ### Human acceptance criteria, each criterion MUST include:
- Steps: block with numbered, copy-pasteable instructions (no placeholders the human must figure out)
- Expected: what success looks like (exact text, status code, or observable outcome)
- If not: diagnostic steps or fallback action
Optionally prefix the criterion with a confidence marker:
[RUBBER-STAMP]— mechanical action, no judgment needed (publish, deploy, click) — convert to Agent AC +## Verification[REVIEWER]— static-scan-verifiable (pattern / wording / convention) — convert to Agent AC +bin/fw reviewer T-XXXin## Verification(T-1811, T-1878)[REVIEW]— genuine human judgment required (tone, UX, architecture decisions, blast-radius)
Example: - [ ] [REVIEW] Voice/tone matches writing style with Steps: (read N paragraphs, compare to reference, check anti-patterns), Expected: (reads like peer discussion, not product pitch), If not: (note paragraphs for revision).
Prerequisite awareness (T-358): Steps must start from the human's actual environment, not the agent's dev context. If the feature requires deployment, upgrade, or setup before testing (e.g., brew upgrade fw, restart a service, push config), include those steps first. Ask: "What must the human do before step 1 is possible?" If the answer isn't "nothing," add prerequisite steps.
Full-path commands (T-609, T-1257): Every command in Steps MUST be copy-pasteable from any directory. Include cd /path/to/project && prefix. Use the project-appropriate fw path: bin/fw in the framework repo, .agentic-framework/bin/fw in consumer projects, bare fw only when the shim is confirmed. Chain multiple commands with && on one line — never assume the human will paste lines separately. See §Copy-Pasteable Commands.
If a human AC cannot be made specific (e.g., "code quality is acceptable"), replace it with a measurable proxy or remove it. Vague ACs that nobody acts on are worse than no AC.
Rendered-content guarantees (T-1575): the framework's Markdown renderer (web/blueprints/tasks.py:_render_md_inline / _render_md_block, web/shared.py:render_markdown_safe) emits clickable anchors for every URL — bare, in [md](url) syntax, OR wrapped in \backticks`. Rule: **any URL appearing in rendered task content (AC Steps, Expected, If-not, Recommendation, Rationale, Evidence) is clickable, regardless of how the agent wrote it.** This is the rendering contract — agent need not remember to avoid backticks around URLs. Pinned by tests/unit/test_extract_recommendation.py:test_render_markdown_safe_makes_backticked_urls_clickable. Same guarantee applies to bare T-NNNNreferences →/tasks/T-NNNN`.
Verification Before Completion
Before setting any task to work-completed:
- Run all commands in the task's
## Verificationsection - Check every
### Agentacceptance criterion checkbox (or all ACs if no split headers) - If tests exist for the changed code, run them
- Report results to user with pass/fail evidence
- Do NOT call
fw task update --status work-completeduntil all agent ACs pass - The verification gate (P-011) enforces this structurally — this rule makes you check BEFORE hitting the gate
Progressive AC ticking (T-1831 C-4): Tick each Agent AC checkbox - [ ] → - [x] as soon as the corresponding content/work is in place, NOT after-the-fact when the gate fires. The completion gate (P-010) and the inception-decide preflight count [x] markers — they cannot read body content. Writing the RCA/candidates/recommendation in the body does NOT tick the boxes; you have to do it explicitly. After-the-fact ticking (only after the gate refuses) is the exact pattern the gate exists to prevent — and the user has caught it. Treat each AC as a milestone: when you complete the work for AC #N, your very next edit ticks that box. Origin: S-2026-0514 errors 1-3 in fw-upgrade-incident-2026-05-14 cluster — same antifragility class as T-1828 (gate measures proxy that diverged from reality).
Consumer-Facing Command Hygiene (T-1633, T-1635)
Any change to fw upgrade, fw vendor, or fw init (the three consumer-facing setup commands) MUST keep tests/unit/upgrade_fresh_machine_simulation.bats green. The simulation builds a synthetic consumer (vendored .agentic-framework/ + file:// upstream) and runs the consumer's vendored fw under env -i with minimal PATH — catching the class where a command silently depends on the framework developer's local filesystem layout.
Why this rule exists: T-1633 origin — fw upgrade worked on the framework developer's /opt/999-Agentic-Engineering-Framework but crashed on every consumer in the wild because path knowledge was implicitly hard-coded. Every consumer-facing flow must run from a clean environment with no developer artifacts; the bats simulation is the cheapest enforcement (no docker dependency, runs in any CI/dev environment). A docker-container variant remains a release-gate follow-up for higher-confidence "true fresh machine" coverage.
Hook Bypass Contract Parity (T-1890, L-399)
When a PreToolUse hook introduces a bypass contract — e.g. "append --switch-focus" or "prefix FW_X=1" — the downstream consumers of every command pattern the hook gates must honour that contract. A hook that recommends a flag whose downstream parser rejects it as "Unknown option" is a silent governance failure: the agent's workaround (direct-invoke, env-strip, or a different command shape) escapes the regex and bypasses the gate with no audit trail.
Authoring rule — when adding a bypass mechanism to a hook:
- Identify every command pattern the hook gates. Each pattern routes to a different downstream consumer (a fw sub-script, an external tool like
git, etc.). - For each consumer under our control (fw sub-scripts), add a silent no-op branch that consumes the flag without rejection.
- For consumers NOT under our control (
git commit, third-party CLIs) — flags fundamentally cannot work, because the external parser rejects unknown options. Use an env-var prefix mechanism (FW_X=1 <command>) which is invisible to downstream parsers. - Ship both mechanisms when the hook gates a mix of internal + external patterns (the focus-drift gate is the canonical case: fw commands work with the flag,
git commitonly with the env var). - Pin the contract with an end-to-end bats test that exercises hook-allow → consumer-accepts → log-entry-written, per mechanism, per gated pattern. Unit-testing the hook in isolation is not sufficient; the bug lives at the join, not in either side alone.
- Block message must name every mechanism with one-line guidance on when to pick which, so the agent doesn't fall back to a workaround.
Why this rule exists: T-1890 origin — T-1730 shipped the --switch-focus bypass contract for the focus-drift gate, but update-task.sh, lib/{learning,pattern,decision}.sh, and git commit all rejected the flag. The agent worked around via direct-invoke bash agents/task-create/update-task.sh — a path the hook's regex didn't match. Result: the gate was silently circumvented for ~3 weeks across multiple sessions, with the bypass log showing entries for command lines that never actually completed (the flag-rejection happened after the hook logged). Producer/consumer split: the contract shipped on one side only. See L-399 for the broader class.
The test: when adding a bypass mechanism, ask: "Can I reach the post-mechanism path end-to-end in a real session?" If the answer is "only after I patch four other files and add the env-var fallback", do all of that in the same task. If you can't ship end-to-end in one commit, the contract is incomplete.
Presenting Work for Human Review (T-679)
When agent ACs are complete and human ACs remain:
-
Write your recommendation into the task file — Add a
## Recommendationsection (Watchtower reads this) with:- Recommendation: GO / NO-GO / DEFER
- Rationale: Why (cite evidence: what was fixed, what was proven, what remains)
- Evidence: Bullet list of concrete proof (test results, file paths, metrics) You are the advisory. The human is the decision-maker. Never present a blank decision for them to fill in — always tell them what you recommend and why.
-
Present via the standardized review command:
fw task review T-XXXThis emits the Watchtower URL, QR code, and links to research artifacts. NEVER give raw CLI commands (
fw inception decide,fw task update --status work-completed --force) for human approvals.
DEFER is for evidence gaps, NOT for confidence gaps (T-2144, T-2145, T-2146): Use DEFER only when you genuinely don't yet have evidence to recommend GO or NO-GO — e.g. a spike is needed, a dependency is unresolved, an external party must respond. Do NOT use DEFER as a hedge when your research artifact is already complete (5-Whys done, candidates analysed, dialogue logged). If you have walked the evidence and still don't want to commit, that's a confidence-calibration failure, not a knowledge gap — recommend GO or NO-GO with the rationale you actually have. The operator needs your advisory weight, not a placeholder. Reviewer-time backstop: lib/reviewer/static_scan.py detector defer-as-hedge (T-2145) fires CONCERN on inception tasks where Recommendation: DEFER co-occurs with ≥2 evidence indicators (5-Whys, Dialogue Log, candidate matrix) and a substantive rationale (>300 chars). At-write-time, ask yourself: if a peer reviewer walked the artifact, would they agree the evidence is incomplete? If not, DEFER is wrong.
Worked example (origin: T-2143, RCA in T-2144): T-2143 was filed with Recommendation: DEFER on the meta-RCA of routing failure. The artifact contained a complete 5-Whys, a multi-round dialogue log, and a candidate matrix with effort estimates. Operator pushed back with one question: "why do you reccomed defer ??" — direct hit. The agent had the evidence to recommend GO — Candidate D; DEFER was a hedge against confidence-calibration in the thread, not a knowledge gap. Self-correction: T-2143 was re-filed GO — Candidate D with explicit reframe note; T-2144 RCA captured the class; T-2145 shipped the static-scan rail; this paragraph is the author-time teach.
Why this rule exists: The agent defaults to (a) pasting CLI commands instead of using Watchtower, and (b) leaving the rationale blank for the human to figure out. Both were corrected 3 times in the same session (T-679). The agent has all the evidence — the human needs a recommendation to confirm or override, not a blank form. T-2144 added a third failure mode: filing a recommendation that looks like a decision (DEFER) but reads as a placeholder once the operator opens the artifact.
Structural enforcement (T-1259, T-1260): lib/inception.sh do_inception_decide refuses when $CLAUDECODE=1 and points agents at fw task review T-XXX. Override flag --i-am-human exists for script/test contexts; --from-watchtower is the Flask-backend exemption.
Recommendation-completeness gate (T-2204, all 4 producer legs + consumer + cron): When an agent files a task with workflow_type: inception under $CLAUDECODE=1, the framework refuses the filing unless --recommendation GO|NO-GO|DEFER AND --rationale "..." are supplied. The gate fires at every plausible filing path plus the handoff verb, and a cron backstop catches anything that slips past:
| Leg | Surface | Origin | Bypass |
|---|---|---|---|
| Producer 1 | fw inception start | T-1715/T-1716 (do_inception_start) | --i-am-human / FW_ALLOW_EMPTY_RECOMMENDATION=1 |
| Producer 2 | fw work-on --type inception | Routes through do_inception_start | (same as Producer 1) |
| Producer 3 | fw task create --type inception | T-2207 (create-task.sh CLI) | --i-am-human / FW_ALLOW_EMPTY_RECOMMENDATION=1 / FW_INCEPTION_PRE_GATED=1 (silent internal trusted-caller signal from do_inception_start) |
| Producer 4 | Direct Write/Edit on .tasks/active/T-*.md | T-2205 (PreToolUse hook check-inception-recommendation) | FW_ALLOW_EMPTY_RECOMMENDATION=1 (env-only — Write tool has no flag surface) |
| Consumer | fw task review / fw task review-batch (emit) | T-2206 (emit_review / emit_review_batch BLOCK) | FW_ALLOW_EMPTY_RECOMMENDATION=1 |
| Backstop | Hourly cron inception-retrofit-rec-hourly | T-2208 (calls fw inception retrofit-recommendations --apply) | n/a — preventive sweep, idempotent on clean state |
The unified bypass-env name FW_ALLOW_EMPTY_RECOMMENDATION=1 per L-399 / T-1890 producer/consumer parity discipline; each agent-initiated bypass writes a Tier-2 entry to .context/working/.gate-bypass-log.yaml. The trusted-caller signal FW_INCEPTION_PRE_GATED=1 is silent (not logged) — it routes between do_inception_start and create-task.sh without double-firing the gate. The cron entry sweeps every hour at minute :19 and injects DEFER stubs into any active inception with an empty / template-only ## Recommendation block — catching the three paths the producer gates cannot reach (direct YAML writes, post-hoc fw task update --workflow-type inception flips, the window before the operator wires T-2205's hook into .claude/settings.json).
Two decision classes, one CLI verb (T-2141): fw task review looks like one handoff but is two structurally distinct decisions. Inception go/no-go decides whether a workflow_type: inception task should produce build slices — recorded via fw inception decide (agent-blocked under $CLAUDECODE=1, see Structural enforcement below); the human GOes/NO-GOes/DEFERs the candidate. Partial-complete review decides whether a build task's unchecked ### Human ACs are satisfied — recorded by the human ticking the box(es) and running fw task update --status work-completed. The two classes route to different Watchtower pages (/inception/<id> vs /review/<id>), produce different audit records, and answer different operator questions. Conflating them in chat handoffs ("review T-2143's inception decision at /review/T-2143") was the T-2125 origin slip.
Per-class URL mapping (T-2125, T-2129): fw task review emits a class-correct URL — /inception/<id> for inceptions, /review/<id> for partial-complete task reviews, etc. When listing handoffs in chat, never synthesise the handoff URL from memory — run fw task review T-XXX and quote the URL it emits, verbatim. The table below is for awareness; the CLI is the source of truth.
| Decision class | Correct URL |
|---|---|
| Inception go/no-go | /inception/<id> (or /approvals for the queue view) |
| Task partial-complete (Human ACs) | /review/<id> |
| Tier-0 approval | /approvals |
| Arc close | /arcs/<slug>/close |
Why this rule exists (T-2125 origin): the agent typed /review/T-XXX for four consecutive inception handoffs in one chat, because the four prior memories ([[feedback_use_fw_task_review]], [[feedback_human_review_links]], [[feedback_review_concrete_links]], [[feedback_post_grill_governance]]) all said "use fw task review" without disambiguating that the URL it emits is class-dependent. The user feedback was unambiguous: "inception decisions are not in review, they should be in approval properly filed." The fix has three legs — render-side 302 forgiveness (build slice A, separate task), this codification (slice B, T-2129), and re-issuing the backlog with class-correct URLs (slice D, done in T-2125 narrative).
Multi-task chat handoffs — use fw task review-batch (T-2182, T-2181): When summarising 2+ tasks for [REVIEW] in chat (session-end queues, mid-thread handoffs, post-grill governance closure tables), paste the verbatim output of fw task review-batch T-A T-B T-C … rather than hand-typing the table. The batch helper emits a class-correct markdown table:
| Task | Workflow | Link |
|------|----------|------|
| T-A | build | http://<host>/review/T-A |
| T-B | inception | http://<host>/inception/T-B |
Reading from the same task-file source of truth as the single-task fw task review T-XXX form, so the per-task URL routing is identical (inception → /inception/<id>, build/refactor/test → /review/<id>). Never hand-type a | Task | Link | table — the 2026-06-02 chat-output regression class (T-2181 RCA) was bare /review/T-XXX paths in hand-typed session-summary tables, despite three sibling memories forbidding it. Brevity in the summary slot is the regression slot; the helper output IS the structural counter — quote it verbatim. Single-task handoffs continue to use fw task review T-XXX (unchanged).
Hypothesis-Driven Debugging
When encountering errors or unexpected behavior:
- State the symptom in one sentence
- Form one hypothesis for the root cause
- Design one test to prove or disprove it (a command, a log check, a code read)
- Run the test and report the result
- If disproved, form the next hypothesis — max 3 hypotheses before escalating to user
- Never shotgun-debug (trying random fixes without understanding the cause)
- After resolution, record the pattern:
fw healing resolve T-XXX --mitigation "what fixed it"
Bug-Fix Learning Checkpoint
When fixing a bug discovered through real-world usage (user testing, production incident, cross-platform failure):
- Classify the bug — Is this a new failure class, or a repeat of a known pattern?
- Check learnings.yaml — Does a learning already exist for this class?
- If new class:
fw context add-learning "description" --task T-XXX --source P-001 - If systemic (same class hit 2+ times): register in
concerns.yaml, consider tooling fix (Level C/D)
Trigger: Any fix cycle addressing a bug found by someone other than the agent (user report, CI failure, production monitoring, cross-platform testing).
Not triggered by: Fixes for bugs found during development (pre-commit). Those are normal development, not field discoveries.
The test: "If another agent encounters this same class of bug in 6 months, would a learning entry help them fix it faster?" If yes, capture it now.
Evidence: 72% of bugfix tasks (31/43) produced zero learnings. T-030 (portability fix) and T-344 (same class, 23 days later) had no learning connecting them. See G-016.
Post-Fix Root Cause Escalation (G-019)
After fixing any problem discovered by the human (not found during development):
- Fix the symptom — make it work (Level A/B/C)
- Ask: "Why did the framework allow this?" — not "why did the code break" but "what structural omission let this go undetected?"
- If the framework was blind for >7 days: register a gap in
concerns.yaml— even if it's a single incident, sustained blindness reveals a systemic flaw - Do not close the gap until prevention exists — mitigation (cleaned up the mess) is not prevention (can't happen again). Ask: "Did I fix the symptom, or did I fix the reason the framework couldn't detect it?"
- The human test: If you're about to close a gap or complete a fix, and you can't explain what structural change prevents recurrence, the gap is not closed.
Trigger: Human corrects the agent's escalation level, or agent discovers a problem that existed undetected for >7 days.
Evidence: G-018 required 3 human corrections to escalate from symptom (handover TODOs) to root cause (no guard against silent quality decay). See G-019.
Structural enforcement (T-1550): Bug-class tasks (workflow_type ∉ {inception, specification, design} AND tag/title matches bug/fix/error/regression/etc.) cannot reach --status work-completed without a substantive ## RCA section. --skip-rca bypasses with logged Tier-2 entry. Origin: T-1549 spike showed 99% of bug-class tasks (315/317) shipped without RCA capture — this rule was advisory text for months while the template never asked.
Arc Completion Discipline (G-062)
Enforced structurally. fw arc create requires --headline-mechanic "<who> <does what> <observes what user-visible result>" and rejects substrate-only phrasing. fw arc close requires --demo <path|url|none> — a wire-level artefact (meta.json, stream-json, screencast, live URL) traceable to the arc, or none with a --justification logged to .context/audits/arc-bypass.jsonl. The gates fire before any closure narrative; substrate-vs-deliverable conflation cannot bypass them.
Trigger: any time you are about to write ## Recommendation on an arc-parent task or run fw arc close. The mandatory question is whether the captured --demo artefact shows the headline_mechanic firing — not whether substrate / tests / AC checkboxes ship. The phrases "forward work, not a closure blocker" and "substrate is in place" are §ACD violations in plain text.
Default-to-OPEN: if ≥2 human pushbacks on the same arc have not been resolved by a captured headline-mechanic instance, the arc is OPEN regardless of new evidence filed since. The pattern is the signal. Gated structurally (T-1671): fw arc close refuses under $CLAUDECODE=1; closure belongs to the human via fw task review <anchor> + Watchtower (overrides: --i-am-human, --from-watchtower).
Evidence: T-1626, T-1633, T-1641 (origin); T-1667 (3rd-incident RCA → demo+headline-mechanic gates); T-1670 (4th-incident agent auto-close → T-1671 closure-decision gate).
Arc Action Handoffs — surface URLs, not CLI (T-2347)
When surfacing an arc-mutating action to the operator — fw arc close, fw arc approve-driver, fw arc abandon, fw arc set-scoped-weight, fw arc remove-driver — paste the Watchtower URL, not the CLI command. The same rule that T-679 establishes for task review (fw task review T-XXX → URL) extends to arc actions because every arc-mutating verb has a Watchtower endpoint that wraps it (web/blueprints/arcs.py:919-1322).
| Action | Watchtower surface | Underlying verb (CLI is fallback) |
|---|---|---|
| Close arc | /arcs/<slug>/close (form + §ACD prompt + demo modes) | fw arc close <slug> --demo <…> --from-watchtower |
| Approve proposed driver | Button on /arcs/<slug> (proposed_scoped_drivers table) | fw arc approve-driver <slug> "<name>" --weight N --from-watchtower |
Approve --none | Form on /arcs/<slug> | fw arc approve-driver <slug> --none --justification "…" --from-watchtower |
| Add ad-hoc driver | Form on /arcs/<slug> | fw arc approve-driver <slug> "<name>" --weight N --rationale "…" --from-watchtower |
| Remove scoped driver | Button on /arcs/<slug> | fw arc remove-driver <slug> "<name>" --rationale "…" --from-watchtower |
| Adjust scoped weight | Slider on /arcs/<slug> | fw arc set-scoped-weight <slug> "<name>" <N> --from-watchtower |
The rule: when handing off an arc action, the primary affordance is the URL ({watchtower_url}/arcs/<slug> or {watchtower_url}/arcs/<slug>/close). The CLI block is the fallback, only for headless / no-Watchtower contexts (cron, SSH, scripted), and only when explicitly asked. CLI handoffs in chat re-introduce the --from-watchtower vs --i-am-human complexity the Watchtower path was built to absorb.
Discovery surfaces (operator-side, no CLI needed):
/arcs→ list of in-progress arcs with completion-ratio badges/arcs/<slug>→ arc detail with proposed-driver approve buttons + (when ≥80% complete with anchor Recommendation) close button (T-2347a slice)/approvals→ "Arc Closure" section auto-surfaces close-ready arcs (T-2347b slice closes the constituent-count parity gap)
Why this rule exists (T-2347 origin, 2026-06-12): the agent surfaced CLI for both arc-011 approve-driver and arc-006 close in one session despite the Watchtower buttons + close form existing (web/blueprints/arcs.py:1253). Operator pushed back: "why can this not be done through teh watchtower". The class is sibling to T-679 but distinct in scope — task review URLs were memorised; arc action URLs were not. RCA at docs/reports/T-2347-arc-closure-ux-rca.md (5-Whys, three defects A/B/C).
Arc-Scoped Driver Suggestion Workflow (T-1925, arc-006)
When a new arc is created (via fw arc create or fw work-on of an arc anchor task), the primary agent runs this 5-step workflow after the arc's anchor-task body is filled but before any driver is approved. The goal is to surface arc-specific drivers that would distinguish the arc from the global D1-D4 directives. Approval stays with the human (M6, D8).
Steps (D5 — timing matters):
- Read the arc anchor-task body in full (Problem Statement, Scope Fence, Risks, Decisions). Do not propose drivers from the arc name alone.
- List 2-3 candidate drivers, each with a one-line rationale of what the driver distinguishes that the four constitutional directives (D1-D4) do not. If you cannot articulate the distinction in one line, the candidate is not worth proposing.
- Write the candidates to
proposed_scoped_drivers:in the arc YAML (each as{name, rationale, source: agent, ts}). This is a proposal, not an assignment —scoped_drivers:only mutates viafw arc approve-driver(T-1926, §ACD-gated). - Surface the proposals to the human via
fw arc show-suggestions <arc-id>(T-1926; D7-reframe — this is a workflow verb the human runs when focus shifts to an arc, not a debug verb). The human reviews, approves up to 3 withfw arc approve-driveror runs--none --justification "..."to indicate the arc has no scoped drivers worth tracking separately. - If the human approves zero drivers, that is a valid outcome. Arcs without scoped drivers rank by global D1-D4 only.
R5 mitigation — the verbatim rule:
Manufacturing drivers to look thorough is worse than proposing zero and recommending --none.
Why this matters: every approved scoped driver costs interpretive bandwidth (humans must reason about it, the estimator must score every member task against it). Drivers that don't distinguish the arc are noise that drowns out signal.
D6 quality criterion:
Rationale must explain what each driver distinguishes that globals don't.
A rationale of "this arc is about reliability" does not meet D6 — that's already D2. A rationale of "this arc trades short-term reliability for long-term observability; we want to score tasks that improve the observability dimension separately" does meet D6.
Asymmetric caps:
proposed_scoped_drivers:— uncapped (D7-reframe: persists for reuse not audit).scoped_drivers:— max 3 entries, each with weight ≤6 (M2). Approved entries only.
Worked example. Consider a hypothetical arc replay-debug ("agent replays a failed dispatch from dispatches.jsonl and observes the same outcome → confirms determinism"). Plausible candidate drivers:
determinism(weight 5) — distinguishes from D1: this arc tests that the framework's reasoning is reproducible, not that it strengthens under stress.replay-fidelity(weight 4) — distinguishes from D2: not just "no silent failures" but "outcome under identical inputs is bit-identical".forensic-detail(weight 3) — distinguishes from D3: usability for the developer doing the replay, not the agent doing the work.
A bad set of candidates (don't do this): reliability, usability, correctness — these duplicate global drivers and would dilute scoring.
Surfaced through: fw arc show-suggestions <arc-id> (T-1926); Watchtower /arcs/<id> shows proposed drivers with Approve buttons (T-1930).
Canonical session prompt: this 5-step protocol is Workflow A (mode=batch_propose) of the BVP driver-session bundle — see §Driver Session Prompt Bundle below + policy/prompts/bvp-driver-session.md for entry conditions, failure modes (driver inflation, overlap with directives, manufactured drivers), and worked examples (policy/prompts/bvp-references/arc-scoped-driver-examples.md). Workflows B (fw bvp driver suggest) and C (fw bvp driver create <topic>) are global / sharpening variants that run the same sharpening subroutine but are not arc-scoped. Note: the CLI loader verbs fw bvp driver suggest|create|recompute|edit|retire are deferred per T-2245 IW-3 — the bundle is invoked manually today; the verb references are stable contracts for the eventual handoff. See lib/bvp.sh:1325 SEE-ALSO comment and the keystone's status note for the same parity statement.
Driver Session Prompt Bundle (T-2245 / T-2246)
When you need to propose a new free driver, an arc-scoped driver, or sharpen an existing one, the canonical workflow lives in policy/prompts/ — NOT inlined into this CLAUDE.md.
| Bundle file | When to reach for it |
|---|---|
policy/prompts/bvp-driver-session.md | Always start here. Keystone. Three workflows (A=batch-propose, B=discover+sharpen, C=sharpen named topic). Entry/exit conditions, outputs, init refusal, degraded mode. |
policy/prompts/artefact-template.md | When writing the research artefact (docs/reports/T-XXXX-bvp-driver-*.md). YAML frontmatter + 10 sections + [CONVERGED]/[SKIPPED]/[REJECTED]/[OPEN] markers. |
bvp-references/sharpening-subroutine.md | When running R1+R2 (required) and O1-O4 (optional) per candidate. Skip-when-stuck mechanics. |
bvp-references/sharpening-tactics.md | Tactical moves: surfacing assumptions, drilling scope without leading, eliciting weight without anchoring, recovering from frustration. |
bvp-references/discipline-failure-modes.md | Anti-patterns: driver inflation, overlap with directives, single-axis routing, defer-as-hedge, spec-over-dialogue drift. |
bvp-references/global-driver-examples.md | Three worked global-free proposals (F-RECALL real, killed-mid-session, recommend --none). |
bvp-references/arc-scoped-driver-examples.md | Three worked arc-scoped proposals (Workflow A/B/C end-to-end). |
The rule: read bvp-driver-session.md first; reach into the bundle's references when a specific tactic, example, or failure mode applies. The bundle is the prompt; CLAUDE.md routes you there.
§Arc-Scoped Driver Suggestion Workflow (above) is the trigger — what causes you to enter a driver session. The bundle is the protocol — how you run the session once entered.
Plan Mode Prohibition
NEVER use the built-in EnterPlanMode tool. It bypasses all framework governance:
- No task gate — planning starts without a task
- No session init — Session Start Protocol is skipped entirely
- No research artifacts — plan files go to
.claude/plans/(untracked, ephemeral) - Its system prompt says "This supercedes any other instructions" — overriding CLAUDE.md
- Post-plan execution skips commit cadence, task updates, and check-ins
Use /plan instead — the framework's governance-aware planning skill that:
- Requires an active task (verified in Step 1)
- Writes to
docs/plans/(tracked, committed) - Respects instruction precedence
If you need to explore before planning, use the Explore agent or /explore skill.
If you need to plan implementation, create a task first, then use /plan.
Built-in Task Tool Ban (T-1115/T-1117)
NEVER use TodoWrite, TaskCreate, TaskUpdate, TaskList, or TaskGet tools. They create a parallel, ungoverned task system outside framework governance:
- Items appear in the Claude Code UI status line ("X tasks"), visually identical to framework tasks
- No task gate, no horizon enforcement, no episodic memory, no handover capture
- Creates confusion about whether the agent is running a parallel ungoverned system
- Human caught this in session and asked "are you bypassing framework governance?"
Structural enforcement: A PreToolUse hook (block-task-tools.sh) blocks all five tools with exit 2 and a redirect message. The hook matches TodoWrite|TaskCreate|TaskUpdate|TaskList|TaskGet.
Use bin/fw work-on "task name" --type build to create real framework tasks.
Use bin/fw task create --name "task name" for more options.
If you need a scratchpad during complex multi-step work, use conversation text or write notes to .context/working/ — never use Claude Code's built-in todo system.
Trunk-Based Session Flow (T-100196, keystone)
⚠ KNOWN CONFLICT — read before following this section (T-100201). The "session runs on
master, commits go straight to master" mechanism below contradicts the T-2394 master-merge-only gate (agents/git/lib/master-guard.sh), which is live in this repo (PROTECT_MASTER=1) and structurally BLOCKS any direct authored commit onmaster. A fast-forward never fires the guard; a direct commit does. This conflict was hit live on 2026-07-05 (operator gotBLOCKED: direct commit on 'master' — master is merge-only).Interim safe rule (until T-100201 resolves the mechanism): the invariant below still holds — the session must never hold a commit that isn't on origin/master. But do NOT commit governance state directly on
masterwhilePROTECT_MASTER=1. Work in a worktree/branch and FF-land to master (fw integrate/ a scopedgit push origin <branch>:masterthat FFs) — FF is the one path that satisfies BOTH T-100196's invariant and T-2394's gate. Precedence: T-2394 is a structural gate; T-100196's "commit on master" is advisory text — the structural gate wins (see Instruction Precedence + L-405). The mechanism reconciliation (scope the guard / bypass infw sync/ exempt the persistent session) is an open operator decision tracked in T-100201.
The persistent session runs on master. There is no long-lived session branch.
This is the permanent fix for the branch/worktree divergence class (T-100194 fork, T-100199 strands). The invariant it buys:
The persistent session never holds a commit that is not already on origin/master.
If that holds, divergence is impossible by construction — no session branch to fork, nothing to strand, nothing to reconcile.
Why a session branch is pure liability here: real code lands on origin/master
via worktree + fw integrate (gated, FF-only) — never from the session directly.
So the session only ever commits governance state (handovers, task files,
.context/ memory), which belongs on master anyway. A session branch protects
nothing and is the sole source of drift.
The flow:
- Session runs on
master. Handover / task-sync / context commits go straight to master, pushed viafw sync(rebase + push — reconciles concurrent sessions without merge noise) orfw handover --commit. - Real code goes through a worktree.
fw worktree create <name>→ build →fw integrate run master --push(FF-lands + auto-prunes the worktree/branch). Run integrate from the main checkout, targeting the worktree — never from inside the worktree it will remove (that self-removal hangs). fw worktree gcreclaims landed worktrees/branches by content comparison (survives re-derivation, which defeatsgit cherry). Dry-run default; branch deletes stay Tier-0.- Guard:
fw doctorWARNs (diverged-fork, T-100195) if a session ever sits on a divergent non-master branch — the regression signal back into the antipattern.
Do NOT create a persistent session branch (t<NNNN>-<topic>). If you find the
main checkout on one, reconcile it to master: git merge --ff-only origin/master
(when it's a clean ancestor) or land its unique commits via a worktree first.
Session Start Protocol
Before beginning any work:
- Initialize context:
fw context init - Read
.context/handovers/LATEST.mdto understand current state - Review the "Suggested First Action" section
- Set focus:
fw context focus T-XXX - Run
fw metricsto see project status - If handover feedback section exists, fill it in
Before ANY implementation (even if a skill says "start now"):
- Verify a task exists for the work:
fw work-on "name" --type buildorfw work-on T-XXX - Confirm focus is set in
.context/working/focus.yaml - THEN proceed with implementation
This gate is non-negotiable. The PreToolUse hook will block Write/Edit without an active task. Use /start-work if unsure.
Manual compaction (/compact):
- Auto-compaction is disabled by design (D-027 — compaction destroys working memory)
/compactis available for manual use when context is high and you want a clean slate- The PreCompact hook automatically generates a handover before compaction
- The SessionStart:compact hook reinjects structured context into the fresh session
- After compaction, follow the recovery steps below
After context compaction (mid-session recovery):
- Run resume:
fw resume status - Sync working memory:
fw resume sync - Continue from recommendations
Quick Reference
Full command catalogue: fw help (or fw <cmd> --help). This section lists the governance-critical verbs the agent uses reflexively — not every subcommand.
Starting and managing work:
fw work-on "name" --type build— create task + set focus + start (the one-step task gate)fw work-on T-XXX— resume existing taskfw task create/fw task update T-XXX --status .../fw task show T-XXX/fw task listfw task update T-XXX --add-tag "ui"/--horizon laterfw task stale [--days N]— list stale tasksfw task review T-XXX— hand a task to the human via Watchtower (T-679, MANDATORY for human approvals)fw review-queue— list active tasks awaiting human review, sorted GO-first (T-1536, CLI companion to /approvals)fw verify-acs [T-XXX] [--auto-check|--execute]— check Human ACsfw pause list/fw pause resolve <dispatch_id> --answer "..."— paused-dispatch chain (dispatch-safety arc slice 5: operator answers worker'spause_requested, retry envelope linked viaretry_of_dispatch_id)
Inceptions and decisions:
fw inception start "name"/fw inception status/fw inception decide T-XXX go|no-go|defer --rationale "..."fw assumption add "..." --task T-XXX/fw assumption validate A-XXX --evidence "..."/fw assumption list
Context and memory:
fw context init(session start) /fw context focus T-XXX/fw context statusfw context add-learning "..." --task T-XXX --source P-001fw resume status|sync|quick— post-compaction recoveryfw healing diagnose|resolve T-XXX/fw healing patterns
Commits, audits, health:
fw git commit -m "T-XXX: ..."/fw git status/fw git install-hooksfw audit/fw doctor/fw gaps/fw metrics [predict --type build]fw test all|unit|integration|web|playwright|lint
Handovers and session end:
fw handover [--commit]/fw handover --checkpoint(mid-session)fw push— push to all remotesfw costs [session|current]— token usagefw mirror sync|status— auto-recover lagging github mirror (T-1594, runs every 15 min via cron)
Fabric (before modifying source):
fw fabric deps <path>/fw fabric impact <path>/fw fabric blast-radius [ref]fw fabric overview/fw fabric drift/fw fabric register <path>fw write-set check <T-A> <T-B>— disjoint-write-set validator for arc-011 parallel dispatch (T-2337); exit 0=disjoint, 1=overlap, 2=undecidable
Dispatch and cross-project:
fw termlink check|spawn|exec|dispatch|status|cleanup|wait|result(see TermLink section)fw bus post|read|manifest|clear --task T-XXX [--remote HOST]fw dispatch send --host HOST .../fw dispatch hostsfw pickup send|process|status|listfw pending register|list|resolve|remind— blocked cross-project actions
Tier 0, approvals, notifications:
fw tier0 approve/fw tier0 statusfw approvals— approval queuefw notify setup|status|enable|disable|test
Scheduling and config:
fw cron generate|status|list|run|pause|resume <job-id>fw config set|get|list|overrides— persistent.framework.yamlsettings
Orchestrator and resolver (v1 dispatch substrate, T-1687 arc):
fw orchestrator status [--json]— substrate observability: dispatch counts, outcome enrichment ratio, recent dispatchesfw orchestrator improve— namespace stub for v2 self-improvement (not yet implemented)fw resolver workflows— list configured workflow filesfw resolver dispatch <task_id> <task_type> [--dry-run] [--json]— build dispatch envelope (workflow → assemble → telemetry)fw resolver explain <dispatch_id>— forensics for a captured dispatch (reads.context/dispatches.jsonl)fw outcome evaluate <task_id> [--json]— run default evaluator (Verification + Agent ACs)fw outcome backprop <task_id>— append outcome rows todispatch-outcomes.jsonlfor matching dispatches (best-effort hook also fires fromupdate-task.shon--status work-completed)fw outcome read <dispatch_id>— merged dispatch + latest outcome (joins both JSONL files)fw outcome list <task_id>— all outcome events for a taskfw peer subscribe [--once]— long-poll inbox.queued events + spawn responders (v2 peer-consult subscriber + responder spawn-bridge; cron mode)
Reviewer (anti-pattern static scan, T-1443):
fw reviewer T-XXX— scan one task; writes verdict to## Reviewer Verdictblockfw reviewer T-XXX --dispatch [--timeout N] [--json]— isolated TermLink worker mode (T-1951, G-066 prong 3)fw reviewer audit— Layer 3 daily Pass-B re-scan of all completed tasksfw reviewer override add T-XXX --pattern X [--ac N] --reason '...' [--ttl 90]— TTL'd FP suppressionfw reviewer override list|prune|remove OV-XXXX
Reviewer dispatch mode (--dispatch, T-1951): Use fw reviewer T-XXX --dispatch when reviewing multiple tasks in parallel, when the parent session is budget-pressured, or when you want the reviewer to run in an isolated process with zero context cost to the parent. The worker spawns a TermLink session (reviewer-{task_id}-{suffix}, observable via termlink list), runs the same static_scan.py inline reviewer, and posts the full JSON verdict to the fw bus (fw bus manifest T-XXX → fw bus read T-XXX R-NNN). Use inline fw reviewer T-XXX for single-task quick checks where blocking is fine. Guard: FW_REVIEWER_IN_DISPATCH=1 is set inside the worker — recursive --dispatch is refused (exit 3).
Knowledge and navigation:
fw decisions/fw learnings/fw practices/fw timelinefw ask "query"/fw recall "query"/fw search "term"/fw docs
Setup and upgrade:
fw init [dir]/fw upgrade [dir]/fw update/fw vendorfw consumer-recover <host> [path] [--apply] [--via {ssh,termlink}] [--session ID] [--upstream URL]— recover a legacy vendored consumer (pre-T-2232 / pre-T-1634) via SSH or TermLink + fresh-clone + env-scoped upgrade. Dry-run default;--applyexecutes. Refuses with exit 2 when the target already has the.upstreamsentinel (use plainfw upgradeinstead). See memoryfeedback_t2232_forward_looking_recoveryfor boundary and rationale (T-2232 protects forward-looking only).fw serve [--port N]— start Watchtowerfw watchtower port|url|status
Auto-restart wrapper: claude-fw [args...] (auto-restarts on handover signal) / claude-fw --no-restart to opt out.
For rarely-used commands (harvest, promote, consolidate, release, self-test, validate-init, plugin-audit, upstream, enforcement, deploy, prompt, note, scan, mcp, build, fix-learned, onboarding, self-audit, test-onboarding, traceability, hook, hook-enable, patterns, preflight, setup, gpu, ux-review), run fw help or fw <cmd>.
TermLink Integration (T-503)
TermLink is an optional cross-terminal session communication tool (Rust, 26 commands, 264 tests).
The framework provides a thin wrapper via fw termlink that adds task-tagging, budget checks,
and cleanup tracking while delegating all real work to the termlink binary.
Distribution model contrast: TermLink is intentionally machine-wide (installed via Homebrew/cargo, single binary on PATH). This is the deliberate inverse of the framework's per-project isolation model. Reason: TermLink's session discovery uses Unix sockets at system paths — per-project vendoring would defeat cross-session, cross-project communication. Do not propose per-project TermLink installs; use the system binary.
- Repo:
https://github.com/DimitriGeelen/termlink(mirrored from OneDev) - Install (macOS):
brew install DimitriGeelen/termlink/termlink - Install (from source):
cargo install --path crates/termlink-cli - Check:
fw termlink check(also infw doctoras WARN)
Key Primitives
| Command | Purpose |
|---|---|
termlink interact <session> <cmd> --json | Run command, get structured output (star primitive) |
termlink pty inject <session> --enter | Send input, fire-and-forget (long-running commands) |
termlink event emit/wait/poll | Inter-session signaling |
termlink discover --json | Find sessions by tag/role/name |
Timeout Orphan Warning (T-577)
Do NOT use termlink run --timeout for long-running tasks. When termlink run times out, it deregisters the session but does NOT kill the process. The process becomes orphaned — invisible to TermLink, unmonitorable, still consuming resources. Discovered when a claude -p agent wrote output 65 minutes after a 900s timeout.
Use fw termlink dispatch instead — it has its own kill watchdog that properly terminates the process on timeout. If you must use termlink run directly, add your own kill logic.
fw termlink cleanup detects and terminates orphaned dispatch processes.
MCP Task Governance (T-1063)
When TERMLINK_TASK_GOVERNANCE=1 is set (configured in .mcp.json), TermLink MCP tools require a task_id parameter. Always pass the current task ID when calling TermLink MCP tools:
termlink_exec:{"command": "...", "session": "...", "task_id": "T-XXX"}termlink_spawn:{"name": "...", "task_id": "T-XXX"}termlink_interact:{"session": "...", "command": "...", "task_id": "T-XXX"}termlink_dispatch:{"name": "...", "prompt": "...", "task_id": "T-XXX"}
The task_id is the current task from focus.yaml. When task_id is passed, it propagates to session tags as task:T-XXX for observability.
Cross-Agent Communication Protocol (T-1126)
When communicating with agents on other machines via TermLink remote, choose the tool based on whether you need a response:
| Need | Tool | Why |
|---|---|---|
| Ask a question, request action, get feedback | termlink remote inject <session> --enter "message" | Direct PTY input — agent processes immediately |
| Deliver files/pickups (no response needed) | termlink remote push | Async inbox — processed on schedule |
| Execute a command and get structured output | termlink remote exec <session> "command" | Synchronous, returns stdout |
| Send a file to a specific session | termlink remote send-file | Caveat: ok:true = hub accepted, NOT delivered. Files silently lost to event-only sessions. Always verify receipt. |
The rule: If you need a response, use inject. If you're delivering and don't need confirmation, use push. Never use push for questions — the receiving agent won't see it until their next cron or prompt.
Evidence: Session S-2026-0412 sent 2 push messages to ring20-manager — zero response. Switched to inject — immediate response + file resend within seconds.
Budget Rules
- Do not spawn new sessions when context > 60% — spawning is expensive, respect budget
- Always cleanup before session end —
fw termlink cleanup - Max 5 parallel workers — same limit as sub-agent dispatch protocol
- Leave 40K tokens headroom before dispatching workers
Auto-Restart (T-179)
When context budget hits critical, checkpoint.sh auto-generates a handover and writes .context/working/.restart-requested. If the user started their session via claude-fw (instead of claude), the wrapper detects this signal on exit and auto-restarts with claude -c. The SessionStart:resume hook then injects handover context into the fresh session.
Flow: Budget critical → auto-handover → signal file → claude exits → wrapper detects → sleep 3 → claude -c → context injected → /resume ready.
Safety: 5-minute TTL on signal files, max 5 consecutive restarts, 3-second cancel window, opt-out via --no-restart.
Remote Session Access (TermLink)
Launch with TermLink wrapping for remote observation and control:
claude-fw --termlink # Register session for remote access
TL_CLAUDE_ENABLED=1 claude-fw # Same via env var
From another terminal:
termlink list # See the session
termlink attach claude-master-<PID> # Full TUI mirror (bidirectional)
termlink pty output claude-master-<PID> --strip-ansi # Read recent output
TermLink is optional. Without it installed, --termlink prints a warning and falls back to direct mode. The PTY session persists across auto-restarts — the same session is reused when claude -c re-launches.
Session End Protocol
Before ending any session:
- Run session capture checklist (
agents/session-capture/AGENT.md) - Create tasks for all uncaptured work
- Update practices with learnings
- Generate handover:
fw handover --commit(auto-commits AND pushes to all remotes) - If push fails: warn the user — unpushed commits are a data loss risk
- Run
fw metricsto verify state
Do not end a session without generating a handover.
Do not end a session with unpushed commits — fw handover --commit pushes automatically (T-1144). If push fails (divergence, auth), flag it to the user before ending.