workstate-bootstrap 0.5.2__tar.gz → 0.6.0__tar.gz

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 (38) hide show
  1. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/CHANGELOG.md +41 -15
  2. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/PKG-INFO +9 -9
  3. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/README.md +7 -7
  4. workstate_bootstrap-0.6.0/docs/tasks/AHMCP-48-skills-imply-lifecycle-hoist-task-plan.md +263 -0
  5. workstate_bootstrap-0.6.0/docs/tasks/AHMCP-49-managed-server-versions-constant-task-plan.md +186 -0
  6. workstate_bootstrap-0.6.0/docs/tasks/AHMCP-50-mcp-sync-config-only-subcommand-task-plan.md +246 -0
  7. workstate_bootstrap-0.6.0/docs/tasks/AHMCP-56-cross-harness-install-manifest-task-plan.md +355 -0
  8. workstate_bootstrap-0.6.0/docs/tasks/AHMCP-57-stale-symlink-repoint-task-plan.md +274 -0
  9. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/pyproject.toml +2 -2
  10. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/src/workstate_bootstrap/__init__.py +1 -1
  11. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/src/workstate_bootstrap/cli.py +6 -6
  12. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/src/workstate_bootstrap/install.py +224 -28
  13. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/src/workstate_bootstrap/mcp_sync.py +6 -1
  14. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/src/workstate_bootstrap/subcommands.py +47 -10
  15. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_bootstrap_install_rehearsal.py +19 -19
  16. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_cli_profile.py +5 -5
  17. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_doctor_repair_sync.py +3 -3
  18. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_ensure_hooks_path_make.py +1 -1
  19. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_install.py +128 -83
  20. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_install_manifest_walker.py +2 -2
  21. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_install_profile_all_lifecycle.py +2 -2
  22. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_install_profiles.py +5 -5
  23. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_manifest_build.py +1 -1
  24. workstate_bootstrap-0.6.0/tests/test_mcp_legacy_rename.py +375 -0
  25. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_mcp_sync_cli.py +2 -2
  26. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_mcp_sync_e2e.py +2 -2
  27. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_mcp_sync_malformed.py +1 -1
  28. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_mcp_sync_prune.py +1 -1
  29. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_mcp_sync_unit.py +1 -1
  30. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_render_seam.py +2 -2
  31. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_root_visible_task_plans.py +2 -2
  32. workstate_bootstrap-0.6.0/tests/test_runtime_path_migration.py +269 -0
  33. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_subcommands.py +25 -25
  34. workstate_bootstrap-0.6.0/uv.lock +1754 -0
  35. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/.gitignore +0 -0
  36. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/src/workstate_bootstrap/__main__.py +0 -0
  37. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/__init__.py +0 -0
  38. {workstate_bootstrap-0.5.2 → workstate_bootstrap-0.6.0}/tests/test_package_metadata.py +0 -0
@@ -2,6 +2,32 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## [0.6.0] — 2026-05-30
6
+
7
+ ### Changed
8
+
9
+ - **MCP server identity cutover (Plan 0013 Slice B).** Default managed servers
10
+ register under the canonical `workstate-handoff-mcp` /
11
+ `workstate-orchestrator-mcp` names. `LEGACY_MCP_SERVER_RENAMES` +
12
+ `_legacy_prune_for()` dedup and forward-rewrite a stale `agent-*-mcp`
13
+ registration across all three config surfaces (`.mcp.json`,
14
+ `.vscode/mcp.json`, `.codex/config.toml`) so a re-install collapses the
15
+ duplicate to one canonical entry — fixing the "MCP servers not loading"
16
+ symptom caused by deep-merge preserving both old and new entries.
17
+ - **Default managed server pins bumped** to `mcp-workstate-handoff@0.12.0` and
18
+ `mcp-workstate-orchestrator@0.5.0`. `workstate-protocol` floor raised to
19
+ `>=0.1.6`.
20
+
21
+ ### Added
22
+
23
+ - **Runtime path migration `.agentic/` → `.workstate/` and
24
+ `docs/agentic/` → `docs/workstate/` (Plan 0013 Slice D).**
25
+ `migrate_runtime_paths()` / `plan_runtime_path_migration()` run on
26
+ `install`/`update`: idempotent, archive-backed (a both-present collision
27
+ moves the legacy tree aside rather than overwriting), dry-run-capable, and
28
+ routed through the shared `workstate_protocol.paths` constants. `doctor`
29
+ flags a surviving legacy tree as `legacy_runtime_path`.
30
+
5
31
  ## [0.5.2] — 2026-05-20
6
32
 
7
33
  ### Changed
@@ -15,7 +41,7 @@
15
41
 
16
42
  ### Fixed
17
43
 
18
- - **WORKSTATE-REF-57 — stale shared-surface symlinks now repointed on rerun.**
44
+ - **AHMCP-57 — stale shared-surface symlinks now repointed on rerun.**
19
45
  When a consumer was installed pre-v0.2.0 (legacy root layout
20
46
  `<clone>/<surface>`) and the layout subsequently moved into
21
47
  `<clone>/packages/workstate-system/<surface>`, the target-side
@@ -35,7 +61,7 @@
35
61
 
36
62
  ### Changed
37
63
 
38
- - **WORKSTATE-REF-56 — cross-harness install manifest is now the single source of
64
+ - **AHMCP-56 — cross-harness install manifest is now the single source of
39
65
  truth for hook adapter wiring.** `config/agent-workflows/portable_commands.json`
40
66
  schema v2 introduces a top-level `hooks[]` array; install dispatches
