The Five Modes

Every structured decision in haft moves through five modes. Each mode has a clear goal, entry conditions, outputs, and legitimate reroutes back to earlier modes when reality doesn't match expectations. This is not a rigid pipeline — it's a directed graph with well-defined upstream arrows.

Understand → Explore → Choose → Execute → Verify
    ↑            ↑         ↑                  |
    |            |         └──────────────────┘
    |            └────────────────────────────┘
    └─────────────────────────────────────────┘

Forward is the default. Reroutes happen when a later mode reveals that earlier framing was wrong — not because you feel like iterating.

Understand

Entry: /h-frame or /h-reason
Goal: Frame the problem before solving it.
Tool: haft_problem(action="frame")

Most engineering mistakes happen because the problem was never properly stated. "We need caching" is not a problem — it's a solution in search of a problem. The actual problem might be "API response time exceeds 500ms on the product listing page."

The agent creates a ProblemCard with:

  • Signal — the anomalous observation (not the assumed cause)
  • Constraints — hard limits that any solution must respect
  • Optimization targets — what to improve (1-3 max)
  • Observation indicators — what to monitor but NOT optimize (Anti-Goodhart)
  • Acceptance criteria — how you'll know the problem is solved
  • Blast radius — what systems/teams are affected

Language precision triggers

The Understand mode watches for ambiguous terms and forces you to unpack them before proceeding. When you use words like service, process, quality, or component, the agent asks: "what do you mean by that, specifically?" These words mean different things to different people. A problem framed with ambiguous terms produces ambiguous solutions.

Explore

Entry: /h-explore (or /h-char first for characterization)
Goal: Generate 2+ genuinely distinct variants — different in kind, not degree.
Tool: haft_solution(action="explore")

Characterization (/h-char)

Before generating options, define how you'll compare them. This prevents bias — you can't cherry-pick criteria to favor the option you already like. Run /h-char to define comparison dimensions with roles:

  • constraint — hard limit, must satisfy (e.g., "latency < 100ms")
  • target — what you're optimizing (e.g., "throughput")
  • observation — watch but don't optimize. Anti-Goodhart: if you optimize throughput, you might silently kill reliability. Marking reliability as "observation" means you track it without letting it distort your optimization.

Characterization is done BEFORE seeing options. This is deliberate — setting the rules after seeing the candidates is how you get biased comparisons.

Variant generation

"Different" means different in kind, not degree:

  • Bad: "Redis vs Memcached vs in-memory cache" — three variations of the same approach
  • Good: "Cache layer vs CDN optimization vs query redesign" — three fundamentally different strategies

Each variant gets:

  • Description — what this approach does
  • Strengths — why it might work
  • Weakest link (WLNK) — what will break first. Not generic "cons" — the single thing that bounds quality.
  • Risks — what could go wrong
  • Stepping stone — does this open future possibilities even if not optimal now?

Diversity check

The tool computes word overlap between variants. If two share more than 50% of their words, it warns: "do these differ in kind, not degree?" You can proceed anyway, but the warning is there to prevent degree-variation masquerading as real alternatives.

Choose

Entry: /h-compare
Goal: Fair comparison on declared dimensions, identify the Pareto front.
Tool: haft_solution(action="compare")

Probe-or-commit gate

Before running the comparison, the agent checks readiness:

  • Dimension coverage — are all declared dimensions scored for all variants?
  • Variant diversity — do variants actually differ?
  • Specific investigation — could a targeted probe (benchmark, prototype, expert consult) change the ranking?

The gate returns one of four verdicts:

Verdict Meaning
commit Proceed with comparison — evidence is sufficient
probe Run a specific investigation first (returns what to probe)
widen Variants are too similar — go back to Explore
reroute Problem framing is wrong — go back to Understand

Constraint-aware Pareto

Constraints eliminate variants BEFORE dominance computation. If a variant violates a hard constraint, it's out — no matter how good it scores on targets. Only surviving variants enter the Pareto front calculation.

