Reading: Applied Part 12. Production SDD Antipatterns: Diagnostic Map of the Applied Cycle

Lesson 1 of 5 in module «Applied Part 12. Production SDD Antipatterns: Diagnostic Map of the Applied Cycle»
You are viewing the lesson without signing in. Sign in to save progress and take tests.

Applied part 12. Production SDD antipatterns: diagnostic map of the applied cycle

Status: Recommendation. The part collects antipatterns that arise specifically in the applied cycle: duels, file arbitration, Spec CI, tiered budgets, anti-Goodhart metrics, and auto-remediation. They continue the line of part 20 of the first volume: artifacts exist, checks exist, the agent works fast, but control over the system gradually slips away.

This part is needed as a diagnostic map for the applied volume. If the production loop has become noisy, contradictory, or defends its own speed at the user's expense, start here.

Before reading

  • Anchor from the first volume: part 20 shows the base SDD antipatterns.
  • Local learning case: any artifact already created from chapters 8–11.
  • Trace for capstone/: three lines blocker / owner / next_check for the selected high_memory_usage package.
  • Main term of the first pass: diagnostic blocker. Names of individual antipatterns are reference material; you do not need to read them in sequence.
  • What to postpone: turning each antipattern into an automatic CI policy.

Goal

The goal of the chapter is not to memorize a list of names, but to conduct a short audit of the already assembled production SDD package. After the chapter you should have three diagnostic lines: what blocks the go-decision, who is responsible for the fix, and when it is checked again.

For the first pass, read this chapter as a checklist, not as a dictionary of all possible failures. The catalog below is needed to recognize the problem you found; you can close the chapter with three lines blocker / owner / next_check.

Escalations of antipatterns from the first volume

Some of the antipatterns in this catalog are not new — they are production variants of those described in part 20 of the first volume. The difference is one of scale: in the learning cycle, a base antipattern burns a day of work; in the applied cycle, it opens up a class of incidents on a live service.

Antipattern in this chapterEscalation from volume 1
Constitution as cosmetics"Constitution that nobody opens" (v.1, pt.20)
Poisoned specification without diff in artifacts"Specification after code" — a patch of the explanation instead of editing the artifact
Drift of validation.md after red CI"Weakening facts after an error" (v.1, pt.20)

| Shadow specification without review deadline | "QWEN.md as a junkyard" — memory without ttl and author | | Trace without evidence tag | "Facts by word of mouth" — missing evidence_ref |

If you have already worked through part 20 of the first volume, these entries can be read selectively — only what adds production context appears here. The other 12 antipatterns of this chapter appear only in the applied cycle (duels, arbitration, budgets, anti-Goodhart, auto-remediation) and are not covered in volume 1.

Minimal learning scenario

Learning case

Before the final assessment, you need to check one production SDD loop for noise. Choose any package from chapters 8–11: judgment.md, validation.md, budget_network.yaml, or the readiness table. The goal is to find at least three antipatterns or explicitly prove that there are none.

Preparation

  • The diagnostic checklist below.
  • book2/examples/templates/retrospective.md — form for a short write-up of the result.
  • One artifact that has already passed through a runnable example or a manual learning scenario.

Steps

  1. Choose one artifact and do not expand the scope of the check. Expectation: the audit takes 15–30 minutes and does not turn into a review of the whole project.
  1. Answer the 12 questions of the checklist. *Expectation: each answer is yes, no, or not_applicable with a short file reference.*
  2. For each no, indicate the antipattern, owner, and deadline for the fix.
  3. Find at least one [project script] in the chosen chapter and check whether it is marked as a runnable analog or as a "implement-it-yourself" interface.
  4. Record the result in antipattern-audit.md or in the retrospective: what blocks the production go-decision, and what can be left as an improvement.

Control fact

There is a list of three items: blocker, owner, next_check. If negative answers are turned only into general advice, the diagnostic map has not fulfilled its function.

How it gets into capstone/

