# Self-Correction — Defect Inspection · Run Measurement · Iterate-or-Propose

The reference for **Mode 5** (`discover` harness). After a run finishes (Discovery → Ingestion → Evaluation → Cartography), the self-correcting step does THREE things, in order:

- **A. Inspect** the finished run against a built-in **defect checklist** → a concrete list of _behavioral defects_ in the prior steps (a list, never a vibe).
- **B. Measure** the run against a fixed **rubric** → a score + vibe band. This runs **every iteration, including the final one** — the self-correcting step ALWAYS evaluates the current run.
- **C. Iterate-or-propose.** Each defect is classified by ONE test — _does fixing it require editing a harness file (a rule or an agent)?_ — into **within-run** (auto-iterate on THIS target, no approval) vs **harness-changing** (the approval-gated proposal). The within-run loop improves the current run; the harness-changing list improves the NEXT, different target.

> **The two gates that bind this step (both unchanged):**
>
> 1. **Harness changes are approval-gated.** The main session presents `05-self-correction-proposal.md` and STOPS; nothing is applied until the user accepts per-item. Structural edits are flagged and never auto-applied.
> 2. **Cost/state/credential actions are confirmation-gated.** The within-run iterate loop is **read-only / no-cost only.** Any fix needing a Pass-2 / credit-spend / state-change / credential action routes through the explicit-confirmation gate (ingestion §7 rule 4, §9.2) — never the auto-loop. The loop can never silently spend money or mutate an app to chase a score.

---

## A. The defect checklist (what to inspect — by the mode that produced it)

Inspect the run and emit **every** defect that fired, each as: `{mode, defect, severity (high|med|low), evidence anchor (research/<target>/…), within-run | harness-changing}`. This list is the load-bearing output of stage A — never skip a category silently; "none in this category" is itself recorded.

### Discovery defects

- **Access axis mis-graded** — esp. `auth:have-login` asserted on a _claimed/expected_ login that was never live-probed (should be `auth:claimed` until confirmed); any `source`/`runtime`/`presence` value later contradicted by what Ingestion actually found. _(high — it mis-routes the whole run)_
- **Method graded from a label, not a retrieved artifact** — `openapi-verbatim` on a page merely _titled_ OpenAPI with no served, parseable spec. _(med)_
- **A predicted-available dimension came back blocked/absent** — Discovery over-promised. _(med)_
- **A sibling-declared count/host-list treated as a ceiling**, not a floor. _(low)_
- **Scope verdict wrong** — a suite/primitive/ambiguous/ethics call that should have fired (or wrongly did). _(high)_

### Ingestion defects

- **Redaction miss** — a secret-shaped value reached `raw/`. _(HIGH — also hard-zeros the safety axis)_
- **Method ≠ planned** — the capture used a weaker method than Discovery predicted, with no recorded reason. _(med)_
- **Missing contract artifact** — a `raw/` file the dimension's contract requires was not produced. _(med)_
- **Low completeness with no recorded gap/reason.** _(med)_
- **Blocked-where-predicted-available and not recovered** — no retry / second method / fold. _(med)_
- **Seam forked** — `api-path-catalog`/`feature-flags` written under a dimension's `raw/` instead of appended to `dimensions/_shared/` (ingestion §6). _(med)_
- **Same artifact double-mined** and the two counts read as corroboration rather than a range. _(med)_
- **Silent absence** — a dimension dropped with no `status: absent/folded` record. _(med)_
- **Went silent / looked hung** — a long fan-out with no summary-first partial (ingestion §8). _(low)_
- **An absence asserted that contradicts a sibling**, without a second-method re-check (ingestion §7 rule 10). _(med)_
- **A negative realtime finding with no library-tap evidence** — "vendor X carries no traffic" / "the dashboard has no realtime layer" asserted from a `window.WebSocket` wrapper alone, when a realtime SDK global or a `pusher*`/`ably-*`/`*Transport*` storage key was present and the mandatory `bind_global`-class tap (ingestion §9.4) was never installed. _(HIGH — a constructor wrapper is structurally blind to a boot-time socket, so this converts instrument blindness into a published refutation)_

### Evaluation defects

