okstra 0.225.0 → 0.226.0
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/dist/cli-registry.mjs +6 -0
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.mjs +5 -0
- package/dist/commands/execute/render-bundle.mjs.map +1 -1
- package/dist/commands/pr/pr.mjs +42 -13
- package/dist/commands/pr/pr.mjs.map +1 -1
- package/docs/cli.md +19 -3
- package/docs/project-structure-overview.md +2 -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/globals.sh +1 -0
- package/runtime/bin/lib/okstra/usage.sh +3 -0
- package/runtime/bin/okstra.sh +2 -0
- package/runtime/prompts/duties/code-reviewer.json +7 -4
- package/runtime/prompts/duties/technical-verification-worker.json +2 -1
- package/runtime/prompts/lead/convergence.md +4 -1
- package/runtime/prompts/lead/phase-routing.md +7 -3
- package/runtime/prompts/lead/team-contract.md +1 -1
- package/runtime/prompts/wizard/prompts.ko.json +15 -0
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +5 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +108 -2
- package/runtime/python/okstra_ctl/approved_plan_stage_extract.py +206 -0
- package/runtime/python/okstra_ctl/brief_sources.py +200 -0
- package/runtime/python/okstra_ctl/business_flow/store.py +15 -2
- package/runtime/python/okstra_ctl/cmux.py +22 -5
- package/runtime/python/okstra_ctl/code_review_coverage.py +26 -2
- package/runtime/python/okstra_ctl/convergence_critic_prompt.py +4 -1
- package/runtime/python/okstra_ctl/design_surfaces.py +19 -7
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +20 -18
- package/runtime/python/okstra_ctl/implementation_direction.py +10 -0
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +41 -0
- package/runtime/python/okstra_ctl/manager_split.py +185 -27
- package/runtime/python/okstra_ctl/next_phase.py +3 -1
- package/runtime/python/okstra_ctl/phases/final_verification/prior_blockers.py +128 -0
- package/runtime/python/okstra_ctl/phases/final_verification/profile.md +8 -5
- package/runtime/python/okstra_ctl/phases/final_verification/report_assets/final-verification.template.html +1 -1
- package/runtime/python/okstra_ctl/phases/final_verification/validation.py +84 -0
- package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-executor.md +1 -1
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/authoring.py +13 -5
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/boundary.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/entry.py +67 -0
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/profile.md +4 -2
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/report_assets/implementation-option-selection.template.html +2 -1
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/validation.py +127 -18
- package/runtime/python/okstra_ctl/phases/implementation_planning/profile.md +2 -1
- package/runtime/python/okstra_ctl/phases/technical_verification/profile.md +2 -2
- package/runtime/python/okstra_ctl/report_assembly.py +23 -2
- package/runtime/python/okstra_ctl/report_views.py +2 -1
- package/runtime/python/okstra_ctl/run.py +46 -1
- package/runtime/python/okstra_ctl/run_context.py +13 -8
- package/runtime/python/okstra_ctl/wizard/confirmation.py +6 -0
- package/runtime/python/okstra_ctl/wizard/ids.py +6 -0
- package/runtime/python/okstra_ctl/wizard/registry.py +21 -0
- package/runtime/python/okstra_ctl/wizard/render.py +4 -0
- package/runtime/python/okstra_ctl/wizard/sources.py +33 -11
- package/runtime/python/okstra_ctl/wizard/state.py +4 -0
- package/runtime/python/okstra_ctl/wizard/steps_options.py +74 -9
- package/runtime/python/okstra_ctl/worker_prompt_body.py +21 -1
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +11 -4
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +3 -0
- package/runtime/schemas/final-report-v2.0.schema.json +74 -0
- package/runtime/schemas/final-report-v3.0.schema.json +99 -2
- package/runtime/skills/okstra-brief-gen/SKILL.md +26 -0
- package/runtime/skills/okstra-code-review/SKILL.md +2 -0
- package/runtime/skills/okstra-code-review/references/census-rules.md +16 -2
- package/runtime/skills/okstra-code-review/references/review-calibration.md +7 -1
- package/runtime/skills/okstra-manager/SKILL.md +15 -2
- package/runtime/skills/okstra-pr-gen/SKILL.md +15 -1
- package/runtime/templates/reports/brief.template.md +10 -1
- package/runtime/templates/reports/html/i18n/en.json +8 -0
- package/runtime/templates/reports/html/i18n/ko.json +8 -0
- package/runtime/templates/worker-prompt-preamble.md +1 -1
- package/runtime/validators/validate-brief.py +39 -1
- package/runtime/validators/validate-run.py +5 -0
|
@@ -10014,6 +10014,80 @@
|
|
|
10014
10014
|
"implementation-option-selection",
|
|
10015
10015
|
"implementation-planning"
|
|
10016
10016
|
]
|
|
10017
|
+
},
|
|
10018
|
+
"defectClass": {
|
|
10019
|
+
"$ref": "#/$defs/DefectClass"
|
|
10020
|
+
}
|
|
10021
|
+
}
|
|
10022
|
+
},
|
|
10023
|
+
"DefectClass": {
|
|
10024
|
+
"type": "object",
|
|
10025
|
+
"description": "The defect class a blocker belongs to: the shared mechanism, every location x case cell swept for it, and whether an earlier final-verification run of the same target already blocked on it. Required on every final-verification acceptance blocker by `phases/final_verification/validation.py`.",
|
|
10026
|
+
"required": [
|
|
10027
|
+
"mechanism",
|
|
10028
|
+
"cells",
|
|
10029
|
+
"recurrence"
|
|
10030
|
+
],
|
|
10031
|
+
"additionalProperties": false,
|
|
10032
|
+
"properties": {
|
|
10033
|
+
"mechanism": {
|
|
10034
|
+
"type": "string",
|
|
10035
|
+
"minLength": 1
|
|
10036
|
+
},
|
|
10037
|
+
"cells": {
|
|
10038
|
+
"type": "array",
|
|
10039
|
+
"minItems": 1,
|
|
10040
|
+
"items": {
|
|
10041
|
+
"$ref": "#/$defs/DefectClassCell"
|
|
10042
|
+
}
|
|
10043
|
+
},
|
|
10044
|
+
"recurrence": {
|
|
10045
|
+
"enum": [
|
|
10046
|
+
"new",
|
|
10047
|
+
"recurring"
|
|
10048
|
+
]
|
|
10049
|
+
},
|
|
10050
|
+
"priorBlockers": {
|
|
10051
|
+
"type": "array",
|
|
10052
|
+
"items": {
|
|
10053
|
+
"type": "string",
|
|
10054
|
+
"pattern": "^final-verification-\\d{3,} AB-\\d{3,}$"
|
|
10055
|
+
}
|
|
10056
|
+
}
|
|
10057
|
+
}
|
|
10058
|
+
},
|
|
10059
|
+
"DefectClassCell": {
|
|
10060
|
+
"type": "object",
|
|
10061
|
+
"required": [
|
|
10062
|
+
"location",
|
|
10063
|
+
"case",
|
|
10064
|
+
"verdict",
|
|
10065
|
+
"evidence"
|
|
10066
|
+
],
|
|
10067
|
+
"additionalProperties": false,
|
|
10068
|
+
"properties": {
|
|
10069
|
+
"location": {
|
|
10070
|
+
"type": "string",
|
|
10071
|
+
"minLength": 1
|
|
10072
|
+
},
|
|
10073
|
+
"case": {
|
|
10074
|
+
"type": "string",
|
|
10075
|
+
"minLength": 1
|
|
10076
|
+
},
|
|
10077
|
+
"verdict": {
|
|
10078
|
+
"enum": [
|
|
10079
|
+
"defective",
|
|
10080
|
+
"sound",
|
|
10081
|
+
"unverified",
|
|
10082
|
+
"not-applicable"
|
|
10083
|
+
]
|
|
10084
|
+
},
|
|
10085
|
+
"evidence": {
|
|
10086
|
+
"type": "string",
|
|
10087
|
+
"minLength": 1
|
|
10088
|
+
},
|
|
10089
|
+
"regressionTest": {
|
|
10090
|
+
"type": "string"
|
|
10017
10091
|
}
|
|
10018
10092
|
}
|
|
10019
10093
|
},
|
|
@@ -3510,6 +3510,9 @@
|
|
|
3510
3510
|
"$ref": "#/$defs/ImplementationOptionSafetyBlocker"
|
|
3511
3511
|
}
|
|
3512
3512
|
},
|
|
3513
|
+
"splitAcceptance": {
|
|
3514
|
+
"$ref": "#/$defs/ImplementationOptionSplitAcceptance"
|
|
3515
|
+
},
|
|
3513
3516
|
"unresolvedFeasibilityFacts": {
|
|
3514
3517
|
"type": "array",
|
|
3515
3518
|
"items": {
|
|
@@ -3608,6 +3611,9 @@
|
|
|
3608
3611
|
"$ref": "#/$defs/ImplementationOptionSafetyBlocker"
|
|
3609
3612
|
}
|
|
3610
3613
|
},
|
|
3614
|
+
"splitAcceptance": {
|
|
3615
|
+
"$ref": "#/$defs/ImplementationOptionSplitAcceptance"
|
|
3616
|
+
},
|
|
3611
3617
|
"unresolvedFeasibilityFacts": {
|
|
3612
3618
|
"type": "array",
|
|
3613
3619
|
"items": {
|
|
@@ -3936,6 +3942,18 @@
|
|
|
3936
3942
|
}
|
|
3937
3943
|
}
|
|
3938
3944
|
},
|
|
3945
|
+
"ImplementationOptionSplitAcceptance": {
|
|
3946
|
+
"type": "object",
|
|
3947
|
+
"description": "The user's accept-risk answer that settles a split feasibility vote. Report assembly checks that clarificationRef names a blocks: approval row answered with accept-risk.",
|
|
3948
|
+
"required": ["clarificationRef"],
|
|
3949
|
+
"additionalProperties": false,
|
|
3950
|
+
"properties": {
|
|
3951
|
+
"clarificationRef": {
|
|
3952
|
+
"type": "string",
|
|
3953
|
+
"pattern": "^C-[0-9]{3}$"
|
|
3954
|
+
}
|
|
3955
|
+
}
|
|
3956
|
+
},
|
|
3939
3957
|
"ImplementationOptionUnresolvedFact": {
|
|
3940
3958
|
"type": "object",
|
|
3941
3959
|
"required": [
|
|
@@ -3947,8 +3965,13 @@
|
|
|
3947
3965
|
"properties": {
|
|
3948
3966
|
"resolutionKind": {
|
|
3949
3967
|
"type": "string",
|
|
3950
|
-
"enum": ["user-decision", "technical-verification"],
|
|
3951
|
-
"description": "Optional for historical reads; blocked report publication requires explicit classification."
|
|
3968
|
+
"enum": ["user-decision", "technical-verification", "deferred-gate"],
|
|
3969
|
+
"description": "Optional for historical reads; blocked report publication requires explicit classification. deferred-gate marks a fact only a later gate can close (for example a deployed-environment measurement); it requires gate and does not block candidate validity."
|
|
3970
|
+
},
|
|
3971
|
+
"gate": {
|
|
3972
|
+
"type": "string",
|
|
3973
|
+
"minLength": 1,
|
|
3974
|
+
"description": "For deferred-gate facts: the gate that closes the fact, such as a brief External Gates entry or final-verification user-owned external QA."
|
|
3952
3975
|
},
|
|
3953
3976
|
"clarificationRefs": {
|
|
3954
3977
|
"type": "array",
|
|
@@ -10480,6 +10503,80 @@
|
|
|
10480
10503
|
"implementation-option-selection",
|
|
10481
10504
|
"implementation-planning"
|
|
10482
10505
|
]
|
|
10506
|
+
},
|
|
10507
|
+
"defectClass": {
|
|
10508
|
+
"$ref": "#/$defs/DefectClass"
|
|
10509
|
+
}
|
|
10510
|
+
}
|
|
10511
|
+
},
|
|
10512
|
+
"DefectClass": {
|
|
10513
|
+
"type": "object",
|
|
10514
|
+
"description": "The defect class a blocker belongs to: the shared mechanism, every location x case cell swept for it, and whether an earlier final-verification run of the same target already blocked on it. Required on every final-verification acceptance blocker by `phases/final_verification/validation.py`.",
|
|
10515
|
+
"required": [
|
|
10516
|
+
"mechanism",
|
|
10517
|
+
"cells",
|
|
10518
|
+
"recurrence"
|
|
10519
|
+
],
|
|
10520
|
+
"additionalProperties": false,
|
|
10521
|
+
"properties": {
|
|
10522
|
+
"mechanism": {
|
|
10523
|
+
"type": "string",
|
|
10524
|
+
"minLength": 1
|
|
10525
|
+
},
|
|
10526
|
+
"cells": {
|
|
10527
|
+
"type": "array",
|
|
10528
|
+
"minItems": 1,
|
|
10529
|
+
"items": {
|
|
10530
|
+
"$ref": "#/$defs/DefectClassCell"
|
|
10531
|
+
}
|
|
10532
|
+
},
|
|
10533
|
+
"recurrence": {
|
|
10534
|
+
"enum": [
|
|
10535
|
+
"new",
|
|
10536
|
+
"recurring"
|
|
10537
|
+
]
|
|
10538
|
+
},
|
|
10539
|
+
"priorBlockers": {
|
|
10540
|
+
"type": "array",
|
|
10541
|
+
"items": {
|
|
10542
|
+
"type": "string",
|
|
10543
|
+
"pattern": "^final-verification-\\d{3,} AB-\\d{3,}$"
|
|
10544
|
+
}
|
|
10545
|
+
}
|
|
10546
|
+
}
|
|
10547
|
+
},
|
|
10548
|
+
"DefectClassCell": {
|
|
10549
|
+
"type": "object",
|
|
10550
|
+
"required": [
|
|
10551
|
+
"location",
|
|
10552
|
+
"case",
|
|
10553
|
+
"verdict",
|
|
10554
|
+
"evidence"
|
|
10555
|
+
],
|
|
10556
|
+
"additionalProperties": false,
|
|
10557
|
+
"properties": {
|
|
10558
|
+
"location": {
|
|
10559
|
+
"type": "string",
|
|
10560
|
+
"minLength": 1
|
|
10561
|
+
},
|
|
10562
|
+
"case": {
|
|
10563
|
+
"type": "string",
|
|
10564
|
+
"minLength": 1
|
|
10565
|
+
},
|
|
10566
|
+
"verdict": {
|
|
10567
|
+
"enum": [
|
|
10568
|
+
"defective",
|
|
10569
|
+
"sound",
|
|
10570
|
+
"unverified",
|
|
10571
|
+
"not-applicable"
|
|
10572
|
+
]
|
|
10573
|
+
},
|
|
10574
|
+
"evidence": {
|
|
10575
|
+
"type": "string",
|
|
10576
|
+
"minLength": 1
|
|
10577
|
+
},
|
|
10578
|
+
"regressionTest": {
|
|
10579
|
+
"type": "string"
|
|
10483
10580
|
}
|
|
10484
10581
|
}
|
|
10485
10582
|
},
|
|
@@ -185,6 +185,23 @@ for every variant and every source type below.
|
|
|
185
185
|
missing and where it lives. A bracketed note inside the captured block
|
|
186
186
|
helps the reader, but it does not replace that row: the row is what a later
|
|
187
187
|
phase sees when it asks whether the source arrived complete.
|
|
188
|
+
4. **Every local document the brief is derived from is its own Source.** A
|
|
189
|
+
spec, plan or note under the project root that you read while drafting —
|
|
190
|
+
one the reporter named, or one you opened because a ticket or the
|
|
191
|
+
conversation pointed at it — gets its own `### Source N — file` entry,
|
|
192
|
+
not only a mention in Context. When git does not track the file (a
|
|
193
|
+
`.project-docs/` spec, for example), write the entry with
|
|
194
|
+
`- format: by-reference`, a project-relative `ref`, and an empty fence:
|
|
195
|
+
the task worktree is a checkout without that file, and prepare copies it
|
|
196
|
+
read-only into the analysis packet's `## Cited Local Documents` on every
|
|
197
|
+
run, so later revisions of the document reach the next phase. A tracked
|
|
198
|
+
file is in the worktree; capture it as-is per rule 1.
|
|
199
|
+
|
|
200
|
+
**Enforced:** `validators/validate-brief.py`
|
|
201
|
+
(`check_cited_local_documents`) refuses a brief that names a git-untracked
|
|
202
|
+
document under the project root outside `## Source Material` without such
|
|
203
|
+
an entry. `okstra brief-sources attach --project-root <PROJECT_ROOT>
|
|
204
|
+
--brief <path>` appends the missing entries by reference.
|
|
188
205
|
|
|
189
206
|
### 1.0. brief variant
|
|
190
207
|
|
|
@@ -235,6 +252,9 @@ each source's raw content **separately** under the `Source Material
|
|
|
235
252
|
Take an absolute or project-relative path via free-text. Read the entire
|
|
236
253
|
file. Re-ask if it doesn't exist. Captured content is preserved **verbatim**
|
|
237
254
|
in Source Material.
|
|
255
|
+
When the file is under the project root and git does not track it, record
|
|
256
|
+
it by reference instead (capture contract rule 4): the entry's `ref` is the
|
|
257
|
+
project-relative path, `format: by-reference`, and the fence stays empty.
|
|
238
258
|
|
|
239
259
|
### 1b. `Issue tracker ticket`
|
|
240
260
|
|
|
@@ -1047,6 +1067,12 @@ Lenses` rules.
|
|
|
1047
1067
|
2. If it exits non-zero, **fix the offending brief(s) and re-run**. Do NOT
|
|
1048
1068
|
print the Step 7 message until the validator passes.
|
|
1049
1069
|
|
|
1070
|
+
A `local document ... has no ### Source N — file entry` failure is fixed
|
|
1071
|
+
by `okstra brief-sources attach --project-root <PROJECT_ROOT> --brief
|
|
1072
|
+
<path>`, which appends the entry and changes no other line. For a brief
|
|
1073
|
+
other than the one you just wrote, show the user the listed path and ask
|
|
1074
|
+
before running it.
|
|
1075
|
+
|
|
1050
1076
|
3. If neither validator copy is present, fall back to a manual checklist:
|
|
1051
1077
|
confirm each brief has all ten required frontmatter keys (`type`,
|
|
1052
1078
|
`brief-id`, `parent-id`, `ticket-id`, `source-type`, `task-group`,
|
|
@@ -195,6 +195,8 @@ The command reuses the existing completion verification boundary and refuses mix
|
|
|
195
195
|
|
|
196
196
|
For missing checks, dispatch one gap-fill invocation per reviewer on that reviewer's original slot, carrying only its incomplete grouped cells, their shared catalog entries, the same range and rule paths. Ask for one complete row for each such cell, not separate replies per missing rule. Use a new invocation ID and repeat the full materialize → verify → dispatch → materialize-result → complete → verify-completion boundary. Re-run the audit with that reviewer's initial and gap-fill markers. Earlier usable verdicts remain unchanged. An invocation with unknown IDs is invalid; record its rejection and request a corrected new invocation before auditing the retained valid completions. Do not silently relabel IDs.
|
|
197
197
|
|
|
198
|
+
Before accepting a clean general cell, open its G2/G3/G5 observations and verify the result-meaning trace, the downstream workload and pre-work bound, and the applicable documented convention checks. If opened code contradicts a clean observation, record the semantic rejection and exclude that completion from the final coverage audit. Request a full corrected invocation on the same reviewer slot and audit the retained valid completion only; otherwise first-usable preservation would keep the disproved claim. Keep the rejected artifacts immutable for the audit trail. Ordinary missing-row or formatting gaps still use targeted gap-fill with retained valid completions. The parser enforces presence and source locations; the lead checks truth. Keep these observations inside the existing Evidence column, without adding verdict rows.
|
|
199
|
+
|
|
198
200
|
The Checked/N/A/Findings fields must account for every expected rule ID exactly once; a generic clean or all-checked statement never covers an omitted rule. Do not start Step 3.6 until every reviewer passes the audit. If evidence remains unavailable, report the specific blocker and incomplete coverage instead of writing a completed review.
|
|
199
201
|
|
|
200
202
|
## Step 3.6 — Check every citation
|
|
@@ -19,11 +19,11 @@ Use these named review criteria. Each criterion includes the relevant subconditi
|
|
|
19
19
|
| Group | Rule IDs and scope |
|
|
20
20
|
|---|---|
|
|
21
21
|
| `structural` | `S1` reuse/DRY; `S2` simplicity/KISS; `S3` responsibilities/SOLID; `S4` current scope/YAGNI; when an architecture pack is routed: `S5` thin ports, `S6` I/O-only adapters, `S7` domain-object placement, `S8` dependency direction, `S9` boundary ports, `S10` domain-owned business rules |
|
|
22
|
-
| `semantic` | `M1` truthful and specific names; `M2` single-purpose readable flow, including size and nesting; `M3` comments and explanatory values |
|
|
22
|
+
| `semantic` | `M1` truthful and specific names, including result tags and diagnostic values; `M2` single-purpose readable flow, including size and nesting; `M3` comments and explanatory values |
|
|
23
23
|
| `state-and-tests` | `T1` state and mutation boundaries; `T2` no self-mocking; `T3` behavioral outcome assertions; `T4` discriminating fixtures, test titles and branch coverage |
|
|
24
24
|
| `general` | `G1` correctness and regressions; `G2` error handling and third-party wrappers; `G3` untrusted-input and resource bounds; `G4` performance; `G5` routed language/framework and project conventions |
|
|
25
25
|
|
|
26
|
-
Write the selected IDs with their absolute pack/section references under `## Rules`. Multiple references for a criterion go in the same Reference field, separated by semicolons. Do not turn every heading, bullet, formatter instruction or example into another rule. Read the original references rather than restating their bodies.
|
|
26
|
+
Write the selected IDs with their absolute pack/section references under `## Rules`. Multiple references for a criterion go in the same Reference field, separated by semicolons. Include the observed-fact error guidance with G2, and list the routed normative clause locations together under G5 instead of citing only a pack's first line. Do not turn every heading, bullet, formatter instruction or example into another rule. Read the original references rather than restating their bodies.
|
|
27
27
|
|
|
28
28
|
Write targets under `## Cells`; each has a stable `C-CR-...` ID, the target and its group. A function target includes its path and position, e.g. `src/orders/pricing.py:42 calculate_price`. Distinguish same-line anonymous functions with their columns. Targets are repository-relative; no absolute or parent-traversal paths.
|
|
29
29
|
|
|
@@ -45,6 +45,20 @@ The example is abbreviated; the actual catalog includes the named criteria above
|
|
|
45
45
|
|
|
46
46
|
Do not omit a cell because a rule seems inapplicable. Record that criterion under `N/A` with its reason in the compact row. The normal case lists checked rule IDs once with one inspected source anchor for the cell. This preserves rule-level omission checks without repeating paths and explanations for every rule.
|
|
47
47
|
|
|
48
|
+
## Inspection before compression
|
|
49
|
+
|
|
50
|
+
Keep discovery separate from output accounting. Read the changed constructs and applicable canonical clauses, trace their dependencies, identify findings, then compress the result into the existing grouped rows. The table is the output format, not the inspection procedure.
|
|
51
|
+
|
|
52
|
+
Use these inspection guides for general cells:
|
|
53
|
+
|
|
54
|
+
| Criterion | Inspection guide |
|
|
55
|
+
|---|---|
|
|
56
|
+
| G2 | Enumerate the dependency's possible outcomes and compare their meanings with the returned tags, diagnostic values and error text. Trace existing callees and types when the changed mapping depends on them. |
|
|
57
|
+
| G3 | Identify each input dimension and the work performed inside changed loops or repeated calls. Follow existing callees to account for scans, parsing and allocation; a type-only callee diff does not remove newly amplified work at its caller. Locate the enforcing bound before the expensive work, or report the unbounded path. |
|
|
58
|
+
| G5 | Inspect every applicable normative clause of the routed language, framework and project policy for the changed constructs. Name those clauses and observed conformance. Evaluate explicit conventions independently of formatter, linter and behavioral-test results. |
|
|
59
|
+
|
|
60
|
+
For checked G2/G3/G5, retain a short source-cited observation in the existing Evidence column as defined by `review-calibration.md`. Format and source existence are enforced by `code_review_coverage.py::_checked_rule_evidence` and the standalone completion tests. The truth and completeness of the observation remain review judgments; the lead checks them before accepting clean coverage.
|
|
61
|
+
|
|
48
62
|
## The one principle
|
|
49
63
|
|
|
50
64
|
**Membership is mechanical; judgment only ever decides a cell's verdict.**
|
|
@@ -61,7 +61,13 @@ Return exactly one row per assigned cell under `## Coverage Verdicts`. Keep deta
|
|
|
61
61
|
|
|
62
62
|
The example uses the base structural and state-and-tests catalogs. `Checked` means inspected and no actionable finding for those rules. A criterion includes all applicable subconditions in its references; one inapplicable subcondition does not make the entire criterion N/A. Explicitly list the IDs; `all`, counts and ID ranges are not verdicts. `N/A` assigns `rule=reason`; `Findings` assigns `rule=finding-id`. Separate assignments with semicolons and multiple finding IDs for one rule with commas. Use `-` for an empty field. Every expected rule appears exactly once across the three status fields. Never infer an omitted rule as clean.
|
|
63
63
|
|
|
64
|
-
Every row supplies one literal repository-relative source `path:line` for that cell's target file. Do not substitute a rule-pack citation such as `cc:5`, a global alias such as `E01`, or another target's path. The checker verifies that the file and line exist inside the project. Additional definitions and evidence remain in finding bodies.
|
|
64
|
+
Every row supplies one literal repository-relative source `path:line` for that cell's target file. The citation may be backtick-quoted and followed by ` — ` and a short observation before any ` :: ` rule observations. Do not substitute a rule-pack citation such as `cc:5`, a global alias such as `E01`, or another target's path. The checker verifies that the file and line exist inside the project. Additional definitions and evidence remain in finding bodies.
|
|
65
|
+
|
|
66
|
+
For `general` cells, checked G2, G3 and G5 each carry a compact observation in that same Evidence field: `target/path:line :: G2=source/path:line observed outcome mapping; G3=source/path:line observed workload and bound; G5=source/path:line applicable clauses and observed conformance`. Each observation begins with an existing project-source citation; it may cite a relevant callee. Keep rule-pack references in the catalog or the observation text, not in the project-source citation slot. Separate assignments with semicolons; use commas within an observation. Findings and reasoned N/A already explain their criteria and need no duplicate clean observation.
|
|
67
|
+
|
|
68
|
+
For example, `jobs/dispatch.py:18 :: G2=jobs/client.py:25 returned states preserve the dependency outcomes; G3=jobs/dispatch.py:9 validated batch limit bounds work before parsing; G5=jobs/dispatch.py:18 public typing and exception-chaining clauses hold` records three observations in one row. `checked`, `clean` and `no issues` alone are verdict labels, not observations. The checker rejects missing observations, notes for unchecked criteria, aliases and invalid source locations. It cannot establish whether a written observation is true.
|
|
69
|
+
|
|
70
|
+
Complete discovery before producing clean rows. Follow dependency results and diagnostic meanings for G2, downstream workload and pre-work bounds for G3, and each applicable documented convention for G5. A documented convention violation can be a `nit` with a concrete fix even when outputs are correct. Trace the shared predicates and algorithms before treating similar orchestration as duplicated rules.
|
|
65
71
|
|
|
66
72
|
A finding ID names a body outside this section, in this reviewer's original or gap-fill result. N/A reasons are short inline applicability explanations, not aliases or rule references. Missing sources, unread rules and blocked inspection remain unaccounted checks and name their blocker. A partial row is rejected with the missing rule IDs; gap-fill rechecks that grouped cell, not every individual rule as a separate output row.
|
|
67
73
|
|
|
@@ -69,9 +69,11 @@ Use this when one Linear project, or one parent issue, covers work in several re
|
|
|
69
69
|
3. For each issue, propose target projects from evidence: labels, team, repository names or paths in the body. Confirm with `prompt_user` (multi-select, recommended projects first, reason in each description).
|
|
70
70
|
4. For each (issue, project) pair, draft the scope: the part of the issue this project implements. Confirm it with `prompt_user` offering one or two drafts plus a custom-input option. A pair without scope is rejected by the CLI.
|
|
71
71
|
5. Author each pair's brief fields under the okstra-brief-gen brief contract: bodies verbatim, `EB-NNN` / `PB-NNN` / `EO-NNN` items as `<id> <observable condition> — verify: <how>`, anything without an observation method under `externalGates`, and `openQuestions` rows prefixed `general:`, `terminology:`, `intent-check:`, `conversion-block:` or `adr-candidate:`.
|
|
72
|
+
List in `localSources` every local document under that project's root you read to author the pair (a spec, a plan, a note), as a path relative to the project root. Each becomes a `### Source N — file` entry with `format: by-reference`, so every run of the child copies the current file into its analysis packet; a task worktree is a checkout and lacks an untracked `.project-docs/` file. A path outside the project root or a missing file rejects the plan, and a brief whose text names an untracked document that `localSources` does not list fails `validate-brief`.
|
|
72
73
|
6. Write the plan JSON below with the Write tool to a scratch path outside the project roots, then run `okstra manager task split ... --plan <path>`.
|
|
73
74
|
- `split plan rejected` or `rendered briefs failed validate-brief`: fix the named fields and run it again. Nothing was written.
|
|
74
|
-
-
|
|
75
|
+
- To add issues or projects to an earlier split, add them to the kept `split-plan.json` content and run the split again without `--overwrite`. Each brief's status is `created`, `unchanged`, `refreshed` or `overwritten`. The CLI renders each existing brief from the kept plan and from the new plan, both with the brief's original `created` date. When the two differ only in `### Out of scope`, `## Related Artifacts` and `## Related Task Graph`, it removes and appends just the changed lines in those sections (`refreshed`) and keeps every other line, hand edits included. An edit to the pair's own `outOfScope` list is applied the same way. A brief the new plan does not change stays `unchanged`, even with hand edits.
|
|
76
|
+
- `briefs already exist with content this plan would change`: the plan changes a listed brief outside those sections, for example its own scope or Context. Show the listed paths and ask before adding `--overwrite`, which re-renders only those briefs and replaces their hand edits. A brief with no kept plan to compare against is also listed when its text differs.
|
|
75
77
|
7. Each `Brief N task key` is `<project-id>:<task-group>:<child-task-id>`. Start one with `okstra manager task run ... --project-id <project-id> --child-task-id <child-task-id>` and follow the Child Launch Rule. The packet already carries `--task-brief`, and `--task-type` until the child task exists.
|
|
76
78
|
|
|
77
79
|
Plan JSON (`schemaVersion` 1). `source.kind` is `project` or `issue`; `recommendedPhase` is `requirements-discovery` (default), `error-analysis` or `improvement-discovery`; `relations[].relation` uses the brief Related Task Graph values; list fields may be omitted.
|
|
@@ -90,7 +92,8 @@ Plan JSON (`schemaVersion` 1). `source.kind` is `project` or `issue`; `recommend
|
|
|
90
92
|
"recommendedPhase": "requirements-discovery",
|
|
91
93
|
"context": "<text>", "problem": "<text>", "desiredOutcome": "<text>",
|
|
92
94
|
"expectedBehavior": [], "preservedBehavior": [], "expectedOutcome": [],
|
|
93
|
-
"externalGates": [], "constraints": [], "openQuestions": []
|
|
95
|
+
"externalGates": [], "constraints": [], "openQuestions": [],
|
|
96
|
+
"localSources": [".project-docs/specs/<spec>.md"]
|
|
94
97
|
}
|
|
95
98
|
]
|
|
96
99
|
}
|
|
@@ -100,6 +103,16 @@ Plan JSON (`schemaVersion` 1). `source.kind` is `project` or `issue`; `recommend
|
|
|
100
103
|
|
|
101
104
|
The CLI writes each brief to `<projectRoot>/.okstra/briefs/<group-slug>/<ticketId>-<file-title>.md` (`<group-slug>` is the task group lowercased, with each run of characters other than `a-z` and `0-9` replaced by `-`, so `Upload V2` becomes `upload-v2`) with a `## Project Scope` section: this project's scope, `outOfScope`, and the scope of every other project the same issue went to. It validates every brief with the brief validator before writing any file, registers the child tasks, and keeps the plan at `split-plan.json` in the manager task directory.
|
|
102
105
|
|
|
106
|
+
## Upgrading an Existing Brief
|
|
107
|
+
|
|
108
|
+
A brief written before `localSources` existed may name a local document only in Context; `validate-brief` and every prepare of that child then refuse it. Do not hand-edit the brief and do not re-split to fix it: `--overwrite` re-renders the brief and replaces later edits. Run
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
okstra brief-sources attach --project-root <project-root> --brief <brief-path>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
It appends one `### Source N — file` entry (`format: by-reference`) per cited untracked document at the end of Source Material and changes no other line, so `EB-` / `PB-` / `EO-` ids, External Gates and later edits stay. Show the user the printed paths. Add the same paths to the pair's `localSources` in the kept `split-plan.json` before any later re-split, so the re-rendered brief carries them.
|
|
115
|
+
|
|
103
116
|
## Overview Page
|
|
104
117
|
|
|
105
118
|
When the user wants to see a manager at a glance, run `okstra manager task sync ...` for each task listed by `okstra manager list tasks ...` whose snapshot must be current, then run `okstra manager view --manager-id <manager-id>` and give the user the returned `View URL`. The page reads manager files only, so each task shows the state of its last sync and prints its own sync command.
|
|
@@ -12,7 +12,7 @@ For authored questions, `prompt_user` means the current host's available questio
|
|
|
12
12
|
Register PR body templates and generate PR descriptions. Templates live in the
|
|
13
13
|
user home at `~/.okstra/template/pr/`. This skill is **global** — it does not
|
|
14
14
|
require `<PROJECT_ROOT>/.okstra/project.json`. PR generation additionally
|
|
15
|
-
requires the current directory
|
|
15
|
+
requires a git repository: the current directory, or the one `--repo` names.
|
|
16
16
|
|
|
17
17
|
## Step 0: Check CLI availability
|
|
18
18
|
|
|
@@ -31,6 +31,13 @@ If `okstra` is not on PATH, tell the user:
|
|
|
31
31
|
`$(...)`, leading `VAR=`, or wrap in `if`/`eval`/`||`/`&&` — keep arguments literal and follow the current host's
|
|
32
32
|
execution-permission guidance. Paste literal values emitted by a prior call.
|
|
33
33
|
|
|
34
|
+
**Target repository:** when the PR's repository is not the session's working
|
|
35
|
+
directory — a sibling repository or another worktree — never `cd` into it. Add
|
|
36
|
+
`--repo <absolute path>` to every `okstra pr branches`, `okstra pr gen` and
|
|
37
|
+
`okstra pr check` call instead; omit it when the working directory is the PR's
|
|
38
|
+
repository. `--template-file` and `--body-file` paths still resolve against the
|
|
39
|
+
working directory, so pass them as absolute paths.
|
|
40
|
+
|
|
34
41
|
## Step 1: Pick the mode (always first)
|
|
35
42
|
|
|
36
43
|
Present a 3-option picker (`prompt_user`):
|
|
@@ -78,6 +85,8 @@ Carry the chosen template name as `<template>` (`default` for the bundled one).
|
|
|
78
85
|
okstra pr branches
|
|
79
86
|
```
|
|
80
87
|
|
|
88
|
+
With a target repository: `okstra pr branches --repo <absolute path>`.
|
|
89
|
+
|
|
81
90
|
It prints the recommended base branches one per line, current branch excluded, or `(no branches)`.
|
|
82
91
|
Present a 3-option base picker from the first two printed branches plus `Enter directly` last; with
|
|
83
92
|
`(no branches)`, ask for the base directly. Carry the choice as `<base>`.
|
|
@@ -88,6 +97,8 @@ Present a 3-option base picker from the first two printed branches plus `Enter d
|
|
|
88
97
|
okstra pr gen --base <base> --template <template>
|
|
89
98
|
```
|
|
90
99
|
|
|
100
|
+
With a target repository, add `--repo <absolute path>`.
|
|
101
|
+
|
|
91
102
|
To describe a branch other than the checkout — a stage branch whose worktree is gone — add
|
|
92
103
|
`--head <branch>`; nothing is checked out.
|
|
93
104
|
|
|
@@ -105,6 +116,9 @@ Write the title and body to a temp file and check them:
|
|
|
105
116
|
okstra pr check --body-file <path-to-body>
|
|
106
117
|
```
|
|
107
118
|
|
|
119
|
+
With a target repository, add `--repo <absolute path>`: the check skips terms that
|
|
120
|
+
repository's tracked files use.
|
|
121
|
+
|
|
108
122
|
Each printed `L<line>: <pattern>: <match>` row is an okstra identifier a reviewer cannot open:
|
|
109
123
|
restate that line in the reviewer's terms and check again. `clean` means none is left.
|
|
110
124
|
|
|
@@ -43,6 +43,15 @@ attachment returned as a link, an opaque embed node) gets a
|
|
|
43
43
|
`conversion-block:` row in Open Questions naming what is missing. Heading was
|
|
44
44
|
originally "Source Material (verbatim — do not modify)" — the parenthetical
|
|
45
45
|
is a reviewer note, not body text.
|
|
46
|
+
`format: by-reference` applies only to a `file` source whose `ref` is a path
|
|
47
|
+
under the project root (absolute, or relative to the project root): leave
|
|
48
|
+
the fence empty, and prepare copies the file read-only into the analysis
|
|
49
|
+
packet's `## Cited Local Documents` on every run. Use it for an untracked
|
|
50
|
+
local document such as a `.project-docs/` spec, which the task worktree does
|
|
51
|
+
not contain. A file only mentioned in Context is not read, and
|
|
52
|
+
`validators/validate-brief.py` refuses a brief that names an untracked
|
|
53
|
+
document outside Source Material without its own `file` entry;
|
|
54
|
+
`okstra brief-sources attach` appends the missing entries.
|
|
46
55
|
-->
|
|
47
56
|
|
|
48
57
|
### Source 1 — <type: file | linear | jira | github | notion | url | user-input>
|
|
@@ -50,7 +59,7 @@ is a reviewer note, not body text.
|
|
|
50
59
|
- ref: <abs file path | LIN-1234 | https://... | "conversation synthesis">
|
|
51
60
|
- fetched-via: <Read | mcp__linear__getIssue | mcp__notion__... | gh issue view | WebFetch | user-paste>
|
|
52
61
|
- fetched-at: <YYYY-MM-DD HH:MM>
|
|
53
|
-
- format: <as-is | "Jira ADF → Markdown (semantics preserved)" | "tool-truncated — missing body requested from reporter">
|
|
62
|
+
- format: <as-is | by-reference | "Jira ADF → Markdown (semantics preserved)" | "tool-truncated — missing body requested from reporter">
|
|
54
63
|
|
|
55
64
|
```
|
|
56
65
|
<Paste the raw source here without changing a single character.>
|
|
@@ -451,6 +451,12 @@
|
|
|
451
451
|
"what-blocks-acceptance": "What blocks acceptance",
|
|
452
452
|
"nothing-is-holding-acceptance": "Nothing is holding acceptance.",
|
|
453
453
|
"follow-up": "Follow-up:",
|
|
454
|
+
"defect-mechanism": "Mechanism:",
|
|
455
|
+
"recurring-class": "Recurring — an earlier run blocked on this class:",
|
|
456
|
+
"location": "Location",
|
|
457
|
+
"case": "Case",
|
|
458
|
+
"verdict": "Verdict",
|
|
459
|
+
"regression-test": "Regression test",
|
|
454
460
|
"residual-risk": "Residual risk",
|
|
455
461
|
"owner": "Owner:",
|
|
456
462
|
"escalation-condition": "Escalation condition:",
|
|
@@ -521,6 +527,8 @@
|
|
|
521
527
|
"safety-blockers": "Safety blockers",
|
|
522
528
|
"technical-verification": "Technical verification pending",
|
|
523
529
|
"user-decision": "User decision record",
|
|
530
|
+
"deferred-gate": "Closes at a later gate",
|
|
531
|
+
"split-acceptance": "Split vote accepted as a risk by the user",
|
|
524
532
|
"classification-required": "Classification required before reassembly",
|
|
525
533
|
"unresolved-facts": "Unresolved feasibility facts",
|
|
526
534
|
"expected-outcome": "Expected outcome",
|
|
@@ -451,6 +451,12 @@
|
|
|
451
451
|
"what-blocks-acceptance": "수용을 막는 것",
|
|
452
452
|
"nothing-is-holding-acceptance": "수용을 막는 것이 없습니다.",
|
|
453
453
|
"follow-up": "후속:",
|
|
454
|
+
"defect-mechanism": "원리:",
|
|
455
|
+
"recurring-class": "재발 — 이전 run 이 이 계열로 막혔음:",
|
|
456
|
+
"location": "위치",
|
|
457
|
+
"case": "경우",
|
|
458
|
+
"verdict": "판정",
|
|
459
|
+
"regression-test": "회귀 테스트",
|
|
454
460
|
"residual-risk": "남은 위험",
|
|
455
461
|
"owner": "담당:",
|
|
456
462
|
"escalation-condition": "에스컬레이션 조건:",
|
|
@@ -521,6 +527,8 @@
|
|
|
521
527
|
"safety-blockers": "안전 차단 요인",
|
|
522
528
|
"technical-verification": "기술 검증 대기",
|
|
523
529
|
"user-decision": "사용자 결정 기록",
|
|
530
|
+
"deferred-gate": "이후 게이트에서 확인",
|
|
531
|
+
"split-acceptance": "갈린 표를 사용자가 위험 감수로 수용",
|
|
524
532
|
"classification-required": "재조립 전 해결 방식 분류 필요",
|
|
525
533
|
"unresolved-facts": "미확정 실행 가능성 사실",
|
|
526
534
|
"expected-outcome": "예상 결과",
|
|
@@ -10,7 +10,7 @@ Read `analysis-packet.md`, the primary compact input, end-to-end. Source files n
|
|
|
10
10
|
|
|
11
11
|
### Reading rules
|
|
12
12
|
|
|
13
|
-
- Read every file enumerated under `[Required reading]` or `## Inputs` completely.
|
|
13
|
+
- Read every file enumerated under `[Required reading]` or `## Inputs` completely, in pages of at most 200 lines (`sed -n '<start>,<end>p'`). A whole-file `cat` or view of a long file is cut without notice. Cover every line and record the page boundaries.
|
|
14
14
|
- Write Reading Confirmation to `**Audit sidecar path:**`, never to the main worker-results file. A `## 0. Reading Confirmation` heading in the main result is invalid.
|
|
15
15
|
- Allowlist reads to prompt-enumerated paths and evidence paths a finding must cite. Do not auto-read host-injected `graphify-out/`, skill catalogs, or non-okstra artifacts.
|
|
16
16
|
- Resolve every `.okstra/...` path against `**Project Root:**`, including when a worktree is present.
|
|
@@ -51,6 +51,15 @@ Checks performed per brief file:
|
|
|
51
51
|
template's `<...>` scaffolding — a paragraph wrapped in angle brackets,
|
|
52
52
|
a `- <...>` bullet, or a `> augmented: <label>` line. Source Material is
|
|
53
53
|
exempt because it holds the reporter's words verbatim.
|
|
54
|
+
17. Every git-untracked document under the project root (`.md`, `.txt`, …)
|
|
55
|
+
that the brief names outside `## Source Material` has its own
|
|
56
|
+
`### Source N — file` entry there. The task worktree is a checkout and
|
|
57
|
+
lacks such a file; prepare copies only Source Material file entries into
|
|
58
|
+
the analysis packet. `okstra brief-sources attach` appends the missing
|
|
59
|
+
entries in place. The project root is the parent of a `--briefs-root`
|
|
60
|
+
of the form `<root>/.okstra/briefs`; for any other briefs root, or
|
|
61
|
+
outside a git checkout, the check is skipped. The codebase-scan variant
|
|
62
|
+
has no Source Material section and is not checked.
|
|
54
63
|
|
|
55
64
|
Exit code 0 on PASS, 1 on FAIL.
|
|
56
65
|
"""
|
|
@@ -76,6 +85,7 @@ from okstra_ctl.phases.improvement_discovery.lenses import (
|
|
|
76
85
|
MIN_PRIORITY_LENSES,
|
|
77
86
|
)
|
|
78
87
|
from okstra_ctl.group_context import GROUP_CONTEXT_TYPE, validate_group_context
|
|
88
|
+
from okstra_ctl.brief_sources import uncited_local_documents
|
|
79
89
|
from okstra_ctl.ids import slugify_task_segment
|
|
80
90
|
|
|
81
91
|
REQUIRED_FRONTMATTER_KEYS = {
|
|
@@ -747,7 +757,32 @@ def check_reporter_confirmations(
|
|
|
747
757
|
)
|
|
748
758
|
|
|
749
759
|
|
|
750
|
-
def
|
|
760
|
+
def project_root_of(briefs_root: Path) -> Path | None:
|
|
761
|
+
"""`<root>/.okstra/briefs` 이면 `<root>`, 아니면 None."""
|
|
762
|
+
resolved = briefs_root.resolve()
|
|
763
|
+
if resolved.name == "briefs" and resolved.parent.name == ".okstra":
|
|
764
|
+
return resolved.parent.parent
|
|
765
|
+
return None
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
def check_cited_local_documents(
|
|
769
|
+
text: str, scope: str, project_root: Path | None, errors: list[str]
|
|
770
|
+
) -> None:
|
|
771
|
+
if project_root is None or scope == "codebase":
|
|
772
|
+
return
|
|
773
|
+
for relative in uncited_local_documents(text, project_root):
|
|
774
|
+
errors.append(
|
|
775
|
+
f"local document {relative!r} is cited in the brief but has no "
|
|
776
|
+
"`### Source N — file` entry in Source Material; it is untracked, "
|
|
777
|
+
"so the task worktree lacks it and no run will show it to workers. "
|
|
778
|
+
f"Run `okstra brief-sources attach --project-root {project_root} "
|
|
779
|
+
"--brief <this brief>` to append it by reference"
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
def validate_brief(
|
|
784
|
+
path: Path, briefs_root: Path, project_root: Path | None = None
|
|
785
|
+
) -> list[str]:
|
|
751
786
|
text = strip_html_comments(path.read_text(encoding="utf-8"))
|
|
752
787
|
errors: list[str] = []
|
|
753
788
|
|
|
@@ -795,6 +830,9 @@ def validate_brief(path: Path, briefs_root: Path) -> list[str]:
|
|
|
795
830
|
check_end_state_sections(text, scope, errors)
|
|
796
831
|
check_variant_required_sections(text, scope, errors)
|
|
797
832
|
check_template_scaffold(text, scope, errors)
|
|
833
|
+
check_cited_local_documents(
|
|
834
|
+
text, scope, project_root or project_root_of(briefs_root), errors
|
|
835
|
+
)
|
|
798
836
|
|
|
799
837
|
# 2. brief-id matches filename stem
|
|
800
838
|
stem = path.stem
|
|
@@ -242,6 +242,7 @@ from okstra_ctl.report_routing import technical_verification_routing_errors
|
|
|
242
242
|
from okstra_ctl.phases.implementation_option_selection.validation import ( # noqa: E402
|
|
243
243
|
validate_blocked_answer_channel,
|
|
244
244
|
validate_implementation_option_selection,
|
|
245
|
+
validate_split_acceptance,
|
|
245
246
|
)
|
|
246
247
|
from okstra_ctl.phases.implementation_planning.validation import (
|
|
247
248
|
_validate_requirement_provenance, _validate_stage_has_requirement,
|
|
@@ -3197,6 +3198,10 @@ def validate_final_report_data(
|
|
|
3197
3198
|
f"implementation-option-selection: {error}"
|
|
3198
3199
|
for error in validate_blocked_answer_channel(data)
|
|
3199
3200
|
)
|
|
3201
|
+
failures.extend(
|
|
3202
|
+
f"implementation-option-selection: {error}"
|
|
3203
|
+
for error in validate_split_acceptance(data)
|
|
3204
|
+
)
|
|
3200
3205
|
elif task_type == "implementation":
|
|
3201
3206
|
_validate_stage_carry_sidecar_exists(data, report_path, failures)
|
|
3202
3207
|
_validate_lead_fixes(data, failures)
|