# Finding Format

The structured shape every audit finding must take — whether produced by a subagent or by a direct audit pass. Shared authority for the playbook and variant finding judgments.

## Why structure matters

Findings flow into a prioritization table and into this run's report. Without structure, the auditor cannot compare leverage across categories, and the change author cannot judge effort honestly. The format forces evidence before opinion.

## Template

```markdown
### [CATEGORY-NN] Short imperative title

- **Evidence**: `path/file.ts:123` — one-sentence description of what's there.
  (Repeat per location; 2–5 strongest locations, note "and ~N similar sites" if widespread.)
- **Impact**: What goes wrong / what's being paid because of this.
  Concrete: "every order-list render issues 1+N queries", not "suboptimal".
- **Effort**: XS | S | M | L | XL — for the *fix*, including tests.
  (Morning Star agent-oriented effort scale.)
- **Risk**: What the fix could break; LOW/MED/HIGH plus one line why.
- **Confidence**: HIGH (read the code, certain) / MED (strong signal, needs verification) /
  LOW (smell, needs investigation). LOW-confidence findings may be reported, but as an
  "investigate" item, not a "fix" item.
- **Fix sketch**: 1–3 sentences, enough to judge effort honestly; when the finding is structural, the sketch names the restructuring move (per the `## Structural remedies` list). Not the fix itself — just enough to judge effort honestly.
```

## Structural remedies

Choose the smallest fix that preserves required behavior and safeguards. Name the deletion, canonical helper to reuse, duplicate branches/near-copies to merge, or responsibility to move/split; preserve meaningful differences. Split by independent responsibility, never line count or helpers that only shorten a function. Prefer removing moving pieces over relocating complexity; do not add a layer, dispatcher, type, dependency, or config merely because a smell suggests one.

## Category codes

| Code | Category |
|------|----------|
| `BUG` | Correctness / bugs |
| `SEC` | Security |
| `PERF` | Performance |
| `TEST` | Test coverage |
| `DEBT` | Tech debt & architecture |
| `DEP` | Dependencies & migrations |
| `DX` | DX & tooling |
| `DOCS` | Documentation |
| `DIR` | Direction (features & roadmap) |

## Machine-readable findings file (`mstar audit scaffold`)

The scaffold command `mstar audit scaffold <findings-file> [--level <quick|standard|deep>] [--round <n>] [--reviews-dir <dir>]` writes this run's audit report (`README.md`) into `{REVIEW_DIR}/<date>-codebase-<level>-<round>/` from a findings file. The file is either a bare JSON array of finding objects or an object `{findings, needsVerification?, hardeningChecked?}`. This section is the contract for the finding objects; what the engine enforces is **carrier acceptance only** — deciding whether a finding is reportable, choosing its severity wording, grouping one root cause, and keeping a claim out of Needs-verification stay reviewer judgement.

### Field mapping and defaults

| JSON field | Engine field | Present-value contract | Absent-field behavior |
|---|---|---|---|
| `title` | `title` | String, trimmed; visible nonempty content | Usage error, exit 2 |
| `description` | `impact` | String, trimmed; visible nonempty content. No `impact` JSON alias | Usage error, exit 2 |
| `priority` | `priority` | `P1` \| `P2` \| `P3` | Usage error, exit 2 |
| `effort` | `effort` | `XS` \| `S` \| `M` \| `L` \| `XL` | Usage error, exit 2 |
| `risk` | `risk` | `LOW` \| `MED` \| `HIGH` | Usage error, exit 2 |
| `category` | `category` | `bug` \| `security` \| `perf` \| `tests` \| `tech-debt` \| `migration` \| `dx` \| `docs` \| `direction` | Usage error, exit 2 |
| `confidence` | `confidence` | `HIGH` \| `MED` \| `LOW`; explicit value is preserved (previously discarded) | Defaults to `MED` |
| `evidence` | `evidence` | Array of non-empty strings (legacy free text, rendered as-is) or `{file, line?, description}` location objects; item order preserved; `[]` valid | Defaults to `[]` |
| `evidence[i].file` | `AuditEvidence.file` | Required safe repository-relative POSIX path | Usage error, exit 2 |
| `evidence[i].line` | `AuditEvidence.line` | Positive integer when present | Omitted; rendered without a colon or invented line |
| `evidence[i].description` | `AuditEvidence.description` | Required non-empty string | Usage error, exit 2 |
| `fingerprint` | `fingerprint` | Non-empty string; passed through untrimmed/unnormalized | Omitted; the engine never invents one |
| `trace` | `trace` | Array of `{kind, file, line, scope, description}`; all five members required | Omitted |
| `severity` | `severity` | Object with all of `likelihood`, `impact`, `overall`, each `informational` \| `low` \| `medium` \| `high` \| `critical` | Omitted; never inferred from priority, risk or confidence |
| `fixSketch` | `fixSketch` | Optional non-empty string | Omitted when absent |
| `verification` | `verification` | Optional non-empty string | Omitted when absent |

A finding is a finding only with non-empty `title` and `description` and valid enums; in `title`, `description`, `priority`, `effort`, `risk`, `category`, `confidence`, `evidence`, `fingerprint`, `trace`, and `severity`, a supplied `null`, wrong type, malformed object, or invalid enum value is a usage error (exit 2, diagnostics name the field path without echoing submitted values), never silent omission or default. Omitting `confidence`, `evidence`, or any optional field is a valid choice, not an error. Authoring guidance such as "2–5 strongest locations" is advice, not an array gate — an accepted carrier is not automatically a reportable finding.

### Deterministic gates the engine runs

> **Engine check (when available):** if the harness engine runtime is present, `validateAuditFindingGates(findings)` runs inside `scaffoldAuditReport` before any file is written, so invalid findings exit 2 with no partial output. Skill text below remains authoritative when the runtime is absent. The gate checks, deterministically and only these:

- **Fingerprint** (when supplied): grammar `^[A-Za-z0-9][A-Za-z0-9._:/@+-]*$`, credential rejection (a value redaction would alter is rejected, never rewritten into a different identity), exact case-sensitive uniqueness in the batch, and strict ASCII ordering of the supplied subsequence. Absent fingerprints are skipped; out-of-order input is rejected, never sorted — positions control the rendered finding order. Mixed legacy/enriched batches are accepted. Choosing a stable root-cause identity, grouping one cause, and cross-run matching remain reviewer duties.
- **Severity**: every rank must be a valid enum value, and `severity.overall` must not exceed `severity.impact`. Nothing else is computed — the gate never infers severity and never proves the claimed impact.
- **Trace**: non-empty; a single step must be `entrypoint` or `sink`, longer traces must run `entrypoint` → `propagation…` → `sink`. Each step needs a positive-integer `line`, a safe typed path, and visible `scope`/`description`. Topology validation proves format, never reachability — the trace still means what the author claims: where data enters, how it travels, where it lands.
- **Evidence locations**: object evidence `file` and trace `file` must be repository-relative POSIX paths (no absolute/drive/UNC paths, backslashes, control characters, lone surrogates, empty/`.`/`..` segments, segments ending in a dot or space), and object evidence `line` must be a positive safe integer when present (`audit.finding.evidence.line`). Unsafe paths are never normalized into acceptance, and the engine never checks filesystem existence. Legacy string evidence is free text and is not path-checked.
- **Text**: `title`, `impact`, supplied `fixSketch`/`verification`, string evidence, structured evidence `description`s, and trace `scope`/`description` must contain visible content (at least one code point outside Unicode whitespace and default-ignorable code points; lone surrogates invalid). Multilingual content is never stripped.

Gate diagnostics use stable codes (`audit.finding.fingerprint.*`, `audit.finding.severity.*`, `audit.finding.trace.*`, `audit.finding.path.*`, `audit.finding.evidence.*`, `audit.finding.text.*`) and `findings[index].field` paths only — never raw submitted values, so credential material cannot leak through error output.

### Rendering consequences

Structured evidence renders `file:line — description` (or `file — description` when `line` is omitted); string evidence keeps its existing rendering. Supplied `fingerprint`, `severity`, and `confidence` persist into the report's two surfaces: the Findings table columns (appended after the Evidence column when any displayed row carries them: `# | Finding | Category | Impact | Effort | Risk | Confidence | Evidence [| Fingerprint][| Likelihood | Severity impact | Severity]`) and the finding's `## Finding detail` block. Each detail block carries the assessment fields plus the full Evidence list, an optional Trace table, Fix sketch and Verification — the run's report is its only artifact, so prose (including fenced command blocks) renders verbatim with its newlines and only the block heading is collapsed to one line. The engine neither enforces finding/lead mutual exclusion (a claim is either a finding or a Needs-verification lead by reviewer judgement — the lead carrier `{lead, how, evidence?}` has no fingerprint field) nor validates coverage; both stay with the auditor.

