prizmkit 1.1.153 → 1.1.155
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.
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/codex/skill-adapter.js +1 -1
- package/bundled/adapters/pi/skill-adapter.js +2 -1
- package/bundled/dev-pipeline/.env.example +2 -6
- package/bundled/dev-pipeline/README.md +120 -113
- package/bundled/dev-pipeline/assets/skill-subagent-integration.md +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +355 -15
- package/bundled/dev-pipeline/prizmkit_runtime/cli.py +189 -110
- package/bundled/dev-pipeline/prizmkit_runtime/commands.py +146 -111
- package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +4 -20
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +48 -14
- package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +0 -1
- package/bundled/dev-pipeline/prizmkit_runtime/paths.py +0 -3
- package/bundled/dev-pipeline/prizmkit_runtime/reset.py +122 -15
- package/bundled/dev-pipeline/prizmkit_runtime/reset_preserve.py +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +50 -2
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +1 -10
- package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +3 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +139 -74
- package/bundled/dev-pipeline/prizmkit_runtime/runtime_commit.py +482 -0
- package/bundled/dev-pipeline/prizmkit_runtime/status.py +106 -1
- package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +64 -11
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +145 -367
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +91 -62
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +105 -69
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +2 -2
- package/bundled/dev-pipeline/scripts/prompt_framework.py +36 -41
- package/bundled/dev-pipeline/scripts/update-bug-status.py +197 -71
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +77 -16
- package/bundled/dev-pipeline/scripts/update-feature-status.py +46 -121
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +46 -123
- package/bundled/dev-pipeline/scripts/utils.py +119 -0
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +22 -27
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +71 -104
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +3 -2
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +59 -111
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +37 -72
- package/bundled/dev-pipeline/templates/sections/artifact-manifest.md +39 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-critical-paths.md +3 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +8 -25
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +3 -3
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-implement.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +3 -3
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +4 -4
- package/bundled/dev-pipeline/templates/sections/bugfix-session-context.md +0 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +7 -4
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +14 -27
- package/bundled/dev-pipeline/templates/sections/feature-completion-summary.md +20 -0
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +8 -71
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +7 -7
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +3 -3
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +4 -4
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +5 -17
- package/bundled/dev-pipeline/templates/sections/refactor-critical-paths.md +3 -1
- package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +10 -23
- package/bundled/dev-pipeline/templates/sections/refactor-phase-implement.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-phase-plan.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +3 -3
- package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +7 -7
- package/bundled/dev-pipeline/templates/sections/refactor-session-context.md +0 -1
- package/bundled/dev-pipeline/templates/sections/retrospective.md +12 -0
- package/bundled/dev-pipeline/templates/sections/runtime-commit-handoff.md +35 -0
- package/bundled/dev-pipeline/templates/session-status-schema.json +45 -62
- package/bundled/dev-pipeline/tests/test_auto_skip.py +111 -32
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +617 -30
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +262 -194
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +62 -23
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +76 -28
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +281 -206
- package/bundled/dev-pipeline/tests/test_recovery_workflow.py +322 -0
- package/bundled/dev-pipeline/tests/test_reset_modes.py +935 -0
- package/bundled/dev-pipeline/tests/test_reset_preserve.py +5 -7
- package/bundled/dev-pipeline/tests/test_runtime_commit.py +608 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +617 -188
- package/bundled/skills/_metadata.json +9 -9
- package/bundled/skills/app-planner/SKILL.md +3 -3
- package/bundled/skills/app-planner/references/generated-plan-review.md +2 -1
- package/bundled/skills/app-planner/references/rules/backend/derivation-rules.md +56 -64
- package/bundled/skills/app-planner/references/rules/backend/fixed-rules.md +85 -240
- package/bundled/skills/app-planner/references/rules/backend/question-bank.md +25 -25
- package/bundled/skills/app-planner/references/rules/backend/question-manifest.json +16 -7
- package/bundled/skills/app-planner/references/rules/backend/template.md +74 -92
- package/bundled/skills/app-planner/references/rules/database/derivation-rules.md +93 -93
- package/bundled/skills/app-planner/references/rules/database/fixed-rules.md +66 -173
- package/bundled/skills/app-planner/references/rules/database/question-bank.md +31 -26
- package/bundled/skills/app-planner/references/rules/database/question-manifest.json +13 -4
- package/bundled/skills/app-planner/references/rules/database/template.md +76 -98
- package/bundled/skills/app-planner/references/rules/frontend/derivation-rules.md +68 -95
- package/bundled/skills/app-planner/references/rules/frontend/fixed-rules.md +93 -166
- package/bundled/skills/app-planner/references/rules/frontend/question-bank.md +46 -36
- package/bundled/skills/app-planner/references/rules/frontend/question-manifest.json +11 -2
- package/bundled/skills/app-planner/references/rules/frontend/template.md +79 -257
- package/bundled/skills/app-planner/references/rules/mobile/derivation-rules.md +91 -99
- package/bundled/skills/app-planner/references/rules/mobile/fixed-rules.md +73 -246
- package/bundled/skills/app-planner/references/rules/mobile/question-bank.md +17 -17
- package/bundled/skills/app-planner/references/rules/mobile/question-manifest.json +17 -2
- package/bundled/skills/app-planner/references/rules/mobile/template.md +73 -85
- package/bundled/skills/app-planner/references/rules-configuration.md +133 -65
- package/bundled/skills/app-planner/scripts/validate-rules-configuration.py +291 -0
- package/bundled/skills/bug-planner/references/schema-validation.md +1 -1
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +21 -13
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +22 -14
- package/bundled/skills/prizmkit/SKILL.md +17 -18
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +22 -18
- package/bundled/skills/prizmkit-code-review/SKILL.md +22 -58
- package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +6 -6
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +2 -2
- package/bundled/skills/prizmkit-committer/SKILL.md +82 -132
- package/bundled/skills/prizmkit-deploy/SKILL.md +27 -11
- package/bundled/skills/prizmkit-deploy/assets/deploy-document-template.md +89 -0
- package/bundled/skills/prizmkit-deploy/references/ci-cd-workflows.md +44 -14
- package/bundled/skills/prizmkit-deploy/references/cloud-platform-deploy.md +19 -3
- package/bundled/skills/prizmkit-deploy/references/database-setup.md +22 -12
- package/bundled/skills/prizmkit-deploy/references/deploy-config-schema.md +3 -1
- package/bundled/skills/prizmkit-deploy/references/deploy-history-schema.md +22 -6
- package/bundled/skills/prizmkit-deploy/references/deploy-metadata-schema.json +49 -0
- package/bundled/skills/prizmkit-deploy/references/live-validation-notes.md +1 -1
- package/bundled/skills/prizmkit-deploy/references/pending-input-schema.json +164 -0
- package/bundled/skills/prizmkit-deploy/references/ssh-adapter-flow.md +9 -8
- package/bundled/skills/prizmkit-deploy/references/ssh-execution-flow.md +6 -6
- package/bundled/skills/prizmkit-implement/SKILL.md +30 -66
- package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +4 -4
- package/bundled/skills/prizmkit-init/SKILL.md +4 -0
- package/bundled/skills/prizmkit-plan/SKILL.md +21 -64
- package/bundled/skills/prizmkit-plan/references/artifact-identity.md +38 -0
- package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +4 -4
- package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +5 -5
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +11 -1
- package/bundled/skills/prizmkit-retrospective/SKILL.md +63 -77
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +3 -3
- package/bundled/skills/prizmkit-retrospective/references/retrospective-result-schema.json +138 -0
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +17 -23
- package/bundled/skills/prizmkit-test/SKILL.md +64 -51
- package/bundled/skills/prizmkit-test/references/boundary-contract-and-test-double-guidance.md +206 -0
- package/bundled/skills/prizmkit-test/references/independent-test-review.md +17 -9
- package/bundled/skills/prizmkit-test/references/test-coverage-model.md +45 -21
- package/bundled/skills/prizmkit-test/references/test-report-template.md +20 -13
- package/bundled/skills/prizmkit-workflow/SKILL.md +35 -37
- package/bundled/skills/prizmkit-workflow/references/artifact-identity.md +34 -0
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +22 -18
- package/bundled/skills/recovery-workflow/SKILL.md +9 -6
- package/bundled/skills/recovery-workflow/evals/evals.json +2 -2
- package/bundled/skills/recovery-workflow/references/detection.md +7 -6
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +16 -6
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +21 -13
- package/bundled/templates/project-memory-template.md +19 -11
- package/package.json +1 -1
- package/src/scaffold.js +33 -22
- package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +0 -228
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +0 -767
- package/bundled/dev-pipeline/templates/sections/bugfix-artifacts.md +0 -16
- package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +0 -31
- package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -7
- package/bundled/dev-pipeline/templates/sections/critical-paths-lite.md +0 -7
- package/bundled/dev-pipeline/templates/sections/directory-convention-agent.md +0 -9
- package/bundled/dev-pipeline/templates/sections/directory-convention-lite.md +0 -6
- package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +0 -15
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +0 -61
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +0 -24
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +0 -12
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +0 -44
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +0 -25
- package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +0 -20
- package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +0 -20
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +0 -20
- package/bundled/dev-pipeline/templates/sections/refactor-artifacts.md +0 -17
- package/bundled/dev-pipeline/templates/sections/refactor-session-status.md +0 -28
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +0 -52
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +0 -52
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-test/references/external-contract-mock-guidance.md +0 -119
|
@@ -9,12 +9,12 @@ Handles eight actions:
|
|
|
9
9
|
- pause: Save pipeline state for graceful shutdown
|
|
10
10
|
- reset: Reset a bug to pending (status + retry count)
|
|
11
11
|
- clean: Reset + delete session history + delete bugfix artifacts
|
|
12
|
-
-
|
|
12
|
+
- reset_state: Recover blocking roots and causally recoverable auto-skipped descendants
|
|
13
13
|
|
|
14
14
|
Usage:
|
|
15
15
|
python3 update-bug-status.py \
|
|
16
16
|
--bug-list <path> --state-dir <path> \
|
|
17
|
-
--action <get_next|start|update|status|pause|reset|clean|
|
|
17
|
+
--action <get_next|start|update|status|pause|reset|clean|reset_state> \
|
|
18
18
|
[--bug-id <id>] [--session-status <status>] \
|
|
19
19
|
[--session-id <id>] [--max-retries <n>] [--max-infra-retries <n>]
|
|
20
20
|
"""
|
|
@@ -23,6 +23,7 @@ import argparse
|
|
|
23
23
|
import json
|
|
24
24
|
import os
|
|
25
25
|
import shutil
|
|
26
|
+
import sys
|
|
26
27
|
from datetime import datetime, timezone
|
|
27
28
|
|
|
28
29
|
from utils import (
|
|
@@ -33,6 +34,7 @@ from utils import (
|
|
|
33
34
|
_build_progress_bar,
|
|
34
35
|
read_checkpoint_cursor,
|
|
35
36
|
dependency_rank_map,
|
|
37
|
+
causal_reset_selection,
|
|
36
38
|
)
|
|
37
39
|
|
|
38
40
|
|
|
@@ -53,7 +55,8 @@ SESSION_STATUS_VALUES = [
|
|
|
53
55
|
"merge_conflict",
|
|
54
56
|
]
|
|
55
57
|
|
|
56
|
-
TERMINAL_STATUSES = {"completed", "failed", "skipped", "needs_info"}
|
|
58
|
+
TERMINAL_STATUSES = {"completed", "failed", "skipped", "needs_info", "auto_skipped"}
|
|
59
|
+
RESET_BLOCKING_ROOT_STATUSES = {"failed", "skipped", "needs_info"}
|
|
57
60
|
|
|
58
61
|
# Severity priority (lower value = higher priority)
|
|
59
62
|
SEVERITY_PRIORITY = {
|
|
@@ -72,7 +75,7 @@ def parse_args():
|
|
|
72
75
|
parser.add_argument("--state-dir", required=True, help="Path to the state directory (default: .prizmkit/state/bugfix)")
|
|
73
76
|
parser.add_argument(
|
|
74
77
|
"--action", required=True,
|
|
75
|
-
choices=["get_next", "start", "update", "status", "pause", "reset", "clean", "
|
|
78
|
+
choices=["get_next", "start", "update", "status", "pause", "reset", "clean", "reset_state", "complete"],
|
|
76
79
|
help="Action to perform",
|
|
77
80
|
)
|
|
78
81
|
parser.add_argument("--bug-id", default=None, help="Bug ID (required for 'update'/'reset'/'clean' actions)")
|
|
@@ -383,19 +386,117 @@ def continuation_metadata_summary(status_data):
|
|
|
383
386
|
}
|
|
384
387
|
|
|
385
388
|
|
|
389
|
+
# ---------------------------------------------------------------------------
|
|
390
|
+
# Auto-skip: cascade terminal blockers to pending downstream bugs
|
|
391
|
+
# ---------------------------------------------------------------------------
|
|
392
|
+
|
|
393
|
+
def auto_skip_blocked_bugs(bug_list_path, state_dir, blocking_bug_id=None):
|
|
394
|
+
"""Mark pending descendants of terminal Bugfix blockers as auto_skipped."""
|
|
395
|
+
data, err = load_json_file(bug_list_path)
|
|
396
|
+
if err:
|
|
397
|
+
error_out("Cannot load bug fix list for auto-skip: {}".format(err))
|
|
398
|
+
return []
|
|
399
|
+
bugs = data.get("bugs", [])
|
|
400
|
+
status_map = {
|
|
401
|
+
bug.get("id"): bug.get("status", "pending")
|
|
402
|
+
for bug in bugs
|
|
403
|
+
if isinstance(bug, dict) and bug.get("id")
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
to_skip = set()
|
|
407
|
+
changed = True
|
|
408
|
+
while changed:
|
|
409
|
+
changed = False
|
|
410
|
+
for bug in bugs:
|
|
411
|
+
if not isinstance(bug, dict):
|
|
412
|
+
continue
|
|
413
|
+
bug_id = bug.get("id")
|
|
414
|
+
if not bug_id or bug_id in to_skip:
|
|
415
|
+
continue
|
|
416
|
+
if status_map.get(bug_id, "pending") != "pending":
|
|
417
|
+
continue
|
|
418
|
+
dependencies = bug.get("dependencies", [])
|
|
419
|
+
if not isinstance(dependencies, list):
|
|
420
|
+
continue
|
|
421
|
+
if any(
|
|
422
|
+
status_map.get(dependency_id, "pending")
|
|
423
|
+
in RESET_BLOCKING_ROOT_STATUSES | {"auto_skipped"}
|
|
424
|
+
or dependency_id in to_skip
|
|
425
|
+
for dependency_id in dependencies
|
|
426
|
+
):
|
|
427
|
+
to_skip.add(bug_id)
|
|
428
|
+
status_map[bug_id] = "auto_skipped"
|
|
429
|
+
changed = True
|
|
430
|
+
|
|
431
|
+
if not to_skip:
|
|
432
|
+
return []
|
|
433
|
+
|
|
434
|
+
for bug in bugs:
|
|
435
|
+
if isinstance(bug, dict) and bug.get("id") in to_skip:
|
|
436
|
+
bug["status"] = "auto_skipped"
|
|
437
|
+
err = write_json_file(bug_list_path, data)
|
|
438
|
+
if err:
|
|
439
|
+
error_out("Failed to write auto-skipped bugs: {}".format(err))
|
|
440
|
+
return []
|
|
441
|
+
|
|
442
|
+
for bug_id in to_skip:
|
|
443
|
+
bs = load_bug_status(state_dir, bug_id)
|
|
444
|
+
bs["updated_at"] = now_iso()
|
|
445
|
+
err = save_bug_status(state_dir, bug_id, bs)
|
|
446
|
+
if err:
|
|
447
|
+
error_out("Failed to save auto-skipped bug status: {}".format(err))
|
|
448
|
+
return []
|
|
449
|
+
|
|
450
|
+
skipped_info = []
|
|
451
|
+
for bug in bugs:
|
|
452
|
+
if not isinstance(bug, dict) or bug.get("id") not in to_skip:
|
|
453
|
+
continue
|
|
454
|
+
dependencies = bug.get("dependencies", [])
|
|
455
|
+
blockers = [
|
|
456
|
+
dependency_id
|
|
457
|
+
for dependency_id in dependencies
|
|
458
|
+
if dependency_id == blocking_bug_id
|
|
459
|
+
or status_map.get(dependency_id, "pending")
|
|
460
|
+
in RESET_BLOCKING_ROOT_STATUSES | {"auto_skipped"}
|
|
461
|
+
]
|
|
462
|
+
skipped_info.append({
|
|
463
|
+
"bug_id": bug["id"],
|
|
464
|
+
"title": bug.get("title", ""),
|
|
465
|
+
"blocked_by": blockers,
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
cause = blocking_bug_id or "existing terminal blockers"
|
|
469
|
+
print(
|
|
470
|
+
"[auto-skip] {} bug(s) auto-skipped due to {}".format(
|
|
471
|
+
len(skipped_info), cause
|
|
472
|
+
),
|
|
473
|
+
file=sys.stderr,
|
|
474
|
+
)
|
|
475
|
+
return skipped_info
|
|
476
|
+
|
|
477
|
+
|
|
386
478
|
# ---------------------------------------------------------------------------
|
|
387
479
|
# Action: get_next
|
|
388
480
|
# ---------------------------------------------------------------------------
|
|
389
481
|
|
|
390
|
-
def action_get_next(bug_list_data, state_dir, project_root=None):
|
|
482
|
+
def action_get_next(bug_list_data, state_dir, project_root=None, bug_list_path=None):
|
|
391
483
|
"""Find the next bug to process.
|
|
392
484
|
|
|
393
485
|
Priority logic:
|
|
394
|
-
1.
|
|
395
|
-
2.
|
|
396
|
-
3.
|
|
397
|
-
4.
|
|
486
|
+
1. Lazily normalize pending descendants of terminal blockers to auto_skipped
|
|
487
|
+
2. Skip terminal statuses (completed, failed, skipped, needs_info, auto_skipped)
|
|
488
|
+
3. Only consider bugs whose dependencies are all completed
|
|
489
|
+
4. Prefer in_progress bugs (interrupted session resume) over pending
|
|
490
|
+
5. Within each status class, use stable topological list order
|
|
398
491
|
"""
|
|
492
|
+
if bug_list_path:
|
|
493
|
+
auto_skip_blocked_bugs(bug_list_path, state_dir)
|
|
494
|
+
refreshed, refresh_error = load_json_file(bug_list_path)
|
|
495
|
+
if refresh_error:
|
|
496
|
+
error_out("Cannot reload bug fix list after auto-skip: {}".format(refresh_error))
|
|
497
|
+
return
|
|
498
|
+
bug_list_data = refreshed
|
|
499
|
+
|
|
399
500
|
bugs = bug_list_data.get("bugs", [])
|
|
400
501
|
if not bugs:
|
|
401
502
|
print("PIPELINE_COMPLETE")
|
|
@@ -513,6 +614,8 @@ def action_update(args, bug_list_path, state_dir):
|
|
|
513
614
|
bs = load_bug_status(state_dir, bug_id)
|
|
514
615
|
|
|
515
616
|
current_list_status = get_bug_status_from_list(bug_list_path, bug_id)
|
|
617
|
+
bs["max_retries"] = max_retries
|
|
618
|
+
bs["max_infra_retries"] = max_infra_retries
|
|
516
619
|
|
|
517
620
|
# Track what status we write to bug-fix-list.json
|
|
518
621
|
new_status = current_list_status
|
|
@@ -677,6 +780,12 @@ def action_update(args, bug_list_path, state_dir):
|
|
|
677
780
|
error_out("Failed to save bug status: {}".format(err))
|
|
678
781
|
return
|
|
679
782
|
|
|
783
|
+
auto_skipped_bugs = []
|
|
784
|
+
if new_status in RESET_BLOCKING_ROOT_STATUSES:
|
|
785
|
+
auto_skipped_bugs = auto_skip_blocked_bugs(
|
|
786
|
+
bug_list_path, state_dir, bug_id
|
|
787
|
+
)
|
|
788
|
+
|
|
680
789
|
summary = {
|
|
681
790
|
"action": "update",
|
|
682
791
|
"bug_id": bug_id,
|
|
@@ -689,6 +798,8 @@ def action_update(args, bug_list_path, state_dir):
|
|
|
689
798
|
"checkpoint_state": semantic_checkpoint,
|
|
690
799
|
"updated_at": bs["updated_at"],
|
|
691
800
|
}
|
|
801
|
+
if auto_skipped_bugs:
|
|
802
|
+
summary["auto_skipped"] = [info["bug_id"] for info in auto_skipped_bugs]
|
|
692
803
|
if session_status in ("commit_missing", "docs_missing", "merge_conflict"):
|
|
693
804
|
summary["degraded_reason"] = session_status
|
|
694
805
|
summary["restart_policy"] = "finalization_retry"
|
|
@@ -863,7 +974,20 @@ def action_status(bug_list_data, state_dir, project_root=None):
|
|
|
863
974
|
project_name = bug_list_data.get("project_name", "Unknown")
|
|
864
975
|
project_root = os.path.abspath(project_root) if project_root else _default_project_root()
|
|
865
976
|
|
|
866
|
-
counts = {
|
|
977
|
+
counts = {
|
|
978
|
+
"completed": 0,
|
|
979
|
+
"in_progress": 0,
|
|
980
|
+
"failed": 0,
|
|
981
|
+
"pending": 0,
|
|
982
|
+
"needs_info": 0,
|
|
983
|
+
"skipped": 0,
|
|
984
|
+
"auto_skipped": 0,
|
|
985
|
+
}
|
|
986
|
+
status_map = {
|
|
987
|
+
bug.get("id"): bug.get("status", "pending")
|
|
988
|
+
for bug in bugs
|
|
989
|
+
if isinstance(bug, dict) and bug.get("id")
|
|
990
|
+
}
|
|
867
991
|
bug_lines = []
|
|
868
992
|
|
|
869
993
|
for bug in bugs:
|
|
@@ -879,11 +1003,8 @@ def action_status(bug_list_data, state_dir, project_root=None):
|
|
|
879
1003
|
bs = load_bug_status(state_dir, bid)
|
|
880
1004
|
retry_count = bs.get("retry_count", 0)
|
|
881
1005
|
max_retries_val = bs.get("max_retries", 3)
|
|
882
|
-
# Read
|
|
1006
|
+
# Read the authoritative checkpoint cursor without mutating cached runtime state.
|
|
883
1007
|
resume_phase = _checkpoint_phase_or_cached(project_root, bid, bs)
|
|
884
|
-
if resume_phase != bs.get("resume_from_phase"):
|
|
885
|
-
bs["resume_from_phase"] = resume_phase
|
|
886
|
-
save_bug_status(state_dir, bid, bs)
|
|
887
1008
|
|
|
888
1009
|
if bstatus in counts:
|
|
889
1010
|
counts[bstatus] += 1
|
|
@@ -901,6 +1022,8 @@ def action_status(bug_list_data, state_dir, project_root=None):
|
|
|
901
1022
|
icon = COLOR_MAGENTA + "[?]" + COLOR_RESET
|
|
902
1023
|
elif bstatus == "skipped":
|
|
903
1024
|
icon = COLOR_GRAY + "[—]" + COLOR_RESET
|
|
1025
|
+
elif bstatus == "auto_skipped":
|
|
1026
|
+
icon = COLOR_GRAY + "[⊘]" + COLOR_RESET
|
|
904
1027
|
else:
|
|
905
1028
|
icon = COLOR_GRAY + "[ ]" + COLOR_RESET
|
|
906
1029
|
|
|
@@ -921,6 +1044,16 @@ def action_status(bug_list_data, state_dir, project_root=None):
|
|
|
921
1044
|
detail = " (failed after {} retries)".format(retry_count)
|
|
922
1045
|
elif bstatus == "needs_info":
|
|
923
1046
|
detail = " (needs more info)"
|
|
1047
|
+
elif bstatus == "auto_skipped":
|
|
1048
|
+
blockers = [
|
|
1049
|
+
dependency_id
|
|
1050
|
+
for dependency_id in bug.get("dependencies", [])
|
|
1051
|
+
if status_map.get(dependency_id, "pending")
|
|
1052
|
+
in RESET_BLOCKING_ROOT_STATUSES | {"auto_skipped"}
|
|
1053
|
+
]
|
|
1054
|
+
detail = " (blocked by {})".format(
|
|
1055
|
+
", ".join(blockers) if blockers else "dependency chain"
|
|
1056
|
+
)
|
|
924
1057
|
|
|
925
1058
|
# Colorize
|
|
926
1059
|
if bstatus == "completed":
|
|
@@ -954,8 +1087,12 @@ def action_status(bug_list_data, state_dir, project_root=None):
|
|
|
954
1087
|
summary_line = "Total: {} bugs | Completed: {} | In Progress: {}".format(
|
|
955
1088
|
total, completed, counts["in_progress"]
|
|
956
1089
|
)
|
|
957
|
-
summary_line2 = "Failed: {} | Pending: {} | Needs Info: {} | Skipped: {}".format(
|
|
958
|
-
counts["failed"],
|
|
1090
|
+
summary_line2 = "Failed: {} | Pending: {} | Needs Info: {} | Skipped: {} | Auto-skipped: {}".format(
|
|
1091
|
+
counts["failed"],
|
|
1092
|
+
counts["pending"],
|
|
1093
|
+
counts["needs_info"],
|
|
1094
|
+
counts["skipped"],
|
|
1095
|
+
counts["auto_skipped"],
|
|
959
1096
|
)
|
|
960
1097
|
|
|
961
1098
|
inner = BOX_WIDTH - 2
|
|
@@ -1146,6 +1283,8 @@ def action_start(args, bug_list_path, state_dir):
|
|
|
1146
1283
|
bs = load_bug_status(state_dir, bug_id)
|
|
1147
1284
|
old_status = get_bug_status_from_list(bug_list_path, bug_id)
|
|
1148
1285
|
|
|
1286
|
+
bs["max_retries"] = args.max_retries
|
|
1287
|
+
bs["max_infra_retries"] = args.max_infra_retries
|
|
1149
1288
|
bs["updated_at"] = now_iso()
|
|
1150
1289
|
|
|
1151
1290
|
err = save_bug_status(state_dir, bug_id, bs)
|
|
@@ -1172,64 +1311,37 @@ def action_start(args, bug_list_path, state_dir):
|
|
|
1172
1311
|
|
|
1173
1312
|
|
|
1174
1313
|
# ---------------------------------------------------------------------------
|
|
1175
|
-
# Action:
|
|
1314
|
+
# Action: reset_state
|
|
1176
1315
|
# ---------------------------------------------------------------------------
|
|
1177
1316
|
|
|
1178
|
-
def
|
|
1179
|
-
"""Reset
|
|
1180
|
-
|
|
1181
|
-
Two modes:
|
|
1182
|
-
- --bug-id B-001: Reset the specified skipped bug to pending.
|
|
1183
|
-
- No --bug-id: Reset ALL skipped bugs to pending.
|
|
1184
|
-
"""
|
|
1185
|
-
bug_id = args.bug_id
|
|
1186
|
-
|
|
1317
|
+
def action_reset_state(args, bug_list_path, state_dir):
|
|
1318
|
+
"""Reset one causal bug closure, or every recoverable closure, to pending."""
|
|
1187
1319
|
data, err = load_json_file(bug_list_path)
|
|
1188
1320
|
if err:
|
|
1189
1321
|
error_out("Cannot load bug fix list: {}".format(err))
|
|
1190
1322
|
return
|
|
1191
1323
|
bugs = data.get("bugs", [])
|
|
1192
1324
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
if not target:
|
|
1203
|
-
error_out("Bug '{}' not found in .prizmkit/plans/bug-fix-list.json".format(bug_id))
|
|
1204
|
-
return
|
|
1205
|
-
if target.get("status") not in ("failed", "skipped", "needs_info"):
|
|
1206
|
-
error_out(
|
|
1207
|
-
"Bug '{}' has status '{}', expected 'failed', 'skipped', or 'needs_info'".format(
|
|
1208
|
-
bug_id, target.get("status", "unknown")
|
|
1209
|
-
)
|
|
1210
|
-
)
|
|
1211
|
-
return
|
|
1212
|
-
to_reset.add(bug_id)
|
|
1213
|
-
else:
|
|
1214
|
-
# No bug-id: reset ALL skipped/failed/needs_info bugs
|
|
1215
|
-
for b in bugs:
|
|
1216
|
-
if isinstance(b, dict) and b.get("id"):
|
|
1217
|
-
if b.get("status") in ("failed", "skipped", "needs_info"):
|
|
1218
|
-
to_reset.add(b["id"])
|
|
1219
|
-
|
|
1220
|
-
if not to_reset:
|
|
1221
|
-
error_out("No bugs to unskip")
|
|
1325
|
+
try:
|
|
1326
|
+
recovery_ids = causal_reset_selection(
|
|
1327
|
+
bugs,
|
|
1328
|
+
selected_id=args.bug_id,
|
|
1329
|
+
blocking_root_statuses=RESET_BLOCKING_ROOT_STATUSES,
|
|
1330
|
+
item_label="Bug",
|
|
1331
|
+
)
|
|
1332
|
+
except ValueError as exc:
|
|
1333
|
+
error_out(str(exc))
|
|
1222
1334
|
return
|
|
1223
1335
|
|
|
1224
|
-
|
|
1336
|
+
to_reset = set(recovery_ids)
|
|
1225
1337
|
reset_details = []
|
|
1226
|
-
for
|
|
1227
|
-
if isinstance(
|
|
1228
|
-
old_status =
|
|
1229
|
-
|
|
1338
|
+
for bug in bugs:
|
|
1339
|
+
if isinstance(bug, dict) and bug.get("id") in to_reset:
|
|
1340
|
+
old_status = bug.get("status", "unknown")
|
|
1341
|
+
bug["status"] = "pending"
|
|
1230
1342
|
reset_details.append({
|
|
1231
|
-
"bug_id":
|
|
1232
|
-
"title":
|
|
1343
|
+
"bug_id": bug["id"],
|
|
1344
|
+
"title": bug.get("title", ""),
|
|
1233
1345
|
"old_status": old_status,
|
|
1234
1346
|
})
|
|
1235
1347
|
|
|
@@ -1238,22 +1350,31 @@ def action_unskip(args, bug_list_path, state_dir):
|
|
|
1238
1350
|
error_out("Failed to write .prizmkit/plans/bug-fix-list.json: {}".format(err))
|
|
1239
1351
|
return
|
|
1240
1352
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1353
|
+
for bug_id in recovery_ids:
|
|
1354
|
+
bs = load_bug_status(state_dir, bug_id)
|
|
1355
|
+
checkout_identity = {
|
|
1356
|
+
key: bs[key]
|
|
1357
|
+
for key in ("active_dev_branch", "base_branch")
|
|
1358
|
+
if key in bs
|
|
1359
|
+
}
|
|
1244
1360
|
bs["retry_count"] = 0
|
|
1245
1361
|
bs["infra_error_count"] = 0
|
|
1246
1362
|
bs["last_infra_error_session_id"] = None
|
|
1247
1363
|
bs["sessions"] = []
|
|
1248
1364
|
bs["last_session_id"] = None
|
|
1365
|
+
bs["last_failed_session_id"] = None
|
|
1249
1366
|
bs["resume_from_phase"] = None
|
|
1250
1367
|
reset_continuation_metadata(bs)
|
|
1368
|
+
bs.update(checkout_identity)
|
|
1251
1369
|
bs["updated_at"] = now_iso()
|
|
1252
|
-
save_bug_status(state_dir,
|
|
1370
|
+
err = save_bug_status(state_dir, bug_id, bs)
|
|
1371
|
+
if err:
|
|
1372
|
+
error_out("Failed to save bug status: {}".format(err))
|
|
1373
|
+
return
|
|
1253
1374
|
|
|
1254
1375
|
result = {
|
|
1255
|
-
"action": "
|
|
1256
|
-
"reset_count": len(
|
|
1376
|
+
"action": "reset_state",
|
|
1377
|
+
"reset_count": len(recovery_ids),
|
|
1257
1378
|
"bugs": reset_details,
|
|
1258
1379
|
}
|
|
1259
1380
|
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
@@ -1283,7 +1404,12 @@ def main():
|
|
|
1283
1404
|
error_out("Cannot load bug fix list: {}".format(err))
|
|
1284
1405
|
|
|
1285
1406
|
if args.action == "get_next":
|
|
1286
|
-
action_get_next(
|
|
1407
|
+
action_get_next(
|
|
1408
|
+
bug_list_data,
|
|
1409
|
+
args.state_dir,
|
|
1410
|
+
args.project_root,
|
|
1411
|
+
args.bug_list,
|
|
1412
|
+
)
|
|
1287
1413
|
elif args.action == "start":
|
|
1288
1414
|
action_start(args, args.bug_list, args.state_dir)
|
|
1289
1415
|
elif args.action == "update":
|
|
@@ -1296,8 +1422,8 @@ def main():
|
|
|
1296
1422
|
action_clean(args, args.bug_list, args.state_dir)
|
|
1297
1423
|
elif args.action == "pause":
|
|
1298
1424
|
action_pause(args.state_dir)
|
|
1299
|
-
elif args.action == "
|
|
1300
|
-
|
|
1425
|
+
elif args.action == "reset_state":
|
|
1426
|
+
action_reset_state(args, args.bug_list, args.state_dir)
|
|
1301
1427
|
elif args.action == "complete":
|
|
1302
1428
|
# Shortcut: 'complete' is equivalent to 'update --session-status success'
|
|
1303
1429
|
args.session_status = "success"
|
|
@@ -14,6 +14,7 @@ if _PIPELINE_ROOT not in sys.path:
|
|
|
14
14
|
|
|
15
15
|
from prizmkit_runtime.checkpoint_state import ( # noqa: E402
|
|
16
16
|
MAX_OUTER_REPAIR_ROUNDS,
|
|
17
|
+
PLAN_RESULTS,
|
|
17
18
|
REVIEW_RESULTS,
|
|
18
19
|
SKILL_WORKFLOW_STAGES,
|
|
19
20
|
STEP_ID_PATTERN,
|
|
@@ -110,9 +111,16 @@ def _mandatory_predecessor_verdict_error(steps, index, workflow_type):
|
|
|
110
111
|
preceding = steps[:index]
|
|
111
112
|
review = next((item for item in preceding if item.get("skill") == "prizmkit-code-review"), None)
|
|
112
113
|
test = next((item for item in preceding if item.get("skill") == "prizmkit-test"), None)
|
|
114
|
+
skill = steps[index].get("skill")
|
|
115
|
+
if skill == "prizmkit-implement":
|
|
116
|
+
planning = [
|
|
117
|
+
item for item in preceding
|
|
118
|
+
if item.get("stage_result") in PLAN_RESULTS
|
|
119
|
+
]
|
|
120
|
+
if len(planning) != 1 or planning[0].get("status") != "completed" or planning[0].get("stage_result") != "PLAN_READY":
|
|
121
|
+
return "Implementation requires a completed PLAN_READY predecessor"
|
|
113
122
|
if review is None and test is None:
|
|
114
123
|
return None
|
|
115
|
-
skill = steps[index].get("skill")
|
|
116
124
|
if skill == "prizmkit-test":
|
|
117
125
|
if not review or review.get("status") != "completed" or review.get("stage_result") != "REVIEW_PASS":
|
|
118
126
|
return "Test stage requires a completed REVIEW_PASS predecessor"
|
|
@@ -153,29 +161,44 @@ def _validate_semantic_update(step, semantic_update):
|
|
|
153
161
|
terminal_status = semantic_update.get("terminal_status")
|
|
154
162
|
if terminal_status is not None and terminal_status not in VALID_TERMINAL_STATUSES:
|
|
155
163
|
return "Invalid terminal_status '{}'".format(terminal_status)
|
|
156
|
-
|
|
164
|
+
blocked_reason = semantic_update.get("blocked_reason")
|
|
165
|
+
blocked_policy_result = stage_result in {"TEST_NEEDS_FIXES", "TEST_BLOCKED"}
|
|
166
|
+
if terminal_status == "WORKFLOW_COMPLETED" and blocked_policy_result:
|
|
167
|
+
return "WORKFLOW_COMPLETED conflicts with blocking stage result {}".format(stage_result)
|
|
168
|
+
if blocked_reason is not None:
|
|
169
|
+
if not isinstance(blocked_reason, str) or not blocked_reason.strip():
|
|
170
|
+
return "blocked_reason must be a non-empty string"
|
|
171
|
+
if terminal_status == "WORKFLOW_COMPLETED":
|
|
172
|
+
return "WORKFLOW_COMPLETED cannot retain blocked_reason"
|
|
173
|
+
if terminal_status != "WORKFLOW_BLOCKED" and not blocked_policy_result:
|
|
174
|
+
return "blocked_reason requires terminal_status=WORKFLOW_BLOCKED"
|
|
175
|
+
if terminal_status == "WORKFLOW_BLOCKED" and not blocked_reason:
|
|
157
176
|
return "WORKFLOW_BLOCKED requires blocked_reason"
|
|
158
177
|
return None
|
|
159
178
|
|
|
160
179
|
|
|
161
180
|
def _apply_repair_policy(l4_state, stage_result, repair_scope):
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
181
|
+
terminal_blockers = {
|
|
182
|
+
"PLAN_BLOCKED": "plan_blocked",
|
|
183
|
+
"IMPLEMENT_REPAIR": "implementation_repair",
|
|
184
|
+
"IMPLEMENT_BLOCKED": "implementation_blocked",
|
|
185
|
+
"TEST_BLOCKED": "test_blocked",
|
|
186
|
+
"RETRO_BLOCKED": "retrospective_blocked",
|
|
187
|
+
"COMMIT_BLOCKED": "commit_blocked",
|
|
188
|
+
}
|
|
189
|
+
if stage_result in terminal_blockers:
|
|
168
190
|
l4_state.update(
|
|
169
191
|
terminal_status="WORKFLOW_BLOCKED",
|
|
170
|
-
blocked_reason=
|
|
171
|
-
"test_needs_fixes"
|
|
172
|
-
if stage_result == "TEST_NEEDS_FIXES"
|
|
173
|
-
else "test_blocked"
|
|
174
|
-
),
|
|
192
|
+
blocked_reason=terminal_blockers[stage_result],
|
|
175
193
|
current_stage=None,
|
|
176
194
|
next_stage=None,
|
|
177
195
|
)
|
|
178
196
|
return False
|
|
197
|
+
if stage_result not in {"REVIEW_NEEDS_FIXES", "TEST_NEEDS_FIXES"}:
|
|
198
|
+
return False
|
|
199
|
+
current_round = l4_state.get("repair_round", 0)
|
|
200
|
+
if not isinstance(current_round, int) or isinstance(current_round, bool):
|
|
201
|
+
current_round = MAX_OUTER_REPAIR_ROUNDS
|
|
179
202
|
if current_round >= MAX_OUTER_REPAIR_ROUNDS:
|
|
180
203
|
l4_state.update(
|
|
181
204
|
repair_round=MAX_OUTER_REPAIR_ROUNDS,
|
|
@@ -194,12 +217,36 @@ def _apply_repair_policy(l4_state, stage_result, repair_scope):
|
|
|
194
217
|
)
|
|
195
218
|
return False
|
|
196
219
|
l4_state["repair_round"] = current_round + 1
|
|
220
|
+
l4_state["repair_scope"] = repair_scope
|
|
197
221
|
l4_state["terminal_status"] = None
|
|
198
222
|
l4_state["blocked_reason"] = None
|
|
199
223
|
l4_state["next_stage"] = "prizmkit-implement"
|
|
200
224
|
return True
|
|
201
225
|
|
|
202
226
|
|
|
227
|
+
def _prepare_repair_reentry(data, index):
|
|
228
|
+
"""Rewind downstream gates when a routed repair re-enters implementation."""
|
|
229
|
+
step = data["steps"][index]
|
|
230
|
+
if step.get("skill") != "prizmkit-implement":
|
|
231
|
+
return
|
|
232
|
+
state_key = semantic_state_key(str(data.get("workflow_type") or ""))
|
|
233
|
+
l4_state = data.get(state_key)
|
|
234
|
+
if not isinstance(l4_state, dict):
|
|
235
|
+
return
|
|
236
|
+
if (
|
|
237
|
+
l4_state.get("next_stage") != "prizmkit-implement"
|
|
238
|
+
or l4_state.get("stage_result")
|
|
239
|
+
not in {"REVIEW_NEEDS_FIXES", "TEST_NEEDS_FIXES"}
|
|
240
|
+
):
|
|
241
|
+
return
|
|
242
|
+
for later in data["steps"][index + 1:]:
|
|
243
|
+
later["status"] = "pending"
|
|
244
|
+
later.pop("stage_result", None)
|
|
245
|
+
later.pop("note", None)
|
|
246
|
+
l4_state["terminal_status"] = None
|
|
247
|
+
l4_state["blocked_reason"] = None
|
|
248
|
+
|
|
249
|
+
|
|
203
250
|
def _sync_cursor(data, l4_state, preserve_next_stage=False):
|
|
204
251
|
active = next(
|
|
205
252
|
(step for step in data["steps"] if step.get("status") not in TERMINAL_STEP_STATUSES),
|
|
@@ -280,6 +327,9 @@ def update_checkpoint(
|
|
|
280
327
|
suffix = " Available: {}".format(", ".join(available)) if available else ""
|
|
281
328
|
return {"ok": False, "error": "{}{}".format(find_error or "Step not found.", suffix)}
|
|
282
329
|
|
|
330
|
+
if new_status == "in_progress":
|
|
331
|
+
_prepare_repair_reentry(data, index)
|
|
332
|
+
|
|
283
333
|
if new_status in {"in_progress", "completed", "skipped"} and not _predecessors_terminal(data["steps"], index):
|
|
284
334
|
return {
|
|
285
335
|
"ok": False,
|
|
@@ -295,6 +345,11 @@ def update_checkpoint(
|
|
|
295
345
|
return {"ok": False, "error": verdict_error}
|
|
296
346
|
|
|
297
347
|
semantic_update = _normalize_semantic_update(semantic_update)
|
|
348
|
+
if semantic_update.get("stage_result") == "COMMITTED":
|
|
349
|
+
return {
|
|
350
|
+
"ok": False,
|
|
351
|
+
"error": "COMMITTED is Runtime-owned and cannot be written by the Prompt checkpoint helper",
|
|
352
|
+
}
|
|
298
353
|
semantic_error = _validate_semantic_update(step, semantic_update)
|
|
299
354
|
if semantic_error:
|
|
300
355
|
return {"ok": False, "error": semantic_error}
|
|
@@ -308,6 +363,10 @@ def update_checkpoint(
|
|
|
308
363
|
stage_result = stage_result or step.get("stage_result")
|
|
309
364
|
if stage_result != "TEST_PASS":
|
|
310
365
|
return {"ok": False, "error": "Test completion requires TEST_PASS"}
|
|
366
|
+
if new_status == "completed" and step.get("skill") == "prizmkit-committer":
|
|
367
|
+
stage_result = stage_result or step.get("stage_result")
|
|
368
|
+
if stage_result != "COMMITTED":
|
|
369
|
+
return {"ok": False, "error": "Committer completion requires Runtime-owned COMMITTED"}
|
|
311
370
|
expected_status = expected_status_for_stage_result(stage_result)
|
|
312
371
|
if expected_status is not None and new_status != expected_status:
|
|
313
372
|
return {
|
|
@@ -339,7 +398,7 @@ def update_checkpoint(
|
|
|
339
398
|
if key in semantic_update:
|
|
340
399
|
l4_state[key] = semantic_update[key]
|
|
341
400
|
l4_state["schema_version"] = 1
|
|
342
|
-
l4_state["orchestrator"] = "
|
|
401
|
+
l4_state["orchestrator"] = "pipeline-runtime"
|
|
343
402
|
skill = step.get("skill")
|
|
344
403
|
l4_state["stage"] = SKILL_WORKFLOW_STAGES.get(skill, skill)
|
|
345
404
|
l4_state["status"] = new_status
|
|
@@ -361,11 +420,13 @@ def update_checkpoint(
|
|
|
361
420
|
exists=True,
|
|
362
421
|
project_root=_checkpoint_project_root(checkpoint_path),
|
|
363
422
|
)
|
|
364
|
-
if not final_state.valid:
|
|
423
|
+
if not final_state.valid or final_state.semantic.error_code:
|
|
424
|
+
error_code = final_state.error_code or final_state.semantic.error_code
|
|
425
|
+
error_message = final_state.error_message or final_state.semantic.error_message
|
|
365
426
|
return {
|
|
366
427
|
"ok": False,
|
|
367
428
|
"error": "Update would create invalid checkpoint ({}): {}".format(
|
|
368
|
-
|
|
429
|
+
error_code, error_message
|
|
369
430
|
),
|
|
370
431
|
}
|
|
371
432
|
|