Move three lines blocker / owner / next_check into capstone/antipattern-audit.md. Do not fix these problems in the same file without a separate record: for the assessment it is important to see the diagnosis and the next check, not a pretty final result without history.

Minimal fragment:

| blocker | owner | next_check |
|---|---|---|
| readiness without `evidence_ref` | platform | repeat dry-run with a link to the fixture |

| `[project script]` without a runnable analog | devex | replace with `examples/real-api` or implement the script |
| `manual_review_floor` not specified | sre | add a guard metric before auto-mode |

Reviewable trace

Keep antipattern-audit.md if the audit relates to a learning package or to capstone/. Do not fix the problems found in the same change without a separate record: first the diagnosis must be visible, then the treatment.

Key ideas

Each antipattern below is broken down using a single scheme of three fields: Symptom — what is observed in artifacts and logs, Why it's bad — how this changes the behavior of the team or agent, How to fix — minimum steps to a state in which the symptom is caught automatically.

The catalog can be read in any order: each entry is self-contained and follows the same three fields. The entries are not alternatives — they are different classes of noise that often appear together. If three or more antipatterns are found in a single production package, read them as a coupled network of problems with a common root in a lost risk contract.

Examples and application

Constitution as cosmetics

> Escalation of an antipattern from volume 1. The base version is "constitution that nobody opens" from part 20 of the first volume. In the applied cycle, the same mistake leads not to bad code, but to a dangerous operation actually executed.

Symptom: the repository has constitution.md with immutable_principles and mutable_rules, but the gate before dangerous actions does not run. In judgment.md there is a reference to the rule forbid_unscoped_delete, but in the team logs (logs/auto-remediation.jsonl) there is not a single call to scripts/constitution/check.py. Over the last 30 days — zero gate triggers.

Why it's bad: rules exist formally, but they do not constrain the agent at the moment of pressure. After several incidents, the team starts to treat the constitution as decoration. On the review of the next incident, a characteristic phrase will come up: "the rule existed, it's just that nobody checked" — this is a signal that the constitution worked as a comment, not as a contract.

How to fix:

  • connect constitution.md to gate checks before executing the playbook (see part 3);
  • route any dangerous operation through scripts/constitution/check.py or an equivalent in CI;
  • record a reference to the constitution version and decision_hash in judgment.md;
  • if a rule is not checked automatically, move it from immutable_principles to the playbook and explicitly mark it as "instruction, not gate";
  • check that over the last 30 days there is at least one log entry where the gate triggered and blocked an action. Zero triggers in a quarter means either the constitution is not connected, or it describes risks that do not exist.

Mutable rule with ttl: ∞

Symptom: in mutable_rules there is a rule without ttl, or ttl is set to years at once. rollback_condition is missing or phrased as "by team decision".

Why it's bad: the rule lives indefinitely and over time turns into a hidden part of the invariant. A year later, participants do not remember why it was introduced and are afraid to touch it. The fix is applied by analogy to situations for which it was never intended.

How to fix:

  • set ttl in days, not years; first review — 30–90 days (see the reference answer in INSTRUCTOR.md);
  • phrase rollback_condition as a checkable predicate: repeat_incidents_same_node>=2, silent_p0_ratio>0.05, safety_veto=true;
  • when ttl expires, block the application of the rule until it is explicitly extended through a referendum;
  • delete rules that have never triggered during their lifetime.

Poisoned specification without diff in artifacts

> Escalation of an antipattern from volume 1. The base version is "specification after code" from part 20 of the first volume: the explanation changes, but the artifact does not. Here the same mistake shows up in the inverse task (specification recovery) — the patch fixes the comment, not the root cause.

Symptom: the team trains specification recovery on poisoned specifications (see part 2), but the patch changes only the text of the explanation or the comment. requirements.md, plan.md, validation.md remain unchanged.

Why it's bad: the exercise stops teaching root-cause localization. A week later, the same class of poisoned specification passes again — the patch did not close the root cause.

