okstra 0.155.0 → 0.157.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/docs/architecture.md +3 -1
- package/docs/for-ai/skills/okstra-schedule-gen.md +5 -4
- package/docs/project-structure-overview.md +7 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/prompts/profiles/_common-contract.md +1 -1
- package/runtime/python/okstra_ctl/clarification_items.py +3 -3
- package/runtime/python/okstra_ctl/render_final_report.py +31 -44
- package/runtime/python/okstra_ctl/report_contract.py +15 -0
- package/runtime/python/okstra_ctl/report_finalize.py +22 -3
- package/runtime/python/okstra_ctl/report_markdown.py +441 -0
- package/runtime/python/okstra_ctl/schedule_semantics.py +186 -91
- package/runtime/python/okstra_ctl/stage_map.py +203 -1
- package/runtime/python/okstra_ctl/wizard.py +1 -11
- package/runtime/python/okstra_project/state.py +14 -2
- package/runtime/skills/okstra-schedule-gen/SKILL.md +43 -18
- package/runtime/templates/reports/final-report-v2.template.md +74 -10
- package/runtime/templates/reports/md/macros/sections.md +19 -0
- package/runtime/templates/reports/md/tasks/change-impact-analysis.template.md +18 -0
- package/runtime/templates/reports/md/tasks/error-analysis.template.md +13 -0
- package/runtime/templates/reports/md/tasks/feature-analysis.template.md +13 -0
- package/runtime/templates/reports/md/tasks/final-verification.template.md +13 -0
- package/runtime/templates/reports/md/tasks/implementation-planning.template.md +15 -0
- package/runtime/templates/reports/md/tasks/implementation.template.md +15 -0
- package/runtime/templates/reports/md/tasks/improvement-discovery.template.md +10 -0
- package/runtime/templates/reports/md/tasks/project-analysis.template.md +15 -0
- package/runtime/templates/reports/md/tasks/release-handoff.template.md +13 -0
- package/runtime/templates/reports/md/tasks/requirements-discovery.template.md +15 -0
- package/runtime/templates/reports/schedule.template.md +166 -63
- package/runtime/validators/validate-run.py +19 -6
- package/runtime/validators/validate-schedule.py +94 -65
- package/src/commands/inspect/stage-map.mjs +6 -1
- package/src/commands/inspect/worker-liveness.mjs +15 -3
- package/src/commands/lifecycle/install.mjs +69 -4
- package/src/commands/lifecycle/uninstall.mjs +21 -35
- package/src/lib/install-assets.mjs +37 -0
|
@@ -77,7 +77,6 @@ from okstra_ctl.run import (
|
|
|
77
77
|
from okstra_ctl.stage_map import (
|
|
78
78
|
StageMapError,
|
|
79
79
|
parse_stage_map_file,
|
|
80
|
-
parse_stage_map_text,
|
|
81
80
|
stage_map_records,
|
|
82
81
|
)
|
|
83
82
|
from okstra_ctl.user_response import (
|
|
@@ -1034,16 +1033,7 @@ def _fix_cycle_confirm_required(state: WizardState) -> bool:
|
|
|
1034
1033
|
def _parse_stage_objects(state: WizardState) -> list:
|
|
1035
1034
|
"""Return the approved plan's strict Stage Map objects for the picker."""
|
|
1036
1035
|
try:
|
|
1037
|
-
|
|
1038
|
-
return parse_stage_map_text(
|
|
1039
|
-
plan_text,
|
|
1040
|
-
source_plan_path=str(Path(state.approved_plan_path).resolve()),
|
|
1041
|
-
)
|
|
1042
|
-
except OSError as exc:
|
|
1043
|
-
raise WizardError(
|
|
1044
|
-
f"approved plan 의 Stage Map 을 읽을 수 없습니다 "
|
|
1045
|
-
f"({state.approved_plan_path}): {exc}"
|
|
1046
|
-
) from exc
|
|
1036
|
+
return parse_stage_map_file(Path(state.approved_plan_path))
|
|
1047
1037
|
except StageMapError as exc:
|
|
1048
1038
|
raise WizardError(
|
|
1049
1039
|
f"approved plan 의 Stage Map 을 신뢰할 수 없습니다 "
|
|
@@ -384,12 +384,23 @@ def stage_map_read_side_snapshot(project_root: Path, task_key: str) -> dict:
|
|
|
384
384
|
"""
|
|
385
385
|
from okstra_ctl.consumers import read_stage_consumer_state
|
|
386
386
|
from okstra_ctl.paths import RunRef
|
|
387
|
-
from okstra_ctl.stage_map import
|
|
387
|
+
from okstra_ctl.stage_map import (
|
|
388
|
+
PlanningDetail,
|
|
389
|
+
StageMapError,
|
|
390
|
+
load_planning_detail,
|
|
391
|
+
load_task_stage_map,
|
|
392
|
+
merge_planning_detail,
|
|
393
|
+
)
|
|
388
394
|
|
|
389
395
|
identity = resolve_task_identity(project_root, task_key)
|
|
390
396
|
task_root = Path(identity["taskRoot"])
|
|
391
397
|
try:
|
|
392
398
|
stage_snapshot = load_task_stage_map(task_root, identity["manifest"])
|
|
399
|
+
detail = (
|
|
400
|
+
load_planning_detail(Path(stage_snapshot.source_plan_path))
|
|
401
|
+
if stage_snapshot.source_plan_path
|
|
402
|
+
else PlanningDetail({}, {})
|
|
403
|
+
)
|
|
393
404
|
except StageMapError as exc:
|
|
394
405
|
raise StateError(str(exc), stage=exc.code) from exc
|
|
395
406
|
plan_run_root = RunRef.from_task_root(
|
|
@@ -407,8 +418,9 @@ def stage_map_read_side_snapshot(project_root: Path, task_key: str) -> dict:
|
|
|
407
418
|
"taskRoot": identity["taskRoot"],
|
|
408
419
|
"state": stage_snapshot.state,
|
|
409
420
|
"sourcePlanPath": stage_snapshot.source_plan_path,
|
|
410
|
-
"stages": stage_snapshot.stages,
|
|
421
|
+
"stages": merge_planning_detail(stage_snapshot.stages, detail),
|
|
411
422
|
"doneStages": done,
|
|
423
|
+
"planning": detail.task_narratives,
|
|
412
424
|
}
|
|
413
425
|
|
|
414
426
|
|
|
@@ -49,13 +49,13 @@ If the call fails with `unknown command: preflight`, the `okstra` binary on PATH
|
|
|
49
49
|
|
|
50
50
|
## Contract SSOT — template + validator
|
|
51
51
|
|
|
52
|
-
The installed template `~/.okstra/templates/reports/schedule.template.md` is the **byte-for-byte SSOT** for the output shape: frontmatter, top header block, the mandatory `##` heading list and order, per-task `Item / Detail` field labels and sub-section order, table column shapes, the ASCII Gantt format (relative day axis, plain fence,
|
|
52
|
+
The installed template `~/.okstra/templates/reports/schedule.template.md` is the **byte-for-byte SSOT** for the output shape: frontmatter, top header block, the mandatory `##` heading list and order, per-task `Item / Detail` field labels and sub-section order, table column shapes, the ASCII Gantt format (relative day axis, plain fence, bar-only rows), the dependency-graph shapes, and the optional `## Glossary` gate. **Read the template before writing the schedule and follow it exactly** — do not re-derive section shapes from memory. Headings and field labels stay English literals regardless of the source-report language; body prose is Korean. When a section has no data, render its heading with `_none_` — never delete or reorder headings. Never emit mermaid or any graph DSL.
|
|
53
53
|
|
|
54
54
|
`~/.okstra/lib/validators/validate-schedule.py` is the enforcement for all of the above (heading order, field labels, controlled vocabulary — e.g. `Med-High` is the canonical risk form — forbidden translations, checkbox bans, Gantt fence rules, unresolved-code detection). The Step 4 ambiguous-classification rationale line is the one rule the validator does not yet enforce — emit it yourself.
|
|
55
55
|
|
|
56
56
|
One computation rule the template scaffold cannot carry inline:
|
|
57
57
|
|
|
58
|
-
- **Effort-to-Day mapping**: day ranges per size are defined once in the template's `### Effort Sizing Criteria` table.
|
|
58
|
+
- **Effort-to-Day mapping**: day ranges per size are defined once in the template's `### Effort Sizing Criteria` table, and a task's `Days` cell takes its range from that row. `XXL` is the exception — it has no finite upper bound there, so an XXL task is sized by its own stage decomposition and its `Days` cell is the sum of its Work Breakdown rows. The At a Glance totals line sums every in-scope task's `Days`, XXL included. Append ` (est)` to a `Days` range the schedule derived rather than read from the plan; implementation-planning states no durations, so that is every range today.
|
|
59
59
|
|
|
60
60
|
## Procedure
|
|
61
61
|
|
|
@@ -85,7 +85,7 @@ For each in-scope task, the **authoritative source is its `implementation-planni
|
|
|
85
85
|
okstra stage-map <task-key> --json
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
The response is source-aware: `{ok, taskKey, taskRoot, state, sourcePlanPath, stages:[
|
|
88
|
+
The response is source-aware: `{ok, taskKey, taskRoot, state, sourcePlanPath, stages:[…], doneStages:[int], planning:{…}}`. Each stage row carries `stage_number`, `title`, `depends_on`, `step_count`, `exit_contract_summary` and — from a schema-v2 report — that stage's `stepwiseExecution` (the numbered steps, each with `action`, `files`, `command`, `expected`), `sliceValue`, `acceptance` and `exitContract`. `planning` carries the report's task-level rows: `rollbackStrategy`, `validationChecklist`, `crossProjectDependencies`, `dependencyMigrationRisk`, `recommendedOption`. Branch on it explicitly:
|
|
89
89
|
|
|
90
90
|
The resolved source, when ready, is a report under `runs/implementation-planning`; the CLI owns choosing exactly one report from that domain.
|
|
91
91
|
|
|
@@ -93,7 +93,7 @@ The resolved source, when ready, is a report under `runs/implementation-planning
|
|
|
93
93
|
- `ok: true, state: "missing"` — record an empty source and empty stage sets. Tag the task `[NEEDS-PLANNING]`, skip the stage picker, and render task-level metadata only, with no forward Work Breakdown row, Gantt row, or day total.
|
|
94
94
|
- `ok: false` or any state other than `ready` and `missing` — this is a structured Stage Map error, such as a corrupt or conflicting plan source. Stop before drafting, preserve the CLI `stage` and `reason` in the user-facing error, and do not guess from another report.
|
|
95
95
|
|
|
96
|
-
For `ready`,
|
|
96
|
+
For `ready`, the CLI response is the source for everything the schedule renders — stage rows and `planning` together. Read `sourcePlanPath` only for the task-level Priority / Risk / Scope / Repos that the payload does not carry, and read no further into it: transcribing the payload is the whole point of Step 4. Read header metadata from `task-manifest.json`: `taskId`, `taskGroup`, `taskKey`, `workCategory`, `workStatus`, `taskType`, and `workflow.currentPhase`. Blocking and approval items are not extracted (see Audience & authority).
|
|
97
97
|
|
|
98
98
|
If unfinished stages are empty while `workStatus` is not `done`, render `_Complete — no remaining stage_` under the task's phase section and contribute no forward day total.
|
|
99
99
|
|
|
@@ -113,28 +113,44 @@ Run this **once per in-scope task that has a non-empty `remainingStages`**, sequ
|
|
|
113
113
|
|
|
114
114
|
Record the chosen `selectedStages` for this task. Reject and re-prompt any custom selection whose transitive `depends_on` closure is not covered by `selectedStages ∪ doneStages`.
|
|
115
115
|
|
|
116
|
-
### Step 4:
|
|
116
|
+
### Step 4: Task ordering and per-task transcription
|
|
117
117
|
|
|
118
118
|
The canonical categories come from `scripts/okstra_ctl/work_categories.py::WORK_CATEGORIES`. Do not infer or publish another category.
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|--------------|---------------|
|
|
122
|
-
| `bugfix` | Phase 1 when risk is High/Med-High; otherwise Phase 2 |
|
|
123
|
-
| `feature` / `improvement` | Phase 2 |
|
|
124
|
-
| `refactor` / `ops` | Phase 3 |
|
|
125
|
-
| unmatched or missing | Phase 2, with rationale `> _workCategory '<raw-value>' undefined — defaulting to Phase 2._` at the top of that phase section |
|
|
120
|
+
**There are no phase buckets and no priority table.** `## Task Details` holds one `### <n>. <Title>` block per in-scope task, numbered from 1 in At a Glance order. The heading is the work's name — the task-id is a machine key and belongs in the At a Glance row, not in a heading the reader has to decode; the block binds to its row by number. At a Glance is already sorted Priority-first, so that ordering carries priority; sequence lives in the stages; and `## Recommended Immediate Actions` is the one place that says what to do next. In the per-task `Item / Detail` table, `Status` says where the work stands in plain words — "구현 계획 승인됨 — 실행 미착수", not `implementation-planning / implementation-planning`. Derive it from `taskType` and `workflow.currentPhase`, but never print those tokens: they are okstra's phase names and the reader has no phases.
|
|
126
121
|
|
|
127
|
-
|
|
122
|
+
Order At a Glance by Priority (`P0` first), breaking ties by `workCategory` in this canonical order: `bugfix`, `feature`, `refactor`, `ops`, `improvement`. A task whose `workCategory` is unmatched or missing sorts last within its priority and keeps its raw value in the `Category` cell — never substitute a category that is not in that list.
|
|
128
123
|
|
|
129
|
-
|
|
124
|
+
Within a task's block, selected stages become the Work Breakdown rows under the exact header `| Stage | Title | Steps | Depends On | Days |`. Render one row per selected stage using its Stage Map `title`, `step_count`, and `depends_on`, plus its proportional day range. Stages excluded because they are already done are listed once as `> _Done stages: stage <n>, …_` and carry no forward effort.
|
|
130
125
|
|
|
131
|
-
|
|
126
|
+
**Write the schedule FOR the team executing it, FROM the plan.** Two different duties:
|
|
127
|
+
|
|
128
|
+
- **Facts are carried exactly.** File paths, commands, counts, day ranges, rollback triggers, the number of specs or tables — a wrong path or a dropped item is a defect. This is what a hand-written summary of the report loses, and why `okstra stage-map` hands them over structurally.
|
|
129
|
+
- **Prose is rewritten for the reader doing the work.** Say what the step does, why it exists, and what they should see when it succeeds. Do NOT paste a source field wholesale: the planning report was written to justify a design to a reviewer, so it carries sentences arguing for the approach — `sliceValue` is exactly that — which tell an implementer nothing. A schedule full of them reads as a record of someone else's deliberation.
|
|
130
|
+
|
|
131
|
+
Selecting which source rows belong in a schedule is not summarising. Dropping a justification is correct; dropping a file path is not.
|
|
132
|
+
|
|
133
|
+
**The reader has no access to the plan's review history.** A sentence like "this answers the objection that sank the direction the first time" or "the reporter's rationale is …" assumes the reader watched the plan being argued. State the engineering reason directly — *why this order, what breaks without it* — and never cite a prior revision, a reviewer, or a decision's provenance. The same applies to okstra's own vocabulary: `task-group`, `implementation-planning`, a `taskType` printed as a Status, a sibling task-id nobody outside the run can look up. Every enum the schedule prints is either defined on the page or replaced by words — the `Kind` / `Direction` labels on the risk and dependency tables classify rows for the plan's author and tell an implementer nothing.
|
|
134
|
+
|
|
135
|
+
**Stage titles and every table cell are written in the schedule's prose language.** Headings and field labels stay English literals; the content beside them does not. A Work Breakdown whose `Title` column is English next to a `Steps` table in Korean makes the reader switch languages inside one block. The selection contract carries the same titles the schedule prints, so the two stay comparable.
|
|
136
|
+
|
|
137
|
+
**A stage block is `Steps` plus `Exit criteria`, and nothing else.** The `Steps` table comes from `stepwiseExecution` — one row per step with what it does (keep the leading `RED:` / `GREEN:` marker), the files it touches, and what the executor should see when it succeeds, from that step's `expected`. `Exit criteria` is one line from `exitContract` saying when the stage is finished.
|
|
138
|
+
|
|
139
|
+
Do not render `sliceValue` — it argues why the plan sliced the work this way, which is a reviewer's question, not an implementer's. Do not render `acceptance` alongside `exitContract`: they restate each other. Measured on one real plan, `acceptance`'s four claims all reappeared in `exitContract`, which then added two more. Sort `Verification Commands` `pre` → `mid` → `post` and introduce those three values above the table — source order interleaves them, and a reader working down the table would run a precondition after the checks it gates.
|
|
140
|
+
|
|
141
|
+
**Transcribing means the content, never the source row's `id`.** `VC-001`, `RB-003`, `XP-001`, `DM-002`, `EO-015` and their kin index a report the schedule's reader cannot open, so the self-contained rule rejects them and the validator enforces it. Drop the id column, and where a row's body cites a sibling code, replace the citation with the thing it names ("the boot probe asserts all 29 keys", not "VC-006").
|
|
132
142
|
|
|
133
143
|
### Step 5: Gantt decision (render by default)
|
|
134
144
|
|
|
135
|
-
`## Gantt Chart` is **rendered by default** — skip ONLY when literally no day signal exists (every task
|
|
145
|
+
`## Gantt Chart` is **rendered by default** — skip ONLY when literally no day signal exists (every in-scope task lacks both effort sizing and stage decomposition). An XXL task is not a skip reason: it is sized by its stages. Render whenever any of these hold: 2+ tasks with effort sizing; 1 task whose effort yields a range (mid-point bar, or `lo`/`hi` two-bar form); 1 task with Part/Phase/Step decomposition in the source (bars at decomposition-unit level); total estimated effort ≥ 3 days. When per-unit day allocations aren't itemized, split the parent range across the visible units yourself and append the `est` annotation (or add `> Per-day allocation is an estimate; refresh recommended after blocking items are resolved.`). "Range is wide", "single task", "user decisions pending" are NOT skip reasons — render an estimate-tagged chart instead.
|
|
146
|
+
|
|
147
|
+
When the source is a Stage Map, the Gantt bars are selected stages. A row is labelled `Stage <n>` when exactly one task is scheduled, and `<TASK-ID> Stage <n>` when more than one is. Spell the stage out — `S1` costs the reader a lookup and saves five characters. A row is a label and a bar and nothing else: no `days=` annotation (the Work Breakdown owns those numbers, and the validator compares the bar against that column), and no per-row `! crit` / `est` markers, which distinguish nothing when every row carries them. One line above the fence states the column unit and what `█` and `░` mean — that is the whole legend, because those two glyphs are the only notation left. Split the task effort range proportionally by `step_count`; round every stage except the last to 0.5 day and let the last stage absorb the remainder. Cross-stage dependency annotations follow `depends_on`. Already-done and non-selected stages never get a bar. A task tagged `[NEEDS-PLANNING]` contributes no bars.
|
|
148
|
+
|
|
149
|
+
**Bar geometry is checked, not decorative.** One column is half a day: a bar runs `lower / 0.5` filled cells `█` then `(upper - lower) / 0.5` open cells `░`. A bar drawn to any other width is a validation error, as is an axis whose last tick overshoots the scheduled work by 5 days or more.
|
|
150
|
+
|
|
151
|
+
**Every controlled code the schedule prints must be defined on the page.** The template's `### Priority & Risk Scale` resolves `P0`–`P3` and `Very Low`–`High`; the validator requires that subsection and both header literals. Stage labels are spelled out for the same reason — Gantt rows read `Stage 3`, and `Depends On` cells read `None` / `Stage 2` / `Stage 1 (done)` rather than a bare number the reader has to match against the Stage column of the same table. A reader who never saw the source report cannot rank `P0` against `P1`, calibrate `High`, or expand `S3`; those are opaque codes wearing a friendlier shape.
|
|
136
152
|
|
|
137
|
-
|
|
153
|
+
**`## Task Dependency Graph` is optional and carries cross-task edges only.** Stage order already lives in the Work Breakdown's `Depends On` column; drawing it again as an arrow list makes the reader cross-reference one table against another to learn nothing new. Omit the section entirely — heading included — when no task depends on another, which is always true of a single-task schedule. A heading whose body only redirects to another table is noise, the same defect as a priority table that restates At a Glance.
|
|
138
154
|
|
|
139
155
|
An unrepresentable half-day allocation is a validation error. Do not switch to a fallback allocation algorithm or publish that draft.
|
|
140
156
|
|
|
@@ -182,7 +198,16 @@ When you do skip, insert in the section's position exactly: `> _Gantt Chart skip
|
|
|
182
198
|
```
|
|
183
199
|
|
|
184
200
|
3. **Run the deterministic gate first.** Execute `python3 ~/.okstra/lib/validators/validate-schedule.py <draft> --selection-json <selection>`. Do not dispatch the narrative verifier when this exits non-zero.
|
|
185
|
-
4. **Run the independent LLM verifier second.** Only after the deterministic gate passes, dispatch an independent verifier subagent with the draft and selection JSON, but without the lead's reasoning. It
|
|
201
|
+
4. **Run the independent LLM verifier second.** Only after the deterministic gate passes, dispatch an independent verifier subagent with the draft and selection JSON, but without the lead's reasoning. It returns `pass` plus concrete findings. Give it these checks:
|
|
202
|
+
- **Executable ordering** — stage sequence, `Depends On`, and Gantt bar positions tell the same story; nothing depends on something scheduled after it.
|
|
203
|
+
- **Arithmetic** — the Work Breakdown `Days` column sums to the At a Glance `Days` cell and to the `Effort sum` line.
|
|
204
|
+
- **Engineering-only scope** — no approval gate, permission check, stakeholder alignment or decision checklist.
|
|
205
|
+
- **Prose vs structure** — the narrative does not contradict the tables.
|
|
206
|
+
- **No assumed background.** Flag any sentence that only parses if the reader watched the plan being argued: a cited objection, a prior revision, "the reporter's rationale", a decision's provenance. Flag okstra's own vocabulary reaching the page — `task-group`, a `taskType` printed as a status, a task-id nobody outside the run can look up — and any enum printed without being defined on the page.
|
|
207
|
+
- **Written for the executor.** Each step says what to do and what the executor should see; a step that only explains why the plan chose this shape is a finding.
|
|
208
|
+
- **One language.** Table cells and stage titles follow the schedule's prose language; only headings and field labels stay English literals.
|
|
209
|
+
|
|
210
|
+
These last three are the checks no validator can make. Do not accept a `pass` that skipped them.
|
|
186
211
|
5. **Revise from the first gate after every change.** If either gate requests a change, the lead revises the same draft in place, then starts again at the deterministic `--selection-json` gate. Allow **Max 2 revise cycles** total across both gates.
|
|
187
212
|
6. **Gate publication.** Only the same draft that passes both gates may be promoted in Step 6. If it still fails after the second revision, do not write the final file; remove the paired staging artifacts and report the residual findings in Korean.
|
|
188
213
|
|
|
@@ -230,5 +255,5 @@ Reached only after both Step 5.5 gates return `pass`. Promote the verified same
|
|
|
230
255
|
|
|
231
256
|
- All user-facing messages in Korean; schedule body prose Korean, identifiers/headings/field labels English (template literals).
|
|
232
257
|
- Use project-relative paths in completion messages.
|
|
233
|
-
-
|
|
258
|
+
- Each detail `Status` is derived from `taskType` and `workflow.currentPhase` but written in plain words; `workStatus` only filters candidates and is never printed.
|
|
234
259
|
- Per-task section Work Breakdown rows are **selected stages**, not free-form items; done stages are summarized, never scheduled forward.
|
|
@@ -14,6 +14,14 @@ approved: {{ "true" if frontmatter.approved else "false" }}
|
|
|
14
14
|
implementation-option: {{ frontmatter.implementationOption | default("") | yaml_scalar }}
|
|
15
15
|
schema-version: {{ schemaVersion | yaml_scalar }}
|
|
16
16
|
---
|
|
17
|
+
{#
|
|
18
|
+
The spine renders these by hand or leaves them to the human HTML, so the
|
|
19
|
+
closing `md_rest("")` sweep must not repeat them: `frontmatter` and `header`
|
|
20
|
+
appear above, `humanSummary` is the HTML's own narrative, and `verdictCard`
|
|
21
|
+
is a non-authoritative index of `finalVerdict`, which §Final Verdict already
|
|
22
|
+
carries in full.
|
|
23
|
+
-#}
|
|
24
|
+
{{ md_claim("schemaVersion", "meta", "frontmatter", "header", "humanSummary", "verdictCard") }}
|
|
17
25
|
|
|
18
26
|
# {{ frontmatter.title }}
|
|
19
27
|
|
|
@@ -33,34 +41,90 @@ schema-version: {{ schemaVersion | yaml_scalar }}
|
|
|
33
41
|
- Blocking IDs: {{ aiBlockingIds | join(", ") if aiBlockingIds else "none" }}
|
|
34
42
|
- Next step: {{ finalVerdict.nextStep }}
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
## Decision Context
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
### Final Verdict
|
|
39
47
|
|
|
40
|
-
|
|
48
|
+
{{ md("finalVerdict", 4) }}
|
|
41
49
|
|
|
42
|
-
|
|
50
|
+
### Summary
|
|
51
|
+
|
|
52
|
+
{{ md("summary", 4) }}
|
|
53
|
+
|
|
54
|
+
### Rationale
|
|
55
|
+
|
|
56
|
+
{{ md("rationale", 4) }}
|
|
57
|
+
{% if md_has("ticketCoverage") %}
|
|
58
|
+
|
|
59
|
+
### Ticket Coverage
|
|
60
|
+
|
|
61
|
+
{{ md("ticketCoverage", 4) }}
|
|
62
|
+
{% endif %}
|
|
63
|
+
|
|
64
|
+
## Next Task Contract
|
|
65
|
+
|
|
66
|
+
### Recommended Next Steps
|
|
67
|
+
|
|
68
|
+
{{ md("recommendedNextSteps", 4) }}
|
|
69
|
+
|
|
70
|
+
### Follow-up Tasks
|
|
71
|
+
|
|
72
|
+
{{ md("followUpTasks", 4) }}
|
|
43
73
|
|
|
44
74
|
## Clarification and User Decisions
|
|
45
75
|
|
|
46
|
-
{{ clarificationItems
|
|
76
|
+
{{ md("clarificationItems", 3) }}
|
|
77
|
+
{% if md_has("clarificationCarryIn") %}
|
|
78
|
+
|
|
79
|
+
### Clarification Carried In
|
|
80
|
+
|
|
81
|
+
{{ md("clarificationCarryIn", 4) }}
|
|
82
|
+
{% endif %}
|
|
47
83
|
|
|
48
84
|
## Evidence Ledger
|
|
49
85
|
|
|
50
|
-
|
|
86
|
+
### Evidence
|
|
87
|
+
|
|
88
|
+
{{ md("evidence", 4) }}
|
|
89
|
+
|
|
90
|
+
### Missing Information
|
|
91
|
+
|
|
92
|
+
{{ md("missingInformation", 4) }}
|
|
93
|
+
|
|
94
|
+
### End-State Coverage
|
|
95
|
+
|
|
96
|
+
{{ md("endStateCoverage", 4) }}
|
|
97
|
+
{% if md_has("analysisCommon") %}
|
|
98
|
+
|
|
99
|
+
### Analysis Common
|
|
100
|
+
|
|
101
|
+
{{ md("analysisCommon", 4) }}
|
|
102
|
+
{% endif %}
|
|
51
103
|
|
|
52
104
|
## Task Deliverable: {{ aiTaskDeliverableTitle }}
|
|
105
|
+
{% if aiTaskTemplate %}
|
|
106
|
+
|
|
107
|
+
{% include aiTaskTemplate %}
|
|
108
|
+
{% else %}
|
|
53
109
|
|
|
54
|
-
{{
|
|
110
|
+
{{ md_rest(aiTaskProperty, 3) }}
|
|
111
|
+
{% endif %}
|
|
55
112
|
|
|
56
113
|
## Cross Verification Audit
|
|
57
114
|
|
|
58
|
-
{{ crossVerification
|
|
115
|
+
{{ md("crossVerification", 3) }}
|
|
59
116
|
|
|
60
117
|
## Execution Audit
|
|
61
118
|
|
|
62
|
-
{{ executionStatus
|
|
119
|
+
{{ md("executionStatus", 3) }}
|
|
63
120
|
|
|
64
121
|
## Token and Cost Audit
|
|
65
122
|
|
|
66
|
-
{{ tokenUsage
|
|
123
|
+
{{ md("tokenUsage", 3) }}
|
|
124
|
+
{% set trailing = md_rest("", 3) %}
|
|
125
|
+
{% if trailing %}
|
|
126
|
+
|
|
127
|
+
## Additional Report Data
|
|
128
|
+
|
|
129
|
+
{{ trailing }}
|
|
130
|
+
{% endif %}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{#
|
|
2
|
+
Task-body section helper.
|
|
3
|
+
|
|
4
|
+
The report spine renders its own fixed sections unconditionally — an empty
|
|
5
|
+
`Missing Information` there is a positive statement. A task body is
|
|
6
|
+
variable-shaped instead: most of its fields are schema-optional, so a heading
|
|
7
|
+
over `_none_` would be noise rather than news. `section` therefore emits
|
|
8
|
+
nothing at all when the path carries no value.
|
|
9
|
+
|
|
10
|
+
Import `with context`, otherwise the `md` / `md_has` globals the renderer
|
|
11
|
+
binds are not visible inside the macro.
|
|
12
|
+
-#}
|
|
13
|
+
{% macro section(path, title, level=3) -%}
|
|
14
|
+
{% if md_has(path) %}
|
|
15
|
+
{{ "#" * level }} {{ title }}
|
|
16
|
+
|
|
17
|
+
{{ md(path, level + 1) }}
|
|
18
|
+
{% endif %}
|
|
19
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{#
|
|
3
|
+
Reading order for the phase that plans this change: what is being asked, what
|
|
4
|
+
must not move, what the change reaches, and what the planner needs carried
|
|
5
|
+
forward. Unaffected boundaries sit next to the blast radius because the pair
|
|
6
|
+
is what bounds the change.
|
|
7
|
+
-#}
|
|
8
|
+
{{ s.section("changeImpactAnalysis.changeRequest", "Change Request") }}
|
|
9
|
+
{{ s.section("changeImpactAnalysis.preservedBehaviors", "Preserved Behaviors") }}
|
|
10
|
+
{{ s.section("changeImpactAnalysis.impactItems", "Impact Items") }}
|
|
11
|
+
{{ s.section("changeImpactAnalysis.dependencyBlastRadius", "Dependency Blast Radius") }}
|
|
12
|
+
{{ s.section("changeImpactAnalysis.unaffectedBoundaries", "Unaffected Boundaries") }}
|
|
13
|
+
{{ s.section("changeImpactAnalysis.compatibilityImpact", "Compatibility Impact") }}
|
|
14
|
+
{{ s.section("changeImpactAnalysis.migrationImpact", "Migration Impact") }}
|
|
15
|
+
{{ s.section("changeImpactAnalysis.rollbackImpact", "Rollback Impact") }}
|
|
16
|
+
{{ s.section("changeImpactAnalysis.securityAndPerformanceImpact", "Security and Performance Impact") }}
|
|
17
|
+
{{ s.section("changeImpactAnalysis.planningInputs", "Planning Inputs") }}
|
|
18
|
+
{{ md_rest("changeImpactAnalysis", 3) }}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{#
|
|
3
|
+
Reading order for the agent that fixes this: the symptom as reported, what is
|
|
4
|
+
observably failing, how to reproduce it, the ranked causes with what would
|
|
5
|
+
disprove each, and the next probe to run.
|
|
6
|
+
-#}
|
|
7
|
+
{{ s.section("errorAnalysis.symptomVerbatim", "Symptom Verbatim") }}
|
|
8
|
+
{{ s.section("errorAnalysis.observableFailure", "Observable Failure") }}
|
|
9
|
+
{{ s.section("errorAnalysis.reproduction", "Reproduction") }}
|
|
10
|
+
{{ s.section("errorAnalysis.causeCandidates", "Cause Candidates") }}
|
|
11
|
+
{{ s.section("errorAnalysis.nextDiagnostic", "Next Diagnostic") }}
|
|
12
|
+
{{ s.section("errorAnalysis.routing", "Routing") }}
|
|
13
|
+
{{ md_rest("errorAnalysis", 3) }}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{#
|
|
3
|
+
Reading order for an agent about to change this feature: what the feature is,
|
|
4
|
+
how it runs, the rules it must keep, what it writes, and what already covers
|
|
5
|
+
it in tests.
|
|
6
|
+
-#}
|
|
7
|
+
{{ s.section("featureAnalysis.target", "Target") }}
|
|
8
|
+
{{ s.section("featureAnalysis.flows", "Flows") }}
|
|
9
|
+
{{ s.section("featureAnalysis.domainRules", "Domain Rules") }}
|
|
10
|
+
{{ s.section("featureAnalysis.stateChanges", "State Changes") }}
|
|
11
|
+
{{ s.section("featureAnalysis.externalInteractions", "External Interactions") }}
|
|
12
|
+
{{ s.section("featureAnalysis.testCoverage", "Test Coverage") }}
|
|
13
|
+
{{ md_rest("featureAnalysis", 3) }}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{#
|
|
3
|
+
Reading order for whoever decides whether this ships: what blocks acceptance,
|
|
4
|
+
what proves the rest passed, and what risk survives a pass.
|
|
5
|
+
-#}
|
|
6
|
+
{{ s.section("finalVerification.acceptanceBlockers", "Acceptance Blockers") }}
|
|
7
|
+
{{ s.section("finalVerification.validationEvidence", "Validation Evidence") }}
|
|
8
|
+
{{ s.section("finalVerification.residualRisk", "Residual Risk") }}
|
|
9
|
+
{{ s.section("finalVerification.manualUserTest", "Manual User Test") }}
|
|
10
|
+
{{ s.section("finalVerification.sourceImplementationReport", "Source Implementation Report") }}
|
|
11
|
+
{{ s.section("finalVerification.stageReports", "Stage Reports") }}
|
|
12
|
+
{{ s.section("finalVerification.routingRecommendation", "Routing Recommendation") }}
|
|
13
|
+
{{ md_rest("finalVerification", 3) }}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{#
|
|
3
|
+
Reading order for the agent that executes this plan: what was chosen, the
|
|
4
|
+
stages it is cut into, how to run each one, and how to tell a stage is done.
|
|
5
|
+
Option candidates and the trade-off matrix answer "why this one" and sit
|
|
6
|
+
after that, in the closing sweep.
|
|
7
|
+
-#}
|
|
8
|
+
{{ s.section("implementationPlanning.recommendedOption", "Recommended Option") }}
|
|
9
|
+
{{ s.section("implementationPlanning.stageMap", "Stage Map") }}
|
|
10
|
+
{{ s.section("implementationPlanning.stages", "Stages") }}
|
|
11
|
+
{{ s.section("implementationPlanning.stepwiseExecution", "Stepwise Execution") }}
|
|
12
|
+
{{ s.section("implementationPlanning.validationChecklist", "Validation Checklist") }}
|
|
13
|
+
{{ s.section("implementationPlanning.rollbackStrategy", "Rollback Strategy") }}
|
|
14
|
+
{{ s.section("implementationPlanning.requirementCoverage", "Requirement Coverage") }}
|
|
15
|
+
{{ md_rest("implementationPlanning", 3) }}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{#
|
|
3
|
+
Reading order for the verifier that picks this stage up: what the plan asked
|
|
4
|
+
for, what actually landed, what proves it, and what the implementer knows is
|
|
5
|
+
still open.
|
|
6
|
+
-#}
|
|
7
|
+
{{ s.section("implementation.approvedPlanReference", "Approved Plan Reference") }}
|
|
8
|
+
{{ s.section("implementation.diffSummary", "Diff Summary") }}
|
|
9
|
+
{{ s.section("implementation.commitList", "Commit List") }}
|
|
10
|
+
{{ s.section("implementation.requirementCoverage", "Requirement Coverage") }}
|
|
11
|
+
{{ s.section("implementation.validationEvidence", "Validation Evidence") }}
|
|
12
|
+
{{ s.section("implementation.outOfPlanEdits", "Out-of-Plan Edits") }}
|
|
13
|
+
{{ s.section("implementation.manualUserTest", "Manual User Test") }}
|
|
14
|
+
{{ s.section("implementation.routingRecommendation", "Routing Recommendation") }}
|
|
15
|
+
{{ md_rest("implementation", 3) }}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{#
|
|
3
|
+
Reading order for whoever picks work off this sidetrack: the candidates
|
|
4
|
+
themselves, then the lens coverage that says which angles were actually swept
|
|
5
|
+
and which were not.
|
|
6
|
+
-#}
|
|
7
|
+
{{ s.section("improvementDiscovery.candidates", "Candidates") }}
|
|
8
|
+
{{ s.section("improvementDiscovery.lensCoverage", "Lens Coverage") }}
|
|
9
|
+
{{ s.section("improvementDiscovery.selectionLimit", "Selection Limit") }}
|
|
10
|
+
{{ md_rest("improvementDiscovery", 3) }}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{#
|
|
3
|
+
Reading order for an agent orienting in an unfamiliar repository: the feature
|
|
4
|
+
index and entry points first, because they are how it finds anything, then the
|
|
5
|
+
component map, then where the risk and the coverage gaps are.
|
|
6
|
+
-#}
|
|
7
|
+
{{ s.section("projectAnalysis.featureIndex", "Feature Index") }}
|
|
8
|
+
{{ s.section("projectAnalysis.entryPoints", "Entry Points") }}
|
|
9
|
+
{{ s.section("projectAnalysis.components", "Components") }}
|
|
10
|
+
{{ s.section("projectAnalysis.dataStores", "Data Stores") }}
|
|
11
|
+
{{ s.section("projectAnalysis.externalSystems", "External Systems") }}
|
|
12
|
+
{{ s.section("projectAnalysis.rankedHotspots", "Ranked Hotspots") }}
|
|
13
|
+
{{ s.section("projectAnalysis.qualityCoverage", "Quality Coverage") }}
|
|
14
|
+
{{ s.section("projectAnalysis.scanCoverage", "Scan Coverage") }}
|
|
15
|
+
{{ md_rest("projectAnalysis", 3) }}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{#
|
|
3
|
+
Reading order for whoever picks the release up: what was handed off, what the
|
|
4
|
+
branch and PR ended up as, and whether a merge would conflict.
|
|
5
|
+
-#}
|
|
6
|
+
{{ s.section("releaseHandoff.handoffScope", "Handoff Scope") }}
|
|
7
|
+
{{ s.section("releaseHandoff.userSelections", "User Selections") }}
|
|
8
|
+
{{ s.section("releaseHandoff.featureBranchState", "Feature Branch State") }}
|
|
9
|
+
{{ s.section("releaseHandoff.pullRequestOutcome", "Pull Request Outcome") }}
|
|
10
|
+
{{ s.section("releaseHandoff.mergeConflictProbe", "Merge Conflict Probe") }}
|
|
11
|
+
{{ s.section("releaseHandoff.executedCommands", "Executed Commands") }}
|
|
12
|
+
{{ s.section("releaseHandoff.routingRecommendation", "Routing Recommendation") }}
|
|
13
|
+
{{ md_rest("releaseHandoff", 3) }}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{#
|
|
3
|
+
Reading order for the phase that plans from these requirements: what the user
|
|
4
|
+
asked verbatim, how the system read it, and which requirements are settled
|
|
5
|
+
versus still open. The verbatim request leads because every later reading is
|
|
6
|
+
judged against it.
|
|
7
|
+
-#}
|
|
8
|
+
{{ s.section("requirementsDiscovery.requestVerbatim", "Request Verbatim") }}
|
|
9
|
+
{{ s.section("requirementsDiscovery.systemInterpretation", "System Interpretation") }}
|
|
10
|
+
{{ s.section("requirementsDiscovery.classification", "Classification") }}
|
|
11
|
+
{{ s.section("requirementsDiscovery.confirmedRequirements", "Confirmed Requirements") }}
|
|
12
|
+
{{ s.section("requirementsDiscovery.unresolvedRequirements", "Unresolved Requirements") }}
|
|
13
|
+
{{ s.section("requirementsDiscovery.rejectionCriteria", "Rejection Criteria") }}
|
|
14
|
+
{{ s.section("requirementsDiscovery.routing", "Routing") }}
|
|
15
|
+
{{ md_rest("requirementsDiscovery", 3) }}
|