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
39 changes: 39 additions & 0 deletions .claude/agent-memory/atomic-executor/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,42 @@
- [Contingency fallback orphans downstream paths](project_contingency_fallback_orphans_downstream_hardcoded_paths.md) · [Stale-citation gate literal is per-comment](project_stale_citation_gate_literal_must_match_the_comments_legitimate_citations.md)
- [TimeoutAfter IsCompleted loses to the Task.Run race](project_timeoutafter_iscompleted_shortcircuit_loses_to_taskrun_race.md) · [One Cobertura filename, several class nodes](project_cobertura_filename_maps_to_several_class_nodes.md)
- [msbuild file logger double-counts warnings](project_msbuild_filelogger_double_counts_each_warning.md) · [Reconciliation merge already tracks the feature docs](project_orchestrator_reconciliation_merge_tracks_feature_docs.md)
- [Seam sentences outlive a removed member](project_preflight_seam_sentences_outlive_a_removed_member.md) — after a decision removes an interface member, grep the spec for "Moq double of the .* interface"/"injectable"; keyword sweeps miss them
- [Explicit Compile items decide membership, not file presence](project_explicit_compile_items_decide_membership_not_file_presence.md) — a grep hit can be uncompiled
- [`git add -N -- .` defeats a "do not stage X" invariant](project_intent_to_add_span_defeats_do_not_stage_invariant.md) — it reads as diff plumbing, so a staging audit skips it
- [Hunk-header literal slides past a blank line](project_git_hunk_header_literal_slides_past_blank_line.md) — measured; assert numstat deleted=0, never `@@ -N,0 +M,`
- [Batch-budget hook discards out-of-root .ps1 writes](project_batch_budget_hook_discards_out_of_root_powershell_writes.md) — hook roots at session worktree; plan gates reading its state become unsatisfiable
- [BOM-bearing .cs files + pre-restore numstat gates](project_bom_bearing_cs_files_and_prerestore_numstat_head_gates.md)
- [CLAUDE.md differs per worktree; read the execution copy](project_claude_md_differs_between_worktrees_read_execution_copy.md)
- [CommandLine token kill hits own bash/pwsh shells](project_commandline_match_on_results_dir_token_kills_own_tool_shells.md)
- [CLI Rebuild omits .vsto/.manifest](project_commandline_rebuild_omits_vsto_manifests_addin_cannot_load.md)
- [git show in pwsh decodes ibm437 + keeps BOM](project_conservation_gate_git_show_decodes_ibm437_and_keeps_bom.md)
- [Dot-sourced coverage helpers: StrictMode $LASTEXITCODE throws](project_coverage_helpers_dotsource_strictmode_lastexitcode_throws.md)
- [Exactly-once literal vs pattern-containment clause](project_exactly_once_literal_clause_conflicts_with_pattern_containment_clause.md)
- [Fixed run-path coverage gate blind to sibling test file](project_fixed_run_path_coverage_gate_blind_to_sibling_test_file.md)
- [Inventory clause omits inherited promotion rename](project_footprint_inventory_clause_omits_inherited_promotion_rename.md)
- [Handoff AC count is a claim; measure the AC file](project_handoff_stated_ac_count_contradicts_the_ac_source_file.md)
- [Kill build-lock waiter by PID, not script name](project_killing_a_build_lock_waiter_by_script_name_hits_every_sibling.md)
- [Malformed pwsh payload surfaces as unrelated hook block](project_malformed_pwsh_payload_surfaces_as_unrelated_hook_block.md)
- [Mandatory [string[]] rejects a blank line](project_mandatory_string_array_param_rejects_blank_line_turning_red_into_binding_error.md)
- [Merge-base diff over branch-created file = one whole-file hunk](project_mergebase_diff_over_branch_created_file_is_whole_file_hunk.md)
- [Mid-plan commit breaks deletion staging/porcelain spans](project_midplan_commit_breaks_deletion_staging_and_porcelain_spans.md)
- [Minute-resolution timestamps can't be strictly increasing](project_minute_resolution_timestamp_cannot_be_strictly_increasing.md)
- [/m "N>" prefix zeroes anchored target counts](project_msbuild_parallel_log_node_prefix_defeats_anchored_target_counts.md)
- [Nested Import-Module -Force unloads session-wide](project_nested_import_module_force_unloads_session_wide.md)
- [Parent orchestrator hold-commits your branch mid-run](project_parent_orchestrator_hold_commits_your_branch_midrun.md)
- [Pester TotalCount includes filtered NotRun](project_pester_filtered_total_counts_notrun.md)
- [Auto-property with setter guard is not expressible](project_plan_mandated_autoproperty_with_setter_guard_is_not_expressible.md)
- [PoshQC format != Invoke-Formatter defaults](project_poshqc_format_rewrites_differ_from_invoke_formatter_defaults.md)
- [PoshQC strips BOM/CRLF only on rewrite](project_poshqc_format_strips_bom_and_crlf_only_when_it_rewrites.md)
- [PS double quotes keep both backslashes](project_powershell_double_quoted_backslash_defeats_msbuild_nonvacuity_grep.md)
- [4 PSScriptAnalyzer traps in new modules](project_psscriptanalyzer_traps_in_new_powershell_modules.md)
- [Param named $args makes msbuild gate vacuous](project_pwsh_function_param_named_args_makes_msbuild_gate_vacuous.md)
- [Nested quotes in "$( )" fail to parse](project_pwsh_nested_quotes_in_subexpression_fail_to_parse.md)
- [$Log/$log case collision flattens array](project_pwsh_param_name_case_collision_flattens_log_array.md)
- [Invoke-VersionReconciliation rewrites Reference version](project_reference_version_rewrite_when_assemblyversion_omitted.md)
- [Replaced-span numstat elides identical boundary lines](project_replacement_span_numstat_elides_identical_boundary_lines.md)
- [Finding's line right, description wrong](project_review_finding_line_number_right_description_wrong.md)
- [Splatting frees lines in ceiling-bound test files](project_splatting_is_the_line_budget_lever_for_ceiling_bound_test_files.md)
- [-WhatIf does not reach module ShouldProcess](project_whatif_does_not_reach_module_session_state.md)
- [WinForms control field installs SyncContext, deadlocks await](project_winforms_control_field_installs_synccontext_and_deadlocks_await.md)
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ between the plan's baseline observation and preflight round 2. Require a **stand
("every path under `.claude/agent-memory/` is out of scope for every gate, whenever it appeared"),
not a point-in-time snapshot.

**A standing porcelain allowance is ALSO not sufficient once the memory files are COMMITTED.** On
issue #839 the agent worktree arrived with four inherited commits above the merge base, one of them
`chore(memory): ...`, so `git diff --name-only <merge-base> HEAD` listed five `.claude/agent-memory/**`
paths permanently while `git status --porcelain --untracked-files=all` printed nothing at all
(measured at preflight). A plan that keys its residue set off the Phase 0 *porcelain* snapshot
therefore builds an EMPTY residue set and its anchored name-listing footprint gate is unsatisfiable
from the first task onward. Capture BOTH snapshots in Phase 0 — the anchored
`git diff --name-only <base> HEAD` set and the porcelain set — and write the standing allowance
against the union.

Related: [[project_preflight_selfderived_gate_thresholds_are_blind]],
[[project_418_plan_rationale_clauses_are_evidence]],
[[project_preflight_ac_checkoff_and_tooloutput_paths]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: batch-budget-hook-discards-out-of-root-powershell-writes
description: enforce-powershell-batch-budget.ps1 roots itself at the SESSION worktree, so Write/Edit of a .ps1 into a different execution worktree is discarded - no slot consumed, no state file written - making any plan gate that reads prodFiles/testFiles unsatisfiable
metadata:
type: project
---

`.claude/hooks/enforce-powershell-batch-budget.ps1` computes
`$Root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent`, and `.claude/settings.json`
registers it with the **relative** command
`pwsh -NoProfile -File .claude/hooks/enforce-powershell-batch-budget.ps1`. The relative path
resolves against the Claude Code project directory, i.e. the **session** worktree. So `$Root` is the
session worktree even when all the work happens in a different execution worktree.

`Invoke-PowerShellBatchBudgetDecision` lines 277-282 then **discard** an out-of-root candidate
rather than denying it: decision `allow`, no slot consumed, `shouldWriteState = $false`. The
containment test (lines 82-92) admits any *relative* path but requires an absolute path to equal or
be prefixed by `$Root`, and the `Write` tool always supplies an absolute path.

**Why:** a plan that splits PowerShell work into batches and then asserts batch membership by
reading the `prodFiles` / `testFiles` arrays out of
`.claude/state/powershell-batch-budget.<session-id>.json` gets empty arrays, or no state file at
all, when the files were written into a non-session worktree. The assertion then cannot fail — the
absence-shaped defect. Plan 911 revision 7 built its P2-T9 / P4-T8 / P6-T7 boundary assertions on
exactly that premise, prescribing `Write`/`Edit` over heredocs as the remedy; the remedy is
insufficient because the discard happens for the location, not the tool.

Other measured details worth keeping: the session id is `$env:CLAUDE_SESSION_ID` first, then
`<Root>/.claude/state/current-session-id`, then `worktree-<leaf>-<sha8>`. The hook stores the
absolute supplied `file_path` with `\` normalised to `/`, so membership checks must compare
suffixes. `.claude/state/powershell-batch-budget.default.json` is **tracked in git** and already at
3/3 prod, but it is a different session's file and its three temp-path entries are dropped by the
containment filter on rehydration, so it is inert. Caps default to 3 prod / 3 test.

**How to apply:** before trusting any batch-budget gate, check which worktree the hook is rooted at
and whether the target files are inside it. Confirm empirically at the first PowerShell `Write`: if
no `powershell-batch-budget.<session-id>.json` appears in either worktree's `.claude/state/`, the
discard path is confirmed and the gate is inert. See
[[planner-and-executor-observe-different-worktrees]] and
[[preflight-selfderived-gate-thresholds-are-blind]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: bom-bearing-cs-files-and-prerestore-numstat-head-gates
description: Some QuickFiler .cs files carry a UTF-8 BOM (ViewerSetup, BreadcrumbBridgeRouter) so a whole-file normalisation rewrite silently adds a line-1 hunk and breaks tight numstat bounds; a mutation task's "git diff --numstat HEAD shows 0 0 after restore" clause is unsatisfiable while the same phase's earlier edit to that file is still uncommitted; a CSharpier-wrapped const alias defeats a single-line initializer literal
metadata:
type: project
---

Three Phase 4 (#792, 2026-09-17) findings that cost a re-run each.

1. **BOM state is per-file, not per-repo.** The caller's brief said "C# working copies here are CRLF, no BOM"; in fact `QuickFiler/Controllers/QfcItemController.ViewerSetup.cs` and `QuickFiler/Controllers/BreadcrumbBridgeRouter.cs` carry EF BB BF at HEAD while the other eight Phase 4 targets do not. A normalisation pass that rewrites the whole file with `UTF8Encoding($false)` strips it, producing a `-using System;` / `+using System;` hunk at line 1 and pushing numstat from 3/8 to 4/9 against a "≤4 insertions, 7 or 8 deletions" bound. The Edit tool preserves CRLF and the BOM on its own, so do not normalise at all after Edit-based changes; only Write-created files need a CRLF pass, and check `[System.IO.File]::ReadAllBytes(path)[0] -eq 0xEF` per file before touching encoding.

2. **Restore-proof gates anchored to HEAD are dead before the phase commit.** [P4-T4] asked for `git diff --numstat HEAD -- <site3 file>` to read `0 0` after a temporary mutation was reverted, but HEAD was the Phase 3 commit and [P4-T3]'s rewrite of the same file was still uncommitted, so the command necessarily printed `18 32`. Prove restoration with a SHA-256 of the bytes captured immediately before the mutation plus `git diff --no-index --numstat <snapshot> <file>` (prints nothing for identical files, exit 0), and record the HEAD numstat as observed with the reason. At preflight, flag any "numstat HEAD shows 0 0" clause whose file is edited earlier in the same uncommitted phase.

3. **Const alias initializer wraps.** `internal const string IncognitoArgument = WebView2EnvironmentContract.AdditionalBrowserArguments;` is 105 columns at 8-space indent; CSharpier 1.2.6 breaks after `=`, so a `-SimpleMatch` on the whole `X = Y;` returns 0 whatever is written (`csharpier check` exits 0 on the two-line shape, so it is the formatter's own output). Verify with the two adjacent single-line halves or a `(?s)` regex over the raw text. Same class as [[csharpier-chain-wrap-defeats-singleline-search-gates]] but for a declaration, not a call chain.

Also observed: a plan clause "`catch (OperationCanceledException)` returns 1" undercounted because the moved sibling method already carried one — always take the HEAD count of a literal before asserting the post-edit count ([[project_plan_authoring_time_token_counts_are_undercounts]]).
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: claude-md-differs-between-worktrees-read-execution-copy
description: CLAUDE.md is branch-tracked and differs between worktrees, so the copy auto-loaded into context is the SESSION worktree's; a Phase 0 policy read must be taken against the execution worktree, where coverage floors and an extra evidence-format rule differ
metadata:
type: project
---

`CLAUDE.md` is a tracked, branch-varying file. The copy Claude Code auto-loads into the system
prompt comes from the **session** worktree. When the plan directs work at a different execution
worktree, that auto-loaded copy is not the governing text.

Measured 2026-09-19 (issue 911): `git hash-object CLAUDE.md` gave `0c650735e…` in the execution
worktree against `67f75c93d…` in the session worktree, while the six `.claude/rules/*.md` files
were byte-identical across both. Two differences were load-bearing:

- **Coverage floors.** The execution copy's UT2 states C# line `>= 80%` / branch `>= 75%` and
PowerShell line `>= 80%`, settled by the maintainer 2026-09-11 under issue #563. That contradicts
`.claude/rules/general-unit-test.md` and `.claude/rules/quality-tiers.md`, which both state a
uniform line floor of `>= 85%`. `CLAUDE.md` is first in the policy compliance order, so 80 wins —
and it matters, because the repo's PowerShell aggregate sits at 83.93 percent, above the
governing floor and below the rule-file figure.
- **`## Committed Test Evidence Format`.** Present only in the execution copy. Committed test
evidence must be a *projection* of a tool's output; a raw coverage-collector document or a raw
test-platform document is prohibited from git "in any form, including under a feature folder's
evidence tree". Plans that write a Pester JaCoCo XML or a Cobertura/trx document straight into
`<FEATURE>/evidence/` and then commit the folder collide with this.

**Why:** a Phase 0 policy-read task that cites line counts or quotes rules from the auto-loaded copy
is describing a different checkout, and the divergences are exactly the kind that silently change a
gate's threshold or make a planned evidence artifact uncommittable.

**How to apply:** in any multi-worktree run, hash the seven policy files in both worktrees, read in
full any that differ from the execution worktree, and record the divergence in the policy-read
artifact rather than resolving it — `.claude/rules/**` is push-down-owned and not editable here.
See [[planner-and-executor-observe-different-worktrees]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
name: commandline-match-on-results-dir-token-kills-own-tool-shells
description: Killing a hung vstest by matching a results-directory token against Win32_Process CommandLine also matches the agent's own bash.exe and pwsh.exe tool wrappers and kills them
metadata:
type: project
---

To stop a hung `vstest.console.exe`, do not select processes with
`Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match "<token>" }` where `<token>`
is a results-directory name such as `p4-t8`. That token is also present in the agent's own
`bash.exe` and `pwsh.exe` tool-invocation command lines, so the filter kills the agent's live tool
shells along with the runner.

**Why:** observed on issue #871 P4-T8. A filter on `p4-t8` matched six `bash.exe` and two
`pwsh.exe` processes belonging to the current session in addition to the two intended runner
processes. Nothing belonging to a sibling item matched, so the blast radius was self-inflicted
rather than cross-item, but the session's in-flight tool calls died.

**How to apply:** select on the executable name first and only then narrow, e.g.
`Where-Object { $_.Name -in @("vstest.console.exe","testhost.exe") -and $_.CommandLine -match "<token>" }`.
Confirm the candidate list by printing PID, Name and CommandLine before any `Stop-Process`.
Separately, a build-lock held by the killed command must still be released explicitly — the
release script is file-based and does not notice the holder's death.

Related: [[project_killing_a_build_lock_waiter_by_script_name_hits_every_sibling]],
[[project_timedout_mstest_leaves_detached_runner]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
name: commandline-rebuild-omits-vsto-manifests-addin-cannot-load
description: A command-line msbuild Rebuild of TaskMaster.sln never writes TaskMaster.vsto / TaskMaster.dll.manifest, so a manual-verification phase that reopens Outlook after the rebuild needs a separate manifest-generating (Visual Studio) build first
metadata:
type: project
---

A plain `msbuild TaskMaster.sln /t:Rebuild` produces `TaskMaster/bin/Debug/TaskMaster.dll` but NOT `TaskMaster.vsto` or `TaskMaster.dll.manifest`; `TaskMaster/TaskMaster.csproj` imports the Office targets only when `BuildingInsideVisualStudio` is true. The registered add-in (`HKCU\...\Outlook\Addins\TaskMaster`, `Manifest` = `.../TaskMaster.vsto|vstolocal`) cannot load without the `.vsto`.

**Why:** On #792 [P8-T1] (2026-09-17) the rebuild artifact recorded the missing manifests as "not a defect, by design". It was a real gap: a VS-driven build generated both manifests 16 minutes later (mtime 21:43:18, over the unchanged 21:27 assembly), four seconds before Outlook started. Had nobody done that, the "person reopens Outlook and confirms the add-in loaded" half of the task would have failed silently.

**How to apply:** In any plan task that rebuilds and then expects Outlook to load the add-in, check `TaskMaster/bin/Debug/TaskMaster.vsto` exists and its mtime is at or after the assembly's; if absent, the human must run a manifest-generating build (F5/Build in VS) before reopening Outlook. Record the manifest mtimes alongside the assembly mtime; proof the add-in ran is the session log appearing under the worktree's own `TaskMaster/bin/Debug/logs/`. Related: [[epic-checkpoint-hooks-scan-command-text-for-worktree-tokens]].
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,15 @@ it as a reason to fingerprint shared state before acting, not as a licence to go
conflict is detectable at preflight for free — hash the plan, count the evidence dir, sample twice.
Do that BEFORE the first check-off. Once you have written even one artifact you have joined the race
and the clean stop is no longer available.

## Third occurrence — #792 Phase 2 relaunch, 2026-09-17

The caller relaunched an executor for [P2-T13]..[P2-T15] stating "nothing is running; I checked the
process table". The predecessor was alive: it wrote its T14 artifact, committed T15 and checked off
both tasks within three minutes of the relaunch, while the relaunched executor's own T14 run was in
progress. Lessons: (1) a caller's "no live process" claim is not evidence — an agent turn does not
show up as a distinguishable `msbuild`/`vstest` process between tool calls; (2) re-read the plan's
check-off state and `git rev-parse HEAD` immediately before EVERY artifact write, not only at
preflight; (3) if your Write clobbers a sibling's already-committed artifact, `git checkout HEAD --
<that one file>` restores it without touching anything else, and put your own observations in a
separate `evidence/other/` artifact rather than re-editing theirs.
Loading
Loading