How to fix:

  • in the lesson's done-criteria, require a diff in at least one artifact (requirements.md, plan.md, validation.md, constitution.md) — not only in the explanation;
  • run a full reverse pass Specify → Plan → Tasks → Implement after the fix;
  • if the patch changes only the text of the explanation, send it back for a re-do;
  • register the class of a controllably defective specification in precedents.md so that the next similar defect is caught automatically.

ask_storm disguised as care

Symptom: the agent in the loop keeps asking clarifying questions and does not move on to a solution. Control string: cycle_count > 0 && ask_storm >= 4 && escalation_path_resolved=false (ask_storm is a counter of repeated clarifications without new data).

Why it's bad: the questions look like caution, but they are a signal of an internal contradiction in the specification. Each human answer on the fly adds a new formulation that is not anchored anywhere and disappears at the next /clear.

How to fix:

  • stop the session after the third consecutive clarification and check requirements.md for contradictions;
  • parse the specification as poisoned (part 2) — one defect, search for the root cause;
  • record answers not in chat, but in requirements.md or clarifications.md;
  • do not let the agent's questions turn into a continuing dialogue: each clarification should either close a point in the specification or return to editing it.

stage_regress without an explicit reason

Symptom: the implement phase rolls back to plan, the plan phase rolls back to specify with no record of a reason. The next day, nobody remembers why the plan was rewritten. (stage_regress — rollback to a previous phase of the SDD cycle without an explicit reason.)

Why it's bad: the SDD cycle turns into drift. Each step back loses the context of the previous one, and a week later the project has three half-drafts of a plan, none of which is closed by a fact in validation.md.

How to fix:

  • record transitions between phases explicitly: a reviewable change record, an entry in genealogy.md, a reason;
  • prohibit a rollback to a previous phase without updating at least one fact in validation.md;
  • use a project skill that, on git status, shows which phase is current and which facts are not yet closed;
  • if a rollback happens more often than once a day, that is a signal that the specification is too small, not that the process is noisy.

phase_context_loss between phases

Symptom: specify has fixed a decision, plan has not inherited it, implement has started from a draft that never went through validation.md. (phase_context_loss — loss of context between phases.)

Why it's bad: each step works with its own picture of the world. After two or three transitions, they diverge so much that the artifacts contradict each other, and any check trivially passes — it checks the wrong thing.

How to fix:

  • between phases, pass only file references (@specs/...), not a retelling from chat;
  • introduce the project skill check_phase_handoff, which checks that the plan refers to the current requirements.md, and the implementation refers to the current plan;
  • after /clear, start a new phase by reading QWEN.md, the current requirements.md, and the current plan.md;
  • if a phase cannot explain which point of the previous phase it implements — go back to the previous phase before editing the code.

Verifier turning into ordinary code review

Symptom: the Verifier writes comments of the form "the style is not great", "better rename it", "let's discuss this". There is no specific counterexample, no reference to a rule, no JSON Schema violation.

Why it's bad: the duel loses its procedural character. The Verifier stops being a formal loop and becomes just another opinion. The Implementer responds in free text, and the dispute moves into chat, where it cannot be reproduced.

How to fix:

  • prohibit the Verifier from making any judgments without a concrete artifact: a counterexample with minimality, a hook log, a JSON Schema, Given/When/Then;
  • replication of the verdict should be local: another person, given the same cases/ and metrics/, should obtain the same judgment.md;
  • if the Verifier did not find a counterexample, record verdict=APPROVE and move on, instead of continuing the discussion in general formulations;
  • move stylistic remarks into a separate review channel, do not mix them with the duel.

File arbitration in which only the majority votes

Symptom: governance_protocol is described as "2 approve out of 3", without a veto from Safety and without a tie-breaker. On a tied score, the system hangs or decides by the date the meeting was called.

Why it's bad: the Safety role loses its meaning. Decisions are taken by the votes of the Verifier and the Implementer, who optimize for speed; critical risks go through as "acceptable".

