diff --git a/artifacts/.mock_concurrent_mma_call_count b/artifacts/.mock_concurrent_mma_call_count new file mode 100644 index 00000000..3e932fe8 --- /dev/null +++ b/artifacts/.mock_concurrent_mma_call_count @@ -0,0 +1 @@ +34 \ No newline at end of file diff --git a/conductor/directives/atomic_per_task_commits/meta.md b/conductor/directives/atomic_per_task_commits/meta.md new file mode 100644 index 00000000..d5c06419 --- /dev/null +++ b/conductor/directives/atomic_per_task_commits/meta.md @@ -0,0 +1,10 @@ +# atomic_per_task_commits + +## v1 + +**Why this iteration:** Lifted verbatim from `conductor/workflow.md` §"Standard Task Workflow" step 9 (line 112) + `AGENTS.md` "Critical Anti-Patterns" (line 55). This is the baseline encoding — the imperative-bullet style currently in production. +Future variants will test alternative encodings (rationale-first, before/after) against this baseline. +**Source:** `conductor/workflow.md:112 + AGENTS.md:55` + +--- +**Lifted:** 2026-07-02 (Phase 1 harvest by Tier 3 worker; user directive 2026-07-02 moved the header into this meta file) diff --git a/conductor/directives/atomic_per_task_commits/v1.md b/conductor/directives/atomic_per_task_commits/v1.md index 7cedd46f..98913084 100644 --- a/conductor/directives/atomic_per_task_commits/v1.md +++ b/conductor/directives/atomic_per_task_commits/v1.md @@ -1,12 +1,3 @@ -# atomic_per_task_commits — v1 - -**Why this iteration:** Lifted verbatim from `conductor/workflow.md` §"Standard Task Workflow" step 9 (line 112) + `AGENTS.md` "Critical Anti-Patterns" (line 55). This is the baseline encoding — the imperative-bullet style currently in production. -Future variants will test alternative encodings (rationale-first, before/after) against this baseline. - -**Source:** `conductor/workflow.md:112 + AGENTS.md:55` - ---- - From `conductor/workflow.md` §"Standard Task Workflow" step 9: - **CRITICAL - ATOMIC PER-TASK COMMITS**: You MUST commit your changes immediately after completing and verifying a single task. Do NOT move on to the next task in the plan without committing the current one. This ensures precise tracking and safe rollback points. - Stage all code changes related to the task. diff --git a/conductor/directives/ban_any_type/meta.md b/conductor/directives/ban_any_type/meta.md new file mode 100644 index 00000000..be01f85f --- /dev/null +++ b/conductor/directives/ban_any_type/meta.md @@ -0,0 +1,11 @@ +# ban_any_type + +## v1 + +**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.2 (lines 267-277). +This is the baseline encoding — the imperative-ban style currently in production. +Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline. +**Source:** `conductor/code_styleguides/python.md:267-277` + +--- +**Lifted:** 2026-07-02 (Phase 1 harvest by Tier 3 worker; user directive 2026-07-02 moved the header into this meta file) diff --git a/conductor/directives/ban_any_type/v1.md b/conductor/directives/ban_any_type/v1.md index 597d095f..cca1376a 100644 --- a/conductor/directives/ban_any_type/v1.md +++ b/conductor/directives/ban_any_type/v1.md @@ -1,13 +1,3 @@ -# ban_any_type — v1 - -**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.2 (lines 267-277). -This is the baseline encoding — the imperative-ban style currently in production. -Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline. - -**Source:** `conductor/code_styleguides/python.md:267-277` - ---- - ### 17.2 Banned: `Any` ```python diff --git a/conductor/directives/ban_arbitrary_core_mocking/meta.md b/conductor/directives/ban_arbitrary_core_mocking/meta.md new file mode 100644 index 00000000..7d5ab071 --- /dev/null +++ b/conductor/directives/ban_arbitrary_core_mocking/meta.md @@ -0,0 +1,10 @@ +# ban_arbitrary_core_mocking + +## v1 + +**Why this iteration:** Lifted verbatim from `conductor/workflow.md` §"Structural Testing Contract" #1 (line 262) + `AGENTS.md` §"Critical Anti-Patterns" (line 53). This is the baseline encoding — the imperative-bullet style currently in production. +Future variants will test alternative encodings (rationale-first, before/after) against this baseline. +**Source:** `conductor/workflow.md:262 + AGENTS.md:53` + +--- +**Lifted:** 2026-07-02 (Phase 1 harvest by Tier 3 worker; user directive 2026-07-02 moved the header into this meta file) diff --git a/conductor/directives/ban_arbitrary_core_mocking/v1.md b/conductor/directives/ban_arbitrary_core_mocking/v1.md index 3237f357..6f025a08 100644 --- a/conductor/directives/ban_arbitrary_core_mocking/v1.md +++ b/conductor/directives/ban_arbitrary_core_mocking/v1.md @@ -1,12 +1,3 @@ -# ban_arbitrary_core_mocking — v1 - -**Why this iteration:** Lifted verbatim from `conductor/workflow.md` §"Structural Testing Contract" #1 (line 262) + `AGENTS.md` §"Critical Anti-Patterns" (line 53). This is the baseline encoding — the imperative-bullet style currently in production. -Future variants will test alternative encodings (rationale-first, before/after) against this baseline. - -**Source:** `conductor/workflow.md:262 + AGENTS.md:53` - ---- - From `conductor/workflow.md` §"Structural Testing Contract": 1. **Ban on Arbitrary Core Mocking:** Tier 3 workers are strictly forbidden from using `unittest.mock.patch` to bypass or stub core infrastructure (e.g., event queues, `ai_client` internals, threading primitives) unless explicitly authorized by the Tier 2 Tech Lead for a specific boundary test. 2. **`live_gui` Standard:** All integration and end-to-end testing must utilize the `live_gui` fixture to interact with a real instance of the application via the Hook API. Bypassing the hook server to directly mutate GUI state in tests is prohibited. diff --git a/conductor/directives/ban_day_estimates/meta.md b/conductor/directives/ban_day_estimates/meta.md new file mode 100644 index 00000000..dbda89da --- /dev/null +++ b/conductor/directives/ban_day_estimates/meta.md @@ -0,0 +1,10 @@ +# ban_day_estimates + +## v1 + +**Why this iteration:** Lifted verbatim from `AGENTS.md` "Critical Anti-Patterns" HARD BAN (line 61) + `conductor/workflow.md` §"Tier 1 Track Initialization Rules" §1 (lines 605-625). This is the baseline encoding — the imperative-bullet-with-rationale style currently in production. +Future variants will test alternative encodings (tabular, replacement-patterns-table) against this baseline. +**Source:** `AGENTS.md:61 + workflow.md:605-625` + +--- +**Lifted:** 2026-07-02 (Phase 1 harvest by Tier 3 worker; user directive 2026-07-02 moved the header into this meta file) diff --git a/conductor/directives/ban_day_estimates/v1.md b/conductor/directives/ban_day_estimates/v1.md index 7b553a1a..c1c31bd3 100644 --- a/conductor/directives/ban_day_estimates/v1.md +++ b/conductor/directives/ban_day_estimates/v1.md @@ -1,12 +1,3 @@ -# ban_day_estimates — v1 - -**Why this iteration:** Lifted verbatim from `AGENTS.md` "Critical Anti-Patterns" HARD BAN (line 61) + `conductor/workflow.md` §"Tier 1 Track Initialization Rules" §1 (lines 605-625). This is the baseline encoding — the imperative-bullet-with-rationale style currently in production. -Future variants will test alternative encodings (tabular, replacement-patterns-table) against this baseline. - -**Source:** `AGENTS.md:61 + workflow.md:605-625` - ---- - From `AGENTS.md` "Critical Anti-Patterns": - **HARD BAN: Day estimates in track artifacts (Tier 1).** Do NOT include day / hour / minute estimates in spec.md, plan.md, metadata.json, or any other track artifact. Day estimates are inaccurate noise; Tier 2 capacity is bounded by attention, not time. Measure effort by **scope** (N files, M sites, N tasks). The user / Tier 2 agent decides the actual pacing. See `conductor/workflow.md` §"Tier 1 Track Initialization Rules" for the full rule, replacement patterns, and rationale. (Added 2026-06-16 per user feedback: "Day estimates are inaccurate. Tier-2s can only do so much in a single track and there is no way in hell its going to be 'DAYS'.") diff --git a/conductor/directives/ban_dict_any/meta.md b/conductor/directives/ban_dict_any/meta.md new file mode 100644 index 00000000..773e02cc --- /dev/null +++ b/conductor/directives/ban_dict_any/meta.md @@ -0,0 +1,11 @@ +# ban_dict_any + +## v1 + +**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.1 (lines 247-265). +This is the baseline encoding — the imperative-ban style currently in production. +Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline. +**Source:** `conductor/code_styleguides/python.md:247-265` + +--- +**Lifted:** 2026-07-02 (Phase 1 harvest by Tier 3 worker; user directive 2026-07-02 moved the header into this meta file) diff --git a/conductor/directives/ban_dict_any/v1.md b/conductor/directives/ban_dict_any/v1.md index 78c84647..0b1b89bb 100644 --- a/conductor/directives/ban_dict_any/v1.md +++ b/conductor/directives/ban_dict_any/v1.md @@ -1,13 +1,3 @@ -# ban_dict_any — v1 - -**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.1 (lines 247-265). -This is the baseline encoding — the imperative-ban style currently in production. -Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline. - -**Source:** `conductor/code_styleguides/python.md:247-265` - ---- - ### 17.1 Banned: `dict[str, Any]` ```python diff --git a/conductor/directives/ban_dict_get_on_known_fields/meta.md b/conductor/directives/ban_dict_get_on_known_fields/meta.md new file mode 100644 index 00000000..96422abf --- /dev/null +++ b/conductor/directives/ban_dict_get_on_known_fields/meta.md @@ -0,0 +1,11 @@ +# ban_dict_get_on_known_fields + +## v1 + +**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.6 (lines 340-350). +This is the baseline encoding — the imperative-ban style currently in production. +Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline. +**Source:** `conductor/code_styleguides/python.md:340-350` + +--- +**Lifted:** 2026-07-02 (Phase 1 harvest by Tier 3 worker; user directive 2026-07-02 moved the header into this meta file) diff --git a/conductor/directives/ban_dict_get_on_known_fields/v1.md b/conductor/directives/ban_dict_get_on_known_fields/v1.md index 6ecfabb0..1af00919 100644 --- a/conductor/directives/ban_dict_get_on_known_fields/v1.md +++ b/conductor/directives/ban_dict_get_on_known_fields/v1.md @@ -1,13 +1,3 @@ -# ban_dict_get_on_known_fields — v1 - -**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.6 (lines 340-350). -This is the baseline encoding — the imperative-ban style currently in production. -Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline. - -**Source:** `conductor/code_styleguides/python.md:340-350` - ---- - ### 17.6 Banned: `.get('field', default)` on a `dict[str, Any]` ```python diff --git a/conductor/directives/ban_getattr_dispatch/meta.md b/conductor/directives/ban_getattr_dispatch/meta.md new file mode 100644 index 00000000..bf693ca1 --- /dev/null +++ b/conductor/directives/ban_getattr_dispatch/meta.md @@ -0,0 +1,11 @@ +# ban_getattr_dispatch + +## v1 + +**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.5 (lines 328-338). +This is the baseline encoding — the imperative-ban style currently in production. +Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline. +**Source:** `conductor/code_styleguides/python.md:328-338` + +--- +**Lifted:** 2026-07-02 (Phase 1 harvest by Tier 3 worker; user directive 2026-07-02 moved the header into this meta file) diff --git a/conductor/directives/ban_getattr_dispatch/v1.md b/conductor/directives/ban_getattr_dispatch/v1.md index dbae5a05..3917a33f 100644 --- a/conductor/directives/ban_getattr_dispatch/v1.md +++ b/conductor/directives/ban_getattr_dispatch/v1.md @@ -1,13 +1,3 @@ -# ban_getattr_dispatch — v1 - -**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.5 (lines 328-338). -This is the baseline encoding — the imperative-ban style currently in production. -Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline. - -**Source:** `conductor/code_styleguides/python.md:328-338` - ---- - ### 17.5 Banned: `getattr(x, 'field', default)` for type dispatch ```python diff --git a/conductor/directives/ban_hasattr_dispatch/meta.md b/conductor/directives/ban_hasattr_dispatch/meta.md new file mode 100644 index 00000000..ccfb855e --- /dev/null +++ b/conductor/directives/ban_hasattr_dispatch/meta.md @@ -0,0 +1,11 @@ +# ban_hasattr_dispatch + +## v1 + +**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.4 (lines 300-326). +This is the baseline encoding — the imperative-ban style currently in production. +Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline. +**Source:** `conductor/code_styleguides/python.md:300-326` + +--- +**Lifted:** 2026-07-02 (Phase 1 harvest by Tier 3 worker; user directive 2026-07-02 moved the header into this meta file) diff --git a/conductor/directives/ban_hasattr_dispatch/v1.md b/conductor/directives/ban_hasattr_dispatch/v1.md index 8ee288a7..2d529c34 100644 --- a/conductor/directives/ban_hasattr_dispatch/v1.md +++ b/conductor/directives/ban_hasattr_dispatch/v1.md @@ -1,13 +1,3 @@ -# ban_hasattr_dispatch — v1 - -**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.4 (lines 300-326). -This is the baseline encoding — the imperative-ban style currently in production. -Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline. - -**Source:** `conductor/code_styleguides/python.md:300-326` - ---- - ### 17.4 Banned: `hasattr()` for entity type dispatch ```python diff --git a/docs/superpowers/plans/2026-07-01-chronology-v2-redo.md b/docs/superpowers/plans/2026-07-01-chronology-v2-redo.md new file mode 100644 index 00000000..54b82a09 --- /dev/null +++ b/docs/superpowers/plans/2026-07-01-chronology-v2-redo.md @@ -0,0 +1,1629 @@ +# Chronology v2 Redo Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the broken v1 `conductor/chronology.md` (167/216 rows with wrong status, stale-metadata classifier) with a correct v2 that uses git-history evidence, close out the stuck `chronology_20260619` track, de-gunk `conductor/tracks.md`, and add a maintenance rule to `conductor/workflow.md` so the chronology doesn't desync again. + +**Architecture:** Rewrite `scripts/audit/generate_chronology.py` with a 4-tier evidence-priority classifier (override signals > git work-commits > directory location > fallback) returning `(status, confidence, reason)`. Add `scripts/audit/chronology_quality_gate.py` with 4 checks + `--strict` mode. Regenerate `conductor/chronology.md` from the current filesystem. Restructure `conductor/tracks.md` to active-queue + standby + pointer only. Add a "Chronology Maintenance" section to `conductor/workflow.md`. + +**Tech Stack:** Python 3.11+, stdlib `pathlib`/`json`/`re`/`subprocess`, `tomllib` for TOML parsing, pytest for TDD, 1-space indentation per project convention. + +**Design spec:** `docs/superpowers/specs/2026-07-01-chronology-v2-redo-design.md` + +--- + +## File Structure + +| File | Action | Responsibility | +|---|---|---| +| `conductor/tracks/chronology_20260619/state.toml` | Modify | Mark as superseded | +| `conductor/tracks.md` | Modify | Update old track row (Phase 1); de-gunk entirely (Phase 5) | +| `conductor/archive/chronology_20260619/` | Create (move) | Archived old track | +| `conductor/tracks/superpowers_review_20260619/state.toml` | Modify | Remove blocked_by entry | +| `conductor/tracks/chronology_v2_20260701/` | Create | New track folder (spec/metadata/state/plan) | +| `tests/test_generate_chronology.py` | Rewrite | TDD tests for the new classifier + summary extractor | +| `tests/test_chronology_quality_gate.py` | Create | TDD tests for the 4 quality-gate checks | +| `scripts/audit/generate_chronology.py` | Rewrite | The new git-history classifier + summary extractor + walk | +| `scripts/audit/chronology_quality_gate.py` | Create | The 4-check quality gate with `--strict` mode | +| `conductor/chronology.md` | Regenerate | The v2 output (replaces v1) | +| `conductor/workflow.md` | Modify | Add "Chronology Maintenance" section | +| `docs/reports/CHRONOLOGY_QUALITY_20260701.md` | Create | The quality report (status distribution, Needs Review queue, v1 comparison) | +| `docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md` | Create | The end-of-track report | + +--- + +## Phase 1: Close out the old track + scaffold the new one + +### Task 1.1: Mark `chronology_20260619` as superseded in its state.toml + +**Files:** +- Modify: `conductor/tracks/chronology_20260619/state.toml` (lines 1-10, the `[meta]` section) + +- [ ] **Step 1: Read the current state.toml to confirm exact content** + +Run: `Get-Content conductor/tracks/chronology_20260619/state.toml | Select-Object -First 12` +Expected: shows `status = "active"` and `current_phase = 10` in the `[meta]` section. + +- [ ] **Step 2: Edit the `[meta]` section** + +Use `manual-slop_edit_file` to replace: +``` +status = "active" # remains "active" until Phase 10 user sign-off recorded +current_phase = 10 # Phase 10 in progress; user sign-off pending +last_updated = "2026-06-20" +``` +with: +``` +status = "superseded" # superseded by chronology_v2_20260701 per user directive 2026-07-01 +current_phase = 10 # Phase 10 sign-off never recorded; track closed as superseded +last_updated = "2026-07-01" +``` + +- [ ] **Step 3: Add a `[supersession]` section at the end of the file** + +Append: +``` +[supersession] +superseded_by = "chronology_v2_20260701" +superseded_date = "2026-07-01" +superseded_reason = "v1 chronology had 167/216 rows with wrong status (stale metadata.json.status classifier); v2 rewrite was specced but never executed; user directed a fresh track with the v2 design as ancestor" +``` + +- [ ] **Step 4: Verify the edit** + +Run: `Get-Content conductor/tracks/chronology_20260619/state.toml | Select-String 'superseded'` +Expected: 2 matches (the `status` line + the `[supersession]` section). + +- [ ] **Step 5: Commit** + +```bash +git add conductor/tracks/chronology_20260619/state.toml +git commit -m "conductor(chronology): mark chronology_20260619 as superseded by chronology_v2_20260701" +``` + +### Task 1.2: Update `chronology_20260619` row in tracks.md + +**Files:** +- Modify: `conductor/tracks.md` (line 64, the chronology row in the active queue) + +- [ ] **Step 1: Read the current row to confirm exact content** + +Run: `Get-Content conductor/tracks.md | Select-Object -Index 63` +Expected: the row starting with `| 21 | A | [Conductor Chronology...` + +- [ ] **Step 2: Replace the row** + +Use `manual-slop_edit_file` to replace the entire row with: +``` +| 21 | — | ~~[Conductor Chronology (chronology.md canonical index)]~~ | **SUPERSEDED** by `chronology_v2_20260701` (2026-07-01); v1 produced 167/216 wrong-status rows; v2 rewrite specced but never executed; archived to `conductor/archive/chronology_20260619/` | — | +``` + +- [ ] **Step 3: Commit** + +```bash +git add conductor/tracks.md +git commit -m "conductor(tracks): mark chronology_20260619 row as superseded" +``` + +### Task 1.3: Archive the old track folder + +**Files:** +- Move: `conductor/tracks/chronology_20260619/` → `conductor/archive/chronology_20260619/` + +- [ ] **Step 1: Move the folder with git mv** + +Run: `git mv conductor/tracks/chronology_20260619 conductor/archive/chronology_20260619` +Expected: no output (success). + +- [ ] **Step 2: Verify the move** + +Run: `Test-Path conductor/archive/chronology_20260619/state.toml` +Expected: `True`. + +Run: `Test-Path conductor/tracks/chronology_20260619` +Expected: `False`. + +- [ ] **Step 3: Commit** + +```bash +git add -A conductor/tracks/chronology_20260619 conductor/archive/chronology_20260619 +git commit -m "conductor(chronology): archive chronology_20260619 folder (superseded)" +``` + +### Task 1.4: Unblock `superpowers_review_20260619` + +**Files:** +- Modify: `conductor/tracks/superpowers_review_20260619/state.toml` (the `[blocked_by]` section, line ~12) + +- [ ] **Step 1: Read the current blocked_by section** + +Run: `Get-Content conductor/tracks/superpowers_review_20260619/state.toml | Select-String 'blocked_by' -Context 0,2` +Expected: shows `chronology_20260619 = "active (per user 2026-06-19 directive)"`. + +- [ ] **Step 2: Remove the blocked_by entry** + +Use `manual-slop_edit_file` to replace: +``` +[blocked_by] +chronology_20260619 = "active (per user 2026-06-19 directive)" +``` +with: +``` +[blocked_by] +# chronology_20260619 superseded 2026-07-01; blocker removed per user directive. +# superpowers_review is now unblocked. +``` + +- [ ] **Step 3: Verify the edit** + +Run: `Get-Content conductor/tracks/superpowers_review_20260619/state.toml | Select-String 'superseded'` +Expected: 1 match in the `[blocked_by]` comment. + +- [ ] **Step 4: Commit** + +```bash +git add conductor/tracks/superpowers_review_20260619/state.toml +git commit -m "conductor(superpowers_review): remove chronology_20260619 blocker (superseded)" +``` + +### Task 1.5: Scaffold the new track folder + +**Files:** +- Create: `conductor/tracks/chronology_v2_20260701/spec.md` +- Create: `conductor/tracks/chronology_v2_20260701/metadata.json` +- Create: `conductor/tracks/chronology_v2_20260701/state.toml` +- Create: `conductor/tracks/chronology_v2_20260701/plan.md` + +- [ ] **Step 1: Create the directory** + +Run: `New-Item -ItemType Directory -Path conductor/tracks/chronology_v2_20260701 -Force` +Expected: directory exists. + +- [ ] **Step 2: Write spec.md** + +Copy the design spec from `docs/superpowers/specs/2026-07-01-chronology-v2-redo-design.md` into `conductor/tracks/chronology_v2_20260701/spec.md` (the design spec IS the track spec). Use `manual-slop_read_file` to read the design spec, then `write` tool to create the track spec.md with the same content. + +- [ ] **Step 3: Write metadata.json** + +```json +{ + "track_id": "chronology_v2_20260701", + "name": "Chronology v2 Redo (git-history classifier + tracks.md de-gunk + maintenance rule)", + "priority": "A", + "category": "meta-tooling", + "status": "spec_written", + "blocked_by": [], + "verification_criteria": [ + "conductor/chronology.md exists with one row per track folder (tracks/ + archive/), sorted newest-first, 6 columns, generated from the current filesystem", + "every row's status is backed by git-history evidence (not metadata.json.status); the evidence reason is non-empty for every row", + "no summary contains metadata-field text (**Priority:**, **Date:**, **Initialized:**, **Track:**, **Parent umbrella:**, **Status:**, **Confidence:**)", + "scripts/audit/chronology_quality_gate.py --strict exits 0", + "conductor/tracks.md contains only the active queue + standby/pending + a pointer to chronology.md + the 'Editing this file' notes; no Phase 0-9 history sections", + "conductor/workflow.md contains the 'Chronology Maintenance' section", + "docs/reports/CHRONOLOGY_QUALITY_20260701.md exists with status distribution + Needs Review queue + v1 comparison + desync gap list", + "docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md exists", + "chronology_20260619 is archived with status = superseded in its state.toml", + "superpowers_review_20260619/state.toml [blocked_by] no longer contains chronology_20260619", + "tests/test_generate_chronology.py + tests/test_chronology_quality_gate.py pass", + "user sign-off recorded in the TRACK_COMPLETION report" + ] +} +``` + +- [ ] **Step 4: Write state.toml** + +```toml +# Track state for chronology_v2_20260701 + +[meta] +track_id = "chronology_v2_20260701" +name = "Chronology v2 Redo" +status = "active" +current_phase = 0 +last_updated = "2026-07-01" + +[blocked_by] +# Independent track. No blockers. + +[blocks] +# superpowers_review_20260619 was blocked by the old chronology_20260619; +# that blocker was removed in Task 1.4. This track does not block anything. + +[phases] +phase_1 = { status = "pending", checkpointsha = "", name = "Close out old track + scaffold new one" } +phase_2 = { status = "pending", checkpointsha = "", name = "TDD the classifier + quality gate (Red)" } +phase_3 = { status = "pending", checkpointsha = "", name = "Implement the classifier + quality gate (Green)" } +phase_4 = { status = "pending", checkpointsha = "", name = "Regenerate chronology.md + write quality report" } +phase_5 = { status = "pending", checkpointsha = "", name = "De-gunk tracks.md + add workflow.md maintenance rule" } +phase_6 = { status = "pending", checkpointsha = "", name = "Verification + end-of-track report" } + +[tasks] +t1_1 = { status = "pending", commit_sha = "", description = "Mark chronology_20260619 as superseded in state.toml" } +t1_2 = { status = "pending", commit_sha = "", description = "Update chronology_20260619 row in tracks.md" } +t1_3 = { status = "pending", commit_sha = "", description = "Archive chronology_20260619 folder" } +t1_4 = { status = "pending", commit_sha = "", description = "Unblock superpowers_review_20260619" } +t1_5 = { status = "pending", commit_sha = "", description = "Scaffold chronology_v2_20260701 track folder" } +``` + +- [ ] **Step 5: Write plan.md** + +Copy this plan file to `conductor/tracks/chronology_v2_20260701/plan.md`. Use `manual-slop_read_file` to read `docs/superpowers/plans/2026-07-01-chronology-v2-redo.md`, then `write` tool to create the track plan.md with the same content. + +- [ ] **Step 6: Commit** + +```bash +git add conductor/tracks/chronology_v2_20260701/ +git commit -m "conductor(track): init chronology_v2_20260701 (spec + metadata + state + plan)" +``` + +### Task 1.6: Phase 1 checkpoint commit + +- [ ] **Step 1: Create checkpoint commit** + +```bash +git commit --allow-empty -m "conductor(checkpoint): Phase 1 complete (close out old track + scaffold new one)" +``` + +- [ ] **Step 2: Update state.toml with checkpoint SHA** + +Run: `git log -1 --format="%H"` to get the checkpoint SHA. Then update `conductor/tracks/chronology_v2_20260701/state.toml`: +- `current_phase = 1` → `current_phase = 2` +- `phase_1` status → `"completed"`, checkpointsha → the SHA +- All `t1_*` tasks → `"completed"` with their commit SHAs + +- [ ] **Step 3: Commit the state update** + +```bash +git add conductor/tracks/chronology_v2_20260701/state.toml +git commit -m "conductor(state): mark Phase 1 complete for chronology_v2_20260701" +``` + +--- + +## Phase 2: TDD the classifier + quality gate (Red) + +### Task 2.1: Write `tests/test_generate_chronology.py` (Red) + +**Files:** +- Rewrite: `tests/test_generate_chronology.py` + +- [ ] **Step 1: Read the current test file to understand what exists** + +Run: `Get-Content tests/test_generate_chronology.py` +Expected: 6 tests for v1 (`extract_slug_date`, `extract_summary`). + +- [ ] **Step 2: Write the new test file** + +Use `write` tool to create `tests/test_generate_chronology.py` with these tests (1-space indentation per project convention): + +```python +from pathlib import Path +import json +import pytest +from unittest.mock import patch, MagicMock +from scripts.audit.generate_chronology import ( + extract_slug_date, + extract_summary, + classify_status, + walk_track_folders, + format_markdown, +) +from scripts.audit.chronology_quality_gate import run_quality_gate + + +def test_slug_date_extraction() -> None: + result: str = extract_slug_date("gencpp_python_bindings_20260308") + assert result == "2026-03-08" + + +def test_slug_date_extraction_handles_missing_date() -> None: + result = extract_slug_date("my_folder") + assert result is None + + +def test_summary_extraction_rejects_priority_line(tmp_path: Path) -> None: + spec_content: str = "# Title\n\n**Priority:** A (foundational)\n\nReal description of the work.\n" + (tmp_path / "spec.md").write_text(spec_content, encoding="utf-8") + result: str = extract_summary(tmp_path) + assert result == "Real description of the work." + + +def test_summary_extraction_rejects_date_line(tmp_path: Path) -> None: + spec_content: str = "# Title\n\n**Date:** 2026-06-20\n\nReal description.\n" + (tmp_path / "spec.md").write_text(spec_content, encoding="utf-8") + result: str = extract_summary(tmp_path) + assert result == "Real description." + + +def test_summary_extraction_rejects_initialized_line(tmp_path: Path) -> None: + spec_content: str = "# Title\n\n**Initialized:** 2026-06-13\n\nReal description.\n" + (tmp_path / "spec.md").write_text(spec_content, encoding="utf-8") + result: str = extract_summary(tmp_path) + assert result == "Real description." + + +def test_summary_extraction_rejects_track_line(tmp_path: Path) -> None: + spec_content: str = "# Title\n\n**Track:** Some track\n\nReal description.\n" + (tmp_path / "spec.md").write_text(spec_content, encoding="utf-8") + result: str = extract_summary(tmp_path) + assert result == "Real description." + + +def test_summary_extraction_rejects_parent_umbrella_line(tmp_path: Path) -> None: + spec_content: str = "# Title\n\n**Parent umbrella:** result_migration_20260616\n\nReal description.\n" + (tmp_path / "spec.md").write_text(spec_content, encoding="utf-8") + result: str = extract_summary(tmp_path) + assert result == "Real description." + + +def test_summary_extraction_rejects_confidence_line(tmp_path: Path) -> None: + spec_content: str = "# Title\n\n**Confidence:** high\n\nReal description.\n" + (tmp_path / "spec.md").write_text(spec_content, encoding="utf-8") + result: str = extract_summary(tmp_path) + assert result == "Real description." + + +def test_summary_extraction_prefers_metadata_description_prose(tmp_path: Path) -> None: + metadata: dict = {"description": "A real prose description of the track."} + (tmp_path / "metadata.json").write_text(json.dumps(metadata), encoding="utf-8") + result: str = extract_summary(tmp_path) + assert result == "A real prose description of the track." + + +def test_summary_extraction_rejects_metadata_description_field_text(tmp_path: Path) -> None: + metadata: dict = {"description": "**Priority:** A (foundational)"} + (tmp_path / "metadata.json").write_text(json.dumps(metadata), encoding="utf-8") + spec_content: str = "# Title\n\nReal description from spec.\n" + (tmp_path / "spec.md").write_text(spec_content, encoding="utf-8") + result: str = extract_summary(tmp_path) + assert result == "Real description from spec." + + +def test_summary_extraction_truncates_to_25_words(tmp_path: Path) -> None: + long_line: str = " ".join(["word"] * 50) + spec_content: str = f"# Title\n\n{long_line}\n" + (tmp_path / "spec.md").write_text(spec_content, encoding="utf-8") + result: str = extract_summary(tmp_path) + expected: str = " ".join(["word"] * 25) + "\u2026" + assert result == expected + + +# --- Classifier tests (the new git-history classifier) --- + +def test_classify_status_completion_report_override(tmp_path: Path) -> None: + """TRACK_COMPLETION report in docs/reports/ overrides everything.""" + result = classify_status( + folder_link="conductor/tracks/my_track_20260701", + current="active", + track_id="my_track_20260701", + repo_root=tmp_path, + reports_dir=tmp_path / "docs/reports", + ) + assert result[0] == "Completed" + assert result[1] == "high" + assert "completion report" in result[2] + + +def test_classify_status_abort_report_override(tmp_path: Path) -> None: + """TRACK_ABORTED report -> Abandoned.""" + result = classify_status( + folder_link="conductor/tracks/my_track_20260701", + current="active", + track_id="my_track_20260701", + repo_root=tmp_path, + reports_dir=tmp_path / "docs/reports", + has_abort_report=True, + ) + assert result[0] == "Abandoned" + assert result[1] == "high" + assert "abort report" in result[2] + + +def test_classify_status_superseded_state_toml_overrides_abort(tmp_path: Path) -> None: + """state.toml status=superseded wins over abort report.""" + result = classify_status( + folder_link="conductor/tracks/my_track_20260701", + current="active", + track_id="my_track_20260701", + repo_root=tmp_path, + reports_dir=tmp_path / "docs/reports", + has_abort_report=True, + state_status="superseded", + ) + assert result[0] == "Superseded" + assert result[1] == "high" + + +def test_classify_status_work_commits_completed(tmp_path: Path) -> None: + """>=3 work commits -> Completed.""" + with patch("scripts.audit.generate_chronology._git_log") as mock_log: + mock_log.return_value = "feat: add thing\nfix: fix thing\nrefactor: refactor thing\n" + result = classify_status( + folder_link="conductor/tracks/my_track_20260701", + current="active", + track_id="my_track_20260701", + repo_root=tmp_path, + reports_dir=tmp_path / "docs/reports", + ) + assert result[0] == "Completed" + assert result[1] == "medium" + assert "work commits" in result[2] + + +def test_classify_status_metadata_commits_not_counted_as_work(tmp_path: Path) -> None: + """conductor(plan): commits don't count as work commits.""" + with patch("scripts.audit.generate_chronology._git_log") as mock_log: + mock_log.return_value = "conductor(plan): mark task\nconductor(state): update\nconductor(track): init\n" + result = classify_status( + folder_link="conductor/tracks/my_track_20260701", + current="active", + track_id="my_track_20260701", + repo_root=tmp_path, + reports_dir=tmp_path / "docs/reports", + ) + # 0 work commits + in tracks/ -> Active + assert result[0] == "Active" + + +def test_classify_status_1_2_work_commits_in_progress(tmp_path: Path) -> None: + """1-2 work commits + in tracks/ -> In Progress.""" + with patch("scripts.audit.generate_chronology._git_log") as mock_log: + mock_log.return_value = "feat: add thing\nfix: fix thing\n" + result = classify_status( + folder_link="conductor/tracks/my_track_20260701", + current="active", + track_id="my_track_20260701", + repo_root=tmp_path, + reports_dir=tmp_path / "docs/reports", + ) + assert result[0] == "In Progress" + + +def test_classify_status_archive_no_override_completed_low(tmp_path: Path) -> None: + """archive/ + no completion report -> Completed (low confidence).""" + result = classify_status( + folder_link="conductor/archive/my_track_20260701", + current="active", + track_id="my_track_20260701", + repo_root=tmp_path, + reports_dir=tmp_path / "docs/reports", + ) + assert result[0] == "Completed" + assert result[1] == "low" + assert "archived" in result[2] + + +def test_classify_status_fallback_needs_review(tmp_path: Path) -> None: + """Inconclusive -> Needs Review.""" + result = classify_status( + folder_link="conductor/tracks/my_track", + current="", + track_id="my_track", + repo_root=tmp_path, + reports_dir=tmp_path / "docs/reports", + ) + assert result[0] == "Needs Review" + + +def test_classify_status_returns_3_tuple() -> None: + """Classifier must return (status, confidence, reason).""" + result = classify_status( + folder_link="conductor/tracks/my_track_20260701", + current="active", + track_id="my_track_20260701", + repo_root=Path("."), + reports_dir=Path("docs/reports"), + ) + assert len(result) == 3 + assert isinstance(result[0], str) + assert isinstance(result[1], str) + assert isinstance(result[2], str) +``` + +- [ ] **Step 3: Run the tests to verify they fail (Red)** + +Run: `uv run pytest tests/test_generate_chronology.py -v` +Expected: FAIL with `ImportError` (cannot import `classify_status` from `scripts.audit.generate_chronology` — the function doesn't exist yet; the old function is `_classify_status`). + +- [ ] **Step 4: Commit** + +```bash +git add tests/test_generate_chronology.py +git commit -m "test(chronology): write Red tests for v2 classifier + summary extractor" +``` + +### Task 2.2: Write `tests/test_chronology_quality_gate.py` (Red) + +**Files:** +- Create: `tests/test_chronology_quality_gate.py` + +- [ ] **Step 1: Write the test file** + +Use `write` tool to create `tests/test_chronology_quality_gate.py`: + +```python +from pathlib import Path +import pytest +from scripts.audit.chronology_quality_gate import run_quality_gate, QualityGateResult + + +def _make_row(status: str, confidence: str, reason: str, summary: str) -> dict: + return { + "status": status, + "confidence": confidence, + "reason": reason, + "summary": summary, + "track_id": "test_track", + "date": "2026-07-01", + "folder_link": "conductor/tracks/test_track", + "init_sha": "abc1234", + "end_sha": "def5678", + "commit_count": 1, + } + + +def test_quality_gate_passes_on_good_rows() -> None: + rows = [ + _make_row("Completed", "high", "completion report found", "A real summary."), + _make_row("Completed", "medium", "3 work commits", "Another real summary."), + _make_row("Active", "medium", "0 work commits in tracks/", "Spec-only track."), + ] + result = run_quality_gate(rows) + assert result.passed is True + assert result.violations == [] + + +def test_quality_gate_fails_on_too_many_needs_review() -> None: + rows = [ + _make_row("Needs Review", "none", "inconclusive", "Summary 1."), + _make_row("Needs Review", "none", "inconclusive", "Summary 2."), + _make_row("Completed", "high", "completion report", "Summary 3."), + ] + # 2/3 = 66% > 30% threshold + result = run_quality_gate(rows) + assert result.passed is False + assert any("Needs Review" in v for v in result.violations) + + +def test_quality_gate_fails_on_zero_completed() -> None: + rows = [ + _make_row("Active", "medium", "0 work commits", "Summary 1."), + _make_row("Active", "medium", "0 work commits", "Summary 2."), + ] + result = run_quality_gate(rows) + assert result.passed is False + assert any("0 rows are Completed" in v for v in result.violations) + + +def test_quality_gate_fails_on_metadata_field_summaries() -> None: + rows = [ + _make_row("Completed", "high", "completion report", "Real summary."), + _make_row("Completed", "high", "completion report", "**Priority:** A (foundational)"), + ] + # 1/2 = 50% > 20% threshold + result = run_quality_gate(rows) + assert result.passed is False + assert any("metadata-field" in v for v in result.violations) + + +def test_quality_gate_fails_on_empty_reason() -> None: + rows = [ + _make_row("Completed", "high", "", "Real summary."), + ] + result = run_quality_gate(rows) + assert result.passed is False + assert any("empty reason" in v for v in result.violations) + + +def test_quality_gate_strict_mode_exits_nonzero(tmp_path: Path) -> None: + """The --strict CLI mode exits 1 on violations.""" + import subprocess + import sys + # This test verifies the CLI wrapper; the function-level test is above + # We test the function returns the right structure; the CLI is a thin wrapper + rows = [_make_row("Active", "medium", "0 work commits", "Summary.")] + result = run_quality_gate(rows) + assert result.passed is False # 0 rows are Completed + assert result.exit_code == 1 +``` + +- [ ] **Step 2: Run the tests to verify they fail (Red)** + +Run: `uv run pytest tests/test_chronology_quality_gate.py -v` +Expected: FAIL with `ImportError` (cannot import `run_quality_gate` from `scripts.audit.chronology_quality_gate` — the module doesn't exist yet). + +- [ ] **Step 3: Commit** + +```bash +git add tests/test_chronology_quality_gate.py +git commit -m "test(chronology): write Red tests for quality gate (4 checks)" +``` + +### Task 2.3: Phase 2 checkpoint + +- [ ] **Step 1: Create checkpoint commit** + +```bash +git commit --allow-empty -m "conductor(checkpoint): Phase 2 complete (Red tests for classifier + quality gate)" +``` + +- [ ] **Step 2: Update state.toml** + +Update `conductor/tracks/chronology_v2_20260701/state.toml`: +- `current_phase = 2` → `current_phase = 3` +- `phase_2` status → `"completed"` with checkpoint SHA +- `t2_1`, `t2_2` → `"completed"` with their commit SHAs + +- [ ] **Step 3: Commit the state update** + +```bash +git add conductor/tracks/chronology_v2_20260701/state.toml +git commit -m "conductor(state): mark Phase 2 complete for chronology_v2_20260701" +``` + +--- + +## Phase 3: Implement the classifier + quality gate (Green) + +### Task 3.1: Rewrite `scripts/audit/generate_chronology.py` (Green) + +**Files:** +- Rewrite: `scripts/audit/generate_chronology.py` (the `_classify_status` function at lines 201-228, the `extract_summary` function at lines 62-92, the `walk_track_folders` function at lines 231-290, and the `format_markdown` function at lines 293-304) + +- [ ] **Step 1: Read the current script to confirm exact structure** + +Use `manual-slop_py_get_definition` for `_classify_status`, `extract_summary`, `walk_track_folders`, and `format_markdown` to confirm the exact current source. (Already done in plan research; the definitions are at the lines listed above.) + +- [ ] **Step 2: Rewrite `extract_summary` to reject all 7 metadata-field prefixes** + +Use `manual-slop_py_update_definition` to replace `extract_summary` with: + +```python +_METADATA_FIELD_PREFIXES = ( + "**Priority:**", + "**Date:**", + "**Initialized:**", + "**Track:**", + "**Track ID:**", + "**Parent umbrella:**", + "**Status:**", + "**Confidence:**", +) + + +def extract_summary(folder_path: Path) -> str: + md_path = folder_path / "metadata.json" + if md_path.is_file(): + try: + data = json.loads(md_path.read_text(encoding="utf-8")) + desc = str(data.get("description", "")).strip() + if desc and not desc.startswith(_METADATA_FIELD_PREFIXES): + return _truncate_to_25_words(_first_sentence(desc)) + except (json.JSONDecodeError, OSError): + pass + for fname in ("spec.md", "plan.md"): + fpath = folder_path / fname + if not fpath.is_file(): + continue + try: + text = fpath.read_text(encoding="utf-8") + except OSError: + continue + for line in text.splitlines(): + stripped = line.strip() + if not stripped: + continue + if stripped.startswith("#"): + continue + if stripped.startswith(">"): + continue + bare = stripped.lstrip(">").strip() + if bare.startswith(_METADATA_FIELD_PREFIXES): + continue + return _truncate_to_25_words(_first_sentence(bare)) + return "Imported from archive (no spec)" +``` + +- [ ] **Step 3: Add the new `classify_status` function** + +Use `manual-slop_py_add_def` to add a new module-level function after `_classify_status`: + +```python +_WORK_COMMIT_PREFIXES = ("feat:", "fix:", "refactor:", "perf:", "test:", "docs(report):") +_METADATA_COMMIT_PREFIXES = ("conductor(plan):", "conductor(state):", "conductor(track):", "docs(spec):", "docs(plan):") + + +def _count_work_commits(folder_relpath: str) -> int: + log: str = _git_log(folder_relpath, "--oneline") + count: int = 0 + for line in log.splitlines(): + msg: str = line.split(" ", 1)[1] if " " in line else "" + if msg.startswith(_WORK_COMMIT_PREFIXES) and not msg.startswith(_METADATA_COMMIT_PREFIXES): + count += 1 + return count + + +def _has_report_matching(reports_dir: Path, track_id: str, prefix: str) -> bool: + if not reports_dir.is_dir(): + return False + for f in reports_dir.iterdir(): + if f.is_file() and f.name.startswith(prefix) and track_id in f.name: + return True + return False + + +def classify_status( + folder_link: str, + current: str, + track_id: str, + repo_root: Path, + reports_dir: Path, + has_abort_report: bool = False, + state_status: str = "", +) -> tuple[str, str, str]: + """Git-history evidence classifier returning (status, confidence, reason). + + Evidence priority: + 1. Override signals (highest): TRACK_COMPLETION/TRACK_ABORTED reports, state.toml superseded + 2. Git commit evidence (medium): work-commit count + 3. Directory location (low): archive/ vs tracks/ + 4. Fallback: Needs Review + """ + # 1. Override signals + if state_status == "superseded": + return ("Superseded", "high", "state.toml status=superseded") + if has_abort_report or _has_report_matching(reports_dir, track_id, "TRACK_ABORTED_"): + return ("Abandoned", "high", "abort report found") + if _has_report_matching(reports_dir, track_id, "TRACK_COMPLETION_"): + return ("Completed", "high", "completion report found") + # 2. Git commit evidence + is_archive = folder_link.startswith("conductor/archive/") + is_tracks = folder_link.startswith("conductor/tracks/") + work_commits: int = _count_work_commits(folder_link) + if work_commits >= 3: + return ("Completed", "medium", f"{work_commits} work commits") + if 1 <= work_commits <= 2 and is_tracks: + return ("In Progress", "medium", f"{work_commits} work commits in tracks/") + if work_commits == 0 and is_tracks: + return ("Active", "medium", "0 work commits in tracks/ (spec/plan only)") + # 3. Directory location + if is_archive: + if work_commits == 0: + return ("Abandoned", "low", "archived with 0 commits") + return ("Completed", "low", "archived but no completion report") + # 4. Fallback + return ("Needs Review", "none", "classifier inconclusive") +``` + +- [ ] **Step 4: Update `walk_track_folders` to use the new classifier** + +Use `manual-slop_py_update_definition` to replace `walk_track_folders`. The key changes: +- Pass `repo_root` and `reports_dir` to `classify_status` instead of calling the old `_classify_status` +- Store `confidence` and `reason` in the row dict alongside `status` + +```python +def walk_track_folders(root: Path) -> list[dict]: + repo_root: Path = _repo_root(root) + reports_dir: Path = repo_root / "docs/reports" + rows: list[dict] = [] + for parent_dir, default_status in ( + (root / "tracks", "Active"), + (root / "archive", "Completed"), + ): + if not parent_dir.is_dir(): + continue + for folder in sorted(parent_dir.iterdir()): + if not folder.is_dir(): + continue + try: + folder_relpath = _to_posix(str(folder.relative_to(repo_root))) + except ValueError: + folder_relpath = _to_posix(str(folder)) + track_id: str = folder.name + slug_date = extract_slug_date(track_id) + if slug_date: + date = slug_date + else: + first_commit = _git_first_line(folder_relpath, "--reverse", "--format=%aI") + date = first_commit[:10] if first_commit else "" + metadata_path = folder / "metadata.json" + meta_status: str = "" + if metadata_path.is_file(): + try: + data = json.loads(metadata_path.read_text(encoding="utf-8")) + meta_status = str(data.get("status", "")).strip() + except (json.JSONDecodeError, OSError): + pass + state_status: str = _parse_state_status(folder / "state.toml") + status, confidence, reason = classify_status( + folder_link=folder_relpath, + current=meta_status or default_status, + track_id=track_id, + repo_root=repo_root, + reports_dir=reports_dir, + state_status=state_status, + ) + summary: str = extract_summary(folder) + init_sha: str = _git_first_line(folder_relpath, "--reverse", "--format=%h") + end_sha: str = _git_first_line(folder_relpath, "-1", "--format=%h") + if init_sha and end_sha: + range_log = _git_log(folder_relpath, "--oneline", f"{init_sha}..{end_sha}") + commit_count: int = range_log.count("\n") + (1 if init_sha != end_sha else 0) + else: + fallback_log = _git_log(folder_relpath, "--oneline") + commit_count = fallback_log.count("\n") + try: + folder_link = _to_posix(str(folder.relative_to(repo_root))) + except ValueError: + folder_link = _to_posix(str(folder)) + rows.append({ + "date": date, + "track_id": track_id, + "status": status, + "confidence": confidence, + "reason": reason, + "summary": summary, + "init_sha": init_sha, + "end_sha": end_sha, + "commit_count": commit_count, + "folder_link": folder_link, + }) + rows.sort(key=lambda r: r["track_id"]) + rows.sort(key=lambda r: r["date"], reverse=True) + return rows +``` + +- [ ] **Step 5: Add `_parse_state_status` helper** + +Use `manual-slop_py_add_def` to add after `_parse_state_phase`: + +```python +def _parse_state_status(state_path: Path) -> str: + if not state_path.is_file(): + return "" + try: + text = state_path.read_text(encoding="utf-8") + except OSError: + return "" + for line in text.splitlines(): + stripped = line.strip() + if stripped.startswith("status"): + parts = stripped.split("=", 1) + if len(parts) == 2: + return parts[1].strip().strip('"').strip("'").split("#")[0].strip() + return "" +``` + +- [ ] **Step 6: Update `format_markdown` to include the Needs Review section + preamble** + +Use `manual-slop_py_update_definition` to replace `format_markdown`: + +```python +def format_markdown(rows: list[dict]) -> str: + from datetime import date as today_date + lines: list[str] = [] + lines.append(f"") + lines.append("") + lines.append("| Date | ID | Status | Summary | Folder | Range |") + lines.append("| --- | --- | --- | --- | --- | --- |") + for r in rows: + range_str: str = f"`{r['init_sha']}..{r['end_sha']}` ({r['commit_count']})" if r['init_sha'] else "n/a" + lines.append(f"| {r['date']} | `{r['track_id']}` | {r['status']} | {r['summary']} | {r['folder_link']} | {range_str} |") + needs_review = [r for r in rows if r["status"] == "Needs Review"] + if needs_review: + lines.append("") + lines.append("## Needs Review") + lines.append("") + for r in needs_review: + lines.append(f"- `{r['track_id']}` ({r['folder_link']}): {r['reason']}") + return "\n".join(lines) + "\n" +``` + +- [ ] **Step 7: Run the tests to verify they pass (Green)** + +Run: `uv run pytest tests/test_generate_chronology.py -v` +Expected: all tests PASS. + +If tests fail, debug using the actual error output (do not loop more than 2 times per the workflow's deduction-loop rule). Read the source, predict the failure, fix, run once more. + +- [ ] **Step 8: Commit** + +```bash +git add scripts/audit/generate_chronology.py +git commit -m "feat(chronology): rewrite classifier to use git-history evidence + 7-status enum + Needs Review section" +``` + +### Task 3.2: Create `scripts/audit/chronology_quality_gate.py` (Green) + +**Files:** +- Create: `scripts/audit/chronology_quality_gate.py` + +- [ ] **Step 1: Write the quality gate script** + +Use `write` tool to create `scripts/audit/chronology_quality_gate.py`: + +```python +"""Quality gate for conductor/chronology.md. + +Checks: + 1. Needs Review threshold: >30% of rows are Needs Review -> FAIL + 2. Status distribution sanity: 0 rows are Completed -> FAIL + 3. Summary quality: >20% of summaries contain metadata-field text -> FAIL + 4. Per-row evidence: every row must have a non-empty reason -> FAIL + +Modes: + default: informational (exits 0, prints report) + --strict: CI gate (exits 1 on any violation) +""" +from __future__ import annotations +from dataclasses import dataclass, field +from pathlib import Path +import argparse +import json +import sys + + +_METADATA_FIELD_PREFIXES = ( + "**Priority:**", + "**Date:**", + "**Initialized:**", + "**Track:**", + "**Track ID:**", + "**Parent umbrella:**", + "**Status:**", + "**Confidence:**", +) + + +@dataclass(frozen=True, slots=True) +class QualityGateResult: + passed: bool + exit_code: int + violations: list[str] = field(default_factory=list) + total_rows: int = 0 + needs_review_count: int = 0 + completed_count: int = 0 + metadata_field_count: int = 0 + empty_reason_count: int = 0 + + +def run_quality_gate(rows: list[dict]) -> QualityGateResult: + violations: list[str] = [] + total: int = len(rows) + if total == 0: + return QualityGateResult(passed=False, exit_code=1, violations=["0 rows provided"]) + needs_review_count: int = sum(1 for r in rows if r.get("status") == "Needs Review") + completed_count: int = sum(1 for r in rows if r.get("status") == "Completed") + metadata_field_count: int = sum( + 1 for r in rows + if any(str(r.get("summary", "")).startswith(p) for p in _METADATA_FIELD_PREFIXES) + ) + empty_reason_count: int = sum(1 for r in rows if not r.get("reason", "").strip()) + # Check 1: Needs Review threshold (>30%) + if total > 0 and (needs_review_count / total) > 0.30: + violations.append( + f"Needs Review threshold exceeded: {needs_review_count}/{total} " + f"({needs_review_count/total*100:.0f}%) > 30%" + ) + # Check 2: 0 rows are Completed + if completed_count == 0: + violations.append("0 rows are Completed (classifier may be misclassifying everything)") + # Check 3: Summary quality (>20% contain metadata-field text) + if total > 0 and (metadata_field_count / total) > 0.20: + violations.append( + f"Summary quality: {metadata_field_count}/{total} " + f"({metadata_field_count/total*100:.0f}%) contain metadata-field text > 20%" + ) + # Check 4: Per-row evidence (every row must have non-empty reason) + if empty_reason_count > 0: + violations.append(f"{empty_reason_count} rows have empty reason (no evidence)") + passed: bool = len(violations) == 0 + return QualityGateResult( + passed=passed, + exit_code=0 if passed else 1, + violations=violations, + total_rows=total, + needs_review_count=needs_review_count, + completed_count=completed_count, + metadata_field_count=metadata_field_count, + empty_reason_count=empty_reason_count, + ) + + +def _load_rows_from_chronology(chronology_path: Path) -> list[dict]: + # The quality gate reads the rows from the generator's output (JSON sidecar) + # or from the generator directly. For CLI use, we import the generator. + from scripts.audit.generate_chronology import walk_track_folders, _repo_root + root: Path = _repo_root(chronology_path.parent) + return walk_track_folders(root) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Quality gate for conductor/chronology.md") + parser.add_argument("--chronology", type=Path, default=Path("conductor/chronology.md")) + parser.add_argument("--strict", action="store_true", help="Exit 1 on any violation") + args = parser.parse_args() + rows = _load_rows_from_chronology(args.chronology) + result = run_quality_gate(rows) + print(f"Quality gate: {'PASS' if result.passed else 'FAIL'}") + print(f" Total rows: {result.total_rows}") + print(f" Needs Review: {result.needs_review_count}") + print(f" Completed: {result.completed_count}") + print(f" Metadata-field summaries: {result.metadata_field_count}") + print(f" Empty reasons: {result.empty_reason_count}") + if result.violations: + print(" Violations:") + for v in result.violations: + print(f" - {v}") + if args.strict and result.exit_code != 0: + sys.exit(result.exit_code) + + +if __name__ == "__main__": + main() +``` + +- [ ] **Step 2: Run the tests to verify they pass (Green)** + +Run: `uv run pytest tests/test_chronology_quality_gate.py -v` +Expected: all tests PASS. + +- [ ] **Step 3: Commit** + +```bash +git add scripts/audit/chronology_quality_gate.py +git commit -m "feat(chronology): add chronology_quality_gate.py (4 checks + --strict mode)" +``` + +### Task 3.3: Phase 3 checkpoint + +- [ ] **Step 1: Create checkpoint commit** + +```bash +git commit --allow-empty -m "conductor(checkpoint): Phase 3 complete (Green: classifier + quality gate implemented)" +``` + +- [ ] **Step 2: Update state.toml** + +Update `conductor/tracks/chronology_v2_20260701/state.toml`: +- `current_phase = 3` → `current_phase = 4` +- `phase_3` status → `"completed"` with checkpoint SHA +- `t3_1`, `t3_2` → `"completed"` with their commit SHAs + +- [ ] **Step 3: Commit the state update** + +```bash +git add conductor/tracks/chronology_v2_20260701/state.toml +git commit -m "conductor(state): mark Phase 3 complete for chronology_v2_20260701" +``` + +--- + +## Phase 4: Regenerate chronology.md + write the quality report + +### Task 4.1: Regenerate `conductor/chronology.md` + +**Files:** +- Regenerate: `conductor/chronology.md` + +- [ ] **Step 1: Run the generator against the current filesystem** + +Run: `uv run python scripts/audit/generate_chronology.py > conductor/chronology.md` +Expected: the file is rewritten with all current tracks (including the new ones from 2026-06-27 onward that were missing from v1). + +- [ ] **Step 2: Verify the output has the new tracks** + +Run: `Select-String -Path conductor/chronology.md -Pattern 'mma_quarantine|default_layout|module_taxonomy|cruft_elimination_20260701'` +Expected: multiple matches (these are the desync-gap tracks that were missing from v1). + +- [ ] **Step 3: Verify the Needs Review section exists (if any rows need review)** + +Run: `Select-String -Path conductor/chronology.md -Pattern '## Needs Review'` +Expected: either a match (if there are Needs Review rows) or no match (if all rows classified confidently). If no match, that's fine — it means the classifier was confident on all rows. + +- [ ] **Step 4: Commit** + +```bash +git add conductor/chronology.md +git commit -m "docs(chronology): regenerate chronology.md with v2 git-history classifier (closes 5-day desync gap)" +``` + +### Task 4.2: Run the quality gate + +**Files:** +- No file changes (verification only); iterate on classifier if gate fails + +- [ ] **Step 1: Run the quality gate in strict mode** + +Run: `uv run python scripts/audit/chronology_quality_gate.py --strict` +Expected: `Quality gate: PASS` and exit code 0. + +- [ ] **Step 2: If the gate fails, investigate and fix** + +If the gate fails, read the violation output. The 4 possible violations: +1. Needs Review > 30% → the classifier is failing on too many rows; review the Needs Review queue, improve the classifier heuristics, return to Phase 3 Task 3.1 +2. 0 Completed → the classifier is misclassifying everything; check the report-matching logic +3. Metadata-field summaries > 20% → the summary extractor is still broken; check `_METADATA_FIELD_PREFIXES` +4. Empty reasons → the classifier is not returning reasons for some rows; check the fallback path + +Do not loop more than 2 times. If still failing after 2 iterations, stop and report to the user. + +- [ ] **Step 3: Commit the passing state (if iterations were needed)** + +If the classifier was iterated on: +```bash +git add scripts/audit/generate_chronology.py conductor/chronology.md +git commit -m "fix(chronology): classifier iterations to pass quality gate" +``` + +### Task 4.3: Write the quality report + +**Files:** +- Create: `docs/reports/CHRONOLOGY_QUALITY_20260701.md` + +- [ ] **Step 1: Gather the quality gate output** + +Run: `uv run python scripts/audit/chronology_quality_gate.py` +Expected: the full report with counts. + +- [ ] **Step 2: Count the v1→v2 status corrections** + +Run a quick comparison: the v1 chronology.md had 218 rows with stale statuses. The v2 has the corrected statuses. Count how many changed. + +- [ ] **Step 3: List the desync-gap tracks (added in v2, missing from v1)** + +These are the tracks created after 2026-06-27: `mma_quarantine_rag_test_decoupling_20260701`, `default_layout_install_20260629`, `default_layout_extract_20260629`, `default_layout_install_followup_20260629`, `module_taxonomy_refactor_20260627`, `post_module_taxonomy_de_cruft_20260627`, `cruft_elimination_20260627`, `directive_hotswap_harness_20260627`, `enforcement_gap_closure_20260627`, `test_engine_integration_20260627`, `fix_mma_concurrent_tracks_sim_20260627`, `type_alias_unfuck_20260626`, `video_analysis_campaign_2_20260627`. + +- [ ] **Step 4: Write the report** + +Use `write` tool to create `docs/reports/CHRONOLOGY_QUALITY_20260701.md`: + +```markdown +# Chronology Quality Report — 2026-07-01 + +**Generated by:** `chronology_v2_20260701` (the v2 redo track) +**Replaces:** v1 chronology.md (generated by `chronology_20260619`, 167/216 rows with wrong status) + +## Summary + +- **Total rows:** +- **Quality gate:** PASS (or FAIL with details) +- **v1 rows:** 218 +- **v2 rows:** +- **Desync-gap tracks added:** (tracks created after 2026-06-27 that were missing from v1) +- **Status corrections (v1→v2):** rows changed status + +## Status Distribution + +| Status | Count | Percentage | +|---|---|---| +| Completed | | % | +| Active | | % | +| In Progress | | % | +| Abandoned | | % | +| Superseded | | % | +| Special | | % | +| Needs Review | | % | + +## Confidence Distribution + +| Confidence | Count | +|---|---| +| high | | +| medium | | +| low | | +| none | | + +## Needs Review Queue + + + +## Desync Gap Closed (tracks added in v2, missing from v1) + +< List the desync-gap tracks: mma_quarantine_rag_test_decoupling_20260701, default_layout_install_20260629, ... > + +## Classifier Heuristics Summary + +- **Override signals matched:** rows (TRACK_COMPLETION report: , TRACK_ABORTED report: , state.toml superseded: ) +- **Git work-commit evidence matched:** rows (≥3 work commits: , 1-2 work commits: , 0 work commits: ) +- **Directory location fallback:** rows (archive/ with no report: ) +- **Needs Review fallback:** rows + +## v1 Comparison + +- **v1 total rows:** 218 +- **v2 total rows:** +- **Rows with changed status:** +- **Root cause of v1 failures:** the v1 `_classify_status` read `metadata.json.status` (a stale snapshot) instead of git history; v2 uses git work-commit count + report-matching overrides + +## Verification + +- `scripts/audit/chronology_quality_gate.py --strict` exits 0: YES (or NO with details) +- Every row has a non-empty `reason`: YES +- No summary contains metadata-field text: YES (or rows still contain it, documented for follow-up) +``` + +Fill in the `` placeholders with the actual numbers from the quality gate output. + +- [ ] **Step 5: Commit** + +```bash +git add docs/reports/CHRONOLOGY_QUALITY_20260701.md +git commit -m "docs(report): add CHRONOLOGY_QUALITY_20260701 (v2 quality report with status distribution + Needs Review queue)" +``` + +### Task 4.4: Phase 4 checkpoint + +- [ ] **Step 1: Create checkpoint commit** + +```bash +git commit --allow-empty -m "conductor(checkpoint): Phase 4 complete (chronology.md regenerated + quality report)" +``` + +- [ ] **Step 2: Update state.toml** + +Update `conductor/tracks/chronology_v2_20260701/state.toml`: +- `current_phase = 4` → `current_phase = 5` +- `phase_4` status → `"completed"` with checkpoint SHA +- `t4_1`, `t4_2`, `t4_3` → `"completed"` with their commit SHAs + +- [ ] **Step 3: Commit the state update** + +```bash +git add conductor/tracks/chronology_v2_20260701/state.toml +git commit -m "conductor(state): mark Phase 4 complete for chronology_v2_20260701" +``` + +--- + +## Phase 5: De-gunk tracks.md + add workflow.md maintenance rule + +### Task 5.1: Restructure `conductor/tracks.md` + +**Files:** +- Modify: `conductor/tracks.md` (the entire file — this is the de-gunk) + +- [ ] **Step 1: Read the current tracks.md "Editing this file" section (to preserve it)** + +Run: `manual-slop_get_file_slice conductor/tracks.md 868 891` +Expected: the "Editing this file" + "Archiving a track (3 steps)" section. This must be preserved in the new tracks.md. + +- [ ] **Step 2: Identify the genuinely-active rows (not shipped)** + +From the 60-row active queue, keep only rows where the track is NOT shipped/completed/superseded. Shipped tracks to remove (their history is in chronology.md): rows 6a, 6b, 6c, 6d-1, 6d-2, 6d-3 (if shipped), 6d-4, 6d-5, 6d-6, 6e, 6f, 24, 27, 28, 30, 31, 33, 34, 35, 32, 7a, 7b, 7d, and the layout tracks at the bottom. Keep: 36 (MMA quarantine), 2 (qwen_llama_grok — Phase 6 in progress), 3 (DOEH), 4 (MCP refactor), 6 (placeholder), 7 (UI Polish), 7c (SQLite docs ai_client), 16 (Test Sandbox Hardening), 8-15 (spec TBD), 15a, 15b, 17 (Code Path Audit), 18, 19, 20, 21 (now superseded — remove), 22b, 23, 25, 26, 29, 29a, 29b, 29c. + +**The implementer must verify each row against its state.toml before removing it.** A row is "shipped" if its state.toml `status` is `completed`/`superseded`/`abandoned` AND it's not marked as having a follow-up track in the active queue. + +- [ ] **Step 3: Write the new tracks.md** + +Use `write` tool to create the new `conductor/tracks.md` with this structure: + +```markdown +# Project Tracks + +This file tracks all major tracks for the project. Each track has its own detailed plan in its respective folder (or in `../archive//` for completed tracks). + +**Structure:** +- **Active Tracks (Current Queue):** In-flight and unblocked work the implementer can pick up today. +- **Standby / Pending Spec:** Tracks with spec TBD or pending decision. +- **Full project history:** see [`chronology.md`](./chronology.md) (the canonical index of all tracks: active, shipped, superseded, abandoned). + +Archive directories live at `../archive//` (from this file's location at `conductor/tracks.md`). + +--- + +## Active Tracks (Current Queue) + +Tracks that are unblocked and ready to start. Ordered by **dependency** (blocked-by first) and **priority** (A foundational → D forward-looking). + +| # | Priority | Track | Status | Blocked By | +|---|---|---|---|---| +| 36 | A (sunset) | [MMA Quarantine + RAG Test Decoupling](#track-mma-quarantine--rag-test-decoupling) | spec ✓, plan pending | (none) | +| 2 | A | [Qwen, Llama & Grok Vendor Integration](#track-qwen-llama-grok-vendor-integration) | Phase 6 in progress (docs); NOT archiving — has follow-up | test_infrastructure_hardening_20260609 (merged) | +| 3 | A | [Data-Oriented Error Handling (Fleury Pattern)](#track-data-oriented-error-handling-fleury-pattern) | spec ✓, plan ✓, ready to start | test_infrastructure_hardening_20260609 (merged), qwen_llama_grok | +| 4 | A | [MCP Architecture Refactor](#track-mcp-architecture-refactor-sub-mcp-extraction) | spec ✓, plan pending | test_infrastructure_hardening_20260609 (merged), data_oriented_error_handling, data_structure_strengthening | +| 16 | A | [Test Sandbox Hardening](#track-test-sandbox-hardening) | spec ✓, plan ✓, ready to start | (none) | +| 17 | A | [Code Path Audit](#track-code-path-audit) | spec ✓ + plan ✓ | test_infrastructure_hardening_20260609 (merged), any_type_componentization_20260621 (shipped), phase2_4_5_call_site_completion_20260621 (shipped) | +| 22b | A | [Meta-Tooling Workflow Review](#track-meta-tooling-workflow-review) | spec ✓, plan ✓, parked | (none) | +| 23 | A | [Intent-Based Scripting Languages Survey](#track-intent-based-scripting-languages-survey) | spec ✓, plan pending | (none) | +| 25 | B | [Fable System Prompt Review](#track-fable-system-prompt-review) | spec ✓, plan pending | (none) | +| 26 | A | [Video Analysis Campaign (Pass 1 of 3)](#track-video-analysis-campaign) | scaffolded; awaiting Phase 0 tooling | (none) | +| 29 | A | [Video Analysis De-obfuscation Campaign (Pass 2 of 3)](#track-video-analysis-deob) | scaffolded; awaits USER action item | (none) | +| 29a | A | [Lexicon v2 Patch (Pass 2 Phase 1.5)](#track-lexicon-v2-patch) | spec DRAFT pending user review | video_analysis_deob_apply_20260621 (shipped) | +| 29b | A | [C11 Reference (Pass 3 Sub-Track)](#track-c11-reference) | in progress | video_analysis_deob_apply_20260621 (shipped), lexicon_v2 (shipped) | +| 29c | A | [Pass 3 — C11/Python Projection](#track-pass-3-c11python-projection) | spec DRAFT pending user review | deob_apply (shipped), lexicon_v2 (shipped), c11_reference (shipped) | +| 7 | — | [UI Polish (Five Issues)](#track-ui-polish-five-issues) | spec ✓, plan ✓, ready to start | (none) | +| 7c | B | [SQLite-Granularity Inline Docs for ai_client.py](#track-sqlite-docs-ai-client) | spec ✓, plan ✓, ready to start | (none) | + +--- + +## Standby / Pending Spec + +| # | Priority | Track | Status | Blocked By | +|---|---|---|---|---| +| 6 | D | [Public API Result Migration](#track-public-api-result-migration) | placeholder; not yet specced | data_oriented_error_handling (deprecated send()) | +| 8 | — | [Bootstrap gencpp Python Bindings](#track-bootstrap-gencpp-python-bindings) | spec TBD | (none) | +| 9 | — | [Tree-Sitter Lua MCP Tools](#track-tree-sitter-lua-mcp-tools) | spec TBD | (none) | +| 10 | — | [GDScript Language Support Tools](#track-gdscript-language-support-tools) | spec TBD | (none) | +| 11 | — | [C# Language Support Tools](#track-c-language-support-tools) | spec TBD | (none) | +| 12 | — | [OpenAI Provider Integration](#track-openai-provider-integration) | spec TBD | (none) | +| 13 | — | [Zhipu AI (GLM) Provider Integration](#track-zhipu-ai-glm-provider-integration) | spec TBD | (none) | +| 14 | — | [AI Provider Caching Optimization](#track-ai-provider-caching-optimization) | spec TBD | (none) | +| 15 | — | [Manual UX Validation & Review](#track-manual-ux-validation--review) | spec TBD | (none) | +| 15a | — | [Manual UX Validation — ASCII-Sketch Workflow](#track-manual-ux-validation-ascii-sketch) | spec ✓, plan ✓, ready to start | (none) | +| 15b | — | [Chunkification Optimization (Contingency)](#track-chunkification-optimization) | spec ✓ (contingency), no plan | hard constraint surface (deferred) | +| 16 | — | [GenCpp Dogfood Feedback Loop](#track-gencpp-dogfood-feedback-loop) | spec TBD | (none) | +| 18 | — | [GUI Architecture Refinement](#track-gui-architecture-refinement) | no spec.md | (TBD) | +| 19 | — | [Context First Message Fix](#track-context-first-message-fix) | spec TBD | (none) | + +--- + +> **Full project history:** see [`chronology.md`](./chronology.md) — the canonical index of all tracks (active, shipped, superseded, abandoned), sorted newest-first, with git-history-evidence-backed status per row. + +--- + +## Notes + +**Archive link convention:** `./archive/...` paths in this file resolve to `conductor/archive/...` (this file is at `conductor/tracks.md`). + +**Status legend:** +- `[ ]` not started +- `[~]` in progress +- `[x]` completed (track may still be in `tracks/` or may have been moved to `archive/`) +- `~~**...**~~` struck-through (renamed/replaced/superseded) + +**Naming convention:** Each track's `spec.md` and `plan.md` (where present) follow the project's standard format: `spec.md` for design intent (the "why"), `plan.md` for executable tasks (the "how"). + +**Editing this file:** When you mark a track as `[x]` and move its folder to `archive/`, remove its row from the Active Tracks table (its history is in `chronology.md`). When you start a new track, create the folder under `tracks/` first, then add the entry to the Active Tracks table. + +**Archiving a track (3 steps):** When a track ships and its folder moves from `conductor/tracks//` to `conductor/archive//`, complete all 3 steps in order: +1. Move the folder: `git mv conductor/tracks/ conductor/archive/` (preserves history as a rename). +2. Remove the row from this file (`conductor/tracks.md`). +3. Run `uv run python scripts/audit/generate_chronology.py > conductor/chronology.md` to regenerate the chronology (the new row appears automatically). Commit with `docs(chronology): regenerate after shipped`. +``` + +**IMPORTANT:** Before writing, the implementer MUST verify each row in the current active queue against its `state.toml` to confirm whether it's still active or has shipped since the plan was written. Remove any row whose `state.toml` `status` is `completed`/`superseded`/`abandoned`. The rows listed above are the plan's best-known state as of 2026-07-01; the implementer must verify. + +- [ ] **Step 4: Verify the new file** + +Run: `(Get-Content conductor/tracks.md | Measure-Object -Line).Lines` +Expected: significantly fewer lines than the original 941 (target: ~80-100 lines). + +Run: `Select-String -Path conductor/tracks.md -Pattern '## Phase', '## Archived', 'Recently Shipped'` +Expected: no matches (the history sections are removed). + +Run: `Select-String -Path conductor/tracks.md -Pattern 'chronology.md'` +Expected: at least 2 matches (the pointer + the archiving step 3). + +- [ ] **Step 5: Commit** + +```bash +git add conductor/tracks.md +git commit -m "docs(tracks): de-gunk tracks.md — remove Phase 0-9 history + shipped rows; keep active queue + standby + pointer to chronology.md" +``` + +### Task 5.2: Add "Chronology Maintenance" section to `conductor/workflow.md` + +**Files:** +- Modify: `conductor/workflow.md` (add a new section after "Track Dependencies and Execution Order") + +- [ ] **Step 1: Find the insertion point** + +Run: `Select-String -Path conductor/workflow.md -Pattern '^## Track Dependencies'` +Expected: the section starts at some line. The new section goes after this section ends (before the next `## ` heading). + +- [ ] **Step 2: Insert the new section** + +Use `manual-slop_edit_file` to insert after the "Track Dependencies and Execution Order" section (before the next `## ` heading): + +```markdown +--- + +## Chronology Maintenance + +**Chronology regeneration cadence.** After every track ships (completion commit + TRACK_COMPLETION report), the implementing agent must run `uv run python scripts/audit/generate_chronology.py > conductor/chronology.md` to regenerate `conductor/chronology.md`. The regeneration is a single atomic commit (`docs(chronology): regenerate after shipped`). If the regeneration produces a diff beyond the new row (e.g., status changes on other rows), the agent must investigate before committing — a status drift on an unrelated row indicates a stale classifier, not a chronology bug. + +**Quality gate.** `scripts/audit/chronology_quality_gate.py` runs as part of the regeneration. It fails (exit 1) if >30% of rows are classified as `Needs Review`. A failing quality gate blocks the regeneration commit. Run `uv run python scripts/audit/chronology_quality_gate.py --strict` before committing the regenerated chronology. + +**When to regenerate:** +- After a track ships (TRACK_COMPLETION report committed) +- After a track is abandoned (TRACK_ABORTED report committed) +- After a track is superseded (state.toml status = "superseded") +- After a track folder is archived (moved to `conductor/archive/`) + +**When NOT to regenerate:** +- Mid-track (the chronology reflects filesystem state; a mid-track folder is correctly "In Progress" or "Active") +- After a plan.md edit (the chronology doesn't track plan content) +``` + +- [ ] **Step 3: Verify the insertion** + +Run: `Select-String -Path conductor/workflow.md -Pattern 'Chronology Maintenance'` +Expected: 1 match (the new section heading). + +- [ ] **Step 4: Commit** + +```bash +git add conductor/workflow.md +git commit -m "docs(workflow): add Chronology Maintenance section (regeneration cadence + quality gate obligation)" +``` + +### Task 5.3: Phase 5 checkpoint + +- [ ] **Step 1: Create checkpoint commit** + +```bash +git commit --allow-empty -m "conductor(checkpoint): Phase 5 complete (tracks.md de-gunked + workflow.md maintenance rule)" +``` + +- [ ] **Step 2: Update state.toml** + +Update `conductor/tracks/chronology_v2_20260701/state.toml`: +- `current_phase = 5` → `current_phase = 6` +- `phase_5` status → `"completed"` with checkpoint SHA +- `t5_1`, `t5_2` → `"completed"` with their commit SHAs + +- [ ] **Step 3: Commit the state update** + +```bash +git add conductor/tracks/chronology_v2_20260701/state.toml +git commit -m "conductor(state): mark Phase 5 complete for chronology_v2_20260701" +``` + +--- + +## Phase 6: Verification + end-of-track report + +### Task 6.1: Final quality gate verification + +**Files:** +- No file changes (verification only) + +- [ ] **Step 1: Run the quality gate in strict mode** + +Run: `uv run python scripts/audit/chronology_quality_gate.py --strict` +Expected: `Quality gate: PASS`, exit code 0. + +- [ ] **Step 2: Run all chronology tests** + +Run: `uv run pytest tests/test_generate_chronology.py tests/test_chronology_quality_gate.py -v` +Expected: all tests PASS. + +- [ ] **Step 3: Commit (if any fixes were needed; otherwise skip)** + +If no fixes needed, no commit. If fixes: +```bash +git add -A +git commit -m "fix(chronology): final verification fixes" +``` + +### Task 6.2: Review the Needs Review queue + +**Files:** +- No file changes (user review) + +- [ ] **Step 1: Read the Needs Review section from chronology.md** + +Run: `Select-String -Path conductor/chronology.md -Pattern '## Needs Review' -Context 0,20` +Expected: either no match (no rows need review) or the list of rows needing manual classification. + +- [ ] **Step 2: Present the Needs Review queue to the user** + +If there are rows in the Needs Review queue, present them to the user for manual classification. The user decides the status for each row. Apply the user's classifications by editing the rows in `conductor/chronology.md` manually (the generator's classifier was inconclusive; the human is the authority). + +- [ ] **Step 3: Commit (if manual classifications were applied)** + +```bash +git add conductor/chronology.md +git commit -m "docs(chronology): apply user manual classifications for Needs Review rows" +``` + +### Task 6.3: Write the end-of-track report + +**Files:** +- Create: `docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md` + +- [ ] **Step 1: Write the report** + +Use `write` tool to create `docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md`: + +```markdown +# Track Completion: chronology_v2_20260701 + +**Date:** 2026-07-01 +**Track ID:** `chronology_v2_20260701` +**Status:** completed (pending user sign-off) + +## Summary + +Replaced the broken v1 `conductor/chronology.md` (167/216 rows with wrong status due to a stale-metadata classifier) with a correct v2 that uses git-history evidence. Closed out the stuck `chronology_20260619` track, de-gunked `conductor/tracks.md`, and added a maintenance rule to `conductor/workflow.md` to prevent future desync. + +## What was done + +1. **Closed out `chronology_20260619`**: marked superseded in state.toml, updated tracks.md row, archived the folder, unblocked `superpowers_review_20260619`. +2. **Rewrote the classifier**: `scripts/audit/generate_chronology.py` now uses a 4-tier evidence-priority chain (override signals > git work-commits > directory location > fallback) returning `(status, confidence, reason)`. 7-status enum: Active / In Progress / Completed / Abandoned / Superseded / Special / Needs Review. +3. **Added a quality gate**: `scripts/audit/chronology_quality_gate.py` with 4 checks (Needs Review threshold, status distribution sanity, summary quality, per-row evidence) + `--strict` CI mode. +4. **Regenerated `conductor/chronology.md`** from the current filesystem, closing the 5-day desync gap (13+ tracks added that were missing from v1). +5. **De-gunked `conductor/tracks.md`**: removed Phase 0-9 history sections + shipped-track rows from the active queue; left active queue + standby + pointer to chronology.md. Reduced from 941 lines to ~ lines. +6. **Added "Chronology Maintenance" section to `conductor/workflow.md`**: regeneration cadence (after every track ships/abandons/supersedes) + quality gate obligation. +7. **Wrote the quality report**: `docs/reports/CHRONOLOGY_QUALITY_20260701.md` with status distribution, confidence distribution, Needs Review queue, v1 comparison, desync gap list, heuristics summary. + +## Files changed + +< List all files created/modified with commit SHAs > + +## Verification criteria + +1. [x] conductor/chronology.md exists, one row per track folder, sorted newest-first, 6 columns, from current filesystem +2. [x] Every row's status backed by git-history evidence; reason non-empty +3. [x] No summary contains metadata-field text +4. [x] chronology_quality_gate.py --strict exits 0 +5. [x] conductor/tracks.md contains only active queue + standby + pointer + Editing notes +6. [x] conductor/workflow.md contains Chronology Maintenance section +7. [x] docs/reports/CHRONOLOGY_QUALITY_20260701.md exists with required contents +8. [x] This report exists +9. [x] chronology_20260619 archived with status = superseded +10. [x] superpowers_review_20260619 [blocked_by] no longer contains chronology_20260619 +11. [x] tests/test_generate_chronology.py + tests/test_chronology_quality_gate.py pass +12. [ ] User sign-off recorded (pending) + +## Known limitations + +< Any rows that remained in Needs Review after the classifier iterations, any edge cases the classifier doesn't handle well, any follow-up recommendations > + +## User sign-off + +[ ] User has reviewed the chronology.md, the quality report, and the Needs Review queue (if any). Track is complete. +``` + +- [ ] **Step 2: Commit** + +```bash +git add docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md +git commit -m "docs(report): add TRACK_COMPLETION_chronology_v2_20260701" +``` + +### Task 6.4: User sign-off + +- [ ] **Step 1: Present the deliverables to the user** + +Present: +- `conductor/chronology.md` (the regenerated v2) +- `docs/reports/CHRONOLOGY_QUALITY_20260701.md` (the quality report) +- `docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md` (the end-of-track report) +- The Needs Review queue (if any rows need manual classification) + +- [ ] **Step 2: Record user sign-off** + +When the user approves, update `docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md`: +- Change `[ ] User sign-off recorded` to `[x] User sign-off recorded (date: 2026-07-01)` +- Change `Status: completed (pending user sign-off)` to `Status: completed` + +Update `conductor/tracks/chronology_v2_20260701/state.toml`: +- `status = "completed"` +- `current_phase = 6` (all phases done) +- `phase_6` status → `"completed"` with checkpoint SHA + +- [ ] **Step 3: Final commit** + +```bash +git add docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md conductor/tracks/chronology_v2_20260701/state.toml +git commit -m "conductor(chronology_v2): user sign-off recorded — track complete" +``` + +--- + +## Self-Review Checklist (for the implementer) + +Before claiming the track is done, verify: + +- [ ] `conductor/chronology.md` has rows for ALL track folders (count them: `conductor/tracks/*` + `conductor/archive/*` directories = rows in chronology.md) +- [ ] No row in `conductor/chronology.md` has a summary starting with `**Priority:**`, `**Date:**`, `**Initialized:**`, `**Track:**`, `**Parent umbrella:**`, `**Status:**`, or `**Confidence:**` +- [ ] `conductor/tracks.md` has no `## Phase` headings (all history sections removed) +- [ ] `conductor/tracks.md` has no `## Archived` headings +- [ ] `conductor/tracks.md` has the `> Full project history:` pointer line +- [ ] `conductor/workflow.md` has the `## Chronology Maintenance` heading +- [ ] `scripts/audit/chronology_quality_gate.py --strict` exits 0 +- [ ] `tests/test_generate_chronology.py` + `tests/test_chronology_quality_gate.py` all pass +- [ ] `conductor/archive/chronology_20260619/state.toml` has `status = "superseded"` +- [ ] `conductor/tracks/superpowers_review_20260619/state.toml` `[blocked_by]` has no `chronology_20260619` entry \ No newline at end of file diff --git a/docs/superpowers/specs/2026-07-01-chronology-v2-redo-design.md b/docs/superpowers/specs/2026-07-01-chronology-v2-redo-design.md new file mode 100644 index 00000000..d944303b --- /dev/null +++ b/docs/superpowers/specs/2026-07-01-chronology-v2-redo-design.md @@ -0,0 +1,444 @@ +# Chronology v2 Redo — Design Spec + +**Date:** 2026-07-01 +**Track ID:** `chronology_v2_20260701` +**Priority:** A (meta-tooling / infrastructure) +**Status:** design (pre-spec) +**Ancestors:** +- `conductor/tracks/chronology_20260619/spec.md` (the v2 rewrite spec, 354 lines — designed but never executed) +- `docs/reports/2026-06-15/CHRONOLOGY_TRACK_HANDOVER_20260620.md` (the v1 failure report, 128 lines) +- `docs/reports/2026-06-15/CHRONOLOGY_MIGRATION_20260619.md` (the v1 migration report) +- `docs/reports/2026-06-15/TRACK_COMPLETION_chronology_20260619.md` (the v1 end-of-track report) + +## Overview + +The `chronology_20260619` track produced a broken `conductor/chronology.md` (v1): +167 of 216 rows had wrong status (the classifier read stale `metadata.json.status` +instead of git history), summaries were metadata-field text instead of track +descriptions, and the per-row cross-check was bypassed. A v2 rewrite was specced +and planned in detail but never executed. The track sits at `current_phase=10` +pending user sign-off that never came, blocking `superpowers_review_20260619`. + +This track is the redo: a **fresh track** that closes out the old one, adopts +the v2 design as a starting point, revises it for the current project state +(5+ days of desync, new track patterns, the tracks.md bloat), and executes it +through to a user sign-off that is actionable this time. + +## What v1 Got Wrong (from the records) + +Per `CHRONOLOGY_TRACK_HANDOVER_20260620.md`: + +1. **`_classify_status()` reads `metadata.json.status`** — a stale field set when + each track was created, rarely updated when work completed or was abandoned. + 167/216 rows had wrong status. +2. **Summaries are metadata-field text** (`**Priority:** A (foundational...)`, + `**Date:** 2026-06-20`) not actual track descriptions. +3. **Phase 8 per-row cross-check was bypassed** in favor of bulk structural + verification; the manual summary-adequacy check was partial (15-row sample). +4. **Phase 6 user review gate was bypassed** in the autonomous session. +5. **No quality gate** to detect a broken classifier before the chronology ships. +6. **No maintenance plan** — the chronology desynced within days because nobody + regenerated it after new tracks shipped. + +The five lessons from the handover (lines 88-98): +1. Bypassing the manual review clause was the original sin. +2. `metadata.json` is a snapshot, not a source of truth. +3. Git history is the project's audit log — use it. +4. Default to "when in doubt, ask" — the chronology is read by humans. +5. The user said "manual review" twice; both times an interpretation was found + to be less strict — listen to the literal request. + +## Goals + +1. Produce a correct `conductor/chronology.md` where every row's status is + backed by git-history evidence, not stale metadata. +2. Produce a per-row evidence artifact (the quality report) so the user can + audit the classification without re-deriving it. +3. Close out `chronology_20260619` (mark superseded, archive, unblock + `superpowers_review_20260619`). +4. De-gunk `conductor/tracks.md` — remove shipped/completed tracks from the + active queue, remove the Phase 0-9 history sections that duplicate + chronology.md, leave only the active queue + standby + a pointer. +5. Add a `conductor/workflow.md` maintenance rule so the chronology is + regenerated after each track ships (closes the desync root cause). +6. Ship a quality-gate script that catches a broken classifier before it + ships (closes the "no quality gate" root cause). + +## Non-Goals + +- Fixing or executing `superpowers_review_20260619` (just unblocking it). +- Changing how `metadata.json.status` is maintained going forward (the + classifier uses git history, not metadata; metadata staleness is no longer + the problem). +- Archiving the 66 folders in `conductor/tracks/` that are already shipped + (separate cleanup; the chronology indexes them regardless of location). +- Renaming or restructuring `conductor/archive/` (out of scope; the + chronology walks it as-is). +- A broader `workflow.md` review for other stale rules (the only workflow.md + change is the chronology maintenance section). + +## Design + +### 1. New track identity + old track close-out + +**New track:** `chronology_v2_20260701` (Priority A; meta-tooling/infrastructure). +Fresh track, not a continuation of `chronology_20260619`. + +**Old track close-out (Phase 1):** +- Mark `chronology_20260619` as superseded in its `state.toml` + (`status = "superseded"`, `current_phase = 10`, add a `[supersession]` + section pointing to `chronology_v2_20260701`). +- Update its tracks.md row (line 64) to reflect supersession. +- Archive `conductor/tracks/chronology_20260619/` → + `conductor/archive/chronology_20260619/`. The v2 spec/plan are preserved + in git history; the new track references them by commit SHA. +- **Unblock `superpowers_review_20260619`** — remove `chronology_20260619` + from its `state.toml` `[blocked_by]` entirely (no re-gating on the new + track). + +**v2 design adoption:** The new track's spec explicitly cites +`conductor/tracks/chronology_20260619/spec.md` (the v2 rewrite spec) and +`CHRONOLOGY_TRACK_HANDOVER_20260620.md` (the failure report) as its design +ancestors. It adopts the v2 status enum, the git-history classifier approach, +and the quality-gate concept — with the revisions below. + +### 2. The six revisions to v2 + +#### Revision 1 — The desync gap (regenerate from current filesystem) + +v2 was specced when the newest track was ~2026-06-20. The chronology now needs +to cover 5+ more days of tracks: the layout saga +(`default_layout_install_20260629`, `default_layout_extract_20260629`, +`default_layout_install_followup_20260629`), the MMA quarantine +(`mma_quarantine_rag_test_decoupling_20260701`), the module_taxonomy abort + +cleanup (`module_taxonomy_refactor_20260627`, `post_module_taxonomy_de_cruft_20260627`), +`cruft_elimination_20260627`, `directive_hotswap_harness_20260627`, +`enforcement_gap_closure_20260627`, `test_engine_integration_20260627`, +`fix_mma_concurrent_tracks_sim_20260627`, `type_alias_unfuck_20260626`, +`video_analysis_campaign_2_20260627`. + +**Change:** The new track's first generation pass runs against the **current** +filesystem (all `conductor/tracks/` + `conductor/archive/` as of execution +day), not the 2026-06-19 snapshot. The generation script walks both directories +fresh each run. + +#### Revision 2 — `superpowers_review_20260619` blocker resolution + +v2 didn't address this because it was rewriting the same track in place. The +new track explicitly closes out `chronology_20260619` and removes it from +`superpowers_review_20260619/state.toml` `[blocked_by]` (no re-gating). + +#### Revision 3 — Classifier heuristics updated for recent track patterns + +v2's 5-step git-history algorithm was designed 2026-06-20. Since then, new +patterns emerged that it would misclassify: + +- **Aborted tracks** (`module_taxonomy_refactor_20260627`): many + `conductor(track):` + `conductor(plan):` commits but a + `TRACK_ABORTED_*.md` report — classifier must detect the abort report as + an `Abandoned`/`Superseded` signal. +- **Phase 9 patches after "completion"** (`result_migration_cruft_removal_20260620`): + a track that "shipped" then got a patch commit days later — classifier must + look at the latest commit, not just count. +- **Tier 2 autonomous tracks**: produce many `conductor(plan):` commits (one + per task) — the "feat/fix/refactor vs chore/docs" heuristic must not count + `conductor(plan):` as a work commit. +- **Follow-up tracks** (`default_layout_install_followup_20260629`): short, + few commits, but legitimately `Completed` — the "0-1 commits + >14 days + old = Abandoned" rule would misfire. + +**Change:** The classifier's commit-message pattern list is extended: + +- `conductor(plan):`, `conductor(state):`, `conductor(track):`, + `docs(spec):`, `docs(plan):` are **metadata commits**, not work commits + (don't count toward the "≥3 work commits = Completed" threshold). +- `feat:`, `fix:`, `refactor:`, `perf:`, `test:`, `docs(report):` are work + commits. +- Presence of `TRACK_ABORTED_*.md` or `TRACK_COMPLETION_*.md` in + `docs/reports/` matching the track ID is a **strong signal** that + overrides commit-count heuristics. +- The "last commit > 14 days = Abandoned" rule is **removed**; replaced + with "no work commits AND no completion/abort report = Needs Review". +- Confidence is reported per-row; anything below a threshold goes to the + Needs Review queue for manual classification. + +#### Revision 4 — tracks.md de-gunk + +v2's scope was only chronology.md. The new track also restructures +`conductor/tracks.md`: + +**Current state (96KB, bloated):** +- 60-row "Active Tracks (Current Queue)" table — ~40 of these rows are + shipped/completed tracks that belong in history, not the active queue. +- Phase 0-9 chronological sections with Completed/Archived subsections — + duplicates chronology.md. +- 4 backlog/follow-up sections — some entries are shipped, some pending. +- "Recently Shipped Tracks (2026-06-29)" section at the bottom. + +**Target state:** +- **Section 1: Active Queue** — only tracks that are genuinely unblocked and + ready to start OR in-progress. Shipped tracks are removed (they're in + chronology.md). Each row: `| # | Priority | Track | Status | Blocked By |` + (same columns, filtered to active-only). +- **Section 2: Standby / Pending Spec** — tracks with spec TBD or pending + decision (the backlog). Same columns. +- **Section 3: Pointer** — one line: + `> Full project history: see [chronology.md](./chronology.md)` +- **Delete:** Phase 0-9 sections, Completed/Archived subsections, backlog/ + follow-up sections that duplicate chronology.md, the "Recently Shipped" + section, the "Archived (Closed 2026-06-23)" video analysis section. +- **Keep:** the "Editing this file" / archiving convention notes at the + bottom (from v1 Phase 4). + +**Migration safety:** the full tracks.md is preserved in git history; the +de-gunk is a single commit. If anything is lost, +`git show HEAD~1:conductor/tracks.md` recovers it. + +#### Revision 5 — workflow.md maintenance rule + +v2 had no maintenance plan (the root cause of the desync). The new track adds +a section to `conductor/workflow.md`: + +**New subsection under "Documentation Refresh Protocol"** (or a new top-level +section "Chronology Maintenance"): + +> **Chronology regeneration cadence.** After every track ships (completion +> commit + TRACK_COMPLETION report), the implementing agent must run +> `uv run python scripts/audit/generate_chronology.py` to regenerate +> `conductor/chronology.md`. The regeneration is a single atomic commit +> (`docs(chronology): regenerate after shipped`). If the +> regeneration produces a diff beyond the new row (e.g., status changes on +> other rows), the agent must investigate before committing — a status drift +> on an unrelated row indicates a stale classifier, not a chronology bug. +> +> **Quality gate.** `scripts/audit/chronology_quality_gate.py` runs as part +> of the regeneration. It fails (exit 1) if >30% of rows are classified as +> `Needs Review`. A failing quality gate blocks the regeneration commit. + +This makes regeneration a per-track-shipping obligation, not a one-shot. + +#### Revision 6 — Report(s) + +Two reports: + +1. **`docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md`** — the + standard end-of-track report (what was done, files changed, verification + results). +2. **`docs/reports/CHRONOLOGY_QUALITY_20260701.md`** — the chronology-quality + report (new, not in v1). Contents: + - Total rows generated + breakdown by status (Active / In Progress / + Completed / Abandoned / Superseded / Special / Needs Review) + - Confidence distribution (high / medium / low) + - The Needs Review queue (list of rows that need manual classification, + with the evidence the classifier found) + - Comparison vs v1 (row count delta, status-correction count: "N rows + changed status vs v1") + - The desync gap closed (list of tracks added that were missing from v1) + - Classifier heuristics summary (which patterns matched, which were + overridden by completion/abort reports) + +The quality report is the evidence artifact — it's what makes this track +auditable rather than "trust the script." v1 failed because there was no +quality gate and no evidence per row; this report is the fix. + +### 3. Architecture — the generation script + quality gate + +#### `scripts/audit/generate_chronology.py` (rewritten) + +**Inputs:** `conductor/tracks/` + `conductor/archive/` (walked fresh each +run); `git log` per folder for commit evidence; `docs/reports/TRACK_COMPLETION_*.md` ++ `TRACK_ABORTED_*.md` for override signals. + +**Extraction pipeline (per folder):** +1. **Date** — slug date from folder name (regex, unchanged from v1). +2. **ID** — folder name (unchanged). +3. **Status** — the new classifier (see below), returns + `(status, confidence, reason)`. +4. **Summary** — rewritten extractor: rejects lines starting with + `**Priority:**`, `**Date:**`, `**Initialized:**`, `**Track:**`, + `**Parent umbrella:**`, `**Status:**`, `**Confidence:**`; prefers + `metadata.json.description` if it's actual prose (not metadata-field + text); falls back to first non-heading, non-metadata line of `spec.md`; + truncates to 25 words. +5. **Folder** — path (unchanged). +6. **Range** — `git log --oneline -- ` → first + last SHA + count. + +**The new classifier (`_classify_status`, returning `(status, confidence, reason)`):** + +Evidence sources, in priority order: + +1. **Override signals (highest confidence):** + - `TRACK_COMPLETION_*.md` exists in `docs/reports/` matching this track + ID → `Completed`, confidence=high, reason="completion report found". + - `TRACK_ABORTED_*.md` exists → `Abandoned`, confidence=high, + reason="abort report found". (If `state.toml` also says `superseded`, + the `Superseded` classification wins — see next row.) + - `state.toml` `status = "superseded"` → `Superseded`, + confidence=high (overrides the abort-report signal if both exist). +2. **Git commit evidence (medium confidence):** + - Count work commits (`feat/fix/refactor/perf/test/docs(report):` prefixes) + via `git log --oneline -- `, excluding metadata commits + (`conductor(plan):`, `conductor(state):`, `conductor(track):`, + `docs(spec):`, `docs(plan):`). + - ≥3 work commits → `Completed`, confidence=medium, reason="N work commits". + - 1-2 work commits + in `tracks/` → `In Progress`, confidence=medium. + - 0 work commits + in `tracks/` → `Active` (spec/plan only), + confidence=medium. +3. **Directory location (low confidence):** + - In `archive/` + no override signal → `Completed`, confidence=low, + reason="archived but no completion report". + - In `archive/` + 0 commits → `Abandoned`, confidence=low, + reason="archived with 0 commits". +4. **Fallback:** `Needs Review`, confidence=none, + reason="classifier inconclusive". + +**Status enum:** `Active` / `In Progress` / `Completed` / `Abandoned` / +`Superseded` / `Special` / `Needs Review` (7 values; v2 had 5, adding +`Superseded` + `Needs Review`). + +**Output format:** Markdown table with 6 columns (Date, ID, Status, Summary, +Folder, Range) + a **"Needs Review" section** at the bottom listing rows with +`Needs Review` status, each with its evidence reason. Sorted newest-first. A +preamble header with generation date + row count. + +#### `scripts/audit/chronology_quality_gate.py` (new) + +**Purpose:** detect a broken classifier before the chronology ships. + +**Checks:** +- **Needs Review threshold:** if >30% of rows are `Needs Review`, exit 1 + (the classifier is failing on too many rows). +- **Status distribution sanity:** if 0 rows are `Completed`, exit 1 (the + classifier is misclassifying everything). +- **Summary quality:** if >20% of summaries still contain metadata-field + text (`**Priority:**` etc.), exit 1 (the summary extractor is broken). +- **Per-row evidence:** every row must have a non-empty `reason` from the + classifier; if any row has no reason, exit 1. + +**Modes:** default informational (exits 0, prints report); `--strict` CI +gate (exits 1 on any violation). Follows the project's audit-script +convention (per `conductor/workflow.md` "Audit Script Policy"). + +#### Tests (TDD) + +`tests/test_generate_chronology.py` (rewritten) + +`tests/test_chronology_quality_gate.py` (new). Tests for: +- The classifier's 7 status values + the evidence priority chain (override + signals > git evidence > directory > fallback). +- The summary extractor's rejection of metadata-field lines. +- The quality gate's 4 checks. +- Edge cases: aborted tracks with completion reports (override conflict), + tracks with 0 commits, archive folders with no metadata.json. + +### 4. Execution plan structure (phases) + +6 phases, each a checkpoint with atomic per-task commits. + +#### Phase 1: Close out the old track + scaffold the new one +- Task 1.1: Update `chronology_20260619/state.toml` → + `status = "superseded"`, add `[supersession]` section. Commit. +- Task 1.2: Update `chronology_20260619` row in tracks.md (line 64) to + "superseded by `chronology_v2_20260701`". Commit. +- Task 1.3: Archive `conductor/tracks/chronology_20260619/` → + `conductor/archive/chronology_20260619/`. Commit. +- Task 1.4: Update `superpowers_review_20260619/state.toml` `[blocked_by]` — + remove `chronology_20260619` entirely. Commit. +- Task 1.5: Create `conductor/tracks/chronology_v2_20260701/` with + `spec.md`, `metadata.json`, `state.toml`, `plan.md`. Commit. + +#### Phase 2: TDD the classifier + quality gate (Red) +- Task 2.1: Write `tests/test_generate_chronology.py` — tests for the + 7-status classifier, evidence priority chain, summary extractor. Red. +- Task 2.2: Write `tests/test_chronology_quality_gate.py` — tests for the + 4 quality-gate checks. Red. + +#### Phase 3: Implement the classifier + quality gate (Green) +- Task 3.1: Rewrite `scripts/audit/generate_chronology.py` — the new + `_classify_status` returning `(status, confidence, reason)`, the + rewritten summary extractor, the git-history evidence pipeline. Green. +- Task 3.2: Create `scripts/audit/chronology_quality_gate.py` — the 4 + checks + `--strict` mode. Green. + +#### Phase 4: Regenerate chronology.md + write the quality report +- Task 4.1: Run the generator against the current filesystem. Capture + output to `conductor/chronology.md` (replacing v1). Commit. +- Task 4.2: Run the quality gate. If it fails, iterate on the classifier + (back to Phase 3) until it passes. Commit the passing state. +- Task 4.3: Write `docs/reports/CHRONOLOGY_QUALITY_20260701.md` — the + quality report. Commit. + +#### Phase 5: De-gunk tracks.md + add workflow.md maintenance rule +- Task 5.1: Restructure `conductor/tracks.md` — remove shipped/completed + rows from the active queue, remove Phase 0-9 history sections, remove + backlog/follow-up sections that duplicate chronology.md, add the pointer + to chronology.md, keep the "Editing this file" notes. Single commit. +- Task 5.2: Add the "Chronology Maintenance" section to + `conductor/workflow.md` — the regeneration cadence + quality gate + obligation. Commit. + +#### Phase 6: Verification + end-of-track report +- Task 6.1: Run the quality gate `--strict` mode. Confirm exit 0. Commit. +- Task 6.2: Verify the Needs Review queue is empty or small (the user + reviews any remaining rows). Commit. +- Task 6.3: Write + `docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md`. Commit. +- Task 6.4: User sign-off (the final gate — same as v1's Phase 10, but + this time the quality report + evidence per row makes it actionable). + +### Commit strategy + +- Per-task atomic commits (no batching). +- Git notes per commit (task summary). +- Phase checkpoints after each phase (per the workflow protocol). + +## Verification Criteria + +1. `conductor/chronology.md` exists with one row per track folder (tracks/ + + archive/), sorted newest-first, 6 columns, generated from the current + filesystem (no 2026-06-19 snapshot pin). +2. Every row's status is backed by git-history evidence (not + `metadata.json.status`); the evidence `reason` is non-empty for every + row. +3. No summary contains metadata-field text (`**Priority:**`, `**Date:**`, + `**Initialized:**`, `**Track:**`, `**Parent umbrella:**`, + `**Status:**`, `**Confidence:**`). +4. `scripts/audit/chronology_quality_gate.py --strict` exits 0. +5. `conductor/tracks.md` contains only the active queue + standby/pending + + a pointer to chronology.md + the "Editing this file" notes. No Phase 0-9 + history sections, no shipped-track rows in the active queue. +6. `conductor/workflow.md` contains the "Chronology Maintenance" section + (regeneration cadence + quality gate obligation). +7. `docs/reports/CHRONOLOGY_QUALITY_20260701.md` exists with the status + distribution, confidence distribution, Needs Review queue, v1 + comparison, desync gap list, and heuristics summary. +8. `docs/reports/TRACK_COMPLETION_chronology_v2_20260701.md` exists. +9. `chronology_20260619` is archived (in `conductor/archive/`) with + `status = "superseded"` in its state.toml. +10. `superpowers_review_20260619/state.toml` `[blocked_by]` no longer + contains `chronology_20260619`. +11. `tests/test_generate_chronology.py` + + `tests/test_chronology_quality_gate.py` pass. +12. User sign-off recorded in the TRACK_COMPLETION report. + +## Risks + +- **R1 (medium):** The git-history classifier may still misclassify some + edge cases (e.g., tracks with `conductor(checkpoint):` commits only). + Mitigation: the Needs Review queue surfaces these for manual + classification; the quality gate fails if >30% are Needs Review. +- **R2 (medium):** The tracks.md de-gunk may accidentally remove a row + that's still active. Mitigation: the full tracks.md is preserved in git + history; recovery is `git show HEAD~1:conductor/tracks.md`. +- **R3 (low):** The workflow.md maintenance rule may not be followed by + future agents. Mitigation: the rule is in the operational workflow doc + that agents read at session start; the quality gate catches a desync + when the next regeneration runs. + +## Out of Scope + +- Fixing or executing `superpowers_review_20260619` (just unblocking it). +- Changing how `metadata.json.status` is maintained going forward. +- Archiving the 66 folders in `conductor/tracks/` that are already shipped. +- Renaming or restructuring `conductor/archive/`. +- A broader `workflow.md` review for other stale rules. +- The `superpowers_review_20260619` track's execution. \ No newline at end of file