@ikon85/agent-workflow-kit 0.37.0 → 0.39.0

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 (44) hide show
  1. package/.agents/skills/kit-update/SKILL.md +33 -1
  2. package/.agents/skills/orchestrate-wave/SKILL.md +5 -5
  3. package/.agents/skills/setup-workflow/SKILL.md +42 -4
  4. package/.agents/skills/setup-workflow/board-sync.md +6 -2
  5. package/.agents/skills/setup-workflow/workflow-advisories.md +34 -0
  6. package/.agents/skills/setup-workflow/worktree-lifecycle.md +47 -1
  7. package/.agents/skills/wrapup/SKILL.md +46 -0
  8. package/.claude/hooks/drift-guard.py +212 -21
  9. package/.claude/skills/kit-update/SKILL.md +33 -1
  10. package/.claude/skills/orchestrate-wave/SKILL.md +5 -5
  11. package/.claude/skills/setup-workflow/SKILL.md +42 -4
  12. package/.claude/skills/setup-workflow/board-sync.md +6 -2
  13. package/.claude/skills/setup-workflow/workflow-advisories.md +34 -0
  14. package/.claude/skills/setup-workflow/worktree-lifecycle.md +47 -1
  15. package/.claude/skills/wrapup/SKILL.md +46 -0
  16. package/README.md +73 -0
  17. package/agent-workflow-kit.package.json +49 -25
  18. package/docs/adr/0007-session-teardown-requires-provenance-bound-ownership.md +88 -0
  19. package/docs/agents/workflow-capabilities.json +11 -1
  20. package/package.json +1 -1
  21. package/scripts/board_bootstrap.py +367 -0
  22. package/scripts/kit-update-pr.mjs +20 -7
  23. package/scripts/kit-update-pr.test.mjs +29 -0
  24. package/scripts/profile_globs.py +347 -0
  25. package/scripts/test_board_bootstrap.py +348 -0
  26. package/scripts/test_drift_guard_diagnostics.py +295 -0
  27. package/scripts/test_orchestrate_wave_contract.py +9 -0
  28. package/scripts/test_profile_globs.py +280 -0
  29. package/scripts/test_skill_setup_workflow_seeds.py +87 -0
  30. package/scripts/test_worktree_wrapup_contract.py +1592 -3
  31. package/scripts/workflow-advisories/core.py +29 -4
  32. package/scripts/worktree-lifecycle/README.md +139 -0
  33. package/scripts/worktree-lifecycle/capabilities.json +9 -1
  34. package/scripts/worktree-lifecycle/cleanup.py +22 -51
  35. package/scripts/worktree-lifecycle/core.py +1206 -5
  36. package/scripts/worktree-lifecycle/profile.py +38 -3
  37. package/scripts/worktree-lifecycle/session.py +1857 -0
  38. package/scripts/worktree-lifecycle/setup.py +15 -0
  39. package/scripts/wrapup-land.py +461 -14
  40. package/src/cli.mjs +32 -1
  41. package/src/commands/update.mjs +30 -21
  42. package/src/consumer-migrations.json +19 -0
  43. package/src/lib/bundle.mjs +11 -0
  44. package/src/lib/consumerMigrations.mjs +161 -0
@@ -105,6 +105,19 @@ release contain the same artifact.
105
105
  and safe routes—never file content. Automated update pull
106
106
  requests carry the same availability summary and remain manual-merge only.
107
107
 
108
+ Read the required consumer migrations alongside the file delta. A release
109
+ that forces the Consumer to commit a decision registers it as declarative,
110
+ versioned data in the Kit's migration registry — never as release-note prose.
111
+ Preview and the terminal report expose the same structured record, so an
112
+ interactive run, a `--yes` run, `update --json`, and the automated update
113
+ pull request all name the same outstanding actions. Each action names the
114
+ workflow that resolves it, the Consumer file that carries the decision, and
115
+ the exact missing decision. `kit-update` only detects and reports it: it
116
+ never writes the decision, never infers a value, and never invents or
117
+ auto-accepts a pattern that would grant deletion authority. An empty but
118
+ explicit decision is a committed decision and clears the action; a missing
119
+ one keeps it pending on every rerun.
120
+
108
121
  For each conflicted kit-shipped file, always ask the user whether the local
