opencode-swarm 7.87.2 → 7.88.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.
Files changed (28) hide show
  1. package/.opencode/skills/swarm-pr-review/SKILL.md +304 -9
  2. package/README.md +1 -0
  3. package/dist/background/candidate-parser.d.ts +189 -0
  4. package/dist/background/candidate-sidecar-store.d.ts +56 -0
  5. package/dist/cli/{config-doctor-6h64pn8n.js → config-doctor-jzbgpbdh.js} +2 -2
  6. package/dist/cli/{guardrail-explain-9fngqx80.js → guardrail-explain-sw5bjxtk.js} +6 -6
  7. package/dist/cli/{guardrail-log-eegabqcp.js → guardrail-log-c7egm5km.js} +3 -3
  8. package/dist/cli/{index-q9h0wb04.js → index-0asbrmdx.js} +4 -0
  9. package/dist/cli/{index-s8bj492g.js → index-32axfg6h.js} +23 -7
  10. package/dist/cli/{index-1cb4wxnm.js → index-819xp49y.js} +1 -1
  11. package/dist/cli/{index-gjdq4na6.js → index-dkytd370.js} +7 -7
  12. package/dist/cli/{index-f41fa3f0.js → index-fwb5f2gr.js} +2 -2
  13. package/dist/cli/{index-5hvbw5xh.js → index-g00qm2gf.js} +1 -1
  14. package/dist/cli/{index-6qkwgdsg.js → index-jch711dq.js} +246 -169
  15. package/dist/cli/{index-5vpe6vq9.js → index-vjsr9bqt.js} +1 -1
  16. package/dist/cli/index.js +5 -5
  17. package/dist/cli/{schema-84146tvk.js → schema-vb6jkxgg.js} +1 -1
  18. package/dist/cli/{skill-generator-3pvpk4y2.js → skill-generator-kz4q8e49.js} +3 -1
  19. package/dist/hooks/knowledge-reader.d.ts +2 -0
  20. package/dist/hooks/knowledge-types.d.ts +3 -0
  21. package/dist/index.js +1640 -797
  22. package/dist/services/skill-generator.d.ts +10 -0
  23. package/dist/services/skill-improver.d.ts +25 -1
  24. package/dist/tools/index.d.ts +1 -0
  25. package/dist/tools/manifest.d.ts +1 -0
  26. package/dist/tools/parse-lane-candidates.d.ts +2 -0
  27. package/dist/tools/tool-metadata.d.ts +4 -0
  28. package/package.json +1 -1
@@ -78,6 +78,8 @@ The orchestrator may:
78
78
  - determine scope,
79
79
  - build or request the context pack,
80
80
  - launch explorers and triggered micro-lanes,
81
+ - extract candidates from lane artifacts via `parse_lane_candidates` or equivalent parser,
82
+ - filter, group, and chunk candidates for reviewer dispatch,
81
83
  - route candidates to reviewers,
82
84
  - route reviewer-confirmed findings to critics,
83
85
  - group validated findings,
@@ -88,7 +90,8 @@ The orchestrator MUST NOT:
88
90
  - re-read a candidate's target code to decide if it is valid,
89
91
  - silently downgrade or discard an explorer candidate,
90
92
  - treat tool output as a confirmed finding,
91
- - report a finding that no reviewer validated.
93
+ - report a finding that no reviewer validated,
94
+ - classify or judge candidates based on preview text alone — always use the structured parser output.
92
95
 
93
96
  If the orchestrator catches itself validating code, it must stop and delegate validation to a reviewer subagent.
94
97
 
@@ -495,9 +498,43 @@ Launch all base lanes with `dispatch_lanes_async` when available. Pass the six l
495
498
 
496
499
  Before Phase 4 or synthesis, call `collect_lane_results` with `wait: true` for the base-lane batch and treat the collected `lane_results` as the join barrier. Missing, stale, cancelled, or failed base lanes are explicit review coverage gaps. If `dispatch_lanes_async` is unavailable, use blocking `dispatch_lanes`; if that is also unavailable, simulate isolated passes. Do not let one lane's conclusions bias another lane, and record unavailable deterministic dispatch in the validation gate.
497
500
 
