Skip to content

Counter-Prior Catalogue

Each file in this directory documents one corpus pathology — a pattern LLM training data biases models toward — and Dazzle's counter-prior: the right shape, why the wrong shape is load-bearing-bad, and which substrate layer enforces the fix.

The three substrate layers (see dev_docs/2026-05-25-substrate-audit.md and the project memory project_prior_correction_substrate.md for the framing):

  • Grammar (layer 1) — the DSL excludes the bad shape by construction. Strongest counter-prior.
  • Inference (layer 2) — agent instructions / KB entries injected at the right moment. This catalogue.
  • Filter (layer 3) — drift gates / conformance / fitness that catch what the first two layers let through.

Each entry's frontmatter declares which layer it primarily belongs to, plus triggers (natural-language fragments and code-shape regexes) that the bootstrap and knowledge counter_prior MCP path use to surface it at relevant moments.

Entries are agent-scannable: each row is a counter-prior that prevents a wrong emission.

Active entries

  • domain-coupled-keywords — Naming DSL keywords / field names after the source spec's domain (pupil_card, customer_id). Domain values belong at the adapter layer; the grammar stays generic.
  • duplicated-parent-fields — Copying a parent's field onto a child alongside the ref. The Repository auto-includes ref data; the copy goes stale, the framework can't keep it in sync.
  • exceptions-as-control-flowtry/except: pass / fallback control flow / EAFP misused. Counters silent failures in user app code.
  • god-entities — Single-entity-spans-everything modelling. Decompose through refs so RBAC, scope, and lifecycle match conceptual boundaries.
  • hand-rolled-soft-delete — Manual deleted_at column + per-surface scope: deleted_at = null. Use the soft_delete: keyword (#1218); the substrate filters tombstones centrally.
  • hand-rolled-temporal — Manual start_date/end_date + per-surface current-row scopes + custom as-of handlers. Use temporal: (#1223); keyword wires auto-filtering, uniqueness, URL param, and latest_one traversal.
  • magic-string-typing — bare str for identifier classes (user_id: str), status discriminators, or lookup keys. Pairs with dazzle.types.NewType, enum.StrEnum, and PA-LLM-10.
  • n-plus-one-in-user-code — Naive ORM-shaped loops over related rows in app/ code. Framework paths aggregate centrally; user code re-introduces N+1 unless explicitly batched.
  • optional-instead-of-resultdef f(...) -> T | None collapsing multiple distinct failure modes into a single None sentinel. Pairs with dazzle.result and PA-LLM-09.
  • polymorphic-associations — Rails-style belongs_to :commentable, polymorphic: true and (subject_type, subject_id) discriminator pairs. Closed by ADR-0027; pairs with the four-question interrogation.
  • raw-sql-string-building — f-string / + SQL in user code. Framework paths parameterise by construction; raw SQL bypasses the predicate algebra and re-introduces injection class.
  • regex-in-dsl-parserre.compile in parser code. A regex on a DSL shape signals a missing IR type. ADR-0024 + drift gate; allowlist sits at zero.
  • shell-without-strict-mode — Shell scripts missing set -euo pipefail. Silent continuation past failed commands is the corpus shape and the highest-leverage one-line fix in the catalogue.
  • stringly-typed-refscustomer_email: str instead of customer: ref Customer. The FK graph + scope predicate algebra depend on typed refs.
  • subtype-polymorphism-defaultsubtype_of: reached for reflexively whenever a spec mentions "variants of X." Walk the three alternatives (separate entities, state machine, nullable fields) first.

Adding a new entry

  1. Write docs/counter-priors/<id-in-kebab-case>.md with YAML frontmatter (see existing entries for the schema).
  2. The four sections ## The corpus prior / ## Wrong shape / ## Right shape / ## Why this matters here are mandatory — the drift test enforces them.
  3. Bump SEED_SCHEMA_VERSION in src/dazzle/mcp/knowledge_graph/seed.py so deployed KGs re-seed.
  4. Add a one-line entry to this index.
  5. The drift test (tests/unit/test_counter_priors_drift.py) will fail until every file is well-formed and every KG row maps back to a markdown file.