okstra 0.142.0 → 0.144.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/project-structure-overview.md +3 -0
- package/docs/task-process/error-analysis.md +9 -4
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +2 -1
- package/runtime/prompts/coding-preflight/overview.md +1 -1
- package/runtime/prompts/lead/context-loader.md +2 -2
- package/runtime/prompts/lead/convergence.md +5 -2
- package/runtime/prompts/lead/report-writer.md +4 -3
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +3 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +48 -2
- package/runtime/prompts/profiles/error-analysis.md +5 -1
- package/runtime/python/okstra_ctl/analysis_packet.py +28 -3
- package/runtime/python/okstra_ctl/brief_frontmatter.py +56 -0
- package/runtime/python/okstra_ctl/convergence_engine.py +66 -15
- package/runtime/python/okstra_ctl/mutation_probe.py +1263 -0
- package/runtime/python/okstra_ctl/run.py +81 -33
- package/runtime/python/okstra_ctl/schema_excerpt.py +5 -3
- package/runtime/python/okstra_ctl/self_mock_signals.py +183 -0
- package/runtime/python/okstra_ctl/wizard.py +2 -44
- package/runtime/schemas/final-report-v1.0.schema.json +99 -0
- package/runtime/templates/reports/final-report.template.md +26 -0
- package/runtime/templates/reports/i18n/en.json +20 -2
- package/runtime/templates/reports/i18n/ko.json +20 -2
- package/runtime/validators/detect_self_mock.py +220 -0
- package/runtime/validators/validate-brief.py +5 -1
- package/runtime/validators/validate-run.py +763 -36
|
@@ -78,7 +78,9 @@ def seed_working_state(grouped_input: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
78
78
|
state["stopReason"] = "auto-disabled"
|
|
79
79
|
return state
|
|
80
80
|
|
|
81
|
-
findings, queue = _parse_groups(
|
|
81
|
+
findings, queue = _parse_groups(
|
|
82
|
+
source.get("groups"), analysis_workers, adversarial=config["adversarial"]
|
|
83
|
+
)
|
|
82
84
|
state["findings"] = findings
|
|
83
85
|
state["queueFindingIds"] = queue
|
|
84
86
|
return state
|
|
@@ -206,6 +208,37 @@ def classify_adversarial_round(
|
|
|
206
208
|
return "partial-consensus"
|
|
207
209
|
|
|
208
210
|
|
|
211
|
+
def _round_has_counter_evidence(
|
|
212
|
+
votes: Mapping[str, Mapping[str, Any]],
|
|
213
|
+
) -> bool:
|
|
214
|
+
return any(
|
|
215
|
+
vote.get("verdict") == "disagree"
|
|
216
|
+
and vote.get("disagreeBasis") == "counter-evidence"
|
|
217
|
+
for vote in votes.values()
|
|
218
|
+
if isinstance(vote, Mapping)
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _classify_adversarial_history(
|
|
223
|
+
rounds: list[Mapping[str, Any]],
|
|
224
|
+
) -> str | None:
|
|
225
|
+
"""Classify adversarial rounds without erasing earlier counter-evidence."""
|
|
226
|
+
counter_evidence_seen = False
|
|
227
|
+
for row in rounds:
|
|
228
|
+
votes = row.get("votes") if isinstance(row, Mapping) else None
|
|
229
|
+
if not isinstance(votes, Mapping):
|
|
230
|
+
continue
|
|
231
|
+
counter_evidence_seen = (
|
|
232
|
+
counter_evidence_seen or _round_has_counter_evidence(votes)
|
|
233
|
+
)
|
|
234
|
+
classification = classify_adversarial_round(votes)
|
|
235
|
+
if classification == "worker-unique":
|
|
236
|
+
return classification
|
|
237
|
+
if classification is not None and not counter_evidence_seen:
|
|
238
|
+
return classification
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
|
|
209
242
|
def apply_round_results(
|
|
210
243
|
state: Mapping[str, Any],
|
|
211
244
|
plan: Mapping[str, Any],
|
|
@@ -251,7 +284,7 @@ def apply_round_results(
|
|
|
251
284
|
{"round": expected_plan["round"], "votes": round_votes}
|
|
252
285
|
)
|
|
253
286
|
classification = (
|
|
254
|
-
|
|
287
|
+
_classify_adversarial_history(finding["rounds"])
|
|
255
288
|
if adversarial
|
|
256
289
|
else classify_collaborative_round(round_votes)
|
|
257
290
|
)
|
|
@@ -1321,20 +1354,21 @@ def _expected_final_classification(
|
|
|
1321
1354
|
rounds = finding.get("rounds")
|
|
1322
1355
|
if not isinstance(rounds, list) or not rounds:
|
|
1323
1356
|
return None
|
|
1357
|
+
if adversarial:
|
|
1358
|
+
try:
|
|
1359
|
+
return _classify_adversarial_history(rounds) or "contested"
|
|
1360
|
+
except ConvergenceContractError:
|
|
1361
|
+
return "contested"
|
|
1324
1362
|
for row in rounds:
|
|
1325
1363
|
if not isinstance(row, Mapping) or not isinstance(row.get("votes"), Mapping):
|
|
1326
1364
|
continue
|
|
1327
1365
|
try:
|
|
1328
|
-
resolved = (
|
|
1329
|
-
classify_adversarial_round(row["votes"])
|
|
1330
|
-
if adversarial
|
|
1331
|
-
else classify_collaborative_round(row["votes"])
|
|
1332
|
-
)
|
|
1366
|
+
resolved = classify_collaborative_round(row["votes"])
|
|
1333
1367
|
except ConvergenceContractError:
|
|
1334
1368
|
continue
|
|
1335
1369
|
if resolved is not None:
|
|
1336
1370
|
return resolved
|
|
1337
|
-
return
|
|
1371
|
+
return _final_collaborative_classification(finding)
|
|
1338
1372
|
|
|
1339
1373
|
|
|
1340
1374
|
def _validate_round_ledger_counts(
|
|
@@ -1347,7 +1381,7 @@ def _validate_round_ledger_counts(
|
|
|
1347
1381
|
if not isinstance(history_row, Mapping):
|
|
1348
1382
|
continue
|
|
1349
1383
|
ledgers = _round_ledgers(findings, round_number)
|
|
1350
|
-
resolved = _resolved_ledger_count(
|
|
1384
|
+
resolved = _resolved_ledger_count(findings, round_number, adversarial)
|
|
1351
1385
|
expected = (len(ledgers), resolved, len(ledgers) - resolved)
|
|
1352
1386
|
actual = (
|
|
1353
1387
|
history_row.get("inputQueueSize"),
|
|
@@ -1381,17 +1415,32 @@ def _round_ledgers(
|
|
|
1381
1415
|
|
|
1382
1416
|
|
|
1383
1417
|
def _resolved_ledger_count(
|
|
1384
|
-
|
|
1418
|
+
findings: list[Any],
|
|
1419
|
+
round_number: int,
|
|
1385
1420
|
adversarial: bool,
|
|
1386
1421
|
) -> int:
|
|
1387
1422
|
resolved = 0
|
|
1388
|
-
for
|
|
1389
|
-
|
|
1423
|
+
for finding in findings:
|
|
1424
|
+
rounds = finding.get("rounds") if isinstance(finding, Mapping) else None
|
|
1425
|
+
if not isinstance(rounds, list):
|
|
1426
|
+
continue
|
|
1427
|
+
current_rounds = [
|
|
1428
|
+
row
|
|
1429
|
+
for row in rounds
|
|
1430
|
+
if isinstance(row, Mapping)
|
|
1431
|
+
and isinstance(row.get("round"), int)
|
|
1432
|
+
and row["round"] <= round_number
|
|
1433
|
+
]
|
|
1434
|
+
current = next(
|
|
1435
|
+
(row for row in current_rounds if row.get("round") == round_number),
|
|
1436
|
+
None,
|
|
1437
|
+
)
|
|
1438
|
+
votes = current.get("votes") if isinstance(current, Mapping) else None
|
|
1390
1439
|
if not isinstance(votes, Mapping):
|
|
1391
1440
|
continue
|
|
1392
1441
|
try:
|
|
1393
1442
|
classification = (
|
|
1394
|
-
|
|
1443
|
+
_classify_adversarial_history(current_rounds)
|
|
1395
1444
|
if adversarial
|
|
1396
1445
|
else classify_collaborative_round(votes)
|
|
1397
1446
|
)
|
|
@@ -1486,7 +1535,7 @@ def _validate_no_reappearance_after_resolution(
|
|
|
1486
1535
|
continue
|
|
1487
1536
|
try:
|
|
1488
1537
|
classification = (
|
|
1489
|
-
|
|
1538
|
+
_classify_adversarial_history(rounds[: index + 1])
|
|
1490
1539
|
if adversarial
|
|
1491
1540
|
else classify_collaborative_round(votes)
|
|
1492
1541
|
)
|
|
@@ -1842,6 +1891,8 @@ def _parse_workers(value: Any) -> list[dict[str, str]]:
|
|
|
1842
1891
|
def _parse_groups(
|
|
1843
1892
|
value: Any,
|
|
1844
1893
|
analysis_workers: list[str],
|
|
1894
|
+
*,
|
|
1895
|
+
adversarial: bool,
|
|
1845
1896
|
) -> tuple[list[dict[str, Any]], list[str]]:
|
|
1846
1897
|
if not isinstance(value, list):
|
|
1847
1898
|
raise ConvergenceContractError("groups must be an array")
|
|
@@ -1855,7 +1906,7 @@ def _parse_groups(
|
|
|
1855
1906
|
raise ConvergenceContractError(f"duplicate findingId: {finding_id}")
|
|
1856
1907
|
seen_ids.add(finding_id)
|
|
1857
1908
|
finding, source_workers = _parse_group(group, index, analysis_workers)
|
|
1858
|
-
if len(source_workers) >= 2:
|
|
1909
|
+
if len(source_workers) >= 2 and not adversarial:
|
|
1859
1910
|
finding["classification"] = "full-consensus"
|
|
1860
1911
|
else:
|
|
1861
1912
|
queue.append(finding_id)
|