The result is a set of non-dominated options where no variant is strictly worse than another on all dimensions. This is not "pick the best" — it's "here are the options that trade off differently, each sacrificing something distinct."

Parity enforcement

Same inputs, same scope, same budget for all variants — or the comparison is junk. The tool checks for parity violations and flags them before producing results.

Language precision triggers

Subjective dimensions get flagged. If a comparison dimension is "maintainability", "simplicity", or "scalability" without a concrete metric, the agent asks you to operationalize it: "what specific measurement would indicate maintainability?"

Execute

Entry: /h-decide
Goal: Record the decision as a contract with invariants.
Tool: haft_decision(action="decide")

Adversarial verification gate

Before recording, the agent challenges the decision:

  • Tactical depth: one-line counter-argument — "strongest reason this is wrong"
  • Standard/Deep depth: five adversarial probes —
    1. Strongest counter-argument against the selected variant
    2. What would make this decision wrong in 3 months?
    3. Is any evidence self-referential (agent's own reasoning as proof)?
    4. Does the comparison hold under different load/scale assumptions?
    5. What's the cheapest experiment that could refute this?

If the counter-argument survives scrutiny, the decision is recorded.

Decision record

The record includes:

  • Problem frame — linked back to the ProblemCard
  • Selected variant — which option and why
  • Invariants — what must always be true
  • DO/DON'T rules — explicit behavioral contract
  • Rollback plan — how to reverse if it goes wrong
  • Refresh triggers — conditions that should trigger re-evaluation
  • Affected files — exact code locations
  • Claims with verify_after dates — "latency stays under 100ms" verified in 2 weeks

After recording, the agent snapshots file hashes as the baseline for drift detection.

Verify

Entry: /h-verify
Goal: Check what's stale, measure outcomes, maintain decision health.
Tools: haft_refresh(...) + haft_decision(action="measure")

Discovery

/h-verify scan finds everything that needs attention:

  • Stale artifacts — R_eff < 0.5 or expired evidence
  • Code drift — files changed since baseline snapshot
  • Pending claimsverify_after dates that have passed without measurement

Actions

Action What it does When to use
scan Find all stale artifacts + code drift + pending claims Routine health check
measure Record evidence for a claim or acceptance criteria Verifying a decision's outcomes
waive Extend validity with justification Decision still valid, just expired
reopen Start new problem cycle from old decision Conditions changed, need to reconsider
supersede Replace with a different artifact New decision replaces old one
deprecate Archive as no longer relevant Decision obsolete, nothing replaces it

Evidence freshness

Each evidence item has a valid_until date and a congruence level (CL). As evidence expires, R_eff degrades. New measurements mark old evidence as superseded. See Decision Lifecycle for the full model.

Reroutes

Reroutes are legitimate upstream movements — not iteration for iteration's sake. Each reroute has a specific trigger:

From To Trigger
Choose Understand Comparison reveals bad framing — dimensions don't capture what actually matters
Explore Understand Exploration reveals the wrong problem — you're solving a symptom, not the cause
Execute Choose Implementation shows chosen option doesn't work — need to pick a different variant
Verify Any Evidence shows the decision needs reconsideration — measurements failed, context changed

Reroutes are signals, not workflow buttons. If you find yourself rerouting frequently, the problem is upstream: either the framing is consistently weak, or the exploration isn't generating genuinely distinct variants.

The Note fast path

Not every decision needs five modes. /h-note captures micro-decisions that don't warrant the full cycle — "we use RWMutex here because contention is under 0.1%" is a valid engineering choice that should be recorded but doesn't need a Pareto front.

Notes are validated (rationale required, conflict check, overlap check), auto-expire after 90 days, and are explicitly NOT architectural decisions. If a note keeps getting referenced, it's time to promote it to a proper decision via /h-frame.

See Notes & Micro-decisions for details.

Mapping from v5 commands

v5 command v6 mode v6 command
/q-frame Understand /h-frame
/q-char Explore (characterization) /h-char
/q-explore Explore /h-explore
/q-compare Choose /h-compare
/q-decide Execute /h-decide
/q-refresh Verify /h-verify

Next