opencode-swarm 7.108.0 → 7.109.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/.opencode/skills/clarify-spec/SKILL.md +15 -9
- package/.opencode/skills/critic-gate/SKILL.md +12 -4
- package/.opencode/skills/execute/SKILL.md +1 -0
- package/.opencode/skills/phase-wrap/SKILL.md +2 -2
- package/.opencode/skills/plan/SKILL.md +23 -11
- package/.opencode/skills/specify/SKILL.md +27 -9
- package/dist/cli/{core-vev13ej2.js → core-mbd2g302.js} +5 -1
- package/dist/cli/{curator-drift-fdfgj9qf.js → curator-drift-169wgaxn.js} +1 -1
- package/dist/cli/{curator-llm-factory-n70bxf40.js → curator-llm-factory-gtfhwnbr.js} +4 -4
- package/dist/cli/{curator-js92gc8a.js → curator-q4febs18.js} +4 -4
- package/dist/cli/{evidence-summary-service-bjhr75v0.js → evidence-summary-service-8tasw2tt.js} +2 -2
- package/dist/cli/{guardrail-explain-sn2a4pb7.js → guardrail-explain-r2d8ftkv.js} +5 -5
- package/dist/cli/{hive-promoter-3hh8ph4a.js → hive-promoter-mfd5p9f5.js} +4 -4
- package/dist/cli/{index-x06d2qqm.js → index-1capawwy.js} +5 -5
- package/dist/cli/{index-f1qyj61w.js → index-3yk9196e.js} +40 -3
- package/dist/cli/{index-48mc4d48.js → index-j2v3w1ds.js} +8 -2
- package/dist/cli/{index-1emhz3zb.js → index-nq9h2t3x.js} +137 -70
- package/dist/cli/{index-91j1sqzm.js → index-scww5b77.js} +47 -0
- package/dist/cli/{index-7jyndvsy.js → index-wsg3vkss.js} +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/commands/registry.d.ts +2 -2
- package/dist/commands/sdd.d.ts +10 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/worktree-isolation-config.d.ts +10 -0
- package/dist/hooks/delegation-gate/worktree-isolation.d.ts +4 -2
- package/dist/hooks/delegation-gate.d.ts +1 -0
- package/dist/hooks/guardrails/index.d.ts +1 -1
- package/dist/hooks/guardrails/tool-before.d.ts +5 -0
- package/dist/index.js +130 -121
- package/dist/plan/ledger.d.ts +60 -0
- package/dist/sdd/effective-spec.d.ts +21 -0
- package/dist/tools/save-plan.d.ts +24 -0
- package/dist/worktree/core.d.ts +58 -7
- package/dist/worktree/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -9,11 +9,13 @@ description: >
|
|
|
9
9
|
This protocol is loaded on demand by the architect stub in src/agents/architect.ts. The architect prompt keeps only activation, action, and hard safety constraints; the full execution details live here.
|
|
10
10
|
|
|
11
11
|
### MODE: CLARIFY-SPEC
|
|
12
|
-
Activates when:
|
|
12
|
+
Activates when: `/swarm sdd status` reports a **single resolved EFFECTIVE spec** (non-null) AND it contains `[NEEDS CLARIFICATION]` markers; OR user says "clarify", "refine spec", "review spec", or "/swarm clarify" is invoked; OR architect transitions from MODE: SPECIFY with open markers.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
`/swarm sdd status` reflects `readEffectiveSpecSync`, which returns **null** (NO effective spec) for: no sources at all, multiple competing sources (e.g. `openspec/` AND `.specify/`), multi-feature Spec-Kit without a selected feature, or any other unresolvable state. CLARIFY-SPEC does NOT activate in these null cases — tell the user: "No resolved effective spec exists. Disambiguate with `/swarm sdd project --source <source>` or `--feature <feature>`, or run `/swarm specify` to generate one first." and stop.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
CONSTRAINT: CLARIFY-SPEC must NEVER create a spec. Always consult `/swarm sdd status` to determine the effective spec source before proceeding.
|
|
17
|
+
|
|
18
|
+
1. Read the **effective spec** resolved by `/swarm sdd status` (native `.swarm/spec.md` OR OpenSpec `openspec/` OR Spec-Kit `.specify/` — read the resolved spec FIRST before making any changes).
|
|
17
19
|
2. Scan for ambiguities beyond explicit `[NEEDS CLARIFICATION]` markers:
|
|
18
20
|
- Vague adjectives ("fast", "secure", "user-friendly") without measurable targets
|
|
19
21
|
- Requirements that overlap or potentially conflict with each other
|
|
@@ -28,10 +30,14 @@ CONSTRAINT: CLARIFY-SPEC must NEVER create a spec. If `.swarm/spec.md` does not
|
|
|
28
30
|
- Offer 2–4 multiple-choice options for each question
|
|
29
31
|
- Mark the recommended option with reasoning (e.g., "Recommended: Option 2 because…")
|
|
30
32
|
- Allow free-form input as an alternative to the options
|
|
31
|
-
5. After each accepted answer:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
5. After each accepted answer, write the resolution to the **resolved effective source** (source-aware write-back):
|
|
34
|
+
- **NATIVE effective spec** (`.swarm/spec.md` exists): update `.swarm/spec.md` with the resolution directly.
|
|
35
|
+
- **NON-NATIVE effective spec** (openspec/specify-only, NO native `.swarm/spec.md`): do NOT write `.swarm/spec.md` — this would silently shadow the non-native source. Instead:
|
|
36
|
+
- (a) If the resolved source supports in-place edits (e.g., OpenSpec sections), update the source artifacts directly.
|
|
37
|
+
- (b) If no in-place edit path exists, ask the user: "The effective spec lives in `<source>`. To persist this resolution as a native spec, run `/swarm sdd project` first to materialize one, or I can stop here. Proceed?" — if the user consents to project, materialize via `/swarm sdd project` then write `.swarm/spec.md`; otherwise stop.
|
|
38
|
+
- (c) If neither (a) nor (b) applies, stop and tell the user the clarification cannot be auto-written to a non-native source without a projection step.
|
|
39
|
+
- Replace the relevant `[NEEDS CLARIFICATION]` marker or vague language with the accepted answer.
|
|
40
|
+
- If the answer invalidates an earlier requirement, update it to remove the contradiction.
|
|
35
41
|
6. Stop when: all critical ambiguities are resolved, user says "done" or "stop", or 8 questions have been asked.
|
|
36
42
|
7. Report a ## Clarification Summary: total questions asked, requirements added/modified/removed, remaining open ambiguities (if any), and suggest next step (`PLAN` if spec is clear, or continue clarifying).
|
|
37
43
|
|
|
@@ -39,9 +45,9 @@ CLARIFY-SPEC RULES:
|
|
|
39
45
|
- FR-ID increment rule: When adding new requirements, find the highest existing FR-ID and increment from there (FR-001 → FR-002). Never reuse or skip FR-IDs.
|
|
40
46
|
- One question at a time — never ask multiple questions in the same message.
|
|
41
47
|
- Do not modify any part of the spec that was not affected by the accepted answer.
|
|
42
|
-
- Always write the accepted answer back to
|
|
48
|
+
- Always write the accepted answer back to the resolved effective source before presenting the next question. Never write `.swarm/spec.md` in a non-native (openspec/specify-only) repo — see step 5 source-aware write-back rule.
|
|
43
49
|
- Max 8 questions per session — if limit reached, report remaining ambiguities and stop.
|
|
44
|
-
- Do not create or
|
|
50
|
+
- Do not create, overwrite, or shadow the spec file — only refine what exists. In non-native (openspec/specify-only) repos, never silently materialize a `.swarm/spec.md` that would shadow the effective source.
|
|
45
51
|
|
|
46
52
|
### Scoped Funnel Protocol (CLARIFY-SPEC only)
|
|
47
53
|
|
|
@@ -11,6 +11,7 @@ This protocol is loaded on demand by the architect stub in src/agents/architect.
|
|
|
11
11
|
### MODE: CRITIC-GATE
|
|
12
12
|
Delegate plan to the active swarm's critic agent for review BEFORE any implementation begins.
|
|
13
13
|
- Send the full plan.md content and codebase context summary
|
|
14
|
+
- Explicitly reference "plan.md" or "critic-gate" in the dispatch prompt text. This lets the mechanical approval-recording gate reliably detect the review and record the critic's APPROVED verdict, which the EXECUTE-phase coder gate then requires.
|
|
14
15
|
- **APPROVED** → Proceed to MODE: EXECUTE
|
|
15
16
|
- **NEEDS_REVISION** → Revise the plan based on critic feedback, then resubmit (max 2 cycles)
|
|
16
17
|
- **REJECTED** → Inform the user of fundamental issues and ask for guidance before proceeding
|
|
@@ -26,11 +27,12 @@ You MUST NOT proceed to MODE: EXECUTE without printing this checklist with fille
|
|
|
26
27
|
CRITIC-GATE TRIGGER: Run ONCE when you first write the complete .swarm/plan.md.
|
|
27
28
|
Do NOT re-run CRITIC-GATE before every project phase.
|
|
28
29
|
If resuming a project with an existing approved plan, CRITIC-GATE is already satisfied.
|
|
30
|
+
Caveat: this assumption breaks if the plan lacks a `plan_critic_gate`-tagged approval snapshot (e.g. a plan approved before this mechanical gate existed, or one where the recording heuristic didn't fire) — in that case the first coder dispatch will fail with `PLAN_CRITIC_GATE_VIOLATION`. If that happens, do not assume CRITIC-GATE is satisfied; re-run it and get a fresh APPROVED verdict.
|
|
29
31
|
|
|
30
32
|
6j. SPEC-GATE (Execute BEFORE any save_plan call):
|
|
31
|
-
-
|
|
32
|
-
- Before calling save_plan, verify spec
|
|
33
|
-
- If spec
|
|
33
|
+
- An effective spec exists iff `/swarm sdd status` reports a resolved spec (it reflects `readEffectiveSpecSync`, which returns null — NO effective spec — for no sources, multiple competing sources (openspec+specify), multi-feature Spec-Kit without a selected feature, or any unresolvable state). `save_plan` rejects (SPEC_REQUIRED) when `/swarm sdd status` reports no resolved spec. The gate is overridable via `SWARM_SKIP_SPEC_GATE=1`.
|
|
34
|
+
- Before calling save_plan, verify an effective spec exists (via `/swarm sdd status` or `lint_spec`).
|
|
35
|
+
- If no effective spec exists: do NOT call save_plan. Generate one first — native via `/swarm specify`, or via the agent-invocable `/swarm sdd project` (from SDD sources, after consent).
|
|
34
36
|
- This rule is satisfied by the save_plan tool's own spec gate — it exists as a reminder that planning requires a spec.
|
|
35
37
|
|
|
36
38
|
6k. SPEC-STALENESS GUARD:
|
|
@@ -60,7 +62,13 @@ If resuming a project with an existing approved plan, CRITIC-GATE is already sat
|
|
|
60
62
|
|
|
61
63
|
6l. OBLIGATION TRACEABILITY CHECK (FR-003):
|
|
62
64
|
- Before the critic's substantive rubric, the critic MUST cross-reference every
|
|
63
|
-
MUST/SHALL SC-### obligation in
|
|
65
|
+
MUST/SHALL SC-### obligation in the EFFECTIVE spec against the plan tasks.
|
|
66
|
+
An effective spec exists iff `/swarm sdd status` reports a resolved spec (it
|
|
67
|
+
reflects `readEffectiveSpecSync`, which returns null — NO effective spec — for
|
|
68
|
+
no sources, multiple competing sources (openspec+specify), multi-feature
|
|
69
|
+
Spec-Kit without a selected feature, or any unresolvable state). Obligations
|
|
70
|
+
are traced only against the resolved effective spec; in a null/unresolved
|
|
71
|
+
state there is nothing to trace (this check is not applicable).
|
|
64
72
|
- If ANY MUST/SHALL SC-### has zero corresponding plan tasks, the critic MUST
|
|
65
73
|
return VERDICT: REJECTED enumerating each unmapped obligation.
|
|
66
74
|
- The critic MUST evaluate coverage against the FULL plan — each task's
|
|
@@ -55,6 +55,7 @@ All other gates: failure → return to coder. No self-fixes. No workarounds.
|
|
|
55
55
|
→ Subsequent `pre_check_batch` calls with `phase: <N>` will automatically diff against this baseline — only NEW findings (not in baseline) drive the fail verdict.
|
|
56
56
|
-> PREFLIGHT CHECKLIST: Before first coder delegation, answer "SAST baseline captured before first coder delegation? yes/no/disabled/error". If the answer is no, do not delegate to coder; run 5b-BASE first. If disabled or error, record the exact tool result.
|
|
57
57
|
5b. the active swarm's coder agent - Implement (if designer scaffold produced, include it as INPUT).
|
|
58
|
+
→ If this dispatch fails with `PLAN_CRITIC_GATE_VIOLATION`: the plan has no current critic-approved snapshot (commonly a plan approved before this mechanical gate existed). Do NOT retry the coder dispatch as-is — re-run MODE: CRITIC-GATE to get a fresh critic `APPROVED` verdict, then retry this step.
|
|
58
59
|
5b-bis. **CODER OUTPUT VERIFICATION**: After the coder reports completion, do NOT accept the self-report alone. Run `diff` (step 5c) and inspect at least one of the modified files yourself to confirm the change exists. The coder may report DONE without having produced any diff. A 30-second read of the changed file(s) catches this failure mode. This is NOT a separate explorer dispatch — the existing `diff` tool at step 5c is the verification mechanism; the key discipline is checking that `diff` returns actual changes before proceeding, rather than forwarding the coder's self-report to the next gate.
|
|
59
60
|
5c. Run `diff` tool. If `hasContractChanges` → the active swarm's explorer agent integration analysis. If COMPATIBILITY SIGNALS=INCOMPATIBLE or MIGRATION_SURFACE=yes → coder retry. If COMPATIBILITY SIGNALS=COMPATIBLE and MIGRATION_SURFACE=no → proceed.
|
|
60
61
|
→ REQUIRED: Print "diff: [PASS | CONTRACT CHANGE — details]"
|
|
@@ -62,7 +62,7 @@ The tool will automatically write the retrospective to \`.swarm/evidence/retro-{
|
|
|
62
62
|
4. Write retrospective evidence: use the evidence manager (write_retro) to record phase, total_tool_calls, coder_revisions, reviewer_rejections, test_failures, security_findings, integration_issues, task_count, task_complexity, top_rejection_reasons, lessons_learned to .swarm/evidence/. Reset Phase Metrics in context.md to 0.
|
|
63
63
|
4.5. Run `evidence_check` to verify all completed tasks have required evidence (review + test). If gaps found, note in retrospective lessons_learned. Optionally run `pkg_audit` if dependencies were modified during this phase. Optionally run `schema_drift` if API routes were modified during this phase.
|
|
64
64
|
5. Run `sbom_generate` with scope='changed' to capture post-implementation dependency snapshot (saved to `.swarm/evidence/sbom/`). This is a non-blocking step - always proceeds to summary.
|
|
65
|
-
5.5. **Drift verification**: Conditional on
|
|
65
|
+
5.5. **Drift verification**: Conditional on an EFFECTIVE spec existing (determined via `/swarm sdd status` or `readEffectiveSpecSync` — native `.swarm/spec.md`, OpenSpec `openspec/`, or Spec-Kit `.specify/`). If NO effective spec exists at all, skip silently. If an effective spec exists (even openspec-only or specify-only), delegate to the active swarm's critic_drift_verifier agent with DRIFT-CHECK context:
|
|
66
66
|
- Provide: phase number being completed, completed task IDs and their descriptions
|
|
67
67
|
- Include evidence path (.swarm/evidence/) for the critic to read implementation artifacts
|
|
68
68
|
The critic reads every target file, verifies described changes exist against the spec, and returns per-task verdicts: ALIGNED, MINOR_DRIFT, MAJOR_DRIFT, or OFF_SPEC.
|
|
@@ -92,7 +92,7 @@ The tool will automatically write the retrospective to \`.swarm/evidence/retro-{
|
|
|
92
92
|
5.58. **Design-doc sync (conditional on `design_docs.enabled` — issue #1080)**: If `design_docs.enabled` is not true, skip silently. Otherwise: `phase_complete` runs a deterministic, non-blocking design-doc drift check and writes `.swarm/doc-drift-phase-{phase}.json`. If its verdict is `DOC_STALE`, enter MODE: DESIGN_DOCS in sync mode for the stale sections only — delegate to the active swarm's `docs_design` agent (NOT the standard `docs` agent) with the changed files + the stale section IDs, and have it update the affected docs and append a `design-changelog.md` entry. This is advisory and NON-BLOCKING — never hold up phase_complete on design-doc lag, and never write `.swarm/spec.md`, `CHANGELOG.md`, or `docs/releases/pending/*` here.
|
|
93
93
|
5.6. **Mandatory gate evidence**: Before calling phase_complete, ensure:
|
|
94
94
|
- `.swarm/evidence/{phase}/completion-verify.json` exists (written automatically by the completion-verify gate)
|
|
95
|
-
|
|
95
|
+
- `.swarm/evidence/{phase}/drift-verifier.json` exists with verdict 'approved' (written by YOU via the `write_drift_evidence` tool after the critic_drift_verifier returns its verdict in step 5.5) — required when an effective spec exists
|
|
96
96
|
- `.swarm/evidence/{phase}/hallucination-guard.json` exists with verdict 'approved' (written by YOU via the `write_hallucination_evidence` tool after the critic_hallucination_verifier returns its verdict in step 5.55) — ONLY required when `hallucination_guard` is enabled in the QA gate profile
|
|
97
97
|
- `.swarm/evidence/{phase}/mutation-gate.json` exists with verdict 'pass' or 'warn' (written by YOU via the `write_mutation_evidence` tool after step 5.56) — ONLY required when `mutation_test` is enabled in the QA gate profile
|
|
98
98
|
- regression-test falsification evidence exists for at least one regression
|
|
@@ -11,22 +11,25 @@ This protocol is loaded on demand by the architect stub in src/agents/architect.
|
|
|
11
11
|
### MODE: PLAN
|
|
12
12
|
|
|
13
13
|
SPEC GATE (soft — check before planning):
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
An effective spec exists iff `/swarm sdd status` reports a resolved spec (it reflects `readEffectiveSpecSync`, which returns null for no sources, multiple competing sources, multi-feature Spec-Kit without a selected feature, or any unresolvable state). Do NOT enumerate these cases — defer to `/swarm sdd status`.
|
|
16
|
+
|
|
17
|
+
- If NO effective spec exists (confirmed via `/swarm sdd status`):
|
|
15
18
|
- PLAN INGESTION DETECTION: Check if the user is providing an external plan (indicators: markdown content with Phase/Task structure, or phrases like "ingest this plan", "implement this plan", "prepare for implementation", "here is a plan", "here's the plan"):
|
|
16
|
-
- If plan ingestion is detected AND no spec
|
|
19
|
+
- If plan ingestion is detected AND no effective spec exists: offer this choice FIRST before any planning:
|
|
17
20
|
1. "Generate spec from this plan first" → enter EXTERNAL PLAN IMPORT PATH in MODE: SPECIFY to reverse-engineer a spec.md from the provided plan, then return to planning
|
|
18
21
|
2. "Skip spec and proceed with the provided plan" → proceed directly to plan ingestion and planning without creating a spec
|
|
19
22
|
- This is a SOFT gate — option 2 always lets the user proceed without a spec
|
|
20
|
-
- If no plan ingestion detected: Warn: "No spec found. A spec helps ensure the plan covers all requirements and gives the critic something to verify against. Would you like to create one first?"
|
|
23
|
+
- If no plan ingestion detected: Warn: "No effective spec found. A spec helps ensure the plan covers all requirements and gives the critic something to verify against. Would you like to create one first?"
|
|
21
24
|
- Offer two options:
|
|
22
25
|
1. "Create a spec first" → transition to MODE: SPECIFY
|
|
23
26
|
2. "Skip and plan directly" → continue with the steps below unchanged
|
|
24
|
-
- If
|
|
27
|
+
- If an effective spec EXISTS:
|
|
25
28
|
- NOTE: Stale detection is intentionally heuristic (compare headings) — false positives are acceptable because this is a SOFT gate. When in doubt, ask the user.
|
|
26
|
-
- Read the spec and compare its first heading (or feature description) against the current planning context (the user's request and any existing plan.md title/phase names)
|
|
29
|
+
- Read the spec (using the effective spec path reported by `/swarm sdd status`) and compare its first heading (or feature description) against the current planning context (the user's request and any existing plan.md title/phase names)
|
|
27
30
|
- STALE SPEC DETECTION: If the spec heading or feature description does NOT match the current work being planned (e.g., spec describes "user authentication" but user is asking to plan "payment integration"), treat the spec as potentially stale and offer three options:
|
|
28
31
|
1. **Archive and create new spec** → attempt to rename .swarm/spec.md to .swarm/spec-archive/spec-{YYYY-MM-DD}.md (create the directory if needed); if archival succeeds: enter MODE: SPECIFY and skip the "spec already exists" prompt; if archival fails: inform user of the failure and offer: retry archival, or proceed with option 2, or proceed with option 3
|
|
29
|
-
2. **Keep existing spec** → use spec
|
|
32
|
+
2. **Keep existing spec** → use the effective spec as-is and proceed with planning below
|
|
30
33
|
3. **Skip spec entirely** → proceed to planning below ignoring the existing spec
|
|
31
34
|
- If the spec appears current (heading matches the work being planned) OR user chose option 2 above, proceed with spec:
|
|
32
35
|
- Read it and use it as the primary input for planning
|
|
@@ -37,7 +40,16 @@ SPEC GATE (soft — check before planning):
|
|
|
37
40
|
|
|
38
41
|
This is a SOFT gate. When the user chooses "Skip and plan directly", proceed to the steps below exactly as before — do NOT modify any planning behavior.
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
**SAVE_PLAN SPEC_REQUIRED RECOVERY:**
|
|
44
|
+
When `save_plan` returns a SPEC_REQUIRED rejection (no effective spec found), the architect MUST:
|
|
45
|
+
1. DIAGNOSE: run `/swarm sdd status` to determine why no effective spec resolved.
|
|
46
|
+
- (a) If `/swarm sdd status` shows NO sources → transition to MODE: SPECIFY.
|
|
47
|
+
- (b) If `/swarm sdd status` shows multiple competing sources (e.g., openspec AND specify with no native) → ask the user which provider to use (`openspec` or `speckit`), then run `/swarm sdd project --source <user_choice>` (obtain explicit consent first; add `--overwrite` only if a native `.swarm/spec.md` already exists). Then re-attempt `save_plan`.
|
|
48
|
+
- (c) If `/swarm sdd status` shows Spec-Kit with multiple features → ask the user which feature, then run `/swarm sdd project --source speckit --feature <id>` (obtain explicit consent first; add `--overwrite` only if a native `.swarm/spec.md` already exists). Then re-attempt `save_plan`.
|
|
49
|
+
2. If `/swarm sdd status` shows a single resolvable source but it was not yet materialized: run `/swarm sdd project` (obtain explicit consent first; add `--overwrite` only if a native `.swarm/spec.md` already exists). Then re-attempt `save_plan`.
|
|
50
|
+
3. If the user does NOT consent to materializing an effective spec: surface the blockage and stop — do not silently skip or retry without a spec.
|
|
51
|
+
|
|
52
|
+
Run CODEBASE REALITY CHECK scoped to codebase elements referenced in the effective spec or user constraints. Discrepancies must be reflected in the generated plan.
|
|
41
53
|
|
|
42
54
|
### GENERAL COUNCIL ADVISORY OPTION (pre-save_plan)
|
|
43
55
|
|
|
@@ -305,17 +317,17 @@ PHASE COUNT GUIDANCE:
|
|
|
305
317
|
|
|
306
318
|
Also create .swarm/context.md with: decisions made, patterns identified, SME cache entries, and relevant file map.
|
|
307
319
|
|
|
308
|
-
TRACEABILITY CHECK (run after plan is written, when spec
|
|
320
|
+
TRACEABILITY CHECK (run after plan is written, when an effective spec exists):
|
|
309
321
|
|
|
310
322
|
OBLIGATION TRACEABILITY — STRUCTURAL COMPLETENESS PRECONDITION
|
|
311
323
|
The obligation-traceability mapping is a STRUCTURAL COMPLETENESS precondition. It MUST be evaluated BEFORE the critic begins its substantive 5-axis/7-dimension rubric. An unmapped MUST/SHALL obligation makes the plan structurally incomplete — it is not an afterthought.
|
|
312
324
|
|
|
313
325
|
1. FR-### MAPPING (existing requirement):
|
|
314
|
-
- Every FR-### in spec
|
|
326
|
+
- Every FR-### in the effective spec (resolved via `/swarm sdd status`) MUST map to at least one task → unmapped FRs = coverage gap, flag to user
|
|
315
327
|
- Every task MUST reference its source FR-### in the description or acceptance field → tasks with no FR = potential gold-plating, flag to critic
|
|
316
328
|
|
|
317
329
|
2. SC-### MAPPING (MUST/SHALL obligations):
|
|
318
|
-
- Parse spec
|
|
330
|
+
- Parse the effective spec (resolved via `/swarm sdd status`) for every SC-### line whose obligation text contains MUST or SHALL/SHALL NOT
|
|
319
331
|
- Each such MUST/SHALL SC-### MUST be referenced by ≥1 task's description or acceptance field
|
|
320
332
|
- Unmapped MUST/SHALL SC-### are structural coverage gaps that must be resolved — surface them prominently, not buried
|
|
321
333
|
- A plan where every MUST/SHALL SC-### is referenced by ≥1 task passes this check and is not blocked by it
|
|
@@ -324,7 +336,7 @@ The obligation-traceability mapping is a STRUCTURAL COMPLETENESS precondition. I
|
|
|
324
336
|
REPORT FORMAT:
|
|
325
337
|
"TRACEABILITY: <N> FRs mapped, <M> unmapped FRs (gap), <K> tasks with no FR mapping (gold-plating risk), <P> MUST/SHALL SCs mapped, <Q> unmapped MUST/SHALL SCs (structural gap)"
|
|
326
338
|
|
|
327
|
-
- If no spec
|
|
339
|
+
- If no effective spec: skip this check silently.
|
|
328
340
|
|
|
329
341
|
### Transition to CRITIC-GATE
|
|
330
342
|
|
|
@@ -9,15 +9,33 @@ description: >
|
|
|
9
9
|
This protocol is loaded on demand by the architect stub in src/agents/architect.ts. The architect prompt keeps only activation, action, and hard safety constraints; the full execution details live here.
|
|
10
10
|
|
|
11
11
|
### MODE: SPECIFY
|
|
12
|
-
Activates when: user asks to "specify", "define requirements", "write a spec", or "define a feature"; OR `/swarm specify` is invoked; OR no `.swarm/
|
|
13
|
-
|
|
14
|
-
1.
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
Activates when: user asks to "specify", "define requirements", "write a spec", or "define a feature"; OR `/swarm specify` is invoked; OR no EFFECTIVE spec exists and no `.swarm/plan.md` exists (use `/swarm sdd status` to determine effective-spec existence — native `.swarm/spec.md`, OpenSpec `openspec/`, or Spec-Kit `.specify/`).
|
|
13
|
+
|
|
14
|
+
1. Run `/swarm sdd status` to determine whether an effective spec exists and, if so, how it should be handled. An effective spec exists iff `/swarm sdd status` reports a resolved spec. `/swarm sdd status` reflects `readEffectiveSpecSync`, which returns null (NO effective spec) for: no sources, multiple competing sources (openspec+speckit), multi-feature Spec-Kit without a selected feature, or any unresolvable state. When `/swarm sdd status` reports a resolved spec, classify it as NATIVE (native `.swarm/spec.md`) vs NON-NATIVE (projected). When it reports NO resolved spec, do NOT treat any source as an effective spec. Based on this classification, branch to the appropriate sub-step:
|
|
15
|
+
- **NATIVE**: proceed to step 1a (overwrite/refine/archive).
|
|
16
|
+
- **NON-NATIVE**: proceed to step 1b (non-shadowing choice).
|
|
17
|
+
- **NO effective spec** (ambiguous or no sources): if multiple SDD sources are present, proceed to step 1c (disambiguation); otherwise proceed to step 1d (native authoring).
|
|
18
|
+
- If this is called from the stale spec archival path (MODE: PLAN option 1) — archival was already completed; skip all branches and proceed directly to generation (step 2).
|
|
19
|
+
1a. **NATIVE SPEC — overwrite/refine/archive.** Ask the user "A spec already exists. Do you want to overwrite it or refine it?"
|
|
20
|
+
- Overwrite → ARCHIVE FIRST: read the existing spec, extract version (priority order): (1) from spec heading, look for patterns like "v{semver}" or "Version {semver}" in the first H1/H2; (2) from package.json version field in project root; create `.swarm/spec-archive/` directory if it does not exist; copy existing spec.md to `.swarm/spec-archive/spec-v{version}.md`; if version cannot be determined, use date-based fallback: `.swarm/spec-archive/spec-{YYYY-MM-DD}.md`; log the archive location to the user ("Archived existing spec to .swarm/spec-archive/spec-v{version}.md"); then proceed to generation (step 2)
|
|
21
|
+
- Refine → delegate to MODE: CLARIFY-SPEC
|
|
22
|
+
1b. **NON-NATIVE SPEC — non-shadowing check (FR-002).** The effective spec comes from `openspec/` or `.specify/` sources with no native `.swarm/spec.md`. Do NOT silently author a competing native spec. Instead OFFER the user a choice:
|
|
23
|
+
- **(a) Project/ingest** the existing SDD sources into `.swarm/spec.md` via the agent-invocable `/swarm sdd project` command. Obtain EXPLICIT user consent before proceeding. (Do not pass `--overwrite` in this branch — no native spec exists yet.)
|
|
24
|
+
- **(b) Proceed with native authoring** (`/swarm specify`) if the user explicitly chooses to ignore the SDD sources and write a new spec from scratch.
|
|
25
|
+
- **(c) Cancel** — abort SPECIFY; the existing SDD sources remain the effective spec.
|
|
26
|
+
- If the user chooses option (a) and `/swarm sdd project` completes successfully: the projected spec is now materialized as `.swarm/spec.md` (NATIVE). Do NOT proceed to generation (step 2) — that would overwrite the just-projected spec. Instead route to step 1a (overwrite/refine/archive) so the user can refine, overwrite, or archive the projected spec.
|
|
27
|
+
- If the user chooses option (b): proceed directly to generation (step 2) with a note that existing SDD sources were bypassed per user decision.
|
|
28
|
+
- If the user chooses option (a) and `/swarm sdd project` fails: report the failure and re-offer the choices.
|
|
29
|
+
1c. **AMBIGUOUS — multiple SDD sources detected.** Both `openspec/` AND `.specify/` exist with no native `.swarm/spec.md`. Per `readEffectiveSpecSync` semantics this is NOT an effective spec (the function returns null). Do NOT treat this as a single-source NON-NATIVE choice. Instead:
|
|
30
|
+
- Inform the user: "Multiple SDD sources detected (openspec AND speckit) but no native spec exists. This is ambiguous — there is no single effective spec. You must choose which source to project, or disambiguate via `/swarm sdd status --source`."
|
|
31
|
+
- Offer the user a choice:
|
|
32
|
+
- **(a) Project from openspec** — run `/swarm sdd project --source openspec` (after consent) to project the openspec source into `.swarm/spec.md`.
|
|
33
|
+
- **(b) Project from speckit** — run `/swarm sdd project --source speckit` (after consent) to project the speckit source into `.swarm/spec.md`.
|
|
34
|
+
- **(c) Cancel** — abort SPECIFY; the ambiguous sources remain as-is.
|
|
35
|
+
- After a successful projection (a or b): the spec is now NATIVE → route to step 1a (overwrite/refine/archive).
|
|
36
|
+
- After a failed projection: report the failure and re-offer the choices.
|
|
37
|
+
1d. **NO EFFECTIVE SPEC.** Proceed directly to generation (step 2).
|
|
38
|
+
1e. Run CODEBASE REALITY CHECK for any codebase references mentioned by the user or implied by the feature. Skip if work is purely greenfield (no existing codebase to check). Report discrepancies before proceeding to explorer.
|
|
21
39
|
2. Delegate to `the active swarm's explorer agent` to scan the codebase for relevant context (existing patterns, related code, affected areas).
|
|
22
40
|
3. Delegate to `the active swarm's sme agent` for domain research on the feature area to surface known constraints, best practices, and integration concerns.
|
|
23
41
|
4. Generate `.swarm/spec.md` capturing:
|
|
@@ -6,13 +6,15 @@ import {
|
|
|
6
6
|
checkPathBudget,
|
|
7
7
|
cleanUntrackedFiles,
|
|
8
8
|
isCleanWorktree,
|
|
9
|
+
isPathUnderSwarmWorktreeBase,
|
|
9
10
|
makeWorktreeBranchName,
|
|
10
11
|
provisionWorktree,
|
|
11
12
|
removeLaneProfileFromDiskReal,
|
|
12
13
|
removeWorktree,
|
|
14
|
+
resolveWorktreeBaseDir,
|
|
13
15
|
shortenWorktreePath,
|
|
14
16
|
writeLaneProfileToDiskReal
|
|
15
|
-
} from "./index-
|
|
17
|
+
} from "./index-3yk9196e.js";
|
|
16
18
|
import"./index-5e4e2hvv.js";
|
|
17
19
|
import"./index-p0arc26j.js";
|
|
18
20
|
import"./index-zgwm4ryv.js";
|
|
@@ -21,10 +23,12 @@ import"./index-a76rekgs.js";
|
|
|
21
23
|
export {
|
|
22
24
|
writeLaneProfileToDiskReal,
|
|
23
25
|
shortenWorktreePath,
|
|
26
|
+
resolveWorktreeBaseDir,
|
|
24
27
|
removeWorktree,
|
|
25
28
|
removeLaneProfileFromDiskReal,
|
|
26
29
|
provisionWorktree,
|
|
27
30
|
makeWorktreeBranchName,
|
|
31
|
+
isPathUnderSwarmWorktreeBase,
|
|
28
32
|
isCleanWorktree,
|
|
29
33
|
cleanUntrackedFiles,
|
|
30
34
|
checkPathBudget,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
createCuratorLLMDelegate
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-nq9h2t3x.js";
|
|
5
5
|
import"./index-wj4jeavn.js";
|
|
6
6
|
import"./index-5gnp8fyw.js";
|
|
7
7
|
import"./index-c8s9a3zh.js";
|
|
8
8
|
import"./index-fm7xz1ne.js";
|
|
9
|
-
import"./index-
|
|
10
|
-
import"./index-
|
|
9
|
+
import"./index-j2v3w1ds.js";
|
|
10
|
+
import"./index-scww5b77.js";
|
|
11
11
|
import"./index-yw8qpf51.js";
|
|
12
12
|
import"./index-85bacexr.js";
|
|
13
13
|
import"./index-09xpycan.js";
|
|
@@ -21,7 +21,7 @@ import"./index-bfwt3abw.js";
|
|
|
21
21
|
import"./index-wvrj16f0.js";
|
|
22
22
|
import"./index-q1exe2b3.js";
|
|
23
23
|
import"./index-x2q0gnt8.js";
|
|
24
|
-
import"./index-
|
|
24
|
+
import"./index-3yk9196e.js";
|
|
25
25
|
import"./index-r3j3458j.js";
|
|
26
26
|
import"./index-jtqkh8jf.js";
|
|
27
27
|
import"./index-5e4e2hvv.js";
|
|
@@ -11,13 +11,13 @@ import {
|
|
|
11
11
|
runCuratorInit,
|
|
12
12
|
runCuratorPhase,
|
|
13
13
|
writeCuratorSummary
|
|
14
|
-
} from "./index-
|
|
14
|
+
} from "./index-nq9h2t3x.js";
|
|
15
15
|
import"./index-wj4jeavn.js";
|
|
16
16
|
import"./index-5gnp8fyw.js";
|
|
17
17
|
import"./index-c8s9a3zh.js";
|
|
18
18
|
import"./index-fm7xz1ne.js";
|
|
19
|
-
import"./index-
|
|
20
|
-
import"./index-
|
|
19
|
+
import"./index-j2v3w1ds.js";
|
|
20
|
+
import"./index-scww5b77.js";
|
|
21
21
|
import"./index-yw8qpf51.js";
|
|
22
22
|
import"./index-85bacexr.js";
|
|
23
23
|
import"./index-09xpycan.js";
|
|
@@ -31,7 +31,7 @@ import"./index-bfwt3abw.js";
|
|
|
31
31
|
import"./index-wvrj16f0.js";
|
|
32
32
|
import"./index-q1exe2b3.js";
|
|
33
33
|
import"./index-x2q0gnt8.js";
|
|
34
|
-
import"./index-
|
|
34
|
+
import"./index-3yk9196e.js";
|
|
35
35
|
import"./index-r3j3458j.js";
|
|
36
36
|
import"./index-jtqkh8jf.js";
|
|
37
37
|
import"./index-5e4e2hvv.js";
|
package/dist/cli/{evidence-summary-service-bjhr75v0.js → evidence-summary-service-8tasw2tt.js}
RENAMED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
loadPlanJsonOnly,
|
|
7
7
|
mergeDurableGateEntriesFromEvidence,
|
|
8
8
|
readDurableGateEvidence
|
|
9
|
-
} from "./index-
|
|
10
|
-
import"./index-
|
|
9
|
+
} from "./index-j2v3w1ds.js";
|
|
10
|
+
import"./index-scww5b77.js";
|
|
11
11
|
import"./index-q1exe2b3.js";
|
|
12
12
|
import"./index-r3j3458j.js";
|
|
13
13
|
import"./index-jtqkh8jf.js";
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
handleGuardrailExplain
|
|
4
|
-
} from "./index-
|
|
5
|
-
import"./index-
|
|
4
|
+
} from "./index-wsg3vkss.js";
|
|
5
|
+
import"./index-nq9h2t3x.js";
|
|
6
6
|
import"./index-wj4jeavn.js";
|
|
7
7
|
import"./index-5gnp8fyw.js";
|
|
8
8
|
import"./index-c8s9a3zh.js";
|
|
9
9
|
import"./index-fm7xz1ne.js";
|
|
10
|
-
import"./index-
|
|
11
|
-
import"./index-
|
|
10
|
+
import"./index-j2v3w1ds.js";
|
|
11
|
+
import"./index-scww5b77.js";
|
|
12
12
|
import"./index-yw8qpf51.js";
|
|
13
13
|
import"./index-85bacexr.js";
|
|
14
14
|
import"./index-09xpycan.js";
|
|
@@ -22,7 +22,7 @@ import"./index-bfwt3abw.js";
|
|
|
22
22
|
import"./index-wvrj16f0.js";
|
|
23
23
|
import"./index-q1exe2b3.js";
|
|
24
24
|
import"./index-x2q0gnt8.js";
|
|
25
|
-
import"./index-
|
|
25
|
+
import"./index-3yk9196e.js";
|
|
26
26
|
import"./index-r3j3458j.js";
|
|
27
27
|
import"./index-jtqkh8jf.js";
|
|
28
28
|
import"./index-5e4e2hvv.js";
|
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
isHiveEligible,
|
|
6
6
|
promoteFromSwarm,
|
|
7
7
|
promoteToHive
|
|
8
|
-
} from "./index-
|
|
8
|
+
} from "./index-nq9h2t3x.js";
|
|
9
9
|
import"./index-wj4jeavn.js";
|
|
10
10
|
import"./index-5gnp8fyw.js";
|
|
11
11
|
import"./index-c8s9a3zh.js";
|
|
12
12
|
import"./index-fm7xz1ne.js";
|
|
13
|
-
import"./index-
|
|
14
|
-
import"./index-
|
|
13
|
+
import"./index-j2v3w1ds.js";
|
|
14
|
+
import"./index-scww5b77.js";
|
|
15
15
|
import"./index-yw8qpf51.js";
|
|
16
16
|
import"./index-85bacexr.js";
|
|
17
17
|
import"./index-09xpycan.js";
|
|
@@ -25,7 +25,7 @@ import"./index-bfwt3abw.js";
|
|
|
25
25
|
import"./index-wvrj16f0.js";
|
|
26
26
|
import"./index-q1exe2b3.js";
|
|
27
27
|
import"./index-x2q0gnt8.js";
|
|
28
|
-
import"./index-
|
|
28
|
+
import"./index-3yk9196e.js";
|
|
29
29
|
import"./index-r3j3458j.js";
|
|
30
30
|
import"./index-jtqkh8jf.js";
|
|
31
31
|
import"./index-5e4e2hvv.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
handleGuardrailExplain
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-wsg3vkss.js";
|
|
5
5
|
import {
|
|
6
6
|
handleGuardrailLog
|
|
7
7
|
} from "./index-vx76tcxe.js";
|
|
@@ -80,13 +80,13 @@ import {
|
|
|
80
80
|
handleWriteRetroCommand,
|
|
81
81
|
normalizeSwarmCommandInput,
|
|
82
82
|
resolveCommand
|
|
83
|
-
} from "./index-
|
|
83
|
+
} from "./index-nq9h2t3x.js";
|
|
84
84
|
import"./index-wj4jeavn.js";
|
|
85
85
|
import"./index-5gnp8fyw.js";
|
|
86
86
|
import"./index-c8s9a3zh.js";
|
|
87
87
|
import"./index-fm7xz1ne.js";
|
|
88
|
-
import"./index-
|
|
89
|
-
import"./index-
|
|
88
|
+
import"./index-j2v3w1ds.js";
|
|
89
|
+
import"./index-scww5b77.js";
|
|
90
90
|
import"./index-yw8qpf51.js";
|
|
91
91
|
import"./index-85bacexr.js";
|
|
92
92
|
import"./index-09xpycan.js";
|
|
@@ -104,7 +104,7 @@ import"./index-bfwt3abw.js";
|
|
|
104
104
|
import"./index-wvrj16f0.js";
|
|
105
105
|
import"./index-q1exe2b3.js";
|
|
106
106
|
import"./index-x2q0gnt8.js";
|
|
107
|
-
import"./index-
|
|
107
|
+
import"./index-3yk9196e.js";
|
|
108
108
|
import"./index-r3j3458j.js";
|
|
109
109
|
import"./index-jtqkh8jf.js";
|
|
110
110
|
import"./index-5e4e2hvv.js";
|
|
@@ -162,9 +162,39 @@ function makeWorktreeBranchName(sessionId, id, options) {
|
|
|
162
162
|
}
|
|
163
163
|
return `swarm/${options.purpose}/${sessionId}/${id}`;
|
|
164
164
|
}
|
|
165
|
+
function resolveWorktreeBaseDir(directory, worktreeDir) {
|
|
166
|
+
return worktreeDir ? path.resolve(directory, worktreeDir) : path.resolve(path.dirname(directory), ".swarm-worktrees");
|
|
167
|
+
}
|
|
168
|
+
function isPathUnderSwarmWorktreeBase(targetPath, directory, worktreeDirOverrides = []) {
|
|
169
|
+
const caseInsensitive = process.platform === "win32";
|
|
170
|
+
let realTarget;
|
|
171
|
+
try {
|
|
172
|
+
realTarget = fs.realpathSync(path.resolve(directory, targetPath));
|
|
173
|
+
} catch {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
const bases = [
|
|
177
|
+
resolveWorktreeBaseDir(directory),
|
|
178
|
+
...worktreeDirOverrides.map((d) => resolveWorktreeBaseDir(directory, d))
|
|
179
|
+
];
|
|
180
|
+
const cmpTarget = caseInsensitive ? realTarget.toLowerCase() : realTarget;
|
|
181
|
+
return bases.some((base) => {
|
|
182
|
+
let realBase;
|
|
183
|
+
try {
|
|
184
|
+
realBase = fs.realpathSync(base);
|
|
185
|
+
} catch {
|
|
186
|
+
realBase = path.resolve(base);
|
|
187
|
+
}
|
|
188
|
+
const cmpBase = caseInsensitive ? realBase.toLowerCase() : realBase;
|
|
189
|
+
if (cmpTarget === cmpBase)
|
|
190
|
+
return true;
|
|
191
|
+
const rel = path.relative(cmpBase, cmpTarget);
|
|
192
|
+
return rel.length > 0 && !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
165
195
|
async function provisionWorktree(directory, id, sessionId, options) {
|
|
166
196
|
const branchName = makeWorktreeBranchName(sessionId, id, options);
|
|
167
|
-
let worktreePath =
|
|
197
|
+
let worktreePath = path.resolve(resolveWorktreeBaseDir(directory, options.worktreeDir), sessionId, id);
|
|
168
198
|
const budgetResult = await checkPathBudget(worktreePath, directory);
|
|
169
199
|
if (budgetResult.ok === false) {
|
|
170
200
|
if (options.worktreeDir) {
|
|
@@ -285,7 +315,7 @@ async function provisionWorktree(directory, id, sessionId, options) {
|
|
|
285
315
|
sessionId
|
|
286
316
|
};
|
|
287
317
|
}
|
|
288
|
-
async function removeWorktree(worktreePath, projectRoot) {
|
|
318
|
+
async function removeWorktree(worktreePath, projectRoot, options) {
|
|
289
319
|
const isWindows = _internals.platform === "win32";
|
|
290
320
|
const MAX_RETRIES = 4;
|
|
291
321
|
const RETRY_DELAY_MS = 2000;
|
|
@@ -300,6 +330,13 @@ async function removeWorktree(worktreePath, projectRoot) {
|
|
|
300
330
|
await _internals.sleep(RETRY_DELAY_MS);
|
|
301
331
|
continue;
|
|
302
332
|
}
|
|
333
|
+
if (options?.force && isPathUnderSwarmWorktreeBase(worktreePath, projectRoot, options.worktreeDir ? [options.worktreeDir] : [])) {
|
|
334
|
+
const forced = await runGit(["worktree", "remove", "--force", worktreePath], projectRoot);
|
|
335
|
+
if (forced.exitCode === 0) {
|
|
336
|
+
return { success: true };
|
|
337
|
+
}
|
|
338
|
+
return { error: forced.stderr.trim() || forced.stdout.trim() };
|
|
339
|
+
}
|
|
303
340
|
return { error: lastError };
|
|
304
341
|
}
|
|
305
342
|
return { error: lastError };
|
|
@@ -415,4 +452,4 @@ async function assertCleanWorkingTree(directory) {
|
|
|
415
452
|
return { clean: true };
|
|
416
453
|
}
|
|
417
454
|
|
|
418
|
-
export { _internals, writeLaneProfileToDiskReal, removeLaneProfileFromDiskReal, checkPathBudget, shortenWorktreePath, makeWorktreeBranchName, provisionWorktree, removeWorktree, isCleanWorktree, autoCommitDirty, cleanUntrackedFiles, assertCleanWorkingTree };
|
|
455
|
+
export { _internals, writeLaneProfileToDiskReal, removeLaneProfileFromDiskReal, checkPathBudget, shortenWorktreePath, makeWorktreeBranchName, resolveWorktreeBaseDir, isPathUnderSwarmWorktreeBase, provisionWorktree, removeWorktree, isCleanWorktree, autoCommitDirty, cleanUntrackedFiles, assertCleanWorkingTree };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
readEffectiveSpecSync
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-scww5b77.js";
|
|
5
5
|
import {
|
|
6
6
|
_internals,
|
|
7
7
|
buildDefaultPolicy,
|
|
@@ -2339,7 +2339,7 @@ async function takeSnapshotWithRetry(directory, plan, options) {
|
|
|
2339
2339
|
} catch {}
|
|
2340
2340
|
}
|
|
2341
2341
|
async function takeSnapshotEvent(directory, plan, options) {
|
|
2342
|
-
const payloadHash = computePlanHash(plan);
|
|
2342
|
+
const payloadHash = options?.payloadHashOverride ?? computePlanHash(plan);
|
|
2343
2343
|
const snapshotPayload = {
|
|
2344
2344
|
plan,
|
|
2345
2345
|
payload_hash: payloadHash
|
|
@@ -2536,6 +2536,9 @@ async function quarantineLedgerSuffix(directory, badSuffix) {
|
|
|
2536
2536
|
}
|
|
2537
2537
|
async function loadLastApprovedPlan(directory, expectedPlanId) {
|
|
2538
2538
|
const events = await readLedgerEvents(directory);
|
|
2539
|
+
return findLastApprovedSnapshot(events, expectedPlanId);
|
|
2540
|
+
}
|
|
2541
|
+
function findLastApprovedSnapshot(events, expectedPlanId, extraFilter) {
|
|
2539
2542
|
if (events.length === 0) {
|
|
2540
2543
|
return null;
|
|
2541
2544
|
}
|
|
@@ -2558,6 +2561,9 @@ async function loadLastApprovedPlan(directory, expectedPlanId) {
|
|
|
2558
2561
|
continue;
|
|
2559
2562
|
}
|
|
2560
2563
|
}
|
|
2564
|
+
if (extraFilter && !extraFilter(payload)) {
|
|
2565
|
+
continue;
|
|
2566
|
+
}
|
|
2561
2567
|
return {
|
|
2562
2568
|
plan: payload.plan,
|
|
2563
2569
|
seq: event.seq,
|