Task — engineering-spec@1

Corpus hygiene - UNREVIEWED fork, module-case lint, stuck-WIP triage

doneTASK-IMP-139
module improvement · class product · priority p1 · created 2026-07-23 · shipped null
depends on none · blocks none

TASK-IMP-139: Corpus hygiene - UNREVIEWED disposition, module-case lint, stuck-WIP triage

Summary

Three corpus-integrity debts: 170 non-draft specs (151 of them done) still carry the # UNREVIEWED markers the 2026-07-14 migration attached to compliance fields with the instruction "a human MUST confirm before this task leaves draft" - so the corpus's ai_authorship and eu_ai_act_risk_class claims on shipped work are unconfirmed and every one of those files fails FM-112 if linted; 251 specs carry mixed-case module: values with no lint rule guarding the field; and 12 tasks sit in implementing - eleven since mid-May - with every forward mid-state otherwise unoccupied, evidence the full lifecycle is rarely exercised and stale WIP accumulates silently. This task dispositions the markers under an explicit operator fork (bulk-clear with recorded verdict vs re-audit wave), normalizes module case and adds the missing lint rule, and triages the 12 via task-reconcile with per-task operator verdicts.

Implementation note: two operator gates inside this task

Gate 1 - the UNREVIEWED fork (do not pre-empt). The marker disposition is a process decision the operator must make explicitly, recorded as a dated source_decisions entry on this spec before any marker is touched:

Gate 2 - the stuck-WIP triage (per-task verdicts). Each of the 12 implementing tasks gets a task-reconcile evidence report (what the ladder says actually shipped vs the status), and the operator records ONE verdict per task - resume / route back per STATUS-REFERENCE §1.3 / on_hold - via the standard override path (which emits memory.status_overridden once TASK-CUO-303 lands, or its documented equivalent before). This task performs no status flip on its own authority; TASK-APP-001 (July-created) is expected to be a legitimate resume, which is exactly why the verdicts are per-task, not batch.

Problem