How to fix:

  • introduce safety_veto: critical_risk for the Safety role;
  • set tie_breaker: safety_first_then_latest_matching_precedent;
  • check governance_protocol with a Spec CI gate: the absence of a tie-breaker and a veto blocks merge;
  • each deviation by Safety's veto is recorded in precedents.md with a reference to the immutable rule, so that a repeated similar dispute is closed faster.

Fictitious [project script]

Symptom: in a specification, checklist, or training chapter, a command of the form python3 scripts/spec_ci/check_scope.py is used, but the script itself does not exist in the repository. Nobody has run it; the fact "the check passed" is implied, not observed.

Why it's bad: a false sense of control appears. CI looks strict, but the check does not run. After a few weeks the team forgets which scripts are real and which are interfaces.

How to fix:

  • next to each [project script] block, explicitly say whether a runnable analog exists in examples/ or whether this is a "implement-it-yourself" interface;
  • in Spec CI, a separate step checks that the commands mentioned in validation.md actually exist (test -x path/to/script);
  • training chapters mark [runnable] only for commands that pass a local python3 scripts/...;
  • if a script is needed but does not exist — open a ticket with a fixed implementation date, instead of leaving it as "later".

Naked KPI without a paired counter-metric

Symptom: validation.md contains a target metric (MTTR<=5m, coverage>=80%, auto_close_rate>=0.9), but no paired counter-metrics (guard metrics). The CI gate passes when the KPI is met.

Why it's bad: classic Goodhart. The agent or the team learns to satisfy the metric at any cost: close complex incidents as easy ones, mark P0 as P2, bypass manual review. The metric grows, real quality falls.

How to fix:

  • pair every target metric with an anti-Goodhart metric (see part 10): to MTTRsilent_p0_ratio and manual_review_floor; to coveragemutation_kill_rate;
  • the gate passes only when the pair is satisfied simultaneously;
  • any change of threshold is a risk event, not a cosmetic YAML edit; it is recorded with a justification;
  • periodically run replays on historical incidents: a new version should not worsen verdicts on already-analyzed cases.

Drift of validation.md after red CI

Symptom: CI failed, after which the pull request author changes a threshold or deletes a fact in validation.md instead of fixing the code. The change description says "clarified validation".

Why it's bad: the process starts defending the implementation rather than the user contract. This is the same mistake as "weakening facts after an error" from part 20 of the first volume, but at applied scale: lowering the silent_p0 threshold from the default 0.05 (AgentClinic baseline, see Appendix D.4) to 0.10 in a single pull request shifts a whole class of risks.

How to fix:

  • any edit of validation.md that weakens a check must be reviewed separately as a change to the risk contract;
  • in the change description, record the reason: the incident identifier, a link to the post-mortem, the expected effect;
  • prohibit deletion of mandatory facts without a record in precedents.md;
  • if a threshold has changed more than twice in the last quarter, that is a signal that the goal and the check live separately.

Switching between tiers without a budget

Symptom: when local-coder fails, all traffic automatically goes to frontier-reviewer. budget_keeper is not configured or does not block over-spending.

Why it's bad: the expensive tier eats up the daily quota in minutes and loses the ability to serve real P0/P1 when they arrive. Failover becomes a source of a secondary incident.

How to fix:

  • describe the switch as ranked, not total (see part 9);
  • send to frontier-reviewer only tasks with severity in [P0, P1] and age > N;
  • everything else — into the degradation queue, after a timeout — into the manual channel;
  • the emergency mode triggers on token_health and puts the system into a protected mode until local-coder is restored.

Shadow specification without a review deadline

> Escalation of an antipattern from volume 1. The base version is "QWEN.md as a junkyard" from part 20 of the first volume. In the learning cycle, the problem is a growing context; in the applied cycle, a heuristic without an author acquires the force of a contract.

Symptom: QWEN.md contains a few-shot example or a heuristic that got there "somehow by itself". The author does not remember who added it or when. The record has no ttl, no evidence, and no link to an evaluation.

Why it's bad: the heuristic acquires the force of a contract without a review procedure. It cannot be challenged (nobody remembers the author) and cannot be checked exactly (no source). Six months later, the rule is applied by analogy to cases for which it was never intended.

