chore: sync local alignment guidance and managed hooks #6

Merged
jercik merged 1 commit from chore/align-local-forge into main 2026-10-24 06:36:36 +00:00
Owner

Updates declaration-check guidance for Align's local command and regenerates managed hooks and review configuration from Align #809. Interrupted hooks clean up their snapshots, and the updated review wrapper retains the zero-failed-unit gate.

Merge after Align 13 is published and verified.

Updates declaration-check guidance for Align's local command and regenerates managed hooks and review configuration from [Align #809](https://code.j4k.dev/j4k/align/pulls/809). Interrupted hooks clean up their snapshots, and the updated review wrapper retains the zero-failed-unit gate. Merge after Align 13 is published and verified.
chore: sync local alignment guidance and managed hooks
All checks were successful
Checks / pr-flow-canary (pull_request) Has been skipped
Checks / reuse (pull_request) Successful in 4s
commit-msg / commitlint (pull_request) Successful in 8s
Checks / quality-checks (26.5.0) (pull_request) Successful in 48s
Checks / quality-checks (24.15.0) (pull_request) Successful in 48s
Review / Review (pull_request_target) Successful in 4m32s
ae059a8165

Review 01M5N1NX7G4415EMQRMX4GDEJA — head ae059a8165fbfd963e7a650746343f7f9d2a842e

Review — j4k-oss/worktree-add @ 77fbdc0dd5

Scope: diff against base tree f56cdec3b805
Review finished: 10 succeeded, 0 failed
Triage: settled
Facts: current review-wide projection

Computed under:

{
  "abandonment": "abandonment-v1",
  "anchor_recipe": 1,
  "batch_policy": "batch-v1",
  "coverage": "coverage-v3",
  "dispatch_policy": "dispatch-v2",
  "grounder_version": 1,
  "grounding_read_rule": "grounding-read-v1",
  "promotion_policy": "promotion-v1",
  "report": "report-v5",
  "tally": "tally-v1",
  "triage_settle": "triage-settle-v2"
}

Findings (1)

medium — The watcher deletes the snapshot without stopping an in-progress checkout

  • claim: 01M5N1VH5EBX27851N1CJCSKCT
  • anchor: .githooks/commit-msg (snippet)
git --git-dir="$git_common_dir" -c worktree.useRelativePaths=false -c core.hooksPath=/dev/null worktree add --quiet --detach "$snapshot_dir" "$snapshot_commit" 6>&-
  • lens: general-bug · arm: default
  • verdicts: 1 valid / 0 invalid / 0 uncertain
    • pass 01M5N1W7PW2D3YKSDJKW12HXWA · valid: The exact-grounded excerpt confirms that checkout runs directly with descriptor 6 closed. The reviewer supplies the remaining mechanism: only run_step registers a process group, so hook-only SIGKILL produces watcher EOF during checkout with no group to terminate, and removal races the surviving Git process and smudge filter. The reported Linux probe observed both surviving owned work and checkout failures after removal; these are reported evidence, not an independently reproduced result. Successful cleanup during supervised install does not protect checkout. SIGKILL being untrappable does not excuse this race in the surviving watcher. This supports a medium cancellation/cleanup defect under ERR4; supervise and register checkout, then stop its children before removing the snapshot.
  • duplicates: 01M5N1VH6E9S0VKTHH60CT0DWT (aposd-plain)
  • disposition: none

Killing the hook PID with SIGKILL during a slow or blocked smudge filter leaves git worktree add and its filter running after the watcher removes their worktree. A blocked filter can therefore remain indefinitely, and a filter that completes resumes checkout into a directory that has already been deleted.

The checkout closes fd 6 but runs synchronously outside run_step. Only run_step sends a process-group PID to tell_watcher, so watch_snapshot still has an empty process_group during checkout. EOF makes it call remove_snapshot_files without terminating the active Git process.

Run checkout in a separately supervised process group and register it with the watcher before allowing cleanup, as for the checks. Ensure that checkout and its children have stopped before deleting the worktree.

