opencode-swarm 7.99.6 → 7.100.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 (34) hide show
  1. package/dist/cli/{config-doctor-1j77p1jy.js → config-doctor-g29kxhek.js} +2 -2
  2. package/dist/cli/{evidence-summary-service-5ww1npaa.js → evidence-summary-service-ch74hb16.js} +2 -2
  3. package/dist/cli/{guardrail-explain-s4c2h8mv.js → guardrail-explain-q8pj9691.js} +8 -8
  4. package/dist/cli/{guardrail-log-ya49kpwn.js → guardrail-log-ywajkn46.js} +3 -3
  5. package/dist/cli/{index-h9ptj4b1.js → index-5bstvpct.js} +1 -1
  6. package/dist/cli/{index-94qwbx11.js → index-7ztmmqpt.js} +6 -0
  7. package/dist/cli/{index-s3esnz7m.js → index-e9n3xsk0.js} +1 -1
  8. package/dist/cli/{index-etssqrff.js → index-jg6m72g7.js} +9 -9
  9. package/dist/cli/{index-7hnwnw5g.js → index-m0qshbr6.js} +41 -1
  10. package/dist/cli/{index-xsswhy6k.js → index-pj3a5fbt.js} +1 -1
  11. package/dist/cli/{index-1kcp4q4y.js → index-q5cae5d7.js} +1064 -332
  12. package/dist/cli/{index-0d3pmjf9.js → index-rpb763g8.js} +1 -1
  13. package/dist/cli/{index-ksc4ftms.js → index-tj0jek4p.js} +2 -2
  14. package/dist/cli/{index-9twtnjkv.js → index-y3x3gvy6.js} +1 -1
  15. package/dist/cli/index.js +7 -7
  16. package/dist/cli/{pending-delegations-vh2nae3n.js → pending-delegations-dvprsdfy.js} +1 -1
  17. package/dist/cli/{pr-subscriptions-d57tq0c0.js → pr-subscriptions-zfxjtyzw.js} +2 -2
  18. package/dist/cli/{schema-nz638xc3.js → schema-n7pd65qq.js} +1 -1
  19. package/dist/config/schema.d.ts +40 -0
  20. package/dist/hooks/utils.d.ts +8 -3
  21. package/dist/index.js +2258 -1455
  22. package/dist/memory/config.d.ts +38 -0
  23. package/dist/memory/embeddings/cache.d.ts +25 -0
  24. package/dist/memory/embeddings/fusion.d.ts +56 -0
  25. package/dist/memory/embeddings/local-provider.d.ts +57 -0
  26. package/dist/memory/embeddings/reranker.d.ts +42 -0
  27. package/dist/memory/embeddings/types.d.ts +27 -0
  28. package/dist/memory/scoring.d.ts +1 -0
  29. package/dist/memory/sqlite-provider.d.ts +19 -0
  30. package/dist/services/version-check.d.ts +0 -4
  31. package/package.json +1 -1
  32. package/tests/fixtures/memory-recall/paraphrase-auth.json +64 -0
  33. package/tests/fixtures/memory-recall/paraphrase-concurrency.json +64 -0
  34. package/tests/fixtures/memory-recall/paraphrase-deployment.json +64 -0
@@ -37,6 +37,26 @@ export interface MemoryConfig {
37
37
  /** Reflection / consolidation pass (issue #1464, Phase 3). */
38
38
  consolidation: ConsolidationConfig;
39
39
  hardDelete: boolean;
40
+ embeddings: {
41
+ enabled: boolean;
42
+ model: string;
43
+ dimension: number;
44
+ version?: string;
45
+ cacheSize: number;
46
+ };
47
+ retrieval: {
48
+ rrfK: number;
49
+ weights: {
50
+ lexical: number;
51
+ dense: number;
52
+ metadata: number;
53
+ };
54
+ rerank: {
55
+ enabled: boolean;
56
+ model?: string;
57
+ };
58
+ latencyBudgetMs: number;
59
+ };
40
60
  }