498
- When any collected or blocking `lane_results[]` item has `output_ref`, treat `output` as a preview only. Call `retrieve_lane_output` and consume the full artifact before extracting candidates, deciding that a lane produced no candidates, or routing work to reviewers. If a lane has `output_truncated: true`, `output_degraded: true`, `transcript_incomplete: true`, or no usable `output_ref`, record an explicit coverage gap and re-dispatch a narrower lane or mark affected candidates/coverage UNVERIFIED; never infer candidate absence from a preview.
499
-
500
- **lane id uniqueness for parallel dispatches:** When re-dispatching failed or re-running explorer lanes, every `dispatch_lanes_async` or `dispatch_lanes` lane `id` MUST be unique within that dispatch batch and should include lane and attempt suffixes (e.g. `pr_review_explore_lane1_attempt2`). Never reuse an id in the same batch unless intentionally replacing that exact lane before dispatch.
501
+ ### Candidate extraction via parser
502
+
503
+ After `collect_lane_results` returns for base lanes, process each lane result
504
+ that carries an `output_ref`. The orchestrator MUST use the candidate parser
505
+ rather than preview-text extraction:
506
+
507
+ 1. For each `output_ref` (or batched), call `parse_lane_candidates` (or the
508
+ internal `parseAndPersist` module function) with `output_ref` and `producer`
509
+ flags; the parser auto-detects the format family per row. The parser reads
510
+ the full artifact from disk (no preview truncation issue) and returns
511
+ structured `ParseResultWithSidecar` records.
512
+ 2. Filter the returned `candidates[]` array by `producer: "swarm-pr-review"` and
513
+ the relevant `row_format_family` (e.g., `base_explorer` for base lanes,
514
+ `micro_lane` for micro-lanes). Filtering happens on the parsed results, NOT
515
+ on the tool input.
516
+ 3. Group the filtered candidates into reviewer-sized chunks:
517
+ - by file area (group by the directory or module of the `file_line` field),
518
+ - by category (group by the `category` field),
519
+ - by count (target max 50 candidates per chunk; smaller chunks are fine).
520
+ 4. Dispatch reviewer lanes (one per chunk) with bounded in-context candidate
521
+ lists. Each reviewer lane receives only the candidates from its assigned
522
+ chunk.
523
+
524
+ If a lane has `output_degraded: true`, `transcript_incomplete: true`, or no usable `output_ref`, record an explicit
525
+ coverage gap and re-dispatch a narrower lane or mark affected candidates
526
+ UNVERIFIED. Never infer candidate absence from a preview.
527
+
528
+ **Fallback convention:** If the parser is unavailable, the explorer MAY emit
529
+ `[CANDIDATE]` rows in the lane output as a fallback convention (see the
530
+ Explorer Prompt Template at the end of this skill), but the orchestrator
531
+ SHOULD use the parser as the primary extraction mechanism.
532
+
533
+ **lane id uniqueness for parallel dispatches:** When re-dispatching failed or
534
+ re-running explorer lanes, every `dispatch_lanes_async` or `dispatch_lanes`
535
+ lane `id` MUST be unique within that dispatch batch and should include lane and
536
+ attempt suffixes (e.g., `pr_review_explore_lane1_attempt2`). Never reuse an id
537
+ in the same batch unless intentionally replacing that exact lane before dispatch.
501
538
 
502
539
  Explorers optimize for recall. Over-reporting is expected. Explorers produce candidates only.
503
540
 
@@ -507,7 +544,7 @@ Explorers optimize for recall. Over-reporting is expected. Explorers produce can
507
544
  | Lane 2: Security and trust boundaries | Injection, authz/authn bypass, SSRF, path traversal, secret exposure, unsafe deserialization, prompt injection | untrusted input sources, sanitization, credential handling, permission boundary, private network access, output escaping |
508
545
  | Lane 3: Dependencies and deployment safety | Import changes, version bumps, lockfile drift, breaking APIs, package scripts, runtime assumptions | lockfile consistency, new transitive deps, Node/Bun/runtime compatibility, platform assumptions, license red flags |
509
546
  | Lane 4: Docs, intent, and drift | PR claims vs implementation, docs mismatch, migration/changelog gaps, stale examples | obligation mapping, changed behavior not documented, docs promising behavior not implemented |
510
- | Lane 5: Tests and falsifiability | Weak assertions, missing edge tests, flaky patterns, mock leakage, fixture drift | assertion strength, tautology patterns (`expect(true).toBe(true)`, `expect(res).toBeDefined()` without further checks, `assertDoesNotThrow` wrapping trivial code), negative paths, isolation, deterministic timing, cross-platform path coverage |
547
+ | Lane 5: Tests and falsifiability | Weak assertions, missing edge tests, flaky patterns, mock leakage, fixture drift | assertion strength, tautology patterns (`expect(true).toBe(true)`, `expect(res).toBeDefined()` without further checks), `assertDoesNotThrow` wrapping trivial code), negative paths, isolation, deterministic timing, cross-platform path coverage |
511
548
  | Lane 6: Performance and architecture | Complexity regressions, memory leaks, over-coupling, inefficient graph scans, global mutable state | algorithmic deltas, caching, resource lifecycle, state ownership, architectural boundary violations |
512
549
 
513
550
  ### Explorer context contract
@@ -523,12 +560,19 @@ Every explorer must inspect or explicitly mark unavailable:
523
560
  7. relevant Swarm knowledge/evidence entries, if present.
524
561
  8. the commit range to analyze (`base_ref..head_ref`),
525
562
 
526
- Explorer output format:
563
+ ### Explorer output format
564
+
565
+ Explorers emit structured candidate records. The parser reads the full lane
566
+ artifact and extracts these records. The canonical record shape is:
527
567
 
