prizmkit 1.1.160 → 1.1.161

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 (25) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +9 -6
  3. package/bundled/dev-pipeline/prizmkit_runtime/runtime_commit.py +145 -110
  4. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
  5. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +2 -2
  6. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +1 -1
  7. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +1 -1
  8. package/bundled/dev-pipeline/templates/sections/runtime-commit-handoff.md +9 -15
  9. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +8 -5
  10. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +4 -2
  11. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +4 -2
  12. package/bundled/dev-pipeline/tests/test_runtime_commit.py +170 -66
  13. package/bundled/dev-pipeline/tests/test_unified_cli.py +172 -4
  14. package/bundled/rules/prizm/prizm-commit-workflow.md +12 -5
  15. package/bundled/skills/_metadata.json +1 -1
  16. package/bundled/skills/prizmkit-code-review/SKILL.md +5 -5
  17. package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +4 -5
  18. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +4 -6
  19. package/bundled/skills/prizmkit-code-review/scripts/render_review_report.py +1 -4
  20. package/bundled/skills/prizmkit-committer/SKILL.md +47 -63
  21. package/bundled/skills/prizmkit-test/SKILL.md +2 -2
  22. package/bundled/skills/prizmkit-test/references/test-coverage-model.md +1 -1
  23. package/bundled/skills/prizmkit-workflow/SKILL.md +4 -6
  24. package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +10 -8
  25. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.1.160",
3
- "bundledAt": "2026-07-28T13:41:42.840Z",
4
- "bundledFrom": "cd9688c"
2
+ "frameworkVersion": "1.1.161",
3
+ "bundledAt": "2026-07-29T09:12:57.978Z",
4
+ "bundledFrom": "5ba2929"
5
5
  }
@@ -59,17 +59,19 @@ def _file_size(path: Path | None) -> int:
59
59
  return 0
60
60
 
61
61
 
62
- def _progress_signature(path: Path | None) -> tuple[str, str]:
62
+ def _progress_signature(path: Path | None) -> tuple[str, str, bool]:
63
63
  if path is None or not path.is_file():
64
- return "", ""
64
+ return "", "", False
65
65
  try:
66
66
  data = json.loads(path.read_text(encoding="utf-8"))
67
67
  except (OSError, json.JSONDecodeError):
68
- return "", ""
68
+ return "", "", False
69
+ current_tool = data.get("current_tool")
70
+ tool_active = isinstance(current_tool, str) and bool(current_tool.strip())
69
71
  child_signature = str(data.get("child_activity_signature") or "")
70
72
  signature = json.dumps(
71
73
  [
72
- data.get("current_tool") or "",
74
+ current_tool or "",
73
75
  int(data.get("message_count") or 0),
74
76
  int(data.get("total_tool_calls") or 0),
75
77
  int(data.get("active_subagent_count") or 0),
@@ -81,7 +83,7 @@ def _progress_signature(path: Path | None) -> tuple[str, str]:
81
83
  ],
82
84
  separators=(",", ":"),
83
85
  )
84
- return signature, child_signature
86
+ return signature, child_signature, tool_active
85
87
 
86
88
 