I traced run_step, watch_snapshot, and remove_snapshot_files, then ran the unmodified hook in a temporary Git repository with staged package and lock files, a stub pnpm, and a Git smudge filter that records its PID and sleeps. Running node /app/probe-orphan.mjs checkout killed only the hook PID after the filter started: three seconds later the snapshot directory and registration were gone while /proc showed the filter still sleeping under its surviving Git parent. A separate two-second-filter run resumed after removal and printed unable to create file a: No such file or directory followed by checkout failures. Normal completion and SIGKILL during the supervised install check both cleaned up successfully.

The decisive regression check is to kill only the hook during a blocked checkout and verify that Git and the filter terminate before snapshot removal. Killing the entire hook process group instead does not exercise this failure.

Other claims

  • grounding-pending (0)
  • ungrounded (0)
  • rejected (0)
  • duplicate-of (1)
    • 01M5N1VH6E9S0VKTHH60CT0DWT medium — The watcher removes the snapshot while its checkout process keeps running → 01M5N1VH5EBX27851N1CJCSKCT
  • unadjudicated (0)

Coverage

Coverage pass: 01M5N1P60JNM81538N919J1148
Accounting: complete (all slots terminal; see slot results for failures)
Slot health: healthy

lens part arm unit status runs loss
general-bug whole default claims-emitted 1 no
writing-quality whole default no-claims 1 no
test-trimming whole default no-claims 1 no
restated-sets whole default no-claims 1 no
project-docs whole default no-claims 1 no
unix-cli whole default no-claims 1 no
use-effect whole default no-claims 1 no
module-design whole default no-claims 1 no
aposd-plain whole default claims-emitted 1 no
react whole default no-claims 1 no
<!-- review:summary --> **Review** `01M5N1NX7G4415EMQRMX4GDEJA` — head `ae059a8165fbfd963e7a650746343f7f9d2a842e` # Review — j4k-oss/worktree-add @ 77fbdc0dd506 Scope: diff against base tree `f56cdec3b805` Review finished: 10 succeeded, 0 failed Triage: settled Facts: current review-wide projection Computed under: ```json { "abandonment": "abandonment-v1", "anchor_recipe": 1, "batch_policy": "batch-v1", "coverage": "coverage-v3", "dispatch_policy": "dispatch-v2", "grounder_version": 1, "grounding_read_rule": "grounding-read-v1", "promotion_policy": "promotion-v1", "report": "report-v5", "tally": "tally-v1", "triage_settle": "triage-settle-v2" } ``` ## Findings (1) ### medium — The watcher deletes the snapshot without stopping an in-progress checkout - claim: `01M5N1VH5EBX27851N1CJCSKCT` - anchor: `.githooks/commit-msg` (snippet) ``` git --git-dir="$git_common_dir" -c worktree.useRelativePaths=false -c core.hooksPath=/dev/null worktree add --quiet --detach "$snapshot_dir" "$snapshot_commit" 6>&- ``` - lens: general-bug · arm: default - verdicts: 1 valid / 0 invalid / 0 uncertain - pass `01M5N1W7PW2D3YKSDJKW12HXWA` · valid: The exact-grounded excerpt confirms that checkout runs directly with descriptor 6 closed. The reviewer supplies the remaining mechanism: only run_step registers a process group, so hook-only SIGKILL produces watcher EOF during checkout with no group to terminate, and removal races the surviving Git process and smudge filter. The reported Linux probe observed both surviving owned work and checkout failures after removal; these are reported evidence, not an independently reproduced result. Successful cleanup during supervised install does not protect checkout. SIGKILL being untrappable does not excuse this race in the surviving watcher. This supports a medium cancellation/cleanup defect under ERR4; supervise and register checkout, then stop its children before removing the snapshot. - duplicates: `01M5N1VH6E9S0VKTHH60CT0DWT` (aposd-plain) - disposition: none > Killing the hook PID with SIGKILL during a slow or blocked smudge filter leaves `git worktree add` and its filter running after the watcher removes their worktree. A blocked filter can therefore remain indefinitely, and a filter that completes resumes checkout into a directory that has already been deleted. > > The checkout closes fd 6 but runs synchronously outside `run_step`. Only `run_step` sends a process-group PID to `tell_watcher`, so `watch_snapshot` still has an empty `process_group` during checkout. EOF makes it call `remove_snapshot_files` without terminating the active Git process. > > Run checkout in a separately supervised process group and register it with the watcher before allowing cleanup, as for the checks. Ensure that checkout and its children have stopped before deleting the worktree. > > I traced `run_step`, `watch_snapshot`, and `remove_snapshot_files`, then ran the unmodified hook in a temporary Git repository with staged package and lock files, a stub pnpm, and a Git smudge filter that records its PID and sleeps. Running `node /app/probe-orphan.mjs checkout` killed only the hook PID after the filter started: three seconds later the snapshot directory and registration were gone while `/proc` showed the filter still sleeping under its surviving Git parent. A separate two-second-filter run resumed after removal and printed `unable to create file a: No such file or directory` followed by checkout failures. Normal completion and SIGKILL during the supervised install check both cleaned up successfully. > > The decisive regression check is to kill only the hook during a blocked checkout and verify that Git and the filter terminate before snapshot removal. Killing the entire hook process group instead does not exercise this failure. ## Other claims - grounding-pending (0) - ungrounded (0) - rejected (0) - duplicate-of (1) - `01M5N1VH6E9S0VKTHH60CT0DWT` medium — The watcher removes the snapshot while its checkout process keeps running → `01M5N1VH5EBX27851N1CJCSKCT` - unadjudicated (0) ## Coverage Coverage pass: 01M5N1P60JNM81538N919J1148 Accounting: complete (all slots terminal; see slot results for failures) Slot health: healthy | lens | part | arm | unit status | runs | loss | | --- | --- | --- | --- | --- | --- | | general-bug | whole | default | claims-emitted | 1 | no | | writing-quality | whole | default | no-claims | 1 | no | | test-trimming | whole | default | no-claims | 1 | no | | restated-sets | whole | default | no-claims | 1 | no | | project-docs | whole | default | no-claims | 1 | no | | unix-cli | whole | default | no-claims | 1 | no | | use-effect | whole | default | no-claims | 1 | no | | module-design | whole | default | no-claims | 1 | no | | aposd-plain | whole | default | claims-emitted | 1 | no | | react | whole | default | no-claims | 1 | no |
@ -146,2 +205,3 @@
exec 6> >(set -m; watch_snapshot <&0 > /dev/null &)
# Relative worktree paths would upgrade the repository format; post-checkout hooks must not run here.
git --git-dir="$git_common_dir" -c worktree.useRelativePaths=false -c core.hooksPath=/dev/null worktree add --quiet --detach "$snapshot_dir" "$snapshot_commit"
git --git-dir="$git_common_dir" -c worktree.useRelativePaths=false -c core.hooksPath=/dev/null worktree add --quiet --detach "$snapshot_dir" "$snapshot_commit" 6>&-

