prizmkit 1.1.159 → 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 (83) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/adapters/codebuddy/settings-adapter.js +76 -25
  3. package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +9 -2
  4. package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +9 -6
  5. package/bundled/dev-pipeline/prizmkit_runtime/runtime_commit.py +155 -93
  6. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
  7. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +2 -2
  8. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +1 -1
  9. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +1 -1
  10. package/bundled/dev-pipeline/templates/sections/runtime-commit-handoff.md +9 -15
  11. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +9 -5
  12. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +4 -2
  13. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +4 -2
  14. package/bundled/dev-pipeline/tests/test_runtime_commit.py +228 -69
  15. package/bundled/dev-pipeline/tests/test_unified_cli.py +182 -10
  16. package/bundled/rules/prizm/prizm-commit-workflow.md +14 -7
  17. package/bundled/rules/prizm/prizm-documentation.md +24 -65
  18. package/bundled/rules/prizm/prizm-progressive-loading.md +9 -8
  19. package/bundled/skills/_metadata.json +1 -1
  20. package/bundled/skills/prizmkit-code-review/SKILL.md +5 -5
  21. package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +4 -5
  22. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +4 -6
  23. package/bundled/skills/prizmkit-code-review/scripts/render_review_report.py +1 -4
  24. package/bundled/skills/prizmkit-committer/SKILL.md +50 -59
  25. package/bundled/skills/prizmkit-deploy/SKILL.md +172 -198
  26. package/bundled/skills/prizmkit-deploy/references/capability-contract.md +137 -0
  27. package/bundled/skills/prizmkit-deploy/references/deploy-record-schema.json +1053 -0
  28. package/bundled/skills/prizmkit-deploy/references/deployment-declaration-schema.json +291 -0
  29. package/bundled/skills/prizmkit-deploy/references/legacy-migration.md +90 -0
  30. package/bundled/skills/prizmkit-deploy/references/policy-and-execution.md +170 -0
  31. package/bundled/skills/prizmkit-deploy/references/real-resource-test-policy.md +73 -0
  32. package/bundled/skills/prizmkit-deploy/references/verification-and-recovery.md +164 -0
  33. package/bundled/skills/prizmkit-deploy/scripts/validate-deploy-artifact.py +371 -0
  34. package/bundled/skills/prizmkit-implement/SKILL.md +2 -2
  35. package/bundled/skills/prizmkit-init/SKILL.md +17 -17
  36. package/bundled/skills/prizmkit-init/references/update-supplement.md +5 -5
  37. package/bundled/skills/prizmkit-prizm-docs/SKILL.md +17 -11
  38. package/bundled/skills/prizmkit-prizm-docs/assets/prizm-docs-format.md +95 -155
  39. package/bundled/skills/prizmkit-prizm-docs/references/op-init.md +6 -5
  40. package/bundled/skills/prizmkit-prizm-docs/references/op-rebuild.md +4 -2
  41. package/bundled/skills/prizmkit-prizm-docs/references/op-status.md +9 -10
  42. package/bundled/skills/prizmkit-prizm-docs/references/op-update.md +6 -5
  43. package/bundled/skills/prizmkit-prizm-docs/references/op-validate.md +13 -12
  44. package/bundled/skills/prizmkit-retrospective/SKILL.md +3 -3
  45. package/bundled/skills/prizmkit-test/SKILL.md +28 -15
  46. package/bundled/skills/prizmkit-test/references/boundary-contract-and-test-double-guidance.md +8 -4
  47. package/bundled/skills/prizmkit-test/references/independent-test-review.md +7 -3
  48. package/bundled/skills/prizmkit-test/references/test-coverage-model.md +34 -11
  49. package/bundled/skills/prizmkit-test/references/test-report-template.md +33 -11
  50. package/bundled/skills/prizmkit-workflow/SKILL.md +4 -8
  51. package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +10 -9
  52. package/bundled/templates/hooks/commit-intent-status.py +9 -41
  53. package/bundled/templates/hooks/commit-intent.json +3 -3
  54. package/bundled/templates/hooks/diff-prizm-docs.py +3 -1
  55. package/bundled/templates/hooks/prizm-pre-commit.py +32 -6
  56. package/bundled/templates/hooks/run-python-hook.cjs +44 -0
  57. package/bundled/templates/hooks/validate-prizm-docs.py +399 -173
  58. package/bundled/templates/project-memory-template.md +19 -19
  59. package/package.json +1 -1
  60. package/src/clean.js +6 -7
  61. package/src/gitignore-template.js +8 -12
  62. package/src/scaffold.js +125 -71
  63. package/bundled/skills/prizmkit-deploy/assets/deploy-document-template.md +0 -89
  64. package/bundled/skills/prizmkit-deploy/references/ci-cd-workflows.md +0 -145
  65. package/bundled/skills/prizmkit-deploy/references/cloud-platform-deploy.md +0 -109
  66. package/bundled/skills/prizmkit-deploy/references/data-safety-examples.md +0 -120
  67. package/bundled/skills/prizmkit-deploy/references/database-setup.md +0 -65
  68. package/bundled/skills/prizmkit-deploy/references/deploy-config-schema.md +0 -150
  69. package/bundled/skills/prizmkit-deploy/references/deploy-history-schema.md +0 -78
  70. package/bundled/skills/prizmkit-deploy/references/deploy-metadata-schema.json +0 -49
  71. package/bundled/skills/prizmkit-deploy/references/deployment-modes.md +0 -50
  72. package/bundled/skills/prizmkit-deploy/references/direct-upload.md +0 -26
  73. package/bundled/skills/prizmkit-deploy/references/dns-setup.md +0 -42
  74. package/bundled/skills/prizmkit-deploy/references/docker-deploy.md +0 -31
  75. package/bundled/skills/prizmkit-deploy/references/firewall-setup.md +0 -37
  76. package/bundled/skills/prizmkit-deploy/references/live-validation-notes.md +0 -21
  77. package/bundled/skills/prizmkit-deploy/references/nginx-blue-green.md +0 -59
  78. package/bundled/skills/prizmkit-deploy/references/pending-input-schema.json +0 -164
  79. package/bundled/skills/prizmkit-deploy/references/ssh-adapter-flow.md +0 -221
  80. package/bundled/skills/prizmkit-deploy/references/ssh-bootstrap-flow.md +0 -49
  81. package/bundled/skills/prizmkit-deploy/references/ssh-execution-flow.md +0 -41
  82. package/bundled/skills/prizmkit-deploy/references/ssh-takeover.md +0 -20
  83. package/bundled/skills/prizmkit-deploy/references/ssl-setup.md +0 -56