## Direction findings — adaptations

Direction findings (`DIR-NN`) use the same format with two field changes:

- **Impact** = product/user value (who wants this and why now), not "what's broken."
- **Confidence** = how grounded the evidence is (not certainty it's the right call).

Direction findings are usually pursued as design/spike work (investigate, prototype, define the API, list open questions), not as a build-everything change.

## What disqualifies a finding

- **No concrete case**: a location or suspicious shape alone is not enough. A defect claim names a reachable input/situation → resulting wrong behavior and cites what the code read or authorized check establishes; distinguish static inference from an executed observation. A test/structural finding names the concrete unprotected behavior or maintenance cost, not an invented product failure. Unsupported vague worries remain leads under the variant's evidence rules.
- **Documented decision within its bounds**: platform conventions or explicit ADR/knowledge/local-comment tradeoffs are not findings merely for being deliberate limits. Recognize existing bounded local decisions; retain the `simplify:` convention from `mstar-coding-behavior`, without requiring a new marker. Reopen only with evidence that expected use/load exceeds the stated ceiling, implementation drifts from the decision, or the choice is independently unsafe (security/data loss/required behavior). A comment is not immunity from a reachable defect.
- **Secret value reproduced**: never. Reference `file:line` and credential type only.
- **Could apply to any project**: direction suggestions without repo-specific grounding ("add dark mode", "add AI") are noise.

## Prioritization

Order by **leverage = impact ÷ effort, discounted by confidence and fix-risk**.

Tiebreakers:
1. Findings that unblock others (verification baseline, characterization tests) float up.
2. HIGH-confidence security findings float above equivalent-leverage non-security findings.
3. Prefer findings with a clean verification story.
4. "Not worth doing" is valid — record with one line of reasoning in the "considered and rejected" index section.