How to fix:

  • format any heuristic in QWEN.md with a minimal header: author, date, evidence, ttl, link to the auction (see part 6);
  • when ttl expires — either update it, or send it to quarantine with a record of the reason;
  • consider few-shot examples that have not worked in the last 50 incidents as candidates for removal;
  • a shadow specification does not replace an approved requirement — it only guides the prompt in ambiguous cases.

Auto-remediation without a minimum of manual review

Symptom: the agent automatically closes incidents based on metrics. manual_review_floor is not set or equals zero.

Why it's bad: even if the metrics look clean, the agent gradually displaces the human from the loop. When a class of incidents appears that the model has not seen, there is no backup mechanism to notice the deviation. After a few weeks, silent failures accumulate, because nobody is left to catch them.

How to fix:

  • set manual_review_floor explicitly: for example, "at least 15% of incidents go through a human regardless of metrics";
  • the rotation is random, not "leave the hardest for humans" — otherwise manual review does not see the baseline;
  • the results of manual review go into the replay set for the next validator run;
  • any lowering of manual_review_floor is processed as a change to the risk contract, not as an optimization.

Readiness 25/25 as a goal, not as a description

Symptom: the team drags all 25 points of the readiness model to green because "we need to ship". Some points are set in advance, without a real evidence tag.

Why it's bad: readiness loses its meaning as an early signal. On the next release, everything is again "25/25", but incidents come back. The scale turns into a ritual.

How to fix:

  • check readiness via evidence_ref, not via a textual assertion;
  • 23/25 with real evidence is a go; 25/25 without evidence on two points is not a go;
  • when a point is not met, explicitly record the risk and the rollback condition, instead of "painting it green";
  • periodically run readiness in the opposite direction: which point worked and caught a real problem, which one did not trigger a single time in a quarter — a candidate for revision.

Genealogy without updates

Symptom: genealogy.md or the constitution's change_log exists, but the last entry is dated three months ago. Meanwhile, five rules have been changed.

Why it's bad: provenance stops working as an evidentiary chain. Six months later, it is impossible to reconstruct "why the agent had the right to perform this action", and the dispute after an incident turns into a general discussion.

How to fix:

  • an entry in change_log is a mandatory part of every constitution amendment; without it, the gate does not allow the merge;
  • parent_version is required; skipping a version is grounds for a separate review;
  • decision_hash is computed automatically from the contents of the decision, so that a substitution does not pass silently;
  • monthly — a short audit: reconciliation of change_log with real edits to the file. A discrepancy is recorded as a process incident.

Trace without an evidence tag

> Escalation of an antipattern from volume 1. The base version is "facts by word of mouth" from part 20 of the first volume. In the learning cycle, a trace is not needed, because the feature is small. In the applied cycle, without evidence_ref it is impossible to reconstruct on what basis an action was performed.

Symptom: the agent saves logs of actions, but the records have no references to source artifacts: which version of the specification was applied, which constitution rules, which prompt. After an incident, it is impossible to reconstruct the decision context.

Why it's bad: audit_trace_coverage is formally close to 100%, but the trace is useless. It is the same mistake as validation.md that nobody ran, but at the audit level.

