agentic-preflight 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. agentic_preflight/__init__.py +0 -0
  2. agentic_preflight/__main__.py +4 -0
  3. agentic_preflight/_bundled_skill/SKILL.md +400 -0
  4. agentic_preflight/_bundled_skill/reference/commands.md +294 -0
  5. agentic_preflight/_bundled_skill/reference/docs-rubric.md +88 -0
  6. agentic_preflight/_bundled_skill/reference/findings-schema.md +102 -0
  7. agentic_preflight/approval.py +122 -0
  8. agentic_preflight/attestation.py +220 -0
  9. agentic_preflight/cli.py +600 -0
  10. agentic_preflight/config.py +277 -0
  11. agentic_preflight/dependencies.py +225 -0
  12. agentic_preflight/diff.py +267 -0
  13. agentic_preflight/envelope.py +125 -0
  14. agentic_preflight/errors.py +219 -0
  15. agentic_preflight/findings.py +161 -0
  16. agentic_preflight/gitx.py +353 -0
  17. agentic_preflight/hook.py +158 -0
  18. agentic_preflight/initcmd.py +141 -0
  19. agentic_preflight/integrations.py +490 -0
  20. agentic_preflight/machine.py +356 -0
  21. agentic_preflight/mergeback.py +158 -0
  22. agentic_preflight/models.py +322 -0
  23. agentic_preflight/publish/__init__.py +0 -0
  24. agentic_preflight/publish/gate.py +52 -0
  25. agentic_preflight/py.typed +0 -0
  26. agentic_preflight/risk.py +146 -0
  27. agentic_preflight/runs/__init__.py +38 -0
  28. agentic_preflight/runs/_session.py +206 -0
  29. agentic_preflight/runs/lifecycle.py +304 -0
  30. agentic_preflight/runs/mergeback.py +234 -0
  31. agentic_preflight/runs/publish.py +226 -0
  32. agentic_preflight/runs/resolve.py +238 -0
  33. agentic_preflight/runs/review.py +763 -0
  34. agentic_preflight/runs/stages.py +400 -0
  35. agentic_preflight/runs/start.py +413 -0
  36. agentic_preflight/runtime.py +299 -0
  37. agentic_preflight/stages/__init__.py +0 -0
  38. agentic_preflight/stages/change_scope.py +57 -0
  39. agentic_preflight/stages/detect.py +116 -0
  40. agentic_preflight/stages/docs.py +154 -0
  41. agentic_preflight/stages/shellstage.py +244 -0
  42. agentic_preflight/store.py +276 -0
  43. agentic_preflight/sync.py +119 -0
  44. agentic_preflight/worktree.py +289 -0
  45. agentic_preflight-0.3.0.dist-info/METADATA +498 -0
  46. agentic_preflight-0.3.0.dist-info/RECORD +49 -0
  47. agentic_preflight-0.3.0.dist-info/WHEEL +4 -0
  48. agentic_preflight-0.3.0.dist-info/entry_points.txt +2 -0
  49. agentic_preflight-0.3.0.dist-info/licenses/LICENSE +201 -0