109
122
  edit is a generic improvement or project-specific; never decide or act
110
123
  automatically. For a generic improvement retained as a bridge, run
@@ -131,7 +144,26 @@ release contain the same artifact.
131
144
  resume the transaction through the update API's `resumeFrom` option. Do not
132
145
  copy staged files into the consumer by hand.
133
146
 
134
- 5. Check the optional project census after the update:
147
+ 5. Review the consumer profile's glob dialect after the update:
148
+
149
+ ```sh
150
+ python3 scripts/profile_globs.py docs/agents/workflow-capabilities.json
151
+ ```
152
+
153
+ Every consumer-profile glob — Worktree Lifecycle `scratchPatterns` and
154
+ `wrapup.landingGeneratedArtifactPatterns`, Workflow Advisories
155
+ `baseline.sourceGlobs` and the `preRefactor`/`stopChecks` surface globs — is
156
+ matched by one shared repository-relative dialect. A pattern written for an
157
+ older matcher can narrow or widen. The check names each such pattern with
158
+ the witness path that proves the difference and marks the keys that carry
159
+ deletion authority; exit code 1 means at least one needs review. Report the
160
+ named patterns and let the user rewrite them. The updater never rewrites a
161
+ consumer pattern, and a widened deletion-authority pattern is never accepted
162
+ silently — an update must not expand what cleanup may remove. A missing or
163
+ disabled profile leaves nothing to review and does not invalidate the
164
+ update.
165
+
166
+ 6. Check the optional project census after the update:
135
167
 
136
168
  ```sh
137
169
  python3 .claude/hooks/drift-guard.py --census-status
@@ -141,7 +141,7 @@ replace the ~30-second heartbeat; otherwise the standing heartbeat remains requi
141
141
  LOCAL annotated tag, so two sessions racing one wave cannot both win. Either
142
142
  `acquired: false` or a work signal you did not create means another session
143
143
  owns the wave — **STOP**, report the returned `claim.owner` plus the exact
144
- branch/worktree, touch nothing. Local coordination state — never push the tag.
144
+ branch/worktree, touch nothing. After acquisition, begin the claim-bound teardown receipt with `python3 scripts/worktree-lifecycle/session.py begin --anchor <anchor> --owner <claim-owner> --base <wave-HEAD>`; claim and receipt stay local — never push them.
145
145
  4. **Wave worktree**: reuse the planning worktree (never re-create; the handoff
146
146
  points there). Bring its branch to current `main`:
147
147
  `git -C <wave> merge --ff-only origin/main` (if your repo guards destructive
@@ -195,8 +195,8 @@ disjoint wave, conflict hub first. Dispatch only `FREI` slices: re-read
195
195
  `frontier <anchor#>` before each wave and clear changed edges only via `dep-remove`.
196
196
  Before each slice, bind **(a) inline vs delegate** and **(b) tier + effort** under
197
197
  Standing rules. Tiny mechanical work may stay inline.
198
- For Path A/B, create one worktree per agent from wave HEAD, using `§Setup` or
199
- `git worktree add <path> -b feat/<anchor>-<slug>`.
198
+ For Path A/B, create each new worktree through the active receipt: `python3 scripts/worktree-lifecycle/session.py create --anchor <anchor> --owner <claim-owner> --base <wave-HEAD> --profile <profile> <slice-issue> <slug> <type>`.
199
+ It refuses reused/pre-existing targets; never replace this ownership proof with raw `git worktree add`.
200
200
  - **Claim each slice issue at builder launch — the wave claim is not a slice
