Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ Use `git grep -c "" HEAD -- <path> [<path>...]` to obtain exact per-file line co

**Why:** The Read tool renders one extra numbered row after the final closing brace (the empty string produced by splitting on the trailing newline). Reading that row as content overcounts by one. On the #821 preflight the plan, and the orchestrator's own "authoritative, re-derived twice" fact list, both asserted `UtilitiesCS/Threading/ProgressViewer.cs` at 93 lines; `git grep -c ""` returned 92 while returning the plan's exact figure for the other seven files in the same call. That single stale digit sat inside a Phase 0 exact-count gate whose task text said "a disagreement means the tree moved; stop and report", so it would have hard-stopped execution at the fourteenth task.

**The phantom row is not consistent (#952 round 2, 2026-10-02):** in one session the Read tool showed a trailing empty row for `run-actionlint.ps1` (row 15 of 14) and `ci.yml` (row 39 of 38) but none for `dependabot-repair.yml` (173 rows, file confirmed CRLF-terminated by a multiline Grep for `\r\n\z`). So a plan rule "subtract the phantom row" is also wrong; only `git grep -c ""` is reliable.

**How to apply:** Whenever a plan pins exact line counts, or a file-size budget lands a file at or near the 500-line ceiling, re-derive every count in one `git grep -c ""` call rather than trusting the plan, the caller's fact list, or a visual read of the last line number. Compare all files at once — a list where seven of eight agree is the shape this defect takes, and the one that disagrees is the load-bearing one. See [[project_caller_stated_preflight_count_drifts_before_execution]] and [[project_preflight_gate_literal_extract_from_plan_not_retype]].
4 changes: 4 additions & 0 deletions .claude/agent-memory/atomic-planner/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
## Preflight revision seams (per issue; newest first)

- [#956 R1](project_956_r1_spec_self_hit_and_raw_doc_name_seams.md) — spec-wording counts self-hit the AC line; `cobertura` substring matches tracked agent-memory .md (anchor `cobertura[^/]*\.xml$`); orchestrator-amended AC needs a P0 literal check
- [#952 R3](project_952_r3_shared_origin_main_ancestry_gate_seam.md) — origin/main is one ref shared by all worktrees: a no-fetch plan still needs a P0 `merge-base == BASE-SHA` gate (`BASE AHEAD OF BRANCH`) beside the late `BASE REF MOVED`; "never fetches so the ref does not move" is false prose
- [#952 R2](project_952_r2_label_counts_helper_enumeration_and_hex_backslash_seams.md) — recount every `all N required` label (observations are not conditions); a Pester helper enumerating a directory is a read site for every file in it; keyed ExpectedExitCode must state its OMITTED branch; backslash in a plan Grep as `\x5C` with `(^|[^A-Za-z])` so `https://` is excluded; line counts via Grep `^` count never Read rows
- [#952 R1](project_952_r1_read_phantom_row_and_frozen_clause_a_seams.md) — Read tool trailing empty row is INCONSISTENT across files (corrected R2; count with Grep `^`, 173 not 174); freeze footprint Clause A from P0 (`COMMITTED` token) or a mid-run parent commit fails the positive control; pair PoshQC MCP test (ok-only payload) with CMD-PESTER; ci.yml has workflow_dispatch so AC5 limit is "head not pushed"
- [#952 R0](project_952_runbook_and_workflow_comment_plan_seams.md) — `.yml` edits are pre-impl-gated; re-count caller line lengths (CRLF +1); removed Markdown bullet diffs as `-- `, exclude only `--- `; actionlint binary tracked, silent on success; Pester static tests read both files

- [#945 R0](project_945_sortemail_trysave_directory_seam_plan_seams.md) — whitespace-stripped token census; compile-red fail-before (runtime-red would create a real dir); uncommitted-fix control via backup copy; no-commit plan, two-dot MERGE-BASE gates
- [#839](project_839_createcancellationtoken_init_plan_seams.md) — lone dead-comment deletion not CSharpier-stable; post-commit porcelain must admit the plan's own check-off; never record a porcelain COUNT; Phase 0 diff sentences prospective; exempt commit form `-m ... -- path`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
name: project_952_r1_read_phantom_row_and_frozen_clause_a_seams
description: "#952 R1 preflight seams - the Read tool shows one empty numbered row after a final line ending (file line count = last non-empty row, never the phantom row); a footprint's Clause A committed list must be frozen from the P0 baseline, not recomputed, or a mid-run parent commit removes the production files; pair the PoshQC MCP test tool (ok-only payload, two-file scan_folders observed) with a direct Pester run for counts; a CI workflow_dispatch trigger changes an AC-deferral rationale from 'PR stage' to 'head not pushed'"
metadata:
type: project
---

Round-1 delta for #952 (six defects, all applied in place; 28 tasks / 3 phases unchanged).

- **Read tool phantom row (CORRECTED in R2).** The Read tool SOMETIMES renders one extra empty numbered row after a final line terminator, and not consistently: in R2 the executor saw it for run-actionlint.ps1 and ci.yml but not dependabot-repair.yml, while the planner's own Read of dependabot-repair.yml did show row 174. R0 recorded the workflow as 174 lines; `git grep -c ""` and the orchestrator both gave 173. Rule: never derive a line count from Read-tool row numbers; measure it with Grep pattern `^` in count mode (equals `git grep -c ""`), and word any Read-tool caveat as "a trailing empty row, when shown, is not a line; when none is shown nothing is subtracted". Cross-check with the CRLF count already in the plan.
- **Freeze the footprint's committed list at baseline.** CMD-FOOTPRINT recomputed `git diff --name-only origin/main...HEAD` at the final task, so any commit made during the run (a parent hold commit) would move RUNBOOK/WORKFLOW into the subtracted set and fail the positive control. Fix: substitute the P0-T3 `COMMITTED-ON-BRANCH:` list as a token (`COMMITTED`), print the live list as `COMMITTED-NOW:` for the record only, and let the "status ` M`" acceptance admit the file appearing in `COMMITTED-NOW:` instead. Then D-9's "valid in either state" claim is actually true.
- **PoshQC MCP test tool pairing.** `mcp__drm-copilot__run_poshqc_test` returns `{"ok":true,"tool":...,"workspace_root":...,"summary":"... N selected scan folder(s)."}` and no counts; two-file `scan_folders` is an observed success case (457 archive qa-gates/poshqc-test.iter2.2026-08-11T01-46.md). It writes `artifacts/pester/pester-junit.xml` (ignored, .gitignore:57), so it stays out of footprint gates. Record `POSHQC-TEST-OK:` true/false (baseline-relative) and keep CMD-PESTER for PASSED/FAILED/TOTAL; `EXIT_CODE:` scoped to the Pester invocation.
- **AC deferral rationale must name the real limit.** ci.yml:8 declares `workflow_dispatch:` and feature-review-workflow SKILL.md:74 accepts a dispatch run, so "exists only at the PR/CI stage" was wrong; the true limit is that the branch head is never pushed (and `git push` had to be added to D-9's prohibited list). Add a read-only `CI-DISPATCH-TRIGGER:` field so the disposition cites the tree.
- **Repair-rule test sets must be enumerated by read site.** "the two tests that read WORKFLOW or RUNBOOK" undercounted: DependabotConfig.Tests.ps1 has 8 `It` blocks (9 read lines) reading `$script:RepairWorkflowPath`; list the line numbers.
- Grep `17[45]` after a line-count fix: post-edit counts legitimately shift (174 = 173 + 1), so verify each hit's role rather than zeroing the pattern.

**Why:** each was a preflight finding on an otherwise clean R0; the first two would have produced a spurious stop or a vacuous gate at execution.
**How to apply:** any plan that records Read-tool line counts, any footprint/scope gate on a branch a parent may commit to mid-run, any PowerShell test stage (pair MCP + direct Pester), any CI-deferred AC. Related: [[project_952_runbook_and_workflow_comment_plan_seams]], [[empty-porcelain-clause-is-unsatisfiable]], [[diff-gates-need-a-commit-task]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
name: project_952_r2_label_counts_helper_enumeration_and_hex_backslash_seams
description: "#952 R2 preflight seams - recount every 'all N required' label against its list (observations are not conditions); a Pester helper that enumerates a directory (Get-ChildItem -Filter *.yml) is a read site for every file in it, so list the It blocks that call it, not only the by-path reads; a keyed ExpectedExitCode must say when it is OMITTED so the default-0 row records the failure; write a backslash in a plan Grep pattern as \\x5C with a leading (^|[^A-Za-z]) class so https:// is excluded; measure line counts with Grep ^ count, never Read rows"
metadata:
type: project
---

Round-2 delta for #952 (five defects plus one advisory, all applied in place; 28 tasks / 3 phases unchanged). Round 1 had been clean on structure; every R2 finding was a figure or a label.

- **"all N required" labels drift.** P0-T6 said "all five required" over four conditions because `CRLF=`/`LF=` were declared observations in the same sentence. Rule: after any edit to an acceptance sentence, recount the enumerated conditions and sweep every `all (two|three|four|five|six) required` label in the plan; an observation ("recorded") is a condition only if the label's own list counts it (P1-T6 does, P0-T6 does not).
- **Helper enumeration is a read site.** DependabotConfig.Tests.ps1 `Get-SetupNuGetStep` (line 116) does `Get-ChildItem -Filter '*.yml'` at 132 and `ReadAllLines` at 133 over every workflow, so the three `It` blocks that call it (268, 279, 323) read dependabot-repair.yml without naming `$script:RepairWorkflowPath`. A "tests that read FILE" list built by grepping the path variable undercounts; also grep for helper functions and follow their callers.
- **Line counts.** Read-tool trailing-row behaviour is inconsistent (see the R1 memory, corrected). run-actionlint.ps1 is 14 lines (Grep `^` count), not 15; its internal citations (throw 8, `&` 11, `exit` 13) were right, only the total was wrong, so a wrong total does not imply wrong internal lines and vice versa; check both.
- **Keyed ExpectedExitCode must state its omission branch.** "1 when failures are all baseline, 0 when none" left the newly-failing case undefined. Add: "omitted when `NEWLY-FAILING:` is not `NONE`, so the expectation defaults to 0 and the row records the failure, and the task proceeds under the repair or stop rule". This is consistent with the skill's default-0 rule and with "always equals the observed value it explains" (an omitted field explains nothing).
- **Backslash in a plan-authored Grep pattern.** `[\\/]` can be collapsed to `[\/]` by a tool layer. Write `(/|\x5C)` (Rust regex hex escape, verified to run through the Grep tool). The bare `[A-Za-z]:(/|\x5C)` matches `https://` (`s:/`); prefix `(^|[^A-Za-z])` and confirm empirically by running the pattern over the plan and issue.md, both of which carry an `https://` and returned zero hits.
- Ripgrep probe `^artifacts/$` on .gitignore returned nothing although line 57 is `artifacts/` (CRLF file); use an unanchored probe or `\r?$` before concluding a citation is wrong.

**Why:** each was a preflight finding on a plan whose structure had already cleared; the label miscount and the omission branch would have been a spurious executor stop, the helper undercount a mis-scoped repair rule.
**How to apply:** every revision round: recount labels, re-measure totals with Grep `^` count, follow Pester helpers to their callers, and run any new regex over the plan itself. Related: [[project_952_r1_read_phantom_row_and_frozen_clause_a_seams]], [[project_952_runbook_and_workflow_comment_plan_seams]], [[verify-line-spans-and-computed-literals]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
name: project-952-r3-shared-origin-main-ancestry-gate-seam
description: Issue 952 preflight round 3 seam - a no-fetch plan anchored to origin/main still needs a P0 merge-base ancestry gate because the ref is shared by every worktree; also the sibling header prose "the ref does not move because this plan never fetches" is false in a multi-worktree repo
metadata:
type: project
---

Round 3 of #952 found one blocking defect: P0 recorded `BASE-SHA` (origin/main) and `HEAD-SHA` but never checked that origin/main is an ancestor of HEAD.

**Why:** `refs/remotes/origin/main` is one ref shared by every worktree of the repo. A fetch in any other session moves it, and a plan that runs later than it was authored can therefore start with origin/main already AHEAD of the branch merge-base. Every two-dot gate (`git diff origin/main -- path` and the whole-tree `git diff --name-only origin/main`) then lists main's post-branch-point changes as if the run made them. Neither the frozen `COMMITTED` list (three-dot `origin/main...HEAD`, which is empty in that direction) nor `INHERITED` (baseline porcelain) subtracts them, so the footprint `OUTSIDE-COUNT` ends greater than 0 with no stop label, and a no-commit / no-merge / no-rebase plan has no repair path. At authoring time HEAD equalled origin/main, so the defect was latent.

**How to apply:**
- In any plan anchored to `origin/main` that does not itself fetch, add `MERGE-BASE=$(git merge-base origin/main HEAD)` to the P0 base-ref payload and gate `MERGE-BASE equals BASE-SHA` with a named stop label (`BASE AHEAD OF BRANCH`), telling the operator to bring the branch up to origin/main OUTSIDE the plan and restart at P0-T1. Pair it with the existing late-phase `BASE REF MOVED` (re-read `git rev-parse origin/main` and compare to the P0 value): the P0 gate covers "already ahead at start", the late gate covers "moved during the run".
- Do not write "this plan never runs `git fetch`, so the ref does not move during the run" in the header: the first clause is true and the conclusion is false in a multi-worktree checkout. State the two stop labels instead. This sibling sentence survived two preflight rounds as "clean" because no round had yet named the shared-ref mechanism.
- Never assert "HEAD equals origin/main" as a plan invariant; it is a preparation-run observation that the executor re-derives.
- The merge-base payload segment is a plain double-quoted string with a `$( )` subexpression: no pipe, no apostrophe, no nested empty string, so it fits the single-quoted `-Command` channel unchanged.

Related: [[project-952-r2-label-counts-helper-enumeration-and-hex-backslash-seams]], [[project-952-r1-read-phantom-row-and-frozen-clause-a-seams]], [[never-pin-head-sha-as-plan-expectation]], [[harness-git-status-may-describe-another-worktree]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: project_952_runbook_and_workflow_comment_plan_seams
description: "#952 R0 minimal-audit plan seams (Markdown runbook line + YAML workflow comment rewrap) - .yml edits are pre-implementation-gated, the prompt's line-length figure was off by 5 (CRLF), a removed Markdown bullet shows as `-- ` in a diff so exclude only `--- `, actionlint binary is tracked and prints nothing on success, Pester static tests read both files"
metadata:
type: project
---

Plan for #952 (runbook line 301 `App ID` to `Client ID`; dependabot-repair.yml header comment rewrapped to 100 columns). 28 tasks, 3 phases, no commits, no C# or coverage tasks.

- **`.yml` edits are implementation to the pre-implementation gate.** `.claude/hooks/enforce-orchestration-preimplementation-gate.ps1:123` lists `yml|yaml` (and `json`), and `:141` classifies `pwsh ... Invoke-Pester` / `tests/scripts/` commands. A comment-only workflow edit therefore needs the ready checkpoint; `.md` edits do not. Read the checkpoint read-only in Phase 0 and stop if not ready.
- **Re-count a caller-supplied line length.** The prompt said line 14 was 150 characters; a hand count gave 145 and a ripgrep probe matched `^.{146}$` because the file is CRLF (ripgrep's `.` counts the CR; `ReadAllLines` strips it). Probe exact lengths with `^.{N}$` and check `\r$` counts before writing a baseline figure; gate the baseline only on "greater than the limit" and record the measured value.
- **A removed Markdown bullet appears as `-- text` in a git diff.** Excluding diff headers with `StartsWith("--- ")` (three dashes plus space) keeps the `-- Private key...` body line; a two-dash exclusion would drop it and make the "exactly one removed line" gate vacuous.
- **Line 13 of the old block equals line 1 of the new block**, so git reports 3 removed / 4 added, not 4 / 5. Gate net growth (`ADDED - REMOVED = 1`, `CHANGED = ADDED + REMOVED`) and record the exact split as expected-not-gated.
- **actionlint:** `actionlint-bin/actionlint.exe` is tracked (no `.gitignore` rule; only `.paket/paket.exe` is ignored at line 300) and present in a fresh worktree; `scripts/dev-tools/run-actionlint.ps1` throws `actionlint executable not found` when absent, runs the binary with no args, prints nothing and exits 0 on success (recorded in #927 p5-t4 and #929 p0-t19 evidence). Run it as a child `pwsh -File` by absolute path and read `$LASTEXITCODE`; gate exit 0 AND output-line count 0.
- **Pester static tests read both target files:** `tests/scripts/dependencies/DependabotConfig.Tests.ps1` (17 `It`, reads the workflow at 296-420) and `RepositoryTreeConsistency.Tests.ps1` (4 `It`, reads workflow and runbook). They assert step inputs and Part D text, never the header comment, but they are the test stage for the touched files; baseline + final run with `NEWLY-FAILING: NONE`.
- **Apostrophes in asserted text:** the comment paragraph contains `App's`; a single-quoted `pwsh -Command` payload cannot carry it, so build the expected string with `+ [char]39 +`.
- **Post-edit line numbers shift.** A five-for-four replacement moves every later line by one; any later task that reads the file by line number (the AC5 disposition read of the `on:` and `if:` blocks) must cite post-edit numbers and say so.
- The feature-review rule `modified-workflow-needs-green-run` (`.claude/skills/feature-review-workflow/SKILL.md:68-74`) cannot be met locally for a `workflow_run`-only workflow with a `dependabot/` branch filter and no `workflow_dispatch`; record a disposition under `evidence/other/`, leave the AC unchecked, list it under Items remaining.

**Why:** each seam would have produced a vacuous gate, a spurious stop, or a preflight finding.
**How to apply:** reuse for any docs-plus-workflow-comment item. Related: [[project_945_sortemail_trysave_directory_seam_plan_seams]], [[empty-porcelain-clause-is-unsatisfiable]], [[pwsh-command-quoting-in-plan-tasks]], [[validate-planner-output-hook-line-anchored-gotchas]].
7 changes: 4 additions & 3 deletions .github/workflows/dependabot-repair.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ name: dependabot-repair
# restricts it by default from 2026-11-02.
#
# Credential: a GitHub App installation token minted from the App's Client ID, stored in
# DEPENDABOT_REPAIR_APP_ID, and the private key in DEPENDABOT_REPAIR_APP_PRIVATE_KEY. When those secrets are absent the token step fails, the job
# stops before it can push, and the pull request keeps the behaviour it has today. See
# .github/workflows/README.md for the degraded mode and the installation runbook.
# DEPENDABOT_REPAIR_APP_ID, and the private key in DEPENDABOT_REPAIR_APP_PRIVATE_KEY. When those
# secrets are absent the token step fails, the job stops before it can push, and the pull request
# keeps the behaviour it has today. See .github/workflows/README.md for the degraded mode and the
# installation runbook.

on:
workflow_run:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ two-axis-model-selection specification's Out of Scope section and is not resolve
- App registration UI navigation (steps 1–9). GitHub Docs — "Registering a GitHub App."
https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app
— captured 2026-09-19.
- Private key generation and App ID location (steps 10–12), and the private-key security guidance in
- Private key generation and Client ID location (steps 10–12), and the private-key security guidance in
the Security Note. GitHub Docs — "Managing private keys for GitHub Apps."
https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps
— captured 2026-09-19.
Expand Down
Loading
Loading