528
568
  ```text
529
569
  [CANDIDATE] | candidate_id | lane | severity | category | file:line | claim | evidence_summary | impact_context | confidence: LOW/MEDIUM/HIGH
530
570
  ```
531
571
 
572
+ The parser normalizes this into a structured `candidates[]` array. If the
573
+ parser is unavailable, the explorer MAY emit the `[CANDIDATE]` row format
574
+ directly in the lane output as a fallback convention.
575
+
532
576
  Explorers must not use `CONFIRMED`, `DISPROVED`, or `PRE_EXISTING`.
533
577
 
534
578
  ---
@@ -537,7 +581,7 @@ Explorers must not use `CONFIRMED`, `DISPROVED`, or `PRE_EXISTING`.
537
581
 
538
582
  After `collect_lane_results` returns for base lanes, inspect the context pack risk triggers. Launch focused micro-lanes for triggered categories only, using `dispatch_lanes_async` again when more than one read-only micro-lane is needed. Collect every micro-lane batch with `wait: true` before reviewer classification. Do not launch irrelevant micro-lanes.
539
583
 
540
- Apply the same `output_ref` rule to micro-lanes: retrieve full output before candidate routing, and treat degraded or incomplete lane artifacts as UNVERIFIED coverage rather than as clean negative evidence.
584
+ Apply the same parser-based extraction to micro-lanes: call `parse_lane_candidates` on each micro-lane `output_ref` (filter the returned `candidates[]` array by `row_format_family === "micro_lane"` after parsing), and treat degraded or incomplete lane artifacts as UNVERIFIED coverage rather than as clean negative evidence.
541
585
 
542
586
  Each micro-lane receives:
543
587
 
@@ -547,7 +591,8 @@ Each micro-lane receives:
547
591
  - relevant deterministic signals,
548
592
  - related historical knowledge with quarantine/staleness status,
549
593
  - expected invariants,
550
- - output format as `[CANDIDATE]` only.
594
+ - structured candidate output (parser-extracted). If the parser is unavailable,
595
+ the micro-lane MAY emit `[CANDIDATE]` rows as a fallback convention.
551
596
 
552
597
  ### Swarm plugin risk trigger map
553
598
 
@@ -596,7 +641,12 @@ Verifier output is advisory until incorporated by the independent reviewer or cr
596
641
 
597
642
  ## Phase 6: Independent Reviewer Confirmation
598
643
 
599
- Route candidates to reviewer subagents. The reviewer must re-read the candidate's file:line evidence and relevant context pack entries directly.
644
+ Route candidates to reviewer subagents. The orchestrator routes candidates
645
+ in bounded chunks produced by the parser-based extraction in Phase 3-4. Each
646
+ reviewer lane receives a bounded list of candidates from a single chunk — by
647
+ file area, category, or count — not the full candidate set. The reviewer must
648
+ re-read the candidate's file:line evidence and relevant context pack entries
649
+ directly.
600
650
 
601
651
  ### Noise budget and universal validation
602
652
 
@@ -813,6 +863,245 @@ Update the verdict only after re-verifying all previously blocking findings.
813
863
 
814
864
  ---
815
865
 
