Run-record integrity: typed-comment validation and suite provenance stamp #57

Closed
opened 2026-08-24 09:54:43 +00:00 by jbr870 · 50 comments
Owner

PREQ: Run-record integrity — typed-comment validation + suite provenance stamp

Created: 2026-08-24 · Panel-amended: 2026-08-24 (Tier 3, all five lenses)

Problem

The Phase Outcome ledger is the pipeline's only durable account of what happened on a feature,
and it is untrustworthy on both of its axes — what it counts and what it can attribute.

What it counts (#54, reproduced live on #50): any comment whose first line merely contains
a schema token (e.g. phase-outcome:v1) is absorbed into the ledger with no shape validation.
Three consequences, in severity order:

  1. Silent, permanent ordinal corruption. The post helper derives the next ordinal from the
    count of scanned comments. One stray note made #50's next Phase Outcome due to mint as
    PO-50-7 while PO-50-6 did not exist — a gap that trips the density check on every
    subsequent read, forever, naming nothing.
  2. Opaque hard failure on read. The reader died with a bare
    jq: null (null) only strings can be parsed — no comment id, no field, no diagnosis. Every
    consumer that funnels through it (including /dev:resolve decision discovery) dies the same way.
  3. The repair looked forbidden. The stray comment appeared in the scan, so contract §8
    immutability seemed to protect it — the very defect made its own fix look illegal.

The exposure is a family, not a one-off: every typed comment schema
(decision-resolution:v1, work-unit-outcome:v1, qa-report:v1, …) is matched the same
substring way. And the trigger is ordinary: the offending header was written by an agent
reaching for the nearest-looking schema name for a note that had no schema.

What it can attribute (#34, record half): nothing records which suite text produced a Phase
Outcome. On the dev machine the skill text is a live checkout that received 20+ commits during
one measured run; on a consumer machine it is an installed release — and in the one real
consumer run, the manual "record the version" runbook step was not done (UAT-F33). A "resolved
at HEAD" verification therefore says nothing about what text a given run actually followed, and
cross-machine or cross-day comparisons are confounded.

Users:

  • Primary: the orchestrator model mid-run — it needs posts that cannot corrupt the ledger and
    reads that name their failure instead of dying opaquely.
  • Secondary: the operator and the meta lane (retrospectives, /meta-amend verification) —
    they need any Phase Outcome to be attributable to the exact suite text that produced it.

Current state: substring header matching, count-based ordinals, bare jq stack errors, no
provenance field; attribution rests on an operator remembering a README step (measured: not done).

Proposed Solution

Make the typed-comment record defensively written, defensively read, and self-attributing:

  1. Anchored header matching for all typed schemas. A comment is a typed record only if its
    first line matches the schema's declared header shape (<!-- {schema} id=… skill=… -->-class,
    per schema; the exact pattern per schema is stated in that schema's doc — a plan deliverable).
    A near-miss (schema token present in the first line, anchored shape absent) is never collected —
    and is surfaced as a warning naming the comment, so a typo'd real record cannot be silently
    dropped. A schema token on a non-first line is a plain comment, not a near-miss. Schema
    header shapes are mutually exclusive by construction (the schema name is the first token).
  2. Parsed-ordinal minting. The Phase Outcome post helper derives the next ordinal as
    max(ordinal parsed from id) + 1 over records that actually parse — never from a count of
    scanned comments. Over an empty ledger this mints ordinal 1. Over a ledger with a
    pre-existing gap it continues from the max (it does not fill gaps; repairing
    already-corrupted ledgers is out of scope).
  3. Refuse loudly on an unreadable collected record. If a comment passes the anchored match but
    its body cannot be parsed, the writer refuses to mint and the reader fails with a diagnosis —
    never a bare jq error. Both name the comment id and the reason (the missing attribute or the
    unparseable element, to the precision the parser has), and the refusal names the sanctioned
    repair route (fix or strip the malformed comment — which is not §8-protected, because a
    comment that does not parse as a record is not a record). A record tool never counts, and
    never writes past, something it cannot read.
    The asymmetry with (1) is deliberate: a near-miss header makes no well-formed claim to be a
    record, so it is warned about and excluded; an anchored header is such a claim, and a claim
    the tooling cannot read must stop the writer rather than be guessed around.
  4. Suite provenance stamp on Phase Outcomes. Every newly posted phase-outcome:v1 records the
    identity of the suite text that produced it, resolved from the suite root (the resolved
    plugin/skills root the run executes from) by one probe with no mode flag and no operator
    declaration: root inside a git work tree → commit SHA + dirty/clean marker; otherwise →
    the release version read from the plugin's own manifest at that root
    (.claude-plugin/plugin.json), which every install carries. If the probe cannot decide
    (e.g. git unavailable mid-probe, manifest missing), the stamp records an explicit
    unknownprovenance never blocks a post. Existing records without the field remain
    readable.

Scope: Standard — matching/diagnosis fixes apply to all typed schemas at the code path
they share (fix the family once); the new provenance field lands on the Phase Outcome schema
only (whether as an optional :v1 field or a :v2 is a plan-time decision).

User Stories

  • As the orchestrator model, I want a stray or malformed comment to be incapable of corrupting
    the Phase Outcome ordinals, so that the ledger's density invariant survives contact with
    imperfect input.
  • As the orchestrator model, I want a failed ledger read to name the offending comment and field,
    so that I can repair the actual cause instead of debugging a jq stack trace.
  • As the operator, I want every new Phase Outcome to carry the suite SHA or release version that
    produced it, so that run behaviour is attributable after the fact on both machines.
  • As a meta-lane reviewer, I want "verified at HEAD" claims to be checkable against what a run
    actually executed, so that retrospective findings rest on evidence rather than assumption.

Acceptance Criteria

"Warns/warning" below always means: a message on the helper's diagnostic (non-data) output
stream that includes the forge comment id of the comment it concerns. Warnings recur on every
affected post/read until the stray comment is cleaned up — persistence is intended.

  • AC1: Given a ledger of N well-formed Phase Outcomes with contiguous ordinals 1..N, plus one
    comment whose first line contains phase-outcome:v1 but does not match the anchored header
    shape, when a new Phase Outcome is posted, then the new record is minted at ordinal N+1
    (no skip), and the post warns, naming the near-miss comment's id.
  • AC2: Given the same ledger, when the ledger is read (read-all and read-latest), then the
    read succeeds (exit 0), returns exactly the N parsed records, excludes the near-miss, and
    warns naming its comment id.
  • AC3: Given a comment that matches the anchored header shape but whose body cannot be parsed
    as the schema, when a new Phase Outcome is posted, then the post exits non-zero, no new
    comment exists on the issue afterwards, and its error names the offending comment id, the
    missing/unparseable element, and the repair route.
  • AC4: Given the same unparseable-body record, when the ledger is read, then the reader exits
    non-zero with a diagnosis naming the comment id and the missing/unparseable element — never
    an unhandled jq error.
  • AC5: Given a non-Phase-Outcome typed schema (e.g. decision-resolution:v1) and a near-miss
    comment for it, when that schema's records are scanned through the shared matching path,
    then the near-miss is excluded with a warning; and the ledger consumers enumerated under
    Dependencies, exercised against such a ledger, complete without error.
  • AC6: Given an empty ledger (no Phase Outcomes yet — every feature's first post), when a
    Phase Outcome is posted, then it is minted at ordinal 1; and a read of an empty ledger
    reports empty without error.
  • AC7: Given a run whose suite root is inside a git work tree, when a Phase Outcome is
    posted, then the record carries the checkout's commit SHA and a dirty/clean marker —
    demonstrated in both a clean and a dirty state.
  • AC8: Given a run whose suite root is not inside a git work tree (an installed plugin), when
    the same post invocation runs unchanged, then the record carries the release version that
    the manifest at the suite root declares.
  • AC9: Given a suite root where the probe cannot determine either identity, when a Phase
    Outcome is posted, then the post still succeeds and the record carries an explicit
    unknown-provenance marker.
  • AC10: Given a pre-feature ledger whose records were all written by the post helper (and so
    are well-formed), when it is read with the new tooling, then the read exits 0, returns the
    same records as the pre-feature tooling did, and emits no warnings.

Out of Scope

  • Pinning — preventing suite text from changing mid-run. Split to #56 (narrowed: consumers
    are already immutable per #50; only the dev box's live checkout can move).
  • The adapter primitive-existence defect (#55) — an unimplemented pipeline_status looking
    like a broken helper. Same "diagnose, don't stack-trace" principle, different surface.
  • Repairing already-corrupted ledgers — a pre-existing ordinal gap is continued past, not
    filled or healed; the density check's existing behaviour on legacy gaps is unchanged.
  • Retrofitting the provenance field onto already-posted Phase Outcomes — backward
    readability is required; backfill is not.
  • Concurrent-writer races — two simultaneous posts to one issue computing the same ordinal.
    The existing collision check keeps its role; a single-orchestrator-per-issue assumption stands.
  • Treating ~/.claude/plugins/installed_plugins.json as an authority — its path and shape
    are Claude Code's, not ours; the version authority in the installed case is the plugin's own
    manifest at the suite root.
  • Changing contract §8 immutability rules — §8 protects records; this feature only makes
    explicit that a comment which does not parse as a record is not one, so repairing it is
    sanctioned. The rule itself is untouched.

Dependencies

  • Forge contract & adapters: the anchored-match change lands at the comment-scanning path
    shared by the four adapters (local-fs, tea-cli, glab-cli, gh-cli); the contract
    (forge-contract.md) and each schema doc must state the exact header pattern being enforced.
    The plan must confirm the match genuinely lives at one shared path — if it turns out to be
    per-adapter, family-wide coverage is a real cost to re-estimate, not a free win.
  • Schema versioning decision (plan-time): whether the Phase Outcome schema gains an optional
    field or becomes :v2 is deferred to /dev:technical-plan — the retry-guard normalisation and
    both readers parse these records, and every already-posted PO lacks the field.
  • Consumers of the ledger (the AC5 verification set): /dev:resolve decision discovery, the
    integrate/promote resume detections, and the QA retry guard — all read through the affected
    helpers; their behaviour on the new diagnostics must not regress.
  • External systems of record: the forge itself (Gitea via tea-cli here) — comment first-lines
    and ids are read back through adapter primitives; no other external system is touched.

Timeline

Milestone Date Notes
Requirements complete 2026-08-24
Development complete
QA complete
UAT approved

Notes

  • Constraints: helper tier stays bash ≥ 3.2 + jq + git (CLAUDE.md baseline); warnings and
    refusals must identify the failing comment per the "a gate's failure output identifies the
    failure" rule (#49's principle, applied here). Provenance detail for the plan: "dirty" =
    non-empty git status --porcelain scoped to the suite root's repo; symlinked roots resolve
    before probing.
  • Open questions: none at requirements level. Plan-time: optional-field vs :v2; exact layer
    for the anchored match (adapter primitive vs shared skill-facing path) — the AC constrains the
    behaviour, not the placement.
  • Key decisions: scope set to "family-wide matching, PO-only provenance"; writer behaviour on
    an unreadable collected record set to "refuse loudly" over "warn and continue" — this is the
    audit trail, and a stall is the loud failure. Both decided by the orchestrator under the
    2026-08-24 decision-escalation protocol.
  • Panel disposition (Tier 3, five lenses, 18 raw → 10 deduped concerns): all resolved by
    amendment in this revision. Notable: the two ordinal phrasings were reconciled (parsed-max
    rule is authoritative; AC1's "N+1" holds only for contiguous ledgers, now stated); "warning"
    and AC10's baseline were made observable; the installed-version authority was named (root
    manifest); probe edge cases got AC9 (explicit unknown, never blocks). Simpler-alternative
    challenges considered and declined with reasons: parsed-ordinals alone fixes minting but not
    the read crash, the silent drop, or the repair trap (matching is the diagnosis layer);
    provenance is kept in this feature because it shares the writer and the schema decision;
    the dirty flag stays because a dirty-checkout SHA without it misattributes exactly the runs
    the meta lane cares about.
  • Provenance (fan-in): consolidates #54 (bug, priority:high) and the record half of #34
    (tech-debt); #34's pinning half split to #56. #54's three-fix hypothesis and #34's
    two-machine ground-truth table (measured 2026-08-22) are the seed material.
# PREQ: Run-record integrity — typed-comment validation + suite provenance stamp **Created:** 2026-08-24 · **Panel-amended:** 2026-08-24 (Tier 3, all five lenses) ## Problem The Phase Outcome ledger is the pipeline's only durable account of what happened on a feature, and it is untrustworthy on both of its axes — **what it counts** and **what it can attribute**. **What it counts (#54, reproduced live on #50):** any comment whose first line merely *contains* a schema token (e.g. `phase-outcome:v1`) is absorbed into the ledger with no shape validation. Three consequences, in severity order: 1. **Silent, permanent ordinal corruption.** The post helper derives the next ordinal from the *count* of scanned comments. One stray note made #50's next Phase Outcome due to mint as `PO-50-7` while `PO-50-6` did not exist — a gap that trips the density check on every subsequent read, forever, naming nothing. 2. **Opaque hard failure on read.** The reader died with a bare `jq: null (null) only strings can be parsed` — no comment id, no field, no diagnosis. Every consumer that funnels through it (including `/dev:resolve` decision discovery) dies the same way. 3. **The repair looked forbidden.** The stray comment appeared in the scan, so contract §8 immutability seemed to protect it — the very defect made its own fix look illegal. The exposure is a **family, not a one-off**: every typed comment schema (`decision-resolution:v1`, `work-unit-outcome:v1`, `qa-report:v1`, …) is matched the same substring way. And the trigger is ordinary: the offending header was written by an agent reaching for the nearest-looking schema name for a note that had no schema. **What it can attribute (#34, record half):** nothing records which suite text produced a Phase Outcome. On the dev machine the skill text is a live checkout that received 20+ commits during one measured run; on a consumer machine it is an installed release — and in the one real consumer run, the manual "record the version" runbook step was not done (UAT-F33). A "resolved at HEAD" verification therefore says nothing about what text a given run actually followed, and cross-machine or cross-day comparisons are confounded. **Users:** - **Primary:** the orchestrator model mid-run — it needs posts that cannot corrupt the ledger and reads that name their failure instead of dying opaquely. - **Secondary:** the operator and the meta lane (retrospectives, `/meta-amend` verification) — they need any Phase Outcome to be attributable to the exact suite text that produced it. **Current state:** substring header matching, count-based ordinals, bare jq stack errors, no provenance field; attribution rests on an operator remembering a README step (measured: not done). ## Proposed Solution Make the typed-comment record defensively written, defensively read, and self-attributing: 1. **Anchored header matching for all typed schemas.** A comment is a typed record only if its **first line** matches the schema's declared header shape (`<!-- {schema} id=… skill=… -->`-class, per schema; the exact pattern per schema is stated in that schema's doc — a plan deliverable). A near-miss (schema token present in the first line, anchored shape absent) is never collected — and is surfaced as a warning naming the comment, so a typo'd real record cannot be silently dropped. A schema token on a *non-first* line is a plain comment, not a near-miss. Schema header shapes are mutually exclusive by construction (the schema name is the first token). 2. **Parsed-ordinal minting.** The Phase Outcome post helper derives the next ordinal as `max(ordinal parsed from id) + 1` over records that actually parse — never from a count of scanned comments. Over an empty ledger this mints ordinal 1. Over a ledger with a pre-existing gap it continues from the max (it does not fill gaps; repairing already-corrupted ledgers is out of scope). 3. **Refuse loudly on an unreadable collected record.** If a comment passes the anchored match but its body cannot be parsed, the writer refuses to mint and the reader fails with a diagnosis — never a bare jq error. Both name the comment id and the reason (the missing attribute or the unparseable element, to the precision the parser has), and the refusal names the sanctioned repair route (fix or strip the malformed comment — which is not §8-protected, because a comment that does not parse as a record is not a record). A record tool never counts, and never writes past, something it cannot read. *The asymmetry with (1) is deliberate:* a near-miss header makes no well-formed claim to be a record, so it is warned about and excluded; an anchored header *is* such a claim, and a claim the tooling cannot read must stop the writer rather than be guessed around. 4. **Suite provenance stamp on Phase Outcomes.** Every newly posted `phase-outcome:v1` records the identity of the suite text that produced it, resolved from the suite root (the resolved plugin/skills root the run executes from) by one probe with no mode flag and no operator declaration: root inside a git work tree → commit SHA + dirty/clean marker; otherwise → the release version read from the plugin's own manifest at that root (`.claude-plugin/plugin.json`), which every install carries. If the probe cannot decide (e.g. `git` unavailable mid-probe, manifest missing), the stamp records an explicit `unknown` — **provenance never blocks a post**. Existing records without the field remain readable. **Scope:** Standard — matching/diagnosis fixes apply to **all** typed schemas at the code path they share (fix the family once); the new provenance field lands on the Phase Outcome schema **only** (whether as an optional `:v1` field or a `:v2` is a plan-time decision). ## User Stories - As the orchestrator model, I want a stray or malformed comment to be incapable of corrupting the Phase Outcome ordinals, so that the ledger's density invariant survives contact with imperfect input. - As the orchestrator model, I want a failed ledger read to name the offending comment and field, so that I can repair the actual cause instead of debugging a jq stack trace. - As the operator, I want every new Phase Outcome to carry the suite SHA or release version that produced it, so that run behaviour is attributable after the fact on both machines. - As a meta-lane reviewer, I want "verified at HEAD" claims to be checkable against what a run actually executed, so that retrospective findings rest on evidence rather than assumption. ## Acceptance Criteria *"Warns/warning" below always means: a message on the helper's diagnostic (non-data) output stream that includes the forge comment id of the comment it concerns. Warnings recur on every affected post/read until the stray comment is cleaned up — persistence is intended.* - [ ] AC1: Given a ledger of N well-formed Phase Outcomes with contiguous ordinals 1..N, plus one comment whose first line contains `phase-outcome:v1` but does not match the anchored header shape, when a new Phase Outcome is posted, then the new record is minted at ordinal N+1 (no skip), and the post warns, naming the near-miss comment's id. - [ ] AC2: Given the same ledger, when the ledger is read (read-all and read-latest), then the read succeeds (exit 0), returns exactly the N parsed records, excludes the near-miss, and warns naming its comment id. - [ ] AC3: Given a comment that matches the anchored header shape but whose body cannot be parsed as the schema, when a new Phase Outcome is posted, then the post exits non-zero, no new comment exists on the issue afterwards, and its error names the offending comment id, the missing/unparseable element, and the repair route. - [ ] AC4: Given the same unparseable-body record, when the ledger is read, then the reader exits non-zero with a diagnosis naming the comment id and the missing/unparseable element — never an unhandled jq error. - [ ] AC5: Given a non-Phase-Outcome typed schema (e.g. `decision-resolution:v1`) and a near-miss comment for it, when that schema's records are scanned through the shared matching path, then the near-miss is excluded with a warning; and the ledger consumers enumerated under Dependencies, exercised against such a ledger, complete without error. - [ ] AC6: Given an empty ledger (no Phase Outcomes yet — every feature's first post), when a Phase Outcome is posted, then it is minted at ordinal 1; and a read of an empty ledger reports empty without error. - [ ] AC7: Given a run whose suite root is inside a git work tree, when a Phase Outcome is posted, then the record carries the checkout's commit SHA and a dirty/clean marker — demonstrated in both a clean and a dirty state. - [ ] AC8: Given a run whose suite root is not inside a git work tree (an installed plugin), when the same post invocation runs unchanged, then the record carries the release version that the manifest at the suite root declares. - [ ] AC9: Given a suite root where the probe cannot determine either identity, when a Phase Outcome is posted, then the post still succeeds and the record carries an explicit unknown-provenance marker. - [ ] AC10: Given a pre-feature ledger whose records were all written by the post helper (and so are well-formed), when it is read with the new tooling, then the read exits 0, returns the same records as the pre-feature tooling did, and emits no warnings. ## Out of Scope - **Pinning** — preventing suite text from changing mid-run. Split to #56 (narrowed: consumers are already immutable per #50; only the dev box's live checkout can move). - **The adapter primitive-existence defect** (#55) — an unimplemented `pipeline_status` looking like a broken helper. Same "diagnose, don't stack-trace" principle, different surface. - **Repairing already-corrupted ledgers** — a pre-existing ordinal gap is continued past, not filled or healed; the density check's existing behaviour on legacy gaps is unchanged. - **Retrofitting the provenance field onto already-posted Phase Outcomes** — backward readability is required; backfill is not. - **Concurrent-writer races** — two simultaneous posts to one issue computing the same ordinal. The existing collision check keeps its role; a single-orchestrator-per-issue assumption stands. - **Treating `~/.claude/plugins/installed_plugins.json` as an authority** — its path and shape are Claude Code's, not ours; the version authority in the installed case is the plugin's own manifest at the suite root. - **Changing contract §8 immutability rules** — §8 protects records; this feature only makes explicit that a comment which does not parse as a record is not one, so repairing it is sanctioned. The rule itself is untouched. ## Dependencies - **Forge contract & adapters:** the anchored-match change lands at the comment-scanning path shared by the four adapters (`local-fs`, `tea-cli`, `glab-cli`, `gh-cli`); the contract (`forge-contract.md`) and each schema doc must state the exact header pattern being enforced. The plan must confirm the match genuinely lives at one shared path — if it turns out to be per-adapter, family-wide coverage is a real cost to re-estimate, not a free win. - **Schema versioning decision (plan-time):** whether the Phase Outcome schema gains an optional field or becomes `:v2` is deferred to `/dev:technical-plan` — the retry-guard normalisation and both readers parse these records, and every already-posted PO lacks the field. - **Consumers of the ledger** (the AC5 verification set): `/dev:resolve` decision discovery, the integrate/promote resume detections, and the QA retry guard — all read through the affected helpers; their behaviour on the new diagnostics must not regress. - **External systems of record:** the forge itself (Gitea via tea-cli here) — comment first-lines and ids are read back through adapter primitives; no other external system is touched. ## Timeline | Milestone | Date | Notes | |-----------|------|-------| | Requirements complete | 2026-08-24 | | | Development complete | | | | QA complete | | | | UAT approved | | | ## Notes - **Constraints:** helper tier stays bash ≥ 3.2 + jq + git (CLAUDE.md baseline); warnings and refusals must identify the failing comment per the "a gate's failure output identifies the failure" rule (#49's principle, applied here). Provenance detail for the plan: "dirty" = non-empty `git status --porcelain` scoped to the suite root's repo; symlinked roots resolve before probing. - **Open questions:** none at requirements level. Plan-time: optional-field vs `:v2`; exact layer for the anchored match (adapter primitive vs shared skill-facing path) — the AC constrains the behaviour, not the placement. - **Key decisions:** scope set to "family-wide matching, PO-only provenance"; writer behaviour on an unreadable collected record set to "refuse loudly" over "warn and continue" — this is the audit trail, and a stall is the loud failure. Both decided by the orchestrator under the 2026-08-24 decision-escalation protocol. - **Panel disposition (Tier 3, five lenses, 18 raw → 10 deduped concerns):** all resolved by amendment in this revision. Notable: the two ordinal phrasings were reconciled (parsed-max rule is authoritative; AC1's "N+1" holds only for contiguous ledgers, now stated); "warning" and AC10's baseline were made observable; the installed-version authority was named (root manifest); probe edge cases got AC9 (explicit `unknown`, never blocks). Simpler-alternative challenges considered and declined with reasons: parsed-ordinals alone fixes minting but not the read crash, the silent drop, or the repair trap (matching is the diagnosis layer); provenance is kept in this feature because it shares the writer and the schema decision; the dirty flag stays because a dirty-checkout SHA without it misattributes exactly the runs the meta lane cares about. - **Provenance (fan-in):** consolidates #54 (bug, priority:high) and the record half of #34 (tech-debt); #34's pinning half split to #56. #54's three-fix hypothesis and #34's two-machine ground-truth table (measured 2026-08-22) are the seed material.
Author
Owner

Linked: this issue is parent #54 (recorded by the devwork pipeline).

Linked: this issue is **parent** #54 (recorded by the devwork pipeline).
Author
Owner

Linked: this issue is parent #34 (recorded by the devwork pipeline).

Linked: this issue is **parent** #34 (recorded by the devwork pipeline).
Author
Owner

Linked: this issue is sibling #56 (recorded by the devwork pipeline).

Linked: this issue is **sibling** #56 (recorded by the devwork pipeline).
Author
Owner

Test Plan: run-record-integrity-typed-comment-validation-and-suite-provenance-stamp

Prerequisites

This feature has no browser or service surface — every scenario is executed against the suite's
record tooling and observed through exit codes, emitted diagnostics, and the comments present on
a forge issue afterwards. The state the scenarios need:

  • A scratch feature issue on the project's declared forge whose comments the scenarios may
    freely write (never a real feature's ledger).
  • The ability to place a hand-authored comment on that issue (to plant near-miss and
    malformed records).
  • A suite root of each of the three provenance shapes: (a) inside a git checkout, reachable
    in both a clean and a locally-modified state; (b) an installed-plugin-shaped directory that
    is not inside any git work tree and carries the plugin's own manifest declaring a version;
    (c) a root where neither identity is determinable.
  • One pre-feature ledger: an issue whose Phase Outcomes were all written by the existing
    tooling before this feature (read-only — it is never written to).

Required Test Data

  • On the scratch issue, a ledger of N (≥2) well-formed Phase Outcome records with contiguous
    ordinals 1..N, written by the record tooling itself.
  • A planted near-miss comment: first line contains the Phase Outcome schema token but
    does not match the anchored header shape (the real-world shape: a prose note borrowing the
    schema name).
  • A planted malformed record: first line matches the anchored header shape, but the body
    does not parse as the schema (e.g. the JSON fence absent or truncated).
  • A planted near-miss for a second typed schema (e.g. a decision-resolution token in a
    prose first line).
  • A second scratch issue with no Phase Outcome comments at all (the empty ledger).

Test Scenarios

Scenario 1: A look-alike comment cannot shift the numbering

Acceptance criterion: AC1 — near-miss present, new record minted at N+1 (no skip), post warns naming the near-miss comment's id.

  1. Start from the scratch issue holding N well-formed records (ordinals 1..N) plus the planted near-miss comment.
  2. Post a new Phase Outcome through the record tooling.
  3. Verify: the post succeeds, and the newly created record carries ordinal N+1 — not N+2.
  4. Verify: the post's diagnostic output contains a warning that names the near-miss comment's forge comment id.
  5. Verify: the near-miss comment itself is unchanged on the issue.

Expected outcome: the ledger numbering is exactly as if the look-alike did not exist, and the operator was told which comment to clean up.

Scenario 2: Reading past a look-alike succeeds and says so

Acceptance criterion: AC2 — reads succeed (exit 0), return exactly the N parsed records, exclude the near-miss, warn naming its comment id.

  1. Same starting state as Scenario 1 (before its new post, or with N+1 records after — either contiguous ledger).
  2. Read the full ledger; then read the latest record.
  3. Verify: both reads exit 0.
  4. Verify: the full read returns exactly the well-formed records — the near-miss appears nowhere in the data output.
  5. Verify: each read's diagnostic output warns, naming the near-miss comment's id.

Expected outcome: consumers get a clean, complete ledger plus a pointer to the stray comment — never a crash.

Scenario 3: A record that claims the format but can't be read blocks the writer, loudly

Acceptance criterion: AC3 — post exits non-zero, no new comment created, error names comment id + unparseable element + repair route.

  1. On the scratch issue, plant the malformed record (anchored header, unparseable body). Note the issue's comment count.
  2. Attempt to post a new Phase Outcome.
  3. Verify: the post exits non-zero.
  4. Verify: the issue's comment count is unchanged — no new comment of any kind was created.
  5. Verify: the error output names the malformed comment's id, states what could not be parsed, and states how to repair (fix or strip that comment).

Expected outcome: the writer refuses to extend a ledger it cannot fully read, and its refusal is a repair instruction, not a stack trace.

Scenario 4: The same bad record makes reads diagnose, not crash

Acceptance criterion: AC4 — reader exits non-zero with a diagnosis naming comment id + element; never an unhandled jq error.

  1. Same starting state as Scenario 3.
  2. Read the full ledger; read the latest record.
  3. Verify: each read exits non-zero.
  4. Verify: the output names the malformed comment's id and the unparseable element.
  5. Verify: the output contains no raw parser stack error of the pre-feature shape (a bare tool error naming no comment).

Expected outcome: the first thing a failed read tells you is which comment broke it and why.

Scenario 5: The fix covers the whole family of record types

Acceptance criterion: AC5 — near-miss for another typed schema is excluded with a warning via the shared path; the enumerated ledger consumers complete without error.

  1. On the scratch issue, plant the second-schema near-miss comment.
  2. Scan/read that schema's records through the tooling.
  3. Verify: the near-miss is excluded from the results, with a warning naming its comment id.
  4. Exercise each consumer enumerated in the PREQ's Dependencies (decision discovery; the resume-detection reads; the QA retry guard's read) against this issue.
  5. Verify: each completes without error.

Expected outcome: no typed record family remains where a look-alike comment can crash or pollute a consumer.

Scenario 6: The very first record of a feature

Acceptance criterion: AC6 — empty ledger: first post mints ordinal 1; a read of an empty ledger reports empty without error.

  1. Start from the second scratch issue (no Phase Outcome comments).
  2. Read the ledger. Verify: the read reports an empty ledger without error.
  3. Post a Phase Outcome. Verify: it is minted at ordinal 1.

Expected outcome: a brand-new feature's first record behaves exactly as every feature's first record always has.

Scenario 7: A dev-checkout run stamps the commit it ran, and whether the tree was clean

Acceptance criterion: AC7 — suite root inside a git work tree: record carries the checkout's commit SHA and a dirty/clean marker, demonstrated in both states.

  1. With the suite root inside a git checkout and the checkout clean, post a Phase Outcome to the scratch issue.
  2. Verify: the new record carries the checkout's current commit SHA and a clean marker.
  3. Make any local modification within the checkout (unreleased edit), post again.
  4. Verify: the new record carries the SHA and a dirty marker.
  5. Revert the modification.

Expected outcome: a record from the dev machine says exactly which commit produced it — and admits when the tree had uncommitted changes on top.

Scenario 8: An installed-plugin run stamps its release version, with the identical invocation

Acceptance criterion: AC8 — suite root not inside a git work tree: the same post invocation, unchanged, records the version the root's own manifest declares.

  1. With the suite root being the installed-plugin-shaped directory (not in any git work tree; manifest declaring a known version), run the same post invocation as Scenario 7 — no additional flag, configuration, or operator input.
  2. Verify: the new record carries exactly the version the manifest declares.

Expected outcome: consumer-machine records are attributable to a release with nobody remembering anything.

Scenario 9: When the tooling can't tell, it says "unknown" and never blocks the record

Acceptance criterion: AC9 — probe cannot determine either identity: post still succeeds; record carries an explicit unknown-provenance marker.

  1. With the suite root arranged so neither identity is determinable, post a Phase Outcome.
  2. Verify: the post succeeds.
  3. Verify: the record carries an explicit unknown-provenance marker — the field is present and says unknown, not absent.

Expected outcome: provenance trouble can never cost you the record itself.

Scenario 10: Yesterday's ledgers still read clean

Acceptance criterion: AC10 — a pre-feature ledger of tooling-written records: read exits 0, returns the same records as the pre-feature tooling did, emits no warnings.

  1. Before switching tooling, capture the pre-feature read output of the designated read-only ledger (record count and ids).
  2. With the new tooling, read the same ledger.
  3. Verify: exit 0; the same records, in the same order, with the same ids; no warnings emitted.

Expected outcome: every ledger written before this feature is untouched by it — no migration, no noise.

Traceability

Forward: AC1→S1, AC2→S2, AC3→S3, AC4→S4, AC5→S5, AC6→S6, AC7→S7, AC8→S8, AC9→S9, AC10→S10.
Backward: every scenario names its criterion above. No orphans; no implementation-necessity
exceptions were flagged (the project's observability policy is none).

<!-- test-plan:v1 issue=57 skill=requirements --> # Test Plan: run-record-integrity-typed-comment-validation-and-suite-provenance-stamp ## Prerequisites This feature has no browser or service surface — every scenario is executed against the suite's record tooling and observed through exit codes, emitted diagnostics, and the comments present on a forge issue afterwards. The state the scenarios need: - [ ] A scratch feature issue on the project's declared forge whose comments the scenarios may freely write (never a real feature's ledger). - [ ] The ability to place a hand-authored comment on that issue (to plant near-miss and malformed records). - [ ] A suite root of each of the three provenance shapes: (a) inside a git checkout, reachable in both a clean and a locally-modified state; (b) an installed-plugin-shaped directory that is not inside any git work tree and carries the plugin's own manifest declaring a version; (c) a root where neither identity is determinable. - [ ] One pre-feature ledger: an issue whose Phase Outcomes were all written by the existing tooling before this feature (read-only — it is never written to). ### Required Test Data - [ ] On the scratch issue, a ledger of N (≥2) well-formed Phase Outcome records with contiguous ordinals 1..N, written by the record tooling itself. - [ ] A planted **near-miss** comment: first line contains the Phase Outcome schema token but does not match the anchored header shape (the real-world shape: a prose note borrowing the schema name). - [ ] A planted **malformed record**: first line matches the anchored header shape, but the body does not parse as the schema (e.g. the JSON fence absent or truncated). - [ ] A planted near-miss for a **second typed schema** (e.g. a decision-resolution token in a prose first line). - [ ] A second scratch issue with **no** Phase Outcome comments at all (the empty ledger). ## Test Scenarios ### Scenario 1: A look-alike comment cannot shift the numbering **Acceptance criterion:** AC1 — near-miss present, new record minted at N+1 (no skip), post warns naming the near-miss comment's id. 1. Start from the scratch issue holding N well-formed records (ordinals 1..N) plus the planted near-miss comment. 2. Post a new Phase Outcome through the record tooling. 3. Verify: the post succeeds, and the newly created record carries ordinal N+1 — not N+2. 4. Verify: the post's diagnostic output contains a warning that names the near-miss comment's forge comment id. 5. Verify: the near-miss comment itself is unchanged on the issue. **Expected outcome:** the ledger numbering is exactly as if the look-alike did not exist, and the operator was told which comment to clean up. ### Scenario 2: Reading past a look-alike succeeds and says so **Acceptance criterion:** AC2 — reads succeed (exit 0), return exactly the N parsed records, exclude the near-miss, warn naming its comment id. 1. Same starting state as Scenario 1 (before its new post, or with N+1 records after — either contiguous ledger). 2. Read the full ledger; then read the latest record. 3. Verify: both reads exit 0. 4. Verify: the full read returns exactly the well-formed records — the near-miss appears nowhere in the data output. 5. Verify: each read's diagnostic output warns, naming the near-miss comment's id. **Expected outcome:** consumers get a clean, complete ledger plus a pointer to the stray comment — never a crash. ### Scenario 3: A record that claims the format but can't be read blocks the writer, loudly **Acceptance criterion:** AC3 — post exits non-zero, no new comment created, error names comment id + unparseable element + repair route. 1. On the scratch issue, plant the malformed record (anchored header, unparseable body). Note the issue's comment count. 2. Attempt to post a new Phase Outcome. 3. Verify: the post exits non-zero. 4. Verify: the issue's comment count is unchanged — no new comment of any kind was created. 5. Verify: the error output names the malformed comment's id, states what could not be parsed, and states how to repair (fix or strip that comment). **Expected outcome:** the writer refuses to extend a ledger it cannot fully read, and its refusal is a repair instruction, not a stack trace. ### Scenario 4: The same bad record makes reads diagnose, not crash **Acceptance criterion:** AC4 — reader exits non-zero with a diagnosis naming comment id + element; never an unhandled jq error. 1. Same starting state as Scenario 3. 2. Read the full ledger; read the latest record. 3. Verify: each read exits non-zero. 4. Verify: the output names the malformed comment's id and the unparseable element. 5. Verify: the output contains no raw parser stack error of the pre-feature shape (a bare tool error naming no comment). **Expected outcome:** the first thing a failed read tells you is which comment broke it and why. ### Scenario 5: The fix covers the whole family of record types **Acceptance criterion:** AC5 — near-miss for another typed schema is excluded with a warning via the shared path; the enumerated ledger consumers complete without error. 1. On the scratch issue, plant the second-schema near-miss comment. 2. Scan/read that schema's records through the tooling. 3. Verify: the near-miss is excluded from the results, with a warning naming its comment id. 4. Exercise each consumer enumerated in the PREQ's Dependencies (decision discovery; the resume-detection reads; the QA retry guard's read) against this issue. 5. Verify: each completes without error. **Expected outcome:** no typed record family remains where a look-alike comment can crash or pollute a consumer. ### Scenario 6: The very first record of a feature **Acceptance criterion:** AC6 — empty ledger: first post mints ordinal 1; a read of an empty ledger reports empty without error. 1. Start from the second scratch issue (no Phase Outcome comments). 2. Read the ledger. Verify: the read reports an empty ledger without error. 3. Post a Phase Outcome. Verify: it is minted at ordinal 1. **Expected outcome:** a brand-new feature's first record behaves exactly as every feature's first record always has. ### Scenario 7: A dev-checkout run stamps the commit it ran, and whether the tree was clean **Acceptance criterion:** AC7 — suite root inside a git work tree: record carries the checkout's commit SHA and a dirty/clean marker, demonstrated in both states. 1. With the suite root inside a git checkout and the checkout clean, post a Phase Outcome to the scratch issue. 2. Verify: the new record carries the checkout's current commit SHA and a clean marker. 3. Make any local modification within the checkout (unreleased edit), post again. 4. Verify: the new record carries the SHA and a dirty marker. 5. Revert the modification. **Expected outcome:** a record from the dev machine says exactly which commit produced it — and admits when the tree had uncommitted changes on top. ### Scenario 8: An installed-plugin run stamps its release version, with the identical invocation **Acceptance criterion:** AC8 — suite root not inside a git work tree: the same post invocation, unchanged, records the version the root's own manifest declares. 1. With the suite root being the installed-plugin-shaped directory (not in any git work tree; manifest declaring a known version), run the same post invocation as Scenario 7 — no additional flag, configuration, or operator input. 2. Verify: the new record carries exactly the version the manifest declares. **Expected outcome:** consumer-machine records are attributable to a release with nobody remembering anything. ### Scenario 9: When the tooling can't tell, it says "unknown" and never blocks the record **Acceptance criterion:** AC9 — probe cannot determine either identity: post still succeeds; record carries an explicit unknown-provenance marker. 1. With the suite root arranged so neither identity is determinable, post a Phase Outcome. 2. Verify: the post succeeds. 3. Verify: the record carries an explicit unknown-provenance marker — the field is present and says unknown, not absent. **Expected outcome:** provenance trouble can never cost you the record itself. ### Scenario 10: Yesterday's ledgers still read clean **Acceptance criterion:** AC10 — a pre-feature ledger of tooling-written records: read exits 0, returns the same records as the pre-feature tooling did, emits no warnings. 1. Before switching tooling, capture the pre-feature read output of the designated read-only ledger (record count and ids). 2. With the new tooling, read the same ledger. 3. Verify: exit 0; the same records, in the same order, with the same ids; no warnings emitted. **Expected outcome:** every ledger written before this feature is untouched by it — no migration, no noise. ## Traceability Forward: AC1→S1, AC2→S2, AC3→S3, AC4→S4, AC5→S5, AC6→S6, AC7→S7, AC8→S8, AC9→S9, AC10→S10. Backward: every scenario names its criterion above. No orphans; no implementation-necessity exceptions were flagged (the project's observability policy is `none`).
Author
Owner
{
  "next_state": "planning",
  "produced": [
    {
      "kind": "preq",
      "ref": "issue-body",
      "summary": "PREQ for run-record integrity: typed-comment validation (anchored matching, parsed-ordinal minting, diagnostic refusals) + suite provenance stamp on Phase Outcomes"
    },
    {
      "kind": "test-plan",
      "ref": "comment:1143",
      "summary": "10 validation cases covering 10/10 acceptance criteria"
    },
    {
      "kind": "promotion",
      "ref": "issue-#54",
      "summary": "promoted #54 (PO ledger integrity bug) into this feature"
    },
    {
      "kind": "promotion",
      "ref": "issue-#34",
      "summary": "promoted #34 record half (provenance stamp) into this feature; pin half split to #56 pre-promotion"
    }
  ],
  "findings": [
    {
      "category": "out-of-scope",
      "summary": "Pinning — preventing suite text from changing mid-run",
      "reasoning": "Split to #56 before promotion, narrowed by what #50 delivered: consumer installs are immutable per version; only the dev box's live checkout can move. Detection (a natural follow-on of the provenance stamp) may cover it — decide after this feature ships.",
      "id": "F-PO-57-1-1"
    },
    {
      "category": "out-of-scope",
      "summary": "Adapter primitive-existence defect: unimplemented pipeline_status indistinguishable from a broken helper (exit 127)",
      "reasoning": "Filed as #55. Shares the diagnose-don't-stack-trace principle but is forge-contract work across four adapters, not run-record work.",
      "id": "F-PO-57-1-2"
    },
    {
      "category": "out-of-scope",
      "summary": "Repairing already-corrupted ledgers (pre-existing ordinal gaps)",
      "reasoning": "Minting continues past a gap (max parsed ordinal + 1); the density check's behaviour on legacy gaps is unchanged. Healing history is a separate, deliberate act if ever wanted.",
      "id": "F-PO-57-1-3"
    },
    {
      "category": "out-of-scope",
      "summary": "Backfilling the provenance field onto already-posted Phase Outcomes",
      "reasoning": "Backward readability is required (AC10); rewriting immutable history is not — old records simply predate the field.",
      "id": "F-PO-57-1-4"
    },
    {
      "category": "out-of-scope",
      "summary": "Concurrent-writer races on ordinal computation",
      "reasoning": "Two simultaneous posts to one issue are outside the single-orchestrator-per-issue assumption; the existing collision check keeps its role.",
      "id": "F-PO-57-1-5"
    }
  ],
  "pending_decisions": []
}
<!-- phase-outcome:v1 id=PO-57-1 skill=requirements --> ```json { "next_state": "planning", "produced": [ { "kind": "preq", "ref": "issue-body", "summary": "PREQ for run-record integrity: typed-comment validation (anchored matching, parsed-ordinal minting, diagnostic refusals) + suite provenance stamp on Phase Outcomes" }, { "kind": "test-plan", "ref": "comment:1143", "summary": "10 validation cases covering 10/10 acceptance criteria" }, { "kind": "promotion", "ref": "issue-#54", "summary": "promoted #54 (PO ledger integrity bug) into this feature" }, { "kind": "promotion", "ref": "issue-#34", "summary": "promoted #34 record half (provenance stamp) into this feature; pin half split to #56 pre-promotion" } ], "findings": [ { "category": "out-of-scope", "summary": "Pinning — preventing suite text from changing mid-run", "reasoning": "Split to #56 before promotion, narrowed by what #50 delivered: consumer installs are immutable per version; only the dev box's live checkout can move. Detection (a natural follow-on of the provenance stamp) may cover it — decide after this feature ships.", "id": "F-PO-57-1-1" }, { "category": "out-of-scope", "summary": "Adapter primitive-existence defect: unimplemented pipeline_status indistinguishable from a broken helper (exit 127)", "reasoning": "Filed as #55. Shares the diagnose-don't-stack-trace principle but is forge-contract work across four adapters, not run-record work.", "id": "F-PO-57-1-2" }, { "category": "out-of-scope", "summary": "Repairing already-corrupted ledgers (pre-existing ordinal gaps)", "reasoning": "Minting continues past a gap (max parsed ordinal + 1); the density check's behaviour on legacy gaps is unchanged. Healing history is a separate, deliberate act if ever wanted.", "id": "F-PO-57-1-3" }, { "category": "out-of-scope", "summary": "Backfilling the provenance field onto already-posted Phase Outcomes", "reasoning": "Backward readability is required (AC10); rewriting immutable history is not — old records simply predate the field.", "id": "F-PO-57-1-4" }, { "category": "out-of-scope", "summary": "Concurrent-writer races on ordinal computation", "reasoning": "Two simultaneous posts to one issue are outside the single-orchestrator-per-issue assumption; the existing collision check keeps its role.", "id": "F-PO-57-1-5" } ], "pending_decisions": [] } ```
Author
Owner

Software Requirements: run-record-integrity

Context

The Phase Outcome ledger — typed <!-- schema:v1 … --> comments on forge issues — is the pipeline's
only durable account of what happened on a feature, and it is untrustworthy on both axes: any comment
whose first line merely contains a schema token is absorbed as a record (corrupting ordinal minting
and killing reads with bare jq errors — #54, reproduced live on #50), and nothing records which suite
text produced a Phase Outcome (#34 record half). Users are the orchestrator model mid-run and the
operator/meta-lane doing retrospective attribution.

Approaches Considered

Approach A: Anchored matching inside each adapter primitive

Summary: Change all four scan_comments.sh primitives to enforce the anchored header shape.
Pros: Filtering happens at the source; consumers untouched.
Cons: Four implementations of schema knowledge in the dumb-transport tier; the primitive contract
changes for every adapter (and any future adapter must re-implement it); near-miss warnings need a
diagnostic channel from primitives that don't have one; violates "fix the family once".
Effort: High

Approach B: Shared validation tier in _lib.sh (selected)

Summary: Adapters stay coarse substring prefilters (contract unchanged); one new shared function
partitions anchored records from near-misses, warns, and every skill-facing consumer routes through it.
Pros: Genuinely one shared path (every consumer already sources _lib.sh); zero adapter changes;
uniform warning channel (stderr, the helper tier's existing _log convention); mechanically
enforceable (lint: no scan call outside the shared function).
Cons: Near-misses travel from the adapter to the shared tier before being excluded (negligible —
they must be seen anyway to be warned about).
Effort: Medium

Approach C: Standalone typed-scan.sh wrapper helper

Summary: Same partition logic, but as a new bin script consumers exec instead of a sourced function.
Pros: Isolation; testable as a unit.
Cons: One extra process per scan; consumers already source _lib.sh, so a script adds surface
without adding capability; the post helper needs the parsed records too, which a sourced function
shares more naturally.
Effort: Medium

Decision

Selected: Approach B — shared validation tier in _lib.sh.
Rationale: The PREQ's family-wide requirement hinges on one shared path existing; _lib.sh is
that path (every scan consumer sources it). The adapter primitive's substring match stops being a
correctness surface and becomes an over-inclusive prefilter — which is exactly what lets the shared
tier see near-misses in order to warn about them.

Architecture

Component Overview

skill-facing consumers (post, read-all/latest, decision-resolution, deliverable-get,
comments-scan, fold/unfold, fold-/promotion-candidates)
        │  all route through
        ▼
_lib.sh: _typed_scan KIND ISSUE          ← anchored partition + near-miss warnings (NEW)
         _suite_provenance               ← git|release|unknown probe (NEW, own marked section)
        │  coarse prefilter (unchanged contract, now stated normatively as a SUPERSET)
        ▼
{adapter}/bin/scan_comments.sh  (local-fs | tea-cli | glab-cli | gh-cli — untouched)

_typed_scan contract (explicit): input KIND ISSUE; KIND validated against [a-z0-9-]+
before any use. Calls _prim scan_comments --header "KIND:v1". Returns on stdout a JSON array in the
same envelope as the primitive[{comment_id, created_ms, edited_ms, raw_body}, …] — filtered
to anchored records; near-miss warnings go to stderr. A primitive failure propagates as a hard
failure (die), never as an empty result
— a failed scan treated as empty would mint a duplicate
ordinal 1. Empty successful scan returns [], exit 0.

Data Flow

  • Post: phase-outcome-post.sh_typed_scan phase-outcome N → pre-mint parse pass over every
    anchored record (refuse loudly on any unparseable one, naming comment id + element + repair route) →
    ordinal = ((map(.ordinal) | max) // 0) + 1 (empty → 1; gaps continued past) → collision check +
    retry guard against parsed records (retry guard compares the highest-ordinal record) → body JSON
    gains optional "suite": <_suite_provenance> → post.
  • Read: phase-outcome-read-all.sh_typed_scan → §8 immutability check over anchored records
    (unchanged in strength for records: an edited anchored record still hard-stops) → per-record parse
    with diagnosis (comment id + missing/unparseable element; never a bare jq error) → suite passed
    through (null on pre-feature records). Density check behaviour on legacy gaps unchanged — and it
    is also the read-side tamper-evidence: a record edited away from its anchored shape, or a duplicate
    parsed ordinal, leaves a gap/dup the density check refuses loudly.
  • Family: every other consumer swaps its direct scan for _typed_scan KIND and keeps its own
    narrowing (exact-ref token filter, attr filters) on the anchored set. The one skill-text fence that
    invokes the primitive directly (requirements/procedures/requirements-from-deferred.md Step D3) is
    re-pointed at comments-scan.sh.

Anchored header rule (family-wide)

First line (after stripping a trailing \r — forge APIs may return CRLF) must match:
^<!-- {kind}:v1( +{key}={value})* -->$ — the schema token anchored as the first token after <!--,
followed only by space-delimited key=value attributes. Three disjoint outcomes:

  1. Anchored + parseable → a record.
  2. Anchored + unparseable → the refusal path (post refuses to mint) / diagnosis path (read
    exits non-zero naming id + element). Repair is an operator action, never automated: delete the
    malformed comment, or edit its first line so it no longer claims the schema — a comment that does
    not parse as a record is not a record, so §8 does not protect it. Do not edit it into a valid
    record: an edited record still violates §8.
  3. Token present, shape absent → near-miss: excluded + warned (stderr, names the comment id,
    recurs every run until cleaned). A near-miss with edited_ms > created_ms gets a distinct,
    louder diagnostic
    ("edited near-miss — if this was once a record, its §8 history is broken;
    verify before cleaning"), because an edit is how a record could be made to vanish; for Phase
    Outcomes the density check additionally hard-stops on the resulting gap.

Each schema doc states its concrete pattern; the generic rule is the one implementation. jq hygiene
(constraint, lint-visible):
comment content and KIND reach jq only via --arg/--rawfile — never
string-spliced into a jq program or shell command.

Suite provenance probe

Root = _SKILLS_ROOT (already derived from BASH_SOURCE in _lib.shnever the caller's cwd,
which is the project repo), resolved physically (pwd -P) so the deployed symlink farm probes the
real checkout. Then, in order:

  1. git -C "$root" rev-parse --is-inside-work-tree outputs true{"source":"git", "sha": <rev-parse HEAD>, "dirty": <status --porcelain non-empty, scoped to that repo>}.
  2. Else .version from "$root/../.claude-plugin/plugin.json" (the plugin manifest every install
    carries
    ; marketplace.json#plugins[0].version is the repo-release-side field and is NOT probed)
    {"source":"release", "version": …}.
  3. Else → {"source":"unknown"}.

The function is guarded so it always exits 0 with a JSON object (checks command output, not just
rc; safe under set -e inside $(…)), because provenance must never block a post (AC-9).

External Data Contracts

Boundary / source Operation Real shape (verified) Provenance
Gitea (git.wihslon.com) via tea-cli scan/post issue comments comment_id string; created_ms/edited_ms epoch-ms ints (equal when unedited); raw_body returns the posted header byte-exact on line 1 recordedexternal-contracts/tea-scan.provenance.json
Pre-feature ledger (already-posted phase-outcome:v1 records — AC10 baseline) read existing records header <!-- phase-outcome:v1 id=PO-N-k skill=s -->, JSON-fence body, no suite field; #50 ledger re-read live: 7 contiguous records recordedexternal-contracts/tea-scan-po-issue57.sample.json

Key Decisions

Decision Choice Rationale
Layer for the anchored match Shared tier (_lib.sh), not adapter primitives One implementation for the family; adapters stay dumb transport; near-misses must be seen to be warned about
Adapter prefilter contract Stated normatively as a superset: line-1, case-exact substring; adapters MUST NOT tighten An adapter that tightened its match would silently drop valid records before _typed_scan ever saw them (panel: architect)
Schema versioning for provenance Optional suite body field, stays :v1; the schema doc states the general policy (additive optional body fields = no bump) and the retry-guard's excluded-field set Every reader projects known keys; pre-feature records read as suite: null; :v2 forces migration for zero consumer benefit
Provenance placement Body JSON field, not a header attribute The header is matching surface; extending it risks near-miss classification of records posted by older writers
Provenance probe order physical _SKILLS_ROOT → git worktree → parent-dir plugin.jsonunknown Resolves the symlink farm; the manifest is the authority every install carries; git wins over manifest because a checkout is the more specific truth
Retry-guard normalization excludes suite Yes (named in schema doc) A timed-out post retried after a suite change must still be recognized; provenance describes the writer, not the content
Near-miss warning channel stderr via _log; distinct diagnostic for edited near-misses Helper tier's existing convention; data stdout stays clean JSON; the edited case is the tamper-relevant one
Legacy-gap density check Unchanged (read-all still refuses) PREQ out-of-scope: repairing corrupted ledgers; it doubles as read-side tamper-evidence for edit-away and duplicate ordinals

Technical Risks

Risk Likelihood Impact Mitigation
A pre-feature record fails the anchored regex (false near-miss) → AC10 regression Low High Regex derived from the post helpers' own printf formats; AC10 executed against the real #50 (7 records) and #57 ledgers before merge
Error-swallowing callers (fold-/promotion-candidates use 2>/dev/null) suppress near-miss warnings Certain (by design) Low Their per-issue tolerance is deliberate (candidate discovery over many issues); the authoritative warn surface is post/read. Accepted
_typed_scan under set -e: consumers that tolerated scan failure now die Medium Medium Deliberate: primitive failure must never read as "empty ledger" (duplicate-ordinal hazard); the two tolerant candidates helpers keep their per-issue `
Provenance probe misidentifies a consumer install sitting inside an unrelated git repo Low Medium Git-first order is by design (a checkout is the more specific truth); SHA+dirty still attributes correctly — a wrong release claim is impossible since git wins
bash-3.2 ceiling violations sneaking in Low Medium scripts/lint-conventions.sh gates; new code is jq-centric; jq precedence trap ((max // 0) + 1 mis-parse) called out in Data Flow

Expert Review

Tier 2 panel on fable: Backend Developer, Solution Architect, Security Specialist (one response
each, 300-token cap, no cross-talk).

Reviewers

  • Backend Developer: provenance root must derive from BASH_SOURCE, not cwd (helpers run with
    cwd = the project repo); edited-record-into-near-miss escapes §8; jq precedence/CRLF/KIND-injection
    and set -e details; manifest field ambiguity; fence-aware lint.
  • Solution Architect: superset guarantee must be normative in the contract; _typed_scan return
    envelope unspecified; primitive-failure vs empty semantics; versioning-policy generalization;
    normalization excluded-field set; exempt-consumer audit.
  • Security Specialist: near-miss exclusion as tamper vector (edited-record vanish); ordinal
    forgery / authorship trust boundary; who is sanctioned to strip; jq injection hygiene; dirty-SHA
    surfacing at release time; read-side duplicate-ordinal semantics.

Changes Made

  • Provenance root pinned to physical _SKILLS_ROOT (BASH_SOURCE-derived), never cwd (Backend).
  • Edited near-misses get a distinct loud diagnostic; documented that the PO density check is the
    read-side tamper-evidence for edit-away and duplicate ordinals (Backend + Security, raised
    independently — promoted).
  • Superset prefilter guarantee added to the forge-contract scan_comments row (Architect).
  • _typed_scan envelope + primitive-failure-propagates semantics specified (Architect).
  • Repair route reworded: operator action, never automated; "do not edit it into a valid record"
    (Security).
  • jq hygiene (--arg/--rawfile only, KIND validated [a-z0-9-]+), CRLF stripping, jq max//0
    precedence, set -e-safe probe absorbed as stated constraints (Backend + Security).
  • Version authority named: plugin.json#version for installs; marketplace.json is release-side
    only (Backend).
  • Lint rule declared fence-aware, reusing the lint-conventions.sh fence tracker (Backend; CLAUDE.md
    #50 lesson).
  • Direct primitive call in requirements-from-deferred.md Step D3 re-pointed at comments-scan.sh
    (Architect's exempt-consumer audit; found by grep).
  • Schema doc gains the additive-versioning policy sentence and the retry-guard excluded-field set
    (Architect).

Noted (not actioned)

  • Comment-author identity verification before a record influences minting/resume (Security) —
    rejected on scope and portability grounds: the PREQ threat model is imperfect input from the
    orchestrator itself, not a malicious forge-writer (who can already delete/edit anything the ACLs
    allow — the forge ACL is the trust boundary); and local-fs has no author identity at all, so a
    contract-level authorship check is not implementable portably.
  • Surface dirty-SHA provenance during release bookkeeping (Security) — real future enhancement,
    outside this PREQ's ACs; the natural home is #56's detection follow-on ("did the suite text move
    mid-run"), to be decided after this feature ships.
  • _suite_provenance in a separate sourced file (Architect) — rejected: a clearly-marked section
    in _lib.sh gives the same separation without adding a second sourcing surface.

Acceptance Criteria

ID Criterion (from PREQ) Verification approach
AC-1 Near-miss on the ledger: new PO minted at N+1 (no skip), post warns naming the near-miss comment id Executed suite _shared/procedures/test/typed-scan.sh (S1)
AC-2 Same ledger: read-all/read-latest exit 0, return exactly the N parsed records, warn naming the id typed-scan.sh (S2)
AC-3 Anchored-but-unparseable record: post exits non-zero, posts nothing, names comment id + element + repair route typed-scan.sh (S3)
AC-4 Same record: read exits non-zero with diagnosis naming comment id + element — never a bare jq error typed-scan.sh (S4)
AC-5 Family: near-miss for a non-PO schema excluded+warned via the shared path; enumerated consumers (decision discovery, resume detections, retry guard) complete without error typed-scan.sh (S5) + mechanical route-through lint rule
AC-6 Empty ledger: first post mints ordinal 1; empty read reports empty, exit 0 typed-scan.sh (S6)
AC-7 Git-worktree suite root: record carries SHA + dirty/clean, demonstrated in both states Executed suite _shared/procedures/test/provenance.sh (S7)
AC-8 Non-git suite root: same invocation records the manifest's release version provenance.sh (S8)
AC-9 Probe cannot decide: post still succeeds, record carries explicit unknown marker provenance.sh (S9)
AC-10 Pre-feature ledger: new tooling reads exit 0, same records as before, no warnings typed-scan.sh (S10) + read-only reads of the real #50 and #57 ledgers

Route-through rule (mechanical, AC-5's second half): no _prim scan_comments or direct
{adapter}/bin/scan_comments.sh invocation may exist in _shared/procedures/bin/*.sh outside
_typed_scan itself, and no skill-text shell fence may invoke the primitive directly — enforced by a
new fence-aware check in scripts/lint-conventions.sh (reusing its existing fence tracker).

Implementation Scope

Areas

Area Files / directories involved Nature of change
Shared library plugin/skills/_shared/procedures/bin/_lib.sh extend: _typed_scan, _suite_provenance (own marked section)
PO writer bin/phase-outcome-post.sh modify: parse-pass refusal, parsed-max mint, parsed collision/retry guard, suite stamp
PO readers bin/phase-outcome-read-all.sh (read-latest inherits) modify: route, records-scoped §8 check + edited-near-miss diagnostic, per-record diagnosis, suite pass-through
Satellite consumers bin/decision-resolution-read-state.sh, deliverable-get.sh, comments-scan.sh, issue-fold-finding.sh, issue-unfold-finding.sh, fold-candidates.sh, promotion-candidates.sh modify: route through _typed_scan
Test suites plugin/skills/_shared/procedures/test/typed-scan.sh, test/provenance.sh new (red-first per test-workflow)
Lint gate scripts/lint-conventions.sh extend: fence-aware route-through rule
Docs forge-contract.md (primitives-table scan_comments row incl. superset rule, §8 non-record note), schemas/*.md (anchored pattern per typed schema; phase-outcome.v1.md also: suite field, parsed-ordinal wording, versioning policy, normalization excluded set), procedures/phase-outcome.md, requirements/procedures/requirements-from-deferred.md (Step D3 call site) modify

File Boundaries

_lib.sh + post + read-all are one dependency chain (sequential). The seven satellite consumers are
independent of each other once _typed_scan exists (parallelizable). Docs and lint rule are
independent of everything except final naming. Test suites are written red-first against the intended
behaviour.

Dependencies & Sequencing

  1. _lib.sh functions (+ red-first suites) → 2. post + read-all → 3. satellite consumers (parallel)
    → 4. docs + lint rule. AC-10 regression check runs read-only against real ledgers at each step.

Constraints & Non-Goals

Constraints:

  • Helper tier: bash ≥ 3.2 + jq + git only (CLAUDE.md portability baseline); no mapfile, no
    zsh-reserved names in emitted glue, JSON via jq only.
  • Comment content and KIND reach jq via --arg/--rawfile only; KIND validated [a-z0-9-]+.
  • Warnings/refusals identify the failing comment id (the #49 gate-output principle).
  • Adapter primitive contract is unchanged in implementation; its doc row gains the normative
    superset wording. No adapter bin/ edits.

Non-goals (do NOT build):

  • Pinning (#56); adapter primitive-existence diagnostics (#55); repairing corrupted ledgers (gaps are
    continued past, never filled); provenance backfill onto existing records; concurrent-writer races;
    treating installed_plugins.json as a version authority; any §8 rule change; comment-author
    identity verification (see Noted).
<!-- sreq:v1 issue=57 skill=technical-plan --> # Software Requirements: run-record-integrity ## Context The Phase Outcome ledger — typed `<!-- schema:v1 … -->` comments on forge issues — is the pipeline's only durable account of what happened on a feature, and it is untrustworthy on both axes: any comment whose first line merely *contains* a schema token is absorbed as a record (corrupting ordinal minting and killing reads with bare jq errors — #54, reproduced live on #50), and nothing records which suite text produced a Phase Outcome (#34 record half). Users are the orchestrator model mid-run and the operator/meta-lane doing retrospective attribution. ## Approaches Considered ### Approach A: Anchored matching inside each adapter primitive **Summary:** Change all four `scan_comments.sh` primitives to enforce the anchored header shape. **Pros:** Filtering happens at the source; consumers untouched. **Cons:** Four implementations of schema knowledge in the dumb-transport tier; the primitive contract changes for every adapter (and any future adapter must re-implement it); near-miss *warnings* need a diagnostic channel from primitives that don't have one; violates "fix the family once". **Effort:** High ### Approach B: Shared validation tier in `_lib.sh` (selected) **Summary:** Adapters stay coarse substring prefilters (contract unchanged); one new shared function partitions anchored records from near-misses, warns, and every skill-facing consumer routes through it. **Pros:** Genuinely one shared path (every consumer already sources `_lib.sh`); zero adapter changes; uniform warning channel (stderr, the helper tier's existing `_log` convention); mechanically enforceable (lint: no scan call outside the shared function). **Cons:** Near-misses travel from the adapter to the shared tier before being excluded (negligible — they must be seen anyway to be warned about). **Effort:** Medium ### Approach C: Standalone `typed-scan.sh` wrapper helper **Summary:** Same partition logic, but as a new bin script consumers exec instead of a sourced function. **Pros:** Isolation; testable as a unit. **Cons:** One extra process per scan; consumers already source `_lib.sh`, so a script adds surface without adding capability; the post helper needs the *parsed* records too, which a sourced function shares more naturally. **Effort:** Medium ## Decision **Selected:** Approach B — shared validation tier in `_lib.sh`. **Rationale:** The PREQ's family-wide requirement hinges on one shared path existing; `_lib.sh` *is* that path (every scan consumer sources it). The adapter primitive's substring match stops being a correctness surface and becomes an over-inclusive prefilter — which is exactly what lets the shared tier see near-misses in order to warn about them. ## Architecture ### Component Overview ``` skill-facing consumers (post, read-all/latest, decision-resolution, deliverable-get, comments-scan, fold/unfold, fold-/promotion-candidates) │ all route through ▼ _lib.sh: _typed_scan KIND ISSUE ← anchored partition + near-miss warnings (NEW) _suite_provenance ← git|release|unknown probe (NEW, own marked section) │ coarse prefilter (unchanged contract, now stated normatively as a SUPERSET) ▼ {adapter}/bin/scan_comments.sh (local-fs | tea-cli | glab-cli | gh-cli — untouched) ``` **`_typed_scan` contract (explicit):** input `KIND ISSUE`; `KIND` validated against `[a-z0-9-]+` before any use. Calls `_prim scan_comments --header "KIND:v1"`. Returns on stdout a JSON array in the **same envelope as the primitive** — `[{comment_id, created_ms, edited_ms, raw_body}, …]` — filtered to anchored records; near-miss warnings go to stderr. **A primitive failure propagates as a hard failure (die), never as an empty result** — a failed scan treated as empty would mint a duplicate ordinal 1. Empty *successful* scan returns `[]`, exit 0. ### Data Flow - **Post:** `phase-outcome-post.sh` → `_typed_scan phase-outcome N` → pre-mint parse pass over every anchored record (refuse loudly on any unparseable one, naming comment id + element + repair route) → ordinal = `((map(.ordinal) | max) // 0) + 1` (empty → 1; gaps continued past) → collision check + retry guard against *parsed* records (retry guard compares the highest-ordinal record) → body JSON gains optional `"suite": <_suite_provenance>` → post. - **Read:** `phase-outcome-read-all.sh` → `_typed_scan` → §8 immutability check over anchored records (unchanged in strength for records: an edited anchored record still hard-stops) → per-record parse with diagnosis (comment id + missing/unparseable element; never a bare jq error) → `suite` passed through (`null` on pre-feature records). Density check behaviour on legacy gaps unchanged — and it is also the read-side tamper-evidence: a record edited away from its anchored shape, or a duplicate parsed ordinal, leaves a gap/dup the density check refuses loudly. - **Family:** every other consumer swaps its direct scan for `_typed_scan KIND` and keeps its own narrowing (exact-ref token filter, attr filters) on the anchored set. The one skill-text fence that invokes the primitive directly (`requirements/procedures/requirements-from-deferred.md` Step D3) is re-pointed at `comments-scan.sh`. ### Anchored header rule (family-wide) First line (after stripping a trailing `\r` — forge APIs may return CRLF) must match: `^<!-- {kind}:v1( +{key}={value})* -->$` — the schema token anchored as the first token after `<!--`, followed only by space-delimited `key=value` attributes. Three disjoint outcomes: 1. **Anchored + parseable** → a record. 2. **Anchored + unparseable** → the *refusal* path (post refuses to mint) / *diagnosis* path (read exits non-zero naming id + element). Repair is an **operator action, never automated**: delete the malformed comment, or edit its first line so it no longer claims the schema — a comment that does not parse as a record is not a record, so §8 does not protect it. Do **not** edit it into a valid record: an edited record still violates §8. 3. **Token present, shape absent** → near-miss: excluded + warned (stderr, names the comment id, recurs every run until cleaned). A near-miss with `edited_ms > created_ms` gets a **distinct, louder diagnostic** ("edited near-miss — if this was once a record, its §8 history is broken; verify before cleaning"), because an edit is how a record could be made to vanish; for Phase Outcomes the density check additionally hard-stops on the resulting gap. Each schema doc states its concrete pattern; the generic rule is the one implementation. **jq hygiene (constraint, lint-visible):** comment content and KIND reach jq only via `--arg`/`--rawfile` — never string-spliced into a jq program or shell command. ### Suite provenance probe Root = `_SKILLS_ROOT` (already derived from `BASH_SOURCE` in `_lib.sh` — **never the caller's cwd**, which is the project repo), resolved physically (`pwd -P`) so the deployed symlink farm probes the real checkout. Then, in order: 1. `git -C "$root" rev-parse --is-inside-work-tree` outputs `true` → `{"source":"git", "sha": <rev-parse HEAD>, "dirty": <status --porcelain non-empty, scoped to that repo>}`. 2. Else `.version` from `"$root/../.claude-plugin/plugin.json"` (the plugin manifest **every install carries**; `marketplace.json#plugins[0].version` is the repo-release-side field and is NOT probed) → `{"source":"release", "version": …}`. 3. Else → `{"source":"unknown"}`. The function is guarded so it always exits 0 with a JSON object (checks command *output*, not just rc; safe under `set -e` inside `$(…)`), because provenance must never block a post (AC-9). ### External Data Contracts | Boundary / source | Operation | Real shape (verified) | Provenance | | ----------------- | --------- | --------------------- | ---------- | | Gitea (git.wihslon.com) via tea-cli | scan/post issue comments | `comment_id` string; `created_ms`/`edited_ms` epoch-ms ints (equal when unedited); `raw_body` returns the posted header byte-exact on line 1 | `recorded` → `external-contracts/tea-scan.provenance.json` | | Pre-feature ledger (already-posted `phase-outcome:v1` records — AC10 baseline) | read existing records | header `<!-- phase-outcome:v1 id=PO-N-k skill=s -->`, JSON-fence body, no `suite` field; #50 ledger re-read live: 7 contiguous records | `recorded` → `external-contracts/tea-scan-po-issue57.sample.json` | ### Key Decisions | Decision | Choice | Rationale | | -------- | ------ | --------- | | Layer for the anchored match | Shared tier (`_lib.sh`), not adapter primitives | One implementation for the family; adapters stay dumb transport; near-misses must be *seen* to be warned about | | Adapter prefilter contract | Stated normatively as a **superset**: line-1, case-exact substring; adapters MUST NOT tighten | An adapter that tightened its match would silently drop valid records before `_typed_scan` ever saw them (panel: architect) | | Schema versioning for provenance | Optional `suite` body field, stays `:v1`; the schema doc states the general policy (additive optional body fields = no bump) and the retry-guard's excluded-field set | Every reader projects known keys; pre-feature records read as `suite: null`; `:v2` forces migration for zero consumer benefit | | Provenance placement | Body JSON field, not a header attribute | The header is matching surface; extending it risks near-miss classification of records posted by older writers | | Provenance probe order | physical `_SKILLS_ROOT` → git worktree → parent-dir `plugin.json` → `unknown` | Resolves the symlink farm; the manifest is the authority every install carries; git wins over manifest because a checkout is the more specific truth | | Retry-guard normalization excludes `suite` | Yes (named in schema doc) | A timed-out post retried after a suite change must still be recognized; provenance describes the *writer*, not the content | | Near-miss warning channel | stderr via `_log`; distinct diagnostic for edited near-misses | Helper tier's existing convention; data stdout stays clean JSON; the edited case is the tamper-relevant one | | Legacy-gap density check | Unchanged (read-all still refuses) | PREQ out-of-scope: repairing corrupted ledgers; it doubles as read-side tamper-evidence for edit-away and duplicate ordinals | ## Technical Risks | Risk | Likelihood | Impact | Mitigation | | ---- | ---------- | ------ | ---------- | | A pre-feature record fails the anchored regex (false near-miss) → AC10 regression | Low | High | Regex derived from the post helpers' own printf formats; AC10 executed against the real #50 (7 records) and #57 ledgers before merge | | Error-swallowing callers (`fold-`/`promotion-candidates` use `2>/dev/null`) suppress near-miss warnings | Certain (by design) | Low | Their per-issue tolerance is deliberate (candidate discovery over many issues); the authoritative warn surface is post/read. Accepted | | `_typed_scan` under `set -e`: consumers that tolerated scan failure now die | Medium | Medium | Deliberate: primitive failure must never read as "empty ledger" (duplicate-ordinal hazard); the two tolerant candidates helpers keep their per-issue `|| echo []` wrapper *around* the routed call | | Provenance probe misidentifies a consumer install sitting inside an unrelated git repo | Low | Medium | Git-first order is by design (a checkout is the more specific truth); SHA+dirty still attributes correctly — a wrong *release* claim is impossible since git wins | | bash-3.2 ceiling violations sneaking in | Low | Medium | `scripts/lint-conventions.sh` gates; new code is jq-centric; jq precedence trap (`(max // 0) + 1` mis-parse) called out in Data Flow | ## Expert Review Tier 2 panel on `fable`: Backend Developer, Solution Architect, Security Specialist (one response each, 300-token cap, no cross-talk). ### Reviewers - **Backend Developer:** provenance root must derive from `BASH_SOURCE`, not cwd (helpers run with cwd = the project repo); edited-record-into-near-miss escapes §8; jq precedence/CRLF/KIND-injection and `set -e` details; manifest field ambiguity; fence-aware lint. - **Solution Architect:** superset guarantee must be normative in the contract; `_typed_scan` return envelope unspecified; primitive-failure vs empty semantics; versioning-policy generalization; normalization excluded-field set; exempt-consumer audit. - **Security Specialist:** near-miss exclusion as tamper vector (edited-record vanish); ordinal forgery / authorship trust boundary; who is sanctioned to strip; jq injection hygiene; dirty-SHA surfacing at release time; read-side duplicate-ordinal semantics. ### Changes Made - Provenance root pinned to physical `_SKILLS_ROOT` (BASH_SOURCE-derived), never cwd (Backend). - Edited near-misses get a distinct loud diagnostic; documented that the PO density check is the read-side tamper-evidence for edit-away and duplicate ordinals (Backend + Security, raised independently — promoted). - Superset prefilter guarantee added to the forge-contract `scan_comments` row (Architect). - `_typed_scan` envelope + primitive-failure-propagates semantics specified (Architect). - Repair route reworded: operator action, never automated; "do not edit it into a valid record" (Security). - jq hygiene (`--arg`/`--rawfile` only, KIND validated `[a-z0-9-]+`), CRLF stripping, jq max//0 precedence, `set -e`-safe probe absorbed as stated constraints (Backend + Security). - Version authority named: `plugin.json#version` for installs; `marketplace.json` is release-side only (Backend). - Lint rule declared fence-aware, reusing the `lint-conventions.sh` fence tracker (Backend; CLAUDE.md #50 lesson). - Direct primitive call in `requirements-from-deferred.md` Step D3 re-pointed at `comments-scan.sh` (Architect's exempt-consumer audit; found by grep). - Schema doc gains the additive-versioning policy sentence and the retry-guard excluded-field set (Architect). ### Noted (not actioned) - **Comment-author identity verification before a record influences minting/resume** (Security) — rejected on scope and portability grounds: the PREQ threat model is imperfect input from the orchestrator itself, not a malicious forge-writer (who can already delete/edit anything the ACLs allow — the forge ACL is the trust boundary); and `local-fs` has no author identity at all, so a contract-level authorship check is not implementable portably. - **Surface dirty-SHA provenance during release bookkeeping** (Security) — real future enhancement, outside this PREQ's ACs; the natural home is #56's detection follow-on ("did the suite text move mid-run"), to be decided after this feature ships. - **`_suite_provenance` in a separate sourced file** (Architect) — rejected: a clearly-marked section in `_lib.sh` gives the same separation without adding a second sourcing surface. ## Acceptance Criteria | ID | Criterion (from PREQ) | Verification approach | | -- | --------------------- | --------------------- | | AC-1 | Near-miss on the ledger: new PO minted at N+1 (no skip), post warns naming the near-miss comment id | Executed suite `_shared/procedures/test/typed-scan.sh` (S1) | | AC-2 | Same ledger: read-all/read-latest exit 0, return exactly the N parsed records, warn naming the id | typed-scan.sh (S2) | | AC-3 | Anchored-but-unparseable record: post exits non-zero, posts nothing, names comment id + element + repair route | typed-scan.sh (S3) | | AC-4 | Same record: read exits non-zero with diagnosis naming comment id + element — never a bare jq error | typed-scan.sh (S4) | | AC-5 | Family: near-miss for a non-PO schema excluded+warned via the shared path; enumerated consumers (decision discovery, resume detections, retry guard) complete without error | typed-scan.sh (S5) + mechanical route-through lint rule | | AC-6 | Empty ledger: first post mints ordinal 1; empty read reports empty, exit 0 | typed-scan.sh (S6) | | AC-7 | Git-worktree suite root: record carries SHA + dirty/clean, demonstrated in both states | Executed suite `_shared/procedures/test/provenance.sh` (S7) | | AC-8 | Non-git suite root: same invocation records the manifest's release version | provenance.sh (S8) | | AC-9 | Probe cannot decide: post still succeeds, record carries explicit unknown marker | provenance.sh (S9) | | AC-10 | Pre-feature ledger: new tooling reads exit 0, same records as before, no warnings | typed-scan.sh (S10) + read-only reads of the real #50 and #57 ledgers | **Route-through rule (mechanical, AC-5's second half):** no `_prim scan_comments` or direct `{adapter}/bin/scan_comments.sh` invocation may exist in `_shared/procedures/bin/*.sh` outside `_typed_scan` itself, and no skill-text shell fence may invoke the primitive directly — enforced by a new **fence-aware** check in `scripts/lint-conventions.sh` (reusing its existing fence tracker). ## Implementation Scope ### Areas | Area | Files / directories involved | Nature of change | | ---- | ---------------------------- | ---------------- | | Shared library | `plugin/skills/_shared/procedures/bin/_lib.sh` | extend: `_typed_scan`, `_suite_provenance` (own marked section) | | PO writer | `bin/phase-outcome-post.sh` | modify: parse-pass refusal, parsed-max mint, parsed collision/retry guard, `suite` stamp | | PO readers | `bin/phase-outcome-read-all.sh` (read-latest inherits) | modify: route, records-scoped §8 check + edited-near-miss diagnostic, per-record diagnosis, `suite` pass-through | | Satellite consumers | `bin/decision-resolution-read-state.sh`, `deliverable-get.sh`, `comments-scan.sh`, `issue-fold-finding.sh`, `issue-unfold-finding.sh`, `fold-candidates.sh`, `promotion-candidates.sh` | modify: route through `_typed_scan` | | Test suites | `plugin/skills/_shared/procedures/test/typed-scan.sh`, `test/provenance.sh` | new (red-first per test-workflow) | | Lint gate | `scripts/lint-conventions.sh` | extend: fence-aware route-through rule | | Docs | `forge-contract.md` (primitives-table `scan_comments` row incl. superset rule, §8 non-record note), `schemas/*.md` (anchored pattern per typed schema; `phase-outcome.v1.md` also: `suite` field, parsed-ordinal wording, versioning policy, normalization excluded set), `procedures/phase-outcome.md`, `requirements/procedures/requirements-from-deferred.md` (Step D3 call site) | modify | ### File Boundaries `_lib.sh` + post + read-all are one dependency chain (sequential). The seven satellite consumers are independent of each other once `_typed_scan` exists (parallelizable). Docs and lint rule are independent of everything except final naming. Test suites are written red-first against the intended behaviour. ### Dependencies & Sequencing 1. `_lib.sh` functions (+ red-first suites) → 2. post + read-all → 3. satellite consumers (parallel) → 4. docs + lint rule. AC-10 regression check runs read-only against real ledgers at each step. ## Constraints & Non-Goals **Constraints:** - Helper tier: bash ≥ 3.2 + jq + git only (CLAUDE.md portability baseline); no `mapfile`, no zsh-reserved names in emitted glue, JSON via jq only. - Comment content and KIND reach jq via `--arg`/`--rawfile` only; KIND validated `[a-z0-9-]+`. - Warnings/refusals identify the failing comment id (the #49 gate-output principle). - Adapter primitive contract is **unchanged in implementation**; its doc row gains the normative superset wording. No adapter `bin/` edits. **Non-goals (do NOT build):** - Pinning (#56); adapter primitive-existence diagnostics (#55); repairing corrupted ledgers (gaps are continued past, never filled); provenance backfill onto existing records; concurrent-writer races; treating `installed_plugins.json` as a version authority; any §8 rule change; comment-author identity verification (see Noted).
Author
Owner

Test Plan: run-record-integrity-typed-comment-validation-and-suite-provenance-stamp

Prerequisites

This feature has no browser or service surface — every scenario is executed against the suite's
record tooling and observed through exit codes, emitted diagnostics, and the comments present on
a forge issue afterwards. The state the scenarios need:

  • A scratch feature issue on the project's declared forge whose comments the scenarios may
    freely write (never a real feature's ledger).
  • The ability to place a hand-authored comment on that issue (to plant near-miss and
    malformed records).
  • A suite root of each of the three provenance shapes: (a) inside a git checkout, reachable
    in both a clean and a locally-modified state; (b) an installed-plugin-shaped directory that
    is not inside any git work tree and carries the plugin's own manifest declaring a version;
    (c) a root where neither identity is determinable.
  • One pre-feature ledger: an issue whose Phase Outcomes were all written by the existing
    tooling before this feature (read-only — it is never written to).

Required Test Data

  • On the scratch issue, a ledger of N (≥2) well-formed Phase Outcome records with contiguous
    ordinals 1..N, written by the record tooling itself.
  • A planted near-miss comment: first line contains the Phase Outcome schema token but
    does not match the anchored header shape (the real-world shape: a prose note borrowing the
    schema name).
  • A planted malformed record: first line matches the anchored header shape, but the body
    does not parse as the schema (e.g. the JSON fence absent or truncated).
  • A planted near-miss for a second typed schema (e.g. a decision-resolution token in a
    prose first line).
  • A second scratch issue with no Phase Outcome comments at all (the empty ledger).

Test Scenarios

Scenario 1: A look-alike comment cannot shift the numbering

Acceptance criterion: AC1 — near-miss present, new record minted at N+1 (no skip), post warns naming the near-miss comment's id.

  1. Start from the scratch issue holding N well-formed records (ordinals 1..N) plus the planted near-miss comment.
  2. Post a new Phase Outcome through the record tooling.
  3. Verify: the post succeeds, and the newly created record carries ordinal N+1 — not N+2.
  4. Verify: the post's diagnostic output contains a warning that names the near-miss comment's forge comment id.
  5. Verify: the near-miss comment itself is unchanged on the issue.

Expected outcome: the ledger numbering is exactly as if the look-alike did not exist, and the operator was told which comment to clean up.

Lane: integration-coveredplugin/skills/_shared/procedures/test/typed-scan.sh (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project).

Scenario 2: Reading past a look-alike succeeds and says so

Acceptance criterion: AC2 — reads succeed (exit 0), return exactly the N parsed records, exclude the near-miss, warn naming its comment id.

  1. Same starting state as Scenario 1 (before its new post, or with N+1 records after — either contiguous ledger).
  2. Read the full ledger; then read the latest record.
  3. Verify: both reads exit 0.
  4. Verify: the full read returns exactly the well-formed records — the near-miss appears nowhere in the data output.
  5. Verify: each read's diagnostic output warns, naming the near-miss comment's id.

Expected outcome: consumers get a clean, complete ledger plus a pointer to the stray comment — never a crash.

Lane: integration-coveredplugin/skills/_shared/procedures/test/typed-scan.sh (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project).

Scenario 3: A record that claims the format but can't be read blocks the writer, loudly

Acceptance criterion: AC3 — post exits non-zero, no new comment created, error names comment id + unparseable element + repair route.

  1. On the scratch issue, plant the malformed record (anchored header, unparseable body). Note the issue's comment count.
  2. Attempt to post a new Phase Outcome.
  3. Verify: the post exits non-zero.
  4. Verify: the issue's comment count is unchanged — no new comment of any kind was created.
  5. Verify: the error output names the malformed comment's id, states what could not be parsed, and states how to repair (fix or strip that comment).

Expected outcome: the writer refuses to extend a ledger it cannot fully read, and its refusal is a repair instruction, not a stack trace.

Lane: integration-coveredplugin/skills/_shared/procedures/test/typed-scan.sh (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project).

Scenario 4: The same bad record makes reads diagnose, not crash

Acceptance criterion: AC4 — reader exits non-zero with a diagnosis naming comment id + element; never an unhandled jq error.

  1. Same starting state as Scenario 3.
  2. Read the full ledger; read the latest record.
  3. Verify: each read exits non-zero.
  4. Verify: the output names the malformed comment's id and the unparseable element.
  5. Verify: the output contains no raw parser stack error of the pre-feature shape (a bare tool error naming no comment).

Expected outcome: the first thing a failed read tells you is which comment broke it and why.

Lane: integration-coveredplugin/skills/_shared/procedures/test/typed-scan.sh (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project).

Scenario 5: The fix covers the whole family of record types

Acceptance criterion: AC5 — near-miss for another typed schema is excluded with a warning via the shared path; the enumerated ledger consumers complete without error.

  1. On the scratch issue, plant the second-schema near-miss comment.
  2. Scan/read that schema's records through the tooling.
  3. Verify: the near-miss is excluded from the results, with a warning naming its comment id.
  4. Exercise each consumer enumerated in the PREQ's Dependencies (decision discovery; the resume-detection reads; the QA retry guard's read) against this issue.
  5. Verify: each completes without error.

Expected outcome: no typed record family remains where a look-alike comment can crash or pollute a consumer.

Lane: integration-coveredplugin/skills/_shared/procedures/test/typed-scan.sh (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project).

Scenario 6: The very first record of a feature

Acceptance criterion: AC6 — empty ledger: first post mints ordinal 1; a read of an empty ledger reports empty without error.

  1. Start from the second scratch issue (no Phase Outcome comments).
  2. Read the ledger. Verify: the read reports an empty ledger without error.
  3. Post a Phase Outcome. Verify: it is minted at ordinal 1.

Expected outcome: a brand-new feature's first record behaves exactly as every feature's first record always has.

Lane: integration-coveredplugin/skills/_shared/procedures/test/typed-scan.sh (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project).

Scenario 7: A dev-checkout run stamps the commit it ran, and whether the tree was clean

Acceptance criterion: AC7 — suite root inside a git work tree: record carries the checkout's commit SHA and a dirty/clean marker, demonstrated in both states.

  1. With the suite root inside a git checkout and the checkout clean, post a Phase Outcome to the scratch issue.
  2. Verify: the new record carries the checkout's current commit SHA and a clean marker.
  3. Make any local modification within the checkout (unreleased edit), post again.
  4. Verify: the new record carries the SHA and a dirty marker.
  5. Revert the modification.

Expected outcome: a record from the dev machine says exactly which commit produced it — and admits when the tree had uncommitted changes on top.

Lane: integration-coveredplugin/skills/_shared/procedures/test/provenance.sh (new suite delivered red-first by this slice; exercises git-clean, git-dirty, manifest-only and undecidable suite-root shapes via temp copies of the helper tree).

Scenario 8: An installed-plugin run stamps its release version, with the identical invocation

Acceptance criterion: AC8 — suite root not inside a git work tree: the same post invocation, unchanged, records the version the root's own manifest declares.

  1. With the suite root being the installed-plugin-shaped directory (not in any git work tree; manifest declaring a known version), run the same post invocation as Scenario 7 — no additional flag, configuration, or operator input.
  2. Verify: the new record carries exactly the version the manifest declares.

Expected outcome: consumer-machine records are attributable to a release with nobody remembering anything.

Lane: integration-coveredplugin/skills/_shared/procedures/test/provenance.sh (new suite delivered red-first by this slice; exercises git-clean, git-dirty, manifest-only and undecidable suite-root shapes via temp copies of the helper tree).

Scenario 9: When the tooling can't tell, it says "unknown" and never blocks the record

Acceptance criterion: AC9 — probe cannot determine either identity: post still succeeds; record carries an explicit unknown-provenance marker.

  1. With the suite root arranged so neither identity is determinable, post a Phase Outcome.
  2. Verify: the post succeeds.
  3. Verify: the record carries an explicit unknown-provenance marker — the field is present and says unknown, not absent.

Expected outcome: provenance trouble can never cost you the record itself.

Lane: integration-coveredplugin/skills/_shared/procedures/test/provenance.sh (new suite delivered red-first by this slice; exercises git-clean, git-dirty, manifest-only and undecidable suite-root shapes via temp copies of the helper tree).

Scenario 10: Yesterday's ledgers still read clean

Acceptance criterion: AC10 — a pre-feature ledger of tooling-written records: read exits 0, returns the same records as the pre-feature tooling did, emits no warnings.

  1. Before switching tooling, capture the pre-feature read output of the designated read-only ledger (record count and ids).
  2. With the new tooling, read the same ledger.
  3. Verify: exit 0; the same records, in the same order, with the same ids; no warnings emitted.

Expected outcome: every ledger written before this feature is untouched by it — no migration, no noise.

Lane: integration-coveredplugin/skills/_shared/procedures/test/typed-scan.sh (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project). Additionally executed read-only against the real #50 and #57 ledgers during QA validation.

Traceability

Forward: AC1→S1, AC2→S2, AC3→S3, AC4→S4, AC5→S5, AC6→S6, AC7→S7, AC8→S8, AC9→S9, AC10→S10.
Backward: every scenario names its criterion above. No orphans; no implementation-necessity
exceptions were flagged (the project's observability policy is none).

<!-- test-plan:v1 issue=57 skill=technical-plan --> # Test Plan: run-record-integrity-typed-comment-validation-and-suite-provenance-stamp ## Prerequisites This feature has no browser or service surface — every scenario is executed against the suite's record tooling and observed through exit codes, emitted diagnostics, and the comments present on a forge issue afterwards. The state the scenarios need: - [ ] A scratch feature issue on the project's declared forge whose comments the scenarios may freely write (never a real feature's ledger). - [ ] The ability to place a hand-authored comment on that issue (to plant near-miss and malformed records). - [ ] A suite root of each of the three provenance shapes: (a) inside a git checkout, reachable in both a clean and a locally-modified state; (b) an installed-plugin-shaped directory that is not inside any git work tree and carries the plugin's own manifest declaring a version; (c) a root where neither identity is determinable. - [ ] One pre-feature ledger: an issue whose Phase Outcomes were all written by the existing tooling before this feature (read-only — it is never written to). ### Required Test Data - [ ] On the scratch issue, a ledger of N (≥2) well-formed Phase Outcome records with contiguous ordinals 1..N, written by the record tooling itself. - [ ] A planted **near-miss** comment: first line contains the Phase Outcome schema token but does not match the anchored header shape (the real-world shape: a prose note borrowing the schema name). - [ ] A planted **malformed record**: first line matches the anchored header shape, but the body does not parse as the schema (e.g. the JSON fence absent or truncated). - [ ] A planted near-miss for a **second typed schema** (e.g. a decision-resolution token in a prose first line). - [ ] A second scratch issue with **no** Phase Outcome comments at all (the empty ledger). ## Test Scenarios ### Scenario 1: A look-alike comment cannot shift the numbering **Acceptance criterion:** AC1 — near-miss present, new record minted at N+1 (no skip), post warns naming the near-miss comment's id. 1. Start from the scratch issue holding N well-formed records (ordinals 1..N) plus the planted near-miss comment. 2. Post a new Phase Outcome through the record tooling. 3. Verify: the post succeeds, and the newly created record carries ordinal N+1 — not N+2. 4. Verify: the post's diagnostic output contains a warning that names the near-miss comment's forge comment id. 5. Verify: the near-miss comment itself is unchanged on the issue. **Expected outcome:** the ledger numbering is exactly as if the look-alike did not exist, and the operator was told which comment to clean up. **Lane:** `integration-covered` — `plugin/skills/_shared/procedures/test/typed-scan.sh` (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project). ### Scenario 2: Reading past a look-alike succeeds and says so **Acceptance criterion:** AC2 — reads succeed (exit 0), return exactly the N parsed records, exclude the near-miss, warn naming its comment id. 1. Same starting state as Scenario 1 (before its new post, or with N+1 records after — either contiguous ledger). 2. Read the full ledger; then read the latest record. 3. Verify: both reads exit 0. 4. Verify: the full read returns exactly the well-formed records — the near-miss appears nowhere in the data output. 5. Verify: each read's diagnostic output warns, naming the near-miss comment's id. **Expected outcome:** consumers get a clean, complete ledger plus a pointer to the stray comment — never a crash. **Lane:** `integration-covered` — `plugin/skills/_shared/procedures/test/typed-scan.sh` (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project). ### Scenario 3: A record that claims the format but can't be read blocks the writer, loudly **Acceptance criterion:** AC3 — post exits non-zero, no new comment created, error names comment id + unparseable element + repair route. 1. On the scratch issue, plant the malformed record (anchored header, unparseable body). Note the issue's comment count. 2. Attempt to post a new Phase Outcome. 3. Verify: the post exits non-zero. 4. Verify: the issue's comment count is unchanged — no new comment of any kind was created. 5. Verify: the error output names the malformed comment's id, states what could not be parsed, and states how to repair (fix or strip that comment). **Expected outcome:** the writer refuses to extend a ledger it cannot fully read, and its refusal is a repair instruction, not a stack trace. **Lane:** `integration-covered` — `plugin/skills/_shared/procedures/test/typed-scan.sh` (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project). ### Scenario 4: The same bad record makes reads diagnose, not crash **Acceptance criterion:** AC4 — reader exits non-zero with a diagnosis naming comment id + element; never an unhandled jq error. 1. Same starting state as Scenario 3. 2. Read the full ledger; read the latest record. 3. Verify: each read exits non-zero. 4. Verify: the output names the malformed comment's id and the unparseable element. 5. Verify: the output contains no raw parser stack error of the pre-feature shape (a bare tool error naming no comment). **Expected outcome:** the first thing a failed read tells you is which comment broke it and why. **Lane:** `integration-covered` — `plugin/skills/_shared/procedures/test/typed-scan.sh` (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project). ### Scenario 5: The fix covers the whole family of record types **Acceptance criterion:** AC5 — near-miss for another typed schema is excluded with a warning via the shared path; the enumerated ledger consumers complete without error. 1. On the scratch issue, plant the second-schema near-miss comment. 2. Scan/read that schema's records through the tooling. 3. Verify: the near-miss is excluded from the results, with a warning naming its comment id. 4. Exercise each consumer enumerated in the PREQ's Dependencies (decision discovery; the resume-detection reads; the QA retry guard's read) against this issue. 5. Verify: each completes without error. **Expected outcome:** no typed record family remains where a look-alike comment can crash or pollute a consumer. **Lane:** `integration-covered` — `plugin/skills/_shared/procedures/test/typed-scan.sh` (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project). ### Scenario 6: The very first record of a feature **Acceptance criterion:** AC6 — empty ledger: first post mints ordinal 1; a read of an empty ledger reports empty without error. 1. Start from the second scratch issue (no Phase Outcome comments). 2. Read the ledger. Verify: the read reports an empty ledger without error. 3. Post a Phase Outcome. Verify: it is minted at ordinal 1. **Expected outcome:** a brand-new feature's first record behaves exactly as every feature's first record always has. **Lane:** `integration-covered` — `plugin/skills/_shared/procedures/test/typed-scan.sh` (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project). ### Scenario 7: A dev-checkout run stamps the commit it ran, and whether the tree was clean **Acceptance criterion:** AC7 — suite root inside a git work tree: record carries the checkout's commit SHA and a dirty/clean marker, demonstrated in both states. 1. With the suite root inside a git checkout and the checkout clean, post a Phase Outcome to the scratch issue. 2. Verify: the new record carries the checkout's current commit SHA and a clean marker. 3. Make any local modification within the checkout (unreleased edit), post again. 4. Verify: the new record carries the SHA and a dirty marker. 5. Revert the modification. **Expected outcome:** a record from the dev machine says exactly which commit produced it — and admits when the tree had uncommitted changes on top. **Lane:** `integration-covered` — `plugin/skills/_shared/procedures/test/provenance.sh` (new suite delivered red-first by this slice; exercises git-clean, git-dirty, manifest-only and undecidable suite-root shapes via temp copies of the helper tree). ### Scenario 8: An installed-plugin run stamps its release version, with the identical invocation **Acceptance criterion:** AC8 — suite root not inside a git work tree: the same post invocation, unchanged, records the version the root's own manifest declares. 1. With the suite root being the installed-plugin-shaped directory (not in any git work tree; manifest declaring a known version), run the same post invocation as Scenario 7 — no additional flag, configuration, or operator input. 2. Verify: the new record carries exactly the version the manifest declares. **Expected outcome:** consumer-machine records are attributable to a release with nobody remembering anything. **Lane:** `integration-covered` — `plugin/skills/_shared/procedures/test/provenance.sh` (new suite delivered red-first by this slice; exercises git-clean, git-dirty, manifest-only and undecidable suite-root shapes via temp copies of the helper tree). ### Scenario 9: When the tooling can't tell, it says "unknown" and never blocks the record **Acceptance criterion:** AC9 — probe cannot determine either identity: post still succeeds; record carries an explicit unknown-provenance marker. 1. With the suite root arranged so neither identity is determinable, post a Phase Outcome. 2. Verify: the post succeeds. 3. Verify: the record carries an explicit unknown-provenance marker — the field is present and says unknown, not absent. **Expected outcome:** provenance trouble can never cost you the record itself. **Lane:** `integration-covered` — `plugin/skills/_shared/procedures/test/provenance.sh` (new suite delivered red-first by this slice; exercises git-clean, git-dirty, manifest-only and undecidable suite-root shapes via temp copies of the helper tree). ### Scenario 10: Yesterday's ledgers still read clean **Acceptance criterion:** AC10 — a pre-feature ledger of tooling-written records: read exits 0, returns the same records as the pre-feature tooling did, emits no warnings. 1. Before switching tooling, capture the pre-feature read output of the designated read-only ledger (record count and ids). 2. With the new tooling, read the same ledger. 3. Verify: exit 0; the same records, in the same order, with the same ids; no warnings emitted. **Expected outcome:** every ledger written before this feature is untouched by it — no migration, no noise. **Lane:** `integration-covered` — `plugin/skills/_shared/procedures/test/typed-scan.sh` (new suite delivered red-first by this slice; drives the record tooling against the local-fs adapter in a temp project). Additionally executed read-only against the real #50 and #57 ledgers during QA validation. ## Traceability Forward: AC1→S1, AC2→S2, AC3→S3, AC4→S4, AC5→S5, AC6→S6, AC7→S7, AC8→S8, AC9→S9, AC10→S10. Backward: every scenario names its criterion above. No orphans; no implementation-necessity exceptions were flagged (the project's observability policy is `none`).
Author
Owner
{
  "next_state": "developing",
  "produced": [
    {
      "kind": "sreq",
      "ref": "comment:1147",
      "summary": "SREQ for run-record-integrity (Tier 2)"
    },
    {
      "kind": "test-plan",
      "ref": "comment:1154",
      "summary": "Test plan for run-record-integrity — one+ scenario per acceptance criterion, lanes annotated (10x integration-covered)"
    }
  ],
  "findings": [
    {
      "category": "out-of-scope",
      "summary": "Pinning — preventing suite text from changing mid-run",
      "reasoning": "Already tracked as #56 (split pre-promotion, narrowed by #50: consumer installs are immutable; only the dev checkout can move). No new issue needed.",
      "proposed_action": "accept",
      "target": null,
      "id": "F-PO-57-2-1"
    },
    {
      "category": "out-of-scope",
      "summary": "Adapter primitive-existence defect (unimplemented pipeline_status indistinguishable from a broken helper)",
      "reasoning": "Already tracked as #55. Forge-contract work across four adapters, not run-record work.",
      "proposed_action": "accept",
      "target": null,
      "id": "F-PO-57-2-2"
    },
    {
      "category": "out-of-scope",
      "summary": "Repairing already-corrupted ledgers (pre-existing ordinal gaps)",
      "reasoning": "Deliberate non-goal in the approved PREQ: minting continues past gaps, the density check keeps flagging them. No follow-up work exists.",
      "proposed_action": "accept",
      "target": null,
      "id": "F-PO-57-2-3"
    },
    {
      "category": "out-of-scope",
      "summary": "Retrofitting the provenance field onto already-posted Phase Outcomes",
      "reasoning": "Deliberate non-goal: backward readability is required (AC10), backfill is not. Old records read as suite:null.",
      "proposed_action": "accept",
      "target": null,
      "id": "F-PO-57-2-4"
    },
    {
      "category": "out-of-scope",
      "summary": "Concurrent-writer races (two simultaneous posts computing the same ordinal)",
      "reasoning": "Deliberate non-goal: single-orchestrator-per-issue assumption stands; the existing collision check keeps its role.",
      "proposed_action": "accept",
      "target": null,
      "id": "F-PO-57-2-5"
    },
    {
      "category": "out-of-scope",
      "summary": "Treating installed_plugins.json as a version authority",
      "reasoning": "Deliberate non-goal: its path and shape are Claude Code internals; the authority is the plugin manifest at the suite root.",
      "proposed_action": "accept",
      "target": null,
      "id": "F-PO-57-2-6"
    },
    {
      "category": "out-of-scope",
      "summary": "Changing contract §8 immutability rules",
      "reasoning": "Deliberate non-goal: §8 is untouched; the plan only states explicitly that a comment which does not parse as a record is not a record, so repairing it is sanctioned.",
      "proposed_action": "accept",
      "target": null,
      "id": "F-PO-57-2-7"
    },
    {
      "category": "in-scope-deferrable",
      "summary": "Comment-author identity verification before a record influences minting or resume detection",
      "reasoning": "Reviewer suggestion acknowledged but not actioned — see Expert Review > Noted. Rejected on scope and portability grounds: the PREQ threat model is imperfect input, not malicious forge-writers (forge ACLs are the trust boundary), and local-fs has no author identity, so a portable contract-level check is not implementable.",
      "proposed_action": "accept",
      "id": "F-PO-57-2-8"
    },
    {
      "category": "in-scope-deferrable",
      "summary": "Surface dirty-SHA provenance during release bookkeeping (promote warning on dirty suite text)",
      "reasoning": "Reviewer suggestion acknowledged but not actioned — outside this PREQ's ACs. Natural home is #56's detection follow-on, to be decided after this feature ships (matches F-PO-57-1-1's disposition).",
      "proposed_action": "accept",
      "id": "F-PO-57-2-9"
    },
    {
      "category": "in-scope-deferrable",
      "summary": "House _suite_provenance in a separate sourced file instead of _lib.sh",
      "reasoning": "Reviewer suggestion rejected on technical grounds: a clearly-marked section in _lib.sh gives the same separation without adding a second sourcing surface every helper would need to resolve.",
      "proposed_action": "accept",
      "id": "F-PO-57-2-10"
    }
  ],
  "pending_decisions": [
    {
      "id": "D-PO-57-2-1",
      "type": "scope-disposition",
      "blocking": false,
      "question": "Out of scope: 'Pinning — preventing suite text from changing mid-run'. Spawn a sibling issue, or accept (no follow-up)?",
      "options": [
        "defer-to-issue",
        "accept"
      ],
      "recommended": "accept",
      "finding_ref": "F-PO-57-2-1",
      "reasoning": "Already tracked as #56 (split pre-promotion, narrowed by #50: consumer installs are immutable; only the dev checkout can move). No new issue needed."
    },
    {
      "id": "D-PO-57-2-2",
      "type": "scope-disposition",
      "blocking": false,
      "question": "Out of scope: 'Adapter primitive-existence defect (unimplemented pipeline_status indistinguishable from a broken helper)'. Spawn a sibling issue, or accept (no follow-up)?",
      "options": [
        "defer-to-issue",
        "accept"
      ],
      "recommended": "accept",
      "finding_ref": "F-PO-57-2-2",
      "reasoning": "Already tracked as #55. Forge-contract work across four adapters, not run-record work."
    },
    {
      "id": "D-PO-57-2-3",
      "type": "scope-disposition",
      "blocking": false,
      "question": "Out of scope: 'Repairing already-corrupted ledgers (pre-existing ordinal gaps)'. Spawn a sibling issue, or accept (no follow-up)?",
      "options": [
        "defer-to-issue",
        "accept"
      ],
      "recommended": "accept",
      "finding_ref": "F-PO-57-2-3",
      "reasoning": "Deliberate non-goal in the approved PREQ: minting continues past gaps, the density check keeps flagging them. No follow-up work exists."
    },
    {
      "id": "D-PO-57-2-4",
      "type": "scope-disposition",
      "blocking": false,
      "question": "Out of scope: 'Retrofitting the provenance field onto already-posted Phase Outcomes'. Spawn a sibling issue, or accept (no follow-up)?",
      "options": [
        "defer-to-issue",
        "accept"
      ],
      "recommended": "accept",
      "finding_ref": "F-PO-57-2-4",
      "reasoning": "Deliberate non-goal: backward readability is required (AC10), backfill is not. Old records read as suite:null."
    },
    {
      "id": "D-PO-57-2-5",
      "type": "scope-disposition",
      "blocking": false,
      "question": "Out of scope: 'Concurrent-writer races (two simultaneous posts computing the same ordinal)'. Spawn a sibling issue, or accept (no follow-up)?",
      "options": [
        "defer-to-issue",
        "accept"
      ],
      "recommended": "accept",
      "finding_ref": "F-PO-57-2-5",
      "reasoning": "Deliberate non-goal: single-orchestrator-per-issue assumption stands; the existing collision check keeps its role."
    },
    {
      "id": "D-PO-57-2-6",
      "type": "scope-disposition",
      "blocking": false,
      "question": "Out of scope: 'Treating installed_plugins.json as a version authority'. Spawn a sibling issue, or accept (no follow-up)?",
      "options": [
        "defer-to-issue",
        "accept"
      ],
      "recommended": "accept",
      "finding_ref": "F-PO-57-2-6",
      "reasoning": "Deliberate non-goal: its path and shape are Claude Code internals; the authority is the plugin manifest at the suite root."
    },
    {
      "id": "D-PO-57-2-7",
      "type": "scope-disposition",
      "blocking": false,
      "question": "Out of scope: 'Changing contract §8 immutability rules'. Spawn a sibling issue, or accept (no follow-up)?",
      "options": [
        "defer-to-issue",
        "accept"
      ],
      "recommended": "accept",
      "finding_ref": "F-PO-57-2-7",
      "reasoning": "Deliberate non-goal: §8 is untouched; the plan only states explicitly that a comment which does not parse as a record is not a record, so repairing it is sanctioned."
    },
    {
      "id": "D-PO-57-2-8",
      "type": "scope-disposition",
      "blocking": false,
      "question": "Expert-review note (not actioned): 'Comment-author identity verification before a record influences minting or resume detection'. Spawn a sibling issue, or accept the disposition?",
      "options": [
        "defer-to-issue",
        "accept"
      ],
      "recommended": "accept",
      "finding_ref": "F-PO-57-2-8",
      "reasoning": "Reviewer suggestion acknowledged but not actioned — see Expert Review > Noted. Rejected on scope and portability grounds: the PREQ threat model is imperfect input, not malicious forge-writers (forge ACLs are the trust boundary), and local-fs has no author identity, so a portable contract-level check is not implementable."
    },
    {
      "id": "D-PO-57-2-9",
      "type": "scope-disposition",
      "blocking": false,
      "question": "Expert-review note (not actioned): 'Surface dirty-SHA provenance during release bookkeeping (promote warning on dirty suite text)'. Spawn a sibling issue, or accept the disposition?",
      "options": [
        "defer-to-issue",
        "accept"
      ],
      "recommended": "accept",
      "finding_ref": "F-PO-57-2-9",
      "reasoning": "Reviewer suggestion acknowledged but not actioned — outside this PREQ's ACs. Natural home is #56's detection follow-on, to be decided after this feature ships (matches F-PO-57-1-1's disposition)."
    },
    {
      "id": "D-PO-57-2-10",
      "type": "scope-disposition",
      "blocking": false,
      "question": "Expert-review note (not actioned): 'House _suite_provenance in a separate sourced file instead of _lib.sh'. Spawn a sibling issue, or accept the disposition?",
      "options": [
        "defer-to-issue",
        "accept"
      ],
      "recommended": "accept",
      "finding_ref": "F-PO-57-2-10",
      "reasoning": "Reviewer suggestion rejected on technical grounds: a clearly-marked section in _lib.sh gives the same separation without adding a second sourcing surface every helper would need to resolve."
    }
  ]
}
<!-- phase-outcome:v1 id=PO-57-2 skill=technical-plan --> ```json { "next_state": "developing", "produced": [ { "kind": "sreq", "ref": "comment:1147", "summary": "SREQ for run-record-integrity (Tier 2)" }, { "kind": "test-plan", "ref": "comment:1154", "summary": "Test plan for run-record-integrity — one+ scenario per acceptance criterion, lanes annotated (10x integration-covered)" } ], "findings": [ { "category": "out-of-scope", "summary": "Pinning — preventing suite text from changing mid-run", "reasoning": "Already tracked as #56 (split pre-promotion, narrowed by #50: consumer installs are immutable; only the dev checkout can move). No new issue needed.", "proposed_action": "accept", "target": null, "id": "F-PO-57-2-1" }, { "category": "out-of-scope", "summary": "Adapter primitive-existence defect (unimplemented pipeline_status indistinguishable from a broken helper)", "reasoning": "Already tracked as #55. Forge-contract work across four adapters, not run-record work.", "proposed_action": "accept", "target": null, "id": "F-PO-57-2-2" }, { "category": "out-of-scope", "summary": "Repairing already-corrupted ledgers (pre-existing ordinal gaps)", "reasoning": "Deliberate non-goal in the approved PREQ: minting continues past gaps, the density check keeps flagging them. No follow-up work exists.", "proposed_action": "accept", "target": null, "id": "F-PO-57-2-3" }, { "category": "out-of-scope", "summary": "Retrofitting the provenance field onto already-posted Phase Outcomes", "reasoning": "Deliberate non-goal: backward readability is required (AC10), backfill is not. Old records read as suite:null.", "proposed_action": "accept", "target": null, "id": "F-PO-57-2-4" }, { "category": "out-of-scope", "summary": "Concurrent-writer races (two simultaneous posts computing the same ordinal)", "reasoning": "Deliberate non-goal: single-orchestrator-per-issue assumption stands; the existing collision check keeps its role.", "proposed_action": "accept", "target": null, "id": "F-PO-57-2-5" }, { "category": "out-of-scope", "summary": "Treating installed_plugins.json as a version authority", "reasoning": "Deliberate non-goal: its path and shape are Claude Code internals; the authority is the plugin manifest at the suite root.", "proposed_action": "accept", "target": null, "id": "F-PO-57-2-6" }, { "category": "out-of-scope", "summary": "Changing contract §8 immutability rules", "reasoning": "Deliberate non-goal: §8 is untouched; the plan only states explicitly that a comment which does not parse as a record is not a record, so repairing it is sanctioned.", "proposed_action": "accept", "target": null, "id": "F-PO-57-2-7" }, { "category": "in-scope-deferrable", "summary": "Comment-author identity verification before a record influences minting or resume detection", "reasoning": "Reviewer suggestion acknowledged but not actioned — see Expert Review > Noted. Rejected on scope and portability grounds: the PREQ threat model is imperfect input, not malicious forge-writers (forge ACLs are the trust boundary), and local-fs has no author identity, so a portable contract-level check is not implementable.", "proposed_action": "accept", "id": "F-PO-57-2-8" }, { "category": "in-scope-deferrable", "summary": "Surface dirty-SHA provenance during release bookkeeping (promote warning on dirty suite text)", "reasoning": "Reviewer suggestion acknowledged but not actioned — outside this PREQ's ACs. Natural home is #56's detection follow-on, to be decided after this feature ships (matches F-PO-57-1-1's disposition).", "proposed_action": "accept", "id": "F-PO-57-2-9" }, { "category": "in-scope-deferrable", "summary": "House _suite_provenance in a separate sourced file instead of _lib.sh", "reasoning": "Reviewer suggestion rejected on technical grounds: a clearly-marked section in _lib.sh gives the same separation without adding a second sourcing surface every helper would need to resolve.", "proposed_action": "accept", "id": "F-PO-57-2-10" } ], "pending_decisions": [ { "id": "D-PO-57-2-1", "type": "scope-disposition", "blocking": false, "question": "Out of scope: 'Pinning — preventing suite text from changing mid-run'. Spawn a sibling issue, or accept (no follow-up)?", "options": [ "defer-to-issue", "accept" ], "recommended": "accept", "finding_ref": "F-PO-57-2-1", "reasoning": "Already tracked as #56 (split pre-promotion, narrowed by #50: consumer installs are immutable; only the dev checkout can move). No new issue needed." }, { "id": "D-PO-57-2-2", "type": "scope-disposition", "blocking": false, "question": "Out of scope: 'Adapter primitive-existence defect (unimplemented pipeline_status indistinguishable from a broken helper)'. Spawn a sibling issue, or accept (no follow-up)?", "options": [ "defer-to-issue", "accept" ], "recommended": "accept", "finding_ref": "F-PO-57-2-2", "reasoning": "Already tracked as #55. Forge-contract work across four adapters, not run-record work." }, { "id": "D-PO-57-2-3", "type": "scope-disposition", "blocking": false, "question": "Out of scope: 'Repairing already-corrupted ledgers (pre-existing ordinal gaps)'. Spawn a sibling issue, or accept (no follow-up)?", "options": [ "defer-to-issue", "accept" ], "recommended": "accept", "finding_ref": "F-PO-57-2-3", "reasoning": "Deliberate non-goal in the approved PREQ: minting continues past gaps, the density check keeps flagging them. No follow-up work exists." }, { "id": "D-PO-57-2-4", "type": "scope-disposition", "blocking": false, "question": "Out of scope: 'Retrofitting the provenance field onto already-posted Phase Outcomes'. Spawn a sibling issue, or accept (no follow-up)?", "options": [ "defer-to-issue", "accept" ], "recommended": "accept", "finding_ref": "F-PO-57-2-4", "reasoning": "Deliberate non-goal: backward readability is required (AC10), backfill is not. Old records read as suite:null." }, { "id": "D-PO-57-2-5", "type": "scope-disposition", "blocking": false, "question": "Out of scope: 'Concurrent-writer races (two simultaneous posts computing the same ordinal)'. Spawn a sibling issue, or accept (no follow-up)?", "options": [ "defer-to-issue", "accept" ], "recommended": "accept", "finding_ref": "F-PO-57-2-5", "reasoning": "Deliberate non-goal: single-orchestrator-per-issue assumption stands; the existing collision check keeps its role." }, { "id": "D-PO-57-2-6", "type": "scope-disposition", "blocking": false, "question": "Out of scope: 'Treating installed_plugins.json as a version authority'. Spawn a sibling issue, or accept (no follow-up)?", "options": [ "defer-to-issue", "accept" ], "recommended": "accept", "finding_ref": "F-PO-57-2-6", "reasoning": "Deliberate non-goal: its path and shape are Claude Code internals; the authority is the plugin manifest at the suite root." }, { "id": "D-PO-57-2-7", "type": "scope-disposition", "blocking": false, "question": "Out of scope: 'Changing contract §8 immutability rules'. Spawn a sibling issue, or accept (no follow-up)?", "options": [ "defer-to-issue", "accept" ], "recommended": "accept", "finding_ref": "F-PO-57-2-7", "reasoning": "Deliberate non-goal: §8 is untouched; the plan only states explicitly that a comment which does not parse as a record is not a record, so repairing it is sanctioned." }, { "id": "D-PO-57-2-8", "type": "scope-disposition", "blocking": false, "question": "Expert-review note (not actioned): 'Comment-author identity verification before a record influences minting or resume detection'. Spawn a sibling issue, or accept the disposition?", "options": [ "defer-to-issue", "accept" ], "recommended": "accept", "finding_ref": "F-PO-57-2-8", "reasoning": "Reviewer suggestion acknowledged but not actioned — see Expert Review > Noted. Rejected on scope and portability grounds: the PREQ threat model is imperfect input, not malicious forge-writers (forge ACLs are the trust boundary), and local-fs has no author identity, so a portable contract-level check is not implementable." }, { "id": "D-PO-57-2-9", "type": "scope-disposition", "blocking": false, "question": "Expert-review note (not actioned): 'Surface dirty-SHA provenance during release bookkeeping (promote warning on dirty suite text)'. Spawn a sibling issue, or accept the disposition?", "options": [ "defer-to-issue", "accept" ], "recommended": "accept", "finding_ref": "F-PO-57-2-9", "reasoning": "Reviewer suggestion acknowledged but not actioned — outside this PREQ's ACs. Natural home is #56's detection follow-on, to be decided after this feature ships (matches F-PO-57-1-1's disposition)." }, { "id": "D-PO-57-2-10", "type": "scope-disposition", "blocking": false, "question": "Expert-review note (not actioned): 'House _suite_provenance in a separate sourced file instead of _lib.sh'. Spawn a sibling issue, or accept the disposition?", "options": [ "defer-to-issue", "accept" ], "recommended": "accept", "finding_ref": "F-PO-57-2-10", "reasoning": "Reviewer suggestion rejected on technical grounds: a clearly-marked section in _lib.sh gives the same separation without adding a second sourcing surface every helper would need to resolve." } ] } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "accept",
  "rationale": "Accepted the recommendation, on the recorded grounds: Already tracked as #56 (split pre-promotion, narrowed by #50: consumer installs are immutable; only the dev checkout can move). No new issue needed. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol."
}
<!-- decision-resolution:v1 ref=D-PO-57-2-1 --> ```json { "status": "resolved", "chosen_option": "accept", "rationale": "Accepted the recommendation, on the recorded grounds: Already tracked as #56 (split pre-promotion, narrowed by #50: consumer installs are immutable; only the dev checkout can move). No new issue needed. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol." } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "accept",
  "rationale": "Accepted the recommendation, on the recorded grounds: Already tracked as #55. Forge-contract work across four adapters, not run-record work. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol."
}
<!-- decision-resolution:v1 ref=D-PO-57-2-2 --> ```json { "status": "resolved", "chosen_option": "accept", "rationale": "Accepted the recommendation, on the recorded grounds: Already tracked as #55. Forge-contract work across four adapters, not run-record work. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol." } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "accept",
  "rationale": "Accepted the recommendation, on the recorded grounds: Deliberate non-goal in the approved PREQ: minting continues past gaps, the density check keeps flagging them. No follow-up work exists. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol."
}
<!-- decision-resolution:v1 ref=D-PO-57-2-3 --> ```json { "status": "resolved", "chosen_option": "accept", "rationale": "Accepted the recommendation, on the recorded grounds: Deliberate non-goal in the approved PREQ: minting continues past gaps, the density check keeps flagging them. No follow-up work exists. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol." } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "accept",
  "rationale": "Accepted the recommendation, on the recorded grounds: Deliberate non-goal: backward readability is required (AC10), backfill is not. Old records read as suite:null. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol."
}
<!-- decision-resolution:v1 ref=D-PO-57-2-4 --> ```json { "status": "resolved", "chosen_option": "accept", "rationale": "Accepted the recommendation, on the recorded grounds: Deliberate non-goal: backward readability is required (AC10), backfill is not. Old records read as suite:null. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol." } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "accept",
  "rationale": "Accepted the recommendation, on the recorded grounds: Deliberate non-goal: single-orchestrator-per-issue assumption stands; the existing collision check keeps its role. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol."
}
<!-- decision-resolution:v1 ref=D-PO-57-2-5 --> ```json { "status": "resolved", "chosen_option": "accept", "rationale": "Accepted the recommendation, on the recorded grounds: Deliberate non-goal: single-orchestrator-per-issue assumption stands; the existing collision check keeps its role. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol." } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "accept",
  "rationale": "Accepted the recommendation, on the recorded grounds: Deliberate non-goal: its path and shape are Claude Code internals; the authority is the plugin manifest at the suite root. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol."
}
<!-- decision-resolution:v1 ref=D-PO-57-2-6 --> ```json { "status": "resolved", "chosen_option": "accept", "rationale": "Accepted the recommendation, on the recorded grounds: Deliberate non-goal: its path and shape are Claude Code internals; the authority is the plugin manifest at the suite root. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol." } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "accept",
  "rationale": "Accepted the recommendation, on the recorded grounds: Deliberate non-goal: §8 is untouched; the plan only states explicitly that a comment which does not parse as a record is not a record, so repairing it is sanctioned. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol."
}
<!-- decision-resolution:v1 ref=D-PO-57-2-7 --> ```json { "status": "resolved", "chosen_option": "accept", "rationale": "Accepted the recommendation, on the recorded grounds: Deliberate non-goal: §8 is untouched; the plan only states explicitly that a comment which does not parse as a record is not a record, so repairing it is sanctioned. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol." } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "accept",
  "rationale": "Accepted the recommendation, on the recorded grounds: Reviewer suggestion acknowledged but not actioned — see Expert Review > Noted. Rejected on scope and portability grounds: the PREQ threat model is imperfect input, not malicious forge-writers (forge ACLs are the trust boundary), and local-fs has no author identity, so a portable contract-level check is not implementable. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol."
}
<!-- decision-resolution:v1 ref=D-PO-57-2-8 --> ```json { "status": "resolved", "chosen_option": "accept", "rationale": "Accepted the recommendation, on the recorded grounds: Reviewer suggestion acknowledged but not actioned — see Expert Review > Noted. Rejected on scope and portability grounds: the PREQ threat model is imperfect input, not malicious forge-writers (forge ACLs are the trust boundary), and local-fs has no author identity, so a portable contract-level check is not implementable. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol." } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "accept",
  "rationale": "Accepted the recommendation, on the recorded grounds: Reviewer suggestion acknowledged but not actioned — outside this PREQ's ACs. Natural home is #56's detection follow-on, to be decided after this feature ships (matches F-PO-57-1-1's disposition). Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol."
}
<!-- decision-resolution:v1 ref=D-PO-57-2-9 --> ```json { "status": "resolved", "chosen_option": "accept", "rationale": "Accepted the recommendation, on the recorded grounds: Reviewer suggestion acknowledged but not actioned — outside this PREQ's ACs. Natural home is #56's detection follow-on, to be decided after this feature ships (matches F-PO-57-1-1's disposition). Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol." } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "accept",
  "rationale": "Accepted the recommendation, on the recorded grounds: Reviewer suggestion rejected on technical grounds: a clearly-marked section in _lib.sh gives the same separation without adding a second sourcing surface every helper would need to resolve. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol."
}
<!-- decision-resolution:v1 ref=D-PO-57-2-10 --> ```json { "status": "resolved", "chosen_option": "accept", "rationale": "Accepted the recommendation, on the recorded grounds: Reviewer suggestion rejected on technical grounds: a clearly-marked section in _lib.sh gives the same separation without adding a second sourcing surface every helper would need to resolve. Authority: operator instruction 2026-08-24 ('run the resolve as recommended') under the standing decision-escalation protocol." } ```
Author
Owner
{
  "waves": [
    {
      "wave": 1,
      "work_units": [
        {
          "id": "WU-57-3-1",
          "title": "Shared validation tier (_typed_scan, _suite_provenance) + PO write/read chain",
          "files": [
            "plugin/skills/_shared/procedures/bin/_lib.sh",
            "plugin/skills/_shared/procedures/bin/phase-outcome-post.sh",
            "plugin/skills/_shared/procedures/bin/phase-outcome-read-all.sh",
            "plugin/skills/_shared/procedures/test/typed-scan.sh",
            "plugin/skills/_shared/procedures/test/provenance.sh"
          ],
          "acceptance_criteria": [
            {"id": "AC-1"}, {"id": "AC-2"}, {"id": "AC-3"}, {"id": "AC-4"},
            {"id": "AC-5", "scope": "shared-path half: near-miss for a non-PO schema excluded+warned via _typed_scan (S5a)"},
            {"id": "AC-6"}, {"id": "AC-7"}, {"id": "AC-8"}, {"id": "AC-9"}, {"id": "AC-10"}
          ]
        }
      ]
    },
    {
      "wave": 2,
      "work_units": [
        {
          "id": "WU-57-3-2",
          "title": "Satellite consumers routed through _typed_scan",
          "files": [
            "plugin/skills/_shared/procedures/bin/decision-resolution-read-state.sh",
            "plugin/skills/_shared/procedures/bin/deliverable-get.sh",
            "plugin/skills/_shared/procedures/bin/comments-scan.sh",
            "plugin/skills/_shared/procedures/bin/issue-fold-finding.sh",
            "plugin/skills/_shared/procedures/bin/issue-unfold-finding.sh",
            "plugin/skills/_shared/procedures/bin/fold-candidates.sh",
            "plugin/skills/_shared/procedures/bin/promotion-candidates.sh",
            "plugin/skills/_shared/procedures/test/typed-scan-family.sh"
          ],
          "acceptance_criteria": [
            {"id": "AC-5", "scope": "consumer half: enumerated consumers (decision discovery, resume detection, retry guard, fold/unfold, candidates) complete without error on a ledger containing a near-miss (S5b)"}
          ]
        }
      ]
    },
    {
      "wave": 3,
      "work_units": [
        {
          "id": "WU-57-3-3",
          "title": "Docs + fence-aware route-through lint rule",
          "files": [
            "scripts/lint-conventions.sh",
            "plugin/skills/_shared/forge-contract.md",
            "plugin/skills/_shared/schemas/phase-outcome.v1.md",
            "plugin/skills/_shared/schemas/README.md",
            "plugin/skills/_shared/procedures/phase-outcome.md",
            "plugin/skills/requirements/procedures/requirements-from-deferred.md"
          ],
          "acceptance_criteria": [
            {"id": "AC-5", "scope": "mechanical half: fence-aware lint rule — no _prim scan_comments / direct scan_comments.sh invocation outside _typed_scan in helper tier or skill-text fences"}
          ]
        }
      ]
    }
  ]
}

Decomposition follows the SREQ's own dependency chain (§Dependencies & Sequencing): the shared tier
plus the PO post/read pair are one chain and land first with the two red-first executed suites
(WU-1); the seven satellite consumers are a mechanical route-through once _typed_scan exists
(WU-2); docs and the fence-aware lint rule come last since the lint rule can only go green after
every consumer is routed (WU-3). One deviation from the SREQ file list: S5's consumer-facing
scenarios live in a new test/typed-scan-family.sh owned by WU-2 rather than being appended to
WU-1's typed-scan.sh — work units must not share files. The workspace is a single worktree, so
waves execute sequentially (Test Writer → Implementer per WU, one WU at a time). WU-3 is
artifact-shape (docs + a lint rule that is itself the mechanical verification), so its Test Writer
is skipped per /dev:develop 3.2; WU-1 and WU-2 have behavioral ACs and run full 2-phase TDD.
Additional per-schema anchored-pattern statements (SREQ: "each schema doc states its concrete
pattern") are covered in WU-3 via schemas/README.md stating the generic family-wide rule, with
phase-outcome.v1.md carrying its concrete pattern plus the suite field, versioning policy, and
retry-guard excluded-field set; other schema docs are touched only if the implementer finds they
already restate a header shape that contradicts the rule.

<!-- wu-plan:v1 po=PO-57-3 skill=develop --> ```json { "waves": [ { "wave": 1, "work_units": [ { "id": "WU-57-3-1", "title": "Shared validation tier (_typed_scan, _suite_provenance) + PO write/read chain", "files": [ "plugin/skills/_shared/procedures/bin/_lib.sh", "plugin/skills/_shared/procedures/bin/phase-outcome-post.sh", "plugin/skills/_shared/procedures/bin/phase-outcome-read-all.sh", "plugin/skills/_shared/procedures/test/typed-scan.sh", "plugin/skills/_shared/procedures/test/provenance.sh" ], "acceptance_criteria": [ {"id": "AC-1"}, {"id": "AC-2"}, {"id": "AC-3"}, {"id": "AC-4"}, {"id": "AC-5", "scope": "shared-path half: near-miss for a non-PO schema excluded+warned via _typed_scan (S5a)"}, {"id": "AC-6"}, {"id": "AC-7"}, {"id": "AC-8"}, {"id": "AC-9"}, {"id": "AC-10"} ] } ] }, { "wave": 2, "work_units": [ { "id": "WU-57-3-2", "title": "Satellite consumers routed through _typed_scan", "files": [ "plugin/skills/_shared/procedures/bin/decision-resolution-read-state.sh", "plugin/skills/_shared/procedures/bin/deliverable-get.sh", "plugin/skills/_shared/procedures/bin/comments-scan.sh", "plugin/skills/_shared/procedures/bin/issue-fold-finding.sh", "plugin/skills/_shared/procedures/bin/issue-unfold-finding.sh", "plugin/skills/_shared/procedures/bin/fold-candidates.sh", "plugin/skills/_shared/procedures/bin/promotion-candidates.sh", "plugin/skills/_shared/procedures/test/typed-scan-family.sh" ], "acceptance_criteria": [ {"id": "AC-5", "scope": "consumer half: enumerated consumers (decision discovery, resume detection, retry guard, fold/unfold, candidates) complete without error on a ledger containing a near-miss (S5b)"} ] } ] }, { "wave": 3, "work_units": [ { "id": "WU-57-3-3", "title": "Docs + fence-aware route-through lint rule", "files": [ "scripts/lint-conventions.sh", "plugin/skills/_shared/forge-contract.md", "plugin/skills/_shared/schemas/phase-outcome.v1.md", "plugin/skills/_shared/schemas/README.md", "plugin/skills/_shared/procedures/phase-outcome.md", "plugin/skills/requirements/procedures/requirements-from-deferred.md" ], "acceptance_criteria": [ {"id": "AC-5", "scope": "mechanical half: fence-aware lint rule — no _prim scan_comments / direct scan_comments.sh invocation outside _typed_scan in helper tier or skill-text fences"} ] } ] } ] } ``` Decomposition follows the SREQ's own dependency chain (§Dependencies & Sequencing): the shared tier plus the PO post/read pair are one chain and land first with the two red-first executed suites (WU-1); the seven satellite consumers are a mechanical route-through once `_typed_scan` exists (WU-2); docs and the fence-aware lint rule come last since the lint rule can only go green after every consumer is routed (WU-3). One deviation from the SREQ file list: S5's consumer-facing scenarios live in a new `test/typed-scan-family.sh` owned by WU-2 rather than being appended to WU-1's `typed-scan.sh` — work units must not share files. The workspace is a single worktree, so waves execute sequentially (Test Writer → Implementer per WU, one WU at a time). WU-3 is artifact-shape (docs + a lint rule that is itself the mechanical verification), so its Test Writer is skipped per /dev:develop 3.2; WU-1 and WU-2 have behavioral ACs and run full 2-phase TDD. Additional per-schema anchored-pattern statements (SREQ: "each schema doc states its concrete pattern") are covered in WU-3 via `schemas/README.md` stating the generic family-wide rule, with `phase-outcome.v1.md` carrying its concrete pattern plus the `suite` field, versioning policy, and retry-guard excluded-field set; other schema docs are touched only if the implementer finds they already restate a header shape that contradicts the rule.
Author
Owner

Development Journal — WU-57-3-1 (shared validation tier + PO write/read chain)

2026-08-24T00:00 DISCOVERY: the anchored partition and the PO parse are two different jobs

type: discovery
timestamp: 2026-08-24T00:00
where: plugin/skills/_shared/procedures/bin/_lib.sh
tags: [typed-scan, layering]

Context: _typed_scan per the SREQ answers one question — "is this comment a record of
kind K?" — and it is kind-agnostic. But both post and read-all then need a second,
phase-outcome-SPECIFIC pass ("does this record actually parse?") whose refusal text is
identical in both (comment id + element + the operator repair route).

Pattern: kept _typed_scan generic, and added a small sibling _po_parse CONTEXT RECORDS_JSON in _lib.sh that owns the parse-and-diagnose pass. It returns
ordinal-ascending records with {id, skill, ordinal, body, missing[]} and dies with the
full diagnosis if any missing[] is non-empty. Post uses .body for the retry guard and
.ordinal for minting; read-all projects the record fields. Only the caller's verb differs,
so CONTEXT is the only parameter.

Why it matters: the alternative was the same ~20-line jq program and the same multi-line
refusal message duplicated in two files, which is exactly the drift surface the SREQ's
"fix the family once" argument was written against. _lib.sh already carries
phase-outcome-specific logic (_validate_producer, the ordinal/collision exit codes), so
this is in-layer, not a new concern. Noted as a small deviation from the WU brief's literal
"add _typed_scan and _suite_provenance" — it adds a third private function to a file
the WU already owns.

2026-08-24T00:20 STRUGGLE: jq // collapses a correct boolean false

type: struggle
timestamp: 2026-08-24T00:20
affected: [jq, provenance.sh]
attempts: 1
resolved: true
tags: [test-conflict, provenance, schema-type]

Problem: provenance.sh S7 asserts the clean-checkout case with
jq -r '.suite.dirty // "absent"' and expects "false".
Expected: {"dirty": false} renders as false.
Actual: jq's // treats boolean false as falsy, so the alternative fires and the check
reads absent. The ONLY value that satisfies that expression is the JSON string "false".
The sibling dirty-state check is blind to this — boolean true and string "true" both
render true under -r — which is why only the clean case shows it.
Solution: not taken unilaterally. The suite is off-limits for this WU, so
_suite_provenance emits the boolean (the right schema type — a string "false" is truthy
in most consumer languages) and the conflict was escalated to the lead with two options:
amend that one assertion to an absence-safe idiom (if (.suite|has("dirty")) then (.suite.dirty|tostring) else "absent" end), or pin dirty to a string in the schema doc.

Key insight: // "default" is only safe as an absence probe for fields that can never

Outcome: the Test Writer adopted the absence-safe idiom (suite_dirty_of, landed as
9d70fe6) rather than pinning a string type. dirty stays a boolean; the suite is green.
legitimately be false or null. For a boolean field the absence probe is has(...).

2026-08-24T00:35 DISCOVERY: AC-10 is a true no-op on the live ledgers

type: discovery
timestamp: 2026-08-24T00:35
tags: [ac-10, backward-compat, tea-cli]

Context: read-only AC-10 check with the modified helpers against the real Gitea ledgers.

Pattern: issue #50 reads 7 records, ordinals [1..7], suite: null on every one, exit 0,
empty stderr; issue #57 reads 2 records, ordinals [1,2], same. Running the coarse
adapter prefilter directly returns 7 and 2 as well — so the anchored partition and the
prefilter currently agree on both issues and no near-miss is live on either.

Why it matters: the #54 near-miss that motivated the feature is no longer present on #50
(cleaned since it was reproduced), so these two ledgers cannot serve as a live near-miss
regression witness — the synthetic S1/S2 fixtures are the only executable evidence for the
exclusion path. Worth QA knowing: "AC-10 passes on the real ledgers" here means "the change
is invisible on clean ledgers", which is the property AC-10 actually asks for.

2026-08-24T00:45 COMPLETE

type: complete
variant: development
timestamp: 2026-08-24T00:45
iterations: 3

Hardest part: deciding not to bend _suite_provenance's schema type to fit an assertion
whose idiom, not whose intent, was wrong.
If I did this again: run the two new suites against a throwaway implementation of just
_suite_provenance before touching the post chain — the type conflict would have surfaced in
the first minute instead of after the whole chain was rewired.

2026-08-24T01:00 DISCOVERY: git add -A is the wrong reflex on a shared branch

type: discovery
timestamp: 2026-08-24T01:00
tags: [agent-teams, git-hygiene]

Context: the Test Writer was mid-edit on test/provenance.sh (a file this WU is
forbidden to touch) in the same working tree when I committed. git add -A swept their
half-finished amendment — the new suite_dirty_of helper without its two call sites —
into my implementation commit, which left HEAD red on the very check the amendment fixes.

Pattern: on a branch two agents share a working tree on, stage by explicit path
(git add <the files this WU owns>), never -A. The escalation resolved on its own —
the Test Writer landed the call-site half as 9d70fe6 and HEAD is green — but the window
where HEAD was red was self-inflicted.

Why it matters: the WU brief's file-ownership boundary only holds if staging respects
it too; a shared tree makes -A a way to commit work you were told not to touch.

<!-- dev-journal:v1 wu=WU-57-3-1 skill=develop --> # Development Journal — WU-57-3-1 (shared validation tier + PO write/read chain) ## 2026-08-24T00:00 DISCOVERY: the anchored partition and the PO parse are two different jobs ```yaml type: discovery timestamp: 2026-08-24T00:00 where: plugin/skills/_shared/procedures/bin/_lib.sh tags: [typed-scan, layering] ``` **Context:** `_typed_scan` per the SREQ answers one question — "is this comment a record of kind K?" — and it is kind-agnostic. But both post and read-all then need a second, phase-outcome-SPECIFIC pass ("does this record actually parse?") whose refusal text is identical in both (comment id + element + the operator repair route). **Pattern:** kept `_typed_scan` generic, and added a small sibling `_po_parse CONTEXT RECORDS_JSON` in `_lib.sh` that owns the parse-and-diagnose pass. It returns ordinal-ascending records with `{id, skill, ordinal, body, missing[]}` and dies with the full diagnosis if any `missing[]` is non-empty. Post uses `.body` for the retry guard and `.ordinal` for minting; read-all projects the record fields. Only the caller's verb differs, so `CONTEXT` is the only parameter. **Why it matters:** the alternative was the same ~20-line jq program and the same multi-line refusal message duplicated in two files, which is exactly the drift surface the SREQ's "fix the family once" argument was written against. `_lib.sh` already carries phase-outcome-specific logic (`_validate_producer`, the ordinal/collision exit codes), so this is in-layer, not a new concern. Noted as a small deviation from the WU brief's literal "add `_typed_scan` and `_suite_provenance`" — it adds a third private function to a file the WU already owns. ## 2026-08-24T00:20 STRUGGLE: jq `//` collapses a correct boolean `false` ```yaml type: struggle timestamp: 2026-08-24T00:20 affected: [jq, provenance.sh] attempts: 1 resolved: true tags: [test-conflict, provenance, schema-type] ``` **Problem:** `provenance.sh` S7 asserts the clean-checkout case with `jq -r '.suite.dirty // "absent"'` and expects `"false"`. **Expected:** `{"dirty": false}` renders as `false`. **Actual:** jq's `//` treats boolean `false` as falsy, so the alternative fires and the check reads `absent`. The ONLY value that satisfies that expression is the JSON *string* `"false"`. The sibling dirty-state check is blind to this — boolean `true` and string `"true"` both render `true` under `-r` — which is why only the clean case shows it. **Solution:** not taken unilaterally. The suite is off-limits for this WU, so `_suite_provenance` emits the boolean (the right schema type — a string `"false"` is truthy in most consumer languages) and the conflict was escalated to the lead with two options: amend that one assertion to an absence-safe idiom (`if (.suite|has("dirty")) then (.suite.dirty|tostring) else "absent" end`), or pin `dirty` to a string in the schema doc. **Key insight:** `// "default"` is only safe as an absence probe for fields that can never **Outcome:** the Test Writer adopted the absence-safe idiom (`suite_dirty_of`, landed as 9d70fe6) rather than pinning a string type. `dirty` stays a boolean; the suite is green. legitimately be `false` or `null`. For a boolean field the absence probe is `has(...)`. ## 2026-08-24T00:35 DISCOVERY: AC-10 is a true no-op on the live ledgers ```yaml type: discovery timestamp: 2026-08-24T00:35 tags: [ac-10, backward-compat, tea-cli] ``` **Context:** read-only AC-10 check with the modified helpers against the real Gitea ledgers. **Pattern:** issue #50 reads 7 records, ordinals `[1..7]`, `suite: null` on every one, exit 0, **empty stderr**; issue #57 reads 2 records, ordinals `[1,2]`, same. Running the coarse adapter prefilter directly returns 7 and 2 as well — so the anchored partition and the prefilter currently agree on both issues and no near-miss is live on either. **Why it matters:** the #54 near-miss that motivated the feature is no longer present on #50 (cleaned since it was reproduced), so these two ledgers cannot serve as a live near-miss regression witness — the synthetic S1/S2 fixtures are the only executable evidence for the exclusion path. Worth QA knowing: "AC-10 passes on the real ledgers" here means "the change is invisible on clean ledgers", which is the property AC-10 actually asks for. ## 2026-08-24T00:45 COMPLETE ```yaml type: complete variant: development timestamp: 2026-08-24T00:45 iterations: 3 ``` **Hardest part:** deciding not to bend `_suite_provenance`'s schema type to fit an assertion whose idiom, not whose intent, was wrong. **If I did this again:** run the two new suites against a throwaway implementation of just `_suite_provenance` before touching the post chain — the type conflict would have surfaced in the first minute instead of after the whole chain was rewired. ## 2026-08-24T01:00 DISCOVERY: `git add -A` is the wrong reflex on a shared branch ```yaml type: discovery timestamp: 2026-08-24T01:00 tags: [agent-teams, git-hygiene] ``` **Context:** the Test Writer was mid-edit on `test/provenance.sh` (a file this WU is forbidden to touch) in the same working tree when I committed. `git add -A` swept their half-finished amendment — the new `suite_dirty_of` helper without its two call sites — into my implementation commit, which left HEAD red on the very check the amendment fixes. **Pattern:** on a branch two agents share a working tree on, stage by explicit path (`git add <the files this WU owns>`), never `-A`. The escalation resolved on its own — the Test Writer landed the call-site half as 9d70fe6 and HEAD is green — but the window where HEAD was red was self-inflicted. **Why it matters:** the WU brief's file-ownership boundary only holds if staging respects it too; a shared tree makes `-A` a way to commit work you were told not to touch.
Author
Owner
{
  "outcome": "complete",
  "summary": "Shared validation tier: _typed_scan (anchored-header partition + near-miss warnings) + _suite_provenance (git/release/unknown probe) + _po_parse in _lib.sh; phase-outcome-post.sh mints from parsed-max ordinal, refuses anchored-unparseable records, stamps suite provenance; read-all diagnoses per record, passes suite through. Red-first suites typed-scan.sh + provenance.sh green; 4 regression suites + lint green.",
  "commit_refs": [
    "9d70fe6",
    "0515649",
    "c79daca"
  ],
  "journal_ref": "1176",
  "dependencies_satisfied": [],
  "dependencies_violated": []
}
<!-- work-unit-outcome:v1 id=WU-57-3-1 skill=develop --> ```json { "outcome": "complete", "summary": "Shared validation tier: _typed_scan (anchored-header partition + near-miss warnings) + _suite_provenance (git/release/unknown probe) + _po_parse in _lib.sh; phase-outcome-post.sh mints from parsed-max ordinal, refuses anchored-unparseable records, stamps suite provenance; read-all diagnoses per record, passes suite through. Red-first suites typed-scan.sh + provenance.sh green; 4 regression suites + lint green.", "commit_refs": [ "9d70fe6", "0515649", "c79daca" ], "journal_ref": "1176", "dependencies_satisfied": [], "dependencies_violated": [] } ```
Author
Owner

Dev Journal — WU-57-3-2 (route satellite consumers through _typed_scan)

2026-08-24T00:00 STRUGGLE: every routed consumer suddenly exited 126

type: struggle
timestamp: 2026-08-24T00:00
attempts: 2
resolved: true
tags: [tooling, file-mode, awk]

Problem: Applying the route-through edits with an awk ... > f.tmp && mv f.tmp f splice.
Expected: Only the scan call and its comment change.
Actual: All 36 family assertions failed with exit 126 — the helpers were no longer executable.
Solution: The awk-and-move rewrite drops the source file's mode; chmod +x restored it, and
git diff --summary confirmed no mode change was staged.
Key insight: A whole-suite failure at a uniform, non-helper exit code (126/127) is an
environment/file-mode symptom, not a logic one — check the mode before re-reading the diff.

2026-08-24T00:00 DISCOVERY: routing decision-resolution widens the primitive's header filter

type: discovery
timestamp: 2026-08-24T00:00
where: plugin/skills/_shared/procedures/bin/decision-resolution-read-state.sh
tags: [contract, typed-scan]

Context: This consumer was the only one passing a composite header to the primitive
(decision-resolution:v1 ref=$did), while _typed_scan takes a bare KIND.
Pattern: Route on the kind alone and let the existing exact-ref post-filter do the narrowing.
Why it matters: No behaviour is lost — the primitive's ref= match was already a substring
(ref=D-PO-2-4-1 pulled in ref=D-PO-2-4-12, observed live in verity), which is exactly why the
post-filter exists. The scan is now broader by one kind's worth of comments and correct by anchoring.

2026-08-24T00:00 COMPLETE

type: complete
variant: development
timestamp: 2026-08-24T00:00
iterations: 3

Hardest part: Nothing in the routing itself — the one real cost was the file-mode regression
introduced by my own editing technique, which masqueraded as a total functional failure.
If I did this again: Preserve the mode in the splice helper (chmod --reference) before the
first run, so a mechanical edit can never look like a behavioural break.

<!-- dev-journal:v1 wu=WU-57-3-2 skill=develop --> # Dev Journal — WU-57-3-2 (route satellite consumers through _typed_scan) ## 2026-08-24T00:00 STRUGGLE: every routed consumer suddenly exited 126 ```yaml type: struggle timestamp: 2026-08-24T00:00 attempts: 2 resolved: true tags: [tooling, file-mode, awk] ``` **Problem:** Applying the route-through edits with an `awk ... > f.tmp && mv f.tmp f` splice. **Expected:** Only the scan call and its comment change. **Actual:** All 36 family assertions failed with exit 126 — the helpers were no longer executable. **Solution:** The awk-and-move rewrite drops the source file's mode; `chmod +x` restored it, and `git diff --summary` confirmed no mode change was staged. **Key insight:** A whole-suite failure at a uniform, non-helper exit code (126/127) is an environment/file-mode symptom, not a logic one — check the mode before re-reading the diff. ## 2026-08-24T00:00 DISCOVERY: routing decision-resolution widens the primitive's header filter ```yaml type: discovery timestamp: 2026-08-24T00:00 where: plugin/skills/_shared/procedures/bin/decision-resolution-read-state.sh tags: [contract, typed-scan] ``` **Context:** This consumer was the only one passing a *composite* header to the primitive (`decision-resolution:v1 ref=$did`), while `_typed_scan` takes a bare KIND. **Pattern:** Route on the kind alone and let the existing exact-`ref` post-filter do the narrowing. **Why it matters:** No behaviour is lost — the primitive's `ref=` match was already a substring (`ref=D-PO-2-4-1` pulled in `ref=D-PO-2-4-12`, observed live in verity), which is exactly why the post-filter exists. The scan is now broader by one kind's worth of comments and correct by anchoring. ## 2026-08-24T00:00 COMPLETE ```yaml type: complete variant: development timestamp: 2026-08-24T00:00 iterations: 3 ``` **Hardest part:** Nothing in the routing itself — the one real cost was the file-mode regression introduced by my own editing technique, which masqueraded as a total functional failure. **If I did this again:** Preserve the mode in the splice helper (`chmod --reference`) before the first run, so a mechanical edit can never look like a behavioural break.
Author
Owner
{
  "outcome": "complete",
  "summary": "All seven satellite consumers (decision-resolution-read-state, deliverable-get, comments-scan, issue-fold/unfold-finding, fold-/promotion-candidates) routed through _typed_scan, keeping their own narrowing; candidates helpers keep deliberate per-issue tolerance around the routed call. Family suite 36/36 green (was 14 red, incl. a live crash in decision-resolution-read-state on a near-miss); no regressions; lint clean. Note: --kind now validated [a-z0-9-]+ (malformed kind dies EXIT_USAGE instead of returning null/[]) — implements the SREQ KIND-validation constraint, flagged for QA eyes.",
  "commit_refs": [
    "da73e47",
    "6c5887e"
  ],
  "journal_ref": "1180",
  "dependencies_satisfied": [
    "WU-57-3-1"
  ],
  "dependencies_violated": []
}
<!-- work-unit-outcome:v1 id=WU-57-3-2 skill=develop --> ```json { "outcome": "complete", "summary": "All seven satellite consumers (decision-resolution-read-state, deliverable-get, comments-scan, issue-fold/unfold-finding, fold-/promotion-candidates) routed through _typed_scan, keeping their own narrowing; candidates helpers keep deliberate per-issue tolerance around the routed call. Family suite 36/36 green (was 14 red, incl. a live crash in decision-resolution-read-state on a near-miss); no regressions; lint clean. Note: --kind now validated [a-z0-9-]+ (malformed kind dies EXIT_USAGE instead of returning null/[]) — implements the SREQ KIND-validation constraint, flagged for QA eyes.", "commit_refs": [ "da73e47", "6c5887e" ], "journal_ref": "1180", "dependencies_satisfied": [ "WU-57-3-1" ], "dependencies_violated": [] } ```
Author
Owner

Dev Journal — WU-57-3-3 (docs + route-through lint)

2026-08-24T00:00 DISCOVERY: the D3 offender was inline prose, not a fence — a fence-only rule would have missed the very defect it guards

type: discovery
timestamp: 2026-08-24T00:00
where: plugin/skills/requirements/procedures/requirements-from-deferred.md
tags: [lint, fence-aware, ac-5]

The SREQ specifies the route-through rule as "no skill-text shell fence may invoke the primitive
directly", and the exempt-consumer audit named requirements-from-deferred.md Step D3 as the one
offender. D3 is not in a fence — it is inline code inside a numbered list item
(`{adapter}/bin/scan_comments.sh --issue {S} --header "folded-finding:v1"`). A fence-only check
would therefore have gone green over the exact call site that motivated it, and nothing would stop the
next one being written the same way.

So the rule splits on the fence boundary rather than restricting itself to one side of it, reusing the
existing tier-1 fence tracker for the partition (CLAUDE.md #50: never line-regex skill markdown):

  • inside a shell fence — any invocation form violates (_prim scan_comments, or a path ending
    /scan_comments.sh), because everything in a fence is a recipe;
  • outside a fence — only a call with arguments (/scan_comments.sh --…) violates. A bare mention
    of the primitive's name is legitimate prose, and there is a lot of it: forge-contract's primitives
    table, decision-resolution.md's abstract scan_comments(issue, header_pattern=…) signatures, and
    the four adapter SKILL.md quick-references (those are additionally path-exempt).

Fenced hits are subtracted from the prose pass by (path, lineno) so an offender is reported once.
Verified at HEAD: the prose half flagged D3 and nothing else — zero false positives across 689
fence lines and the whole markdown population.

2026-08-24T00:00 STRUGGLE: awk -v eats backslashes, so the shared pattern carries none

type: struggle
timestamp: 2026-08-24T00:00
attempts: 1
resolved: true
tags: [portability, awk]

Problem: one regex serves all three passes — two grep -E and one awk dynamic match.
Actual: awk processes escape sequences in a -v assignment, so \. arrives as a bare . (and
gawk warns). The pattern would silently widen.
Solution: write the literal dot as the bracket expression [.], which means the same thing to
both grep -E and awk and survives the -v round trip. The pattern now contains no backslash at all.

<!-- dev-journal:v1 wu=WU-57-3-3 skill=develop --> # Dev Journal — WU-57-3-3 (docs + route-through lint) ## 2026-08-24T00:00 DISCOVERY: the D3 offender was inline prose, not a fence — a fence-only rule would have missed the very defect it guards ```yaml type: discovery timestamp: 2026-08-24T00:00 where: plugin/skills/requirements/procedures/requirements-from-deferred.md tags: [lint, fence-aware, ac-5] ``` The SREQ specifies the route-through rule as "no skill-text shell **fence** may invoke the primitive directly", and the exempt-consumer audit named `requirements-from-deferred.md` Step D3 as the one offender. D3 is **not** in a fence — it is inline code inside a numbered list item (`` `{adapter}/bin/scan_comments.sh --issue {S} --header "folded-finding:v1"` ``). A fence-only check would therefore have gone green over the exact call site that motivated it, and nothing would stop the next one being written the same way. So the rule splits on the fence boundary rather than restricting itself to one side of it, reusing the existing tier-1 fence tracker for the partition (CLAUDE.md #50: never line-regex skill markdown): - **inside a shell fence** — any invocation form violates (`_prim scan_comments`, or a path ending `/scan_comments.sh`), because everything in a fence is a recipe; - **outside a fence** — only a call *with arguments* (`/scan_comments.sh --…`) violates. A bare mention of the primitive's name is legitimate prose, and there is a lot of it: forge-contract's primitives table, `decision-resolution.md`'s abstract `scan_comments(issue, header_pattern=…)` signatures, and the four adapter SKILL.md quick-references (those are additionally path-exempt). Fenced hits are subtracted from the prose pass by `(path, lineno)` so an offender is reported once. Verified at HEAD: the prose half flagged D3 and nothing else — zero false positives across 689 fence lines and the whole markdown population. ## 2026-08-24T00:00 STRUGGLE: awk `-v` eats backslashes, so the shared pattern carries none ```yaml type: struggle timestamp: 2026-08-24T00:00 attempts: 1 resolved: true tags: [portability, awk] ``` **Problem:** one regex serves all three passes — two `grep -E` and one `awk` dynamic match. **Actual:** awk processes escape sequences in a `-v` assignment, so `\.` arrives as a bare `.` (and gawk warns). The pattern would silently widen. **Solution:** write the literal dot as the bracket expression `[.]`, which means the same thing to both grep -E and awk and survives the `-v` round trip. The pattern now contains no backslash at all.
Author
Owner
{
  "outcome": "complete",
  "summary": "Artifact-shape WU (Test Writer skipped per develop 3.2; ACs verified by the lint rule itself + executed negative self-test). Fence-aware route-through lint rule (helper tier by function boundary, skill markdown fence+prose partition, allow-anchor escape, non-vacuity line, 3-round planted-violation self-test); forge-contract superset wording + §8 non-record note; anchored-header rule in schemas/README; phase-outcome.v1 gains suite field (dirty pinned boolean), parsed-ordinal wording, additive versioning policy, retry-guard excluded set; phase-outcome.md aligned; requirements-from-deferred D3 re-pointed at comments-scan.sh; decision-resolution.md composite-header + created_ms consistency fixes.",
  "commit_refs": [
    "b3660bf",
    "c3b2e20",
    "8652ce1"
  ],
  "journal_ref": "1182",
  "dependencies_satisfied": [
    "WU-57-3-1",
    "WU-57-3-2"
  ],
  "dependencies_violated": []
}
<!-- work-unit-outcome:v1 id=WU-57-3-3 skill=develop --> ```json { "outcome": "complete", "summary": "Artifact-shape WU (Test Writer skipped per develop 3.2; ACs verified by the lint rule itself + executed negative self-test). Fence-aware route-through lint rule (helper tier by function boundary, skill markdown fence+prose partition, allow-anchor escape, non-vacuity line, 3-round planted-violation self-test); forge-contract superset wording + §8 non-record note; anchored-header rule in schemas/README; phase-outcome.v1 gains suite field (dirty pinned boolean), parsed-ordinal wording, additive versioning policy, retry-guard excluded set; phase-outcome.md aligned; requirements-from-deferred D3 re-pointed at comments-scan.sh; decision-resolution.md composite-header + created_ms consistency fixes.", "commit_refs": [ "b3660bf", "c3b2e20", "8652ce1" ], "journal_ref": "1182", "dependencies_satisfied": [ "WU-57-3-1", "WU-57-3-2" ], "dependencies_violated": [] } ```
Author
Owner
{
  "phase": "develop",
  "learnings": [
    {"id": "L-1", "scope": "stack:jq", "status": "applied", "summary": "jq `//` treats a present boolean false as absent — never use `.f // default` as an absence probe on boolean fields; test has() and tostring instead", "evidence": "WU-57-3-1 STRUGGLE: provenance S7 assertion could only pass against the string \"false\"; triggered a cross-agent test amendment", "source_ref": "comment:1176", "applied_to": ".claude/skills/stack-jq/SKILL.md"},
    {"id": "L-2", "scope": "project", "status": "applied", "summary": "Whole-suite uniform exit 126/127 = environment regression (lost exec bit from file-rewriting edits), not filter logic — check modes first, preserve them when replacing bin scripts", "evidence": "WU-57-3-2 STRUGGLE (attempts=2): every routed consumer failed identically until chmod restored the bit", "source_ref": "comment:1180", "applied_to": "CLAUDE.md"},
    {"id": "L-3", "scope": "devwork", "status": "unhomed", "summary": "Fence-only markdown-scan rules miss inline-code call sites in prose — the real D3 offender was a numbered-list inline invocation; scanning rules must partition by fence, not restrict to fences", "evidence": "WU-57-3-3 DISCOVERY: a rule per the SREQ's literal wording would have passed over the exact call site that motivated it", "source_ref": "comment:1182", "applied_to": null},
    {"id": "L-4", "scope": "devwork", "status": "unhomed", "summary": "On a shared worktree, teammates must stage by explicit path — `git add -A` swept a concurrent teammate's half-finished test edit into an unrelated commit; WU file-ownership only holds if staging respects it", "evidence": "WU-57-3-1 DISCOVERY (post-incident): HEAD momentarily red until the co-owner landed the remainder", "source_ref": "comment:1176", "applied_to": null}
  ]
}
<!-- learning:v1 issue=57 skill=develop po=PO-57-3 --> ```json { "phase": "develop", "learnings": [ {"id": "L-1", "scope": "stack:jq", "status": "applied", "summary": "jq `//` treats a present boolean false as absent — never use `.f // default` as an absence probe on boolean fields; test has() and tostring instead", "evidence": "WU-57-3-1 STRUGGLE: provenance S7 assertion could only pass against the string \"false\"; triggered a cross-agent test amendment", "source_ref": "comment:1176", "applied_to": ".claude/skills/stack-jq/SKILL.md"}, {"id": "L-2", "scope": "project", "status": "applied", "summary": "Whole-suite uniform exit 126/127 = environment regression (lost exec bit from file-rewriting edits), not filter logic — check modes first, preserve them when replacing bin scripts", "evidence": "WU-57-3-2 STRUGGLE (attempts=2): every routed consumer failed identically until chmod restored the bit", "source_ref": "comment:1180", "applied_to": "CLAUDE.md"}, {"id": "L-3", "scope": "devwork", "status": "unhomed", "summary": "Fence-only markdown-scan rules miss inline-code call sites in prose — the real D3 offender was a numbered-list inline invocation; scanning rules must partition by fence, not restrict to fences", "evidence": "WU-57-3-3 DISCOVERY: a rule per the SREQ's literal wording would have passed over the exact call site that motivated it", "source_ref": "comment:1182", "applied_to": null}, {"id": "L-4", "scope": "devwork", "status": "unhomed", "summary": "On a shared worktree, teammates must stage by explicit path — `git add -A` swept a concurrent teammate's half-finished test edit into an unrelated commit; WU file-ownership only holds if staging respects it", "evidence": "WU-57-3-1 DISCOVERY (post-incident): HEAD momentarily red until the co-owner landed the remainder", "source_ref": "comment:1176", "applied_to": null} ] } ```
Author
Owner
{
  "next_state": "qa",
  "produced": [
    {
      "kind": "wu-plan",
      "ref": "1169",
      "summary": "3 WUs / 3 sequential waves: shared tier + PO chain; satellite consumers; docs + lint rule"
    },
    {
      "kind": "work-unit-outcome",
      "ref": "1179",
      "summary": "WU-57-3-1: _typed_scan + _suite_provenance + _po_parse; PO post/read routed; suites green"
    },
    {
      "kind": "work-unit-outcome",
      "ref": "1181",
      "summary": "WU-57-3-2: seven satellite consumers routed through _typed_scan; family suite 36/36"
    },
    {
      "kind": "work-unit-outcome",
      "ref": "1184",
      "summary": "WU-57-3-3: fence-aware route-through lint rule + forge-contract/schema/procedure docs (artifact-shape, Test Writer skipped)"
    },
    {
      "kind": "dev-journal",
      "ref": "1176",
      "summary": "WU-57-3-1 development journal"
    },
    {
      "kind": "dev-journal",
      "ref": "1180",
      "summary": "WU-57-3-2 development journal"
    },
    {
      "kind": "dev-journal",
      "ref": "1182",
      "summary": "WU-57-3-3 development journal"
    },
    {
      "kind": "learning",
      "ref": "comment:1185",
      "summary": "4 learnings (2 applied, 2 unhomed)"
    }
  ],
  "findings": [
    {
      "category": "in-scope-blocking",
      "summary": "SREQ's route-through lint rule as literally worded (fences only) would have missed the motivating D3 offender, which is inline code in prose — the shipped rule partitions by fence and also flags prose invocations-with-arguments",
      "reasoning": "Deliberate widening beyond the SREQ's mechanical-rule wording, applied and negative-self-tested in WU-57-3-3; QA should verify the rule against the shipped lint behavior and docs, not the SREQ's literal sentence.",
      "proposed_action": "fix-in-this-feature",
      "fix_cost": "trivial",
      "feature_value": "core",
      "requires_product_decision": false,
      "applied_disposition": "fix-now",
      "id": "F-PO-57-3-1"
    },
    {
      "category": "in-scope-deferrable",
      "summary": "deliverable-get.sh and comments-scan.sh now die EXIT_USAGE=2 on a malformed --kind (uppercase/dotted) instead of returning null/[] — a new refusal path from _typed_scan's KIND validation, beyond pure route-through",
      "reasoning": "Implements the SREQ's stated KIND-validation constraint at the consumer surface; every in-tree --kind value is lowercase-hyphen (grep-verified), so no caller is affected. Flagged so QA exercises the malformed-kind path deliberately.",
      "proposed_action": "accept",
      "fix_cost": "trivial",
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "accept",
      "id": "F-PO-57-3-2"
    }
  ],
  "pending_decisions": [],
  "suite": {
    "source": "git",
    "sha": "9c6cc88af7bde8f62b4c31193a928fe8a4b1aeaf",
    "dirty": false
  }
}
<!-- phase-outcome:v1 id=PO-57-3 skill=develop --> ```json { "next_state": "qa", "produced": [ { "kind": "wu-plan", "ref": "1169", "summary": "3 WUs / 3 sequential waves: shared tier + PO chain; satellite consumers; docs + lint rule" }, { "kind": "work-unit-outcome", "ref": "1179", "summary": "WU-57-3-1: _typed_scan + _suite_provenance + _po_parse; PO post/read routed; suites green" }, { "kind": "work-unit-outcome", "ref": "1181", "summary": "WU-57-3-2: seven satellite consumers routed through _typed_scan; family suite 36/36" }, { "kind": "work-unit-outcome", "ref": "1184", "summary": "WU-57-3-3: fence-aware route-through lint rule + forge-contract/schema/procedure docs (artifact-shape, Test Writer skipped)" }, { "kind": "dev-journal", "ref": "1176", "summary": "WU-57-3-1 development journal" }, { "kind": "dev-journal", "ref": "1180", "summary": "WU-57-3-2 development journal" }, { "kind": "dev-journal", "ref": "1182", "summary": "WU-57-3-3 development journal" }, { "kind": "learning", "ref": "comment:1185", "summary": "4 learnings (2 applied, 2 unhomed)" } ], "findings": [ { "category": "in-scope-blocking", "summary": "SREQ's route-through lint rule as literally worded (fences only) would have missed the motivating D3 offender, which is inline code in prose — the shipped rule partitions by fence and also flags prose invocations-with-arguments", "reasoning": "Deliberate widening beyond the SREQ's mechanical-rule wording, applied and negative-self-tested in WU-57-3-3; QA should verify the rule against the shipped lint behavior and docs, not the SREQ's literal sentence.", "proposed_action": "fix-in-this-feature", "fix_cost": "trivial", "feature_value": "core", "requires_product_decision": false, "applied_disposition": "fix-now", "id": "F-PO-57-3-1" }, { "category": "in-scope-deferrable", "summary": "deliverable-get.sh and comments-scan.sh now die EXIT_USAGE=2 on a malformed --kind (uppercase/dotted) instead of returning null/[] — a new refusal path from _typed_scan's KIND validation, beyond pure route-through", "reasoning": "Implements the SREQ's stated KIND-validation constraint at the consumer surface; every in-tree --kind value is lowercase-hyphen (grep-verified), so no caller is affected. Flagged so QA exercises the malformed-kind path deliberately.", "proposed_action": "accept", "fix_cost": "trivial", "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "accept", "id": "F-PO-57-3-2" } ], "pending_decisions": [], "suite": { "source": "git", "sha": "9c6cc88af7bde8f62b4c31193a928fe8a4b1aeaf", "dirty": false } } ```
Author
Owner

Linked: this issue is sibling #59 (recorded by the devwork pipeline).

Linked: this issue is **sibling** #59 (recorded by the devwork pipeline).
Author
Owner
{"outcome":"skipped","summary":"No rendered UI in this project.","findings":[],"not_applicable_reason":"no rendered UI — the deliverables are markdown skill text and shell helpers"}
<!-- qa-report:v1 issue=57 skill=qa domain=a11y phase=validate --> ```json {"outcome":"skipped","summary":"No rendered UI in this project.","findings":[],"not_applicable_reason":"no rendered UI — the deliverables are markdown skill text and shell helpers"} ```
Author
Owner
{"outcome":"skipped","summary":"No browser surface in this project.","findings":[],"not_applicable_reason":"no browser surface — nothing is served or rendered"}
<!-- qa-report:v1 issue=57 skill=qa domain=security-browser phase=validate --> ```json {"outcome":"skipped","summary":"No browser surface in this project.","findings":[],"not_applicable_reason":"no browser surface — nothing is served or rendered"} ```
Author
Owner
{"outcome":"skipped","summary":"Project declares api_invocation: mode: none.","findings":[],"not_applicable_reason":"project declares api_invocation: { mode: none } — no API; markdown+shell skill suite"}
<!-- qa-report:v1 issue=57 skill=qa domain=api phase=validate --> ```json {"outcome":"skipped","summary":"Project declares api_invocation: mode: none.","findings":[],"not_applicable_reason":"project declares api_invocation: { mode: none } — no API; markdown+shell skill suite"} ```
Author
Owner
{"outcome":"skipped","summary":"Project declares api_invocation: mode: none.","findings":[],"not_applicable_reason":"project declares api_invocation: { mode: none } — no API surface to attack; runtime security coverage is the code domain static review"}
<!-- qa-report:v1 issue=57 skill=qa domain=security-api phase=validate --> ```json {"outcome":"skipped","summary":"Project declares api_invocation: mode: none.","findings":[],"not_applicable_reason":"project declares api_invocation: { mode: none } — no API surface to attack; runtime security coverage is the code domain static review"} ```
Author
Owner
{"outcome":"issues-found","summary":"5-reviewer team (fable Bug Hunter + 4 sonnet) over the 11-commit diff: 26 raw candidates -> 21 verified (0 false positives, 2 below floor, 1 carried-forward SREQ-accepted risk). 5 in-scope-blocking, all probe-verified against temp local-fs projects: CR-1 dead per-issue tolerance wrapper silently kills the candidates sweep; CR-2 first-match fence split lets the writer mint records that wedge the ledger (post+read refuse, diagnosis advises deleting a valid record); CR-3 foreign-issue PO id corrupts minting via retry guard or ordinal inflation; CR-4 valid records of another kind warned as near-misses with delete advice; CR-5 lint prose route-through rule dead when no fenced violation coexists. 16 demoted findings all developer-decidable with dispositions applied per disposition-recommend.sh: 13 fix-now, 1 defer-to-issue (CR-15 -> #59, sibling-linked), 2 accept. Full detail: .devwork/feature-57-run-record-integrity/code-report.md","findings":[{"id":"CR-1","category":"in-scope-blocking","severity":"high","summary":"fold-candidates.sh:64 / promotion-candidates.sh:66: the per-issue tolerance wrapper is dead code — _typed_scan is a sourced function, so its _die exits the command-substitution subshell before the or-else fallback applies; one failing issue scan kills the whole candidates sweep with exit 7 and zero diagnostics (stderr swallowed by the 2>/dev/null). Verified by direct probe. The adjacent comment documents the opposite behavior."},{"id":"CR-2","category":"in-scope-blocking","severity":"high","summary":"_lib.sh _PO_PARSE_JQ extracts the JSON body with first-match splits on the fence delimiters, so a finding summary or decision rationale containing a triple-backtick sequence truncates the extraction; the writer happily mints such a record (verified), after which every read AND every future post on the issue refuse, and the diagnostic instructs the operator to delete a valid record. Self-wedging ledger, no automated recovery by design."},{"id":"CR-3","category":"in-scope-blocking","severity":"medium","summary":"phase-outcome-post.sh accepts anchored records whose id belongs to another issue: a pasted PO-999-7 on issue 301 triggered the retry guard and the post returned po_id PO-999-7 without minting anything (verified); with differing content it inflates the minted ordinal from the foreign max. Either way the ledger corrupts silently at write time and only surfaces as a read-side density refusal that names no comment id."},{"id":"CR-4","category":"in-scope-blocking","severity":"medium","summary":"_typed_scan classifies a valid anchored record of ANOTHER kind as a near-miss when the requested kind is a substring of that kind (kind=finding matches every folded-finding:v1 record) and its warning advises deleting or rewording the valid record. Verified: comments-scan --kind finding on a valid folded-finding record emits the delete advice."},{"id":"CR-5","category":"in-scope-blocking","severity":"medium","summary":"lint-conventions.sh route-through rule (b, prose): the awk fenced-hit dedup reads the scratch file first with NR==FNR, so when the scratch is EMPTY (the normal clean-fences case) every prose violation is swallowed and the rule reports nothing. Verified with a direct awk probe. The prose half of the AC-5 mechanical gate only fires when a fenced violation coexists."},{"id":"CR-6","category":"in-scope-deferrable","severity":"medium","summary":"forge-contract.md scan_comments: the new normative note says the match is a case-exact substring of line 1 and nothing more, and adapters MUST NOT tighten — but all four shipped adapters also gate on line 1 starting with the comment-marker prefix, and the same table row still says 'starts with a matching header'. The normative wording contradicts every shipped implementation and itself.","reasoning":"Doc-only inconsistency: no record can be lost (every anchored record starts with the comment marker); only near-miss classes not starting with the marker go unwarned. Fix is a wording change stating the real guarantee.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-7","category":"in-scope-deferrable","severity":"low","summary":"schemas/README.md states the three-outcomes rule family-wide including refusal-with-diagnosis for anchored-but-unparseable records, but only phase-outcome implements outcome 2; a malformed decision-resolution record still dies with a bare jq error naming no comment id (verified by probe).","reasoning":"Doc overpromise relative to shipped behavior; the minimal correct fix is scoping the README claim to what ships, with the family-wide behavior tracked separately (see CR-15).","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-8","category":"in-scope-deferrable","severity":"medium","summary":"fold-candidates.sh:44 / promotion-candidates.sh:44: the or-else-null wrapper around phase-outcome-read-latest converts every NEW hard-refusal path this feature added (unparseable record, density refusal, primitive failure) into 'no Phase Outcome yet', making an issue with a broken ledger silently eligible as a fold/promotion target.","reasoning":"Wrapper lines are unchanged by the feature but the failure surface behind them changed; a read refusal should skip the issue, not classify it as backlog. Small guard in two files the CR-1 fix already touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-9","category":"in-scope-deferrable","severity":"low","summary":"_lib.sh _fenced_json is now dead code (zero callers after the retry guard rewrite), leaving four divergent fence extractors in the tree.","reasoning":"Duplicate-over-reuse hygiene; delete the dead function while CR-2 consolidates the extraction it duplicated.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-10","category":"in-scope-deferrable","severity":"low","summary":"lint route-through rule (b) prose pattern misses copy-pasteable spellings: a slashless scan_comments.sh call, a prim-helper scan call in prose, and calls inside non-shell fences are not matched.","reasoning":"False-negative hardening of a rule this feature introduced; cheap regex widening in the file the CR-5 fix already touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-11","category":"in-scope-deferrable","severity":"low","summary":"lint route-through rule (a) population is only _shared/procedures/bin; a future non-adapter helper elsewhere calling the scan primitive escapes the rule.","reasoning":"Latent today (no such directories exist); widening the population is small and future-proofs the gate. Test suites keep their deliberate direct-primitive exemption, stated explicitly.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-12","category":"in-scope-deferrable","severity":"low","summary":"_suite_provenance inherits GIT_DIR/GIT_WORK_TREE from the environment (e.g. running under a git hook), which overrides the -C probe and attributes the PROJECT's sha to the suite.","reasoning":"Trivial env -u guard on the probe calls. The sibling case (install dir under an unrelated ancestor git repo) is an SREQ-accepted Technical Risk and stays accepted — coverage note, not re-raised.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-13","category":"in-scope-deferrable","severity":"low","summary":"_lib.sh comment claims pwd -P makes a deployed symlink farm report the real checkout, but _SKILLS_ROOT is derived with a LOGICAL cd (line 33), so a farm symlinking individual skill directories strips the symlink component before physical resolution; such installs degrade to release/unknown provenance.","reasoning":"Safe degradation, wrong comment; align the comment (or resolve physically at derivation). No record is ever blocked.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-14","category":"in-scope-deferrable","severity":"low","summary":"phase-outcome-post.sh checks collision only against the newly minted id; a ledger already carrying duplicate parsed ordinals is extended without refusal, minting on top of a state every reader refuses.","reasoning":"Write-side symmetry with the read-side density check; refuse duplicates only (legacy gaps stay continued-past by design). Lands in the code region the CR-3 fix touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-15","category":"in-scope-deferrable","severity":"low","summary":"Anchored-but-unparseable records of non-phase-outcome kinds (decision-resolution, deliverable-get consumers, fold/unfold) have no diagnosis path: consumers die with bare parser errors or silently misbehave, unlike the PO refusal that names comment id and element.","reasoning":"Real family-wide behavior gap but a substantial build (a shared parse-with-diagnosis layer per kind); does not block this feature whose ACs scope the refusal to phase-outcome. Spun out to its own issue. Deferred to issue #59 (sibling-linked).","proposed_action":"defer-to-issue","fix_cost":"substantial","adjacent_to_blocking":false,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"defer-to-issue"},{"id":"CR-16","category":"in-scope-deferrable","severity":"low","summary":"_typed_scan usage errors name the internal function rather than the caller-facing flag: an operator passing --kind phase-outcome:v1 is told _typed_scan rejected it, with no hint to drop the :v1 suffix.","reasoning":"Diagnostic polish in the file other fixes already touch; add the flag name and suffix hint.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-17","category":"in-scope-deferrable","severity":"low","summary":"The anchored-header regex tolerates no trailing or doubled whitespace; a single trailing space on line 1 reclassifies a real record as a near-miss.","reasoning":"By design: anchored means byte-exact, the writers printf the exact shape, and whitespace tolerance would weaken the edit-tamper evidence. The near-miss warning names the id, which is the designed diagnostic.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-18","category":"in-scope-deferrable","severity":"low","summary":"The provenance dirty flag reflects git status of the entire repo containing the suite; in dogfood runs (project == suite repo) unrelated uncommitted project files stamp dirty=true on every record.","reasoning":"Implements the SREQ wording (scoped to that repo) exactly; narrowing to the suite subtree is a refinement that belongs with the issue-56 provenance follow-on, not a defect in this feature.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-19","category":"in-scope-deferrable","severity":"low","summary":"phase-outcome.md documents a read_all output key order that differs from what the implementation emits (suite/comment_id/created_ms ordering).","reasoning":"Cosmetic doc drift; key-addressed consumers unaffected. One-line doc alignment alongside the other doc fixes.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-20","category":"in-scope-deferrable","severity":"low","summary":"Suite coverage gaps found by the audit and spec check: S3/S4 do not assert the element-and-repair-route halves of AC-3/AC-4 wording; typed-scan.sh S5a retains a pre-implementation fallback branch that cannot fail; no CRLF fixture exercises the carriage-return stripping; missing-attribute branches of the parse diagnosis are unpinned.","reasoning":"The implementation satisfies the ACs (verified by execution this round); the gaps are in test assertion strength, fixable in the suites the tests stage touches anyway. Assert distinctive diagnostic phrases, not raw substrings, to avoid the coincidental-match trap the suite authors documented.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-21","category":"in-scope-deferrable","severity":"low","summary":"The new suites call touch -d with fractional epoch directly in fixtures; on a box whose touch lacks that capability (stock macOS) the suites fail with a confusing error rather than a clear capability message.","reasoning":"Repo-local test tier, runs on dev boxes where the capability exists (probe-verified for local-fs elsewhere); a one-line capability check at suite start turns the confusing failure into a loud skip.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"}],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md","journal_path":".devwork/feature-57-run-record-integrity/code-journal.md"}}
<!-- qa-report:v1 issue=57 skill=qa domain=code phase=validate --> ```json {"outcome":"issues-found","summary":"5-reviewer team (fable Bug Hunter + 4 sonnet) over the 11-commit diff: 26 raw candidates -> 21 verified (0 false positives, 2 below floor, 1 carried-forward SREQ-accepted risk). 5 in-scope-blocking, all probe-verified against temp local-fs projects: CR-1 dead per-issue tolerance wrapper silently kills the candidates sweep; CR-2 first-match fence split lets the writer mint records that wedge the ledger (post+read refuse, diagnosis advises deleting a valid record); CR-3 foreign-issue PO id corrupts minting via retry guard or ordinal inflation; CR-4 valid records of another kind warned as near-misses with delete advice; CR-5 lint prose route-through rule dead when no fenced violation coexists. 16 demoted findings all developer-decidable with dispositions applied per disposition-recommend.sh: 13 fix-now, 1 defer-to-issue (CR-15 -> #59, sibling-linked), 2 accept. Full detail: .devwork/feature-57-run-record-integrity/code-report.md","findings":[{"id":"CR-1","category":"in-scope-blocking","severity":"high","summary":"fold-candidates.sh:64 / promotion-candidates.sh:66: the per-issue tolerance wrapper is dead code — _typed_scan is a sourced function, so its _die exits the command-substitution subshell before the or-else fallback applies; one failing issue scan kills the whole candidates sweep with exit 7 and zero diagnostics (stderr swallowed by the 2>/dev/null). Verified by direct probe. The adjacent comment documents the opposite behavior."},{"id":"CR-2","category":"in-scope-blocking","severity":"high","summary":"_lib.sh _PO_PARSE_JQ extracts the JSON body with first-match splits on the fence delimiters, so a finding summary or decision rationale containing a triple-backtick sequence truncates the extraction; the writer happily mints such a record (verified), after which every read AND every future post on the issue refuse, and the diagnostic instructs the operator to delete a valid record. Self-wedging ledger, no automated recovery by design."},{"id":"CR-3","category":"in-scope-blocking","severity":"medium","summary":"phase-outcome-post.sh accepts anchored records whose id belongs to another issue: a pasted PO-999-7 on issue 301 triggered the retry guard and the post returned po_id PO-999-7 without minting anything (verified); with differing content it inflates the minted ordinal from the foreign max. Either way the ledger corrupts silently at write time and only surfaces as a read-side density refusal that names no comment id."},{"id":"CR-4","category":"in-scope-blocking","severity":"medium","summary":"_typed_scan classifies a valid anchored record of ANOTHER kind as a near-miss when the requested kind is a substring of that kind (kind=finding matches every folded-finding:v1 record) and its warning advises deleting or rewording the valid record. Verified: comments-scan --kind finding on a valid folded-finding record emits the delete advice."},{"id":"CR-5","category":"in-scope-blocking","severity":"medium","summary":"lint-conventions.sh route-through rule (b, prose): the awk fenced-hit dedup reads the scratch file first with NR==FNR, so when the scratch is EMPTY (the normal clean-fences case) every prose violation is swallowed and the rule reports nothing. Verified with a direct awk probe. The prose half of the AC-5 mechanical gate only fires when a fenced violation coexists."},{"id":"CR-6","category":"in-scope-deferrable","severity":"medium","summary":"forge-contract.md scan_comments: the new normative note says the match is a case-exact substring of line 1 and nothing more, and adapters MUST NOT tighten — but all four shipped adapters also gate on line 1 starting with the comment-marker prefix, and the same table row still says 'starts with a matching header'. The normative wording contradicts every shipped implementation and itself.","reasoning":"Doc-only inconsistency: no record can be lost (every anchored record starts with the comment marker); only near-miss classes not starting with the marker go unwarned. Fix is a wording change stating the real guarantee.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-7","category":"in-scope-deferrable","severity":"low","summary":"schemas/README.md states the three-outcomes rule family-wide including refusal-with-diagnosis for anchored-but-unparseable records, but only phase-outcome implements outcome 2; a malformed decision-resolution record still dies with a bare jq error naming no comment id (verified by probe).","reasoning":"Doc overpromise relative to shipped behavior; the minimal correct fix is scoping the README claim to what ships, with the family-wide behavior tracked separately (see CR-15).","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-8","category":"in-scope-deferrable","severity":"medium","summary":"fold-candidates.sh:44 / promotion-candidates.sh:44: the or-else-null wrapper around phase-outcome-read-latest converts every NEW hard-refusal path this feature added (unparseable record, density refusal, primitive failure) into 'no Phase Outcome yet', making an issue with a broken ledger silently eligible as a fold/promotion target.","reasoning":"Wrapper lines are unchanged by the feature but the failure surface behind them changed; a read refusal should skip the issue, not classify it as backlog. Small guard in two files the CR-1 fix already touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-9","category":"in-scope-deferrable","severity":"low","summary":"_lib.sh _fenced_json is now dead code (zero callers after the retry guard rewrite), leaving four divergent fence extractors in the tree.","reasoning":"Duplicate-over-reuse hygiene; delete the dead function while CR-2 consolidates the extraction it duplicated.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-10","category":"in-scope-deferrable","severity":"low","summary":"lint route-through rule (b) prose pattern misses copy-pasteable spellings: a slashless scan_comments.sh call, a prim-helper scan call in prose, and calls inside non-shell fences are not matched.","reasoning":"False-negative hardening of a rule this feature introduced; cheap regex widening in the file the CR-5 fix already touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-11","category":"in-scope-deferrable","severity":"low","summary":"lint route-through rule (a) population is only _shared/procedures/bin; a future non-adapter helper elsewhere calling the scan primitive escapes the rule.","reasoning":"Latent today (no such directories exist); widening the population is small and future-proofs the gate. Test suites keep their deliberate direct-primitive exemption, stated explicitly.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-12","category":"in-scope-deferrable","severity":"low","summary":"_suite_provenance inherits GIT_DIR/GIT_WORK_TREE from the environment (e.g. running under a git hook), which overrides the -C probe and attributes the PROJECT's sha to the suite.","reasoning":"Trivial env -u guard on the probe calls. The sibling case (install dir under an unrelated ancestor git repo) is an SREQ-accepted Technical Risk and stays accepted — coverage note, not re-raised.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-13","category":"in-scope-deferrable","severity":"low","summary":"_lib.sh comment claims pwd -P makes a deployed symlink farm report the real checkout, but _SKILLS_ROOT is derived with a LOGICAL cd (line 33), so a farm symlinking individual skill directories strips the symlink component before physical resolution; such installs degrade to release/unknown provenance.","reasoning":"Safe degradation, wrong comment; align the comment (or resolve physically at derivation). No record is ever blocked.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-14","category":"in-scope-deferrable","severity":"low","summary":"phase-outcome-post.sh checks collision only against the newly minted id; a ledger already carrying duplicate parsed ordinals is extended without refusal, minting on top of a state every reader refuses.","reasoning":"Write-side symmetry with the read-side density check; refuse duplicates only (legacy gaps stay continued-past by design). Lands in the code region the CR-3 fix touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-15","category":"in-scope-deferrable","severity":"low","summary":"Anchored-but-unparseable records of non-phase-outcome kinds (decision-resolution, deliverable-get consumers, fold/unfold) have no diagnosis path: consumers die with bare parser errors or silently misbehave, unlike the PO refusal that names comment id and element.","reasoning":"Real family-wide behavior gap but a substantial build (a shared parse-with-diagnosis layer per kind); does not block this feature whose ACs scope the refusal to phase-outcome. Spun out to its own issue. Deferred to issue #59 (sibling-linked).","proposed_action":"defer-to-issue","fix_cost":"substantial","adjacent_to_blocking":false,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"defer-to-issue"},{"id":"CR-16","category":"in-scope-deferrable","severity":"low","summary":"_typed_scan usage errors name the internal function rather than the caller-facing flag: an operator passing --kind phase-outcome:v1 is told _typed_scan rejected it, with no hint to drop the :v1 suffix.","reasoning":"Diagnostic polish in the file other fixes already touch; add the flag name and suffix hint.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-17","category":"in-scope-deferrable","severity":"low","summary":"The anchored-header regex tolerates no trailing or doubled whitespace; a single trailing space on line 1 reclassifies a real record as a near-miss.","reasoning":"By design: anchored means byte-exact, the writers printf the exact shape, and whitespace tolerance would weaken the edit-tamper evidence. The near-miss warning names the id, which is the designed diagnostic.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-18","category":"in-scope-deferrable","severity":"low","summary":"The provenance dirty flag reflects git status of the entire repo containing the suite; in dogfood runs (project == suite repo) unrelated uncommitted project files stamp dirty=true on every record.","reasoning":"Implements the SREQ wording (scoped to that repo) exactly; narrowing to the suite subtree is a refinement that belongs with the issue-56 provenance follow-on, not a defect in this feature.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-19","category":"in-scope-deferrable","severity":"low","summary":"phase-outcome.md documents a read_all output key order that differs from what the implementation emits (suite/comment_id/created_ms ordering).","reasoning":"Cosmetic doc drift; key-addressed consumers unaffected. One-line doc alignment alongside the other doc fixes.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-20","category":"in-scope-deferrable","severity":"low","summary":"Suite coverage gaps found by the audit and spec check: S3/S4 do not assert the element-and-repair-route halves of AC-3/AC-4 wording; typed-scan.sh S5a retains a pre-implementation fallback branch that cannot fail; no CRLF fixture exercises the carriage-return stripping; missing-attribute branches of the parse diagnosis are unpinned.","reasoning":"The implementation satisfies the ACs (verified by execution this round); the gaps are in test assertion strength, fixable in the suites the tests stage touches anyway. Assert distinctive diagnostic phrases, not raw substrings, to avoid the coincidental-match trap the suite authors documented.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-21","category":"in-scope-deferrable","severity":"low","summary":"The new suites call touch -d with fractional epoch directly in fixtures; on a box whose touch lacks that capability (stock macOS) the suites fail with a confusing error rather than a clear capability message.","reasoning":"Repo-local test tier, runs on dev boxes where the capability exists (probe-verified for local-fs elsewhere); a one-line capability check at suite start turns the confusing failure into a loud skip.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"}],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md","journal_path":".devwork/feature-57-run-record-integrity/code-journal.md"}} ```
Author
Owner
{
  "next_state": "qa",
  "produced": [
    {
      "kind": "qa-report",
      "ref": "comment:1196",
      "note": "a11y validate: skipped (declared)"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1197",
      "note": "security-browser validate: skipped (declared)"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1198",
      "note": "api validate: skipped (declared)"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1199",
      "note": "security-api validate: skipped (declared)"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1200",
      "note": "code validate: issues-found (21 findings, 5 blocking)"
    }
  ],
  "findings": [
    {
      "id": "F-PO-57-4-1",
      "category": "in-scope-blocking",
      "severity": "high",
      "summary": "fold-candidates.sh:64 / promotion-candidates.sh:66: the per-issue tolerance wrapper is dead code — _typed_scan is a sourced function, so its _die exits the command-substitution subshell before the or-else fallback applies; one failing issue scan kills the whole candidates sweep with exit 7 and zero diagnostics (stderr swallowed by the 2>/dev/null). Verified by direct probe. The adjacent comment documents the opposite behavior."
    },
    {
      "id": "F-PO-57-4-2",
      "category": "in-scope-blocking",
      "severity": "high",
      "summary": "_lib.sh _PO_PARSE_JQ extracts the JSON body with first-match splits on the fence delimiters, so a finding summary or decision rationale containing a triple-backtick sequence truncates the extraction; the writer happily mints such a record (verified), after which every read AND every future post on the issue refuse, and the diagnostic instructs the operator to delete a valid record. Self-wedging ledger, no automated recovery by design."
    },
    {
      "id": "F-PO-57-4-3",
      "category": "in-scope-blocking",
      "severity": "medium",
      "summary": "phase-outcome-post.sh accepts anchored records whose id belongs to another issue: a pasted PO-999-7 on issue 301 triggered the retry guard and the post returned po_id PO-999-7 without minting anything (verified); with differing content it inflates the minted ordinal from the foreign max. Either way the ledger corrupts silently at write time and only surfaces as a read-side density refusal that names no comment id."
    },
    {
      "id": "F-PO-57-4-4",
      "category": "in-scope-blocking",
      "severity": "medium",
      "summary": "_typed_scan classifies a valid anchored record of ANOTHER kind as a near-miss when the requested kind is a substring of that kind (kind=finding matches every folded-finding:v1 record) and its warning advises deleting or rewording the valid record. Verified: comments-scan --kind finding on a valid folded-finding record emits the delete advice."
    },
    {
      "id": "F-PO-57-4-5",
      "category": "in-scope-blocking",
      "severity": "medium",
      "summary": "lint-conventions.sh route-through rule (b, prose): the awk fenced-hit dedup reads the scratch file first with NR==FNR, so when the scratch is EMPTY (the normal clean-fences case) every prose violation is swallowed and the rule reports nothing. Verified with a direct awk probe. The prose half of the AC-5 mechanical gate only fires when a fenced violation coexists."
    },
    {
      "id": "F-PO-57-4-6",
      "category": "in-scope-deferrable",
      "severity": "medium",
      "summary": "forge-contract.md scan_comments: the new normative note says the match is a case-exact substring of line 1 and nothing more, and adapters MUST NOT tighten — but all four shipped adapters also gate on line 1 starting with the comment-marker prefix, and the same table row still says 'starts with a matching header'. The normative wording contradicts every shipped implementation and itself.",
      "reasoning": "Doc-only inconsistency: no record can be lost (every anchored record starts with the comment marker); only near-miss classes not starting with the marker go unwarned. Fix is a wording change stating the real guarantee.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "core",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-7",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "schemas/README.md states the three-outcomes rule family-wide including refusal-with-diagnosis for anchored-but-unparseable records, but only phase-outcome implements outcome 2; a malformed decision-resolution record still dies with a bare jq error naming no comment id (verified by probe).",
      "reasoning": "Doc overpromise relative to shipped behavior; the minimal correct fix is scoping the README claim to what ships, with the family-wide behavior tracked separately (see CR-15).",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "core",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-8",
      "category": "in-scope-deferrable",
      "severity": "medium",
      "summary": "fold-candidates.sh:44 / promotion-candidates.sh:44: the or-else-null wrapper around phase-outcome-read-latest converts every NEW hard-refusal path this feature added (unparseable record, density refusal, primitive failure) into 'no Phase Outcome yet', making an issue with a broken ledger silently eligible as a fold/promotion target.",
      "reasoning": "Wrapper lines are unchanged by the feature but the failure surface behind them changed; a read refusal should skip the issue, not classify it as backlog. Small guard in two files the CR-1 fix already touches.",
      "proposed_action": "fix-now",
      "fix_cost": "small",
      "adjacent_to_blocking": true,
      "feature_value": "core",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-9",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "_lib.sh _fenced_json is now dead code (zero callers after the retry guard rewrite), leaving four divergent fence extractors in the tree.",
      "reasoning": "Duplicate-over-reuse hygiene; delete the dead function while CR-2 consolidates the extraction it duplicated.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-10",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "lint route-through rule (b) prose pattern misses copy-pasteable spellings: a slashless scan_comments.sh call, a prim-helper scan call in prose, and calls inside non-shell fences are not matched.",
      "reasoning": "False-negative hardening of a rule this feature introduced; cheap regex widening in the file the CR-5 fix already touches.",
      "proposed_action": "fix-now",
      "fix_cost": "small",
      "adjacent_to_blocking": true,
      "feature_value": "core",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-11",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "lint route-through rule (a) population is only _shared/procedures/bin; a future non-adapter helper elsewhere calling the scan primitive escapes the rule.",
      "reasoning": "Latent today (no such directories exist); widening the population is small and future-proofs the gate. Test suites keep their deliberate direct-primitive exemption, stated explicitly.",
      "proposed_action": "fix-now",
      "fix_cost": "small",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-12",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "_suite_provenance inherits GIT_DIR/GIT_WORK_TREE from the environment (e.g. running under a git hook), which overrides the -C probe and attributes the PROJECT's sha to the suite.",
      "reasoning": "Trivial env -u guard on the probe calls. The sibling case (install dir under an unrelated ancestor git repo) is an SREQ-accepted Technical Risk and stays accepted — coverage note, not re-raised.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-13",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "_lib.sh comment claims pwd -P makes a deployed symlink farm report the real checkout, but _SKILLS_ROOT is derived with a LOGICAL cd (line 33), so a farm symlinking individual skill directories strips the symlink component before physical resolution; such installs degrade to release/unknown provenance.",
      "reasoning": "Safe degradation, wrong comment; align the comment (or resolve physically at derivation). No record is ever blocked.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-14",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "phase-outcome-post.sh checks collision only against the newly minted id; a ledger already carrying duplicate parsed ordinals is extended without refusal, minting on top of a state every reader refuses.",
      "reasoning": "Write-side symmetry with the read-side density check; refuse duplicates only (legacy gaps stay continued-past by design). Lands in the code region the CR-3 fix touches.",
      "proposed_action": "fix-now",
      "fix_cost": "small",
      "adjacent_to_blocking": true,
      "feature_value": "core",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-15",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "Anchored-but-unparseable records of non-phase-outcome kinds (decision-resolution, deliverable-get consumers, fold/unfold) have no diagnosis path: consumers die with bare parser errors or silently misbehave, unlike the PO refusal that names comment id and element.",
      "reasoning": "Real family-wide behavior gap but a substantial build (a shared parse-with-diagnosis layer per kind); does not block this feature whose ACs scope the refusal to phase-outcome. Spun out to its own issue. Deferred to issue #59 (sibling-linked).",
      "proposed_action": "defer-to-issue",
      "fix_cost": "substantial",
      "adjacent_to_blocking": false,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "defer-to-issue"
    },
    {
      "id": "F-PO-57-4-16",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "_typed_scan usage errors name the internal function rather than the caller-facing flag: an operator passing --kind phase-outcome:v1 is told _typed_scan rejected it, with no hint to drop the :v1 suffix.",
      "reasoning": "Diagnostic polish in the file other fixes already touch; add the flag name and suffix hint.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-17",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "The anchored-header regex tolerates no trailing or doubled whitespace; a single trailing space on line 1 reclassifies a real record as a near-miss.",
      "reasoning": "By design: anchored means byte-exact, the writers printf the exact shape, and whitespace tolerance would weaken the edit-tamper evidence. The near-miss warning names the id, which is the designed diagnostic.",
      "proposed_action": "accept",
      "fix_cost": "small",
      "adjacent_to_blocking": false,
      "feature_value": "none",
      "requires_product_decision": false,
      "applied_disposition": "accept"
    },
    {
      "id": "F-PO-57-4-18",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "The provenance dirty flag reflects git status of the entire repo containing the suite; in dogfood runs (project == suite repo) unrelated uncommitted project files stamp dirty=true on every record.",
      "reasoning": "Implements the SREQ wording (scoped to that repo) exactly; narrowing to the suite subtree is a refinement that belongs with the issue-56 provenance follow-on, not a defect in this feature.",
      "proposed_action": "accept",
      "fix_cost": "small",
      "adjacent_to_blocking": false,
      "feature_value": "none",
      "requires_product_decision": false,
      "applied_disposition": "accept"
    },
    {
      "id": "F-PO-57-4-19",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "phase-outcome.md documents a read_all output key order that differs from what the implementation emits (suite/comment_id/created_ms ordering).",
      "reasoning": "Cosmetic doc drift; key-addressed consumers unaffected. One-line doc alignment alongside the other doc fixes.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-20",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "Suite coverage gaps found by the audit and spec check: S3/S4 do not assert the element-and-repair-route halves of AC-3/AC-4 wording; typed-scan.sh S5a retains a pre-implementation fallback branch that cannot fail; no CRLF fixture exercises the carriage-return stripping; missing-attribute branches of the parse diagnosis are unpinned.",
      "reasoning": "The implementation satisfies the ACs (verified by execution this round); the gaps are in test assertion strength, fixable in the suites the tests stage touches anyway. Assert distinctive diagnostic phrases, not raw substrings, to avoid the coincidental-match trap the suite authors documented.",
      "proposed_action": "fix-now",
      "fix_cost": "small",
      "adjacent_to_blocking": true,
      "feature_value": "core",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-4-21",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "The new suites call touch -d with fractional epoch directly in fixtures; on a box whose touch lacks that capability (stock macOS) the suites fail with a confusing error rather than a clear capability message.",
      "reasoning": "Repo-local test tier, runs on dev boxes where the capability exists (probe-verified for local-fs elsewhere); a one-line capability check at suite start turns the confusing failure into a loud skip.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    }
  ],
  "pending_decisions": [],
  "suite": {
    "source": "git",
    "sha": "9c6cc88af7bde8f62b4c31193a928fe8a4b1aeaf",
    "dirty": false
  }
}
<!-- phase-outcome:v1 id=PO-57-4 skill=qa --> ```json { "next_state": "qa", "produced": [ { "kind": "qa-report", "ref": "comment:1196", "note": "a11y validate: skipped (declared)" }, { "kind": "qa-report", "ref": "comment:1197", "note": "security-browser validate: skipped (declared)" }, { "kind": "qa-report", "ref": "comment:1198", "note": "api validate: skipped (declared)" }, { "kind": "qa-report", "ref": "comment:1199", "note": "security-api validate: skipped (declared)" }, { "kind": "qa-report", "ref": "comment:1200", "note": "code validate: issues-found (21 findings, 5 blocking)" } ], "findings": [ { "id": "F-PO-57-4-1", "category": "in-scope-blocking", "severity": "high", "summary": "fold-candidates.sh:64 / promotion-candidates.sh:66: the per-issue tolerance wrapper is dead code — _typed_scan is a sourced function, so its _die exits the command-substitution subshell before the or-else fallback applies; one failing issue scan kills the whole candidates sweep with exit 7 and zero diagnostics (stderr swallowed by the 2>/dev/null). Verified by direct probe. The adjacent comment documents the opposite behavior." }, { "id": "F-PO-57-4-2", "category": "in-scope-blocking", "severity": "high", "summary": "_lib.sh _PO_PARSE_JQ extracts the JSON body with first-match splits on the fence delimiters, so a finding summary or decision rationale containing a triple-backtick sequence truncates the extraction; the writer happily mints such a record (verified), after which every read AND every future post on the issue refuse, and the diagnostic instructs the operator to delete a valid record. Self-wedging ledger, no automated recovery by design." }, { "id": "F-PO-57-4-3", "category": "in-scope-blocking", "severity": "medium", "summary": "phase-outcome-post.sh accepts anchored records whose id belongs to another issue: a pasted PO-999-7 on issue 301 triggered the retry guard and the post returned po_id PO-999-7 without minting anything (verified); with differing content it inflates the minted ordinal from the foreign max. Either way the ledger corrupts silently at write time and only surfaces as a read-side density refusal that names no comment id." }, { "id": "F-PO-57-4-4", "category": "in-scope-blocking", "severity": "medium", "summary": "_typed_scan classifies a valid anchored record of ANOTHER kind as a near-miss when the requested kind is a substring of that kind (kind=finding matches every folded-finding:v1 record) and its warning advises deleting or rewording the valid record. Verified: comments-scan --kind finding on a valid folded-finding record emits the delete advice." }, { "id": "F-PO-57-4-5", "category": "in-scope-blocking", "severity": "medium", "summary": "lint-conventions.sh route-through rule (b, prose): the awk fenced-hit dedup reads the scratch file first with NR==FNR, so when the scratch is EMPTY (the normal clean-fences case) every prose violation is swallowed and the rule reports nothing. Verified with a direct awk probe. The prose half of the AC-5 mechanical gate only fires when a fenced violation coexists." }, { "id": "F-PO-57-4-6", "category": "in-scope-deferrable", "severity": "medium", "summary": "forge-contract.md scan_comments: the new normative note says the match is a case-exact substring of line 1 and nothing more, and adapters MUST NOT tighten — but all four shipped adapters also gate on line 1 starting with the comment-marker prefix, and the same table row still says 'starts with a matching header'. The normative wording contradicts every shipped implementation and itself.", "reasoning": "Doc-only inconsistency: no record can be lost (every anchored record starts with the comment marker); only near-miss classes not starting with the marker go unwarned. Fix is a wording change stating the real guarantee.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "core", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-7", "category": "in-scope-deferrable", "severity": "low", "summary": "schemas/README.md states the three-outcomes rule family-wide including refusal-with-diagnosis for anchored-but-unparseable records, but only phase-outcome implements outcome 2; a malformed decision-resolution record still dies with a bare jq error naming no comment id (verified by probe).", "reasoning": "Doc overpromise relative to shipped behavior; the minimal correct fix is scoping the README claim to what ships, with the family-wide behavior tracked separately (see CR-15).", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "core", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-8", "category": "in-scope-deferrable", "severity": "medium", "summary": "fold-candidates.sh:44 / promotion-candidates.sh:44: the or-else-null wrapper around phase-outcome-read-latest converts every NEW hard-refusal path this feature added (unparseable record, density refusal, primitive failure) into 'no Phase Outcome yet', making an issue with a broken ledger silently eligible as a fold/promotion target.", "reasoning": "Wrapper lines are unchanged by the feature but the failure surface behind them changed; a read refusal should skip the issue, not classify it as backlog. Small guard in two files the CR-1 fix already touches.", "proposed_action": "fix-now", "fix_cost": "small", "adjacent_to_blocking": true, "feature_value": "core", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-9", "category": "in-scope-deferrable", "severity": "low", "summary": "_lib.sh _fenced_json is now dead code (zero callers after the retry guard rewrite), leaving four divergent fence extractors in the tree.", "reasoning": "Duplicate-over-reuse hygiene; delete the dead function while CR-2 consolidates the extraction it duplicated.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-10", "category": "in-scope-deferrable", "severity": "low", "summary": "lint route-through rule (b) prose pattern misses copy-pasteable spellings: a slashless scan_comments.sh call, a prim-helper scan call in prose, and calls inside non-shell fences are not matched.", "reasoning": "False-negative hardening of a rule this feature introduced; cheap regex widening in the file the CR-5 fix already touches.", "proposed_action": "fix-now", "fix_cost": "small", "adjacent_to_blocking": true, "feature_value": "core", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-11", "category": "in-scope-deferrable", "severity": "low", "summary": "lint route-through rule (a) population is only _shared/procedures/bin; a future non-adapter helper elsewhere calling the scan primitive escapes the rule.", "reasoning": "Latent today (no such directories exist); widening the population is small and future-proofs the gate. Test suites keep their deliberate direct-primitive exemption, stated explicitly.", "proposed_action": "fix-now", "fix_cost": "small", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-12", "category": "in-scope-deferrable", "severity": "low", "summary": "_suite_provenance inherits GIT_DIR/GIT_WORK_TREE from the environment (e.g. running under a git hook), which overrides the -C probe and attributes the PROJECT's sha to the suite.", "reasoning": "Trivial env -u guard on the probe calls. The sibling case (install dir under an unrelated ancestor git repo) is an SREQ-accepted Technical Risk and stays accepted — coverage note, not re-raised.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-13", "category": "in-scope-deferrable", "severity": "low", "summary": "_lib.sh comment claims pwd -P makes a deployed symlink farm report the real checkout, but _SKILLS_ROOT is derived with a LOGICAL cd (line 33), so a farm symlinking individual skill directories strips the symlink component before physical resolution; such installs degrade to release/unknown provenance.", "reasoning": "Safe degradation, wrong comment; align the comment (or resolve physically at derivation). No record is ever blocked.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-14", "category": "in-scope-deferrable", "severity": "low", "summary": "phase-outcome-post.sh checks collision only against the newly minted id; a ledger already carrying duplicate parsed ordinals is extended without refusal, minting on top of a state every reader refuses.", "reasoning": "Write-side symmetry with the read-side density check; refuse duplicates only (legacy gaps stay continued-past by design). Lands in the code region the CR-3 fix touches.", "proposed_action": "fix-now", "fix_cost": "small", "adjacent_to_blocking": true, "feature_value": "core", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-15", "category": "in-scope-deferrable", "severity": "low", "summary": "Anchored-but-unparseable records of non-phase-outcome kinds (decision-resolution, deliverable-get consumers, fold/unfold) have no diagnosis path: consumers die with bare parser errors or silently misbehave, unlike the PO refusal that names comment id and element.", "reasoning": "Real family-wide behavior gap but a substantial build (a shared parse-with-diagnosis layer per kind); does not block this feature whose ACs scope the refusal to phase-outcome. Spun out to its own issue. Deferred to issue #59 (sibling-linked).", "proposed_action": "defer-to-issue", "fix_cost": "substantial", "adjacent_to_blocking": false, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "defer-to-issue" }, { "id": "F-PO-57-4-16", "category": "in-scope-deferrable", "severity": "low", "summary": "_typed_scan usage errors name the internal function rather than the caller-facing flag: an operator passing --kind phase-outcome:v1 is told _typed_scan rejected it, with no hint to drop the :v1 suffix.", "reasoning": "Diagnostic polish in the file other fixes already touch; add the flag name and suffix hint.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-17", "category": "in-scope-deferrable", "severity": "low", "summary": "The anchored-header regex tolerates no trailing or doubled whitespace; a single trailing space on line 1 reclassifies a real record as a near-miss.", "reasoning": "By design: anchored means byte-exact, the writers printf the exact shape, and whitespace tolerance would weaken the edit-tamper evidence. The near-miss warning names the id, which is the designed diagnostic.", "proposed_action": "accept", "fix_cost": "small", "adjacent_to_blocking": false, "feature_value": "none", "requires_product_decision": false, "applied_disposition": "accept" }, { "id": "F-PO-57-4-18", "category": "in-scope-deferrable", "severity": "low", "summary": "The provenance dirty flag reflects git status of the entire repo containing the suite; in dogfood runs (project == suite repo) unrelated uncommitted project files stamp dirty=true on every record.", "reasoning": "Implements the SREQ wording (scoped to that repo) exactly; narrowing to the suite subtree is a refinement that belongs with the issue-56 provenance follow-on, not a defect in this feature.", "proposed_action": "accept", "fix_cost": "small", "adjacent_to_blocking": false, "feature_value": "none", "requires_product_decision": false, "applied_disposition": "accept" }, { "id": "F-PO-57-4-19", "category": "in-scope-deferrable", "severity": "low", "summary": "phase-outcome.md documents a read_all output key order that differs from what the implementation emits (suite/comment_id/created_ms ordering).", "reasoning": "Cosmetic doc drift; key-addressed consumers unaffected. One-line doc alignment alongside the other doc fixes.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-20", "category": "in-scope-deferrable", "severity": "low", "summary": "Suite coverage gaps found by the audit and spec check: S3/S4 do not assert the element-and-repair-route halves of AC-3/AC-4 wording; typed-scan.sh S5a retains a pre-implementation fallback branch that cannot fail; no CRLF fixture exercises the carriage-return stripping; missing-attribute branches of the parse diagnosis are unpinned.", "reasoning": "The implementation satisfies the ACs (verified by execution this round); the gaps are in test assertion strength, fixable in the suites the tests stage touches anyway. Assert distinctive diagnostic phrases, not raw substrings, to avoid the coincidental-match trap the suite authors documented.", "proposed_action": "fix-now", "fix_cost": "small", "adjacent_to_blocking": true, "feature_value": "core", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-4-21", "category": "in-scope-deferrable", "severity": "low", "summary": "The new suites call touch -d with fractional epoch directly in fixtures; on a box whose touch lacks that capability (stock macOS) the suites fail with a confusing error rather than a clear capability message.", "reasoning": "Repo-local test tier, runs on dev boxes where the capability exists (probe-verified for local-fs elsewhere); a one-line capability check at suite start turns the confusing failure into a loud skip.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" } ], "pending_decisions": [], "suite": { "source": "git", "sha": "9c6cc88af7bde8f62b4c31193a928fe8a4b1aeaf", "dirty": false } } ```
Author
Owner
{"outcome":"issues-found","summary":"SUPERSEDES comment 1200 (tests-stage evidence correction, findings otherwise identical): CR-5 demoted in-scope-blocking->in-scope-deferrable (low, applied fix-now) — the prose route-through rule DOES fire on this repo because the tier-1 fence scratch is never empty; the empty-scratch awk swallow is real but latent (fence-free trees only), now red-tested in scripts/test-lint-conventions.sh against a synthetic tree. Blocking set is now CR-1..CR-4 (all probe-verified). Also recorded: the CR-1 end-to-end fixture cannot induce a local-fs primitive failure (scan_comments returns empty-success on a broken comments dir), so the CR-1 red test is unit-level with a stubbed failing primitive.","findings":[{"id":"CR-1","category":"in-scope-blocking","severity":"high","summary":"fold-candidates.sh:64 / promotion-candidates.sh:66: the per-issue tolerance wrapper is dead code — _typed_scan is a sourced function, so its _die exits the command-substitution subshell before the or-else fallback applies; one failing issue scan kills the whole candidates sweep with exit 7 and zero diagnostics (stderr swallowed by the 2>/dev/null). Verified by direct probe. The adjacent comment documents the opposite behavior."},{"id":"CR-2","category":"in-scope-blocking","severity":"high","summary":"_lib.sh _PO_PARSE_JQ extracts the JSON body with first-match splits on the fence delimiters, so a finding summary or decision rationale containing a triple-backtick sequence truncates the extraction; the writer happily mints such a record (verified), after which every read AND every future post on the issue refuse, and the diagnostic instructs the operator to delete a valid record. Self-wedging ledger, no automated recovery by design."},{"id":"CR-3","category":"in-scope-blocking","severity":"medium","summary":"phase-outcome-post.sh accepts anchored records whose id belongs to another issue: a pasted PO-999-7 on issue 301 triggered the retry guard and the post returned po_id PO-999-7 without minting anything (verified); with differing content it inflates the minted ordinal from the foreign max. Either way the ledger corrupts silently at write time and only surfaces as a read-side density refusal that names no comment id."},{"id":"CR-4","category":"in-scope-blocking","severity":"medium","summary":"_typed_scan classifies a valid anchored record of ANOTHER kind as a near-miss when the requested kind is a substring of that kind (kind=finding matches every folded-finding:v1 record) and its warning advises deleting or rewording the valid record. Verified: comments-scan --kind finding on a valid folded-finding record emits the delete advice."},{"id":"CR-5","category":"in-scope-deferrable","severity":"low","summary":"lint-conventions.sh:379: the prose-rule awk dedup uses the empty-first-file NR==FNR idiom; on a tree with ZERO fenced shell lines the scratch is empty and every prose hit is silently swallowed. On this repo the scratch is never empty (tier-1 extracts hundreds of fenced lines), so the rule works here — latent robustness defect, demoted from blocking after tests-stage evidence.","reasoning":"Blocking classification was refuted by the tests stage: planting a prose-only violation in the real tree IS caught (scratch non-empty). The awk semantics bug is probe-confirmed and fires only on a fence-free tree; one-line FILENAME guard, red-tested against a synthetic fence-free tree in scripts/test-lint-conventions.sh.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-6","category":"in-scope-deferrable","severity":"medium","summary":"forge-contract.md scan_comments: the new normative note says the match is a case-exact substring of line 1 and nothing more, and adapters MUST NOT tighten — but all four shipped adapters also gate on line 1 starting with the comment-marker prefix, and the same table row still says 'starts with a matching header'. The normative wording contradicts every shipped implementation and itself.","reasoning":"Doc-only inconsistency: no record can be lost (every anchored record starts with the comment marker); only near-miss classes not starting with the marker go unwarned. Fix is a wording change stating the real guarantee.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-7","category":"in-scope-deferrable","severity":"low","summary":"schemas/README.md states the three-outcomes rule family-wide including refusal-with-diagnosis for anchored-but-unparseable records, but only phase-outcome implements outcome 2; a malformed decision-resolution record still dies with a bare jq error naming no comment id (verified by probe).","reasoning":"Doc overpromise relative to shipped behavior; the minimal correct fix is scoping the README claim to what ships, with the family-wide behavior tracked separately (see CR-15).","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-8","category":"in-scope-deferrable","severity":"medium","summary":"fold-candidates.sh:44 / promotion-candidates.sh:44: the or-else-null wrapper around phase-outcome-read-latest converts every NEW hard-refusal path this feature added (unparseable record, density refusal, primitive failure) into 'no Phase Outcome yet', making an issue with a broken ledger silently eligible as a fold/promotion target.","reasoning":"Wrapper lines are unchanged by the feature but the failure surface behind them changed; a read refusal should skip the issue, not classify it as backlog. Small guard in two files the CR-1 fix already touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-9","category":"in-scope-deferrable","severity":"low","summary":"_lib.sh _fenced_json is now dead code (zero callers after the retry guard rewrite), leaving four divergent fence extractors in the tree.","reasoning":"Duplicate-over-reuse hygiene; delete the dead function while CR-2 consolidates the extraction it duplicated.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-10","category":"in-scope-deferrable","severity":"low","summary":"lint route-through rule (b) prose pattern misses copy-pasteable spellings: a slashless scan_comments.sh call, a prim-helper scan call in prose, and calls inside non-shell fences are not matched.","reasoning":"False-negative hardening of a rule this feature introduced; cheap regex widening in the file the CR-5 fix already touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-11","category":"in-scope-deferrable","severity":"low","summary":"lint route-through rule (a) population is only _shared/procedures/bin; a future non-adapter helper elsewhere calling the scan primitive escapes the rule.","reasoning":"Latent today (no such directories exist); widening the population is small and future-proofs the gate. Test suites keep their deliberate direct-primitive exemption, stated explicitly.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-12","category":"in-scope-deferrable","severity":"low","summary":"_suite_provenance inherits GIT_DIR/GIT_WORK_TREE from the environment (e.g. running under a git hook), which overrides the -C probe and attributes the PROJECT's sha to the suite.","reasoning":"Trivial env -u guard on the probe calls. The sibling case (install dir under an unrelated ancestor git repo) is an SREQ-accepted Technical Risk and stays accepted — coverage note, not re-raised.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-13","category":"in-scope-deferrable","severity":"low","summary":"_lib.sh comment claims pwd -P makes a deployed symlink farm report the real checkout, but _SKILLS_ROOT is derived with a LOGICAL cd (line 33), so a farm symlinking individual skill directories strips the symlink component before physical resolution; such installs degrade to release/unknown provenance.","reasoning":"Safe degradation, wrong comment; align the comment (or resolve physically at derivation). No record is ever blocked.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-14","category":"in-scope-deferrable","severity":"low","summary":"phase-outcome-post.sh checks collision only against the newly minted id; a ledger already carrying duplicate parsed ordinals is extended without refusal, minting on top of a state every reader refuses.","reasoning":"Write-side symmetry with the read-side density check; refuse duplicates only (legacy gaps stay continued-past by design). Lands in the code region the CR-3 fix touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-15","category":"in-scope-deferrable","severity":"low","summary":"Anchored-but-unparseable records of non-phase-outcome kinds (decision-resolution, deliverable-get consumers, fold/unfold) have no diagnosis path: consumers die with bare parser errors or silently misbehave, unlike the PO refusal that names comment id and element.","reasoning":"Real family-wide behavior gap but a substantial build (a shared parse-with-diagnosis layer per kind); does not block this feature whose ACs scope the refusal to phase-outcome. Spun out to its own issue. Deferred to issue #59 (sibling-linked).","proposed_action":"defer-to-issue","fix_cost":"substantial","adjacent_to_blocking":false,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"defer-to-issue"},{"id":"CR-16","category":"in-scope-deferrable","severity":"low","summary":"_typed_scan usage errors name the internal function rather than the caller-facing flag: an operator passing --kind phase-outcome:v1 is told _typed_scan rejected it, with no hint to drop the :v1 suffix.","reasoning":"Diagnostic polish in the file other fixes already touch; add the flag name and suffix hint.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-17","category":"in-scope-deferrable","severity":"low","summary":"The anchored-header regex tolerates no trailing or doubled whitespace; a single trailing space on line 1 reclassifies a real record as a near-miss.","reasoning":"By design: anchored means byte-exact, the writers printf the exact shape, and whitespace tolerance would weaken the edit-tamper evidence. The near-miss warning names the id, which is the designed diagnostic.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-18","category":"in-scope-deferrable","severity":"low","summary":"The provenance dirty flag reflects git status of the entire repo containing the suite; in dogfood runs (project == suite repo) unrelated uncommitted project files stamp dirty=true on every record.","reasoning":"Implements the SREQ wording (scoped to that repo) exactly; narrowing to the suite subtree is a refinement that belongs with the issue-56 provenance follow-on, not a defect in this feature.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-19","category":"in-scope-deferrable","severity":"low","summary":"phase-outcome.md documents a read_all output key order that differs from what the implementation emits (suite/comment_id/created_ms ordering).","reasoning":"Cosmetic doc drift; key-addressed consumers unaffected. One-line doc alignment alongside the other doc fixes.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-20","category":"in-scope-deferrable","severity":"low","summary":"Suite coverage gaps found by the audit and spec check: S3/S4 do not assert the element-and-repair-route halves of AC-3/AC-4 wording; typed-scan.sh S5a retains a pre-implementation fallback branch that cannot fail; no CRLF fixture exercises the carriage-return stripping; missing-attribute branches of the parse diagnosis are unpinned.","reasoning":"The implementation satisfies the ACs (verified by execution this round); the gaps are in test assertion strength, fixable in the suites the tests stage touches anyway. Assert distinctive diagnostic phrases, not raw substrings, to avoid the coincidental-match trap the suite authors documented.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-21","category":"in-scope-deferrable","severity":"low","summary":"The new suites call touch -d with fractional epoch directly in fixtures; on a box whose touch lacks that capability (stock macOS) the suites fail with a confusing error rather than a clear capability message.","reasoning":"Repo-local test tier, runs on dev boxes where the capability exists (probe-verified for local-fs elsewhere); a one-line capability check at suite start turns the confusing failure into a loud skip.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"}],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md","journal_path":".devwork/feature-57-run-record-integrity/code-journal.md"}}
<!-- qa-report:v1 issue=57 skill=qa domain=code phase=validate --> ```json {"outcome":"issues-found","summary":"SUPERSEDES comment 1200 (tests-stage evidence correction, findings otherwise identical): CR-5 demoted in-scope-blocking->in-scope-deferrable (low, applied fix-now) — the prose route-through rule DOES fire on this repo because the tier-1 fence scratch is never empty; the empty-scratch awk swallow is real but latent (fence-free trees only), now red-tested in scripts/test-lint-conventions.sh against a synthetic tree. Blocking set is now CR-1..CR-4 (all probe-verified). Also recorded: the CR-1 end-to-end fixture cannot induce a local-fs primitive failure (scan_comments returns empty-success on a broken comments dir), so the CR-1 red test is unit-level with a stubbed failing primitive.","findings":[{"id":"CR-1","category":"in-scope-blocking","severity":"high","summary":"fold-candidates.sh:64 / promotion-candidates.sh:66: the per-issue tolerance wrapper is dead code — _typed_scan is a sourced function, so its _die exits the command-substitution subshell before the or-else fallback applies; one failing issue scan kills the whole candidates sweep with exit 7 and zero diagnostics (stderr swallowed by the 2>/dev/null). Verified by direct probe. The adjacent comment documents the opposite behavior."},{"id":"CR-2","category":"in-scope-blocking","severity":"high","summary":"_lib.sh _PO_PARSE_JQ extracts the JSON body with first-match splits on the fence delimiters, so a finding summary or decision rationale containing a triple-backtick sequence truncates the extraction; the writer happily mints such a record (verified), after which every read AND every future post on the issue refuse, and the diagnostic instructs the operator to delete a valid record. Self-wedging ledger, no automated recovery by design."},{"id":"CR-3","category":"in-scope-blocking","severity":"medium","summary":"phase-outcome-post.sh accepts anchored records whose id belongs to another issue: a pasted PO-999-7 on issue 301 triggered the retry guard and the post returned po_id PO-999-7 without minting anything (verified); with differing content it inflates the minted ordinal from the foreign max. Either way the ledger corrupts silently at write time and only surfaces as a read-side density refusal that names no comment id."},{"id":"CR-4","category":"in-scope-blocking","severity":"medium","summary":"_typed_scan classifies a valid anchored record of ANOTHER kind as a near-miss when the requested kind is a substring of that kind (kind=finding matches every folded-finding:v1 record) and its warning advises deleting or rewording the valid record. Verified: comments-scan --kind finding on a valid folded-finding record emits the delete advice."},{"id":"CR-5","category":"in-scope-deferrable","severity":"low","summary":"lint-conventions.sh:379: the prose-rule awk dedup uses the empty-first-file NR==FNR idiom; on a tree with ZERO fenced shell lines the scratch is empty and every prose hit is silently swallowed. On this repo the scratch is never empty (tier-1 extracts hundreds of fenced lines), so the rule works here — latent robustness defect, demoted from blocking after tests-stage evidence.","reasoning":"Blocking classification was refuted by the tests stage: planting a prose-only violation in the real tree IS caught (scratch non-empty). The awk semantics bug is probe-confirmed and fires only on a fence-free tree; one-line FILENAME guard, red-tested against a synthetic fence-free tree in scripts/test-lint-conventions.sh.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-6","category":"in-scope-deferrable","severity":"medium","summary":"forge-contract.md scan_comments: the new normative note says the match is a case-exact substring of line 1 and nothing more, and adapters MUST NOT tighten — but all four shipped adapters also gate on line 1 starting with the comment-marker prefix, and the same table row still says 'starts with a matching header'. The normative wording contradicts every shipped implementation and itself.","reasoning":"Doc-only inconsistency: no record can be lost (every anchored record starts with the comment marker); only near-miss classes not starting with the marker go unwarned. Fix is a wording change stating the real guarantee.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-7","category":"in-scope-deferrable","severity":"low","summary":"schemas/README.md states the three-outcomes rule family-wide including refusal-with-diagnosis for anchored-but-unparseable records, but only phase-outcome implements outcome 2; a malformed decision-resolution record still dies with a bare jq error naming no comment id (verified by probe).","reasoning":"Doc overpromise relative to shipped behavior; the minimal correct fix is scoping the README claim to what ships, with the family-wide behavior tracked separately (see CR-15).","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-8","category":"in-scope-deferrable","severity":"medium","summary":"fold-candidates.sh:44 / promotion-candidates.sh:44: the or-else-null wrapper around phase-outcome-read-latest converts every NEW hard-refusal path this feature added (unparseable record, density refusal, primitive failure) into 'no Phase Outcome yet', making an issue with a broken ledger silently eligible as a fold/promotion target.","reasoning":"Wrapper lines are unchanged by the feature but the failure surface behind them changed; a read refusal should skip the issue, not classify it as backlog. Small guard in two files the CR-1 fix already touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-9","category":"in-scope-deferrable","severity":"low","summary":"_lib.sh _fenced_json is now dead code (zero callers after the retry guard rewrite), leaving four divergent fence extractors in the tree.","reasoning":"Duplicate-over-reuse hygiene; delete the dead function while CR-2 consolidates the extraction it duplicated.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-10","category":"in-scope-deferrable","severity":"low","summary":"lint route-through rule (b) prose pattern misses copy-pasteable spellings: a slashless scan_comments.sh call, a prim-helper scan call in prose, and calls inside non-shell fences are not matched.","reasoning":"False-negative hardening of a rule this feature introduced; cheap regex widening in the file the CR-5 fix already touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-11","category":"in-scope-deferrable","severity":"low","summary":"lint route-through rule (a) population is only _shared/procedures/bin; a future non-adapter helper elsewhere calling the scan primitive escapes the rule.","reasoning":"Latent today (no such directories exist); widening the population is small and future-proofs the gate. Test suites keep their deliberate direct-primitive exemption, stated explicitly.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-12","category":"in-scope-deferrable","severity":"low","summary":"_suite_provenance inherits GIT_DIR/GIT_WORK_TREE from the environment (e.g. running under a git hook), which overrides the -C probe and attributes the PROJECT's sha to the suite.","reasoning":"Trivial env -u guard on the probe calls. The sibling case (install dir under an unrelated ancestor git repo) is an SREQ-accepted Technical Risk and stays accepted — coverage note, not re-raised.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-13","category":"in-scope-deferrable","severity":"low","summary":"_lib.sh comment claims pwd -P makes a deployed symlink farm report the real checkout, but _SKILLS_ROOT is derived with a LOGICAL cd (line 33), so a farm symlinking individual skill directories strips the symlink component before physical resolution; such installs degrade to release/unknown provenance.","reasoning":"Safe degradation, wrong comment; align the comment (or resolve physically at derivation). No record is ever blocked.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-14","category":"in-scope-deferrable","severity":"low","summary":"phase-outcome-post.sh checks collision only against the newly minted id; a ledger already carrying duplicate parsed ordinals is extended without refusal, minting on top of a state every reader refuses.","reasoning":"Write-side symmetry with the read-side density check; refuse duplicates only (legacy gaps stay continued-past by design). Lands in the code region the CR-3 fix touches.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-15","category":"in-scope-deferrable","severity":"low","summary":"Anchored-but-unparseable records of non-phase-outcome kinds (decision-resolution, deliverable-get consumers, fold/unfold) have no diagnosis path: consumers die with bare parser errors or silently misbehave, unlike the PO refusal that names comment id and element.","reasoning":"Real family-wide behavior gap but a substantial build (a shared parse-with-diagnosis layer per kind); does not block this feature whose ACs scope the refusal to phase-outcome. Spun out to its own issue. Deferred to issue #59 (sibling-linked).","proposed_action":"defer-to-issue","fix_cost":"substantial","adjacent_to_blocking":false,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"defer-to-issue"},{"id":"CR-16","category":"in-scope-deferrable","severity":"low","summary":"_typed_scan usage errors name the internal function rather than the caller-facing flag: an operator passing --kind phase-outcome:v1 is told _typed_scan rejected it, with no hint to drop the :v1 suffix.","reasoning":"Diagnostic polish in the file other fixes already touch; add the flag name and suffix hint.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-17","category":"in-scope-deferrable","severity":"low","summary":"The anchored-header regex tolerates no trailing or doubled whitespace; a single trailing space on line 1 reclassifies a real record as a near-miss.","reasoning":"By design: anchored means byte-exact, the writers printf the exact shape, and whitespace tolerance would weaken the edit-tamper evidence. The near-miss warning names the id, which is the designed diagnostic.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-18","category":"in-scope-deferrable","severity":"low","summary":"The provenance dirty flag reflects git status of the entire repo containing the suite; in dogfood runs (project == suite repo) unrelated uncommitted project files stamp dirty=true on every record.","reasoning":"Implements the SREQ wording (scoped to that repo) exactly; narrowing to the suite subtree is a refinement that belongs with the issue-56 provenance follow-on, not a defect in this feature.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-19","category":"in-scope-deferrable","severity":"low","summary":"phase-outcome.md documents a read_all output key order that differs from what the implementation emits (suite/comment_id/created_ms ordering).","reasoning":"Cosmetic doc drift; key-addressed consumers unaffected. One-line doc alignment alongside the other doc fixes.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-20","category":"in-scope-deferrable","severity":"low","summary":"Suite coverage gaps found by the audit and spec check: S3/S4 do not assert the element-and-repair-route halves of AC-3/AC-4 wording; typed-scan.sh S5a retains a pre-implementation fallback branch that cannot fail; no CRLF fixture exercises the carriage-return stripping; missing-attribute branches of the parse diagnosis are unpinned.","reasoning":"The implementation satisfies the ACs (verified by execution this round); the gaps are in test assertion strength, fixable in the suites the tests stage touches anyway. Assert distinctive diagnostic phrases, not raw substrings, to avoid the coincidental-match trap the suite authors documented.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-21","category":"in-scope-deferrable","severity":"low","summary":"The new suites call touch -d with fractional epoch directly in fixtures; on a box whose touch lacks that capability (stock macOS) the suites fail with a confusing error rather than a clear capability message.","reasoning":"Repo-local test tier, runs on dev boxes where the capability exists (probe-verified for local-fs elsewhere); a one-line capability check at suite start turns the confusing failure into a loud skip.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"}],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md","journal_path":".devwork/feature-57-run-record-integrity/code-journal.md"}} ```
Author
Owner
{"outcome":"clean","summary":"Every intended regression test written and its red/green state verified by execution (commits bf4715e + 3ac72a4). Red now, green after fix: R1 backtick-safe ledger (CR-2, 2 red asserts), R2 foreign-issue refusal (CR-3, 3 red), R3 duplicate-ordinal refusal (CR-14, 2 red), R4 cross-kind not-a-near-miss (CR-4, 1 red), S5b-9 unit-level wrapper-survives-failing-primitive (CR-1, 2 red), test-lint-conventions.sh prose-rule-on-fence-free-tree (CR-5, 1 red). Green pins added: S3/S4 element+repair-route phrase asserts (AC-3/AC-4 halves, CR-20), CRLF-header and missing-id-attribute scenarios, S5b-8 broken-issue sweep tolerance, lint clean-tree control, capability guards (CR-21); S5a dead fallback removed. Note: the CR-1 end-to-end fixture cannot induce a local-fs primitive failure (empty-success -d guard), so CR-1 is pinned unit-level with a stubbed failing primitive.","findings":[],"artifacts":{"test_files":["plugin/skills/_shared/procedures/test/typed-scan.sh","plugin/skills/_shared/procedures/test/typed-scan-family.sh","scripts/test-lint-conventions.sh"],"test_commit":"3ac72a4","test_marker":{"runner":"bespoke bash harness (suite scripts under _shared/procedures/test + scripts/test-lint-conventions.sh)","write":"scenario asserts the FIXED behavior and reports FAIL while the defect stands (recorded red run: 11 red assertions across 6 scenarios)","promote":"no marker edit — the fix turns the scenarios green; suite exit codes are the gate"}}}
<!-- qa-report:v1 issue=57 skill=qa domain=code phase=tests --> ```json {"outcome":"clean","summary":"Every intended regression test written and its red/green state verified by execution (commits bf4715e + 3ac72a4). Red now, green after fix: R1 backtick-safe ledger (CR-2, 2 red asserts), R2 foreign-issue refusal (CR-3, 3 red), R3 duplicate-ordinal refusal (CR-14, 2 red), R4 cross-kind not-a-near-miss (CR-4, 1 red), S5b-9 unit-level wrapper-survives-failing-primitive (CR-1, 2 red), test-lint-conventions.sh prose-rule-on-fence-free-tree (CR-5, 1 red). Green pins added: S3/S4 element+repair-route phrase asserts (AC-3/AC-4 halves, CR-20), CRLF-header and missing-id-attribute scenarios, S5b-8 broken-issue sweep tolerance, lint clean-tree control, capability guards (CR-21); S5a dead fallback removed. Note: the CR-1 end-to-end fixture cannot induce a local-fs primitive failure (empty-success -d guard), so CR-1 is pinned unit-level with a stubbed failing primitive.","findings":[],"artifacts":{"test_files":["plugin/skills/_shared/procedures/test/typed-scan.sh","plugin/skills/_shared/procedures/test/typed-scan-family.sh","scripts/test-lint-conventions.sh"],"test_commit":"3ac72a4","test_marker":{"runner":"bespoke bash harness (suite scripts under _shared/procedures/test + scripts/test-lint-conventions.sh)","write":"scenario asserts the FIXED behavior and reports FAIL while the defect stands (recorded red run: 11 red assertions across 6 scenarios)","promote":"no marker edit — the fix turns the scenarios green; suite exit codes are the gate"}}} ```
Author
Owner
{"outcome":"clean","summary":"All 15 fix-now findings resolved in commits eed5ed2 (CR-1,2,3,4,8,9,12,13,14,16), 4a21bcf (CR-5,10,11), 52b35b6 (CR-6,7,19) on top of tests baseline 3ac72a4. All 11 red assertions green; full ladder green: typed-scan 47, typed-scan-family 42, provenance 12, smoke 56, disposition-flow 42, promotion-flow 20, release-flow 20, slot-flow 180, test-lint-conventions PASS, lint clean (48 non-adapter scripts in the widened route-through population). AC-10 live reads: issue 50 rc=0 7 records no warnings; issue 57 rc=0 4 records dense. No test file modified since the red baseline (verified by diff); executable bits intact. Two recorded fixer judgment calls, both under independent fresh-context re-validation: (1) CR-1 fixed inside _typed_scan — runtime failures now log and return the env exit code instead of exiting the substitution subshell, so set -e still kills non-tolerant callers (failure never reads as empty) while deliberate per-issue fallbacks actually fire; usage errors still die. (2) CR-2 fence closer anchored to line START rather than exact-line, because a smoke.sh tamper fixture legitimately produces a suffixed closer; safe since JSON newline-escaping means no valid body line can begin with a backtick. Accepts CR-17/CR-18 and deferral CR-15 (issue 59) unchanged.","findings":[],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md"}}
<!-- qa-report:v1 issue=57 skill=qa domain=code phase=fix --> ```json {"outcome":"clean","summary":"All 15 fix-now findings resolved in commits eed5ed2 (CR-1,2,3,4,8,9,12,13,14,16), 4a21bcf (CR-5,10,11), 52b35b6 (CR-6,7,19) on top of tests baseline 3ac72a4. All 11 red assertions green; full ladder green: typed-scan 47, typed-scan-family 42, provenance 12, smoke 56, disposition-flow 42, promotion-flow 20, release-flow 20, slot-flow 180, test-lint-conventions PASS, lint clean (48 non-adapter scripts in the widened route-through population). AC-10 live reads: issue 50 rc=0 7 records no warnings; issue 57 rc=0 4 records dense. No test file modified since the red baseline (verified by diff); executable bits intact. Two recorded fixer judgment calls, both under independent fresh-context re-validation: (1) CR-1 fixed inside _typed_scan — runtime failures now log and return the env exit code instead of exiting the substitution subshell, so set -e still kills non-tolerant callers (failure never reads as empty) while deliberate per-issue fallbacks actually fire; usage errors still die. (2) CR-2 fence closer anchored to line START rather than exact-line, because a smoke.sh tamper fixture legitimately produces a suffixed closer; safe since JSON newline-escaping means no valid body line can begin with a backtick. Accepts CR-17/CR-18 and deferral CR-15 (issue 59) unchanged.","findings":[],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md"}} ```
Author
Owner
{"outcome":"skipped","summary":"No browser e2e surface. The PREQ-derived validation run executed instead via the shell harness (fresh sonnet execution of test-plan:v1 S1-S10): typed-scan.sh and provenance.sh both green, 10/10 scenarios validated — 8 directly, S5 via cross-suite evidence (typed-scan-family.sh S5b-1 drives the decision-discovery consumer against a near-miss ledger with baseline comparison; S2 covers resume detection read-latest; the retry guard runs inside every posted scenario), S1 step 5 (near-miss comment untouched by the post) pinned by a new green assertion in commit e2a2f4f after the run flagged it unasserted. S10 live-ledger reads against the real tracker: issue 50 rc=0, 7 records dense, all suite null, no warnings; issue 57 rc=0, 4 records dense, pre-feature records suite null, new-tooling records carry source git — exactly the required pattern. Traceability AC1-AC10 -> S1-S10 complete, no orphans.","findings":[],"not_applicable_reason":"markdown+shell skill suite — no application or browser surface to drive end-to-end; validation is the shell test harness plus dogfooding"}
<!-- qa-report:v1 issue=57 skill=qa domain=e2e phase=validate --> ```json {"outcome":"skipped","summary":"No browser e2e surface. The PREQ-derived validation run executed instead via the shell harness (fresh sonnet execution of test-plan:v1 S1-S10): typed-scan.sh and provenance.sh both green, 10/10 scenarios validated — 8 directly, S5 via cross-suite evidence (typed-scan-family.sh S5b-1 drives the decision-discovery consumer against a near-miss ledger with baseline comparison; S2 covers resume detection read-latest; the retry guard runs inside every posted scenario), S1 step 5 (near-miss comment untouched by the post) pinned by a new green assertion in commit e2a2f4f after the run flagged it unasserted. S10 live-ledger reads against the real tracker: issue 50 rc=0, 7 records dense, all suite null, no warnings; issue 57 rc=0, 4 records dense, pre-feature records suite null, new-tooling records carry source git — exactly the required pattern. Traceability AC1-AC10 -> S1-S10 complete, no orphans.","findings":[],"not_applicable_reason":"markdown+shell skill suite — no application or browser surface to drive end-to-end; validation is the shell test harness plus dogfooding"} ```
Author
Owner
{"outcome":"issues-found","summary":"Round 2 = the sweep adversarial pass (fable, fresh context, all probes executed) over the round-1 fix and test commits, plus the full ladder re-run (10/10 green at e2a2f4f) and the stage-B validation run (10/10 scenarios, live-ledger reads clean). Sweep: 8 candidates -> 11 findings after lead verification and splits (1 blocking: CR-22 read-side foreign-record acceptance — read-latest reports a pasted foreign record as the issue phase while the post side refuses, reproduced independently; 5 applied fix-now trivials: lint slashless-in-fence and quoted-prose spellings, CRLF tolerance in the fold-summary captures, numeric issue-segment compare, date -r capability guard; 5 applied accepts with reasoning). Sweep also verified sound under executed probes: fence-regex edge cases incl. prose-fence-before-real-fence and two-fence bodies, post gate ordering vs retry guard incl. the foreign-highest-record leak attempt, all nine _typed_scan call sites for empty-ledger swallow, lint clean-tree false-positive check. Round 2 loops: red test for CR-22, fix, fresh re-validate, and a NEW sweep before exit.","findings":[{"id":"CR-22","category":"in-scope-blocking","severity":"medium","summary":"Read-side foreign-record acceptance (sweep F1, reproduced twice): phase-outcome-read-all.sh has no foreign-issue gate, so a pasted anchored record with a foreign id at the dense-next ordinal is returned as part of the ledger and read-latest reports the FOREIGN record's id and next_state as the issue's current phase — while the post side (CR-3 fix) refuses. Write-wedged but read-lies: every phase-eligibility consumer computes state from another issue's record."},{"id":"CR-23","category":"in-scope-deferrable","severity":"low","summary":"Lint fence-side PRIM_RE requires the pathful spelling or the prim-helper form, so a slashless scan_comments.sh call WITH arguments inside a backtick shell fence escapes both route-through halves (sweep F2, reproduced).","reasoning":"The exact spelling the prose rule was widened to catch is structurally exempt inside fences; one alternation in PRIM_RE closes it.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-24","category":"in-scope-deferrable","severity":"low","summary":"Lint prose regex requires whitespace directly after the script name, so the quoted pathful spelling (closing double-quote after .sh, the D12-conventional form) escapes the prose rule (sweep F3, reproduced).","reasoning":"Tolerating an optional closing quote before the whitespace is a one-character regex widening.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-25","category":"in-scope-deferrable","severity":"low","summary":"The lint fence extractor tracks backtick fences only; a route-through violation inside a tilde fence or indented code block is caught by nothing (sweep F3b, reproduced).","reasoning":"Tilde and indented fences are not a convention this repo emits anywhere (the shared tracker is deliberately the backtick CommonMark subset per the CLAUDE.md fence rule); guarding a spelling nobody writes is dead defensiveness. Revisit only if the repo ever adopts tilde fences.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-26","category":"in-scope-deferrable","severity":"low","summary":"fold-candidates.sh and promotion-candidates.sh kept a fence-opener regex without CRLF tolerance in the active-folds summary capture, unlike the fixed extractors; a CRLF folded-finding record's summary silently drops from the candidate context (sweep F7).","reasoning":"Same defect class the round fixed elsewhere; the fix commit touched exactly these lines. Two-character regex addition.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-27","category":"in-scope-deferrable","severity":"low","summary":"The new foreign-issue gate compares issue segments as strings, so a zero-padded --issue value makes the issue's own records look foreign (sweep F8b).","reasoning":"Numeric comparison via tonumber on both sides; applies to the post gate and the new read gate alike.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-28","category":"in-scope-deferrable","severity":"low","summary":"The S1 step-5 pin reads mtime with a GNU-only date -r form; on BSD/macOS the suite aborts confusingly instead of the loud capability skip the suites otherwise standardize (sweep F8a).","reasoning":"Extend the existing capability guard to probe date -r once at suite start; test-tier change only.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-29","category":"in-scope-deferrable","severity":"low","summary":"An anchored record of an INVENTED kind that carries a real schema token as a substring (prefix form, e.g. extra-phase-outcome:v1) is now excluded silently by the cross-kind branch where it near-miss-warned pre-fix; the suffix form never clears the adapter prefilter in either era (sweep F4, mechanism corrected by lead probe).","reasoning":"The silent-exclusion trade is deliberate: the partition cannot distinguish an invented kind from a legitimate other kind without a schema roster, which the helper tier cannot read portably; a mis-authored record of the SCANNED kind still warns (non-anchored) or refuses (anchored-unparseable). Bounded corner accepted; the roster question rides the number-59 family work if anywhere.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-30","category":"in-scope-deferrable","severity":"low","summary":"An EDITED record of another kind (edited_ms > created_ms) excluded by the cross-kind branch loses the loud edited-near-miss diagnostic it incidentally received pre-fix (sweep F5, reproduced).","reasoning":"That diagnostic was an accident of the mislabeling bug: edit evidence for a kind belongs to that kind's own consumers, and folded-finding is a latest-wins schema, not section-8-immutable, so no tamper contract weakened. Accepted.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-31","category":"in-scope-deferrable","severity":"low","summary":"The two tolerant sweep wrappers also swallow a usage-error die from _typed_scan into the empty-array fallback, contradicting the stated never-swallow-bad-kind invariant (sweep F6).","reasoning":"Both call sites pass hardcoded literal kinds, so the masked state is unreachable today; distinguishing exit codes in the wrapper adds branching for an impossible case. Accepted as latent; revisit if kinds ever become parameterized there.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-32","category":"in-scope-deferrable","severity":"low","summary":"The widened lint population misses hypothetical .bash-extension helpers and scripts/ subdirectories (sweep F8c, reproduced with planted files).","reasoning":"No such files exist and the repo's conventions produce none; the population matches what the tree can contain, same reasoning as the round-1 population disposition. Accepted.","proposed_action":"accept","fix_cost":"trivial","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"}],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md"}}
<!-- qa-report:v1 issue=57 skill=qa domain=code phase=validate --> ```json {"outcome":"issues-found","summary":"Round 2 = the sweep adversarial pass (fable, fresh context, all probes executed) over the round-1 fix and test commits, plus the full ladder re-run (10/10 green at e2a2f4f) and the stage-B validation run (10/10 scenarios, live-ledger reads clean). Sweep: 8 candidates -> 11 findings after lead verification and splits (1 blocking: CR-22 read-side foreign-record acceptance — read-latest reports a pasted foreign record as the issue phase while the post side refuses, reproduced independently; 5 applied fix-now trivials: lint slashless-in-fence and quoted-prose spellings, CRLF tolerance in the fold-summary captures, numeric issue-segment compare, date -r capability guard; 5 applied accepts with reasoning). Sweep also verified sound under executed probes: fence-regex edge cases incl. prose-fence-before-real-fence and two-fence bodies, post gate ordering vs retry guard incl. the foreign-highest-record leak attempt, all nine _typed_scan call sites for empty-ledger swallow, lint clean-tree false-positive check. Round 2 loops: red test for CR-22, fix, fresh re-validate, and a NEW sweep before exit.","findings":[{"id":"CR-22","category":"in-scope-blocking","severity":"medium","summary":"Read-side foreign-record acceptance (sweep F1, reproduced twice): phase-outcome-read-all.sh has no foreign-issue gate, so a pasted anchored record with a foreign id at the dense-next ordinal is returned as part of the ledger and read-latest reports the FOREIGN record's id and next_state as the issue's current phase — while the post side (CR-3 fix) refuses. Write-wedged but read-lies: every phase-eligibility consumer computes state from another issue's record."},{"id":"CR-23","category":"in-scope-deferrable","severity":"low","summary":"Lint fence-side PRIM_RE requires the pathful spelling or the prim-helper form, so a slashless scan_comments.sh call WITH arguments inside a backtick shell fence escapes both route-through halves (sweep F2, reproduced).","reasoning":"The exact spelling the prose rule was widened to catch is structurally exempt inside fences; one alternation in PRIM_RE closes it.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-24","category":"in-scope-deferrable","severity":"low","summary":"Lint prose regex requires whitespace directly after the script name, so the quoted pathful spelling (closing double-quote after .sh, the D12-conventional form) escapes the prose rule (sweep F3, reproduced).","reasoning":"Tolerating an optional closing quote before the whitespace is a one-character regex widening.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"core","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-25","category":"in-scope-deferrable","severity":"low","summary":"The lint fence extractor tracks backtick fences only; a route-through violation inside a tilde fence or indented code block is caught by nothing (sweep F3b, reproduced).","reasoning":"Tilde and indented fences are not a convention this repo emits anywhere (the shared tracker is deliberately the backtick CommonMark subset per the CLAUDE.md fence rule); guarding a spelling nobody writes is dead defensiveness. Revisit only if the repo ever adopts tilde fences.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-26","category":"in-scope-deferrable","severity":"low","summary":"fold-candidates.sh and promotion-candidates.sh kept a fence-opener regex without CRLF tolerance in the active-folds summary capture, unlike the fixed extractors; a CRLF folded-finding record's summary silently drops from the candidate context (sweep F7).","reasoning":"Same defect class the round fixed elsewhere; the fix commit touched exactly these lines. Two-character regex addition.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-27","category":"in-scope-deferrable","severity":"low","summary":"The new foreign-issue gate compares issue segments as strings, so a zero-padded --issue value makes the issue's own records look foreign (sweep F8b).","reasoning":"Numeric comparison via tonumber on both sides; applies to the post gate and the new read gate alike.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-28","category":"in-scope-deferrable","severity":"low","summary":"The S1 step-5 pin reads mtime with a GNU-only date -r form; on BSD/macOS the suite aborts confusingly instead of the loud capability skip the suites otherwise standardize (sweep F8a).","reasoning":"Extend the existing capability guard to probe date -r once at suite start; test-tier change only.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-29","category":"in-scope-deferrable","severity":"low","summary":"An anchored record of an INVENTED kind that carries a real schema token as a substring (prefix form, e.g. extra-phase-outcome:v1) is now excluded silently by the cross-kind branch where it near-miss-warned pre-fix; the suffix form never clears the adapter prefilter in either era (sweep F4, mechanism corrected by lead probe).","reasoning":"The silent-exclusion trade is deliberate: the partition cannot distinguish an invented kind from a legitimate other kind without a schema roster, which the helper tier cannot read portably; a mis-authored record of the SCANNED kind still warns (non-anchored) or refuses (anchored-unparseable). Bounded corner accepted; the roster question rides the number-59 family work if anywhere.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-30","category":"in-scope-deferrable","severity":"low","summary":"An EDITED record of another kind (edited_ms > created_ms) excluded by the cross-kind branch loses the loud edited-near-miss diagnostic it incidentally received pre-fix (sweep F5, reproduced).","reasoning":"That diagnostic was an accident of the mislabeling bug: edit evidence for a kind belongs to that kind's own consumers, and folded-finding is a latest-wins schema, not section-8-immutable, so no tamper contract weakened. Accepted.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-31","category":"in-scope-deferrable","severity":"low","summary":"The two tolerant sweep wrappers also swallow a usage-error die from _typed_scan into the empty-array fallback, contradicting the stated never-swallow-bad-kind invariant (sweep F6).","reasoning":"Both call sites pass hardcoded literal kinds, so the masked state is unreachable today; distinguishing exit codes in the wrapper adds branching for an impossible case. Accepted as latent; revisit if kinds ever become parameterized there.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-32","category":"in-scope-deferrable","severity":"low","summary":"The widened lint population misses hypothetical .bash-extension helpers and scripts/ subdirectories (sweep F8c, reproduced with planted files).","reasoning":"No such files exist and the repo's conventions produce none; the population matches what the tree can contain, same reasoning as the round-1 population disposition. Accepted.","proposed_action":"accept","fix_cost":"trivial","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"}],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md"}} ```
Author
Owner
{"outcome":"clean","summary":"Round-2 red baseline commit 5e149bc: scenario R7 (CR-22 read-side foreign-record refusal) recorded red with exactly 3 failing assertions (read-all accepts, no comment-id pointer, read-latest inherits); test-writer independently reproduced the defect before writing the assertions. CR-28 capability guard (date -r probe) added green. Not red-harnessed with reasoning: CR-23/CR-24 land in the lint self-test file as post-fix controls (lint rules are probed by planting, the existing self-test pattern); CR-26 (CRLF fold-summary capture) and CR-27 (numeric issue compare) are verified by direct execution in the fix stage — trivial regex/comparison changes with the fixer forbidden from touching any test.","findings":[],"artifacts":{"test_files":["plugin/skills/_shared/procedures/test/typed-scan.sh"],"test_commit":"5e149bc","test_marker":{"runner":"bespoke bash harness","write":"scenario asserts the FIXED behavior and reports FAIL while the defect stands (recorded red: 3 assertions)","promote":"no marker edit — the fix turns R7 green; suite exit code is the gate"}}}
<!-- qa-report:v1 issue=57 skill=qa domain=code phase=tests --> ```json {"outcome":"clean","summary":"Round-2 red baseline commit 5e149bc: scenario R7 (CR-22 read-side foreign-record refusal) recorded red with exactly 3 failing assertions (read-all accepts, no comment-id pointer, read-latest inherits); test-writer independently reproduced the defect before writing the assertions. CR-28 capability guard (date -r probe) added green. Not red-harnessed with reasoning: CR-23/CR-24 land in the lint self-test file as post-fix controls (lint rules are probed by planting, the existing self-test pattern); CR-26 (CRLF fold-summary capture) and CR-27 (numeric issue compare) are verified by direct execution in the fix stage — trivial regex/comparison changes with the fixer forbidden from touching any test.","findings":[],"artifacts":{"test_files":["plugin/skills/_shared/procedures/test/typed-scan.sh"],"test_commit":"5e149bc","test_marker":{"runner":"bespoke bash harness","write":"scenario asserts the FIXED behavior and reports FAIL while the defect stands (recorded red: 3 assertions)","promote":"no marker edit — the fix turns R7 green; suite exit code is the gate"}}} ```
Author
Owner
{"outcome":"clean","summary":"Round-2 fix-now set resolved in commits 4dfd223 (CR-22+CR-27: shared _po_foreign_gate in _lib.sh, called by post AND read-all so the two gates cannot drift; numeric issue-segment compare with non-numeric fallback; read-latest inherits; phase-outcome.md spec updated with the new refusal row), a6f7dd1 (CR-23+CR-24: slashless-in-fence and quoted-prose spellings — including a second latent defect the probe exposed: the fence rule matched against the path-prefixed scratch line so the widened alternation could never fire; restructured to test fence content directly), 1562cf2 (CR-26: CRLF tolerance in both fold-summary captures, probe-verified old-vs-new on the same CRLF body). R7 green (typed-scan 52 ok); full ladder green incl. plugin-gates 17/17; lint clean; plant-probe evidence for both lint spellings; AC-10 live reads clean (issue 50: 7 records, issue 57: dense, no warnings). No test file touched since red baseline 5e149bc; no mode changes. Accepts CR-25/29/30/31/32 stand as recorded in comment 1206.","findings":[],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md"}}
<!-- qa-report:v1 issue=57 skill=qa domain=code phase=fix --> ```json {"outcome":"clean","summary":"Round-2 fix-now set resolved in commits 4dfd223 (CR-22+CR-27: shared _po_foreign_gate in _lib.sh, called by post AND read-all so the two gates cannot drift; numeric issue-segment compare with non-numeric fallback; read-latest inherits; phase-outcome.md spec updated with the new refusal row), a6f7dd1 (CR-23+CR-24: slashless-in-fence and quoted-prose spellings — including a second latent defect the probe exposed: the fence rule matched against the path-prefixed scratch line so the widened alternation could never fire; restructured to test fence content directly), 1562cf2 (CR-26: CRLF tolerance in both fold-summary captures, probe-verified old-vs-new on the same CRLF body). R7 green (typed-scan 52 ok); full ladder green incl. plugin-gates 17/17; lint clean; plant-probe evidence for both lint spellings; AC-10 live reads clean (issue 50: 7 records, issue 57: dense, no warnings). No test file touched since red baseline 5e149bc; no mode changes. Accepts CR-25/29/30/31/32 stand as recorded in comment 1206.","findings":[],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md"}} ```
Author
Owner
{"outcome":"issues-found","summary":"Round 3 = the round-2 sweep (fable, fresh, all probes executed) + fresh re-validation of the round-2 fix diff. Every core attack held: gate ordering both sides (foreign before dup at post, foreign before density at read), retry guard preserved, padded/zero/plus-sign issue forms cannot lock out own records, CRLF captures across mixed endings, lint dedup and lookalike exemptions, blast radius of the new read refusal bounded to the two log-and-skip sweeps. 5 findings, all low, all developer-decidable applied: 3 fix-now (regression pins for the round-2 fixes; the fence-comment rule statement in lint; a one-line spec-order hedge in phase-outcome.md — the round-3 changeset is tests+docs+comments only, zero behavior), 2 accept (pre-1.7-jq tonumber precision theoretical; colon-filename lint report cosmetic). Non-findings recorded: padded mint writes the padded segment into the durable id (pre-existing adapter addressing, outside this diff); unclosed active-folds fence weakens discovery signal silently by design.","findings":[{"id":"CR-33","category":"in-scope-deferrable","severity":"low","summary":"Round-2 fixes CR-23/24/26/27 are probe-verified but carry no automated regression pins: nothing in the suites would catch a future regression of the two lint spellings, the CRLF fold-summary capture, or the padded-issue gate compare (re-validator finding).","reasoning":"Green pins only — the behaviors are fixed and verified; test-only commit by the tests-stage actor.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-34","category":"in-scope-deferrable","severity":"low","summary":"The widened fence-side PRIM_RE now also flags a commented-out slashless call with args inside a shell fence, while the in-file comment still claims fence-comment mentions stay legal (sweep-2 F1, reproduced; zero hits in the tree).","reasoning":"The tightening is desirable — a commented-out call is still copy-pasteable — so the fix is correcting the in-file comment to state the real rule, not weakening the rule.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-35","category":"in-scope-deferrable","severity":"low","summary":"phase-outcome.md documents the read_all refusal order as parse then foreign then immutability, but the implementation enforces immutability FIRST on the raw scan: an edited foreign record yields the section-8 refusal, not the foreign one (sweep-2 F2, reproduced; the parse-vs-immutability half pre-existed the spec edit).","reasoning":"Diagnostic-wording mismatch only — both refusals are loud and name the comment. One-line spec hedge stating immutability runs first on the raw scan.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-36","category":"in-scope-deferrable","severity":"low","summary":"On jq older than 1.7, tonumber compares beyond-2^53 issue numbers as doubles, so two absurdly large issue numbers differing in the last digit could defeat the foreign gate; on the installed jq 1.8.1 the refusal reproduces correctly (sweep-2 F3).","reasoning":"No forge approaches 2 to the 53rd issues; the baseline declares jq without a version floor and this corner does not justify one. Accepted as theoretical.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-37","category":"in-scope-deferrable","severity":"low","summary":"A markdown filename containing a colon breaks the lint report's path:lineno split — the violation is still caught but reported without a line number (sweep-2 F4, pre-existing, reproduced).","reasoning":"No repo convention produces colon-bearing filenames; catch-without-lineno degrades gracefully. Accepted as cosmetic.","proposed_action":"accept","fix_cost":"trivial","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"}],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md"}}
<!-- qa-report:v1 issue=57 skill=qa domain=code phase=validate --> ```json {"outcome":"issues-found","summary":"Round 3 = the round-2 sweep (fable, fresh, all probes executed) + fresh re-validation of the round-2 fix diff. Every core attack held: gate ordering both sides (foreign before dup at post, foreign before density at read), retry guard preserved, padded/zero/plus-sign issue forms cannot lock out own records, CRLF captures across mixed endings, lint dedup and lookalike exemptions, blast radius of the new read refusal bounded to the two log-and-skip sweeps. 5 findings, all low, all developer-decidable applied: 3 fix-now (regression pins for the round-2 fixes; the fence-comment rule statement in lint; a one-line spec-order hedge in phase-outcome.md — the round-3 changeset is tests+docs+comments only, zero behavior), 2 accept (pre-1.7-jq tonumber precision theoretical; colon-filename lint report cosmetic). Non-findings recorded: padded mint writes the padded segment into the durable id (pre-existing adapter addressing, outside this diff); unclosed active-folds fence weakens discovery signal silently by design.","findings":[{"id":"CR-33","category":"in-scope-deferrable","severity":"low","summary":"Round-2 fixes CR-23/24/26/27 are probe-verified but carry no automated regression pins: nothing in the suites would catch a future regression of the two lint spellings, the CRLF fold-summary capture, or the padded-issue gate compare (re-validator finding).","reasoning":"Green pins only — the behaviors are fixed and verified; test-only commit by the tests-stage actor.","proposed_action":"fix-now","fix_cost":"small","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-34","category":"in-scope-deferrable","severity":"low","summary":"The widened fence-side PRIM_RE now also flags a commented-out slashless call with args inside a shell fence, while the in-file comment still claims fence-comment mentions stay legal (sweep-2 F1, reproduced; zero hits in the tree).","reasoning":"The tightening is desirable — a commented-out call is still copy-pasteable — so the fix is correcting the in-file comment to state the real rule, not weakening the rule.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-35","category":"in-scope-deferrable","severity":"low","summary":"phase-outcome.md documents the read_all refusal order as parse then foreign then immutability, but the implementation enforces immutability FIRST on the raw scan: an edited foreign record yields the section-8 refusal, not the foreign one (sweep-2 F2, reproduced; the parse-vs-immutability half pre-existed the spec edit).","reasoning":"Diagnostic-wording mismatch only — both refusals are loud and name the comment. One-line spec hedge stating immutability runs first on the raw scan.","proposed_action":"fix-now","fix_cost":"trivial","adjacent_to_blocking":true,"feature_value":"incidental","requires_product_decision":false,"applied_disposition":"fix-now"},{"id":"CR-36","category":"in-scope-deferrable","severity":"low","summary":"On jq older than 1.7, tonumber compares beyond-2^53 issue numbers as doubles, so two absurdly large issue numbers differing in the last digit could defeat the foreign gate; on the installed jq 1.8.1 the refusal reproduces correctly (sweep-2 F3).","reasoning":"No forge approaches 2 to the 53rd issues; the baseline declares jq without a version floor and this corner does not justify one. Accepted as theoretical.","proposed_action":"accept","fix_cost":"small","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"},{"id":"CR-37","category":"in-scope-deferrable","severity":"low","summary":"A markdown filename containing a colon breaks the lint report's path:lineno split — the violation is still caught but reported without a line number (sweep-2 F4, pre-existing, reproduced).","reasoning":"No repo convention produces colon-bearing filenames; catch-without-lineno degrades gracefully. Accepted as cosmetic.","proposed_action":"accept","fix_cost":"trivial","adjacent_to_blocking":false,"feature_value":"none","requires_product_decision":false,"applied_disposition":"accept"}],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md"}} ```
Author
Owner
{"outcome":"clean","summary":"Round-3 fix-now set resolved, changeset is tests+docs+comments only (zero behavior): CR-33 regression pins in commit 48e5185 (lint self-test gains cr23/cr24 scenarios with an argument-free not-flagged control; typed-scan-family S5b-10 CRLF fold fixture; typed-scan R8 padded-issue gate pin at unit level — local-fs folder addressing intercepts padded issues end-to-end, so the gate is pinned directly, matching the S5b-9 pattern); CR-34+CR-35 doc/comment corrections in commit a9bd87d. Full ladder re-run green at final HEAD 48e5185 (8 suites + lint self-test 4/4 + lint clean); AC-10 live reads clean (issue 50: ordinals 1-7, 0 stderr bytes; issue 57: 1-4, 0 stderr bytes). Accepts CR-36/CR-37 stand as recorded in comment 1209.","findings":[],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md"}}
<!-- qa-report:v1 issue=57 skill=qa domain=code phase=fix --> ```json {"outcome":"clean","summary":"Round-3 fix-now set resolved, changeset is tests+docs+comments only (zero behavior): CR-33 regression pins in commit 48e5185 (lint self-test gains cr23/cr24 scenarios with an argument-free not-flagged control; typed-scan-family S5b-10 CRLF fold fixture; typed-scan R8 padded-issue gate pin at unit level — local-fs folder addressing intercepts padded issues end-to-end, so the gate is pinned directly, matching the S5b-9 pattern); CR-34+CR-35 doc/comment corrections in commit a9bd87d. Full ladder re-run green at final HEAD 48e5185 (8 suites + lint self-test 4/4 + lint clean); AC-10 live reads clean (issue 50: ordinals 1-7, 0 stderr bytes; issue 57: 1-4, 0 stderr bytes). Accepts CR-36/CR-37 stand as recorded in comment 1209.","findings":[],"artifacts":{"report_path":".devwork/feature-57-run-record-integrity/code-report.md"}} ```
Author
Owner
{
  "next_state": "integrating",
  "produced": [
    {
      "kind": "qa-report",
      "ref": "comment:1202",
      "note": "code validate r1 (supersedes 1200): 21 findings, 5 blocking -> 4 after CR-5 reclassification"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1203",
      "note": "code tests r1: red baseline bf4715e+3ac72a4, 11 red assertions"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1204",
      "note": "code fix r1: eed5ed2+4a21bcf+52b35b6, ladder green, fresh re-validate clean"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1205",
      "note": "e2e: skipped surface; validation run 10/10 scenarios, live-ledger reads clean"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1206",
      "note": "code validate r2 (sweep-1): CR-22 blocking read-side foreign gate + 10 dispositioned"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1207",
      "note": "code tests r2: red baseline 5e149bc (R7)"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1208",
      "note": "code fix r2: 4dfd223+a6f7dd1+1562cf2, shared _po_foreign_gate, fresh re-validate clean"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1209",
      "note": "code validate r3 (sweep-2): all core attacks held; 5 low findings, tests+docs only"
    },
    {
      "kind": "qa-report",
      "ref": "comment:1210",
      "note": "code fix r3: pins 48e5185 + docs a9bd87d; final ladder green"
    },
    {
      "kind": "qa-sweep-head",
      "ref": "commit:48e5185",
      "note": "QA-final HEAD. Sweep verdict over this tree: behavioral commits adversarially swept (fable, fresh, executed probes) and cleared; trailing docs+tests-only diff cleared by fresh review; ladder 8 suites + lint + lint self-test green; AC-10 live reads clean (issue 50 ordinals 1-7, issue 57 1-4, zero warnings); coverage check passed — all six domains hold a validate qa-report; no open decisions. UAT prerequisites card: .devwork/feature-57-run-record-integrity/test-prerequisites.md (commands verified by execution this session). Deferral: CR-15 -> issue 59 (sibling-linked)."
    }
  ],
  "findings": [
    {
      "id": "F-PO-57-5-1",
      "category": "in-scope-blocking",
      "severity": "medium",
      "summary": "Read-side foreign-record acceptance (sweep F1, reproduced twice): phase-outcome-read-all.sh has no foreign-issue gate, so a pasted anchored record with a foreign id at the dense-next ordinal is returned as part of the ledger and read-latest reports the FOREIGN record's id and next_state as the issue's current phase — while the post side (CR-3 fix) refuses. Write-wedged but read-lies: every phase-eligibility consumer computes state from another issue's record."
    },
    {
      "id": "F-PO-57-5-2",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "Lint fence-side PRIM_RE requires the pathful spelling or the prim-helper form, so a slashless scan_comments.sh call WITH arguments inside a backtick shell fence escapes both route-through halves (sweep F2, reproduced).",
      "reasoning": "The exact spelling the prose rule was widened to catch is structurally exempt inside fences; one alternation in PRIM_RE closes it.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "core",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-5-3",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "Lint prose regex requires whitespace directly after the script name, so the quoted pathful spelling (closing double-quote after .sh, the D12-conventional form) escapes the prose rule (sweep F3, reproduced).",
      "reasoning": "Tolerating an optional closing quote before the whitespace is a one-character regex widening.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "core",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-5-4",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "The lint fence extractor tracks backtick fences only; a route-through violation inside a tilde fence or indented code block is caught by nothing (sweep F3b, reproduced).",
      "reasoning": "Tilde and indented fences are not a convention this repo emits anywhere (the shared tracker is deliberately the backtick CommonMark subset per the CLAUDE.md fence rule); guarding a spelling nobody writes is dead defensiveness. Revisit only if the repo ever adopts tilde fences.",
      "proposed_action": "accept",
      "fix_cost": "small",
      "adjacent_to_blocking": false,
      "feature_value": "none",
      "requires_product_decision": false,
      "applied_disposition": "accept"
    },
    {
      "id": "F-PO-57-5-5",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "fold-candidates.sh and promotion-candidates.sh kept a fence-opener regex without CRLF tolerance in the active-folds summary capture, unlike the fixed extractors; a CRLF folded-finding record's summary silently drops from the candidate context (sweep F7).",
      "reasoning": "Same defect class the round fixed elsewhere; the fix commit touched exactly these lines. Two-character regex addition.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-5-6",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "The new foreign-issue gate compares issue segments as strings, so a zero-padded --issue value makes the issue's own records look foreign (sweep F8b).",
      "reasoning": "Numeric comparison via tonumber on both sides; applies to the post gate and the new read gate alike.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-5-7",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "The S1 step-5 pin reads mtime with a GNU-only date -r form; on BSD/macOS the suite aborts confusingly instead of the loud capability skip the suites otherwise standardize (sweep F8a).",
      "reasoning": "Extend the existing capability guard to probe date -r once at suite start; test-tier change only.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-5-8",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "An anchored record of an INVENTED kind that carries a real schema token as a substring (prefix form, e.g. extra-phase-outcome:v1) is now excluded silently by the cross-kind branch where it near-miss-warned pre-fix; the suffix form never clears the adapter prefilter in either era (sweep F4, mechanism corrected by lead probe).",
      "reasoning": "The silent-exclusion trade is deliberate: the partition cannot distinguish an invented kind from a legitimate other kind without a schema roster, which the helper tier cannot read portably; a mis-authored record of the SCANNED kind still warns (non-anchored) or refuses (anchored-unparseable). Bounded corner accepted; the roster question rides the number-59 family work if anywhere.",
      "proposed_action": "accept",
      "fix_cost": "small",
      "adjacent_to_blocking": false,
      "feature_value": "none",
      "requires_product_decision": false,
      "applied_disposition": "accept"
    },
    {
      "id": "F-PO-57-5-9",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "An EDITED record of another kind (edited_ms > created_ms) excluded by the cross-kind branch loses the loud edited-near-miss diagnostic it incidentally received pre-fix (sweep F5, reproduced).",
      "reasoning": "That diagnostic was an accident of the mislabeling bug: edit evidence for a kind belongs to that kind's own consumers, and folded-finding is a latest-wins schema, not section-8-immutable, so no tamper contract weakened. Accepted.",
      "proposed_action": "accept",
      "fix_cost": "small",
      "adjacent_to_blocking": false,
      "feature_value": "none",
      "requires_product_decision": false,
      "applied_disposition": "accept"
    },
    {
      "id": "F-PO-57-5-10",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "The two tolerant sweep wrappers also swallow a usage-error die from _typed_scan into the empty-array fallback, contradicting the stated never-swallow-bad-kind invariant (sweep F6).",
      "reasoning": "Both call sites pass hardcoded literal kinds, so the masked state is unreachable today; distinguishing exit codes in the wrapper adds branching for an impossible case. Accepted as latent; revisit if kinds ever become parameterized there.",
      "proposed_action": "accept",
      "fix_cost": "small",
      "adjacent_to_blocking": false,
      "feature_value": "none",
      "requires_product_decision": false,
      "applied_disposition": "accept"
    },
    {
      "id": "F-PO-57-5-11",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "The widened lint population misses hypothetical .bash-extension helpers and scripts/ subdirectories (sweep F8c, reproduced with planted files).",
      "reasoning": "No such files exist and the repo's conventions produce none; the population matches what the tree can contain, same reasoning as the round-1 population disposition. Accepted.",
      "proposed_action": "accept",
      "fix_cost": "trivial",
      "adjacent_to_blocking": false,
      "feature_value": "none",
      "requires_product_decision": false,
      "applied_disposition": "accept"
    },
    {
      "id": "F-PO-57-5-12",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "Round-2 fixes CR-23/24/26/27 are probe-verified but carry no automated regression pins: nothing in the suites would catch a future regression of the two lint spellings, the CRLF fold-summary capture, or the padded-issue gate compare (re-validator finding).",
      "reasoning": "Green pins only — the behaviors are fixed and verified; test-only commit by the tests-stage actor.",
      "proposed_action": "fix-now",
      "fix_cost": "small",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-5-13",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "The widened fence-side PRIM_RE now also flags a commented-out slashless call with args inside a shell fence, while the in-file comment still claims fence-comment mentions stay legal (sweep-2 F1, reproduced; zero hits in the tree).",
      "reasoning": "The tightening is desirable — a commented-out call is still copy-pasteable — so the fix is correcting the in-file comment to state the real rule, not weakening the rule.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-5-14",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "phase-outcome.md documents the read_all refusal order as parse then foreign then immutability, but the implementation enforces immutability FIRST on the raw scan: an edited foreign record yields the section-8 refusal, not the foreign one (sweep-2 F2, reproduced; the parse-vs-immutability half pre-existed the spec edit).",
      "reasoning": "Diagnostic-wording mismatch only — both refusals are loud and name the comment. One-line spec hedge stating immutability runs first on the raw scan.",
      "proposed_action": "fix-now",
      "fix_cost": "trivial",
      "adjacent_to_blocking": true,
      "feature_value": "incidental",
      "requires_product_decision": false,
      "applied_disposition": "fix-now"
    },
    {
      "id": "F-PO-57-5-15",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "On jq older than 1.7, tonumber compares beyond-2^53 issue numbers as doubles, so two absurdly large issue numbers differing in the last digit could defeat the foreign gate; on the installed jq 1.8.1 the refusal reproduces correctly (sweep-2 F3).",
      "reasoning": "No forge approaches 2 to the 53rd issues; the baseline declares jq without a version floor and this corner does not justify one. Accepted as theoretical.",
      "proposed_action": "accept",
      "fix_cost": "small",
      "adjacent_to_blocking": false,
      "feature_value": "none",
      "requires_product_decision": false,
      "applied_disposition": "accept"
    },
    {
      "id": "F-PO-57-5-16",
      "category": "in-scope-deferrable",
      "severity": "low",
      "summary": "A markdown filename containing a colon breaks the lint report's path:lineno split — the violation is still caught but reported without a line number (sweep-2 F4, pre-existing, reproduced).",
      "reasoning": "No repo convention produces colon-bearing filenames; catch-without-lineno degrades gracefully. Accepted as cosmetic.",
      "proposed_action": "accept",
      "fix_cost": "trivial",
      "adjacent_to_blocking": false,
      "feature_value": "none",
      "requires_product_decision": false,
      "applied_disposition": "accept"
    }
  ],
  "pending_decisions": [],
  "suite": {
    "source": "git",
    "sha": "48e5185a5852c786b5c93f8313c19829e98c5bf0",
    "dirty": false
  }
}
<!-- phase-outcome:v1 id=PO-57-5 skill=qa --> ```json { "next_state": "integrating", "produced": [ { "kind": "qa-report", "ref": "comment:1202", "note": "code validate r1 (supersedes 1200): 21 findings, 5 blocking -> 4 after CR-5 reclassification" }, { "kind": "qa-report", "ref": "comment:1203", "note": "code tests r1: red baseline bf4715e+3ac72a4, 11 red assertions" }, { "kind": "qa-report", "ref": "comment:1204", "note": "code fix r1: eed5ed2+4a21bcf+52b35b6, ladder green, fresh re-validate clean" }, { "kind": "qa-report", "ref": "comment:1205", "note": "e2e: skipped surface; validation run 10/10 scenarios, live-ledger reads clean" }, { "kind": "qa-report", "ref": "comment:1206", "note": "code validate r2 (sweep-1): CR-22 blocking read-side foreign gate + 10 dispositioned" }, { "kind": "qa-report", "ref": "comment:1207", "note": "code tests r2: red baseline 5e149bc (R7)" }, { "kind": "qa-report", "ref": "comment:1208", "note": "code fix r2: 4dfd223+a6f7dd1+1562cf2, shared _po_foreign_gate, fresh re-validate clean" }, { "kind": "qa-report", "ref": "comment:1209", "note": "code validate r3 (sweep-2): all core attacks held; 5 low findings, tests+docs only" }, { "kind": "qa-report", "ref": "comment:1210", "note": "code fix r3: pins 48e5185 + docs a9bd87d; final ladder green" }, { "kind": "qa-sweep-head", "ref": "commit:48e5185", "note": "QA-final HEAD. Sweep verdict over this tree: behavioral commits adversarially swept (fable, fresh, executed probes) and cleared; trailing docs+tests-only diff cleared by fresh review; ladder 8 suites + lint + lint self-test green; AC-10 live reads clean (issue 50 ordinals 1-7, issue 57 1-4, zero warnings); coverage check passed — all six domains hold a validate qa-report; no open decisions. UAT prerequisites card: .devwork/feature-57-run-record-integrity/test-prerequisites.md (commands verified by execution this session). Deferral: CR-15 -> issue 59 (sibling-linked)." } ], "findings": [ { "id": "F-PO-57-5-1", "category": "in-scope-blocking", "severity": "medium", "summary": "Read-side foreign-record acceptance (sweep F1, reproduced twice): phase-outcome-read-all.sh has no foreign-issue gate, so a pasted anchored record with a foreign id at the dense-next ordinal is returned as part of the ledger and read-latest reports the FOREIGN record's id and next_state as the issue's current phase — while the post side (CR-3 fix) refuses. Write-wedged but read-lies: every phase-eligibility consumer computes state from another issue's record." }, { "id": "F-PO-57-5-2", "category": "in-scope-deferrable", "severity": "low", "summary": "Lint fence-side PRIM_RE requires the pathful spelling or the prim-helper form, so a slashless scan_comments.sh call WITH arguments inside a backtick shell fence escapes both route-through halves (sweep F2, reproduced).", "reasoning": "The exact spelling the prose rule was widened to catch is structurally exempt inside fences; one alternation in PRIM_RE closes it.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "core", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-5-3", "category": "in-scope-deferrable", "severity": "low", "summary": "Lint prose regex requires whitespace directly after the script name, so the quoted pathful spelling (closing double-quote after .sh, the D12-conventional form) escapes the prose rule (sweep F3, reproduced).", "reasoning": "Tolerating an optional closing quote before the whitespace is a one-character regex widening.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "core", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-5-4", "category": "in-scope-deferrable", "severity": "low", "summary": "The lint fence extractor tracks backtick fences only; a route-through violation inside a tilde fence or indented code block is caught by nothing (sweep F3b, reproduced).", "reasoning": "Tilde and indented fences are not a convention this repo emits anywhere (the shared tracker is deliberately the backtick CommonMark subset per the CLAUDE.md fence rule); guarding a spelling nobody writes is dead defensiveness. Revisit only if the repo ever adopts tilde fences.", "proposed_action": "accept", "fix_cost": "small", "adjacent_to_blocking": false, "feature_value": "none", "requires_product_decision": false, "applied_disposition": "accept" }, { "id": "F-PO-57-5-5", "category": "in-scope-deferrable", "severity": "low", "summary": "fold-candidates.sh and promotion-candidates.sh kept a fence-opener regex without CRLF tolerance in the active-folds summary capture, unlike the fixed extractors; a CRLF folded-finding record's summary silently drops from the candidate context (sweep F7).", "reasoning": "Same defect class the round fixed elsewhere; the fix commit touched exactly these lines. Two-character regex addition.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-5-6", "category": "in-scope-deferrable", "severity": "low", "summary": "The new foreign-issue gate compares issue segments as strings, so a zero-padded --issue value makes the issue's own records look foreign (sweep F8b).", "reasoning": "Numeric comparison via tonumber on both sides; applies to the post gate and the new read gate alike.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-5-7", "category": "in-scope-deferrable", "severity": "low", "summary": "The S1 step-5 pin reads mtime with a GNU-only date -r form; on BSD/macOS the suite aborts confusingly instead of the loud capability skip the suites otherwise standardize (sweep F8a).", "reasoning": "Extend the existing capability guard to probe date -r once at suite start; test-tier change only.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-5-8", "category": "in-scope-deferrable", "severity": "low", "summary": "An anchored record of an INVENTED kind that carries a real schema token as a substring (prefix form, e.g. extra-phase-outcome:v1) is now excluded silently by the cross-kind branch where it near-miss-warned pre-fix; the suffix form never clears the adapter prefilter in either era (sweep F4, mechanism corrected by lead probe).", "reasoning": "The silent-exclusion trade is deliberate: the partition cannot distinguish an invented kind from a legitimate other kind without a schema roster, which the helper tier cannot read portably; a mis-authored record of the SCANNED kind still warns (non-anchored) or refuses (anchored-unparseable). Bounded corner accepted; the roster question rides the number-59 family work if anywhere.", "proposed_action": "accept", "fix_cost": "small", "adjacent_to_blocking": false, "feature_value": "none", "requires_product_decision": false, "applied_disposition": "accept" }, { "id": "F-PO-57-5-9", "category": "in-scope-deferrable", "severity": "low", "summary": "An EDITED record of another kind (edited_ms > created_ms) excluded by the cross-kind branch loses the loud edited-near-miss diagnostic it incidentally received pre-fix (sweep F5, reproduced).", "reasoning": "That diagnostic was an accident of the mislabeling bug: edit evidence for a kind belongs to that kind's own consumers, and folded-finding is a latest-wins schema, not section-8-immutable, so no tamper contract weakened. Accepted.", "proposed_action": "accept", "fix_cost": "small", "adjacent_to_blocking": false, "feature_value": "none", "requires_product_decision": false, "applied_disposition": "accept" }, { "id": "F-PO-57-5-10", "category": "in-scope-deferrable", "severity": "low", "summary": "The two tolerant sweep wrappers also swallow a usage-error die from _typed_scan into the empty-array fallback, contradicting the stated never-swallow-bad-kind invariant (sweep F6).", "reasoning": "Both call sites pass hardcoded literal kinds, so the masked state is unreachable today; distinguishing exit codes in the wrapper adds branching for an impossible case. Accepted as latent; revisit if kinds ever become parameterized there.", "proposed_action": "accept", "fix_cost": "small", "adjacent_to_blocking": false, "feature_value": "none", "requires_product_decision": false, "applied_disposition": "accept" }, { "id": "F-PO-57-5-11", "category": "in-scope-deferrable", "severity": "low", "summary": "The widened lint population misses hypothetical .bash-extension helpers and scripts/ subdirectories (sweep F8c, reproduced with planted files).", "reasoning": "No such files exist and the repo's conventions produce none; the population matches what the tree can contain, same reasoning as the round-1 population disposition. Accepted.", "proposed_action": "accept", "fix_cost": "trivial", "adjacent_to_blocking": false, "feature_value": "none", "requires_product_decision": false, "applied_disposition": "accept" }, { "id": "F-PO-57-5-12", "category": "in-scope-deferrable", "severity": "low", "summary": "Round-2 fixes CR-23/24/26/27 are probe-verified but carry no automated regression pins: nothing in the suites would catch a future regression of the two lint spellings, the CRLF fold-summary capture, or the padded-issue gate compare (re-validator finding).", "reasoning": "Green pins only — the behaviors are fixed and verified; test-only commit by the tests-stage actor.", "proposed_action": "fix-now", "fix_cost": "small", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-5-13", "category": "in-scope-deferrable", "severity": "low", "summary": "The widened fence-side PRIM_RE now also flags a commented-out slashless call with args inside a shell fence, while the in-file comment still claims fence-comment mentions stay legal (sweep-2 F1, reproduced; zero hits in the tree).", "reasoning": "The tightening is desirable — a commented-out call is still copy-pasteable — so the fix is correcting the in-file comment to state the real rule, not weakening the rule.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-5-14", "category": "in-scope-deferrable", "severity": "low", "summary": "phase-outcome.md documents the read_all refusal order as parse then foreign then immutability, but the implementation enforces immutability FIRST on the raw scan: an edited foreign record yields the section-8 refusal, not the foreign one (sweep-2 F2, reproduced; the parse-vs-immutability half pre-existed the spec edit).", "reasoning": "Diagnostic-wording mismatch only — both refusals are loud and name the comment. One-line spec hedge stating immutability runs first on the raw scan.", "proposed_action": "fix-now", "fix_cost": "trivial", "adjacent_to_blocking": true, "feature_value": "incidental", "requires_product_decision": false, "applied_disposition": "fix-now" }, { "id": "F-PO-57-5-15", "category": "in-scope-deferrable", "severity": "low", "summary": "On jq older than 1.7, tonumber compares beyond-2^53 issue numbers as doubles, so two absurdly large issue numbers differing in the last digit could defeat the foreign gate; on the installed jq 1.8.1 the refusal reproduces correctly (sweep-2 F3).", "reasoning": "No forge approaches 2 to the 53rd issues; the baseline declares jq without a version floor and this corner does not justify one. Accepted as theoretical.", "proposed_action": "accept", "fix_cost": "small", "adjacent_to_blocking": false, "feature_value": "none", "requires_product_decision": false, "applied_disposition": "accept" }, { "id": "F-PO-57-5-16", "category": "in-scope-deferrable", "severity": "low", "summary": "A markdown filename containing a colon breaks the lint report's path:lineno split — the violation is still caught but reported without a line number (sweep-2 F4, pre-existing, reproduced).", "reasoning": "No repo convention produces colon-bearing filenames; catch-without-lineno degrades gracefully. Accepted as cosmetic.", "proposed_action": "accept", "fix_cost": "trivial", "adjacent_to_blocking": false, "feature_value": "none", "requires_product_decision": false, "applied_disposition": "accept" } ], "pending_decisions": [], "suite": { "source": "git", "sha": "48e5185a5852c786b5c93f8313c19829e98c5bf0", "dirty": false } } ```
Author
Owner
{
  "integration_branch": "main",
  "feature_branch": "feature/57-run-record-integrity",
  "repos": [
    {
      "repo": ".",
      "base_sha": "f0f3348b3bcc03116ed88fac442c340624eb6553",
      "tip_sha": "48e5185a5852c786b5c93f8313c19829e98c5bf0",
      "commit_count": 23,
      "commits": [
        {
          "sha": "48e5185a5852c786b5c93f8313c19829e98c5bf0",
          "subject": "_shared/test: pin the round-2 fixes [QA-57-R3]"
        },
        {
          "sha": "a9bd87dc6eed02dd67eeaace20b520e3e0d2908f",
          "subject": "_shared/docs: state the real fence-comment rule and the read_all enforcement order [QA-57-R3]"
        },
        {
          "sha": "1562cf207edd6af09150eb8ce21fa14b573bfdd6",
          "subject": "_shared/bin: tolerate CRLF in the active-folds summary capture [QA-57-R2]"
        },
        {
          "sha": "a6f7dd14d4dcd790fed4316550b196fd4b04538d",
          "subject": "lint: catch the slashless and quoted scan-primitive spellings [QA-57-R2]"
        },
        {
          "sha": "4dfd2231b38fef6d7ff341efa1df9f5ebef01071",
          "subject": "_shared/bin: mirror the foreign-issue gate on the read side [QA-57-R2]"
        },
        {
          "sha": "5e149bca64427359df966d840c65830da3afebdd",
          "subject": "_shared/test: add read-side foreign-record red scenario [QA-57-R2]"
        },
        {
          "sha": "e2a2f4f7658a3acef813bd0178f194aa78b22344",
          "subject": "_shared/test: pin S1 step 5 — near-miss untouched by post [QA-57-R1]"
        },
        {
          "sha": "52b35b6c77a9c0661fb78cd7012c4f1e9d7b9007",
          "subject": "_shared/docs: state the real scan guarantee and scope the family claims [QA-57-R1]"
        },
        {
          "sha": "4a21bcf743e085eb6efdc73cc8f1de74768731e7",
          "subject": "lint: fix the prose-rule dedup and widen the route-through population [QA-57-R1]"
        },
        {
          "sha": "eed5ed256b4dab24a40f4ffc4738e2cda85f05e8",
          "subject": "_shared/bin: fix QA round-1 blocking findings [QA-57-R1]"
        },
        {
          "sha": "3ac72a4cbf964f144f0eb353de62c9f9ceb4537c",
          "subject": "_shared/test: add CR-1 unit-level red scenario [QA-57-R1]"
        },
        {
          "sha": "bf4715e7e8d9b6501d88d805214026061e175900",
          "subject": "_shared/test: add QA round-1 red regression scenarios [QA-57-R1]"
        },
        {
          "sha": "9c6cc88af7bde8f62b4c31193a928fe8a4b1aeaf",
          "subject": "docs: capture learnings from the #57 develop run"
        },
        {
          "sha": "b3660bff5e2efa406c37f52fadb8937287a644b4",
          "subject": "_shared/docs: state the anchored header rule and suite provenance [WU-57-3-3]"
        },
        {
          "sha": "c3b2e20927108488a838801899c84e51b15224c3",
          "subject": "lint: enforce the typed-scan route-through rule [WU-57-3-3]"
        },
        {
          "sha": "8652ce1d9d02df20f31db135d65ec5f87a93719f",
          "subject": "requirements: re-point Step D3 at the skill-facing comments-scan helper [WU-57-3-3]"
        },
        {
          "sha": "da73e47a0a37e7c8cd49432ae02a62a276319beb",
          "subject": "_shared/bin: route satellite consumers through _typed_scan [WU-57-3-2]"
        },
        {
          "sha": "6c5887eea8eeba705907491a07a666a179c3ad63",
          "subject": "_shared/test: add red-first typed-scan family-consumer suite [WU-57-3-2]"
        },
        {
          "sha": "9d70fe6d01ad7b61ddb8ed3d8984b79d7f8651b6",
          "subject": "_shared/test: fix boolean-false jq default in provenance S7 check [WU-57-3-1]"
        },
        {
          "sha": "051564936552871754ae4cedefc51db5e222d664",
          "subject": "_shared/bin: add _typed_scan + _suite_provenance and route the PO chain [WU-57-3-1]"
        },
        {
          "sha": "c79daca74ed288681a6cc28bad0dbf913ab1ab8e",
          "subject": "_shared/test: add red-first typed-scan and provenance suites [WU-57-3-1]"
        },
        {
          "sha": "1adf7a90e6d834186d6c5f561d6e9bce598450f0",
          "subject": "docs(run-record-integrity): test plan"
        },
        {
          "sha": "5f527766188387122d6d505620800b23cd4b32ba",
          "subject": "docs(run-record-integrity): software requirements"
        }
      ],
      "compare_url": null,
      "pushed": true,
      "remote_url": "ssh://git@forge-devwork/jbr870/devwork-skills.git"
    }
  ]
}
<!-- code-bundle:v1 issue=57 skill=integrate --> ```json { "integration_branch": "main", "feature_branch": "feature/57-run-record-integrity", "repos": [ { "repo": ".", "base_sha": "f0f3348b3bcc03116ed88fac442c340624eb6553", "tip_sha": "48e5185a5852c786b5c93f8313c19829e98c5bf0", "commit_count": 23, "commits": [ { "sha": "48e5185a5852c786b5c93f8313c19829e98c5bf0", "subject": "_shared/test: pin the round-2 fixes [QA-57-R3]" }, { "sha": "a9bd87dc6eed02dd67eeaace20b520e3e0d2908f", "subject": "_shared/docs: state the real fence-comment rule and the read_all enforcement order [QA-57-R3]" }, { "sha": "1562cf207edd6af09150eb8ce21fa14b573bfdd6", "subject": "_shared/bin: tolerate CRLF in the active-folds summary capture [QA-57-R2]" }, { "sha": "a6f7dd14d4dcd790fed4316550b196fd4b04538d", "subject": "lint: catch the slashless and quoted scan-primitive spellings [QA-57-R2]" }, { "sha": "4dfd2231b38fef6d7ff341efa1df9f5ebef01071", "subject": "_shared/bin: mirror the foreign-issue gate on the read side [QA-57-R2]" }, { "sha": "5e149bca64427359df966d840c65830da3afebdd", "subject": "_shared/test: add read-side foreign-record red scenario [QA-57-R2]" }, { "sha": "e2a2f4f7658a3acef813bd0178f194aa78b22344", "subject": "_shared/test: pin S1 step 5 — near-miss untouched by post [QA-57-R1]" }, { "sha": "52b35b6c77a9c0661fb78cd7012c4f1e9d7b9007", "subject": "_shared/docs: state the real scan guarantee and scope the family claims [QA-57-R1]" }, { "sha": "4a21bcf743e085eb6efdc73cc8f1de74768731e7", "subject": "lint: fix the prose-rule dedup and widen the route-through population [QA-57-R1]" }, { "sha": "eed5ed256b4dab24a40f4ffc4738e2cda85f05e8", "subject": "_shared/bin: fix QA round-1 blocking findings [QA-57-R1]" }, { "sha": "3ac72a4cbf964f144f0eb353de62c9f9ceb4537c", "subject": "_shared/test: add CR-1 unit-level red scenario [QA-57-R1]" }, { "sha": "bf4715e7e8d9b6501d88d805214026061e175900", "subject": "_shared/test: add QA round-1 red regression scenarios [QA-57-R1]" }, { "sha": "9c6cc88af7bde8f62b4c31193a928fe8a4b1aeaf", "subject": "docs: capture learnings from the #57 develop run" }, { "sha": "b3660bff5e2efa406c37f52fadb8937287a644b4", "subject": "_shared/docs: state the anchored header rule and suite provenance [WU-57-3-3]" }, { "sha": "c3b2e20927108488a838801899c84e51b15224c3", "subject": "lint: enforce the typed-scan route-through rule [WU-57-3-3]" }, { "sha": "8652ce1d9d02df20f31db135d65ec5f87a93719f", "subject": "requirements: re-point Step D3 at the skill-facing comments-scan helper [WU-57-3-3]" }, { "sha": "da73e47a0a37e7c8cd49432ae02a62a276319beb", "subject": "_shared/bin: route satellite consumers through _typed_scan [WU-57-3-2]" }, { "sha": "6c5887eea8eeba705907491a07a666a179c3ad63", "subject": "_shared/test: add red-first typed-scan family-consumer suite [WU-57-3-2]" }, { "sha": "9d70fe6d01ad7b61ddb8ed3d8984b79d7f8651b6", "subject": "_shared/test: fix boolean-false jq default in provenance S7 check [WU-57-3-1]" }, { "sha": "051564936552871754ae4cedefc51db5e222d664", "subject": "_shared/bin: add _typed_scan + _suite_provenance and route the PO chain [WU-57-3-1]" }, { "sha": "c79daca74ed288681a6cc28bad0dbf913ab1ab8e", "subject": "_shared/test: add red-first typed-scan and provenance suites [WU-57-3-1]" }, { "sha": "1adf7a90e6d834186d6c5f561d6e9bce598450f0", "subject": "docs(run-record-integrity): test plan" }, { "sha": "5f527766188387122d6d505620800b23cd4b32ba", "subject": "docs(run-record-integrity): software requirements" } ], "compare_url": null, "pushed": true, "remote_url": "ssh://git@forge-devwork/jbr870/devwork-skills.git" } ] } ```
Author
Owner
{
  "next_state": "uat",
  "produced": [
    {
      "kind": "code-bundle",
      "ref": "comment:1223",
      "summary": "Merged 23 commit(s) to main across 1 repo(s), pushed to origin"
    }
  ],
  "findings": [],
  "pending_decisions": [
    {
      "id": "D-PO-57-6-1",
      "type": "approval",
      "blocking": true,
      "question": "UAT: does the feature work, and is it what we wanted?",
      "options": [
        "approve",
        "reject"
      ],
      "recommended": "approve",
      "reasoning": "Merged to main and pushed (f0f3348..48e5185, 23 commits, fast-forward); integration verification green (lint-conventions + lint self-test + all 8 shared suites). No integration deploy — this repo ships no runtime service; UAT runs locally per the UAT card (.devwork/feature-57-run-record-integrity/test-prerequisites.md): review the merged skill text on main and walk the scenarios. approve = a second /dev:integrate run posts accepted (queued for release) — it does NOT deploy; /dev:promote ships the accepted queue on your own cadence. reject = record what is wrong as findings; the UAT return path (qa-playbook.md §9) classifies how far back to re-enter — the feature branch is already cleaned up, so rework starts from a branch recreated off the current main tip."
    }
  ],
  "suite": {
    "source": "git",
    "sha": "48e5185a5852c786b5c93f8313c19829e98c5bf0",
    "dirty": false
  }
}
<!-- phase-outcome:v1 id=PO-57-6 skill=integrate --> ```json { "next_state": "uat", "produced": [ { "kind": "code-bundle", "ref": "comment:1223", "summary": "Merged 23 commit(s) to main across 1 repo(s), pushed to origin" } ], "findings": [], "pending_decisions": [ { "id": "D-PO-57-6-1", "type": "approval", "blocking": true, "question": "UAT: does the feature work, and is it what we wanted?", "options": [ "approve", "reject" ], "recommended": "approve", "reasoning": "Merged to main and pushed (f0f3348..48e5185, 23 commits, fast-forward); integration verification green (lint-conventions + lint self-test + all 8 shared suites). No integration deploy — this repo ships no runtime service; UAT runs locally per the UAT card (.devwork/feature-57-run-record-integrity/test-prerequisites.md): review the merged skill text on main and walk the scenarios. approve = a second /dev:integrate run posts accepted (queued for release) — it does NOT deploy; /dev:promote ships the accepted queue on your own cadence. reject = record what is wrong as findings; the UAT return path (qa-playbook.md §9) classifies how far back to re-enter — the feature branch is already cleaned up, so rework starts from a branch recreated off the current main tip." } ], "suite": { "source": "git", "sha": "48e5185a5852c786b5c93f8313c19829e98c5bf0", "dirty": false } } ```
Author
Owner
{
  "status": "resolved",
  "chosen_option": "approve",
  "rationale": "Human approval (operator, 2026-08-25). Accepted on the QA record (10/10 test-plan scenarios green, 3-round adversarial sweep, PO-57-5) plus the live dogfood during integration: the new writers/readers posted the code-bundle (comment 1223) and PO-57-6 (comment 1224) against the real #57 ledger correctly. No deployed surface to walk — per uat.url_source, review is the merged skill text on main."
}
<!-- decision-resolution:v1 ref=D-PO-57-6-1 --> ```json { "status": "resolved", "chosen_option": "approve", "rationale": "Human approval (operator, 2026-08-25). Accepted on the QA record (10/10 test-plan scenarios green, 3-round adversarial sweep, PO-57-5) plus the live dogfood during integration: the new writers/readers posted the code-bundle (comment 1223) and PO-57-6 (comment 1224) against the real #57 ledger correctly. No deployed surface to walk — per uat.url_source, review is the merged skill text on main." } ```
Author
Owner
{
  "next_state": "accepted",
  "produced": [
    {
      "kind": "code-bundle",
      "ref": "comment:1223",
      "summary": "Code bundle for the merge recorded on PO-57-6 (f0f3348..48e5185, 23 commits to main); acceptance run — no new git work"
    }
  ],
  "findings": [],
  "pending_decisions": [],
  "suite": {
    "source": "git",
    "sha": "48e5185a5852c786b5c93f8313c19829e98c5bf0",
    "dirty": false
  }
}
<!-- phase-outcome:v1 id=PO-57-7 skill=integrate --> ```json { "next_state": "accepted", "produced": [ { "kind": "code-bundle", "ref": "comment:1223", "summary": "Code bundle for the merge recorded on PO-57-6 (f0f3348..48e5185, 23 commits to main); acceptance run — no new git work" } ], "findings": [], "pending_decisions": [], "suite": { "source": "git", "sha": "48e5185a5852c786b5c93f8313c19829e98c5bf0", "dirty": false } } ```
Author
Owner
{
  "tag": "dev--v0.2.0",
  "status": "tag-only",
  "repos": [
    {
      "repo": ".",
      "tag": "dev--v0.2.0",
      "target_sha": "15650c0a4a5b49dd8bedfac5292ef49d9ba4d29c"
    }
  ]
}
<!-- release-link:v1 issue=57 skill=promote tag=dev--v0.2.0 --> ```json { "tag": "dev--v0.2.0", "status": "tag-only", "repos": [ { "repo": ".", "tag": "dev--v0.2.0", "target_sha": "15650c0a4a5b49dd8bedfac5292ef49d9ba4d29c" } ] } ```
Author
Owner
{
  "next_state": "deployed",
  "produced": [
    {
      "kind": "release-link",
      "ref": "comment:1231",
      "summary": "Shipped in dev--v0.2.0 (release commit 15650c0, tag-only: tags live on the github remote; deploy check PASS)"
    }
  ],
  "findings": [],
  "pending_decisions": [],
  "suite": {
    "source": "git",
    "sha": "ca0669b779b92a8cb57689d52c2fc12ee372ac12",
    "dirty": false
  }
}
<!-- phase-outcome:v1 id=PO-57-8 skill=promote --> ```json { "next_state": "deployed", "produced": [ { "kind": "release-link", "ref": "comment:1231", "summary": "Shipped in dev--v0.2.0 (release commit 15650c0, tag-only: tags live on the github remote; deploy check PASS)" } ], "findings": [], "pending_decisions": [], "suite": { "source": "git", "sha": "ca0669b779b92a8cb57689d52c2fc12ee372ac12", "dirty": false } } ```
jbr870 2026-08-25 07:29:24 +00:00
Sign in to join this conversation.
No description provided.