medium — The watcher deletes the snapshot without stopping an in-progress checkout

Killing the hook PID with SIGKILL during a slow or blocked smudge filter leaves git worktree add and its filter running after the watcher removes their worktree. A blocked filter can therefore remain indefinitely, and a filter that completes resumes checkout into a directory that has already been deleted.

The checkout closes fd 6 but runs synchronously outside run_step. Only run_step sends a process-group PID to tell_watcher, so watch_snapshot still has an empty process_group during checkout. EOF makes it call remove_snapshot_files without terminating the active Git process.

Run checkout in a separately supervised process group and register it with the watcher before allowing cleanup, as for the checks. Ensure that checkout and its children have stopped before deleting the worktree.

I traced run_step, watch_snapshot, and remove_snapshot_files, then ran the unmodified hook in a temporary Git repository with staged package and lock files, a stub pnpm, and a Git smudge filter that records its PID and sleeps. Running node /app/probe-orphan.mjs checkout killed only the hook PID after the filter started: three seconds later the snapshot directory and registration were gone while /proc showed the filter still sleeping under its surviving Git parent. A separate two-second-filter run resumed after removal and printed unable to create file a: No such file or directory followed by checkout failures. Normal completion and SIGKILL during the supervised install check both cleaned up successfully.

The decisive regression check is to kill only the hook during a blocked checkout and verify that Git and the filter terminate before snapshot removal. Killing the entire hook process group instead does not exercise this failure.

