Decisions

ADR-117: Module guidelines

Today the agent brief is the only contract for module-typed work (DB changes, playbook authoring, API endpoints, UI components, ansible roles, agent briefs themselves, docs). Each brief is full prose, hand-assembled by…

#117

ADR-117: Module guidelines

Status: Proposed Date: 2026-04-26 Related: ADR-067 (playbooks-unified-primitive), ADR-099 (brief-as-pr), ADR-107 (gates-as-playbook-primitives), ADR-109 (type-claude-step-and-observer-field), ADR-110 (observe-and-file-dominant-pattern), ADR-112 (principles tier — sibling), ADR-115 (principle-aware reviewers — sibling), ADR-116 (RAG — sibling)

Context

Today the agent brief is the only contract for module-typed work (DB changes, playbook authoring, API endpoints, UI components, ansible roles, agent briefs themselves, docs). Each brief is full prose, hand-assembled by the operator or a parent agent, drift-prone, and re-derived per ticket. The recurring memory rule feedback_warp_items_need_structural_detail.md names the symptom: stub items poison the board because there is no canonical shape for "what this kind of work needs to include".

Principles (warp #339, ADR-112) constrain system-design judgements. They do not say "a DB-changes ticket must include a rollback plan and a regenerated-types check." That is module-implementation guidance — a different scope, a different artifact, the same shape. Today that guidance is dispersed across CLAUDE.md sections, memory files, and tribal knowledge encoded in operator briefs.

The decision is tracked in warp #346 (sub-tickets r1 through r9). It collapses several open questions into a single primitive: principle-aware reviewers (ADR-115) need a per-module data source; the RAG (ADR-116) needs a categorical syllabus, not just a library; structural checks (warp #335) need to fire by module membership, not universally.

Decision

A module guideline is a versioned, immutable-on-publish per-module spec stored at dark-factory/guidelines/<id>@v<n>.md. Each guideline carries:

  • applies_to — file/path globs and ticket-tag patterns that match this module
  • required_steps — the ordered shape of work in this module
  • structural_checks — automated checks that fire on PRs in this module
  • forbidden_patterns — anti-cases (regex or rule references)
  • ticket_body_template — the canonical issue/ticket body shape (warp-add rejects ticket bodies that fail this template)
  • acceptance_proof_required — what the PR must demonstrate to merge
  • knowledge_base_routes — RAG queries the agent should run for context
  • reviewer_directive — the guideline-anchored prompt insertion for ADR-115 reviewers
  • constrained_by — citations of principles (ADR-112) this guideline rests on

The seven founding guidelines (warp #346 r3), backfilled from existing memory rules, ADRs, and CLAUDE.md sections (mostly compaction, not new content):

  • db-changes@v1 — migrations, rollback, schema review, regenerate types
  • playbook-authoring@v1 — schema, hard-assert terminal, dry-run-clean audit
  • api-endpoint@v1 — OpenAPI spec first, generated types, runtime validation, contract tests
  • ui-component@v1 — gy-* convention, design tokens, smoke spec, page-type categorisation
  • ansible-role@v1 — tag-friendly tasks, become true on system mods, idempotency tests
  • agent-brief@v1 — time-box, atomic chunks, no-touch-working-tree
  • docs-authoring@v1docs/shared/ vs repo-root, AUTO-GENERATED markers, viewer-validator

Enforcement is three-layer:

  1. warp-add — walks new ticket tags, attaches guideline_cited, rejects malformed bodies against the guideline's ticket_body_template.
  2. warp-claim — bundles the cited guideline (full text plus current version) into the claim response so the agent reads it before opening files.
  3. gyrum-review-pr — fifth gate (alongside the four personas) verifies the guideline's acceptance_proof_required is satisfied; reviewers fetch the guideline alongside applicable principles and anchor blocks to both.

Versioning: bumping a guideline mints a new file (@v<n+1>.md) and writes a migration note for in-flight cites. Old version stays immutable so retroactive judgement against the version-as-of-claim remains possible.

Consequences

What becomes easier.

  • Agent briefs shrink and stop drifting. The guideline owns the shape of work; the brief becomes "implement X per playbook-authoring@v1" plus the case-specific scope. The agent-brief@v1 guideline itself codifies time-box and atomic-chunk rules so even meta-briefs follow a contract.
  • Ticket bodies follow a per-module template, killing the stub-ticket failure mode the operator has flagged repeatedly. warp-add rejection is structural, not advisory.
  • Reviewers (ADR-115) anchor to two documents per PR — the cited principles plus the cited guideline's acceptance_proof_required — so blocking has a much narrower premise space.
  • Structural checks (warp #335) fire by module membership: the db-changes checks run on PRs the db-changes@v1 guideline matches, not on every PR. The check stack stops being one-size-fits-all.
  • The RAG (ADR-116) gains a categorical layer: guidelines are syllabi that route queries; principles and ADRs are the library being queried.

What becomes harder.

  • Guidelines themselves drift if unmaintained. Mitigation: a nightly stale-guideline alert (warp #346 r9) flags any guideline unreviewed for >90 days; the principles ↔ guideline back-citation lets revision-on-principle-change cascade.
  • One-time authoring overhead for the seven founding modules. Most content already exists in memory and CLAUDE.md; the work is compaction and shape-enforcement, not invention.
  • A new validator (gyrum-validate-module, warp #346 r7) becomes part of the pre-PR loop. Local CLI fires it, agent runs it pre-complete, review-pr re-runs in CI as the gate. That is more friction per PR but predictable, structural friction — the kind the feedback_structural_over_prose.md memory rule prefers.
  • Edge cases that touch two modules (a UI component that also adds an API endpoint) need guideline-composition rules. v1 keeps it simple: the PR cites both, both gates run, both must pass.

What we sign up to maintain.

  • The dark-factory/guidelines/ directory, the schema, and the seven founding guideline files.
  • gyrum-validate-module and its per-module check implementations.
  • The warp-add rejection logic, the warp-claim bundling logic, and the fifth review gate in gyrum-review-pr.
  • The nightly stale-guideline cron and its alert routing.
  • Versioning + revision workflow: minting @v<n+1> files and migration notes for in-flight cites.

Alternatives considered

  • Keep guidance in CLAUDE.md sections (status quo). Rejected: CLAUDE.md is a single document per repo with no per-module structure; the consumers (warp-add, warp-claim, review-pr) cannot route to "the playbook-authoring section" without grep-hacking. Guidelines as separate, versioned files give a clean addressable shape.
  • Use principles for everything — drop the guideline tier. Rejected: principles are system-design constraints ("structural beats prose"); guidelines are module-implementation contracts ("a db-change PR includes a rollback plan"). Conflating them dilutes both. The constrained_by field on guidelines keeps them honest by forcing each guideline to cite its underlying principles.
  • Per-repo guidelines instead of fleet-wide. Rejected: the seven founding modules cut across repos (every repo writes ansible roles, every UI repo authors components). Per-repo duplication re-introduces drift; fleet-wide gives one source of truth and per-repo applies_to patterns handle the local variation.
  • Make guidelines mutable in place — no versioning. Rejected: an in-flight ticket claimed against playbook-authoring@v1 should be judged against v1 even if v2 lands mid-implementation. Immutable versions plus explicit migration notes is the smallest correct shape.

Supersedes: none Superseded by: none yet

Amendment — 2026-04-29 (warp#698 / ADR-124)

The eight founding guideline list grows to include pipeline-step@v1 — typed I/O ports per ADR-085, block-composition over imperative per ADR-124, generic block name plus domain-specific pipeline name, single concern per block. constrained_by cites both ADR-085 (the typed-port decision) and ADR-124 (the block-composition principle). The guideline's forbidden_patterns includes func.*\(.*RunContext\) for new code under cmd/server/pipeline/** — existing matches are legacy graduation backlog (warp#707), not gate violations.