opencode-skills-collection 4.0.47 → 4.0.49

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 (63) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +11 -2
  2. package/bundled-skills/agent-qa-authoring/SKILL.md +3 -1
  3. package/bundled-skills/agy-delegate/SKILL.md +1 -1
  4. package/bundled-skills/aider-delegate/SKILL.md +1 -1
  5. package/bundled-skills/api-rate-limit-handler/SKILL.md +1 -1
  6. package/bundled-skills/atlas-cloud-media/SKILL.md +52 -18
  7. package/bundled-skills/babysit-pr/SKILL.md +1 -1
  8. package/bundled-skills/boost-asio-pro/references/pre-cpp20.md +8 -1
  9. package/bundled-skills/check-identity-pack/SKILL.md +88 -0
  10. package/bundled-skills/claude-delegate/SKILL.md +1 -1
  11. package/bundled-skills/cline-delegate/SKILL.md +1 -1
  12. package/bundled-skills/codex-delegate/SKILL.md +1 -1
  13. package/bundled-skills/commandcode-delegate/SKILL.md +1 -1
  14. package/bundled-skills/copilot-delegate/SKILL.md +1 -1
  15. package/bundled-skills/cursor-delegate/SKILL.md +1 -1
  16. package/bundled-skills/detect-ai-text/SKILL.md +91 -0
  17. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  18. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  19. package/bundled-skills/docs/maintainers/repo-growth-seo.md +1 -1
  20. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  21. package/bundled-skills/docs/users/aas-core.md +1 -1
  22. package/bundled-skills/docs/users/bundles.md +1 -1
  23. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  24. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  25. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  26. package/bundled-skills/docs/users/usage.md +3 -3
  27. package/bundled-skills/docs/users/visual-guide.md +4 -4
  28. package/bundled-skills/extract-document-data/SKILL.md +99 -0
  29. package/bundled-skills/falsify/SKILL.md +18 -1
  30. package/bundled-skills/find-matching-tenders/SKILL.md +100 -0
  31. package/bundled-skills/graceful-shutdown/SKILL.md +76 -70
  32. package/bundled-skills/grok-delegate/SKILL.md +1 -1
  33. package/bundled-skills/inngest/SKILL.md +2 -0
  34. package/bundled-skills/kimi-delegate/SKILL.md +1 -1
  35. package/bundled-skills/liuguang-banlan-ui/SKILL.md +6 -4
  36. package/bundled-skills/liuguang-banlan-ui/scripts/manifest_parser.py +178 -0
  37. package/bundled-skills/liuguang-banlan-ui/scripts/measure_preview.py +14 -17
  38. package/bundled-skills/liuguang-banlan-ui/scripts/validate_manifest.py +12 -16
  39. package/bundled-skills/lovable-cleanup/SKILL.md +32 -6
  40. package/bundled-skills/lovable-cleanup/references/favicon-vercel-cleanup.md +106 -0
  41. package/bundled-skills/lovable-cleanup/scripts/write-transparent-favicon.js +88 -0
  42. package/bundled-skills/multi-source-search/scripts/validate_report.py +5 -0
  43. package/bundled-skills/omp-delegate/SKILL.md +1 -1
  44. package/bundled-skills/opencode-delegate/SKILL.md +1 -1
  45. package/bundled-skills/pentest-tools/references/pentest-ai-agents-matrix.md +8 -5
  46. package/bundled-skills/pi-delegate/SKILL.md +1 -1
  47. package/bundled-skills/prompt-caching/SKILL.md +3 -1
  48. package/bundled-skills/qoder-delegate/SKILL.md +1 -1
  49. package/bundled-skills/screen-adverse-media/SKILL.md +100 -0
  50. package/bundled-skills/slideops/SKILL.md +201 -0
  51. package/bundled-skills/trigger-dev/SKILL.md +2 -0
  52. package/bundled-skills/unsloth-finetuning/SKILL.md +45 -2
  53. package/bundled-skills/upstash-qstash/SKILL.md +1 -1
  54. package/bundled-skills/upstash-ratelimit/SKILL.md +186 -0
  55. package/bundled-skills/upstash-redis/SKILL.md +156 -0
  56. package/bundled-skills/verify-citations/SKILL.md +88 -0
  57. package/bundled-skills/verify-document/SKILL.md +94 -0
  58. package/bundled-skills/vibe-delegate/SKILL.md +1 -1
  59. package/bundled-skills/warp-delegate/SKILL.md +1 -1
  60. package/bundled-skills/zcode-delegate/SKILL.md +1 -1
  61. package/package.json +1 -1
  62. package/skills_index.json +347 -54
  63. package/bundled-skills/ui-slop-score/SKILL.md +0 -80
