okstra 0.207.0 → 0.208.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 +3 -2
- package/dist/cli-registry.mjs +6 -0
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.mjs +1 -1
- package/dist/commands/lifecycle/doctor.mjs +1 -1
- package/dist/lib/skill-catalog.mjs +1 -0
- package/dist/lib/skill-catalog.mjs.map +1 -1
- package/docs/architecture/storage-model.md +14 -0
- package/docs/architecture.md +30 -9
- package/docs/cli.md +26 -22
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/project-structure-overview.md +15 -8
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/operations/explain-flow.json +6 -0
- package/runtime/bin/lib/okstra/cli.sh +1 -5
- package/runtime/bin/lib/okstra/globals.sh +0 -2
- package/runtime/bin/lib/okstra/usage.sh +5 -3
- package/runtime/bin/okstra.sh +0 -2
- package/runtime/prompts/duties/business-flow-investigator.json +14 -0
- package/runtime/prompts/lead/context-loader.md +1 -1
- package/runtime/prompts/lead/convergence.md +22 -7
- package/runtime/prompts/lead/okstra-lead-contract.md +10 -6
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/lead/team-contract.md +12 -17
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/wizard/prompts.ko.json +0 -91
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +6 -0
- package/runtime/python/okstra_ctl/agent/invocation.py +1 -1
- package/runtime/python/okstra_ctl/agent/prompt_cli/batch.py +1 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +1 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +11 -125
- package/runtime/python/okstra_ctl/agent/standalone.py +183 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +39 -8
- package/runtime/python/okstra_ctl/assignment_resolver.py +7 -1
- package/runtime/python/okstra_ctl/brief_frontmatter.py +10 -0
- package/runtime/python/okstra_ctl/business_flow/__init__.py +4 -0
- package/runtime/python/okstra_ctl/business_flow/cli.py +134 -0
- package/runtime/python/okstra_ctl/business_flow/contracts.py +268 -0
- package/runtime/python/okstra_ctl/business_flow/engine.py +518 -0
- package/runtime/python/okstra_ctl/business_flow/hooks.py +221 -0
- package/runtime/python/okstra_ctl/business_flow/invocation.py +170 -0
- package/runtime/python/okstra_ctl/business_flow/report.py +49 -0
- package/runtime/python/okstra_ctl/business_flow/source.py +206 -0
- package/runtime/python/okstra_ctl/business_flow/store.py +388 -0
- package/runtime/python/okstra_ctl/convergence.py +173 -2
- package/runtime/python/okstra_ctl/convergence_critic_verify_prompt.py +18 -0
- package/runtime/python/okstra_ctl/convergence_provenance.py +8 -0
- package/runtime/python/okstra_ctl/coverage_census.py +596 -0
- package/runtime/python/okstra_ctl/design_surfaces.py +4 -0
- package/runtime/python/okstra_ctl/direct_work.py +1 -1
- package/runtime/python/okstra_ctl/dispatch_core.py +7 -3
- package/runtime/python/okstra_ctl/doctor.py +12 -6
- package/runtime/python/okstra_ctl/domain/role.py +1 -0
- package/runtime/python/okstra_ctl/group_context.py +5 -4
- package/runtime/python/okstra_ctl/legacy_model_selection.py +7 -51
- package/runtime/python/okstra_ctl/manager_split.py +4 -1
- package/runtime/python/okstra_ctl/manager_view.py +9 -3
- package/runtime/python/okstra_ctl/model_io/lines.py +1 -24
- package/runtime/python/okstra_ctl/model_io/renderers.py +54 -41
- package/runtime/python/okstra_ctl/phases/change_impact_analysis/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/change_impact_analysis/profile.md +0 -8
- package/runtime/python/okstra_ctl/phases/error_analysis/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/error_analysis/profile.md +6 -8
- package/runtime/python/okstra_ctl/phases/feature_analysis/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/feature_analysis/profile.md +0 -8
- package/runtime/python/okstra_ctl/phases/final_verification/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/final_verification/profile.md +2 -8
- package/runtime/python/okstra_ctl/phases/implementation/boundary.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation/profile.md +0 -6
- package/runtime/python/okstra_ctl/phases/implementation/report_assets/implementation-input.template.md +1 -1
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/authoring.py +4 -3
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/entry.py +1 -14
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/profile.md +5 -8
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/spec.md +3 -3
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/validation.py +15 -5
- package/runtime/python/okstra_ctl/phases/implementation_planning/authoring.py +9 -1
- package/runtime/python/okstra_ctl/phases/implementation_planning/boundary.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation_planning/instructions/plan-body-verification.md +4 -2
- package/runtime/python/okstra_ctl/phases/implementation_planning/plan_body.py +25 -9
- package/runtime/python/okstra_ctl/phases/implementation_planning/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation_planning/profile.md +7 -10
- package/runtime/python/okstra_ctl/phases/improvement_discovery/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/improvement_discovery/profile.md +4 -11
- package/runtime/python/okstra_ctl/phases/project_analysis/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/project_analysis/profile.md +0 -8
- package/runtime/python/okstra_ctl/phases/release_handoff/profile.md +1 -1
- package/runtime/python/okstra_ctl/phases/release_handoff/spec.md +1 -1
- package/runtime/python/okstra_ctl/phases/requirements_discovery/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/requirements_discovery/profile.md +10 -8
- package/runtime/python/okstra_ctl/phases/requirements_discovery/spec.md +3 -3
- package/runtime/python/okstra_ctl/phases/technical_verification/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/technical_verification/profile.md +0 -4
- package/runtime/python/okstra_ctl/plan_items.py +1 -1
- package/runtime/python/okstra_ctl/render.py +10 -43
- package/runtime/python/okstra_ctl/render_final_report.py +3 -0
- package/runtime/python/okstra_ctl/report_assembly.py +15 -1
- package/runtime/python/okstra_ctl/report_finalize.py +40 -0
- package/runtime/python/okstra_ctl/report_html/render.py +3 -0
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +1 -2
- package/runtime/python/okstra_ctl/run.py +78 -409
- package/runtime/python/okstra_ctl/wizard/__init__.py +2 -24
- package/runtime/python/okstra_ctl/wizard/cli.py +3 -6
- package/runtime/python/okstra_ctl/wizard/confirmation.py +3 -35
- package/runtime/python/okstra_ctl/wizard/engine.py +2 -4
- package/runtime/python/okstra_ctl/wizard/ids.py +1 -88
- package/runtime/python/okstra_ctl/wizard/registry.py +36 -228
- package/runtime/python/okstra_ctl/wizard/render.py +2 -2
- package/runtime/python/okstra_ctl/wizard/roles.py +1 -3
- package/runtime/python/okstra_ctl/wizard/sources.py +9 -40
- package/runtime/python/okstra_ctl/wizard/state.py +36 -145
- package/runtime/python/okstra_ctl/wizard/statefile.py +27 -128
- package/runtime/python/okstra_ctl/wizard/steps_identity.py +22 -10
- package/runtime/python/okstra_ctl/wizard/steps_options.py +5 -4
- package/runtime/python/okstra_ctl/wizard/steps_roles.py +15 -565
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +9 -2
- package/runtime/schemas/business-flow-v1.schema.json +847 -0
- package/runtime/schemas/convergence-groups-v2.0.schema.json +7 -0
- package/runtime/skills/okstra-explain-flow/SKILL.md +42 -0
- package/runtime/skills/okstra-inspect/facets/history.md +5 -5
- package/runtime/skills/okstra-run/SKILL.md +2 -2
- package/runtime/templates/manager/view.template.html +7 -4
- package/runtime/templates/reports/business-flow.template.md +106 -0
- package/runtime/templates/reports/html/base.template.html +14 -1
- package/runtime/templates/reports/html/business-flow.template.html +31 -0
- package/runtime/templates/reports/html/i18n/en.json +1 -0
- package/runtime/templates/reports/html/i18n/ko.json +1 -0
- package/runtime/templates/worker-prompt-preamble.md +11 -2
- package/runtime/validators/checks/validate-prompt-metadata-01.py +10 -10
- package/runtime/validators/validate-run.py +70 -21
- package/runtime/validators/validate_analysis_report.py +21 -21
- package/runtime/python/okstra_ctl/workers.py +0 -133
|
@@ -784,7 +784,7 @@ def render_team_state(team_state_path: str, ctx: dict) -> None:
|
|
|
784
784
|
catalog = _worker_catalog(ctx)
|
|
785
785
|
worker_dispatch_plan = _worker_dispatch_plan(ctx)
|
|
786
786
|
workers = []
|
|
787
|
-
for row in
|
|
787
|
+
for row in _critic_role_rows(ctx):
|
|
788
788
|
workers.append({
|
|
789
789
|
**{key: row[key] for key in (
|
|
790
790
|
"workerId", "role", "agent", "provider", "runner",
|
|
@@ -1180,7 +1180,7 @@ def migrate_legacy_run_artifacts(ctx: dict) -> None:
|
|
|
1180
1180
|
# --------------------------------------------------------------------------- #
|
|
1181
1181
|
|
|
1182
1182
|
|
|
1183
|
-
def
|
|
1183
|
+
def _worker_role_rows(ctx: dict, reviewers: list[str]) -> list[dict]:
|
|
1184
1184
|
catalog = _worker_catalog(ctx)
|
|
1185
1185
|
return [
|
|
1186
1186
|
{
|
|
@@ -1199,7 +1199,7 @@ def _required_worker_roles(ctx: dict, reviewers: list[str]) -> list[dict]:
|
|
|
1199
1199
|
]
|
|
1200
1200
|
|
|
1201
1201
|
|
|
1202
|
-
def
|
|
1202
|
+
def _critic_role_rows(ctx: dict) -> list[dict]:
|
|
1203
1203
|
"""선택해 배정한 비평 역할. 선택 후에는 실행 또는 미실행 사유가 필요하다.
|
|
1204
1204
|
|
|
1205
1205
|
`invocationAssignments`의 `critic/*` 배정만 선언하므로 역할을 선택하지
|
|
@@ -1412,7 +1412,7 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
1412
1412
|
reviewers = _resolve_workers(ctx)
|
|
1413
1413
|
worker_dispatch_plan = _worker_dispatch_plan(ctx)
|
|
1414
1414
|
catalog = _worker_catalog(ctx)
|
|
1415
|
-
|
|
1415
|
+
worker_role_rows = _worker_role_rows(ctx, reviewers)
|
|
1416
1416
|
worker_prompt_paths = {item: catalog[item]["promptPath"] for item in reviewers}
|
|
1417
1417
|
lead_agent = _lead_agent(ctx)
|
|
1418
1418
|
lead_role = _lead_role(ctx)
|
|
@@ -1557,13 +1557,10 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
1557
1557
|
"finalReportFormat": "markdown",
|
|
1558
1558
|
"finalReportFilename": ctx.get("FINAL_REPORT_FILENAME", ""),
|
|
1559
1559
|
"finalStatusFilename": ctx.get("FINAL_STATUS_FILENAME", ""),
|
|
1560
|
-
"
|
|
1561
|
-
"
|
|
1562
|
-
"requiredWorkerRoles": required_worker_roles,
|
|
1563
|
-
"optionalWorkerRoles": _optional_worker_roles(ctx),
|
|
1560
|
+
"workerRoles": worker_role_rows,
|
|
1561
|
+
"criticRoles": _critic_role_rows(ctx),
|
|
1564
1562
|
"requiredAgentStatusEntries": required_agent_status_entries,
|
|
1565
1563
|
"requireDistinctLeadFromWorkerSession": True,
|
|
1566
|
-
"requireAllRequiredWorkerAttempts": True,
|
|
1567
1564
|
"requireAntigravityWorkerAttempt": "antigravity" in reviewers,
|
|
1568
1565
|
"requireCollectedWorkerStatusesBeforeFinalVerdict": True,
|
|
1569
1566
|
"disallowLeadSoloAnalysisAsWorkerResult": True,
|
|
@@ -1574,6 +1571,7 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
1574
1571
|
"Safe or Reasonable Areas",
|
|
1575
1572
|
"Uncertain Points",
|
|
1576
1573
|
"Recommended Next Actions",
|
|
1574
|
+
"Coverage Verdicts",
|
|
1577
1575
|
],
|
|
1578
1576
|
"finalReportSections": [
|
|
1579
1577
|
"Problem or Validation Summary",
|
|
@@ -1736,7 +1734,7 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
|
|
|
1736
1734
|
reviewers = _resolve_workers(ctx)
|
|
1737
1735
|
worker_dispatch_plan = _worker_dispatch_plan(ctx)
|
|
1738
1736
|
catalog = _worker_catalog(ctx)
|
|
1739
|
-
|
|
1737
|
+
worker_role_rows = _worker_role_rows(ctx, reviewers)
|
|
1740
1738
|
worker_prompt_paths = {item: catalog[item]["promptPath"] for item in reviewers}
|
|
1741
1739
|
lead_agent = _lead_agent(ctx)
|
|
1742
1740
|
lead_role = _lead_role(ctx)
|
|
@@ -1903,18 +1901,15 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
|
|
|
1903
1901
|
"leadModelExecutionValue": ctx.get("LEAD_MODEL_EXECUTION_VALUE", ""),
|
|
1904
1902
|
"leadExecutionMode": "synthesis-only",
|
|
1905
1903
|
"finalSynthesisOwner": lead_role,
|
|
1906
|
-
"
|
|
1907
|
-
"
|
|
1908
|
-
"optionalWorkerRoles": _optional_worker_roles(ctx),
|
|
1904
|
+
"workerRoles": worker_role_rows,
|
|
1905
|
+
"criticRoles": _critic_role_rows(ctx),
|
|
1909
1906
|
"requiredAgentStatusEntries": [lead_role]
|
|
1910
1907
|
+ [catalog[item]["role"] for item in reviewers],
|
|
1911
1908
|
"requireDistinctLeadFromWorkerSession": True,
|
|
1912
|
-
"requireAllRequiredWorkerAttempts": True,
|
|
1913
1909
|
"requireAntigravityWorkerAttempt": "antigravity" in reviewers,
|
|
1914
1910
|
"requireCollectedWorkerStatusesBeforeFinalVerdict": True,
|
|
1915
1911
|
"disallowLeadSoloAnalysisAsWorkerResult": True,
|
|
1916
1912
|
"disallowGenericParallelOnlyExecution": True,
|
|
1917
|
-
"preferredCompletedWorkerResults": len(reviewers),
|
|
1918
1913
|
"executor": _executor_contract(ctx),
|
|
1919
1914
|
},
|
|
1920
1915
|
"validation": {
|
|
@@ -2696,7 +2691,6 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
2696
2691
|
model_assignment_lines = [
|
|
2697
2692
|
fmt_assignment(lead_role, lead_model, lead_model_execution)
|
|
2698
2693
|
]
|
|
2699
|
-
worker_role_labels: list[str] = []
|
|
2700
2694
|
execution_status_entries = [f"`{lead_role}`"]
|
|
2701
2695
|
execution_status_table_lines = [
|
|
2702
2696
|
"| 에이전트 | 역할 | 모델 | 상태 | 핵심 발견 요약 |",
|
|
@@ -2714,35 +2708,11 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
2714
2708
|
model_assignment_lines.append(
|
|
2715
2709
|
fmt_assignment(m["role"], m["model"], m["modelExecutionValue"])
|
|
2716
2710
|
)
|
|
2717
|
-
worker_role_labels.append(f"`{m['role']}`")
|
|
2718
2711
|
execution_status_entries.append(f"`{m['role']}`")
|
|
2719
2712
|
execution_status_table_lines.append(
|
|
2720
2713
|
f"| {m['agentLabel']} | {m['role']} | {m['model']} | completed / timeout / error / not-run | {m['role']}의 핵심 발견 요약 |"
|
|
2721
2714
|
)
|
|
2722
2715
|
|
|
2723
|
-
if worker_role_labels:
|
|
2724
|
-
if len(worker_role_labels) == 1:
|
|
2725
|
-
worker_role_sentence = (
|
|
2726
|
-
f"- {worker_role_labels[0]} is the required worker role."
|
|
2727
|
-
)
|
|
2728
|
-
else:
|
|
2729
|
-
worker_role_sentence = (
|
|
2730
|
-
f"- {', '.join(worker_role_labels[:-1])}, "
|
|
2731
|
-
f"and {worker_role_labels[-1]} are the required worker roles."
|
|
2732
|
-
)
|
|
2733
|
-
preferred_results_sentence = (
|
|
2734
|
-
f"- Aim to collect completed results from all "
|
|
2735
|
-
f"{len(worker_role_labels)} required workers."
|
|
2736
|
-
)
|
|
2737
|
-
else:
|
|
2738
|
-
worker_role_sentence = "- No worker roles were selected for this run."
|
|
2739
|
-
preferred_results_sentence = "- No worker results are expected for this run."
|
|
2740
|
-
worker_attempt_sentence = (
|
|
2741
|
-
"- `Antigravity worker` is mandatory to attempt for this workflow."
|
|
2742
|
-
if "antigravity" in selected
|
|
2743
|
-
else "- `Antigravity worker` is not selected for this run, so no Antigravity attempt is required."
|
|
2744
|
-
)
|
|
2745
|
-
|
|
2746
2716
|
task_type = ctx.get("TASK_TYPE", "")
|
|
2747
2717
|
adapter_setup_facts: list[str] = []
|
|
2748
2718
|
if lead_runtime == "claude-code":
|
|
@@ -2865,9 +2835,6 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
|
|
|
2865
2835
|
ctx["WORKER_RESULT_PATH_LINES"] = "\n".join(worker_result_lines)
|
|
2866
2836
|
ctx["MODEL_ASSIGNMENT_LINES"] = "\n".join(model_assignment_lines)
|
|
2867
2837
|
ctx["TEAM_ROLE_LINES"] = "\n".join(team_role_lines)
|
|
2868
|
-
ctx["REQUIRED_WORKER_ROLE_SENTENCE"] = worker_role_sentence
|
|
2869
|
-
ctx["ANTIGRAVITY_ATTEMPT_SENTENCE"] = worker_attempt_sentence
|
|
2870
|
-
ctx["PREFERRED_WORKER_RESULTS_SENTENCE"] = preferred_results_sentence
|
|
2871
2838
|
ctx["EXECUTION_STATUS_EXACT_ENTRIES"] = ", ".join(execution_status_entries)
|
|
2872
2839
|
ctx["EXECUTION_STATUS_TABLE_ROWS"] = "\n".join(execution_status_table_lines)
|
|
2873
2840
|
|
|
@@ -817,6 +817,9 @@ def render_to_file(
|
|
|
817
817
|
raise _with_excerpt_drift_hint(exc, data_path, str(task_type or "")) from exc
|
|
818
818
|
|
|
819
819
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
820
|
+
from .business_flow.hooks import markdown_report_links
|
|
821
|
+
|
|
822
|
+
rendered += markdown_report_links(data_path)
|
|
820
823
|
tmp = output_path.with_suffix(output_path.suffix + f".tmp.{os.getpid()}")
|
|
821
824
|
tmp.write_text(rendered, encoding="utf-8")
|
|
822
825
|
tmp.replace(output_path)
|
|
@@ -25,6 +25,7 @@ from .report_synthesis_packet import (
|
|
|
25
25
|
verify_report_synthesis_packet_sources,
|
|
26
26
|
)
|
|
27
27
|
from .conformance import strip_stage_declaration_label
|
|
28
|
+
from .coverage_census import census_warning_rows, read_census_state
|
|
28
29
|
from .design_prep import DesignPrepError, materialize_design_prep_requests
|
|
29
30
|
from .phases.implementation_option_selection.validation import validate_blocked_answer_channel
|
|
30
31
|
from .phases.technical_verification.validation import validate_technical_verification_report
|
|
@@ -498,7 +499,7 @@ def _compose(
|
|
|
498
499
|
data["verificationScope"] = scope
|
|
499
500
|
data["agentActivity"] = activities
|
|
500
501
|
data["evidence"] = {"primary": _promoted_evidence(convergence_data)}
|
|
501
|
-
data["missingInformation"] =
|
|
502
|
+
data["missingInformation"] = _census_missing_information(project_root, manifest)
|
|
502
503
|
# 스냅샷 복사가 _attach_metadata 보다 먼저다 — frontmatter 가
|
|
503
504
|
# selectedDirectionRef.snapshotPath 를 읽는다.
|
|
504
505
|
_apply_selected_direction_snapshot(data, project_root, manifest)
|
|
@@ -509,6 +510,19 @@ def _compose(
|
|
|
509
510
|
return data
|
|
510
511
|
|
|
511
512
|
|
|
513
|
+
def _census_missing_information(
|
|
514
|
+
project_root: Path, manifest: Mapping[str, Any],
|
|
515
|
+
) -> list[dict[str, Any]]:
|
|
516
|
+
"""Coverage-census warnings are the only rows this field carries under the
|
|
517
|
+
v3 contract, so they number from `R-001`."""
|
|
518
|
+
state = read_census_state(project_root, manifest)
|
|
519
|
+
if state is None:
|
|
520
|
+
return []
|
|
521
|
+
_project_id, _task_group, task_id = _identity(manifest)
|
|
522
|
+
rows = census_warning_rows(state, ticket_id=task_id or "unknown")
|
|
523
|
+
return [{"id": f"R-{index:03d}", **row} for index, row in enumerate(rows, start=1)]
|
|
524
|
+
|
|
525
|
+
|
|
512
526
|
def _selected_direction_inputs(
|
|
513
527
|
project_root: Path, manifest: Mapping[str, Any]
|
|
514
528
|
) -> tuple[Path, Path]:
|
|
@@ -51,6 +51,7 @@ from typing import Any, Callable, Mapping, Sequence
|
|
|
51
51
|
from . import group_context
|
|
52
52
|
from .agent.activity import ActivityProjectionError, project_agent_activity
|
|
53
53
|
from .report_contract import apply_execution_roles
|
|
54
|
+
from .coverage_census import census_warnings, read_census_state
|
|
54
55
|
from .report_assembly import ReportAssemblyError, assemble_report
|
|
55
56
|
from .dispatch_state import (
|
|
56
57
|
DispatchError,
|
|
@@ -722,9 +723,25 @@ def run_finalize(
|
|
|
722
723
|
# closeout 이 실제로 건넬 명령. 표를 리드의 기억에 맡기지 않는다.
|
|
723
724
|
payload["nextCommand"] = closeout_command(payload)
|
|
724
725
|
payload["reportPaths"] = _closeout_report_paths(ctx)
|
|
726
|
+
for step in steps:
|
|
727
|
+
if step["name"] == "business-flow":
|
|
728
|
+
payload["businessProcess"] = step["result"]
|
|
729
|
+
census = _census_summary(ctx)
|
|
730
|
+
if census is not None:
|
|
731
|
+
payload["censusWarnings"] = census
|
|
725
732
|
return payload
|
|
726
733
|
|
|
727
734
|
|
|
735
|
+
def _census_summary(ctx: FinalizeContext) -> dict[str, Any] | None:
|
|
736
|
+
"""Unverdicted census cells per worker, for the lead's completion line."""
|
|
737
|
+
try:
|
|
738
|
+
manifest = load_owned_object(ctx.manifest_path, artifact="run manifest")
|
|
739
|
+
except JsonBoundaryError as exc:
|
|
740
|
+
return {"auditProblem": f"the run manifest could not be read: {exc}"}
|
|
741
|
+
state = read_census_state(ctx.project_root, manifest)
|
|
742
|
+
return census_warnings(state) if state is not None else None
|
|
743
|
+
|
|
744
|
+
|
|
728
745
|
def _execute_finalize_steps(
|
|
729
746
|
ctx: FinalizeContext, commands: Sequence[tuple[str, list[str]]],
|
|
730
747
|
before_step: Callable[[str], None] | None,
|
|
@@ -768,11 +785,34 @@ def _execute_finalize_steps(
|
|
|
768
785
|
first_failure = f"{name} failed with exit code {result.returncode}"
|
|
769
786
|
if name == STEP_VALIDATE_RUN and result.returncode == 0:
|
|
770
787
|
validated = True
|
|
788
|
+
if not first_failure:
|
|
789
|
+
business = _finalize_business_effects(ctx)
|
|
790
|
+
if business is not None:
|
|
791
|
+
steps.append({"name": "business-flow", "exitCode": 0, "result": business})
|
|
771
792
|
if validated and not first_failure:
|
|
772
793
|
_record_run_end(ctx.team_state_path)
|
|
773
794
|
return steps, first_failure
|
|
774
795
|
|
|
775
796
|
|
|
797
|
+
def _finalize_business_effects(ctx: FinalizeContext) -> dict[str, Any] | None:
|
|
798
|
+
from .business_flow.hooks import finalize_business_flow
|
|
799
|
+
|
|
800
|
+
result = None
|
|
801
|
+
try:
|
|
802
|
+
result = finalize_business_flow(ctx.project_root, _load_manifest(ctx.manifest_path), ctx.data_path)
|
|
803
|
+
if result is not None and result.get("executions"):
|
|
804
|
+
from .render_final_report import render_to_file
|
|
805
|
+
from .report_html.render import render_v2_html_view
|
|
806
|
+
from .report_html.models import HtmlRunMeta
|
|
807
|
+
|
|
808
|
+
render_to_file(ctx.data_path, ctx.markdown_path)
|
|
809
|
+
render_v2_html_view(ctx.data_path, run_meta=HtmlRunMeta(
|
|
810
|
+
ctx.task_key, ctx.task_type, ctx.seq, str(ctx.markdown_path)))
|
|
811
|
+
return result
|
|
812
|
+
except Exception as exc:
|
|
813
|
+
return {**result, "viewError": str(exc)} if result is not None else {"status": "failed", "error": str(exc), "executions": []}
|
|
814
|
+
|
|
815
|
+
|
|
776
816
|
def _finalize_recovery(ctx: FinalizeContext, result: Mapping[str, Any]) -> dict[str, Any]:
|
|
777
817
|
"""새 실행 대신 실패한 처리부터 재개할 명령과 원본 소유자 오류를 전달한다."""
|
|
778
818
|
order = V3_STEP_ORDER if ctx.report_contract_version == "3.0" else STEP_ORDER
|
|
@@ -226,6 +226,9 @@ def render_v2_html_view(
|
|
|
226
226
|
"css": (root / "html/assets/base.css").read_text(encoding="utf-8"),
|
|
227
227
|
"js": response_js + "\n" + base_js,
|
|
228
228
|
}
|
|
229
|
+
from ..business_flow.hooks import report_links
|
|
230
|
+
|
|
231
|
+
context["businessFlow"] = report_links(data_path)
|
|
229
232
|
output_path = _html_path(data_path)
|
|
230
233
|
document = env.get_template(route.template_name).render(**context)
|
|
231
234
|
document = inject_report_index(document, label=translate("base.contents"))
|
|
@@ -364,8 +364,7 @@ _SOURCE_FIELDS = (
|
|
|
364
364
|
_READ_CHUNK_BYTES = 16_000
|
|
365
365
|
_SHARED_TEXT_MIN_BYTES = 256
|
|
366
366
|
_ANALYSER_OPERATION_SECTIONS = frozenset({
|
|
367
|
-
"
|
|
368
|
-
"Worker interaction model", "Tooling — read-only MCP availability",
|
|
367
|
+
"Team contract", "Worker interaction model", "Tooling — read-only MCP availability",
|
|
369
368
|
"Run-scoped worker-resource lifecycle",
|
|
370
369
|
})
|
|
371
370
|
|