201
201
  claim.** It guards the anchor on this machine only; the slice issue is the
202
202
  grabbable unit a second machine or cloud agent sees. Before dispatch, skip and
@@ -358,8 +358,8 @@ written · merge order documented.
358
358
  - A skill edited during the wave → sync its dual-surface mirror in the SAME PR
359
359
  using the tool named by `§Landing` when present; mirror parity remains a pre-PR
360
360
  gate.
361
- - Leave slice worktrees for the user to inspect, or note they're
362
- post-merge-removable.
361
+ - After reading every `ANNAHMEN.md`, seal the receipt, inspect it against fetched canonical `main`, then run its `teardown`; only ancestry/one-to-one patch-equivalent exact owned OIDs are compare-deleted, while foreign targets, unique/ambiguous content, changed OIDs, dirty/protected worktrees, and open/unknown PR evidence stop.
362
+ - Keep the completed receipt as recovery-OID evidence; only then release this run's wave claim.
363
363
 
364
364
  **Done when:** no orphan process · this run's wave claim removed · this run's
365
365
  unlanded slice claims released · ANNAHMEN propagated ·
@@ -204,14 +204,27 @@ Worktree Lifecycle for this repository?"* Offer exactly **Yes**, **Later**, and
204
204
  - **Yes** — create or deepen only the `worktreeLifecycle` section in
205
205
  `docs/agents/workflow-capabilities.json`, preserve every other section and
206
206
  unknown key, reconcile an explicit consumer-derived `scratchPatterns` array,
207
+ explicitly derive and review the consumer's
208
+ `wrapup.landingGeneratedArtifactPatterns` array from real landing outputs,
207
209
  and reconcile only the exact kit-owned hook commands listed in the seed.
208
210
  - **Later / No** — record the choice in the tracked profile without installing
209
211
  hook wiring. Ordinary reruns do not ask again.
210
212
  - **Existing** — adopt the current section and wiring without normalizing
211
- consumer-owned values.
213
+ consumer-owned values. Preserve an existing landing-artifact policy
214
+ byte-for-byte; if it is missing, surface the explicit derivation decision and
215
+ do not write a default.
212
216
  - **Disable** — remove only the exact kit-owned hook commands first, then set
213
217
  `enabled: false`; retain the profile, setup policy, and unknown keys.
214
218
 
219
+ Whenever a profile already carries patterns — on **Yes** over an existing
220
+ section and on **Existing** — run
221
+ `python3 scripts/profile_globs.py docs/agents/workflow-capabilities.json` and
222
+ report every pattern it names. Patterns whose match set narrows or widens under
223
+ the shared dialect are a consumer decision: name them with their witness path,
224
+ mark the deletion-authority keys, and let the consumer rewrite them. Never
225
+ rewrite a pattern for them, and never accept a widened deletion-authority
226
+ pattern silently.
227
+
215
228
  The default setup entry is
216
229
  `python3 scripts/worktree-lifecycle/setup.py`; a proven consumer-native helper
217
230
  may remain the configured `setupEntry` for parity. The handoff advisory always
@@ -269,6 +282,12 @@ repository?"* Offer exactly **Yes**, **Later**, and **No**.
269
282
  - **Disable** — remove exact kit-owned commands first, then set `enabled:
270
283
  false`; preserve every profile value and unknown key.
271
284
 
285
+ `baseline.sourceGlobs` and the `preRefactor`/`stopChecks` surface globs use the
286
+ same shared dialect as the Worktree Lifecycle patterns above. An adopted
287
+ profile written for the older whole-string matcher can change meaning, so run
288
+ `python3 scripts/profile_globs.py docs/agents/workflow-capabilities.json` and
289
+ report every pattern it names before treating the section as reconciled.
290
+
272
291
  ### 2f. Section A7 — Optional Safety Guardrails
273
292
 