@@ -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 before writing the request:
15
- - all `.prizmkit/**` paths are PrizmKit-internal and stay outside the task commit, including config, plans, Prizm docs, lifecycle Artifacts, Runtime files, and state;
16
- - `AGENTS.md`, `skills-lock.json`, installed platform directories, and local platform settings are support data and stay outside the task commit;
17
- - project-specific transient data must already be covered by Git ignore rules;
18
- - every other Git-visible path must be justified task output and included exactly, otherwise the handoff blocks.
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 internal/support namespaces, rejects a request that explicitly names one of those namespaces, stages only the requested literal paths, executes and verifies the local commit, finalizes the Runtime-owned checkpoint to `COMMITTED`, and then performs its configured merge/publication behavior. 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,11 +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 "all `.prizmkit/**` paths are PrizmKit-internal" in tier3
1560
- assert "project-specific transient data must already be covered by git ignore rules" in tier3.lower()
1561
- assert "Unknown Git-visible paths are not silently ignored" 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
1565
+ assert "Never force-add or change/interpret project ignore policy" in tier3
1562
1566
  assert "git add" not in tier3
1563
1567
  assert "git commit" not in tier3
1564
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 "all `.prizmkit/**` paths are PrizmKit-internal" in prompt
600
- assert "Unknown Git-visible paths are not silently ignored" 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 "all `.prizmkit/**` paths are PrizmKit-internal" in prompt
439
- assert "Unknown Git-visible paths are not silently ignored" 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()
@@ -171,16 +171,15 @@ def _write_commit_ready_checkpoint(root: Path) -> tuple[Path, Path]:
171
171
  return artifact_dir, checkpoint_path