File without changes
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,400 @@
1
+ ---
2
+ name: agentic-preflight
3
+ description: Use when shipping a branch — reviewing, documenting, linting, testing, and pushing work behind a quality gate. Also use when a push is blocked by the agentic-preflight pre-push hook or when the user says agentic-preflight:uninstall to remove this tool from the current project.
4
+ ---
5
+
6
+ # agentic-preflight
7
+
8
+ You review, judge, and fix. The CLI holds all state and tells you what to do next.
9
+ Python here never calls a model — every judgment in this workflow is yours.
10
+
11
+ ## Non-negotiables
12
+
13
+ 1. **You think, the CLI holds state.** Never guess where a run is. Ask `status`.
14
+ 2. **Parse stdout as JSON and obey `next`.** Every command prints exactly one JSON
15
+ object. `next.command` is the single next legal move. Follow it. On any
16
+ **non-`ok`** envelope, print the whole `data` object — never a selection of keys
17
+ you expected. Failure payloads carry recovery material that success payloads do
18
+ not (`resolution`, `conflicting_files`, `candidates`, `by_file`), and some of it
19
+ exists nowhere else afterwards.
20
+ 3. **Never invent code-assigned finding fields.** You submit `path`, optional delivered
21
+ review `unit`, `line`, `severity`, `action`, `title`, `detail`, and `suggestion`.
22
+ Sending `id`, `stage`, or `code_owned` is a hard validation error, not a nudge.
23
+ 4. **Never run `git push --no-verify`.** It exists for humans, not for you.
24
+ 5. **Never push without user authorization.** An explicit request to push, publish, or
25
+ create/open a pull request authorizes the matching push in that task; after `gate`,
26
+ show what will be pushed and proceed without asking a second time. If publication
27
+ was not explicitly requested, or the remote, branch, commits, or risk summary is
28
+ materially different from what the user authorized, show the summary and wait for
29
+ an actual answer. A generic request to implement, commit, or "proceed" is not push
30
+ authorization. `[pr] mode = "auto"` is standing authorization to open or reuse the
31
+ pull request after the authorized push and preflight finish. With `mode = "manual"`,
32
+ never open the PR for them.
33
+ 6. **Never resolve a merge-back conflict.** Paste the resolution block and stop.
34
+ 7. **Keep the validation checkout clean for the whole run.** The default
35
+ `in_place` mode uses the current checkout, so only deliberate repair commits may
36
+ move its branch; uncommitted changes or an unaccounted commit stop the run.
37
+ `.agentic-preflight.toml` must be committed **before `start`** and must not be edited
38
+ mid-run. In `reusable` or `strict` mode, make repairs only in the absolute
39
+ `worktree_path` returned by the CLI.
40
+ 8. **Never merge a high-risk `manual_merge` pull request or enable auto-merge.** The
41
+ hosted check fails while auto-merge is enabled; when successful, it records that the
42
+ user must perform the merge and is not authorization for the agent to merge.
43
+
44
+ ## The loop
45
+
46
+ ```
47
+ $ agentic-preflight start --intent "<the user's objective and acceptance criteria>"
48
+ {"ok":true,"run_id":"r_4f2a","state":"REVIEW_AWAITING_FINDINGS",
49
+ "data":{"worktree_path":"/repos/my-project","worktree_mode":"in_place","changed_files":["src/auth.py"]},
50
+ "next":{"instruction":"Fetch the diff before judging it.","command":"agentic-preflight context"}}
51
+
52
+ $ agentic-preflight context
53
+ {"ok":true,"state":"REVIEW_AWAITING_FINDINGS",
54
+ "data":{"diff":"diff --git a/src/auth.py ...","changed_files":["src/auth.py"],
55
+ "review_coverage":{"manifest":"<digest>","total_units":1,"units":[{"id":"U0001",...}]}},
56
+ "next":{"command":"agentic-preflight submit-findings --file findings.json"}}
57
+
58
+ # If `next.command` is `agentic-preflight review run`, do not submit your own findings.
59
+ # The configured independent reviewer receives this same data bundle and returns the
60
+ # strict submission through the same validation path.
61
+ $ agentic-preflight review run
62
+
63
+ # You read the diff and decide. Write findings.json, then:
64
+ $ agentic-preflight submit-findings --file findings.json
65
+ {"ok":true,"state":"REVIEW_AWAITING_RESPONSES","blocking":[{"id":"F001","severity":"high",...}],
66
+ "next":{"command":"agentic-preflight respond --id F001 --action fixed --commit <sha>"}}
67
+
68
+ # Fix it in data.worktree_path, commit there, then:
69
+ $ cd /repos/my-project && git add -A && git commit -m "use constant-time compare"
70
+ $ agentic-preflight respond --id F001 --action fixed --commit 9c3d1ab
71
+ {"ok":true,"state":"REVIEW_FIXING","next":{"command":"agentic-preflight verify"}}
72
+
73
+ $ agentic-preflight verify
74
+ {"ok":true,"state":"REVIEW_AWAITING_FINDINGS","data":{"coverage_invalidated":true},
75
+ "next":{"command":"agentic-preflight context"}}
76
+
77
+ # The fix changed the snapshot. Review the complete current diff and submit its new
78
+ # manifest. With no new issue, every unreferenced unit is explicitly examined clean.
79
+ $ agentic-preflight context
80
+ $ agentic-preflight submit-findings --file findings-clean.json
81
+ {"ok":true,"state":"REVIEW_GREEN","next":{"command":"agentic-preflight context --section docs"}}
82
+
83
+ $ agentic-preflight context --section docs
84
+ {"ok":true,"state":"DOCS_AWAITING_FINDINGS","data":{"doc_surface":[{"path":"README.md",...}]},
85
+ "next":{"command":"agentic-preflight submit-findings --file findings.json"}}
86
+
87
+ $ agentic-preflight submit-findings --file findings.json # often just {"findings": []}
88
+ {"ok":true,"state":"DOCS_GREEN","next":{"command":"agentic-preflight stage run lint"}}
89
+
90
+ $ agentic-preflight stage run lint
91
+ {"ok":true,"state":"LINT_GREEN","next":{"command":"agentic-preflight stage run test"}}
92
+
93
+ # For a documentation/CI-configuration-only diff, green lint instead records test
94
+ # as skipped and returns TEST_GREEN with mergeback as next. Obey the envelope.
95
+
96
+ $ agentic-preflight stage run test
97
+ {"ok":true,"state":"TEST_GREEN","next":{"command":"agentic-preflight mergeback"}}
98
+
99
+ $ agentic-preflight mergeback
100
+ {"ok":true,"state":"VERIFIED","data":{"worktree_mode":"in_place","applied":[],"tree_equivalent":true},
101
+ "next":{"command":"agentic-preflight gate"}}
102
+
103
+ $ agentic-preflight gate
104
+ {"ok":true,"state":"AWAITING_PUSH_CONFIRM","data":{"token":"a1b2c3d4","pr_mode":"auto","commits":[...]},
105
+ "next":{"command":"agentic-preflight push --confirm a1b2c3d4"}}
106
+
107
+ # Show the remote, branch, and commits. If this task explicitly requested a push,
108
+ # publish, or pull request and the summary matches, that request is the confirmation.
109
+ # Otherwise STOP and ask whether to push. Once authorized:
110
+ $ agentic-preflight push --confirm a1b2c3d4
111
+ $ agentic-preflight finish
112
+ $ agentic-preflight gc
113
+
114
+ # Auto PR mode: after preflight finishes, reuse an existing PR for the branch or
115
+ # create one automatically without asking about PR creation.
116
+ $ gh pr create --title "Use constant-time password comparison" --body-file pr-body.md
117
+ $ gh pr checks --watch
118
+
119
+ # Manual PR mode: never create it. Give the user the repository compare URL instead.
120
+ ```
121
+
122
+ Work happens in the absolute **validation checkout** named by `worktree_path`. In the
123
+ default `in_place` mode that is the current PR checkout; in `reusable` and `strict`
124
+ modes it is an isolated worktree. Never assume `cd` persists between tool calls.
125
+ The complete command and option reference is in `reference/commands.md`; use it when
126
+ an envelope calls for a command or recovery path not expanded in this playbook.
127
+
128
+ ## How to review
129
+
130
+ Judge the diff, not the repo. Only findings against changed files are accepted.
131
+ Account for the complete `review_coverage` manifest returned by `context`; never reuse a
132
+ manifest after a commit. The payload's one `examined: "all"` assertion keeps clean hunks
133
+ quiet while code verifies that no delivered unit disappears.
134
+
135
+ | Severity | Means | Example |
136
+ |---|---|---|
137
+ | `critical` | Data loss, security hole, corruption | Password compared with `==`; SQL built by string concatenation |
138
+ | `high` | Wrong behaviour a user will hit | Off-by-one dropping the last record; error swallowed silently |
139
+ | `medium` | Real problem, not urgent | Duplicated logic that will drift; missing edge-case handling |
140
+ | `low` | Style, naming, nits | Inconsistent naming; a stale comment |
141
+
142
+ `critical` and `high` block by default. Pick the action deliberately:
143
+
144
+ - **`auto_fix`** — mechanical and locally verifiable. You can fix it correctly
145
+ without asking anyone. Most findings should be this.
146
+ - **`ask_user`** — behavioural, API, or product judgment. **Blocks at any
147
+ severity**, because choosing for the user *is* the decision you declined to make.
148
+ - **`no_op`** — worth recording, not worth acting on.
149
+
150
+ Be specific. "Consider improving error handling" is not a finding. "Line 42 swallows
151
+ `ConnectionError`, so a network failure looks like an empty result" is.
152
+
153
+ ## How to check docs
154
+
155
+ One question, and only this one:
156
+
157
+ > **Would a reader following the current documentation now be wrong?**
158
+
159
+ Not "could the docs be better" — they always could. Zero findings is a **normal and
160
+ common outcome**, and reporting zero is a success, not a failure to try.
161
+
162
+ Docs findings may target files the diff never touched — that is the entire point. But
163
+ they must land on documentation: a finding against `src/auth.py` is a review finding
164
+ wearing a docs hat, and is rejected. `context --section docs` gives you `doc_surface`;
165
+ use it rather than hunting for docs yourself.
166
+
167
+ The surface is an allowlist, and a rejection is not a verdict on the finding. Repos
168
+ often keep their binding rules outside it — `.claude/rules/*.md`, `PRODUCT.md`,
169
+ `DESIGN.md`. If a genuinely stale doc sits outside the allowlist, fix it in the same
170
+ commit anyway, say in the commit message that it could not be filed, and tell the user
171
+ to add it to `[docs] paths` so the next run can see it.
172
+
173
+ Full rubric: `reference/docs-rubric.md`.
174
+
175
+ ## Findings schema
176
+
177
+ ```json
178
+ {"coverage": {"manifest": "<from context>", "examined": "all"}, "findings": [
179
+ {"unit": "U0001", "path": "src/auth.py", "line": 42,
180
+ "severity": "high", "action": "auto_fix",
181
+ "title": "Password compared with ==",
182
+ "detail": "Timing-variable comparison leaks length. Use secrets.compare_digest.",
183
+ "suggestion": "if secrets.compare_digest(supplied, stored):"}
184
+ ]}
185
+ ```
186
+
187
+ **No `id`. No `stage`. No `code_owned`.** All three are assigned by the CLI. IDs run
188
+ `F001`, `F002`, … continuously across the whole run — docs findings continue review
189
+ numbering, they do not restart. Full field reference:
190
+ `reference/findings-schema.md`.
191
+
192
+ ## Exit codes
193
+
194
+ | Code | Meaning | What to do |
195
+ |---|---|---|
196
+ | 0 | OK | Follow `next` |
197
+ | 1 | Usage or internal error | Read `error.message`; fix your invocation |
198
+ | 2 | Stage failed | Read the log, fix the cause, re-run the stage |
199
+ | 3 | Precondition violated | **Run `status`, then obey `next`** |
200
+ | 4 | Human resolution required | Stop. Show the user. Do not improvise |
201
+ | 5 | Confirmation required | Ask the user, then re-run with the token |
202
+ | 10 | Hook blocked a push | Run the gate: `agentic-preflight start --intent "..."` |
203
+
204
+ **Universal recovery rule: any exit 3 → run `status` → obey `next`.** `status` is legal
205
+ in every state. If you are ever unsure where you are, that is always the right call.
206
+
207
+ ## Failure playbooks
208
+
209
+ **Merge-back conflict (exit 4, isolated modes only).** The branch has already been
210
+ restored exactly and your fix commits are safe in the worktree. Paste
211
+ `data.resolution` to the user verbatim and **stop**. Do not cherry-pick, do not force,
212
+ do not pick a side. A conflict is a content decision and it is not yours to make.
213
+
214
+ The full conflict report is stored in the event log and replayed by `status`. After a
215
+ person resolves or restores the reported paths, `mergeback` is the legal retry and
216
+ completed verification remains intact when the resulting tree is still identical to
217
+ the verified tree. A different tree must go through a fresh run. Before concluding
218
+ the conflict is real, check the user's tree was clean — see non-negotiable 7.
219
+
220
+ **Stage red after max attempts (exit 4).** Stop retrying — you have already tried
221
+ `max_attempts` times and the tool is telling you the loop is not converging. Show the
222
+ user `agentic-preflight logs --stage <name>` output and ask how to proceed.
223
+
224
+ **Hosted CI failed.** Inspect the failed check with `gh pr checks` and `gh run view
225
+ --log-failed`. Fix and commit the source branch, then start a fresh synchronized
226
+ preflight run with the original intent. Do not push the repair until the new
227
+ review → docs → lint → test run reaches green. Push through the gate again, then
228
+ resume check monitoring with `gh`.
229
+
230
+ **Stale head (exit 3, `stale_run`).** The branch moved after review began, so
231
+ everything verified so far describes a tree that no longer exists. There is no partial
232
+ recovery: run `agentic-preflight abort --force`, then run the fresh `start` command from
233
+ the abort response. It preserves the original user intent.
234
+
235
+ **Diff too large (exit 2, `diff_too_large`).** The diff is never truncated, so
236
+ reviewing part of it is not an option. Look at `data.by_file`; if the bulk is generated
237
+ (lockfiles, vendored code, snapshots), add those globs to `[diff] exclude`. Raise
238
+ `[diff] max_bytes` only if the change genuinely is that large.
239
+
240
+ **No command configured (exit 2, `needs_command`).** For lint/test, pick from
241
+ `data.candidates` and re-invoke with `--command`; offer to write it into `[commands]` so
242
+ it is settled. For review, configure `[review] command` and retry `review run` — reviewer
243
+ commands are never detected. If lint/test `candidates` is empty, the repo simply has no
244
+ manifest detection understands (Unity,
245
+ Unreal, Xcode, most engine projects) — ask the user for the invocation instead of
246
+ hunting for a build file that does not exist.
247
+
248
+ Then treat its first green as unproven. Pass/fail is the exit code alone, so a command
249
+ that no-ops and exits 0 reads as a pass forever — and a false green retires the check
250
+ instead of costing a retry. Confirm the run actually did work (a test count, a results
251
+ file, a non-empty log) before believing it. The trap is usually a flag: `-quit` on a
252
+ Unity `-runTests` invocation exits 0 having run zero tests.
253
+
254
+ **Stage far slower than normal.** Check `[worktree] mode`. The default `in_place` mode
255
+ uses the checkout's existing environment and does not run an automatic dependency
256
+ install. The reusable runner retains ignored build caches and skips Node installation
257
+ while its fingerprint matches. Strict mode has no build cache and runs the frozen
258
+ install every time. Isolated modes do not share the source checkout's `node_modules`;
259
+ use `[worktree] setup_command` to prepare non-Node caches.
260
+ `copy_files` is for ignored files such as `.env`, not directories. Do not raise
261
+ `[stage] max_attempts` to paper over it.
262
+
263
+ **Copy refused (exit 3).** A `copy_files` entry is not gitignored. Do not work around
264
+ it — tell the user to gitignore and commit it first. This guard prevents a secret
265
+ being committed and pushed.
266
+
267
+ **Stage reports zero files to work on.** Check where `worktree_path` actually points.
268
+ If it is under `.git/`, tools that skip VCS directories cannot see it and will exit
269
+ non-zero on an empty set, which reads as a red stage. Jest is the common case:
270
+ `jest-haste-map` ORs a hardcoded `/.git/` ignore into its crawl with no config
271
+ override, so it finds zero test files no matter how healthy the code is. Symlinks do
272
+ not help — real paths are resolved. Confirm by running the same command in a worktree
273
+ outside `.git`; if that finds files, point the stage command at a script that checks
274
+ the commit under test out to a non-`.git` path and runs there. Never point an isolated
275
+ run at the source checkout: that reports on the wrong content and is a false green.
276
+
277
+ **Green in your shell, red under the gate.** Stages run non-interactively, so
278
+ version-manager shims (nvm, rbenv, pyenv, asdf) are absent and tools resolve to
279
+ system-wide installs. Compare the toolchain version *inside the stage* against the
280
+ project's declared range before you debug the code — a native module built for another
281
+ ABI fails as missing bindings, not as a version error. A repo with no `.nvmrc` (or
282
+ equivalent) has nothing pinning it, so this bites fresh clones and CI too, not just
283
+ the gate.
284
+
285
+ ## Escalation etiquette
286
+
287
+ At the gate, show the user — in plain prose, not JSON:
288
+
289
+ - which **remote and branch** the push targets
290
+ - the **commit subjects** being pushed
291
+ - the deterministic **risk level and verdict**, including every matched
292
+ `human_review_path`
293
+ - anything you resolved as `ask_user`, and what you decided
294
+ - any finding you dismissed, and why
295
+
296
+ If the user explicitly asked in this task to push, publish, or create/open a pull
297
+ request, and this summary matches that request, display it as a progress update and
298
+ continue with the token. Do not ask them to confirm the same publication twice.
299
+
300
+ Otherwise ask, plainly: *"Ready to push this to `origin/feature-x`?"* Wait for a real
301
+ answer. A request only to implement or commit, or a generic "proceed" from a previous
302
+ step, is not consent for the push gate. Ask again if the summary reveals an unexpected
303
+ remote, branch, commit, or risk decision.
304
+
305
+ In `[pr] mode = "auto"`, the committed configuration is standing authorization for PR
306
+ creation. After the authorized push, `finish`, and `gc`, reuse an existing pull request
307
+ for the branch or call `gh pr create` automatically without asking about the PR.
308
+
309
+ In `[pr] mode = "manual"`, ask only whether to push. Afterward, never open a pull
310
+ request; construct the forge compare URL from the repository URL, base branch, and head
311
+ branch and give it to the user.
312
+
313
+ If risk returns `needs_human`, explain the merge restriction before pushing, then follow
314
+ the configured `[approval] mode`. An explicit request to create the pull request still
315
+ authorizes publication when the gate summary matches:
316
+
317
+ - `manual_merge`: the hosted check reports success only while auto-merge is disabled;
318
+ never merge or enable auto-merge, and tell the user that they must review and merge the
319
+ pull request manually.
320
+ - `environment`: wait for approval through the configured GitHub Environment before the
321
+ hosted approval check can pass.
322
+ - `peer_review`: require an eligible repository-associated person other than the author
323
+ to approve the exact current head.
324
+
325
+ Only an explicit `[gate] mode = "manual"` hands the push itself to a person.
326
+
327
+ For `ask_user` findings, present the trade-off and let them choose. Do not present a
328
+ decision you have already made as if it were a question.
329
+
330
+ Branch names are often poor human-facing PR titles, so offer a concise title that
331
+ describes the verified change before calling `gh pr create`.
332
+
333
+ When an automatic pull request is opened or an existing one is reused, report its URL
334
+ and tell the user exactly what a later cleanup request will do: verify that this PR was
335
+ merged, switch a clean source checkout to the base branch when necessary, remove only
336
+ this run's validation worktree and `ap/*` branch, delete the local PR branch and its
337
+ remote branch, and fast-forward the base branch.
338
+
339
+ ## What to publish, and what it proves
340
+
341
+ Publish the **findings** in the PR body passed to `gh`: id, severity, path, and the
342
+ commit that resolved each. That is the part CI cannot reproduce — no test
343
+ suite tells a reviewer which judgment calls were made — and it stops a human
344
+ re-deriving what the gate already caught.
345
+
346
+ The commit's Git-note attestation already carries review coverage plus the local stage
347
+ commands, exit codes, and output hashes. Do not copy those into the PR body; if the repo
348
+ runs CI, point at the forge's execution for stronger, remote evidence.
349
+
350
+ Publish the gaps in the same breath: a bypassed hook, a stage that could not run, a
351
+ SHA with no green run. An attestation that can only report success is marketing, and a
352
+ partial record that reads as complete is worse than none.
353
+
354
+ State the limit plainly when you show it: this proves what the gate *reported*, including
355
+ that every delivered unit was cited or marked examined clean; it does not prove the agent
356
+ understood those units or that the review was good. The same diff reviewed twice can
357
+ yield different findings. It is an audit trail, not a quality proof, and it substitutes
358
+ for neither CI nor a human reviewer.
359
+
360
+ ## Project uninstall trigger (`agentic-preflight:uninstall`)
361
+
362
+ When the user says `agentic-preflight:uninstall`, treat that exact phrase as approval
363
+ to remove agentic-preflight from the current repository without another confirmation.
364
+ Resolve the repository root with `git rev-parse --show-toplevel`; stop if the current
365
+ directory is not inside a Git repository.
366
+
367
+ Before changing anything, resolve the actual hook path with `git rev-parse --git-path
368
+ hooks/pre-push` and inspect both it and the repository status. Then:
369
+
370
+ - delete only the repository root's `.agentic-preflight.toml` file, if present;
371
+ - if the pre-push hook is the standalone generated hook marked `Installed by
372
+ agentic-preflight` and ending in `exec agentic-preflight hook-check`, delete it;
373
+ - if it is a shared or custom hook, remove only the clearly bounded
374
+ agentic-preflight invocation and its associated wrapper logic; and
375
+ - stop and report the exact hook path instead of modifying it if the
376
+ agentic-preflight portion cannot be separated confidently.
377
+
378
+ Do not remove other hook behavior, `.git/agentic-preflight` run history, or
379
+ `refs/notes/agentic-preflight`. Report every path removed, anything already absent,
380
+ and anything deliberately preserved.
381
+
382
+ ## Cleanup after a merge
383
+
384
+ An explicit user request to clean up a merged pull request is the approval for the
385
+ whole run-scoped operation. Inspect the exact targets and verify through `gh` that the
386
+ PR is merged, then perform the cleanup in the same turn without asking again. Re-check
387
+ the merge and head/base branches immediately before mutation, switch a clean source
388
+ checkout to the base branch when necessary, remove only that run's validation worktree
389
+ and `ap/*` branch, delete the local PR source branch and the remote PR source branch,
390
+ then run `git pull --ff-only` so the base checkout contains the merged result.
391
+
392
+ Stop instead of deleting if the PR is not merged, the checkout is dirty, the PR head or
393
+ base differs from the disclosed cleanup scope, or a branch is checked out in an
394
+ unrelated worktree. Cleanup never performs a blanket `ap/*` deletion. Afterward, report
395
+ the exact targets removed and whether the remote branch was already absent.
396
+
397
+ For a pushed run with no PR, follow `finish` with `gc`. `gc` compares original fixes
398
+ with post-mergeback history using stable patch IDs. Only patch-equivalent fixes are
399
+ reclaimed automatically; anything unmerged is retained unless the user explicitly
400
+ chooses `--force`. Run directories remain because they hold durable stage logs.