274
293
  > Safety Guardrails is a counted group of seven independently selectable
@@ -407,13 +426,29 @@ Seed `docs/agents/domain.md` from [domain.md](./domain.md).
407
426
 
408
427
  **Skip entirely if Section A is not GitHub** → write `board-sync.md` with `state=not-applicable`, `mode=none`, a one-line "this project does not use a GitHub-Projects board" note. Terminal; never re-prompts.
409
428
 
410
- **Preflight:** `gh auth status`. If `project`/`read:project` scope is missing, surface the exact remedy: `gh auth refresh -s project,read:project`, then fall through to the stub path below.
429
+ **Preflight:** `gh auth status` (or `python3 scripts/board_bootstrap.py preflight --json` for the machine-readable answer). If `project`/`read:project` scope is missing, surface the exact remedy: `gh auth refresh -s project,read:project`, then fall through to the stub path below — without the write scope, never attempt creation.
411
430
 
412
- **Discover (the success path):** `gh project list --owner <remote-owner> --format json`. If **exactly one** project clearly belongs to this repo's owner, read its fields: `gh project field-list <number> --owner <owner> --format json`. Record each field's `id`, `name`, `dataType` (single-select / number / text), and for single-selects the option `id`s. Write `board-sync.md` with `state=filled`, `mode=github-projects-v2`, the project node id + repo + the discovered field/option IDs, seeded from [board-sync.md](./board-sync.md). Fill the `<!-- board-sync:profile -->` **JSON block** (the machine SSOT `scripts/board_config.py` parses) — replace each `<…>` placeholder under `repo`/`project`/`fields` with the discovered value; leave the convention values (`labels`/`branchPrefixes`/`prMarkers`/`headings`) at their seeded defaults unless this project's conventions differ.
431
+ **Discover (the existing-board path):** `gh project list --owner <remote-owner> --format json`. If **exactly one** project clearly belongs to this repo's owner, read its fields: `gh project field-list <number> --owner <owner> --format json`. Record each field's `id`, `name`, `dataType` (single-select / number / text), and for single-selects the option `id`s. Write `board-sync.md` with `state=filled`, `mode=github-projects-v2`, the project node id + repo + the discovered field/option IDs, seeded from [board-sync.md](./board-sync.md). Fill the `<!-- board-sync:profile -->` **JSON block** (the machine SSOT `scripts/board_config.py` parses) — replace each `<…>` placeholder under `repo`/`project`/`fields` with the discovered value; leave the convention values (`labels`/`branchPrefixes`/`prMarkers`/`headings`) at their seeded defaults unless this project's conventions differ.
413
432
 
