ai-quality
Five pillars of AI-assisted development — decomposition, TDD, architecture-first, focused work, context. Method size limits, DoD, stubbing.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Five pillars of AI-assisted development — decomposition, TDD, architecture-first, focused work, context. Method size limits, DoD, stubbing.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Schema and migration semantics for /dr-doctor — thin one-liner contract, 6-pass migration, data-loss safety, conflict resolution. Loaded by self-heal.
Core Datarim rules. Load this entry first, then only the fragment needed for paths, storage, numbering, backlog, routing, or archive behavior.
Post-QA hardening — detects task type (code, docs, research, legal, content, infra) and applies the matching verification checklist before archiving.
Testing pyramid, frameworks, mocking. Load first; then the fragment for the active gate (live smoke, silent failure, bats, legacy triage).
Preserve Datarim task continuity while orchestrated Claude Code or Codex sessions compact or clear context at deterministic pressure thresholds.
Immutability contract for all pipeline stages: artefact freeze, V-AC parity, non-code parity, anti-tautological rule, and return-to-source transition.
| name | ai-quality |
| description | Five pillars of AI-assisted development — decomposition, TDD, architecture-first, focused work, context. Method size limits, DoD, stubbing. |
| current_aal | 1 |
| target_aal | 2 |
TL;DR: These 5 pillars guide AI-assisted development. Apply them consistently for 30-50% better code quality and 40-50% fewer bugs.
Break complex tasks into small, focused units.
KEY LIMITS:
|- Max 50 lines per method
|- Max 7-9 objects in working memory
|- One responsibility per function
|- Separate signals — one variable, one question
Why: AI loses focus with complexity. Small units = better output.
Separate-signals rule. When a single variable answers two semantically distinct questions (e.g. "what to display" AND "is body non-empty"), refactoring one role silently breaks the other. Always extract independent signals for independent questions, even if they currently compute from the same source. The cost is one extra line at definition; the saving is not chasing regressions through downstream branches that read the variable as a proxy for something it no longer represents.
Measuring the 50-line limit — discount template-literals and object-literal method blocks. A naive awk/regex heuristic that spans "from one function keyword to the next" over-counts badly: it swallows a trailing object-literal whose methods follow the function, and it counts a function defined inside a page.evaluate('...') (or any embedded-script) template-literal string as part of the host method. Before flagging a >50-line finding, cross-check the actual brace span by reading the file — an orchestrator that hands work to a step-object below it, or one that embeds a DOM-walker as a string for the browser to run, is usually far shorter than the heuristic reports. Flag on the real span, not the keyword-to-keyword distance.
Tests are hallucination filters. Mock edges, not logic.
SEQUENCE:
1. Write tests BEFORE code
2. Define "done" explicitly (DoD)
3. Cover corner cases upfront
4. STRICT mocking: edges only, NO data fitting
Why: Tests catch AI mistakes. No tests = no safety net.
Approve structure before coding.
APPROACH:
1. Create skeleton with stubs
2. Review architecture
3. Implement one method at a time
Why: Bad architecture = wasted work. Validate first.
Narrow context improves quality.
PRACTICES:
|- Review one method at a time
|- Define clear boundaries (what we DON'T do)
|- Verify AI can solve before starting
|- Wire ALL planned features in first pass — if code/prompts are ready
and wiring is <30 min, do it. "Low risk deferral" is still deferral.
|- Decompose multi-clause success criteria — a criterion joined by "and"
or listing N independent requirements is N checks. Close each sub-clause
with its own named test and verify each before marking the wish done.
One green test on the first clause does NOT satisfy a two-clause wish.
|- Authorization prompts to user: 1 sentence risk + 1 yes/no question.
Threat models → docs, not interactive prompt.
Why: Broad context = scattered results. Focus = precision. Source (auth UX): prior incident — user requested simpler prompts after a 7-option authorization table. Source (wire-all): prior incident — dedup/rerank deferred as "low risk", user challenged, wiring took <15 min. Source (decompose): prior incident — a "select backend AND inject per-role allowed-tools" wish had its first clause implemented and the second silently skipped (the registry declared the tools but no consumer read them); the partial surfaced only at the expectations gate, costing one extra implement→review round.
Right information at right time.
ELEMENTS:
|- Gather requirements BEFORE coding
|- Document transaction isolation needs
|- Structure datarim hierarchically
|- Engineer prompts carefully
Why: Bad context = bad output. Quality in = quality out.
Run project linters after each TDD code-change step, before moving to the next stub/method. Defers to
/dr-complianceonly when the project has no auto-detectable linter — not when findings need fixing.
AUTO-DETECTION PATTERNS (check manifests in order of precedence):
1. eslint / prettier → `package.json` (devDependencies or scripts)
2. ruff / flake8 / pylint → `pyproject.toml`, `ruff.toml`, `setup.cfg`
3. clippy / rustfmt → `Cargo.toml` (under [lints] or as dev-dependency)
4. golangci-lint → `.golangci.yml` or `go.mod` + `tools.go`
5. rubocop → `.rubocop.yml`
6. Any linter configured in project root config files → detect by extension
Procedure:
/dr-compliance: that stage assumes a clean baseline.Why: Lint findings discovered at compliance time force a fixup commit outside the TDD loop, breaking the RED-GREEN-REFACTOR cadence. Each lint rule is a potential bug that was visible at code time but deferred to a stage that expects cleanup, not bug-fixing.
Covered by: commands/dr-do.md § Step 7 ACTION — Lint-on-the-spot (MANDATORY).
Load only the rules relevant to your current stage:
| Stage | Rules to Apply | Focus |
|---|---|---|
| /dr-init | #4 Requirements, #12 Complexity | Is the task well-defined? Can AI solve it? |
| /dr-plan | #1 Stubbing, #5 DoD, #6 Corner Cases, #7 Skeleton, #11 Boundaries | Decompose, define scope and done criteria |
| /dr-design | #6 Corner Cases, #7 Skeleton, #9 Cognitive Load, #13 Transactions | Design quality, keep it simple |
| /dr-do | #2 TDD, #3 Method Size, #8 Iterative, #9 Cognitive Load | Write tests first, small methods, one at a time |
| /dr-qa | #5 DoD verification, #10 Focused Review | Review one method at a time, check done criteria |
| /dr-archive | #8 Iterative verification + #10 Review (Step 0.5 reflection), #14 Structure (Step 2 archive doc) | Was the process followed? Hierarchical summaries for future context |
| # | Rule | One-Liner |
|---|---|---|
| 1 | Stubbing | Break into 50-line stubs |
| 2 | TDD | Tests before code (Strict Mocking) |
| 3 | Method Size | Max 50 lines, 7-9 objects |
| 4 | Requirements | Context before coding |
| 5 | DoD | Explicit done criteria |
| 6 | Corner Cases | List boundaries first |
| 7 | Skeleton | Architecture before code |
| 8 | Iterative | One method at a time |
| 9 | Cognitive | 7+/-2 objects max |
| 10 | Review | Review one method only |
| 11 | Boundaries | State what's out of scope |
| 12 | Complexity | Verify AI can solve |
| 13 | Transaction | Explicit isolation levels |
| 14 | Structure | Hierarchical summaries |
| 15 | Prompts | Structured prompt creation |
Before proceeding, ask:
[ ] Is this task decomposed into small units?
[ ] Do I have tests/DoD defined?
[ ] Is the architecture approved?
[ ] Am I focused on one thing?
[ ] Do I have the right context?
If NO to any: Stop and address before coding.
When a task changes output format, structure, or contract across multiple files (e.g. CTA block (definition)s across 17 commands + 5 agents, response envelopes across N services, log fields across handlers), apply this pattern as a default rule for L3+ tasks:
SEQUENCE:
1. Spec-as-skill → write the canonical specification first as a single
source-of-truth skill (e.g. cta-format.md). Define
structure, field rules, anti-patterns.
2. Golden fixtures → create one fixture per variant (single, multi,
fail-routing, etc.) under tests/{topic}/fixtures/.
These are the visual artefacts agents produce.
3. Spec-regression tests → bats / language-native tests verify:
(a) every consumer file references the skill
(b) every consumer agent loads the skill
(c) fixtures match all spec invariants
(d) anti-pattern guards (forbidden chars, etc.)
4. Mechanical propagation → only after 1-3 land, propagate the change to all
consumers. Tests guard against drift.
Why: Without fixtures + tests, the same drift problem re-emerges every time a new consumer is added without spec compliance. Mechanical propagation alone protects current state, not future state.
When to apply:
When NOT to apply:
Source: prior incident — Approach C (Spec-First with Golden Fixtures) chosen over Approach A (Big Bang refactor) for canonical CTA block. 39 tests now guard 17 commands + 5 agents from drift; mechanical sweep alone (Approach A) would have left the same problem to re-emerge with the next added command.
When an Acceptance Criterion asserts an HTTP status code (e.g. → 401, should return 403), trace the request through the full middleware/filter chain — rate limiter → CORS → body parser → validator → guard → controller — before locking the literal status. Any layer upstream of the asserted source can short-circuit the chain and return a different code than expected.
Failure mode: AC declares → 401 (auth-rejected). Validator (Zod / class-validator / Pydantic / Joi) runs before auth, sees an empty body, returns 400 Validation failed. AC literally fails — but the asserted behavior (auth bypass works) is correct. PRD/plan/QA all need amendment under self-review.
Rule:
not <failure_class> instead of == <specific_status>.[[ "$code" != "<failure_status>" ]] || ! echo "$body" | grep -q '<failure_marker>'. Asserts «failure class N did not happen», not «specific success class M did happen». Robust to upstream layer swaps.When to apply: any L2+ task that ships HTTP-routed code. Mandatory for L3+ when the controller sits behind ≥2 middleware layers.
Stack-agnostic: applies to any HTTP framework with a middleware/filter chain. Concrete examples: Express, Fastify, NestJS, Koa, Hapi, Django, Flask, FastAPI, Rails, Spring Boot, ASP.NET Core, Phoenix, Gin.
Applies equally to non-HTTP protocols with a layered guard chain. The same trace-step / literal-vs-semantic gate / template rule covers gRPC interceptors, message-broker authorizers, and any RPC pipeline that emits a typed status code. Concrete example: an Acceptance Criterion that asserts → PermissionDenied on a guarded RPC may instead surface as Unauthenticated if the service-layer require_admin() predicate distinguishes "no bearer" from "wrong role". Both codes communicate the same intent ("admin RPC denied"); a literal-status AC trips on a no-op refactor of the predicate. Phrase as semantic gate: code ∈ {Unauthenticated, PermissionDenied}.
Anti-pattern: copying the literal status from upstream PRD without re-tracing when middleware order changes (e.g. switching framework, adding rate limiter, moving validator). Re-trace on every PRD that touches HTTP routing.
For services with programmatic API consumers (SPA, mobile clients, server-to-server), HTTP error responses MUST be parseable by machines, not just by humans reading a log line. Standardize on RFC 7807 application/problem+json as the ecosystem error envelope and concentrate the mapping in a single seam, not scattered per-handler try/catch blocks.
Contract.
type → title mapping centrally in one module. Every recognised problem type has a stable URI in type and a frozen short title. Adding a new problem type is an explicit edit to the table, not an ad-hoc string at the throw site.{type, title, status, detail?, instance?, ...extensions} and is thrown from anywhere in the call stack. The global seam recognises it by class and pass-through-maps it to the response. Foreign exceptions (validation library errors, framework HTTP exceptions, generic Error) are mapped by class in a priority chain inside the seam.5xx) MUST omit user-facing detail strings to prevent information disclosure (stack frames, internal paths, library messages). Client-class responses (4xx) MAY carry detail describing what the caller did wrong.reply.send({error: ...}) defeats the contract — programmatic consumers will receive two different envelope shapes from the same service. Replace such call sites with throw new ProblemException(...) and let the global seam render.Why. A programmatic consumer parses by Content-Type and field names. Inconsistent envelopes (some handlers use {error, code}, others {message}, others raw text) force defensive client code at every call site. RFC 7807 is a published standard; any well-formed problem document is parseable by off-the-shelf libraries. Concentrating the mapping in one seam means new endpoints get correct error shapes for free, and audit/refactor of error contracts is one file, not N.
When to apply. Any service exposing an HTTP API to a programmatic consumer outside the team writing the service. Internal-only utilities with human-only callers may use simpler shapes, but the moment a SPA or mobile client lands as a consumer the global seam is mandatory.
Anti-patterns.
@UseFilters decorators / per-route error handlers — re-creates the per-handler scattering the rule is preventing.if (err) reply.code(400).send({error: 'foo'}) after the global seam exists — silent contract divergence.When an Acceptance Criterion's location moves mid-implementation — different controller, different module, different URL path, different scope boundary — update all parallel artefacts atomically within the same revision cycle, not lazily across separate commits or "I'll fix the plan later" deferrals. The parallel surfaces typically include:
AMENDED YYYY-MM-DD marker and a one-line justification).Rule.
Why. When PRD, plan, and task description carry the same AC under three different locations, future readers (QA at archive time, the next developer touching the area, a security audit) cannot distinguish authoritative from stale. Drift compounds: the next amendment based on a stale surface re-amplifies the divergence. Atomic updates with cross-check make the artefact set self-consistent at every commit.
When to apply. Any L2+ task that maintains parallel PRD + plan + task description artefacts. Mandatory when an AC's path/module/controller changes mid-implementation. Recommended when scope reduction or expansion changes which surface owns which assertion.
Anti-patterns.
For any task that implements a client, SDK wrapper, or adapter against an existing upstream interface (REST/gRPC contract, third-party SDK, internal service client), the plan MUST cite the exact file:line of every upstream symbol (method name, field name, enum value, request/response shape) the new code will call or mirror — not just name it in prose.
Rule.
file:line next to the plan step.sendMessage method" is not a citation. src/vendor/sdk/client.ts:142 is.Why. A 1:1 wire-shape mirror verified via cited file:line in the plan is the cheapest correctness gate for any client/SDK/adapter task — a symbol-existence grep at plan time catches shape drift (renamed field, removed method, changed enum) before code generation, instead of during implementation or QA when the fix is more expensive.
When to apply. Any L2+ task whose Acceptance Criteria describe matching or wrapping an existing upstream interface. Evidence cohort: a prior connector-integration incident (Class A).
Anti-patterns.
Before writing a subprocess wrapper, sidecar, or pipeline adapter against an external CLI or API, run a ≤60-second probe with representative input before committing to an implementation plan. Capture stdout, stderr, and exit code from the probe run. Treat the tool's documentation as a hypothesis, not a fact, until the probe confirms the actual wire semantics.
Rule.
--help output, or README describing the tool's behavior is an untested claim until the probe reproduces it. Do not encode documented behavior into a wrapper's control flow before the probe confirms it.Why. Source: prior incident — an inline plan assumed the target CLI exposed a persistent stdin pipe for streaming requests, based solely on its documentation. A 30-second probe with representative input disproved this immediately — the process was one-shot per invocation. Catching this before the harness was written saved roughly 200 lines of wrapper code and a day of false-build work that a docs-only design would have produced and then had to unwind.
When to apply. Any task that writes a subprocess wrapper, sidecar process, or pipeline adapter against an external CLI or API whose wire behavior is not already proven inside this codebase. Mandatory before /dr-plan locks the integration approach for such a task.
Anti-pattern: designing the full wrapper/adapter surface (retry logic, streaming assumptions, schema parsing) from documentation alone, then discovering the real behavior diverges only once the harness is built and tests start failing in ways that don't match the design.
When a task description cites a <file>:<line> reference (e.g. "CHANGELOG.md:45 templates 18→23"), the referenced line can drift between when the description was written and when /dr-do reads it — a prior step in the same task edits the file, a concurrent task touches it, or the claim is simply stale copy-paste. Editing against a stale line reference is worse than a clean failure: the agent edits the wrong line, or the wrong content at a coincidentally-valid line number, and the mistake surfaces only later.
Rule.
<file>:<line> claim before editing it. At /dr-do startup (while reading the task description) AND at any mid-implementation point where a NEW <file>:<line> claim is introduced (Gap Discovery finding, review comment, self-authored note), run:
grep -n '<expected-content>' <file>
where <expected-content> is the literal text the claim asserts lives at that line.line-not-found: expected "<X>" at line N, found zero matches in <file>
Why. A line number is a snapshot, not a live pointer. Source: prior incident, recurring twice — a task description cited a stale line-count claim that no longer matched the file by the time implementation reached it. The smoke-check costs one grep -n; skipping it costs a revert-and-redo cycle when the edit lands on the wrong line or the wrong file region.
When to apply. Any task description containing a <file>:<line> citation — mandatory at /dr-do startup for citations already present, and at the moment any new citation is introduced mid-implementation.
Stack-agnostic. grep -n + line lookup is universal across languages and stacks; this is a workflow gate, not stack-specific content.
Anti-patterns.
/dr-do-startup gate only — a new file:line claim introduced mid-implementation needs the same check before it drives an edit.A spike (or any exploratory prototype meant to falsify a design hypothesis before full build-out) needs a numeric pass/fail threshold — latency ceiling, cost ceiling, error-rate ceiling — to be falsifiable at all. That threshold MUST be derived from the consuming surface's own documented UX budget, never copied from generic latency folklore or from a different project's convention.
Rule.
Why. A threshold inherited from generic folklore rather than the actual consumer mis-scopes the whole exploration: a criterion tuned for an interactive chat surface (sub-2-second) will falsify a spike whose real consumer is an async-tolerant surface that comfortably absorbs 10-30 seconds — killing a viable design over a threshold nobody asked for. The reverse mistake is equally possible: a threshold borrowed from an async surface would wrongly pass a design that is unusable on an interactive surface. Both failures trace to the same root cause — writing the number before identifying the consumer.
When to apply. Any spike/prototype task that sets a falsifiable numeric threshold (latency, cost, error rate, throughput) as its pass/fail gate.
Anti-patterns.
A spike (or any isolated harness/prototype built to validate an approach outside the production codebase) proves feasibility — nothing more. Its output is a go/no-go decision plus a follow-up production task, never the spike's own code shipped directly into the production path. Treating a validated spike as "close enough to ship" smuggles unreviewed, unstubbed, untested exploratory code into production under the cover of the spike's validation result.
Rule.
Why. A spike's isolation is what makes it fast and safe to falsify — it skips the review, testing, and architectural discipline that production code requires precisely because it is not meant to ship. If validated spike code is merged directly, all of that skipped discipline ships with it: no tests written against the target's real test suite, no adherence to the target's error-handling conventions, no review of the parts the spike stubbed out to stay fast. Separating "decision" from "implementation" keeps the spike's speed advantage without letting its shortcuts leak into production.
When to apply. Any task that builds an isolated spike, prototype, or harness to validate a design or technical approach before committing to a full build-out.
Anti-patterns.
Load only the fragment needed for the current sub-problem:
incident-patterns.md
Use when adding safety guards, reviewing integration failure attribution, or making scope decisions for untracked files.deployment-patterns.md
Use when deploying services (Docker, venv, NestJS DI, CLI connectors in containers).
bash-pitfalls.md
Use when writing or reviewing any .sh, especially regex/grep/sed-heavy ops scripts. Mandatory shellcheck rule for /dr-do, plus the five recurring traps (grep -F + ^, boundary-alternation regex, raw ${var} in regex, password in process arglist, set -e + pipelines).These principles reduce bugs by 40-50% and improve code quality by 30-50%.