Swarm tracks two kinds of knowledge:
- Swarm knowledge — project-specific lessons learned during a session. Lives in
.swarm/knowledge.jsonl. - Hive knowledge — evergreen lessons that have been confirmed across projects. Lives in your user data directory.
When an architect receives a new message, entries from both stores are merged and deduplicated before injection.
v2 actionable directives. Knowledge entries can carry optional fields (
triggers,required_actions,forbidden_actions,applies_to_tools,applies_to_agents,directive_priority,generated_skill_path). The Architect receives these as a structured<swarm_knowledge_directives>block and must acknowledge each applicable directive (KNOWLEDGE_APPLIED) or explicitly close it as not-applicable, skipped, or violated (KNOWLEDGE_N_A reason=.../KNOWLEDGE_IGNORED reason=.../KNOWLEDGE_VIOLATED reason=...). See Actionable directives and Knowledge application contract below.
.swarm/knowledge.jsonl # active entries
.swarm/knowledge-rejected.jsonl # entries that failed validation
.swarm/knowledge-receipts-v2.jsonl # authoritative receipt journal
.swarm/knowledge-receipts-v2.snapshot.json # rebuildable live-state checkpoint
.swarm/knowledge-receipts-v2-archive.jsonl # closed receipt summaries
The V2 receipt files are always anchored to this canonical project root. They never follow a knowledge link and never move to the hive or a cohort store.
Resolved platform-specifically:
| Platform | Path |
|---|---|
| Linux | $XDG_DATA_HOME/opencode-swarm/shared-learnings.jsonl (default ~/.local/share/opencode-swarm/) |
| macOS | ~/Library/Application Support/opencode-swarm/shared-learnings.jsonl |
| Windows | %LOCALAPPDATA%\opencode-swarm\Data\shared-learnings.jsonl |
Rejected hive lessons: shared-learnings-rejected.jsonl next to the main file.
An opt-in knowledge store shared between worktrees of the same project (or between similar projects that share a link name). Lives beside the hive store:
<dataDir>/links/<linkId>/
knowledge.jsonl
knowledge-rejected.jsonl
knowledge-retractions.jsonl
...
Each linked worktree keeps a pointer at .swarm/link.json:
{ "version": 1, "linkId": "<id>", "createdAt": "<ISO-8601>", "source": "manual" }| Command | Purpose |
|---|---|
/swarm link [name|status] |
Link this worktree. Default name is the project hash (all worktrees of one repo share a store). Supply a name to tie similar projects together. |
/swarm link status |
Show current link state. |
/swarm unlink [--no-copy] |
Unlink. By default, copies shared lessons back to the local swarm store first; --no-copy skips that copy. |
At session start, if multiple worktrees for the same project are detected, the Architect emits a one-time, non-blocking suggestion to link. The suggestion is suppressed after the first display per session.
When linked, the following files are redirected to the shared store:
knowledge.jsonlknowledge-rejected.jsonlknowledge-retractions.jsonl- Counter baseline
- Quarantine
- Unactionable queue
- Application log
The following remain per-worktree (not redirected):
.knowledge-shown.json- V2 receipt journal, snapshot, archive, and receipt lock
- Plan (
.swarm/plan.json,.swarm/plan.md) - Evidence (
.swarm/evidence/)
- 2-second cache TTL. Cross-process writes by another linked worktree may not be visible for up to 2 seconds. If you just wrote a lesson and another worktree queries immediately, it may see the prior state.
- Manual cleanup.
<dataDir>/links/accumulates one directory per linkId. After all peers unlink, the shared store is not auto-deleted. Prune abandoned stores manually:rm -rf <dataDir>/links/<old-link-id>/ - Outcome-history reset. When a worktree first links, outcome-history counters in the shared store re-accrue from zero. This is a documented "self-healing ranking effect" — prior per-worktree counters do not carry over, so the shared store's confidence signal starts fresh.
- Close-stage behavior.
/swarm closeskips archiving shared knowledge-family artifacts when linked, because peers may still be active. - Orphaned local file diagnostic. When linked,
.swarm/knowledge.jsonlis ignored unless it is the link-store symlink. If a regular local.swarm/knowledge.jsonlremains beside.swarm/link.json, the link resolver emits a debug warning and reports the shared-store path as authoritative.
Every entry is a JSON line with these fields (see src/hooks/knowledge-types.ts):
{
"id": "lesson-abc123",
"tier": "swarm",
"lesson": "Prefer stream.Readable.from(generator) over async iterators for backpressure.",
"category": "architecture",
"tags": ["node", "streams"],
"scope": "global",
"confidence": 0.9,
"status": "established",
"confirmed_by": ["..."],
"retrieval_outcomes": [],
"phases_alive": 0,
"max_phases": 10
}Swarm entries add project_name and a PhaseConfirmationRecord[]. Hive entries add source_project and encounter_score.
Entries can be created four ways:
- Agents write via
knowledge_addtool — normal flow during a phase. - Manual:
/swarm promote "<lesson>"— write directly to hive. - Curator recommends — LLM-driven promotions from the curator agent.
- Migration:
/swarm knowledge migrate— one-time import from legacy.swarm/context.md.
Near-duplicate lessons are not discarded when they match an active swarm entry.
The curator and knowledge_add reinforce the existing entry by appending one
confirmed_by record for the current phase when that phase is not already
present. Same-phase repeats are idempotent no-ops. A real new phase
confirmation refreshes updated_at, resets phases_alive to 0, and
recomputes confidence from the distinct phase-confirmation count.
Retrieval is also a confirmation signal (#1768): when the architect auto-
injection path or a delegate injection surfaces an entry during a phase, that
entry accrues a confirmed_by record for the phase (same dedup and confidence
recompute as reinforcement). This lets multi-phase confirmation accumulate from
normal loop activity, not only from near-duplicate re-adds. History is capped at
MAX_CONFIRMED_BY (50) records per entry.
Three routes in checkHivePromotions():
| Route | Trigger |
|---|---|
| Explicit | hive_eligible=true AND ≥3 distinct phases confirmed |
| Fast-track | Entry tagged hive-fast-track (bypasses phase count) |
| Age-based | Entry age ≥ auto_promote_days (default 90) |
Manual: /swarm promote --from-swarm <id>.
Quarantined entries are hidden from queries but preserved:
/swarm knowledge quarantine lesson-abc123 "false positive"
/swarm knowledge restore lesson-abc123/swarm knowledge restore <id> also restores archived entries: an
archived-status entry is restored to its pre-archive lifecycle status (e.g.
established or promoted), recording the prior status at archive time. This
is useful for recovering an erroneously archived entry without losing its
confirmations or standing.
Quarantined, archived, and quarantined_unactionable entries are inactive for
query injection, hive promotion, and hive encounter-score reinforcement. New
near-duplicate evidence reinforces only active entries; inactive duplicates stay
archived/quarantined and a fresh candidate can be created instead.
At every successful phase completion, sweepAgedEntries() runs:
- Increments
phases_aliveon all active entries. - Archives entries whose
phases_alive > max_phases. - TTLs:
default_max_phases(10) for most,todo_max_phases(3) fortodo-category entries. - Promoted entries are TTL-exempt — they live until explicitly quarantined.
TODO entries are removed, not archived, after their TTL.
Knowledge is injected into the architect's prompt at phase start via createKnowledgeInjectorHook(). The reader merges swarm + hive with near-duplicate removal (Jaccard bigram similarity).
Injection budget adapts to available context:
| Context headroom | Regime |
|---|---|
| >60% | Full budget — up to max_inject_count entries, inject_char_budget chars |
| 20–60% | Half — half the entries, half the chars |
| 5–20% | Quarter — minimal injection |
| <5% | Skipped |
Defaults:
max_inject_count: 5 entriesinject_char_budget: 2000 chars totalmax_lesson_display_chars: 120 chars per lesson (truncation only; stored text unchanged)dedup_threshold: 0.6
All keys live under knowledge.* in your config (see src/config/schema.ts:1043):
| Key | Type | Default | Purpose |
|---|---|---|---|
enabled |
bool | true |
Master switch |
swarm_max_entries |
int | 100 | FIFO cap per project |
hive_max_entries |
int | 200 | FIFO cap cross-project |
auto_promote_days |
int | 90 | Age threshold for auto-promotion |
max_inject_count |
int | 5 | Max entries per injection |
inject_char_budget |
int | 2000 | Total injection block size |
max_lesson_display_chars |
int | 120 | Per-lesson truncation |
dedup_threshold |
float | 0.6 | Near-duplicate detection |
scope_filter |
array | ["global"] |
Scope tags to include |
hive_enabled |
bool | true |
Read/write hive knowledge |
validation_enabled |
bool | true |
Run 3-layer validator |
evergreen_confidence |
float | 0.9 | Confidence threshold for evergreen |
evergreen_utility |
float | 0.8 | Utility score for evergreen |
low_utility_threshold |
float | 0.3 | Flag for removal at or below |
min_retrievals_for_utility |
int | 3 | Retrievals before utility scoring |
receipt_close_grace_days |
int | 7 | Days to retain resolved receipt membership after its phase closes (0-3650) |
default_max_phases |
int | 10 | General TTL |
todo_max_phases |
int | 3 | TODO-category TTL |
same_project_weight |
float | 1.0 | Encounter score (source project) |
cross_project_weight |
float | 0.5 | Encounter score (other projects) |
enrichment.max_calls_per_day |
int | 30 | Dedicated daily quota for curator/close-time/unactionable-hardening LLM enrichment of plain prose into actionable directives |
enrichment.quota_window |
"utc"/"local" |
"utc" |
Calendar window for the enrichment quota |
/swarm knowledge migrate imports from legacy .swarm/context.md into .swarm/knowledge.jsonl. Idempotent — a sentinel file .swarm/.knowledge-migrated prevents re-runs.
Receipt migration is separate and automatic. The first receipt-ledger operation
performs a lazy, lock-protected cutover; plugin initialization does no migration
I/O. Only complete, still-live retrievals from this project's local legacy event
file are imported. Missing, evicted, linked, malformed, or partial legacy state
is recorded as typed legacy_unverifiable uncertainty and is never reconstructed
from counters or inferred from cohort data. V2 is read first throughout cutover,
and legacy access is restricted to the explicit imported pre-cutover trace set
until that set drains to zero.
Legacy sections imported (each bullet's category is inferred from its text into a current
KnowledgeCategory — see inferCategoryFromText in src/hooks/knowledge-migrator.ts):
lessons-learnedpatternssme-cachedecisions
Entries that fail schema validation are dropped. Near-duplicates are collapsed via the dedup threshold.
Three layers enforce quality. An entry must pass all three or it lands in -rejected.jsonl:
- Structural — length 15–280 chars, valid category, scope, confidence in [0,1].
- Content safety — rejects dangerous commands (
rm -rf,mkfs,chmod 777,eval, etc.) even in lesson text. - Semantic — flags vague lessons (no technical reference + no action verb) and contradictions with existing entries.
Entries with confidence ≥ 0.9 AND utility_score ≥ 0.8 are marked evergreen. They survive curation sweeps without review.
Calculated after min_retrievals_for_utility retrievals (default 3). Entries at or below low_utility_threshold (default 0.3) with shown_count ≥ 5 are flagged for removal. (Pre-v2 entries used applied_count as the dominant signal; the v1→v2 normalizer copies the legacy field into shown_count on read so historical entries continue to trip this audit correctly. New code must read shown_count for "saw it" and applied_explicit_count for "applied it".)
Hive entries track an encounter_score weighted by project:
- Same project encounter: ×
same_project_weight(default 1.0) - Cross-project encounter: ×
cross_project_weight(default 0.5)
This prevents one noisy project from dominating hive promotions.
| Command | Purpose |
|---|---|
/swarm knowledge |
List active entries |
/swarm knowledge migrate |
Import legacy .swarm/context.md |
/swarm knowledge quarantine <id> [reason] |
Hide entry from queries |
/swarm knowledge restore <id> |
Restore a quarantined or archived entry |
/swarm promote <text> |
Write new hive entry |
/swarm promote --from-swarm <id> |
Promote existing swarm entry |
/swarm curate |
Run curator review, apply gated recommendations when session context exists, and review hive promotion candidates |
See Commands Reference for full flag details.
- Architecture Deep Dive — knowledge in the control loop
- Evidence and Telemetry — how retrieval outcomes feed utility scoring
- Configuration Reference — full
knowledge.*schema
A v2 entry can carry optional metadata that turns a passive lesson into an actionable directive. All fields are optional and v1 entries continue to read without migration.
| Field | Type | Purpose |
|---|---|---|
triggers |
string[] |
Phrases that surface the directive when the current task / tool / agent context matches. |
required_actions |
string[] |
What the architect / subagent MUST do when the trigger fires. |
forbidden_actions |
string[] |
What the directive forbids. |
applies_to_agents |
string[] |
Agent role names (snake_case) the directive applies to. |
applies_to_tools |
string[] |
Tool names the directive applies to. |
verification_checks |
string[] |
Reviewer / test_engineer / runtime checks. |
directive_priority |
"low"|"medium"|"high"|"critical" |
Ranking + enforcement weight. |
source_refs |
string[] |
Pointers (file:line, plan section). Sanitized; no path traversal. |
source_knowledge_ids |
string[] |
UUIDs of source entries (for derived/clustered entries). |
generated_skill_slug |
string |
Slug of compiled SKILL.md. |
generated_skill_path |
string |
Repo-local path to compiled SKILL.md. Must live under .opencode/skills/generated/ or .swarm/skills/proposals/. |
last_applied_at |
ISO 8601 | Updated by recordAcknowledgment("applied"). |
last_acknowledged_at |
ISO 8601 | Updated by any explicit ack. |
Retrieval-outcome counters now distinguish:
shown_count— included in an injection block.acknowledged_count— any explicit ack received.applied_explicit_count— explicitKNOWLEDGE_APPLIED.ignored_count— explicitKNOWLEDGE_IGNORED(relevant but deliberately not followed).n_a_count— explicitKNOWLEDGE_N_A(not applicable; neutral, event-log only — intentionally stripped before per-entryretrieval_outcomes, so it is never persisted on entries and never reaches the outcome signal).violated_count— explicitKNOWLEDGE_VIOLATED(or runtime-inferred).succeeded_after_shown_count— phase succeeded after this entry was shown.failed_after_shown_count— phase failed after this entry was shown.
Frozen legacy fields (kept on disk for backward compatibility, never auto-incremented in v2):
applied_count— pre-v2 it was bumped on every "shown" event. The v1→v2 normalizer copies the historical value intoshown_counton read.succeeded_after_count/failed_after_count— replaced bysucceeded_after_shown_count/failed_after_shown_count.
If you have analytics or downstream tooling that reads applied_count,
migrate it to shown_count (for "shown") or applied_explicit_count (for
"actually applied"). The frozen fields still exist on disk; they will not
change after this release.
The injector now uses
searchKnowledge({ directory, config, context: ctx, mode: 'auto_injection', agent: 'architect' })
where ctx carries the current phase, task id, tool/action name, target agent,
file paths, recent reviewer/test failures, declared scope, and a mode value
(phase_start, delegation, tool_before, phase_complete, manual_recall,
curator). The unified searchKnowledge service supersedes the earlier
readContextualKnowledge helper and is also used by knowledge_recall.
Ranking rules:
- A
directive_priority: "critical"entry whose trigger / tool / agent matches the context is forced into the top-N within budget. - An entry whose
confidence >= directive_min_confidenceand whoseapplies_to_tools/applies_to_agentsmatches the current action gets a strong rank boost. - Entries with an active
generated_skill_pathare preferred over raw lesson repetition. - Archived entries are excluded (also enforced by
knowledge_recall).
knowledge_recall returns each result with a score_breakdown object so
agents and diagnostics can see how the final score was composed. The breakdown
includes text, metadata, directive, confidence, generated-skill, outcome,
cold-start, synonym, trigger-recall, status, and final-score components.
Cache key: phase + tool + action + targetAgent + taskId + filePaths hash + SHA-1(ctx.lastUserMessage)[:16]. The phase-only cache from v1 has been retired. The user-message hash invalidates the cache between user turns and within a turn (e.g., after message compaction).
The Architect now receives a structured directive block. Its receipt metadata
also carries the originating retrieval trace so acknowledgments join the exact
(trace_id, entry_id) pair rather than an entry ID from another display:
<swarm_knowledge_directives>
- id: <uuid>
confidence: 0.94
priority: critical
trigger: coder delegation modifying source files
required: call declare_scope before coder delegation
forbidden: bash/eval/heredoc file writes
skill: file:.opencode/skills/generated/scope-discipline/SKILL.md
verification: reviewer must reject scope bypass
</swarm_knowledge_directives>
The Architect prompt requires inspecting this block before:
- Producing or saving a plan (
save_plan). - Updating a task status (
update_task_status). - Delegating to coder, reviewer, test_engineer, sme, docs, or designer.
- Calling
phase_complete. - Escalating or invoking
skill_improve.
For each applicable directive, the Architect emits the exact colon-separated token pair
carried by the block above (the (trace_id, entry_id) pair, not a bare entry id):
KNOWLEDGE_APPLIED:<trace_id>:<entry_id>— directive observed in the next compliant action.KNOWLEDGE_N_A:<trace_id>:<entry_id> reason=<short>— does not apply this turn (neutral).KNOWLEDGE_IGNORED:<trace_id>:<entry_id> reason=<short>— judged relevant but deliberately not followed (counts against the directive).KNOWLEDGE_CONTRADICTED:<trace_id>:<entry_id> reason=<short>— current authority or repository evidence disproves it.KNOWLEDGE_VIOLATED:<trace_id>:<entry_id> reason=<short>— runtime evidence shows it was breached.
Chat-text markers (KNOWLEDGE_APPLIED/N_A/IGNORED/CONTRADICTED/VIOLATED) are the sole mechanism that
satisfies the knowledge-application enforcement gate. The knowledge_receipt tool
(which replaced the former knowledge_ack) records knowledge-usage receipts for
audit — including applied/ignored/n_a/contradicted outcomes and new-lesson persistence
— but does NOT satisfy the enforcement gate.
Every producer and consumer of knowledge terminals shares one typed contract
(ReceiptOutcome + ReceiptSourceCode, declared once in
src/hooks/knowledge-receipt-ledger.ts):
| Outcome | Meaning | Outcome signal | Gate obligation |
|---|---|---|---|
applied |
the directive was followed | positive | clears |
ignored |
judged relevant but deliberately not followed | negative | clears only with a reason |
n_a |
not applicable to the current action | neutral | clears only with a reason |
contradicted |
current authority or evidence disproves it | negative | blocks phase until remediated |
violated |
runtime evidence shows a breach | negative | blocks phase until remediated |
n_a clears ONLY the acknowledgement/applicability obligation. It never
proves application, never produces promotion evidence, and never satisfies
high-risk acceptance on its own; delegate self-report remains non-independent,
with reviewer adjudication and deterministic test/evidence as the independent
verification roles. unacknowledged (silent non-critical exposure) and
no_relevant (empty-trace tombstone) are audit-only event types, never
terminal outcomes.
source records WHO produced a terminal as a provenance class, distinct from
the agent identity field: delegate (the exposed agent's own self-report —
stamped on every delegate terminal in both the V2 ledger and the diagnostic
event log), reviewer (independent verdict), architect /
architect_marker, test_engineer, phase_override,
application_gate_staleness_release / application_gate_denial_limit_release
(nonterminal, audited gate releases), manual, migration, and unknown. Legacy records
with an absent or ambiguous source stay unknown — never coerced to
delegate, ignored, or zero. The knowledge_receipt_transition
observability payload carries receiptSemantics (currently 2) versioning
this meaning contract.
One documented asymmetry is preserved by design: the V2 ledger and the
diagnostic event log store n_a verbatim, while the legacy
knowledge-application.jsonl audit log has no n_a result value and
down-converts it to acknowledged (knowledge-application.ts). Historical
records are never rewritten.
Correctness-critical receipt state is committed to the canonical project's V2 journal before a directive is displayed or a terminal is accepted:
.swarm/knowledge-receipts-v2.jsonl
Only the exact final displayed set becomes retrieval membership; candidates removed by ranking, filtering, or caps create no obligation. If optional injection membership cannot be committed, the block is not displayed. Terminal validation and commit are one cross-process-locked transition, so duplicate, late, and reordered outcomes are idempotent or explicitly rejected. Best-effort legacy, counter, promotion, and observability writes occur only after the correctness lock is released.
The journal never evicts live membership because of an event-count cap. Its
2,000-record threshold triggers a self-contained checkpoint rewrite; it is not
a retention limit. Resolved state remains protected until its phase is durably closed and
knowledge.receipt_close_grace_days has elapsed. Eligible closed summaries move
to the separate project-local receipt archive; a bounded audit tail and a
rebuildable snapshot do not share the diagnostic event budget.
Receipt reads never repair authority as a side effect. An unterminated final
line, interior corruption, hash mismatch, unsupported schema, permission error,
or unavailable store fails receipt gates closed with a typed recovery action.
The architect may then call repair_knowledge_receipt_ledger for the exact
phase and session with a substantive reason. Readable authority only rebuilds
the derived snapshot. Corrupt authority is captured byte-for-byte in a bounded
append-only quarantine, and only its validated hash-chain prefix is installed.
The repaired phase/session remains explicitly uncertain and blocked until the
architect performs a comprehensive knowledge_recall for the affected scope
and supplies the exact repair_id, phase, optional task, and
scope_complete: true in repair_re_evaluation. Ordinary recall, automatic
injection, partial searches, wrong repair IDs, and unrelated empty retrievals
remain untagged and cannot clear the uncertainty.
Do not hand-edit or truncate the authoritative journal or its quarantine. Free disk space or correct permissions when writes fail, then retry the typed recovery action. When immutable quarantine capacity is exhausted, archive it out of band before retrying; the repair tool never discards an older record to make room. The snapshot is explicitly rebuildable and is never a substitute for the journal or archive.
Operational knowledge observations continue to be appended to:
.swarm/knowledge-events.jsonl
This FIFO is diagnostics only. Churn or eviction in it cannot change receipt validation, application/phase gates, promotion evidence, escalation, or curation decisions. Linked stores may still redirect this diagnostic stream, but can never redirect or satisfy project-local V2 receipt authority.
Issue #2032 normalized outcome and source meanings into one typed contract
(see Outcome and source semantics
above). V2 preserves the producer's typed value and records unknown when
the producer has no stronger source; it never reinterprets or coerces legacy
semantics — historical records keep the meanings they were written with.
In enforce mode the gate (knowledgeApplicationGateBefore in
src/hooks/knowledge-application-gate.ts) blocks high-risk actions when a
critical directive was shown but received no terminal acknowledgment
(KNOWLEDGE_APPLIED, KNOWLEDGE_N_A, KNOWLEDGE_IGNORED, or
KNOWLEDGE_VIOLATED) — bounded by
two escape hatches so the gate cannot deadlock a session forever:
max_gate_denials(default5) — after this many consecutive denials against the same unacknowledged critical-directive set (keyed by the stable entry-id set, so re-injecting the same directive under a fresh trace does not reset the count — #2398), the gate records a nonterminal gate release for the pending directives and lets the action through without claiming they were applied.gate_staleness_ms(default600000, 10 minutes) — a critical directive shown longer ago than this receives the same nonterminal release, regardless of denial count. A release does not reset the denial budget: a fresh re-display of the same entry continues the accumulated count (#2398).
Both auto-clears write an audit event to .swarm/events.jsonl
(knowledge_application_gate_denial_limit_clear or
knowledge_application_gate_staleness_clear) before the action proceeds — the
bypass is never silent (the /swarm reset-session escape writes
knowledge_application_gate_session_reset_clear the same way). These are
safety nets against a broken acknowledgment
pipeline (e.g. an ack marker that failed to parse), not a routine escape path:
enforcement still applies for the first max_gate_denials attempts or the
full gate_staleness_ms window. A release is not a terminal outcome, does not
count as application or promotion evidence, and does not erase the original
directive membership.
high_risk_tools — optionally override the set of tools that trigger the
acknowledgment gate. When absent, defaults to ["save_plan", "update_task_status", "phase_complete", "task", "Task"]. Narrow the list to
reduce noise; expand it to cover additional tools that should require knowledge
directive acknowledgment before executing.
Mature, repeated, high-confidence knowledge can be compiled into a SKILL.md
that subagents load via the existing SKILLS: delegation field.
| Tool | Purpose |
|---|---|
skill_generate |
Compile candidates into draft (.swarm/skills/proposals/<slug>.md) or active (.opencode/skills/generated/<slug>/SKILL.md) skills. |
skill_list |
List drafts and active generated skills. |
skill_apply |
Activate a draft into .opencode/skills/generated/<slug>/SKILL.md. |
skill_inspect |
Print a skill body with source knowledge IDs. |
skill_regenerate |
Rebuild an active generated skill from current source knowledge. |
.swarm/skills/proposals/<slug>.md # drafts (curator + skill_improver)
.swarm/skills/evals/<slug>/*.json # optional validation fixtures
.swarm/skills/rejected-edits.jsonl # FIFO buffer for rejected candidates
.opencode/skills/generated/<slug>/SKILL.md # active generated skills
Generated files include the marker
<!-- generated by opencode-swarm skill-generator. ... -->
skill_apply will refuse to overwrite an active SKILL.md that lacks this
marker (i.e. one a human authored or modified) unless force=true is passed.
Generated frontmatter includes triggers: when source knowledge provided
trigger phrases. The propagation scorer treats those phrases as bounded literal
hints, so a task such as "fix the biome lint config" can surface a skill whose
frontmatter includes triggers: ["biome", "lint config"].
Entries with directive_priority: critical or directive_priority: high are eligible
for a fast-track maturity path (added in issue #1477):
- Minimum 1 distinct phase confirmation (not the standard 2), AND
- Confidence ≥ 0.60 (
HIGH_PRIORITY_SKILL_MIN_CONFIDENCE, vs. the standard 0.70 floor)
Without this path, a critical entry that was only auto-generated (1 phase, 0.6 confidence)
would fail the standard gate on both dimensions simultaneously — the 0.7 confidence floor
AND the 2-phase confirmation requirement — making it impossible to mature regardless of
how valuable the directive is.
Net-negative outcome signal (computeOutcomeSignal < 0) still blocks even high-priority
entries. The standard gate for medium/low entries is unchanged.
Optional eval fixtures under .swarm/skills/evals/<slug>/*.json gate generated
skill changes when callers pass evaluate=true. Each fixture can be a single
case, an array, or { "cases": [...] }; cases support
required_phrases and forbidden_phrases.
When no eval set exists, the gate fails open and reports unevaluated. When an
incumbent active skill exists, the candidate must strictly improve the incumbent
before skill_apply, active skill_generate, skill_regenerate, or automatic
skill revision writes anything. Rejected candidates are recorded in the bounded
.swarm/skills/rejected-edits.jsonl buffer.
When curator.skill_generation_enabled is true (default), the curator's
phase analysis can emit skill_candidates and knowledge_application_findings
JSON blocks that are parsed strictly. Malformed JSON is skipped without writes
and reported through debug-gated curator diagnostics.
High-confidence candidates (>= curator.min_skill_confidence) trigger
skill_generate in draft mode; activation always requires a human or
architect to call skill_apply.
When a caller supplies explicit source_knowledge_ids, the IDs are treated as
one requested thematic cluster and compiled into one skill. The semantic
similarity clusterer is only used for automatic candidate discovery when no
explicit source IDs are supplied.
The system enhancer injects an architect-only [SWARM LEARNING NUDGE] during
longer sessions. The nudge is cadence-gated by total session tool calls and
asks the architect to decide whether the current work revealed a durable,
actionable lesson that should be captured immediately with knowledge_add or
left for curator phase/postmortem review.
This is intentionally not automatic skill mutation. It mirrors the safe part of Hermes-style in-session learning - review while context is fresh - but keeps opencode-swarm's existing boundaries:
knowledge_addremains the write path for active knowledge and still runs validation, deduplication, reinforcement, and quarantine rules.- The curator remains the review engine for evidence-level learning. Phase and
postmortem curator passes can emit
knowledge_application_findings,skill_candidates, draft skills, skill revisions, and stale-skill signals. skill_improveremains quota-bounded and proposal/draft gated; generated skills are never auto-activated.- Transient environment failures, stale negative tool claims, and one-off repo accidents should not be captured unless they have a reusable trigger.
Configure the cadence under knowledge.realtime_learning_nudge:
"knowledge": {
"realtime_learning_nudge": {
"enabled": true,
"first_after_tool_calls": 10,
"repeat_after_tool_calls": 25
}
}An entry passes the maturity gate according to the following decision logic:
- Negative outcome signal — Entries with
computeOutcomeSignal < 0are rejected regardless of confirmations or confidence. - Strong outcome bypass — entries with a strong outcome record (
applied_explicit_count >= 3orsucceeded_after_shown_count >= 3) and a strictly positive outcome signal (computeOutcomeSignal > 0) bypass all remaining gates and are accepted regardless of confidence or confirmation count. Negative signals are still rejected by step 1 regardless of outcome record strength. - Legacy AND gates — for entries that reach this step, confidence must be >=
min_skill_confidence(unless a strong outcome record is present, which bypasses the confidence floor), and either distinct phases >=min_skill_confirmationsor a strong outcome record is present. When no strong outcome record is present, both conditions must hold independently; neither alone is sufficient.
Configuration keys (under curator config):
min_skill_confidence: Confidence floor for candidates (default0.70). Configurable via config schema.min_skill_confirmations: Minimum distinct phases required for non-strong entries (default2). Configurable via config schema.
Singleton promotion — After the maturity gate above, 1-entry clusters are evaluated for promotion by isSkillSingletonEligible during clustering. A singleton passes this post-maturity check if it is a high/critical priority directive or has a strong outcome record (applied/succeeded >= 3). This allows well-evidenced singletons to become skills even when they haven't accumulated multiple confirmations yet.
See Generated Skills for the generated-skill lifecycle and file layout.
The skill_improver agent runs rare, high-capability reviews of accumulated
knowledge / skills / spec / architect prompt under a hard daily quota.
When invoked the skill_improve tool dispatches the registered
skill_improver agent on an ephemeral OpenCode session (same pattern as
the curator LLM delegate). The agent's prompt requires it to emit a markdown
proposal with sections: Inventory snapshot, Repeated ignored or violated
directives, Concrete recommendations, Optional cluster suggestions, Risks.
"skill_improver": {
"enabled": false,
"max_calls_per_day": 10,
"trigger": "manual",
"consolidation_interval_hours": 24,
"consolidation_max_calls_per_run": 1,
"targets": ["skills", "spec", "architect_prompt", "knowledge"],
"write_mode": "proposal", // 'proposal' (no source mutation) | 'draft_skills'
"require_user_approval": true,
"quota_window": "utc", // 'utc' (default) | 'local'
"allow_deterministic_fallback": true
}Set the agent model under agents.skill_improver.model (or
swarms.<id>.agents.skill_improver.model for multi-swarm). The legacy
top-level skill_improver.model field is deprecated and no longer drives
the agent — use the standard agents.<name>.model precedence instead. (See
the Configuration precedence section for details.)
Every proposal carries a YAML frontmatter line source: llm or
source: deterministic_fallback so reviewers can immediately tell which path
produced it:
source |
When | Quality |
|---|---|---|
llm |
The OpenCode client was wired AND the configured skill_improver agent responded |
Real LLM analysis |
deterministic_fallback |
No client wired AND allow_deterministic_fallback: true (default for one minor — will flip to false in the next release) |
Inventory-only summary; ⚠ NOT an LLM analysis |
Set allow_deterministic_fallback: false to refuse with no_llm_client when
no delegate is available.
Skill-improver proposal quota state lives at .swarm/skill-improver-quota.json:
{
"date": "2026-05-08",
"calls_used": 3,
"max_calls": 10,
"last_run_at": "2026-05-08T15:42:11Z",
"window": "utc"
}- Quota reservation runs under a
proper-lockfileso parallelskill_improveinvocations cannot lost-update each other. - Knowledge enrichment uses a separate
.swarm/knowledge-enrichment-quota.jsonfile, governed byknowledge.enrichment.*, so curator/close-time/hardening LLM attempts do not consume the skill-improver proposal budget. - No-client + fallback-disabled → refuse pre-flight; quota untouched.
- Inventory failure (pre-network) → release the reservation.
- LLM call started → slot stays consumed even on failure (anti-flake policy: a flaky model must not be allowed to burn unbounded retries within a window).
-
The improver is a separately-registered agent (
skill_improver) dispatched via the same ephemeral-session-per-call pattern as curator — seesrc/hooks/skill-improver-llm-factory.ts. -
Its model is independently configurable under
agents.skill_improver, typically a more expensive OpenRouter model than the Architect's. -
Architect's prompt tells it to suggest
skill_improveonly after repeated failures, many ignored directives, or stale skills, and to ask the user before invoking whenrequire_user_approvalis true. -
Daily-quota enforcement caps cost — typically
max_calls_per_day: 10. -
Default
write_mode: "proposal"means the agent produces.swarm/skill-improver/proposals/<timestamp>.mdonly. Withwrite_mode: "draft_skills"it additionally drafts SKILL.md proposals via theskill_generatepipeline (still draft mode — never auto-activated). -
Set
trigger: "scheduled"to allow opportunistic consolidation on startup and phase completion. The scheduler is cadence-gated byconsolidation_interval_hours, reserves at mostconsolidation_max_calls_per_runcalls per run, validates drafted skills against matching eval fixtures, and never auto-activates proposals. Use/swarm consolidatefor an explicit manual pass.
CI verification limitation. The real-LLM dispatch path requires an OpenCode runtime to wire
swarmState.opencodeClient. Unit and integration tests inject a mocked delegate and assert the dispatch shape. End-to-end verification with a live model requires a manual smoke run.
spec_writer is an independently-modelled agent for authoring .swarm/spec.md.
It can run on a higher-capability model than Architect.
"spec_writer": {
"enabled": true,
"allow_spec_write": true // gate for the safe spec_write tool
}Set the agent model under agents.spec_writer.model (or
swarms.<id>.agents.spec_writer.model). The legacy top-level
spec_writer.model field is deprecated and no longer drives the agent.
The agent has read-only access to the codebase plus the safe spec_write tool
which atomically writes .swarm/spec.md (256 KiB cap, must contain a top-level
# Heading). It cannot edit source files.
The Architect prompt routes substantial spec authoring or revision to
spec_writer while keeping itself on a cheaper model.
The model used by an agent is resolved in this order (highest priority first):
agents.<name>.model(root-level agent override) andswarms.<id>.agents.<name>.model(per-swarm override).DEFAULT_MODELS.<name>(built-in default insrc/config/constants.ts).
For new v2 agents:
| Agent | Where to set model | Where it WAS / now-deprecated |
|---|---|---|
skill_improver |
agents.skill_improver.model |
top-level skill_improver.model (deprecated, no effect) |
spec_writer |
agents.spec_writer.model |
top-level spec_writer.model (deprecated, no effect) |
The deprecated top-level model fields remain in the schema only so that
config-doctor can warn when they are present without an agents.<name>.model
counterpart. They will be removed in a future major release.
The machine-global hive store (shared-learnings.jsonl under the platform data dir —
%LOCALAPPDATA%\opencode-swarm\Data on Windows, XDG data roots on macOS/Linux) can be
polluted by leaked test fixtures or bad promotions. /swarm knowledge hive-quarantine is
the sanctioned operator remediation. It is human-only in depth: agents are refused at
swarm_command classification, the chat-fallback policy blocks agent-typed usage, the
shell guardrail blocks direct/quoted/path-qualified/shell-variable-indirected CLI
invocations from agent shells, and the CLI entry refuses human-only commands from
non-interactive shells. The confirmation token is HMAC-bound to a per-install secret.
No --yes/--force flag exists; the layers are defense-in-depth plus honest audit —
not a tamper-proof boundary against the machine's own user.
- Preview —
/swarm knowledge hive-quarantine preview <id>[,<id>…]prints each exact candidate with status, confidence, source project, lineage actor, per-line sha256, the whole-store fingerprint (entry count + file sha256), the plugin version, and a short-lived confirmation token bound to all of them (15-minute TTL). - Commit —
/swarm knowledge hive-quarantine commit --token <token> [--reason …]writes and hash-verifies a complete backup plusmanifest.jsonBEFORE any mutation (outside the hive lock), then runs ONE fasttransactHiveStoretransaction that re-verifies the live store against that backup under lock — any concurrent append, curation, per-entry change, version bump, or duplicate-id ambiguity aborts with no mutation and cleans up the orphaned backup. EXACTLY the selected entries move to theshared-learnings-quarantined.jsonlsidecar (mirroring the swarm-tierknowledge-quarantined.jsonlconvention), audit records land inshared-knowledge-events.jsonl, and counts/hashes are verified afterwards — a verification failure attempts an automatic restore whose outcome is reported honestly (a failed restore is never claimed as success). - Rollback —
/swarm knowledge hive-quarantine rollback --token <token12> | --latestrestores the EXACT original line bytes from the manifest's backup, idempotently: ids already present with identical bytes are skipped; an id present with different content (re-promoted after quarantine) aborts with a collision report and no mutation. - Status —
/swarm knowledge hive-quarantine statuslists backups (read-only).
- Exact-ID selection only. No text, substring, "test-looking phrase", cohort, age, or blacklist matching exists in the module; there is no bulk operation. A store that legitimately contains test-like text keeps it unless its exact id is selected.
- Recoverable by construction. Backups live beside the store under
<dataDir>/quarantine-backups/<timestamp>-<token12>-<random8>/(machine-global, discoverable from any project — deliberately NOT under a project's.swarm/). Quarantined entries are removed from recall and query by construction (the sidecar is not a retrieval source;isActiveStatussemantics are untouched). - Audit trail. Every phase emits a metadata-only
knowledge_maintenancetelemetry event (bounded phase/abort codes, counts, hash/token prefixes — never lesson text or paths) plusquarantined/rollbackrecords inshared-knowledge-events.jsonl. - Byte-fidelity scope. Restored ids come back byte-exact and hash-verified. Two standing-transaction behaviors apply to everything EXCEPT the selected ids: unselected entries may be re-serialized by the store's normalize-on-write pipeline, and unparseable (corrupt) lines are DROPPED by BOTH a commit rewrite and a rollback rewrite (both rebuild from parsed entries) — they survive only in the hash-verified backup copy, from which they can be recovered manually.
Tests must never touch the real platform stores. The suite installs a fail-closed
production-store tripwire (tests/preload/prod-store-tripwire.ts, loaded via
bunfig.toml [test] preload) that captures the real hive/link paths at process start and
throws on content reads and all mutation attempts against them (node:fs guards plus a
Bun.write wrap; metadata reads — readdir/stat/existsSync — and mkdir are
intentionally unguarded). The preload registers a global afterEach that re-arms the
guards after any mock.restore() and a global afterAll that verifies the real stores
are unchanged after every suite. To
isolate, redirect LOCALAPPDATA and XDG_DATA_HOME and HOME to a temp dir
(createIsolatedTestEnv() or explicit env redirection) — a POSIX-only redirect is a
no-op on Windows, where the resolvers read LOCALAPPDATA first.