172
172
 
173
173
 
174
- def _write_request(root: Path, artifact_dir: Path, base_head: str, paths: list[str]) -> Path:
174
+ def _write_request(root: Path, artifact_dir: Path, base_head: str) -> Path:
175
175
  request_path = artifact_dir / "runtime-commit-request.json"
176
176
  request_path.write_text(
177
177
  json.dumps(
178
178
  {
179
- "schema_version": 1,
179
+ "schema_version": 2,
180
180
  "artifact_dir": artifact_dir.relative_to(root).as_posix(),
181
181
  "base_head": base_head,
182
182
  "commit_message": "fix(runtime): finalize prepared task",
183
- "intended_paths": paths,
184
183
  }
185
184
  ),
186
185
  encoding="utf-8",
@@ -188,7 +187,7 @@ def _write_request(root: Path, artifact_dir: Path, base_head: str, paths: list[s
188
187
  return request_path
189
188
 
190
189
 
191
- def test_runtime_commit_executes_exact_request_and_finalizes_only_checkpoint(tmp_path):
190
+ def test_runtime_commit_executes_complete_workspace_and_finalizes_only_checkpoint(tmp_path):
192
191
  base_head = _init_repo(tmp_path)
193
192
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
194
193
  caller_state = tmp_path / ".prizmkit" / "state" / "workflows" / "caller.json"
@@ -196,7 +195,7 @@ def test_runtime_commit_executes_exact_request_and_finalizes_only_checkpoint(tmp
196
195
  caller_state.write_text('{"owner":"external-coordinator"}\n', encoding="utf-8")
197
196
  expected_caller_state = caller_state.read_bytes()
198
197
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
199
- _write_request(tmp_path, artifact_dir, base_head, ["tracked.txt"])
198
+ _write_request(tmp_path, artifact_dir, base_head)
200
199
 
201
200
  result = finalize_runtime_commit(
202
201
  tmp_path,
@@ -229,18 +228,18 @@ def test_runtime_commit_executes_exact_request_and_finalizes_only_checkpoint(tmp
229
228
  ".prizmkit/prizm-docs/src/counter.prizm",
230
229
  ],
231
230
  )
232
- def test_runtime_commit_excludes_tracked_prizmkit_internal_paths(tmp_path, durable_path):
231
+ def test_runtime_commit_includes_tracked_prizmkit_path_from_workspace(tmp_path, durable_path):
233
232
  _init_repo(tmp_path)
234
233
  durable = tmp_path / durable_path
235
234
  durable.parent.mkdir(parents=True, exist_ok=True)
236
235
  durable.write_text("before\n", encoding="utf-8")
237
236
  _git(tmp_path, "add", "-f", durable_path)
238
- _git(tmp_path, "commit", "-m", "track durable PrizmKit project data")
237
+ _git(tmp_path, "commit", "-m", "track project-selected PrizmKit data")
239
238
  base_head = _git(tmp_path, "rev-parse", "HEAD")
240
239
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
241
240
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
242
241
  durable.write_text("after\n", encoding="utf-8")
243
- _write_request(tmp_path, artifact_dir, base_head, ["tracked.txt"])
242
+ _write_request(tmp_path, artifact_dir, base_head)
244
243
 
245
244
  result = finalize_runtime_commit(
246
245
  tmp_path,
@@ -249,24 +248,19 @@ def test_runtime_commit_excludes_tracked_prizmkit_internal_paths(tmp_path, durab
249
248
  )
250
249
 
251
250
  assert result.status == "committed", result
252
- assert _git(
251
+ changed = set(_git(
253
252
  tmp_path,
254
253
  "diff-tree",
255
254
  "--no-commit-id",
256
255
  "--name-only",
257
256
  "-r",
258
257
  "HEAD",
259
- ) == "tracked.txt"
258
+ ).splitlines())
259
+ assert changed == {"tracked.txt", durable_path}
260
260
  assert durable.read_text(encoding="utf-8") == "after\n"
261
- assert durable_path in _git(
262
- tmp_path,
263
- "status",
264
- "--short",
265
- "--untracked-files=all",
266
- )
267
261
 
268
262
 
269
- def test_runtime_commit_ignores_runner_support_paths_but_leaves_them_unmodified(tmp_path):
263
+ def test_runtime_commit_includes_valid_host_support_paths_from_workspace(tmp_path):
270
264
  base_head = _init_repo(tmp_path)
271
265
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
272
266
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
@@ -274,7 +268,7 @@ def test_runtime_commit_ignores_runner_support_paths_but_leaves_them_unmodified(
274
268
  codex_config = tmp_path / ".codex" / "config.toml"
275
269
  codex_config.parent.mkdir()
276
270
  codex_config.write_text("[project]\ntrusted = true\n", encoding="utf-8")
277
- _write_request(tmp_path, artifact_dir, base_head, ["tracked.txt"])
271
+ _write_request(tmp_path, artifact_dir, base_head)
278
272
 
279
273
  result = finalize_runtime_commit(
280
274
  tmp_path,
@@ -283,11 +277,11 @@ def test_runtime_commit_ignores_runner_support_paths_but_leaves_them_unmodified(
283
277
  )
284
278
 
285
279
  assert result.status == "committed"
286
- assert _git(tmp_path, "diff-tree", "--no-commit-id", "--name-only", "-r", "HEAD") == "tracked.txt"
287
- assert (tmp_path / "AGENTS.md").read_text(encoding="utf-8") == "@./AGENTS.private.md\n"
288
- assert codex_config.read_text(encoding="utf-8") == "[project]\ntrusted = true\n"
289
- untracked = set(_git(tmp_path, "ls-files", "--others", "--exclude-standard").splitlines())
290
- assert {"AGENTS.md", ".codex/config.toml"}.issubset(untracked)
280
+ changed = set(
281
+ _git(tmp_path, "diff-tree", "--no-commit-id", "--name-only", "-r", "HEAD").splitlines()
282
+ )
283
+ assert changed == {"tracked.txt", "AGENTS.md", ".codex/config.toml"}
284
+ assert _git(tmp_path, "status", "--porcelain") == ""
291
285
 
292
286
 
293
287
  def test_runtime_commit_allows_task_path_adjacent_to_internal_namespace(tmp_path):
@@ -296,7 +290,7 @@ def test_runtime_commit_allows_task_path_adjacent_to_internal_namespace(tmp_path
296
290
  adjacent_path = tmp_path / ".prizmkitx" / "task.txt"
297
291
  adjacent_path.parent.mkdir()
298
292
  adjacent_path.write_text("task-owned\n", encoding="utf-8")
299
- _write_request(tmp_path, artifact_dir, base_head, [".prizmkitx/task.txt"])
293
+ _write_request(tmp_path, artifact_dir, base_head)
300
294
 
301
295
  result = finalize_runtime_commit(
302
296
  tmp_path,
@@ -320,7 +314,7 @@ def test_runtime_commit_ignores_project_gitignored_transient_data(tmp_path):
320
314
  transient = tmp_path / "tmp" / "tool-cache.json"
321
315
  transient.parent.mkdir()
322
316
  transient.write_text('{"cache":true}\n', encoding="utf-8")
323
- _write_request(tmp_path, artifact_dir, base_head, ["tracked.txt"])
317
+ _write_request(tmp_path, artifact_dir, base_head)
324
318
 
325
319
  result = finalize_runtime_commit(
326
320
  tmp_path,
@@ -333,12 +327,12 @@ def test_runtime_commit_ignores_project_gitignored_transient_data(tmp_path):
333
327
  assert transient.read_text(encoding="utf-8") == '{"cache":true}\n'
334
328
 
335
329
 
336
- def test_runtime_commit_rejects_unlisted_git_visible_change(tmp_path):
330
+ def test_runtime_commit_includes_preexisting_git_visible_change_without_manifest(tmp_path):
337
331
  base_head = _init_repo(tmp_path)
338
332
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
339
333
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
340
- (tmp_path / "unexpected.txt").write_text("unexpected\n", encoding="utf-8")
341
- _write_request(tmp_path, artifact_dir, base_head, ["tracked.txt"])
334
+ (tmp_path / "preexisting.txt").write_text("validated existing work\n", encoding="utf-8")
335
+ _write_request(tmp_path, artifact_dir, base_head)
342
336
 
343
337
  result = finalize_runtime_commit(
344
338
  tmp_path,
@@ -346,16 +340,18 @@ def test_runtime_commit_rejects_unlisted_git_visible_change(tmp_path):
346
340
  checkpoint_path=checkpoint_path,
347
341
  )
348
342
 
349
- assert result.status == "failed"
350
- assert result.reason == "intended_paths_mismatch"
351
- assert _git(tmp_path, "rev-parse", "HEAD") == base_head
343
+ assert result.status == "committed"
344
+ changed = set(
345
+ _git(tmp_path, "diff-tree", "--no-commit-id", "--name-only", "-r", "HEAD").splitlines()
346
+ )
347
+ assert changed == {"tracked.txt", "preexisting.txt"}
352
348
 
353
349
 
354
350
  def test_runtime_commit_recovers_commit_created_before_state_finalization(tmp_path):
355
351
  base_head = _init_repo(tmp_path)
356
352
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
357
353
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
358
- _write_request(tmp_path, artifact_dir, base_head, ["tracked.txt"])
354
+ _write_request(tmp_path, artifact_dir, base_head)
359
355
  _git(tmp_path, "add", "tracked.txt")
360
356
  _git(tmp_path, "commit", "-m", "fix(runtime): finalize prepared task")
361
357
  committed_head = _git(tmp_path, "rev-parse", "HEAD")
@@ -372,15 +368,38 @@ def test_runtime_commit_recovers_commit_created_before_state_finalization(tmp_pa
372
368
  assert _git(tmp_path, "rev-list", "--count", f"{base_head}..HEAD") == "1"
373
369
 
374
370
 
371
+ def test_runtime_commit_rejects_recovery_commit_containing_causal_bookkeeping(tmp_path):
372
+ base_head = _init_repo(tmp_path)
373
+ artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
374
+ (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
375
+ request_path = _write_request(tmp_path, artifact_dir, base_head)
376
+ _git(
377
+ tmp_path,
378
+ "add",
379
+ "-f",
380
+ "tracked.txt",
381
+ checkpoint_path.relative_to(tmp_path).as_posix(),
382
+ request_path.relative_to(tmp_path).as_posix(),
383
+ )
384
+ _git(tmp_path, "commit", "-m", "fix(runtime): finalize prepared task")
385
+
386
+ result = finalize_runtime_commit(
387
+ tmp_path,
388
+ artifact_dir=artifact_dir,
389
+ checkpoint_path=checkpoint_path,
390
+ )
391
+
392
+ assert result.status == "failed"
393
+ assert result.reason == "base_head_mismatch"
394
+
395
+
375
396
  def test_runtime_commit_recovers_after_checkpoint_finalization_failure(tmp_path, monkeypatch):
376
397
  import prizmkit_runtime.runtime_commit as runtime_commit
377
398
 
378
399
  base_head = _init_repo(tmp_path)
379
400
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
380
401
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
381
- request_path = _write_request(
382
- tmp_path, artifact_dir, base_head, ["tracked.txt"]
383
- )
402
+ request_path = _write_request(tmp_path, artifact_dir, base_head)
384
403
 
385
404
  original_finalize = runtime_commit._finalize_checkpoint
386
405
  monkeypatch.setattr(
@@ -417,9 +436,7 @@ def test_runtime_commit_recovers_cleanup_after_completed_checkpoint(tmp_path, mo
417
436
  base_head = _init_repo(tmp_path)
418
437
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
419
438
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
420
- request_path = _write_request(
421
- tmp_path, artifact_dir, base_head, ["tracked.txt"]
422
- )
439
+ request_path = _write_request(tmp_path, artifact_dir, base_head)
423
440
 
424
441
  original_unlink = Path.unlink
425
442
 
@@ -462,7 +479,7 @@ def test_runtime_commit_disables_repository_hooks(tmp_path):
462
479
  )
463
480
  hook.chmod(0o755)
464
481
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
465
- _write_request(tmp_path, artifact_dir, base_head, ["tracked.txt"])
482
+ _write_request(tmp_path, artifact_dir, base_head)
466
483
 
467
484
  result = finalize_runtime_commit(
468
485
  tmp_path,
@@ -484,12 +501,7 @@ def test_runtime_commit_handles_rename_as_exact_delete_and_add(tmp_path):
484
501
  base_head = _git(tmp_path, "rev-parse", "HEAD")
485
502
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
486
503
  old_path.rename(tmp_path / "new-name.txt")
487
- _write_request(
488
- tmp_path,
489
- artifact_dir,
490
- base_head,
491
- ["old-name.txt", "new-name.txt"],
492
- )
504
+ _write_request(tmp_path, artifact_dir, base_head)
493
505
 
494
506
  result = finalize_runtime_commit(
495
507
  tmp_path,
@@ -513,28 +525,95 @@ def test_runtime_commit_handles_rename_as_exact_delete_and_add(tmp_path):
513
525
  @pytest.mark.parametrize(
514
526
  "unsafe_path",
515
527
  [
516
- "../escape.txt",
517
- ".git/config",
518
528
  ".env",
519
- "AGENTS.md",
520
- "skills-lock.json",
521
- ".codex/config.toml",
522
- ".CLAUDE/settings.local.json",
523
- ".prizmkit/config.json",
524
- ".prizmkit/prizm-docs/src/counter.prizm",
525
- ".prizmkit/state/runtime.json",
526
- ".prizmkit/dev-pipeline/cli.py",
527
- ".prizmkit/scripts/helper.py",
528
- ".prizmkit/manifest.json",
529
- ".prizmkit/specs/001-runtime-commit/workflow-checkpoint.json",
530
- ".prizmkit/specs/001-runtime-commit/runtime-commit-request.json",
529
+ "credentials.json",
530
+ "secrets.json",
531
+ "private.pem",
532
+ ".codex/settings.local.json",
531
533
  ],
532
534
  )
533
- def test_runtime_commit_rejects_unsafe_intended_paths(tmp_path, unsafe_path):
535
+ def test_runtime_commit_rejects_sensitive_git_visible_workspace_path(tmp_path, unsafe_path):
536
+ base_head = _init_repo(tmp_path)
537
+ artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
538
+ unsafe = tmp_path / unsafe_path
539
+ unsafe.parent.mkdir(parents=True, exist_ok=True)
540
+ unsafe.write_text("sensitive\n", encoding="utf-8")
541
+ _write_request(tmp_path, artifact_dir, base_head)
542
+
543
+ result = finalize_runtime_commit(
544
+ tmp_path,
545
+ artifact_dir=artifact_dir,
546
+ checkpoint_path=checkpoint_path,
547
+ )
548
+
549
+ assert result.status == "failed"
550
+ assert result.reason == "sensitive_workspace_path"
551
+ assert _git(tmp_path, "rev-parse", "HEAD") == base_head
552
+
553
+
554
+ def test_runtime_commit_rejects_empty_workspace(tmp_path):
555
+ base_head = _init_repo(tmp_path)
556
+ artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
557
+ _write_request(tmp_path, artifact_dir, base_head)
558
+
559
+ result = finalize_runtime_commit(
560
+ tmp_path,
561
+ artifact_dir=artifact_dir,
562
+ checkpoint_path=checkpoint_path,
563
+ )
564
+
565
+ assert result.status == "failed"
566
+ assert result.reason == "empty_workspace_change"
567
+ assert _git(tmp_path, "rev-parse", "HEAD") == base_head
568
+
569
+
570
+ def test_runtime_commit_rejects_unresolved_merge_state(tmp_path):
571
+ _init_repo(tmp_path)
572
+ initial_branch = _git(tmp_path, "branch", "--show-current")
573
+ _git(tmp_path, "checkout", "-b", "other")
574
+ (tmp_path / "tracked.txt").write_text("other\n", encoding="utf-8")
575
+ _git(tmp_path, "add", "tracked.txt")
576
+ _git(tmp_path, "commit", "-m", "other change")
577
+ _git(tmp_path, "checkout", initial_branch)
578
+ (tmp_path / "tracked.txt").write_text("master\n", encoding="utf-8")
579
+ _git(tmp_path, "add", "tracked.txt")
580
+ _git(tmp_path, "commit", "-m", "master change")
581
+ base_head = _git(tmp_path, "rev-parse", "HEAD")
582
+ merged = subprocess.run(
583
+ ["git", "merge", "other"],
584
+ cwd=tmp_path,
585
+ stdout=subprocess.PIPE,
586
+ stderr=subprocess.PIPE,
587
+ text=True,
588
+ check=False,
589
+ )
590
+ assert merged.returncode != 0
591
+ artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
592
+ _write_request(tmp_path, artifact_dir, base_head)
593
+
594
+ result = finalize_runtime_commit(
595
+ tmp_path,
596
+ artifact_dir=artifact_dir,
597
+ checkpoint_path=checkpoint_path,
598
+ )
599
+
600
+ assert result.status == "failed"
601
+ assert result.reason == "unresolved_merge_state"
602
+ assert _git(tmp_path, "rev-parse", "HEAD") == base_head
603
+
604
+
605
+ def test_runtime_commit_rejects_already_staged_runtime_bookkeeping(tmp_path):
534
606
  base_head = _init_repo(tmp_path)
535
607
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
536
608
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
537
- _write_request(tmp_path, artifact_dir, base_head, [unsafe_path])
609
+ request_path = _write_request(tmp_path, artifact_dir, base_head)
610
+ _git(
611
+ tmp_path,
612
+ "add",
613
+ "-f",
614
+ checkpoint_path.relative_to(tmp_path).as_posix(),
615
+ request_path.relative_to(tmp_path).as_posix(),
616
+ )
538
617
 
539
618
  result = finalize_runtime_commit(
540
619
  tmp_path,
@@ -543,17 +622,99 @@ def test_runtime_commit_rejects_unsafe_intended_paths(tmp_path, unsafe_path):
543
622
  )
544
623
 
545
624
  assert result.status == "failed"
546
- assert result.reason == "commit_request_path_unsafe"
625
+ assert result.reason == "runtime_bookkeeping_staged"
547
626
  assert _git(tmp_path, "rev-parse", "HEAD") == base_head
548
627
 
549
628
 
550
- def test_runtime_commit_rejects_caller_orchestration_fields(tmp_path):
629
+ def test_runtime_commit_rejects_schema_v1_path_manifest(tmp_path):
551
630
  base_head = _init_repo(tmp_path)
552
631
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
553
632
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
554
- request_path = _write_request(
555
- tmp_path, artifact_dir, base_head, ["tracked.txt"]
633
+ request_path = _write_request(tmp_path, artifact_dir, base_head)
634
+ request = json.loads(request_path.read_text(encoding="utf-8"))
635
+ request["schema_version"] = 1
636
+ request["intended_paths"] = ["tracked.txt"]
637
+ request_path.write_text(json.dumps(request), encoding="utf-8")
638
+
639
+ result = finalize_runtime_commit(
640
+ tmp_path,
641
+ artifact_dir=artifact_dir,
642
+ checkpoint_path=checkpoint_path,
643
+ )
644
+
645
+ assert result.status == "failed"
646
+ assert result.reason == "commit_request_invalid_schema"
647
+ assert _git(tmp_path, "rev-parse", "HEAD") == base_head
648
+
649
+
650
+ def test_runtime_commit_includes_already_staged_and_unstaged_workspace(tmp_path):
651
+ _init_repo(tmp_path)
652
+ support = tmp_path / "AGENTS.md"
653
+ support.write_text("before\n", encoding="utf-8")
654
+ _git(tmp_path, "add", "AGENTS.md")
655
+ _git(tmp_path, "commit", "-m", "track support")
656
+ base_head = _git(tmp_path, "rev-parse", "HEAD")
657
+ artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
658
+ (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
659
+ support.write_text("after\n", encoding="utf-8")
660
+ _git(tmp_path, "add", "AGENTS.md")
661
+ _write_request(tmp_path, artifact_dir, base_head)
662
+
663
+ result = finalize_runtime_commit(
664
+ tmp_path,
665
+ artifact_dir=artifact_dir,
666
+ checkpoint_path=checkpoint_path,
556
667
  )
668
+
669
+ assert result.status == "committed"
670
+ changed = set(
671
+ _git(tmp_path, "diff-tree", "--no-commit-id", "--name-only", "-r", "HEAD").splitlines()
672
+ )
673
+ assert changed == {"AGENTS.md", "tracked.txt"}
674
+
675
+
676
+ def test_runtime_commit_keeps_tracked_post_commit_bookkeeping_out_of_snapshot(tmp_path):
677
+ from prizmkit_runtime.gitops import HIDDEN_TOOL_WORKTREE_EXCLUDES
678
+
679
+ initial_head = _init_repo(tmp_path)
680
+ artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
681
+ request_path = _write_request(tmp_path, artifact_dir, initial_head)
682
+ checkpoint_rel = checkpoint_path.relative_to(tmp_path).as_posix()
683
+ request_rel = request_path.relative_to(tmp_path).as_posix()
684
+ _git(tmp_path, "add", "-f", checkpoint_rel, request_rel)
685
+ _git(tmp_path, "commit", "-m", "track runtime bookkeeping by project policy")
686
+ base_head = _git(tmp_path, "rev-parse", "HEAD")
687
+ _write_request(tmp_path, artifact_dir, base_head)
688
+ (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
689
+
690
+ result = finalize_runtime_commit(
691
+ tmp_path,
692
+ artifact_dir=artifact_dir,
693
+ checkpoint_path=checkpoint_path,
694
+ )
695
+
696
+ assert result.status == "committed", result
697
+ assert _git(tmp_path, "diff-tree", "--no-commit-id", "--name-only", "-r", "HEAD") == "tracked.txt"
698
+ assert checkpoint_rel not in _git(tmp_path, "diff-tree", "--no-commit-id", "--name-only", "-r", "HEAD")
699
+ visible = _git(
700
+ tmp_path,
701
+ "status",
702
+ "--porcelain",
703
+ "--",
704
+ ".",
705
+ *HIDDEN_TOOL_WORKTREE_EXCLUDES,
706
+ )
707
+ assert visible == ""
708
+ raw_status = _git(tmp_path, "status", "--porcelain", "--", checkpoint_rel, request_rel)
709
+ assert checkpoint_rel in raw_status
710
+ assert request_rel in raw_status
711
+
712
+
713
+ def test_runtime_commit_rejects_caller_orchestration_fields(tmp_path):
714
+ base_head = _init_repo(tmp_path)
715
+ artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
716
+ (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
717
+ request_path = _write_request(tmp_path, artifact_dir, base_head)
557
718
  request = json.loads(request_path.read_text(encoding="utf-8"))
558
719
  request["caller_state_path"] = ".runtime/caller-state.json"
559
720
  request_path.write_text(json.dumps(request), encoding="utf-8")
@@ -573,9 +734,7 @@ def test_runtime_commit_rejects_ai_supplied_premature_runtime_hash(tmp_path):
573
734
  base_head = _init_repo(tmp_path)
574
735
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
575
736
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
576
- request_path = _write_request(
577
- tmp_path, artifact_dir, base_head, ["tracked.txt"]
578
- )
737
+ request_path = _write_request(tmp_path, artifact_dir, base_head)
579
738
  request = json.loads(request_path.read_text(encoding="utf-8"))
580
739
  request["runtime_commit_hash"] = "a" * 40
581
740
  request_path.write_text(json.dumps(request), encoding="utf-8")
@@ -595,7 +754,7 @@ def test_runtime_commit_rejects_stale_base_head(tmp_path):
595
754
  base_head = _init_repo(tmp_path)
596
755
  artifact_dir, checkpoint_path = _write_commit_ready_checkpoint(tmp_path)
597
756
  (tmp_path / "tracked.txt").write_text("after\n", encoding="utf-8")
598
- _write_request(tmp_path, artifact_dir, "a" * 40, ["tracked.txt"])
757
+ _write_request(tmp_path, artifact_dir, "a" * 40)
599
758
 
600
759
  result = finalize_runtime_commit(
601
760
  tmp_path,