From 5eba6917f9da8e75b8ed05b805c51a6f85d17c15 Mon Sep 17 00:00:00 2001 From: hkwuks <25031515+hkwuks@users.noreply.github.com> Date: Wed, 23 Sep 2026 00:52:08 +0800 Subject: [PATCH 1/2] chore: archive the task-approve task Complete 454ac4 after PR #53 merged. --- TaskFlowDocs/{ => achieved}/2026-09-23-task-approve/plan.md | 2 +- TaskFlowDocs/{ => achieved}/2026-09-23-task-approve/prd.md | 2 +- TaskFlowDocs/todo.md | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename TaskFlowDocs/{ => achieved}/2026-09-23-task-approve/plan.md (99%) rename TaskFlowDocs/{ => achieved}/2026-09-23-task-approve/prd.md (99%) diff --git a/TaskFlowDocs/2026-09-23-task-approve/plan.md b/TaskFlowDocs/achieved/2026-09-23-task-approve/plan.md similarity index 99% rename from TaskFlowDocs/2026-09-23-task-approve/plan.md rename to TaskFlowDocs/achieved/2026-09-23-task-approve/plan.md index 584460c..8273fd4 100644 --- a/TaskFlowDocs/2026-09-23-task-approve/plan.md +++ b/TaskFlowDocs/achieved/2026-09-23-task-approve/plan.md @@ -1,7 +1,7 @@ # Plan — Record Approval from a hook instead of hand-editing the five fixed fields > Task version: v1 -> Status: in_progress +> Status: completed No spec required — small, self-contained task. diff --git a/TaskFlowDocs/2026-09-23-task-approve/prd.md b/TaskFlowDocs/achieved/2026-09-23-task-approve/prd.md similarity index 99% rename from TaskFlowDocs/2026-09-23-task-approve/prd.md rename to TaskFlowDocs/achieved/2026-09-23-task-approve/prd.md index 5cd1f71..87d4d5b 100644 --- a/TaskFlowDocs/2026-09-23-task-approve/prd.md +++ b/TaskFlowDocs/achieved/2026-09-23-task-approve/prd.md @@ -1,7 +1,7 @@ # Record Approval from a hook instead of hand-editing the five fixed fields > Task version: v1 -> Status: in_progress +> Status: completed ## Goal diff --git a/TaskFlowDocs/todo.md b/TaskFlowDocs/todo.md index c011b11..ff6eeae 100644 --- a/TaskFlowDocs/todo.md +++ b/TaskFlowDocs/todo.md @@ -702,15 +702,15 @@ Every direct request or imported requireme ## Record approval in the plan from a hook instead of hand-editing four fixed-forma - ID: TF-20260918-454ac4 -- Status: in_progress +- Status: done - Priority: normal - Owner: Codex - Source: user request - Added: 2026-09-18 - Updated: 2026-09-23 - Goal: Record approval in the plan from a hook instead of hand-editing four fixed-format fields, and fix hooks/version writing an Approval block that disagrees with hooks/task's template. -- Task: `TaskFlowDocs/2026-09-23-task-approve/` -- Next action: Complete PRD / Spec / Plan and request approval. +- Task: `TaskFlowDocs/achieved/2026-09-23-task-approve/` +- Next action: None — completed and archived. - Notes: **2026-09-18 用户提出**:手改 Approval 块能否自动化。判定——`## Approval` 的字段里只有 `Approved by` 是语义的(必须由人表态),其余都可推导:`Status` 是该子命令的动词,`Approved at` 是当天日期(hook 已有 `today`),`Approved version` 直接读 plan 自己的 `> Task version:` 行,`Approved scope` 由存在的核心文档推出。`skills/taskflow/SKILL.md` 已把模板钉成「`- Approved by:` 起的四行」,格式无需 Agent 判断。 **先修一个现成的自相矛盾**:`hooks/version` 的重置只写 `- Approved by/at/version/scope: pending` 四条,**不写 `- Status:`**;而 `hooks/task` 的模板是**五**行、带 `- Status: requested`。于是经 `version` 迁移过的任务,Approval 块是 version 的字段集加上一条滞留在旧值的 `- Status:` 行——两个 hook 对同一块内容的形状意见不一致。**本任务就是实例**:v1→v2 后 `- Status:` 留在 `checking` 一路没人管,直到 `complete` 读它才发现对不上(`require_approval` 用的是 `grep -qx -- "- Status: approved"`)。 **时间格式也打架**:模板写 `YYYY-MM-DD HH:mm +08:00`,`version` 复位只写 `pending`。若自动化,`today` 目前只有 `+%Y-%m-%d`,要扩出分钟与时区。 From c55ac53ef526e617848725c57e370fbc5c60c25f Mon Sep 17 00:00:00 2001 From: hkwuks <25031515+hkwuks@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:02:33 +0800 Subject: [PATCH 2/2] feat: report orphan task directories in repository-check A directory with prd/plan that no Todo - Task: line names is a lost record beside live documents. repository-check lists active and achieved orphans and sets needs; the hook never deletes. Uncommitted artifact reporting stays needs-neutral. Smoke covers both directions of the reference. --- .../2026-09-23-orphan-task-dirs/plan.md | 98 ++++++++++++++ .../2026-09-23-orphan-task-dirs/prd.md | 127 ++++++++++++++++++ TaskFlowDocs/todo.md | 8 +- hooks/repository-check | 29 ++++ hooks/smoke-test | 65 +++++++++ 5 files changed, 323 insertions(+), 4 deletions(-) create mode 100644 TaskFlowDocs/2026-09-23-orphan-task-dirs/plan.md create mode 100644 TaskFlowDocs/2026-09-23-orphan-task-dirs/prd.md diff --git a/TaskFlowDocs/2026-09-23-orphan-task-dirs/plan.md b/TaskFlowDocs/2026-09-23-orphan-task-dirs/plan.md new file mode 100644 index 0000000..76682a8 --- /dev/null +++ b/TaskFlowDocs/2026-09-23-orphan-task-dirs/plan.md @@ -0,0 +1,98 @@ +# Plan — Detect a task directory that no Todo entry references + +> Task version: v1 +> Status: in_progress + +No spec required — small, self-contained task. + +## Reference Pointers + +- `hooks/repository-check` — uncommitted artifact loop (~69–90); add orphan + pass beside it. +- `hooks/smoke-test` — `== repository-check … ==` sections (~1078+). +- `TaskFlowDocs/todo.md` — `- Task:` backticked paths. +- Predecessor: `TF-20260916-ab381b` (isolation + uncommitted report). + +## Related Tasks + +- `TF-20260916-a357ee` — this task’s ID. +- `TF-20260916-ab381b` — done; different direction (misplaced/uncommitted). + +## Skills / Tools Used + +- `Unaided — no capability applied to this phase; considered: shell text + processing and the repository's own check conventions, all read directly.` + +## Preconditions + +- [x] Docs/rules inspected; worktree `.worktrees/feat-orphan-task-dirs` on + `feat/orphan-task-dirs` from `main` = `5eba691`. +- [x] Incident, direction, and ab381b boundary recorded in PRD Background. + +## Approval + +- Status: approved +- Approved by: user +- Approved at: 2026-09-23 +- Approved version: v1 +- Approved scope: PRD / Plan + +## Steps + +### Step 1 — repository-check orphan report + +- Goal: R1–R5. +- Files: `hooks/repository-check`. +- Checklist: + - [x] Collect `- Task:` backticked paths from `todo.md`. + - [x] Scan `TaskFlowDocs/*/` and `TaskFlowDocs/achieved/*/` with prd or + plan; report dirs not referenced (trailing-slash tolerant). + - [x] Orphans set `needs=1`; uncommitted artifacts unchanged. + - [x] `bash -n hooks/repository-check`. +- Acceptance: A, B, C. +- Status: done + +### Step 2 — smoke + +- Goal: R6/R7. +- Files: `hooks/smoke-test`. +- Checklist: + - [x] Unreferenced active + achieved → listed, exit 2. + - [x] After adding matching `Task:` → not listed. + - [x] Existing repository-check sections still green. +- Acceptance: D, E. +- Status: done + +## Checkpoints + +- After Step 1: `bash -n` + A/B/C fixtures. +- After Step 2: one full suite; no chained mutations; pkill if killed. + +## Verification / Review + +| Check | Result | +|---|---| +| `bash -n hooks/repository-check` | pass | +| `bash -n hooks/smoke-test` | pass | +| `bash hooks/smoke-test` | `ALL SMOKE PASSED` | +| `git diff --check` | clean | +| Active orphan listed / ref clears (A/B) | pass (suite) | +| Achieved orphan (C) | pass (suite) | +| Existing check sections (D) | pass (suite) | + +## Change Log + +- v1 — planning; Q1 orphan sets needs, Q2 no reverse hard-fail, Q3 no + special-case names. +- v1 — approved (user, 2026-09-23); Steps 1–2 done; suite green. + +## Follow-ups + +- Housekeeping commit `5eba691` is on local main only until next push. +- If live tree reports the known historical achieved orphan after merge: + human decides tombstone vs extra Todo line (data, not code). + +## Version History + +- v1 — planning. +- v1 — approved (user, 2026-09-23); Steps 1–2 done; suite green. diff --git a/TaskFlowDocs/2026-09-23-orphan-task-dirs/prd.md b/TaskFlowDocs/2026-09-23-orphan-task-dirs/prd.md new file mode 100644 index 0000000..ffc259f --- /dev/null +++ b/TaskFlowDocs/2026-09-23-orphan-task-dirs/prd.md @@ -0,0 +1,127 @@ +# Detect a task directory that no Todo entry references + +> Task version: v1 +> Status: in_progress + +## Goal + +`hooks/repository-check` reports every task directory (active and +`achieved/`) whose path no `- Task:` line in `todo.md` names — a lost Todo +record must not leave orphaned documents invisible. Report only; the hook +never moves or deletes files. + +## Background / Confirmed Facts + +Measured on `main` = `5eba691`, 2026-09-23, worktree +`.worktrees/feat-orphan-task-dirs`. Nothing inferred. + +**Incident (Notes, 2026-09-16):** while staging PR #28 for one task, Todo +entries for B/C/D were stripped; `git checkout -- TaskFlowDocs/todo.md` +then restored the file, but for a window the three directories had **no +Todo reference** (workspace only; restored same day). Historical example +still true on this tree: `TaskFlowDocs/achieved/2026-09-10-repository-document-placement/` +— never had a Todo entry in any commit. + +**Direction (Notes):** only **directory → Todo**. Reverse (Todo → missing +directory) was measured empty on 2026-09-16; not part of this check unless +it costs nothing extra (see Open Questions). + +**`ab381b` (done)** fixed isolation-before-documents and taught +`repository-check` to list **uncommitted** task artifacts. Those lines never +set `needs`. They do **not** answer “directory exists, Todo forgot it” — +committed and archived orphans are invisible to that loop today +(`TaskFlowDocs/*/` only, and only when not in `HEAD`). + +**`repository-check` shape (hooks/repository-check):** read-only; artifact +section prints and leaves `needs` alone; final `STATUS: pass` (0) or +`needs-user-input` (2). Existing smoke sections: general status + uncommitted +artifacts without changing verdict. + +**Todo `Task:` values that *do* reference a directory** (backticked): + +- `TaskFlowDocs//` +- `TaskFlowDocs/achieved//` + +Non-references: `Not promoted.`, `None — …`, template placeholders. + +**Task directory =** has `prd.md` or `plan.md`. That already skips +`repository-docs/`, `todo.md`, and `achieved/` itself. Must also scan +`TaskFlowDocs/achieved/*/` — current loop does not. + +**Survey on `5eba691`:** with that rule, **0 active + 0 achieved orphans** +among dirs that have core docs *except* the known historical achieved dir +if its Task: is still missing — counted in implementation smoke fixtures, +not by mutating real history here. (Fixture-driven; live tree may report +the known achieved orphan until a human adds a tombstone or accepts the +line.) + +## Requirements + +- **R1.** In `hooks/repository-check`, after the uncommitted-artifacts + section (or adjacent), report **orphan task directories**: every + `TaskFlowDocs//` and `TaskFlowDocs/achieved//` that has `prd.md` + or `plan.md`, for which no `- Task:` line in `TaskFlowDocs/todo.md` equals + that path with or without a trailing backtick slash. +- **R2.** Read-only. Never delete, move, or rewrite Todo/dirs. +- **R3.** Orphan findings **set `needs=1`** (STATUS `needs-user-input`, + exit 2) — a lost Todo record is actionable integrity, unlike the + informational uncommitted-artifact lines which stay `needs`-neutral. +- **R4.** Empty orphan set → no behavior change vs today for that condition + (does not by itself fail a clean repo that already passes). +- **R5.** Output names each path once under a clear heading (e.g. + `Orphan task directories:`) with one path per line. +- **R6.** Smoke: (a) fixture with an unreferenced active dir → report lists + it, status 2; (b) same dir referenced by `- Task:` → not listed; (c) + achieved/ orphan listed; (d) clean fixture with only referenced dirs → + no orphan section / still pass when nothing else is wrong; (e) existing + repository-check sections still green. +- **R7.** `bash hooks/smoke-test` → `ALL SMOKE PASSED`. + +## Acceptance Criteria + +- **A.** Fixture `TaskFlowDocs/2026-01-01-lost/{prd,plan}.md` + Todo with no + matching `Task:` → line contains that path; exit 2. +- **B.** Add `- Task: \`TaskFlowDocs/2026-01-01-lost/\`` → path gone from + orphan list. +- **C.** `TaskFlowDocs/achieved/2026-01-01-gone/` with core docs, no Task: + → listed. +- **D.** Uncommitted-artifact fixture still reports staged/untracked and + does not require an orphan to exercise that path. +- **E.** Suite green; `bash -n hooks/repository-check`. + +## In Scope + +- `hooks/repository-check` orphan report. +- `hooks/smoke-test`. + +## Out of Scope + +- Auto-delete or auto-repair orphans. +- Reverse check (Todo → missing dir) as a hard requirement (optional + freebie only if free; see Q2). +- New hook binary. +- Changing uncommitted-artifact `needs` neutrality. +- Rewriting historical achieved records to invent Todo entries. + +## Risks / Deferred Items + +- Known historical achieved orphan may turn real repos to exit 2 until a + human records a tombstone/entry — **mitigate:** report is accurate; + fix is data, not code. Note in Change Log if live survey trips. +- `Task:` written without backticks would be missed — current repo uses + backticks for real paths; match backticked form only (R1) unless a + non-backticked path appears (survey: none for real dirs). + +## Open Questions + +1. Orphan sets `needs` — **Recommend: yes (R3)**; distinct from uncommitted + artifacts. +2. Also assert Todo → dir exists — **Recommend: no hard fail this PR**; + Notes said measured empty; add only if a free one-liner in the same + loop, still report-only. Default: skip to keep the diff small. +3. Live known achieved orphan — **Recommend: report it**; do not special- + case names. + +## Version History + +- v1 — planning. diff --git a/TaskFlowDocs/todo.md b/TaskFlowDocs/todo.md index ff6eeae..613ec75 100644 --- a/TaskFlowDocs/todo.md +++ b/TaskFlowDocs/todo.md @@ -586,15 +586,15 @@ Every direct request or imported requireme ## Detect a task directory that no Todo entry references - ID: TF-20260916-a357ee -- Status: inbox +- Status: in_progress - Priority: normal - Owner: Codex - Source: user review - Added: 2026-09-16 -- Updated: 2026-09-16 +- Updated: 2026-09-23 - Goal: Detect a task directory that no Todo entry references, so a lost Todo record cannot leave orphaned task documents behind. -- Task: Not promoted. -- Next action: Clarify whether this belongs in the existing `hooks/todo-check` or is a separate check; then promote. +- Task: `TaskFlowDocs/2026-09-23-orphan-task-dirs/` +- Next action: Complete PRD / Spec / Plan and request approval. - Notes: 发现于 2026-09-16 —— 提交 PR #28 时为让提交只含任务 A 而摘掉 B/C/D 的 Todo 条目,随后一次 `git checkout -- TaskFlowDocs/todo.md` 把工作区文件整体回退,三条条目一度丢失(同日已按原样补回),而三个任务目录始终在 `TaskFlowDocs/` 下,形成「目录存在、无 Todo 指向」的孤儿态(仅工作区,未进入任何提交)。同一形态在历史里也有一例:`TaskFlowDocs/achieved/2026-09-10-repository-document-placement/`,全部历史中从未有过对应的 Todo 条目。目前该缺口无自动检查兜住。注意:只查「任务目录 → Todo」这一个方向;反方向(Todo 条目指向不存在的目录)本轮已实测为空。2026-09-16 追问:本条与 `TF-20260916-ab381b` 是同一根因的两个方向——孤儿目录是「文档先于隔离产生」的结果,`ab381b` 修根因并在 `hooks/repository-check` 里报告错位产物;本条仍是更广的历史审计(含已提交、已归档的目录),是否仍要独立成检查待定。 ## Publish TaskFlow v1.0.6 with the CodeBuddy host, the Todo merge-drop audit, and the release version check diff --git a/hooks/repository-check b/hooks/repository-check index 9e143dc..0a8dab5 100755 --- a/hooks/repository-check +++ b/hooks/repository-check @@ -90,6 +90,35 @@ else echo " branch or moving the work to its own working tree is what removes the report." fi +# A task directory (active or achieved) that no Todo `- Task:` line names is a +# lost Todo record beside live documents — actionable integrity, unlike the +# informational uncommitted-artifact lines above. Report only: never delete. +# Sets `needs`. +orphans="" +if [ -d TaskFlowDocs ]; then + refs="" + if [ -f TaskFlowDocs/todo.md ]; then + refs="$(sed -n 's/^- Task: `\([^`]*\)`.*/\1/p' TaskFlowDocs/todo.md | sed 's|/*$||')" + fi + for dir in TaskFlowDocs/*/ TaskFlowDocs/achieved/*/; do + [ -d "$dir" ] || continue + [ -f "$dir/prd.md" ] || [ -f "$dir/plan.md" ] || continue + name="${dir%/}" + if [ -z "$refs" ] || ! printf '%s\n' "$refs" | grep -qxF "$name"; then + orphans="$orphans- $name +" + needs=1 + fi + done +fi +if [ -z "$orphans" ]; then + echo "Orphan task directories: none" +else + printf 'Orphan task directories:\n%s' "$orphans" + echo " A directory with prd.md or plan.md that no Todo - Task: line names." + echo " Restore or recreate the Todo entry, or archive/remove the directory deliberately." +fi + if [ "$needs" -eq 1 ]; then echo "STATUS: needs-user-input" exit 2 diff --git a/hooks/smoke-test b/hooks/smoke-test index 9477579..b3a992d 100755 --- a/hooks/smoke-test +++ b/hooks/smoke-test @@ -1117,6 +1117,10 @@ echo "$art_out" | grep -q 'Task artifacts: none uncommitted' \ mkdir -p "$art_root/TaskFlowDocs/2026-09-01-misplaced" printf '# PRD\n' > "$art_root/TaskFlowDocs/2026-09-01-misplaced/prd.md" +# Referenced by Todo so this section stays about uncommitted state only; +# orphan detection is exercised in its own section below. +printf '# Todo\n## Fixture\n- ID: T-20260901-01\n- Task: `TaskFlowDocs/2026-09-01-misplaced/`\n' \ + > "$art_root/TaskFlowDocs/todo.md" set +e art_out=$(bash "$HERE/repository-check" "$art_root" 2>&1) art_status=$? @@ -1126,6 +1130,8 @@ set -e echo "$art_out" | grep -q 'STATUS: pass' || { echo "FAIL the task-artifact report changed the status"; cat "$art_out"; exit 1; } echo "$art_out" | grep -q -- '- TaskFlowDocs/2026-09-01-misplaced/ (untracked)' \ || { echo "FAIL misplaced task artifact not named"; cat "$art_out"; exit 1; } +echo "$art_out" | grep -q 'Orphan task directories: none' \ + || { echo "FAIL referenced artifact reported as orphan"; cat "$art_out"; exit 1; } # A directory with no commit cannot be attributed to a branch, and the output must # say what it can and cannot claim instead of letting the reader assume. echo "$art_out" | grep -q 'follows you to whatever branch you check out' \ @@ -1153,6 +1159,65 @@ echo "$art_out" | grep -q '2026-09-01-misplaced' \ && { echo "FAIL committed task artifact named in the report"; cat "$art_out"; exit 1; } echo ok +echo "== repository-check reports orphan task directories ==" +orphan_root="$tmp/repository-check-orphans" +mkdir -p "$orphan_root/TaskFlowDocs/repository-docs" +git -C "$orphan_root" init -q +git -C "$orphan_root" config user.email smoke@example.invalid +git -C "$orphan_root" config user.name smoke +printf '# Catalog\n' > "$orphan_root/TaskFlowDocs/repository-docs/index.md" +printf 'CONTRIBUTING\n' > "$orphan_root/CONTRIBUTING.md" +printf 'CODE_STYLE\n' > "$orphan_root/CODE_STYLE.md" +printf '# Todo\n' > "$orphan_root/TaskFlowDocs/todo.md" +git -C "$orphan_root" add -A && git -C "$orphan_root" commit -qm base +git -C "$orphan_root" remote add origin "$orphan_root" +git -C "$orphan_root" update-ref refs/remotes/origin/main HEAD +git -C "$orphan_root" branch -q -u origin/main +# Unreferenced active dir → listed, exit 2 +mkdir -p "$orphan_root/TaskFlowDocs/2026-01-01-lost" +printf '# PRD\n> Task version: v1\n' > "$orphan_root/TaskFlowDocs/2026-01-01-lost/prd.md" +printf '# Plan\n> Task version: v1\n' > "$orphan_root/TaskFlowDocs/2026-01-01-lost/plan.md" +set +e +orphan_out=$(bash "$HERE/repository-check" "$orphan_root" 2>&1) +orphan_status=$? +set -e +[ "$orphan_status" -eq 2 ] || { echo "FAIL orphan did not set needs: $orphan_status"; printf '%s\n' "$orphan_out"; exit 1; } +echo "$orphan_out" | grep -q 'STATUS: needs-user-input' || { echo "FAIL orphan status"; printf '%s\n' "$orphan_out"; exit 1; } +# Orphan lines have no trailing slash; the uncommitted-artifact line does. +echo "$orphan_out" | grep -q -- '^- TaskFlowDocs/2026-01-01-lost$' \ + || { echo "FAIL active orphan not named"; printf '%s\n' "$orphan_out"; exit 1; } +# Achieved orphan is listed too +mkdir -p "$orphan_root/TaskFlowDocs/achieved/2026-01-01-gone" +printf '# PRD\n> Task version: v1\n' > "$orphan_root/TaskFlowDocs/achieved/2026-01-01-gone/prd.md" +set +e +orphan_out=$(bash "$HERE/repository-check" "$orphan_root" 2>&1) +set -e +echo "$orphan_out" | grep -q -- '^- TaskFlowDocs/achieved/2026-01-01-gone$' \ + || { echo "FAIL achieved orphan not named"; printf '%s\n' "$orphan_out"; exit 1; } +# Referencing the active path clears it from the orphan list +printf '# Todo\n## Fixture\n- ID: T-20260101-01\n- Task: `TaskFlowDocs/2026-01-01-lost/`\n' \ + > "$orphan_root/TaskFlowDocs/todo.md" +set +e +orphan_out=$(bash "$HERE/repository-check" "$orphan_root" 2>&1) +orphan_status=$? +set -e +echo "$orphan_out" | grep -q -- '^- TaskFlowDocs/2026-01-01-lost$' \ + && { echo "FAIL referenced active dir still orphan"; printf '%s\n' "$orphan_out"; exit 1; } +echo "$orphan_out" | grep -q -- '^- TaskFlowDocs/achieved/2026-01-01-gone$' \ + || { echo "FAIL achieved orphan dropped early"; printf '%s\n' "$orphan_out"; exit 1; } +[ "$orphan_status" -eq 2 ] || { echo "FAIL remaining achieved orphan should keep needs"; printf '%s\n' "$orphan_out"; exit 1; } +# Clear achieved too → pass +printf '# Todo\n## Fixture\n- ID: T-20260101-01\n- Task: `TaskFlowDocs/2026-01-01-lost/`\n- Task: `TaskFlowDocs/achieved/2026-01-01-gone/`\n' \ + > "$orphan_root/TaskFlowDocs/todo.md" +set +e +orphan_out=$(bash "$HERE/repository-check" "$orphan_root" 2>&1) +orphan_status=$? +set -e +[ "$orphan_status" -eq 0 ] || { echo "FAIL clean-orphan fixture not pass: $orphan_status"; printf '%s\n' "$orphan_out"; exit 1; } +echo "$orphan_out" | grep -q 'Orphan task directories: none' \ + || { echo "FAIL empty orphan set should report none"; printf '%s\n' "$orphan_out"; exit 1; } +echo ok + echo "== todo merge driver keeps both branches' entries ==" # A real repository, so the assertion exercises `git merge` rather than a # simulation of it. The driver is installed the way a user gets it, by the hook.