prizmkit 1.1.7 → 1.1.9
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/codebuddy/skill-adapter.js +21 -7
- package/bundled/agents/prizm-dev-team-reviewer.md +53 -173
- package/bundled/dev-pipeline/.env.example +45 -0
- package/bundled/dev-pipeline/README.md +64 -64
- package/bundled/dev-pipeline/SCHEMA_ANALYSIS.md +535 -0
- package/bundled/dev-pipeline/assets/feature-list-example.json +0 -1
- package/bundled/dev-pipeline/launch-bugfix-daemon.sh +64 -18
- package/bundled/dev-pipeline/launch-feature-daemon.sh +15 -12
- package/bundled/dev-pipeline/launch-refactor-daemon.sh +64 -18
- package/bundled/dev-pipeline/lib/branch.sh +6 -1
- package/bundled/dev-pipeline/lib/common.sh +71 -0
- package/bundled/dev-pipeline/lib/heartbeat.sh +2 -2
- package/bundled/dev-pipeline/reset-bug.sh +10 -9
- package/bundled/dev-pipeline/reset-feature.sh +9 -8
- package/bundled/dev-pipeline/reset-refactor.sh +10 -9
- package/bundled/dev-pipeline/retry-bugfix.sh +67 -29
- package/bundled/dev-pipeline/retry-feature.sh +54 -18
- package/bundled/dev-pipeline/retry-refactor.sh +112 -29
- package/bundled/dev-pipeline/run-bugfix.sh +281 -59
- package/bundled/dev-pipeline/run-feature.sh +53 -18
- package/bundled/dev-pipeline/run-refactor.sh +392 -66
- package/bundled/dev-pipeline/scripts/check-session-status.py +24 -1
- package/bundled/dev-pipeline/scripts/cleanup-logs.py +2 -2
- package/bundled/dev-pipeline/scripts/detect-stuck.py +195 -85
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +57 -33
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +25 -9
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +104 -17
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +34 -9
- package/bundled/dev-pipeline/scripts/init-pipeline.py +10 -10
- package/bundled/dev-pipeline/scripts/init-refactor-pipeline.py +19 -8
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +1 -5
- package/bundled/dev-pipeline/scripts/patch-completion-notes.py +191 -0
- package/bundled/dev-pipeline/scripts/update-bug-status.py +167 -22
- package/bundled/dev-pipeline/scripts/update-feature-status.py +104 -62
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +351 -21
- package/bundled/dev-pipeline/templates/agent-prompts/dev-fix.md +1 -1
- package/bundled/dev-pipeline/templates/agent-prompts/reviewer-review.md +7 -11
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +41 -7
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +27 -3
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +43 -19
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +54 -26
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +6 -15
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +36 -25
- package/bundled/dev-pipeline/templates/feature-list-schema.json +109 -31
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +270 -0
- package/bundled/dev-pipeline/templates/refactor-list-schema.json +11 -3
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +3 -1
- package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +1 -0
- package/bundled/dev-pipeline/templates/sections/feature-context.md +2 -0
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +29 -2
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +22 -0
- package/bundled/dev-pipeline/templates/sections/phase-deploy-verification.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +8 -6
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +7 -5
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +3 -3
- package/bundled/skills/_metadata.json +5 -22
- package/bundled/skills/app-planner/SKILL.md +98 -72
- package/bundled/skills/app-planner/assets/app-design-guide.md +1 -1
- package/bundled/skills/app-planner/references/architecture-decisions.md +1 -1
- package/bundled/skills/app-planner/references/project-brief-guide.md +69 -66
- package/bundled/skills/bug-fix-workflow/SKILL.md +52 -9
- package/bundled/skills/bug-planner/SKILL.md +139 -197
- package/bundled/skills/bug-planner/assets/bug-confirmation-template.md +43 -0
- package/bundled/skills/bug-planner/references/critic-and-verification.md +44 -0
- package/bundled/skills/bug-planner/references/error-recovery.md +73 -0
- package/bundled/skills/bug-planner/references/input-formats.md +53 -0
- package/bundled/skills/bug-planner/references/schema-validation.md +25 -0
- package/bundled/skills/bug-planner/references/severity-rules.md +16 -0
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +4 -8
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +34 -39
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +49 -36
- package/bundled/skills/feature-pipeline-launcher/scripts/preflight-check.py +3 -3
- package/bundled/skills/feature-planner/SKILL.md +53 -142
- package/bundled/skills/feature-planner/assets/evaluation-guide.md +1 -1
- package/bundled/skills/feature-planner/assets/planning-guide.md +21 -5
- package/bundled/skills/feature-planner/references/browser-interaction.md +2 -4
- package/bundled/skills/feature-planner/references/completeness-review.md +57 -0
- package/bundled/skills/feature-planner/references/error-recovery.md +16 -35
- package/bundled/skills/feature-planner/references/incremental-feature-planning.md +1 -1
- package/bundled/skills/feature-planner/references/new-project-planning.md +2 -2
- package/bundled/skills/feature-planner/scripts/validate-and-generate.py +19 -20
- package/bundled/skills/feature-workflow/SKILL.md +24 -25
- package/bundled/skills/prizm-kit/SKILL.md +39 -49
- package/bundled/skills/prizmkit-code-review/SKILL.md +51 -64
- package/bundled/skills/prizmkit-code-review/rules/dimensions.md +85 -0
- package/bundled/skills/prizmkit-code-review/rules/fix-strategy.md +11 -11
- package/bundled/skills/prizmkit-committer/SKILL.md +3 -31
- package/bundled/skills/prizmkit-deploy/SKILL.md +34 -31
- package/bundled/skills/prizmkit-deploy/assets/deploy-template.md +1 -1
- package/bundled/skills/prizmkit-implement/SKILL.md +35 -68
- package/bundled/skills/prizmkit-init/SKILL.md +112 -65
- package/bundled/skills/prizmkit-init/assets/project-brief-template.md +82 -0
- package/bundled/skills/prizmkit-plan/SKILL.md +120 -79
- package/bundled/skills/prizmkit-plan/assets/plan-template.md +28 -18
- package/bundled/skills/prizmkit-plan/assets/spec-template.md +28 -11
- package/bundled/skills/prizmkit-plan/references/clarify-guide.md +3 -3
- package/bundled/skills/prizmkit-plan/references/verification-checklist.md +60 -0
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +10 -81
- package/bundled/skills/prizmkit-prizm-docs/assets/{PRIZM-SPEC.md → prizm-docs-format.md} +41 -526
- package/bundled/skills/prizmkit-prizm-docs/references/op-init.md +46 -0
- package/bundled/skills/prizmkit-prizm-docs/references/op-rebuild.md +16 -0
- package/bundled/skills/prizmkit-prizm-docs/references/op-status.md +14 -0
- package/bundled/skills/prizmkit-prizm-docs/references/op-update.md +19 -0
- package/bundled/skills/prizmkit-prizm-docs/references/op-validate.md +17 -0
- package/bundled/skills/prizmkit-retrospective/SKILL.md +27 -65
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +3 -4
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +7 -25
- package/bundled/skills/recovery-workflow/SKILL.md +22 -22
- package/bundled/skills/recovery-workflow/evals/evals.json +5 -5
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +43 -10
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +48 -40
- package/bundled/skills/refactor-planner/SKILL.md +43 -61
- package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +17 -17
- package/bundled/skills/refactor-workflow/SKILL.md +23 -24
- package/bundled/team/prizm-dev-team.json +1 -1
- package/bundled/{skills/prizm-kit/assets → templates}/project-memory-template.md +1 -1
- package/package.json +1 -1
- package/src/clean.js +3 -4
- package/src/gitignore-template.js +7 -9
- package/src/scaffold.js +14 -5
- package/bundled/dev-pipeline/templates/agent-prompts/reviewer-analyze.md +0 -5
- package/bundled/dev-pipeline/templates/sections/phase-analyze-agent.md +0 -19
- package/bundled/dev-pipeline/templates/sections/phase-analyze-full.md +0 -19
- package/bundled/skills/app-planner/references/project-conventions.md +0 -93
- package/bundled/skills/prizmkit-analyze/SKILL.md +0 -207
- package/bundled/skills/prizmkit-code-review/rules/dimensions-bugfix.md +0 -25
- package/bundled/skills/prizmkit-code-review/rules/dimensions-feature.md +0 -43
- package/bundled/skills/prizmkit-code-review/rules/dimensions-refactor.md +0 -25
- package/bundled/skills/prizmkit-implement/references/deploy-guide-protocol.md +0 -69
- package/bundled/skills/prizmkit-verify/SKILL.md +0 -281
- package/bundled/skills/prizmkit-verify/scripts/verify-light.py +0 -402
|
@@ -38,13 +38,13 @@ def parse_args():
|
|
|
38
38
|
parser = argparse.ArgumentParser(
|
|
39
39
|
description=(
|
|
40
40
|
"Generate a session-specific bootstrap prompt from a template "
|
|
41
|
-
"and feature-list.json."
|
|
41
|
+
"and .prizmkit/plans/feature-list.json."
|
|
42
42
|
)
|
|
43
43
|
)
|
|
44
44
|
parser.add_argument(
|
|
45
45
|
"--feature-list",
|
|
46
46
|
required=True,
|
|
47
|
-
help="Path to feature-list.json",
|
|
47
|
+
help="Path to .prizmkit/plans/feature-list.json",
|
|
48
48
|
)
|
|
49
49
|
parser.add_argument(
|
|
50
50
|
"--feature-id",
|
|
@@ -274,7 +274,12 @@ def format_global_context(global_context, project_root=None):
|
|
|
274
274
|
|
|
275
275
|
|
|
276
276
|
def get_completed_dependencies(features, feature):
|
|
277
|
-
"""Look up dependency features and list those with status=completed.
|
|
277
|
+
"""Look up dependency features and list those with status=completed.
|
|
278
|
+
|
|
279
|
+
When a completed dependency has completion_notes (written by the AI
|
|
280
|
+
session and propagated by the pipeline runner), include them as rich
|
|
281
|
+
context so the downstream session knows what was built.
|
|
282
|
+
"""
|
|
278
283
|
deps = feature.get("dependencies", [])
|
|
279
284
|
if not deps:
|
|
280
285
|
return "- (no dependencies)"
|
|
@@ -285,17 +290,26 @@ def get_completed_dependencies(features, feature):
|
|
|
285
290
|
if isinstance(f, dict) and "id" in f:
|
|
286
291
|
feature_map[f["id"]] = f
|
|
287
292
|
|
|
288
|
-
|
|
293
|
+
sections = []
|
|
289
294
|
for dep_id in deps:
|
|
290
295
|
dep = feature_map.get(dep_id)
|
|
291
296
|
if dep and dep.get("status") == "completed":
|
|
292
|
-
|
|
297
|
+
header = "- **{}** — {} (completed)".format(
|
|
293
298
|
dep_id, dep.get("title", "Untitled")
|
|
294
|
-
)
|
|
299
|
+
)
|
|
300
|
+
notes = dep.get("completion_notes", [])
|
|
301
|
+
if notes and isinstance(notes, list):
|
|
302
|
+
note_lines = "\n".join(
|
|
303
|
+
" - {}".format(n) for n in notes
|
|
304
|
+
if isinstance(n, str) and n.strip()
|
|
305
|
+
)
|
|
306
|
+
if note_lines:
|
|
307
|
+
header += "\n" + note_lines
|
|
308
|
+
sections.append(header)
|
|
295
309
|
|
|
296
|
-
if not
|
|
310
|
+
if not sections:
|
|
297
311
|
return "- (no completed dependencies yet)"
|
|
298
|
-
return "\n".join(
|
|
312
|
+
return "\n".join(sections)
|
|
299
313
|
|
|
300
314
|
|
|
301
315
|
def get_prev_session_status(state_dir, feature_id):
|
|
@@ -348,7 +362,7 @@ def get_prev_session_status(state_dir, feature_id):
|
|
|
348
362
|
|
|
349
363
|
|
|
350
364
|
def _read_project_brief(project_root):
|
|
351
|
-
"""Read project-brief.md from
|
|
365
|
+
"""Read project-brief.md from new or old location with fallback.
|
|
352
366
|
|
|
353
367
|
Returns the file content as a string, or a fallback message if absent.
|
|
354
368
|
This brief is generated by app-planner during interactive planning and
|
|
@@ -356,13 +370,25 @@ def _read_project_brief(project_root):
|
|
|
356
370
|
marked [ ] for pending or [x] for completed. Feature sessions should mark
|
|
357
371
|
items [x] and append key file paths when implementing relevant ideas.
|
|
358
372
|
"""
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
373
|
+
# Check both new and old paths for backward compatibility
|
|
374
|
+
new_path = os.path.join(project_root, ".prizmkit", "plans", "project-brief.md")
|
|
375
|
+
old_path = os.path.join(project_root, "project-brief.md")
|
|
376
|
+
|
|
377
|
+
for brief_path in [new_path, old_path]:
|
|
378
|
+
if os.path.isfile(brief_path):
|
|
379
|
+
try:
|
|
380
|
+
with open(brief_path, "r", encoding="utf-8") as f:
|
|
381
|
+
content = f.read().strip()
|
|
382
|
+
if brief_path == old_path:
|
|
383
|
+
# Warn user about old path
|
|
384
|
+
import sys
|
|
385
|
+
print("⚠️ Migration notice: project-brief.md found in root. "
|
|
386
|
+
"Please move to .prizmkit/plans/project-brief.md",
|
|
387
|
+
file=sys.stderr)
|
|
388
|
+
return content
|
|
389
|
+
except IOError:
|
|
390
|
+
return "(project-brief.md exists but could not be read)"
|
|
391
|
+
|
|
366
392
|
return "(No project brief available)"
|
|
367
393
|
|
|
368
394
|
|
|
@@ -549,7 +575,6 @@ SECTION_TO_SKILL = {
|
|
|
549
575
|
".prizmkit/specs/{slug}/plan.md"]),
|
|
550
576
|
"phase-plan": ("prizmkit-plan", "Plan & Tasks",
|
|
551
577
|
[".prizmkit/specs/{slug}/plan.md"]),
|
|
552
|
-
"phase-analyze": ("prizmkit-analyze", "Analyze", []),
|
|
553
578
|
"phase-critic-plan": ("critic-plan-review", "Critic: Plan Review", []),
|
|
554
579
|
"phase-implement": ("prizmkit-implement", "Implement + Test", []),
|
|
555
580
|
"phase-critic-code": ("critic-code-review", "Critic: Code Review", []),
|
|
@@ -945,16 +970,6 @@ def assemble_sections(pipeline_mode, sections_dir, init_done, is_resume,
|
|
|
945
970
|
load_section(sections_dir,
|
|
946
971
|
"phase-plan-agent.md")))
|
|
947
972
|
|
|
948
|
-
# --- Analyze (only for agent tiers) ---
|
|
949
|
-
if pipeline_mode == "standard":
|
|
950
|
-
sections.append(("phase-analyze",
|
|
951
|
-
load_section(sections_dir,
|
|
952
|
-
"phase-analyze-agent.md")))
|
|
953
|
-
elif pipeline_mode == "full":
|
|
954
|
-
sections.append(("phase-analyze",
|
|
955
|
-
load_section(sections_dir,
|
|
956
|
-
"phase-analyze-full.md")))
|
|
957
|
-
|
|
958
973
|
# --- Critic: Plan Challenge (only if critic enabled) ---
|
|
959
974
|
if critic_enabled:
|
|
960
975
|
if pipeline_mode == "full":
|
|
@@ -980,12 +995,23 @@ def assemble_sections(pipeline_mode, sections_dir, init_done, is_resume,
|
|
|
980
995
|
load_section(sections_dir,
|
|
981
996
|
"phase-implement-agent.md")))
|
|
982
997
|
|
|
998
|
+
# --- Test Failure Recovery Protocol (all tiers) ---
|
|
999
|
+
sections.append(("test-failure-recovery",
|
|
1000
|
+
load_section(sections_dir, "test-failure-recovery.md")))
|
|
1001
|
+
|
|
983
1002
|
# --- Critic: Code Challenge (only if critic enabled, agent tiers) ---
|
|
984
1003
|
if critic_enabled and pipeline_mode in ("standard", "full"):
|
|
985
1004
|
sections.append(("phase-critic-code",
|
|
986
1005
|
load_section(sections_dir,
|
|
987
1006
|
"phase-critic-code.md")))
|
|
988
1007
|
|
|
1008
|
+
# --- AC Verification Checklist (all tiers) ---
|
|
1009
|
+
ac_checklist_path = os.path.join(sections_dir, "ac-verification-checklist.md")
|
|
1010
|
+
if os.path.isfile(ac_checklist_path):
|
|
1011
|
+
sections.append(("ac-verification-checklist",
|
|
1012
|
+
load_section(sections_dir,
|
|
1013
|
+
"ac-verification-checklist.md")))
|
|
1014
|
+
|
|
989
1015
|
# --- Review (only for agent tiers) ---
|
|
990
1016
|
if pipeline_mode == "full":
|
|
991
1017
|
sections.append(("phase-review",
|
|
@@ -1179,12 +1205,12 @@ def build_replacements(args, feature, features, global_context, script_dir):
|
|
|
1179
1205
|
validator_scripts_dir = os.path.join(project_root, "dev-pipeline", "scripts")
|
|
1180
1206
|
init_script_path = os.path.join(validator_scripts_dir, "init-dev-team.py")
|
|
1181
1207
|
|
|
1182
|
-
# Session status path (relative to
|
|
1208
|
+
# Session status path (relative to project root)
|
|
1183
1209
|
session_status_path = os.path.join(
|
|
1184
|
-
"
|
|
1210
|
+
".prizmkit", "state", "features", args.feature_id,
|
|
1185
1211
|
"sessions", args.session_id, "session-status.json",
|
|
1186
1212
|
)
|
|
1187
|
-
# Make it
|
|
1213
|
+
# Make it absolute from project root
|
|
1188
1214
|
session_status_abs = os.path.join(project_root, session_status_path)
|
|
1189
1215
|
|
|
1190
1216
|
prev_status = get_prev_session_status(args.state_dir, args.feature_id)
|
|
@@ -1221,7 +1247,7 @@ def build_replacements(args, feature, features, global_context, script_dir):
|
|
|
1221
1247
|
critic_enabled = bool(feature.get("critic", False))
|
|
1222
1248
|
|
|
1223
1249
|
# Determine critic count (from feature field, default 1)
|
|
1224
|
-
# Multi-critic voting (3) must be explicitly set by the user in feature-list.json
|
|
1250
|
+
# Multi-critic voting (3) must be explicitly set by the user in .prizmkit/plans/feature-list.json
|
|
1225
1251
|
critic_count = feature.get("critic_count", 1)
|
|
1226
1252
|
|
|
1227
1253
|
# Guard: if critic enabled but agent file missing, force disable and warn
|
|
@@ -1309,7 +1335,6 @@ def build_replacements(args, feature, features, global_context, script_dir):
|
|
|
1309
1335
|
"{{DEV_SUBAGENT_PATH}}": dev_subagent,
|
|
1310
1336
|
"{{REVIEWER_SUBAGENT_PATH}}": reviewer_subagent,
|
|
1311
1337
|
"{{CRITIC_SUBAGENT_PATH}}": critic_subagent,
|
|
1312
|
-
"{{VALIDATOR_SCRIPTS_DIR}}": validator_scripts_dir,
|
|
1313
1338
|
"{{INIT_SCRIPT_PATH}}": init_script_path,
|
|
1314
1339
|
"{{SESSION_STATUS_PATH}}": session_status_abs,
|
|
1315
1340
|
"{{PROJECT_ROOT}}": project_root,
|
|
@@ -1324,7 +1349,6 @@ def build_replacements(args, feature, features, global_context, script_dir):
|
|
|
1324
1349
|
"{{INIT_DONE}}": "true" if init_done else "false",
|
|
1325
1350
|
"{{HAS_SPEC}}": "true" if artifacts["has_spec"] else "false",
|
|
1326
1351
|
"{{HAS_PLAN}}": "true" if artifacts["has_plan"] else "false",
|
|
1327
|
-
"{{ARTIFACTS_COMPLETE}}": "true" if artifacts["all_complete"] else "false",
|
|
1328
1352
|
"{{BROWSER_URL}}": browser_url,
|
|
1329
1353
|
"{{BROWSER_SETUP_COMMAND}}": browser_setup_command,
|
|
1330
1354
|
"{{BROWSER_VERIFY_STEPS}}": browser_verify_steps,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Generate a session-specific bug fix bootstrap prompt from template and bug-fix-list.json.
|
|
2
|
+
"""Generate a session-specific bug fix bootstrap prompt from template and .prizmkit/plans/bug-fix-list.json.
|
|
3
3
|
|
|
4
|
-
Reads the bugfix-bootstrap-prompt.md template and a bug-fix-list.json, resolves all
|
|
4
|
+
Reads the bugfix-bootstrap-prompt.md template and a .prizmkit/plans/bug-fix-list.json, resolves all
|
|
5
5
|
{{PLACEHOLDER}} variables, handles conditional blocks, and writes the rendered
|
|
6
6
|
prompt to the specified output path.
|
|
7
7
|
|
|
@@ -31,18 +31,20 @@ def parse_args():
|
|
|
31
31
|
parser = argparse.ArgumentParser(
|
|
32
32
|
description=(
|
|
33
33
|
"Generate a session-specific bug fix bootstrap prompt from a template "
|
|
34
|
-
"and bug-fix-list.json."
|
|
34
|
+
"and .prizmkit/plans/bug-fix-list.json."
|
|
35
35
|
)
|
|
36
36
|
)
|
|
37
|
-
parser.add_argument("--bug-list", required=True, help="Path to bug-fix-list.json")
|
|
37
|
+
parser.add_argument("--bug-list", required=True, help="Path to .prizmkit/plans/bug-fix-list.json")
|
|
38
38
|
parser.add_argument("--bug-id", required=True, help="Bug ID to generate prompt for (e.g. B-001)")
|
|
39
39
|
parser.add_argument("--session-id", required=True, help="Session ID for this pipeline session")
|
|
40
40
|
parser.add_argument("--run-id", required=True, help="Pipeline run ID")
|
|
41
41
|
parser.add_argument("--retry-count", required=True, help="Current retry count")
|
|
42
42
|
parser.add_argument("--resume-phase", required=True, help='Phase to resume from, or "null" for fresh start')
|
|
43
|
-
parser.add_argument("--state-dir", default=None, help="State directory
|
|
43
|
+
parser.add_argument("--state-dir", default=None, help="State directory (default: .prizmkit/state/bugfix)")
|
|
44
44
|
parser.add_argument("--output", required=True, help="Output path for the rendered prompt")
|
|
45
45
|
parser.add_argument("--template", default=None, help="Custom template path. Defaults to {script_dir}/../templates/bugfix-bootstrap-prompt.md")
|
|
46
|
+
parser.add_argument("--mode", default=None, help="Pipeline execution mode override: lite, standard, full")
|
|
47
|
+
parser.add_argument("--critic", default=None, help="Enable critic agent: true/false")
|
|
46
48
|
return parser.parse_args()
|
|
47
49
|
|
|
48
50
|
|
|
@@ -217,7 +219,7 @@ def build_replacements(args, bug, global_context, script_dir):
|
|
|
217
219
|
|
|
218
220
|
# Session status path
|
|
219
221
|
session_status_path = os.path.join(
|
|
220
|
-
project_root, "
|
|
222
|
+
project_root, ".prizmkit", "state", "bugfix", "bugs", args.bug_id,
|
|
221
223
|
"sessions", args.session_id, "session-status.json"
|
|
222
224
|
)
|
|
223
225
|
|
|
@@ -254,7 +256,6 @@ def build_replacements(args, bug, global_context, script_dir):
|
|
|
254
256
|
"{{ACCEPTANCE_CRITERIA}}": format_acceptance_criteria(
|
|
255
257
|
bug.get("acceptance_criteria", [])
|
|
256
258
|
),
|
|
257
|
-
"{{AFFECTED_FEATURE}}": bug.get("affected_feature", "N/A"),
|
|
258
259
|
"{{ENVIRONMENT}}": format_environment(bug.get("environment")),
|
|
259
260
|
"{{GLOBAL_CONTEXT}}": format_global_context(global_context, project_root),
|
|
260
261
|
"{{TEAM_CONFIG_PATH}}": team_config_path,
|
|
@@ -263,7 +264,7 @@ def build_replacements(args, bug, global_context, script_dir):
|
|
|
263
264
|
"{{SESSION_STATUS_PATH}}": session_status_path,
|
|
264
265
|
"{{PROJECT_ROOT}}": project_root,
|
|
265
266
|
"{{FIX_SCOPE}}": fix_scope,
|
|
266
|
-
"{{TIMESTAMP}}": "", #
|
|
267
|
+
"{{TIMESTAMP}}": "", # Placeholder, agent fills in the timestamp
|
|
267
268
|
}
|
|
268
269
|
|
|
269
270
|
return replacements
|
|
@@ -280,7 +281,7 @@ def process_conditional_blocks(content, bug):
|
|
|
280
281
|
content = content.replace("{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}\n", "")
|
|
281
282
|
content = content.replace("{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}", "")
|
|
282
283
|
else:
|
|
283
|
-
#
|
|
284
|
+
# Remove the entire conditional block
|
|
284
285
|
content = re.sub(
|
|
285
286
|
r"\{\{IF_VERIFICATION_MANUAL_OR_HYBRID\}\}.*?\{\{END_IF_VERIFICATION_MANUAL_OR_HYBRID\}\}\n?",
|
|
286
287
|
"", content, flags=re.DOTALL,
|
|
@@ -497,11 +498,26 @@ def main():
|
|
|
497
498
|
json.dump(checkpoint, f, indent=2, ensure_ascii=False)
|
|
498
499
|
LOGGER.info("Wrote bugfix checkpoint to %s", checkpoint_path)
|
|
499
500
|
|
|
501
|
+
# Resolve critic and mode
|
|
502
|
+
bug_critic = bug.get("critic", False)
|
|
503
|
+
if args.critic is not None:
|
|
504
|
+
critic_enabled = str(args.critic).lower() == "true"
|
|
505
|
+
else:
|
|
506
|
+
critic_enabled = bool(bug_critic)
|
|
507
|
+
|
|
508
|
+
pipeline_mode = args.mode or "standard"
|
|
509
|
+
agent_count = 5 if critic_enabled else 3
|
|
510
|
+
|
|
500
511
|
# Success
|
|
512
|
+
bug_model = bug.get("model", "")
|
|
501
513
|
output = {
|
|
502
514
|
"success": True,
|
|
503
515
|
"output_path": os.path.abspath(args.output),
|
|
504
516
|
"checkpoint_path": checkpoint_path,
|
|
517
|
+
"model": bug_model,
|
|
518
|
+
"pipeline_mode": pipeline_mode,
|
|
519
|
+
"agent_count": agent_count,
|
|
520
|
+
"critic_enabled": critic_enabled,
|
|
505
521
|
}
|
|
506
522
|
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
507
523
|
sys.exit(0)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Generate a session-specific refactor bootstrap prompt from template and refactor-list.json.
|
|
2
|
+
"""Generate a session-specific refactor bootstrap prompt from template and .prizmkit/plans/refactor-list.json.
|
|
3
3
|
|
|
4
|
-
Reads the refactor-bootstrap-prompt.md template and a refactor-list.json, resolves all
|
|
4
|
+
Reads the refactor-bootstrap-prompt.md template and a .prizmkit/plans/refactor-list.json, resolves all
|
|
5
5
|
{{PLACEHOLDER}} variables, handles conditional blocks, and writes the rendered
|
|
6
6
|
prompt to the specified output path.
|
|
7
7
|
|
|
@@ -16,6 +16,7 @@ Usage:
|
|
|
16
16
|
import argparse
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
|
+
import re
|
|
19
20
|
import sys
|
|
20
21
|
|
|
21
22
|
from utils import enrich_global_context, load_json_file, setup_logging
|
|
@@ -30,18 +31,20 @@ def parse_args():
|
|
|
30
31
|
parser = argparse.ArgumentParser(
|
|
31
32
|
description=(
|
|
32
33
|
"Generate a session-specific refactor bootstrap prompt from a template "
|
|
33
|
-
"and refactor-list.json."
|
|
34
|
+
"and .prizmkit/plans/refactor-list.json."
|
|
34
35
|
)
|
|
35
36
|
)
|
|
36
|
-
parser.add_argument("--refactor-list", required=True, help="Path to refactor-list.json")
|
|
37
|
+
parser.add_argument("--refactor-list", required=True, help="Path to .prizmkit/plans/refactor-list.json")
|
|
37
38
|
parser.add_argument("--refactor-id", required=True, help="Refactor ID to generate prompt for (e.g. R-001)")
|
|
38
39
|
parser.add_argument("--session-id", required=True, help="Session ID for this pipeline session")
|
|
39
40
|
parser.add_argument("--run-id", required=True, help="Pipeline run ID")
|
|
40
41
|
parser.add_argument("--retry-count", required=True, help="Current retry count")
|
|
41
42
|
parser.add_argument("--resume-phase", required=True, help='Phase to resume from, or "null" for fresh start')
|
|
42
|
-
parser.add_argument("--state-dir", default=None, help="State directory
|
|
43
|
+
parser.add_argument("--state-dir", default=None, help="State directory (default: .prizmkit/state/refactor)")
|
|
43
44
|
parser.add_argument("--output", required=True, help="Output path for the rendered prompt")
|
|
44
45
|
parser.add_argument("--template", default=None, help="Custom template path. Defaults to {script_dir}/../templates/refactor-bootstrap-prompt.md")
|
|
46
|
+
parser.add_argument("--mode", default=None, help="Pipeline execution mode override: lite, standard, full")
|
|
47
|
+
parser.add_argument("--critic", default=None, help="Enable critic agent: true/false")
|
|
45
48
|
return parser.parse_args()
|
|
46
49
|
|
|
47
50
|
|
|
@@ -161,15 +164,42 @@ def format_behavior_preservation(bp):
|
|
|
161
164
|
return "\n".join(lines)
|
|
162
165
|
|
|
163
166
|
|
|
164
|
-
def format_dependencies(dependencies):
|
|
165
|
-
"""Format dependencies list as a markdown bullet list.
|
|
167
|
+
def format_dependencies(dependencies, refactors=None):
|
|
168
|
+
"""Format dependencies list as a markdown bullet list with completion context.
|
|
169
|
+
|
|
170
|
+
When refactors list is provided, look up completed dependencies and include
|
|
171
|
+
their completion_notes for rich context propagation.
|
|
172
|
+
"""
|
|
166
173
|
if not dependencies or not isinstance(dependencies, list):
|
|
167
174
|
return "- (none)"
|
|
168
175
|
if len(dependencies) == 0:
|
|
169
176
|
return "- (none)"
|
|
177
|
+
|
|
178
|
+
# Build lookup map if refactors list is provided
|
|
179
|
+
refactor_map = {}
|
|
180
|
+
if refactors:
|
|
181
|
+
for r in refactors:
|
|
182
|
+
if isinstance(r, dict) and "id" in r:
|
|
183
|
+
refactor_map[r["id"]] = r
|
|
184
|
+
|
|
170
185
|
lines = []
|
|
171
186
|
for dep in dependencies:
|
|
172
|
-
|
|
187
|
+
dep_info = refactor_map.get(dep)
|
|
188
|
+
if dep_info and dep_info.get("status") == "completed":
|
|
189
|
+
header = "- **{}** — {} (completed)".format(
|
|
190
|
+
dep, dep_info.get("title", "Untitled")
|
|
191
|
+
)
|
|
192
|
+
notes = dep_info.get("completion_notes", [])
|
|
193
|
+
if notes and isinstance(notes, list):
|
|
194
|
+
note_lines = [
|
|
195
|
+
" - {}".format(n) for n in notes
|
|
196
|
+
if isinstance(n, str) and n.strip()
|
|
197
|
+
]
|
|
198
|
+
if note_lines:
|
|
199
|
+
header += "\n" + "\n".join(note_lines)
|
|
200
|
+
lines.append(header)
|
|
201
|
+
else:
|
|
202
|
+
lines.append("- `{}`".format(dep))
|
|
173
203
|
return "\n".join(lines)
|
|
174
204
|
|
|
175
205
|
|
|
@@ -219,7 +249,7 @@ def resolve_project_root(script_dir):
|
|
|
219
249
|
return os.path.abspath(project_root)
|
|
220
250
|
|
|
221
251
|
|
|
222
|
-
def build_replacements(args, refactor, global_context, script_dir):
|
|
252
|
+
def build_replacements(args, refactor, refactors, global_context, script_dir):
|
|
223
253
|
"""Build the full dict of placeholder -> replacement value."""
|
|
224
254
|
project_root = resolve_project_root(script_dir)
|
|
225
255
|
|
|
@@ -247,7 +277,7 @@ def build_replacements(args, refactor, global_context, script_dir):
|
|
|
247
277
|
|
|
248
278
|
# Session status path
|
|
249
279
|
session_status_path = os.path.join(
|
|
250
|
-
project_root, "
|
|
280
|
+
project_root, ".prizmkit", "state", "refactor", "refactors", args.refactor_id,
|
|
251
281
|
"sessions", args.session_id, "session-status.json"
|
|
252
282
|
)
|
|
253
283
|
|
|
@@ -260,7 +290,11 @@ def build_replacements(args, refactor, global_context, script_dir):
|
|
|
260
290
|
bp = refactor.get("behavior_preservation", {})
|
|
261
291
|
behavior_strategy = bp.get("strategy", "test-gate") if isinstance(bp, dict) else "test-gate"
|
|
262
292
|
existing_tests = bp.get("existing_tests", []) if isinstance(bp, dict) else []
|
|
293
|
+
if not isinstance(existing_tests, list):
|
|
294
|
+
existing_tests = []
|
|
263
295
|
new_tests_needed = bp.get("new_tests_needed", []) if isinstance(bp, dict) else []
|
|
296
|
+
if not isinstance(new_tests_needed, list):
|
|
297
|
+
new_tests_needed = []
|
|
264
298
|
|
|
265
299
|
# Format existing tests
|
|
266
300
|
if existing_tests:
|
|
@@ -296,7 +330,7 @@ def build_replacements(args, refactor, global_context, script_dir):
|
|
|
296
330
|
refactor.get("acceptance_criteria", [])
|
|
297
331
|
),
|
|
298
332
|
"{{DEPENDENCIES}}": format_dependencies(
|
|
299
|
-
refactor.get("dependencies", [])
|
|
333
|
+
refactor.get("dependencies", []), refactors
|
|
300
334
|
),
|
|
301
335
|
"{{GLOBAL_CONTEXT}}": format_global_context(global_context, project_root),
|
|
302
336
|
"{{TEAM_CONFIG_PATH}}": team_config_path,
|
|
@@ -304,17 +338,55 @@ def build_replacements(args, refactor, global_context, script_dir):
|
|
|
304
338
|
"{{REVIEWER_SUBAGENT_PATH}}": reviewer_subagent,
|
|
305
339
|
"{{SESSION_STATUS_PATH}}": session_status_path,
|
|
306
340
|
"{{PROJECT_ROOT}}": project_root,
|
|
341
|
+
"{{CHECKPOINT_PATH}}": os.path.join(
|
|
342
|
+
".prizmkit", "refactor", args.refactor_id, "workflow-checkpoint.json",
|
|
343
|
+
),
|
|
307
344
|
"{{TIMESTAMP}}": "", # Placeholder — agent fills in timestamp
|
|
308
345
|
}
|
|
309
346
|
|
|
310
347
|
return replacements
|
|
311
348
|
|
|
312
349
|
|
|
313
|
-
def
|
|
314
|
-
"""
|
|
315
|
-
|
|
350
|
+
def process_conditional_blocks(content, resume_phase):
|
|
351
|
+
"""Handle conditional blocks based on resume_phase.
|
|
352
|
+
|
|
353
|
+
- {{IF_RESUME}}...{{END_IF_RESUME}} — include only when resuming (resume_phase != "null")
|
|
354
|
+
- {{IF_FRESH_START}}...{{END_IF_FRESH_START}} — include only on fresh start (resume_phase == "null")
|
|
355
|
+
"""
|
|
356
|
+
is_resume = resume_phase != "null"
|
|
357
|
+
|
|
358
|
+
if is_resume:
|
|
359
|
+
# Keep IF_RESUME content, strip markers
|
|
360
|
+
content = content.replace("{{IF_RESUME}}\n", "")
|
|
361
|
+
content = content.replace("{{IF_RESUME}}", "")
|
|
362
|
+
content = content.replace("{{END_IF_RESUME}}\n", "")
|
|
363
|
+
content = content.replace("{{END_IF_RESUME}}", "")
|
|
364
|
+
# Remove IF_FRESH_START blocks entirely
|
|
365
|
+
content = re.sub(
|
|
366
|
+
r"\{\{IF_FRESH_START\}\}.*?\{\{END_IF_FRESH_START\}\}\n?",
|
|
367
|
+
"", content, flags=re.DOTALL,
|
|
368
|
+
)
|
|
369
|
+
else:
|
|
370
|
+
# Keep IF_FRESH_START content, strip markers
|
|
371
|
+
content = content.replace("{{IF_FRESH_START}}\n", "")
|
|
372
|
+
content = content.replace("{{IF_FRESH_START}}", "")
|
|
373
|
+
content = content.replace("{{END_IF_FRESH_START}}\n", "")
|
|
374
|
+
content = content.replace("{{END_IF_FRESH_START}}", "")
|
|
375
|
+
# Remove IF_RESUME blocks entirely
|
|
376
|
+
content = re.sub(
|
|
377
|
+
r"\{\{IF_RESUME\}\}.*?\{\{END_IF_RESUME\}\}\n?",
|
|
378
|
+
"", content, flags=re.DOTALL,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
return content
|
|
382
|
+
|
|
316
383
|
|
|
317
|
-
|
|
384
|
+
def render_template(template_content, replacements, resume_phase):
|
|
385
|
+
"""Render the template by processing conditionals and replacing placeholders."""
|
|
386
|
+
# Step 1: Process conditional blocks
|
|
387
|
+
content = process_conditional_blocks(template_content, resume_phase)
|
|
388
|
+
|
|
389
|
+
# Step 2: Replace all {{PLACEHOLDER}} variables
|
|
318
390
|
for placeholder, value in replacements.items():
|
|
319
391
|
content = content.replace(placeholder, value)
|
|
320
392
|
|
|
@@ -388,20 +460,35 @@ def main():
|
|
|
388
460
|
global_context = {}
|
|
389
461
|
|
|
390
462
|
# Build replacements
|
|
391
|
-
replacements = build_replacements(args, refactor, global_context, script_dir)
|
|
463
|
+
replacements = build_replacements(args, refactor, refactors, global_context, script_dir)
|
|
392
464
|
|
|
393
465
|
# Render the template
|
|
394
|
-
rendered = render_template(template_content, replacements)
|
|
466
|
+
rendered = render_template(template_content, replacements, args.resume_phase)
|
|
395
467
|
|
|
396
468
|
# Write the output
|
|
397
469
|
err = write_output(args.output, rendered)
|
|
398
470
|
if err:
|
|
399
471
|
emit_failure(err)
|
|
400
472
|
|
|
473
|
+
# Resolve critic and mode
|
|
474
|
+
refactor_critic = refactor.get("critic", False)
|
|
475
|
+
if args.critic is not None:
|
|
476
|
+
critic_enabled = str(args.critic).lower() == "true"
|
|
477
|
+
else:
|
|
478
|
+
critic_enabled = bool(refactor_critic)
|
|
479
|
+
|
|
480
|
+
pipeline_mode = args.mode or "standard"
|
|
481
|
+
agent_count = 5 if critic_enabled else 3
|
|
482
|
+
|
|
401
483
|
# Success
|
|
484
|
+
refactor_model = refactor.get("model", "")
|
|
402
485
|
output = {
|
|
403
486
|
"success": True,
|
|
404
487
|
"output_path": os.path.abspath(args.output),
|
|
488
|
+
"model": refactor_model,
|
|
489
|
+
"pipeline_mode": pipeline_mode,
|
|
490
|
+
"agent_count": agent_count,
|
|
491
|
+
"critic_enabled": critic_enabled,
|
|
405
492
|
}
|
|
406
493
|
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
407
494
|
sys.exit(0)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Initialize the bug-fix pipeline state directory from a bug-fix-list.json file.
|
|
2
|
+
"""Initialize the bug-fix pipeline state directory from a .prizmkit/plans/bug-fix-list.json file.
|
|
3
3
|
|
|
4
4
|
Validates the bug fix list schema, sorts by priority/severity, and creates
|
|
5
5
|
the state directory structure with pipeline and per-bug status files.
|
|
@@ -33,24 +33,28 @@ REQUIRED_BUG_FIELDS = [
|
|
|
33
33
|
VALID_SEVERITIES = ["critical", "high", "medium", "low"]
|
|
34
34
|
VALID_VERIFICATION_TYPES = ["automated", "manual", "hybrid"]
|
|
35
35
|
VALID_STATUSES = [
|
|
36
|
-
"pending", "
|
|
37
|
-
"
|
|
36
|
+
"pending", "in_progress", "completed", "failed",
|
|
37
|
+
"skipped", "needs_info",
|
|
38
|
+
]
|
|
39
|
+
TERMINAL_STATUSES = {"completed", "failed", "skipped", "needs_info"}
|
|
40
|
+
VALID_ERROR_SOURCE_TYPES = [
|
|
41
|
+
"stack_trace", "user_report", "failed_test", "log_pattern", "monitoring_alert",
|
|
38
42
|
]
|
|
39
43
|
|
|
40
44
|
|
|
41
45
|
def parse_args():
|
|
42
46
|
parser = argparse.ArgumentParser(
|
|
43
|
-
description="Initialize bug-fix pipeline state from a bug-fix-list.json file."
|
|
47
|
+
description="Initialize bug-fix pipeline state from a .prizmkit/plans/bug-fix-list.json file."
|
|
44
48
|
)
|
|
45
49
|
parser.add_argument(
|
|
46
50
|
"--bug-list",
|
|
47
51
|
required=True,
|
|
48
|
-
help="Path to the bug-fix-list.json file",
|
|
52
|
+
help="Path to the .prizmkit/plans/bug-fix-list.json file",
|
|
49
53
|
)
|
|
50
54
|
parser.add_argument(
|
|
51
55
|
"--state-dir",
|
|
52
56
|
required=True,
|
|
53
|
-
help="Path to the state directory
|
|
57
|
+
help="Path to the state directory (default: .prizmkit/state/bugfix)",
|
|
54
58
|
)
|
|
55
59
|
return parser.parse_args()
|
|
56
60
|
|
|
@@ -173,6 +177,17 @@ def validate_bugs(bugs):
|
|
|
173
177
|
bid or "index {}".format(i)
|
|
174
178
|
)
|
|
175
179
|
)
|
|
180
|
+
else:
|
|
181
|
+
es_type = error_source["type"]
|
|
182
|
+
if es_type not in VALID_ERROR_SOURCE_TYPES:
|
|
183
|
+
# Warn but don't error — the pipeline can still attempt the fix
|
|
184
|
+
print(
|
|
185
|
+
"WARNING: Bug '{}' error_source.type '{}' is not one of {} "
|
|
186
|
+
"— pipeline will still attempt to process this bug".format(
|
|
187
|
+
bid or "index {}".format(i), es_type, VALID_ERROR_SOURCE_TYPES
|
|
188
|
+
),
|
|
189
|
+
file=sys.stderr,
|
|
190
|
+
)
|
|
176
191
|
|
|
177
192
|
# Validate acceptance_criteria is a list
|
|
178
193
|
ac = bug.get("acceptance_criteria")
|
|
@@ -195,12 +210,18 @@ def create_state_directory(state_dir, bug_list_path, bugs):
|
|
|
195
210
|
bugs_dir = os.path.join(abs_state_dir, "bugs")
|
|
196
211
|
|
|
197
212
|
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
198
|
-
run_id = "bugfix-run-" + datetime.now(timezone.utc).strftime("%Y%m%d%H%M")
|
|
213
|
+
run_id = "bugfix-run-" + datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S")
|
|
199
214
|
|
|
200
215
|
# Create top-level state directory
|
|
201
216
|
os.makedirs(abs_state_dir, exist_ok=True)
|
|
202
217
|
os.makedirs(bugs_dir, exist_ok=True)
|
|
203
218
|
|
|
219
|
+
# Count bugs already in terminal status at init time
|
|
220
|
+
completed_count = sum(
|
|
221
|
+
1 for b in bugs
|
|
222
|
+
if isinstance(b, dict) and b.get("status") in TERMINAL_STATUSES
|
|
223
|
+
)
|
|
224
|
+
|
|
204
225
|
# Write pipeline.json
|
|
205
226
|
pipeline_state = {
|
|
206
227
|
"run_id": run_id,
|
|
@@ -209,7 +230,7 @@ def create_state_directory(state_dir, bug_list_path, bugs):
|
|
|
209
230
|
"bug_list_path": rel_bug_list_path,
|
|
210
231
|
"created_at": now,
|
|
211
232
|
"total_bugs": len(bugs),
|
|
212
|
-
"completed_bugs":
|
|
233
|
+
"completed_bugs": completed_count,
|
|
213
234
|
}
|
|
214
235
|
pipeline_path = os.path.join(abs_state_dir, "pipeline.json")
|
|
215
236
|
with open(pipeline_path, "w", encoding="utf-8") as f:
|
|
@@ -228,9 +249,13 @@ def create_state_directory(state_dir, bug_list_path, bugs):
|
|
|
228
249
|
sessions_dir = os.path.join(bug_dir, "sessions")
|
|
229
250
|
os.makedirs(sessions_dir, exist_ok=True)
|
|
230
251
|
|
|
252
|
+
# Respect existing terminal status from bug-fix-list.json
|
|
253
|
+
bl_status = bug.get("status", "pending")
|
|
254
|
+
init_status = bl_status if bl_status in TERMINAL_STATUSES else "pending"
|
|
255
|
+
|
|
231
256
|
bug_status = {
|
|
232
257
|
"bug_id": bid,
|
|
233
|
-
"status":
|
|
258
|
+
"status": init_status,
|
|
234
259
|
"retry_count": 0,
|
|
235
260
|
"max_retries": 3,
|
|
236
261
|
"sessions": [],
|