okstra 0.146.1 → 0.148.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/README.md +23 -9
- package/docs/architecture/storage-model.md +39 -65
- package/docs/architecture.md +68 -60
- package/docs/cli.md +40 -23
- package/docs/for-ai/skills/okstra-run.md +13 -34
- package/docs/performance-improvement-plan-v2.md +2 -2
- package/docs/pr-template-usage.md +1 -1
- package/docs/project-structure-overview.md +26 -22
- package/docs/task-process/README.md +4 -4
- package/docs/task-process/common-flow.md +12 -12
- package/docs/task-process/final-verification.md +2 -2
- package/docs/task-process/implementation.md +1 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +2 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/agents/workers/codex-worker.md +2 -2
- package/runtime/agents/workers/grok-worker.md +256 -0
- package/runtime/agents/workers/kimi-worker.md +256 -0
- package/runtime/agents/workers/report-writer-worker.md +12 -12
- package/runtime/bin/lib/okstra/cli.sh +13 -1
- package/runtime/bin/lib/okstra/globals.sh +3 -0
- package/runtime/bin/lib/okstra/usage.sh +17 -12
- package/runtime/bin/okstra-grok-exec.sh +5 -0
- package/runtime/bin/okstra-kimi-exec.sh +5 -0
- package/runtime/bin/okstra-provider-exec.py +235 -0
- package/runtime/bin/okstra-render-final-report.py +4 -4
- package/runtime/bin/okstra-render-report-views.py +100 -12
- package/runtime/bin/okstra.sh +3 -0
- package/runtime/prompts/lead/adapters/antigravity.md +48 -0
- package/runtime/prompts/lead/adapters/claude-code.md +13 -11
- package/runtime/prompts/lead/adapters/codex.md +7 -7
- package/runtime/prompts/lead/okstra-lead-contract.md +5 -5
- package/runtime/prompts/lead/report-writer.md +16 -12
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +16 -10
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
- package/runtime/prompts/profiles/_implementation-executor.md +12 -12
- package/runtime/prompts/profiles/_implementation-self-check.md +4 -4
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
- package/runtime/prompts/profiles/change-impact-analysis.md +2 -0
- package/runtime/prompts/profiles/error-analysis.md +2 -0
- package/runtime/prompts/profiles/feature-analysis.md +2 -0
- package/runtime/prompts/profiles/final-verification.md +3 -1
- package/runtime/prompts/profiles/forbidden-actions.json +4 -4
- package/runtime/prompts/profiles/implementation-planning.md +3 -1
- package/runtime/prompts/profiles/implementation.md +2 -2
- package/runtime/prompts/profiles/improvement-discovery.md +6 -2
- package/runtime/prompts/profiles/project-analysis.md +2 -0
- package/runtime/prompts/profiles/release-handoff.md +7 -7
- package/runtime/prompts/profiles/requirements-discovery.md +2 -0
- package/runtime/prompts/wizard/prompts.ko.json +9 -1
- package/runtime/python/okstra_ctl/codex_dispatch.py +68 -87
- package/runtime/python/okstra_ctl/dispatch_core.py +4 -22
- package/runtime/python/okstra_ctl/final_report_schema.py +37 -12
- package/runtime/python/okstra_ctl/lead_events.py +1 -1
- package/runtime/python/okstra_ctl/lead_runtime.py +13 -2
- package/runtime/python/okstra_ctl/models.py +156 -8
- package/runtime/python/okstra_ctl/path_hints.py +9 -25
- package/runtime/python/okstra_ctl/paths.py +1 -1
- package/runtime/python/okstra_ctl/render.py +172 -74
- package/runtime/python/okstra_ctl/render_final_report.py +136 -28
- package/runtime/python/okstra_ctl/report_contract.py +124 -0
- package/runtime/python/okstra_ctl/report_finalize.py +1 -1
- package/runtime/python/okstra_ctl/report_html/__init__.py +10 -0
- package/runtime/python/okstra_ctl/report_html/common.py +86 -0
- package/runtime/python/okstra_ctl/report_html/filters.py +104 -0
- package/runtime/python/okstra_ctl/report_html/models.py +59 -0
- package/runtime/python/okstra_ctl/report_html/render.py +76 -0
- package/runtime/python/okstra_ctl/report_html/router.py +40 -0
- package/runtime/python/okstra_ctl/report_html/view_models/__init__.py +1 -0
- package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +39 -0
- package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +49 -0
- package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +39 -0
- package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +47 -0
- package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +47 -0
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +103 -0
- package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +43 -0
- package/runtime/python/okstra_ctl/report_html/view_models/project_analysis.py +54 -0
- package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +54 -0
- package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +55 -0
- package/runtime/python/okstra_ctl/report_html/visualizations.py +139 -0
- package/runtime/python/okstra_ctl/report_view_artifacts.py +4 -1
- package/runtime/python/okstra_ctl/report_views.py +15 -43
- package/runtime/python/okstra_ctl/run.py +276 -51
- package/runtime/python/okstra_ctl/runner_resolution.py +103 -0
- package/runtime/python/okstra_ctl/schema_excerpt.py +7 -17
- package/runtime/python/okstra_ctl/team.py +2 -7
- package/runtime/python/okstra_ctl/wizard.py +194 -21
- package/runtime/python/okstra_ctl/worker_artifacts.py +46 -0
- package/runtime/python/okstra_ctl/workers.py +3 -1
- package/runtime/python/okstra_ctl/workflow.py +4 -2
- package/runtime/python/okstra_token_usage/__init__.py +1 -0
- package/runtime/python/okstra_token_usage/collect.py +32 -23
- package/runtime/python/okstra_token_usage/pricing.py +35 -3
- package/runtime/schemas/final-report-v2.0.schema.json +3923 -0
- package/runtime/skills/okstra-run/SKILL.md +31 -42
- package/runtime/templates/prd/pr-body.template.md +1 -1
- package/runtime/templates/reports/final-report-v2.template.md +66 -0
- package/runtime/templates/reports/html/assets/base.css +41 -0
- package/runtime/templates/reports/html/assets/base.js +5 -0
- package/runtime/templates/reports/html/base.template.html +79 -0
- package/runtime/templates/reports/html/macros/forms.html +47 -0
- package/runtime/templates/reports/html/macros/layout.html +19 -0
- package/runtime/templates/reports/html/macros/visualizations.html +27 -0
- package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +40 -0
- package/runtime/templates/reports/html/tasks/error-analysis.template.html +40 -0
- package/runtime/templates/reports/html/tasks/feature-analysis.template.html +40 -0
- package/runtime/templates/reports/html/tasks/final-verification.template.html +39 -0
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +47 -0
- package/runtime/templates/reports/html/tasks/implementation.template.html +40 -0
- package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +29 -0
- package/runtime/templates/reports/html/tasks/project-analysis.template.html +57 -0
- package/runtime/templates/reports/html/tasks/release-handoff.template.html +36 -0
- package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +37 -0
- package/runtime/templates/reports/report.js +21 -4
- package/runtime/templates/reports/settings.template.json +4 -0
- package/runtime/templates/reports/task-brief.template.md +7 -7
- package/runtime/validators/validate-report-views.py +86 -4
- package/runtime/validators/validate-run.py +73 -15
- package/runtime/validators/validate_improvement_report.py +55 -0
- package/runtime/validators/validate_session_conformance.py +2 -1
- package/src/cli-registry.mjs +4 -4
- package/src/commands/execute/codex-dispatch.mjs +7 -10
- package/src/commands/execute/render-bundle.mjs +3 -3
- package/src/commands/execute/run.mjs +17 -52
- package/src/commands/execute/wizard.mjs +4 -1
- package/src/commands/lifecycle/doctor.mjs +6 -3
- package/src/commands/lifecycle/install.mjs +49 -21
- package/src/commands/report/finalize.mjs +2 -3
- package/src/commands/report/render-final-report.mjs +4 -2
- package/src/commands/report/render-views.mjs +8 -8
- package/src/lib/runtime-manifest.mjs +1 -1
- package/src/lib/runtime-resolver.mjs +2 -2
- package/src/lib/worker-agent-render.mjs +50 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: okstra-run
|
|
3
|
-
description: Use when the user wants to start an okstra
|
|
3
|
+
description: Use when the user wants to start or continue an okstra cross-verification task directly from the current agent session. Trigger words include "okstra run", "okstra start", "start okstra", "begin okstra task", "run okstra in this session", "okstra here", "continue on", and "run the next phase".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# OKSTRA Run (in-session)
|
|
7
7
|
|
|
8
|
-
Launch an okstra task — gather inputs interactively via the **wizard state machine** (`okstra wizard ...`), then take over as `
|
|
8
|
+
Launch an okstra task — gather inputs interactively via the **wizard state machine** (`okstra wizard ...`), then take over as `Okstra lead` in the current session using the adapter selected for that host.
|
|
9
9
|
|
|
10
10
|
**Single authority**: this skill drives `okstra wizard`, which owns every step (ordering, branching, validation). The skill is just a thin prompt-relay loop — it never decides "what to ask next" itself. If the flow needs to change, edit `scripts/okstra_ctl/wizard.py`, not this file.
|
|
11
11
|
|
|
@@ -13,12 +13,12 @@ Launch an okstra task — gather inputs interactively via the **wizard state mac
|
|
|
13
13
|
|
|
14
14
|
## When to Use
|
|
15
15
|
|
|
16
|
-
- The user is inside a
|
|
17
|
-
- Continue an existing task (next phase) without leaving the current
|
|
16
|
+
- The user is inside a supported agent host and asks to start an okstra task ("run okstra here", "start an error-analysis on this branch", "okstra implementation-planning for INV-1234").
|
|
17
|
+
- Continue an existing task (next phase) without leaving the current agent session.
|
|
18
18
|
|
|
19
19
|
## When NOT to Use
|
|
20
20
|
|
|
21
|
-
- User explicitly asks to spawn a new terminal / new
|
|
21
|
+
- User explicitly asks to spawn a new terminal / new agent session — use `okstra-inspect history.4` (resume command) or instruct them to run `okstra` in another terminal.
|
|
22
22
|
- User wants status only — use `okstra-inspect status`.
|
|
23
23
|
- User wants past runs — use `okstra-inspect history`.
|
|
24
24
|
|
|
@@ -57,18 +57,27 @@ Never invent additional questions. Never reorder. **Never drop, hide, or merge a
|
|
|
57
57
|
|
|
58
58
|
## Step 1: Preflight
|
|
59
59
|
|
|
60
|
+
Resolve `<host-runtime>` from the host that is executing this skill. This is a harness capability declaration, never a `which`/`PATH` inference and never a worker-provider choice:
|
|
61
|
+
|
|
62
|
+
- Claude Code host → `claude-code`
|
|
63
|
+
- Codex host → `codex`
|
|
64
|
+
- Antigravity CLI host → `antigravity`
|
|
65
|
+
- other host → `external`
|
|
66
|
+
|
|
67
|
+
The host-native provider runs as the current lead session when its role is supported. Other selected providers run through their registered CLI wrappers. Claude remains only the model-policy default; it is not the process owner.
|
|
68
|
+
|
|
60
69
|
Run one Bash tool call (Bash invocation rule from the top of this file applies):
|
|
61
70
|
|
|
62
71
|
```bash
|
|
63
|
-
okstra preflight --runtime
|
|
72
|
+
okstra preflight --runtime <host-runtime> --json
|
|
64
73
|
```
|
|
65
74
|
|
|
66
75
|
Parse the stdout JSON. `ok: false` (or `okstra` not on `PATH` at all) → tell the user: "okstra not set up — run `/okstra-setup` first." Then stop. If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary). Do **not** try to invoke `npx -y okstra@latest ...` as a fallback — `npx` is not on the literal-token allow-list and will force a confirmation prompt on every wizard call afterward. Every subsequent `okstra <subcmd>` call self-bootstraps its Python path, so never `export PYTHONPATH=...`.
|
|
67
76
|
|
|
68
|
-
On `ok: true`, inspect `runtimeReadiness.checks` for the check whose `id` is `workspace-trust`. This is
|
|
77
|
+
On `ok: true`, inspect `runtimeReadiness.checks` for the check whose `id` is `workspace-trust`. This check is host-specific and independent of the selected worker providers.
|
|
69
78
|
|
|
70
|
-
- `status: required` with `action: restart-and-trust` → tell the user: "Claude Code must trust `<projectRoot>` before Okstra can dispatch workers. Close this session, reopen that project in Claude Code, choose `Yes, I trust this folder`, then run `/okstra-run` again." Then stop before Step 2. Never start the wizard or dispatch a worker from this session.
|
|
71
|
-
- `status: unavailable` → tell the user that Claude Code workspace trust could not be verified, ask them to open `<projectRoot>` directly in Claude Code and complete any trust prompt, then run `/okstra-run` again. Then stop before Step 2.
|
|
79
|
+
- On `claude-code`, `status: required` with `action: restart-and-trust` → tell the user: "Claude Code must trust `<projectRoot>` before Okstra can dispatch workers. Close this session, reopen that project in Claude Code, choose `Yes, I trust this folder`, then run `/okstra-run` again." Then stop before Step 2. Never start the wizard or dispatch a worker from this session.
|
|
80
|
+
- On `claude-code`, `status: unavailable` → tell the user that Claude Code workspace trust could not be verified, ask them to open `<projectRoot>` directly in Claude Code and complete any trust prompt, then run `/okstra-run` again. Then stop before Step 2.
|
|
72
81
|
- `status: accepted` or `status: not-applicable` → carry `projectRoot` and `projectId` as literal strings into Step 2.
|
|
73
82
|
- If `runtimeReadiness` is absent, preserve compatibility with an older preflight response: carry `projectRoot` and `projectId` into Step 2. The existing `unknown command: preflight` branch remains the authoritative stale-CLI failure.
|
|
74
83
|
- Any other `workspace-trust` status is unrecognized runtime output: show the status and stop before Step 2 instead of guessing that trust exists.
|
|
@@ -89,7 +98,8 @@ Then initialize the wizard with the literal `projectRoot` / `projectId` you pars
|
|
|
89
98
|
okstra wizard init \
|
|
90
99
|
--state-file /var/folders/.../okstra-wizard.AbCd.json \
|
|
91
100
|
--project-root /abs/path/to/project \
|
|
92
|
-
--project-id my-project-id
|
|
101
|
+
--project-id my-project-id \
|
|
102
|
+
--host-runtime <host-runtime>
|
|
93
103
|
```
|
|
94
104
|
|
|
95
105
|
Output: the same `{ok, next}` JSON described above. The first `next` is always `step: "task_pick"`.
|
|
@@ -101,7 +111,7 @@ Repeat until `next.kind == "done"` (or `"aborted"` — terminal cancel, see "How
|
|
|
101
111
|
1. **Render** the prompt according to `kind` (and `multi` for pick). **Always append the progress marker to the rendered question label** (the `AskUserQuestion` question text, or the `text`-prompt message): suffix it with ` (<next.progress.label>)` — render `progress.label` exactly as the wizard sent it, never recompute it. Example: label `Step 8/11 · 3 steps remaining` → `Select a model (Step 8/11 · 3 steps remaining)`. Re-prompts after `ok: false` reuse `current.progress.label` the same way. The progress marker is presentation-only — never send it back to the wizard as part of an answer.
|
|
102
112
|
- `pick` + `presentation: "numbered-text"` → plain text containing `label`, followed by every option as a numbered Markdown list. Consume the user's next message verbatim as the answer string.
|
|
103
113
|
- `pick` + `multi: false` → `AskUserQuestion` with `multiSelect: false`, `label`, and `options`. The user's chosen option's `value` is the answer string.
|
|
104
|
-
- `pick` + `multi: true` → `AskUserQuestion` with `multiSelect: true`, `label`, and `options`. Join the selected `value`s with `,` into a single literal CSV string (e.g. `"claude,codex,
|
|
114
|
+
- `pick` + `multi: true` → `AskUserQuestion` with `multiSelect: true`, `label`, and `options`. Join the selected `value`s with `,` into a single literal CSV string (e.g. `"claude,codex,grok,kimi"`) and submit it as one `--answer` value. Empty selection submits `--answer ""` and the wizard re-prompts.
|
|
105
115
|
- `pick_group` → one `AskUserQuestion` with one question per `questions[]` entry (tab). Map each tab's selected `value` back by `questions[].step`, assemble a JSON object, and submit it as a single literal `--answer '<json>'`.
|
|
106
116
|
- `text` → plain text message containing `label`. Consume the user's next reply verbatim as the answer string (empty reply = empty string).
|
|
107
117
|
2. **Submit** the answer — call `okstra wizard step` with the literal state-file path from Step 2 and the literal user answer (no shell variables, no `$(...)`):
|
|
@@ -185,38 +195,17 @@ Before rendering the next phase's bundle — and between worker rounds within a
|
|
|
185
195
|
|
|
186
196
|
Build the `okstra render-bundle` invocation from `outcome.renderArgs`, passing each key as `--<key>` and the value verbatim (including empty strings — they are intentional `use phase default` markers).
|
|
187
197
|
|
|
198
|
+
Analysis sidetracks therefore forward wizard-owned entries such as `--analysis-target "<args.analysis-target>"` and `--evidence-inputs "<args.evidence-inputs>"` when those keys are present. These are examples of the generic mapping rule, not a separate hard-coded argument list.
|
|
199
|
+
|
|
188
200
|
Step 3's empty-answer and escaping rules apply verbatim: every flag whose value is the empty string MUST still be passed explicitly as `--<key> ""` (e.g. `--workers ""`, `--directive ""`) — `render-bundle` distinguishes "flag absent" from "flag present with empty value", and the wizard's intent is always the latter.
|
|
189
201
|
|
|
190
|
-
|
|
202
|
+
Add `--lead-runtime <host-runtime>` from Step 1. Pass every `outcome.renderArgs` entry exactly once; do not enumerate a fixed provider list in this skill because the wizard and provider registry own those keys. If `renderArgs` ever contains `lead-runtime`, replace it with the Step 1 host value rather than emitting the flag twice.
|
|
191
203
|
|
|
192
204
|
```bash
|
|
193
205
|
okstra render-bundle \
|
|
194
|
-
--lead-runtime
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
--task-group "<args.task-group>" \
|
|
198
|
-
--task-id "<args.task-id>" \
|
|
199
|
-
--task-type "<args.task-type>" \
|
|
200
|
-
--task-brief "<args.task-brief>" \
|
|
201
|
-
--analysis-target "<args.analysis-target>" \
|
|
202
|
-
--evidence-inputs "<args.evidence-inputs>" \
|
|
203
|
-
--executor "<args.executor>" \
|
|
204
|
-
--critic "<args.critic>" \
|
|
205
|
-
--approved-plan "<args.approved-plan>" \
|
|
206
|
-
--stage "<args.stage>" \
|
|
207
|
-
--stages "<args.stages>" \
|
|
208
|
-
--base-ref "<args.base-ref>" \
|
|
209
|
-
--workers "<args.workers>" \
|
|
210
|
-
--directive "<args.directive>" \
|
|
211
|
-
--lead-model "<args.lead-model>" \
|
|
212
|
-
--claude-model "<args.claude-model>" \
|
|
213
|
-
--codex-model "<args.codex-model>" \
|
|
214
|
-
--antigravity-model "<args.antigravity-model>" \
|
|
215
|
-
--report-writer-model "<args.report-writer-model>" \
|
|
216
|
-
--related-tasks "<args.related-tasks>" \
|
|
217
|
-
--clarification-response "<args.clarification-response>" \
|
|
218
|
-
--pr-template-path "<args.pr-template-path>" \
|
|
219
|
-
--fix-cycle "<args.fix-cycle>"
|
|
206
|
+
--lead-runtime <host-runtime> \
|
|
207
|
+
--<first-renderArgs-key> "<first-renderArgs-value>" \
|
|
208
|
+
--<each-remaining-renderArgs-key> "<corresponding-value>"
|
|
220
209
|
```
|
|
221
210
|
|
|
222
211
|
`render-bundle` auto-supplies `--workspace-root` and forces `--render-only`. Stdout prints `okstra task root:`, `okstra instruction-set:`, and the full rendered lead prompt. Parse the labelled lines for `TASK_ROOT` and `INSTRUCTION_SET_PATH`. Also watch for an optional `okstra concurrent-run stages:` label line — present only when a concurrent run is detected (see "Concurrent-run detection branch" below).
|
|
@@ -265,14 +254,14 @@ If `render-bundle` fails with a `PrepareError` containing `Recorded stage SHAs n
|
|
|
265
254
|
|
|
266
255
|
If the anchor (`implementation_base_commit`) is reported unresolvable, run the same command's `--reset-anchor <ref>` after user confirmation. Correcting a confirm item without the picker is forbidden — the runtime also rejects a confirm correction without `--use-ref`.
|
|
267
256
|
|
|
268
|
-
## Step 6: Take over as
|
|
257
|
+
## Step 6: Take over as Okstra lead
|
|
269
258
|
|
|
270
|
-
Read `<INSTRUCTION_SET_PATH>/
|
|
259
|
+
Read `<INSTRUCTION_SET_PATH>/lead-execution-prompt.md` verbatim and take over as `Okstra lead` in the current host-native session. The prompt selects exactly one runtime adapter and 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.
|
|
271
260
|
|
|
272
261
|
Then proceed through the phases exactly as the lead prompt directs (Phase 1 context → Phase 2+ worker dispatch → final synthesis → final report).
|
|
273
262
|
|
|
274
263
|
Inform the user with one short line:
|
|
275
|
-
> Took over as
|
|
264
|
+
> Took over as Okstra lead (`<host-runtime>`) for `<taskKey>` (`<task-type>`). Run dir: `<RUN_DIR_RELATIVE_PATH>`. Beginning Phase 1 (context loading).
|
|
276
265
|
|
|
277
266
|
## Step 7: implementation unattended chaining (chain-stages)
|
|
278
267
|
|
|
@@ -281,7 +270,7 @@ When `task-type == implementation` and Step 5 render-args' `chain-stages` CSV ha
|
|
|
281
270
|
Queue = the topologically-sorted stage list from splitting `chain-stages` on `,` (the order Task 5 emitted by topologically sorting the dependency closure). For each stage `N` in the queue, in order:
|
|
282
271
|
|
|
283
272
|
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`.
|
|
284
|
-
2. As in Step 6, become
|
|
273
|
+
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).
|
|
285
274
|
3. After confirming that `done` row was written, reclaim the completed teammate panes of the stage you just finished: run `$HOME/.okstra/bin/okstra-trace-cleanup.sh --run-dir "<the run dir of the stage you just completed>"` (add `--keep report-writer-worker` if the report writer is still in flight). Then move to the next stage.
|
|
286
275
|
4. One-line report at each stage start/finish: `stage N/<total> start` / `stage N done → next K`.
|
|
287
276
|
|
|
@@ -9,7 +9,7 @@ This file is used when there is no custom PR template. Priority:
|
|
|
9
9
|
|
|
10
10
|
To use your own templates in project or global settings, add the `prTemplatePath` key to one of the paths above. (Absolute path or relative path based on `project_root`)
|
|
11
11
|
|
|
12
|
-
The placeholder is filled in directly by the
|
|
12
|
+
The placeholder is filled in directly by the Okstra lead for the release-handoff based on the following inputs:
|
|
13
13
|
- The intent and scope of the run brief
|
|
14
14
|
- The grounds for the verdict in the referenced final-verification report
|
|
15
15
|
- The commit range from `git log --oneline <base>..HEAD`
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: {{ frontmatter.title | yaml_scalar }}
|
|
3
|
+
id: {{ frontmatter.id | yaml_scalar }}
|
|
4
|
+
tags: {{ frontmatter.tags | yaml_inline_list }}
|
|
5
|
+
status: {{ frontmatter.status | yaml_scalar }}
|
|
6
|
+
aliases: {{ frontmatter.aliases | yaml_inline_list }}
|
|
7
|
+
date: {{ frontmatter.date | yaml_scalar }}
|
|
8
|
+
task-id: {{ frontmatter.taskId | yaml_scalar }}
|
|
9
|
+
task-group: {{ frontmatter.taskGroup | yaml_scalar }}
|
|
10
|
+
project-id: {{ frontmatter.projectId | yaml_scalar }}
|
|
11
|
+
task-type: {{ frontmatter.taskType | yaml_scalar }}
|
|
12
|
+
worker-id: {{ frontmatter.workerId | yaml_scalar }}
|
|
13
|
+
approved: {{ "true" if frontmatter.approved else "false" }}
|
|
14
|
+
implementation-option: {{ frontmatter.implementationOption | default("") | yaml_scalar }}
|
|
15
|
+
schema-version: {{ schemaVersion | yaml_scalar }}
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# {{ frontmatter.title }}
|
|
19
|
+
|
|
20
|
+
- Task Key: `{{ header.taskKey }}`
|
|
21
|
+
- Created At: `{{ header.createdAt }}`
|
|
22
|
+
- Task Type: `{{ header.taskType }}`
|
|
23
|
+
- Report Owner: `{{ header.reportOwner }}`
|
|
24
|
+
- Report Author: `{{ header.reportAuthor }}`
|
|
25
|
+
- Lead Model: `{{ header.leadModel | model_detail }}`
|
|
26
|
+
- Okstra Version: `{{ header.okstraVersion }}`
|
|
27
|
+
|
|
28
|
+
## AI Handoff Summary
|
|
29
|
+
|
|
30
|
+
- Verdict: `{{ finalVerdict.verdictToken }}`
|
|
31
|
+
- Direction: `{{ finalVerdict.direction }}`
|
|
32
|
+
- Rationale IDs: {{ finalVerdict.rationaleRowIds | join(", ") }}
|
|
33
|
+
- Blocking IDs: {{ aiBlockingIds | join(", ") if aiBlockingIds else "none" }}
|
|
34
|
+
- Next step: {{ finalVerdict.nextStep }}
|
|
35
|
+
|
|
36
|
+
### Decision Context
|
|
37
|
+
|
|
38
|
+
{{ aiDecisionContext | json_block }}
|
|
39
|
+
|
|
40
|
+
### Next Task Contract
|
|
41
|
+
|
|
42
|
+
{{ aiRoutingContract | json_block }}
|
|
43
|
+
|
|
44
|
+
## Clarification and User Decisions
|
|
45
|
+
|
|
46
|
+
{{ clarificationItems | json_block }}
|
|
47
|
+
|
|
48
|
+
## Evidence Ledger
|
|
49
|
+
|
|
50
|
+
{{ aiEvidenceLedger | json_block }}
|
|
51
|
+
|
|
52
|
+
## Task Deliverable: {{ aiTaskDeliverableTitle }}
|
|
53
|
+
|
|
54
|
+
{{ aiTaskDeliverable | json_block }}
|
|
55
|
+
|
|
56
|
+
## Cross Verification Audit
|
|
57
|
+
|
|
58
|
+
{{ crossVerification | json_block }}
|
|
59
|
+
|
|
60
|
+
## Execution Audit
|
|
61
|
+
|
|
62
|
+
{{ executionStatus | json_block }}
|
|
63
|
+
|
|
64
|
+
## Token and Cost Audit
|
|
65
|
+
|
|
66
|
+
{{ tokenUsage | json_block }}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
* { box-sizing: border-box; }
|
|
2
|
+
:root { color-scheme: light dark; font-family: Inter, ui-sans-serif, system-ui, sans-serif; line-height: 1.55; }
|
|
3
|
+
body { margin: 0; background: Canvas; color: CanvasText; }
|
|
4
|
+
.skip-link { position: absolute; left: -999px; }
|
|
5
|
+
.skip-link:focus { left: 1rem; top: 1rem; z-index: 20; background: Canvas; padding: .6rem; }
|
|
6
|
+
.human-report-header, main, .audit { width: min(1120px, calc(100% - 2rem)); margin-inline: auto; }
|
|
7
|
+
.human-report-header { padding: 3rem 0 1.5rem; }
|
|
8
|
+
.human-report-header h1 { max-width: 30ch; font-size: clamp(1.5rem, 3vw, 2.25rem); line-height: 1.15; margin: .25rem 0 .75rem; }
|
|
9
|
+
.lede { max-width: 70ch; font-size: clamp(1.05rem, 2vw, 1.35rem); line-height: 1.4; margin: 0 0 1rem; }
|
|
10
|
+
.eyebrow { text-transform: uppercase; letter-spacing: .08em; font-size: .78rem; color: GrayText; }
|
|
11
|
+
main { display: grid; gap: 1rem; padding-bottom: 3rem; }
|
|
12
|
+
section { padding: 1.4rem; border: 1px solid color-mix(in srgb, CanvasText 14%, transparent); border-radius: 16px; background: color-mix(in srgb, Canvas 94%, CanvasText 6%); }
|
|
13
|
+
h2 { margin-top: 0; font-size: 1.45rem; }
|
|
14
|
+
h3 { margin-bottom: .35rem; }
|
|
15
|
+
.summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: .8rem; }
|
|
16
|
+
.summary-card { padding: 1rem; border-radius: 12px; background: color-mix(in srgb, Highlight 8%, Canvas); border-left: 4px solid color-mix(in srgb, Highlight 55%, GrayText); }
|
|
17
|
+
.tone-high, .tone-important { border-left-color: #d94b4b; }
|
|
18
|
+
.tone-medium { border-left-color: #d79b25; }
|
|
19
|
+
.evidence-refs { color: GrayText; font-size: .85rem; }
|
|
20
|
+
figure { margin: 1rem 0 0; }
|
|
21
|
+
figcaption { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .5rem; margin-bottom: .7rem; }
|
|
22
|
+
.visualization { overflow-x: auto; border-radius: 12px; background: color-mix(in srgb, CanvasText 4%, Canvas); }
|
|
23
|
+
svg { max-width: 100%; min-width: 520px; height: auto; }
|
|
24
|
+
svg .edge { stroke: GrayText; stroke-width: 2; }
|
|
25
|
+
svg .edge-arrow-head { fill: GrayText; }
|
|
26
|
+
svg .node rect { fill: color-mix(in srgb, Highlight 14%, Canvas); stroke: Highlight; }
|
|
27
|
+
svg .node text { fill: CanvasText; font-size: 13px; }
|
|
28
|
+
table { width: 100%; border-collapse: collapse; margin-top: 1rem; font-size: .9rem; }
|
|
29
|
+
th, td { text-align: left; vertical-align: top; border-bottom: 1px solid color-mix(in srgb, CanvasText 18%, transparent); padding: .55rem; }
|
|
30
|
+
.visualization-fallback { margin-top: .8rem; }
|
|
31
|
+
.status { display: inline-block; border-radius: 999px; padding: .1rem .5rem; background: color-mix(in srgb, GrayText 15%, Canvas); }
|
|
32
|
+
.status-gap, .status-risk { background: color-mix(in srgb, #d94b4b 18%, Canvas); }
|
|
33
|
+
.audit { margin-bottom: 3rem; padding: 1rem; border: 1px solid GrayText; border-radius: 12px; }
|
|
34
|
+
.audit pre { overflow: auto; white-space: pre-wrap; }
|
|
35
|
+
.human-report-footer { width: min(1120px, calc(100% - 2rem)); margin: 0 auto 2rem; display: flex; flex-wrap: wrap; gap: .6rem; }
|
|
36
|
+
.human-report-footer pre { flex-basis: 100%; white-space: pre-wrap; max-height: 14em; overflow: auto; }
|
|
37
|
+
fieldset, label { display: block; margin: .7rem 0; }
|
|
38
|
+
textarea, select { width: 100%; max-width: 60rem; padding: .55rem; font: inherit; }
|
|
39
|
+
@media (max-width: 640px) { section { padding: 1rem; } .visualization { display: none; } }
|
|
40
|
+
@media print { .skip-link, .audit, script { display: none !important; } body { color: #000; background: #fff; } section { break-inside: avoid; border-color: #bbb; } .visualization-fallback { display: table; } }
|
|
41
|
+
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em; padding: .1em .3em; border-radius: 4px; background: color-mix(in srgb, CanvasText 8%, Canvas); }
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
{% from "html/macros/forms.html" import clarification_responses %}
|
|
3
|
+
<html lang="{{ 'ko' if humanSummary else 'en' }}" data-task-template="{{ taskType }}">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
7
|
+
<title>{{ (runMeta.task_key.split(':') | last)[:16] }} · {{ taskType }} #{{ runMeta.seq }}</title>
|
|
8
|
+
<style>{{ css | safe }}</style>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<a class="skip-link" href="#main-content">Skip to report content</a>
|
|
12
|
+
<header class="human-report-header">
|
|
13
|
+
<p class="eyebrow">{{ taskType }}</p>
|
|
14
|
+
<h1>{{ taskType }} #{{ runMeta.seq }}</h1>
|
|
15
|
+
<p class="lede">{{ humanSummary.headline | inline_code }}</p>
|
|
16
|
+
<p>{{ humanSummary.outcome | inline_code }}</p>
|
|
17
|
+
<button type="button" data-action="export-user-response">사용자 응답 내보내기</button>
|
|
18
|
+
</header>
|
|
19
|
+
<main id="main-content" data-report-role="human-main">
|
|
20
|
+
{% block human_content %}{% endblock %}
|
|
21
|
+
{{ clarification_responses(clarificationItems) }}
|
|
22
|
+
{% if evidenceIndex %}
|
|
23
|
+
<section data-report-section="evidence-ledger">
|
|
24
|
+
<h2>근거 대장</h2>
|
|
25
|
+
<table>
|
|
26
|
+
<thead><tr><th>ID</th><th>내용</th><th>출처 · 신뢰도</th></tr></thead>
|
|
27
|
+
<tbody>
|
|
28
|
+
{% for row_id, row in evidenceIndex | dictsort %}
|
|
29
|
+
<tr id="ev-{{ row_id }}"><td>{{ row_id }}</td><td>{{ row.text | inline_code }}</td><td>{% if row.codeEvidence %}{{ row.codeEvidence | code_evidence }}{% else %}{{ row.source | inline_code }}{% endif %}</td></tr>
|
|
30
|
+
{% endfor %}
|
|
31
|
+
</tbody>
|
|
32
|
+
</table>
|
|
33
|
+
</section>
|
|
34
|
+
{% endif %}
|
|
35
|
+
</main>
|
|
36
|
+
<details id="audit-details" class="audit" data-report-role="audit">
|
|
37
|
+
<summary>감사 정보</summary>
|
|
38
|
+
<section data-audit-field="crossVerification">
|
|
39
|
+
<h2>Cross Verification</h2>
|
|
40
|
+
<pre>{{ audit.crossVerification | audit_json }}</pre>
|
|
41
|
+
</section>
|
|
42
|
+
<section data-audit-field="executionStatus">
|
|
43
|
+
<h2>Execution Status by Agent</h2>
|
|
44
|
+
<pre>{{ audit.executionStatus | audit_json }}</pre>
|
|
45
|
+
</section>
|
|
46
|
+
<section data-audit-field="tokenUsage">
|
|
47
|
+
<h2>Token Usage</h2>
|
|
48
|
+
<pre>{{ audit.tokenUsage | audit_json }}</pre>
|
|
49
|
+
</section>
|
|
50
|
+
{% if audit.planBodyVerification is defined %}
|
|
51
|
+
<section data-audit-field="planBodyVerification">
|
|
52
|
+
<h2>Plan Body Verification</h2>
|
|
53
|
+
<pre>{{ audit.planBodyVerification | audit_json }}</pre>
|
|
54
|
+
</section>
|
|
55
|
+
{% endif %}
|
|
56
|
+
{% if audit.deliveryEvidence is defined %}
|
|
57
|
+
<section data-audit-field="deliveryEvidence">
|
|
58
|
+
<h2>Delivery Evidence</h2>
|
|
59
|
+
<pre>{{ audit.deliveryEvidence | audit_json }}</pre>
|
|
60
|
+
</section>
|
|
61
|
+
{% endif %}
|
|
62
|
+
</details>
|
|
63
|
+
<footer class="human-report-footer">
|
|
64
|
+
<button type="button" data-action="export-user-response">사용자 응답 내보내기</button>
|
|
65
|
+
<button type="button" data-action="copy-user-response">복사</button>
|
|
66
|
+
<button type="button" data-action="dismiss-user-response" hidden>닫기</button>
|
|
67
|
+
<pre id="user-response-output" aria-live="polite"></pre>
|
|
68
|
+
</footer>
|
|
69
|
+
<script id="run-meta" type="application/json">{{ {
|
|
70
|
+
"task-key": runMeta.task_key,
|
|
71
|
+
"task-type": runMeta.task_type,
|
|
72
|
+
"seq": runMeta.seq,
|
|
73
|
+
"source-report": runMeta.source_report,
|
|
74
|
+
"source-data-sha256": dataSha256,
|
|
75
|
+
"source-md-sha256": markdownSha256
|
|
76
|
+
} | tojson }}</script>
|
|
77
|
+
<script>{{ js | safe }}</script>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{% macro analysis_review(selector_ids) -%}
|
|
2
|
+
<section id="analysis-review" data-report-section="analysis-review">
|
|
3
|
+
<h2>분석 검토</h2>
|
|
4
|
+
<fieldset><legend>판정</legend>
|
|
5
|
+
<input id="analysis-review-accepted" type="radio" name="analysis-review-status" value="accepted"><label for="analysis-review-accepted">수락</label>
|
|
6
|
+
<input id="analysis-review-revision-requested" type="radio" name="analysis-review-status" value="revision-requested"><label for="analysis-review-revision-requested">수정 요청</label>
|
|
7
|
+
<input id="analysis-review-rejected" type="radio" name="analysis-review-status" value="rejected"><label for="analysis-review-rejected">거절</label>
|
|
8
|
+
</fieldset>
|
|
9
|
+
<label for="analysis-review-affected-ids">영향받는 ID</label>
|
|
10
|
+
<select id="analysis-review-affected-ids" multiple>
|
|
11
|
+
{% for analysis_id in selector_ids %}<option value="{{ analysis_id }}">{{ analysis_id }}</option>{% endfor %}
|
|
12
|
+
</select>
|
|
13
|
+
<label for="analysis-review-reason">이유</label><textarea id="analysis-review-reason" rows="3"></textarea>
|
|
14
|
+
<label for="analysis-review-evidence">추가 근거</label><textarea id="analysis-review-evidence" rows="3"></textarea>
|
|
15
|
+
<label for="analysis-review-scope-change">요청 범위 변경</label><textarea id="analysis-review-scope-change" rows="3"></textarea>
|
|
16
|
+
</section>
|
|
17
|
+
{%- endmacro %}
|
|
18
|
+
|
|
19
|
+
{% macro plan_approval(state) -%}
|
|
20
|
+
<section id="plan-approval" data-report-section="plan-approval">
|
|
21
|
+
<h2>계획 승인</h2>
|
|
22
|
+
<label for="approval-option">구현 옵션</label>
|
|
23
|
+
<select id="approval-option"{% if state.disabled_reason %} disabled{% endif %}>
|
|
24
|
+
{% for name in state.option_names %}
|
|
25
|
+
{% if name == state.recommended_option %}<option data-recommended="true" selected value="{{ name }}">{{ name | inline_code }} (권장)</option>{% else %}<option value="{{ name }}">{{ name | inline_code }}</option>{% endif %}
|
|
26
|
+
{% endfor %}
|
|
27
|
+
</select>
|
|
28
|
+
<input type="checkbox" id="approval-checkbox"{% if state.disabled_reason %} disabled{% endif %}><label for="approval-checkbox">이 계획을 승인합니다.</label>
|
|
29
|
+
{% if state.disabled_reason %}<div class="approval-disabled-reason"><p><strong>{{ state.disabled_reason }}</strong>라 아직 승인할 수 없습니다.</p><p>{{ state.blocker_ids | join(', ') }}에 답하고 사용자 응답을 내보낸 뒤 clarification 재개 명령으로 보고서를 다시 생성하세요.</p></div>{% endif %}
|
|
30
|
+
</section>
|
|
31
|
+
{%- endmacro %}
|
|
32
|
+
|
|
33
|
+
{% macro clarification_responses(items) -%}
|
|
34
|
+
{% if items %}
|
|
35
|
+
<section class="clarification-responses" aria-label="열린 질문에 답하기">
|
|
36
|
+
<h2>열린 질문에 답하기</h2>
|
|
37
|
+
<table><thead><tr><th>ID</th><th>질문</th><th>기대 형식</th><th>사용자 응답</th></tr></thead><tbody>
|
|
38
|
+
{% for row in items %}
|
|
39
|
+
<tr data-response-id="{{ row.id }}" data-kind="{{ row.kind }}">
|
|
40
|
+
<td>{{ row.id }}</td><td>{{ row.statement | inline_code }}</td><td>{{ row.expectedForm | inline_code }}</td>
|
|
41
|
+
<td><label for="response-{{ row.id }}">{{ row.id }} 사용자 응답</label><textarea id="response-{{ row.id }}" data-response-id="{{ row.id }}" rows="3">{{ row.userInput | default('') }}</textarea></td>
|
|
42
|
+
</tr>
|
|
43
|
+
{% endfor %}
|
|
44
|
+
</tbody></table>
|
|
45
|
+
</section>
|
|
46
|
+
{% endif %}
|
|
47
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{% macro narrative(value, field_name, evidence_index={}) -%}
|
|
2
|
+
<div class="narrative" data-report-field="{{ field_name }}">
|
|
3
|
+
{{ value.text | paragraphs }}
|
|
4
|
+
<p class="evidence-refs">근거: {{ value.evidenceRefs | evidence_refs(evidence_index) }}</p>
|
|
5
|
+
</div>
|
|
6
|
+
{%- endmacro %}
|
|
7
|
+
|
|
8
|
+
{% macro action_list(actions) -%}
|
|
9
|
+
<ol class="action-list">
|
|
10
|
+
{% for action in actions %}<li>{{ action | inline_code }}</li>{% endfor %}
|
|
11
|
+
</ol>
|
|
12
|
+
{%- endmacro %}
|
|
13
|
+
|
|
14
|
+
{% macro summary_card(title, body, tone='neutral') -%}
|
|
15
|
+
<article class="summary-card tone-{{ tone }}">
|
|
16
|
+
<h3>{{ title }}</h3>
|
|
17
|
+
<p>{{ body | inline_code }}</p>
|
|
18
|
+
</article>
|
|
19
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{% macro figure(model) -%}
|
|
2
|
+
<figure data-figure-kind="{{ model.kind }}" aria-labelledby="{{ model.figure_id }}-title" aria-describedby="{{ model.figure_id }}-summary">
|
|
3
|
+
<figcaption>
|
|
4
|
+
<strong id="{{ model.figure_id }}-title">{{ model.title }}</strong>
|
|
5
|
+
<span id="{{ model.figure_id }}-summary">{{ model.summary }}</span>
|
|
6
|
+
</figcaption>
|
|
7
|
+
<div class="visualization" aria-hidden="true">{{ model.svg | safe }}</div>
|
|
8
|
+
<table class="visualization-fallback">
|
|
9
|
+
<thead><tr><th>ID</th><th>이름</th><th>그룹</th><th>상태</th><th>설명</th></tr></thead>
|
|
10
|
+
<tbody>
|
|
11
|
+
{% for node in model.nodes %}
|
|
12
|
+
<tr data-fallback-id="{{ node.id }}"><td>{{ node.id }}</td><td>{{ node.label | inline_code }}</td><td>{{ node.group | inline_code }}</td><td>{{ node.status }}</td><td>{{ node.detail | inline_code }}</td></tr>
|
|
13
|
+
{% endfor %}
|
|
14
|
+
</tbody>
|
|
15
|
+
</table>
|
|
16
|
+
{% if model.edges %}
|
|
17
|
+
<table class="visualization-fallback visualization-edges">
|
|
18
|
+
<thead><tr><th>출발</th><th>도착</th><th>관계</th></tr></thead>
|
|
19
|
+
<tbody>
|
|
20
|
+
{% for edge in model.edges %}
|
|
21
|
+
<tr data-fallback-edge="{{ edge.source }}-{{ edge.target }}"><td>{{ edge.source }}</td><td>{{ edge.target }}</td><td>{{ edge.label | inline_code }}</td></tr>
|
|
22
|
+
{% endfor %}
|
|
23
|
+
</tbody>
|
|
24
|
+
</table>
|
|
25
|
+
{% endif %}
|
|
26
|
+
</figure>
|
|
27
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
|
|
3
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
4
|
+
{% from "html/macros/forms.html" import analysis_review %}
|
|
5
|
+
|
|
6
|
+
{% block human_content %}
|
|
7
|
+
<section data-report-section="change-overview">
|
|
8
|
+
<h2>변경 개요</h2>
|
|
9
|
+
{{ render_narrative(narrative.changeExplanation, "changeImpactAnalysis.userNarrative.changeExplanation", evidenceIndex) }}
|
|
10
|
+
<p>{{ change.changeRequest.summary | inline_code }}</p>
|
|
11
|
+
</section>
|
|
12
|
+
|
|
13
|
+
<section data-report-section="affected-and-unaffected">
|
|
14
|
+
<h2>영향받는 영역과 영향 없는 경계</h2>
|
|
15
|
+
{{ render_narrative(narrative.impactExplanation, "changeImpactAnalysis.userNarrative.impactExplanation", evidenceIndex) }}
|
|
16
|
+
<div data-report-field="changeImpactAnalysis.impactItems"><h3>영향받는 영역</h3>{% for row in change.impactItems %}{{ summary_card(row.id ~ " · " ~ (row.target | inline_code), row.impactKind ~ " / 영향 " ~ row.level ~ " / 신뢰 " ~ row.confidence) }}{% endfor %}</div>
|
|
17
|
+
<div data-report-field="changeImpactAnalysis.unaffectedBoundaries"><h3>영향 없는 경계</h3>{{ render_narrative(narrative.unaffectedExplanation, "changeImpactAnalysis.userNarrative.unaffectedExplanation", evidenceIndex) }}<ul>{% for row in change.unaffectedBoundaries %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<section data-report-section="blast-radius" data-report-field="changeImpactAnalysis.dependencyBlastRadius">
|
|
21
|
+
<h2>파급 범위</h2>
|
|
22
|
+
{{ figure(blastRadiusFigure) }}
|
|
23
|
+
</section>
|
|
24
|
+
|
|
25
|
+
<section data-report-section="test-and-operations">
|
|
26
|
+
<h2>테스트·운영·호환성</h2>
|
|
27
|
+
<div data-report-field="changeImpactAnalysis.compatibilityImpact"><h3>호환성</h3><ul>{% for row in change.compatibilityImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
|
|
28
|
+
<div data-report-field="changeImpactAnalysis.migrationImpact"><h3>마이그레이션</h3><ul>{% for row in change.migrationImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
|
|
29
|
+
<div data-report-field="changeImpactAnalysis.rollbackImpact"><h3>롤백</h3><ul>{% for row in change.rollbackImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
|
|
30
|
+
<div data-report-field="changeImpactAnalysis.securityAndPerformanceImpact"><h3>보안과 성능</h3><ul>{% for row in change.securityAndPerformanceImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<section data-report-section="planning-readiness" data-report-field="changeImpactAnalysis.planningInputs">
|
|
34
|
+
<h2>계획 준비도</h2>
|
|
35
|
+
{{ render_narrative(narrative.planningReadiness, "changeImpactAnalysis.userNarrative.planningReadiness", evidenceIndex) }}
|
|
36
|
+
<ul>{% for row in change.planningInputs %}<li>{{ row.constraint | inline_code }}{% if row.unknown %} — 미확정{% endif %}</li>{% endfor %}</ul>
|
|
37
|
+
</section>
|
|
38
|
+
|
|
39
|
+
{{ analysis_review(analysisReviewIds) }}
|
|
40
|
+
{% endblock %}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
|
|
3
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
4
|
+
|
|
5
|
+
{% block human_content %}
|
|
6
|
+
<section data-report-section="symptom">
|
|
7
|
+
<h2>사용자가 겪은 증상</h2>
|
|
8
|
+
{{ render_narrative(narrative.symptomExplanation, "errorAnalysis.userNarrative.symptomExplanation", evidenceIndex) }}
|
|
9
|
+
<blockquote data-report-field="errorAnalysis.symptomVerbatim">{{ error.symptomVerbatim | inline_code }}</blockquote>
|
|
10
|
+
<p data-report-field="errorAnalysis.observableFailure">{{ error.observableFailure | inline_code }}</p>
|
|
11
|
+
</section>
|
|
12
|
+
|
|
13
|
+
<section data-report-section="reproduction">
|
|
14
|
+
<h2>재현 상태</h2>
|
|
15
|
+
<p><span class="status status-{{ error.reproduction.status }}">{{ error.reproduction.status }}</span></p>
|
|
16
|
+
<ul>{% for item in error.reproduction.evidence %}<li>{{ [item] | evidence_refs(evidenceIndex) }}</li>{% endfor %}</ul>
|
|
17
|
+
{% if error.reproduction.blockedReason %}<p>{{ error.reproduction.blockedReason | inline_code }}</p>{% endif %}
|
|
18
|
+
</section>
|
|
19
|
+
|
|
20
|
+
<section data-report-section="causes">
|
|
21
|
+
<h2>원인 후보</h2>
|
|
22
|
+
{{ render_narrative(narrative.causeExplanation, "errorAnalysis.userNarrative.causeExplanation", evidenceIndex) }}
|
|
23
|
+
{{ figure(causeFigure) }}
|
|
24
|
+
<div class="summary-grid">{% for row in error.causeCandidates %}<article class="summary-card tone-{{ row.confidence }}"><p class="eyebrow">{{ row.id }} · 신뢰도 {{ row.confidence }}</p><h3>{{ row.statement | inline_code }}</h3><p class="evidence-refs">지지 근거: {{ row.supportingEvidence | evidence_refs(evidenceIndex) }}</p></article>{% else %}<p>현재 확인된 원인 후보가 없습니다.</p>{% endfor %}</div>
|
|
25
|
+
</section>
|
|
26
|
+
|
|
27
|
+
<section data-report-section="uncertainty">
|
|
28
|
+
<h2>아직 확정할 수 없는 부분</h2>
|
|
29
|
+
{{ render_narrative(narrative.uncertaintyExplanation, "errorAnalysis.userNarrative.uncertaintyExplanation", evidenceIndex) }}
|
|
30
|
+
{% for row in error.causeCandidates %}<article class="summary-card"><h3>{{ row.id }}</h3><p data-report-field="errorAnalysis.causeCandidates.confidence"><strong>현재 신뢰도:</strong> {{ row.confidence }}</p><p data-report-field="errorAnalysis.causeCandidates.falsifyingEvidenceChecked"><strong>확인한 반증:</strong> {{ row.falsifyingEvidenceChecked | join(', ') | inline_code }}</p><p data-report-field="errorAnalysis.causeCandidates.disproveWith"><strong>이 가설을 기각하는 방법:</strong> {{ row.disproveWith | inline_code }}</p></article>{% endfor %}
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<section data-report-section="next-diagnostic">
|
|
34
|
+
<h2>다음 진단 한 가지</h2>
|
|
35
|
+
{{ render_narrative(narrative.diagnosticGuidance, "errorAnalysis.userNarrative.diagnosticGuidance", evidenceIndex) }}
|
|
36
|
+
{{ summary_card("실행", error.nextDiagnostic.action, "important") }}
|
|
37
|
+
<p><strong>확인 신호:</strong> {{ error.nextDiagnostic.confirmingSignal | inline_code }}</p>
|
|
38
|
+
<p><strong>기각 신호:</strong> {{ error.nextDiagnostic.rejectingSignal | inline_code }}</p>
|
|
39
|
+
</section>
|
|
40
|
+
{% endblock %}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% from "html/macros/layout.html" import narrative as render_narrative, summary_card %}
|
|
3
|
+
{% from "html/macros/visualizations.html" import figure %}
|
|
4
|
+
{% from "html/macros/forms.html" import analysis_review %}
|
|
5
|
+
|
|
6
|
+
{% block human_content %}
|
|
7
|
+
<section data-report-section="feature-overview">
|
|
8
|
+
<h2>기능 개요</h2>
|
|
9
|
+
{{ render_narrative(narrative.userBehaviorExplanation, "featureAnalysis.userNarrative.userBehaviorExplanation", evidenceIndex) }}
|
|
10
|
+
<p><strong>대상:</strong> {{ feature.target.requestedValue | inline_code }}</p>
|
|
11
|
+
<ul>{% for row in feature.actors %}<li>{{ row.description | inline_code }}</li>{% endfor %}</ul>
|
|
12
|
+
</section>
|
|
13
|
+
|
|
14
|
+
<section data-report-section="behavior-flow" data-report-field="featureAnalysis.flows">
|
|
15
|
+
<h2>사용자 행동 흐름</h2>
|
|
16
|
+
{{ render_narrative(narrative.flowExplanation, "featureAnalysis.userNarrative.flowExplanation", evidenceIndex) }}
|
|
17
|
+
{{ figure(flowFigure) }}
|
|
18
|
+
<div class="summary-grid">{% for row in feature.flows %}<article class="summary-card" data-flow-kind="{{ 'alternate' if row.kind == 'alternative' else row.kind }}"><p class="eyebrow">{{ row.id }} · {{ row.kind }}</p><ol>{% for step in row.steps %}<li>{{ step.action | inline_code }}</li>{% endfor %}</ol></article>{% endfor %}</div>
|
|
19
|
+
</section>
|
|
20
|
+
|
|
21
|
+
<section data-report-section="rules-and-state">
|
|
22
|
+
<h2>규칙과 상태 변경</h2>
|
|
23
|
+
{{ render_narrative(narrative.rulesAndStateExplanation, "featureAnalysis.userNarrative.rulesAndStateExplanation", evidenceIndex) }}
|
|
24
|
+
<div data-report-field="featureAnalysis.domainRules"><h3>도메인 규칙</h3>{% for row in feature.domainRules %}{{ summary_card(row.id ~ " · " ~ (row.condition | inline_code), row.outcome) }}{% endfor %}</div>
|
|
25
|
+
<div data-report-field="featureAnalysis.stateChanges"><h3>상태 변경</h3>{% for row in feature.stateChanges %}{{ summary_card(row.id ~ " · " ~ row.field, row.change) }}{% endfor %}</div>
|
|
26
|
+
</section>
|
|
27
|
+
|
|
28
|
+
<section data-report-section="external-interactions" data-report-field="featureAnalysis.externalInteractions">
|
|
29
|
+
<h2>외부 상호작용</h2>
|
|
30
|
+
{% for row in feature.externalInteractions %}{{ summary_card(row.target | inline_code, row.input ~ " → " ~ row.output ~ ". 실패 시: " ~ row.failureHandling) }}{% endfor %}
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<section data-report-section="test-gaps" data-report-field="featureAnalysis.testCoverage">
|
|
34
|
+
<h2>테스트와 공백</h2>
|
|
35
|
+
{{ render_narrative(narrative.testGapExplanation, "featureAnalysis.userNarrative.testGapExplanation", evidenceIndex) }}
|
|
36
|
+
{% for row in feature.testCoverage %}{{ summary_card(row.id, "테스트: " ~ (row.testPaths | join(', ')) ~ "; 공백: " ~ ((row.gaps | join(', ')) or '없음')) }}{% endfor %}
|
|
37
|
+
</section>
|
|
38
|
+
|
|
39
|
+
{{ analysis_review(analysisReviewIds) }}
|
|
40
|
+
{% endblock %}
|