From 556e42c7417ee1a88bb4f47bc443331ec7aa1fa5 Mon Sep 17 00:00:00 2001 From: "eric.fang" Date: Tue, 28 Jul 2026 09:26:34 +0800 Subject: [PATCH 01/10] docs: split migration contract into references/migration.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Progressive disclosure per the writing-great-skills review: migration is a single-branch path, so its ~110 lines leave SKILL.md's top level. The new references/migration.md is the one authoritative migration document — discovery/consent, KI-001 rationale, merged step-by-step flow, authoritative command reference, and Kind/disposition rules — absorbing both SKILL.md's detail and workflow.md's parallel seven-step section. SKILL.md and workflow.md keep the preflight rules, the byte-synced MIGRATION_GATE block, and an imperative read-in-full pointer. Sentence-level no-op pruning takes SKILL.md from 302 to 195 lines. Co-Authored-By: Claude Fable 5 --- AGENTS.md | 2 +- README.md | 3 +- agents/progress-tracker.md | 3 +- docs/design-decisions.md | 26 ++- docs/domain-models.md | 8 +- docs/project-overview.md | 6 +- .../PROGRESS.md | 40 ++++ progress/INDEX.md | 1 + ...osure-progress-tracker-improvement-plan.md | 111 +++++++++++ skills/progress-tracker/SKILL.md | 185 ++++-------------- .../progress-tracker/references/migration.md | 170 ++++++++++++++++ .../progress-tracker/references/workflow.md | 103 ++-------- 12 files changed, 414 insertions(+), 244 deletions(-) create mode 100644 progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md create mode 100644 progress/_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md create mode 100644 skills/progress-tracker/references/migration.md diff --git a/AGENTS.md b/AGENTS.md index 88baed2..1dea9c7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,7 +44,7 @@ single-line edits) can skip; do not pre-load all docs. | Changing lifecycle update, close-out, or audit behavior | [SKILL.md](skills/progress-tracker/SKILL.md) + [update_progress.py](skills/progress-tracker/scripts/update_progress.py) | | Changing the item template or INDEX shape | [PROGRESS.template.md](skills/progress-tracker/references/PROGRESS.template.md) + [INDEX.template.md](skills/progress-tracker/references/INDEX.template.md) | | Changing the status lifecycle | [SKILL.md](skills/progress-tracker/SKILL.md) §Status lifecycle + [workflow.md](skills/progress-tracker/references/workflow.md) | -| Changing migration-inventory/migration-audit behavior or the record shape | [SKILL.md](skills/progress-tracker/SKILL.md) §Before creating anything + [MIGRATION.template.md](skills/progress-tracker/references/MIGRATION.template.md) + [KNOWN-ISSUE.md](KNOWN-ISSUE.md) KI-001 | +| Changing migration-inventory/migration-audit behavior or the record shape | [migration.md](skills/progress-tracker/references/migration.md) + [MIGRATION.template.md](skills/progress-tracker/references/MIGRATION.template.md) + [KNOWN-ISSUE.md](KNOWN-ISSUE.md) KI-001 | | Changing eval scenarios or the trigger matrix | [evals/README.md](evals/README.md) | | Changing Codex plugin packaging or installation | [README.md](README.md) §Install + [docs/design-decisions.md](docs/design-decisions.md) Codex packaging decision | | Understanding why it's built this way | [docs/design-decisions.md](docs/design-decisions.md) | diff --git a/README.md b/README.md index 29daac6..2b51bb0 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ progress-tracker/ │ └── progress-tracker/ │ ├── SKILL.md # canonical entry point: lifecycle, args, status enum │ ├── agents/ # Codex UI metadata (openai.yaml) -│ ├── references/ # workflow spec + item/index templates + seed READMEs +│ ├── references/ # workflow spec + migration contract + templates + seed READMEs │ └── scripts/ # create/update/check CLIs + their pytest suites ├── agents/ # dedicated agent definition (preloads the skill) ├── AGENTS.md # maintainer guide for this repo (CLAUDE.md is a symlink to it) @@ -181,6 +181,7 @@ progress-tracker/ | [AGENTS.md](AGENTS.md) | Maintainer guide: hard constraints, the verify gate | | [docs/design-decisions.md](docs/design-decisions.md) | Decision log with rationale: generic scope model, script-first eval strategy | | [workflow.md](skills/progress-tracker/references/workflow.md) | Full workflow spec: folder structure, field semantics, cleanup policy | +| [migration.md](skills/progress-tracker/references/migration.md) | The migration contract in full: flow, command reference, Kind/disposition rules | | [PROGRESS.template.md](skills/progress-tracker/references/PROGRESS.template.md) | The item template | | [INDEX.template.md](skills/progress-tracker/references/INDEX.template.md) | The item-list seed | | [evals/README.md](evals/README.md) | Scenario + trigger-matrix strategy and how to run them | diff --git a/agents/progress-tracker.md b/agents/progress-tracker.md index 08eb090..dae6c7d 100644 --- a/agents/progress-tracker.md +++ b/agents/progress-tracker.md @@ -36,7 +36,8 @@ The preloaded **progress-tracker** skill is your operating manual. Follow it str "in progress" section, and defaults every unrecognized heading to blocking rather than assuming it is safe to ignore. Copy every actionable entry, dispose of every inventory row, update every pointer, and audit all old - path/name references afterward. + path/name references afterward. A migration was approved → read the skill's + `references/migration.md` in full before running any migration command. Migration is script-gated. The deletion question MUST NOT be asked until both diff --git a/docs/design-decisions.md b/docs/design-decisions.md index 6558696..ad591a4 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -2,13 +2,37 @@ > **Type:** Reference > **Audience:** Maintainers, AI agents -> **Last updated:** 2026-07-27 +> **Last updated:** 2026-07-28 A decision log with rationale, in chronological order. When a design choice seems arbitrary, check here before changing it. --- +## 2026-07-28 — Progressive disclosure for the migration contract + +Migration detail (~110 of SKILL.md's 302 lines) sat top-level in `SKILL.md` +even though only the migration branch ever needs it, and the same seven-step +contract was restated nearly in full in `references/workflow.md`. Both copies +now collapse to the shared preflight rules, the byte-synced +`MIGRATION_GATE` block, and an imperative pointer ("read +`references/migration.md` in full before running any migration command"); +`references/migration.md` is the single authoritative migration document — +discovery/consent, the KI-001 rationale, the merged step-by-step flow, the +command reference, and the Kind/disposition rules. + +Deliberate residuals: the gate block stays byte-identical in `SKILL.md`, +`workflow.md`, and `agents/progress-tracker.md` (verify.sh check 3b) and is +**not** duplicated into `migration.md`, which would be a fourth, unchecked +copy; `agents/progress-tracker.md` keeps its condensed migration paraphrase +(a subagent definition must be self-contained) plus the same read-in-full +pointer; and SKILL.md's preflight detection rules remain top-level because +every create runs them, not just the migration branch. `migration.md` is not +in verify.sh's scaffold-link map, so it is never copied into user projects — +the scaffolded seed docs already direct readers back to the installed skill. + +--- + ## 2026-07-27 — Package Codex directly from the repository root The repository root is the plugin root: `.codex-plugin/plugin.json` points to diff --git a/docs/domain-models.md b/docs/domain-models.md index ec983e6..f9ae358 100644 --- a/docs/domain-models.md +++ b/docs/domain-models.md @@ -2,14 +2,16 @@ > **Type:** Reference > **Audience:** Developers, AI assistants, code reviewers -> **Last updated:** 2026-07-27 +> **Last updated:** 2026-07-28 --- The project has no database models. Its domain consists of Markdown records, parsed in-memory dataclasses, and guarded transitions between filesystem states. The canonical -workflow contract is [`SKILL.md`](../skills/progress-tracker/SKILL.md); this document -maps that contract to the implementing functions in the two Python CLIs. +workflow contract is [`SKILL.md`](../skills/progress-tracker/SKILL.md) (with the +migration contract in +[`references/migration.md`](../skills/progress-tracker/references/migration.md)); this +document maps that contract to the implementing functions in the two Python CLIs. ## 1. Model Details diff --git a/docs/project-overview.md b/docs/project-overview.md index 1ddc822..e4edeff 100644 --- a/docs/project-overview.md +++ b/docs/project-overview.md @@ -2,7 +2,7 @@ > **Type:** Explanation > **Audience:** Developers, AI assistants, and tooling that needs project context -> **Last updated:** 2026-07-27 +> **Last updated:** 2026-07-28 > > A reusable agent skill and deterministic CLI toolkit for durable, local development-progress tracking. Related docs: [domain-models.md](domain-models.md), [coding-style.md](coding-style.md), and [design-decisions.md](design-decisions.md). @@ -90,7 +90,7 @@ The repository separates human/agent workflow guidance from deterministic mutati User or agent request | v -skills/progress-tracker/SKILL.md --------> references/workflow.md +skills/progress-tracker/SKILL.md --------> references/workflow.md + migration.md | | | chooses command | explains fields and policy v v @@ -143,7 +143,7 @@ progress-tracker/ └── skills/progress-tracker/ ├── SKILL.md # Canonical agent workflow and public command contract ├── agents/openai.yaml # Codex skill UI metadata - ├── references/ # Workflow, templates, and generated tracker seed docs + ├── references/ # Workflow, migration contract, templates, and seed docs └── scripts/ ├── new_progress.py # Scaffold and create CLI ├── update_progress.py # Update, close, audit, and migration CLI diff --git a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md new file mode 100644 index 0000000..ff61670 --- /dev/null +++ b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md @@ -0,0 +1,40 @@ +# SKILL.md progressive-disclosure refactor + +**Slug:** skill-docs-progressive-disclosure +**Status:** in-progress +**Ticket:** N/A +**Related plan:** [skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md](../_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md) +**Created:** 2026-07-28 +**Updated:** 2026-07-28 + +--- + +## Scope + +| Scope | Branch | Ticket | Notes | +|---|---|---|---| +| `progress-tracker` | TBD | TBD | | + +## Background & goals + +> Describe the background, motivation, and expected outcome of this task. + +## Task list + +- [ ] +- [ ] + +## Work log + +### 2026-07-28 + +- +- Phase 1: split migration contract into references/migration.md (single authoritative doc, absorbing workflow.md's parallel section), pruned SKILL.md 302 -> 195 lines, synced doc surfaces; verify.sh green. + +## Outcome + +> Fill in after development finishes. + +**Final status:** +**PR / Commit:** +**Follow-ups:** diff --git a/progress/INDEX.md b/progress/INDEX.md index daa01c9..c37be76 100644 --- a/progress/INDEX.md +++ b/progress/INDEX.md @@ -10,6 +10,7 @@ workflow. | Status | Item | Folder | Scope | Ticket | Plan | Created | Notes | |---|---|---|---|---|---|---|---| | `done` | Progress Tracker Improvements | `progress/2026-07-24-progress-tracker-improvements/` | `progress-tracker` | N/A | _plans/progress-tracker-improvements-plan.md | 2026-07-24 | P0 verification truthfulness; P1 lifecycle automation and input hardening | +| `in-progress` | SKILL.md progressive-disclosure refactor | `progress/2026-07-28-skill-docs-progressive-disclosure/` | `progress-tracker` | N/A | [skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md](_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md) | 2026-07-28 | | ## Status legend diff --git a/progress/_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md b/progress/_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md new file mode 100644 index 0000000..b5a96dc --- /dev/null +++ b/progress/_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md @@ -0,0 +1,111 @@ +# skill-progress-tracker 改善計劃 + +> **依據:** 對照 `mattpocock/skills` 的 `writing-great-skills` 原則,逐行審視現行 `skills/progress-tracker/SKILL.md`(309 行)後制定。 +> **核心目標:** 降低 context load、消除 duplication、用 leading words 強化行為錨定 — 全程以既有 evals(trigger matrix + scenarios)當回歸安全網。 +> **建立日期:** 2026-07-27 + +--- + +## Phase 1 — Pruning pass(優先度:最高) + +### 1a. Migration 段落 progressive disclosure + +**問題:** SKILL.md 309 行中 migration 佔約 120 行,但 migration 是只有單一 branch 會走到的路徑;大多數 session 只做 create / update / close。依 branch 判準(所有分支都需要的留頂層,只有部分分支會走到的推下去),這 120 行不該常駐頂層。 + +**做法:** + +1. SKILL.md 只保留: + - Preflight 偵測規則(每次 create 都要跑,屬於全分支內容) + - `MIGRATION_GATE_START/END` 硬約束區塊(verify.sh 已鏡射,維持 single source of truth 機制) + - 一行 context pointer +2. 以下整段搬到新檔 `references/migration.md`: + - 七步 migration 流程細節(步驟 3–7 的展開內容) + - 五種 `Kind` 值語義與 disposition 規則 + - v1 → v2 record 升級行為 + - Migration command reference 區塊 +3. **Pointer 措辭用命令式**,決定觸發率的是措辭不是目標: + - ✅ `A migration was approved → read references/migration.md in full before running any migration command.` + - ❌ `See references/migration.md for details.` + +### 1b. 消除 migration 指令的 duplication + +**問題:** migration 指令目前出現兩次 — 編號步驟內一次、「Migration command reference」區塊一次。Duplication 除了維護成本,還會虛增該內容在 information hierarchy 上的地位。 + +**做法:** 權威版本留在 `references/migration.md` 的 command reference,步驟內文改為指向。 + +### 1c. 逐句 no-op 測試 + +**規則:** 對每一句問「這句有沒有改變相對於模型預設的行為?」失敗的**整句刪除**,不修剪字詞。 + +**已識別候選:** + +- `Use --dry-run first to preview what would be created.` — flag 說明已含 preview 語意,近乎 no-op +- 各處「brief notes」「as needed」等修飾語逐一檢驗 + +**驗收:** 拆分 + 修剪後 SKILL.md 目標 ≤ 180 行,`bash scripts/verify.sh` 通過,evals 全綠。 + +--- + +## Phase 2 — Description 重寫 + invocation 語意 + +### 2a. Description 依「one trigger per branch」重寫 + +現行 description 的 trigger 條列品質不錯(含 negative trigger),但仍可依三規則收緊: + +1. **Front-load leading word** — 第一個詞就做觸發工作 +2. **每個 branch 只留一個 trigger** — create / update / audit / close-out / migrate 各一,同義改寫視為 duplication 合併 +3. **刪掉 body 已有的 identity 描述** + +Description 每一輪都躺在 context window 裡,是全 repo 最貴的文字。 + +### 2b. Invocation 語意決策(記錄進 design-decisions.md) + +- `check`(audit):保留 model-invoked — 期望 agent 在 close-out 前自動跑 +- Migration:1a 拆出後天然降級為「僅由主 skill 指路才載入」的層級,等效降低 context load,**不另拆成獨立 skill**(granularity 經濟學:每次 split 都花掉一種 load,目前拆不出對應價值) +- Router skill:目前僅兩個 skill,不需要 + +**驗收:** trigger-matrix.json 全數通過(description 改動直接影響觸發,這是它的安全網)。 + +--- + +## Phase 3 — Leading words 重構 + +### 3a. Migration → **two-phase commit** + +inventory → audit(= prepare)→ finalize(= commit)→ confirm-deleted 的結構天生就是 2PC。在 SKILL.md gate 區塊與 `references/migration.md` 開頭各錨定一次: + +> Migration is a two-phase commit: the audit is the prepare phase; nothing is deleted until it votes yes. + +徵召模型既有的 2PC 先驗 — 其語意模型中不存在「跳過 prepare 直接 commit」的路徑,比條列 MUST NOT 更穩。 + +### 3b. 強化既有 leading words + +- **preflight** — 已是好詞,擴大到 description 也使用,讓觸發與執行共用同一錨點 +- Status lifecycle 的 `blocked ↕` 圖已足夠緊湊,維持 + +### 3c. Negation 掃描 + +保留正當的 hard guardrail(「Never delete current tracker items automatically」屬此類,保留並已配對正向指示)。其餘 do not / never 逐一檢查能否改寫為正向陳述目標行為。 + +--- + +## Phase 4 — 功能性移植(挑選執行) + +1. **`CONTEXT.md`(共享語言)** — 集中定義 domain 詞彙:scope、disposition、Kind、tracker-dir、preflight、two-phase commit。AGENTS.md 與 SKILL.md 引用之,減少各處重複解釋。 +2. **Failure-mode review checklist** — 把六個 failure mode(premature completion / duplication / sediment / sprawl / no-op / negation)做成人工 checklist,納入 verify.sh 流程說明;之後每次改 SKILL.md 都過一遍。 +3. **Tautological eval 自查** — 檢查 grader 是否用與 skill 相同的邏輯判分;期望值必須來自獨立 truth source。 + +--- + +## 執行順序與驗收總表 + +| 順序 | 項目 | 產出 | 驗收 | +|---|---|---|---| +| 1 | 1a + 1b Migration 拆分 | `references/migration.md` + 縮短的 SKILL.md | verify.sh + evals 全綠 | +| 2 | 1c No-op 修剪 | SKILL.md ≤ 180 行 | 同上 | +| 3 | 2a Description 重寫 | 新 description | trigger matrix 全過 | +| 4 | 2b Invocation 決策 | design-decisions.md 新條目 | — | +| 5 | 3a–3c Leading words | 修訂後全文 | scenarios 全綠 | +| 6 | Phase 4 擇項 | CONTEXT.md 等 | verify.sh 通過 | + +**每個 phase 是一個獨立 commit / PR,eval 全綠才進下一個。** diff --git a/skills/progress-tracker/SKILL.md b/skills/progress-tracker/SKILL.md index 466d41c..24ee0d1 100644 --- a/skills/progress-tracker/SKILL.md +++ b/skills/progress-tracker/SKILL.md @@ -17,14 +17,11 @@ description: >- # Development Progress Tracker Track development tasks locally under a `progress/` directory at the project -root (configurable — see below). Works for a single-scope project or one that -spans several scopes (services, packages, sibling repos); "scope" is a -free-form label, not validated against anything. +root (configurable). A task may span one scope or several (services, packages, +sibling repos). Full spec: `references/workflow.md` -Item list: `/INDEX.md` -Template: `/_template/PROGRESS.md` -Migration record template: `references/MIGRATION.template.md` +Migration contract: `references/migration.md` ## Before creating anything: existing-tracker preflight @@ -36,33 +33,13 @@ in `AGENTS.md`, `README.md`, or project docs. Judge by content, not filename alone. An existing `/INDEX.md` with this skill's structure is an already-adopted tracker, not a migration candidate. -If a separate tracking mechanism exists: - -1. **Do not scaffold or mutate anything yet.** Show the user the artifacts and - the documents/scripts that reference them. -2. **Ask whether to migrate.** An explicit answer is required; silence is not - consent. If the user declines, preserve the existing mechanism and do not - create a second tracker unless they explicitly choose coexistence. -3. If approved, run `migration-inventory` to produce a section-by-section - inventory of the source (see below) **before** editing anything. Do not - rely on an "in progress" or "current work" section alone to decide what is - actionable — inventory the whole document. Do not invent missing facts. - Keep the source unchanged during migration and audit. -4. Copy every actionable entry the inventory lists into the new item(s): - current status, scope, branches/tickets/plans, goals, unfinished tasks, - current work log, blockers, next actions, and live references. For each - entry, fill in the record's Disposition (`migrated`, `excluded`, or - `not-applicable`) and Destination. Every `migrated` row also needs a - non-trivial, row-specific Evidence locator copied from the destination item; verify - semantic equivalence against wherever it landed. -5. Update every live pointer to the old mechanism, including Markdown links, - path mentions, agent instructions, command examples, scripts, and - configuration. -6. Run `update_progress.py check`, search the whole project for every old path - or filename, and inspect all changed links. Classify any remaining match as - an intentional historical/compatibility reference. Tick the record's human - sign-off checklist only once each item has actually been verified. -7. Run `migration-audit` — the pre-deletion gate: +A separate tracking mechanism exists → show the user the artifacts and the +documents that reference them, then ask whether to migrate. An explicit answer +is required; silence is not consent. A declined migration preserves the +existing mechanism; a second tracker requires an explicit coexistence choice. + +A migration was approved → read `references/migration.md` in full before +running any migration command. Migration is script-gated. The deletion question MUST NOT be asked until both @@ -80,124 +57,55 @@ sign-offs cover work already performed. An empty WIP section is not evidence of an empty actionable set. - Only after `migration-audit` exits 0, show its output and ask whether to - delete the original tracking artifacts. Do not infer consent. Record a - declined deletion with `migration-finalize --decision retain`. If - deletion is approved, run `migration-finalize --decision delete`; - this reruns the audit, seals the exact sources and record fingerprint, and - **does not delete anything**. Remove only those approved sources, update - affected pointers, run pointer/link checks, then run - `migration-finalize --confirm-deleted`. - -Report the migration record's location, schema and final outcome, -`migration-audit`'s pass/fail result, the historical/reference entry counts it -disclosed, pointer files updated, and intentional legacy references. - -**Migration command reference:** - -```bash -# Scan the legacy source(s) and write/refresh /_migrations/.md. -# --source may repeat; a directory source is scanned recursively for *.md. -# If no tracker exists yet, this command scaffolds its support files without -# creating a progress item, so inventory still precedes destination creation. -# Re-running upgrades v1 records to v2 and preserves every compatible -# Disposition, Destination, and Evidence whose -# entry is unchanged. Sign-offs survive only an identical source + inventory; -# any inventory change resets all of them and prints that refresh result. -uv run /scripts/update_progress.py migration-inventory --source [--source ...] - -# Rescan and reconcile against the record — the pre-deletion gate. Exits non-zero -# while any actionable/ambiguous entry lacks a Disposition and Destination, -# any generated field was hand-edited, any migrated Destination isn't an existing item -# slug, its Evidence is absent/non-unique, or any human sign-off box is unticked. -uv run /scripts/update_progress.py migration-audit - -# Persist the user's choice. The delete decision never removes files itself. -uv run /scripts/update_progress.py migration-finalize --decision retain -uv run /scripts/update_progress.py migration-finalize --decision delete -uv run /scripts/update_progress.py migration-finalize --confirm-deleted -``` - -Every row in the generated record is one of five `Kind` values: -`actionable` and `ambiguous` block the audit until dispositioned; `done`, -`empty`, and `historical` are pre-filled and non-blocking. An unrecognized -heading becomes `ambiguous`, not `historical` — the scanner defaults to -blocking on what it doesn't recognize, never to treating it as already -covered. - -`actionable` and `ambiguous` rows accept only `migrated` or `excluded`. -`done` and `historical` rows are seeded `not-applicable` but may instead be -`migrated` or `excluded`; `empty` rows accept only `not-applicable`. Every -historical entry is retained as its own non-blocking record row. Retaining the -whole legacy source is a durable document-level outcome after the audit, not -an entry-level `archived` disposition. - --- ## Before starting work Create the progress item with the scaffold script. Resolve `` to -the directory containing this `SKILL.md`; the script can then run from -anywhere inside the project because it locates the project root itself: +the directory containing this `SKILL.md`; the script locates the project root +itself, so it runs from anywhere inside the project: ```bash -# Minimal — one scope entry (branch/ticket filled in later as TBD) -uv run /scripts/new_progress.py \ - --scope api \ - [--plan ] \ - [--title "Task title"] \ - [--dry-run] - -# Full — per-scope-entry branch and ticket; --ticket is the umbrella/epic reference uv run /scripts/new_progress.py \ --scope "api:feature/my-branch:JIRA-111,worker:feature/my-branch" \ --ticket EPIC-100 \ - --plan + --plan \ + [--title "Task title"] [--dry-run] ``` Key arguments: - `slug` — kebab-case identifier, e.g. `subscription-refund-flow` - `--scope` — `name[:branch[:ticket]]`, comma-separated. `name` is a free-form label — not validated against any directory. Escape a literal - comma, colon, or backslash as `\,`, `\:`, or `\\`. `branch` and - per-entry `ticket` default to `TBD` when omitted. Ticket values are kept - **verbatim** — this skill has no opinion on your tracker's numbering - convention (serial, `#123`, `JIRA-111`, a URL — all pass through as given). + comma, colon, or backslash as `\,`, `\:`, or `\\`. `branch` and per-entry + `ticket` default to `TBD` when omitted. Ticket values are kept **verbatim** + — any numbering convention passes through as given. - `--ticket` — umbrella/epic reference for the whole task (optional, `N/A` if omitted). Kept verbatim. -- `--plan` — path to the associated plan file (optional but recommended when - a plan exists). The plan is **copied** into `/_plans/` as a - version-controlled `-` snapshot and linked via an explicit - relative Markdown link in `PROGRESS.md`. - - A path (absolute, or containing `/`) is validated by existence directly — - works with plan output from any tool or agent. - - A bare filename (e.g. `my-plan.md`) is resolved against - `$PROGRESS_TRACKER_PLANS_DIR`, if that env var is set. Without it, a bare - filename is an error asking for a path instead. +- `--plan` — path to the associated plan file. The plan is **copied** into + `/_plans/` as a version-controlled `-` + snapshot and linked via a relative Markdown link in `PROGRESS.md`. A path + (absolute, or containing `/`) is validated by existence directly; a bare + filename resolves against `$PROGRESS_TRACKER_PLANS_DIR` and is an error + when that env var is unset. - `--title` — human-readable title (defaults to the slug title-cased) - `--dir` — tracker directory path, relative to and strictly inside the - project root. Nested paths and dot-directories are allowed; absolute paths, - `.`, `..`, and symlinks that resolve outside the root are rejected. Defaults - to `$PROGRESS_TRACKER_DIR`, then `progress`. -- `--root` — project root directory. Defaults to the current git repository's - top level, falling back to the current working directory. + project root; absolute paths, `.`, `..`, and symlinks that resolve outside + the root are rejected. Defaults to `$PROGRESS_TRACKER_DIR`, then `progress`. +- `--root` — project root. Defaults to the git toplevel, then the cwd. If a plan for this task exists anywhere, **always** pass it via `--plan`. -Use `--dry-run` first to preview what would be created. - On first use in a project, the script scaffolds the tracker directory's supporting files (`README.md`, `INDEX.md`, `_template/PROGRESS.md`, -`_plans/README.md`) from this skill's bundled references — nothing to set up -by hand. An approved migration normally scaffolds those files earlier through -`migration-inventory`, before the first destination item is created. +`_plans/README.md`) from this skill's bundled references. --- ## During work Use the lifecycle script so `PROGRESS.md` and `INDEX.md` are validated and -updated together. Preview with `--dry-run` when changing status or scope: +updated together: ```bash uv run /scripts/update_progress.py update \ @@ -213,14 +121,10 @@ All update options are optional individually, but at least one is required. Task list entry. `--scope` replaces the full Scope table using the same escaped syntax as `new_progress.py`. -The script performs these lifecycle duties: - -1. **Back-fill `TBD` values** in `## Scope` as branches are created and tickets are opened -2. Tick off completed items in `## Task list` (`- [x]`) -3. Add a `### YYYY-MM-DD` entry under `## Work log` each day with brief notes -4. Update the **Updated** field to today -5. Update Status in both this `PROGRESS.md` and `/INDEX.md` per - the lifecycle below; the two values MUST stay identical +In one validated operation it back-fills `TBD` scope values as branches and +tickets appear, ticks completed Task list items, adds the dated `## Work log` +entry, bumps **Updated**, and keeps Status identical in `PROGRESS.md` and +`/INDEX.md` per the lifecycle below. Run an audit at any time (and before review/close-out): @@ -229,8 +133,7 @@ uv run /scripts/update_progress.py check [--dir ] [--root ``` `check` detects invalid statuses, duplicate slugs/rows, missing or stale INDEX -rows, and status drift. Manual edits remain possible, but run `check` -afterward. +rows, and status drift; run it after any manual edit. --- @@ -260,15 +163,8 @@ Any non-terminal status → abandoned | `done` | Development complete (PR/MR merged) | | `abandoned` | Stopped without completing | -Allowed transitions are enforced by `update_progress.py`: - -| From | To | -|---|---| -| `planning` | `in-progress`, `abandoned` | -| `in-progress` | `review`, `blocked`, `abandoned` | -| `review` | `in-progress`, `done`, `abandoned` | -| `blocked` | `in-progress`, `abandoned` | -| `done`, `abandoned` | None (terminal) | +`update_progress.py` enforces exactly the transitions the diagram shows; +`done` and `abandoned` are terminal. --- @@ -286,9 +182,8 @@ uv run /scripts/update_progress.py close \ [--dry-run] ``` -This fills `## Outcome`, appends a final Work log entry, updates **Updated**, -and changes both status sources in one validated operation. Run `check` after -close-out. +This fills `## Outcome`, appends a final Work log entry, and changes both +status sources in one validated operation. Run `check` after close-out. --- @@ -296,7 +191,5 @@ close-out. **Never delete current tracker items automatically.** Deleting current item folders or removing rows from `INDEX.md` is a manual human decision. A legacy -source may be deleted only after migration audits pass and the user explicitly -confirms the exact source target. Record approval with `migration-finalize ---decision delete`, remove only its confirmed sources, then record completion -with `migration-finalize --confirm-deleted` after pointer/link checks. +source may be deleted only through the migration flow's audited finalize +sequence — see `references/migration.md`. diff --git a/skills/progress-tracker/references/migration.md b/skills/progress-tracker/references/migration.md new file mode 100644 index 0000000..288aa25 --- /dev/null +++ b/skills/progress-tracker/references/migration.md @@ -0,0 +1,170 @@ +# Legacy-tracker migration + +Migration is a two-phase commit: the audit is the prepare phase; nothing is +deleted until it votes yes. + +> **Type:** How-to +> **Audience:** Developers, AI assistants +> **Last updated:** 2026-07-28 +> +> This document is the migration contract in full: discovery, consent, the +> step-by-step flow, the authoritative command reference, and the +> Kind/disposition rules. The normative gate block +> (`MIGRATION_GATE_START/END`) lives byte-identically in +> [`SKILL.md`](../SKILL.md), [`workflow.md`](workflow.md), and the bundled +> subagent definition; it is deliberately not duplicated here. +> +> **Terminology:** this document uses RFC 2119 keywords — **MUST**, +> **SHOULD**, **MAY**. + +--- + +## Discovery and consent + +Before first use, the agent **MUST** inspect the project for an existing local +tracking mechanism and for project documents, agent instructions, scripts, or +configuration that point to it (detection rules → [`SKILL.md`](../SKILL.md) +§Before creating anything). + +When a separate mechanism exists, the agent **MUST NOT** scaffold a parallel +tracker without first showing the discovered artifacts and asking whether the +user wants to migrate. Migration requires explicit approval; silence is not +consent. A declined migration preserves the existing mechanism; coexistence +also requires an explicit user choice. + +--- + +## Why the gate exists (KI-001) + +An approved migration is **script-gated**, not a judgment call: a real +migration trial once treated an empty "current work" section as proof a legacy +source held no actionable content, while a "Next steps" section and a backlog +it never inspected still held unmigrated work (see `KNOWN-ISSUE.md` KI-001 in +the repository root for the incident this contract was hardened against). +`migration-inventory` and `migration-audit` exist specifically so that +judgment call is no longer load-bearing. + +--- + +## Migration flow + +Command invocations for every step live in the +[command reference](#command-reference-authoritative) below. + +1. **Inventory first.** Run `migration-inventory` to produce a + section-by-section inventory record at `/_migrations/.md` + **before** editing anything. It scans the **whole document**, not just an + "in progress" or "current work" section, and defaults every unrecognized + heading to `ambiguous` (blocking). If the tracker does not exist yet, the + command scaffolds only its support files; create the destination item(s) + after the inventory exists. +2. **Agree on a source-to-destination mapping.** Copy only facts the source + states; leave unknowns explicit rather than inventing them. +3. **Copy, keeping the source unchanged.** Copy every actionable entry the + inventory lists into the new item(s): current status, scope, + branch/ticket/plan references, goals, unfinished tasks, current work log, + blockers, next actions, and live links. For each `actionable`/`ambiguous` + row, fill in its Disposition (`migrated`, `excluded`, or `not-applicable`) + and Destination. Every `migrated` row also needs a non-trivial, + row-specific Evidence locator copied from the destination item. +4. **Verify field by field.** For each `migrated` row, verify semantic + equivalence with wherever it landed, and tick the record's corresponding + sign-off box only once verified. Missing or unexplained active content — + any row still `TBD` — fails the audit. +5. **Update every live pointer** to the old mechanism: Markdown links, path + mentions, agent instructions, command examples, scripts, and configuration. +6. **Audit the project.** Run `update_progress.py check`, search the whole + project for every old path or filename, and verify every changed relative + link resolves. Classify any remaining match as an intentional + historical/compatibility reference, then tick the record's remaining + sign-off boxes (historical sections disclosed, pointer audit passed, link + audit passed). +7. **Run `migration-audit` — the pre-deletion gate** (the gate block in + `SKILL.md` is normative). Only after it exits 0, show its output and ask + whether to delete the original artifacts. An explicit answer is required. + Persist a declined deletion with `migration-finalize --decision retain`. + For an approved deletion, run `migration-finalize --decision delete`; it + reruns the audit, seals the exact sources and record fingerprint, and + **does not delete anything**. Remove only the approved sources, update + affected pointers, run old-reference/link audits, then run + `migration-finalize --confirm-deleted`. + +The migration is complete only when `migration-audit` exits 0, all active +content is accounted for, the two records agree, every changed pointer has +been reviewed, and the record outcome has left `pending`. The final report +lists the migration record's location, schema, and final outcome, +`migration-audit`'s pass/fail result, the historical/reference entry counts it +disclosed, pointer files updated, and intentional legacy references. + +--- + +## Command reference (authoritative) + +```bash +# Scan the legacy source(s) and write/refresh /_migrations/.md. +# --source may repeat; a directory source is scanned recursively for *.md. +# If no tracker exists yet, this command scaffolds its support files without +# creating a progress item, so inventory still precedes destination creation. +# Re-running upgrades v1 records to v2 and preserves every compatible +# Disposition, Destination, and Evidence whose +# entry is unchanged. Sign-offs survive only an identical source + inventory; +# any inventory change resets all of them and prints that refresh result. +uv run /scripts/update_progress.py migration-inventory --source [--source ...] + +# Rescan and reconcile against the record — the pre-deletion gate. Exits non-zero +# while any actionable/ambiguous entry lacks a Disposition and Destination, +# any generated field was hand-edited, any migrated Destination isn't an existing item +# slug, its Evidence is absent/non-unique, or any human sign-off box is unticked. +uv run /scripts/update_progress.py migration-audit + +# Persist the user's choice. The delete decision never removes files itself. +uv run /scripts/update_progress.py migration-finalize --decision retain +uv run /scripts/update_progress.py migration-finalize --decision delete +uv run /scripts/update_progress.py migration-finalize --confirm-deleted +``` + +--- + +## Kind values and disposition rules + +Every row in the generated record is one of five `Kind` values: +`actionable` and `ambiguous` block the audit until dispositioned; `done`, +`empty`, and `historical` are pre-filled and non-blocking. An unrecognized +heading becomes `ambiguous`, not `historical` — the scanner defaults to +blocking on what it doesn't recognize, never to treating it as already +covered. + +`actionable` and `ambiguous` rows accept only `migrated` or `excluded`. +`done` and `historical` rows are seeded `not-applicable` but may instead be +`migrated` or `excluded`; `empty` rows accept only `not-applicable`. Every +historical entry is retained as its own non-blocking record row. Retaining the +whole legacy source is a durable document-level outcome after the audit, not +an entry-level `archived` disposition. + +--- + +## ✅ Do / ❌ Don't + +✅ **Do** + +- Discover existing tracking artifacts and ask before migrating or scaffolding + a parallel tracker +- Run `migration-inventory` before copying anything — inventory the whole + source document, not just an "in progress" or "current work" section +- Dispose of every `actionable`/`ambiguous` inventory row and run + `migration-audit` before asking whether to delete the source +- Record the user's retain/delete choice with `migration-finalize` +- Audit every old path/name and changed link after an approved migration + +❌ **Don't** + +- Don't treat silence as migration consent or leave stale pointers to the old + tracking mechanism +- Don't ask to delete the source until `migration-audit` exits 0 — an empty + "in progress" section is not proof the rest of the document holds no + actionable content +- Don't hand-edit a migration record's `ID`, `Kind`, `Source`, `Loc`, + `Section`, or `Entry` cells; re-run `migration-inventory` instead +- Don't delete a `/_migrations/.md` record while its legacy + source still exists — it is the audit trail proving the migration was + complete diff --git a/skills/progress-tracker/references/workflow.md b/skills/progress-tracker/references/workflow.md index 691ef0b..93ac19d 100644 --- a/skills/progress-tracker/references/workflow.md +++ b/skills/progress-tracker/references/workflow.md @@ -2,12 +2,13 @@ > **Type:** How-to > **Audience:** Developers, AI assistants -> **Last updated:** 2026-07-26 +> **Last updated:** 2026-07-28 > > This document explains the **workflow and purpose** of the progress tracker > (why, folder structure, field semantics, cleanup policy). The single > source of truth for **operating mechanics** (script argument spec, `--plan` -> resolution rules, status enum) is [`../SKILL.md`](../SKILL.md). +> resolution rules, status enum) is [`../SKILL.md`](../SKILL.md); the +> migration contract in full is [`migration.md`](migration.md). > > **Terminology:** this document uses RFC 2119 keywords — **MUST**, > **SHOULD**, **MAY**. @@ -39,55 +40,13 @@ across sessions and machines. ## Existing-tracker discovery and migration -Before first use, the agent **MUST** inspect the project for an existing local -tracking mechanism and for project documents, agent instructions, scripts, or -configuration that point to it. Likely names include root-level `PROGRESS.md`, -`progress_note/`, `progress-notes/`, and `WORKLOG.md`, but content and documented -purpose determine whether an artifact is a tracker. A tracker already using -this skill's `/INDEX.md` structure is not a migration candidate. - -When a separate mechanism exists, the agent **MUST NOT** scaffold a parallel -tracker without first showing the discovered artifacts and asking whether the -user wants to migrate. Migration requires explicit approval. A declined -migration preserves the existing mechanism; coexistence also requires an -explicit user choice. - -An approved migration follows this contract. It is **script-gated**, not a -judgment call: a real migration trial once treated an empty "current work" -section as proof a legacy source held no actionable content, while a -"Next steps" section and a backlog it never inspected still held unmigrated -work (see `KNOWN-ISSUE.md` KI-001 in the repository root for the incident this -contract was hardened against). `migration-inventory` and `migration-audit` -exist specifically so that judgment call is no longer load-bearing. - -1. Inventory source artifacts and all live pointers to them. Run - `migration-inventory --source ` to produce a - section-by-section inventory record at `/_migrations/.md` - — this scans the **whole document**, not just an "in progress" or "current - work" section, and defaults every unrecognized heading to `ambiguous` - (blocking) rather than assuming it is safe to ignore. If the tracker does - not exist yet, this command scaffolds only its support files; create the - destination item(s) after the inventory exists. -2. Agree on a source-to-destination mapping; do not infer missing task facts. -3. Keep the source unchanged and copy every in-progress concern into the new - item(s): current status, scope, branch/ticket/plan references, goals, - unfinished tasks, current work log, blockers, next actions, and live links. - For each `actionable`/`ambiguous` row in the inventory record, fill in its - Disposition (`migrated`, `excluded`, or `not-applicable`) and - Destination. Every `migrated` row also gets a non-trivial Evidence locator - copied from the destination item. -4. Compare source and destination field by field. For each `migrated` row, - verify semantic equivalence with wherever it landed and tick the record's - corresponding sign-off box. Missing or unexplained active content — any - row still `TBD` — fails the audit. -5. Update every live pointer: links, path mentions, agent instructions, command - examples, scripts, and configuration. -6. Run `update_progress.py check`, search the project for each old path/name, - and verify every changed relative link resolves. Classify any remaining - match as an intentional historical/compatibility reference, then tick the - record's remaining sign-off boxes (historical sections disclosed, pointer - audit passed, link audit passed). -7. Run `migration-audit ` — the pre-deletion gate: +Before first use, the agent **MUST** run the existing-tracker preflight +(detection rules → [`SKILL.md`](../SKILL.md) §Before creating anything). + +A separate tracking mechanism was discovered → read +[`migration.md`](migration.md) in full before running any migration command. +That document owns discovery, consent, the KI-001 rationale, the step-by-step +flow, the authoritative command reference, and the Kind/disposition rules. Migration is script-gated. The deletion question MUST NOT be asked until both @@ -105,20 +64,6 @@ sign-offs cover work already performed. An empty WIP section is not evidence of an empty actionable set. - Only once `migration-audit` exits 0, show its output and ask whether to - delete the original artifacts. Persist a declined decision with - `migration-finalize --decision retain`. For an approved deletion, - run `migration-finalize --decision delete`; it reruns the audit, - seals the record and source list, and does not delete anything. Remove only - the approved sources, update affected pointers, run old-reference/link - audits, then use `migration-finalize --confirm-deleted`. - -The migration is not complete while `migration-audit` exits non-zero, active -content is missing, the two records disagree, an unreviewed stale pointer -remains, or the record outcome is still `pending`. The final report lists the -migration record's schema and outcome, `migration-audit`'s result, historical -entry counts, updated pointer files, and intentional legacy references. - --- ## Quick usage @@ -278,14 +223,9 @@ its own. - Scripts and AI agents **MUST NOT** proactively delete current item folders or INDEX rows. -- A migrated legacy source MAY be deleted only after `migration-audit` exits 0 - and `migration-finalize --decision delete` records the user's exact target - approval. That command does not delete files. After removal and pointer/link - checks, `migration-finalize --confirm-deleted` records completion only when - all approved sources are absent and the sealed record is unchanged. -- Do not delete a `/_migrations/.md` record while its - legacy source still exists — it is the audit trail proving the migration - was complete. +- A migrated legacy source MAY be deleted only through the audited finalize + sequence in [`migration.md`](migration.md), which also covers retention of + the migration record itself. - Other cleanup remains a **human decision**, performed manually as needed. - Consider keeping `done` / `abandoned` items around for at least one sprint for later reference and retrospectives. @@ -296,14 +236,8 @@ its own. ✅ **Do** -- Discover existing tracking artifacts and ask before migrating or scaffolding - a parallel tracker -- Run `migration-inventory` before copying anything — inventory the whole - source document, not just an "in progress" or "current work" section -- Dispose of every `actionable`/`ambiguous` inventory row and run - `migration-audit` before asking whether to delete the source -- Record the user's retain/delete choice with `migration-finalize` -- Audit every old path/name and changed link after an approved migration +- Follow [`migration.md`](migration.md) end-to-end when an existing tracking + mechanism is discovered - Preview with `--dry-run` before creating a task - Include **every scope entry involved** in `--scope`, even read-only ones - Fill in `TBD` for branch/ticket before they exist, then back-fill `## Scope` once known @@ -316,13 +250,6 @@ its own. ❌ **Don't** -- Don't treat silence as migration consent or leave stale pointers to the old - tracking mechanism -- Don't ask to delete the source until `migration-audit` exits 0 — an empty - "in progress" section is not proof the rest of the document holds no - actionable content -- Don't hand-edit a migration record's `ID`, `Kind`, `Source`, `Loc`, - `Section`, or `Entry` cells; re-run `migration-inventory` instead - Don't delete tracker-directory folders directly (update the INDEX status to `abandoned` first, then clean up manually) - Don't fill real content into `_template/PROGRESS.md` (the template should From dd15dd11429eeaa880c95da699116d73b32c3243 Mon Sep 17 00:00:00 2001 From: "eric.fang" Date: Tue, 28 Jul 2026 09:29:30 +0800 Subject: [PATCH 02/10] docs: rewrite skill description with one trigger per branch Front-loads the preflight leading word, gives each lifecycle branch (create / update / audit / close out / migrate) exactly one trigger, and drops identity prose duplicated by the body (110 words, within the 40-160 gate). The activation boundary is unchanged, so the trigger matrix stays as-is; the invocation-semantics decision (check stays model-invoked, migration stays in-skill via progressive disclosure, no router) is recorded in docs/design-decisions.md. Continued no-op pruning lands SKILL.md at 178 lines. Co-Authored-By: Claude Fable 5 --- docs/design-decisions.md | 17 +++++ .../PROGRESS.md | 1 + skills/progress-tracker/SKILL.md | 73 +++++++------------ 3 files changed, 46 insertions(+), 45 deletions(-) diff --git a/docs/design-decisions.md b/docs/design-decisions.md index ad591a4..c61bf5c 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -9,6 +9,23 @@ seems arbitrary, check here before changing it. --- +## 2026-07-28 — Invocation semantics stay single-skill + +Reviewed alongside the progressive-disclosure split: `check` (the tracker +audit) stays model-invoked inside this skill so agents run it before +review/close-out without a separate activation; migration stays in-skill too, +loaded only through the read-in-full pointer to `references/migration.md` +rather than split into a second skill — a split would spend an extra +description slot in every session's context for a branch most sessions never +take. With only two skills in this repository, a router skill is likewise +unjustified. The frontmatter description was rewritten in the same spirit: +one leading trigger per lifecycle branch (create / update / audit / close out +/ migrate), front-loaded with the preflight anchor. The activation boundary +is unchanged — the same prompts trigger and the same prompts do not — so +`evals/trigger-matrix.json` and its case counts stay as they are. + +--- + ## 2026-07-28 — Progressive disclosure for the migration contract Migration detail (~110 of SKILL.md's 302 lines) sat top-level in `SKILL.md` diff --git a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md index ff61670..d1294cb 100644 --- a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md +++ b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md @@ -30,6 +30,7 @@ - - Phase 1: split migration contract into references/migration.md (single authoritative doc, absorbing workflow.md's parallel section), pruned SKILL.md 302 -> 195 lines, synced doc surfaces; verify.sh green. +- Phase 2: rewrote frontmatter description (one leading trigger per lifecycle branch, preflight anchor, 110 words) and recorded the single-skill invocation-semantics decision; SKILL.md at 178 lines. ## Outcome diff --git a/skills/progress-tracker/SKILL.md b/skills/progress-tracker/SKILL.md index 24ee0d1..3906a13 100644 --- a/skills/progress-tracker/SKILL.md +++ b/skills/progress-tracker/SKILL.md @@ -1,25 +1,20 @@ --- name: progress-tracker description: >- - Manages local development progress tracking under a project's progress/ - directory. Trigger this skill when: (1) the user wants to start tracking a - development task that runs a full lifecycle (investigate → fix → test → - PR/MR), whether it touches one scope (service, package, repo) or spans - several — and wants a progress record; (2) the user asks to create, - update, audit/check, close out, or migrate a progress item or existing local - tracking documents; (3) the user explicitly invokes /progress-tracker. Do - NOT trigger for genuine one-off questions or trivial - edits with no lifecycle to track (a typo fix, a config tweak, answering a - question) — a single-scope bug fix that goes through investigate/fix/test/PR - still qualifies for (1). + Preflight-checked local development progress tracking under a project's + progress/ directory. Trigger when: (1) the user starts a development task + with a full lifecycle (investigate → fix → test → PR/MR) and wants a durable + record — create; (2) an existing item's status, scope, or work log needs + updating — update; (3) the tracker needs a consistency audit — audit; + (4) finished work needs its outcome recorded — close out; (5) existing local + tracking documents should be adopted — migrate; (6) the user invokes + /progress-tracker. Do NOT trigger for one-off questions or trivial edits + with no lifecycle (a typo, a config tweak); a single-scope fix with a full + lifecycle still qualifies. --- # Development Progress Tracker -Track development tasks locally under a `progress/` directory at the project -root (configurable). A task may span one scope or several (services, packages, -sibling repos). - Full spec: `references/workflow.md` Migration contract: `references/migration.md` @@ -61,9 +56,8 @@ of an empty actionable set. ## Before starting work -Create the progress item with the scaffold script. Resolve `` to -the directory containing this `SKILL.md`; the script locates the project root -itself, so it runs from anywhere inside the project: +Create the progress item with the scaffold script — resolve `` to +this `SKILL.md`'s directory; the script locates the project root itself: ```bash uv run /scripts/new_progress.py \ @@ -80,14 +74,12 @@ Key arguments: comma, colon, or backslash as `\,`, `\:`, or `\\`. `branch` and per-entry `ticket` default to `TBD` when omitted. Ticket values are kept **verbatim** — any numbering convention passes through as given. -- `--ticket` — umbrella/epic reference for the whole task (optional, `N/A` - if omitted). Kept verbatim. +- `--ticket` — umbrella/epic reference for the whole task; optional, verbatim. - `--plan` — path to the associated plan file. The plan is **copied** into `/_plans/` as a version-controlled `-` - snapshot and linked via a relative Markdown link in `PROGRESS.md`. A path - (absolute, or containing `/`) is validated by existence directly; a bare - filename resolves against `$PROGRESS_TRACKER_PLANS_DIR` and is an error - when that env var is unset. + snapshot and linked from `PROGRESS.md`. A path (absolute, or containing + `/`) is validated by existence directly; a bare filename resolves against + `$PROGRESS_TRACKER_PLANS_DIR` and is an error when that env var is unset. - `--title` — human-readable title (defaults to the slug title-cased) - `--dir` — tracker directory path, relative to and strictly inside the project root; absolute paths, `.`, `..`, and symlinks that resolve outside @@ -96,9 +88,8 @@ Key arguments: If a plan for this task exists anywhere, **always** pass it via `--plan`. -On first use in a project, the script scaffolds the tracker directory's -supporting files (`README.md`, `INDEX.md`, `_template/PROGRESS.md`, -`_plans/README.md`) from this skill's bundled references. +On first use, the script scaffolds the tracker's supporting files +(`README.md`, `INDEX.md`, `_template/PROGRESS.md`, `_plans/README.md`). --- @@ -117,14 +108,11 @@ uv run /scripts/update_progress.py update \ ``` All update options are optional individually, but at least one is required. -`--complete-task` may be repeated and matches the exact text of an unchecked -Task list entry. `--scope` replaces the full Scope table using the same escaped -syntax as `new_progress.py`. - -In one validated operation it back-fills `TBD` scope values as branches and -tickets appear, ticks completed Task list items, adds the dated `## Work log` -entry, bumps **Updated**, and keeps Status identical in `PROGRESS.md` and -`/INDEX.md` per the lifecycle below. +`--complete-task` (repeatable) matches the exact text of an unchecked Task +list entry; `--scope` replaces the full Scope table using the same escaped +syntax as `new_progress.py`. Back-fill `TBD` scope values as branches and +tickets appear, log work daily, and keep Status identical in `PROGRESS.md` +and `/INDEX.md` per the lifecycle below. Run an audit at any time (and before review/close-out): @@ -154,14 +142,9 @@ Any non-terminal status → abandoned ``` -| Status | Meaning | -|---|---| -| `planning` | Item created, implementation not started (scaffold-script default) | -| `in-progress` | Under active development | -| `review` | PR/MR opened, in code review / QA — **not** `done`; that comes after merge | -| `blocked` | Paused on an external dependency | -| `done` | Development complete (PR/MR merged) | -| `abandoned` | Stopped without completing | +`planning` is the scaffold default. `review` means a PR/MR is open (code +review / QA) — **not** `done`, which comes only after merge. `blocked` is +paused on an external dependency; `abandoned` is stopped without completing. `update_progress.py` enforces exactly the transitions the diagram shows; `done` and `abandoned` are terminal. @@ -182,8 +165,8 @@ uv run /scripts/update_progress.py close \ [--dry-run] ``` -This fills `## Outcome`, appends a final Work log entry, and changes both -status sources in one validated operation. Run `check` after close-out. +This fills `## Outcome` and changes both status sources in one validated +operation. Run `check` after close-out. --- From 8b44ba0907769034abb4bc22746bd8d73bbb4b50 Mon Sep 17 00:00:00 2001 From: "eric.fang" Date: Tue, 28 Jul 2026 09:31:21 +0800 Subject: [PATCH 03/10] docs: anchor migration as a two-phase commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the leading-words anchor — 'Migration is a two-phase commit: the audit is the prepare phase; nothing is deleted until it votes yes.' — immediately above the gate block in SKILL.md and at the top of references/migration.md. The sentence stays outside the byte-synced MIGRATION_GATE markers so the three gate copies remain the only enforced duplication. Negation scan: hard guardrails (never-delete, silence-is-not-consent, the frozen gate wording) stay verbatim; remaining soft negations were already rewritten positively during the Phase 1 split. SKILL.md lands at 180 lines. Co-Authored-By: Claude Fable 5 --- .../PROGRESS.md | 1 + skills/progress-tracker/SKILL.md | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md index d1294cb..07a5ba6 100644 --- a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md +++ b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md @@ -31,6 +31,7 @@ - - Phase 1: split migration contract into references/migration.md (single authoritative doc, absorbing workflow.md's parallel section), pruned SKILL.md 302 -> 195 lines, synced doc surfaces; verify.sh green. - Phase 2: rewrote frontmatter description (one leading trigger per lifecycle branch, preflight anchor, 110 words) and recorded the single-skill invocation-semantics decision; SKILL.md at 178 lines. +- Phase 3: anchored the two-phase-commit leading sentence above the gate block (outside the byte-synced markers) and in migration.md's opening; negation scan kept hard guardrails, soft negations were already rewritten positively during the split. ## Outcome diff --git a/skills/progress-tracker/SKILL.md b/skills/progress-tracker/SKILL.md index 3906a13..6b3ecd7 100644 --- a/skills/progress-tracker/SKILL.md +++ b/skills/progress-tracker/SKILL.md @@ -15,8 +15,7 @@ description: >- # Development Progress Tracker -Full spec: `references/workflow.md` -Migration contract: `references/migration.md` +Full spec: `references/workflow.md` · Migration contract: `references/migration.md` ## Before creating anything: existing-tracker preflight @@ -36,6 +35,9 @@ existing mechanism; a second tracker requires an explicit coexistence choice. A migration was approved → read `references/migration.md` in full before running any migration command. +Migration is a two-phase commit: the audit is the prepare phase; nothing is +deleted until it votes yes. + Migration is script-gated. The deletion question MUST NOT be asked until both commands have run and the second exited 0: From 1e43bedc54dcf7534c82f7b36acdafae545dd38f Mon Sep 17 00:00:00 2001 From: "eric.fang" Date: Tue, 28 Jul 2026 09:33:47 +0800 Subject: [PATCH 04/10] docs: add shared vocabulary, editing checklist, and eval audit; bump to 1.8.0 Phase 4 of the writing-great-skills refactor: a Shared Vocabulary section in docs/domain-models.md (scope, tracker-dir, preflight, two-phase commit, Kind, disposition) instead of a new CONTEXT.md; a six-failure-mode SKILL.md editing checklist in AGENTS.md; and a design-decision entry recording that the eval suite is not tautological with SKILL.md (graders assert only on script output in disposable repos). Both plugin manifests bump 1.7.1 -> 1.8.0 for the user-visible description and structure changes. Dogfood progress item closed through the real lifecycle scripts. Co-Authored-By: Claude Fable 5 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- AGENTS.md | 17 +++++++++++++++++ docs/design-decisions.md | 15 +++++++++++++++ docs/domain-models.md | 14 ++++++++++++++ .../PROGRESS.md | 12 +++++++----- progress/INDEX.md | 2 +- 7 files changed, 56 insertions(+), 8 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index c9ad269..4fd60be 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "progress-tracker", "displayName": "Progress Tracker", - "version": "1.7.1", + "version": "1.8.0", "description": "Tracks local development progress across scopes with consented, script-gated migration (a whole-document inventory audit, not just an eyeballed active-work section), and safe lifecycle scripts — for any project.", "author": { "name": "FWcloud916", diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 972a128..1d18112 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "progress-tracker", - "version": "1.7.1", + "version": "1.8.0", "description": "Track local development progress across single- or multi-scope task lifecycles.", "author": { "name": "FWcloud916", diff --git a/AGENTS.md b/AGENTS.md index 1dea9c7..c44a753 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -65,6 +65,23 @@ python3 evals/scripts/test_grade_scenarios.py - Requirement keywords (MUST/SHOULD/MAY) follow RFC 2119, uppercase. - English throughout — templates, field names, and section headings. +## SKILL.md editing checklist + +Before committing any change to `skills/progress-tracker/SKILL.md`, ask: + +1. **Premature completion** — does any sentence claim behavior that no script + or eval enforces yet? +2. **Duplication** — does the same rule now live in two places? Keep one + authoritative copy and point to it (only the gate and lifecycle marker + blocks are deliberately duplicated, byte-synced by verify.sh). +3. **Sediment** — is any sentence a leftover from a superseded design? +4. **Sprawl** — should single-branch detail move to a `references/` doc + behind a read-in-full pointer instead of staying top-level? +5. **No-op** — does each sentence change behavior relative to the model's + default? Delete failing sentences whole. +6. **Negation** — can a "do not" be restated as the positive target behavior? + (Hard guardrails stay negative.) + ## Docs maintenance When modifying any file under `docs/`, update its `> **Last updated:** diff --git a/docs/design-decisions.md b/docs/design-decisions.md index c61bf5c..b03df01 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -9,6 +9,21 @@ seems arbitrary, check here before changing it. --- +## 2026-07-28 — Eval suite is not tautological with SKILL.md + +Audited whether the graders judge with the same logic the skill documents — +which would let a docs change rubber-stamp itself. They do not: every +scenario runs the real CLIs in a disposable git repo, and +`grade_scenarios.py` asserts only on the files those runs produce +(`files_exist` / `files_absent` / `content_contains` / `content_not_contains` +/ `content_count`); no scenario, grader, or grader-regression test reads +`SKILL.md` or any `references/` doc. Expected values therefore come from an +independent truth source (script behavior), so the documentation refactor +cannot false-green the evals and the evals cannot vouch for prose claims — +`scripts/verify.sh` remains the gate for doc-level consistency. + +--- + ## 2026-07-28 — Invocation semantics stay single-skill Reviewed alongside the progressive-disclosure split: `check` (the tracker diff --git a/docs/domain-models.md b/docs/domain-models.md index f9ae358..cbace31 100644 --- a/docs/domain-models.md +++ b/docs/domain-models.md @@ -13,6 +13,20 @@ migration contract in [`references/migration.md`](../skills/progress-tracker/references/migration.md)); this document maps that contract to the implementing functions in the two Python CLIs. +## 0. Shared Vocabulary + +Terms used with a fixed meaning across `SKILL.md`, the `references/` docs, +and this document: + +| Term | Definition | +|---|---| +| **scope** | A free-form label for one piece of a task (e.g. `api`, `payments-service`); never validated against any directory. | +| **tracker-dir** | The tracker's root directory inside the project — `progress/` by default, overridden by `--dir` / `$PROGRESS_TRACKER_DIR` — holding `INDEX.md`, item folders, `_template/`, `_plans/`, and `_migrations/`. | +| **preflight** | The mandatory pre-creation inspection for an existing tracking mechanism and the documents that point to it (`SKILL.md` §Before creating anything). | +| **two-phase commit** | The migration deletion model: `migration-audit` is the prepare phase, and deletion may proceed only after it exits 0 and `migration-finalize` records the user's explicit decision (the commit). | +| **Kind** | A migration-inventory row's generated classification: `actionable` and `ambiguous` block the audit; `done`, `empty`, and `historical` do not. | +| **disposition** | The reviewer's ruling on an inventory row: `migrated`, `excluded`, or `not-applicable`. | + ## 1. Model Details ```text diff --git a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md index 07a5ba6..797ed2c 100644 --- a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md +++ b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md @@ -1,7 +1,7 @@ # SKILL.md progressive-disclosure refactor **Slug:** skill-docs-progressive-disclosure -**Status:** in-progress +**Status:** done **Ticket:** N/A **Related plan:** [skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md](../_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md) **Created:** 2026-07-28 @@ -32,11 +32,13 @@ - Phase 1: split migration contract into references/migration.md (single authoritative doc, absorbing workflow.md's parallel section), pruned SKILL.md 302 -> 195 lines, synced doc surfaces; verify.sh green. - Phase 2: rewrote frontmatter description (one leading trigger per lifecycle branch, preflight anchor, 110 words) and recorded the single-skill invocation-semantics decision; SKILL.md at 178 lines. - Phase 3: anchored the two-phase-commit leading sentence above the gate block (outside the byte-synced markers) and in migration.md's opening; negation scan kept hard guardrails, soft negations were already rewritten positively during the split. +- Phase 4: shared vocabulary in domain-models.md, SKILL.md editing checklist in AGENTS.md, eval-tautology audit recorded, plugin manifests bumped to 1.8.0; full gate green. Branch ready for review. +- Closed item as `done`. ## Outcome -> Fill in after development finishes. +Executed the writing-great-skills refactor: SKILL.md 302 -> 180 lines via progressive disclosure into references/migration.md (single authoritative migration doc), description rewritten with one trigger per lifecycle branch, two-phase-commit leading anchor added, shared vocabulary + SKILL.md editing checklist + eval-tautology audit recorded; plugin 1.8.0. verify.sh, ruff, 14 scenarios, and grader regression all green. -**Final status:** -**PR / Commit:** -**Follow-ups:** +**Final status:** done +**PR / Commit:** branch claude/progress-tracker-improvement-plan-950330 +**Follow-ups:** None diff --git a/progress/INDEX.md b/progress/INDEX.md index c37be76..e23d867 100644 --- a/progress/INDEX.md +++ b/progress/INDEX.md @@ -10,7 +10,7 @@ workflow. | Status | Item | Folder | Scope | Ticket | Plan | Created | Notes | |---|---|---|---|---|---|---|---| | `done` | Progress Tracker Improvements | `progress/2026-07-24-progress-tracker-improvements/` | `progress-tracker` | N/A | _plans/progress-tracker-improvements-plan.md | 2026-07-24 | P0 verification truthfulness; P1 lifecycle automation and input hardening | -| `in-progress` | SKILL.md progressive-disclosure refactor | `progress/2026-07-28-skill-docs-progressive-disclosure/` | `progress-tracker` | N/A | [skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md](_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md) | 2026-07-28 | | +| `done` | SKILL.md progressive-disclosure refactor | `progress/2026-07-28-skill-docs-progressive-disclosure/` | `progress-tracker` | N/A | [skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md](_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md) | 2026-07-28 | | ## Status legend From c93862ed0d7261a76b837efce09b084135df66e2 Mon Sep 17 00:00:00 2001 From: "eric.fang" Date: Tue, 28 Jul 2026 09:38:06 +0800 Subject: [PATCH 05/10] docs: point README and overview at the authoritative migration contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collapses README's fourth paraphrase of the migration audit conditions — the most drift-prone restatement — into the two-phase-commit framing with a link to references/migration.md, and adds the same pointer to project-overview §1.1. Other description surfaces (plugin manifests, openai.yaml, the agent frontmatter) describe behavior the refactor did not change, so they stay as-is. Co-Authored-By: Claude Fable 5 --- README.md | 16 +++++++--------- docs/project-overview.md | 4 +++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2b51bb0..3e3de5e 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,13 @@ several — for any project. tracker directory's supporting files. - **Migration guard, script-gated** — before first use, inventories existing tracking documents and their pointers and asks whether to migrate. - `migration-inventory` scans a legacy source **whole-document** (never just - an "in progress" section), can scaffold an empty tracker before destination - creation, and `migration-audit` is the pre-deletion gate: it - fails while any actionable/ambiguous entry lacks a valid `migrated` or - `excluded` disposition and destination, migrated Evidence is absent or - non-unique, any generated record field changed, - any old path/name or changed link is unverified, or the pre-deletion human - sign-off checklist is incomplete. `migration-finalize` durably records the - user's retain/delete decision without deleting source files. + Migration is a two-phase commit: `migration-inventory` scans each legacy + source **whole-document** (never just an "in progress" section), + `migration-audit` is the prepare-phase gate that must pass before the + deletion question may even be asked, and `migration-finalize` durably + records the user's retain/delete decision without deleting source files. + The full contract lives in + [`references/migration.md`](skills/progress-tracker/references/migration.md). - **Multi-scope, tool-agnostic** — `--scope name[:branch[:ticket]]` accepts any free-form label (a service, a package, a sibling repo — not validated against a directory) and any ticket format (serial, `#123`, `JIRA-111`, a diff --git a/docs/project-overview.md b/docs/project-overview.md index e4edeff..6a56f31 100644 --- a/docs/project-overview.md +++ b/docs/project-overview.md @@ -30,7 +30,9 @@ runtime. The public behavior is specified in [`skills/progress-tracker/SKILL.md`](../skills/progress-tracker/SKILL.md). The longer operational rationale and field reference live in -[`workflow.md`](../skills/progress-tracker/references/workflow.md). +[`workflow.md`](../skills/progress-tracker/references/workflow.md); the migration +contract in full lives in +[`migration.md`](../skills/progress-tracker/references/migration.md). ### 1.2 Relationship with Other Systems From 86d2ec442fa4e0f99d0a16a14ef4ac95200c7267 Mon Sep 17 00:00:00 2001 From: "eric.fang" Date: Tue, 28 Jul 2026 12:03:04 +0800 Subject: [PATCH 06/10] docs: corroborate the progressive-disclosure decision with Anthropic's context-engineering post Records how the refactor maps onto the post's paradigm shifts and the two deliberate divergences: incident-driven gate wording stays (the load-bearing minority of rules), and CLI examples/Key-arguments stay pending a separate interface-design experiment. Co-Authored-By: Claude Fable 5 --- docs/design-decisions.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/design-decisions.md b/docs/design-decisions.md index b03df01..5e9d626 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -63,6 +63,22 @@ every create runs them, not just the migration branch. `migration.md` is not in verify.sh's scaffold-link map, so it is never copied into user projects — the scaffolded seed docs already direct readers back to the installed skill. +External corroboration (added the same day): Anthropic's post ["The new +rules of context engineering for Claude 5 generation +models"](https://claude.com/blog/the-new-rules-of-context-engineering-for-claude-5-generation-models) +independently prescribes the same moves this refactor made — progressive +disclosure over upfront content, focused single-home guidance over +repetition, and pruning constraint prose in favor of model judgment +("unhobbling"). Two deliberate divergences stand: the migration gate's +MUST NOT wording stays, because it is an incident-driven (KI-001), +script-enforced guardrail — exactly the load-bearing minority of rules the +post's own framing says to keep; and SKILL.md retains its three CLI +examples plus the Key-arguments list, because verify.sh check 6 requires +the command strings and the examples are already one-per-lifecycle-stage +minimal. A possible future step in the post's "interface design" spirit — +collapsing the Key-arguments list into a `--help` pointer — is deferred; it +would be an independent change needing its own eval pass. + --- ## 2026-07-27 — Package Codex directly from the repository root From 3c51b62fbb893bdaf4c6c61214c661e33c7d15cc Mon Sep 17 00:00:00 2001 From: "eric.fang" Date: Tue, 28 Jul 2026 12:10:31 +0800 Subject: [PATCH 07/10] docs: defer SKILL.md argument semantics to the CLI interface Interface-design experiment, evaluated by a fact-by-fact coverage matrix against live --help output and probed error messages: the interface fully teaches slug format, scope syntax/defaults, plan resolution, dir containment, and root discovery, and every probed mistake fails with an actionable message. Those sentences leave SKILL.md (180 -> 162 lines) behind a read---help-first pointer. Behavioral policies and the one silent failure the interface cannot catch (an unescaped comma splits a --scope entry without error) stay in SKILL.md. Method and adoption rationale recorded in docs/design-decisions.md. Co-Authored-By: Claude Fable 5 --- docs/design-decisions.md | 35 ++++++++++++++++++++-- skills/progress-tracker/SKILL.md | 50 ++++++++++---------------------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/docs/design-decisions.md b/docs/design-decisions.md index 5e9d626..221efb1 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -9,6 +9,34 @@ seems arbitrary, check here before changing it. --- +## 2026-07-28 — SKILL.md defers argument semantics to the CLI interface + +Experiment: can SKILL.md's Key-arguments list and option prose be replaced +by the scripts' own `--help`, per the interface-design principle (the +interface teaches through its structure; errors teach at the moment of the +mistake)? Method: a fact-by-fact coverage matrix — every sentence proposed +for deletion was checked against live `--help` output and probed error +messages, keeping the eval philosophy's independent truth source (actual +CLI behavior, not the docs being edited). + +Result: adopted. `--help` fully covers slug format, `--scope` syntax and +defaults, verbatim tickets, `--plan` resolution, `--dir` containment, and +`--root` discovery; `choices=` enums expose the status values; and every +probed mistake (bad slug, bare plan filename, dir escape, optionless +update, invalid transition, unknown slug) fails with an actionable message +that names the fix. Those sentences left SKILL.md (180 → 162 lines) behind +a "read `--help` before first use" pointer. + +Two classes of prose stayed, by rule: **behavioral policy** the interface +cannot express (always pass `--plan` when a plan exists; back-fill `TBD`; +keep the two Status fields identical; `review` ≠ `done`), and **silent- +failure traps** the interface cannot catch at the right moment — the one +found: an unescaped comma in a `--scope` value splits the entry in two with +no error, so the escaping rule remains in SKILL.md. If a future CLI change +makes that failure loud, the sentence can move to `--help` too. + +--- + ## 2026-07-28 — Eval suite is not tautological with SKILL.md Audited whether the graders judge with the same logic the skill documents — @@ -75,9 +103,10 @@ script-enforced guardrail — exactly the load-bearing minority of rules the post's own framing says to keep; and SKILL.md retains its three CLI examples plus the Key-arguments list, because verify.sh check 6 requires the command strings and the examples are already one-per-lifecycle-stage -minimal. A possible future step in the post's "interface design" spirit — -collapsing the Key-arguments list into a `--help` pointer — is deferred; it -would be an independent change needing its own eval pass. +minimal. The post's "interface design" step — collapsing the Key-arguments list into +a `--help` pointer — was run as its own experiment and adopted the same day; +see the next entry for the coverage-matrix method and the one fact the +interface cannot teach. --- diff --git a/skills/progress-tracker/SKILL.md b/skills/progress-tracker/SKILL.md index 6b3ecd7..d4c430f 100644 --- a/skills/progress-tracker/SKILL.md +++ b/skills/progress-tracker/SKILL.md @@ -69,26 +69,14 @@ uv run /scripts/new_progress.py \ [--title "Task title"] [--dry-run] ``` -Key arguments: -- `slug` — kebab-case identifier, e.g. `subscription-refund-flow` -- `--scope` — `name[:branch[:ticket]]`, comma-separated. `name` is a - free-form label — not validated against any directory. Escape a literal - comma, colon, or backslash as `\,`, `\:`, or `\\`. `branch` and per-entry - `ticket` default to `TBD` when omitted. Ticket values are kept **verbatim** - — any numbering convention passes through as given. -- `--ticket` — umbrella/epic reference for the whole task; optional, verbatim. -- `--plan` — path to the associated plan file. The plan is **copied** into - `/_plans/` as a version-controlled `-` - snapshot and linked from `PROGRESS.md`. A path (absolute, or containing - `/`) is validated by existence directly; a bare filename resolves against - `$PROGRESS_TRACKER_PLANS_DIR` and is an error when that env var is unset. -- `--title` — human-readable title (defaults to the slug title-cased) -- `--dir` — tracker directory path, relative to and strictly inside the - project root; absolute paths, `.`, `..`, and symlinks that resolve outside - the root are rejected. Defaults to `$PROGRESS_TRACKER_DIR`, then `progress`. -- `--root` — project root. Defaults to the git toplevel, then the cwd. - -If a plan for this task exists anywhere, **always** pass it via `--plan`. +Argument semantics (slug format, per-entry defaults, `--plan` path +resolution, `--dir` containment, `--root` discovery) live in the script's +`--help` — read it before first use. Two rules the interface cannot teach at +the right moment: + +- Escape a literal comma, colon, or backslash in `--scope` values as `\,`, + `\:`, or `\\` — an unescaped comma silently splits the entry in two. +- If a plan for this task exists anywhere, **always** pass it via `--plan`. On first use, the script scaffolds the tracker's supporting files (`README.md`, `INDEX.md`, `_template/PROGRESS.md`, `_plans/README.md`). @@ -109,22 +97,18 @@ uv run /scripts/update_progress.py update \ [--dry-run] ``` -All update options are optional individually, but at least one is required. -`--complete-task` (repeatable) matches the exact text of an unchecked Task -list entry; `--scope` replaces the full Scope table using the same escaped -syntax as `new_progress.py`. Back-fill `TBD` scope values as branches and -tickets appear, log work daily, and keep Status identical in `PROGRESS.md` -and `/INDEX.md` per the lifecycle below. +Option semantics live in `update --help`; invalid input fails with the +allowed values. Back-fill `TBD` scope values as branches and tickets appear, +log work daily, and keep Status identical in `PROGRESS.md` and +`/INDEX.md` per the lifecycle below. -Run an audit at any time (and before review/close-out): +Audit consistency at any time — before review/close-out and after any manual +edit: ```bash uv run /scripts/update_progress.py check [--dir ] [--root ] ``` -`check` detects invalid statuses, duplicate slugs/rows, missing or stale INDEX -rows, and status drift; run it after any manual edit. - --- ## Status lifecycle (canonical) @@ -155,8 +139,7 @@ paused on an external dependency; `abandoned` is stopped without completing. ## After completing work -Close the item with the lifecycle script. `--outcome` is required; `--status` -defaults to `done` and may also be `abandoned`: +Close the item with the lifecycle script: ```bash uv run /scripts/update_progress.py close \ @@ -167,8 +150,7 @@ uv run /scripts/update_progress.py close \ [--dry-run] ``` -This fills `## Outcome` and changes both status sources in one validated -operation. Run `check` after close-out. +Run `check` after close-out. --- From 35c221461685a6145f31bac51596f39074dc8f32 Mon Sep 17 00:00:00 2001 From: "eric.fang" Date: Tue, 28 Jul 2026 13:48:18 +0800 Subject: [PATCH 08/10] feat: reject ambiguous scope commas and echo parsed scope Closes the one silent-failure trap the interface-design pass could not teach: an unescaped comma in --scope with adjacent whitespace is now rejected as ambiguous (the error teaches both resolutions), an empty entry from a trailing/leading/doubled comma is rejected instead of silently dropped, and both CLIs echo the parsed scope names in normal mode so any remaining wrong split is immediately visible. With the failure now loud, the escaping rule moves out of SKILL.md (159 lines) into --help, leaving always-pass---plan as the sole interface-untaught rule. Adds 16 pytest cases, the ambiguous-scope-refuse scenario (15 total), and syncs workflow.md, domain-models.md, design-decisions.md, and evals/README.md. Co-Authored-By: Claude Fable 5 --- docs/design-decisions.md | 14 ++++-- docs/domain-models.md | 3 ++ evals/README.md | 6 +-- .../ambiguous-scope-refuse/scenario.json | 12 +++++ skills/progress-tracker/SKILL.md | 13 ++--- .../progress-tracker/references/workflow.md | 2 + .../progress-tracker/scripts/new_progress.py | 40 +++++++++++++--- .../scripts/test_new_progress.py | 47 ++++++++++++++++++- .../scripts/test_update_progress.py | 33 +++++++++++++ .../scripts/update_progress.py | 11 +++-- 10 files changed, 156 insertions(+), 25 deletions(-) create mode 100644 evals/scenarios/ambiguous-scope-refuse/scenario.json diff --git a/docs/design-decisions.md b/docs/design-decisions.md index 221efb1..24452bd 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -31,9 +31,12 @@ Two classes of prose stayed, by rule: **behavioral policy** the interface cannot express (always pass `--plan` when a plan exists; back-fill `TBD`; keep the two Status fields identical; `review` ≠ `done`), and **silent- failure traps** the interface cannot catch at the right moment — the one -found: an unescaped comma in a `--scope` value splits the entry in two with -no error, so the escaping rule remains in SKILL.md. If a future CLI change -makes that failure loud, the sentence can move to `--help` too. +found: an unescaped comma in a `--scope` value used to split the entry in +two with no error. That pre-registered condition has since fired: the parser +now rejects whitespace-adjacent unescaped commas and empty entries, and both +CLIs echo the parsed scope in normal mode (see the amended 2026-07-24 +escaping entry), so the escaping sentence moved out of SKILL.md into +`--help`, leaving `--plan` as the sole interface-untaught rule. --- @@ -153,7 +156,10 @@ Allowed transitions reflect normal review rework and explicit termination: Scope labels remain free-form and filesystem-independent. The compact legacy syntax stays compatible, with backslash escaping added for literal commas, colons, and backslashes. The first two unescaped colons delimit fields and an -unescaped comma delimits scope entries. +unescaped comma delimits scope entries. Amended 2026-07-28: an unescaped +comma with adjacent whitespace is rejected as ambiguous and an empty entry +(trailing, leading, or doubled comma) is rejected — both previously failed +silently — and both CLIs echo the parsed scope names in normal-mode output. All CLI values written into Markdown tables are now rendered through shared helpers. Pipes and backslashes are escaped, code spans choose a delimiter that diff --git a/docs/domain-models.md b/docs/domain-models.md index cbace31..87d71c8 100644 --- a/docs/domain-models.md +++ b/docs/domain-models.md @@ -311,6 +311,9 @@ Only then does the record enter `deleted`. - `require_project_descendant()` resolves paths before accepting them, including paths behind existing symlinks. - Scope labels are parsed text and never validated as directories. +- Ambiguous scope input fails closed: an unescaped comma touching whitespace + and empty scope entries are rejected at parse time, and both CLIs echo the + parsed scope names in normal-mode output. - Ticket values are trimmed and defaulted but never prefixed or reformatted. - Markdown table values escape pipes and backslashes; inline code chooses a delimiter longer than any backtick run in the value. diff --git a/evals/README.md b/evals/README.md index 1aac254..70c9908 100644 --- a/evals/README.md +++ b/evals/README.md @@ -8,9 +8,9 @@ is a deterministic script, not a model judgment call). 1. **Lifecycle scenarios** run the real scaffold script end-to-end in a disposable git repository and grade filesystem/content invariants — - creation, multi-scope expansion, escaped input, slug-namespaced plan - snapshots, lifecycle-script close-out, contained custom `--dir`/`--root` - overrides, first-inventory tracker scaffolding, and the + creation, multi-scope expansion, escaped input, ambiguous-scope refusal, + slug-namespaced plan snapshots, lifecycle-script close-out, contained + custom `--dir`/`--root` overrides, first-inventory tracker scaffolding, and the migration-inventory/migration-audit gate, per-row Evidence, durable migration-finalize outcomes (including the KI-001 regression: an empty "in progress" section must not make the audit diff --git a/evals/scenarios/ambiguous-scope-refuse/scenario.json b/evals/scenarios/ambiguous-scope-refuse/scenario.json new file mode 100644 index 0000000..706d482 --- /dev/null +++ b/evals/scenarios/ambiguous-scope-refuse/scenario.json @@ -0,0 +1,12 @@ +{ + "description": "An unescaped comma with adjacent whitespace in --scope is rejected as ambiguous instead of silently splitting the entry, and nothing is written.", + "steps": [ + {"type": "run_expect_fail", "args": ["ambiguous-task", "--scope", "my label, with comma"], "expect_stderr_contains": "ambiguous"} + ], + "checks": { + "files_absent": [ + "progress", + "*-ambiguous-task" + ] + } +} diff --git a/skills/progress-tracker/SKILL.md b/skills/progress-tracker/SKILL.md index d4c430f..3f2d329 100644 --- a/skills/progress-tracker/SKILL.md +++ b/skills/progress-tracker/SKILL.md @@ -69,14 +69,11 @@ uv run /scripts/new_progress.py \ [--title "Task title"] [--dry-run] ``` -Argument semantics (slug format, per-entry defaults, `--plan` path -resolution, `--dir` containment, `--root` discovery) live in the script's -`--help` — read it before first use. Two rules the interface cannot teach at -the right moment: - -- Escape a literal comma, colon, or backslash in `--scope` values as `\,`, - `\:`, or `\\` — an unescaped comma silently splits the entry in two. -- If a plan for this task exists anywhere, **always** pass it via `--plan`. +Argument semantics (slug format, `--scope` escaping, per-entry defaults, +`--plan` path resolution, `--dir` containment, `--root` discovery) live in +the script's `--help` — read it before first use. One rule the interface +cannot teach: if a plan for this task exists anywhere, **always** pass it +via `--plan`. On first use, the script scaffolds the tracker's supporting files (`README.md`, `INDEX.md`, `_template/PROGRESS.md`, `_plans/README.md`). diff --git a/skills/progress-tracker/references/workflow.md b/skills/progress-tracker/references/workflow.md index 93ac19d..74f1f88 100644 --- a/skills/progress-tracker/references/workflow.md +++ b/skills/progress-tracker/references/workflow.md @@ -258,3 +258,5 @@ its own. - Don't pass a `slug` containing uppercase letters, underscores, or spaces (the script rejects it) - Don't use a separator other than comma between `--scope` entries (comma separates entries, colon separates name/branch/ticket within one) +- Don't put whitespace around a separating comma — the parser rejects it as + ambiguous (escape a literal comma inside one label as `\,`) diff --git a/skills/progress-tracker/scripts/new_progress.py b/skills/progress-tracker/scripts/new_progress.py index 943ff4e..2557ea8 100755 --- a/skills/progress-tracker/scripts/new_progress.py +++ b/skills/progress-tracker/scripts/new_progress.py @@ -208,13 +208,16 @@ def parse_scope(scope_arg: str) -> list[ScopeEntry]: - name is a free-form label; not validated against any directory - branch defaults to 'TBD' when omitted or empty - ticket defaults to 'TBD' when omitted; kept verbatim otherwise + - an unescaped separator comma with whitespace on either side is + rejected as ambiguous, and an empty entry (trailing, leading, or + doubled comma) is rejected — both would otherwise fail silently """ entries: list[ScopeEntry] = [] parsed_entries: list[list[str]] = [] segments = [""] escaped = False - for char in scope_arg: + for i, char in enumerate(scope_arg): if escaped: if char not in {",", ":", "\\"}: sys.exit( @@ -226,6 +229,15 @@ def parse_scope(scope_arg: str) -> list[ScopeEntry]: elif char == "\\": escaped = True elif char == ",": + prev_char = scope_arg[i - 1] if i else "" + next_char = scope_arg[i + 1] if i + 1 < len(scope_arg) else "" + if prev_char.isspace() or next_char.isspace(): + sys.exit( + f"ERROR: ambiguous --scope value {scope_arg!r}: whitespace " + "touches an unescaped comma. Entry separators take no " + "surrounding whitespace (api,worker); a literal comma inside " + "one label must be escaped (my label\\, with comma)." + ) parsed_entries.append(segments) segments = [""] elif char == ":" and len(segments) < 3: @@ -239,7 +251,16 @@ def parse_scope(scope_arg: str) -> list[ScopeEntry]: for segments in parsed_entries: if len(segments) == 1 and not segments[0].strip(): - continue + if len(parsed_entries) == 1: + sys.exit( + "ERROR: --scope produced no valid entries. " + "Provide at least one scope name." + ) + sys.exit( + f"ERROR: empty --scope entry in {scope_arg!r}: a trailing, " + "leading, or doubled comma creates an empty entry. Remove the " + "extra comma." + ) name = segments[0].strip() branch = segments[1].strip() if len(segments) > 1 else "" ticket_raw = segments[2].strip() if len(segments) > 2 else "" @@ -256,12 +277,16 @@ def parse_scope(scope_arg: str) -> list[ScopeEntry]: entries.append((name, branch, ticket)) - if not entries: - sys.exit("ERROR: --scope produced no valid entries. Provide at least one scope name.") - return entries +def scope_summary(entries: list[ScopeEntry]) -> str: + """One-line echo of the parsed scope names, so a wrong split is visible.""" + names = " · ".join(name for name, _, _ in entries) + unit = "entry" if len(entries) == 1 else "entries" + return f"{names} ({len(entries)} {unit})" + + def resolve_plan(plan_arg: str | None) -> tuple[str, Path | None]: """Resolve a plan argument to (plan_name, source_path). @@ -554,7 +579,9 @@ def build_arg_parser() -> argparse.ArgumentParser: "Comma-separated `name[:branch[:ticket]]` entries. " "name is a free-form label (service, package, repo — not validated). " "Escape literal commas, colons, and backslashes with a backslash. " - "branch defaults to TBD when omitted. " + "An unescaped comma with adjacent whitespace is rejected as " + "ambiguous, and an empty entry (trailing/doubled comma) is " + "rejected. branch defaults to TBD when omitted. " "ticket defaults to TBD when omitted; kept verbatim otherwise." ), ) @@ -712,6 +739,7 @@ def main() -> None: item_dir.mkdir(parents=True) progress_file.write_text(rendered, encoding="utf-8") print(f"Created: {progress_file}") + print(f"Scope: {scope_summary(scope_entries)}") if source_path: copy_plan(source_path, plan_name, plans_local_dir, dry_run) diff --git a/skills/progress-tracker/scripts/test_new_progress.py b/skills/progress-tracker/scripts/test_new_progress.py index 6b31894..c155270 100644 --- a/skills/progress-tracker/scripts/test_new_progress.py +++ b/skills/progress-tracker/scripts/test_new_progress.py @@ -110,8 +110,9 @@ def test_no_directory_validation(self, tmp_path, monkeypatch): assert entries[0][0] == "totally-made-up-service-name" def test_empty_scope_errors(self): - with pytest.raises(SystemExit): + with pytest.raises(SystemExit) as exc_info: np.parse_scope("") + assert "no valid entries" in str(exc_info.value) def test_empty_name_in_entry_errors(self): with pytest.raises(SystemExit): @@ -136,6 +137,39 @@ def test_unknown_escape_errors(self): with pytest.raises(SystemExit): np.parse_scope(r"api\q") + @pytest.mark.parametrize( + "value", + [ + "api, worker", # space after the separator + "api ,worker", # space before the separator + "api,\tworker", # tab counts as whitespace + "a:b, c", # inside a fielded entry + r"api\,, worker", # escaped comma, then an ambiguous real one + ], + ) + def test_whitespace_adjacent_to_comma_is_ambiguous(self, value): + with pytest.raises(SystemExit) as exc_info: + np.parse_scope(value) + assert "ambiguous" in str(exc_info.value) + + def test_padded_single_entry_is_stripped(self): + assert np.parse_scope(" api ") == [("api", "TBD", "TBD")] + + @pytest.mark.parametrize("value", ["api,", ",api", "api,,worker"]) + def test_empty_entry_from_extra_comma_errors(self, value): + with pytest.raises(SystemExit) as exc_info: + np.parse_scope(value) + assert "empty --scope entry" in str(exc_info.value) + + +class TestScopeSummary: + def test_single_entry(self): + assert np.scope_summary([("api", "TBD", "TBD")]) == "api (1 entry)" + + def test_multiple_entries(self): + entries = [("api", "feature/x", "JIRA-1"), ("worker", "TBD", "TBD")] + assert np.scope_summary(entries) == "api · worker (2 entries)" + class TestResolvePlan: def test_no_plan(self): @@ -349,6 +383,17 @@ def test_index_row_appended_with_planning_status(self, project): assert "| `planning` | Demo Task |" in index assert "`api`" in index + def test_scope_echo_in_normal_mode(self, project): + result = run_cli(["echo-task", "--scope", "api:feature/x,worker"], cwd=project) + assert result.returncode == 0, result.stderr + assert "Scope: api · worker (2 entries)" in result.stdout + + def test_ambiguous_scope_writes_nothing(self, project): + result = run_cli(["bad-task", "--scope", "api, worker"], cwd=project) + assert result.returncode != 0 + assert "ambiguous" in result.stderr + assert not (project / "progress").exists() + def test_scope_row_expansion_multi_scope(self, project): run_cli( ["multi-task", "--scope", "api:feature/x:JIRA-1,worker:feature/y"], diff --git a/skills/progress-tracker/scripts/test_update_progress.py b/skills/progress-tracker/scripts/test_update_progress.py index 308c100..6d5ce40 100644 --- a/skills/progress-tracker/scripts/test_update_progress.py +++ b/skills/progress-tracker/scripts/test_update_progress.py @@ -127,6 +127,39 @@ def test_replaces_scope_table_with_escaped_values(self, project): assert "JIRA-1,JIRA-2" in content assert "`worker` | TBD | TBD" in content + def test_scope_replacement_echoes_parsed_entries(self, project): + result = run_script( + UPDATE_SCRIPT, + ["update", "demo-task", "--scope", "api:feature/x,worker"], + project, + ) + assert result.returncode == 0, result.stderr + assert "Scope: api · worker (2 entries)" in result.stdout + + def test_ambiguous_scope_leaves_files_unchanged(self, project): + before_progress = item_file(project).read_text() + before_index = (project / "progress" / "INDEX.md").read_text() + result = run_script( + UPDATE_SCRIPT, + ["update", "demo-task", "--scope", "api, worker"], + project, + ) + assert result.returncode != 0 + assert "ambiguous" in result.stderr + assert item_file(project).read_text() == before_progress + assert (project / "progress" / "INDEX.md").read_text() == before_index + + def test_empty_scope_entry_leaves_files_unchanged(self, project): + before_progress = item_file(project).read_text() + result = run_script( + UPDATE_SCRIPT, + ["update", "demo-task", "--scope", "api,"], + project, + ) + assert result.returncode != 0 + assert "empty --scope entry" in result.stderr + assert item_file(project).read_text() == before_progress + def test_dry_run_writes_nothing(self, project): before_progress = item_file(project).read_text() before_index = (project / "progress" / "INDEX.md").read_text() diff --git a/skills/progress-tracker/scripts/update_progress.py b/skills/progress-tracker/scripts/update_progress.py index 767076a..0991f6f 100755 --- a/skills/progress-tracker/scripts/update_progress.py +++ b/skills/progress-tracker/scripts/update_progress.py @@ -27,6 +27,7 @@ from new_progress import ( DEFAULT_TRACKER_DIRNAME, REFERENCES_DIR, + ScopeEntry, markdown_code, markdown_table_text, parse_scope, @@ -35,6 +36,7 @@ resolve_project_root, resolve_tracker_dir, scaffold_tracker_dir, + scope_summary, validate_scaffold, validate_single_line, validate_slug, @@ -409,8 +411,7 @@ def replace_metadata(content: str, field: str, value: str, path: Path) -> str: return updated -def replace_scope_table(content: str, scope_arg: str, path: Path) -> str: - entries = parse_scope(scope_arg) +def replace_scope_table(content: str, entries: list[ScopeEntry], path: Path) -> str: start = content.find(SCOPE_TABLE_HEADER) if start == -1: sys.exit(f"ERROR: Scope table header not found in {path}") @@ -590,8 +591,10 @@ def run_mutation(args: argparse.Namespace) -> int: progress_after = progress_before if target_status != current_status: progress_after = replace_metadata(progress_after, "Status", target_status, progress_path) + scope_entries = None if args.command == "update" and args.scope: - progress_after = replace_scope_table(progress_after, args.scope, progress_path) + scope_entries = parse_scope(args.scope) + progress_after = replace_scope_table(progress_after, scope_entries, progress_path) if args.command == "update" and args.complete_task: progress_after = complete_tasks(progress_after, args.complete_task, progress_path) work_log = args.work_log @@ -630,6 +633,8 @@ def run_mutation(args: argparse.Namespace) -> int: print(f"Updated: {progress_path}") if index_before != index_after: print(f"Updated: {index_path}") + if scope_entries is not None: + print(f"Scope: {scope_summary(scope_entries)}") print(f"Status: {target_status}") return 0 From e7f7fcbfc4378908b88e78c05abac6a89d59b709 Mon Sep 17 00:00:00 2001 From: "eric.fang" Date: Tue, 28 Jul 2026 14:09:38 +0800 Subject: [PATCH 09/10] =?UTF-8?q?fix:=20address=20PR=20#1=20review=20?= =?UTF-8?q?=E2=80=94=20reject=20explicit=20empty=20--scope,=20honest=20rev?= =?UTF-8?q?iew=20status,=20plan=20deviations=20recorded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update_progress.py: --scope checks use 'is not None' so an explicitly empty --scope reaches parse_scope and fails loudly ('no valid entries') instead of being silently treated as omitted — both at the mutation guard and at the replacement site. Two pytest cases added (193 total). - new_progress.py --help: empty-entry wording now names leading commas too, matching parse_scope's actual contract. - Progress records: item status done -> review in PROGRESS.md and INDEX.md (an item is not done while its PR is open); Outcome links the PR; the premature close is corrected by an appended log line, not rewritten history. - Plan snapshot: dated deviation record explains the 309 -> 302 baseline (measured a day apart across other main landings) and the Phase 4 reroute from CONTEXT.md to domain-models.md §0 Shared Vocabulary. - design-decisions: new entry recording why shared vocabulary lives in domain-models.md rather than a repo-root CONTEXT.md (and when to revisit, given doc-architect 2.4.0's opt-in glossary module). Gate: verify.sh all green, uvx ruff==0.16.0 clean, pytest 193 passed. Co-Authored-By: Claude Fable 5 --- docs/design-decisions.md | 16 ++++++++++++++ .../PROGRESS.md | 10 ++++++--- progress/INDEX.md | 2 +- ...osure-progress-tracker-improvement-plan.md | 13 ++++++++++++ .../progress-tracker/scripts/new_progress.py | 4 ++-- .../scripts/test_update_progress.py | 21 +++++++++++++++++++ .../scripts/update_progress.py | 4 ++-- 7 files changed, 62 insertions(+), 8 deletions(-) diff --git a/docs/design-decisions.md b/docs/design-decisions.md index 24452bd..ca6a370 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -9,6 +9,22 @@ seems arbitrary, check here before changing it. --- +## 2026-07-28 — Shared vocabulary lives in domain-models.md, not a repo-root CONTEXT.md + +The improvement plan's Phase 4 prescribed a repo-root `CONTEXT.md` for shared domain +vocabulary (scope, disposition, Kind, tracker-dir, preflight, two-phase commit). +Implemented instead as `docs/domain-models.md` §0 Shared Vocabulary: + +- The terms are structural domain terminology — they sit one section above the + entities that define them, so definitions and structure cannot drift apart. +- `AGENTS.md`'s task→doc table already routes "domain behavior" reads to + domain-models.md; a second repo-root file would split the same lookup across two + homes for no added recall. +- doc-architect 2.4.0 ships an opt-in `CONTEXT.md` glossary module; this repo + declines the module deliberately — revisit only if the vocabulary outgrows the + domain reference or needs rulings that contradict it (`_Avoid_` synonym lists, + cross-doc drift tripwires). + ## 2026-07-28 — SKILL.md defers argument semantics to the CLI interface Experiment: can SKILL.md's Key-arguments list and option prose be replaced diff --git a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md index 797ed2c..e0bc18f 100644 --- a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md +++ b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md @@ -1,7 +1,7 @@ # SKILL.md progressive-disclosure refactor **Slug:** skill-docs-progressive-disclosure -**Status:** done +**Status:** review **Ticket:** N/A **Related plan:** [skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md](../_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md) **Created:** 2026-07-28 @@ -34,11 +34,15 @@ - Phase 3: anchored the two-phase-commit leading sentence above the gate block (outside the byte-synced markers) and in migration.md's opening; negation scan kept hard guardrails, soft negations were already rewritten positively during the split. - Phase 4: shared vocabulary in domain-models.md, SKILL.md editing checklist in AGENTS.md, eval-tautology audit recorded, plugin manifests bumped to 1.8.0; full gate green. Branch ready for review. - Closed item as `done`. +- Reopened to `review` per PR #1 code review: an item is not `done` while its PR is + still open. Also addressed the review findings (scope-comma guard: explicit empty + `--scope` now rejected instead of silently ignored; `--help` names leading commas; + plan deviations recorded). ## Outcome Executed the writing-great-skills refactor: SKILL.md 302 -> 180 lines via progressive disclosure into references/migration.md (single authoritative migration doc), description rewritten with one trigger per lifecycle branch, two-phase-commit leading anchor added, shared vocabulary + SKILL.md editing checklist + eval-tautology audit recorded; plugin 1.8.0. verify.sh, ruff, 14 scenarios, and grader regression all green. -**Final status:** done -**PR / Commit:** branch claude/progress-tracker-improvement-plan-950330 +**Final status:** review — [PR #1](https://github.com/FWcloud916/skill-progress-tracker/pull/1) open; close as `done` after merge +**PR / Commit:** [PR #1](https://github.com/FWcloud916/skill-progress-tracker/pull/1) (branch claude/progress-tracker-improvement-plan-950330) **Follow-ups:** None diff --git a/progress/INDEX.md b/progress/INDEX.md index e23d867..67f00f9 100644 --- a/progress/INDEX.md +++ b/progress/INDEX.md @@ -10,7 +10,7 @@ workflow. | Status | Item | Folder | Scope | Ticket | Plan | Created | Notes | |---|---|---|---|---|---|---|---| | `done` | Progress Tracker Improvements | `progress/2026-07-24-progress-tracker-improvements/` | `progress-tracker` | N/A | _plans/progress-tracker-improvements-plan.md | 2026-07-24 | P0 verification truthfulness; P1 lifecycle automation and input hardening | -| `done` | SKILL.md progressive-disclosure refactor | `progress/2026-07-28-skill-docs-progressive-disclosure/` | `progress-tracker` | N/A | [skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md](_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md) | 2026-07-28 | | +| `review` | SKILL.md progressive-disclosure refactor | `progress/2026-07-28-skill-docs-progressive-disclosure/` | `progress-tracker` | N/A | [skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md](_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md) | 2026-07-28 | | ## Status legend diff --git a/progress/_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md b/progress/_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md index b5a96dc..96ba227 100644 --- a/progress/_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md +++ b/progress/_plans/skill-docs-progressive-disclosure-progress-tracker-improvement-plan.md @@ -109,3 +109,16 @@ inventory → audit(= prepare)→ finalize(= commit)→ confirm-deleted 的結 | 6 | Phase 4 擇項 | CONTEXT.md 等 | verify.sh 通過 | **每個 phase 是一個獨立 commit / PR,eval 全綠才進下一個。** + +--- + +## 執行偏差記錄(2026-07-28,PR #1 review 後補記) + +1. **基線行數 309 → 302:** 本計劃撰寫時(2026-07-27)量測 SKILL.md 為 309 行; + 執行開始時(2026-07-28)main 上的 SKILL.md 為 302 行(期間 main 有其他變更落地)。 + 驗收指標「≤ 180 行」以執行起點 302 為基線計算,PROGRESS.md 記錄的 302 為準。 +2. **Phase 4 第 1 項改道:** 計劃寫 `CONTEXT.md`,實作裁決為 + `docs/domain-models.md` §0 Shared Vocabulary — 詞彙緊鄰其實體定義、 + AGENTS.md 的 task→doc 表已路由到該檔;獨立的 repo-root 檔案會把命名與 + agents 實際閱讀的 domain 參考拆成兩處。完整理由見 + `docs/design-decisions.md`「Shared vocabulary lives in domain-models.md」條目。 diff --git a/skills/progress-tracker/scripts/new_progress.py b/skills/progress-tracker/scripts/new_progress.py index 2557ea8..db87d2e 100755 --- a/skills/progress-tracker/scripts/new_progress.py +++ b/skills/progress-tracker/scripts/new_progress.py @@ -580,8 +580,8 @@ def build_arg_parser() -> argparse.ArgumentParser: "name is a free-form label (service, package, repo — not validated). " "Escape literal commas, colons, and backslashes with a backslash. " "An unescaped comma with adjacent whitespace is rejected as " - "ambiguous, and an empty entry (trailing/doubled comma) is " - "rejected. branch defaults to TBD when omitted. " + "ambiguous, and an empty entry (leading, trailing, or doubled " + "comma) is rejected. branch defaults to TBD when omitted. " "ticket defaults to TBD when omitted; kept verbatim otherwise." ), ) diff --git a/skills/progress-tracker/scripts/test_update_progress.py b/skills/progress-tracker/scripts/test_update_progress.py index 6d5ce40..254e335 100644 --- a/skills/progress-tracker/scripts/test_update_progress.py +++ b/skills/progress-tracker/scripts/test_update_progress.py @@ -160,6 +160,27 @@ def test_empty_scope_entry_leaves_files_unchanged(self, project): assert "empty --scope entry" in result.stderr assert item_file(project).read_text() == before_progress + def test_explicitly_empty_scope_is_rejected_not_ignored(self, project): + before_progress = item_file(project).read_text() + result = run_script( + UPDATE_SCRIPT, + ["update", "demo-task", "--scope", "", "--work-log", "note"], + project, + ) + assert result.returncode != 0 + assert "no valid entries" in result.stderr + assert item_file(project).read_text() == before_progress + + def test_empty_scope_alone_is_a_mutation_not_omitted(self, project): + result = run_script( + UPDATE_SCRIPT, + ["update", "demo-task", "--scope", ""], + project, + ) + assert result.returncode != 0 + assert "no valid entries" in result.stderr + assert "requires at least one" not in result.stderr + def test_dry_run_writes_nothing(self, project): before_progress = item_file(project).read_text() before_index = (project / "progress" / "INDEX.md").read_text() diff --git a/skills/progress-tracker/scripts/update_progress.py b/skills/progress-tracker/scripts/update_progress.py index 0991f6f..2d4958e 100755 --- a/skills/progress-tracker/scripts/update_progress.py +++ b/skills/progress-tracker/scripts/update_progress.py @@ -567,7 +567,7 @@ def prepare_item(args: argparse.Namespace) -> tuple[Path, str, Path, str, list[s def run_mutation(args: argparse.Namespace) -> int: if args.command == "update" and not any( - (args.status, args.scope, args.work_log, args.complete_task) + (args.status, args.scope is not None, args.work_log, args.complete_task) ): sys.exit( "ERROR: update requires at least one of --status, --scope, " @@ -592,7 +592,7 @@ def run_mutation(args: argparse.Namespace) -> int: if target_status != current_status: progress_after = replace_metadata(progress_after, "Status", target_status, progress_path) scope_entries = None - if args.command == "update" and args.scope: + if args.command == "update" and args.scope is not None: scope_entries = parse_scope(args.scope) progress_after = replace_scope_table(progress_after, scope_entries, progress_path) if args.command == "update" and args.complete_task: From 26c897aacacfd84ad6c9dda2473a8a8d90739fb2 Mon Sep 17 00:00:00 2001 From: "eric.fang" Date: Tue, 28 Jul 2026 14:23:23 +0800 Subject: [PATCH 10/10] chore: fill in the dogfooded progress record's Background & Task list CodeRabbit's review-status finding on PR #1 was only partially addressed by e7f7fcb: Status/INDEX/Outcome were corrected, but Background & goals was still the unfilled template placeholder and Task list still had two empty checkboxes. Fills both in against the actual delivered work, and drops a stray empty Work log bullet left over from the initial scaffold. Co-Authored-By: Claude Fable 5 --- .../PROGRESS.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md index e0bc18f..d66946a 100644 --- a/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md +++ b/progress/2026-07-28-skill-docs-progressive-disclosure/PROGRESS.md @@ -17,18 +17,29 @@ ## Background & goals -> Describe the background, motivation, and expected outcome of this task. +Execute the user-supplied `progress-tracker-improvement-plan.md` (per the +writing-great-skills review of `SKILL.md`): reduce context load, eliminate +duplication between `SKILL.md` and `references/workflow.md`, and strengthen +leading-word anchoring — using the repo's own evals (trigger matrix + +scenarios) and `scripts/verify.sh` as the regression net throughout. Expected +outcome: `SKILL.md` shrinks well below its 302-line starting point without +losing any enforced behavior, and the migration contract lives in exactly one +authoritative place. ## Task list -- [ ] -- [ ] +- [x] Phase 1 — split the migration contract into `references/migration.md`; prune no-op sentences +- [x] Phase 2 — rewrite the frontmatter description; record invocation-semantics decision +- [x] Phase 3 — anchor migration as a two-phase commit; negation scan +- [x] Phase 4 — shared vocabulary, editing checklist, eval-tautology audit, plugin version bump +- [x] Interface-design experiment — defer argument semantics to `--help` via a coverage matrix +- [x] Close the scope-comma silent-failure trap the experiment surfaced (ambiguity + empty-entry errors, scope echo) +- [x] Address PR #1 code review (explicit-empty-`--scope` handling, review-status honesty, plan deviation record) ## Work log ### 2026-07-28 -- - Phase 1: split migration contract into references/migration.md (single authoritative doc, absorbing workflow.md's parallel section), pruned SKILL.md 302 -> 195 lines, synced doc surfaces; verify.sh green. - Phase 2: rewrote frontmatter description (one leading trigger per lifecycle branch, preflight anchor, 110 words) and recorded the single-skill invocation-semantics decision; SKILL.md at 178 lines. - Phase 3: anchored the two-phase-commit leading sentence above the gate block (outside the byte-synced markers) and in migration.md's opening; negation scan kept hard guardrails, soft negations were already rewritten positively during the split.