- **A conflict left flagged-but-unresolved** where the direct-observation tiebreaker applies. _(med)_
- **Over-promotion** — a confidence band raised without ≥2 _independent_ dimensions (or on two views of one artifact). _(med)_
- **Inference overrode a direct observation.** _(high)_
- **A single-source / low-confidence claim stated as fact.** _(med)_
- **Marketed write/execution behavior promoted to fact** on a `write_side_observed:false` session. _(med)_
- **A gap not surfaced**, or a count-divergence rendered as a conflict instead of a range. _(low)_
- **The published-vs-app-own API diff omitted** where a published API exists. _(med)_

### Cartography defects (Mode 4 — the experiential / coverage layer)

- **Marketed feature not located and not dispositioned** — a website/docs/changelog feature with no in-product route AND no `edition-gated | roadmap-not-shipped | over-claim | deeper-than-looked` disposition in `feature-coverage.md`. _(med — an unfilled coverage hole masquerading as "covered")_
- **`not-yet-reachable` surface mis-filed as `deeper-than-looked`** — a shipped surface that does not render until a state change (deploy / connect / upgrade / first-run) filed as a navigation shortfall, sending the read-only auto-loop after something no amount of walking can reveal. Equally: an entitlement flag reading `true` treated as proof the surface is reachable, when the gate is two-factor (flag **and** state). _(med — it mis-routes the within-run loop and mis-reads an authorization boundary as sloppiness)_
- **Located-but-not-walked not queued** — a feature found in the nav but never navigated, absent from the within-run re-walk queue (so Mode 5 never closes it). _(med)_
- **Nav destination unmapped** — a top-level nav destination with no surface card in `information-architecture.md` (`ia_nav_complete:false` with no recorded reason). _(med)_
- **Primary flow not diagrammed** — an identified core journey (a captured write/auth/realtime/AI wire) with no `ux-flows.md` sequence diagram. _(med)_
- **Buried flagship not surfaced** — a marketing-promoted feature sitting at ≥D3 that the IA doc didn't flag as a positioning-vs-product gap. _(low)_
- **Depth not recorded** — surface cards missing the numeric `Dn` depth measure (so promoted-vs-buried can't be reconciled). _(low)_
- **Screenshot gap unrecorded** — primary surfaces without a screenshot AND no `screenshot_coverage` gap recorded (a silent visual-coverage miss vs an honest one). _(low)_
- **Write flow drawn as observed on a `write_side_observed:false` run** — a generate/submit journey diagrammed solid (observed) when it was only inferred. _(med — same family as the evaluation write-side cap)_

### Cross-cutting defects

- **Provenance frontmatter incomplete or dishonest** — `status`/`confidence` not matching reality. _(high)_
- **An absent dimension not recorded as a finding.** _(low)_

> The checklist is the floor, not the ceiling — a run may exhibit a behavioral defect not listed here. Record a novel defect with the same shape; if it would recur on a different target, route it harness-changing (and it becomes a checklist addition in its own right).

---

## B. The measurement rubric (the score — always computed)

**Six axes → /100** (re-weighted from the prior five to give the experiential layer first-class weight), so iterations are comparable and "deteriorating" is a real drop, not noise. The score is a **heuristic to catch regression and give the loop direction** — not a precise grade.

| # | Axis (max) | How to score |
| --- | --- | --- |
| 1 | **Dimensional coverage** (15) | `15 × (dimensions captured 'complete' ÷ dimensions reachable per the access vector)`; **−3** per dimension blocked where Discovery predicted available. |
| 2 | **Experiential / IA coverage** (20) | the Cartography scorecard, fed in: `8 × feature_location_rate/100` + `6 × flow_coverage/100` + `4 × screenshot_coverage/100` + `2 ×` (`ia_nav_complete` ? 1 : surface-cards ÷ top-level-destinations); **−2** per _claimed-but-not-located_ feature left **un-dispositioned**. Floored at 0. **When the session/experiential lane is deliberately deferred — gated by policy or no-login, NOT a tool failure — set `experiential_gated:true`:** keep the honest 0 on observed `flow_coverage`/`screenshot_coverage` (the run truly lacks live coverage), but **record the cause** beside the axis so cross-run comparison can tell a policy-deferral from a sloppy coverage miss — the annotation documents, it does **not** inflate the score. _(the heavily-weighted axis: a run that collects feature pages but never locates / walks / diagrams / screenshots them scores low here.)_ |
| 3 | **Provenance integrity** (15) | `7.5 × (captures whose §5.4 dump manifest is satisfied ÷ all captures)` + `7.5 × (evaluation claims **backed by a resolvable `raw/` artifact** ÷ all claims)`. **Score the artifact, not the syntax.** An anchor is satisfied only if the cited file exists AND contains the claim's key token — a well-formatted citation pointing at a file that does not contain the value scores **zero**, not full marks. (Origin: layer-ai — a finding promoted on *"five independent dimensions"* cited a dimension in which the relevant string appears zero times; the evidence was real in-session and never dumped. Scoring anchor *presence* rewarded exactly the behaviour that lost it — see ingestion §5.3.) |
| 4 | **Reconciliation correctness** (20) | start 20; **−3** per weighting violation (over-promotion, tiebreaker skipped, single-source-as-fact, count-divergence-as-conflict, published-vs-app diff missing where a public API exists, a UX-flow drawn observed on a `write_side_observed:false` run). |
| 5 | **Calibration & safety** (15) | gaps/absences recorded honestly + confidence bands honest, scaled to 15. **A redaction miss hard-zeros this axis** and flags the run `unsafe` regardless of the others. |
| 6 | **Defect load** (15) | `15 − Σ(severity weight)` over **defects still OUTSTANDING at the end of the run** (high=3, med=2, low=1), floored at 0. **A defect found AND fixed within the run is charged on its home axis only, not here** — it is already paid for by the axis it damaged, and charging it twice makes auditing strictly dominated by not looking. Record every defect found in the ledger regardless (the list stays monotonic and honest); the *score* charges only what remains broken. |

**Rollup:** sum the six → `score/100` (max 15+20+15+20+15+15). **Vibe band:** ≥85 strong · 70–84 solid · 50–69 thin · <50 weak. Plus a one-line gestalt (e.g. "solid wire capture, but cartography located only 9 of 17 marketed features and screenshotted none").

**The iteration ledger** (written into `05-self-correction-proposal.md`):

| iter | score | Δ | dim | **ia** | prov | recon | calib | defects | within-run fixes applied | remaining |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 0 | … | — | … | … | … | … | … | … | (baseline) | … |
| 1 | … | +n | … | … | … | … | … | … | re-walked 4 surfaces; captured screenshots; located Rules engine | … |

---

## C. Iterate-or-propose (the loop)

**Classify every stage-A defect by one test:** _does fixing it require editing a rule or an agent file?_

- **No → within-run.** Fixable by re-running a Mode 1–4 step on THIS target, better (re-collect a dimension deeper, re-run a reconciliation pass, do a missed entitlements mine, run the §7-rule-10 second probe, append a forked catalog into `_shared/`, recover a blocked dimension after a trivial fix). **For Cartography defects this is the live re-walk:** re-enter the product (read-only nav) and walk the `feature-coverage.md` _located-but-not-walked_ queue, capture the missed screenshots, and locate any _deeper-than-looked_ marketed feature — then re-run Mode 4 and re-score. (A _claimed-but-not-located_ feature that is **edition-gated** / **roadmap-only** is _dispositioned_, not re-walked — re-walking can't find what isn't shipped.) **The main session auto-iterates these — no approval** (within the read-only/no-cost boundary; the live re-walk is pure navigation + screenshots, so it stays inside it — a feature is never _triggered_ to be located).
- **Yes → harness-changing.** A generic methodology fix the NEXT different target would also hit → accumulate into `05-self-correction-proposal.md`, **approval-gated** (present + STOP).
- A single defect may be **both**: fix it within-run now AND propose the rule so it never recurs.

### The loop (driven by the main session, not a sub-agent)

```
score[0] = measure(run); best = run; iter = 0
loop:
  defects   = inspect(run)                       # stage A
  within    = defects.where(within-run AND read-only/no-cost)
  harness   = defects.where(harness-changing)
  if within is empty:                  break     # CONVERGED
  apply within-run fixes → run'                  # re-run cheapest affected Mode 1–4 steps (incl. live re-walk)
  iter += 1; score[iter] = measure(run')         # stage B — ALWAYS measure
  if score[iter] < score[iter-1] - TOL:          # REGRESSION (clear drop, not noise)
      run = best; break                          #   revert to the best prior run, stop
  best = run = run'
  if iter >= CAP:                      break      # CAP ≥ 5
present(harness) as 05-self-correction-proposal.md + the iteration ledger    # approval gate
```

**Stop conditions** (any one ends the loop):

1. **Converged** — no within-run defects remain.
2. **Regression** — `score[iter] < score[iter-1] − TOL` → **revert to the best prior run and stop** (never let the loop make the run worse chasing a defect). `TOL` is a small tolerance (≈2 points) so a within-noise wobble doesn't false-stop — stop only on a _clear_ drop; always keep the highest-scoring run as `best`.

   > **Distinguish a _quality_ regression from a _measurement_ correction — they look identical in the ledger and must be handled oppositely.** If a score drop is caused by **discovering that a PRIOR iteration's score rested on an error** (a claim it scored well for turns out false; a redaction sweep that "passed" is found to have been incapable of matching the target shape; a coverage number counted an unverified surface), that is **NOT a regression — do NOT revert.** Reverting would restore the *false* artifacts and discard the correction, i.e. the loop would actively select for undetected error. Keep the corrected iteration, record the earlier score as **superseded (inflated)** rather than as the `best` to beat, and note the correction in the ledger. **The revert rule applies only when a fix made the artifacts themselves worse.** _(Corollary — defect load is monotonic: a defect, once found, stays on the ledger permanently, so a run that verifies harder scores its axis-6 lower than one that never looked. Read a falling score alongside the defect list; "score fell because we found real errors" is a run getting **better**, and the ledger must say so.)_
3. **Cap** — `iter` hits the cap. **The cap is ≥ 5**: the loop must be _willing_ to run at least five iterations on a still-improving run before giving up; raise it while the score keeps climbing.

---

## Output — `05-self-correction-proposal.md`

```markdown
---
target: <slug>
proposed_at: <YYYY-MM-DD>
status: PROPOSED # → APPLIED only after per-item approval (harness-change rows)
awaiting_approval: true
run_score: <final /100> # the score of the run AS SHIPPED (post-iteration)
vibe: strong | solid | thin | weak
iterations: <n>
stopped_on: converged | regression | cap
---

# <Target> — Self-Correction (Mode 5)

## Run measurement (the final score + per-axis breakdown + the one-line vibe gestalt)

## Iteration ledger (the per-iteration table — score trajectory + fixes applied each round)

## Behavioral defects found (stage-A list: mode · defect · severity · evidence anchor · within-run|harness)

## Within-run fixes applied (what the auto-loop re-ran on this target, and the score delta each bought)

## Harness-change proposals (APPROVAL-GATED table: # · sev · additive|structural · finding+anchor · fix · target file)

## Approval gate (present + STOP; main session applies accepted items, flips each to APPLIED, logs)
```

A **thin harness-change table with a high `run_score` and `stopped_on: converged`** is the success signal — the run self-corrected to a good state and left little for the harness to learn. A **batch** of runs additionally rolls up into a cross-target synthesis that promotes only patterns ≥2 _independent_ targets hit (guard against a homogeneous batch: 6/6 of one category is one vote, not six).

---

## How Mode 5 uses this reference

Stage A — inspect against the checklist (now including the **Cartography** category), emit the defect list. Stage B — score the run (always), the **experiential / IA coverage** axis fed by the Cartography scorecard. Stage C — split within-run vs harness-changing; **auto-iterate** the within-run set (read-only/no-cost, ≥5 iterations capable, stop on regression/convergence/cap), re-measuring each round — Cartography defects iterate as the **live read-only re-walk** of the located-but-not-walked queue; **accumulate** the harness-changing set into the approval-gated proposal. Present the proposal + ledger and STOP. The main session applies accepted harness rows, flips them to APPLIED, logs the change, and (per ingestion / the orchestrator) mirrors to the harness home. Promote only what the next, different target would hit; prefer additive edits; structural edits are flagged, never auto-applied.