41
61
  export interface ImportanceConfig {
42
62
  wRecency: number;
@@ -67,6 +87,24 @@ export interface ConsolidationConfig {
67
87
  export declare const DEFAULT_DECAY_HALF_LIFE_DAYS: Record<MemoryKind, number>;
68
88
  export declare const DEFAULT_IMPORTANCE_CONFIG: ImportanceConfig;
69
89
  export declare const DEFAULT_CONSOLIDATION_CONFIG: ConsolidationConfig;
90
+ export declare const DEFAULT_EMBEDDINGS_CONFIG: {
91
+ enabled: boolean;
92
+ model: string;
93
+ dimension: number;
94
+ cacheSize: number;
95
+ };
96
+ export declare const DEFAULT_RETRIEVAL_CONFIG: {
97
+ rrfK: number;
98
+ weights: {
99
+ lexical: number;
100
+ dense: number;
101
+ metadata: number;
102
+ };
103
+ rerank: {
104
+ enabled: boolean;
105
+ };
106
+ latencyBudgetMs: number;
107
+ };
70
108
  export declare const DEFAULT_MEMORY_CONFIG: MemoryConfig;
71
109
  export declare const DURABLE_MEMORY_KINDS: ReadonlySet<MemoryKind>;
72
110
  export declare const EVIDENCE_REQUIRED_KINDS: ReadonlySet<MemoryKind>;
@@ -0,0 +1,25 @@
1
+ import type { EmbeddingCacheEntry } from './types';
2
+ export declare class EmbeddingCache {
3
+ private readonly cache;
4
+ private readonly maxSize;
5
+ constructor(maxSize?: number);
6
+ /** Number of entries currently in the cache. */
7
+ get size(): number;
8
+ /**
9
+ * Retrieve a cached entry, marking it as recently used.
10
+ * Returns undefined if the key is not present.
11
+ */
12
+ get(modelVersion: string, query: string): EmbeddingCacheEntry | undefined;
13
+ /**
14
+ * Store an entry in the cache. Evicts the least-recently-used entry
15
+ * if the cache is at capacity before inserting.
16
+ */
17
+ set(modelVersion: string, query: string, entry: EmbeddingCacheEntry): void;
18
+ /**
19
+ * Check whether an entry exists for the given modelVersion and query,
20
+ * without altering LRU ordering.
21
+ */
22
+ has(modelVersion: string, query: string): boolean;
23
+ /** Remove all entries from the cache. Called on store reset / reindex. */
24
+ clear(): void;
25
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Reciprocal Rank Fusion (RRF) + score normalization for multi-channel
3
+ * memory recall. Pure functions — no I/O, no side effects.
4
+ *
5
+ * RRF formula per channel:
6
+ * contribution = weight * (1 / (rrfK + rank))
7
+ *
8
+ * where rank is 1-indexed (position + 1) for ids present in that channel;
9
+ * ids absent from a channel contribute 0 from it. Contributions are summed
10
+ * across the three channels for each unique id.
11
+ *
12
+ * The raw fused score is bounded by construction (each RRF term is ≤ 1/rrfK
13
+ * when weight ≤ 1). The final output is min-max normalised across the result
14
+ * set so the top result is always 1.0 and all scores fall in [0, 1].
15
+ *
16
+ * Lexical raw-score normalisation: the lexical channel produces an unnormalised
17
+ * weighted sum whose maximum is the sum of SCORING_WEIGHTS (1.13, see
18
+ * src/memory/scoring.ts:27-37). Callers that need to mix raw lexical scores
19
+ * with other [0,1] signals can use `normalizeLexicalScore`.
20
+ */
21
+ /**
22
+ * Sum of all scoring weight coefficients in src/memory/scoring.ts.
23
+ * Dividing a raw lexical score by this value maps it to [0, 1].
24
+ *
25
+ * Derived from SCORING_WEIGHTS:
26
+ * 0.38 + 0.16 + 0.12 + 0.08 + 0.08 + 0.12 + 0.06 + 0.05 + 0.08 = 1.13
27
+ */
28
+ export declare const LEXICAL_WEIGHT_SUM = 1.13;
29
+ export interface FusedCandidate {
30
+ id: string;
31
+ fusedScore: number;
32
+ lexicalRank: number | null;
33
+ denseRank: number | null;
34
+ metadataRank: number | null;
35
+ }
36
+ export interface FusionWeights {
37
+ lexical: number;
38
+ dense: number;
39
+ metadata: number;
40
+ }
41
+ /**
42
+ * Normalise a raw lexical score to [0, 1] by dividing by LEXICAL_WEIGHT_SUM.
43
+ * The result is clamped so callers never receive an out-of-range value.
44
+ */
45
+ export declare function normalizeLexicalScore(raw: number): number;
46
+ /**
47
+ * Reciprocal Rank Fusion across three ranked id lists.
48
+ *
49
+ * @param lexicalRankedIds best-first list; position 0 → rank 1
50
+ * @param denseRankedIds best-first list; position 0 → rank 1
51
+ * @param metadataRankedIds best-first list; position 0 → rank 1
52
+ * @param weights per-channel weights
53
+ * @param rrfK rank-smoothing constant (default 60 in config)
54
+ * @returns FusedCandidate[] sorted by fusedScore descending, scores in [0, 1]
55
+ */
56
+ export declare function fuseRankings(lexicalRankedIds: string[], denseRankedIds: string[], metadataRankedIds: string[], weights: FusionWeights, rrfK: number): FusedCandidate[];
@@ -0,0 +1,57 @@
1
+ import type { EmbeddingProvider } from './types';
2
+ export declare const _internals: {
3
+ /**
4
+ * Exposed for direct unit testing — do not call from production code.
5
+ * Verifies .swarm containment by checking path segments post-resolution.
6
+ */
7
+ resolveEmbeddingCacheDir(): string;
8
+ };
9
+ /**
10
+ * Embedding provider backed by @xenova/transformers (ONNX Runtime WASM).
11
+ *
12
+ * The transformers dependency is loaded lazily on first embed()/embedBatch()
13
+ * via createRequire(import.meta.url) — never at module scope — so the plugin
14
+ * bundle remains Node-ESM-loadable even when @xenova/transformers is not installed.
15
+ *
16
+ * Graceful degradation (FR-003): if the package is missing or the model fails
17
+ * to load, available is set to false and embed() rejects with EmbeddingUnavailableError.
18
+ */
19
+ export declare class LocalEmbeddingProvider implements EmbeddingProvider {
20
+ private readonly modelName;
21
+ /** The dimension of vectors this provider emits (e.g. 384 for all-MiniLM-L6-v2). */
22
+ readonly dimension: number;
23
+ /** The pinned model+dimension version identifier this provider produces. */
24
+ readonly modelVersion: string;
25
+ private _available;
26
+ /** Cached failure flag — prevents repeated failed require() calls on every embed(). */
27
+ private loadFailed;
28
+ private pipeline;
29
+ /** One-time download notice guard — prints once per process. */
30
+ private static downloadNoticePrinted;
31
+ constructor(config: {
32
+ model: string;
33
+ dimension: number;
34
+ version?: string;
35
+ });
36
+ /** false until the model pipeline loads successfully; true after. */
37
+ get available(): boolean;
38
+ /**
39
+ * Lazily load @xenova/transformers and initialize the feature-extraction pipeline.
40
+ * Returns the pipeline function, or null if loading failed (graceful degradation).
41
+ */
42
+ private ensurePipeline;
43
+ /**
44
+ * Compute an embedding vector for a single text.
45
+ * Rejects with EmbeddingUnavailableError if the provider is not available.
46
+ */
47
+ embed(text: string): Promise<Float32Array>;
48
+ /**
49
+ * Compute embeddings for a batch of texts. Order preserved.
50
+ * Rejects with EmbeddingUnavailableError if the provider is not available.
51
+ */
52
+ embedBatch(texts: string[]): Promise<Float32Array[]>;
53
+ /** Convert a @xenova/transformers output tensor to a Float32Array. */
54
+ private tensorToFloat32Array;
55
+ /** Split a batch tensor [batchSize, dim] into an array of Float32Array. */
56
+ private tensorToFloat32ArrayBatch;
57
+ }
@@ -0,0 +1,42 @@
1
+ export interface RerankCandidate {
2
+ id: string;
3
+ text: string;
4
+ score?: number;
5
+ }
6
+ /**
7
+ * Latency gate: skip reranking when the previous recall step already
8
+ * exceeded the caller's latency budget.
9
+ */
10
+ export declare function shouldRerank(previousRecallElapsedMs: number, latencyBudgetMs: number): boolean;
11
+ /**
12
+ * Cross-encoder reranker backed by @xenova/transformers (ONNX Runtime WASM).
13
+ *
14
+ * The transformers dependency is loaded lazily on first rerank() via
15
+ * createRequire(import.meta.url) — never at module scope — so importing
16
+ * this module does not pull in @xenova/transformers at load time.
17
+ *
18
+ * Graceful degradation: if the package is missing or the model fails to
19
+ * load, `available` is false and rerank() returns candidates unchanged.
20
+ */
21
+ export declare class CrossEncoderReranker {
22
+ private readonly options;
23
+ private loadFailed;
24
+ private _available;
25
+ private pipeline;
26
+ constructor(options: {
27
+ model?: string;
28
+ });
29
+ get available(): boolean;
30
+ /**
31
+ * Lazily load @xenova/transformers and initialise the text-classification
32
+ * (cross-encoder) pipeline. Returns the pipeline, or null on failure.
33
+ */
34
+ private ensurePipeline;
35
+ /**
36
+ * Reorder candidates by cross-encoder relevance to the query.
37
+ *
38
+ * Returns the top `topN` candidates sorted descending by cross-encoder
39
+ * score. If the model is unavailable, returns candidates unchanged.
40
+ */
41
+ rerank(candidates: RerankCandidate[], query: string, topN?: number): Promise<RerankCandidate[]>;
42
+ }
@@ -0,0 +1,27 @@
1
+ export interface EmbeddingProvider {
2
+ /** Compute an embedding vector for a single text. Reject if the provider is unavailable. */
3
+ embed(text: string): Promise<Float32Array>;
4
+ /** Compute embeddings for a batch of texts. Order preserved. Reject if unavailable. */
5
+ embedBatch(texts: string[]): Promise<Float32Array[]>;
6
+ /** The pinned model+dimension version identifier this provider produces. Used for cross-version mismatch detection. */
7
+ readonly modelVersion: string;
8
+ /** The dimension of vectors this provider emits (e.g. 384 for all-MiniLM-L6-v2). */
9
+ readonly dimension: number;
10
+ /** false when the underlying dependency/model is not installed or failed to load — caller must fall back to lexical-only. */
11
+ readonly available: boolean;
12
+ }
13
+ /** A pinned model+dimension identifier string (e.g. "Xenova/all-MiniLM-L6-v2:384"). */
14
+ export type EmbeddingVersion = string;
15
+ export interface EmbeddingCacheEntry {
16
+ vector: Float32Array;
17
+ modelVersion: string;
18
+ queryHash: string;
19
+ }
20
+ export declare class EmbeddingUnavailableError extends Error {
21
+ constructor(message?: string);
22
+ }
23
+ export declare class EmbeddingVersionMismatchError extends Error {
24
+ readonly queryVersion: string;
25
+ readonly storedVersion: string;
26
+ constructor(queryVersion: string, storedVersion: string);
27
+ }
@@ -6,6 +6,7 @@ export interface RecallScoringDiagnostics {
6
6
  returnedCount: number;
7
7
  noSignalCount: number;
8
8
  belowThresholdCount: number;
9
+ fusionActive?: boolean;
9
10
  }
10
11
  /**
11
12
  * Recall scoring weight coefficients. Sum is 1.13 (scores are an unnormalised
@@ -23,6 +23,10 @@ export declare class SQLiteMemoryProvider implements MemoryProvider, MemoryPropo
23
23
  private initPromise;
24
24
  private db;
25
25
  private ftsAvailable;
26
+ private vecAvailable;
27
+ private embeddingProvider;
28
+ private embeddingCache;
29
+ private reranker;
26
30
  private memories;
27
31
  private proposals;
28
32
  private lastAutomaticJsonlMigration;
@@ -50,6 +54,16 @@ export declare class SQLiteMemoryProvider implements MemoryProvider, MemoryPropo
50
54
  }): Promise<MemoryProposal[]>;
51
55
  applyCuratorDecision(decision: ResolvedCuratorMemoryDecision): Promise<AppliedMemoryChange>;
52
56
  close(): void;
57
+ /**
58
+ * Re-embed all durable memory records with the current embedding provider
59
+ * model, update the stored global model_version, and clear the embedding
60
+ * cache. This is the recovery path for EmbeddingVersionMismatchError.
61
+ *
62
+ * No-op (with a warning) when vec or the embedding provider is unavailable.
63
+ * Individual record failures are caught per-record so one bad embedding
64
+ * does not abort the whole rebuild.
65
+ */
66
+ rebuildEmbeddingIndex(): Promise<void>;
53
67
  importJsonl(): Promise<SQLiteJsonlImportResult>;
54
68
  exportJsonl(): Promise<{
55
69
  directory: string;
@@ -62,9 +76,12 @@ export declare class SQLiteMemoryProvider implements MemoryProvider, MemoryPropo
62
76
  hasMigration(name: string): boolean;
63
77
  markMigration(version: number, name: string): void;
64
78
  private selectRecallCandidates;
79
+ private getStoredModelVersion;
80
+ private selectDenseCandidates;
65
81
  private runMigrations;
66
82
  private backfillScopeKeys;
67
83
  private initializeFtsIndex;
84
+ private initializeVecExtension;
68
85
  private recreateFtsIndex;
69
86
  private rebuildFtsIndex;
70
87
  private countValidMemoryRows;
@@ -74,7 +91,9 @@ export declare class SQLiteMemoryProvider implements MemoryProvider, MemoryPropo
74
91
  private loadProposals;
75
92
  private writeMemory;
76
93
  private writeMemoryFts;
94
+ private writeMemoryVec;
77
95
  private deleteMemoryFts;
96
+ private deleteMemoryVec;
78
97
  private writeProposal;
79
98
  private applyDecisionToStorage;
80
99
  private readPendingProposal;
@@ -7,10 +7,6 @@ interface VersionCheckCache {
7
7
  npmLatest: string | null;
8
8
  }
9
9
  export declare function readVersionCache(): VersionCheckCache | null;
10
- /**
11
- * Compare two semver-ish version strings. Returns 1 if `a > b`, -1 if `a < b`,
12
- * 0 if equal. Treats prerelease tags as lower than the release. Pure function.
13
- */
14
10
  export declare function compareVersions(a: string, b: string): number;
15
11
  /**
16
12
  * Fetch the latest published version from the npm registry with strict,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.99.6",
3
+ "version": "7.100.0",
4
4
  "description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "paraphrase-auth",
3
+ "query": "how do I handle a failed login",
4
+ "task": "Handle failed login authentication securely.",
5
+ "agentRole": "security-engineer",
6
+ "scopes": [
7
+ {
8
+ "type": "repository",
9
+ "repoId": "opencode-swarm"
10
+ }
11
+ ],
12
+ "kinds": ["security_note", "code_pattern"],
13
+ "k": 2,
14
+ "maxItems": 2,
15
+ "tokenBudget": 1000,
16
+ "expectedLabels": ["auth-failure-bcrypt"],
17
+ "records": [
18
+ {
19
+ "label": "auth-failure-bcrypt",
20
+ "scope": {
21
+ "type": "repository",
22
+ "repoId": "opencode-swarm"
23
+ },
24
+ "kind": "security_note",
25
+ "text": "bcrypt session invalidation on authentication failure",
26
+ "tags": ["session", "invalidation", "bcrypt"],
27
+ "confidence": 0.95,
28
+ "source": {
29
+ "type": "manual",
30
+ "ref": "auth-failure-bcrypt"
31
+ }
32
+ },
33
+ {
34
+ "label": "auth-noise-1",
35
+ "scope": {
36
+ "type": "repository",
37
+ "repoId": "opencode-swarm"
38
+ },
39
+ "kind": "code_pattern",
40
+ "text": "Handle failed login by clearing active user tokens.",
41
+ "tags": ["login", "token", "clear"],
42
+ "confidence": 0.9,
43
+ "source": {
44
+ "type": "file",
45
+ "filePath": "src/auth/token.ts"
46
+ }
47
+ },
48
+ {
49
+ "label": "auth-noise-2",
50
+ "scope": {
51
+ "type": "repository",
52
+ "repoId": "opencode-swarm"
53
+ },
54
+ "kind": "code_pattern",
55
+ "text": "Login failures should redirect to the password reset page.",
56
+ "tags": ["login", "failure", "redirect"],
57
+ "confidence": 0.85,
58
+ "source": {
59
+ "type": "file",
60
+ "filePath": "src/auth/redirect.ts"
61
+ }
62
+ }
63
+ ]
64
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "paraphrase-concurrency",
3
+ "query": "running tasks in parallel safely",
4
+ "task": "Run tasks in parallel without race conditions.",
5
+ "agentRole": "backend-engineer",
6
+ "scopes": [
7
+ {
8
+ "type": "repository",
9
+ "repoId": "opencode-swarm"
10
+ }
11
+ ],
12
+ "kinds": ["code_pattern", "failure_pattern"],
13
+ "k": 2,
14
+ "maxItems": 2,
15
+ "tokenBudget": 1000,
16
+ "expectedLabels": ["concurrency-mutex-deadlock"],
17
+ "records": [
18
+ {
19
+ "label": "concurrency-mutex-deadlock",
20
+ "scope": {
21
+ "type": "repository",
22
+ "repoId": "opencode-swarm"
23
+ },
24
+ "kind": "code_pattern",
25
+ "text": "mutex lock contention deadlock avoidance",
26
+ "tags": ["concurrency", "mutex", "deadlock"],
27
+ "confidence": 0.95,
28
+ "source": {
29
+ "type": "manual",
30
+ "ref": "concurrency-mutex-deadlock"
31
+ }
32
+ },
33
+ {
34
+ "label": "concurrency-noise-1",
35
+ "scope": {
36
+ "type": "repository",
37
+ "repoId": "opencode-swarm"
38
+ },
39
+ "kind": "code_pattern",
40
+ "text": "Parallel tasks should use mutex locks to avoid race conditions.",
41
+ "tags": ["parallel", "mutex", "race"],
42
+ "confidence": 0.9,
43
+ "source": {
44
+ "type": "file",
45
+ "filePath": "src/concurrency/mutex.ts"
46
+ }
47
+ },
48
+ {
49
+ "label": "concurrency-noise-2",
50
+ "scope": {
51
+ "type": "repository",
52
+ "repoId": "opencode-swarm"
53
+ },
54
+ "kind": "failure_pattern",
55
+ "text": "Running tasks in parallel caused data corruption due to unsynchronized access.",
56
+ "tags": ["parallel", "corruption", "unsynchronized"],
57
+ "confidence": 0.85,
58
+ "source": {
59
+ "type": "file",
60
+ "filePath": "src/concurrency/race.ts"
61
+ }
62
+ }
63
+ ]
64
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "paraphrase-deployment",
3
+ "query": "ship code to production",
4
+ "task": "Deploy code to production safely.",
5
+ "agentRole": "devops-engineer",
6
+ "scopes": [
7
+ {
8
+ "type": "repository",
9
+ "repoId": "opencode-swarm"
10
+ }
11
+ ],
12
+ "kinds": ["repo_convention", "code_pattern"],
13
+ "k": 2,
14
+ "maxItems": 2,
15
+ "tokenBudget": 1000,
16
+ "expectedLabels": ["deployment-cicd-pipeline"],
17
+ "records": [
18
+ {
19
+ "label": "deployment-cicd-pipeline",
20
+ "scope": {
21
+ "type": "repository",
22
+ "repoId": "opencode-swarm"
23
+ },
24
+ "kind": "repo_convention",
25
+ "text": "continuous integration deployment pipeline release",
26
+ "tags": ["deployment", "ci", "cd"],
27
+ "confidence": 0.95,
28
+ "source": {
29
+ "type": "manual",
30
+ "ref": "deployment-cicd-pipeline"
31
+ }
32
+ },
33
+ {
34
+ "label": "deployment-noise-1",
35
+ "scope": {
36
+ "type": "repository",
37
+ "repoId": "opencode-swarm"
38
+ },
39
+ "kind": "code_pattern",
40
+ "text": "Ship code to production through the CI/CD pipeline.",
41
+ "tags": ["ship", "production", "pipeline"],
42
+ "confidence": 0.9,
43
+ "source": {
44
+ "type": "file",
45
+ "filePath": "src/deploy/pipeline.ts"
46
+ }
47
+ },
48
+ {
49
+ "label": "deployment-noise-2",
50
+ "scope": {
51
+ "type": "repository",
52
+ "repoId": "opencode-swarm"
53
+ },
54
+ "kind": "repo_convention",
55
+ "text": "Production deployments require manual approval before release.",
56
+ "tags": ["production", "deployment", "approval"],
57
+ "confidence": 0.85,
58
+ "source": {
59
+ "type": "file",
60
+ "filePath": "docs/deployment.md"
61
+ }
62
+ }
63
+ ]
64
+ }