okstra 0.199.0 → 0.199.1
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/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/prompts/host-orchestration/implementation-planning.md +7 -0
- package/runtime/prompts/profiles/implementation-planning.md +1 -1
- package/runtime/python/okstra_ctl/analysis_packet.py +10 -1
- package/runtime/python/okstra_ctl/stage_map.py +6 -4
- package/runtime/schemas/final-report-v2.0.schema.json +1 -0
- package/runtime/schemas/final-report-v3.0.schema.json +1 -0
- package/runtime/skills/okstra-run/SKILL.md +7 -0
- package/runtime/validators/validate-implementation-plan-stages.py +10 -15
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -54,3 +54,10 @@ re-plan it without a RED step. Check `okstra stage-map <task-key>` first: when
|
|
|
54
54
|
what is left. That command refuses unless the commit exists and the stage's
|
|
55
55
|
conformance gate permits progress, so it cannot close a stage the run validator
|
|
56
56
|
would have blocked.
|
|
57
|
+
|
|
58
|
+
If `okstra stage-map` cannot read the prior plan during this planning run,
|
|
59
|
+
follow the analysis packet's Stage Ledger recovery guidance. A notation error
|
|
60
|
+
in an existing `dependsOn` cell is work for the new plan, not a reason to ask
|
|
61
|
+
the user to edit JSON. Preserve the prior report, existing stage numbers and
|
|
62
|
+
completed work; do not close or execute a stage from an unreadable map.
|
|
63
|
+
An ambiguous dependency or stage identity remains a blocker.
|
|
@@ -69,7 +69,7 @@ roles:
|
|
|
69
69
|
- Every stage number in the ledger is taken. A new stage takes the next number after the highest one listed; numbers are never reused or reordered. **Not yet machine-enforced** — the validator for this rule lands with the plan-amendment feature.
|
|
70
70
|
- The ledger answers two questions from two sources, and the block names both. `sourcePlan` is the plan the completed stages were actually built against; `latestPlan` is the plan the `stages` list came from and is therefore the numbering authority. When they differ, the completed work followed the former and the highest taken number comes from the latter.
|
|
71
71
|
- A `planDivergence` entry means one of two things: the two plans disagree about a stage that is already `done` — the same number naming different work, or a completed stage the latest plan no longer declares — or the plan the completed stages were built against could not be read at all, so that comparison never ran. Both block the same way: do not pick one of the two plans yourself; register it as a `Blocks=approval` clarification row and assign no new stage number until it is resolved. Completed stages built against an *earlier* plan are not a divergence — that is the normal shape of an amended plan and the ledger folds it silently.
|
|
72
|
-
- The block is absent ONLY on a task's first planning run. Its absence then means there is no prior plan, not that no stage is done. When the ledger could not be read, the packet
|
|
72
|
+
- The block is absent ONLY on a task's first planning run. Its absence then means there is no prior plan, not that no stage is done. When the ledger could not be read, the packet names the source and reason. Continue planning to repair unambiguous dependency notation in the new report, retaining every stage number and title and every completed stage body. Record the original and corrected values with their source. Do not overwrite the prior report or select an older plan. Assign no new stage number until the corrected Stage Map validates; unresolved dependency meaning remains a blocker. The preparation behavior is covered by `tests/run/test_stage_ledger_prepare.py`; preservation of author intent remains a review guideline.
|
|
73
73
|
- **Prior Planning Run (read before re-deriving anything):** on a re-run the packet carries a `## Prior Planning Run` block with the previous planning run's clarification rows, its Stage Map one line per stage, the findings its convergence left `contested` / `worker-unique`, and the path to the report itself. It is context, not an approved plan — the previous run did not reach approval. Use it so a stage you are not changing keeps the wording it already had and a settled question is not re-opened under a new `P-*` number; where the brief, the directive, or a clarification answer disagrees with it, they win. The block is absent on a first planning run and on a clarification re-run, which receives the same rows through `## Clarification Carry-In Extract` instead. **Enforced:** `okstra_ctl.prior_planning.build_prior_planning_summary` assembles it and `okstra_ctl.analysis_packet._prior_planning_block` renders it; nothing checks that a worker read it.
|
|
74
74
|
- Primary focus areas:
|
|
75
75
|
- requirement gaps
|
|
@@ -538,10 +538,19 @@ def _stage_ledger_unavailable_block(notice: str) -> list[str]:
|
|
|
538
538
|
"",
|
|
539
539
|
f"- Reason: {notice.strip()}",
|
|
540
540
|
"",
|
|
541
|
+
"Continue this planning run by reading that report and the implementation",
|
|
542
|
+
"carry records. Repair an unambiguous dependency notation error in the",
|
|
543
|
+
"new planning report, preserving every existing stage number and title",
|
|
544
|
+
"and the bodies of completed stages. Record each original and corrected",
|
|
545
|
+
"value with its source path. Do not overwrite the prior report or fall",
|
|
546
|
+
"back to an older plan. If the intended dependency cannot be established",
|
|
547
|
+
"from those records, report that uncertainty as a blocker; do not guess.",
|
|
548
|
+
"Validate the corrected Stage Map before treating its dependencies as facts.",
|
|
549
|
+
"",
|
|
541
550
|
"Do not assign a number to any new stage in this state. A number taken",
|
|
542
551
|
"by a plan this run could not read would collide with the completed",
|
|
543
552
|
"work under it, and that collision stays silent until integration.",
|
|
544
|
-
"
|
|
553
|
+
"Until the corrected map validates, keep new stage numbering blocked.",
|
|
545
554
|
"",
|
|
546
555
|
]
|
|
547
556
|
|
|
@@ -159,15 +159,17 @@ def _stage_map_table_lines(text: str, source_plan_path: str) -> list[str]:
|
|
|
159
159
|
]
|
|
160
160
|
|
|
161
161
|
|
|
162
|
-
def
|
|
162
|
+
def parse_stage_dependencies(
|
|
163
163
|
value: str, row_number: int, source_plan_path: str,
|
|
164
164
|
) -> tuple[int, ...]:
|
|
165
|
+
"""계획 작성 검증과 실행 시 읽기가 같은 의존성 문법을 사용한다."""
|
|
166
|
+
value = value.strip()
|
|
165
167
|
if value in {"", "(none)"}:
|
|
166
168
|
return ()
|
|
167
169
|
dependencies: list[int] = []
|
|
168
170
|
for token in value.split(","):
|
|
169
171
|
normalized = token.strip()
|
|
170
|
-
if not
|
|
172
|
+
if not re.fullmatch(r"[1-9][0-9]*", normalized):
|
|
171
173
|
raise StageMapError(
|
|
172
174
|
"stage_map",
|
|
173
175
|
f"Stage Map row {row_number} has invalid depends-on token "
|
|
@@ -215,7 +217,7 @@ def _parse_stage_map_row(
|
|
|
215
217
|
return StageMapStage(
|
|
216
218
|
stage_number,
|
|
217
219
|
cells[1],
|
|
218
|
-
|
|
220
|
+
parse_stage_dependencies(cells[2], row_number, source_plan_path),
|
|
219
221
|
step_count,
|
|
220
222
|
cells[4],
|
|
221
223
|
)
|
|
@@ -337,7 +339,7 @@ def _parse_data_stage_map_row(
|
|
|
337
339
|
return StageMapStage(
|
|
338
340
|
stage_number,
|
|
339
341
|
title,
|
|
340
|
-
|
|
342
|
+
parse_stage_dependencies(depends_on, row_number, source_plan_path),
|
|
341
343
|
step_count,
|
|
342
344
|
exit_summary,
|
|
343
345
|
)
|
|
@@ -7741,6 +7741,7 @@
|
|
|
7741
7741
|
"dependsOn": {
|
|
7742
7742
|
"type": "string",
|
|
7743
7743
|
"minLength": 1,
|
|
7744
|
+
"pattern": "^\\s*(?:\\(none\\)|[1-9][0-9]*(?:\\s*,\\s*[1-9][0-9]*)*)\\s*$",
|
|
7744
7745
|
"description": "Literal Stage Map depends-on cell: `(none)` or a comma-separated stage number list."
|
|
7745
7746
|
},
|
|
7746
7747
|
"stepCount": {
|
|
@@ -7839,6 +7839,7 @@
|
|
|
7839
7839
|
"dependsOn": {
|
|
7840
7840
|
"type": "string",
|
|
7841
7841
|
"minLength": 1,
|
|
7842
|
+
"pattern": "^\\s*(?:\\(none\\)|[1-9][0-9]*(?:\\s*,\\s*[1-9][0-9]*)*)\\s*$",
|
|
7842
7843
|
"description": "Literal Stage Map depends-on cell: `(none)` or a comma-separated stage number list."
|
|
7843
7844
|
},
|
|
7844
7845
|
"stepCount": {
|
|
@@ -354,6 +354,13 @@ re-plan it without a RED step. Check `okstra stage-map <task-key>` first: when
|
|
|
354
354
|
what is left. That command refuses unless the commit exists and the stage's
|
|
355
355
|
conformance gate permits progress, so it cannot close a stage the run validator
|
|
356
356
|
would have blocked.
|
|
357
|
+
|
|
358
|
+
If `okstra stage-map` cannot read the prior plan during this planning run,
|
|
359
|
+
follow the analysis packet's Stage Ledger recovery guidance. A notation error
|
|
360
|
+
in an existing `dependsOn` cell is work for the new plan, not a reason to ask
|
|
361
|
+
the user to edit JSON. Preserve the prior report, existing stage numbers and
|
|
362
|
+
completed work; do not close or execute a stage from an unreadable map.
|
|
363
|
+
An ambiguous dependency or stage identity remains a blocker.
|
|
357
364
|
<!-- END FRAGMENT: host-orchestration-implementation-planning -->
|
|
358
365
|
|
|
359
366
|
<!-- BEGIN FRAGMENT: host-orchestration-implementation -->
|
|
@@ -30,6 +30,7 @@ from okstra_ctl.stage_map import ( # noqa: E402
|
|
|
30
30
|
STAGE_MAP_HEADING,
|
|
31
31
|
StageMapError,
|
|
32
32
|
StageMapStage,
|
|
33
|
+
parse_stage_dependencies,
|
|
33
34
|
parse_stage_map_text,
|
|
34
35
|
schema_v2_report,
|
|
35
36
|
)
|
|
@@ -79,17 +80,6 @@ def _check_stage_map_present(text: str) -> List[ValidationError]:
|
|
|
79
80
|
return []
|
|
80
81
|
|
|
81
82
|
|
|
82
|
-
def _parse_depends_on_cell(raw: str) -> List[int] | None:
|
|
83
|
-
"""Stage numbers from schema-v2 `stageMap[].dependsOn`."""
|
|
84
|
-
value = raw.strip()
|
|
85
|
-
if value in ("(none)", ""):
|
|
86
|
-
return []
|
|
87
|
-
try:
|
|
88
|
-
return [int(x.strip()) for x in value.split(",") if x.strip()]
|
|
89
|
-
except ValueError:
|
|
90
|
-
return None
|
|
91
|
-
|
|
92
|
-
|
|
93
83
|
def _stage_numbers_monotonic(
|
|
94
84
|
stages: List[StageMapStage],
|
|
95
85
|
) -> List[ValidationError]:
|
|
@@ -495,11 +485,16 @@ def _data_stage_metas(
|
|
|
495
485
|
stage_map: List[dict],
|
|
496
486
|
) -> Tuple[List[StageMapStage], List[ValidationError]]:
|
|
497
487
|
rows = []
|
|
498
|
-
|
|
488
|
+
errors = []
|
|
489
|
+
for row_number, row in enumerate(stage_map, start=1):
|
|
499
490
|
if not isinstance(row, dict) or not isinstance(row.get("stage"), int):
|
|
500
491
|
continue
|
|
501
|
-
|
|
502
|
-
|
|
492
|
+
try:
|
|
493
|
+
depends = parse_stage_dependencies(
|
|
494
|
+
str(row.get("dependsOn") or ""), row_number, ""
|
|
495
|
+
)
|
|
496
|
+
except StageMapError as exc:
|
|
497
|
+
errors.append(ValidationError("S2", row["stage"], exc.reason))
|
|
503
498
|
continue
|
|
504
499
|
rows.append(StageMapStage(
|
|
505
500
|
row["stage"],
|
|
@@ -508,7 +503,7 @@ def _data_stage_metas(
|
|
|
508
503
|
row.get("stepCount") if isinstance(row.get("stepCount"), int) else -1,
|
|
509
504
|
str(row.get("exitContractSummary") or ""),
|
|
510
505
|
))
|
|
511
|
-
return rows, _stage_numbers_monotonic(rows)
|
|
506
|
+
return rows, errors or _stage_numbers_monotonic(rows)
|
|
512
507
|
|
|
513
508
|
|
|
514
509
|
def _check_data_slice_tdd(
|