866
+ ## Dry-Run: Parser-Based Candidate Extraction
867
+
868
+ This section demonstrates the new parser-based extraction path end-to-end
869
+ using synthetic data. It is concrete enough to implement the same pattern in
870
+ another skill.
871
+
872
+ ### Scenario
873
+
874
+ A PR review has dispatched six base explorer lanes via `dispatch_lanes_async`.
875
+ The batch completed and `collect_lane_results` returned:
876
+
877
+ ```json
878
+ {
879
+ "batch_id": "batch-a1b2c3",
880
+ "lane_results": [
881
+ {
882
+ "lane_id": "pr_review_lane1_correctness",
883
+ "status": "completed",
884
+ "output_ref": ".swarm/lane-results/batch-a1b2c3/lane-1/out-abc123.json",
885
+ "output_degraded": false
886
+ },
887
+ {
888
+ "lane_id": "pr_review_lane2_security",
889
+ "status": "completed",
890
+ "output_ref": ".swarm/lane-results/batch-a1b2c3/lane-2/out-def456.json",
891
+ "output_degraded": false
892
+ }
893
+ ]
894
+ }
895
+ ```
896
+
897
+ ### Step 1 — Call the parser
898
+
899
+ The orchestrator calls `parse_lane_candidates` for each `output_ref`:
900
+
901
+ ```json
902
+ {
903
+ "tool": "parse_lane_candidates",
904
+ "arguments": {
905
+ "output_ref": ".swarm/lane-results/batch-a1b2c3/lane-1/out-abc123.json",
906
+ "producer": "swarm-pr-review"
907
+ }
908
+ }
909
+ ```
910
+
911
+ ### Step 2 — Structured response
912
+
913
+ The parser returns a `ParseResultWithSidecar`. On success, `error` and `error_code` are absent:
914
+
915
+ ```json
916
+ {
917
+ "candidates": [
918
+ {
919
+ "record_type": "candidate",
920
+ "row_format_family": "base_explorer",
921
+ "row_format_version": 1,
922
+ "record_version": { "major": 1, "minor": 0 },
923
+ "source_output_ref": ".swarm/lane-results/batch-a1b2c3/lane-1/out-abc123.json",
924
+ "source_batch_id": "B-2025-06-22-001",
925
+ "source_lane_id": "explorer-1",
926
+ "source_agent": "paid_explorer",
927
+ "source_digest": "sha256:abc123def456...",
928
+ "extracted_from_partial_source": false,
929
+ "sessionId": "ses_01HXYZ...",
930
+ "parentSessionId": "ses_01HABC...",
931
+ "producer": "swarm-pr-review",
932
+ "candidate_id": "C-001",
933
+ "lane": "Lane 1: Correctness and edge cases",
934
+ "micro_lane": null,
935
+ "severity": "HIGH",
936
+ "category": "null-safety",
937
+ "file_line": "src/utils/cache.ts:142",
938
+ "claim": "Uncached getter may return undefined on cold start",
939
+ "evidence_summary": "The `getCached` function returns `cache[key]` without a fallback when the cache is empty.",
940
+ "impact_context": "Downstream callers in `src/handlers/*.ts` expect a defined value and call `.toString()` directly.",
941
+ "invariant_violated": null,
942
+ "confidence": "HIGH"
943
+ },
944
+ {
945
+ "record_type": "candidate",
946
+ "row_format_family": "base_explorer",
947
+ "row_format_version": 1,
948
+ "record_version": { "major": 1, "minor": 0 },
949
+ "source_output_ref": ".swarm/lane-results/batch-a1b2c3/lane-1/out-abc123.json",
950
+ "source_batch_id": "B-2025-06-22-001",
951
+ "source_lane_id": "explorer-1",
952
+ "source_agent": "paid_explorer",
953
+ "source_digest": "sha256:abc123def456...",
954
+ "extracted_from_partial_source": false,
955
+ "sessionId": "ses_01HXYZ...",
956
+ "parentSessionId": "ses_01HABC...",
957
+ "producer": "swarm-pr-review",
958
+ "candidate_id": "C-002",
959
+ "lane": "Lane 1: Correctness and edge cases",
960
+ "micro_lane": null,
961
+ "severity": "MEDIUM",
962
+ "category": "async-ordering",
963
+ "file_line": "src/services/queue.ts:88",
964
+ "claim": "Race between `drain` and `processNext` may drop items",
965
+ "evidence_summary": "`drain` sets `active = false` before awaiting `processNext`, which also checks `active`.",
966
+ "impact_context": "Items submitted during the drain window are silently dropped.",
967
+ "invariant_violated": null,
968
+ "confidence": "MEDIUM"
969
+ }
970
+ ],
971
+ "invocation_envelope": {
972
+ "record_type": "invocation",
973
+ "source_output_ref": ".swarm/lane-results/batch-a1b2c3/lane-1/out-abc123.json",
974
+ "source_batch_id": "B-2025-06-22-001",
975
+ "source_lane_id": "explorer-1",
976
+ "source_agent": "paid_explorer",
977
+ "source_digest": "sha256:abc123def456...",
978
+ "row_format_version": 1,
979
+ "record_version": { "major": 1, "minor": 0 },
980
+ "sessionId": "ses_01HXYZ...",
981
+ "parentSessionId": "ses_01HABC...",
982
+ "producer": "swarm-pr-review",
983
+ "produced_at": "2025-06-22T14:30:00.000Z",
984
+ "format_families_detected": ["base_explorer"],
985
+ "candidate_count": 2,
986
+ "parse_errors": 2,
987
+ "malformed_rows": 0
988
+ },
989
+ "diagnostics": {
990
+ "candidate_count": 2,
991
+ "parse_errors": 2,
992
+ "parse_error_details": [
993
+ {
994
+ "row_index": 0,
995
+ "field": "row",
996
+ "message": "Both format-family discriminators present; defaulting to base_explorer"
997
+ },
998
+ {
999
+ "row_index": 1,
1000
+ "field": "row",
1001
+ "message": "Both format-family discriminators present; defaulting to base_explorer"
1002
+ }
1003
+ ],
1004
+ "malformed_rows": 0,
1005
+ "duplicate_id_count": 0,
1006
+ "duplicate_id_warnings": [],
1007
+ "degraded_source_count": 0,
1008
+ "incomplete_source_count": 0,
1009
+ "format_families_detected": ["base_explorer"]
1010
+ }
1011
+ }
1012
+ ```
1013
+ > **Note**: `parse_errors: 2` reflects FR-017/SC-017 position-based detection: when a `[CANDIDATE]` row has both `evidence_summary` and `impact_context` populated, the parser emits a `parse_error_details` entry per row with `field: "row"` and `message: "Both format-family discriminators present; defaulting to base_explorer"`. This is documented behavior, not a parser bug. To get `parse_errors: 0` with the row format, leave one of the two fields empty; to silence the warning entirely, emit structured JSON candidate records.
1014
+
1015
+ On refusal (e.g. `output_ref` does not exist), `error` and `error_code` are present; `candidates` is `[]`; `invocation_envelope` and `diagnostics` are populated with empty fields for traceability:
1016
+
1017
+ ```json
1018
+ {
1019
+ "error": "Artifact reference not found in store",
1020
+ "error_code": "ref-not-found",
1021
+ "candidates": [],
1022
+ "invocation_envelope": {
1023
+ "record_type": "invocation",
1024
+ "source_output_ref": ".swarm/lane-results/batch-a1b2c3/lane-1/missing.json",
1025
+ "source_batch_id": "",
1026
+ "source_lane_id": "",
1027
+ "source_agent": "",
1028
+ "source_digest": "",
1029
+ "row_format_version": 1,
1030
+ "record_version": { "major": 1, "minor": 0 },
1031
+ "produced_at": "2025-06-22T14:30:00.000Z",
1032
+ "format_families_detected": [],
1033
+ "candidate_count": 0,
1034
+ "parse_errors": 0,
1035
+ "malformed_rows": 0
1036
+ },
1037
+ "diagnostics": {
1038
+ "candidate_count": 0,
1039
+ "parse_errors": 0,
1040
+ "parse_error_details": [],
1041
+ "malformed_rows": 0,
1042
+ "duplicate_id_count": 0,
1043
+ "duplicate_id_warnings": [],
1044
+ "degraded_source_count": 0,
1045
+ "incomplete_source_count": 0,
1046
+ "format_families_detected": []
1047
+ }
1048
+ }
1049
+ ```
1050
+
1051
+ ### Step 3 — Filter and group
1052
+
1053
+ The orchestrator filters the returned `candidates[]` array by `producer: "swarm-pr-review"` and `row_format_family` (e.g. `base_explorer` or `micro_lane`), then groups
1054
+ the candidates. In this synthetic example, the two candidates above are grouped
1055
+ by file area:
1056
+
1057
+ - **Chunk A — `src/utils/`** (1 candidate): C-001
1058
+ - **Chunk B — `src/services/`** (1 candidate): C-002
1059
+
1060
+ If there were more candidates, the orchestrator would also group by category
1061
+ (e.g., `null-safety`, `async-ordering`) and cap each chunk at 50 candidates.
1062
+
1063
+ ### Step 4 — Dispatch reviewer lanes
1064
+
1065
+ The orchestrator dispatches one reviewer lane per chunk:
1066
+
1067
+ ```text
1068
+ You are the independent reviewer. Validate only the candidates assigned below.
1069
+ Do not search for new issues except where needed to validate reachability or
1070
+ mitigation. Do not trust explorer severity.
1071
+
1072
+ Context pack summary:
1073
+ - scope: ...
1074
+ - obligations: ...
1075
+ - impact cone: ...
1076
+ - deterministic signals: ...
1077
+ - relevant Swarm artifacts / knowledge: ...
1078
+ - base_ref: <commit SHA of base branch>
1079
+ - head_ref: <commit SHA of PR head branch>
1080
+
1081
+ Candidates (Chunk A — src/utils/):
1082
+ - C-001 | HIGH | null-safety | src/utils/cache.ts:142 | Uncached getter may return undefined on cold start
1083
+
1084
+ For each candidate, return:
1085
+ [REVIEWED] | candidate_id | CONFIRMED/DISPROVED/UNVERIFIED/PRE_EXISTING | evidence_type | final_severity | introduced_by_pr | file:line | rationale | falsification_probe | reviewer_id
1086
+
1087
+ You must check caller context, reachability, schema/middleware/framework mitigations, state-machine constraints, test coverage, PR-introducedness, and severity.
1088
+
1089
+ IMPORTANT: If a finding claims behavior is "new" or "introduced by the PR", you MUST read the equivalent code on the base branch (git show <base_ref>:<file>) to verify it was not present before. A reviewer claim of "this is new" is invalid without base-branch evidence. Do not compare the new code to an idealized baseline — compare it to what actually existed on the base branch at the time of the PR.
1090
+ ```
1091
+
1092
+ ### Key invariants
1093
+
1094
+ - The parser reads the **full artifact**, not a preview. Truncation in the
1095
+ `dispatch_lanes` preview does not affect candidate extraction.
1096
+ - The orchestrator never classifies candidates — it only filters, groups, and
1097
+ routes them.
1098
+ - Each reviewer receives a bounded chunk. A chunk with more than 50 candidates
1099
+ is split before dispatch.
1100
+ - The `invocation_envelope` in the parser response provides audit provenance
1101
+ for every extracted candidate.
1102
+
1103
+ ---
1104
+
816
1105
  # Council Mode Workflow
817
1106
 
818
1107
  Council mode is opt-in only and adversarial.
@@ -1116,4 +1405,10 @@ Return:
1116
1405
  [CANDIDATE] | candidate_id | lane | severity | category | file:line | claim | evidence_summary | impact_context | confidence
1117
1406
  ```
1118
1407
 
1408
+ The orchestrator extracts candidates from the full lane artifact via
1409
+ `parse_lane_candidates` as the primary mechanism. The `[CANDIDATE]` row
1410
+ format above is a fallback convention for environments where the parser is
1411
+ unavailable. Explorers should still emit structured records regardless of
1412
+ whether the parser is present.
1413
+
1119
1414
  Do not let speed degrade validation quality.
package/README.md CHANGED
@@ -800,6 +800,7 @@ Every candidate passes a 3-gate pipeline before entering quarantine:
800
800
  | mutation_test | Applies LLM-generated mutation patches to source files and runs tests to measure kill rate; verdict is pass/warn/fail based on configurable thresholds; used by the mutation_test gate (opt-in, off by default) |
801
801
  | generate_mutants | Architect-only: generates LLM-based mutation patches (5–10 per function across 6 types: off-by-one, null substitution, operator swap, guard removal, branch swap, side-effect deletion) for direct consumption by the mutation_test tool; returns SKIP verdict on LLM failure rather than throwing |
802
802
  | write_mutation_evidence | Architect-only: writes mutation gate results atomically to `.swarm/evidence/{phase}/mutation-gate.json`; accepts verdict (PASS/WARN/FAIL/SKIP), kill rate metrics, and optional survived mutant details; normalizes uppercase-to-lowercase before persisting |
803
+ | parse_lane_candidates | Architect-only: parses `[CANDIDATE]` rows from a `dispatch_lanes` or `collect_lane_results` artifact by `output_ref`; produces structured records with provenance and optional sidecar JSONL persistence; returns `ParseResultWithSidecar` on success or `{ error, error_code, candidates: [] }` on refusal |
803
804
  | git_blame | Per-line git blame metadata (sha, author, date, summary) via `git blame --porcelain`; supports optional line range filtering |
804
805
  | diff | Structured git diff with contract change detection; supports `summaryOnly` mode returning file list with additions/deletions counts |
805
806
  | suggest_patch | Reviewer-safe structured patch suggestion; supports `format` parameter ('json' or 'unified') where unified outputs valid unified diff with `diff --git` headers, hunks, and context |
@@ -0,0 +1,189 @@
1
+ import { z } from 'zod';
2
+ declare const ArtifactInputSchema: z.ZodObject<{
3
+ output_ref: z.ZodString;
4
+ batchId: z.ZodString;
5
+ laneId: z.ZodString;
6
+ agent: z.ZodString;
7
+ role: z.ZodString;
8
+ sessionId: z.ZodOptional<z.ZodString>;
9
+ parentSessionId: z.ZodOptional<z.ZodString>;
10
+ digest: z.ZodString;
11
+ text: z.ZodString;
12
+ transcriptIncomplete: z.ZodOptional<z.ZodBoolean>;
13
+ artifact_status: z.ZodEnum<{
14
+ ok: "ok";
15
+ "ref-not-found": "ref-not-found";
16
+ "artifact-corrupted": "artifact-corrupted";
17
+ }>;
18
+ source: z.ZodEnum<{
19
+ dispatch_lanes: "dispatch_lanes";
20
+ collect_lane_results: "collect_lane_results";
21
+ }>;
22
+ produced_at: z.ZodString;
23
+ }, z.core.$strict>;
24
+ declare const ParseFlagsSchema: z.ZodObject<{
25
+ accept_partial: z.ZodBoolean;
26
+ accept_degraded: z.ZodBoolean;
27
+ degraded: z.ZodBoolean;
28
+ row_format_version: z.ZodNumber;
29
+ producer: z.ZodOptional<z.ZodString>;
30
+ }, z.core.$strict>;
31
+ export type ArtifactInput = z.infer<typeof ArtifactInputSchema>;
32
+ export type ParseFlags = z.infer<typeof ParseFlagsSchema>;
33
+ /**
34
+ * The two supported pipe-delimited format families produced by lane agents.
35
+ */
36
+ export type RowFormatFamily = 'base_explorer' | 'micro_lane';
37
+ /**
38
+ * A single parsed candidate record extracted from lane text.
39
+ */
40
+ export interface CandidateRecord {
41
+ record_type: 'candidate';
42
+ row_format_family: RowFormatFamily;
43
+ row_format_version: number;
44
+ record_version: {
45
+ major: number;
46
+ minor: number;
47
+ };
48
+ source_output_ref: string;
49
+ source_batch_id: string;
50
+ source_lane_id: string;
51
+ source_agent: string;
52
+ source_digest: string;
53
+ extracted_from_partial_source: boolean;
54
+ sessionId?: string;
55
+ parentSessionId?: string;
56
+ producer?: string;
57
+ candidate_id: string;
58
+ lane: string | null;
59
+ micro_lane: string | null;
60
+ severity: string | null;
61
+ category: string | null;
62
+ file_line: string | null;
63
+ claim: string | null;
64
+ evidence_summary: string | null;
65
+ impact_context: string | null;
66
+ invariant_violated: string | null;
67
+ confidence: string | null;
68
+ }
69
+ /**
70
+ * One invocation-envelope record per parseCandidates call.
71
+ * Part of the return value but not persisted to a sidecar in this phase.
72
+ */
73
+ export interface InvocationEnvelope {
74
+ record_type: 'invocation';
75
+ source_output_ref: string;
76
+ source_batch_id: string;
77
+ source_lane_id: string;
78
+ source_agent: string;
79
+ source_digest: string;
80
+ row_format_version: number;
81
+ producer?: string;
82
+ produced_at: string;
83
+ record_version: {
84
+ major: number;
85
+ minor: number;
86
+ };
87
+ sessionId?: string;
88
+ parentSessionId?: string;
89
+ format_families_detected: string[];
90
+ candidate_count: number;
91
+ parse_errors: number;
92
+ malformed_rows: number;
93
+ }
94
+ /**
95
+ * Detail record for a required-field violation inside a data row.
96
+ */
97
+ export interface ParseErrorDetail {
98
+ row_index: number;
99
+ field: string;
100
+ message: string;
101
+ }
102
+ /**
103
+ * Warning record for a candidate_id that occurs more than once.
104
+ */
105
+ export interface DuplicateIdWarning {
106
+ candidate_id: string;
107
+ occurrences: number;
108
+ }
109
+ /**
110
+ * Aggregate diagnostics returned alongside every parse result.
111
+ */
112
+ export interface DiagnosticsSummary {
113
+ candidate_count: number;
114
+ parse_errors: number;
115
+ parse_error_details: ParseErrorDetail[];
116
+ malformed_rows: number;
117
+ duplicate_id_count: number;
118
+ duplicate_id_warnings: DuplicateIdWarning[];
119
+ degraded_source_count: number;
120
+ incomplete_source_count: number;
121
+ format_families_detected: string[];
122
+ }
123
+ /**
124
+ * Top-level return value from parseCandidates.
125
+ */
126
+ export interface ParseResult {
127
+ error?: string;
128
+ error_code?: string;
129
+ candidates: CandidateRecord[];
130
+ invocation_envelope: InvocationEnvelope;
131
+ diagnostics: DiagnosticsSummary;
132
+ }
133
+ /**
134
+ * Options for the parse-and-persist path.
135
+ */
136
+ export interface ParsePersistOptions {
137
+ /** Project root directory (OpenCode process working directory). */
138
+ projectRoot: string;
139
+ /** Override the batch digest. When omitted, SHA-256(batchId) is used. */
140
+ batchDigest?: string;
141
+ /**
142
+ * Passed through to the sidecar store's `useLockfile` option.
143
+ * When true, a proper-lockfile lock is acquired on the batch directory
144
+ * before the append; on lock failure `sidecar_write_error` is set.
145
+ * Default: false (no lock — existing append-only pattern).
146
+ */
147
+ useLockfile?: boolean;
148
+ }
149
+ /**
150
+ * ParseResult extended with an optional sidecar write error.
151
+ * When sidecar_write_error is present, the parse succeeded but the
152
+ * sidecar append failed; the caller should treat the parse as valid
153
+ * and log/report the write error separately.
154
+ */
155
+ export interface ParseResultWithSidecar extends ParseResult {
156
+ sidecar_write_error?: string;
157
+ }
158
+ /**
159
+ * Parse candidate records from structured lane-output text.
160
+ *
161
+ * This is a pure function: no filesystem writes, no store lookups, no I/O.
162
+ * The caller is responsible for store lookup, constructing the ArtifactInput,
163
+ * and setting artifact_status based on lookup outcome.
164
+ *
165
+ * @param input Structured artifact metadata and raw text.
166
+ * @param flags Caller-controlled acceptance flags and format version.
167
+ * @returns Parsed candidates, invocation envelope, and diagnostics.
168
+ */
169
+ export declare function parseCandidates(input: ArtifactInput, flags: ParseFlags): ParseResult;
170
+ /**
171
+ * Parse candidates and append the invocation envelope + candidate records
172
+ * to the sidecar JSONL file.
173
+ *
174
+ * parseCandidates remains pure (no I/O). This wrapper adds sidecar
175
+ * persistence: on success the envelope + candidates are appended to
176
+ * `.swarm/lane-results/{batchDigest}/candidates.jsonl`; on append
177
+ * failure the parse still succeeds and sidecar_write_error is populated
178
+ * (SC-023).
179
+ *
180
+ * batchDigest is derived as SHA-256(batchId) when not explicitly provided
181
+ * in options (Option A — consistent with lane-output-store.ts internals).
182
+ *
183
+ * @param input Structured artifact metadata and raw text.
184
+ * @param flags Caller-controlled acceptance flags and format version.
185
+ * @param options Persistence options (projectRoot, optional batchDigest).
186
+ * @returns Parse result with optional sidecar_write_error field.
187
+ */
188
+ export declare function parseAndPersist(input: ArtifactInput, flags: ParseFlags, options: ParsePersistOptions): ParseResultWithSidecar;
189
+ export {};
@@ -0,0 +1,56 @@
1
+ export interface SidecarStoreOptions {
2
+ /** Project root directory (the OpenCode process working directory). */
3
+ projectRoot: string;
4
+ /** Override the batch digest. When omitted, SHA-256(batchId) is used. */
5
+ batchDigest?: string;
6
+ /**
7
+ * When true, acquire a `proper-lockfile` lock on the batch directory
8
+ * before the append and release it afterwards. Default: false (no lock —
9
+ * matches the existing append-only pattern the codebase already uses).
10
+ *
11
+ * On lock acquisition failure the function throws; the caller
12
+ * (`parseAndPersist`) catches and records `sidecar_write_error`.
13
+ */
14
+ useLockfile?: boolean;
15
+ }
16
+ /**
17
+ * Sanitize a single string value for sidecar persistence.
18
+ *
19
+ * Transformations applied in order:
20
+ * 1. NUL bytes (`\x00`) → `\u0000` escape (preserves information).
21
+ * 2. ANSI escape sequences (`\x1B[...]`) → entire sequence stripped.
22
+ * Handles CSI (`ESC [ params intermediate final`), OSC (`ESC ] ... BEL/ST`),
23
+ * and two-character escape sequences.
24
+ * 3. Unicode bidi markers (U+202A–U+202E, U+2066–U+2069) → stripped.
25
+ *
26
+ * All other characters pass through unchanged.
27
+ *
28
+ * @param value - Raw string value (may contain NUL bytes, ANSI, bidi markers).
29
+ * @returns Sanitized string safe for JSONL persistence.
30
+ */
31
+ export declare function sanitizeString(value: string): string;
32
+ /**
33
+ * Append an invocation envelope followed by zero or more candidate records
34
+ * to the sidecar JSONL file for the given batch.
35
+ *
36
+ * The envelope is always the first record in the append batch (SC-015).
37
+ * Cross-skill coexistence is enabled via the `producer` field on the
38
+ * envelope (FR-019).
39
+ *
40
+ * Content sanitization (FR-021) is applied to the persisted copy: NUL bytes
41
+ * are escaped to `\u0000`, ANSI ESC bytes are stripped, and Unicode bidi
42
+ * markers are stripped. The caller's in-memory records are NOT mutated.
43
+ *
44
+ * Concurrency safety (FR-014): when `options.useLockfile` is true, a
45
+ * `proper-lockfile` lock is acquired on the batch directory before the
46
+ * append and released afterwards. Lock acquisition failures propagate as
47
+ * throws. Default behaviour (useLockfile omitted or false) is lockless
48
+ * append — the existing pattern used throughout the codebase.
49
+ *
50
+ * @param options Store configuration (projectRoot, optional batchDigest, useLockfile).
51
+ * @param batchId The batch identifier (used to derive batchDigest when not provided).
52
+ * @param envelope The invocation envelope record (validated before write).
53
+ * @param candidates Candidate records to append after the envelope (validated before write).
54
+ * @throws If schema validation fails, lock acquisition fails, or the filesystem write fails.
55
+ */
56
+ export declare function appendToSidecar(options: SidecarStoreOptions, batchId: string, envelope: unknown, candidates: unknown[]): void;
@@ -12,8 +12,8 @@ import {
12
12
  shouldRunOnStartup,
13
13
  writeBackupArtifact,
14
14
  writeDoctorArtifact
15
- } from "./index-1cb4wxnm.js";
16
- import"./index-q9h0wb04.js";
15
+ } from "./index-819xp49y.js";
16
+ import"./index-0asbrmdx.js";
17
17
  import"./index-5e4e2hvv.js";
18
18
  import"./index-p0arc26j.js";
19
19
  import"./index-zgwm4ryv.js";
@@ -1,14 +1,14 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-f41fa3f0.js";
5
- import"./index-6qkwgdsg.js";
6
- import"./index-5hvbw5xh.js";
4
+ } from "./index-fwb5f2gr.js";
5
+ import"./index-jch711dq.js";
6
+ import"./index-g00qm2gf.js";
7
7
  import"./index-yhsmmv2z.js";
8
- import"./index-s8bj492g.js";
8
+ import"./index-32axfg6h.js";
9
9
  import"./index-e8pk68cc.js";
10
- import"./index-1cb4wxnm.js";
11
- import"./index-q9h0wb04.js";
10
+ import"./index-819xp49y.js";
11
+ import"./index-0asbrmdx.js";
12
12
  import"./index-8y7qetpg.js";
13
13
  import"./index-adz3nk9b.js";
14
14
  import"./index-v4fcn4tr.js";