414
433
  **Map the status roles (`fields.status.roles`):** the seeded map carries the recommended English stage names (`Idea/Triaged/Spec/In Progress/Review/Done`). Reconcile it against the **discovered** Status option names: an exact (case-sensitive) match keeps its seeded entry; for every role whose seeded name is NOT among the discovered options, ask the user which of their actual option names plays that role (a stage they don't have may be omitted from the map — say so). Never guess a mapping from similarity. Finish with a validation pass: every role value in the map MUST appear in `fields.status.options`, else fix before writing `state=filled`.
415
434
 
416
- **Fallback (the single catch-all no board / >1 / ambiguous / scope error / read failure):** do **not** auto-create a board (`gh project create` alone cannot provision the Status options + workflow fields a board needs). Write `board-sync.md` with `state=stub`, `mode=github-projects-v2`, and inline **instructions**: which fields the workflow profile needs (a Status single-select with your stage options; optionally a Wave number, a Cluster text, a Spec-Path / Plan-Path text), how to create the board in the GitHub UI / `gh`, and "then run `/setup-workflow` againit will discover and fill the IDs." Retryable.
435
+ **Offer creation (no board found, scope present):** a fresh repo can reach a working board in this run `gh project create` provisions the shell and `gh project field-create --single-select-options` provisions the Status single-select together with its options. Minting a GitHub Project is an outward, user-visible action, so **ask first and never create a board without an explicit yes**. Name the stage options the board will get (the seed's `fields.status.roles` values) in the question, and edit those role values in the seed *before* creating if the user wants a different stage vocabulary they become the board's Status options verbatim. Present exactly two choices:
436
+
437
+ - **Create it now** — run the one creation path (never hand-assemble the `gh` sequence):
438
+
439
+ ```bash
440
+ python3 scripts/board_bootstrap.py create --owner <owner> --repo <owner>/<repo> \
441
+ --title "<repo> Workflow" --seed <path of this skill folder>/board-sync.md \
442
+ --out docs/agents/board-sync.md
443
+ ```
444
+
445
+ It re-checks the destination and the scope before it writes anything, creates the project, the Status single-select (option names read from the seed profile's `fields.status.roles` — the board's own stage vocabulary, never a literal), and the Wave / Cluster / Spec-Path / Plan-Path fields, then discovers the real field/option IDs via `gh project field-list` and writes `docs/agents/board-sync.md` at `state=filled`, `mode=github-projects-v2` **from that readback**. Show the user `--dry-run` output first if they want to see the exact sequence. Report the created project's number and URL, and that its Status roles now resolve.
446
+
447
+ - **Not now** — create nothing and take the stub path below unchanged.
448
+
449
+ **A non-zero exit is a stub, never a partial claim.** The helper writes no profile when the destination is consumer-owned, the scope is missing (exit 3), or creation/readback/validation fails — so a half-created board can never produce a profile claiming fields that do not exist. On any non-zero exit, relay its message (it names the project it created, if any, so the user can finish or delete it), then take the stub path; never hand-write a `state=filled` profile and never retry creation without asking again.
450
+
451
+ **Fallback (the single catch-all — declined / no board / >1 / ambiguous / scope error / read failure / creation failure):** write `board-sync.md` with `state=stub`, `mode=github-projects-v2`, and inline **instructions**: which fields the workflow profile needs (a Status single-select with your stage options; optionally a Wave number, a Cluster text, a Spec-Path / Plan-Path text), how to create the board in the GitHub UI / `gh`, and "then run `/setup-workflow` again — it will discover and fill the IDs." Retryable.
417
452
 
418
453
  **Optional — Phase field + saved Views (Program route only):** never
419
454
  auto-discovered or auto-created, unlike the fields above — a Phase field's
@@ -520,6 +555,9 @@ unknown consumer keys share this profile. Apply the transition and hook
520
555
  ownership rules from [worktree-lifecycle.md](./worktree-lifecycle.md),
521
556
  [workflow-advisories.md](./workflow-advisories.md), and
522
557
  [safety-guardrails.md](./safety-guardrails.md) transactionally.
558
+ The landing-artifact policy is deletion authority: it is always an explicit
559
+ consumer-reviewed setup value, never an update-time default or an inference
560
+ from ignored paths.
523
561
 
524
562
  For Memory Lifecycle, use only the deterministic setup helper from Section A5.
525
563
  Do not copy templates with shell commands or edit the capability profile by
@@ -3,8 +3,8 @@
3
3
  How the board-managed workflow skills (`to-prd`, `to-issues`, `board-to-waves`, …) address this project's GitHub-Projects board. A board stores its fields under opaque GraphQL IDs that differ per board, so they are recorded here rather than hardcoded in any skill.
4
4
 
5
5
  `/setup-workflow` writes this file in one of three states (see the sentinel on the first line):
6
- - **`state=filled`** — the IDs below were discovered from your board.
7
- - **`state=stub`** — no single board was found; fill the IDs by creating the board (below) and re-running `/setup-workflow`.
6
+ - **`state=filled`** — the IDs below were discovered from your board (an existing one, or the one `/setup-workflow` offered to create for you).
7
+ - **`state=stub`** — no single board was filled: you declined the creation offer, `gh` lacked the `project` scope, several boards were ambiguous, or creation failed. Create the board (below) and re-run `/setup-workflow`.
8
8
  - **`state=not-applicable`** (`mode=none`) — this project does not use a GitHub-Projects board.
9
9
 
10
10
  ## Board profile — fields the workflow skills use
@@ -106,6 +106,10 @@ their titles unchanged; set it once to match your board's language.
106
106
 
107
107
  ## If the IDs are not yet filled (stub)
108
108
 
109
+ **Let setup create the board.** Ensure `gh` has the scopes (`gh auth refresh -s project,read:project`) and re-run `/setup-workflow`: with no board and the scope present it *offers* to create one. On your yes it runs `scripts/board_bootstrap.py`, which creates the project plus the Status single-select (its options named after the `roles` map below) and the Wave / Cluster / Spec-Path / Plan-Path fields, reads the real IDs back, and rewrites this file at `state=filled`. Nothing is created without that explicit yes, and a failed run leaves this stub in place rather than a half-true profile.
110
+
111
+ **Or build it by hand:**
112
+
109
113
  1. Create a GitHub-Projects (v2) board for this owner and add the fields above (at minimum a `Status` single-select with your stage options — the recommended stage names are `Idea, Triaged, Spec, In Progress, Review, Done`, matching the seeded `roles` defaults).
110
114
  2. Ensure `gh` has the scopes: `gh auth refresh -s project,read:project`.
111
115
  3. Re-run `/setup-workflow` — it discovers the board (`gh project field-list`) and fills the IDs here automatically.
@@ -74,6 +74,40 @@ key. Consumer values are never normalized on adoption.
74
74
  Empty command surfaces are honest inactive defaults. Setup recommends concrete
75
75
  project commands from the tools already present, then asks before activating.
76
76
 
77
+ ## Profile glob dialect
78
+
79
+ `baseline.sourceGlobs`, `preRefactor.surfaces[].globs`, and
80
+ `stopChecks.surfaces[].globs` are matched by the one shared dialect in
81
+ `scripts/profile_globs.py` — the same matcher Worktree Lifecycle uses for its
82
+ scratch and landing-artifact policies. There is no second matcher and no
83
+ per-capability variant:
84
+
85
+ - `*` matches any run of characters inside one path segment, never `/`.
86
+ - `?` matches exactly one character inside one path segment.
87
+ - `[seq]` and `[!seq]` are per-segment character classes; `/` is always a
88
+ separator and never a class member.
89
+ - `**` as a complete segment matches zero or more segments, so a leading `**/`
90
+ also matches the repository root and `dir/**` also matches `dir` itself.
91
+ - Matching is always case-sensitive, on every host filesystem.
92
+ - A pattern must match the whole repository-relative path.
93
+
94
+ Thus `src/**` covers `src/a/b.ts`, `**/*.ts` covers both `index.ts` and
95
+ `src/index.ts`, and `*.ts` covers only a root-level file.
96
+
97
+ A profile written for the older whole-string matcher can therefore change
98
+ meaning. When adopting an existing profile, or after any kit update, review it:
99
+
100
+ ```bash
101
+ python3 scripts/profile_globs.py docs/agents/workflow-capabilities.json
102
+ ```
103
+
104
+ The check names every pattern whose match set narrows or widens, prints the
105
+ concrete witness path that proves the difference, and separately flags patterns
106
+ that only matched case-insensitively on the previous matcher. Exit code 1 means
107
+ at least one pattern needs review. Report the named patterns and let the
108
+ consumer rewrite them; never migrate a pattern automatically. The check reads
109
+ the profile and never edits it.
110
+
77
111
  ## Hook ownership
78
112
 
79
113
  - PreToolUse on Read:
@@ -29,6 +29,7 @@ advisory, and safe cleanup policy as one unit.
29
29
  "operations": [
30
30
  "record-choice",
31
31
  "reconcile-profile-enabled",
32
+ "reconcile-landing-artifact-policy",
32
33
  "reconcile-hook-wiring"
33
34
  ]
34
35
  },
@@ -50,7 +51,8 @@ advisory, and safe cleanup policy as one unit.
50
51
  "state": "existing",
51
52
  "choice": "yes",
52
53
  "operations": [
53
- "adopt-existing"
54
+ "adopt-existing",
55
+ "reconcile-landing-artifact-policy"
54
56
  ]
55
57
  },