Verified first-hand 2026-07-23:

  1. Compliance fields unconfirmed at scale (audit H2). The migration marked every auto-set ai_authorship / eu_ai_act_risk_class with # UNREVIEWED ... a human MUST confirm before this task leaves draft. 170 files left draft anyway - 151 all the way to done. FM-112 exists precisely to stop this (error, marker past draft) but only fires when the linter runs against a file, which happens at authoring/audit time, not retroactively over the corpus. The EU-AI-Act-adjacent fields on shipped work are, today, machine guesses wearing confirmed clothes.
  2. module: field ungoverned. 251 files say module: AUTH-style uppercase inside lowercase folders. Nothing breaks loudly (the regenerator groups by folder), but the field feeds the status hub and any future module-scoped tooling, and task-lint has NO rule for it - the only frontmatter field of its kind with zero validation.
  3. Stale WIP, invisible. 11 of 12 implementing tasks are ~10 weeks old with zero forward mid-states occupied corpus-wide; either work stalled silently or statuses were never advanced. Both are lifecycle-integrity failures, and nothing surfaces them (the stuck-WIP sentinel is a 1.4.x roadmap item; this triage is the manual pass that clears today's backlog of doubt).

Proposed Solution

Markers: enumerate the 170 files mechanically (the enumeration script ships in the test suite so review sees the exact set); halt at Gate 1 for the fork verdict; execute the chosen branch; either way the end state is zero # UNREVIEWED markers on non-draft specs whose fields carry a recorded confirmation trail, and FM-112 keeps guarding the future. Module case: one mechanical commit lowercasing the 251 module: values; add a task-lint rule (next free FM id per the rubric's numbering, documented in RUBRIC.md in the same change per the lint-and-rubric-ship-together discipline) asserting module: is lowercase AND equals the containing docs/tasks/<module>/ folder name; regen + status hub verified unchanged (the field now matches what the folder always said). Triage: run task-reconcile per stuck task, attach each evidence report to the triage record, halt at Gate 2 for per-task verdicts, and apply exactly the operator's verdicts through the standard paths. Guard: scripts/tests/test_corpus_hygiene.sh pins the end states - zero non-draft markers, zero module-case mismatches, lint rule fires on fixtures - and rides run_all.sh's glob so regression is loud.

Alternatives Considered

Success Metrics

Scope

In scope: the marker enumeration + fork execution, the 251-file normalization, the lint rule + rubric documentation, the 12 reconcile runs + verdict application, the hygiene test suite, CHANGELOG.

Out of scope / Non-Goals

Dependencies

None blocking. Related: TASK-IMP-100/101 (task-reconcile, the Gate-2 instrument), TASK-IMP-108 (status semantics + FM-115/116 the reconcile verdicts may set), TASK-IMP-117 (the FM-001 conformance precedent for corpus-wide frontmatter sweeps - the module-case normalization follows its mechanical-commit pattern).

AI Authorship Disclosure

1. Description (normative)

2. Acceptance criteria

3. Edge cases

Audit

§1 — Verdict summary

Seven §1 clauses, seven ACs, seven edge cases. Like TASK-IMP-138, this task carries operator gates INSIDE its implementation (the UNREVIEWED fork, the per-task triage verdicts) and the audit's first job was verifying the spec forces those gates mechanically rather than trusting the implementer's manners. Second job: measurement honesty - the audit-report figures (167 files, 148 done, "6 MCP") were all corrected to fresh measurements (170, 151, 5 MCP + 6 OBS + 1 APP) with the discrepancies recorded in source_pages.

§2 — Findings (all resolved)

ISS-001 — the fork could be pre-empted by an implementer with an opinion

"Operator decides the marker disposition" without an ordering clause lets a queue-picked implementer bulk-clear first and record a verdict after. Resolved: clause 1.1 makes the dated verdict + attached enumeration a MUST-precondition to any marker-touching commit; AC 1 verifies precedence via spec + git history at review.

ISS-002 — the verdict would have covered a description, not a set

"Clear the ~170 files" approves a moving target; files accrue markers between authoring and implementation (three did between audit and authoring). Resolved: 1.1 requires attaching the re-derived enumeration to the verdict; the edge case names the re-derivation explicitly.

ISS-003 — batch-verdict temptation on the 12 stuck tasks

The plan's phrasing ("triage the 12") invites one collective route-back - and the measured set contains TASK-APP-001, created nine days ago, plausibly live. A batch verdict would route back genuinely in-flight work. Resolved: clause 1.5 + the implementation note require per-task verdicts; Alternatives records why batch was rejected; "resume unchanged" is named as a legitimate verdict so the record can be complete without forced motion.

ISS-004 — silent-status-change risk in the triage commit

A triage that flips statuses "per verdicts" needs a mechanical check that no OTHER status moved in the same commit. Resolved: AC 5 asserts the 1:1 verdict-to-change mapping AND that no unlisted task's status changed.

ISS-005 — normalization vs marker-sweep ordering was undefined

Both sweeps touch overlapping files; if the gated marker work blocks the mechanical case fix, 251 files wait on a fork that has nothing to do with them. Resolved: edge case pins normalization-first (mechanical, ungated) with markers second (gated) - the fork never blocks the mechanical half.

ISS-006 — lint rule needed the folder-equality half, not just lowercase

module: auth inside docs/tasks/improvement/ is lowercase and still wrong; the real invariant is field == folder. Resolved: clause 1.3/1.4 pin both halves; AC 4 tests a folder-mismatch fixture separately from the case fixture.

ISS-007 — case-insensitive-filesystem hazard checked and discharged

A case sweep that renamed FILES would hit APFS case-folding hazards. Verified: only frontmatter VALUES change; folders were already lowercase. Resolved as an explicit edge case so the implementer doesn't "helpfully" rename anything.

§3 — TRACE-006 semantic sufficiency (per clause)

ClauseVerb demandCited test assertsVerdict
1.1 MUST NOT begin markers until verdict + enumeration recordedverdict precedence + attached setAC 1: asserts both (review-time inspection + t01 presence)sufficient
1.2 zero non-draft markers + confirmation trailempty census + branch-appropriate trailAC 2: asserts both halvessufficient
1.3 MUST lowercase + equal folderzero mismatches corpus-wideAC 3: asserts the full invariantsufficient after revision (ISS-006)
1.4 MUST add error rule + document in RUBRICfires on two fixture classes, passes conformant, rubric documents idAC 4: asserts all foursufficient
1.5 MUST reconcile all 12; changes only per verdict12 reports + per-task verdicts + 1:1 mapping + no stray changesAC 5: asserts all foursufficient after revision (ISS-004)
1.6 MUST assert end states + idempotence, glob-registeredsuite green under discovery + byte-stable regenAC 6: asserts bothsufficient
1.7 MUST record branch/count/rule/tallyfour substrings in top entryAC 7: asserts all foursufficient

§4 — Resolution

Seven findings - three gate-integrity, one measurement-truth, three material - all resolved in the audited revision. Score = 10/10.

Status transition draft -> ready_to_implement is authorised by this verdict per STATUS-REFERENCE.md §1.1 - noting that clauses 1.1 and 1.5 embed the two operator gates inside implementation, per the plan's approval boundary. The two human-acceptance gates in /ship-tasks are unchanged.


End of TASK-IMP-139 audit.