docs(chronology): switch to manual maintenance; delete generator scripts; archive review report
Deleted scripts/audit/generate_chronology.py + chronology_quality_gate.py after repeated corruption incidents (auto-classifier drifted from user intent, silently rewrote rows). chronology.md is now maintained by hand: add a row at the top when a track ships/abandons/is archived. Updated conductor/workflow.md §Chronology Maintenance and conductor/tracks.md §Archiving a track to reflect manual maintenance. Added 40 rows to conductor/chronology.md for the 2026-07-05 archive batch, with archive-folder paths and deferral notes where applicable. Removed 12 archived-track rows from conductor/tracks.md Active Tracks table (rows 2, 3, 7c, 16, 17, 23b, 23c, 23d, 25, 26, 29, 29c) and their track-detail anchors. Wrote docs/reports/ARCHIVE_REVIEW_20260705.md: a categorized index of unfinished work items from the archived tracks' state.toml files that remain relevant to the codebase, with a ranked follow-up list.
This commit is contained in:
+42
-1
@@ -1,7 +1,48 @@
|
||||
<!-- Generated 2026-07-02 | 244 rows -->
|
||||
<!-- Manually maintained (since 2026-07-05, when the generator scripts were deleted after repeated corruption incidents) -->
|
||||
|
||||
| Date | ID | Status | Summary | Folder | Range |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-07-05 | `superpowers_review_apply_high_20260705` | Completed | Phase 1: `26dd9258` Session Start Checklist item 13 added; Phase 2: `670a919e` tests/test_mma_skill_discipline.py (25 cases); Phase 3: `5037f48f` cross-reference + finalization. | `conductor/archive/superpowers_review_apply_high_20260705` | `0522252f..5037f48f` (3) |
|
||||
| 2026-07-05 | `superpowers_review_20260619` | Completed | 16-section review of the superpowers skill suite: 14 PARITY / 3 PARTIAL / 2 ARCH-DIFF / 1 GAP; 25 decisions logged; 3 side artifacts (decisions.md, comparison_table.md, nagent_takeaways). | `conductor/archive/superpowers_review_20260619` | `eb272cb7..137868a1` (40) |
|
||||
| 2026-07-05 | `agent_directives_consolidation_20260705` | Completed | AGENTS.md reduced from 202→87 lines; conductor/workflow.md §Process Anti-Patterns promoted to canonical home; conductor/edit_workflow.md §9 + product-guidelines.md subsections thin-pointed to canonical styleguides; 9 atomic commits with git notes. | `conductor/archive/agent_directives_consolidation_20260705` | `48ddd15c..8ab82022` (4) |
|
||||
| 2026-07-05 | `test_sandbox_hardening_20260619` | Completed | Hard file-I/O sandbox for the test suite so a misbehaving test cannot leak artifacts into the working tree. Adds FR1 runtime guard + addopts redirect to `tests/artifacts/_pytest_tmp`. | `conductor/archive/test_sandbox_hardening_20260619` | `ec0716c9..eec44a09` (9) |
|
||||
| 2026-07-05 | `test_patch_fixes_20260513` | Abandoned | After the refactor to use `ai_client_stub` as the module alias for `app_controller`, several tests fail because they use `patch('src.ai_client.X')`. Stale; superseded by later test_infrastructure_hardening + result_migration waves. | `conductor/archive/test_patch_fixes_20260513` | `12f16e9a..12f16e9a` (1) |
|
||||
| 2026-07-05 | `send_result_to_send_20260616` | Completed | Tier 2 migration of the `send()` public API to `Result[str, ErrorInfo]` return type, replacing the str/None return. | `conductor/archive/send_result_to_send_20260616` | `c1d9a966..e2e57036` (15) |
|
||||
| 2026-07-05 | `post_module_taxonomy_de_cruft_20260627` | Completed | Followup to module_taxonomy_refactor_20260627: removed leftover cruft, dead imports, and stale references after the models.py split. | `conductor/archive/post_module_taxonomy_de_cruft_20260627` | `05647d94..e4f652a7` (3) |
|
||||
| 2026-07-05 | `phase2_4_5_call_site_completion_20260621` | Completed | Tier 2 autonomous completion of the phase 2/4/5 call-site migration left over from any_type_componentization_20260621. | `conductor/archive/phase2_4_5_call_site_completion_20260621` | `1a739ece..6e734a49` (3) |
|
||||
| 2026-07-05 | `cruft_elimination_20260627` | Partially Completed | Goal: make Python behave as close to C11/Odin/Jai as possible. Phases 2, 4, 5, 6, 7 DEFERRED (ProjectContext dataclass; _do_generate return type; rag_engine.search() return type; 30 Optional[T] sites; 69 Any/dict[str,Any] sites). | `conductor/archive/cruft_elimination_20260627` | `f219616f..0a65056f` (8) |
|
||||
| 2026-07-05 | `fable_review_20260617` | Completed | 10-cluster review of the "Fable" system-prompt artifact; 42 commits; 7-phase structure producing comparison_table, decisions, and per-cluster research notes. Section 16 (deferred nagent-rebuild recommendations) PENDING. | `conductor/archive/fable_review_20260617` | `058e2c93..22d3234b` (42) |
|
||||
| 2026-07-05 | `context_preview_fixes_20260516` | Abandoned | Fix critical failures in the context composition feature (Preview button + Inspect/Slices buttons). Superseded by later context_composition_ux_20260601 + structural_file_editor_20260601 work. | `conductor/archive/context_preview_fixes_20260516` | `45de48bc..2249606e` (5) |
|
||||
| 2026-07-05 | `public_api_migration_and_ui_polish_20260615` | Completed | First public-API migration sub-track: Result[T] adoption in the UI layer + polish. | `conductor/archive/public_api_migration_and_ui_polish_20260615` | `3febdab4..bbd4c7b5` (8) |
|
||||
| 2026-07-05 | `code_path_audit_polish_20260622` | Completed | Tight surgical follow-up to code_path_audit_20260607 v2 (the MVP brute-force state). | `conductor/archive/code_path_audit_polish_20260622` | `1e92fbe9..ca219163` (12) |
|
||||
| 2026-07-05 | `tier2_leak_prevention_20260620` | Completed | Prevents Tier 2 autonomous sandbox from leaking into AppData/Temp/Documents. Establishes the 3-layer hard-ban enforcement (OpenCode + OS + git hook). | `conductor/archive/tier2_leak_prevention_20260620` | `9224be7a..9224be7a` (1) |
|
||||
| 2026-07-05 | `type_alias_unfuck_20260626` | Completed | The MINIMAL track to fix the type-usage problem that metadata_promotion_20260624 left as a tar pit. Replaces opaque aliases with typed dataclasses; vc4_codepaths_drop metric deferred. | `conductor/archive/type_alias_unfuck_20260626` | `f47be0ec..c0f30f28` (3) |
|
||||
| 2026-07-05 | `data_oriented_error_handling_20260606` | Completed | The Fleury-pattern data-oriented error handling convention: Result[T] + NIL_T sentinels + ErrorInfo classification. Path C (mcp_client.py tool surface assert-removal) DEFERRED to follow-up. | `conductor/archive/data_oriented_error_handling_20260606` | `494f68f9..92cff705` (20) |
|
||||
| 2026-07-05 | `data_structure_strengthening_20260606` | Completed | TypeAlias conventions + per-aggregate dataclass migration. Adds the audit_weak_types.py gate. typed_dict_migration_followup + public_api_migration_followup sections retained. | `conductor/archive/data_structure_strengthening_20260606` | `ed42a97a..1dce3203` (8) |
|
||||
| 2026-07-05 | `code_path_audit_20260607` | Completed | First-pass brute-force code-path audit. Identifies NG2 (Optional[T] returns) and other anti-pattern sites; baseline for phase_2 + phase_3 + polish follow-ups. | `conductor/archive/code_path_audit_20260607` | `f069a8b2..5ac0618a` (22) |
|
||||
| 2026-07-05 | `code_path_audit_phase_2_20260624` | Completed | The actual follow-up to code_path_audit_20260607. Snapshots pre-Phase-3 baseline and addresses NG2 violations. | `conductor/archive/code_path_audit_phase_2_20260624` | `7c352e1c..705cb50d` (2) |
|
||||
| 2026-07-05 | `code_path_audit_phase_3_provider_state_20260624` | Completed | The fix for the 4 NG2 violations and 1 partial NG2 violation left by code_path_audit_phase_2_20260624. | `conductor/archive/code_path_audit_phase_3_provider_state_20260624` | `f7a29179..c3d575de` (10) |
|
||||
| 2026-07-05 | `result_migration_20260616` | Completed | Umbrella track for the public-API Result[T] migration campaign (8 sub-tracks; 7 shipped + 1 Active per 2026-07-02). | `conductor/archive/result_migration_20260616` | `4c0b19b4..9c30ef64` (14) |
|
||||
| 2026-07-05 | `result_migration_review_pass_20260617` | Completed | Audit + documentation pass over the Result[T] migration; informational, no production code change. | `conductor/archive/result_migration_review_pass_20260617` | `396eb82c..33479267` (19) |
|
||||
| 2026-07-05 | `result_migration_baseline_cleanup_20260620` | Completed | Final baseline cleanup of the Result[T] migration campaign; 18 commits. Phase 10-14 invariant tests marked in_progress (per state.toml). | `conductor/archive/result_migration_baseline_cleanup_20260620` | `e9016749..9c30ef64` (18) |
|
||||
| 2026-07-05 | `exception_handling_audit_20260616` | Completed | Audit of try/except/finally/raise sites per the data-oriented convention; adds the audit_exception_handling.py classifier + CI gate. | `conductor/archive/exception_handling_audit_20260616` | `e81413a2..ed660227` (5) |
|
||||
| 2026-07-05 | `fix_mma_concurrent_tracks_sim_20260627` | Completed | Single-test fix track addressing the 1 remaining tier-3 failure after post_module_taxonomy_de_cruft_20260627. | `conductor/archive/fix_mma_concurrent_tracks_sim_20260627` | `ee185758..9d22c37c` (4) |
|
||||
| 2026-07-05 | `fix_test_failures_20260624` | Completed | 3 surgical fixes to make the full test suite green after the polish merge. | `conductor/archive/fix_test_failures_20260624` | `7a9261c4..45876aef` (7) |
|
||||
| 2026-07-05 | `live_gui_test_fixes_20260618` | Completed | Addresses 2 test failures reported as "documented issues" by result_migration_small_files_20260617 Phase 13. | `conductor/archive/live_gui_test_fixes_20260618` | `ff40138f..6ce55cba` (2) |
|
||||
| 2026-07-05 | `default_layout_extract_20260629` | Completed | Extract tier-2's GOOD work on the default layout setup (layouts/ directory, install-on-empty-INI helpers, pre-run install timing fix, orphan-end-child cleanup). panel_defs_fleury_migration + render_persona_editor_window_fix DEFERRED. | `conductor/archive/default_layout_extract_20260629` | `466d2656..410d81fb` (2) |
|
||||
| 2026-07-05 | `default_layout_install_20260629` | Completed | Fix GUI panels becoming invisible at startup whenever manualslop_layout.ini is missing/empty/stale. User Manual Verification DEFERRED. | `conductor/archive/default_layout_install_20260629` | `5ad062b1..4bf5ecd6` (10) |
|
||||
| 2026-07-05 | `default_layout_install_followup_20260629` | Completed | Followup to default_layout_install_20260629: restore docking structure (strip stale dockspace IDs, force live-session apply). | `conductor/archive/default_layout_install_followup_20260629` | `34538639..4bf5ecd6` (2) |
|
||||
| 2026-07-05 | `doeh_test_thinking_cleanup_20260615` | Completed | Cleanup of test-thinking-trace handling after the data-oriented error handling convention landed. | `conductor/archive/doeh_test_thinking_cleanup_20260615` | `925e366c..a8c81251` (5) |
|
||||
| 2026-07-05 | `metadata_nil_sentinel_20260624` | Completed | Child 1 of metadata_ssdl_defusing_20260624 campaign. Introduces NIL_T sentinels for the Metadata aggregate. | `conductor/archive/metadata_nil_sentinel_20260624` | `84c0b4ec..7c352e1c` (3) |
|
||||
| 2026-07-05 | `metadata_promotion_20260624` | Partially Completed | Promotes 5 sub-aggregates (CommsLogEntry, HistoryMessage, FileItem, ToolDefinition, ToolCall) to typed dataclasses. 3 open blockers (rag return type, tool builders dicts, drifted field types) deferred to follow-up tracks. | `conductor/archive/metadata_promotion_20260624` | `e50bebdd..b4bd772d` (16) |
|
||||
| 2026-07-05 | `ai_loop_regressions_20260614` | Completed | Fixes regressions in the AI loop after the Result[T] migration; 11 commits. | `conductor/archive/ai_loop_regressions_20260614` | `7a4dcc96..6edeb2b5` (11) |
|
||||
| 2026-07-05 | `module_taxonomy_refactor_20260627` | Completed | The user-reported models.py "dumping ground" (1044 lines, 36 classes, 5+ unrelated domains). Splits into per-system files (mma.py, project_files.py, type_aliases.py, mcp_tool_specs.py, result_types.py). | `conductor/archive/module_taxonomy_refactor_20260627` | `cba6e7d7..91a61288` (13) |
|
||||
| 2026-07-05 | `any_type_componentization_20260621` | Partially Completed | Componentize `Any`-typed call sites across src/. The phase 2/4/5 call-site completion was deferred to phase2_4_5_call_site_completion_20260621 (shipped). | `conductor/archive/any_type_componentization_20260621` | `ed0d198a..751b94d4` (12) |
|
||||
| 2026-07-05 | `chronology_v2_20260701` | Completed | The v2 chronology rewrite spec (354 lines — designed but never executed as a track; the v2 design informed the manual-maintenance switch on 2026-07-05). | `conductor/archive/chronology_v2_20260701` | `c1da0f99..c6593278` (6) |
|
||||
| 2026-07-05 | `sqlite_docs_gui_2_20260612` | Completed | SQLite-granularity inline docs for gui_2.py (the original gui_2 doc track). | `conductor/archive/sqlite_docs_gui_2_20260612` | `99e7b6e8..56e1950b` (8) |
|
||||
| 2026-07-05 | `sqlite_docs_gui_2_continued_20260613` | Completed | The ai_client.py continuation of the SQLite-granularity inline docs campaign. | `conductor/archive/sqlite_docs_gui_2_continued_20260613` | `cb129aae..e02a865d` (3) |
|
||||
| 2026-07-05 | `directive_hotswap_harness_20260627` | Partially Completed | Harness for hot-swapping agent directive presets at session warm. Phase 3.2 (manual verification: does the LLM follow warm-with) DEFERRED to user per directive. 3 deferred + 2 in_progress per state.toml. | `conductor/archive/directive_hotswap_harness_20260627` | `03c7cfd5..03c7cfd5` (1) |
|
||||
| 2026-07-02 | `intent_dsl_survey_20260612` | Completed | Intent-based scripting-languages survey (spec); Tier 1 authored, Tier 2 executed. 12 commits. | `conductor/tracks/intent_dsl_survey_20260612` | `b389f1be..45144872` (12) |
|
||||
| 2026-07-01 | `chronology_v2_20260701` | Completed | - `conductor/tracks/chronology_20260619/spec.md` (the v2 rewrite spec, 354 lines — designed but never executed) | `conductor/tracks/chronology_v2_20260701` | `c1da0f99..c6593278` (6) |
|
||||
| 2026-07-01 | `mma_quarantine_rag_test_decoupling_20260701` | Active | Two surgical interventions: | `conductor/tracks/mma_quarantine_rag_test_decoupling_20260701` | `e5f37e74..e5f37e74` (1) |
|
||||
| 2026-06-29 | `default_layout_extract_20260629` | In Progress | Extract tier-2's GOOD work on the default layout setup (the `layouts/` directory, the install-on-empty-INI helpers, the pre-run install timing fix, and the orphan-end-child cleanup) into… | `conductor/tracks/default_layout_extract_20260629` | `466d2656..410d81fb` (2) |
|
||||
|
||||
+4
-30
@@ -18,45 +18,19 @@ Tracks that are unblocked and ready to start. Ordered by **dependency** (blocked
|
||||
| # | 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) | **Completed** (per chronology.md; Phase 6 follow-up shipped) — row retained for cross-ref; archive candidate | test_infrastructure_hardening_20260609 (merged) |
|
||||
| 3 | A | [Data-Oriented Error Handling (Fleury Pattern)](#track-data-oriented-error-handling) | **Completed** (per chronology.md) — archive candidate | test_infrastructure_hardening_20260609 (merged), qwen_llama_grok |
|
||||
| 4 | A | [MCP Architecture Refactor (Sub-MCP Extraction)](#track-mcp-architecture-refactor) | 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) | **Completed** (per chronology.md) — archive candidate | test_infrastructure_hardening_20260609 (merged), any_type_componentization (shipped), phase2_4_5_call_site_completion (shipped) |
|
||||
| 4 | A | [MCP Architecture Refactor (Sub-MCP Extraction)](#track-mcp-architecture-refactor) | spec ✓, plan pending | (none) |
|
||||
| 22b | A | [Meta-Tooling Workflow Review](#track-meta-tooling-workflow-review) | spec ✓, plan ✓, parked | (none) |
|
||||
| 23 | A | [Intent-Based Scripting Languages Survey](#track-intent-dsl-survey) | spec ✓, plan pending | (none) |
|
||||
| 23b | B | [Superpowers Skills Review](#track-superpowers-skills-review) | **Completed** (Phase 10 finalize `deecd931`; current_phase=10; 16 sections + 3 side artifacts; 25 decisions; verdict distribution 14 PARITY / 3 PARTIAL / 2 ARCH-DIFF / 1 GAP) — archive candidate | (none) |
|
||||
| 23c | B | [Apply HIGH-Priority Recommendations](#track-superpowers-apply-high) | **Completed** (Phase 1: `26dd9258` Session Start Checklist item 13 added; Phase 2: `670a919e` tests/test_mma_skill_discipline.py (25 cases); Phase 3: `5037f48f` cross-reference + finalization) — archive candidate | superpowers_review_20260619 (shipped) |
|
||||
| 23d | B | [Agent Directives Consolidation](#track-agent-directives-consolidation) | **Completed** (4 phases: AGENTS.md reduced from 202→87 lines; conductor/workflow.md §Process Anti-Patterns promoted to canonical home; conductor/edit_workflow.md §9 + product-guidelines.md subsections thin-pointed to canonical styleguides; 9 atomic commits with git notes) — archive candidate | (none) |
|
||||
| 25 | B | [Fable System Prompt Review](#track-fable-review) | **Completed** (shipped 2026-06-17 per Phase 7 commit `22d3234b`; state.toml stale) — retained in tracks/ per user; will be archived later; reports kept for future reference | (none) |
|
||||
| 26 | A | [Video Analysis Campaign (Pass 1 of 3)](#track-video-analysis-campaign) | **Completed** (archived 2026-06-24 → `archive/analysis/` with 22 sibling video_analysis tracks) — archive candidate | (none) |
|
||||
| 29 | A | [Video Analysis De-obfuscation Campaign (Pass 2 of 3)](#track-video-analysis-deob) | **Completed** (archived 2026-06-24 → `archive/analysis/`) — archive candidate | (none) |
|
||||
| 29c | A | [Pass 3 — C11/Python Projection](#track-pass-3-c11python-projection) | **Completed** (archived 2026-06-24 → `archive/analysis/`) — archive candidate | 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) | **Completed** (shipped 2026-06-13 per commit `e02a865d`; archived) — archive candidate | (none) |
|
||||
| 19 | — | [Context First Message Fix](#track-context-first-message-fix) | spec TBD | (none) |
|
||||
|
||||
> **Stale-row cleanup (2026-07-02):** rows 2, 3, 17 marked Completed per `conductor/chronology.md` (which is the canonical status source). They are retained here as archive candidates pending a `git mv` to `conductor/archive/`. `data_structure_strengthening_20260606` (also Completed per chronology) was already dropped from this table.
|
||||
|
||||
### Track detail anchors
|
||||
|
||||
- **MMA Quarantine + RAG Test Decoupling** → `conductor/tracks/mma_quarantine_rag_test_decoupling_20260701/`
|
||||
- **Qwen, Llama & Grok Vendor Integration** → `conductor/tracks/qwen_llama_grok_integration_20260606/` (Phase 6 in progress)
|
||||
- **Data-Oriented Error Handling** → `conductor/tracks/data_oriented_error_handling_20260606/`
|
||||
- **MCP Architecture Refactor** → `conductor/tracks/mcp_architecture_refactor_20260606/`
|
||||
- **Test Sandbox Hardening** → `conductor/tracks/test_sandbox_hardening_20260619/`
|
||||
- **Code Path Audit** → `conductor/tracks/code_path_audit_20260607/`
|
||||
- **Meta-Tooling Workflow Review** → `conductor/tracks/meta_tooling_workflow_review_20260620/`
|
||||
- **Intent-Based Scripting Languages Survey** → `conductor/tracks/intent_dsl_survey_20260612/`
|
||||
- **Superpowers Skills Review** → `conductor/tracks/superpowers_review_20260619/`
|
||||
- **Apply HIGH-Priority Recommendations** → `conductor/tracks/superpowers_review_apply_high_20260705/`
|
||||
- **Agent Directives Consolidation** → `conductor/tracks/agent_directives_consolidation_20260705/`
|
||||
- **Fable System Prompt Review** → `conductor/tracks/fable_review_20260617/`
|
||||
- **Video Analysis Campaign (Pass 1)** → `conductor/tracks/video_analysis_campaign_20260621/`
|
||||
- **Video Analysis De-obfuscation (Pass 2)** → `conductor/tracks/video_analysis_deob_20260621/`
|
||||
- **Pass 3 — C11/Python Projection** → `conductor/tracks/video_analysis_deob_pass3_20260623/`
|
||||
- **UI Polish (Five Issues)** → `conductor/tracks/` (see spec for details)
|
||||
- **SQLite-Granularity Inline Docs for ai_client.py** → `conductor/archive/sqlite_docs_gui_2_continued_20260613/` (7c is the ai_client continuation; the original gui_2 track is `conductor/archive/sqlite_docs_gui_2_20260612/`)
|
||||
- **Context First Message Fix** → `conductor/tracks/context_first_message_fix_20260604/`
|
||||
|
||||
---
|
||||
@@ -65,7 +39,7 @@ Tracks that are unblocked and ready to start. Ordered by **dependency** (blocked
|
||||
|
||||
| # | Priority | Track | Status | Blocked By |
|
||||
|---|---|---|---|---|
|
||||
| 6 | D | [Public API Result Migration](#track-public-api-result-migration) | **Largely Completed** (umbrella + 6 sub-tracks shipped per chronology): `public_api_migration_and_ui_polish_20260615` ✓, `send_result_to_send_20260616` ✓, `result_migration_review_pass_20260617` ✓, `result_migration_small_files_20260617` ✓, `result_migration_app_controller_20260618` ✓, `result_migration_gui_2_20260619` ✓, `result_migration_baseline_cleanup_20260620` ✓; only `result_migration_cruft_removal_20260620` still Active (Round 4 corrections applied; user-confirmed closeout; state.toml stale) | data_oriented_error_handling (deprecated send()) |
|
||||
| 6 | D | [Public API Result Migration](#track-public-api-result-migration) | **Largely Completed** (umbrella + 6 sub-tracks shipped per chronology): `public_api_migration_and_ui_polish_20260615` ✓, `send_result_to_send_20260616` ✓, `result_migration_review_pass_20260617` ✓, `result_migration_small_files_20260617` ✓, `result_migration_app_controller_20260618` ✓, `result_migration_gui_2_20260619` ✓, `result_migration_baseline_cleanup_20260620` ✓; only `result_migration_cruft_removal_20260620` still Active (Round 4 corrections applied; user-confirmed closeout; state.toml stale) | (none) |
|
||||
| 8 | — | [Bootstrap gencpp Python Bindings](#track-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) |
|
||||
@@ -97,7 +71,7 @@ Tracks that are unblocked and ready to start. Ordered by **dependency** (blocked
|
||||
|
||||
---
|
||||
|
||||
> **Full project history:** see [`chronology.md`](./chronology.md) — the canonical index of all 244 tracks (active, shipped, superseded, abandoned), sorted newest-first, with git-history-evidence-backed status per row. Generated by `scripts/audit/generate_chronology.py`; quality-gated by `scripts/audit/chronology_quality_gate.py`.
|
||||
> **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. Maintained manually (the generator scripts were deleted 2026-07-05 after repeated corruption incidents).
|
||||
|
||||
---
|
||||
|
||||
@@ -118,4 +92,4 @@ Tracks that are unblocked and ready to start. Ordered by **dependency** (blocked
|
||||
**Archiving a track (3 steps):** When a track ships and its folder moves from `conductor/tracks/<id>/` to `conductor/archive/<id>/`, complete all 3 steps in order:
|
||||
1. Move the folder: `git mv conductor/tracks/<id> conductor/archive/<id>` (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 --draft > conductor/chronology.md` to regenerate the chronology (the new row appears automatically). Commit with `docs(chronology): regenerate after <track-id> shipped`. Run `uv run python -m scripts.audit.chronology_quality_gate --strict` before committing to verify the quality gate passes.
|
||||
3. Add a row to `conductor/chronology.md` (top of the table, newest-first) summarizing the track: Date, ID, Status, one-line Summary, Folder path, commit Range. Commit with `docs(chronology): add row for <track-id> archive`. (Chronology is maintained manually as of 2026-07-05 — the generator scripts were deleted after repeated corruption incidents.)
|
||||
@@ -624,17 +624,15 @@ The recommended execution order is the topological sort of the `blocked_by` grap
|
||||
|
||||
## 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 --draft > conductor/chronology.md` to regenerate `conductor/chronology.md`. The regeneration is a single atomic commit (`docs(chronology): regenerate after <track-id> 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.
|
||||
**Manual maintenance (changed 2026-07-05).** The chronology generator scripts (`scripts/audit/generate_chronology.py` + `scripts/audit/chronology_quality_gate.py`) were deleted on 2026-07-05 after repeated corruption incidents (the auto-classifier drifted from the user's intent and silently rewrote rows). `conductor/chronology.md` is now maintained **manually**: when a track ships, is abandoned, is superseded, or its folder is archived, the implementing agent adds a row to the top of the chronology table (newest-first) by hand. Fields: Date, ID, Status, one-line Summary, Folder path, commit Range. Commit with `docs(chronology): add row for <track-id> archive`.
|
||||
|
||||
**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 -m scripts.audit.chronology_quality_gate --strict` before committing the regenerated chronology.
|
||||
|
||||
**When to regenerate:**
|
||||
**When to add a row:**
|
||||
- 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:**
|
||||
**When NOT to add a row:**
|
||||
- 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)
|
||||
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
# Archive Review: Unfinished Work Still Relevant to the Codebase
|
||||
|
||||
**Date:** 2026-07-05
|
||||
**Author:** Tier 1 Orchestrator (post-archive audit)
|
||||
**Scope:** Tracks archived in the 2026-07-05 batch (commits `98977f5c`..`2f3a8283`, ~21 archive commits). This report enumerates the unfinished work items recorded in each archived track's `state.toml` that remain relevant to the current codebase, grouped by category.
|
||||
|
||||
The intent is a single index the user can scan to decide what (if anything) deserves a follow-up track. Each entry cites the archived track ID + the file:line / scope the deferral points at.
|
||||
|
||||
---
|
||||
|
||||
## 1. The Python Type Promotion Mandate (the largest cluster)
|
||||
|
||||
The C11/Odin/Jai-in-Python mandate (`conductor/code_styleguides/data_oriented_design.md` §8.5) is the project's Core Value. Several archived tracks deferred sub-scopes of this mandate; the deferrals are still live debt.
|
||||
|
||||
### 1.1 `cruft_elimination_20260627` (Partially Completed)
|
||||
|
||||
The biggest single source of deferred type-promotion work. Per `conductor/archive/cruft_elimination_20260627/state.toml`:
|
||||
|
||||
| Phase | Status | Deferred Scope |
|
||||
|---|---|---|
|
||||
| phase_2 | deferred | Add `ProjectContext` dataclass for `flat_config` (spec mismatch) |
|
||||
| phase_4 | deferred | Fix `_do_generate` return type |
|
||||
| phase_5 | deferred | Fix `rag_engine.search()` return type (`List[Dict[str, Any]]` → typed) |
|
||||
| phase_6 | deferred | Eliminate `Optional[T]` returns (30 sites across 14 files) |
|
||||
| phase_7 | deferred | Eliminate `Any` and `dict[str, Any]` from internal signatures (69 sites) |
|
||||
| phase_3 (partial) | completed (with deferral) | 13 `hasattr` checks removed in `app_controller.py`; **18 `hasattr` checks in `gui_2.py` deferred** |
|
||||
|
||||
**Relevance now:** the 30 `Optional[T]` sites and 69 `Any`/`dict[str, Any]` sites are still present in `src/`. The audit scripts `scripts/audit_weak_types.py --strict` and `scripts/audit_optional_in_3_files.py` continue to enforce the boundary; these deferred sites are the remaining work the audits surface.
|
||||
|
||||
### 1.2 `metadata_promotion_20260624` (Partially Completed)
|
||||
|
||||
Per `conductor/archive/metadata_promotion_20260624/state.toml`, three open blockers remain:
|
||||
|
||||
| Blocker | Location | Deferral |
|
||||
|---|---|---|
|
||||
| `blocker_3_rag_return_type` | `src/rag_engine.py:367` | `rag_engine.search()` returns `List[Dict[str, Any]]`; `RAGChunk` dataclass exists but consumers read dict keys directly (`chunk['document']`, `chunk['metadata']['path']`). Cascading return-type change affects 3+ sites. Deferred to `typed_rag_return_type_followup`. |
|
||||
| `blocker_4_tool_builders_dicts` | `src/ai_client.py:609,615,665,671,1132,1138` | Per-vendor tool builders construct wire-format dicts directly (`raw_tools.append({'type': 'function', ...})`); `ToolDefinition` dataclass exists but isn't used. Deferred to `typed_tool_builders_followup`. |
|
||||
| `blocker_5_drifted_field_types` | `src/type_aliases.py:10-148` | Field types drifted from the plan: `CommsLogEntry.kind` default, `CommsLogEntry.direction` default, `CommsLogEntry.content` type, `HistoryMessage.ts` type, `HistoryMessage.tool_calls` type, `HistoryMessage.role` default, no `@dataclass(slots=True)`, `PathInfo.logs_dir` type. Deferred to `field_type_alignment_followup`. |
|
||||
|
||||
Also: `typed_dispatcher_boundaries_followup_20260625` was planned (typed parameters at function boundaries) but never opened.
|
||||
|
||||
**Relevance now:** these are the most concrete unfinished type-promotion items. Each has a specific file:line and a specific migration target. They would compose into a single follow-up track or three small ones.
|
||||
|
||||
### 1.3 `data_oriented_error_handling_20260606` (Completed, with deferrals)
|
||||
|
||||
Per `conductor/archive/data_oriented_error_handling_20260606/state.toml`:
|
||||
|
||||
- `t2_7` cancelled: the 30+ `assert p is not None` chain in the `mcp_client.py` tool functions is **not** removed. Deferred to "a follow-up track that will refactor the full `mcp_client.py` tool surface."
|
||||
- `t2_8` cancelled: `async_dispatch` internals are not changed; the old str-returning API is preserved.
|
||||
|
||||
**Relevance now:** `src/mcp_client.py` still has the `assert p is not None` chain in its tool dispatch. The audit_exception_handling.py gate continues to allow it under the existing baseline; a follow-up would tighten the baseline.
|
||||
|
||||
### 1.4 `type_alias_unfuck_20260626`
|
||||
|
||||
- `vc4_codepaths_drop = "not_measured"` — the required metric computation (number of value-class codepaths dropped) was deferred. The track shipped without the metric.
|
||||
|
||||
### 1.5 `any_type_componentization_20260621` (Partially Completed)
|
||||
|
||||
- The phase 2/4/5 call-site completion **was** deferred to `phase2_4_5_call_site_completion_20260621` (which shipped).
|
||||
- `phase2_4_5_call_site_completion_20260621` has its own deferral: `OpenAICompatibleRequest.tools: list[dict] -> list[ToolSpec]; deferred` (cross_phase_coupling).
|
||||
|
||||
**Relevance now:** `OpenAICompatibleRequest.tools` is still `list[dict]`, not `list[ToolSpec]`. This is the same site as `blocker_4_tool_builders_dicts` above (§1.2).
|
||||
|
||||
---
|
||||
|
||||
## 2. The Metadata SSDL Campaign (parent + 2 of 3 children still Active)
|
||||
|
||||
The `metadata_ssdl_defusing_20260624` campaign was a 3-child effort to defuse the `Metadata` aggregate's combinatoric explosion (4.01e22 effective codepaths). State:
|
||||
|
||||
| Track | Status | Folder |
|
||||
|---|---|---|
|
||||
| `metadata_ssdl_defusing_20260624` (parent) | **Active** | `conductor/tracks/metadata_ssdl_defusing_20260624` |
|
||||
| `metadata_nil_sentinel_20260624` (child 1) | Completed (archived) | `conductor/archive/metadata_nil_sentinel_20260624` |
|
||||
| `metadata_generational_handle_20260624` (child 2) | **Active** | `conductor/tracks/metadata_generational_handle_20260624` |
|
||||
| `metadata_field_cache_20260624` (child 3) | **Active** | `conductor/tracks/metadata_field_cache_20260624` |
|
||||
|
||||
**Note:** these three Active tracks were NOT archived in the 2026-07-05 batch. They remain in `conductor/tracks/`. The user may want to either resume or formally abandon them; their state is currently stale (last commit `7c352e1c`, 2026-06-24).
|
||||
|
||||
---
|
||||
|
||||
## 3. The Result[T] Migration Campaign (umbrella still Active)
|
||||
|
||||
The umbrella `result_migration_20260616` and `result_migration_cruft_removal_20260620` were NOT archived and remain in `conductor/tracks/`. The archived children:
|
||||
|
||||
- `public_api_migration_and_ui_polish_20260615` ✓
|
||||
- `send_result_to_send_20260616` ✓
|
||||
- `result_migration_review_pass_20260617` ✓
|
||||
- `result_migration_small_files_20260617` ✓
|
||||
- `result_migration_app_controller_20260618` ✓
|
||||
- `result_migration_gui_2_20260619` ✓
|
||||
- `result_migration_baseline_cleanup_20260620` — Phase 10-14 invariant tests marked `in_progress` per state.toml. The track shipped, but the invariant tests were never committed as checkpoints.
|
||||
|
||||
**Relevance now:** the umbrella and `result_migration_cruft_removal_20260620` are the two remaining Active tracks in this campaign. The user previously confirmed closeout of the cruft_removal track; state.toml is stale.
|
||||
|
||||
---
|
||||
|
||||
## 4. Provider Integration: Qwen / Llama / Grok (deferrals + 1 cancellation)
|
||||
|
||||
### `qwen_llama_grok_followup_20260611` (archived via earlier commit `8ac8e64d`)
|
||||
|
||||
Per its `[deferred_work]` section:
|
||||
- **Anthropic + Gemini + DeepSeek** — deferred (use vendored call paths; the v2 matrix fields weren't wired into these providers' send functions). Only `gemini_cli` got the v2 wiring.
|
||||
- **Qwen `_dashscope_call`** — deferred (uses `_dashscope_call`, not `send_openai_compatible`). Qwen v2 field wiring was not done.
|
||||
- **8 of 9 UX adaptations** — deferred from `qwen_llama_grok_integration_20260606` Phase 5 (only 1 of 9 shipped: "Screenshot button iff vision").
|
||||
- **Per-field UI** (toggles, attachment buttons, panels) — design work deferred to a follow-up track. Only visibility-only badges shipped (`t5_4`).
|
||||
- **Meta Llama API adapter** — `t4_3` / `t6_1` CANCELLED permanently. Meta does not publish a public OpenAI-compat surface; waiting for Meta.
|
||||
|
||||
### `qwen_llama_grok_integration_20260606` (archived via earlier commit `8ac8e64d`)
|
||||
|
||||
- `t1_11` in_progress: `dashscope>=1.14.0,<2.0.0` dependency was never added to `pyproject.toml`.
|
||||
- Phase 5 was partial: 1 of 9 UX adaptations done.
|
||||
|
||||
**Relevance now:** the v2 capability matrix is wired only for `gemini_cli`. Anthropic, Gemini (direct), DeepSeek, and Qwen still need the v2 wiring if the user wants full v2 coverage. The `dashscope` dependency still needs to be pinned. The 8 deferred UX adaptations (interactive per-field toggles/panels) are still open design work.
|
||||
|
||||
---
|
||||
|
||||
## 5. Default Layout Campaign (3 tracks, multiple deferrals)
|
||||
|
||||
### `default_layout_extract_20260629`
|
||||
- `panel_defs_fleury_migration = "deferred (consumes LayoutFile + get_layouts_dir)"` — panel_defs still uses the old structure, not the Fleury-pattern dataclass migration.
|
||||
- `render_persona_editor_window_fix = "deferred (Layer 1 sentinel catches the empty-content bug)"` — the persona editor window has an empty-content bug that's masked by a Layer 1 sentinel, not actually fixed.
|
||||
|
||||
### `default_layout_install_20260629`
|
||||
- `t2_9` deferred: User Manual Verification — needs a desktop session, can't run in headless Tier 2 sandbox.
|
||||
|
||||
### `default_layout_install_followup_20260629`
|
||||
- `t4_5` / `t4_6` in_progress: commit final plan + state + tracks.md row. (The track shipped functionally; the bookkeeping was never finished.)
|
||||
|
||||
**Relevance now:** the persona editor empty-content bug is still masked, not fixed. The panel_defs Fleury migration is still open.
|
||||
|
||||
---
|
||||
|
||||
## 6. Superpowers Review + Apply (the apply-high track has 1 in_progress task)
|
||||
|
||||
### `superpowers_review_apply_high_20260705`
|
||||
- `t3_4 = { status = "in_progress", commit_sha = "PENDING", description = "Commit + git notes" }` — the final commit + git notes for the apply-high track was never made. The work itself is done (Phase 1-3 shipped), but the bookkeeping commit is missing.
|
||||
|
||||
### `superpowers_review_20260619`
|
||||
- `research_only = "Per user Q1 = A (2026-06-19): no src/, tests/, or agent-directive changes. Recommendations go in decisions.md for the deferred rebuild."` — the deferred rebuild (nagent-rebuild) is a user-deferred decision; no track exists yet.
|
||||
|
||||
**Relevance now:** minor. The `t3_4` bookkeeping commit is the only outstanding item; it's a 1-line git operation.
|
||||
|
||||
---
|
||||
|
||||
## 7. Fable Review (1 pending section)
|
||||
|
||||
### `fable_review_20260617`
|
||||
- `t3_16 = { status = "pending", commit_sha = "", description = "Section 16: Recommendations for the deferred nagent-rebuild. Source: all clusters. Approx LOC: 200." }` — the report is complete through Section 15; Section 16 (the recommendations for the deferred nagent-rebuild) was never written.
|
||||
- `deferred_nagent_rebuild = "user-deferred (no track yet); the Fable review's decisions.md is one of several inputs"` — the nagent-rebuild is a user-deferred decision that would consume Fable + Superpowers + nagent_review_20260608 outputs.
|
||||
|
||||
**Relevance now:** if/when the user decides to do the nagent-rebuild, the Fable review's Section 16 is one of the inputs that doesn't exist yet.
|
||||
|
||||
---
|
||||
|
||||
## 8. Directive Hotswap Harness (Partially Completed)
|
||||
|
||||
### `directive_hotswap_harness_20260627`
|
||||
- `t3_2 = { status = "deferred", ... "Manual verification: does the LLM follow the warm with: instruction? DEFERRED to user per directive" }` — the harness was built but never verified end-to-end with a live LLM session.
|
||||
- `t3_4 = { status = "in_progress", ... "Commit the end-of-track report + this state.toml update atomically" }` — the end-of-track report + state.toml final commit was never made.
|
||||
- 3 additional deferred items + 2 in_progress items per state.toml.
|
||||
|
||||
**Relevance now:** the directive hotswap harness is functional but unverified. The user would need to test it in a live OpenCode session to confirm the LLM follows the `warm with:` instruction. This is the user-deferred verification gate.
|
||||
|
||||
---
|
||||
|
||||
## 9. Chronology Maintenance Switch (this session)
|
||||
|
||||
The chronology generator scripts (`scripts/audit/generate_chronology.py` + `scripts/audit/chronology_quality_gate.py`) were **deleted** in this session (2026-07-05) after repeated corruption incidents. `conductor/chronology.md` is now maintained manually:
|
||||
|
||||
- `conductor/workflow.md` §"Chronology Maintenance" updated to reflect manual maintenance.
|
||||
- `conductor/tracks.md` §"Archiving a track (3 steps)" step 3 updated to "Add a row to `conductor/chronology.md` (top of the table, newest-first) by hand."
|
||||
- The footer of `conductor/tracks.md` updated: the "Full project history" pointer now says "Maintained manually (the generator scripts were deleted 2026-07-05...)".
|
||||
|
||||
**Relevance now:** future agents must NOT try to regenerate the chronology via a script. The convention is now: add a row by hand when a track ships/abandons/is superseded/is archived.
|
||||
|
||||
The archived tracks `chronology_20260619` and `chronology_v2_20260701` contain the design history for the deleted scripts; they're retained as reference but the scripts themselves are gone.
|
||||
|
||||
---
|
||||
|
||||
## 10. Test Infrastructure (minor deferrals)
|
||||
|
||||
### `test_sandbox_hardening_20260619`
|
||||
- `phase_8 = { status = "partial", ... "Full suite verification (Tier-1 smoke run showed FR1 guard catches real corruption; full suite deferred to user)" }` — the full-suite verification was deferred to the user; only a Tier-1 smoke run was done.
|
||||
|
||||
### `fix_mma_concurrent_tracks_sim_20260627`
|
||||
- `t2_3 = { status = "in_progress", ... "Update TRACK_COMPLETION_fix_mma_concurrent_tracks_sim_20260627.md to include all 5 fixes" }` — the TRACK_COMPLETION report was never updated with all 5 fixes.
|
||||
- `phase_1` is marked in_progress despite the track being functionally complete.
|
||||
|
||||
### `result_migration_baseline_cleanup_20260620`
|
||||
- Phases 10-14 invariant tests marked `in_progress` — never committed as checkpoints.
|
||||
|
||||
**Relevance now:** minor. These are bookkeeping deferrals, not functional debt.
|
||||
|
||||
---
|
||||
|
||||
## 11. Other Minor Deferrals
|
||||
|
||||
### `tier2_leak_prevention_20260620`
|
||||
- `[deferred]` section exists in state.toml (content not enumerated here; the track's primary work — the 3-layer hard-ban enforcement — shipped).
|
||||
|
||||
### `data_structure_strengthening_20260606`
|
||||
- `[typed_dict_migration_followup]` and `[public_api_migration_followup]` sections retained as forward-pointers; both were consumed by later tracks (the Result[T] migration campaign).
|
||||
|
||||
### `post_module_taxonomy_de_cruft_20260627`
|
||||
- `out_of_scope = "VC4/VC13 (full batched suite deferred); 2 pre-existing audit failures (main_thread_imports + exception_handling)"` — two pre-existing audit failures were noted as out-of-scope. They may still be failing.
|
||||
|
||||
### `phase2_4_5_call_site_completion_20260621`
|
||||
- `cross_phase_coupling = "OpenAICompatibleRequest.tools: list[dict] -> list[ToolSpec]; deferred"` — same as §1.2 `blocker_4_tool_builders_dicts`.
|
||||
|
||||
### `code_path_audit_phase_2_20260624`
|
||||
- `t3_1` was a snapshot task deferred to post-Phase-3 (the metric was captured post-phase instead).
|
||||
|
||||
---
|
||||
|
||||
## Summary: What Deserves a Follow-Up Track?
|
||||
|
||||
Ranked by codebase relevance (highest first):
|
||||
|
||||
1. **`typed_rag_return_type_followup`** — `src/rag_engine.py:367` returns `List[Dict[str, Any]]`; `RAGChunk` exists but isn't used. Cascading change across 3+ consumer sites. (From §1.2 blocker_3.)
|
||||
2. **`typed_tool_builders_followup`** — `src/ai_client.py:609,615,665,671,1132,1138` construct wire-format dicts directly; `ToolDefinition` exists but isn't used. Affects `OpenAICompatibleRequest.tools: list[dict]`. (From §1.2 blocker_4 + §1.5 + §4.)
|
||||
3. **`field_type_alignment_followup`** — `src/type_aliases.py:10-148` has drifted field types vs the plan. Consumer migration could work or break. (From §1.2 blocker_5.)
|
||||
4. **`cruft_elimination_phase_6_followup`** — 30 `Optional[T]` return sites across 14 files. (From §1.1 phase_6.)
|
||||
5. **`cruft_elimination_phase_7_followup`** — 69 `Any`/`dict[str, Any]` internal-signature sites. (From §1.1 phase_7.)
|
||||
6. **`mcp_client_tool_surface_assert_removal`** — the 30+ `assert p is not None` chain in `mcp_client.py` tool dispatch. (From §1.3.)
|
||||
7. **`gui_2_hasattr_removal`** — 18 `hasattr` checks in `gui_2.py` (the deferred half of cruft_elimination phase_3). (From §1.1.)
|
||||
8. **`qwen_v2_wiring` + `anthropic_gemini_deepseek_v2_wiring`** — v2 capability matrix fields not wired into Qwen/Anthropic/Gemini-direct/DeepSeek send functions. (From §4.)
|
||||
9. **`dashscope_dependency_pin`** — add `dashscope>=1.14.0,<2.0.0` to `pyproject.toml`. (From §4, 1-line fix.)
|
||||
10. **`v2_ux_interactive_toggles`** — per-field UI toggles/attachment buttons/panels (design work; the visibility-only badges shipped). (From §4.)
|
||||
11. **`persona_editor_empty_content_fix`** — the persona editor window has an empty-content bug masked by a Layer 1 sentinel. (From §5.)
|
||||
12. **`metadata_ssdl_defusing_campaign_resume_or_abandon`** — parent + 2 of 3 children still Active; state stale since 2026-06-24. User decision needed. (From §2.)
|
||||
13. **`result_migration_cruft_removal_closeout`** — umbrella + cruft_removal still Active per state.toml; user confirmed closeout. Update state.toml. (From §3.)
|
||||
14. **`fable_section_16`** — write Section 16 (nagent-rebuild recommendations, ~200 LOC). Only relevant if the user decides to do the nagent-rebuild. (From §7.)
|
||||
|
||||
Items 1-3 are the most concrete type-promotion work and have specific file:line targets. Items 4-6 are the bulk type-promotion sweeps. Items 7-10 are provider-integration debt. Items 11-14 are smaller / decision-gated.
|
||||
|
||||
The user should pick which (if any) of these to open as follow-up tracks. The archive review is complete; this report is the index for that decision.
|
||||
@@ -1,126 +0,0 @@
|
||||
"""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 sys
|
||||
|
||||
|
||||
_METADATA_FIELD_PREFIXES = (
|
||||
"**Priority:**",
|
||||
"**Date:**",
|
||||
"**Initialized:**",
|
||||
"**Track:**",
|
||||
"**Track ID:**",
|
||||
"**Parent umbrella:**",
|
||||
"**Status:**",
|
||||
"**Confidence:**",
|
||||
"**Ancestors:**",
|
||||
)
|
||||
|
||||
|
||||
@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]:
|
||||
from scripts.audit.generate_chronology import walk_track_folders, _repo_root
|
||||
# chronology_path is conductor/chronology.md; walk_track_folders expects the conductor dir
|
||||
conductor_dir: Path = chronology_path.parent.resolve()
|
||||
if not (conductor_dir / "tracks").is_dir():
|
||||
# Fallback: resolve from repo root
|
||||
repo_root: Path = _repo_root(conductor_dir)
|
||||
conductor_dir = repo_root / "conductor"
|
||||
return walk_track_folders(conductor_dir)
|
||||
|
||||
|
||||
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("--rows-json", type=Path, default=None, help="Load rows from JSON file instead of re-walking")
|
||||
parser.add_argument("--strict", action="store_true", help="Exit 1 on any violation")
|
||||
args = parser.parse_args()
|
||||
if args.rows_json and args.rows_json.is_file():
|
||||
import json
|
||||
rows = json.loads(args.rows_json.read_text(encoding="utf-8"))
|
||||
else:
|
||||
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()
|
||||
@@ -1,468 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate chronology for Manual Slop conductor tracks.
|
||||
|
||||
Walks conductor/tracks/ and conductor/archive/, extracts per-track data
|
||||
(date, ID, status, summary, commit range), and emits a markdown chronology
|
||||
to stdout.
|
||||
|
||||
The v2 classifier uses git-history evidence (work-commit count + report
|
||||
overrides) instead of stale metadata.json.status. Returns
|
||||
(status, confidence, reason) per row.
|
||||
|
||||
Usage:
|
||||
uv run python scripts/audit/generate_chronology.py --draft
|
||||
uv run python scripts/audit/generate_chronology.py --root conductor/
|
||||
uv run python scripts/audit/generate_chronology.py # JSON dump
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
_SLUG_DATE_RE = re.compile(r"\d{8}$")
|
||||
_SENTENCE_END_RE = re.compile(r"\.\s")
|
||||
_GIT_TIMEOUT = 30
|
||||
_DEFAULT_ROOT = "conductor/"
|
||||
|
||||
_METADATA_FIELD_PREFIXES = (
|
||||
"**Priority:**",
|
||||
"**Date:**",
|
||||
"**Initialized:**",
|
||||
"**Track:**",
|
||||
"**Track ID:**",
|
||||
"**Parent umbrella:**",
|
||||
"**Status:**",
|
||||
"**Confidence:**",
|
||||
"**Ancestors:**",
|
||||
)
|
||||
|
||||
_WORK_COMMIT_PREFIXES = ("feat", "fix", "refactor", "perf", "test", "docs(report)")
|
||||
_METADATA_COMMIT_PREFIXES = ("conductor(plan):", "conductor(state):", "conductor(track):", "docs(spec):", "docs(plan):")
|
||||
|
||||
|
||||
def _is_work_commit(msg: str) -> bool:
|
||||
"""Check if a commit message is a work commit (feat/fix/refactor/perf/test with optional scope)."""
|
||||
for prefix in _WORK_COMMIT_PREFIXES:
|
||||
if msg.startswith(prefix + ":") or msg.startswith(prefix + "("):
|
||||
if not any(msg.startswith(m) for m in _METADATA_COMMIT_PREFIXES):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def extract_slug_date(folder_name: str) -> Optional[str]:
|
||||
m = _SLUG_DATE_RE.search(folder_name)
|
||||
if not m:
|
||||
return None
|
||||
raw: str = m.group(0)
|
||||
return f"{raw[:4]}-{raw[4:6]}-{raw[6:]}"
|
||||
|
||||
|
||||
def _md_escape(text: str) -> str:
|
||||
return text.replace("|", "\\|").replace("\n", " ").replace("\r", " ")
|
||||
|
||||
|
||||
def _to_posix(path_str: str) -> str:
|
||||
return path_str.replace("\\", "/")
|
||||
|
||||
|
||||
def _first_sentence(line: str) -> str:
|
||||
m = _SENTENCE_END_RE.search(line)
|
||||
if m:
|
||||
return line[: m.start() + 1].strip()
|
||||
return line.strip()
|
||||
|
||||
|
||||
def _truncate_to_25_words(text: str) -> str:
|
||||
words: list[str] = text.split()
|
||||
if len(words) <= 25:
|
||||
return text
|
||||
return " ".join(words[:25]) + "\u2026"
|
||||
|
||||
|
||||
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)"
|
||||
|
||||
|
||||
def _git_log(folder_relpath: str, *args: str) -> str:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "log", *args, "--", folder_relpath],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=_GIT_TIMEOUT,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return ""
|
||||
return result.stdout
|
||||
except (subprocess.SubprocessError, OSError):
|
||||
return ""
|
||||
|
||||
|
||||
def _git_log_multi(*folder_relpaths: str) -> str:
|
||||
"""Get git log for multiple paths in a single subprocess call."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "log", "--oneline", "--", *folder_relpaths],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=_GIT_TIMEOUT,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return ""
|
||||
return result.stdout
|
||||
except (subprocess.SubprocessError, OSError):
|
||||
return ""
|
||||
|
||||
|
||||
def _git_first_line(folder_relpath: str, *args: str) -> str:
|
||||
out = _git_log(folder_relpath, *args)
|
||||
stripped = out.strip()
|
||||
if not stripped:
|
||||
return ""
|
||||
return stripped.splitlines()[0]
|
||||
|
||||
|
||||
def _repo_root(start: Path) -> Path:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
check=False,
|
||||
cwd=str(start),
|
||||
)
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
return Path(result.stdout.strip())
|
||||
except (subprocess.SubprocessError, OSError):
|
||||
pass
|
||||
return start.parent
|
||||
|
||||
|
||||
def _parse_state_phase(state_path: Path) -> str:
|
||||
if not state_path.is_file():
|
||||
return "no-state-toml"
|
||||
try:
|
||||
for line in state_path.read_text(encoding="utf-8").splitlines():
|
||||
if line.startswith("current_phase"):
|
||||
v = line.split("=", 1)[1].strip().split("#")[0].strip().strip('"')
|
||||
return v
|
||||
except (OSError, Exception):
|
||||
pass
|
||||
return "?"
|
||||
|
||||
|
||||
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") and "=" in stripped:
|
||||
parts = stripped.split("=", 1)
|
||||
if len(parts) == 2:
|
||||
val: str = parts[1].split("#")[0].strip()
|
||||
val = val.strip('"').strip("'").strip()
|
||||
return val
|
||||
return ""
|
||||
|
||||
|
||||
def _last_commit_date(folder_relpath: str) -> str:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "log", "-1", "--format=%ad", "--date=short", "--", folder_relpath],
|
||||
capture_output=True, text=True, timeout=_GIT_TIMEOUT, check=False,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except (subprocess.SubprocessError, OSError):
|
||||
return "never"
|
||||
|
||||
|
||||
def _count_work_commits_from_log(log: str) -> int:
|
||||
count: int = 0
|
||||
for line in log.splitlines():
|
||||
msg: str = line.split(" ", 1)[1] if " " in line else ""
|
||||
if _is_work_commit(msg):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def _count_work_commits(folder_relpath: str) -> int:
|
||||
log: str = _git_log(folder_relpath, "--oneline")
|
||||
return _count_work_commits_from_log(log)
|
||||
|
||||
|
||||
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 = "",
|
||||
work_commits: int = -1,
|
||||
) -> tuple[str, str, str]:
|
||||
"""Git-history evidence classifier returning (status, confidence, reason).
|
||||
|
||||
Evidence priority:
|
||||
1. Override signals (highest): state.toml status (human-set), TRACK_COMPLETION/TRACK_ABORTED reports
|
||||
2. Git commit evidence (medium): work-commit count
|
||||
3. Directory location (low): archive/ vs tracks/
|
||||
4. Fallback: Needs Review
|
||||
"""
|
||||
if "PLACEHOLDER" in track_id:
|
||||
return ("Special", "high", "placeholder track")
|
||||
if "contingency" in current.lower():
|
||||
return ("Special", "high", "contingency track")
|
||||
# 1. Override signals — state.toml is human-set, trust it
|
||||
if state_status == "superseded":
|
||||
return ("Superseded", "high", "state.toml status=superseded")
|
||||
if state_status in ("completed", "complete", "shipped"):
|
||||
return ("Completed", "high", f"state.toml status={state_status}")
|
||||
if state_status == "abandoned":
|
||||
return ("Abandoned", "high", "state.toml status=abandoned")
|
||||
if state_status == "archived":
|
||||
return ("Completed", "high", "state.toml status=archived (treated as completed)")
|
||||
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/")
|
||||
if work_commits < 0:
|
||||
work_commits = _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:
|
||||
# Archive tracks: the git mv to archive/ is a signal but not a definitive one.
|
||||
# Tracks can be archived because they're completed OR because they're abandoned.
|
||||
# Check the metadata.json status for human-set values, then fall back to Needs Review.
|
||||
# (The state.toml override and TRACK_ABORTED report check already ran above.)
|
||||
if work_commits >= 3:
|
||||
return ("Completed", "medium", f"{work_commits} work commits")
|
||||
# Check for plan-progression / "mark as completed" heuristics
|
||||
full_log: str = _git_log(folder_link, "--oneline")
|
||||
if folder_link.startswith("conductor/archive/"):
|
||||
original: str = folder_link.replace("conductor/archive/", "conductor/tracks/", 1)
|
||||
full_log = _git_log(original, "--oneline") + full_log
|
||||
plan_progression: int = sum(
|
||||
1 for line in full_log.splitlines()
|
||||
if ("mark" in line.lower() and "complete" in line.lower())
|
||||
or "complete" in line.lower() and ("phase" in line.lower() or "track" in line.lower() or "task" in line.lower() or "verification" in line.lower() or "audit" in line.lower())
|
||||
)
|
||||
if plan_progression >= 1:
|
||||
return ("Completed", "low", f"archived with {plan_progression} completion-signal commits")
|
||||
archive_move_completed: bool = any(
|
||||
"completed" in line.lower() and ("archive" in line.lower() or "move" in line.lower())
|
||||
for line in full_log.splitlines()[:3]
|
||||
)
|
||||
if archive_move_completed:
|
||||
return ("Completed", "low", "archived with 'completed' in archive-move commit")
|
||||
# No evidence of completion (no mark-complete, no feat commits, no report).
|
||||
# If the track folder has planning commits only, it was likely planned but never
|
||||
# implemented (abandoned). The work would have shown up as feat/fix commits.
|
||||
if work_commits == 0:
|
||||
# Last resort: check if "complete" appears in any commit message at all
|
||||
has_any_complete: bool = any("complete" in line.lower() for line in full_log.splitlines())
|
||||
if has_any_complete:
|
||||
return ("Completed", "low", "archived with 'complete' in commit messages")
|
||||
return ("Abandoned", "low", "archived with 0 work commits and 0 completion signals")
|
||||
return ("Completed", "low", f"archived with {work_commits} work commits on track folder")
|
||||
# 4. Fallback
|
||||
return ("Needs Review", "none", "classifier inconclusive")
|
||||
|
||||
|
||||
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)
|
||||
# Get the full oneline log once. For archive folders, include both the
|
||||
# archive path and the original tracks/ path (git mv preserves history
|
||||
# but git log -- <archive_path> alone may miss pre-move commits)
|
||||
if folder_relpath.startswith("conductor/archive/"):
|
||||
original_path: str = folder_relpath.replace("conductor/archive/", "conductor/tracks/", 1)
|
||||
oneline_log: str = _git_log_multi(folder_relpath, original_path)
|
||||
else:
|
||||
oneline_log = _git_log(folder_relpath, "--oneline")
|
||||
log_lines: list[str] = [l for l in oneline_log.splitlines() if l.strip()]
|
||||
commit_count: int = len(log_lines)
|
||||
if slug_date:
|
||||
date = slug_date
|
||||
elif log_lines:
|
||||
# First commit date via reverse log
|
||||
first_commit = _git_first_line(folder_relpath, "--reverse", "--format=%aI")
|
||||
date = first_commit[:10] if first_commit else ""
|
||||
else:
|
||||
date = ""
|
||||
# Derive init_sha and end_sha from the oneline log
|
||||
if log_lines:
|
||||
init_sha: str = log_lines[-1].split(" ", 1)[0] # oldest (last in reverse-chronological git log)
|
||||
end_sha: str = log_lines[0].split(" ", 1)[0] # newest (first in git log)
|
||||
else:
|
||||
init_sha = ""
|
||||
end_sha = ""
|
||||
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")
|
||||
work_commits: int = _count_work_commits_from_log(oneline_log)
|
||||
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,
|
||||
work_commits=work_commits,
|
||||
)
|
||||
summary: str = extract_summary(folder)
|
||||
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
|
||||
|
||||
|
||||
def format_markdown(rows: list[dict]) -> str:
|
||||
from datetime import date as today_date
|
||||
lines: list[str] = []
|
||||
lines.append(f"<!-- Generated {today_date.today().isoformat()} | {len(rows)} rows -->")
|
||||
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']} | "
|
||||
f"{_md_escape(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"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate chronology for Manual Slop conductor tracks.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--draft",
|
||||
action="store_true",
|
||||
help="Emit markdown table to stdout.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--root",
|
||||
default=_DEFAULT_ROOT,
|
||||
help=f"Path to conductor root (default: {_DEFAULT_ROOT}).",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
root = Path(args.root)
|
||||
if not root.is_absolute():
|
||||
root = Path.cwd() / root
|
||||
rows = walk_track_folders(root)
|
||||
if args.draft:
|
||||
sys.stdout.write(format_markdown(rows))
|
||||
else:
|
||||
sys.stdout.write(json.dumps(rows, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user