56
58
  {
@@ -87,6 +89,50 @@ Derive its glob values from the consumer's ignored planning artefacts; an empty
87
89
  array is valid. Existing values are consumer-owned and remain byte-identical on
88
90
  adoption or rerun. Core never supplies filename defaults.
89
91
 
92
+ Also reconcile an explicit
93
+ `wrapup.landingGeneratedArtifactPatterns` array. Derive candidates from the
94
+ consumer's real landing commands and ignored outputs, show the exact list for
95
+ review, and write it only as part of that explicit setup decision. Never copy
96
+ another repository's values or infer deletion authority from `.gitignore`
97
+ alone. An empty array is valid. Existing configured values remain byte-identical.
98
+ If an existing enabled profile lacks the key, report one actionable setup
99
+ decision and leave the project layer unchanged until the consumer confirms the
100
+ derived list.
101
+
102
+ ## Profile glob dialect
103
+
104
+ Every consumer-profile glob in this kit — Worktree Lifecycle and Workflow
105
+ Advisories alike — is matched by the one shared dialect in
106
+ `scripts/profile_globs.py`. There is no second matcher and no per-capability
107
+ variant:
108
+
109
+ - `*` matches any run of characters inside one path segment, never `/`.
110
+ - `?` matches exactly one character inside one path segment.
111
+ - `[seq]` and `[!seq]` are per-segment character classes; `/` is always a
112
+ separator and never a class member.
113
+ - `**` as a complete segment matches zero or more segments, so a leading `**/`
114
+ also matches the repository root and `dir/**` also matches `dir` itself.
115
+ - Matching is always case-sensitive, on every host filesystem.
116
+ - A pattern must match the whole repository-relative path.
117
+
118
+ Thus `**/__pycache__/**` covers root and nested caches, while `dist-kit/*` does
119
+ not cover `dist-kit/a/b`.
120
+
121
+ When adopting an existing profile, or after any kit update, review it before
122
+ trusting its patterns:
123
+
124
+ ```bash
125
+ python3 scripts/profile_globs.py docs/agents/workflow-capabilities.json
126
+ ```
127
+
128
+ The check names every pattern whose match set narrows or widens against that
129
+ key's legacy matcher, prints the concrete witness path that proves the
130
+ difference, and marks the keys that carry deletion authority. Exit code 1 means
131
+ at least one pattern needs review. Report the named patterns and let the
132
+ consumer rewrite them; never migrate a pattern automatically and never treat a
133
+ widened deletion-authority pattern as an accepted default. The check reads the
134
+ profile and never edits it.
135
+
90
136
  The shipped read-only inventory is
91
137
  `python3 scripts/worktree-lifecycle/cleanup.py sweep`. The same profile powers
92
138
  its branch issue extraction and scratch-only cleanup verdicts.
@@ -105,6 +105,52 @@ One call covers: push → PR create/reuse (+ drift markers merged into the body)
105
105
 
106
106
  STOP → diagnose in the main conversation, fix, re-run `land` (an already-merged PR resumes at teardown).
107
107
 
108
+ Before merge, the committed worktree policy only nominates exact landing
109
+ evidence. After merge, cleanup reloads `docs/agents/workflow-capabilities.json`
110
+ from canonical `origin/main`; only the identical merged, attempt-bound scratch
111
+ and generator policy authorizes each evidence path and deletion. A mismatch is
112
+ a hard STOP that preserves every file—never bypass it with the unmerged branch
113
+ profile.
114
+
115
+ Landing provenance has one explicit relinquish route. If the STOP names an
116
+ unfinished or drifted landing attempt and the ambiguous files cannot be restored
117
+ to their frozen identities, run:
118
+
119
+ ```bash
120
+ python3 scripts/wrapup-land.py land --branch "<branch>" --abandon-unfinished-attempt
121
+ ```
122
+
123
+ This archives the started or frozen attempt receipt only. It deletes and claims
124
+ no files, works even when the older creation baseline is missing, and returns
125
+ before push, PR, or merge. The next `land` conservatively protects every current
126
+ matching path; classify or move those blockers first. Do not use relinquish for
127
+ an exact unchanged frozen attempt: a normal retry validates that evidence and
128
+ resumes deterministically.
129
+
130
+ An attempt journaled under the superseded v1 contract is **legacy, not
131
+ corruption**. The STOP says so explicitly and names the same
132
+ `--abandon-unfinished-attempt` archive route; archival stays valid for a v1 and
133
+ a v2 receipt even without a creation baseline or a local main profile, and no
134
+ generated or consumer file is deleted or claimed on that route.
135
+
136
+ If canonical `origin/main` changed its scratch or generator policy *after* the
137
+ attempt started, the PR can merge and cleanup then STOPs with `worktree cleanup
138
+ policy differs from merged canonical origin/main`. That STOP stays fail-closed
139
+ and names its one supported route out:
140
+
141
+ ```bash
142
+ python3 scripts/wrapup-land.py land --branch "<branch>" --recover-canonical-cleanup
143
+ ```
144
+
145
+ This resumes the teardown of an **already-merged** worktree only. It never reads
146
+ the stale worktree candidate policy: it re-reads the merged canonical policy,
147
+ requires the branch to already be an ancestor of canonical `origin/main`, and
148
+ revalidates each frozen landing identity against that canonical policy before
149
+ the ordinary shared assessment and removal run. Evidence the canonical policy no
150
+ longer names, a changed identity, and any pre-existing or foreign file are hard
151
+ STOPs that delete nothing. Re-running after a successful teardown is a no-op.
152
+ The flag is mutually exclusive with `--abandon-unfinished-attempt`.
153
+
108
154
  ### 6 · Post-merge (agent)
109
155
  - **Sibling propagation:** for each `drift_markers` entry in the land report, append the note to the target issue's `vor_bau` section + re-stamp its `plan_revision`. Log-based markers → **write directly, then show what was written where** (mandatory report — visibility moved from a pre-gate into the report, decision 2026-07-06); fallback candidates the user hasn't confirmed yet → confirm first. Program context widens the target set to unbuilt wave-stubs/leaves and the Program-PRD itself — same append-only mechanism. **Exception:** appends to the Program-PRD or unbuilt wave-stubs do **not** re-stamp `plan_revision` — that stays reserved for structural wave-plan edits via the `to-waves` escalation path; a mere drift note must not stale-block published stubs.
110
156
  - **Anchor close:** report says `anchor_complete: true` → `gh issue close <anchor> -c "Wave complete — all slices merged via PR #<pr>."` and verify board status Done. The guard keeps anchors away from every auto-close — this verified close is the only close path; without it the anchor stays silently open after the last slice. **Then re-run the upward propagation**: the land-time `program-sync` ran BEFORE this close, so on a wave-completing slice the Wellenplan still shows 🔄 and the Phasen-Gate stays unchecked — after the board shows Done, run `python3 scripts/board-sync.py program-sync <program-prd#>` once more (the report's `program_sync.program` names it; skip when the report says the parent is not a program). Board auto-rules can lag the close (Close→Done race) — verify Done first, that's what the token reads.