From 8f30413cd12f4db52282dc924f68ea9ef936d637 Mon Sep 17 00:00:00 2001 From: Alex Wegener Date: Wed, 11 Nov 2026 11:29:03 +0100 Subject: [PATCH 01/10] chore: plan M021 (status line segment, issue #31): context, roadmap, slices, S01-T01 plan Co-Authored-By: Claude Fable 5.2 --- .ytstack/M021-CONTEXT.md | 43 +++++++++++++++++++++++++++++++++++ .ytstack/M021-ROADMAP.md | 40 ++++++++++++++++++++++++++++++++ .ytstack/M021-S01-PLAN.md | 27 ++++++++++++++++++++++ .ytstack/M021-S01-T01-PLAN.md | 43 +++++++++++++++++++++++++++++++++++ .ytstack/M021-S02-PLAN.md | 27 ++++++++++++++++++++++ .ytstack/STATE.md | 16 ++++++++----- 6 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 .ytstack/M021-CONTEXT.md create mode 100644 .ytstack/M021-ROADMAP.md create mode 100644 .ytstack/M021-S01-PLAN.md create mode 100644 .ytstack/M021-S01-T01-PLAN.md create mode 100644 .ytstack/M021-S02-PLAN.md diff --git a/.ytstack/M021-CONTEXT.md b/.ytstack/M021-CONTEXT.md new file mode 100644 index 0000000..30bc5fa --- /dev/null +++ b/.ytstack/M021-CONTEXT.md @@ -0,0 +1,43 @@ +--- +milestone: M021 +project: ytstack +created: 2026-11-11T10:25:52Z +size: M +--- + +# M021 -- Context + +## Goal + +Any status line script can append one compact ytstack segment that shows project progress and the current task of the directory it runs in, read from `.ytstack/STATE.md` without per-project scraping. + +## Exit criteria + +- With a `progress` block in the `STATE.md` frontmatter, `bin/ytstack-status` prints label, percentage and done/total; with `waiting_on` set it also prints that item, truncated. +- Without the block it prints the active slice's task count taken from the checkboxes in `M###-S##-PLAN.md`. +- Outside a ytstack project it prints nothing and exits non-zero. +- In a linked worktree the plan progress comes from the default branch and the task pointer from the worktree's own `STATE.md`. +- A malformed block prints nothing; `yt_state_lint` reports it (integers, `done <= total`, short label). +- `summarize-task` and `quick-task` keep the block current when they advance STATE. +- `docs/statusline.md` documents the block, the two-line integration and a stable script location that survives plugin updates; README and CHANGELOG point at it. +- `bash tests/run-all.sh` passes and `bin/ytstack-check` reports no new failures. + +## Size + +M -- 2 slices. See `M021-ROADMAP.md` for slice breakdown. + +## Planning input + +GitHub issue #31 and the user's instruction to implement it. Milestone number M021 because M020 (Kimi adapter, issue #30) is planned in the main checkout and not committed yet. + +## Decisions locked in discuss phase + +2026-11-11: Exactly one `progress` track with `label`, `done`, `total` and optional `waiting_on`, as the issue proposes. Several named tracks and a `done`/`open` pair (the issue's open questions) stay out until a project needs them; the single block can be extended to a list later without breaking readers. + +2026-11-11: One parser for the block lives in `hooks/lib/git-ctx.sh` and serves both the status script and the STATE lint, so the two cannot disagree about what is malformed. + +2026-11-11: No network and no `gh` in the status script. The default branch is read from local refs only. + +## Open questions + +(none) diff --git a/.ytstack/M021-ROADMAP.md b/.ytstack/M021-ROADMAP.md new file mode 100644 index 0000000..706d79b --- /dev/null +++ b/.ytstack/M021-ROADMAP.md @@ -0,0 +1,40 @@ +--- +milestone: M021 +project: ytstack +size: M +created: 2026-11-11T10:25:52Z +status: in-progress +total_slices: 2 +completed_slices: 0 +--- + +# M021 Roadmap + +**Goal:** Any status line script can append one compact ytstack segment that shows project progress and the current task of the directory it runs in, read from `.ytstack/STATE.md` without per-project scraping. + +**Exit criteria:** +- With a `progress` block in the `STATE.md` frontmatter, `bin/ytstack-status` prints label, percentage and done/total; with `waiting_on` set it also prints that item, truncated. +- Without the block it prints the active slice's task count taken from the checkboxes in `M###-S##-PLAN.md`. +- Outside a ytstack project it prints nothing and exits non-zero. +- In a linked worktree the plan progress comes from the default branch and the task pointer from the worktree's own `STATE.md`. +- A malformed block prints nothing; `yt_state_lint` reports it (integers, `done <= total`, short label). +- `summarize-task` and `quick-task` keep the block current when they advance STATE. +- `docs/statusline.md` documents the block, the two-line integration and a stable script location that survives plugin updates; README and CHANGELOG point at it. +- `bash tests/run-all.sh` passes and `bin/ytstack-check` reports no new failures. + +## Slices + +Slice detail lives in per-slice `M021-S##-PLAN.md` files, created by `ytstack:slice-milestone`. + +- [ ] S01 -- `bin/ytstack-status` prints the progress segment for any directory, from one shared parser that the STATE lint also uses. +- [ ] S02 -- The block stays current without hand upkeep and a user can wire the segment into their status line from the documentation. + +## Run order + +Slices execute sequentially. After each slice, `ytstack:reassess-roadmap` checks if the plan still fits reality. + +## How to update this file + +- Flip slice checkbox `[ ]` -> `[x]` when its tasks are all `summarize-task`-confirmed +- Update `completed_slices` count +- On milestone completion, flip `status: planned` -> `status: done` and update global ROADMAP.md diff --git a/.ytstack/M021-S01-PLAN.md b/.ytstack/M021-S01-PLAN.md new file mode 100644 index 0000000..c9fe509 --- /dev/null +++ b/.ytstack/M021-S01-PLAN.md @@ -0,0 +1,27 @@ +--- +milestone: M021 +slice: S01 +project: ytstack +created: 2026-11-11T10:25:52Z +status: planned +task_count: 2 +completed_tasks: 0 +--- + +# M021-S01 -- Slice Plan + +**Goal:** `bin/ytstack-status` prints the progress segment for any directory, from one shared parser that the STATE lint also uses. + +## Tasks + +- [ ] T01 -- `yt_progress_parse` in `hooks/lib/git-ctx.sh` (reads STATE content on stdin; sets state none / ok / bad plus label, done, total, waiting_on) and lint finding W7 in `yt_state_lint` for a malformed block; cases in `tests/git-ctx.test.sh`. +- [ ] T02 -- `bin/ytstack-status`: cwd from the status line JSON (`workspace.current_dir`, `cwd`, else `$PWD`), plan progress from the default branch outside it, task pointer from the local `STATE.md`, slice checkbox fallback, `waiting_on` truncation, `NO_COLOR` / `--no-color`, silent non-zero exit outside a project or on a malformed block; new `tests/ytstack-status.test.sh`. + +## Done when + +All tasks marked `[x]` and verified via `ytstack:summarize-task`. + +## Notes + +- Claude Code documents no stable path to plugin files for a `statusLine` command: `bin/` is on PATH for the Bash tool only, and `CLAUDE_PLUGIN_ROOT` changes on every update (statusline and plugin manifest reference, read 2026-11-11). +- Considered and dropped: SessionStart maintaining a `~/.ytstack/bin/ytstack-status` link. The hook is read-only by contract and every suite that fires it would need an isolated HOME. The integration snippet in `docs/statusline.md` (S02-T02) resolves the newest installed version itself instead. diff --git a/.ytstack/M021-S01-T01-PLAN.md b/.ytstack/M021-S01-T01-PLAN.md new file mode 100644 index 0000000..31ff2a4 --- /dev/null +++ b/.ytstack/M021-S01-T01-PLAN.md @@ -0,0 +1,43 @@ +--- +milestone: M021 +slice: S01 +task: T01 +project: ytstack +created: 2026-11-11T10:40:00Z +status: planned +--- + +# M021-S01-T01 -- Task Plan + +**Summary:** `yt_progress_parse` in `hooks/lib/git-ctx.sh` and lint finding W7 in `yt_state_lint` for a malformed `progress` block. + +## Files + +- Modify: `hooks/lib/git-ctx.sh` (new function before `yt_state_lint`; W7 inside it; header comment lists W7) +- Modify: `tests/git-ctx.test.sh` (new section after the STATE lint cases) + +## What this does + +`yt_progress_parse` reads STATE.md content on stdin, so the caller can feed a file or `git show` output. It looks at the frontmatter only (up to the closing `---`, or the first `#` heading when the file has no delimiters) for a `progress:` key followed by indented `label`, `done`, `total` and optional `waiting_on`. Values lose surrounding quotes and a trailing ` # comment`; comment lines and unknown keys inside the block are skipped. + +Result variables: + +- `YT_PROGRESS`: `none` (no block), `ok`, or `bad` +- `YT_PROGRESS_LABEL`, `YT_PROGRESS_DONE`, `YT_PROGRESS_TOTAL`, `YT_PROGRESS_WAITING`: set only when `ok` +- `YT_PROGRESS_ERRORS`: one line per problem when `bad` + +Rules for `ok`: `label` present and at most 16 characters; `done` and `total` are plain digits (at most 9); `total >= 1`; `done <= total`. `progress:` with an inline value is `bad`. + +`yt_state_lint` calls the parser on `STATE.md` and adds one warning per error line (W7). No block means no warning. + +## Verification + +```bash +bash tests/git-ctx.test.sh +``` + +Success: command exits 0 and shows expected output. Failure: re-read plan, check if spec drifted. + +## Done signal + +Run `/ytstack:summarize-task` after execution to mark complete and advance STATE.md. diff --git a/.ytstack/M021-S02-PLAN.md b/.ytstack/M021-S02-PLAN.md new file mode 100644 index 0000000..6ff6a73 --- /dev/null +++ b/.ytstack/M021-S02-PLAN.md @@ -0,0 +1,27 @@ +--- +milestone: M021 +slice: S02 +project: ytstack +created: 2026-11-11T10:25:52Z +status: planned +task_count: 3 +completed_tasks: 0 +--- + +# M021-S02 -- Slice Plan + +**Goal:** The block stays current without hand upkeep and a user can wire the segment into their status line from the documentation. + +## Tasks + +- [ ] T01 -- `summarize-task` and `quick-task` update the `progress` block when they advance STATE and re-run the lint; skill versions bumped; `bin/ytstack-check` clean. +- [ ] T02 -- `docs/statusline.md` (block reference, output forms, two-line integration for Claude Code, other hosts, colour switch); pointers in README, `docs/concept.md` and `docs/codex.md`; CHANGELOG `[Unreleased]` entry. +- [ ] T03 -- Verification and close: `bash tests/run-all.sh`, `bash tests/run-all.sh --linux`, `bin/ytstack-check`, live run against this repository and a linked worktree; M021 roadmap, global ROADMAP, DECISIONS and STATE closed. + +## Done when + +All tasks marked `[x]` and verified via `ytstack:summarize-task`. + +## Notes + +- Push, release tag and issue closing are operator actions (standing instruction). diff --git a/.ytstack/STATE.md b/.ytstack/STATE.md index d50166b..f734128 100644 --- a/.ytstack/STATE.md +++ b/.ytstack/STATE.md @@ -1,15 +1,17 @@ --- project: ytstack slug: ytstack -last_updated: 2026-11-07T21:30:00Z -current_milestone: none -active_slice: none -active_task: none +last_updated: 2026-11-11T10:40:00Z +current_milestone: M021 +active_slice: S01 +active_task: T01 --- # State -**Status (2026-10-13, PR #23 correction):** Removed the generated Codex skill/hook copies and build tooling. Both hosts load the original 21 skills from skills/ and reuse the lifecycle scripts in hooks/. Codex-specific runtime instructions live only in codex/adapter.md, supplied at SessionStart or read explicitly without trusted hooks. The helper provides the existing script environment and resolves repository-root paths, including starts in subdirectories. All 13 integration tests pass, including both hosts' six vendor imports, lifecycle state/journal behavior, and the single-source package check. Fresh Codex discovery loads the 21 original skill files and three hooks without errors or warnings. The contract checker reports the same 109 baseline warnings, no failures. The interactive desktop walkthrough remains a manual follow-up. This supersedes the earlier generated-package implementation in this PR. +**Status:** M021 / S01 / T01 planned -- ready to execute. + +**Previous status (2026-10-13, PR #23 correction):** Removed the generated Codex skill/hook copies and build tooling. Both hosts load the original 21 skills from skills/ and reuse the lifecycle scripts in hooks/. Codex-specific runtime instructions live only in codex/adapter.md, supplied at SessionStart or read explicitly without trusted hooks. The helper provides the existing script environment and resolves repository-root paths, including starts in subdirectories. All 13 integration tests pass, including both hosts' six vendor imports, lifecycle state/journal behavior, and the single-source package check. Fresh Codex discovery loads the 21 original skill files and three hooks without errors or warnings. The contract checker reports the same 109 baseline warnings, no failures. The interactive desktop walkthrough remains a manual follow-up. This supersedes the earlier generated-package implementation in this PR. **Status (2026-05-31, vendor drift audit + superpowers pull):** Audited both vendored subtrees for upstream breaking changes against the ytstack wrapper contract (wrappers inline the six wrapped SKILL.md verbatim via `` ```!cat ``; breaking surface = path-rename = silent empty `cat`, or procedure rewrite). **superpowers PULLED** `6efe32c -> 6fd4507`: all three wrapped SKILL.md (TDD, systematic-debugging, verification-before-completion) byte-identical upstream, zero ytstack-behavior change; only drumherum moved (README, tests, using-git-worktrees, finishing-a-development-branch). Merge `ca6675e` / squash `ad14900`; `bin/ytstack-check` = 0 fails (exit 2 = warnings-only). **NOTE: ca6675e is already on origin/main (pushed)** -- the push went out without an explicit push in-conversation (auto-mechanism or the still-present `thirsty-bhabha-a03490` worktree); flagged to the human. **gstack NOT pulled**, held at `aeea57f` (upstream `3bef43b` is a v1 restructure, ~1000 changed lines/file): methodology anchors all survive (no hard break), but a net-new `gbrain:` frontmatter `context_queries` block + `GSTACK_PLAN_MODE` is surface NOT covered by DECISIONS 2026-04-25 "Vendored-preamble drift accepted" (that decision only covers `|| true` shell, not frontmatter) -- revisit that decision before pulling gstack. KNOWLEDGE gotchas added (drift-audit method + gbrain). Hold-gstack is deliberately NOT yet a locked DECISIONS entry (left as a STATE/KNOWLEDGE fact pending the gbrain-strategy call). No version bump for the vendor refresh -- decided 2026-05-31, no ytstack-behavior change so it stays 0.1.6. @@ -83,7 +85,9 @@ M009 Docs & Community [####] 4/4 DONE ## Next action -**Status:** **M019 DONE -- delivered as 0.4.2 (0.4.0 -> 0.4.1 Codex-support merge of PR #23 -> 0.4.2 Linux `stat` fix), pushed, CI green on `f2a439c`, issues #18/#24/#25/#27 closed, installed plugin on 0.4.2, gstack v1.96.0.0 installed and live-validated (`tests/gstack-live.test.sh` 39/39).** The public catalog (`Yesterday-AI/skills`) pins no version, so it tracks `plugin.json`. No milestone open; M020 candidates are listed in ROADMAP. 0.4.0 bundles M010 (adopt-brownfield), M018 (ceremony dials, quick-task, reconcile, minimal-code, STATE lint, states, layouts) and M019 (`metadata:` frontmatter + trigger-only descriptions, `evals/` for `claude plugin eval` + CI, six lifecycle skills `code-review` / `finish-branch` / `grill` / `retro` / `ship` / `document-release`, worktree-subagent execution model with Agent Teams opt-in, Stop hook, no plugin dependencies). Clean clone at the release commit: 12 suites green, checker 0 fails, validate clean; evals 10/11 + re-run of the changed cases. **No milestone is open**: the modernisation programme from the 2026-11-07 research is complete; `ytstack:plan-milestone` for M020 when there is a next goal (candidates: live dogfooding of `spawn-milestone-team` 0.2.0 on a real milestone; PowerShell hook variants if a Windows consumer appears; per-worktree pointer if the orchestrator model needs it). **Operator actions:** `git push origin main --tags` (v0.2.0, v0.3.0, v0.4.0 are local), `gh issue close 24 25 27` (evidence posted), `/plugin update` to 0.4.0, gstack install one-liner (QUICKSTART) for the six gstack-backed skills, catalog sync in `Yesterday-AI/skills` (version 0.4.0, README skill table), skills.sh listing refresh. +Execute M021-S01-T01 (test first), then `ytstack:summarize-task`. + +**Previous status:** **M019 DONE -- delivered as 0.4.2 (0.4.0 -> 0.4.1 Codex-support merge of PR #23 -> 0.4.2 Linux `stat` fix), pushed, CI green on `f2a439c`, issues #18/#24/#25/#27 closed, installed plugin on 0.4.2, gstack v1.96.0.0 installed and live-validated (`tests/gstack-live.test.sh` 39/39).** The public catalog (`Yesterday-AI/skills`) pins no version, so it tracks `plugin.json`. No milestone open; M020 candidates are listed in ROADMAP. 0.4.0 bundles M010 (adopt-brownfield), M018 (ceremony dials, quick-task, reconcile, minimal-code, STATE lint, states, layouts) and M019 (`metadata:` frontmatter + trigger-only descriptions, `evals/` for `claude plugin eval` + CI, six lifecycle skills `code-review` / `finish-branch` / `grill` / `retro` / `ship` / `document-release`, worktree-subagent execution model with Agent Teams opt-in, Stop hook, no plugin dependencies). Clean clone at the release commit: 12 suites green, checker 0 fails, validate clean; evals 10/11 + re-run of the changed cases. **No milestone is open**: the modernisation programme from the 2026-11-07 research is complete; `ytstack:plan-milestone` for M020 when there is a next goal (candidates: live dogfooding of `spawn-milestone-team` 0.2.0 on a real milestone; PowerShell hook variants if a Windows consumer appears; per-worktree pointer if the orchestrator model needs it). **Operator actions:** `git push origin main --tags` (v0.2.0, v0.3.0, v0.4.0 are local), `gh issue close 24 25 27` (evidence posted), `/plugin update` to 0.4.0, gstack install one-liner (QUICKSTART) for the six gstack-backed skills, catalog sync in `Yesterday-AI/skills` (version 0.4.0, README skill table), skills.sh listing refresh. **Previous:** M018 DONE and M010 DONE (both now in 0.4.0). From d29ab536b617e8330a3a9db0261726d4df48fb48 Mon Sep 17 00:00:00 2001 From: Alex Wegener Date: Wed, 11 Nov 2026 11:29:03 +0100 Subject: [PATCH 02/10] M021-S01-T01: add yt_progress_parse and STATE lint finding W7 for the progress block Co-Authored-By: Claude Fable 5.2 --- hooks/lib/git-ctx.sh | 77 +++++++++++++++++++++++++++++++++++++++++++ tests/git-ctx.test.sh | 26 +++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/hooks/lib/git-ctx.sh b/hooks/lib/git-ctx.sh index 7b2ed55..b70181a 100644 --- a/hooks/lib/git-ctx.sh +++ b/hooks/lib/git-ctx.sh @@ -385,6 +385,74 @@ yt_epoch() { printf '%s' "$e" } +# ---- progress block (M021, issue #31) --------------------------------------- +# yt_progress_parse: reads STATE.md content on stdin (a file or `git show` +# output) and parses the optional frontmatter block +# progress: +# label: V2 # required, at most 16 characters +# done: 138 # integer +# total: 180 # integer >= 1, done <= total +# waiting_on: "…" # optional +# Sets YT_PROGRESS (none | ok | bad); on ok YT_PROGRESS_LABEL / _DONE / _TOTAL +# / _WAITING; on bad YT_PROGRESS_ERRORS (one line per problem). Shared by +# bin/ytstack-status and yt_state_lint so both agree on what is malformed. +yt_progress_parse() { + local found="" inline="" label="" done="" total="" waiting="" e="" + YT_PROGRESS=none; YT_PROGRESS_LABEL=""; YT_PROGRESS_DONE=""; YT_PROGRESS_TOTAL="" + YT_PROGRESS_WAITING=""; YT_PROGRESS_ERRORS="" + { IFS= read -r found; IFS= read -r inline; IFS= read -r label; IFS= read -r done + IFS= read -r total; IFS= read -r waiting; } < 0 ? substr(v, 1, i - 1) : v + } + if (v ~ /^#/) return "" + sub(/[ \t]+#.*$/, "", v) + return v + } + NR == 1 && /^---[ \t\r]*$/ { delim = 1; next } + /^---[ \t\r]*$/ { exit } + !delim && /^#/ { exit } + inb && /^[ \t]*$/ { next } + inb && /^[ \t]+#/ { next } + inb && /^[ \t]+[A-Za-z_]+:/ { + line = $0; sub(/^[ \t]+/, "", line) + k = line; sub(/:.*/, "", k) + val[k] = clean(substr(line, length(k) + 2)) + next + } + inb { inb = 0 } + !found && /^progress:/ { found = 1; inb = 1; v = $0; sub(/^progress:/, "", v); inline = clean(v) } + END { print found + 0; print inline; print val["label"]; print val["done"]; print val["total"]; print val["waiting_on"] } + ' 2>/dev/null) +EOF + [ "$found" = 1 ] || return 0 + _yt_perr() { e="${e:+$e +}progress: $1"; } + [ -z "$inline" ] || _yt_perr "must be a block with label, done and total, not the inline value '$inline'" + if [ -z "$label" ]; then _yt_perr "label is missing" + elif [ "${#label}" -gt 16 ]; then _yt_perr "label '$label' is longer than 16 characters"; fi + local k v nums=ok + for k in done total; do + eval "v=\$$k" + case "$v" in ''|*[!0-9]*|??????????*) _yt_perr "$k '$v' is not a non-negative integer"; nums=bad ;; esac + done + if [ "$nums" = ok ]; then + done=$((10#$done)); total=$((10#$total)) + if [ "$total" -lt 1 ]; then _yt_perr "total must be at least 1" + elif [ "$done" -gt "$total" ]; then _yt_perr "done $done is greater than total $total"; fi + fi + unset -f _yt_perr + if [ -n "$e" ]; then YT_PROGRESS=bad; YT_PROGRESS_ERRORS="$e"; return 0; fi + YT_PROGRESS=ok; YT_PROGRESS_LABEL="$label"; YT_PROGRESS_DONE="$done" + YT_PROGRESS_TOTAL="$total"; YT_PROGRESS_WAITING="$waiting" + return 0 +} + # ---- STATE pointer lint (M018) ---------------------------------------------- # yt_state_lint : sets YT_STATE_WARNINGS (one line per finding, "" when # clean) and YT_STATE_WARN_COUNT. Read-only. Findings: @@ -392,6 +460,7 @@ yt_epoch() { # W3 active task already summarized W4 slice/task set while milestone none # W5 last_updated older than the newest commit touching # W6 current milestone marked done while a slice is still active +# W7 malformed `progress` block (one finding per problem) yt_state_lint() { local ytd="$1" sf="$1/STATE.md" m s t lu w="" n=0 _yt_warn() { w="${w:+$w @@ -445,6 +514,14 @@ yt_state_lint() { _yt_warn "$m-ROADMAP.md has status: done but STATE still points at $m / $s" fi fi + # W7: malformed progress block + yt_progress_parse < "$sf" + if [ "$YT_PROGRESS" = bad ]; then + local pl + while IFS= read -r pl; do _yt_warn "$pl"; done < "$SL/.ytstack/M003-ROADMAP.md" case "$(lint "$SL/.ytstack")" in *"status: done"*) ok "lint: done milestone still current with open slice";; *) bad "lint: done but current" "$(lint "$SL/.ytstack")";; esac +# ---- 13b. progress block (M021, issue #31) ------------------------------------ +echo "# progress block" +# prog : "state|label|done|total|waiting|errors" +prog() { printf -- '---\nproject: fx\ncurrent_milestone: M003\n%b---\n# State\n\nprogress:\n label: BODY\n done: 9\n total: 9\n' "$1" \ + | ( . "$LIB"; yt_progress_parse; printf '%s|%s|%s|%s|%s|%s' "$YT_PROGRESS" "$YT_PROGRESS_LABEL" "$YT_PROGRESS_DONE" "$YT_PROGRESS_TOTAL" "$YT_PROGRESS_WAITING" "$(printf '%s' "$YT_PROGRESS_ERRORS" | tr '\n' ';')" ); } +eq "progress: no block -> none (body ignored)" "none|||||" "$(prog '')" +eq "progress: full block" "ok|V2|138|180|review of PR 12|" "$(prog 'progress:\n label: V2 # short\n done: 138\n total: 180\n waiting_on: "review of PR 12"\nactive_slice: S01\n')" +eq "progress: without waiting_on" "ok|V2|0|7||" "$(prog 'progress:\n label: V2\n done: 0\n total: 7\n')" +eq "progress: comment lines and unknown keys" "ok|V2|1|7||" "$(prog 'progress:\n # counts: tasks\n label: V2\n unit: tasks\n done: 1\n total: 7\n')" +eq "progress: single quotes, hash inside quotes" "ok|V2|1|7|issue #31|" "$(prog "progress:\n label: 'V2'\n done: 1\n total: 7\n waiting_on: 'issue #31'\n")" +eq "progress: key after the block is not part" "ok|V2|1|7||" "$(prog 'progress:\n label: V2\n done: 1\n total: 7\nlabel: other\ndone: 99\n')" +case "$(prog 'progress:\n label: V2\n done: 8\n total: 7\n')" in bad\|\|\|\|\|*"done 8 is greater than total 7"*) ok "progress: done > total -> bad, no values";; *) bad "progress: done > total" "$(prog 'progress:\n label: V2\n done: 8\n total: 7\n')";; esac +case "$(prog 'progress:\n label: V2\n done: 1.5\n total: many\n')" in bad\|*done*integer*total*integer*) ok "progress: non-integers -> two errors";; *) bad "progress: non-integers" "$(prog 'progress:\n label: V2\n done: 1.5\n total: many\n')";; esac +case "$(prog 'progress:\n label: V2\n done: 0\n total: 0\n')" in bad\|*"total"*) ok "progress: total 0 -> bad";; *) bad "progress: total 0";; esac +case "$(prog 'progress:\n done: 1\n total: 2\n')" in bad\|*label*) ok "progress: missing label -> bad";; *) bad "progress: missing label";; esac +case "$(prog 'progress:\n label: this label is far too long\n done: 1\n total: 2\n')" in bad\|*label*16*) ok "progress: long label -> bad";; *) bad "progress: long label";; esac +case "$(prog 'progress: 5\n')" in bad\|*block*) ok "progress: inline value -> bad";; *) bad "progress: inline value" "$(prog 'progress: 5\n')";; esac +eq "progress: no frontmatter delimiters" "ok|V2|1|2||" "$(printf 'project: fx\nprogress:\n label: V2\n done: 1\n total: 2\n\n# State\n' | ( . "$LIB"; yt_progress_parse; printf '%s|%s|%s|%s|%s|%s' "$YT_PROGRESS" "$YT_PROGRESS_LABEL" "$YT_PROGRESS_DONE" "$YT_PROGRESS_TOTAL" "$YT_PROGRESS_WAITING" "$YT_PROGRESS_ERRORS" ))" +# lint W7 +printf -- '---\nmilestone: M003\nstatus: in-progress\n---\n' > "$SL/.ytstack/M003-ROADMAP.md" +write_state_prog() { printf -- '---\nproject: fx\nlast_updated: %s\ncurrent_milestone: M003\nactive_slice: S01\nactive_task: T02\nprogress:\n%b---\n# State\n' "$NOW" "$1" > "$SL/.ytstack/STATE.md"; } +write_state_prog ' label: V2\n done: 3\n total: 7\n' +eq "lint: valid progress block -> 0 warnings" "0|" "$(lint "$SL/.ytstack")" +write_state_prog ' label: V2\n done: 9\n total: 7\n' +case "$(lint "$SL/.ytstack")" in 1\|*"progress: done 9 is greater than total 7"*) ok "lint: malformed progress block reported (W7)";; *) bad "lint: W7" "$(lint "$SL/.ytstack")";; esac + # ---- 14. brownfield signals (M010, issue #18) -------------------------------- echo "# brownfield signals" bf() { ( . "$LIB"; yt_brownfield_signals "$1" >/dev/null; printf '%s' "$YT_BROWNFIELD_SIGNALS" ); } From 967d90d8921e34ef9f53bbf8c7db617514385df5 Mon Sep 17 00:00:00 2001 From: Alex Wegener Date: Wed, 11 Nov 2026 11:31:28 +0100 Subject: [PATCH 03/10] M021-S01-T02: add bin/ytstack-status, the status line segment for a ytstack project Co-Authored-By: Claude Fable 5.2 --- bin/ytstack-status | 99 ++++++++++++++++++++++ tests/ytstack-status.test.sh | 156 +++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100755 bin/ytstack-status create mode 100644 tests/ytstack-status.test.sh diff --git a/bin/ytstack-status b/bin/ytstack-status new file mode 100755 index 0000000..2290224 --- /dev/null +++ b/bin/ytstack-status @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# ytstack status line segment (M021, issue #31). +# +# Prints one compact segment for the ytstack project that owns the current +# directory, for a status line script to append: +# +# V2 76% 138/180 · M021/S01/T02 · waiting: review of PR 12 (progress block) +# M021/S01/T02 3/7 (no block: slice checkboxes) +# +# Input: the status line JSON on stdin (only `cwd` is read); without it $PWD. +# Output: the segment and exit 0, or nothing and exit 1 (no .ytstack, nothing +# to show, malformed progress block; the STATE lint reports the last one). +# Colour is on by default; NO_COLOR or --no-color turns it off. +# +# Plan progress is repository-wide: on any branch other than the default one +# (a linked worktree carries a stale copy) STATE.md is read from the newer of +# origin/ and . The task pointer is always the local one. +# Local refs only: no network, no gh. See docs/statusline.md. + +set -uo pipefail + +. "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/hooks/lib/git-ctx.sh" + +COLOR=yes +[ -z "${NO_COLOR:-}" ] || COLOR=no +[ "${1:-}" != "--no-color" ] || COLOR=no + +yt_read_stdin +yt_git_ctx "${YT_CWD:-$PWD}" +yt_resolve_yt_dir >/dev/null +STATE_FILE="${YT_DIR:+$YT_DIR/STATE.md}" +[ -n "$STATE_FILE" ] && [ -f "$STATE_FILE" ] || exit 1 + +# ---- task pointer (local) ---------------------------------------------------- +M=$(sed -n 's/^current_milestone: *//p' "$STATE_FILE" | head -1) +S=$(sed -n 's/^active_slice: *//p' "$STATE_FILE" | head -1) +T=$(sed -n 's/^active_task: *//p' "$STATE_FILE" | head -1) +POINTER="" +case "$M" in M[0-9][0-9][0-9]) + POINTER="$M" + case "$S" in S[0-9][0-9]) + POINTER="$POINTER/$S" + case "$T" in T[0-9][0-9]) POINTER="$POINTER/$T" ;; esac ;; + esac ;; +esac + +# ---- plan progress (default branch when we are not on it) -------------------- +PLAN_STATE="" +if [ "$YT_IS_GIT" = yes ] && [ "$YT_SCOPE" = project ]; then + DEF=$(git -C "$YT_TOPLEVEL" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null) + DEF=${DEF#origin/} + if [ -z "$DEF" ]; then + for c in main master; do + if git -C "$YT_TOPLEVEL" show-ref --verify --quiet "refs/heads/$c"; then DEF=$c; break; fi + done + fi + if [ -n "$DEF" ] && [ "$(git -C "$YT_TOPLEVEL" symbolic-ref --quiet --short HEAD 2>/dev/null)" != "$DEF" ]; then + REF=$(git -C "$YT_TOPLEVEL" for-each-ref --sort=-committerdate --count=1 --format='%(refname)' \ + "refs/heads/$DEF" "refs/remotes/origin/$DEF" 2>/dev/null) + [ -z "$REF" ] || PLAN_STATE=$(git -C "$YT_TOPLEVEL" show "$REF:.ytstack/STATE.md" 2>/dev/null) + fi +fi +if [ -n "$PLAN_STATE" ]; then + yt_progress_parse < [args/env...]: "|" with the dir passed as JSON cwd +seg() { + local d="$1" out ec; shift + out=$(cd "$ELSEWHERE" && printf '{"cwd":"%s","workspace":{"current_dir":"%s"},"model":{"display_name":"x"}}' "$d" "$d" | env -u NO_COLOR "$@" bash "$BIN" --no-color 2>/dev/null); ec=$? + printf '%s|%s' "$ec" "$out" +} + +# state [progress block lines] +state() { + printf -- '---\nproject: fx\nlast_updated: 2026-01-01T00:00:00Z\ncurrent_milestone: %s\nactive_slice: %s\nactive_task: %s\n%b---\n# State\n' "$2" "$3" "$4" "${5:-}" > "$1/.ytstack/STATE.md" +} +new_repo() { + local d="$TMP/$1" + mkdir -p "$d/.ytstack" + ( cd "$d" && git init -q -b main && git config user.email t@t.t && git config user.name t && git config commit.gpgsign false ) + printf -- '---\nmilestone: M003\nslice: S01\n---\n# plan\n\n- [x] T01 -- one\n- [X] T02 -- two\n- [ ] T03 -- three\n- [ ] T04 -- four\n- [ ] T05 -- five\n\n## Notes\n\n- [ ] not a task\n' > "$d/.ytstack/M003-S01-PLAN.md" + echo "$d" +} +commit() { ( cd "$1" && git add -A && git commit -qm "$2" ) >/dev/null 2>&1; } +BLOCK='progress:\n label: V2\n done: 138\n total: 180\n' + +# ---- 1. outside a ytstack project ------------------------------------------- +echo "# outside a project" +PLAIN="$TMP/plain"; mkdir -p "$PLAIN" +eq "plain dir: no output, exit 1" "1|" "$(seg "$PLAIN")" +eq "missing dir: no output, exit 1" "1|" "$(seg "$TMP/does-not-exist")" + +# ---- 2. fallback: slice checkbox count --------------------------------------- +echo "# no progress block" +R=$(new_repo main) +state "$R" M003 S01 T03 +commit "$R" seed +eq "pointer + slice count" "0|M003/S01/T03 2/5" "$(seg "$R")" +mkdir -p "$R/src/deep" +eq "subdirectory resolves to the project" "0|M003/S01/T03 2/5" "$(seg "$R/src/deep")" +state "$R" M003 S01 none +eq "no active task: slice count only" "0|M003/S01 2/5" "$(seg "$R")" +state "$R" M003 S02 T01 +eq "slice plan missing: pointer alone" "0|M003/S02/T01" "$(seg "$R")" +state "$R" M003 none none +eq "milestone only" "0|M003" "$(seg "$R")" +state "$R" none none none +eq "nothing to show: no output, exit 1" "1|" "$(seg "$R")" +mkdir -p "$R/.ytstack/milestones/M003"; git -C "$R" mv .ytstack/M003-S01-PLAN.md .ytstack/milestones/M003/ 2>/dev/null +state "$R" M003 S01 T03 +eq "slice plan under milestones/M003/" "0|M003/S01/T03 2/5" "$(seg "$R")" + +# ---- 3. progress block ------------------------------------------------------- +echo "# progress block" +state "$R" M003 S01 T03 "$BLOCK" +eq "label, percentage, done/total, pointer" "0|V2 76% 138/180 · M003/S01/T03" "$(seg "$R")" +state "$R" none none none "$BLOCK" +eq "block without pointer" "0|V2 76% 138/180" "$(seg "$R")" +state "$R" M003 S01 T03 "${BLOCK} waiting_on: \"review of PR 12\"\n" +eq "waiting_on shown" "0|V2 76% 138/180 · M003/S01/T03 · waiting: review of PR 12" "$(seg "$R")" +state "$R" M003 S01 T03 "${BLOCK} waiting_on: the operator has to approve the budget for phase three\n" +eq "waiting_on truncated to 32 characters" "0|V2 76% 138/180 · M003/S01/T03 · waiting: the operator has to approve the …" "$(seg "$R")" +state "$R" M003 S01 T03 'progress:\n label: V2\n done: 7\n total: 7\n' +eq "complete: 100%" "0|V2 100% 7/7 · M003/S01/T03" "$(seg "$R")" +state "$R" M003 S01 T03 'progress:\n label: V2\n done: 179\n total: 180\n' +eq "percentage rounds down (never 100% early)" "0|V2 99% 179/180 · M003/S01/T03" "$(seg "$R")" +state "$R" M003 S01 T03 'progress:\n label: V2\n done: 9\n total: 7\n' +eq "malformed block: no output, exit 1" "1|" "$(seg "$R")" + +# ---- 4. input handling ------------------------------------------------------- +echo "# input" +state "$R" M003 S01 T03 "$BLOCK" +WANT="V2 76% 138/180 · M003/S01/T03" +eq "no stdin JSON: falls back to PWD" "$WANT" "$(cd "$R" && bash "$BIN" --no-color /dev/null 2>&1 +state "$WT" M003 S01 T04 'progress:\n label: V2\n done: 3\n total: 10\n' +eq "linked worktree: plan from main, pointer local" "0|V2 60% 6/10 · M003/S01/T04" "$(seg "$WT")" +state "$WT" M003 S01 T04 'progress:\n label: V2\n done: 99\n total: 10\n' +eq "linked worktree: local block is not read" "0|V2 60% 6/10 · M003/S01/T04" "$(seg "$WT")" +state "$D" M003 S01 T02 'progress:\n label: V2\n done: 99\n total: 10\n'; commit "$D" "broken on main" +state "$WT" M003 S01 T04 'progress:\n label: V2\n done: 3\n total: 10\n' +eq "linked worktree: malformed block on main prints nothing" "1|" "$(seg "$WT")" +state "$D" M003 S01 T02 'progress:\n label: V2\n done: 7\n total: 10\n'; commit "$D" "main state 7" +( cd "$WT" && git checkout -q --detach ) 2>/dev/null +eq "detached HEAD: plan from main" "0|V2 70% 7/10 · M003/S01/T04" "$(seg "$WT")" + +# clone: origin/HEAD names the default branch; origin/main is newer than local main +C="$TMP/clone"; git clone -q "$D" "$C" 2>/dev/null +( cd "$C" && git config user.email t@t.t && git config user.name t && git config commit.gpgsign false && git checkout -q -b topic ) +state "$D" M003 S01 T02 'progress:\n label: V2\n done: 8\n total: 10\n' +( cd "$D" && git add -A && GIT_COMMITTER_DATE="2099-01-01T00:00:00Z" git commit -qm "main state 8" ) >/dev/null 2>&1 +git -C "$C" fetch -q origin 2>/dev/null +state "$C" M003 S01 T05 '' +eq "clone on a topic branch: newer origin/main wins" "0|V2 80% 8/10 · M003/S01/T05" "$(seg "$C")" + +# no default branch at all: working tree +N="$TMP/nodef"; mkdir -p "$N/.ytstack" +( cd "$N" && git init -q -b trunk && git config user.email t@t.t && git config user.name t && git config commit.gpgsign false ) +state "$N" M003 none none "$BLOCK"; commit "$N" seed +eq "no main/master/origin: working tree" "0|V2 76% 138/180 · M003" "$(seg "$N")" + +# no git at all +G="$TMP/nogit"; mkdir -p "$G/.ytstack"; state "$G" M003 none none "$BLOCK" +eq "not a git repository: working tree" "0|V2 76% 138/180 · M003" "$(seg "$G")" + +echo +echo "$PASS passed, $FAIL failed" +[ "$FAIL" -eq 0 ] From d6beab2b4ba5d2d1b210f236b630091245728c7e Mon Sep 17 00:00:00 2001 From: Alex Wegener Date: Wed, 11 Nov 2026 11:33:03 +0100 Subject: [PATCH 04/10] chore: close M021-S01 (summaries, slice plan, roadmap, state), plan S02-T01 Co-Authored-By: Claude Fable 5.2 --- .ytstack/M021-ROADMAP.md | 4 +-- .ytstack/M021-S01-PLAN.md | 8 +++--- .ytstack/M021-S01-T01-SUMMARY.md | 32 +++++++++++++++++++++++ .ytstack/M021-S01-T02-PLAN.md | 45 ++++++++++++++++++++++++++++++++ .ytstack/M021-S01-T02-SUMMARY.md | 33 +++++++++++++++++++++++ .ytstack/M021-S02-T01-PLAN.md | 35 +++++++++++++++++++++++++ .ytstack/STATE.md | 8 +++--- 7 files changed, 155 insertions(+), 10 deletions(-) create mode 100644 .ytstack/M021-S01-T01-SUMMARY.md create mode 100644 .ytstack/M021-S01-T02-PLAN.md create mode 100644 .ytstack/M021-S01-T02-SUMMARY.md create mode 100644 .ytstack/M021-S02-T01-PLAN.md diff --git a/.ytstack/M021-ROADMAP.md b/.ytstack/M021-ROADMAP.md index 706d79b..e2569d9 100644 --- a/.ytstack/M021-ROADMAP.md +++ b/.ytstack/M021-ROADMAP.md @@ -5,7 +5,7 @@ size: M created: 2026-11-11T10:25:52Z status: in-progress total_slices: 2 -completed_slices: 0 +completed_slices: 1 --- # M021 Roadmap @@ -26,7 +26,7 @@ completed_slices: 0 Slice detail lives in per-slice `M021-S##-PLAN.md` files, created by `ytstack:slice-milestone`. -- [ ] S01 -- `bin/ytstack-status` prints the progress segment for any directory, from one shared parser that the STATE lint also uses. +- [x] S01 -- `bin/ytstack-status` prints the progress segment for any directory, from one shared parser that the STATE lint also uses. - [ ] S02 -- The block stays current without hand upkeep and a user can wire the segment into their status line from the documentation. ## Run order diff --git a/.ytstack/M021-S01-PLAN.md b/.ytstack/M021-S01-PLAN.md index c9fe509..7470d92 100644 --- a/.ytstack/M021-S01-PLAN.md +++ b/.ytstack/M021-S01-PLAN.md @@ -3,9 +3,9 @@ milestone: M021 slice: S01 project: ytstack created: 2026-11-11T10:25:52Z -status: planned +status: done task_count: 2 -completed_tasks: 0 +completed_tasks: 2 --- # M021-S01 -- Slice Plan @@ -14,8 +14,8 @@ completed_tasks: 0 ## Tasks -- [ ] T01 -- `yt_progress_parse` in `hooks/lib/git-ctx.sh` (reads STATE content on stdin; sets state none / ok / bad plus label, done, total, waiting_on) and lint finding W7 in `yt_state_lint` for a malformed block; cases in `tests/git-ctx.test.sh`. -- [ ] T02 -- `bin/ytstack-status`: cwd from the status line JSON (`workspace.current_dir`, `cwd`, else `$PWD`), plan progress from the default branch outside it, task pointer from the local `STATE.md`, slice checkbox fallback, `waiting_on` truncation, `NO_COLOR` / `--no-color`, silent non-zero exit outside a project or on a malformed block; new `tests/ytstack-status.test.sh`. +- [x] T01 -- `yt_progress_parse` in `hooks/lib/git-ctx.sh` (reads STATE content on stdin; sets state none / ok / bad plus label, done, total, waiting_on) and lint finding W7 in `yt_state_lint` for a malformed block; cases in `tests/git-ctx.test.sh`. +- [x] T02 -- `bin/ytstack-status`: cwd from the status line JSON (`workspace.current_dir`, `cwd`, else `$PWD`), plan progress from the default branch outside it, task pointer from the local `STATE.md`, slice checkbox fallback, `waiting_on` truncation, `NO_COLOR` / `--no-color`, silent non-zero exit outside a project or on a malformed block; new `tests/ytstack-status.test.sh`. ## Done when diff --git a/.ytstack/M021-S01-T01-SUMMARY.md b/.ytstack/M021-S01-T01-SUMMARY.md new file mode 100644 index 0000000..c444a3c --- /dev/null +++ b/.ytstack/M021-S01-T01-SUMMARY.md @@ -0,0 +1,32 @@ +--- +milestone: M021 +slice: S01 +task: T01 +project: ytstack +closed: 2026-11-11T10:55:00Z +verification: passed +--- + +# M021-S01-T01 -- Summary + +## Commits + +- `d29ab53` M021-S01-T01: add yt_progress_parse and STATE lint finding W7 for the progress block + +## Outcome + +`yt_progress_parse` reads STATE.md content on stdin and reports `none`, `ok` or `bad` for the optional frontmatter `progress` block, with label, done, total and waiting_on on `ok` and one error line per problem on `bad`. `yt_state_lint` runs it on `STATE.md` and turns each error line into a warning (W7). A project without the block gets no warning. + +## Deviations from plan + +none + +## Follow-ups + +none + +## Verification + +Command: `bash tests/git-ctx.test.sh` -- passed (150 passed, 0 failed; same result under `/bin/bash` 3.2). + +Minimal-code review: rung 6. One awk pass instead of a YAML dependency. Left out on purpose: several tracks, a `done`/`open` pair, multi-line YAML scalars. diff --git a/.ytstack/M021-S01-T02-PLAN.md b/.ytstack/M021-S01-T02-PLAN.md new file mode 100644 index 0000000..06a413b --- /dev/null +++ b/.ytstack/M021-S01-T02-PLAN.md @@ -0,0 +1,45 @@ +--- +milestone: M021 +slice: S01 +task: T02 +project: ytstack +created: 2026-11-11T10:55:00Z +status: planned +--- + +# M021-S01-T02 -- Task Plan + +**Summary:** `bin/ytstack-status` prints one compact segment for the directory named in the status line JSON. + +## Files + +- Create: `bin/ytstack-status` (executable) +- Create: `tests/ytstack-status.test.sh` + +## What this does + +Input: status line JSON on stdin (only `cwd` is used, through `yt_read_stdin`); without usable input the script falls back to `$PWD`. Flag `--no-color`; a non-empty `NO_COLOR` does the same. + +Resolution: `yt_git_ctx` + `yt_resolve_yt_dir`. No `.ytstack` or no `STATE.md`: no output, exit 1. + +Task pointer: `current_milestone` / `active_slice` / `active_task` from the local `STATE.md`; parts that are `none`, `all` or malformed are left out. + +Plan progress: in a project-scope git checkout whose current branch is not the default branch, `STATE.md` is read with `git show` from the newer of `origin/` and the local ``; the default branch comes from `origin/HEAD`, else `main`, else `master`. Otherwise, or when that read yields nothing, the working tree file is used. Local refs only, no network. + +Output (one line, exit 0): + +- block `ok`: `V2 76% 138/180 · M021/S01/T02 · waiting: ` (pointer and waiting part only when present; the item is cut to 32 characters plus `…`) +- no block: `M021/S01/T02 3/7`, the count taken from the task checkboxes of the active slice plan (flat path first, then `yt_find_artifacts`); pointer alone when no slice plan is found +- block `bad`, or neither block nor pointer: no output, exit 1 + +## Verification + +```bash +bash tests/ytstack-status.test.sh +``` + +Success: command exits 0 and shows expected output. Failure: re-read plan, check if spec drifted. + +## Done signal + +Run `/ytstack:summarize-task` after execution to mark complete and advance STATE.md. diff --git a/.ytstack/M021-S01-T02-SUMMARY.md b/.ytstack/M021-S01-T02-SUMMARY.md new file mode 100644 index 0000000..d19e063 --- /dev/null +++ b/.ytstack/M021-S01-T02-SUMMARY.md @@ -0,0 +1,33 @@ +--- +milestone: M021 +slice: S01 +task: T02 +project: ytstack +closed: 2026-11-11T11:15:00Z +verification: passed +--- + +# M021-S01-T02 -- Summary + +## Commits + +- `967d90d` M021-S01-T02: add bin/ytstack-status, the status line segment for a ytstack project + +## Outcome + +`bin/ytstack-status` reads `cwd` from the status line JSON (else `$PWD`), resolves the ytstack project and prints one line: with a `progress` block `V2 76% 138/180 · M021/S01/T02 · waiting: …`, without it the pointer plus the active slice's checkbox count (`M021/S01/T02 1/2`). On a branch other than the default one the block is read with `git show` from the newer of `origin/` and ``; the pointer stays local. It prints nothing and exits 1 outside a project, for a malformed block, and when there is neither block nor pointer. `NO_COLOR` and `--no-color` switch the colour off. + +## Deviations from plan + +none + +## Follow-ups + +- `shortcut:` in `bin/ytstack-status`: `waiting_on` is cut by characters of the current locale; under `LC_ALL=C` a multi-byte character at the cut can be split. +- One run takes about 90 ms on this machine (ten runs 0.92 s), most of it the git calls in `yt_git_ctx`. Fine for a status line that is debounced at 300 ms; a cache is the lever if it ever matters. + +## Verification + +Command: `bash tests/ytstack-status.test.sh` -- passed (31 passed, 0 failed; same under `/bin/bash` 3.2). Live: this worktree printed `M021/S01/T02 1/2`, `/tmp` printed nothing with exit 1. + +Minimal-code review: rung 2 for input and resolution (`yt_read_stdin`, `yt_git_ctx`, `yt_resolve_yt_dir`, `yt_find_artifacts` reused), rung 6 for the rest. Left out on purpose: reading `workspace.current_dir` (Claude Code documents it as equal to `cwd`), symlink resolution of the script path, a cache. diff --git a/.ytstack/M021-S02-T01-PLAN.md b/.ytstack/M021-S02-T01-PLAN.md new file mode 100644 index 0000000..e70dda3 --- /dev/null +++ b/.ytstack/M021-S02-T01-PLAN.md @@ -0,0 +1,35 @@ +--- +milestone: M021 +slice: S02 +task: T01 +project: ytstack +created: 2026-11-11T11:15:00Z +status: planned +--- + +# M021-S02-T01 -- Task Plan + +**Summary:** `summarize-task` and `quick-task` update the `progress` block when they advance STATE. + +## Files + +- Modify: `skills/summarize-task/SKILL.md` (Step 10, checklist item 10, `metadata.version`) +- Modify: `skills/quick-task/SKILL.md` (Step 6 closing paragraph, `metadata.version`) + +## What this does + +`summarize-task` Step 10: when the frontmatter has a `progress:` block, the same edit adds 1 to `done` if the closed task is an item the block counts (tasks by default; a comment line inside the block names another ledger), raises `total` for a task that was added after the total was set, never writes `done` above `total`, and re-runs `yt_state_lint`. Swarm teammates skip it with the other frontmatter writes and note the pending increment under Follow-ups. No block: nothing happens and none is added. + +`quick-task`: same rule in one sentence, limited to a change that completes a counted item. + +## Verification + +```bash +bin/ytstack-check; bash tests/run-all.sh +``` + +Success: checker without failures (exit 0 or 2), all suites pass. Failure: re-read plan, check if spec drifted. + +## Done signal + +Run `/ytstack:summarize-task` after execution to mark complete and advance STATE.md. diff --git a/.ytstack/STATE.md b/.ytstack/STATE.md index f734128..4ae82ac 100644 --- a/.ytstack/STATE.md +++ b/.ytstack/STATE.md @@ -1,15 +1,15 @@ --- project: ytstack slug: ytstack -last_updated: 2026-11-11T10:40:00Z +last_updated: 2026-11-11T11:15:00Z current_milestone: M021 -active_slice: S01 +active_slice: S02 active_task: T01 --- # State -**Status:** M021 / S01 / T01 planned -- ready to execute. +**Status:** M021 / S01 done (2/2). Roadmap reassessed at the slice boundary: S02 stands as planned. M021 / S02 / T01 planned -- ready to execute. **Previous status (2026-10-13, PR #23 correction):** Removed the generated Codex skill/hook copies and build tooling. Both hosts load the original 21 skills from skills/ and reuse the lifecycle scripts in hooks/. Codex-specific runtime instructions live only in codex/adapter.md, supplied at SessionStart or read explicitly without trusted hooks. The helper provides the existing script environment and resolves repository-root paths, including starts in subdirectories. All 13 integration tests pass, including both hosts' six vendor imports, lifecycle state/journal behavior, and the single-source package check. Fresh Codex discovery loads the 21 original skill files and three hooks without errors or warnings. The contract checker reports the same 109 baseline warnings, no failures. The interactive desktop walkthrough remains a manual follow-up. This supersedes the earlier generated-package implementation in this PR. @@ -85,7 +85,7 @@ M009 Docs & Community [####] 4/4 DONE ## Next action -Execute M021-S01-T01 (test first), then `ytstack:summarize-task`. +Execute M021-S02-T01, then `ytstack:summarize-task`. **Previous status:** **M019 DONE -- delivered as 0.4.2 (0.4.0 -> 0.4.1 Codex-support merge of PR #23 -> 0.4.2 Linux `stat` fix), pushed, CI green on `f2a439c`, issues #18/#24/#25/#27 closed, installed plugin on 0.4.2, gstack v1.96.0.0 installed and live-validated (`tests/gstack-live.test.sh` 39/39).** The public catalog (`Yesterday-AI/skills`) pins no version, so it tracks `plugin.json`. No milestone open; M020 candidates are listed in ROADMAP. 0.4.0 bundles M010 (adopt-brownfield), M018 (ceremony dials, quick-task, reconcile, minimal-code, STATE lint, states, layouts) and M019 (`metadata:` frontmatter + trigger-only descriptions, `evals/` for `claude plugin eval` + CI, six lifecycle skills `code-review` / `finish-branch` / `grill` / `retro` / `ship` / `document-release`, worktree-subagent execution model with Agent Teams opt-in, Stop hook, no plugin dependencies). Clean clone at the release commit: 12 suites green, checker 0 fails, validate clean; evals 10/11 + re-run of the changed cases. **No milestone is open**: the modernisation programme from the 2026-11-07 research is complete; `ytstack:plan-milestone` for M020 when there is a next goal (candidates: live dogfooding of `spawn-milestone-team` 0.2.0 on a real milestone; PowerShell hook variants if a Windows consumer appears; per-worktree pointer if the orchestrator model needs it). **Operator actions:** `git push origin main --tags` (v0.2.0, v0.3.0, v0.4.0 are local), `gh issue close 24 25 27` (evidence posted), `/plugin update` to 0.4.0, gstack install one-liner (QUICKSTART) for the six gstack-backed skills, catalog sync in `Yesterday-AI/skills` (version 0.4.0, README skill table), skills.sh listing refresh. From b4140724eac5cc7ae7bebd736035e626dfc13895 Mon Sep 17 00:00:00 2001 From: Alex Wegener Date: Wed, 11 Nov 2026 11:33:03 +0100 Subject: [PATCH 05/10] M021-S02-T01: summarize-task and quick-task keep the STATE progress block current Co-Authored-By: Claude Fable 5.2 --- skills/quick-task/SKILL.md | 4 ++-- skills/summarize-task/SKILL.md | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/skills/quick-task/SKILL.md b/skills/quick-task/SKILL.md index e6ab60f..fc7263b 100644 --- a/skills/quick-task/SKILL.md +++ b/skills/quick-task/SKILL.md @@ -3,7 +3,7 @@ name: quick-task description: "Quick lane for a change the user can describe in one sentence (fix, tweak, rename, bump, small refactor) in a project with .ytstack/ and PREFERENCES quick_lane on: Q###-PLAN and Q###-SUMMARY under .ytstack/quick/, a verification command, no pitch, no review, no milestone bookkeeping. Not for new features, migrations, unknown root causes or more than about three files (plan-task, systematic-debugging)." metadata: tier: core - version: 0.1.0 + version: 0.1.1 allowed-tools: - Bash - Read @@ -161,7 +161,7 @@ verification: passed Command: `{command}` -- passed. {one line of real output} ``` -Then flip the PLAN's `status: in-progress` to `status: done` (Edit) and bump `last_updated` in `STATE.md` (no pointer change: the quick lane does not touch `current_milestone` / `active_slice` / `active_task`). +Then flip the PLAN's `status: in-progress` to `status: done` (Edit) and bump `last_updated` in `STATE.md` (no pointer change: the quick lane does not touch `current_milestone` / `active_slice` / `active_task`). If the frontmatter has a `progress:` block and this change completes an item the block counts, add 1 to `done` in the same edit (never above `total`) and run `yt_state_lint "$_YT_DIR"`; a quick task outside the counted plan leaves the block alone. ### Step 7: Report + return diff --git a/skills/summarize-task/SKILL.md b/skills/summarize-task/SKILL.md index 04a9874..d43912b 100644 --- a/skills/summarize-task/SKILL.md +++ b/skills/summarize-task/SKILL.md @@ -3,7 +3,7 @@ name: summarize-task description: "Closes the active task: writes M###-S##-T##-SUMMARY.md with commits, outcome, deviations, follow-ups and the verification result, flips the slice-plan checkbox and advances STATE. Use when a task's verification passed, or when the user says task done, close the task, summarize. Not before verification passed and not for quick-lane changes (quick-task writes its own summary)." metadata: tier: core - version: 0.1.0 + version: 0.1.1 triggers: - summarize task - task done @@ -38,7 +38,7 @@ You MUST create a TodoWrite task for each of these items: 7. **Ask verification result** -- did the verification command pass? 8. **Write `M###-S##-T##-SUMMARY.md`** 9. **Flip slice-plan checkbox** `[ ]` → `[x]` -10. **Update STATE.md** -- clear `active_task`, bump counts, update status line +10. **Update STATE.md** -- clear `active_task`, bump counts (including a `progress:` block when present), update status line 11. **Offer git stage** -- ask if user wants git-add of touched files 12. **Report + return** -- suggest next task or slice-close @@ -275,6 +275,8 @@ Regardless of teammate mode: - `last_updated: ...` → bump to current - Status line → `**Status:** {CURRENT_MILESTONE} / {ACTIVE_SLICE} -- {N}/{TOTAL} tasks done in slice. Next task: pick from slice-plan or run `/ytstack:plan-task`.` +**Progress block (M021):** if the `STATE.md` frontmatter has a `progress:` block, update it in the same edit. Add 1 to `done` when the closed task is an item the block counts. Tasks are the default unit; a project that counts something else says so in a comment line inside the block, and `done` / `total` then follow that ledger. If the closed task was added after `total` was set, raise `total` as well. Never write `done` above `total`. Then run `yt_state_lint "$_YT_DIR"`: a warning that starts with `progress:` means the edit is wrong; fix it before Step 11. As a swarm teammate skip the block with the other frontmatter writes and note the pending increment under Follow-ups in the summary. No block: do nothing and do not add one. `bin/ytstack-status` shows these numbers in the status line (`docs/statusline.md`). + If the slice is now fully complete (all tasks `[x]`): update status to recommend `/ytstack:reassess-roadmap`, flip the slice's `status:` to `done` in `{SLICE_PLAN}` frontmatter, and when every slice in `{CURRENT_MILESTONE}-ROADMAP.md` is checked flip the milestone's `status:` to `done` there and mirror the global `ROADMAP.md` row (M018 artifact states). ### Step 11: Offer git stage From f7950c84b4d94f145fbd4a1f1e4bef348e256847 Mon Sep 17 00:00:00 2001 From: Alex Wegener Date: Wed, 11 Nov 2026 11:33:54 +0100 Subject: [PATCH 06/10] M021-S02-T02: document the status line segment (docs/statusline.md, README, concept, codex, CHANGELOG) Co-Authored-By: Claude Fable 5.2 --- CHANGELOG.md | 15 +++++++++ README.md | 6 ++-- docs/codex.md | 4 +++ docs/concept.md | 2 ++ docs/statusline.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 docs/statusline.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 2efe450..bc5459b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **Status line segment** (`bin/ytstack-status`, issue #31): one compact + line for a status line script. With an optional `progress` block in the + `STATE.md` frontmatter (`label`, `done`, `total`, `waiting_on`) it shows + label, percentage, done/total and the waiting item; without it the active + slice's task count from the slice plan checkboxes; in both cases the + current task pointer. Plan progress is read from the default branch when + the checkout is on another branch (linked worktrees), the pointer from the + checkout. Local refs only, no network. Integration in `docs/statusline.md`. +- `yt_progress_parse` in the shared library and STATE lint finding W7 for a + malformed `progress` block; a malformed block prints nothing. +- `summarize-task` and `quick-task` update the `progress` block when they + advance STATE. + ### Changed - Docs only: CONTRIBUTING release checklist (fetch + merge before the diff --git a/README.md b/README.md index 5920634..8db9606 100644 --- a/README.md +++ b/README.md @@ -333,7 +333,8 @@ Ships: - 31 skills: project-OS lifecycle (`init-project`, `adopt-brownfield`, `plan-milestone`, `slice-milestone`, `plan-task`, `summarize-task`, `reassess-roadmap`, `quick-task`, `reconcile`), continuity (`resume-session`, `handoff-session`), gstack wrappers over the installed dependency (`office-hours`, `plan-ceo-review`, `plan-eng-review`, `retro`, `ship`, `document-release`), superpowers wrappers over the vendored subtree (`test-driven-development`, `systematic-debugging`, `verification-before-completion`, `code-review`, `finish-branch`, `grill`), orchestration (`spawn-milestone-team`), two directives (`using-ytstack`, `minimal-code`) and 5 engineering-OS references (`atomic-design`, `deutschland-stack-api`, `european-alternatives-api`, `oss-project`, `software-craftsmanship`). - 9 hooks (SessionStart / PreCompact / SessionEnd / Stop / TeammateIdle / TaskCreated / TaskCompleted / PreToolUse-Edit / PostToolUse-Bash) on a shared resolver library (`hooks/lib/git-ctx.sh`); SessionStart injects the using-ytstack directive + project state + handoff pointers + STATE lint + lifecycle hints. -- Twelve shell test suites (`tests/run-all.sh`, macOS bash 3.2 + Linux bash 5), `bin/ytstack-check` (UX contracts, `metadata:` + description contract, resolver contract, vendor leaks) and an eval suite for `claude plugin eval` (`evals/`, 13 cases). +- A status line segment (`bin/ytstack-status`): project progress from an optional `progress` block in `STATE.md`, or the active slice's task count, plus the current task. See [`docs/statusline.md`](./docs/statusline.md). +- Thirteen shell test suites (`tests/run-all.sh`, macOS bash 3.2 + Linux bash 5), `bin/ytstack-check` (UX contracts, `metadata:` + description contract, resolver contract, vendor leaks) and an eval suite for `claude plugin eval` (`evals/`, 13 cases). - Full docs (`AGENTS.md`, `CONTRIBUTING.md`, `QUICKSTART.md`, UX contracts, references, methodology) - Plugin manifest + marketplace manifest, published via the `yesterday-public-plugins` catalog - Codex manifest + self-marketplace, shared skills, and 3 lifecycle hooks; see [host support](docs/codex.md#supported-behavior) @@ -348,7 +349,8 @@ ytstack/ ├── .ytstack/ dogfood: ytstack tracks itself ├── hooks/ 8 hook scripts + hooks.json + lib/git-ctx.sh (shared resolver) ├── skills/ 21 skill packages -├── tests/ 10 shell test suites + run-all.sh (--linux via Docker) +├── bin/ ytstack-check (contracts), ytstack-status (status line segment), ytstack-codex, dev-reinstall +├── tests/ 13 shell test suites + run-all.sh (--linux via Docker) ├── vendor/ read-only subtree: superpowers v7.0.2 (gstack is an installed dependency, not vendored) ├── docs/ │ ├── ux/ mandatory skill-authoring contracts diff --git a/docs/codex.md b/docs/codex.md index 271a7e3..0c6a693 100644 --- a/docs/codex.md +++ b/docs/codex.md @@ -55,6 +55,10 @@ Codex itself supplies plugin-root compatibility aliases for hooks; the helper also resolves skill paths and the target repository root. No Claude executable is needed. Codex-specific tool mappings stay in `codex/adapter.md`. +`bin/ytstack-status` prints the project's progress segment for a status line +and is host-neutral: run it with the project as working directory or pipe a +JSON object with a `cwd` field. See `docs/statusline.md`. + ## Supported behavior | Capability | Codex support | diff --git a/docs/concept.md b/docs/concept.md index 6707f56..c092d2e 100644 --- a/docs/concept.md +++ b/docs/concept.md @@ -272,6 +272,8 @@ Lifecycle + artifact + orchestration pieces that GSD inspired but we re-implemen **Ceremony dials and states (M018):** `PREFERENCES.md` `granularity` / `autonomy` / `quick_lane` / `minimal_code` (read via `yt_prefs`), milestone states `planned | in-progress | done | dropped | shipped-informally` (`docs/ux/skill-structure.md` "Artifact states"), STATE pointer lint (`yt_state_lint`, injected by `session-start`, enforced by `plan-task` / `summarize-task`), read-side layout tolerance (`yt_find_artifacts`). +**Status line segment (M021):** `bin/ytstack-status` prints one compact line for a status line script: an optional `progress` block in the `STATE.md` frontmatter (`label`, `done`, `total`, `waiting_on`; parsed by `yt_progress_parse`, linted as W7 by `yt_state_lint`, kept current by `summarize-task` / `quick-task`) or, without it, the active slice's checkbox count, plus the task pointer. Plan progress is read from the default branch outside it, the pointer from the checkout. Details in `docs/statusline.md`. + ### 3.5 Distribution (two marketplaces split by visibility, per-plugin own repos) Per DECISIONS 2026-04-25 (afternoon) "Marketplace architecture -- split into ystacks (public) + ystacks-internal (private), per-plugin own repos" -- supersedes the morning's "ystacks monorepo + catalog hybrid" decision (its catalog-only-with-some-subdirs model leaked private plugin names through a public catalog and made cross-marketplace deps fragile). diff --git a/docs/statusline.md b/docs/statusline.md new file mode 100644 index 0000000..368931e --- /dev/null +++ b/docs/statusline.md @@ -0,0 +1,83 @@ +# Status line segment + +`bin/ytstack-status` prints one compact segment for the ytstack project that owns the current directory. Your own status line script appends it. A Claude Code plugin cannot set the main `statusLine` itself, so ytstack ships the segment and you wire it in once. + +``` +V2 76% 138/180 · M021/S01/T02 · waiting: review of PR 12 +``` + +## What it shows + +| Project state | Segment | +|---|---| +| `progress` block in `STATE.md` | label, percentage, done/total, task pointer, `waiting_on` if set | +| no block, active slice | task pointer and the slice's task count, for example `M021/S01/T02 3/7` | +| no block, no slice | the milestone, for example `M021` | +| no `.ytstack/`, nothing to show, or a malformed block | nothing, exit code 1 | + +The percentage rounds down, so 100% appears only when `done` equals `total`. `waiting_on` is cut after 32 characters. + +The task count without a block comes from the checkboxes (`- [x] T01 -- …`) in the active slice plan `M###-S##-PLAN.md`. Every ytstack project gets this form with no upkeep. + +## The `progress` block + +Optional, in the `STATE.md` frontmatter next to the pointers: + +```yaml +--- +project: example +current_milestone: M021 +active_slice: S01 +active_task: T02 +progress: + label: V2 # required, at most 16 characters + done: 138 # integer + total: 180 # integer, at least 1, done <= total + waiting_on: "review of PR 12" # optional: the one thing that waits for a human +--- +``` + +Use it when the project has a plan that is larger than the active slice: a release, a migration ledger, a rewrite. There is exactly one block. Comment lines and unknown keys inside it are ignored, so a line such as `# counts: ledger rows in docs/plan.md` can say what the numbers count. Tasks are the default unit. + +Keeping it current: + +- `summarize-task` adds 1 to `done` when it closes a counted task, and `quick-task` does the same for a counted change. Both re-run the STATE lint. +- The STATE lint (`yt_state_lint`, shown at session start) reports a malformed block: missing or long label, non-integer values, `total` below 1, `done` above `total`. +- A malformed block never produces a number. The segment stays empty until the block is fixed. + +## Two levels: plan and task + +Plan progress belongs to the repository, the task pointer to the checkout. + +- **Plan progress** is read from the default branch whenever the checkout is on another branch. A linked worktree or a feature branch carries a stale copy of `STATE.md`, so the script reads the file with `git show` from the newer of `origin/` and the local ``. The default branch is taken from `origin/HEAD`, else `main`, else `master`. On the default branch itself, and outside Git, the working tree file is used. +- **The task pointer** (`M###/S##/T##`) always comes from the `STATE.md` of the directory you are in. + +Only local refs are read. The script makes no network calls and does not use `gh`; one run takes well under a tenth of a second. + +## Integration in Claude Code + +Your status line script receives a JSON object on stdin. Pass it on and append the output. In a bash script: + +```bash +input=$(cat) +yt=$(ls -d ~/.claude/plugins/cache/*/ytstack/*/bin/ytstack-status 2>/dev/null | sort -V | tail -1) +seg=$([ -n "$yt" ] && printf '%s' "$input" | "$yt") +``` + +Then print `$seg` where you want it, for example `printf '%s%s' "$line" "${seg:+ | $seg}"`. + +The second line is the stable way to find the script. Plugin files live under a versioned directory that changes with every update, and Claude Code does not put a plugin's `bin/` on the `PATH` of the status line command. The glob picks the highest installed version, so the segment follows `/plugin update` without an edit. If you work from a local checkout, set `yt` to `/bin/ytstack-status` instead. + +The script reads only `cwd` from the JSON. If your script already knows the directory, `(cd "$dir" && "$yt" `) have no branch copy, so their plan progress always comes from the file on disk. +- Calling the script through a symlink does not work; it finds its library relative to its own path. From e6264b4f921fcca497a8d6c220bd45698ff46e47 Mon Sep 17 00:00:00 2001 From: Alex Wegener Date: Wed, 11 Nov 2026 11:35:58 +0100 Subject: [PATCH 07/10] M021-S02-T03: close milestone M021 (summaries, roadmaps, decision, state) Co-Authored-By: Claude Fable 5.2 --- .ytstack/DECISIONS.md | 19 +++++++++++++++ .ytstack/M021-ROADMAP.md | 6 ++--- .ytstack/M021-S02-PLAN.md | 10 ++++---- .ytstack/M021-S02-T01-SUMMARY.md | 30 +++++++++++++++++++++++ .ytstack/M021-S02-T02-PLAN.md | 37 ++++++++++++++++++++++++++++ .ytstack/M021-S02-T02-SUMMARY.md | 30 +++++++++++++++++++++++ .ytstack/M021-S02-T03-PLAN.md | 33 +++++++++++++++++++++++++ .ytstack/M021-S02-T03-SUMMARY.md | 42 ++++++++++++++++++++++++++++++++ .ytstack/ROADMAP.md | 1 + .ytstack/STATE.md | 12 ++++----- 10 files changed, 206 insertions(+), 14 deletions(-) create mode 100644 .ytstack/M021-S02-T01-SUMMARY.md create mode 100644 .ytstack/M021-S02-T02-PLAN.md create mode 100644 .ytstack/M021-S02-T02-SUMMARY.md create mode 100644 .ytstack/M021-S02-T03-PLAN.md create mode 100644 .ytstack/M021-S02-T03-SUMMARY.md diff --git a/.ytstack/DECISIONS.md b/.ytstack/DECISIONS.md index 870c45d..97c5fd5 100644 --- a/.ytstack/DECISIONS.md +++ b/.ytstack/DECISIONS.md @@ -797,3 +797,22 @@ Plus: ops-layer split during the same session (yopstack created as own public re **How to apply:** Keep Codex tool mappings in codex/adapter.md. The Codex SessionStart entry passes these as optional host context to the shared hook. Without trusted hooks, read the adapter explicitly before following skills. The small runtime helper supplies the shared scripts' existing CLAUDE_* inputs and resolves the repository root; those compatibility names do not require a Claude executable. All three lifecycle events use the original hook scripts. Do not reintroduce generated copies or a second skill tree. **Supersedes:** The earlier generated-package decision and per-skill Codex-link approach. The host-specific manifests, three-event Codex registration, hook trust, and repository-subdirectory fix remain. + +--- + +## 2026-11-11: Status line progress is one optional `progress` block in STATE.md, read by a plugin script (M021, issue #31) + +**Context:** A project built its own status line segment by scraping a totals row out of a prose table in `STATE.md`. That works for one table and breaks silently when the prose changes. Claude Code plugins cannot set the main `statusLine`, and no documented path lets a status line command find plugin files across versions. + +**Options considered:** +- A) A new progress file per project. +- B) An optional `progress` block in the `STATE.md` frontmatter, with the slice plan checkboxes as fallback. +- C) Several named tracks, or a `done` / `open` pair instead of `done` / `total`. + +**Chose:** B with exactly one track (`label`, `done`, `total`, optional `waiting_on`). `bin/ytstack-status` prints the segment; `yt_progress_parse` is the only parser and serves the script and the STATE lint (W7). Plan progress is read from the default branch when the checkout is on another branch, the task pointer from the checkout. The script is found by a glob over the plugin cache in the user's own status line script. SessionStart does not maintain a link. + +**Reason:** `STATE.md` exists in every project, already has machine-read frontmatter and is already linted. One parser means the segment and the lint cannot disagree, so a malformed block shows nothing instead of a wrong number. One track covers the case that motivated the issue; a list of tracks can be added later without breaking readers of the single block. A hook-maintained link would break the read-only contract of SessionStart and force every suite that fires the hook to isolate HOME. + +**How to apply:** New readers of the block call `yt_progress_parse`; do not add a second parser. Skills that advance STATE update the block (`summarize-task`, `quick-task`). The status script stays free of network calls and `gh`. C is open for a later milestone when a project needs it. + +**Supersedes:** None. diff --git a/.ytstack/M021-ROADMAP.md b/.ytstack/M021-ROADMAP.md index e2569d9..5554803 100644 --- a/.ytstack/M021-ROADMAP.md +++ b/.ytstack/M021-ROADMAP.md @@ -3,9 +3,9 @@ milestone: M021 project: ytstack size: M created: 2026-11-11T10:25:52Z -status: in-progress +status: done total_slices: 2 -completed_slices: 1 +completed_slices: 2 --- # M021 Roadmap @@ -27,7 +27,7 @@ completed_slices: 1 Slice detail lives in per-slice `M021-S##-PLAN.md` files, created by `ytstack:slice-milestone`. - [x] S01 -- `bin/ytstack-status` prints the progress segment for any directory, from one shared parser that the STATE lint also uses. -- [ ] S02 -- The block stays current without hand upkeep and a user can wire the segment into their status line from the documentation. +- [x] S02 -- The block stays current without hand upkeep and a user can wire the segment into their status line from the documentation. ## Run order diff --git a/.ytstack/M021-S02-PLAN.md b/.ytstack/M021-S02-PLAN.md index 6ff6a73..aec2bd1 100644 --- a/.ytstack/M021-S02-PLAN.md +++ b/.ytstack/M021-S02-PLAN.md @@ -3,9 +3,9 @@ milestone: M021 slice: S02 project: ytstack created: 2026-11-11T10:25:52Z -status: planned +status: done task_count: 3 -completed_tasks: 0 +completed_tasks: 3 --- # M021-S02 -- Slice Plan @@ -14,9 +14,9 @@ completed_tasks: 0 ## Tasks -- [ ] T01 -- `summarize-task` and `quick-task` update the `progress` block when they advance STATE and re-run the lint; skill versions bumped; `bin/ytstack-check` clean. -- [ ] T02 -- `docs/statusline.md` (block reference, output forms, two-line integration for Claude Code, other hosts, colour switch); pointers in README, `docs/concept.md` and `docs/codex.md`; CHANGELOG `[Unreleased]` entry. -- [ ] T03 -- Verification and close: `bash tests/run-all.sh`, `bash tests/run-all.sh --linux`, `bin/ytstack-check`, live run against this repository and a linked worktree; M021 roadmap, global ROADMAP, DECISIONS and STATE closed. +- [x] T01 -- `summarize-task` and `quick-task` update the `progress` block when they advance STATE and re-run the lint; skill versions bumped; `bin/ytstack-check` clean. +- [x] T02 -- `docs/statusline.md` (block reference, output forms, two-line integration for Claude Code, other hosts, colour switch); pointers in README, `docs/concept.md` and `docs/codex.md`; CHANGELOG `[Unreleased]` entry. +- [x] T03 -- Verification and close: `bash tests/run-all.sh`, `bash tests/run-all.sh --linux`, `bin/ytstack-check`, live run against this repository and a linked worktree; M021 roadmap, global ROADMAP, DECISIONS and STATE closed. ## Done when diff --git a/.ytstack/M021-S02-T01-SUMMARY.md b/.ytstack/M021-S02-T01-SUMMARY.md new file mode 100644 index 0000000..5313ae8 --- /dev/null +++ b/.ytstack/M021-S02-T01-SUMMARY.md @@ -0,0 +1,30 @@ +--- +milestone: M021 +slice: S02 +task: T01 +project: ytstack +closed: 2026-11-11T11:40:00Z +verification: passed +--- + +# M021-S02-T01 -- Summary + +## Commits + +- `b414072` M021-S02-T01: summarize-task and quick-task keep the STATE progress block current + +## Outcome + +`summarize-task` Step 10 now updates a `progress:` block in the same STATE edit: plus one on `done` for a counted task, `total` raised for a task added later, never `done` above `total`, lint re-run. Swarm teammates skip it and note the pending increment. `quick-task` carries the same rule for a counted change. Both skills are at 0.1.1. + +## Deviations from plan + +none + +## Follow-ups + +- The instruction is prose for the agent, not code. No eval case covers it yet; add one to `evals/` when the block is in use on a real project. + +## Verification + +Command: `bin/ytstack-check; bash tests/run-all.sh` -- passed (checker `OK (with 27 warning[s])`, the same count as `main`; 13 suites, `ALL SUITES PASSED`). diff --git a/.ytstack/M021-S02-T02-PLAN.md b/.ytstack/M021-S02-T02-PLAN.md new file mode 100644 index 0000000..6ccb487 --- /dev/null +++ b/.ytstack/M021-S02-T02-PLAN.md @@ -0,0 +1,37 @@ +--- +milestone: M021 +slice: S02 +task: T02 +project: ytstack +created: 2026-11-11T11:40:00Z +status: done +--- + +# M021-S02-T02 -- Task Plan + +**Summary:** Document the segment: `docs/statusline.md`, pointers in README, `docs/concept.md` and `docs/codex.md`, CHANGELOG entry. + +## Files + +- Create: `docs/statusline.md` +- Modify: `README.md` (Ships list, repo layout) +- Modify: `docs/concept.md` (native components) +- Modify: `docs/codex.md` (helper paragraph) +- Modify: `CHANGELOG.md` (`[Unreleased]` / Added) + +## What this does + +`docs/statusline.md` covers the output forms, the `progress` block and its rules, the two levels (plan from the default branch, pointer from the checkout), the integration snippet for a Claude Code status line script including how it finds the newest installed version, other hosts, the colour switch and the limits. The other files get one pointer each. + +## Verification + +```bash +bash /tmp/yt-snip/sl.sh # the documented snippet against a fake plugin cache with versions 0.4.2, 0.9.0, 0.10.0 +grep -c "—" docs/statusline.md +``` + +Success: the snippet picks 0.10.0 and appends the segment; no em-dash in the new file. + +## Done signal + +Run `/ytstack:summarize-task` after execution to mark complete and advance STATE.md. diff --git a/.ytstack/M021-S02-T02-SUMMARY.md b/.ytstack/M021-S02-T02-SUMMARY.md new file mode 100644 index 0000000..21cddcb --- /dev/null +++ b/.ytstack/M021-S02-T02-SUMMARY.md @@ -0,0 +1,30 @@ +--- +milestone: M021 +slice: S02 +task: T02 +project: ytstack +closed: 2026-11-11T11:40:00Z +verification: passed +--- + +# M021-S02-T02 -- Summary + +## Commits + +- `f7950c8` M021-S02-T02: document the status line segment (docs/statusline.md, README, concept, codex, CHANGELOG) + +## Outcome + +`docs/statusline.md` describes the segment, the block, the plan/task split, the integration and the limits. README lists the segment under Ships and `bin/` in the repo layout; `docs/concept.md` and `docs/codex.md` point at the new page; CHANGELOG has the `[Unreleased]` entry. + +## Deviations from plan + +- README repo layout said "10 shell test suites"; it now says 13, the real count. The Ships list went from twelve to thirteen. + +## Follow-ups + +- The locator glob relies on the plugin cache layout `~/.claude/plugins/cache//ytstack//`, which Claude Code does not document. If the layout changes, the snippet in `docs/statusline.md` is the one place to fix. + +## Verification + +Command: the documented snippet against a fake cache with 0.4.2 (no script), 0.9.0 and 0.10.0 -- passed: picked `0.10.0`, printed `line | M021/S02/T01 0/3`; for `/tmp` it printed `line`; with no plugin installed it printed `line`. `grep -c "—" docs/statusline.md` printed 0. diff --git a/.ytstack/M021-S02-T03-PLAN.md b/.ytstack/M021-S02-T03-PLAN.md new file mode 100644 index 0000000..c1cefa1 --- /dev/null +++ b/.ytstack/M021-S02-T03-PLAN.md @@ -0,0 +1,33 @@ +--- +milestone: M021 +slice: S02 +task: T03 +project: ytstack +created: 2026-11-11T11:50:00Z +status: done +--- + +# M021-S02-T03 -- Task Plan + +**Summary:** Verify the milestone against its exit criteria and close it on paper. + +## Files + +- Modify: `.ytstack/M021-S02-PLAN.md`, `.ytstack/M021-ROADMAP.md`, `.ytstack/ROADMAP.md`, `.ytstack/DECISIONS.md`, `.ytstack/STATE.md` +- Create: `.ytstack/M021-S02-T0{1,2,3}-SUMMARY.md` + +## What this does + +Runs the complete verification on the final commit and records the result, the decision and the operator actions. No source change. + +## Verification + +```bash +bash tests/run-all.sh && bin/ytstack-check; python3 -m unittest discover -s tests -p 'codex*_test.py'; claude plugin validate . +``` + +Success: all suites pass, checker without failures and without new warnings, validation passes. + +## Done signal + +Run `/ytstack:summarize-task` after execution to mark complete and advance STATE.md. diff --git a/.ytstack/M021-S02-T03-SUMMARY.md b/.ytstack/M021-S02-T03-SUMMARY.md new file mode 100644 index 0000000..e5e7fcb --- /dev/null +++ b/.ytstack/M021-S02-T03-SUMMARY.md @@ -0,0 +1,42 @@ +--- +milestone: M021 +slice: S02 +task: T03 +project: ytstack +closed: 2026-11-11T11:50:00Z +verification: passed_with_caveats +--- + +# M021-S02-T03 -- Summary + +## Commits + +- The commit that carries this file (`M021-S02-T03: close milestone M021`). Artifacts only, no source change. + +## Outcome + +M021 is closed on the branch `feat/status-segment`. Exit criteria against evidence: + +| Criterion | Evidence | +|---|---| +| Block: label, percentage, done/total, truncated `waiting_on` | `tests/ytstack-status.test.sh` "progress block" cases | +| No block: slice task count | "no progress block" cases; live run in this worktree printed `M021/S02/T01 0/3` | +| Outside a project: nothing, non-zero | "outside a project" cases; live run for `/tmp` exit 1 | +| Linked worktree: plan from default branch, pointer local | "default branch" cases (worktree, detached HEAD, clone with newer `origin/main`) | +| Malformed block: nothing, lint reports it | status suite "malformed block", `tests/git-ctx.test.sh` W7 case | +| Skills keep the block current | `summarize-task` Step 10, `quick-task` Step 6 (prose, checker clean) | +| Docs and stable location | `docs/statusline.md`; snippet run against a fake cache picked the highest version | +| Suites and checker | 13 suites `ALL SUITES PASSED`, checker 27 warnings as on `main`, 13 Codex tests OK, `claude plugin validate .` passed | + +## Deviations from plan + +- `bash tests/run-all.sh --linux` was skipped: no Docker daemon on this machine. Linux coverage comes from CI after the push. + +## Follow-ups + +- Operator: push `feat/status-segment` (or merge it into `main`), let CI run the ubuntu job, then release and `/plugin update` so the script exists in the plugin cache. Close issue #31 after that. +- Merge note: the main checkout holds uncommitted M020 planning that also edits `STATE.md` and appends to `DECISIONS.md`. Both files will conflict textually; keep both DECISIONS entries and the M020 pointer. + +## Verification + +Command: `bash tests/run-all.sh && bin/ytstack-check; python3 -m unittest discover -s tests -p 'codex*_test.py'; claude plugin validate .` -- passed_with_caveats (macOS only, see Deviations). diff --git a/.ytstack/ROADMAP.md b/.ytstack/ROADMAP.md index 70f58f3..62538cc 100644 --- a/.ytstack/ROADMAP.md +++ b/.ytstack/ROADMAP.md @@ -38,6 +38,7 @@ Progress legend: `[ ]` todo, `[~]` in progress, `[x]` done, `[-]` skipped. | M018 | Ceremony scaling + STATE pointer validation (report point 5) -- `granularity`/`autonomy`/`quick_lane`/`minimal_code` preferences, `quick-task`, states incl. `dropped`/`shipped-informally`, `yt_state_lint` + session-start injection, `reconcile`, `minimal-code` guard (ponytail concept), read-side layouts. Per-worktree `active_task` deferred to M019. Ships in 0.4.0. | **done** | 11 | 11 | | M019 | Skill quality, evals, execution model (report point 7 + developer-skills research; absorbs M013) -- trigger-only descriptions within the 1,536-char budget (mattpocock/Anthropic criteria, superpowers' "never summarise the workflow"), `tier`/`version` -> `metadata`, `disable-model-invocation`/`user-invocable` instead of `tier`, `claude plugin eval` suite + CI gate, new skills `code-review` (standards + spec reviewers), `pr`, `retro`, `grilling`, `writing-for-agents` wrapper; orchestrator + `Agent(isolation: worktree)` slices with `worktree.baseRef: head`, verifier Stop-hook; listing on skills.sh; version bump discipline. Released 0.4.0 -> 0.4.1 (Codex merge, PR #23) -> 0.4.2 (Linux `stat` fix), all pushed with green CI 2026-11-07. Shipped: `metadata:` + trigger-only descriptions + checker rules (S01), `evals/` 13 cases + CI (S02), `code-review` / `finish-branch` / `grill` / `retro` / `ship` / `document-release` (S03, absorbs M011), `spawn-milestone-team` 0.2.0 orchestrator + worktree subagents with Agent Teams opt-in, agents to the subagent contract, Stop hook (S04), release 0.4.0 (S05). Not done: `writing-for-agents` wrapper (no consumer asked), skills.sh listing (operator action). | **done** | 18 | 18 | | M020 | Candidates, not planned (pick with `ytstack:plan-milestone`): (a) dogfood `spawn-milestone-team` 0.2.0 on a real multi-slice milestone (orchestrator + worktree subagents, merge step, STATE resolution) and fix what the live run shows; (b) Codex host: verify the SessionStart `additionalContextLimit` (REVIEW-NOTES 2026-11-07) and the hook-free flow on a real Codex install; (c) PowerShell hook variants when a Windows consumer appears; (d) per-worktree STATE pointer if (a) needs it; (e) re-run the consumer audit (`docs/research/2026-11-07-notes/local-consumer-repos-audit.md`) after the 17 repos update to 0.4.x. | candidate | TBD | 0 | +| M021 | Status line segment (issue #31) -- `bin/ytstack-status`, optional `progress` block in the `STATE.md` frontmatter, `yt_progress_parse` + STATE lint W7, upkeep in `summarize-task` / `quick-task`, `docs/statusline.md`. Done 2026-11-11 (2 slices, 5 tasks); unreleased. Numbered M021 because M020 is taken by the Kimi adapter planning (issue #30). | **done** | 5 | 5 | **Total:** 39 tasks across M001-M009, 37 done (95%). 2 deferred to user action (git init + push, GitHub repo creation). M010 done (6/6, 2026-11-07); M011 task count pending milestone planning. diff --git a/.ytstack/STATE.md b/.ytstack/STATE.md index 4ae82ac..78fcb2a 100644 --- a/.ytstack/STATE.md +++ b/.ytstack/STATE.md @@ -1,15 +1,15 @@ --- project: ytstack slug: ytstack -last_updated: 2026-11-11T11:15:00Z -current_milestone: M021 -active_slice: S02 -active_task: T01 +last_updated: 2026-11-11T11:50:00Z +current_milestone: none +active_slice: none +active_task: none --- # State -**Status:** M021 / S01 done (2/2). Roadmap reassessed at the slice boundary: S02 stands as planned. M021 / S02 / T01 planned -- ready to execute. +**Status (2026-11-11):** M021 DONE on branch `feat/status-segment` (issue #31): `bin/ytstack-status` status line segment, optional `progress` block in the STATE frontmatter, `yt_progress_parse` + lint W7, upkeep in `summarize-task` / `quick-task`, `docs/statusline.md`. 13 suites green on macOS, checker 27 warnings as on `main`, `claude plugin validate .` passed. Not pushed, not released; the Linux suite run waits for CI (no Docker here). M020 (Kimi adapter, issue #30) is planned in the main checkout and uncommitted there; it is the next pointer once both lines meet. **Previous status (2026-10-13, PR #23 correction):** Removed the generated Codex skill/hook copies and build tooling. Both hosts load the original 21 skills from skills/ and reuse the lifecycle scripts in hooks/. Codex-specific runtime instructions live only in codex/adapter.md, supplied at SessionStart or read explicitly without trusted hooks. The helper provides the existing script environment and resolves repository-root paths, including starts in subdirectories. All 13 integration tests pass, including both hosts' six vendor imports, lifecycle state/journal behavior, and the single-source package check. Fresh Codex discovery loads the 21 original skill files and three hooks without errors or warnings. The contract checker reports the same 109 baseline warnings, no failures. The interactive desktop walkthrough remains a manual follow-up. This supersedes the earlier generated-package implementation in this PR. @@ -85,7 +85,7 @@ M009 Docs & Community [####] 4/4 DONE ## Next action -Execute M021-S02-T01, then `ytstack:summarize-task`. +**Operator actions for M021:** push or merge `feat/status-segment`, let CI run the ubuntu job, cut the release, `/plugin update`, wire the snippet from `docs/statusline.md` into the status line script, close issue #31. Merging meets the uncommitted M020 planning in `STATE.md` and `DECISIONS.md`: keep both DECISIONS entries and the M020 pointer. **Previous status:** **M019 DONE -- delivered as 0.4.2 (0.4.0 -> 0.4.1 Codex-support merge of PR #23 -> 0.4.2 Linux `stat` fix), pushed, CI green on `f2a439c`, issues #18/#24/#25/#27 closed, installed plugin on 0.4.2, gstack v1.96.0.0 installed and live-validated (`tests/gstack-live.test.sh` 39/39).** The public catalog (`Yesterday-AI/skills`) pins no version, so it tracks `plugin.json`. No milestone open; M020 candidates are listed in ROADMAP. 0.4.0 bundles M010 (adopt-brownfield), M018 (ceremony dials, quick-task, reconcile, minimal-code, STATE lint, states, layouts) and M019 (`metadata:` frontmatter + trigger-only descriptions, `evals/` for `claude plugin eval` + CI, six lifecycle skills `code-review` / `finish-branch` / `grill` / `retro` / `ship` / `document-release`, worktree-subagent execution model with Agent Teams opt-in, Stop hook, no plugin dependencies). Clean clone at the release commit: 12 suites green, checker 0 fails, validate clean; evals 10/11 + re-run of the changed cases. **No milestone is open**: the modernisation programme from the 2026-11-07 research is complete; `ytstack:plan-milestone` for M020 when there is a next goal (candidates: live dogfooding of `spawn-milestone-team` 0.2.0 on a real milestone; PowerShell hook variants if a Windows consumer appears; per-worktree pointer if the orchestrator model needs it). **Operator actions:** `git push origin main --tags` (v0.2.0, v0.3.0, v0.4.0 are local), `gh issue close 24 25 27` (evidence posted), `/plugin update` to 0.4.0, gstack install one-liner (QUICKSTART) for the six gstack-backed skills, catalog sync in `Yesterday-AI/skills` (version 0.4.0, README skill table), skills.sh listing refresh. From 94a1bed18516da902965581733e63ea962dc40a1 Mon Sep 17 00:00:00 2001 From: Alex Wegener Date: Wed, 11 Nov 2026 11:44:52 +0100 Subject: [PATCH 08/10] fix: pin and document that the slice checkbox count follows the local pointer (PR #32 review) Co-Authored-By: Claude Fable 5.2 --- bin/ytstack-status | 4 +++- docs/statusline.md | 3 ++- tests/ytstack-status.test.sh | 10 ++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/bin/ytstack-status b/bin/ytstack-status index 2290224..6c075cd 100755 --- a/bin/ytstack-status +++ b/bin/ytstack-status @@ -14,7 +14,9 @@ # # Plan progress is repository-wide: on any branch other than the default one # (a linked worktree carries a stale copy) STATE.md is read from the newer of -# origin/ and . The task pointer is always the local one. +# origin/ and . The task pointer is always the local one, +# and so is the slice checkbox count of the fallback: it describes the slice +# the local pointer names, whose checkboxes are flipped in this checkout first. # Local refs only: no network, no gh. See docs/statusline.md. set -uo pipefail diff --git a/docs/statusline.md b/docs/statusline.md index 368931e..d6a4c4d 100644 --- a/docs/statusline.md +++ b/docs/statusline.md @@ -49,8 +49,9 @@ Keeping it current: Plan progress belongs to the repository, the task pointer to the checkout. -- **Plan progress** is read from the default branch whenever the checkout is on another branch. A linked worktree or a feature branch carries a stale copy of `STATE.md`, so the script reads the file with `git show` from the newer of `origin/` and the local ``. The default branch is taken from `origin/HEAD`, else `main`, else `master`. On the default branch itself, and outside Git, the working tree file is used. +- **Plan progress** (the `progress` block) is read from the default branch whenever the checkout is on another branch. A linked worktree or a feature branch carries a stale copy of `STATE.md`, so the script reads the file with `git show` from the newer of `origin/` and the local ``. The default branch is taken from `origin/HEAD`, else `main`, else `master`. On the default branch itself, and outside Git, the working tree file is used. - **The task pointer** (`M###/S##/T##`) always comes from the `STATE.md` of the directory you are in. +- **The slice task count** of a project without a block goes with the pointer: it is read from the slice plan in the directory you are in. A slice that runs in a worktree flips its checkboxes there first, and the default branch may not have that slice plan yet. Only local refs are read. The script makes no network calls and does not use `gh`; one run takes well under a tenth of a second. diff --git a/tests/ytstack-status.test.sh b/tests/ytstack-status.test.sh index 50905f1..040e0f2 100644 --- a/tests/ytstack-status.test.sh +++ b/tests/ytstack-status.test.sh @@ -141,6 +141,16 @@ git -C "$C" fetch -q origin 2>/dev/null state "$C" M003 S01 T05 '' eq "clone on a topic branch: newer origin/main wins" "0|V2 80% 8/10 · M003/S01/T05" "$(seg "$C")" +# slice count without a block: belongs to the local pointer, so it is read +# from the checkout even off the default branch (a slice executed in a +# worktree flips its checkboxes there first) +E=$(new_repo slicewt); state "$E" M003 S01 T03; commit "$E" seed +EW="$TMP/slicewt-wt"; git -C "$E" worktree add -q -b slice-work "$EW" >/dev/null 2>&1 +printf -- '---\nmilestone: M003\nslice: S01\n---\n# plan\n\n- [x] T01 -- one\n- [x] T02 -- two\n- [x] T03 -- three\n- [ ] T04 -- four\n- [ ] T05 -- five\n' > "$EW/.ytstack/M003-S01-PLAN.md" +state "$EW" M003 S01 T04 +eq "linked worktree, no block: slice count is local" "0|M003/S01/T04 3/5" "$(seg "$EW")" +eq "main checkout keeps its own count" "0|M003/S01/T03 2/5" "$(seg "$E")" + # no default branch at all: working tree N="$TMP/nodef"; mkdir -p "$N/.ytstack" ( cd "$N" && git init -q -b trunk && git config user.email t@t.t && git config user.name t && git config commit.gpgsign false ) From a51a91ec902f876f94b9869b74e911a4b2feb091 Mon Sep 17 00:00:00 2001 From: Alex Wegener Date: Wed, 11 Nov 2026 11:44:52 +0100 Subject: [PATCH 09/10] chore: STATE reflects PR #32; real timestamps in the M021 task artifacts Co-Authored-By: Claude Fable 5.2 --- .ytstack/M021-S01-T01-PLAN.md | 2 +- .ytstack/M021-S01-T01-SUMMARY.md | 2 +- .ytstack/M021-S01-T02-PLAN.md | 2 +- .ytstack/M021-S01-T02-SUMMARY.md | 2 +- .ytstack/M021-S02-T01-PLAN.md | 2 +- .ytstack/M021-S02-T01-SUMMARY.md | 2 +- .ytstack/M021-S02-T02-PLAN.md | 2 +- .ytstack/M021-S02-T02-SUMMARY.md | 2 +- .ytstack/M021-S02-T03-PLAN.md | 2 +- .ytstack/M021-S02-T03-SUMMARY.md | 2 +- .ytstack/STATE.md | 6 +++--- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.ytstack/M021-S01-T01-PLAN.md b/.ytstack/M021-S01-T01-PLAN.md index 31ff2a4..a3040fb 100644 --- a/.ytstack/M021-S01-T01-PLAN.md +++ b/.ytstack/M021-S01-T01-PLAN.md @@ -3,7 +3,7 @@ milestone: M021 slice: S01 task: T01 project: ytstack -created: 2026-11-11T10:40:00Z +created: 2026-11-11T10:27:00Z status: planned --- diff --git a/.ytstack/M021-S01-T01-SUMMARY.md b/.ytstack/M021-S01-T01-SUMMARY.md index c444a3c..3ab4c06 100644 --- a/.ytstack/M021-S01-T01-SUMMARY.md +++ b/.ytstack/M021-S01-T01-SUMMARY.md @@ -3,7 +3,7 @@ milestone: M021 slice: S01 task: T01 project: ytstack -closed: 2026-11-11T10:55:00Z +closed: 2026-11-11T10:29:03Z verification: passed --- diff --git a/.ytstack/M021-S01-T02-PLAN.md b/.ytstack/M021-S01-T02-PLAN.md index 06a413b..95c53aa 100644 --- a/.ytstack/M021-S01-T02-PLAN.md +++ b/.ytstack/M021-S01-T02-PLAN.md @@ -3,7 +3,7 @@ milestone: M021 slice: S01 task: T02 project: ytstack -created: 2026-11-11T10:55:00Z +created: 2026-11-11T10:29:30Z status: planned --- diff --git a/.ytstack/M021-S01-T02-SUMMARY.md b/.ytstack/M021-S01-T02-SUMMARY.md index d19e063..0a4d06b 100644 --- a/.ytstack/M021-S01-T02-SUMMARY.md +++ b/.ytstack/M021-S01-T02-SUMMARY.md @@ -3,7 +3,7 @@ milestone: M021 slice: S01 task: T02 project: ytstack -closed: 2026-11-11T11:15:00Z +closed: 2026-11-11T10:31:28Z verification: passed --- diff --git a/.ytstack/M021-S02-T01-PLAN.md b/.ytstack/M021-S02-T01-PLAN.md index e70dda3..2bfb82d 100644 --- a/.ytstack/M021-S02-T01-PLAN.md +++ b/.ytstack/M021-S02-T01-PLAN.md @@ -3,7 +3,7 @@ milestone: M021 slice: S02 task: T01 project: ytstack -created: 2026-11-11T11:15:00Z +created: 2026-11-11T10:32:00Z status: planned --- diff --git a/.ytstack/M021-S02-T01-SUMMARY.md b/.ytstack/M021-S02-T01-SUMMARY.md index 5313ae8..d94b66b 100644 --- a/.ytstack/M021-S02-T01-SUMMARY.md +++ b/.ytstack/M021-S02-T01-SUMMARY.md @@ -3,7 +3,7 @@ milestone: M021 slice: S02 task: T01 project: ytstack -closed: 2026-11-11T11:40:00Z +closed: 2026-11-11T10:33:03Z verification: passed --- diff --git a/.ytstack/M021-S02-T02-PLAN.md b/.ytstack/M021-S02-T02-PLAN.md index 6ccb487..87ddbe8 100644 --- a/.ytstack/M021-S02-T02-PLAN.md +++ b/.ytstack/M021-S02-T02-PLAN.md @@ -3,7 +3,7 @@ milestone: M021 slice: S02 task: T02 project: ytstack -created: 2026-11-11T11:40:00Z +created: 2026-11-11T10:33:10Z status: done --- diff --git a/.ytstack/M021-S02-T02-SUMMARY.md b/.ytstack/M021-S02-T02-SUMMARY.md index 21cddcb..cc18414 100644 --- a/.ytstack/M021-S02-T02-SUMMARY.md +++ b/.ytstack/M021-S02-T02-SUMMARY.md @@ -3,7 +3,7 @@ milestone: M021 slice: S02 task: T02 project: ytstack -closed: 2026-11-11T11:40:00Z +closed: 2026-11-11T10:33:54Z verification: passed --- diff --git a/.ytstack/M021-S02-T03-PLAN.md b/.ytstack/M021-S02-T03-PLAN.md index c1cefa1..fcc27ae 100644 --- a/.ytstack/M021-S02-T03-PLAN.md +++ b/.ytstack/M021-S02-T03-PLAN.md @@ -3,7 +3,7 @@ milestone: M021 slice: S02 task: T03 project: ytstack -created: 2026-11-11T11:50:00Z +created: 2026-11-11T10:34:30Z status: done --- diff --git a/.ytstack/M021-S02-T03-SUMMARY.md b/.ytstack/M021-S02-T03-SUMMARY.md index e5e7fcb..1d012a1 100644 --- a/.ytstack/M021-S02-T03-SUMMARY.md +++ b/.ytstack/M021-S02-T03-SUMMARY.md @@ -3,7 +3,7 @@ milestone: M021 slice: S02 task: T03 project: ytstack -closed: 2026-11-11T11:50:00Z +closed: 2026-11-11T10:35:58Z verification: passed_with_caveats --- diff --git a/.ytstack/STATE.md b/.ytstack/STATE.md index 78fcb2a..640f43f 100644 --- a/.ytstack/STATE.md +++ b/.ytstack/STATE.md @@ -1,7 +1,7 @@ --- project: ytstack slug: ytstack -last_updated: 2026-11-11T11:50:00Z +last_updated: 2026-11-11T10:45:00Z current_milestone: none active_slice: none active_task: none @@ -9,7 +9,7 @@ active_task: none # State -**Status (2026-11-11):** M021 DONE on branch `feat/status-segment` (issue #31): `bin/ytstack-status` status line segment, optional `progress` block in the STATE frontmatter, `yt_progress_parse` + lint W7, upkeep in `summarize-task` / `quick-task`, `docs/statusline.md`. 13 suites green on macOS, checker 27 warnings as on `main`, `claude plugin validate .` passed. Not pushed, not released; the Linux suite run waits for CI (no Docker here). M020 (Kimi adapter, issue #30) is planned in the main checkout and uncommitted there; it is the next pointer once both lines meet. +**Status (2026-11-11):** M021 DONE on branch `feat/status-segment` (issue #31): `bin/ytstack-status` status line segment, optional `progress` block in the STATE frontmatter, `yt_progress_parse` + lint W7, upkeep in `summarize-task` / `quick-task`, `docs/statusline.md`. 13 suites green on macOS, checker 27 warnings as on `main`, `claude plugin validate .` passed. Open as PR #32 (CI green on macOS and ubuntu); not merged, not released. M020 (Kimi adapter, issue #30) is planned in the main checkout and uncommitted there; it is the next pointer once both lines meet. **Previous status (2026-10-13, PR #23 correction):** Removed the generated Codex skill/hook copies and build tooling. Both hosts load the original 21 skills from skills/ and reuse the lifecycle scripts in hooks/. Codex-specific runtime instructions live only in codex/adapter.md, supplied at SessionStart or read explicitly without trusted hooks. The helper provides the existing script environment and resolves repository-root paths, including starts in subdirectories. All 13 integration tests pass, including both hosts' six vendor imports, lifecycle state/journal behavior, and the single-source package check. Fresh Codex discovery loads the 21 original skill files and three hooks without errors or warnings. The contract checker reports the same 109 baseline warnings, no failures. The interactive desktop walkthrough remains a manual follow-up. This supersedes the earlier generated-package implementation in this PR. @@ -85,7 +85,7 @@ M009 Docs & Community [####] 4/4 DONE ## Next action -**Operator actions for M021:** push or merge `feat/status-segment`, let CI run the ubuntu job, cut the release, `/plugin update`, wire the snippet from `docs/statusline.md` into the status line script, close issue #31. Merging meets the uncommitted M020 planning in `STATE.md` and `DECISIONS.md`: keep both DECISIONS entries and the M020 pointer. +**Operator actions for M021:** merge PR #32, cut the release, `/plugin update`, wire the snippet from `docs/statusline.md` into the status line script, close issue #31. Merging meets the uncommitted M020 planning in `STATE.md` and `DECISIONS.md`: keep both DECISIONS entries and the M020 pointer. **Previous status:** **M019 DONE -- delivered as 0.4.2 (0.4.0 -> 0.4.1 Codex-support merge of PR #23 -> 0.4.2 Linux `stat` fix), pushed, CI green on `f2a439c`, issues #18/#24/#25/#27 closed, installed plugin on 0.4.2, gstack v1.96.0.0 installed and live-validated (`tests/gstack-live.test.sh` 39/39).** The public catalog (`Yesterday-AI/skills`) pins no version, so it tracks `plugin.json`. No milestone open; M020 candidates are listed in ROADMAP. 0.4.0 bundles M010 (adopt-brownfield), M018 (ceremony dials, quick-task, reconcile, minimal-code, STATE lint, states, layouts) and M019 (`metadata:` frontmatter + trigger-only descriptions, `evals/` for `claude plugin eval` + CI, six lifecycle skills `code-review` / `finish-branch` / `grill` / `retro` / `ship` / `document-release`, worktree-subagent execution model with Agent Teams opt-in, Stop hook, no plugin dependencies). Clean clone at the release commit: 12 suites green, checker 0 fails, validate clean; evals 10/11 + re-run of the changed cases. **No milestone is open**: the modernisation programme from the 2026-11-07 research is complete; `ytstack:plan-milestone` for M020 when there is a next goal (candidates: live dogfooding of `spawn-milestone-team` 0.2.0 on a real milestone; PowerShell hook variants if a Windows consumer appears; per-worktree pointer if the orchestrator model needs it). **Operator actions:** `git push origin main --tags` (v0.2.0, v0.3.0, v0.4.0 are local), `gh issue close 24 25 27` (evidence posted), `/plugin update` to 0.4.0, gstack install one-liner (QUICKSTART) for the six gstack-backed skills, catalog sync in `Yesterday-AI/skills` (version 0.4.0, README skill table), skills.sh listing refresh. From 8dd603e50443c5327b5c4f6152452fdcb524507b Mon Sep 17 00:00:00 2001 From: Alex Wegener Date: Wed, 11 Nov 2026 11:46:18 +0100 Subject: [PATCH 10/10] fix: read the fallback slice plan from the default ref off the default branch (PR #32 review) The slice checkbox count now follows the same source rule as the progress block; the checkout's copy is counted only when the slice plan does not exist on the default branch yet. Replaces the local-only reading pinned in the previous fix commit. Co-Authored-By: Claude Fable 5.2 --- bin/ytstack-status | 30 +++++++++++++++++++----------- docs/statusline.md | 4 ++-- tests/ytstack-status.test.sh | 16 +++++++++++----- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/bin/ytstack-status b/bin/ytstack-status index 6c075cd..0382d8d 100755 --- a/bin/ytstack-status +++ b/bin/ytstack-status @@ -14,9 +14,9 @@ # # Plan progress is repository-wide: on any branch other than the default one # (a linked worktree carries a stale copy) STATE.md is read from the newer of -# origin/ and . The task pointer is always the local one, -# and so is the slice checkbox count of the fallback: it describes the slice -# the local pointer names, whose checkboxes are flipped in this checkout first. +# origin/ and , and so is the slice plan of the fallback +# (the checkout's copy only when the slice plan does not exist there yet). +# The task pointer is always the local one. # Local refs only: no network, no gh. See docs/statusline.md. set -uo pipefail @@ -47,7 +47,7 @@ case "$M" in M[0-9][0-9][0-9]) esac # ---- plan progress (default branch when we are not on it) -------------------- -PLAN_STATE="" +PLAN_STATE=""; REF="" if [ "$YT_IS_GIT" = yes ] && [ "$YT_SCOPE" = project ]; then DEF=$(git -C "$YT_TOPLEVEL" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null) DEF=${DEF#origin/} @@ -88,13 +88,21 @@ else OUT="$POINTER" # Fallback: task checkboxes of the active slice plan. case "$POINTER" in */S[0-9][0-9]*) - PLAN="$YT_DIR/$M-$S-PLAN.md" - [ -f "$PLAN" ] || PLAN=$(yt_find_artifacts "$YT_DIR" "$M-$S-PLAN.md" | head -1) - if [ -n "$PLAN" ] && [ -f "$PLAN" ]; then - TOTAL=$(grep -cE '^- \[[ xX]\] T[0-9][0-9]' "$PLAN") - DONE=$(grep -cE '^- \[[xX]\] T[0-9][0-9]' "$PLAN") - [ "${TOTAL:-0}" -eq 0 ] || OUT="$OUT $C_PLAN$DONE/$TOTAL$C_OFF" - fi ;; + # Same source rule as the block: the default ref when we are off the + # default branch; the checkout when the slice plan does not exist there. + TASKS="" + if [ -n "$REF" ]; then + TASKS=$(git -C "$YT_TOPLEVEL" show "$REF:.ytstack/$M-$S-PLAN.md" 2>/dev/null) \ + || TASKS=$(git -C "$YT_TOPLEVEL" show "$REF:.ytstack/milestones/$M/$M-$S-PLAN.md" 2>/dev/null) || TASKS="" + fi + if [ -z "$TASKS" ]; then + PLAN="$YT_DIR/$M-$S-PLAN.md" + [ -f "$PLAN" ] || PLAN=$(yt_find_artifacts "$YT_DIR" "$M-$S-PLAN.md" | head -1) + [ -z "$PLAN" ] || TASKS=$(cat "$PLAN" 2>/dev/null) + fi + TOTAL=$(printf '%s\n' "$TASKS" | grep -cE '^- \[[ xX]\] T[0-9][0-9]') + DONE=$(printf '%s\n' "$TASKS" | grep -cE '^- \[[xX]\] T[0-9][0-9]') + [ "${TOTAL:-0}" -eq 0 ] || OUT="$OUT $C_PLAN$DONE/$TOTAL$C_OFF" ;; esac fi diff --git a/docs/statusline.md b/docs/statusline.md index d6a4c4d..82f917c 100644 --- a/docs/statusline.md +++ b/docs/statusline.md @@ -49,9 +49,9 @@ Keeping it current: Plan progress belongs to the repository, the task pointer to the checkout. -- **Plan progress** (the `progress` block) is read from the default branch whenever the checkout is on another branch. A linked worktree or a feature branch carries a stale copy of `STATE.md`, so the script reads the file with `git show` from the newer of `origin/` and the local ``. The default branch is taken from `origin/HEAD`, else `main`, else `master`. On the default branch itself, and outside Git, the working tree file is used. +- **Plan progress** is read from the default branch whenever the checkout is on another branch. A linked worktree or a feature branch carries a stale copy of `STATE.md`, so the script reads the file with `git show` from the newer of `origin/` and the local ``. The default branch is taken from `origin/HEAD`, else `main`, else `master`. On the default branch itself, and outside Git, the working tree file is used. - **The task pointer** (`M###/S##/T##`) always comes from the `STATE.md` of the directory you are in. -- **The slice task count** of a project without a block goes with the pointer: it is read from the slice plan in the directory you are in. A slice that runs in a worktree flips its checkboxes there first, and the default branch may not have that slice plan yet. +- **The slice task count** of a project without a block follows the same rule as the block: off the default branch the slice plan named by the pointer is read from the default branch. Only when that slice plan does not exist there yet (a slice planned on the branch) is the copy in your directory counted. A slice that runs in a worktree therefore shows the merged count until its work lands. Only local refs are read. The script makes no network calls and does not use `gh`; one run takes well under a tenth of a second. diff --git a/tests/ytstack-status.test.sh b/tests/ytstack-status.test.sh index 040e0f2..04d82be 100644 --- a/tests/ytstack-status.test.sh +++ b/tests/ytstack-status.test.sh @@ -141,15 +141,21 @@ git -C "$C" fetch -q origin 2>/dev/null state "$C" M003 S01 T05 '' eq "clone on a topic branch: newer origin/main wins" "0|V2 80% 8/10 · M003/S01/T05" "$(seg "$C")" -# slice count without a block: belongs to the local pointer, so it is read -# from the checkout even off the default branch (a slice executed in a -# worktree flips its checkboxes there first) +# slice count without a block: same source rule as the block. Off the default +# branch the slice plan is read from the default ref (PR #32 review); the +# checkout's copy is used only for a slice plan that does not exist there yet. E=$(new_repo slicewt); state "$E" M003 S01 T03; commit "$E" seed EW="$TMP/slicewt-wt"; git -C "$E" worktree add -q -b slice-work "$EW" >/dev/null 2>&1 printf -- '---\nmilestone: M003\nslice: S01\n---\n# plan\n\n- [x] T01 -- one\n- [x] T02 -- two\n- [x] T03 -- three\n- [ ] T04 -- four\n- [ ] T05 -- five\n' > "$EW/.ytstack/M003-S01-PLAN.md" state "$EW" M003 S01 T04 -eq "linked worktree, no block: slice count is local" "0|M003/S01/T04 3/5" "$(seg "$EW")" -eq "main checkout keeps its own count" "0|M003/S01/T03 2/5" "$(seg "$E")" +eq "linked worktree, no block: slice count from main, pointer local" "0|M003/S01/T04 2/5" "$(seg "$EW")" +eq "main checkout: its own working tree" "0|M003/S01/T03 2/5" "$(seg "$E")" +printf -- '---\nmilestone: M003\nslice: S02\n---\n# plan\n\n- [x] T01 -- one\n- [ ] T02 -- two\n' > "$EW/.ytstack/M003-S02-PLAN.md" +state "$EW" M003 S02 T02 +eq "slice plan only in the worktree: local count" "0|M003/S02/T02 1/2" "$(seg "$EW")" +mkdir -p "$E/.ytstack/milestones/M003"; git -C "$E" mv .ytstack/M003-S01-PLAN.md .ytstack/milestones/M003/; commit "$E" "nested layout" +state "$EW" M003 S01 T04; rm -f "$EW/.ytstack/M003-S01-PLAN.md" +eq "slice plan under milestones/M003/ on main" "0|M003/S01/T04 2/5" "$(seg "$EW")" # no default branch at all: working tree N="$TMP/nodef"; mkdir -p "$N/.ytstack"