okstra 0.176.1 → 0.177.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/dist/commands/execute/team.mjs +14 -4
- package/dist/commands/execute/team.mjs.map +1 -1
- package/dist/commands/lifecycle/install.mjs +0 -1
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/docs/architecture.md +3 -3
- package/docs/cli.md +1 -1
- package/docs/project-structure-overview.md +2 -2
- package/docs/task-process/final-verification.md +5 -3
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +2 -2
- package/runtime/bin/okstra-compact-reminder.sh +2 -2
- package/runtime/bin/okstra-provider-exec.py +2 -7
- package/runtime/bin/okstra-render-report-views.py +13 -10
- package/runtime/prompts/coding-preflight/overview.md +2 -1
- package/runtime/prompts/lead/convergence.md +11 -6
- package/runtime/prompts/lead/okstra-lead-contract.md +3 -3
- package/runtime/prompts/lead/plan-body-verification.md +4 -2
- package/runtime/prompts/lead/report-writer.md +8 -4
- package/runtime/prompts/profiles/_common-contract.md +2 -2
- package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
- package/runtime/prompts/profiles/final-verification.md +11 -9
- package/runtime/prompts/profiles/improvement-discovery.md +1 -1
- package/runtime/prompts/profiles/release-handoff.md +7 -6
- package/runtime/prompts/wizard/prompts.ko.json +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +5 -6
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +23 -1
- package/runtime/python/okstra_ctl/agent_invocation.py +17 -0
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +15 -2
- package/runtime/python/okstra_ctl/dispatch_core.py +140 -17
- package/runtime/python/okstra_ctl/dispatch_state.py +60 -3
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +49 -3
- package/runtime/python/okstra_ctl/handoff.py +27 -14
- package/runtime/python/okstra_ctl/model_cli.py +11 -2
- package/runtime/python/okstra_ctl/model_discovery.py +12 -0
- package/runtime/python/okstra_ctl/pane_reclaim.py +49 -43
- package/runtime/python/okstra_ctl/release_gate.py +56 -0
- package/runtime/python/okstra_ctl/render.py +53 -0
- package/runtime/python/okstra_ctl/report_contract.py +1 -0
- package/runtime/python/okstra_ctl/report_finalize.py +54 -0
- package/runtime/python/okstra_ctl/report_html/render.py +7 -4
- package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +4 -1
- package/runtime/python/okstra_ctl/run.py +119 -18
- package/runtime/python/okstra_ctl/stage_targets.py +73 -1
- package/runtime/python/okstra_ctl/team.py +84 -14
- package/runtime/python/okstra_ctl/tmux.py +2 -3
- package/runtime/python/okstra_ctl/wizard.py +19 -10
- package/runtime/python/okstra_ctl/worker_liveness.py +3 -1
- package/runtime/python/okstra_ctl/worker_runner.py +2 -2
- package/runtime/python/okstra_ctl/wrapper_status.py +15 -0
- package/runtime/python/okstra_ctl/write_policy.py +9 -1
- package/runtime/schemas/final-report-v2.0.schema.json +58 -19
- package/runtime/skills/okstra-run/SKILL.md +3 -3
- package/runtime/templates/reports/html/base.template.html +1 -2
- package/runtime/templates/reports/html/i18n/en.json +4 -0
- package/runtime/templates/reports/html/i18n/ko.json +4 -0
- package/runtime/templates/reports/html/tasks/final-verification.template.html +7 -1
- package/runtime/validators/validate-report-views.py +30 -17
- package/runtime/validators/validate-run.py +221 -90
- package/runtime/validators/validate_analysis_report.py +2 -5
- package/runtime/validators/validate_session_conformance.py +1 -1
- package/runtime/bin/okstra-trace-cleanup.sh +0 -185
|
@@ -175,7 +175,15 @@ def planned_paths_from_run_manifest(
|
|
|
175
175
|
active_value = manifest.get("activeRunContextPath")
|
|
176
176
|
if not isinstance(active_value, str) or not active_value:
|
|
177
177
|
raise WritePolicyError("implementer write policy has no active run context")
|
|
178
|
-
|
|
178
|
+
# `compact_active_run_context` drops `sourceArtifacts` and stores path
|
|
179
|
+
# hints instead; the hydrator is what puts the block back. Reading the file
|
|
180
|
+
# raw always saw `None` here and reported the run as having no approved
|
|
181
|
+
# stage authority, which blocked every implementer dispatch.
|
|
182
|
+
from .path_hints import hydrate_active_run_context
|
|
183
|
+
|
|
184
|
+
active = hydrate_active_run_context(
|
|
185
|
+
_read_json(_rooted(project_root, active_value), "active run context")
|
|
186
|
+
)
|
|
179
187
|
source = active.get("sourceArtifacts")
|
|
180
188
|
run_inputs_value = source.get("runInputsPath") if isinstance(source, Mapping) else None
|
|
181
189
|
run = active.get("run")
|
|
@@ -230,10 +230,9 @@
|
|
|
230
230
|
},
|
|
231
231
|
"verdictCard": {
|
|
232
232
|
"type": "object",
|
|
233
|
-
"description": "Top-of-report at-a-glance card.
|
|
233
|
+
"description": "Top-of-report at-a-glance card. It carries no verdict token — the token lives once, in finalVerdict. `direction` and `nextStep` MUST byte-match finalVerdict and recommendedNextSteps[0].",
|
|
234
234
|
"required": [
|
|
235
235
|
"finalConclusion",
|
|
236
|
-
"verdictToken",
|
|
237
236
|
"direction",
|
|
238
237
|
"approvalRequired",
|
|
239
238
|
"nextStep"
|
|
@@ -245,9 +244,6 @@
|
|
|
245
244
|
"minLength": 1,
|
|
246
245
|
"description": "Conclusion SSOT for the whole report: at most 3 sentences. Other prose blocks reference this instead of restating it."
|
|
247
246
|
},
|
|
248
|
-
"verdictToken": {
|
|
249
|
-
"$ref": "#/$defs/VerdictToken"
|
|
250
|
-
},
|
|
251
247
|
"direction": {
|
|
252
248
|
"$ref": "#/$defs/Direction"
|
|
253
249
|
},
|
|
@@ -1406,6 +1402,7 @@
|
|
|
1406
1402
|
"description": "RENDER_IF taskType == final-verification. §5.8 deliverables.",
|
|
1407
1403
|
"required": [
|
|
1408
1404
|
"sourceImplementationReport",
|
|
1405
|
+
"addedSurfaceAudit",
|
|
1409
1406
|
"acceptanceBlockers",
|
|
1410
1407
|
"residualRisk",
|
|
1411
1408
|
"validationEvidence",
|
|
@@ -1475,6 +1472,13 @@
|
|
|
1475
1472
|
}
|
|
1476
1473
|
}
|
|
1477
1474
|
},
|
|
1475
|
+
"addedSurfaceAudit": {
|
|
1476
|
+
"type": "array",
|
|
1477
|
+
"description": "Over-delivery axis. One row per identifier / module / configuration entry the merged diff ADDS, each traced to the brief requirement it serves. An empty array states the diff added no such surface; it is not a licence to skip the enumeration.",
|
|
1478
|
+
"items": {
|
|
1479
|
+
"$ref": "#/$defs/AddedSurfaceRow"
|
|
1480
|
+
}
|
|
1481
|
+
},
|
|
1478
1482
|
"acceptanceBlockers": {
|
|
1479
1483
|
"type": "array",
|
|
1480
1484
|
"items": {
|
|
@@ -2049,17 +2053,6 @@
|
|
|
2049
2053
|
},
|
|
2050
2054
|
"then": {
|
|
2051
2055
|
"properties": {
|
|
2052
|
-
"verdictCard": {
|
|
2053
|
-
"properties": {
|
|
2054
|
-
"verdictToken": {
|
|
2055
|
-
"enum": [
|
|
2056
|
-
"analysis-complete",
|
|
2057
|
-
"analysis-partial",
|
|
2058
|
-
"blocked"
|
|
2059
|
-
]
|
|
2060
|
-
}
|
|
2061
|
-
}
|
|
2062
|
-
},
|
|
2063
2056
|
"finalVerdict": {
|
|
2064
2057
|
"not": {
|
|
2065
2058
|
"required": [
|
|
@@ -7006,7 +6999,9 @@
|
|
|
7006
6999
|
"enum": [
|
|
7007
7000
|
"Claude Code",
|
|
7008
7001
|
"Codex",
|
|
7009
|
-
"Antigravity"
|
|
7002
|
+
"Antigravity",
|
|
7003
|
+
"Grok",
|
|
7004
|
+
"Kimi"
|
|
7010
7005
|
]
|
|
7011
7006
|
},
|
|
7012
7007
|
"role": {
|
|
@@ -9576,6 +9571,51 @@
|
|
|
9576
9571
|
}
|
|
9577
9572
|
}
|
|
9578
9573
|
},
|
|
9574
|
+
"AddedSurfaceRow": {
|
|
9575
|
+
"type": "object",
|
|
9576
|
+
"description": "One surface the merged diff added, and what requirement it serves.",
|
|
9577
|
+
"required": [
|
|
9578
|
+
"id",
|
|
9579
|
+
"surface",
|
|
9580
|
+
"callers",
|
|
9581
|
+
"requirement",
|
|
9582
|
+
"disposition",
|
|
9583
|
+
"note"
|
|
9584
|
+
],
|
|
9585
|
+
"additionalProperties": false,
|
|
9586
|
+
"properties": {
|
|
9587
|
+
"id": {
|
|
9588
|
+
"type": "string",
|
|
9589
|
+
"pattern": "^AS-\\d{3,}$"
|
|
9590
|
+
},
|
|
9591
|
+
"surface": {
|
|
9592
|
+
"type": "string",
|
|
9593
|
+
"minLength": 1,
|
|
9594
|
+
"description": "path:line plus the added identifier / module / configuration key."
|
|
9595
|
+
},
|
|
9596
|
+
"callers": {
|
|
9597
|
+
"type": "string",
|
|
9598
|
+
"minLength": 1,
|
|
9599
|
+
"description": "The caller found by searching the whole repository, or `none`. A declaration, its own test, or a commented-out line is not a caller."
|
|
9600
|
+
},
|
|
9601
|
+
"requirement": {
|
|
9602
|
+
"type": "string",
|
|
9603
|
+
"description": "The brief requirement this surface serves. Empty unless disposition is `traced`."
|
|
9604
|
+
},
|
|
9605
|
+
"disposition": {
|
|
9606
|
+
"enum": [
|
|
9607
|
+
"traced",
|
|
9608
|
+
"exempt",
|
|
9609
|
+
"over-delivery"
|
|
9610
|
+
]
|
|
9611
|
+
},
|
|
9612
|
+
"note": {
|
|
9613
|
+
"type": "string",
|
|
9614
|
+
"minLength": 1,
|
|
9615
|
+
"description": "`traced`: how the surface serves the requirement. `exempt`: which exit applies and its cited evidence. `over-delivery`: the `AB-NNN` or `CA-NNN` row this became."
|
|
9616
|
+
}
|
|
9617
|
+
}
|
|
9618
|
+
},
|
|
9579
9619
|
"AcceptanceBlockerRow": {
|
|
9580
9620
|
"type": "object",
|
|
9581
9621
|
"required": [
|
|
@@ -9594,8 +9634,7 @@
|
|
|
9594
9634
|
"severity": {
|
|
9595
9635
|
"enum": [
|
|
9596
9636
|
"critical",
|
|
9597
|
-
"major"
|
|
9598
|
-
"minor"
|
|
9637
|
+
"major"
|
|
9599
9638
|
]
|
|
9600
9639
|
},
|
|
9601
9640
|
"statement": {
|
|
@@ -239,9 +239,9 @@ okstra config set pr-template-path "<value>" --scope global
|
|
|
239
239
|
|
|
240
240
|
If an action has an unknown `command`, `key`, or `scope`, stop and report the wizard output instead of inventing a command.
|
|
241
241
|
|
|
242
|
-
Before rendering the next phase's bundle — and between worker rounds within a phase (reverify/critic/gapverify batches), after you have collected that round's results and token usage and before you dispatch the next round —
|
|
242
|
+
Before rendering the next phase's bundle — and between worker rounds within a phase (reverify/critic/gapverify batches), after you have collected that round's results and token usage and before you dispatch the next round — close the panes of the dispatches that finished in the prior round so they do not accumulate, in two passes. First count: `okstra team reclaim --project-root <projectRoot> --run-manifest <RUN_MANIFEST_PATH> --dry-run` closes nothing and prints one `<paneId>\t<kind>` line per pane it would close — count those lines as `<n>`. Then run the same command **without** `--dry-run` to close them, and emit `PROGRESS: phase-batch-cleanup panes=<n>` with that count at the batch boundary. The command reads each dispatch's recorded status, so an in-progress worker keeps its pane whichever moment you call it. It closes only the panes okstra opened and recorded — a pane the harness opened for its own teammate carries no recorded id and is not okstra's to close. `shutdown_request` alone only idles the agent and frees no pane, so it stays part of the run-end sequence for roster/token hygiene. A `cli-wrapper` run holds no pane at all, so `<n>` is `0` — still emit the checkpoint.
|
|
243
243
|
|
|
244
|
-
Before you ask the user for any approval, clarification, or decision after workers have been dispatched, run the same
|
|
244
|
+
Before you ask the user for any approval, clarification, or decision after workers have been dispatched, run the same two passes first: `okstra team reclaim … --dry-run` to count the panes, then the same command without `--dry-run` to close them, emit `PROGRESS: phase-gate-cleanup panes=<n>`, and `TaskStop` each completed worker. A `TaskStop` by itself idles the task but leaves the pane open — the `team reclaim` call is what closes it. This keeps a user gate from being shown while finished worker panes remain; in-progress dispatches keep their panes.
|
|
245
245
|
|
|
246
246
|
Build the `okstra render-bundle` invocation from `outcome.renderArgv`, passing every token verbatim and in order (including empty strings — they are intentional `use phase default` markers).
|
|
247
247
|
|
|
@@ -359,7 +359,7 @@ Queue = the topologically-sorted stage list from splitting `orchestration.chainS
|
|
|
359
359
|
|
|
360
360
|
1. Call Step 5's `render-bundle` with the same arguments but `--stage N` (the base commit is auto-computed by prepare from the predecessor's done `head_commit`, so do not pass it by hand). Step 5's blocking local conformance waiver offer·concurrent-run detection·git-reconcile gates apply identically to each stage's `render-bundle`.
|
|
361
361
|
2. As in Step 6, become the host-native Okstra lead and run that stage's Phase 1–7 inline. Phase 6's lead post-stage persistence appends that stage's `status:"done"` row to `runs/<plan-task-key>/consumers.jsonl` (per the implementation profile directive).
|
|
362
|
-
3. After confirming that `done` row was written,
|
|
362
|
+
3. After confirming that `done` row was written, close the panes of the stage you just finished: run `okstra team teardown --project-root <projectRoot> --run-manifest <that stage's RUN_MANIFEST_PATH>`. That stage's run is over, so this is the run-end command rather than the round-boundary one. Then move to the next stage. A `status:"failed"` row instead of `done` means the stage ended `FAIL` — close the panes the same way, then stop the queue per "Stage ended FAIL" above.
|
|
363
363
|
4. One-line report at each stage start/finish: `stage N/<total> start` / `stage N done → next K`.
|
|
364
364
|
|
|
365
365
|
Once the whole queue is consumed, end the chain and report completion to the user.
|
|
@@ -121,8 +121,7 @@
|
|
|
121
121
|
"seq": runMeta.seq,
|
|
122
122
|
"source-report": runMeta.source_report,
|
|
123
123
|
"source-data": sourceData,
|
|
124
|
-
"source-data-sha256": dataSha256
|
|
125
|
-
"source-md-sha256": markdownSha256
|
|
124
|
+
"source-data-sha256": dataSha256
|
|
126
125
|
} | tojson }}</script>
|
|
127
126
|
<script>{{ js | safe }}</script>
|
|
128
127
|
</body>
|
|
@@ -239,6 +239,10 @@
|
|
|
239
239
|
"final-verification": {
|
|
240
240
|
"verification-verdict": "Verification verdict",
|
|
241
241
|
"result-per-requirement": "Result per requirement",
|
|
242
|
+
"what-the-change-added": "What the change added",
|
|
243
|
+
"traced-to": "Serves",
|
|
244
|
+
"callers": "Callers",
|
|
245
|
+
"the-change-added-no-new-surface": "The change added no new identifier, module, or configuration entry.",
|
|
242
246
|
"what-blocks-acceptance": "What blocks acceptance",
|
|
243
247
|
"nothing-is-holding-acceptance": "Nothing is holding acceptance.",
|
|
244
248
|
"residual-risk": "Residual risk",
|
|
@@ -239,6 +239,10 @@
|
|
|
239
239
|
"final-verification": {
|
|
240
240
|
"verification-verdict": "검증 판정",
|
|
241
241
|
"result-per-requirement": "요구사항별 결과",
|
|
242
|
+
"what-the-change-added": "이 변경이 추가한 것",
|
|
243
|
+
"traced-to": "봉사하는 요구사항",
|
|
244
|
+
"callers": "호출자",
|
|
245
|
+
"the-change-added-no-new-surface": "이 변경이 새로 추가한 식별자·모듈·설정 항목은 없습니다.",
|
|
242
246
|
"what-blocks-acceptance": "수용을 막는 것",
|
|
243
247
|
"nothing-is-holding-acceptance": "수용을 막는 것이 없습니다.",
|
|
244
248
|
"residual-risk": "남은 위험",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
{% block human_content %}
|
|
6
6
|
<section data-report-section="verification-verdict">
|
|
7
7
|
<h2>{{ t('tasks.final-verification.verification-verdict') }}</h2>
|
|
8
|
-
<p><span class="status status-{{
|
|
8
|
+
<p><span class="status status-{{ verdictToken }}">{{ verdictToken }}</span></p>
|
|
9
9
|
{{ render_narrative(narrative.verdictExplanation, "finalVerification.userNarrative.verdictExplanation") }}
|
|
10
10
|
</section>
|
|
11
11
|
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
{{ figure(coverageFigure, anchor_nodes=true) }}
|
|
16
16
|
</section>
|
|
17
17
|
|
|
18
|
+
<section data-report-section="added-surface" data-report-field="finalVerification.addedSurfaceAudit">
|
|
19
|
+
<h2>{{ t('tasks.final-verification.what-the-change-added') }}</h2>
|
|
20
|
+
<div class="table-scroll"><table><thead><tr><th>ID</th><th>{{ t('tasks.final-verification.what-the-change-added') }}</th><th>{{ t('tasks.final-verification.callers') }}</th><th>{{ t('tasks.final-verification.traced-to') }}</th><th>{{ t('tasks.final-verification.result-per-requirement') }}</th></tr></thead>
|
|
21
|
+
<tbody>{% for row in final.addedSurfaceAudit %}<tr id="id-{{ row.id }}"><td>{{ row.id }}</td><td>{{ row.surface | inline_code }}</td><td>{{ row.callers | inline_code }}</td><td>{{ row.requirement | inline_code }}</td><td><span class="status status-{{ row.disposition }}">{{ row.disposition }}</span> {{ row.note | inline_code }}</td></tr>{% else %}<tr><td colspan="5">{{ t('tasks.final-verification.the-change-added-no-new-surface') }}</td></tr>{% endfor %}</tbody></table></div>
|
|
22
|
+
</section>
|
|
23
|
+
|
|
18
24
|
<section data-report-section="blockers" data-report-field="finalVerification.acceptanceBlockers">
|
|
19
25
|
<h2>{{ t('tasks.final-verification.what-blocks-acceptance') }}</h2>
|
|
20
26
|
{{ render_narrative(narrative.blockerExplanation, "finalVerification.userNarrative.blockerExplanation") }}
|
|
@@ -2,20 +2,26 @@
|
|
|
2
2
|
"""Validate the self-contained HTML view produced by Phase 7 step 1.5
|
|
3
3
|
(``scripts/okstra-render-report-views.py``).
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
5.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
5
|
+
Takes the report's Markdown path as its handle and branches on schema.
|
|
6
|
+
|
|
7
|
+
A schema-v2 report is checked against its ``.data.json`` — the record the
|
|
8
|
+
HTML was rendered from — and never against the Markdown sibling, which is a
|
|
9
|
+
second rendering of that same record:
|
|
10
|
+
1. the ``*.html`` sibling exists;
|
|
11
|
+
2. the document carries the task type's own template;
|
|
12
|
+
3. every human field the task type requires is present, and no audit field
|
|
13
|
+
leaked into the human main;
|
|
14
|
+
4. each visualization's svg node ids match its fallback ids;
|
|
15
|
+
5. the Response IDs in the HTML match ``clarificationItems[]`` 1:1;
|
|
16
|
+
6. no external URL appears in ``<script src=>`` / ``<link href=>`` /
|
|
17
|
+
``<img src=>`` — the self-contained guarantee.
|
|
18
|
+
|
|
19
|
+
A schema-v1 report keeps the Markdown as its source, so its checks read the
|
|
20
|
+
§1 Clarification Items table (fail-closed when the heading is present but the
|
|
21
|
+
table will not parse), require the HTML sibling only when the report carries
|
|
22
|
+
clarification rows or an analysis-review / plan-approval contract, forbid form
|
|
23
|
+
controls in the §5.6 / §5.7 / §5.8 deliverable regions, and enforce the same
|
|
24
|
+
Response-ID parity and self-contained rules.
|
|
19
25
|
|
|
20
26
|
Exit codes: 0 on success, 1 on any failure. Failures are printed one
|
|
21
27
|
per line to stderr.
|
|
@@ -179,14 +185,21 @@ def _validate_analysis_review_html(
|
|
|
179
185
|
|
|
180
186
|
|
|
181
187
|
def validate(report_path: Path) -> list[str]:
|
|
188
|
+
"""Validate the html view of the report named by *report_path*.
|
|
189
|
+
|
|
190
|
+
A schema-v2 report is checked against its data.json and the rendered html;
|
|
191
|
+
the AI-handoff markdown is a sibling rendering of the same record and is
|
|
192
|
+
never read here, so it does not have to exist. Schema-v1 reports keep the
|
|
193
|
+
markdown as their source and still require it.
|
|
194
|
+
"""
|
|
182
195
|
failures: list[str] = []
|
|
196
|
+
v2 = _load_v2_data(report_path)
|
|
197
|
+
if v2 is not None:
|
|
198
|
+
return _validate_v2(report_path, v2[0], v2[1])
|
|
183
199
|
if not report_path.is_file():
|
|
184
200
|
return [f"final-report not found: {report_path}"]
|
|
185
201
|
|
|
186
202
|
md = report_path.read_text(encoding="utf-8")
|
|
187
|
-
v2 = _load_v2_data(report_path)
|
|
188
|
-
if v2 is not None:
|
|
189
|
-
return _validate_v2(report_path, v2[0], v2[1])
|
|
190
203
|
html_path = html_view_path(report_path)
|
|
191
204
|
# §1 헤딩이 있는데 파싱이 실패하면 md_ids 가 빈 []이 되어 "clarification 없음
|
|
192
205
|
# → skip" 으로 흘러 HTML form parity 게이트가 조용히 열린다. fail-closed.
|