How to fix:

  • in every trace record, spec_version, constitution_version, prompt_hash, decision_source, evidence_ref are required;
  • Spec CI checks the completeness of fields and blocks the merge if at least one of them is empty;
  • evidence_ref is a path and an identifier inside the artifact (logs/node-2026-05-12.parquet#row_4123), not a general link to a directory;
  • any record with evidence_ref=null is considered invalid for audit.

Diagnostic checklist

If the applied SDD loop has become noisy or stopped catching regressions, answer:

  1. Does constitution.md work as a gate before execution, not only as a review after?
  2. Are there rules in mutable_rules without ttl or with ttl greater than 90 days?
  3. After a poisoned specification fails, is at least one artifact changed (requirements.md, plan.md, validation.md)?
  4. Does the Verifier use a counterexample, a hook log, or a JSON Schema — or only free text?
  5. Does governance_protocol contain a Safety veto and a deterministic tie-breaker?
  6. Are runnable commands in the repository marked separately from [project script] interfaces?
  7. Is every target metric accompanied by a paired anti-Goodhart metric?
  8. When CI fails, is the code fixed, or is validation.md weakened?
  9. Does the switch between tiers have a budget cap and an emergency mode?
  10. Does every record in QWEN.md have an author, evidence, and ttl?
  11. Is manual_review_floor maintained regardless of the KPI value?
  12. Is evidence_ref filled in in every trace record?

If three or more questions are answered negatively — do not add new layers of automation: file arbitration, tier routing, new emergency-mode rules. First remove the noise and close the gaps in the current loop.

Summary

Antipatterns of the applied cycle are not catastrophic on their own. Their accumulation is dangerous: after several releases, the team does not see the risk contract behind "green CI". The diagnostic map is the first step toward repair. Each negative answer turns into a project skill, a Spec CI gate, or a constitution rule with a checkable rollback_condition. Return to this chapter after every major incident: the same artifact three months later shows three different blockers.

Related parts of the first volume

  • Part 20 of the first volume — base SDD antipatterns: specification after code, giant requirements.md, ritual /clear, QWEN.md as a junkyard.
  • Part 18 of the first volume — antipatterns that are simultaneously security threats.
  • Part 2 — poisoned specifications as a training tool against most antipatterns in this chapter.
  • Part 10 — anti-Goodhart as protection against a naked KPI.

Artifacts and readiness criteria

ArtifactReady when
antipattern-audit.md (or retrospective)three fields are filled in: blocker, owner, next_check; every antipattern found has an owner and a next checkable step
Answers to the 12 checklist questionscompleted for one chosen artifact; for each negative answer there is a plan
Separation of blockers and improvementsthe audit does not fix problems in the same change without a separate diagnosis record

The full track adds an updated diagnostic checklist to [appendix-c-checklists.md](appendix-c-checklists.md), entries in precedents.md for each encountered antipattern, additions to QWEN.md for recurring ones, and a Spec CI check for at least one of them. Consider it ready if Spec CI contains at least one check that catches an antipattern automatically (for example, mutable_rules without ttl), and recurring antipatterns get into precedents.md or QWEN.md only with evidence and a review deadline.

Practice

  1. Open the team's current constitution.md and check mutable_rules for the presence of ttl and rollback_condition. Find at least one rule that should be either updated or sent to quarantine. *Expectation: in antipattern-audit.md there is one line blocker / owner / next_check for a specific rule; the ttl is set in days, not years.*
  2. Take the last pull request that edited validation.md. Determine what was changed — a threshold or the content of a fact. If a threshold, check whether the change description has a link to a post-mortem or an incident identifier. *Expectation: for the pull request, one of two outcomes is recorded — either a justified change to the risk contract with a link to the incident, or the antipattern "drift of validation.md after red CI" with an owner and a rollback deadline.*
  3. Go through the list of [project script] blocks in one chosen chapter and check which commands are real and which are interfaces. Add tags to the chapter's README. *Expectation: for every [project script], it is explicitly indicated "there is a runnable analog in examples/..." or "implement it yourself"; there are no untagged blocks.*

Control questions

  1. Why is a mutable rule with ttl: ∞ more dangerous than a rule with no formulation at all?
  2. How does ask_storm differ from bona fide clarifications, and how do you tell one from the other?
  3. Which three fields make a trace record suitable for audit, and why is audit_trace_coverage=100% without them a Goodhart metric?
  4. In a pull request review, you see that the author changed the silent_p0 threshold from 0.05 to 0.10 and added the comment "temporarily, until stabilization". What will you do with this pull request, and which three conditions must be met before such a change can be merged?
My notes
0 / 10000

Notes are saved in this browser. They will not appear on another device.

Course menu

Course

Using SDD in Development for Qwen Code CLI. Applied Course
Progress 0 / 95