okstra 0.150.0 → 0.151.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/cli.md +2 -2
- package/docs/project-structure-overview.md +1 -1
- package/package.json +3 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +2 -1
- package/runtime/agents/workers/claude-worker.md +2 -1
- package/runtime/agents/workers/codex-worker.md +2 -1
- package/runtime/agents/workers/grok-worker.md +2 -1
- package/runtime/agents/workers/kimi-worker.md +2 -1
- package/runtime/agents/workers/report-writer-worker.md +9 -1
- package/runtime/bin/okstra-report-translate.py +56 -2
- package/runtime/prompts/launch.template.md +1 -1
- package/runtime/prompts/lead/convergence.md +16 -4
- package/runtime/prompts/lead/plan-body-verification.md +1 -1
- package/runtime/prompts/lead/report-writer.md +28 -16
- package/runtime/prompts/lead/team-contract.md +2 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
- package/runtime/prompts/profiles/final-verification.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +8 -7
- package/runtime/python/okstra_ctl/analysis_packet.py +1 -0
- package/runtime/python/okstra_ctl/convergence.py +15 -1
- package/runtime/python/okstra_ctl/dispatch_state.py +5 -1
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +7 -0
- package/runtime/python/okstra_ctl/report_finalize.py +27 -4
- package/runtime/python/okstra_ctl/report_translation.py +29 -0
- package/runtime/python/okstra_ctl/worker_prompt_body.py +4 -2
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +41 -1
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +3 -0
- package/runtime/templates/implementation-worker-preamble.md +12 -3
- package/runtime/templates/report-writer-prompt-preamble.md +5 -1
- package/runtime/templates/worker-prompt-preamble.md +12 -3
- package/runtime/validators/validate-run.py +142 -0
|
@@ -61,6 +61,10 @@ from okstra_ctl.mutation_probe import ( # noqa: E402
|
|
|
61
61
|
classify_reason,
|
|
62
62
|
)
|
|
63
63
|
from okstra_ctl.self_mock_signals import selfmock_path_key # noqa: E402
|
|
64
|
+
from okstra_ctl.report_translation import ( # noqa: E402
|
|
65
|
+
HANGUL_PROSE_LIMIT,
|
|
66
|
+
hangul_share,
|
|
67
|
+
)
|
|
64
68
|
from okstra_ctl.stage_citations import cited_stage_numbers # noqa: E402
|
|
65
69
|
from okstra_ctl.workflow import DEFAULT_NEXT_PHASE, PHASE_SEQUENCE # noqa: E402
|
|
66
70
|
from okstra_ctl.md_table import ( # noqa: E402
|
|
@@ -99,6 +103,7 @@ from okstra_ctl.worker_prompt_contract import ( # noqa: E402
|
|
|
99
103
|
PromptRecord,
|
|
100
104
|
validate_initial_prompt_records,
|
|
101
105
|
)
|
|
106
|
+
from okstra_ctl.worker_prompt_headers import EVIDENCE_LEDGER_HEADER # noqa: E402
|
|
102
107
|
from validate_analysis_report import validate_analysis_report # noqa: E402
|
|
103
108
|
from okstra_ctl.convergence_engine import ( # noqa: E402
|
|
104
109
|
grouped_input_digest,
|
|
@@ -2374,6 +2379,16 @@ def validate_report(
|
|
|
2374
2379
|
_WORKER_RESULT_BASENAME_RE = re.compile(
|
|
2375
2380
|
r"^(?P<worker>[a-z][a-z0-9-]*-worker)-(?P<task_type>[a-z][a-z-]*?)-(?P<seq>\d{3})\.md$"
|
|
2376
2381
|
)
|
|
2382
|
+
_EVIDENCE_READ_RE = re.compile(
|
|
2383
|
+
r"^- Evidence read: `(?P<path>[^`\n]+)`\s*$",
|
|
2384
|
+
re.MULTILINE,
|
|
2385
|
+
)
|
|
2386
|
+
_FILE_LINE_CITATION_RE = re.compile(
|
|
2387
|
+
r"`(?P<path>(?!https?://)[^`\n]+?):(?P<line>\d+(?:-\d+)?)`"
|
|
2388
|
+
)
|
|
2389
|
+
_EXTENSIONLESS_SOURCE_FILENAMES = frozenset(
|
|
2390
|
+
{"Dockerfile", "Justfile", "Makefile", "Procfile", "Rakefile"}
|
|
2391
|
+
)
|
|
2377
2392
|
|
|
2378
2393
|
_REPORT_BASENAME_SEQ_RE = re.compile(r"-(?P<seq>\d{3})(?:\.data)?\.(?:md|json)$")
|
|
2379
2394
|
|
|
@@ -2386,6 +2401,90 @@ def _report_run_seq(report_path: Path) -> str | None:
|
|
|
2386
2401
|
return match.group("seq") if match else None
|
|
2387
2402
|
|
|
2388
2403
|
|
|
2404
|
+
def _cited_file_paths(content: str) -> set[str]:
|
|
2405
|
+
paths: set[str] = set()
|
|
2406
|
+
for match in _FILE_LINE_CITATION_RE.finditer(content):
|
|
2407
|
+
path = match.group("path")
|
|
2408
|
+
if _looks_like_file_path(path):
|
|
2409
|
+
paths.add(path)
|
|
2410
|
+
return paths
|
|
2411
|
+
|
|
2412
|
+
|
|
2413
|
+
def _looks_like_file_path(path: str) -> bool:
|
|
2414
|
+
if (
|
|
2415
|
+
not path
|
|
2416
|
+
or path.startswith(("-", "$"))
|
|
2417
|
+
or any(char.isspace() for char in path)
|
|
2418
|
+
):
|
|
2419
|
+
return False
|
|
2420
|
+
if re.fullmatch(r"[0-9a-fA-F]{7,64}", path):
|
|
2421
|
+
return False
|
|
2422
|
+
return (
|
|
2423
|
+
"/" in path
|
|
2424
|
+
or "." in Path(path).name
|
|
2425
|
+
or Path(path).name in _EXTENSIONLESS_SOURCE_FILENAMES
|
|
2426
|
+
)
|
|
2427
|
+
|
|
2428
|
+
|
|
2429
|
+
def _audit_evidence_read_paths(content: str) -> set[str]:
|
|
2430
|
+
return {
|
|
2431
|
+
match.group("path")
|
|
2432
|
+
for match in _EVIDENCE_READ_RE.finditer(content)
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
|
|
2436
|
+
def _worker_prompt_path(
|
|
2437
|
+
report_path: Path,
|
|
2438
|
+
worker_role: str,
|
|
2439
|
+
task_type: str,
|
|
2440
|
+
seq: str,
|
|
2441
|
+
) -> Path:
|
|
2442
|
+
return (
|
|
2443
|
+
report_path.parent.parent
|
|
2444
|
+
/ "prompts"
|
|
2445
|
+
/ f"{worker_role}-prompt-{task_type}-{seq}.md"
|
|
2446
|
+
)
|
|
2447
|
+
|
|
2448
|
+
|
|
2449
|
+
def _validate_worker_evidence_read_ledger(
|
|
2450
|
+
*,
|
|
2451
|
+
report_path: Path,
|
|
2452
|
+
worker_role: str,
|
|
2453
|
+
task_type: str,
|
|
2454
|
+
seq: str,
|
|
2455
|
+
result_name: str,
|
|
2456
|
+
result_content: str,
|
|
2457
|
+
audit_path: Path,
|
|
2458
|
+
failures: list[str],
|
|
2459
|
+
) -> None:
|
|
2460
|
+
if worker_role == "report-writer-worker":
|
|
2461
|
+
return
|
|
2462
|
+
prompt_path = _worker_prompt_path(report_path, worker_role, task_type, seq)
|
|
2463
|
+
try:
|
|
2464
|
+
prompt_content = prompt_path.read_text(encoding="utf-8")
|
|
2465
|
+
except OSError:
|
|
2466
|
+
return
|
|
2467
|
+
if EVIDENCE_LEDGER_HEADER not in prompt_content.splitlines():
|
|
2468
|
+
return
|
|
2469
|
+
try:
|
|
2470
|
+
audit_content = audit_path.read_text(encoding="utf-8")
|
|
2471
|
+
except OSError as exc:
|
|
2472
|
+
failures.append(
|
|
2473
|
+
f"worker audit sidecar unreadable: {audit_path.name} ({exc})"
|
|
2474
|
+
)
|
|
2475
|
+
return
|
|
2476
|
+
|
|
2477
|
+
missing_paths = sorted(
|
|
2478
|
+
_cited_file_paths(result_content) - _audit_evidence_read_paths(audit_content)
|
|
2479
|
+
)
|
|
2480
|
+
for missing_path in missing_paths:
|
|
2481
|
+
failures.append(
|
|
2482
|
+
f"worker `{worker_role}` result `{result_name}` cites "
|
|
2483
|
+
f"`{missing_path}:line` without an Evidence read row for "
|
|
2484
|
+
f"`{missing_path}` in `{audit_path.name}`"
|
|
2485
|
+
)
|
|
2486
|
+
|
|
2487
|
+
|
|
2389
2488
|
def validate_worker_results_audit(
|
|
2390
2489
|
report_path: Path, task_type: str, failures: list[str]
|
|
2391
2490
|
) -> None:
|
|
@@ -2400,6 +2499,10 @@ def validate_worker_results_audit(
|
|
|
2400
2499
|
2. The matching audit sidecar exists at
|
|
2401
2500
|
`<worker>-audit-<task-type>-<seq>.md`. Missing sidecar means the
|
|
2402
2501
|
worker silently skipped the reading-confirmation step.
|
|
2502
|
+
3. For new prompts carrying the required-v1 marker, every canonical
|
|
2503
|
+
backticked `path:line` citation has a matching Evidence read row in
|
|
2504
|
+
that audit sidecar. Historical prompts without the marker retain the
|
|
2505
|
+
existence-only contract.
|
|
2403
2506
|
|
|
2404
2507
|
Scoped to this run's seq. `worker-results/` accumulates every run's
|
|
2405
2508
|
artifacts, so scanning the whole directory judged a run by files it did
|
|
@@ -2461,6 +2564,18 @@ def validate_worker_results_audit(
|
|
|
2461
2564
|
f"Confirmation block (one short line per input file). Workers "
|
|
2462
2565
|
f"write this in the same step as the main worker-results file."
|
|
2463
2566
|
)
|
|
2567
|
+
continue
|
|
2568
|
+
|
|
2569
|
+
_validate_worker_evidence_read_ledger(
|
|
2570
|
+
report_path=report_path,
|
|
2571
|
+
worker_role=worker_role,
|
|
2572
|
+
task_type=task_type,
|
|
2573
|
+
seq=seq,
|
|
2574
|
+
result_name=rel,
|
|
2575
|
+
result_content=content,
|
|
2576
|
+
audit_path=audit_path,
|
|
2577
|
+
failures=failures,
|
|
2578
|
+
)
|
|
2464
2579
|
|
|
2465
2580
|
|
|
2466
2581
|
def validate_team_state_usage(team_state: dict, failures: list[str]) -> None:
|
|
@@ -6316,6 +6431,32 @@ def _validate_improvement_discovery(
|
|
|
6316
6431
|
failures.append(f"improvement-discovery: {err}")
|
|
6317
6432
|
|
|
6318
6433
|
|
|
6434
|
+
def _validate_ssot_is_english(data: dict, failures: list[str]) -> None:
|
|
6435
|
+
"""Enforce: the final-report data.json is authored in English.
|
|
6436
|
+
|
|
6437
|
+
`meta.reportLanguage` names the language the human HTML renders in, and
|
|
6438
|
+
Phase 7's translator serves it from a sidecar. The data.json itself is the
|
|
6439
|
+
record every later phase, validator and agent reads, so a worker that
|
|
6440
|
+
authors it in the reader's language instead splits the record — and does so
|
|
6441
|
+
silently, because rendering, follow-up spawning and validation all succeed
|
|
6442
|
+
on it.
|
|
6443
|
+
|
|
6444
|
+
`report-finalize` runs the same check as its first step, before anything
|
|
6445
|
+
derives from the report. This is the second gate, for a report that reached
|
|
6446
|
+
validation by some other path.
|
|
6447
|
+
"""
|
|
6448
|
+
if not data:
|
|
6449
|
+
return
|
|
6450
|
+
share, length = hangul_share(data)
|
|
6451
|
+
if length and share >= HANGUL_PROSE_LIMIT:
|
|
6452
|
+
failures.append(
|
|
6453
|
+
f"final-report data.json was authored in Korean ({share:.0%} of its "
|
|
6454
|
+
f"prose, limit {HANGUL_PROSE_LIMIT:.0%}). The data.json is the "
|
|
6455
|
+
"English SSOT; meta.reportLanguage selects the human HTML's "
|
|
6456
|
+
"language and is served by the Phase 7 translator sidecar."
|
|
6457
|
+
)
|
|
6458
|
+
|
|
6459
|
+
|
|
6319
6460
|
def _validate_fix_cycle(run_manifest: dict, data: dict, failures: list[str]) -> None:
|
|
6320
6461
|
"""Enforce: when the run-manifest carries a fixCycleId, the final-report
|
|
6321
6462
|
data.json MUST contain a fixCycle block whose ``cycle`` matches it.
|
|
@@ -7206,6 +7347,7 @@ def main() -> int:
|
|
|
7206
7347
|
),
|
|
7207
7348
|
)
|
|
7208
7349
|
validation_data = report_data if isinstance(report_data, Mapping) else {}
|
|
7350
|
+
_validate_ssot_is_english(validation_data, failures)
|
|
7209
7351
|
_validate_fix_cycle(run_manifest, validation_data, failures)
|
|
7210
7352
|
validate_report(
|
|
7211
7353
|
report_path,
|