@@ -39,7 +39,7 @@ If you came in through a **Claude Code** or **Codex** plugin instead of AAS Core
39
39
 
40
40
  When you ran `npx agentic-awesome-skills` or cloned the repository, you:
41
41
 
42
- ✅ **Downloaded 2,098+ skill files** to your computer (default: `~/.agents/skills/`; or a custom path like `~/.agent/skills/` if you used `--path`)
42
+ ✅ **Downloaded 2,107+ skill files** to your computer (default: `~/.agents/skills/`; or a custom path like `~/.agent/skills/` if you used `--path`)
43
43
  ✅ **Made them available** to your AI assistant
44
44
  ❌ **Did NOT enable them all automatically** (they're just sitting there, waiting)
45
45
 
@@ -231,7 +231,7 @@ Let's actually use a skill right now. Follow these steps:
231
231
 
232
232
  ## Direct-install Step 5: Pick Skills Manually
233
233
 
234
- Don't try to use all 2,098+ skills at once. Here's a sensible approach:
234
+ Don't try to use all 2,107+ skills at once. Here's a sensible approach:
235
235
 
236
236
  If you want a tool-specific starting point before choosing skills, use:
237
237
 
@@ -362,7 +362,7 @@ Usually no, but if your AI doesn't recognize a skill:
362
362
 
363
363
  ### "Can I load all skills into the model at once?"
364
364
 
365
- No. Even though you have 2,098+ skills installed locally, you should **not** concatenate every `SKILL.md` into a single system prompt or context block.
365
+ No. Even though you have 2,107+ skills installed locally, you should **not** concatenate every `SKILL.md` into a single system prompt or context block.
366
366
 
367
367
  The intended pattern is:
368
368
 
@@ -40,7 +40,7 @@ agentic-awesome-skills/
40
40
  ├── 📄 CONTRIBUTING.md ← Contributor workflow
41
41
  ├── 📄 CATALOG.md ← Full generated catalog
42
42
 
43
- ├── 📁 skills/ ← 2,098+ skills live here
43
+ ├── 📁 skills/ ← 2,107+ skills live here
44
44
  │ │
45
45
  │ ├── 📁 brainstorming/
46
46
  │ │ └── 📄 SKILL.md ← Skill definition
@@ -53,7 +53,7 @@ agentic-awesome-skills/
53
53
  │ │ └── 📁 2d-games/
54
54
  │ │ └── 📄 SKILL.md ← Nested skills also supported
55
55
  │ │
56
- │ └── ... (2,098+ total)
56
+ │ └── ... (2,107+ total)
57
57
 
58
58
  ├── 📁 apps/
59
59
  │ └── 📁 web-app/ ← Interactive browser
@@ -106,7 +106,7 @@ agentic-awesome-skills/
106
106
 
107
107
  ```
108
108
  ┌─────────────────────────┐
109
- │ 2,098+ SKILLS │
109
+ │ 2,107+ SKILLS │
110
110
  └────────────┬────────────┘
111
111
 
112
112
  ┌────────────────────────┼────────────────────────┐
@@ -207,7 +207,7 @@ If you want a workspace-style manual install instead, cloning into `.agent/skill
207
207
  │ ├── 📁 brainstorming/ │
208
208
  │ ├── 📁 stripe-integration/ │
209
209
  │ ├── 📁 react-best-practices/ │
210
- │ └── ... (2,098+ total) │
210
+ │ └── ... (2,107+ total) │
211
211
  └─────────────────────────────────────────┘
212
212
  ```
213
213
 
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: extract-document-data
3
+ description: Extract structured, grounded fields from documents — values cite their page, missing values abstain instead of hallucinating. Use for parsing invoices, payslips, statements, contracts.
4
+ category: document-verification
5
+ risk: critical
6
+ source: community
7
+ source_repo: Sketchjar/stipple-agent-skills
8
+ source_type: community
9
+ date_added: "2026-08-31"
10
+ author: Sketchjar
11
+ tags: [document-verification, fact-checking, stipple, authenticity]
12
+ tools: [claude, cursor, gemini, codex]
13
+ license: "Apache-2.0"
14
+ license_source: "https://github.com/Sketchjar/stipple-agent-skills/blob/main/LICENSE"
15
+ ---
16
+
17
+ # Extract Document Data
18
+
19
+ Extract structured JSON from documents with per-value grounding: every extracted value cites where it came from (page number, confidence), and values that aren't clearly present are reported in `not_found` rather than hallucinated. Uses the Stipple API (free anonymous tier).
20
+
21
+ ## When to use
22
+
23
+ - Parsing payslips, invoices, bank statements, receipts, or contracts
24
+ - Converting unstructured documents to JSON for downstream systems
25
+ - Any extraction where hallucinated values are worse than missing values (lending, accounting, compliance)
26
+
27
+ ## Instructions
28
+
29
+ 1. **Get the document.** URL or local file path (PDF, PNG, JPEG, DOCX).
30
+
31
+ 2. **Choose the extraction mode:**
32
+ - **Ad-hoc fields** — tell the API exactly which fields you want:
33
+ ```bash
34
+ curl -X POST https://www.stipple.sh/v1/extract \
35
+ -F "file=@payslip.pdf" \
36
+ -F 'fields=[{"name":"employer_name"},{"name":"net_pay"},{"name":"pay_date"}]' \
37
+ -H "Authorization: Bearer $STIPPLE_API_KEY"
38
+ ```
39
+ - **Template** — use a built-in schema: `payslip`, `tax_invoice`, `bank_statement`, `receipt`, `contract`
40
+ - **Schema-free** — omit `fields` and let the model extract what it finds
41
+
42
+ 3. **Interpret the response.**
43
+
44
+ ```json
45
+ {
46
+ "mode": "schema_free",
47
+ "document_type": "payslip",
48
+ "pages_read": 1,
49
+ "fields": {
50
+ "employer_name": {"value": "Acme Cleaning Pty Ltd", "confidence": 0.95, "page": 1},
51
+ "net_pay": {"value": "2845.10", "confidence": 0.97, "page": 1}
52
+ },
53
+ "not_found": ["ytd_tax"]
54
+ }
55
+ ```
56
+
57
+ - Every value carries `confidence` (the model's self-report) and `page` (grounding)
58
+ - `not_found[]` lists requested fields the model couldn't find — **absences are reported, never guessed**
59
+ - `pages_read` shows how many pages were processed (page limits apply per document)
60
+
61
+ 4. **Report honestly.** This is *extraction, not verification* — values are what the document **shows**, not proof it's genuine:
62
+ - "Employer: Acme Cleaning Pty Ltd (confidence 0.95, page 1)"
63
+ - "ytd_tax: not found in document" — never "ytd_tax: 0" or a guess
64
+ - For "is this document genuine?", pair with the `verify-document` skill first
65
+
66
+ ## Output format
67
+
68
+ ```
69
+ Payslip fields (grounded, not guessed):
70
+
71
+ Employer Acme Cleaning Pty Ltd (confidence 0.95, page 1)
72
+ Employee J. Citizen (confidence 0.98, page 1)
73
+ Net pay 2,845.10 (confidence 0.97, page 1)
74
+ Superannuation 268.20 (confidence 0.93, page 1)
75
+
76
+ not_found: ytd_tax
77
+ (absences are reported, never hallucinated)
78
+ ```
79
+
80
+ ## Limitations and Safety
81
+
82
+ - Invoices, statements, payslips, and contracts often contain sensitive personal,
83
+ financial, or commercial data. Obtain explicit approval before uploading them to
84
+ a hosted third party, minimize the submitted content, and confirm current
85
+ retention, residency, access, and deletion terms.
86
+ - Confidence and page grounding do not prove that an extracted value is correct or
87
+ that the source document is authentic. Reconcile consequential values against the
88
+ original document and authoritative systems before payment, lending, accounting,
89
+ compliance, or legal action.
90
+ - Keep the original file and extraction response so a human reviewer can reproduce
91
+ and correct disputed fields.
92
+
93
+ ## Notes
94
+
95
+ - Costs 1 credit per page read by the model (minimum 1); free weekly allowance applies
96
+ - Templates: `payslip`, `tax_invoice`, `bank_statement`, `receipt`, `contract` — pass as the `template` form field
97
+ - Tables are extracted with structure preserved; multi-page documents are processed page by page
98
+ - Pairs with `verify-document` (run first, for authenticity) — an extracted value from a tampered document is still wrong
99
+ - Free key at https://www.stipple.sh for metering beyond the anonymous allowance
@@ -35,6 +35,20 @@ NO VERDICT WITHOUT A FALSIFIABLE HYPOTHESIS.
35
35
  If you cannot write down what would prove you wrong, you are not allowed to conclude. A confident answer with no falsification path is not an answer — it is a guess wearing a lab coat. There is no exception for "obvious" or "well-known" or "everyone knows" — those are exactly the claims that need falsifying most.
36
36
  </EXTREMELY-IMPORTANT>
37
37
 
38
+ ## MODE SELECTION — route BEFORE answering (mandatory)
39
+
40
+ First decide which mode this question is, then act accordingly. **Do not run the five stages unless you picked Depth.** The wrong mode is itself a protocol failure.
41
+
42
+ | If the ask is... | Mode | Do |
43
+ |---|---|---|
44
+ | Live incident / production down / outage / "act now" / degrading | **Incident (OODA)** | **ACT first** at ~70% confidence with a known rollback and a time box. Do NOT run the five stages. Stabilize, then falsify the effect. Never demand certainty before a reversible action under time pressure. |
45
+ | Trivial / one-lookup fact / small talk / zero consequence | **Simple** | Answer briefly and directly. No protocol, no follow-up questions, no stage labels. |
46
+ | Rough estimate / ballpark / "about how much" / "大概" (low-stakes, reversible) | **Nudge** | Give the helpful estimate with its main assumption stated, then 2–3 targeted questions. No five-stage ledger. If being wrong costs time/money/trust, escalate to Depth. |
47
+ | Under-specified / unfalsifiable / missing key inputs | **Question** | Ask the whole open frontier in ONE round (numbered, with a recommended default each). Do not conclude, do not fabricate a default justification. |
48
+ | High-stakes / correctness gate / "why" about a failing system / will be acted on | **Depth** | Run the five stages below. |
49
+
50
+ In an incident, the Iron Law means "act reversibly, then falsify the effect" — never "analyze first, act later".
51
+
38
52
  ## When to Use This Skill
39
53
 
40
54
 
@@ -45,6 +59,9 @@ If you cannot write down what would prove you wrong, you are not allowed to conc
45
59
  - Claims about what a user, market, or system "will" do
46
60
  - Anything where being wrong costs time, money, or trust
47
61
 
62
+
63
+ **Default to Nudge (not depth) when the ask is a rough ballpark** — "rough estimate", "ballpark", "about how much", "大概", "粗略": give the helpful estimate directly with its main assumption stated, then 2–3 questions. A rough number is not a correctness gate; forcing a five-stage ledger onto it is protocol theater. **Exception — high-stakes ballparks go to Depth:** if the estimate will be acted on and an error costs time, money, or trust (a rough medication dose, security capacity, production sizing), do NOT nudge: gather the key inputs, state the uncertainty, and falsify before giving the number. The shortcut only pays when the error is cheap.
64
+
48
65
  **Do NOT activate (answer simply)** for:
49
66
  - Factual recall you can verify in one lookup
50
67
  - Trivial questions where the answer is obvious and consequences are zero
@@ -181,7 +198,7 @@ For each detected bias, state the direction (pushes the estimate up or down) and
181
198
  ## The Nudge
182
199
 
183
200
 
184
- When the question does not warrant full depth but the answer will still be acted on, do not run the five stages — append **at most 2–3 short questions**, once per conversation, each tied to something specific in the answer just given:
201
+ When the question does not warrant full depth but the answer will still be acted on, do not run the five stages — append **at most 2–3 short questions**, once per conversation, each tied to something specific in the answer just given. **High-stakes ballparks are NOT nudge territory:** a rough medication dose, security capacity, or production sizing estimate is Depth — being wrong there costs more than the shortcut saves.
185
202
 
186
203
  1. **Check a fact** — "which claim here would be worth verifying, and against what?"
187
204
  2. **Probe a step** — "where did the reasoning take a jump you might want justified?"
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: find-matching-tenders
3
+ description: Find open AU/NZ government tenders matching what a company does, ranked by fit with why and gap analysis. Use when the user asks to find tenders, bid opportunities, government contracts, or RFPs for their business (or a client's).
4
+ category: document-verification
5
+ risk: critical
6
+ source: community
7
+ source_repo: Sketchjar/stipple-agent-skills
8
+ source_type: community
9
+ date_added: "2026-08-31"
10
+ author: Sketchjar
11
+ tags: [document-verification, fact-checking, stipple, authenticity]
12
+ tools: [claude, cursor, gemini, codex]
13
+ license: "Apache-2.0"
14
+ license_source: "https://github.com/Sketchjar/stipple-agent-skills/blob/main/LICENSE"
15
+ ---
16
+
17
+ # Find Matching Tenders
18
+
19
+ Search live AU/NZ government tenders, rank them against what a company actually does (read from its website), and explain why each opportunity is relevant — including the capability gaps to prepare evidence for. Tender search is free forever on the Stipple API; no API key or signup needed.
20
+
21
+ ## When to use
22
+
23
+ - Business development: "find tenders we could bid on"
24
+ - Market research: "what government work is out there for cybersecurity firms in NSW?"
25
+ - Bid pipeline maintenance: "check for new construction tenders this week"
26
+
27
+ ## Instructions
28
+
29
+ 1. **Get the company profile.** Ask for the company's website URL. If the user provides a description instead, skip resolution and use their description directly as capability context.
30
+
31
+ 2. **Resolve the company** (optional but improves matching):
32
+
33
+ ```bash
34
+ curl -X POST https://www.stipple.sh/v1/companies/resolve \
35
+ -H "Content-Type: application/json" \
36
+ -d '{"query": "https://your-company.com"}'
37
+ ```
38
+
39
+ Returns the company's registered name, ABN, jurisdiction, and status.
40
+
41
+ 3. **Search open tenders** (free, no key):
42
+
43
+ ```bash
44
+ curl "https://www.stipple.sh/v1/tenders?q=construction&jurisdiction=AU&limit=10"
45
+ ```
46
+
47
+ Filters: `q` (keyword), `jurisdiction` (AU, AU-NSW, AU-VIC, NZ, ...), `limit`, `offset`.
48
+
49
+ 4. **Rank against the company** (optional, uses free weekly allowance):
50
+
51
+ ```bash
52
+ curl -X POST https://www.stipple.sh/v1/tenders/match \
53
+ -H "Content-Type: application/json" \
54
+ -d '{"url": "https://your-company.com", "jurisdiction": "AU", "limit": 5}'
55
+ ```
56
+
57
+ Returns ranked matches with `why[]` (why it fits) and `gaps[]` (capability evidence to prepare).
58
+
59
+ 5. **Check data provenance** if results seem thin (free):
60
+
61
+ ```bash
62
+ curl "https://www.stipple.sh/v1/tenders/sources"
63
+ ```
64
+
65
+ Shows which government feeds are indexed and their freshness — explains why a search can be empty.
66
+
67
+ 6. **Report honestly.** Match scores are *fit signals*, not win probabilities. Present `gaps[]` as "prepare evidence for this", not disqualification.
68
+
69
+ ## Output format
70
+
71
+ ```
72
+ Found 403 open tenders matching "construction" (AU/NZ)
73
+
74
+ #1 [AU-NSW] Digital platform modernisation services
75
+ buyer: NSW Department of ...
76
+ why: matches 'cloud migration', 'API integration' from your services
77
+ gap: no evidence found for 'ICT security assessment'
78
+ closes: 2026-09-15
79
+ https://...
80
+
81
+ #2 [NZ] ...
82
+ ```
83
+
84
+ ## Limitations and Safety
85
+
86
+ - The hosted service receives the search terms and, for matching, the supplied
87
+ company URL or description. Obtain approval before sending private capability or
88
+ client information and do not include secrets or non-public bid strategy.
89
+ - Feed coverage and freshness can be incomplete. Confirm eligibility, scope,
90
+ amendments, deadlines, and submission instructions on the issuing authority's
91
+ primary tender page before investing time or submitting a bid.
92
+ - Match scores and gap analysis are triage signals, not procurement advice, an
93
+ eligibility ruling, or a probability of winning.
94
+
95
+ ## Notes
96
+
97
+ - Tender search and source listing are free forever — no key, no credits
98
+ - Matching costs a small number of credits on the free weekly allowance
99
+ - For ongoing monitoring, pair with a cron job that re-runs step 3 daily and diffs results
100
+ - Data sources: NZ GETS, NSW eTendering, VendorPanel, and other government feeds
@@ -97,20 +97,21 @@ async function drainAndExit(): Promise<void> {
97
97
 
98
98
  ### Step 4: Implement readiness and liveness probes
99
99
 
100
- Orchestrators use these to decide whether to route traffic and whether to restart the container.
100
+ Orchestrators use these to decide whether to route traffic and whether to restart the container. Liveness proves the process is alive; readiness controls whether traffic is routed. While the listener is still available during a drain, keep liveness healthy and return 503 only from readiness. After the listener closes, new probes cannot connect, so do not promise that HTTP liveness remains reachable for the entire termination window.
101
101
 
102
102
  ```typescript
103
103
  import { createServer, IncomingMessage, ServerResponse } from "node:http";
104
104
 
105
105
  function handleHealthCheck(req: IncomingMessage, res: ServerResponse): void {
106
106
  if (req.url === "/healthz") {
107
- // Liveness: is the process alive and not deadlocked?
107
+ // Keep liveness distinct from readiness while the listener is available.
108
+ // Drain-rejection middleware must not turn this endpoint into a 503.
108
109
  res.writeHead(200).end("ok");
109
110
  return;
110
111
  }
111
112
 
112
113
  if (req.url === "/readyz") {
113
- // Readiness: should traffic be routed here?
114
+ // Readiness: 503 during shutdown so the load balancer stops routing.
114
115
  if (isShuttingDown) {
115
116
  res.writeHead(503).end("shutting down");
116
117
  } else {
@@ -123,21 +124,25 @@ function handleHealthCheck(req: IncomingMessage, res: ServerResponse): void {
123
124
 
124
125
  ### Step 5: Track active connections
125
126
 
126
- Maintain a count of in-flight requests so you know when draining is complete.
127
+ Maintain a count of in-flight requests so you know when draining is complete. Use a once guard covering both `finish` and `close` events so that client disconnects (aborted requests) correctly decrement the counter.
127
128
 
128
129
  ```typescript
129
130
  let activeConnections = 0;
130
131
  let drainResolve: (() => void) | null = null;
131
132
 
132
- function onRequestStart(): void {
133
+ function trackRequest(res: ServerResponse): void {
133
134
  activeConnections++;
134
- }
135
-
136
- function onRequestEnd(): void {
137
- activeConnections--;
138
- if (isShuttingDown && activeConnections === 0 && drainResolve) {
139
- drainResolve();
135
+ let counted = true;
136
+ function release(): void {
137
+ if (!counted) return;
138
+ counted = false;
139
+ activeConnections--;
140
+ if (isShuttingDown && activeConnections === 0 && drainResolve) {
141
+ drainResolve();
142
+ }
140
143
  }
144
+ res.on("finish", release);
145
+ res.on("close", release);
141
146
  }
142
147
 
143
148
  function waitForActiveConnections(): Promise<void> {
@@ -160,25 +165,41 @@ const app = express();
160
165
  const server = createServer(app);
161
166
  let isShuttingDown = false;
162
167
  let activeRequests = 0;
168
+ let drainResolve: (() => void) | null = null;
169
+
170
+ // Health endpoints — registered BEFORE the drain-rejection middleware so it
171
+ // cannot turn liveness into a 503 while the listener is still available.
172
+ app.get("/healthz", (_, res) => res.send("ok"));
173
+ app.get("/readyz", (_, res) => {
174
+ res.status(isShuttingDown ? 503 : 200).send(isShuttingDown ? "draining" : "ready");
175
+ });
163
176
 
164
- // Track in-flight requests
177
+ // Track in-flight requests and reject new application work during drain.
165
178
  app.use((req, res, next) => {
166
179
  if (isShuttingDown) {
167
180
  res.setHeader("Connection", "close");
168
181
  res.status(503).json({ error: "Server is shutting down" });
169
182
  return;
170
183
  }
184
+
171
185
  activeRequests++;
172
- res.on("finish", () => activeRequests--);
186
+ let counted = true;
187
+ function release(): void {
188
+ if (!counted) return;
189
+ counted = false;
190
+ activeRequests--;
191
+ if (isShuttingDown && activeRequests === 0 && drainResolve) {
192
+ drainResolve();
193
+ }
194
+ }
195
+ // Listen for both finish (normal) and close (client abort) so the
196
+ // counter always decrements. The once guard prevents double-decrement
197
+ // when both events fire.
198
+ res.on("finish", release);
199
+ res.on("close", release);
173
200
  next();
174
201
  });
175
202
 
176
- // Health endpoints
177
- app.get("/healthz", (_, res) => res.send("ok"));
178
- app.get("/readyz", (_, res) => {
179
- res.status(isShuttingDown ? 503 : 200).send(isShuttingDown ? "draining" : "ready");
180
- });
181
-
182
203
  // Application routes
183
204
  app.get("/api/data", async (req, res) => {
184
205
  const data = await fetchData();
@@ -199,13 +220,15 @@ function shutdown(signal: string): void {
199
220
  }, 25_000);
200
221
  forceExit.unref();
201
222
 
202
- const poll = setInterval(() => {
203
- if (activeRequests === 0) {
204
- clearInterval(poll);
205
- console.log("All requests drained — exiting cleanly");
206
- process.exit(0);
207
- }
208
- }, 100);
223
+ if (activeRequests === 0) {
224
+ console.log("No active requests — exiting cleanly");
225
+ process.exit(0);
226
+ }
227
+
228
+ drainResolve = () => {
229
+ console.log("All requests drained — exiting cleanly");
230
+ process.exit(0);
231
+ };
209
232
  }
210
233
 
211
234
  process.on("SIGTERM", () => shutdown("SIGTERM"));
@@ -214,60 +237,28 @@ process.on("SIGINT", () => shutdown("SIGINT"));
214
237
  server.listen(3000, () => console.log("Server ready on :3000"));
215
238
  ```
216
239
 
217
- ### Example 2: Python FastAPI with graceful shutdown
240
+ ### Example 2: Python FastAPI under Uvicorn
241
+
242
+ Uvicorn owns SIGTERM handling and request draining. It stops accepting new connections, asks existing connections to shut down, waits for connections and tasks up to `--timeout-graceful-shutdown`, and only then sends the ASGI lifespan shutdown event. Do not replace its signal handler or wait for requests again inside `lifespan`; use that hook to release application resources after Uvicorn's drain.
218
243
 
219
244
  ```python
220
- import asyncio
221
- import signal
222
245
  from contextlib import asynccontextmanager
223
- from fastapi import FastAPI, Request, Response
224
-
225
- active_requests = 0
226
- is_shutting_down = False
227
- shutdown_event = asyncio.Event()
246
+ from fastapi import FastAPI
228
247
 
229
248
 
230
249
  @asynccontextmanager
231
250
  async def lifespan(app: FastAPI):
232
- # Startup
233
- loop = asyncio.get_event_loop()
234
- loop.add_signal_handler(signal.SIGTERM, begin_shutdown)
235
- yield
236
- # Shutdown wait for in-flight requests
237
- if active_requests > 0:
238
- try:
239
- await asyncio.wait_for(shutdown_event.wait(), timeout=25.0)
240
- except asyncio.TimeoutError:
241
- print(f"Drain timeout — {active_requests} requests abandoned")
242
- print("Shutdown complete")
251
+ app.state.db_pool = await open_database_pool()
252
+ try:
253
+ yield
254
+ finally:
255
+ # Uvicorn has already completed or timed out its request drain.
256
+ await app.state.db_pool.close()
243
257
 
244
258
 
245
259
  app = FastAPI(lifespan=lifespan)
246
260
 
247
261
 
248
- def begin_shutdown():
249
- global is_shutting_down
250
- is_shutting_down = True
251
- print(f"SIGTERM received — draining {active_requests} requests")
252
- if active_requests == 0:
253
- shutdown_event.set()
254
-
255
-
256
- @app.middleware("http")
257
- async def track_requests(request: Request, call_next):
258
- global active_requests
259
- if is_shutting_down:
260
- return Response("Service shutting down", status_code=503)
261
- active_requests += 1
262
- try:
263
- response = await call_next(request)
264
- return response
265
- finally:
266
- active_requests -= 1
267
- if is_shutting_down and active_requests == 0:
268
- shutdown_event.set()
269
-
270
-
271
262
  @app.get("/healthz")
272
263
  async def healthz():
273
264
  return {"status": "ok"}
@@ -275,11 +266,17 @@ async def healthz():
275
266
 
276
267
  @app.get("/readyz")
277
268
  async def readyz():
278
- if is_shutting_down:
279
- return Response("draining", status_code=503)
280
269
  return {"status": "ready"}
281
270
  ```
282
271
 
272
+ Run Uvicorn with a deadline shorter than the orchestrator's kill timeout:
273
+
274
+ ```bash
275
+ uvicorn app:app --timeout-graceful-shutdown 25
276
+ ```
277
+
278
+ If readiness must turn 503 before SIGTERM, coordinate a separately secured and tested pre-stop drain signal plus a propagation delay. FastAPI's lifespan shutdown hook is too late for that transition because Uvicorn invokes it after request draining.
279
+
283
280
  ### Example 3: Background worker with checkpoint
284
281
 
285
282
  ```typescript
@@ -325,6 +322,15 @@ async function processJobs(queue: JobQueue): Promise<void> {
325
322
 
326
323
  ## Common Pitfalls
327
324
 
325
+ - **Problem:** Drain-rejection middleware turns both readiness and liveness into 503 while the HTTP listener is still available.
326
+ **Solution:** Register health routes before that middleware and flip only readiness. Once the listener closes, new probes may no longer connect; the shutdown deadline, not a promise of HTTP liveness, bounds termination.
327
+
328
+ - **Problem:** Drain hangs until the force-exit timeout even though all clients have disconnected.
329
+ **Solution:** Track request completion with both `finish` and `close` events (Node.js) or equivalent. If a client aborts the connection, `finish` may never fire — `close` will. Use a once guard to prevent double-decrementing the counter.
330
+
331
+ - **Problem:** A FastAPI application replaces Uvicorn's SIGTERM handler or waits for in-flight requests inside lifespan shutdown.
332
+ **Solution:** Let Uvicorn own signal handling, connection/task draining, and `--timeout-graceful-shutdown`. Use lifespan shutdown for resource cleanup; it runs after Uvicorn's request-drain phase.
333
+
328
334
  - **Problem:** Kubernetes kills the pod before connections drain because `terminationGracePeriodSeconds` is too short.
329
335
  **Solution:** Set it to at least drain timeout + 5s buffer. If your longest request takes 60s, use `terminationGracePeriodSeconds: 70` and drain timeout of 65s.
330
336
 
@@ -2,7 +2,7 @@
2
2
  name: grok-delegate
3
3
  description: Delegate coding tasks to the Grok Build CLI only when the user explicitly
4
4
  requests it, while the orchestrator retains review and landing responsibility.
5
- risk: safe
5
+ risk: critical
6
6
  category: agent-orchestration
7
7
  source: https://github.com/amElnagdy/delegate-skills
8
8
  source_repo: amElnagdy/delegate-skills
@@ -37,6 +37,7 @@ and durable execution without managing queues or workers.
37
37
  ## Scope
38
38
 
39
39
  - redis-queues -> bullmq-specialist
40
+ - serverless-queues -> upstash-qstash
40
41
  - workflow-orchestration -> temporal-craftsman
41
42
  - message-streaming -> event-architect
42
43
  - infrastructure -> infra-architect
@@ -388,6 +389,7 @@ Fix action: Add idempotency: 'event.data.orderId' to function config
388
389
  ### Delegation Triggers
389
390
 
390
391
  - redis|queue infrastructure|bullmq -> bullmq-specialist (Need Redis-based queue with existing infrastructure)
392
+ - serverless queue|http queue|scheduled http -> upstash-qstash (Need plain HTTP delivery and cron without an event framework)
391
393
  - saga|compensation|rollback|long-running workflow -> temporal-craftsman (Need complex workflow orchestration with compensation)
392
394
  - event sourcing|event store|cqrs -> event-architect (Need event sourcing patterns)
393
395
  - vercel|deploy|production -> vercel-deployment (Need deployment configuration)
@@ -2,7 +2,7 @@
2
2
  name: kimi-delegate
3
3
  description: Delegate coding tasks to the Kimi Code CLI (`kimi`) only when the user
4
4
  explicitly requests it, while the orchestrator retains review and landing responsibility.
5
- risk: safe
5
+ risk: critical
6
6
  category: agent-orchestration
7
7
  source: https://github.com/amElnagdy/delegate-skills
8
8
  source_repo: amElnagdy/delegate-skills
@@ -90,9 +90,10 @@ Maintain a serializable manifest with these top-level fields:
90
90
  ### 7. Validate and report
91
91
 
92
92
  - Scaffold a clean starter with `scripts/scaffold_template.py` when a neutral implementation is needed.
93
- - Treat JavaScript manifests as executable code: inspect them first and run the
94
- bundled helpers only on reviewed, locally authored configuration. Never pass
95
- an untrusted or freshly downloaded manifest to either Python helper.
93
+ - The bundled helpers parse only the restricted data-literal assignment used by
94
+ the starter. They reject expressions, function calls, duplicate keys,
95
+ unsupported syntax, trailing statements, and oversized manifests without
96
+ executing JavaScript. Keep runtime theme configs data-only as well.
96
97
  - Run `scripts/validate_manifest.py` on each theme config before rendering.
97
98
  - Capture desktop and mobile screenshots with a real browser. Inspect them directly if visual capability is available.
98
99
  - Run `scripts/measure_preview.py` on the pure field screenshot and retain measured chromatic ratio, luminance statistics, per-color coverage, and effective share.
@@ -122,7 +123,8 @@ Use the bundled starter under `assets/starter/` as a neutral base. Copy only the
122
123
  ### scripts/
123
124
 
124
125
  - `scaffold_template.py`: copy the neutral starter for `opal`, `obsidian`, or both.
125
- - `validate_manifest.py`: parse a JavaScript manifest through Node and validate required fields and ranges.
126
+ - `manifest_parser.py`: statically parse the restricted data-only manifest grammar without executing JavaScript.
127
+ - `validate_manifest.py`: validate required manifest fields and ranges.
126
128
  - `measure_preview.py`: measure a rendered pure-field PNG against the configured OKLCH palette.
127
129
 
128
130
  ### references/