87
89
  def observe_heartbeat_state(
@@ -91,13 +93,14 @@ def observe_heartbeat_state(
91
93
  """Observe log/progress activity and compute staleness."""
92
94
  now = time.time()
93
95
  log_size = _file_size(config.session_log)
94
- progress_signature, child_signature = _progress_signature(config.progress_file)
96
+ progress_signature, child_signature, tool_active = _progress_signature(config.progress_file)
95
97
  prev_log_size = previous.log_size if previous else 0
96
98
  progressed = (
97
99
  previous is None
98
100
  or log_size > prev_log_size
99
101
  or (progress_signature and progress_signature != previous.progress_signature)
100
102
  or (child_signature and child_signature != previous.child_activity_signature)
103
+ or tool_active
101
104
  )
102
105
  if progressed:
103
106
  last_seen_epoch = now
@@ -9,17 +9,15 @@ import subprocess
9
9
  import tempfile
10
10
  from dataclasses import dataclass
11
11
  from pathlib import Path, PurePosixPath
12
- from typing import Mapping, Sequence
12
+ from typing import Mapping
13
13
 
14
14
  from .checkpoint_state import (
15
15
  load_checkpoint_state,
16
16
  semantic_state_key,
17
17
  validate_checkpoint_data,
18
18
  )
19
- from .gitops import HIDDEN_TOOL_WORKTREE_EXCLUDES
20
-
21
19
  REQUEST_FILENAME = "runtime-commit-request.json"
22
- REQUEST_SCHEMA_VERSION = 1
20
+ REQUEST_SCHEMA_VERSION = 2
23
21
  _HEX_COMMIT = re.compile(r"^[0-9a-fA-F]{40,64}$")
24
22
  _FORBIDDEN_EXACT_PATHS = {
25
23
  ".claude/settings.local.json",
@@ -37,8 +35,6 @@ _SENSITIVE_NAMES = {
37
35
  "secrets.json",
38
36
  }
39
37
  _SENSITIVE_SUFFIXES = {".pem", ".p12", ".pfx"}
40
- _SUPPORT_ROOT_FILES = {"agents.md", "skills-lock.json"}
41
- _SUPPORT_ROOT_DIRS = {".claude", ".codebuddy", ".codex", ".agents", ".pi"}
42
38
 
43
39
 
44
40
  @dataclass(frozen=True)
@@ -48,7 +44,6 @@ class RuntimeCommitRequest:
48
44
  artifact_dir: str
49
45
  base_head: str
50
46
  commit_message: str
51
- intended_paths: tuple[str, ...]
52
47
  runtime_commit_hash: str = ""
53
48
 
54
49
 
@@ -120,17 +115,37 @@ def finalize_runtime_commit(
120
115
  return RuntimeCommitResult("failed", "premature_runtime_commit_hash")
121
116
  if state.semantic_complete:
122
117
  return RuntimeCommitResult("failed", "completed_checkpoint_without_commit")
123
- changed, status_error = _task_visible_changed_paths(
118
+ unmerged = _git(root, "ls-files", "-u")
119
+ if unmerged.return_code != 0:
120
+ return RuntimeCommitResult("failed", "git_unmerged_state_unavailable")
121
+ if unmerged.stdout.strip():
122
+ return RuntimeCommitResult("failed", "unresolved_merge_state")
123
+ changed, status_error = _workspace_changed_paths(
124
124
  root,
125
- artifact_dir=artifact,
126
125
  checkpoint_path=checkpoint,
127
126
  request_path=request_path,
128
127
  )
129
128
  if status_error:
130
129
  return RuntimeCommitResult("failed", status_error)
131
- if set(changed) != set(request.intended_paths):
132
- return RuntimeCommitResult("failed", "intended_paths_mismatch")
133
- staged = _stage_intended_paths(root, request.intended_paths)
130
+ if not changed:
131
+ return RuntimeCommitResult("failed", "empty_workspace_change")
132
+ unsafe = next((path for path in changed if _sensitive_commit_path(path)), "")
133
+ if unsafe:
134
+ return RuntimeCommitResult("failed", "sensitive_workspace_path")
135
+ bookkeeping_staged = _staged_bookkeeping_paths(
136
+ root,
137
+ checkpoint_path=checkpoint,
138
+ request_path=request_path,
139
+ )
140
+ if bookkeeping_staged is None:
141
+ return RuntimeCommitResult("failed", "git_staged_set_unavailable")
142
+ if bookkeeping_staged:
143
+ return RuntimeCommitResult("failed", "runtime_bookkeeping_staged")
144
+ staged = _stage_workspace(
145
+ root,
146
+ checkpoint_path=checkpoint,
147
+ request_path=request_path,
148
+ )
134
149
  if staged.return_code != 0:
135
150
  return RuntimeCommitResult("failed", "git_stage_failed")
136
151
  staged_paths = _git(
@@ -139,10 +154,19 @@ def finalize_runtime_commit(
139
154
  if staged_paths.return_code != 0:
140
155
  return RuntimeCommitResult("failed", "git_staged_set_unavailable")
141
156
  staged_set = {path for path in staged_paths.stdout.split("\0") if path}
142
- if staged_set != set(request.intended_paths):
143
- return RuntimeCommitResult("failed", "staged_paths_mismatch")
157
+ if staged_set != set(changed):
158
+ return RuntimeCommitResult("failed", "staged_workspace_mismatch")
144
159
  if not staged_set:
145
160
  return RuntimeCommitResult("failed", "empty_staged_change")
161
+ unstaged_candidates = _workspace_has_unstaged_candidates(
162
+ root,
163
+ checkpoint_path=checkpoint,
164
+ request_path=request_path,
165
+ )
166
+ if unstaged_candidates is None:
167
+ return RuntimeCommitResult("failed", "git_workspace_verification_failed")
168
+ if unstaged_candidates:
169
+ return RuntimeCommitResult("failed", "workspace_changed_during_staging")
146
170
  committed = _git(
147
171
  root,
148
172
  "-c", "core.hooksPath=/dev/null",
@@ -204,7 +228,7 @@ def _load_request(
204
228
  if not isinstance(raw, Mapping) or raw.get("schema_version") != REQUEST_SCHEMA_VERSION:
205
229
  return None, "commit_request_invalid_schema"
206
230
  required_keys = {
207
- "schema_version", "artifact_dir", "base_head", "commit_message", "intended_paths",
231
+ "schema_version", "artifact_dir", "base_head", "commit_message",
208
232
  }
209
233
  allowed_keys = required_keys | {"runtime_commit_hash"}
210
234
  if not required_keys.issubset(raw) or not set(raw).issubset(allowed_keys):
@@ -223,18 +247,6 @@ def _load_request(
223
247
  if not message or "\n" in message or len(message) > 240:
224
248
  return None, "commit_request_message_invalid"
225
249
 
226
- raw_paths = raw.get("intended_paths")
227
- if not isinstance(raw_paths, list) or not raw_paths:
228
- return None, "commit_request_paths_invalid"
229
- intended: list[str] = []
230
- for value in raw_paths:
231
- relative = _safe_relative_path(value)
232
- if relative is None or _forbidden_commit_path(relative, expected_artifact):
233
- return None, "commit_request_path_unsafe"
234
- intended.append(relative)
235
- if len(intended) != len(set(intended)):
236
- return None, "commit_request_paths_duplicate"
237
-
238
250
  runtime_commit_hash = str(raw.get("runtime_commit_hash") or "").strip()
239
251
  if runtime_commit_hash and not _HEX_COMMIT.fullmatch(runtime_commit_hash):
240
252
  return None, "runtime_commit_hash_invalid"
@@ -243,45 +255,25 @@ def _load_request(
243
255
  artifact_dir=expected_artifact,
244
256
  base_head=base_head.lower(),
245
257
  commit_message=message,
246
- intended_paths=tuple(intended),
247
258
  runtime_commit_hash=runtime_commit_hash.lower(),
248
259
  ), ""
249
260
 
250
261
 
251
- def _safe_relative_path(value: object) -> str | None:
252
- text = str(value or "").strip().replace("\\", "/")
253
- if not text or text.startswith("/") or re.match(r"^[A-Za-z]:/", text):
254
- return None
255
- path = PurePosixPath(text)
256
- if path.is_absolute() or any(part in {"", ".", ".."} for part in path.parts):
257
- return None
258
- return path.as_posix()
259
-
260
-
261
- def _forbidden_commit_path(path: str, artifact_dir: str) -> bool:
262
+ def _sensitive_commit_path(path: str) -> bool:
262
263
  lower = path.lower()
263
264
  name = PurePosixPath(lower).name
264
265
  if lower in _FORBIDDEN_EXACT_PATHS:
265
266
  return True
266
267
  if lower == ".git" or lower.startswith(".git/"):
267
268
  return True
268
- if _known_support_path(lower):
269
- return True
270
- artifact_prefix = artifact_dir.lower().rstrip("/") + "/"
271
- if lower in {
272
- artifact_prefix + REQUEST_FILENAME,
273
- artifact_prefix + "workflow-checkpoint.json",
274
- }:
275
- return True
276
269
  if name in _SENSITIVE_NAMES or (name.startswith(".env.") and name != ".env.example"):
277
270
  return True
278
271
  return PurePosixPath(lower).suffix in _SENSITIVE_SUFFIXES
279
272
 
280
273
 
281
- def _task_visible_changed_paths(
274
+ def _workspace_changed_paths(
282
275
  project_root: Path,
283
276
  *,
284
- artifact_dir: Path,
285
277
  checkpoint_path: Path,
286
278
  request_path: Path,
287
279
  ) -> tuple[tuple[str, ...], str]:
@@ -293,56 +285,31 @@ def _task_visible_changed_paths(
293
285
  "--untracked-files=all",
294
286
  "--",
295
287
  ".",
296
- *HIDDEN_TOOL_WORKTREE_EXCLUDES,
297
288
  )
298
289
  if result.return_code != 0:
299
290
  return (), "git_status_failed"
300
- local_paths = {
301
- checkpoint_path.relative_to(project_root).as_posix(),
302
- request_path.relative_to(project_root).as_posix(),
303
- }
304
- visible = []
305
- for path in _status_record_paths(result.stdout):
306
- if path in local_paths:
307
- continue
308
- if _known_support_path(path.lower()) or path.lower() in _FORBIDDEN_EXACT_PATHS:
309
- continue
310
- visible.append(path)
291
+ local_paths = _runtime_bookkeeping_paths(
292
+ project_root,
293
+ checkpoint_path=checkpoint_path,
294
+ request_path=request_path,
295
+ )
296
+ visible = [
297
+ path for path in _status_record_paths(result.stdout)
298
+ if path not in local_paths
299
+ ]
311
300
  return tuple(dict.fromkeys(visible)), ""
312
301
 
313
302
 
314
- def _known_support_path(lower_path: str) -> bool:
315
- path = PurePosixPath(lower_path)
316
- parts = path.parts
317
- if lower_path in _SUPPORT_ROOT_FILES:
318
- return True
319
- if parts and parts[0] in _SUPPORT_ROOT_DIRS:
320
- return True
321
- if lower_path == ".prizmkit-worktree" or lower_path.startswith(
322
- ".prizmkit-worktree/"
323
- ):
324
- return True
325
- if (
326
- len(parts) >= 2
327
- and parts[0].startswith(".")
328
- and parts[1] in {"worktree", "worktrees"}
329
- ):
330
- return True
331
- if lower_path in {".prizmkit/manifest.json"}:
332
- return True
333
- if any(
334
- lower_path == prefix or lower_path.startswith(prefix + "/")
335
- for prefix in (
336
- ".prizmkit/dev-pipeline",
337
- ".prizmkit/state",
338
- ".prizmkit/scripts",
339
- )
340
- ):
341
- return True
342
- return (
343
- lower_path.startswith(".prizmkit/specs/")
344
- and path.name in {REQUEST_FILENAME, "workflow-checkpoint.json"}
345
- )
303
+ def _runtime_bookkeeping_paths(
304
+ project_root: Path,
305
+ *,
306
+ checkpoint_path: Path,
307
+ request_path: Path,
308
+ ) -> set[str]:
309
+ return {
310
+ checkpoint_path.relative_to(project_root).as_posix(),
311
+ request_path.relative_to(project_root).as_posix(),
312
+ }
346
313
 
347
314
 
348
315
  def _status_record_paths(stdout: str) -> tuple[str, ...]:
@@ -363,22 +330,85 @@ def _status_record_paths(stdout: str) -> tuple[str, ...]:
363
330
  return tuple(dict.fromkeys(path for path in changed if path))
364
331
 
365
332
 
366
- def _stage_intended_paths(project_root: Path, paths: Sequence[str]) -> _GitResult:
367
- tracked: list[str] = []
368
- untracked: list[str] = []
369
- for path in paths:
370
- pathspec = f":(top,literal){path}"
371
- indexed = _git(project_root, "ls-files", "--error-unmatch", "--", pathspec)
372
- (tracked if indexed.return_code == 0 else untracked).append(pathspec)
373
- if tracked:
374
- updated = _git(project_root, "add", "-u", "--", *tracked)
375
- if updated.return_code != 0:
376
- return updated
377
- if untracked:
378
- added = _git(project_root, "add", "--", *untracked)
379
- if added.return_code != 0:
380
- return added
381
- return _GitResult(0, "", "")
333
+ def _staged_bookkeeping_paths(
334
+ project_root: Path,
335
+ *,
336
+ checkpoint_path: Path,
337
+ request_path: Path,
338
+ ) -> tuple[str, ...] | None:
339
+ staged = _git(
340
+ project_root,
341
+ "diff",
342
+ "--cached",
343
+ "--name-only",
344
+ "--no-renames",
345
+ "-z",
346
+ )
347
+ if staged.return_code != 0:
348
+ return None
349
+ local_paths = _runtime_bookkeeping_paths(
350
+ project_root,
351
+ checkpoint_path=checkpoint_path,
352
+ request_path=request_path,
353
+ )
354
+ return tuple(
355
+ path for path in staged.stdout.split("\0")
356
+ if path and path in local_paths
357
+ )
358
+
359
+
360
+ def _stage_workspace(
361
+ project_root: Path,
362
+ *,
363
+ checkpoint_path: Path,
364
+ request_path: Path,
365
+ ) -> _GitResult:
366
+ staged = _git(project_root, "add", "-A", "--", ".")
367
+ if staged.return_code != 0:
368
+ return staged
369
+ local_pathspecs = [
370
+ f":(top,literal){path}"
371
+ for path in sorted(
372
+ _runtime_bookkeeping_paths(
373
+ project_root,
374
+ checkpoint_path=checkpoint_path,
375
+ request_path=request_path,
376
+ )
377
+ )
378
+ ]
379
+ return _git(project_root, "reset", "-q", "HEAD", "--", *local_pathspecs)
380
+
381
+
382
+ def _workspace_has_unstaged_candidates(
383
+ project_root: Path,
384
+ *,
385
+ checkpoint_path: Path,
386
+ request_path: Path,
387
+ ) -> bool | None:
388
+ local_paths = _runtime_bookkeeping_paths(
389
+ project_root,
390
+ checkpoint_path=checkpoint_path,
391
+ request_path=request_path,
392
+ )
393
+ unstaged = _git(project_root, "diff", "--name-only", "--no-renames", "-z")
394
+ untracked = _git(
395
+ project_root,
396
+ "ls-files",
397
+ "--others",
398
+ "--exclude-standard",
399
+ "-z",
400
+ "--",
401
+ ".",
402
+ )
403
+ if unstaged.return_code != 0 or untracked.return_code != 0:
404
+ return None
405
+ candidates = {
406
+ path
407
+ for output in (unstaged.stdout, untracked.stdout)
408
+ for path in output.split("\0")
409
+ if path and path not in local_paths
410
+ }
411
+ return bool(candidates)
382
412
 
383
413
 
384
414
  def _matches_prepared_commit(project_root: Path, request: RuntimeCommitRequest, head: str) -> bool:
@@ -392,12 +422,17 @@ def _matches_prepared_commit(project_root: Path, request: RuntimeCommitRequest,
392
422
  return False
393
423
  parent_list = parents.stdout.strip().split()
394
424
  changed = {path for path in paths.stdout.split("\0") if path}
425
+ artifact_prefix = request.artifact_dir.rstrip("/") + "/"
426
+ causal_bookkeeping = {
427
+ artifact_prefix + REQUEST_FILENAME,
428
+ artifact_prefix + "workflow-checkpoint.json",
429
+ }
395
430
  return (
396
431
  len(parent_list) == 1
397
432
  and parent_list[0].lower() == request.base_head.lower()
398
433
  and subject.stdout.strip() == request.commit_message
399
434
  and bool(changed)
400
- and changed == set(request.intended_paths)
435
+ and changed.isdisjoint(causal_bookkeeping)
401
436
  )
402
437
 
403
438
 
@@ -38,7 +38,7 @@ Complete the feature in headless non-interactive mode using the active atomic sk
38
38
  5. Invoke `/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/`. Validate its report/result pair. For `TEST_NEEDS_FIXES`, classify the concrete correction into an allowed `repair_scope`; unknown/unsafe scope blocks, while a safe in-budget route re-enters Implement and requires fresh Review, configured verification, and Test. Do not duplicate the Skill's internal repair loop.
39
39
  6. Write `completion-summary.json` before retrospective and commit preparation.
40
40
  7. Invoke `/prizmkit-retrospective` with the exact artifact root, non-`.prizmkit/` `change_paths`, and `change_summary`; validate `outcome=RETRO_COMPLETE`.
41
- 8. Before commit preparation, classify paths by semantic role rather than a blanket `.prizmkit/**` rule. Safe Git-visible framework paths may be exact task output; ignored paths remain absent, and exact Runtime bookkeeping/installed support, host support, Secrets, unrelated, or unknown paths remain excluded or blocking. No path is force-added and no ignore policy is changed. After validating every required artifact, invoke `/prizmkit-committer operation=prepare-runtime-commit artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/ evidence_paths=<exact paths> excluded_paths=.prizmkit/specs/{{FEATURE_SLUG}}/workflow-checkpoint.json request_path=.prizmkit/specs/{{FEATURE_SLUG}}/runtime-commit-request.json`. Require `COMMIT_REQUEST_READY`; this session then records `COMMIT_PENDING` and stops. The Python runtime executes and verifies the local commit.
41
+ 8. Before commit preparation, validate the complete Git-visible workspace regardless of when or where each correct change originated. Final Review/Test authority must cover every behavior-affecting visible change; ignored paths remain local, while merge conflicts, Secrets, sensitive content, stale evidence, and known incorrect or unverifiable content block. After validating every required artifact, invoke `/prizmkit-committer operation=prepare-runtime-commit artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/ evidence_paths=<exact paths> request_path=.prizmkit/specs/{{FEATURE_SLUG}}/runtime-commit-request.json`. Require a schema-v2 pathless request and `COMMIT_REQUEST_READY`; this session then records `COMMIT_PENDING` and stops. The Python runtime re-enumerates and commits the complete non-ignored workspace with only exact causal request/checkpoint bookkeeping omitted.
42
42
 
43
43
  ## Failure Capture
44
44
 
@@ -448,9 +448,9 @@ Invoke `/prizmkit-retrospective` with the exact artifact root, exact non-`.prizm
448
448
 
449
449
  **6c. Runtime commit preparation**
450
450
 
451
- Before commit preparation, classify paths by semantic role rather than a blanket `.prizmkit/**` exclusion. Safe Git-visible framework paths may be justified and included as exact task output; ignored paths remain naturally absent. Exact Runtime request/checkpoint/state and installed Runtime/host support stay outside `intended_paths`; Secrets, unrelated, and unknown Git-visible paths block. Never force-add or change/interpret project ignore policy.
451
+ Before commit preparation, validate the complete Git-visible workspace regardless of when or where each correct change originated. Code Review and Test authority must cover every behavior-affecting visible change. Ignored paths remain naturally absent; merge conflicts, Secrets, sensitive content, stale evidence, and known incorrect or unverifiable content block. Never force-add or change/interpret project ignore policy.
452
452
 
453
- After this injected session validates every required artifact, invoke `/prizmkit-committer operation=prepare-runtime-commit artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/ evidence_paths=<exact paths> excluded_paths=.prizmkit/specs/{{FEATURE_SLUG}}/workflow-checkpoint.json request_path=.prizmkit/specs/{{FEATURE_SLUG}}/runtime-commit-request.json`. Require `COMMIT_REQUEST_READY`; this session records `COMMIT_PENDING` and stops. The Python runtime validates the request, executes/verifies the local commit, and finalizes checkpoint `COMMITTED`.
453
+ After this injected session validates every required artifact, invoke `/prizmkit-committer operation=prepare-runtime-commit artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/ evidence_paths=<exact paths> request_path=.prizmkit/specs/{{FEATURE_SLUG}}/runtime-commit-request.json`. Require a schema-v2 pathless request and `COMMIT_REQUEST_READY`; this session records `COMMIT_PENDING` and stops. The Python runtime re-enumerates and commits the complete non-ignored workspace, omitting only exact causal request/checkpoint bookkeeping, then verifies the local commit and finalizes checkpoint `COMMITTED`.
454
454
 
455
455
  ## Critical Paths
456
456
 
@@ -245,7 +245,7 @@ Invoke `/prizmkit-retrospective` with the exact artifact root, exact non-`.prizm
245
245
 
246
246
  **c. Runtime commit preparation**
247
247
 
248
- After this injected session validates every required artifact, invoke `/prizmkit-committer operation=prepare-runtime-commit artifact_dir=.prizmkit/bugfix/{{BUG_ID}}/ evidence_paths=<exact paths> excluded_paths=.prizmkit/bugfix/{{BUG_ID}}/workflow-checkpoint.json request_path=.prizmkit/bugfix/{{BUG_ID}}/runtime-commit-request.json`. Require `COMMIT_REQUEST_READY`; this session records `COMMIT_PENDING` and stops. The Python runtime validates the request, executes/verifies the local commit, finalizes checkpoint `COMMITTED`, and controls any later publication.
248
+ After this injected session validates every required artifact and confirms final Review/Test authority covers the complete behavior-affecting Git-visible workspace, invoke `/prizmkit-committer operation=prepare-runtime-commit artifact_dir=.prizmkit/bugfix/{{BUG_ID}}/ evidence_paths=<exact paths> request_path=.prizmkit/bugfix/{{BUG_ID}}/runtime-commit-request.json`. Require a schema-v2 pathless request and `COMMIT_REQUEST_READY`; this session records `COMMIT_PENDING` and stops. Ignored files remain local. The Python runtime re-enumerates and commits the complete non-ignored workspace with only exact causal request/checkpoint bookkeeping omitted, verifies the local commit, finalizes checkpoint `COMMITTED`, and controls any later publication.
249
249
 
250
250
  ---
251
251
 
@@ -210,7 +210,7 @@ Write `.prizmkit/refactor/{{REFACTOR_ID}}/completion-summary.json` for downstrea
210
210
 
211
211
  Complete `refactor-report` and `completion-summary` before retrospective. Then invoke `/prizmkit-retrospective` with the exact artifact root, exact non-`.prizmkit/` `change_paths`, and a concise `change_summary`. Validate `outcome=RETRO_COMPLETE`; do not stage or commit.
212
212
 
213
- After this injected session validates every required artifact, invoke `/prizmkit-committer operation=prepare-runtime-commit artifact_dir=.prizmkit/refactor/{{REFACTOR_ID}}/ evidence_paths=<exact paths> excluded_paths=.prizmkit/refactor/{{REFACTOR_ID}}/workflow-checkpoint.json request_path=.prizmkit/refactor/{{REFACTOR_ID}}/runtime-commit-request.json`. Require `COMMIT_REQUEST_READY`; this session records `COMMIT_PENDING` and stops. The Python runtime executes/verifies the local commit, finalizes checkpoint `COMMITTED`, and controls any later publication.
213
+ After this injected session validates every required artifact and confirms final Review/Test authority covers the complete behavior-affecting Git-visible workspace, invoke `/prizmkit-committer operation=prepare-runtime-commit artifact_dir=.prizmkit/refactor/{{REFACTOR_ID}}/ evidence_paths=<exact paths> request_path=.prizmkit/refactor/{{REFACTOR_ID}}/runtime-commit-request.json`. Require a schema-v2 pathless request and `COMMIT_REQUEST_READY`; this session records `COMMIT_PENDING` and stops. Ignored files remain local. The Python runtime re-enumerates and commits the complete non-ignored workspace with only exact causal request/checkpoint bookkeeping omitted, verifies the local commit, finalizes checkpoint `COMMITTED`, and controls any later publication.
214
214
 
215
215
  ---
216
216
 
@@ -5,31 +5,25 @@ The Python runtime, not the AI session, executes the pipeline's local Git commit
5
5
  After Code Review, all configured verification, `TEST_PASS`, family reports/summaries, and retrospective have completed:
6
6
 
7
7
  1. As the session orchestrator, validate every required stage artifact and result first. Atomic Skills do not perform this sequencing check.
8
- 2. Invoke `/prizmkit-committer` with the same artifact root, exact readiness evidence, and request path:
8
+ 2. Re-inventory the complete final Git-visible workspace: staged, unstaged, added, deleted, renamed, copied, and non-ignored untracked content. Confirm Code Review and Test authority covers every behavior-affecting change, including correct work that predated this session or lives in framework, generated, instruction, lock, or host-support paths. If production behavior changed after those gates, re-enter them before commit preparation.
9
+ 3. Invoke `/prizmkit-committer` with the same artifact root, exact readiness evidence, and request path:
9
10
 
10
11
  ```text
11
- /prizmkit-committer operation=prepare-runtime-commit artifact_dir={{ARTIFACT_DIR}} evidence_paths=<exact validated artifact paths> excluded_paths={{ARTIFACT_DIR}}/workflow-checkpoint.json request_path={{ARTIFACT_DIR}}/runtime-commit-request.json
12
+ /prizmkit-committer operation=prepare-runtime-commit artifact_dir={{ARTIFACT_DIR}} evidence_paths=<exact validated artifact paths> request_path={{ARTIFACT_DIR}}/runtime-commit-request.json
12
13
  ```
13
14
 
14
- 3. The committer validates only the supplied evidence and final diff, then classifies paths by semantic role before writing the request:
15
- - safe Git-visible `.prizmkit/**` requirement output is admitted like any other exact task path and needs no documentation-specific evidence;
16
- - ignored paths remain absent and are never force-added; project ignore policy is not changed or interpreted;
17
- - exact Runtime request/checkpoint/state, installed Runtime/host payloads, and local host settings remain support/bookkeeping outside the task commit;
18
- - global Secret checks still apply, and every unrelated or unknown Git-visible path blocks rather than disappearing.
19
-
20
- It then rejects sensitive/unrelated/unknown paths, generates one Conventional Commit message, and writes this request without staging or committing:
15
+ 4. The committer validates the complete final workspace without a caller-supplied path manifest or provenance classification. Ignored paths remain local and never block or require force-add. Merge conflicts, Secrets, sensitive local content, known incorrect or incoherent changes, unverifiable content, and stale readiness evidence still block. It generates one truthful Conventional Commit message and writes this request without staging or committing:
21
16
 
22
17
  ```json
23
18
  {
24
- "schema_version": 1,
19
+ "schema_version": 2,
25
20
  "artifact_dir": "{{ARTIFACT_DIR}}",
26
21
  "base_head": "<current HEAD commit hash>",
27
- "commit_message": "<type>(<scope>): <description>",
28
- "intended_paths": ["<exact project-relative changed path>"]
22
+ "commit_message": "<type>(<scope>): <description>"
29
23
  }
30
24
  ```
31
25
 
32
- 4. Require the stage-local result `COMMIT_REQUEST_READY`. Then this injected session—not the Skill—marks `prizmkit-committer` `in_progress` with `stage_result=COMMIT_PENDING` through the checkpoint helper. Do not mark it completed or write `COMMITTED`.
33
- 5. Stop normal work after the valid request and checkpoint handoff exist. Do not run `git add`, `git commit`, `git commit --amend`, merge, push, or create additional files.
26
+ 5. Require the stage-local result `COMMIT_REQUEST_READY`. Then this injected session—not the Skill—marks `prizmkit-committer` `in_progress` with `stage_result=COMMIT_PENDING` through the checkpoint helper. Do not mark it completed or write `COMMITTED`.
27
+ 6. Stop normal work after the valid request and checkpoint handoff exist. Do not run `git add`, `git commit`, `git commit --amend`, merge, push, or create additional task files.
34
28
 
35
- The Runtime validates exact task-owned path coverage outside recognized semantic support/bookkeeping, does not reject a safe path solely because it is under `.prizmkit/**`, stages only requested literal paths, verifies staged/committed set equality and the Git receipt, and only then writes the Runtime-owned receipt/checkpoint and performs configured integration. Those post-commit writes are never inserted retroactively into the committed snapshot or allowed to falsify integration. Unknown Git-visible paths are not silently ignored: they produce a mismatch and preserve the task checkout for recovery. A malformed, stale, unsafe, incomplete, or contradictory request also blocks without deleting user data.
29
+ The Runtime validates the pathless schema, base, complete non-ignored workspace, sensitive path safeguards, merge state, and causal bookkeeping boundary. It stages the complete workspace, omitting only the exact active request/checkpoint whose final receipt cannot truthfully precede the commit; verifies a non-empty commit with the expected parent and message; records its Git receipt; and only then finalizes `COMMITTED` and performs configured integration. A malformed, stale, unsafe, incomplete, or contradictory handoff blocks without deleting user data.
@@ -1381,6 +1381,9 @@ class TestHeadlessPromptCleanupF033:
1381
1381
  assert artifact in prompt
1382
1382
  assert "Atomic Skills write only their stage-owned artifacts and domain results" in prompt
1383
1383
  assert "COMMIT_REQUEST_READY" in prompt
1384
+ assert '"schema_version": 2' in prompt
1385
+ assert '"intended_paths"' not in prompt
1386
+ assert "complete final Git-visible workspace" in prompt
1384
1387
  assert "workflow_state_path" not in prompt
1385
1388
  assert "outcome=RETRO_COMPLETE" in prompt
1386
1389
  assert "Never recreate the testing-local repair loop" in prompt
@@ -1554,12 +1557,12 @@ class TestFeatureBootstrapShellExtraction:
1554
1557
  assert "text search . --pattern \"123-prompt-cleanup\" --json" in tier3
1555
1558
  assert "text count .prizmkit/specs/123-prompt-cleanup/plan.md --pattern \"- [ ]\"" in tier3
1556
1559
  assert "/prizmkit-committer operation=prepare-runtime-commit" in tier3
1557
- assert "Require `COMMIT_REQUEST_READY`; this session records `COMMIT_PENDING`" in tier3
1558
- assert "The Python runtime validates the request" in tier3
1559
- assert "classify paths by semantic role rather than a blanket `.prizmkit/**` exclusion" in tier3
1560
- assert "Safe Git-visible framework paths" in tier3
1560
+ assert "Require a schema-v2 pathless request and `COMMIT_REQUEST_READY`" in tier3
1561
+ assert "The Python runtime re-enumerates and commits the complete non-ignored workspace" in tier3
1562
+ assert "validate the complete Git-visible workspace" in tier3
1563
+ assert "Code Review and Test authority must cover every behavior-affecting visible change" in tier3
1564
+ assert "Ignored paths remain naturally absent" in tier3
1561
1565
  assert "Never force-add or change/interpret project ignore policy" in tier3
1562
- assert "unknown Git-visible paths block" in tier3
1563
1566
  assert "git add" not in tier3
1564
1567
  assert "git commit" not in tier3
1565
1568
 
@@ -596,8 +596,10 @@ class TestBugfixCheckpointGeneration:
596
596
  assert "operation=prepare-runtime-commit" in prompt
597
597
  assert "NEVER ask for user confirmation" in prompt
598
598
  assert "The Python runtime, not the AI session, executes" in prompt
599
- assert "safe Git-visible `.prizmkit/**` requirement output is admitted" in prompt
600
- assert "unknown Git-visible path blocks" in prompt
599
+ assert "complete final Git-visible workspace" in prompt
600
+ assert '"schema_version": 2' in prompt
601
+ assert '"intended_paths"' not in prompt
602
+ assert "Ignored paths remain local" in prompt
601
603
  assert "push_authorized\": true" not in prompt
602
604
  assert ".prizmkit/bugfix/B-001/" in prompt
603
605
  assert "write the session status" not in prompt.lower()
@@ -435,8 +435,10 @@ class TestRefactorCheckpointGeneration:
435
435
  assert "local_commit_authorized" not in prompt
436
436
  assert "operation=prepare-runtime-commit" in prompt
437
437
  assert "The Python runtime, not the AI session, executes" in prompt
438
- assert "safe Git-visible `.prizmkit/**` requirement output is admitted" in prompt
439
- assert "unknown Git-visible path blocks" in prompt
438
+ assert "complete final Git-visible workspace" in prompt
439
+ assert '"schema_version": 2' in prompt
440
+ assert '"intended_paths"' not in prompt
441
+ assert "Ignored paths remain local" in prompt
440
442
  assert "automatic push" not in prompt.lower()
441
443
  assert "push_authorized\": true" not in prompt
442
444
  assert "write the session status" not in prompt.lower()