okstra 0.55.0 → 0.56.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/bin/okstra +24 -7
- package/docs/kr/architecture.md +2 -2
- package/docs/project-structure-overview.md +0 -1
- package/docs/superpowers/plans/2026-05-25-okstra-project-root-rename.md +0 -1
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase2.md +275 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase3.md +282 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4a.md +147 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4b.md +262 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4c.md +184 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4d.md +88 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4e.md +250 -0
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa.md +409 -0
- package/docs/superpowers/specs/2026-06-07-stage-conformance-qa-design.md +169 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/bin/lib/okstra/cli.sh +5 -1
- package/runtime/bin/lib/okstra/usage.sh +5 -0
- package/runtime/bin/okstra.sh +1 -0
- package/runtime/prompts/profiles/_common-contract.md +4 -4
- package/runtime/prompts/profiles/_implementation-deliverable.md +4 -4
- package/runtime/prompts/profiles/_implementation-executor.md +1 -4
- package/runtime/prompts/profiles/_implementation-verifier.md +23 -2
- package/runtime/prompts/profiles/final-verification.md +2 -1
- package/runtime/prompts/profiles/implementation-planning.md +9 -5
- package/runtime/prompts/profiles/implementation.md +6 -6
- package/runtime/prompts/profiles/improvement-discovery.md +1 -0
- package/runtime/prompts/profiles/release-handoff.md +4 -4
- package/runtime/python/okstra_ctl/conformance.py +270 -0
- package/runtime/python/okstra_ctl/paths.py +2 -0
- package/runtime/python/okstra_ctl/run.py +29 -0
- package/runtime/schemas/final-report-v1.0.schema.json +127 -10
- package/runtime/skills/okstra-coding-preflight/SKILL.md +8 -0
- package/runtime/skills/okstra-coding-preflight/clean-code.md +6 -0
- package/runtime/skills/okstra-run/SKILL.md +12 -0
- package/runtime/skills/okstra-run/templates/pr-body.template.md +12 -12
- package/runtime/skills/okstra-setup/SKILL.md +35 -0
- package/runtime/templates/reports/final-report.template.md +63 -19
- package/runtime/templates/reports/i18n/en.json +1 -1
- package/runtime/templates/reports/i18n/ko.json +1 -1
- package/runtime/templates/reports/implementation-input.template.md +1 -1
- package/runtime/templates/reports/implementation-planning-input.template.md +3 -3
- package/runtime/validators/validate-implementation-plan-stages.py +28 -3
- package/runtime/validators/validate-run.py +98 -0
- package/src/okstra-dirs.mjs +1 -1
- package/src/migrate.mjs +0 -146
|
@@ -276,6 +276,9 @@ class PrepareInputs:
|
|
|
276
276
|
work_category: str = ""
|
|
277
277
|
base_ref: str = ""
|
|
278
278
|
approved_plan_path: str = ""
|
|
279
|
+
# implementation 전용: `--qa-waiver "<stageKey>:<reason>"` 사용자 확인형 우회.
|
|
280
|
+
# prepare-time 에 task-level conformance 매니페스트 entry.waiver 를 채운다.
|
|
281
|
+
qa_waiver: str = ""
|
|
279
282
|
stage: str = "auto"
|
|
280
283
|
clarification_response_path: str = "" # absolute or empty
|
|
281
284
|
# release-handoff 전용: PR 본문 템플릿 1회성 override. 빈 문자열이면
|
|
@@ -1092,6 +1095,28 @@ def _validate_prepare_inputs(project_root: Path, inp: PrepareInputs) -> list:
|
|
|
1092
1095
|
return ctx_stage_map
|
|
1093
1096
|
|
|
1094
1097
|
|
|
1098
|
+
def _apply_qa_waiver_if_requested(inp: "PrepareInputs", project_root: Path) -> None:
|
|
1099
|
+
"""`--qa-waiver` 가 있으면 task-level 매니페스트 entry 의 waiver 를 채운다."""
|
|
1100
|
+
if not inp.qa_waiver:
|
|
1101
|
+
return
|
|
1102
|
+
from .conformance import apply_qa_waiver, parse_qa_waiver_arg
|
|
1103
|
+
from .paths import task_dir
|
|
1104
|
+
parsed = parse_qa_waiver_arg(inp.qa_waiver)
|
|
1105
|
+
if parsed is None:
|
|
1106
|
+
raise PrepareError(
|
|
1107
|
+
f'--qa-waiver must be "<stageKey>:<reason>", got {inp.qa_waiver!r}'
|
|
1108
|
+
)
|
|
1109
|
+
stage_key, reason = parsed
|
|
1110
|
+
manifest_path = task_dir(project_root, inp.task_group, inp.task_id) / "qa" / "conformance-manifest.json"
|
|
1111
|
+
if not manifest_path.is_file():
|
|
1112
|
+
raise PrepareError(f"--qa-waiver: conformance manifest not found at {manifest_path}")
|
|
1113
|
+
manifest = json.loads(manifest_path.read_text())
|
|
1114
|
+
when = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
1115
|
+
if not apply_qa_waiver(manifest, stage_key, reason, at=when):
|
|
1116
|
+
raise PrepareError(f"--qa-waiver: stageKey {stage_key!r} not in manifest {manifest_path}")
|
|
1117
|
+
manifest_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n")
|
|
1118
|
+
|
|
1119
|
+
|
|
1095
1120
|
def _register_and_check_project(project_root: Path, inp: PrepareInputs) -> None:
|
|
1096
1121
|
"""project.json self-registration + (implementation 한정) qaCommands gate 검증."""
|
|
1097
1122
|
from okstra_project import ResolverError
|
|
@@ -1120,6 +1145,7 @@ def _register_and_check_project(project_root: Path, inp: PrepareInputs) -> None:
|
|
|
1120
1145
|
qa_errors = validate_qa_commands(project_meta.get("qaCommands"))
|
|
1121
1146
|
if qa_errors:
|
|
1122
1147
|
raise PrepareError(_format_qa_errors(qa_errors))
|
|
1148
|
+
_apply_qa_waiver_if_requested(inp, project_root)
|
|
1123
1149
|
|
|
1124
1150
|
|
|
1125
1151
|
def _resolve_roster(inp: PrepareInputs, profile_file: Path) -> tuple[list[str], str]:
|
|
@@ -1860,6 +1886,8 @@ def main(argv: list[str]) -> int:
|
|
|
1860
1886
|
p.add_argument("--critic", default="")
|
|
1861
1887
|
p.add_argument("--related-tasks", default="", dest="related_tasks_raw")
|
|
1862
1888
|
p.add_argument("--approved-plan", default="", dest="approved_plan_path")
|
|
1889
|
+
p.add_argument("--qa-waiver", default="", dest="qa_waiver",
|
|
1890
|
+
help='Stage conformance 우회: "<stageKey>:<reason>" (사용자 확인형, 매니페스트 entry.waiver 기록)')
|
|
1863
1891
|
p.add_argument(
|
|
1864
1892
|
"--stage", default="auto", dest="stage",
|
|
1865
1893
|
help=(
|
|
@@ -1975,6 +2003,7 @@ def main(argv: list[str]) -> int:
|
|
|
1975
2003
|
work_category=args.work_category,
|
|
1976
2004
|
base_ref=args.base_ref,
|
|
1977
2005
|
approved_plan_path=args.approved_plan_path,
|
|
2006
|
+
qa_waiver=args.qa_waiver,
|
|
1978
2007
|
stage=args.stage,
|
|
1979
2008
|
clarification_response_path=clarification_abs,
|
|
1980
2009
|
pr_template_path=args.pr_template_path,
|
|
@@ -330,12 +330,13 @@
|
|
|
330
330
|
|
|
331
331
|
"implementationPlanning": {
|
|
332
332
|
"type": "object",
|
|
333
|
-
"description": "RENDER_IF taskType == implementation-planning. §
|
|
333
|
+
"description": "RENDER_IF taskType == implementation-planning. §5.5 deliverables.",
|
|
334
334
|
"required": [
|
|
335
335
|
"optionCandidates",
|
|
336
336
|
"tradeoffMatrix",
|
|
337
337
|
"recommendedOption",
|
|
338
|
-
"
|
|
338
|
+
"stageMap",
|
|
339
|
+
"stages",
|
|
339
340
|
"dependencyMigrationRisk",
|
|
340
341
|
"validationChecklist",
|
|
341
342
|
"rollbackStrategy",
|
|
@@ -355,7 +356,18 @@
|
|
|
355
356
|
"items": { "$ref": "#/$defs/TradeoffRow" }
|
|
356
357
|
},
|
|
357
358
|
"recommendedOption": { "$ref": "#/$defs/RecommendedOption" },
|
|
359
|
+
"stageMap": {
|
|
360
|
+
"type": "array",
|
|
361
|
+
"minItems": 1,
|
|
362
|
+
"items": { "$ref": "#/$defs/StageMapRow" }
|
|
363
|
+
},
|
|
364
|
+
"stages": {
|
|
365
|
+
"type": "array",
|
|
366
|
+
"minItems": 1,
|
|
367
|
+
"items": { "$ref": "#/$defs/ImplementationPlanStage" }
|
|
368
|
+
},
|
|
358
369
|
"stepwiseExecution": {
|
|
370
|
+
"description": "Legacy flat summary kept for compatibility only. New reports use stageMap/stages.",
|
|
359
371
|
"type": "array",
|
|
360
372
|
"minItems": 1,
|
|
361
373
|
"items": { "$ref": "#/$defs/StepRow" }
|
|
@@ -385,7 +397,7 @@
|
|
|
385
397
|
|
|
386
398
|
"releaseHandoff": {
|
|
387
399
|
"type": "object",
|
|
388
|
-
"description": "RENDER_IF taskType == release-handoff. §
|
|
400
|
+
"description": "RENDER_IF taskType == release-handoff. §5.6 deliverables.",
|
|
389
401
|
"required": [
|
|
390
402
|
"sourceVerificationReport",
|
|
391
403
|
"featureBranchState",
|
|
@@ -419,11 +431,14 @@
|
|
|
419
431
|
},
|
|
420
432
|
"userSelections": {
|
|
421
433
|
"type": "object",
|
|
422
|
-
"required": ["h1", "h3"],
|
|
434
|
+
"required": ["h1", "h2b", "h3"],
|
|
423
435
|
"additionalProperties": false,
|
|
424
436
|
"properties": {
|
|
425
437
|
"h1": { "enum": ["local only", "push + PR", "skip"] },
|
|
426
438
|
"h2": { "type": "string" },
|
|
439
|
+
"h2b": {
|
|
440
|
+
"enum": ["not-run", "clean", "proceed anyway", "change base branch", "cancel"]
|
|
441
|
+
},
|
|
427
442
|
"h3": { "enum": ["use as-is", "edit then proceed", "cancel"] }
|
|
428
443
|
}
|
|
429
444
|
},
|
|
@@ -482,12 +497,13 @@
|
|
|
482
497
|
|
|
483
498
|
"implementation": {
|
|
484
499
|
"type": "object",
|
|
485
|
-
"description": "RENDER_IF taskType == implementation. §
|
|
500
|
+
"description": "RENDER_IF taskType == implementation. §5.7 deliverables.",
|
|
486
501
|
"required": [
|
|
487
502
|
"approvedPlanReference",
|
|
488
503
|
"commitList",
|
|
489
504
|
"diffSummary",
|
|
490
505
|
"outOfPlanEdits",
|
|
506
|
+
"stageSidecarEvidence",
|
|
491
507
|
"validationEvidence",
|
|
492
508
|
"verifierResults",
|
|
493
509
|
"rollbackVerification",
|
|
@@ -526,6 +542,7 @@
|
|
|
526
542
|
"type": "array",
|
|
527
543
|
"items": { "$ref": "#/$defs/OutOfPlanEditRow" }
|
|
528
544
|
},
|
|
545
|
+
"stageSidecarEvidence": { "$ref": "#/$defs/StageSidecarEvidence" },
|
|
529
546
|
"validationEvidence": {
|
|
530
547
|
"type": "array",
|
|
531
548
|
"minItems": 1,
|
|
@@ -547,7 +564,7 @@
|
|
|
547
564
|
|
|
548
565
|
"finalVerification": {
|
|
549
566
|
"type": "object",
|
|
550
|
-
"description": "RENDER_IF taskType == final-verification. §
|
|
567
|
+
"description": "RENDER_IF taskType == final-verification. §5.8 deliverables.",
|
|
551
568
|
"required": [
|
|
552
569
|
"sourceImplementationReport",
|
|
553
570
|
"acceptanceBlockers",
|
|
@@ -624,7 +641,7 @@
|
|
|
624
641
|
|
|
625
642
|
"allOf": [
|
|
626
643
|
{
|
|
627
|
-
"description": "implementation-planning task-type requires §
|
|
644
|
+
"description": "implementation-planning task-type requires §5.5 block.",
|
|
628
645
|
"if": {
|
|
629
646
|
"properties": { "header": { "properties": { "taskType": { "const": "implementation-planning" } } } },
|
|
630
647
|
"required": ["header"]
|
|
@@ -634,7 +651,7 @@
|
|
|
634
651
|
}
|
|
635
652
|
},
|
|
636
653
|
{
|
|
637
|
-
"description": "release-handoff task-type requires §
|
|
654
|
+
"description": "release-handoff task-type requires §5.6 block.",
|
|
638
655
|
"if": {
|
|
639
656
|
"properties": { "header": { "properties": { "taskType": { "const": "release-handoff" } } } },
|
|
640
657
|
"required": ["header"]
|
|
@@ -644,7 +661,7 @@
|
|
|
644
661
|
}
|
|
645
662
|
},
|
|
646
663
|
{
|
|
647
|
-
"description": "implementation task-type requires §
|
|
664
|
+
"description": "implementation task-type requires §5.7 block.",
|
|
648
665
|
"if": {
|
|
649
666
|
"properties": { "header": { "properties": { "taskType": { "const": "implementation" } } } },
|
|
650
667
|
"required": ["header"]
|
|
@@ -654,7 +671,7 @@
|
|
|
654
671
|
}
|
|
655
672
|
},
|
|
656
673
|
{
|
|
657
|
-
"description": "final-verification task-type requires §
|
|
674
|
+
"description": "final-verification task-type requires §5.8 block.",
|
|
658
675
|
"if": {
|
|
659
676
|
"properties": { "header": { "properties": { "taskType": { "const": "final-verification" } } } },
|
|
660
677
|
"required": ["header"]
|
|
@@ -1066,6 +1083,90 @@
|
|
|
1066
1083
|
}
|
|
1067
1084
|
},
|
|
1068
1085
|
|
|
1086
|
+
"StageMapRow": {
|
|
1087
|
+
"type": "object",
|
|
1088
|
+
"required": ["stage", "title", "dependsOn", "stepCount", "exitContractSummary"],
|
|
1089
|
+
"additionalProperties": false,
|
|
1090
|
+
"properties": {
|
|
1091
|
+
"stage": { "type": "integer", "minimum": 1 },
|
|
1092
|
+
"title": { "type": "string", "minLength": 1 },
|
|
1093
|
+
"dependsOn": {
|
|
1094
|
+
"type": "string",
|
|
1095
|
+
"minLength": 1,
|
|
1096
|
+
"description": "Literal Stage Map depends-on cell: `(none)` or a comma-separated stage number list."
|
|
1097
|
+
},
|
|
1098
|
+
"stepCount": { "type": "integer", "minimum": 0, "maximum": 6 },
|
|
1099
|
+
"exitContractSummary": { "type": "string", "minLength": 1 }
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
|
|
1103
|
+
"ImplementationPlanStage": {
|
|
1104
|
+
"type": "object",
|
|
1105
|
+
"required": [
|
|
1106
|
+
"stage",
|
|
1107
|
+
"title",
|
|
1108
|
+
"sliceValue",
|
|
1109
|
+
"acceptance",
|
|
1110
|
+
"carryIn",
|
|
1111
|
+
"stepwiseExecution",
|
|
1112
|
+
"exitContract",
|
|
1113
|
+
"stageValidation"
|
|
1114
|
+
],
|
|
1115
|
+
"additionalProperties": false,
|
|
1116
|
+
"oneOf": [
|
|
1117
|
+
{
|
|
1118
|
+
"required": ["conformanceTests"],
|
|
1119
|
+
"not": { "required": ["conformanceExemption"] }
|
|
1120
|
+
},
|
|
1121
|
+
{
|
|
1122
|
+
"required": ["conformanceExemption"],
|
|
1123
|
+
"not": { "required": ["conformanceTests"] }
|
|
1124
|
+
}
|
|
1125
|
+
],
|
|
1126
|
+
"properties": {
|
|
1127
|
+
"stage": { "type": "integer", "minimum": 1 },
|
|
1128
|
+
"title": { "type": "string", "minLength": 1 },
|
|
1129
|
+
"sliceValue": { "type": "string", "minLength": 1 },
|
|
1130
|
+
"acceptance": { "type": "string", "minLength": 1 },
|
|
1131
|
+
"conformanceTests": {
|
|
1132
|
+
"type": "string",
|
|
1133
|
+
"minLength": 1,
|
|
1134
|
+
"description": "Text after the `Conformance tests: stage-N — ...` prefix."
|
|
1135
|
+
},
|
|
1136
|
+
"conformanceExemption": {
|
|
1137
|
+
"type": "string",
|
|
1138
|
+
"minLength": 1,
|
|
1139
|
+
"description": "Text after the `Conformance exemption:` prefix."
|
|
1140
|
+
},
|
|
1141
|
+
"tddExemption": {
|
|
1142
|
+
"type": "string",
|
|
1143
|
+
"description": "Optional text after the `TDD exemption:` prefix."
|
|
1144
|
+
},
|
|
1145
|
+
"carryIn": { "type": "string", "minLength": 1 },
|
|
1146
|
+
"stepwiseExecution": {
|
|
1147
|
+
"type": "array",
|
|
1148
|
+
"minItems": 1,
|
|
1149
|
+
"maxItems": 6,
|
|
1150
|
+
"items": { "$ref": "#/$defs/StageStepRow" }
|
|
1151
|
+
},
|
|
1152
|
+
"exitContract": { "type": "string", "minLength": 1 },
|
|
1153
|
+
"stageValidation": { "type": "string", "minLength": 1 }
|
|
1154
|
+
}
|
|
1155
|
+
},
|
|
1156
|
+
|
|
1157
|
+
"StageStepRow": {
|
|
1158
|
+
"type": "object",
|
|
1159
|
+
"required": ["step", "action", "files", "command", "expected"],
|
|
1160
|
+
"additionalProperties": false,
|
|
1161
|
+
"properties": {
|
|
1162
|
+
"step": { "type": "integer", "minimum": 1 },
|
|
1163
|
+
"action": { "type": "string", "minLength": 1 },
|
|
1164
|
+
"files": { "type": "string", "minLength": 1 },
|
|
1165
|
+
"command": { "type": "string", "minLength": 1 },
|
|
1166
|
+
"expected": { "type": "string", "minLength": 1 }
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
|
|
1069
1170
|
"StepRow": {
|
|
1070
1171
|
"type": "object",
|
|
1071
1172
|
"required": ["step", "ticketId", "action", "files", "commandOrTest", "expectedOutcome"],
|
|
@@ -1080,6 +1181,22 @@
|
|
|
1080
1181
|
}
|
|
1081
1182
|
},
|
|
1082
1183
|
|
|
1184
|
+
"StageSidecarEvidence": {
|
|
1185
|
+
"type": "object",
|
|
1186
|
+
"required": ["stageNumber", "stageTitle", "carryJson", "consumerRows"],
|
|
1187
|
+
"additionalProperties": false,
|
|
1188
|
+
"properties": {
|
|
1189
|
+
"stageNumber": { "type": "integer", "minimum": 1 },
|
|
1190
|
+
"stageTitle": { "type": "string", "minLength": 1 },
|
|
1191
|
+
"carryJson": { "type": "string", "minLength": 1 },
|
|
1192
|
+
"consumerRows": {
|
|
1193
|
+
"type": "array",
|
|
1194
|
+
"minItems": 1,
|
|
1195
|
+
"items": { "type": "string", "minLength": 1 }
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
},
|
|
1199
|
+
|
|
1083
1200
|
"DependencyRow": {
|
|
1084
1201
|
"type": "object",
|
|
1085
1202
|
"required": ["id", "kind", "item", "impact", "mitigation"],
|
|
@@ -62,6 +62,14 @@ If you suspect an overlay applies but the layout is non-standard, ask one questi
|
|
|
62
62
|
- [ ] Existing code searched: `grep` for the symbol / file / identifier you are about to add. Do not duplicate.
|
|
63
63
|
- [ ] Project conventions checked: `.editorconfig`, `CONTRIBUTING.md`, formatter config (`.prettierrc`, `rustfmt.toml`, `ktlint`, `google-java-format`, etc.). **Project rules override this skill on conflict.**
|
|
64
64
|
|
|
65
|
+
## Completion sweep (before declaring a multi-file change done)
|
|
66
|
+
|
|
67
|
+
Per-file checks miss cross-cutting issues; each commit can be individually clean while the sum violates DRY. Before saying "done":
|
|
68
|
+
|
|
69
|
+
- [ ] **Domain-literal sweep:** `grep -rn` every domain enum value / predicate you added or touched in WHERE clauses, filters, or branches. The same literal at 2+ I/O sites is a *candidate* scattered decision — ask: would these sites change together when the business rule changes? Same decision → consolidate into one named constant or query builder in the domain layer and make every site reference it. Different decisions that merely share a value → leave them separate; coupling incidental duplication is worse than the repetition. (The identifier grep above does NOT catch this — sweep *values*, not just names.)
|
|
70
|
+
- [ ] **Stand-alone name test for exports:** for each exported identifier, look at its siblings — can a caller pick the right one from the names alone? If a comment must explain which to use, the name fails; encode the distinguishing fact in it (e.g., the input shape: `parseRows` vs `parseRowsFromFlatItems`).
|
|
71
|
+
- [ ] **No documented forks:** two deliberate variants of one capability must not survive as parallel implementations with a comment explaining the delta. Re-read both bodies and check the deltas are genuinely parametric: if they reduce to a few orthogonal options, collapse into one implementation taking explicit option parameters that encode them. If encoding the delta would take more than ~3 options, or add more branching than the duplication it removes, they are two capabilities — keep two implementations with distinct honest names and delete the "variant of" framing. Either way the comment-documented fork dies. "The divergence is documented" stays a refused rationalization, and a two-capabilities verdict must come from reading the bodies, not from reluctance to refactor.
|
|
72
|
+
|
|
65
73
|
## Boundaries
|
|
66
74
|
|
|
67
75
|
- This skill does **not** auto-format. Run the project's formatter yourself.
|
|
@@ -22,6 +22,11 @@ const sum = (arr) => arr.reduce((acc, x) => acc + x, 0);
|
|
|
22
22
|
|
|
23
23
|
Caveat: do not extract until the duplication is real (rule of three). Premature abstraction is also a code smell.
|
|
24
24
|
|
|
25
|
+
Two more forms of duplication that hide from symbol-level grep:
|
|
26
|
+
|
|
27
|
+
- **Scattered domain literals** — the same enum value / predicate repeated across queries or filters *may* be one business decision duplicated. The test: would the sites change together when the rule changes? If yes, consolidate to a named constant or builder at a single reference point; if they merely share a value, leave them apart — coupling incidental duplication is worse than repetition.
|
|
28
|
+
- **Documented forks** — two deliberate variants of one capability kept as parallel implementations "because the delta is commented". If the delta is genuinely parametric (a few orthogonal options), collapse into one implementation with explicit option parameters; if not, split into two distinctly named capabilities. The commented fork itself is never the end state.
|
|
29
|
+
|
|
25
30
|
## KISS — Keep It Simple
|
|
26
31
|
|
|
27
32
|
The simplest solution that meets the requirement wins. Cleverness has a maintenance cost.
|
|
@@ -104,6 +109,7 @@ The test: if the name appeared in a stacktrace, an autocomplete list, or a grep
|
|
|
104
109
|
- Generic verbs with no information: `handle`, `process`, `execute`, `doStuff`, `manage`. If the function deletes-then-inserts, name it `replace`, not `update`.
|
|
105
110
|
- Constants that will collide with siblings later: `BUCKET` → `FONTS_BUCKET`, `TIMEOUT` → `HTTP_READ_TIMEOUT_MS`, `URL` → `AUTH_SERVICE_URL`.
|
|
106
111
|
- Repository / port methods named after the rule they enforce, not the data they fetch: `findValid*` / `findActive*` / `findEligible*` — the adjective encodes a business rule the caller can't inspect from the name. Prefer `findDesktopLibrariesForUser(userId)` + a domain predicate applied by the caller.
|
|
112
|
+
- Near-twin siblings a caller can't tell apart from the names alone (`parseRows` vs `parseRowsFromItems`): if a comment must explain which to pick, rename — encode the distinguishing fact (e.g., input shape: `parseRowsFromFlatItems`). Matching the existing sibling's style does not excuse the ambiguity.
|
|
107
113
|
|
|
108
114
|
This rule applies most strongly to **names crossing module boundaries**: public methods, exported constants, port methods. Private helpers in a tight local scope get more slack.
|
|
109
115
|
|
|
@@ -184,6 +184,18 @@ The python function underneath is mutex-protected (`~/.okstra/.locks/<task-key>.
|
|
|
184
184
|
|
|
185
185
|
You can delete the literal state-file path after this point — its job is done. Invoke `rm` with the literal path (e.g. `rm /var/folders/.../okstra-wizard.AbCd.json`), not a shell variable.
|
|
186
186
|
|
|
187
|
+
### Step 5.1 (implementation only): conformance waiver offer
|
|
188
|
+
|
|
189
|
+
`render-bundle` accepts an optional `--qa-waiver "<stageKey>:<reason>"` flag (implementation only). It records a **user-acknowledged** waiver into the task-level conformance manifest entry (`entry.waiver`), letting the run proceed when a stage's Tier 3 conformance script genuinely cannot run (e.g. the replica DB is unreachable). The waiver records the user's reason **verbatim**.
|
|
190
|
+
|
|
191
|
+
This is **never** a lead/worker self-exemption — only the user may waive. Offer it **only** when conformance BLOCKING is expected (the chosen stage declares a conformance entry whose script you cannot run in this environment). Surface it as a 3-option recommendation picker (per the run-prompt recommendation rule):
|
|
192
|
+
|
|
193
|
+
1. (recommended) Run the conformance script — no waiver.
|
|
194
|
+
2. Waive this stage — ask the user for the exact `<stageKey>` and reason, then pass `--qa-waiver "<stageKey>:<reason>"` to `render-bundle` (reason = the user's words, unedited).
|
|
195
|
+
3. 직접 입력 — the user types the full `<stageKey>:<reason>` value.
|
|
196
|
+
|
|
197
|
+
When the user picks a waiver, append `--qa-waiver "<stageKey>:<reason>"` to the `render-bundle` invocation above. Omit the flag entirely otherwise (do **not** pass `--qa-waiver ""`). A malformed value or unknown `<stageKey>` aborts `render-bundle` with a `PrepareError`.
|
|
198
|
+
|
|
187
199
|
## Step 6: Take over as Claude lead
|
|
188
200
|
|
|
189
201
|
Read `<INSTRUCTION_SET_PATH>/claude-execution-prompt.md` verbatim and enter `Claude lead` mode. The lead prompt now points to compact intake artifacts first (`active-run-context`, `analysis-profile.md`, and `analysis-packet.md`); full source files such as `analysis-material.md`, `reference-expectations.md`, and `final-report-template.md` are lazy/fallback inputs. Follow the rendered prompt order, do not preempt it.
|
|
@@ -17,25 +17,25 @@ okstra release-handoff 기본 PR 본문 템플릿.
|
|
|
17
17
|
- `git log --oneline <base>..HEAD` 의 commit 범위
|
|
18
18
|
- `git diff <base>..HEAD --stat` 의 변경 파일 통계
|
|
19
19
|
|
|
20
|
-
빈 섹션은 그대로 두지 말고 통째로 삭제합니다. HTML 주석은 PR 생성 전에
|
|
21
|
-
모두 제거됩니다.
|
|
22
20
|
-->
|
|
23
21
|
|
|
24
|
-
##
|
|
22
|
+
## **Please check if the PR fulfills these requirements**
|
|
23
|
+
- [ ] Commits have a single intent
|
|
24
|
+
- [ ] Tests for the changes have been added (for bug fixes / features)
|
|
25
|
+
- [ ] I reviewed my own code
|
|
26
|
+
- [ ] I tested the changes (if not, explain why in the "Other information" section)
|
|
27
|
+
- [ ] Docs have been added / updated
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
## **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
|
|
27
30
|
|
|
28
|
-
## Changes
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
## **What is the current behavior?** (You can also link to an open issue here)
|
|
31
33
|
|
|
32
|
-
## Test plan
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
## **What is the new behavior (if this is a feature change)?**
|
|
35
36
|
|
|
36
|
-
- [ ] <검증 항목>
|
|
37
|
-
- [ ] <검증 항목>
|
|
38
37
|
|
|
39
|
-
##
|
|
38
|
+
## **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
## **Other information**:
|
|
@@ -181,6 +181,41 @@ The field is preserved across the runtime's auto-upserts of
|
|
|
181
181
|
`updatedAt` are runtime-owned, so manual edits to `qaCommands`
|
|
182
182
|
survive every subsequent `okstra setup` / `okstra run` invocation.
|
|
183
183
|
|
|
184
|
+
### Step 4.6.1 (optional): `qaEnv` — Tier 3 conformance environment
|
|
185
|
+
|
|
186
|
+
`implementation` / `final-verification` verifiers run **stage
|
|
187
|
+
conformance scripts** (Tier 3) that may need to reach a database or an
|
|
188
|
+
HTTP endpoint to prove the diff satisfies upstream requirements. Declare
|
|
189
|
+
the environment those scripts are allowed to touch under `qaEnv`. Every
|
|
190
|
+
field is optional; declare only what your conformance scripts use.
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
"qaEnv": {
|
|
194
|
+
"replicaDbDsn": "<replica/test DB DSN — never shared/staging/prod>",
|
|
195
|
+
"appBaseUrl": "http://localhost:3000",
|
|
196
|
+
"envFile": ".okstra/qa.env",
|
|
197
|
+
"surfacePatterns": { "db": ["*.sql", "*repository*"], "http": ["*controller*"] }
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- `replicaDbDsn` — DSN the conformance script connects to. MUST be a
|
|
202
|
+
replica / disposable test DB, **never** a shared, staging, or
|
|
203
|
+
production database (conformance scripts may write).
|
|
204
|
+
- `appBaseUrl` — base URL for endpoint-level conformance checks
|
|
205
|
+
(local app only).
|
|
206
|
+
- `envFile` — path (under `.okstra/`) to an env file the verifier
|
|
207
|
+
sources before running conformance scripts.
|
|
208
|
+
- `surfacePatterns` — per-project **override** of the diff-surface
|
|
209
|
+
cross-check map (`capability → glob list`). The validator maps each
|
|
210
|
+
changed file to a capability surface (`db` / `http` / `io`) and fails
|
|
211
|
+
the run when the diff touches a surface no stage `requires`. The
|
|
212
|
+
built-in patterns (e.g. `*router*` for `http`, `*storage*` for `io`)
|
|
213
|
+
are broad and match many front-end files, so front-end-heavy repos
|
|
214
|
+
should override with narrower globs to avoid false BLOCKING verdicts
|
|
215
|
+
(Phase 4b review note). An over-broad pattern over-blocks; an
|
|
216
|
+
over-narrow one lets an undeclared surface through — tune to the
|
|
217
|
+
repo's real db/http/io file naming.
|
|
218
|
+
|
|
184
219
|
## Step 4.7 (automatic): project-local Claude settings symlink
|
|
185
220
|
|
|
186
221
|
`okstra setup` (and `okstra run` on its first invocation per project)
|
|
@@ -140,7 +140,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
140
140
|
{% if header.taskType == 'implementation-planning' %}
|
|
141
141
|
## 5.5 Implementation Plan Deliverables
|
|
142
142
|
|
|
143
|
-
###
|
|
143
|
+
### Option Candidates{% if t("sectionAside.optionCandidates") != "Option Candidates" %} ({{ t("sectionAside.optionCandidates") }}){% endif %}
|
|
144
144
|
|
|
145
145
|
{% for opt in implementationPlanning.optionCandidates %}
|
|
146
146
|
**{{ opt.name }}**
|
|
@@ -158,7 +158,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
158
158
|
|
|
159
159
|
{% endfor %}
|
|
160
160
|
|
|
161
|
-
###
|
|
161
|
+
### Trade-off Matrix{% if t("sectionAside.tradeOffMatrix") != "Trade-off Matrix" %} ({{ t("sectionAside.tradeOffMatrix") }}){% endif %}
|
|
162
162
|
|
|
163
163
|
| Option | Complexity | Risk | Reversibility | Test Coverage Cost | Rollout Cost |
|
|
164
164
|
|--------|-----------|------|---------------|--------------------|--------------|
|
|
@@ -166,7 +166,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
166
166
|
| {{ row.option }} | {{ row.complexity }} | {{ row.risk }} | {{ row.reversibility }} | {{ row.testCoverageCost }} | {{ row.rolloutCost }} |
|
|
167
167
|
{% endfor %}
|
|
168
168
|
|
|
169
|
-
###
|
|
169
|
+
### Recommended Option{% if t("sectionAside.recommendedOption") != "Recommended Option" %} ({{ t("sectionAside.recommendedOption") }}){% endif %}
|
|
170
170
|
|
|
171
171
|
| {{ t("implementationPlanning.recommendedTableHeaderLabel") }} | {{ t("implementationPlanning.recommendedTableHeaderValue") }} |
|
|
172
172
|
|------|----|
|
|
@@ -175,15 +175,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
175
175
|
| {{ t("implementationPlanning.rationaleLabel") }} | {{ implementationPlanning.recommendedOption.rationale }} |
|
|
176
176
|
| {{ t("implementationPlanning.rejectedSummaryLabel") }} | {{ implementationPlanning.recommendedOption.rejectedSummary }} |
|
|
177
177
|
|
|
178
|
-
###
|
|
179
|
-
|
|
180
|
-
| Step | Ticket ID | Action (≤ 5min) | Files | Command / Test | Expected outcome |
|
|
181
|
-
|------|-----------|------------------|-------|----------------|-------------------|
|
|
182
|
-
{% for row in implementationPlanning.stepwiseExecution -%}
|
|
183
|
-
| {{ row.step }} | `{{ row.ticketId }}` | {{ row.action }} | `{{ row.files }}` | `{{ row.commandOrTest }}` | {{ row.expectedOutcome }} |
|
|
184
|
-
{% endfor %}
|
|
185
|
-
|
|
186
|
-
### 5.5.5 Dependency / Migration Risk{% if t("sectionAside.dependencyRisk") != "Dependency / Migration Risk" %} ({{ t("sectionAside.dependencyRisk") }}){% endif %}
|
|
178
|
+
### Dependency / Migration Risk{% if t("sectionAside.dependencyRisk") != "Dependency / Migration Risk" %} ({{ t("sectionAside.dependencyRisk") }}){% endif %}
|
|
187
179
|
|
|
188
180
|
{% if implementationPlanning.dependencyMigrationRisk | length == 0 -%}
|
|
189
181
|
{{ t("emptyState.dependencyRisk") }}
|
|
@@ -195,7 +187,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
195
187
|
{% endfor %}
|
|
196
188
|
{%- endif %}
|
|
197
189
|
|
|
198
|
-
###
|
|
190
|
+
### Validation Checklist{% if t("sectionAside.validationChecklist") != "Validation Checklist" %} ({{ t("sectionAside.validationChecklist") }}){% endif %}
|
|
199
191
|
|
|
200
192
|
| Phase | Ticket ID | Check | Command / Observation | Expected outcome |
|
|
201
193
|
|-------|-----------|-------|------------------------|-------------------|
|
|
@@ -203,7 +195,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
203
195
|
| {{ row.phase }} | `{{ row.ticketId }}` | {{ row.check }} | `{{ row.commandOrObservation }}` | {{ row.expectedOutcome }} |
|
|
204
196
|
{% endfor %}
|
|
205
197
|
|
|
206
|
-
###
|
|
198
|
+
### Rollback Strategy{% if t("sectionAside.rollbackStrategy") != "Rollback Strategy" %} ({{ t("sectionAside.rollbackStrategy") }}){% endif %}
|
|
207
199
|
|
|
208
200
|
| ID | Step | Action | Trigger signal | {{ t("columns.checkMethod") }} |
|
|
209
201
|
|----|------|--------|----------------|-----------|
|
|
@@ -211,7 +203,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
211
203
|
| {{ row.id }} | {{ row.step }} | `{{ row.action }}` | {{ row.triggerSignal }} | `{{ row.verificationMethod }}` |
|
|
212
204
|
{% endfor %}
|
|
213
205
|
|
|
214
|
-
###
|
|
206
|
+
### Requirement Coverage
|
|
215
207
|
|
|
216
208
|
| ID | Source | Requirement | Covered by option / stage / step | Status |
|
|
217
209
|
|----|--------|-------------|-----------------------------------|--------|
|
|
@@ -219,6 +211,45 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
219
211
|
| {{ row.id }} | `{{ row.source }}` | {{ row.requirement }} | {{ row.coveredBy }} | `{{ row.status }}` |
|
|
220
212
|
{% endfor %}
|
|
221
213
|
|
|
214
|
+
## 5.5 Stage Map
|
|
215
|
+
|
|
216
|
+
| stage | title | depends-on | step-count | exit-contract-summary |
|
|
217
|
+
|-------|-------|------------|------------|-----------------------|
|
|
218
|
+
{% for row in implementationPlanning.stageMap -%}
|
|
219
|
+
| {{ row.stage }} | {{ row.title }} | {{ row.dependsOn }} | {{ row.stepCount }} | {{ row.exitContractSummary }} |
|
|
220
|
+
{% endfor %}
|
|
221
|
+
|
|
222
|
+
{% for stage in implementationPlanning.stages %}
|
|
223
|
+
## 5.5.{{ stage.stage }} Stage {{ stage.stage }}: {{ stage.title }}
|
|
224
|
+
|
|
225
|
+
Slice value: {{ stage.sliceValue }}
|
|
226
|
+
Acceptance: {{ stage.acceptance }}
|
|
227
|
+
{% if stage.conformanceTests %}Conformance tests: stage-{{ stage.stage }} — {{ stage.conformanceTests }}
|
|
228
|
+
{% else %}Conformance exemption: {{ stage.conformanceExemption }}
|
|
229
|
+
{% endif %}{% if stage.tddExemption %}TDD exemption: {{ stage.tddExemption }}
|
|
230
|
+
{% endif %}
|
|
231
|
+
### Carry-In
|
|
232
|
+
|
|
233
|
+
{{ stage.carryIn }}
|
|
234
|
+
|
|
235
|
+
### Stepwise Execution Order
|
|
236
|
+
|
|
237
|
+
| step | action | files | command | expected |
|
|
238
|
+
|------|--------|-------|---------|----------|
|
|
239
|
+
{% for row in stage.stepwiseExecution -%}
|
|
240
|
+
| {{ row.step }} | {{ row.action }} | `{{ row.files }}` | `{{ row.command }}` | {{ row.expected }} |
|
|
241
|
+
{% endfor %}
|
|
242
|
+
|
|
243
|
+
### Stage Exit Contract
|
|
244
|
+
|
|
245
|
+
{{ stage.exitContract }}
|
|
246
|
+
|
|
247
|
+
### Stage Validation
|
|
248
|
+
|
|
249
|
+
{{ stage.stageValidation }}
|
|
250
|
+
|
|
251
|
+
{% endfor %}
|
|
252
|
+
|
|
222
253
|
### 5.5.9 Plan Body Verification{% if t("sectionAside.planBodyVerification") != "Plan Body Verification" %} ({{ t("sectionAside.planBodyVerification") }}){% endif %}
|
|
223
254
|
|
|
224
255
|
{{ t("sectionIntro.planBodyVerification") }}
|
|
@@ -271,6 +302,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
271
302
|
|---------|-----------|--------------------|--------------------|
|
|
272
303
|
| H1 | {{ t("releaseHandoff.h1Body") }} | `{{ releaseHandoff.userSelections.h1 }}` | `local only` / `push + PR` / `skip` |
|
|
273
304
|
| H2 | {{ t("releaseHandoff.h2Body") }} | `{{ releaseHandoff.userSelections.h2 or t("releaseHandoff.h2DefaultLabel") }}` | {{ t("releaseHandoff.h2OptionsLabel") }} |
|
|
305
|
+
| H2b | Merge conflict probe | `{{ releaseHandoff.userSelections.h2b or releaseHandoff.mergeConflictProbe.kind }}` | `not-run` / `clean` / `proceed anyway` / `change base branch` / `cancel` |
|
|
274
306
|
| H3 | {{ t("releaseHandoff.h3Body") }} | `{{ releaseHandoff.userSelections.h3 }}` | `use as-is` / `edit then proceed` / `cancel` |
|
|
275
307
|
|
|
276
308
|
### 5.6.4 Executed Commands
|
|
@@ -371,7 +403,19 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
371
403
|
{% endfor %}
|
|
372
404
|
{%- endif %}
|
|
373
405
|
|
|
374
|
-
### 5.7.5
|
|
406
|
+
### 5.7.5 Stage Sidecar Evidence
|
|
407
|
+
|
|
408
|
+
- Stage: `{{ implementation.stageSidecarEvidence.stageNumber }}` — {{ implementation.stageSidecarEvidence.stageTitle }}
|
|
409
|
+
- Carry sidecar JSON:
|
|
410
|
+
```json
|
|
411
|
+
{{ implementation.stageSidecarEvidence.carryJson }}
|
|
412
|
+
```
|
|
413
|
+
- Appended `consumers.jsonl` rows:
|
|
414
|
+
{% for row in implementation.stageSidecarEvidence.consumerRows -%}
|
|
415
|
+
> {{ row }}
|
|
416
|
+
{% endfor %}
|
|
417
|
+
|
|
418
|
+
### 5.7.6 Validation Evidence
|
|
375
419
|
|
|
376
420
|
| Phase | Command | Exit code | Output tail | TDD evidence |
|
|
377
421
|
|-------|---------|-----------|-------------|--------------|
|
|
@@ -379,7 +423,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
379
423
|
| {{ row.phase }} | `{{ row.command }}` | `{{ row.exitCode }}` | {{ row.outputTail }} | {{ row.tddEvidence or '--' }} |
|
|
380
424
|
{% endfor %}
|
|
381
425
|
|
|
382
|
-
### 5.7.
|
|
426
|
+
### 5.7.7 Verifier Results
|
|
383
427
|
|
|
384
428
|
{% for block in implementation.verifierResults %}
|
|
385
429
|
- **{{ block.verifier }}** — Verdict: `{{ block.verdict }}`
|
|
@@ -390,7 +434,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
390
434
|
- Discrepancy: {{ block.discrepancy or t("emptyState.discrepancy") }}
|
|
391
435
|
{% endfor %}
|
|
392
436
|
|
|
393
|
-
### 5.7.
|
|
437
|
+
### 5.7.8 Rollback Verification
|
|
394
438
|
|
|
395
439
|
| Category | Rollback command | Verification | Result |
|
|
396
440
|
|----------|-------------------|---------------|--------|
|
|
@@ -398,7 +442,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
398
442
|
| {{ row.category }} | `{{ row.rollbackCommand }}` | {{ row.verification }} | `{{ row.result }}` |
|
|
399
443
|
{% endfor %}
|
|
400
444
|
|
|
401
|
-
### 5.7.
|
|
445
|
+
### 5.7.9 Routing Recommendation
|
|
402
446
|
|
|
403
447
|
{{ implementation.routingRecommendation }}
|
|
404
448
|
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"h2Body": "PR base branch (when H1 = `push + PR`)",
|
|
106
106
|
"h3Body": "How should the PR title/body draft be handled?",
|
|
107
107
|
"h2DefaultLabel": "(n/a)",
|
|
108
|
-
"h2OptionsLabel": "staging / preprod /
|
|
108
|
+
"h2OptionsLabel": "staging / preprod / main / user input",
|
|
109
109
|
"noMutationNote": "(no mutating command — H1=`skip` or H3=`cancel`)",
|
|
110
110
|
"commandsTableHeader": {
|
|
111
111
|
"outputSummary": "stdout/stderr summary"
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"h2Body": "PR base 브랜치 (H1=`push + PR` 인 경우)",
|
|
106
106
|
"h3Body": "PR title/body 초안 처리",
|
|
107
107
|
"h2DefaultLabel": "(n/a)",
|
|
108
|
-
"h2OptionsLabel": "staging / preprod /
|
|
108
|
+
"h2OptionsLabel": "staging / preprod / main / 사용자 입력",
|
|
109
109
|
"noMutationNote": "(mutating 명령 미실행 — H1=`skip` 또는 H3=`cancel`)",
|
|
110
110
|
"commandsTableHeader": {
|
|
111
111
|
"outputSummary": "stdout/stderr 요약"
|
|
@@ -27,7 +27,7 @@ taskType: "{{FM_TASK_TYPE}}"
|
|
|
27
27
|
## Approved Plan Reference (mandatory)
|
|
28
28
|
|
|
29
29
|
- Approved plan path: `runs/implementation-planning/<run-id>/reports/final-report.md`
|
|
30
|
-
- Approval evidence (quoted exactly from the plan's
|
|
30
|
+
- Approval evidence (quoted exactly from the plan's YAML frontmatter, e.g. `approved: true`):
|
|
31
31
|
- Recommended option name selected from the plan:
|
|
32
32
|
- Plan's bite-sized step list (paste or reference by anchor):
|
|
33
33
|
|