---
name: correction-signal-derive
description: Derive a run's objective, artifact-based correction signal from git/PR/CI after the run settles, and persist it via log_correction_signal (revision 1). Read-only against every repo; the ground-truth half of the north-star numerator (O-INTEGRITY KR-A). Use post-merge, per settled run.
---

# correction-signal-derive — The Artifact-Derived Correction Signal

This skill computes the OBJECTIVE half of the north star: what the artifacts say humans had to
correct, derived **read-only** from git/PR/CI after a run settles, severity-weighted per the
PM's pre-committed [weight table](/planning/evals/o-integrity-severity-weights.md), and written
to `correction_signals` via the `log_correction_signal` MCP tool. It COMPLEMENTS the builder's
self-report (`agent_runs.human_corrections`) — pre-commit conversational steering is
git-invisible and stays the self-report's lane; the **delta** between the two is the point
(KR-B consumes it).

> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)).
> Level 1 frontmatter above is the trigger; this Level 2 body is the full procedure.

## Non-negotiable stance

- **Read-only (D01, rubric F1).** You derive FROM repos; you never write TO them. After every
  derivation run, `git status --porcelain` on the product repo must be empty. Any write is a
  disqualifying defect. The only write this skill performs is `log_correction_signal`.
- **Secret-safe (D08, rubric F1).** Read env-var *names* only. Never store or log a secret
  value. If derivation surfaces a leaked secret, record it **by path only** and flag the PM.
- **Provenance or it didn't happen.** Every component cites a concrete SHA / PR # / CI run id.
  An un-sourced component is a defect — drop it or source it.
- **Never guess an attribution.** A commit you cannot confidently classify is `ambiguous`:
  flagged to the PM, held OUT of the score (rubric F2). Silently scoring a handoff/unrelated as
  a correction manufactures the exact watermelon this exists to kill.
- **Weights are the PM's, versioned.** Score only with the current committed
  [weight table](/planning/evals/o-integrity-severity-weights.md); cite its version in
  `severity_weights_version`. Never retune weights inside a run — a re-weight **or formula change**
  is a PM escalation and a new version.

## Preconditions

1. The run is **settled**: its PR is merged or closed, and the settle window context is known
   (default `window_days: 14` from the sprint contract).
2. The run has an `agent_runs` row (`run_id`, `pre_run_contract`) — the contract's scope defines
   the attribution boundary.
3. `correction_signals` has **no revision for this run yet** (revision 1 is this skill's; late
   revisions belong to the linkage settling job).

## Procedure

1. **Load the run.** `get_agent_runs({initiative_id})` → the run's `run_id`,
   `pre_run_contract` (scope: in-contract files/areas), and the self-reported
   `human_corrections` (recorded, not trusted).
2. **Fix the anchors.** Identify the agent's HEAD SHA (last agent-authored commit of the run)
   and the PR #. Record both.
3. **Collect candidate evidence (read-only)**, three sources:
   - **git** — commits after agent HEAD touching the run's output: agent-authored lines
     changed/removed, reverts/force-pushes on agent lines.
     **Squash-merge fallback (the pmos default — verified 2026-07-03):** per-commit attribution
     dies at squash; diff the **agent branch tip vs the merged result** instead, and treat the
     delta as the human-correction surface.
   - **PR** — changes-requested review rounds; review comments that forced diffs.
   - **CI** — human-attributable red→green cycles on the run's commits.
4. **Attribute every human-change-after-agent-HEAD** (scope from `pre_run_contract`):
   | Class | Rule | Scores? |
   |---|---|---|
   | `correction` | agent-authored lines, in-contract-scope files, within the settle window | yes |
   | `handoff` | out-of-scope files — human doing undelegated work | no |
   | `unrelated` | different subsystem riding the PR | no |
   | `ambiguous` | signals conflict or motive unrecoverable | no — **flag to PM** |
   Calibrate against the [labeled fixtures](/planning/evals/fixtures/o-integrity/attribution-fixtures.json)
   (rubric floor: ≥ 0.70 balanced accuracy).
5. **Classify severity per correction component** using the
   [weight table](/planning/evals/o-integrity-severity-weights.md) (revert > behavioral >
   contract > config-docs > mechanical > reformat). Sanity-check against the
   [severity pair fixture](/planning/evals/fixtures/o-integrity/severity-pair-fixture.json):
   no inversion (rubric F3).
6. **Score** per the weight table's **current** rule — **weights-v2** (RATIFIED 2026-07-14):
   `Σ [ class-weight × lines(c) ] ÷ delivered_lines`, over correction components only — a bounded,
   scale-invariant fraction. (weights-v1's log-damped formula is retained in the weight table as
   history; always cite the version you scored under in `severity_weights_version`.)
7. **Verify cleanliness**: `git status --porcelain` empty on every repo touched; no secret
   value anywhere in components/attribution/notes.
8. **Persist revision 1**: `log_correction_signal({initiative_id, run_id, revision: 1,
   artifact_correction_score, components, attribution, severity_weights_version,
   settle_window_days, source: 'correction-signal-derive@v2'})`.
   Duplicate `(run_id, revision)` = this run was already derived — stop, do not rewrite.
9. **Report the delta**: state self-report vs artifact score side by side. Material divergence
   is a finding, not a failure — it is the signal KR-B calibrates on. Log friction if the
   derivation itself fought you.

## What this skill never does

- Write to any product repo, or leave one dirty (F1).
- Store or print a secret value (F1) — names/paths only.
- Score an `ambiguous` or out-of-scope change (F2).
- Let raw line count outrank severity class (F3).
- Append revisions > 1 or flip `settled` — that is the
  [followup-corrective-linkage](/planning/prd/pmos-followup-corrective-linkage-prd.md) job's lane.
- Decide anything: the score informs the PM; the Acceptance Gate stays human (D12).

## Level 3 sub-cases

- Rebase-heavy or force-pushed histories where even tip-diff is ambiguous: author
  `derive-ambiguous-history.md` on first need (flag the whole run `ambiguous` rather than guess).
