opencode-skills-collection 3.1.17 → 3.1.19

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.
@@ -0,0 +1,350 @@
1
+ export const meta = {
2
+ name: 'go-in-depth',
3
+ description: 'Go in depth harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report.',
4
+ whenToUse: 'When the user wants a deep, multi-source, fact-checked research report on any topic. BEFORE invoking, check if the question is specific enough to research directly — if underspecified (e.g., "what car to buy" without budget/use-case/region), ask 2-3 clarifying questions to narrow scope. Then pass the refined question as args, weaving the answers in.',
5
+ phases: [{"title":"Scope","detail":"Decompose question (from args) into 5 search angles"},{"title":"Search","detail":"5 parallel WebSearch agents, one per angle"},{"title":"Fetch","detail":"URL-dedup, fetch top 15 sources, extract falsifiable claims"},{"title":"Verify","detail":"3-vote adversarial verification per claim (need 2/3 refutes to kill)"},{"title":"Synthesize","detail":"Merge semantic dupes, rank by confidence, cite sources"}],
6
+ }
7
+
8
+ // go-in-depth: Scope → pipeline(Search → URL-dedup → Fetch+Extract) → 3-vote Verify → Synthesize
9
+ // Uses a bug-hunting-style fan-out and verification pattern, adapted for web research.
10
+ // Question is passed via Workflow({name: 'go-in-depth', args: '<question>'}).
11
+
12
+ const VOTES_PER_CLAIM = 3
13
+ const REFUTATIONS_REQUIRED = 2
14
+ const MAX_FETCH = 15
15
+ const MAX_VERIFY_CLAIMS = 25
16
+
17
+ // ─── Schemas ───
18
+ const SCOPE_SCHEMA = {
19
+ type: "object", required: ["question", "angles", "summary"],
20
+ properties: {
21
+ question: { type: "string" },
22
+ summary: { type: "string" },
23
+ angles: { type: "array", minItems: 3, maxItems: 6, items: {
24
+ type: "object", required: ["label", "query"],
25
+ properties: {
26
+ label: { type: "string" },
27
+ query: { type: "string" },
28
+ rationale: { type: "string" },
29
+ },
30
+ }},
31
+ },
32
+ }
33
+ const SEARCH_SCHEMA = {
34
+ type: "object", required: ["results"],
35
+ properties: {
36
+ results: { type: "array", maxItems: 6, items: {
37
+ type: "object", required: ["url", "title", "relevance"],
38
+ properties: {
39
+ url: { type: "string" },
40
+ title: { type: "string" },
41
+ snippet: { type: "string" },
42
+ relevance: { enum: ["high", "medium", "low"] },
43
+ },
44
+ }},
45
+ },
46
+ }
47
+ const EXTRACT_SCHEMA = {
48
+ type: "object", required: ["claims", "sourceQuality"],
49
+ properties: {
50
+ sourceQuality: { enum: ["primary", "secondary", "blog", "forum", "unreliable"] },
51
+ publishDate: { type: "string" },
52
+ claims: { type: "array", maxItems: 5, items: {
53
+ type: "object", required: ["claim", "quote", "importance"],
54
+ properties: {
55
+ claim: { type: "string" },
56
+ quote: { type: "string" },
57
+ importance: { enum: ["central", "supporting", "tangential"] },
58
+ },
59
+ }},
60
+ },
61
+ }
62
+ const VERDICT_SCHEMA = {
63
+ type: "object", required: ["refuted", "evidence", "confidence"],
64
+ properties: {
65
+ refuted: { type: "boolean" },
66
+ evidence: { type: "string" },
67
+ confidence: { enum: ["high", "medium", "low"] },
68
+ counterSource: { type: "string" },
69
+ },
70
+ }
71
+ const REPORT_SCHEMA = {
72
+ type: "object", required: ["summary", "findings", "caveats"],
73
+ properties: {
74
+ summary: { type: "string" },
75
+ findings: { type: "array", items: {
76
+ type: "object", required: ["claim", "confidence", "sources", "evidence"],
77
+ properties: {
78
+ claim: { type: "string" },
79
+ confidence: { enum: ["high", "medium", "low"] },
80
+ sources: { type: "array", items: { type: "string" } },
81
+ evidence: { type: "string" },
82
+ vote: { type: "string" },
83
+ },
84
+ }},
85
+ caveats: { type: "string" },
86
+ openQuestions: { type: "array", items: { type: "string" } },
87
+ },
88
+ }
89
+
90
+ // ─── Phase 0: Scope — decompose question into search angles ───
91
+ phase("Scope")
92
+ const QUESTION = (typeof args === "string" ? args.trim() : args?.query?.trim()) || ""
93
+ if (!QUESTION) {
94
+ return { error: "No research question provided. Pass it as args: Workflow({name: 'go-in-depth', args: '<question>'})." }
95
+ }
96
+ const scope = await agent(
97
+ "Decompose this research question into complementary search angles.\n\n" +
98
+ "## Question\n" + QUESTION + "\n\n" +
99
+ "## Task\n" +
100
+ "Generate 5 distinct web search queries that together cover the question from different angles. Pick angles that suit the question's domain. Examples:\n" +
101
+ "- broad/primary · academic/technical · recent news · contrarian/skeptical · practitioner/implementation\n" +
102
+ "- For medical: anatomy · common causes · serious differentials · authoritative refs · red flags\n" +
103
+ "- For tech: state-of-art · benchmarks · limitations · industry adoption · cost/tradeoffs\n\n" +
104
+ "Make queries specific enough to surface high-signal results. Avoid redundancy.\n" +
105
+ "Return: the question (verbatim or lightly normalized), a 1-2 sentence decomposition strategy, and the angles.\n\nStructured output only.",
106
+ { label: "scope", schema: SCOPE_SCHEMA }
107
+ )
108
+ if (!scope) {
109
+ return { error: "Scope agent returned no result — cannot decompose the research question." }
110
+ }
111
+ log("Q: " + QUESTION.slice(0, 80) + (QUESTION.length > 80 ? "…" : ""))
112
+ log("Decomposed into " + scope.angles.length + " angles: " + scope.angles.map(a => a.label).join(", "))
113
+
114
+ // ─── Dedup state — accumulates across searchers as they complete ───
115
+ const normURL = u => {
116
+ try {
117
+ const p = new URL(u)
118
+ return (p.hostname.replace(/^www\./, "") + p.pathname.replace(/\/$/, "")).toLowerCase()
119
+ } catch { return u.toLowerCase() }
120
+ }
121
+ const seen = new Map()
122
+ const dupes = []
123
+ const budgetDropped = []
124
+ const relRank = { high: 0, medium: 1, low: 2 }
125
+ let fetchSlots = MAX_FETCH
126
+
127
+ // ─── Prompts ───
128
+ const SEARCH_PROMPT = (angle) =>
129
+ "## Web Searcher: " + angle.label + "\n\n" +
130
+ "Research question: \"" + QUESTION + "\"\n\n" +
131
+ "Your angle: **" + angle.label + "** — " + (angle.rationale || "") + "\n" +
132
+ "Search query: `" + angle.query + "`\n\n" +
133
+ "## Task\nUse WebSearch with the query above (or a refined version). Return the top 4-6 most relevant results.\n" +
134
+ "Rank by relevance to the ORIGINAL question, not just the search query. Skip obvious SEO spam/content farms.\n" +
135
+ "Include a short snippet capturing why each result is relevant.\n\nStructured output only."
136
+
137
+ const FETCH_PROMPT = (source, angle) =>
138
+ "## Source Extractor\n\n" +
139
+ "Research question: \"" + QUESTION + "\"\n\n" +
140
+ "Fetch and extract key claims from this source:\n" +
141
+ "**URL:** " + source.url + "\n**Title:** " + source.title + "\n**Found via:** " + angle + " search\n\n" +
142
+ "## Task\n1. Use WebFetch to retrieve the page content.\n" +
143
+ "2. Assess source quality: primary research/institution? secondary reporting? blog/opinion? forum? unreliable?\n" +
144
+ "3. Extract 2-5 FALSIFIABLE claims that bear on the research question. Each claim must:\n" +
145
+ " - be a concrete, checkable statement (not vague generalities)\n" +
146
+ " - include a direct quote from the source as support\n" +
147
+ " - be rated central/supporting/tangential to the research question\n" +
148
+ "4. Note publish date if available.\n\n" +
149
+ "If the fetch fails or the page is irrelevant/paywalled, return claims: [] and sourceQuality: \"unreliable\".\n\nStructured output only."
150
+
151
+ const VERIFY_PROMPT = (claim, v) =>
152
+ "## Adversarial Claim Verifier (voter " + (v + 1) + "/" + VOTES_PER_CLAIM + ")\n\n" +
153
+ "Be SKEPTICAL. Try to REFUTE this claim. ≥" + REFUTATIONS_REQUIRED + "/" + VOTES_PER_CLAIM + " refutations kill it.\n\n" +
154
+ "## Research question\n" + QUESTION + "\n\n" +
155
+ "## Claim under review\n\"" + claim.claim + "\"\n\n" +
156
+ "**Source:** " + claim.sourceUrl + " (" + claim.sourceQuality + ")\n" +
157
+ "**Supporting quote:** \"" + claim.quote + "\"\n\n" +
158
+ "## Checklist\n" +
159
+ "1. Is the claim actually supported by the quote, or is it an overreach/misread?\n" +
160
+ "2. WebSearch for contradicting evidence — does any credible source dispute or heavily qualify this?\n" +
161
+ "3. Is the source quality sufficient for the claim's strength? (extraordinary claims need primary sources)\n" +
162
+ "4. Is the claim outdated? (check dates — old claims about fast-moving fields are suspect)\n" +
163
+ "5. Is this a marketing claim / press release / cherry-picked benchmark / forum speculation?\n\n" +
164
+ "**refuted=true** if: unsupported by quote / contradicted / low-quality source for strong claim / outdated / marketing fluff.\n" +
165
+ "**refuted=false** ONLY if: claim is well-supported, current, and source quality matches claim strength.\n" +
166
+ "Default to refuted=true if uncertain.\n\nStructured output only. Evidence MUST be specific."
167
+
168
+ // ─── Pipeline: search → dedup → fetch+extract (no barrier) ───
169
+ const searchResults = await pipeline(
170
+ scope.angles,
171
+
172
+ angle => agent(SEARCH_PROMPT(angle), {
173
+ label: "search:" + angle.label, phase: "Search", schema: SEARCH_SCHEMA
174
+ }).then(r => {
175
+ if (!r) return null
176
+ log(angle.label + ": " + r.results.length + " results")
177
+ return { angle: angle.label, results: r.results }
178
+ }),
179
+
180
+ searchResult => {
181
+ const sorted = [...searchResult.results].sort((a, b) => relRank[a.relevance] - relRank[b.relevance])
182
+ const novel = sorted.filter(r => {
183
+ const key = normURL(r.url)
184
+ if (seen.has(key)) {
185
+ dupes.push({ ...r, angle: searchResult.angle, dupOf: seen.get(key) })
186
+ return false
187
+ }
188
+ if (fetchSlots <= 0) {
189
+ budgetDropped.push({ ...r, angle: searchResult.angle })
190
+ return false
191
+ }
192
+ seen.set(key, { angle: searchResult.angle, title: r.title })
193
+ fetchSlots--
194
+ return true
195
+ })
196
+ if (novel.length < searchResult.results.length) {
197
+ log(searchResult.angle + ": " + novel.length + " novel (" + (searchResult.results.length - novel.length) + " filtered)")
198
+ }
199
+ return parallel(
200
+ novel.map(source => () => {
201
+ let host = "unknown"
202
+ try { host = new URL(source.url).hostname.replace(/^www\./, "") } catch {}
203
+ return agent(FETCH_PROMPT(source, searchResult.angle), {
204
+ label: "fetch:" + host,
205
+ phase: "Fetch",
206
+ schema: EXTRACT_SCHEMA,
207
+ }).then(ext => {
208
+ // User-skip → null; drop it (filtered by searchResults.flat().filter(Boolean))
209
+ // rather than throwing into .catch() and mislabeling it "unreliable".
210
+ if (!ext) return null
211
+ return {
212
+ url: source.url, title: source.title, angle: searchResult.angle,
213
+ sourceQuality: ext.sourceQuality, publishDate: ext.publishDate,
214
+ claims: ext.claims.map(c => ({ ...c, sourceUrl: source.url, sourceQuality: ext.sourceQuality })),
215
+ }
216
+ }).catch(e => {
217
+ log("fetch failed: " + source.url + " — " + (e.message || e))
218
+ return { url: source.url, title: source.title, angle: searchResult.angle, sourceQuality: "unreliable", claims: [] }
219
+ })
220
+ })
221
+ )
222
+ }
223
+ )
224
+
225
+ const allSources = searchResults.flat().filter(Boolean)
226
+ const allClaims = allSources.flatMap(s => s.claims)
227
+ const impRank = { central: 0, supporting: 1, tangential: 2 }
228
+ const qualRank = { primary: 0, secondary: 1, blog: 2, forum: 3, unreliable: 4 }
229
+
230
+ const rankedClaims = [...allClaims]
231
+ .sort((a, b) => (impRank[a.importance] - impRank[b.importance]) || (qualRank[a.sourceQuality] - qualRank[b.sourceQuality]))
232
+ .slice(0, MAX_VERIFY_CLAIMS)
233
+
234
+ log("Fetched " + allSources.length + " sources → " + allClaims.length + " claims → verifying top " + rankedClaims.length)
235
+
236
+ if (rankedClaims.length === 0) {
237
+ return {
238
+ question: QUESTION,
239
+ summary: "No claims extracted. " + allSources.length + " sources fetched, all empty/failed. " + dupes.length + " URL dupes, " + budgetDropped.length + " budget-dropped.",
240
+ findings: [], refuted: [], sources: allSources.map(s => ({ url: s.url, quality: s.sourceQuality })),
241
+ stats: { angles: scope.angles.length, sources: allSources.length, claims: 0, dupes: dupes.length },
242
+ }
243
+ }
244
+
245
+ // ─── Verify: 3-vote adversarial ───
246
+ // Barrier here is intentional — claim pool must be fully assembled before ranking/verification.
247
+ phase("Verify")
248
+ const voted = (await parallel(
249
+ rankedClaims.map(claim => () =>
250
+ parallel(
251
+ Array.from({ length: VOTES_PER_CLAIM }, (_, v) => () =>
252
+ agent(VERIFY_PROMPT(claim, v), {
253
+ label: "v" + v + ":" + claim.claim.slice(0, 40),
254
+ phase: "Verify",
255
+ schema: VERDICT_SCHEMA,
256
+ })
257
+ )
258
+ ).then(verdicts => {
259
+ // A vote can be null (user-skip or agent error) — treat as abstain.
260
+ const valid = verdicts.filter(Boolean)
261
+ const refuted = valid.filter(v => v.refuted).length
262
+ // Survive only if the claim was actually adjudicated: a quorum of
263
+ // valid votes AND fewer than REFUTATIONS_REQUIRED refuting. Too many
264
+ // abstentions = unverified, which must NOT pass into the report
265
+ // (otherwise all-abstain → refuted=0 → false survive).
266
+ const abstained = VOTES_PER_CLAIM - valid.length
267
+ const survives = valid.length >= REFUTATIONS_REQUIRED && refuted < REFUTATIONS_REQUIRED
268
+ log("\"" + claim.claim.slice(0, 50) + "…\": " + (valid.length - refuted) + "-" + refuted + (abstained > 0 ? " (" + abstained + " abstain)" : "") + " " + (survives ? "✓" : "✗"))
269
+ return { ...claim, verdicts: valid, refutedVotes: refuted, survives }
270
+ })
271
+ )
272
+ )).filter(Boolean)
273
+
274
+ const confirmed = voted.filter(c => c.survives)
275
+ const killed = voted.filter(c => !c.survives)
276
+ log("Verify done: " + voted.length + " claims → " + confirmed.length + " confirmed, " + killed.length + " killed")
277
+
278
+ if (confirmed.length === 0) {
279
+ return {
280
+ question: QUESTION,
281
+ summary: "All " + voted.length + " claims refuted by adversarial verification. Research inconclusive — sources may be low-quality or claims overstated.",
282
+ findings: [],
283
+ refuted: killed.map(c => ({ claim: c.claim, vote: (c.verdicts.length - c.refutedVotes) + "-" + c.refutedVotes, source: c.sourceUrl })),
284
+ sources: allSources.map(s => ({ url: s.url, quality: s.sourceQuality, claimCount: s.claims.length })),
285
+ stats: { angles: scope.angles.length, sources: allSources.length, claims: allClaims.length, verified: voted.length, confirmed: 0, killed: killed.length },
286
+ }
287
+ }
288
+
289
+ // ─── Synthesize ───
290
+ phase("Synthesize")
291
+ const confRank = { high: 0, medium: 1, low: 2 }
292
+ const block = confirmed.map((c, i) => {
293
+ const best = c.verdicts.filter(v => !v.refuted).sort((a, b) => confRank[a.confidence] - confRank[b.confidence])[0]
294
+ return "### [" + i + "] " + c.claim + "\n" +
295
+ "Vote: " + (c.verdicts.length - c.refutedVotes) + "-" + c.refutedVotes + " · Source: " + c.sourceUrl + " (" + c.sourceQuality + ")\n" +
296
+ "Quote: \"" + c.quote + "\"\nVerifier evidence (" + best.confidence + "): " + best.evidence + "\n"
297
+ }).join("\n")
298
+
299
+ const killedBlock = killed.length > 0
300
+ ? "\n## Refuted claims (for transparency)\n" +
301
+ killed.map(c => "- \"" + c.claim + "\" (" + c.sourceUrl + ", vote " + (c.verdicts.length - c.refutedVotes) + "-" + c.refutedVotes + ")").join("\n")
302
+ : ""
303
+
304
+ const report = await agent(
305
+ "## Synthesis: research report\n\n" +
306
+ "**Question:** " + QUESTION + "\n\n" +
307
+ confirmed.length + " claims survived " + VOTES_PER_CLAIM + "-vote adversarial verification. Merge semantic duplicates and synthesize.\n\n" +
308
+ "## Confirmed claims\n" + block + "\n" + killedBlock + "\n\n" +
309
+ "## Instructions\n" +
310
+ "1. Identify claims that say the same thing — merge them, combine their sources.\n" +
311
+ "2. Group related claims into coherent findings. Each finding should directly address the research question.\n" +
312
+ "3. Assign confidence per finding: high (multiple primary sources, unanimous votes), medium (secondary sources or split votes), low (single source or blog-quality).\n" +
313
+ "4. Write a 3-5 sentence executive summary answering the research question.\n" +
314
+ "5. Note caveats: what's uncertain, what sources were weak, what time-sensitivity applies.\n" +
315
+ "6. List 2-4 open questions that emerged but weren't answered.\n\nStructured output only.",
316
+ { label: "synthesize", schema: REPORT_SCHEMA }
317
+ )
318
+
319
+ if (!report) {
320
+ // Synthesis skipped/errored — salvage the verified claims raw rather
321
+ // than throwing on report.findings and discarding the whole run.
322
+ return {
323
+ question: QUESTION,
324
+ summary: "Synthesis step was skipped or failed — returning " + confirmed.length + " verified claims unmerged.",
325
+ findings: [],
326
+ confirmed: confirmed.map(c => ({ claim: c.claim, source: c.sourceUrl, quote: c.quote, vote: (c.verdicts.length - c.refutedVotes) + "-" + c.refutedVotes })),
327
+ refuted: killed.map(c => ({ claim: c.claim, vote: (c.verdicts.length - c.refutedVotes) + "-" + c.refutedVotes, source: c.sourceUrl })),
328
+ sources: allSources.map(s => ({ url: s.url, quality: s.sourceQuality, claimCount: s.claims.length })),
329
+ stats: { angles: scope.angles.length, sources: allSources.length, claims: allClaims.length, verified: voted.length, confirmed: confirmed.length, killed: killed.length, afterSynthesis: 0 },
330
+ }
331
+ }
332
+
333
+ return {
334
+ question: QUESTION,
335
+ ...report,
336
+ refuted: killed.map(c => ({ claim: c.claim, vote: (c.verdicts.length - c.refutedVotes) + "-" + c.refutedVotes, source: c.sourceUrl })),
337
+ sources: allSources.map(s => ({ url: s.url, quality: s.sourceQuality, angle: s.angle, claimCount: s.claims.length })),
338
+ stats: {
339
+ angles: scope.angles.length,
340
+ sourcesFetched: allSources.length,
341
+ claimsExtracted: allClaims.length,
342
+ claimsVerified: voted.length,
343
+ confirmed: confirmed.length,
344
+ killed: killed.length,
345
+ afterSynthesis: report.findings.length,
346
+ urlDupes: dupes.length,
347
+ budgetDropped: budgetDropped.length,
348
+ agentCalls: 1 + scope.angles.length + allSources.length + (voted.length * VOTES_PER_CLAIM) + 1,
349
+ },
350
+ }
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: pilot-protocol
3
+ description: "Give an AI agent a permanent network address, encrypted P2P messaging, and an installable app store via Pilot Protocol"
4
+ category: ai-agents
5
+ risk: critical
6
+ source: community
7
+ source_repo: pilot-protocol/pilotprotocol
8
+ source_type: official
9
+ date_added: "2026-07-07"
10
+ author: pilot-protocol
11
+ tags: [agent-networking, p2p, nat-traversal, overlay-network, agent-apps]
12
+ tools: [claude, cursor, gemini, codex]
13
+ license: "AGPL-3.0"
14
+ license_source: "https://github.com/pilot-protocol/pilotprotocol/blob/main/LICENSE"
15
+ ---
16
+
17
+ # Pilot Protocol
18
+
19
+ ## Overview
20
+
21
+ Pilot Protocol is an open-source overlay network that gives AI agents first-class
22
+ network citizenship: a permanent virtual address, encrypted UDP tunnels, NAT
23
+ traversal, and an explicit per-peer trust model. It also ships an app store of
24
+ installable, agent-native capabilities that run locally as typed JSON-in/JSON-out
25
+ services. Use this skill when an agent needs to reach other agents directly,
26
+ discover live external data through public service agents, or install a local
27
+ capability without writing REST plumbing.
28
+
29
+ If this skill adapts material from an external GitHub repository, it declares:
30
+
31
+ - `source_repo: pilot-protocol/pilotprotocol`
32
+ - `source_type: official`
33
+
34
+ ## When to Use This Skill
35
+
36
+ - Use when an agent needs a stable address that survives restarts, IP changes,
37
+ or moving across clouds (no more re-registering webhooks).
38
+ - Use when two or more agents need direct, encrypted communication without a
39
+ shared cloud account or a hand-rolled tunnel.
40
+ - Use when an agent needs live external data (crypto/FX prices, weather,
41
+ package metadata, etc.) via structured JSON instead of scraping HTML.
42
+ - Use when you want to install a local, typed capability (search, deploy,
43
+ people/company lookups) with one command instead of standing up a service.
44
+
45
+ ## How It Works
46
+
47
+ ### Step 1: Install the daemon
48
+
49
+ Download the installer, inspect it, then run it — do not pipe it straight into a shell.
50
+
51
+ ```bash
52
+ curl -fsSL https://pilotprotocol.network/install.sh -o /tmp/pilot-install.sh
53
+ less /tmp/pilot-install.sh # review before executing
54
+ sh /tmp/pilot-install.sh
55
+ ```
56
+
57
+ ### Step 2: Start the node and confirm it registered
58
+
59
+ ```bash
60
+ pilotctl daemon start
61
+ pilotctl info
62
+ ```
63
+
64
+ ### Step 3: Query a service agent (no handshake needed)
65
+
66
+ Service agents in the public directory auto-approve incoming messages.
67
+
68
+ ```bash
69
+ pilotctl send-message list-agents --data '/data {"search":"weather"}' --wait
70
+ jq -r '.data' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"
71
+ ```
72
+
73
+ ### Step 4: Handshake a peer agent for direct messaging
74
+
75
+ Peer nodes (as opposed to service agents) require mutual approval before a
76
+ tunnel works.
77
+
78
+ ```bash
79
+ pilotctl handshake <hostname|node_id|address> "<reason>"
80
+ pilotctl trust
81
+ pilotctl send-message <peer> --data '<message>'
82
+ ```
83
+
84
+ ### Step 5: Install and call an agent app
85
+
86
+ ```bash
87
+ pilotctl appstore catalogue
88
+ pilotctl appstore install <app-id>
89
+ pilotctl appstore call <app-id> <app>.help '{}'
90
+ ```
91
+
92
+ ## Examples
93
+
94
+ ### Example 1: Ask a live-data service agent
95
+
96
+ ```bash
97
+ pilotctl send-message list-agents --data '/data {"search":"bitcoin"}' --wait
98
+ jq -r '.data' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"
99
+ ```
100
+
101
+ ### Example 2: Install and call a local capability app
102
+
103
+ ```bash
104
+ pilotctl appstore install io.pilot.cosift
105
+ pilotctl appstore call io.pilot.cosift cosift.answer '{"q":"What is HNSW?"}'
106
+ ```
107
+
108
+ ## Best Practices
109
+
110
+ - ✅ Use `--wait` on `send-message` so the reply is guaranteed to be in the
111
+ inbox before you read it.
112
+ - ✅ Query `list-agents` before guessing a hostname — the catalogue changes.
113
+ - ❌ Don't assume peer trust is immediate; approval + registry propagation can
114
+ take a few seconds.
115
+ - ❌ Don't set `--auto-answer` on your own node — it's a service-agent-only flag.
116
+
117
+ ## Limitations
118
+
119
+ - This skill does not replace reading `pilotctl --help` or the project docs
120
+ for less common commands.
121
+ - Stop and ask for clarification if the daemon isn't installed or the task
122
+ needs credentials this skill doesn't cover.
123
+
124
+ ## Security & Safety Notes
125
+
126
+ - The install script fetches an installer from `pilotprotocol.network`;
127
+ download it to disk and review it before running in a sensitive environment.
128
+ - `~/.pilot/identity.json` is a private keypair — never copy it between hosts.
129
+ - Running the daemon starts a persistent background process, joins a public
130
+ P2P network, and can install app-store packages locally — treat this as a
131
+ state-changing operation, not a read-only one.
132
+
133
+ ## Common Pitfalls
134
+
135
+ - **Problem:** A `send-message` to a peer silently fails right after a handshake.
136
+ **Solution:** Trust propagates through the registry and can take seconds; wait
137
+ briefly and retry before assuming the handshake failed.
138
+ - **Problem:** Large replies arrive truncated in the inbox JSON.
139
+ **Solution:** Pass a `limit` filter to the query, or use `/summary` for a
140
+ synthesized digest instead of the raw `/data` payload.
141
+
142
+ ## Related Skills
143
+
144
+ - `@network-101` - General networking background before diving into overlay
145
+ networks specifically.
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: pre-ship-gate
3
+ description: "A ship gate that runs before any production deploy: checks the silent failure modes that make a deploy 'succeed' while prod stays broken, then verifies the live revision instead of trusting deploy output."
4
+ category: quality
5
+ risk: safe
6
+ source: community
7
+ source_repo: Sharrmavishal/operating-kit
8
+ source_type: community
9
+ date_added: "2026-07-07"
10
+ author: Sharrmavishal
11
+ tags: [deployment, quality-gate, verification, ci-cd, production]
12
+ tools: [claude, cursor, gemini]
13
+ license: MIT
14
+ license_source: "https://github.com/Sharrmavishal/operating-kit/blob/main/LICENSE"
15
+ ---
16
+
17
+ # Pre-Ship Gate
18
+
19
+ ## Overview
20
+
21
+ Most bad deploys do not fail loudly. The pipeline goes green, the CLI prints "deployed", and the old or broken version is still what users hit. This skill is the gate you run right before a production deploy and right after, so an agent stops trusting deploy output and starts confirming what is actually live. It exists because "the deploy command exited 0" and "the new version is serving traffic" are two different facts, and agents routinely confuse them.
22
+
23
+ ## When to Use This Skill
24
+
25
+ - Use before running any command that pushes to a production or staging environment.
26
+ - Use when an agent is about to report "shipped", "deployed", or "live".
27
+ - Use when a deploy reported success but users still see the old behavior.
28
+ - Use when a release involves database migrations, feature flags, or a staged rollout.
29
+
30
+ ## How It Works
31
+
32
+ The gate has three phases. Do not skip to phase 3.
33
+
34
+ ### Phase 1: Pre-flight (before the deploy runs)
35
+
36
+ Walk the silent failure catalog. These are the modes that let a deploy "succeed" while production stays broken. For each one, confirm it or flag it. Do not assume.
37
+
38
+ - **Migrations**: Are schema migrations part of this release, and will they run against the target before the new code serves traffic? A deploy that ships code expecting a column that does not exist yet fails silently for users, not for the pipeline.
39
+ - **Feature flags**: Is the flag that gates this change actually enabled in the target environment, not just in dev? Shipped code behind an off flag looks like a no-op deploy.
40
+ - **Build cache / stale assets**: Could a cached build or CDN layer serve the previous bundle after deploy? Confirm the artifact hash or asset fingerprint changed.
41
+ - **Release pointer**: Does the deploy update the symlink, active revision, or traffic pointer, or does it only upload the new build? Uploading is not releasing.
42
+ - **Staged rollout / canary**: If traffic is staged, is it stuck at 0 percent or waiting on a manual promote? A canary that never promotes is not a deploy.
43
+ - **Env and secrets**: Are the env vars and secrets the new code needs present in the target, not just locally? Missing config surfaces as runtime errors, not deploy errors.
44
+
45
+ ### Phase 2: Run the deploy
46
+
47
+ The human or the deploy tooling runs the actual command. This skill does not execute the production deploy itself. It gates it.
48
+
49
+ ### Phase 3: Verify live (before saying "shipped")
50
+
51
+ Confirm the running system, not the deploy log.
52
+
53
+ - Fetch the live version or revision identifier from the running service and compare it to the one you intended to ship.
54
+ - Hit a health or status endpoint and confirm it returns the expected version, not just HTTP 200.
55
+ - Tail production logs for the first errors after cutover.
56
+ - Only after the live revision matches the intended revision may you report "shipped". If it does not match, report the mismatch, not success.
57
+
58
+ ## Examples
59
+
60
+ ### Example 1: Verifying the live revision instead of trusting the deploy log
61
+
62
+ ```bash
63
+ # You intended to ship this commit
64
+ INTENDED="$(git rev-parse --short HEAD)"
65
+
66
+ # Ask the running service what it is actually serving
67
+ LIVE="$(curl -fsS https://your-service.example.com/health | jq -r '.revision')"
68
+
69
+ if [ "$INTENDED" = "$LIVE" ]; then
70
+ echo "Live revision $LIVE matches intended $INTENDED: verified shipped."
71
+ else
72
+ echo "MISMATCH: intended $INTENDED but live is $LIVE. Do not report shipped."
73
+ fi
74
+ ```
75
+
76
+ ### Example 2: Pre-flight verdict format an agent can emit
77
+
78
+ ```markdown
79
+ PRE-SHIP GATE, verdict: HOLD
80
+
81
+ - Migrations: 1 pending (add_users_status_col): NOT yet applied to prod. BLOCK.
82
+ - Feature flags: new_checkout flag is OFF in prod. Enabling required post-deploy.
83
+ - Build assets: new bundle hash confirmed (a1b2c3 != previous 9f8e7d). OK.
84
+ - Release pointer: deploy updates active symlink. OK.
85
+ - Rollout: canary at 10%, manual promote required. NOTE.
86
+ - Env/secrets: STRIPE_KEY present in prod. OK.
87
+
88
+ Reason for HOLD: run migration add_users_status_col before cutover, or the
89
+ new code will 500 on /orders.
90
+ ```
91
+
92
+ ## Best Practices
93
+
94
+ - ✅ Treat "the command exited 0" and "the new version is live" as separate facts, and verify the second one.
95
+ - ✅ Emit an explicit verdict (SHIP / HOLD) with the failing item named, not a vague "looks good".
96
+ - ✅ Compare a live revision identifier against the intended one after every deploy.
97
+ - ✅ Name the specific silent failure mode you are worried about, so a human can override with context.
98
+ - ❌ Do not report "shipped" from deploy output alone.
99
+ - ❌ Do not skip the pre-flight because the pipeline is green.
100
+ - ❌ Do not treat a passing health check as proof the right version is live. Check the version field.
101
+
102
+ ## Limitations
103
+
104
+ - This skill does not run the production deploy for you. It gates and verifies around it.
105
+ - It cannot know your environment's exact health or version endpoint. Wire in the real one before relying on the verification phase.
106
+ - The silent failure catalog is common cases, not exhaustive. Systems with unusual release mechanics need their own additions.
107
+ - It does not replace environment-specific testing, load testing, or expert review.
108
+ - Stop and ask for clarification if the target environment, the intended revision, or the verification endpoint is unknown.
109
+
110
+ ## Common Pitfalls
111
+
112
+ - **Problem:** Health check returns 200 but users still see the old version.
113
+ **Solution:** The check is hitting a cached edge or the old pod. Verify the revision field in the response, not just the status code.
114
+ - **Problem:** Migration runs after the new code is already serving traffic.
115
+ **Solution:** Sequence migrations before cutover, or gate the code path behind a flag until the migration lands.
116
+ - **Problem:** Deploy "succeeds" but the canary is stuck at 0 percent.
117
+ **Solution:** Confirm the traffic pointer or promotion step, not just the upload step.
118
+
119
+ ## Security & Safety Notes
120
+
121
+ - This skill is defensive and read-oriented. Its own commands are verification calls (fetching a version endpoint, tailing logs, comparing revisions). It does not itself mutate production.
122
+ - The example commands use `curl -fsS` against a status endpoint and are illustrative. Replace the placeholder host and version field with your own before use.
123
+ - The actual production deploy is performed by your existing tooling and is out of this skill's scope. Keep human confirmation on the deploy step.
124
+ - No credentials or tokens are embedded. Do not paste secrets into health-check URLs.
125
+
126
+ ## Related Skills
127
+
128
+ - `@codebase-audit-pre-push`: clean and audit the code before it ever reaches a deploy.
129
+ - `@dos-verify-done-claims`: verify a "done" claim against git ground truth after the fact.