41
67
  adapter rows through a manifest-driven walker (closed-set operation
@@ -73,17 +99,17 @@
73
99
 
74
100
  - **Bump default managed MCP server pins** to `mcp-workstate-handoff@0.11.1`
75
101
  and `mcp-workstate-orchestrator@0.4.5` so consumer repos pick up the
76
- WORKSTATE-REF-54 (multi-active CURRENT_TASK projection, import/export
102
+ AHMCP-54 (multi-active CURRENT_TASK projection, import/export
77
103
  malformed-payload rejection, target_branch/worktree_path/plan_path
78
- preservation) and WORKSTATE-REF-55 (compaction env-var namespace
79
- consolidation under `AGENT_HANDOFF_COMPACTION_*` with `WORKSTATE_*` kept
104
+ preservation) and AHMCP-55 (compaction env-var namespace
105
+ consolidation under `AGENT_HANDOFF_COMPACTION_*` with `AHMCP_*` kept
80
106
  as a deprecated alias) fixes by default.
81
107
 
82
108
  ## [0.4.1] — 2026-05-09
83
109
 
84
110
  ### Fixed
85
111
 
86
- - **`--profile all` now performs the lifecycle hoist** (WORKSTATE-REF-48). The
112
+ - **`--profile all` now performs the lifecycle hoist** (AHMCP-48). The
87
113
  legacy default profile shipped lifecycle-referencing skills
88
114
  (`branch-lifecycle`, `tdd`, `incremental-implementation`,
89
115
  `branch-review`, `handoff-lifecycle`) but did not install the
@@ -116,11 +142,11 @@
116
142
  ### Added
117
143
 
118
144
  - **Install profile contract: `--profile {minimal,lifecycle,all}`**
119
- (implementation note / WORKSTATE-REF-40 implementation note.5.a). The CLI now accepts an explicit
145
+ (Plan 0009 / AHMCP-40 Slice 1.5.a). The CLI now accepts an explicit
120
146
  install profile flag and honors it across the manifest layers, so
121
147
  consumer repos can pick a smaller hoist surface than the default.
122
- - **Hoist `Makefile.d/plans.mk` + `git-plan-cat.sh` stub** (implementation note /
123
- WORKSTATE-REF-38 implementation note). Consumer repos installed via `workstate-bootstrap`
148
+ - **Hoist `Makefile.d/plans.mk` + `git-plan-cat.sh` stub** (Plan 0007 /
149
+ AHMCP-38 Slice 0). Consumer repos installed via `workstate-bootstrap`
124
150
  pick up `make plan-show / plan-edit / plans-list / plan-register`
125
151
  out of the box; the targets shell out to `uvx mcp-workstate-handoff`
126
152
  under the hood.
@@ -134,14 +160,14 @@
134
160
 
135
161
  ## [0.3.1] — 2026-05-03
136
162
 
137
- - **implementation note BR-01 — raise `workstate-protocol` lower bound to
163
+ - **Plan 0006 BR-01 — raise `workstate-protocol` lower bound to
138
164
  `>=0.1.2,<0.2.0`.** Bootstrap's default install path invokes
139
165
  `uvx mcp-workstate-handoff`, which imports `workstate_protocol.branch_naming`
140
166
  at startup; the previous `>=0.1.0` floor let `uvx` resolve a protocol
141
167
  release missing the module, crashing init-state on a fresh install. A
142
168
  new packaging test (`tests/test_package_metadata.py`) pins the floor
143
169
  so the declaration cannot silently drift back below the contract.
144
- - **implementation note implementation note — install rehearsal pins six-hook surface +
170
+ - **Plan 0006 Slice 5 — install rehearsal pins six-hook surface +
145
171
  helper materialization.** `SHARED_GIT_HOOK_NAMES` now includes
146
172
  `pre-commit` (in addition to `post-checkout`, `post-commit`,
147
173
  `post-merge`, `post-rewrite`, `pre-push`); the install rehearsal
@@ -169,7 +195,7 @@
169
195
  --workspace-root . serve-stdio` into `.mcp.json`,
170
196
  `.vscode/mcp.json`, and `.codex/config.toml`, so external clients
171
197
  start runnable MCP servers from a fresh install or update.
172
- - **`regenerate-task-views` harness hook contract dropped (implementation note).**
198
+ - **`regenerate-task-views` harness hook contract dropped (Plan 0005).**
173
199
  Bootstrap no longer materializes any Claude / VS Code / Codex hook
174
200
  wiring that invokes `regenerate-task-views`; `DASHBOARD.txt` is now
175
201
  auto-regenerated server-side inside `mcp-workstate-handoff` on every
@@ -182,7 +208,7 @@
182
208
 
183
209
  ## 0.3.0 — 2026-04-28
184
210
 
185
- - **Install-time state provisioning (implementation note).** `install` now runs
211
+ - **Install-time state provisioning (Plan 0003).** `install` now runs
186
212
  the handoff server's `init-state` after surface/config materialization
187
213
  but before `core.hooksPath` is set, so a fresh install ends with a
188
214
  schema-current `.task-state/handoff.db` and `.task-state/exports/`
@@ -206,7 +232,7 @@
206
232
  gated on `.mcp.json` being present in the manifest's `configs` array
207
233
  so config-only installs (`--no-mcp-servers`) do not produce
208
234
  false-positive drift.
209
- - **`switch_task` cold-start fix.** implementation note implementation note (in
235
+ - **`switch_task` cold-start fix.** Plan 0003 Slice 1 (in
210
236
  `mcp-workstate-handoff` 0.5.0+) drops `BranchMismatchError` from
211
237
  `switch_task`; the cold-start cycle (register task → `switch_task`
212
238
  → first content write) now completes from any branch. Branch
@@ -229,7 +255,7 @@
229
255
  `packages/workstate-system/` (the workstate monorepo layout) with
230
256
  fallback to the clone root for legacy hoisted overlays. Fixes
231
257
  `BootstrapManifestValidationError: required surface 'scripts/hooks' was
232
- not materialized` against monorepo refs at or after implementation note step 1.
258
+ not materialized` against monorepo refs at or after Plan 0002 step 1.
233
259
  - Rehearsal fixture (`fake_remote_with_generator`) now mirrors the real
234
260
  monorepo layout so this regression cannot return silently.
235
261
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workstate-bootstrap
3
- Version: 0.5.2
3
+ Version: 0.6.0
4
4
  Summary: Bootstrap CLI that hoists the shared workstate-system surface into consumer repos.
5
5
  Project-URL: Homepage, https://github.com/darce/workstate
6
6
  Project-URL: Source, https://github.com/darce/workstate/tree/main/packages/workstate-bootstrap
@@ -11,7 +11,7 @@ License: Proprietary
11
11
  Requires-Python: >=3.11
12
12
  Requires-Dist: pyyaml>=6
13
13
  Requires-Dist: tomlkit>=0.13
14
- Requires-Dist: workstate-protocol<0.2.0,>=0.1.4
14
+ Requires-Dist: workstate-protocol<0.2.0,>=0.1.6
15
15
  Provides-Extra: dev
16
16
  Requires-Dist: mcp-workstate-handoff; (python_version >= '3.12') and extra == 'dev'
17
17
  Requires-Dist: pytest>=8; extra == 'dev'
@@ -117,20 +117,20 @@ installer implementation in
117
117
  | ------------------------------------ | ---------- | ----------- |
118
118
  | `scripts/hooks/` | shared | symlink |
119
119
  | `.github/hooks/` | shared | symlink |
120
- | `docs/agentic/contracts/` | shared | symlink |
121
- | `docs/agentic/rules/` | shared | symlink |
120
+ | `docs/workstate/contracts/` | shared | symlink |
121
+ | `docs/workstate/rules/` | shared | symlink |
122
122
  | `Makefile.d/` non-excluded children | shared | carved dir |
123
123
  | `scripts/workstate/` non-excluded children | shared | carved dir |
124
124
  | `.github/prompts/` | generated | real dir |
125
- | `.agentic/generated/plugins/workstate-system/base/` | generated | real dir |
126
- | `.agentic/generated/plugins/workstate-system/effective/` | generated | real dir |
125
+ | `.workstate/generated/plugins/workstate-system/base/` | generated | real dir |
126
+ | `.workstate/generated/plugins/workstate-system/effective/` | generated | real dir |
127
127
  | `.mcp.json` | generated | real file |
128
128
  | `.vscode/mcp.json` | generated | real file |
129
129
  | `.codex/config.toml` | generated | real file |
130
130
  | `core.hooksPath` git config | generated | git config |
131
131
  | `.task-state/handoff.db` | runtime | sqlite |
132
132
  | `.task-state/exports/` | runtime | dir |
133
- | `.agentic/remote/` | bootstrap | git clone |
133
+ | `.workstate/remote/` | bootstrap | git clone |
134
134
  | `.workstate-bootstrap.json` | bootstrap | manifest |
135
135
 
136
136
  `.task-state/` is provisioned by the handoff server's `init-state`
@@ -161,13 +161,13 @@ regenerates it through `workstate-bootstrap install`.
161
161
  `install`, `update`, `doctor`, or `repair` for a non-default root;
162
162
  bootstrap records that path so later update/doctor/repair runs reuse
163
163
  it. Override-aware installs generate effective plugin trees under
164
- `.agentic/generated/plugins/workstate-system/effective/{claude,codex}`
164
+ `.workstate/generated/plugins/workstate-system/effective/{claude,codex}`
165
165
  and point marketplace pins at those generated trees.
166
166
  - `install` and `update` preserve plugin override files by default.
167
167
  `--reset-overrides` is the explicit destructive path; it removes only
168
168
  the resolved override root, refuses dirty git worktrees unless
169
169
  `--backup` is supplied, and archives backups under
170
- `.agentic/override-backups/<timestamp>/` before removal.
170
+ `.workstate/override-backups/<timestamp>/` before removal.
171
171
 
172
172
  ## Development
173
173
 
@@ -98,20 +98,20 @@ installer implementation in
98
98
  | ------------------------------------ | ---------- | ----------- |
99
99
  | `scripts/hooks/` | shared | symlink |
100
100
  | `.github/hooks/` | shared | symlink |
101
- | `docs/agentic/contracts/` | shared | symlink |
102
- | `docs/agentic/rules/` | shared | symlink |
101
+ | `docs/workstate/contracts/` | shared | symlink |
102
+ | `docs/workstate/rules/` | shared | symlink |
103
103
  | `Makefile.d/` non-excluded children | shared | carved dir |
104
104
  | `scripts/workstate/` non-excluded children | shared | carved dir |
105
105
  | `.github/prompts/` | generated | real dir |
106
- | `.agentic/generated/plugins/workstate-system/base/` | generated | real dir |
107
- | `.agentic/generated/plugins/workstate-system/effective/` | generated | real dir |
106
+ | `.workstate/generated/plugins/workstate-system/base/` | generated | real dir |
107
+ | `.workstate/generated/plugins/workstate-system/effective/` | generated | real dir |
108
108
  | `.mcp.json` | generated | real file |
109
109
  | `.vscode/mcp.json` | generated | real file |
110
110
  | `.codex/config.toml` | generated | real file |
111
111
  | `core.hooksPath` git config | generated | git config |
112
112
  | `.task-state/handoff.db` | runtime | sqlite |
113
113
  | `.task-state/exports/` | runtime | dir |
114
- | `.agentic/remote/` | bootstrap | git clone |
114
+ | `.workstate/remote/` | bootstrap | git clone |
115
115
  | `.workstate-bootstrap.json` | bootstrap | manifest |
116
116
 
117
117
  `.task-state/` is provisioned by the handoff server's `init-state`
@@ -142,13 +142,13 @@ regenerates it through `workstate-bootstrap install`.
142
142
  `install`, `update`, `doctor`, or `repair` for a non-default root;
143
143
  bootstrap records that path so later update/doctor/repair runs reuse
144
144
  it. Override-aware installs generate effective plugin trees under
145
- `.agentic/generated/plugins/workstate-system/effective/{claude,codex}`
145
+ `.workstate/generated/plugins/workstate-system/effective/{claude,codex}`
146
146
  and point marketplace pins at those generated trees.
147
147
  - `install` and `update` preserve plugin override files by default.
148
148
  `--reset-overrides` is the explicit destructive path; it removes only
149
149
  the resolved override root, refuses dirty git worktrees unless
150
150
  `--backup` is supplied, and archives backups under
151
- `.agentic/override-backups/<timestamp>/` before removal.
151
+ `.workstate/override-backups/<timestamp>/` before removal.
152
152
 
153
153
  ## Development
154
154
 
@@ -0,0 +1,263 @@
1
+ # AHMCP-48 Task Plan — Bootstrap `all` Profile Implies Lifecycle Hoist (Skills→Lifecycle Drift Fix)
2
+
3
+ > - **Date**: 2026-05-08 EST
4
+ > - **Author**: Claude Opus 4.7
5
+ > - **Project**: agentic-bootstrap
6
+ > - **Owning Epic**: `docs/scopes/bootstrap-consumption-hardening-scope.md`
7
+ > - **Task ID**: AHMCP-48
8
+ > - **Task Plan Status**: `done`
9
+ > - **Target Branch**: `feature/ahmcp-48-skills-imply-lifecycle-hoist`
10
+ > - **Review Coverage Target**: 1
11
+
12
+ ## AHMCP-48. Bootstrap `all` Profile Implies Lifecycle Hoist (Skills→Lifecycle Drift Fix)
13
+
14
+ ## Objective
15
+
16
+ Make `agentic-bootstrap install --profile all` (the legacy/default profile that ships skills) also hoist `Makefile.d/lifecycle.mk` and `scripts/agentic/lifecycle/`, so a consumer cannot end up with the `branch-lifecycle` / `tdd` / `incremental-implementation` / `branch-review` / `handoff-lifecycle` skills referencing `make task-start` / `make slice-start` / `make context` / `make review-ready` / `make handoff-close-check` / `make format-all` while the matching make fragment and Python runner are absent.
17
+
18
+ ## Intake (epic context)
19
+
20
+ - **Scope one-pager**: `docs/scopes/bootstrap-consumption-hardening-scope.md`
21
+ - **Key Q&A decisions**: Intake decisions 1–5 captured inline in the scope doc (MCP recording is a pre-planning blocker — recorded once `record_event` is reachable; until then this plan references the scope's inline table).
22
+ - **Not-Doing**: bootstrap will not write managed `.gitignore` blocks for harness-created files (`.claude/worktrees/`, `.claude/settings.local.json`); that gap is explicitly out of scope per user direction.
23
+
24
+ ## Problem Statement
25
+
26
+ `altcontext-marketing-monorepo` was bootstrapped with the default profile (`all`) and ended up with the branch-lifecycle skill body but no `Makefile.d/lifecycle.mk` and no `scripts/agentic/lifecycle/`. Every `/branch-lifecycle` invocation in that consumer references make targets the install did not provide; the canonical-policy doc paths (`docs/agentic/...`) referenced from the skill are also absent.
27
+
28
+ The cause is structural, not transitional. `install.py:548-557` deliberately scopes the lifecycle hoist to `profile == PROFILE_LIFECYCLE`, while skills are materialized only under `profile == PROFILE_ALL` (`install.py:525-526`). The two profiles are disjoint paths in the same `install()` body, and `all` predates `lifecycle`. The code comment at `install.py:548-552` already anticipates folding the hoist into `all` ("folding the hoist into `all` is a follow-on once the seven-profile contract lands in full") — this task is that follow-on, scoped to the minimum diff that closes the drift class.
29
+
30
+ A consumer cannot fix this from their side: re-running `bootstrap install --profile lifecycle` would re-do the install in lifecycle-only mode and lose every other surface. The drift has to close at install time, in the bootstrap.
31
+
32
+ ## Constraints
33
+
34
+ - The fix must be additive: `--profile lifecycle` must continue to mean "lifecycle-only" (no skills, no generated surfaces). Only `--profile all` gains the lifecycle hoist.
35
+ - `--profile minimal` must continue to be lean. It does not ship skills today; it must not start shipping `lifecycle.mk` either.
36
+ - Idempotency is required: re-running `install --profile all` against a consumer that already has lifecycle hoisted must not duplicate the `Makefile.d/lifecycle.mk` file, the runner directory, or the `-include Makefile.d/*.mk` sentinel block in the consumer `Makefile`.
37
+ - The manifest contract (`agentic_protocol.BootstrapManifest`) must continue to validate; lifecycle entries already appear under `surfaces` with `source: "lifecycle"` and `configs` (the Makefile-include directive) — those shapes do not change.
38
+ - No change to `LIFECYCLE_HOISTS` membership. The two existing entries (`Makefile.d/lifecycle.mk`, `scripts/agentic/lifecycle`) cover the consumer-visible surface today.
39
+ - No change to `--profile lifecycle` semantics. Operators who want lifecycle without skills keep using it.
40
+
41
+ ## Workflow Principles
42
+
43
+ - Land the install-side hoist promotion and its regression test in one slice. They are inseparable: the test asserts the new behavior, and the production code is small enough that splitting them adds bookkeeping without buying revertability.
44
+ - Prefer asserting against materialized filesystem state (the consumer target after `install()` returns) rather than against the manifest dict alone. Manifest entries can drift from disk if a future bug skips a copy step; only filesystem assertions catch that.
45
+ - Do not re-derive the lifecycle hoist logic. Reuse `_install_lifecycle_profile` and `_ensure_consumer_makefile_include` exactly — both are already idempotent.
46
+
47
+ ## Terminology
48
+
49
+ - **Profile**: A named install plan (`minimal`, `lifecycle`, `all`). Selects which surfaces, generated artifacts, and configs the bootstrap materializes.
50
+ - **Surface**: A path materialized into the consumer (skill bodies, hook scripts, `Makefile.d/lifecycle.mk`, the lifecycle runner package).
51
+ - **Lifecycle hoist**: The action of copying `Makefile.d/lifecycle.mk` and `scripts/agentic/lifecycle/` into the consumer and injecting the `-include` sentinel into the consumer `Makefile`. Implemented by `_install_lifecycle_profile` + `_ensure_consumer_makefile_include`.
52
+ - **Skills→Lifecycle drift**: The state where a consumer has skill bodies that reference lifecycle make targets but the runner / Makefile fragment that defines those targets is missing.
53
+
54
+ ## Current State Analysis
55
+
56
+ - `packages/agentic-bootstrap/src/agentic_bootstrap/install.py:104-107` declares `LIFECYCLE_HOISTS = (("Makefile.d/lifecycle.mk", ...), ("scripts/agentic/lifecycle", ...))`.
57
+ - `install.py:380-405` (`_install_lifecycle_profile`) is idempotent: file copies use `shutil.copy2`; directory copies use `shutil.copytree(..., dirs_exist_ok=True)`; missing sources are silently skipped.
58
+ - `install.py:408-432` (`_ensure_consumer_makefile_include`) wraps the `-include Makefile.d/*.mk` directive in `LIFECYCLE_INCLUDE_SENTINEL_BEGIN`/`_END` markers. Re-runs short-circuit when the sentinel is already present (`action='already_present'`); first runs either create or append.
59
+ - `install.py:525-557` is the profile-dispatch body of `install()`. Today:
60
+ - `if profile == PROFILE_ALL`: materializes skills + generated surfaces; runs the workflow generator; writes harness configs; runs init-state.
61
+ - `if profile == PROFILE_LIFECYCLE`: hoists lifecycle, ensures Makefile include.
62
+ - The two branches are independent `if` statements (not `elif`), so a hypothetical caller could already pass `profile=PROFILE_ALL` and then re-run with `profile=PROFILE_LIFECYCLE` — but no caller does this in practice, and the CLI / API rejects multi-profile calls because `profile` is a single string.
63
+ - `install.py:96-103` comment: "Plan 0009 (AHMCP-40) lifecycle profile … destination paths are flat under the consumer root because the runner/Makefile fragment must be reachable from a vanilla consumer with no monorepo packaging knowledge." Confirms the hoist is the canonical mechanism.
64
+ - `install.py:548-552` comment: explicit acknowledgement that folding the hoist into `all` is the planned follow-on.
65
+ - The `branch-lifecycle` skill body (`packages/agentic-system/skills/branch-lifecycle/body.md`) references `make task-start`, `make slice-start`, `make slice-commit`, `make task-finish`, `make review-ready`, `make handoff-close-check`, `make context`, `make format-all`. These are all defined in `Makefile.d/lifecycle.mk` (verified at `packages/agentic-system/Makefile.d/lifecycle.mk:41-58`).
66
+ - The same skill is materialized into the consumer's `.claude/skills/branch-lifecycle/` and `.codex/skills/branch-lifecycle/` under `--profile all` via `_materialize_surfaces` + the workflow generator. The skill body and the make fragment ship from the same monorepo and the same `remote_sha`, so a single `--profile all` install can keep them in sync as long as both are hoisted.
67
+ - Existing test surface in `packages/agentic-bootstrap/tests/`: confirm with the test author whether `tests/test_install.py` (or equivalent) already exercises `_install_lifecycle_profile` under `--profile lifecycle`. The new regression must add coverage for `--profile all` hoisting lifecycle, not duplicate the lifecycle-only assertion.
68
+
69
+ ### Lifecycle-touching skill inventory (closed audit)
70
+
71
+ The fix must close drift for *every* skill body that materializes under `--profile all` and references a lifecycle make target. Two disjoint sources contribute to that set: (a) the generated command map in `packages/agentic-system/config/agent-workflows/portable_commands.json`, and (b) skill body prose that references `make …` targets without exposing them through the manifest's `makefile_target` field. Both are covered by hoisting lifecycle under `PROFILE_ALL` because the skill body and the lifecycle make fragment ship from the same monorepo at the same `remote_sha`.
72
+
73
+ **(a) Generated `portable_commands.json` entries that route through a lifecycle make target.** The `command_id` → `makefile_target`/`secondary_targets` mapping is the closed, manifest-grade list:
74
+
75
+ | `command_id` | Lifecycle target(s) referenced via the manifest | Why `PROFILE_ALL ⇒ lifecycle` covers it |
76
+ | --------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
77
+ | `branch-lifecycle` | `make task-start`, `make review-ready`, `make handoff-close-check`, `make task-finish` | All four are defined in `packages/agentic-system/Makefile.d/lifecycle.mk` and only resolve in the consumer when the hoist runs. |
78
+ | `branch-review` | `make review-run` | Defined in `lifecycle.mk`; same hoist closes the gap. |
79
+ | `handoff-lifecycle` | `make context` | Defined in `lifecycle.mk`; hoist materializes the runner and the include sentinel. |
80
+ | `incremental-implementation`| `make slice-start`, `make slice-commit` | Defined in `lifecycle.mk`; covered by the hoist. |
81
+ | `plan-analyze` | `make plan-analyze DOC=<path>` | Defined in `lifecycle.mk`; covered by the hoist. |
82
+ | `planning-review` | `make plan-review DOC=<path>` | Defined in `lifecycle.mk`; covered by the hoist. |
83
+ | `tdd` | `make slice-start` | Defined in `lifecycle.mk`; covered by the hoist. |
84
+ | `scope` | (none — in-session intake) | No lifecycle dependency; included for completeness so the inventory is closed. |
85
+ | `auto-fix` | (none in the manifest target; **see (b)**) | Manifest does not advertise a lifecycle target, but the skill body does — see body-only audit below. |
86
+ | `review-parallel` | (none in the manifest target; **see (b)**) | Same as auto-fix: covered by the body-only audit. |
87
+
88
+ **(b) Body-only references that bypass the manifest's `makefile_target` field.** These are caught by `grep -n '\bmake \(task-start\|slice-start\|slice-commit\|task-finish\|review-ready\|review-run\|handoff-close-check\|context\|plan-analyze\|plan-review\|format-all\|status\|tasks\|maint-archive-stale\)\b'` against `packages/agentic-system/skills/*/body.md`:
89
+
90
+ | Skill body | Lifecycle target(s) referenced in prose only | Why `PROFILE_ALL ⇒ lifecycle` covers it |
91
+ | ------------------------------------------ | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
92
+ | `skills/auto-fix/body.md` | `make task-start` | Body cites `make task-start TASK=<id>` as the precondition recovery path. Hoisting `lifecycle.mk` under `--profile all` makes that target resolvable. |
93
+ | `skills/review-parallel/body.md` | `make context`, `make maint-archive-stale` | Body cites these as ambiguity-recovery commands. Both are defined in `lifecycle.mk`. |
94
+ | `skills/investigate/body.md` | `make status`, `make tasks`, `make context`, `make maint-archive-stale` | Body cites these as the orientation path before raw MCP reads. All are defined in `lifecycle.mk`. |
95
+ | `skills/branch-lifecycle/body.md` | (already covered by manifest row; body adds `make slice-start`, `make slice-commit`, `make format-all`) | All defined in `lifecycle.mk`; subsumed by manifest-row coverage. |
96
+
97
+ **Audit checklist (cold-start implementer must run before merging the fix):**
98
+
99
+ - [x] Run the manifest-row pass: open `packages/agentic-system/config/agent-workflows/portable_commands.json` and confirm the 7 lifecycle-target rows above (`branch-lifecycle`, `branch-review`, `handoff-lifecycle`, `incremental-implementation`, `plan-analyze`, `planning-review`, `tdd`) still resolve to targets defined in `Makefile.d/lifecycle.mk`. If a new row gains a lifecycle `makefile_target`, the table above must be extended.
100
+ - [x] Run the body-only grep above against `packages/agentic-system/skills/*/body.md` and confirm the only hits outside the manifest-row skills are `auto-fix`, `review-parallel`, and `investigate`. A new hit means a new skill ships with a lifecycle dependency and the inventory must be re-closed before merge.
101
+ - [x] Confirm every target referenced in either pass is defined in `packages/agentic-system/Makefile.d/lifecycle.mk` (so the hoist is sufficient). If a referenced target lives in a non-hoisted fragment (e.g. a future `Makefile.d/review.mk`), the hoist set must be extended — that is out of scope for AHMCP-48 but flagged here so the auditor cannot silently miss it.
102
+
103
+ This closes the drift class for the entire `--profile all` skill payload: every lifecycle-target reference, whether routed through the generated manifest or only in skill prose, is satisfied by the same `_install_lifecycle_profile` + `_ensure_consumer_makefile_include` pair this task promotes into `--profile all`.
104
+
105
+ ## Target Outcome
106
+
107
+ `install(profile=PROFILE_ALL, ...)` returns with the consumer containing:
108
+
109
+ - All `--profile all` surfaces it produces today (skills, generated workflow files, harness configs, init-state).
110
+ - **Plus** `Makefile.d/lifecycle.mk` and `scripts/agentic/lifecycle/` materialized in the consumer (identical to what `--profile lifecycle` produces today).
111
+ - **Plus** the `-include Makefile.d/*.mk` sentinel block injected idempotently into the consumer's `Makefile`.
112
+ - A returned manifest whose `surfaces` list includes both `source: "lifecycle"` entries and whose `configs` list includes the Makefile-include directive entry (when newly added).
113
+
114
+ `install(profile=PROFILE_LIFECYCLE, ...)` and `install(profile=PROFILE_MINIMAL, ...)` are unchanged.
115
+
116
+ `install(profile=PROFILE_ALL, ...)` re-run against the same target is idempotent: no duplicate sentinel block, no duplicate manifest entries (or, if duplication is impossible-by-construction in the manifest writer, no diff in the materialized files).
117
+
118
+ ## Context Loading
119
+
120
+ - Rules: `packages/agentic-system/docs/agentic/rules/development-workflow.md` (packaged anchor; the only loadable rule that bears on this task — bootstrap consumers receive a copy under `.agentic/rules/` after install). `docs/agentic/rules/planning-pipeline.md` does not exist in this monorepo and is intentionally not referenced.
121
+ - Contracts: `packages/agentic-protocol/...` for `BootstrapManifest` shape (only consult if a manifest field changes — this task does not change the shape).
122
+ - Code anchors: `packages/agentic-bootstrap/src/agentic_bootstrap/install.py:96-107`, `:380-432`, `:435-557`. Read once at session start; do not re-load on every slice.
123
+ - Existing tests: `packages/agentic-bootstrap/tests/` — locate the lifecycle profile test at session start. The new test sits next to it.
124
+ - Handoff/MCP state: scope decisions for `SCOPE-bootstrap-consumption-hardening-20260508` (inline in scope doc until MCP recording lands).
125
+ - External docs via `ctx7`: not required for this task.
126
+
127
+ ## Contract and Boundary Impact
128
+
129
+ | Boundary | Owner | Current Contract | Expected Change | Compatibility Needed? | Verification |
130
+ | ---------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
131
+ | `agentic-bootstrap install` profile API | agentic-bootstrap | `profile in {minimal, lifecycle, all}`; `all` → skills + generated + configs; `lifecycle` → hoist lifecycle + Makefile-include | `all` additionally performs the lifecycle hoist + Makefile-include. `minimal` and `lifecycle` semantics unchanged. | yes — `--profile all` is the legacy/default profile; existing consumers must keep working. The change is purely additive (more files materialized) so no caller-visible removal. | New `tests/test_install_profile_all_lifecycle.py` (or extension of existing suite) asserts the hoist + idempotency after `--profile all`. |
132
+ | `BootstrapManifest` (`packages/agentic-protocol`) | agentic-protocol | `surfaces: list[{path, source}]`, `configs: list[{path, action}]`. Lifecycle entries use `source: "lifecycle"`; Makefile-include uses `action: "appended"\|"created"\|"already_present"`. | No shape change. New behavior produces additional list entries already supported by the schema. | no | Existing `BootstrapManifest.model_validate(manifest)` call at `install.py:585` continues to pass. |
133
+ | Consumer `Makefile` | consumer repo | If absent, bootstrap creates one with the sentinel block; if present, bootstrap appends or no-ops. Always wrapped in sentinels for clean uninstall. | No change to the writer logic itself. Now triggered under `--profile all` as well. | yes — must remain idempotent across profile re-runs. | Regression test invokes `install(profile=PROFILE_ALL, ...)` twice against the same target; asserts single sentinel block. |
134
+
135
+ ## Proposed Solution
136
+
137
+ Replace the dedicated `if profile == PROFILE_LIFECYCLE:` block at `install.py:553-557` with a guard that fires for both `PROFILE_ALL` and `PROFILE_LIFECYCLE`. Concretely:
138
+
139
+ ```python
140
+ # Lifecycle hoist now fires for both `all` (so consumers that ship the
141
+ # branch-lifecycle / tdd / etc. skills also get the matching make
142
+ # fragment + runner) and `lifecycle` (the dedicated lean profile).
143
+ if profile in (PROFILE_ALL, PROFILE_LIFECYCLE):
144
+ surfaces.extend(_install_lifecycle_profile(target, clone))
145
+ include_entry = _ensure_consumer_makefile_include(target)
146
+ if include_entry is not None:
147
+ configs.append(include_entry)
148
+ ```
149
+
150
+ Update the in-source comment block at `install.py:548-552` to note the follow-on has landed and reference AHMCP-48.
151
+
152
+ That is the entire production change. `_install_lifecycle_profile` and `_ensure_consumer_makefile_include` are already idempotent; the manifest schema already accepts the entries; the lifecycle hoist sources are already cloned via the existing `_resolve_in_clone` mechanism.
153
+
154
+ The reason a more elaborate "skill registry" design (per the scope's "skills-imply-lifecycle" framing) is not used here: today, skills are materialized only under `--profile all`. There is no profile that ships skills without `all`. Promoting `all` to imply lifecycle therefore covers every drift case in the current code, with the smallest possible diff and no new data structures. If a future profile adds skills, this plan must be reconsidered — captured in Stretch Goals as a follow-up guard.
155
+
156
+ ## Files and Surfaces to Change
157
+
158
+ | Surface | File | Change |
159
+ | ---------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
160
+ | backend (install body) | `packages/agentic-bootstrap/src/agentic_bootstrap/install.py:548-557` | Replace `if profile == PROFILE_LIFECYCLE:` with `if profile in (PROFILE_ALL, PROFILE_LIFECYCLE):`. Update comment block to reflect that the AHMCP-40 follow-on has landed. |
161
+ | backend (update path) | `packages/agentic-bootstrap/src/agentic_bootstrap/subcommands.py:461-492` | **No production change.** `update()` already delegates to `install()` (line 486), so the install fix automatically propagates to the `agentic-bootstrap update` path. Listed here so the auditor sees the surface explicitly and the test below has a named home. |
162
+ | tests (install profile) | `packages/agentic-bootstrap/tests/test_install_profile_all_lifecycle.py` (new file) | New regression test: `install(profile=PROFILE_ALL, ...)` materializes `Makefile.d/lifecycle.mk`, `scripts/agentic/lifecycle/`, and the `-include` sentinel block; second run is idempotent. |
163
+ | tests (update path) | `packages/agentic-bootstrap/tests/test_subcommands.py` (extend existing file) | Add a focused test that calls `update()` against a target previously bootstrapped with `--profile all` and asserts the consumer now contains `Makefile.d/lifecycle.mk`, `scripts/agentic/lifecycle/`, and exactly one `LIFECYCLE_INCLUDE_SENTINEL` block in `Makefile`. This proves the epic-level drift contract holds for the `update` path, not just first-time installs. |
164
+ | changelog | `packages/agentic-bootstrap/CHANGELOG.md` | Entry under the next unreleased version describing the behavior change ("`--profile all` now also hoists `Makefile.d/lifecycle.mk` so consumers that ship lifecycle-referencing skills can run them; `agentic-bootstrap update` inherits the same hoist via its existing `install()` delegation"). |
165
+
166
+ ## Related Files
167
+
168
+ | File | Note |
169
+ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
170
+ | `packages/agentic-system/Makefile.d/lifecycle.mk` | Source for the hoist; not modified, but its existence at the resolved clone path is a precondition for the assertion in the new test. |
171
+ | `packages/agentic-system/scripts/agentic/lifecycle/` | Source for the hoist runner; same precondition. |
172
+ | `packages/agentic-system/skills/branch-lifecycle/body.md` | The skill whose `make task-start` references motivated the fix. Not modified by this task; AHMCP-62 (cold-start runbook + commit-prefix, formerly AHMCP-49) edits it separately. |
173
+ | `packages/agentic-bootstrap/tests/...` (existing lifecycle-profile test) | Locate at session start; the new test reuses any helpers it exposes (e.g. fixture clones, target tempdirs). Do not duplicate the lifecycle-only assertion. |
174
+ | `packages/agentic-protocol/...` (`BootstrapManifest`) | Validation continues unchanged; named for traceability. |
175
+
176
+ ## Verification Strategy
177
+
178
+ - Deterministic tests:
179
+ - `cd packages/agentic-bootstrap && uv run pytest tests/test_install_profile_all_lifecycle.py -x`
180
+ - `cd packages/agentic-bootstrap && uv run pytest tests/test_subcommands.py -x` (must include the new `update`-path lifecycle-hoist assertion; protects against an `update()` regression that breaks the epic's drift contract even though the production code change lives entirely in `install.py`).
181
+ - `cd packages/agentic-bootstrap && uv run pytest -x` (full suite must remain green; in particular the existing `--profile lifecycle` and `--profile minimal` tests).
182
+ - Runtime-parity / environment checks:
183
+ - `cd /tmp && rm -rf bootstrap-smoke && mkdir bootstrap-smoke && cd bootstrap-smoke && git init -q && uvx --from /Users/daniel/Development/agentic-protocol-monorepo/packages/agentic-bootstrap agentic-bootstrap install --remote-url file:///Users/daniel/Development/agentic-protocol-monorepo --remote-ref HEAD --profile all && test -f Makefile.d/lifecycle.mk && test -d scripts/agentic/lifecycle && grep -q LIFECYCLE_INCLUDE_SENTINEL Makefile && echo "smoke OK"` _(adapt the `uvx` invocation to the local install path; the assertion is "lifecycle artifacts present after `--profile all`")._
184
+ - Idempotency: re-run the same install command and assert no second sentinel block (`grep -c "AGENTIC_BOOTSTRAP LIFECYCLE INCLUDE" Makefile` returns `1`).
185
+ - Contract/fixture verification:
186
+ - `BootstrapManifest.model_validate(manifest)` is exercised by the install path itself; no extra fixture needed.
187
+ - Manual verification:
188
+ - Re-bootstrap `altcontext-marketing-monorepo` against the candidate build and confirm `make task-start TASK=… OBJECTIVE=…` runs end-to-end. This is the epic-level done-signal and is verified after the PR lands, not as part of merge gating.
189
+
190
+ ## Slice Delivery
191
+
192
+ ### Slice 1: Promote `--profile all` to imply lifecycle hoist
193
+
194
+ **Goal**: `install(profile=PROFILE_ALL, ...)` materializes `Makefile.d/lifecycle.mk` + `scripts/agentic/lifecycle/` + Makefile `-include` sentinel; behavior under `lifecycle` and `minimal` is unchanged; re-runs are idempotent.
195
+
196
+ Changes:
197
+
198
+ - Replace the `if profile == PROFILE_LIFECYCLE:` guard at `install.py:553` with `if profile in (PROFILE_ALL, PROFILE_LIFECYCLE):`.
199
+ - Update the inline comment block at `install.py:548-552` to record that the AHMCP-40 follow-on has landed under AHMCP-48.
200
+ - Add `tests/test_install_profile_all_lifecycle.py` with at minimum:
201
+ - One test that asserts post-install file presence after `--profile all` (the three expected artifacts).
202
+ - One test that asserts manifest entries (`surfaces` has two `source: "lifecycle"` entries; `configs` has the Makefile-include entry on first run).
203
+ - One test that asserts idempotency on a second run against the same target (single sentinel; no exception).
204
+ - Extend `tests/test_subcommands.py` with one focused test that:
205
+ - Bootstraps a target via `install(profile=PROFILE_ALL, ...)`.
206
+ - Calls `update(target=..., remote_ref=...)` against that target.
207
+ - Asserts `Makefile.d/lifecycle.mk` and `scripts/agentic/lifecycle/` are present after the update and that `Makefile` contains exactly one `LIFECYCLE_INCLUDE_SENTINEL_BEGIN` marker (idempotent across the install+update sequence).
208
+ - This is the epic-level drift contract for the `update` path: even though `update()` delegates to `install()` and inherits the fix indirectly, an explicit assertion prevents a future refactor of `update()` from silently regressing the contract.
209
+ - CHANGELOG entry.
210
+
211
+ Proof:
212
+
213
+ - `uv run pytest tests/test_install_profile_all_lifecycle.py -v` shows three passing cases.
214
+ - `uv run pytest tests/test_subcommands.py -v` shows the new update-path lifecycle-hoist case passing alongside the existing subcommand tests.
215
+ - `uv run pytest tests/` full suite green.
216
+ - Smoke command from the Verification Strategy returns `smoke OK` and the idempotency `grep -c` returns `1`.
217
+
218
+ ## Consolidated Checklist
219
+
220
+ ## Context and Ownership
221
+
222
+ - [x] Loaded `install.py:96-107`, `:380-432`, `:525-557` and the existing lifecycle-profile test in `tests/`.
223
+ - [x] Confirmed `BootstrapManifest` shape does not require updates (entries are already supported).
224
+ - [x] Confirmed `_install_lifecycle_profile` and `_ensure_consumer_makefile_include` are idempotent before relying on them under a second profile.
225
+
226
+ ### Checklist for Slice 1: Promote `--profile all` to imply lifecycle hoist
227
+
228
+ - [x] Edit `install.py` profile-dispatch guard from `== PROFILE_LIFECYCLE` to `in (PROFILE_ALL, PROFILE_LIFECYCLE)`.
229
+ - [x] Update the inline comment block at `install.py:548-552` to reflect AHMCP-48.
230
+ - [x] Add `tests/test_install_profile_all_lifecycle.py` covering filesystem state, manifest shape, and idempotency.
231
+ - [x] Extend `tests/test_subcommands.py` with a focused `update`-path test that asserts `Makefile.d/lifecycle.mk`, `scripts/agentic/lifecycle/`, and a single `LIFECYCLE_INCLUDE_SENTINEL_BEGIN` marker after `install(--profile all)` followed by `update()`.
232
+ - [x] Confirm by code reading that no other entry point bypasses `install()` (`subcommands.py:update`, `subcommands.py:repair`, CLI). Document the audit result in the slice-complete decision.
233
+ - [x] Add CHANGELOG entry under the next unreleased agentic-bootstrap version.
234
+ - [x] Run `uv run pytest -x` and capture green output.
235
+
236
+ ## Review Readiness
237
+
238
+ - [x] No boundary-touching implementation is left without matching contract/test evidence (`BootstrapManifest` validation is exercised by the test path itself; no manifest schema change required).
239
+ - [x] Runtime-parity smoke test (manual, against a temp consumer repo) was run and produced the lifecycle artifacts under `--profile all`.
240
+ - [x] Handoff decision records the install-time behavior change and links to the AHMCP-48 PR.
241
+
242
+ ## Stretch Goals
243
+
244
+ - [ ] Add a defensive runtime check in `install()` that detects "skills materialized but lifecycle hoist did not run" and refuses the install with a clear error. This is a guard against future profiles that add skills without auditing the lifecycle dependency. Stretch because the immediate drift class is closed by the `PROFILE_ALL ⇒ lifecycle` promotion alone.
245
+
246
+ ## Success Criteria
247
+
248
+ - [ ] After `agentic-bootstrap install --profile all` against any target, `Makefile.d/lifecycle.mk`, `scripts/agentic/lifecycle/`, and the `-include` sentinel block are all present in the consumer.
249
+ - [ ] Re-running the same install against the same target produces no duplicate sentinel blocks and no test failures.
250
+ - [ ] `--profile lifecycle` and `--profile minimal` produce byte-identical surface and config sets to their pre-AHMCP-48 outputs (regression coverage in the existing test suite).
251
+ - [ ] After the PR lands and `altcontext-marketing-monorepo` re-bootstraps against the candidate build, `make task-start TASK=<ref> OBJECTIVE="…"` runs end-to-end without "missing target" errors. This is the epic-level done-signal; verified out-of-band, not as a merge gate for AHMCP-48 itself.
252
+
253
+ ---
254
+
255
+ ## Epic Sequencing Note
256
+
257
+ This is task **1 of 4** under the `bootstrap-consumption-hardening` epic. The remaining sub-tasks each need their own task plan:
258
+
259
+ - **AHMCP-62** (formerly AHMCP-49, renamed 2026-05-17 to resolve a collision with the bootstrap `managed-server-versions-constant` plan): Document `<branch-name>:` commit-prefix convention + cold-start worktree-isolation runbook in `branch-lifecycle/body.md` (gaps 2 + 3 of the scope, bundled because both edit the same skill body).
260
+ - **AHMCP-50**: New canonical rule `docs/agentic/rules/planning-artifact-home.md` + optional `git status` guardrail for untracked `docs/scopes|plans|assessments/*` on `main` (gap 4 of the scope).
261
+ - **Verification**: Re-bootstrap `altcontext-marketing-monorepo` clean as the epic-level done-signal (not a code task).
262
+
263
+ These plans are deliberately not drafted in this file. Each gets its own branch and review cycle, per the scope's "one epic, sub-tasks each as their own task plan and branch" decision.