lens general-bug · arm default · tally 1 valid / 0 invalid / 0 uncertain
claim 01M5N1VH5EBX27851N1CJCSKCT of review 01M5N1NX7G4415EMQRMX4GDEJA

<!-- review:claim:01M5N1VH5EBX27851N1CJCSKCT --> **medium** — The watcher deletes the snapshot without stopping an in-progress checkout > Killing the hook PID with SIGKILL during a slow or blocked smudge filter leaves `git worktree add` and its filter running after the watcher removes their worktree. A blocked filter can therefore remain indefinitely, and a filter that completes resumes checkout into a directory that has already been deleted. > > The checkout closes fd 6 but runs synchronously outside `run_step`. Only `run_step` sends a process-group PID to `tell_watcher`, so `watch_snapshot` still has an empty `process_group` during checkout. EOF makes it call `remove_snapshot_files` without terminating the active Git process. > > Run checkout in a separately supervised process group and register it with the watcher before allowing cleanup, as for the checks. Ensure that checkout and its children have stopped before deleting the worktree. > > I traced `run_step`, `watch_snapshot`, and `remove_snapshot_files`, then ran the unmodified hook in a temporary Git repository with staged package and lock files, a stub pnpm, and a Git smudge filter that records its PID and sleeps. Running `node /app/probe-orphan.mjs checkout` killed only the hook PID after the filter started: three seconds later the snapshot directory and registration were gone while `/proc` showed the filter still sleeping under its surviving Git parent. A separate two-second-filter run resumed after removal and printed `unable to create file a: No such file or directory` followed by checkout failures. Normal completion and SIGKILL during the supervised install check both cleaned up successfully. > > The decisive regression check is to kill only the hook during a blocked checkout and verify that Git and the filter terminate before snapshot removal. Killing the entire hook process group instead does not exercise this failure. lens `general-bug` · arm `default` · tally 1 valid / 0 invalid / 0 uncertain claim `01M5N1VH5EBX27851N1CJCSKCT` of review `01M5N1NX7G4415EMQRMX4GDEJA`
Author
Owner

Declining this hardening change for the owned-repository migration at ae059a8165. I traced the direct worktree-add call and the watcher: checkout does not register a process group, so the reported hook-only SIGKILL ordering is a plausible cleanup gap. I am not claiming it is fixed or disproved. The reported experiment adds a blocking smudge filter that these tracked files do not use. Git attribute inspection found no assigned filter on any of this repository’s 94 tracked paths. The user explicitly limited this migration to our actual repositories and asked us to avoid expanding tooling for unusual edge cases. We retain that scope and accept this limitation; no follow-up PR or broader cleanup guarantee is claimed.

<!-- gh-feedback:reply-to:182208 --> Declining this hardening change for the owned-repository migration at ae059a8165fbfd963e7a650746343f7f9d2a842e. I traced the direct worktree-add call and the watcher: checkout does not register a process group, so the reported hook-only SIGKILL ordering is a plausible cleanup gap. I am not claiming it is fixed or disproved. The reported experiment adds a blocking smudge filter that these tracked files do not use. Git attribute inspection found no assigned filter on any of this repository’s 94 tracked paths. The user explicitly limited this migration to our actual repositories and asked us to avoid expanding tooling for unusual edge cases. We retain that scope and accept this limitation; no follow-up PR or broader cleanup guarantee is claimed.
jercik marked this conversation as resolved
jercik merged commit 4ecd5f1f2d into main 2026-10-24 06:36:36 +00:00
jercik deleted branch chore/align-local-forge 2026-10-24 06:36:36 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
j4k-oss/worktree-add!6
No description provided.