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 numberDiff line numberDiff line change
@@ -0,0 +1 @@
| 2026-08-18 | claude/code-setup-review-a95519 | bfc0e1a0aeff5f6fc2cf094cad5a61dd8d8525a4 | Follow-up to PR #2113: five outstanding-issues inbox requests, plus a reporting-only correction in clean-worktree.mjs | shipped as PR #2117. Five follow-ups captured that #2113 could not close, each blocked on something outside the repo: deferred worktree cleanup, elevated fsutil devdrv check, PreCompact context-injection confirmation, session-start.sh confirmation on a web container, and a P1 that PR churn has exhausted both review bots so #2113 landed with zero automated review. Separately fixed a false reassurance shipped in #2113: every --squashed candidate printed '0 commits ahead' while genuinely 11/2/1 commits ahead of origin/main, because a squash-merged branch keeps its commits forever and only the UNLANDED count is zero; the line now reports both numbers. Corrected the comment calling the ahead check belt-and-braces, which is true in ancestor mode but tautological in squash mode since gitAheadUnlandedCount returns 0 for any branch the squash test just accepted. Raw count is reporting-only and never gates. Two landed worktrees removed manually (fleet 50 to 48, D: 51 to 48 percent full); seven left in place, one in active use, two not fully corroborated, four on C: belonging to other agents' sessions | clean-worktree --self-test passed; --merged --squashed run against the live 48-worktree fleet before and after with an identical 9-candidate set and worktree count unchanged, --remove not run; check:outstanding-issues passed (361 rows, 105 open, collision-free); check:ledger-write-discipline passed 5ae2bb6ec703..HEAD; prettier --check and format:check clean; eslint clean; pr-policy classifier all four risk flags false; NOT run: full unit suite (diff is five JSON request files plus a reporting-only string in a maintenance script no product code imports), verify:ui, and all provider-backed gates |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
{
"version": 2,
"id": "0f96874b-127c-4fe7-a6cd-349032007e00",
"createdOn": "2026-08-18",
"action": "add",
"payload": {
"pri": "P3",
"type": "task",
"summary": "Confirm D:\\.npm-cache is a registered Dev Drive trusted cache, or Defender is scanning every npm ci",
"detail": "The repo lives on a Windows Dev Drive (D:, ReFS, 50 GB) and npm config get cache resolves to D:\\.npm-cache, which is correctly on the same volume. Whether that path is registered as a Dev Drive TRUSTED cache is unverified: 'fsutil devdrv query D:' returns 'Failed to open the volume. Error 5: Access is denied' without elevation, and the non-elevated registry fallback (HKLM:\\SYSTEM\\CurrentControlSet\\Control\\FileSystem, FilterAttachModeOnDevDrive and DevDriveTrustSetting) reads empty. If it is not registered, Microsoft Defender real-time scanning runs over every npm ci — and this machine performs a lot of them: 21 D: worktrees each carry their own ~0.89 GB / 51,735-file node_modules, because npm extracts fresh copies rather than hardlinking from cache (ReFS does support hardlinks here, probed directly, but npm does not use them). Next: from an ELEVATED prompt run 'fsutil devdrv query D:' and, if the cache is not listed as trusted, 'fsutil devdrv trust D:\\.npm-cache'. Cheap, one-off, no code change. Not blocking anything.",
"source": "session 2026-08-18; fsutil Error 5 without elevation",
"issueUlid": "01M0ACEE5B6SMMB4SCK225BG40"
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
{
"version": 2,
"id": "89d7fe98-1873-41e8-bc21-d77cab91663b",
"createdOn": "2026-08-18",
"action": "add",
"payload": {
"pri": "P3",
"type": "task",
"summary": "Confirm from its own log whether the PreCompact hook's output actually reaches model context",
"detail": ".claude/hooks/precompact-issues-capture.sh was added in PR #2113 to ask for /issues capture BEFORE compaction discards the follow-ups it wants recorded; the pre-existing issues-surface.sh reminder is a SessionStart hook and therefore fires AFTER compaction, when the material is already gone. Claude Code is known to inject hook stdout into model context for SessionStart, UserPromptSubmit, PreToolUse and PostToolUse. Whether it does so for PreCompact could NOT be determined: the installed CLI at %APPDATA%\\npm\\node_modules\\@anthropic-ai\\claude-code ships a compiled claude.exe with no inspectable JS bundle to grep. The hook therefore prints plain text rather than a hookSpecificOutput JSON envelope, so that if the platform does not inject it the operator still sees a clean transcript line instead of a raw JSON blob, and it appends one line per firing to a log so the question is answerable rather than permanently open. Next, after any compaction in a session using this repo: cat \"$(git rev-parse --absolute-git-dir)/claude-precompact.log\". Lines present but no reminder seen in context means the limit is real and the SessionStart backstop is carrying it. No lines at all means the registration is wrong. WARNING: that log lives under the worktree's own git dir, so it is destroyed when the worktree is removed — check it before cleaning up the worktree this was authored in.",
"source": "PR #2113 .claude/hooks/precompact-issues-capture.sh",
"issueUlid": "01M0ACENGDRZQQBT0WJ6MSQ6KS"
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
{
"version": 2,
"id": "9864a5d7-134d-4115-84ca-11eaddfa6c97",
"createdOn": "2026-08-18",
"action": "add",
"payload": {
"pri": "P1",
"type": "rec",
"summary": "PR churn has exhausted the review-bot budget, so PRs are now landing with no automated review at all",
"detail": "CodeRabbit on PR #2113: '101 included PR reviews in the past 7 days; at that activity level, included reviews refill at 1 review per hour. Your organization has reached its usage spending cap.' The Codex connector reported its own usage limit on the same PR. Net effect: #2113 received ZERO automated review, and so will subsequent PRs until the cap resets or credits are added. AGENTS.md 'PR bundling' already measured the CI half of this cost on 2026-07-30 (437 PR-triggered runs over ~3 days, ~40% cancelled mid-run, ~12 Production-UI-hours burned on runs that never completed). This is the second bill for the same behaviour and the more dangerous one, because CI waste is money while missing review is undetected defects — and the PRs most likely to need review are the ones landing during a churn spike. The bundling rule exists as prose in AGENTS.md and is evidently not binding; the newtask skill also asks the question in prose. Decide whether it gets a gate. Note the repo has already learned this lesson once in a different area: .claude/hooks/pr-handoff-stop.sh states in its own header that 'prose rules in AGENTS.md have not held, a denied tool call does.' Next: decide between (a) a push/PR-creation gate that refuses a new branch when an open PR of the same scope exists, (b) raising the bot spending cap, or (c) accepting unreviewed merges deliberately rather than by accident. Stop rule: do not weaken any required check to compensate for missing bot review.",
"source": "CodeRabbit + Codex connector comments on PR #2113, 2026-08-18; AGENTS.md 'PR bundling (reduce one-task-one-PR churn)'",
"issueUlid": "01M0ACDDJQCCZ4HB9R4TK678FX"
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
{
"version": 2,
"id": "bbd0c2e5-6e2f-4aa9-802e-31a95ccb2b2d",
"createdOn": "2026-08-18",
"action": "add",
"payload": {
"pri": "P3",
"type": "task",
"summary": "Confirm on a real Claude Code web session that the session-start hook now runs, after the exec-bit fix",
"detail": "PR #2113 fixed .claude/hooks/session-start.sh, which was checked in as mode 100644 while both sibling hooks were 100755, and was the only hook registered by bare path rather than through bash. What was PROVEN: the index mode, the bare-path registration, and that core.fileMode=false on this Windows ReFS Dev Drive hides both (a local chmod +x is a silent no-op; only git update-index --chmod=+x works). What was NOT proven: that it actually failed on a Linux web container, because no container was available to test from. The 100755/100755/100644 asymmetry makes accident overwhelmingly likely rather than a deliberate choice, and the script's whole body is gated on CLAUDE_CODE_REMOTE=true so the web container is the only place it does any work — it provisions the Node 24 the engine floor requires, after npm ci EBADENGINE blocked PRs #1611, #1697, #1705 and #1740. This is confirmation, not risk: the registration now uses bash \"$CLAUDE_PROJECT_DIR/...\", which removes the dependency on the mode entirely, and tests/session-start-hook.test.ts pins every hook at 100755 with LF-only line endings while tests/claude-code-settings.test.ts pins every hook command to start with an interpreter. Next: on the first Claude Code web session on this repo, check the session start output for the '[session-start] Using node ...' line and confirm npm ci ran. If it did not, the failure is something other than the exec bit and this item becomes a real defect rather than a confirmation.",
"source": "PR #2113; AGENTS.md 'Claude Code hook scripts'",
"issueUlid": "01M0ACF5KP164Z0HTZBWDPG8C3"
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
{
"version": 2,
"id": "ec356a7d-96ec-4739-aa23-4429aa424028",
"createdOn": "2026-08-18",
"action": "add",
"payload": {
"pri": "P2",
"type": "task",
"summary": "Nine landed worktrees are still on disk holding ~4.5 GB on a 51%-full Dev Drive; removal was deferred because the fleet was live",
"detail": "clean-worktree.mjs gained list-only --merged and --squashed in PR #2113 and identified 9 landed worktrees across the 50-worktree fleet, 5 of them on D:. Removal was NOT performed and must not be run blind. Re-verifying each candidate immediately before deletion, twice, showed the fleet is actively worked: database-coordination-chat-9c8cbd and database-drift-remeasure-phase2-7c4215 each held 2 unmerged commits despite the scan minutes earlier reporting '0 commits ahead', their newest files were written the same afternoon, and bundle-baseline had been switched to a different branch mid-scan and was running Playwright (the push guard named it as holding the heavy-run lease). Deleting any of them would have destroyed unmerged work. Next: run 'node scripts/clean-worktree.mjs --merged --squashed' when no other Codex/Gemini/Claude session is active, read the confidence line on each candidate, and re-run with --remove. Skip any candidate marked 'NOT fully corroborated' — that label means the patch-id test inferred the landing but some changed files still differ from origin/main, which is usually base churn but is not proof. D: was 25.3 GB of 50 GB used with roughly 19 GB of that duplicated node_modules across 21 worktrees at ~0.89 GB each. Ignore the C: worktrees entirely; they belong to Codex and Antigravity sessions. Stop rule: never pass --force to git worktree remove, and never remove a worktree that is ahead of origin/main.",
"source": "PR #2113 scripts/clean-worktree.mjs; live fleet re-verification 2026-08-18",
"issueUlid": "01M0ACDZTT6GW95D8BVH6EAHKV"
}
}
25 changes: 22 additions & 3 deletions scripts/clean-worktree.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -113,6 +113,9 @@ export function identifyMergedWorktrees(
isMergedFn = () => false,
statusFn = () => "",
aheadCountFn = () => 0,
// Raw `baseRef..branch` count, used for REPORTING only — never as a gate. See the note at
// the ahead check below for why the two counters have to be reported separately.
rawAheadCountFn = null,
existsFn = existsSync,
mainPath = null,
currentPath = null,
Expand DownExpand Up@@ -152,15 +155,30 @@ export function identifyMergedWorktrees(
const status = statusFn(wt.path);
if (typeof status !== "string" || status.trim() !== "") continue;

// Belt-and-braces against the ancestor test: refs move underneath long-lived worktrees,
// and a non-numeric answer (git failed) must fail closed rather than read as zero.
// Under the ANCESTOR test this is belt-and-braces: refs move underneath long-lived
// worktrees, and a non-numeric answer (git failed) must fail closed rather than read as
// zero. Under the SQUASH test it is not a second opinion at all — `gitAheadUnlandedCount`
// returns 0 for any branch the squash test just accepted, so the check is satisfied by
// construction and can only fire on a candidate that was already skipped. It is kept
// because it is the real gate in ancestor mode, not because it adds anything in squash mode.
const ahead = aheadCountFn(wt.branch, baseRef);
if (!Number.isFinite(ahead) || ahead !== 0) continue;

// Report the RAW count alongside it. A squash-merged branch keeps its original commits
// forever, so it stays genuinely ahead of the base — 3, 4, even 19 commits — while the
// unlanded count is 0. Printing a bare "0 commits ahead" therefore stated something the
// reader could disprove in one `git rev-list` and made the whole line look untrustworthy.
// Observed 2026-08-18 reviewing a real fleet: every squash candidate read "0 commits
// ahead" while being ahead by 3 to 19.
const rawAhead = (rawAheadCountFn ?? aheadCountFn)(wt.branch, baseRef);
const aheadNote = Number.isFinite(rawAhead) && rawAhead !== ahead ? `${rawAhead} ahead of ${baseRef}, ` : "";

merged.push({
...wt,
mergedInto: baseRef,
reason: `branch merged into ${baseRef}; clean tree; 0 commits ahead${wt.head ? ` (tip ${wt.head.slice(0, 9)})` : ""}`,
aheadUnlanded: ahead,
aheadRaw: Number.isFinite(rawAhead) ? rawAhead : null,
reason: `branch merged into ${baseRef}; clean tree; ${aheadNote}0 unlanded commits${wt.head ? ` (tip ${wt.head.slice(0, 9)})` : ""}`,
});
}
return merged;
Expand DownExpand Up@@ -750,6 +768,7 @@ export function runMergedWorktreeReport(options = {}) {
isMergedFn: squashed ? gitBranchSquashMerged : gitBranchIsAncestor,
statusFn: gitWorktreeStatus,
aheadCountFn: squashed ? gitAheadUnlandedCount : gitAheadCount,
rawAheadCountFn: gitAheadCount,
existsFn: existsSync,
currentPath,
baseRef,
Expand Down
Loading