skilldrop-cli 0.16.2 → 0.16.4

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 (31) hide show
  1. package/README.md +12 -1
  2. package/catalogue.json +4 -1
  3. package/model-routing.json +12 -0
  4. package/package.json +2 -2
  5. package/packs/dev-team/skills/pr-description-writer/SKILL.md +96 -0
  6. package/packs/dev-team/skills/pr-description-writer/evals/eval_queries.json +30 -0
  7. package/packs/dev-team/skills/pr-description-writer/evals/evals.json +43 -0
  8. package/packs/dev-team/skills/pr-description-writer/evals/files/1/change.diff +61 -0
  9. package/packs/dev-team/skills/pr-description-writer/evals/files/1/commits.txt +5 -0
  10. package/packs/dev-team/skills/pr-description-writer/evals/files/2/fix.diff +30 -0
  11. package/packs/dev-team/skills/pr-description-writer/evals/files/3/big.diff +90 -0
  12. package/packs/dev-team/skills/pr-description-writer/manifest.json +30 -0
  13. package/packs/dev-team/skills/release-notes/SKILL.md +1 -1
  14. package/packs/dev-team/skills/release-notes/manifest.json +2 -2
  15. package/packs/dev-team/skills/tech-debt-register/SKILL.md +103 -0
  16. package/packs/dev-team/skills/tech-debt-register/evals/eval_queries.json +34 -0
  17. package/packs/dev-team/skills/tech-debt-register/evals/evals.json +45 -0
  18. package/packs/dev-team/skills/tech-debt-register/evals/files/1/debt-notes.md +9 -0
  19. package/packs/dev-team/skills/tech-debt-register/evals/files/2/incidents.md +7 -0
  20. package/packs/dev-team/skills/tech-debt-register/evals/files/2/todos.txt +7 -0
  21. package/packs/dev-team/skills/tech-debt-register/manifest.json +31 -0
  22. package/packs/grc/skills/security-questionnaire-response/SKILL.md +106 -0
  23. package/packs/grc/skills/security-questionnaire-response/evals/eval_queries.json +34 -0
  24. package/packs/grc/skills/security-questionnaire-response/evals/evals.json +43 -0
  25. package/packs/grc/skills/security-questionnaire-response/evals/files/1/evidence/previous-answers.csv +5 -0
  26. package/packs/grc/skills/security-questionnaire-response/evals/files/1/evidence/security-policy.md +31 -0
  27. package/packs/grc/skills/security-questionnaire-response/evals/files/1/evidence/soc2-summary.md +9 -0
  28. package/packs/grc/skills/security-questionnaire-response/evals/files/1/questionnaire.csv +13 -0
  29. package/packs/grc/skills/security-questionnaire-response/evals/files/3/evidence/security-policy.md +31 -0
  30. package/packs/grc/skills/security-questionnaire-response/evals/files/3/rfp-security.md +10 -0
  31. package/packs/grc/skills/security-questionnaire-response/manifest.json +32 -0
package/README.md CHANGED
@@ -1,14 +1,17 @@
1
1
  # skilldrop
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/skilldrop-cli)](https://www.npmjs.com/package/skilldrop-cli)
4
+ [![PyPI](https://img.shields.io/pypi/v/skilldrop-cli)](https://pypi.org/project/skilldrop-cli/)
4
5
  [![license](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](LICENSE)
5
6
 
6
- **Portable AI-agent skills for the deliverables knowledge workers actually ship:** ADRs, design docs, PRDs, runbooks, threat models, decks, postmortems and adversarial reviews. Each one is a folder you copy into Claude Code, Cursor, Kiro, Codex, Antigravity or Copilot.
7
+ **Portable AI-agent skills, measured against the agent without them.** They cover the deliverables knowledge workers actually ship: ADRs, design docs, PRDs, runbooks, threat models, decks, postmortems and adversarial reviews. Each one is a folder you copy into Claude Code, Cursor, Kiro, Codex, Antigravity or Copilot.
7
8
 
8
9
  ```text
9
10
  messy input → a skill with a quality bar → a gate that can say no → your decision
10
11
  ```
11
12
 
13
+ Every skill is run as a real agent session twice, once with the skill and once without, and the results are published either way. [See the numbers and how they were measured](https://sananthanarayan.github.io/skilldrop/evals/).
14
+
12
15
  [Browse the packs](https://sananthanarayan.github.io/skilldrop/packs/) · [Search all skills](https://sananthanarayan.github.io/skilldrop/catalogue/) · [Docs](https://sananthanarayan.github.io/skilldrop/docs/) · [Install options](guides/how-to/install.md) · [Contribute](CONTRIBUTING.md)
13
16
 
14
17
  ## Choose your role
@@ -39,6 +42,13 @@ Install one pack. Every role pack also brings `core` (intake, critique, review c
39
42
  npx skilldrop-cli install --pack dev-team
40
43
  ```
41
44
 
45
+ Prefer Python tooling? The same CLI is on PyPI. It still runs on Node, so Node 16.7 or newer has to be on your `PATH`:
46
+
47
+ ```bash
48
+ pipx install skilldrop-cli # or: pip install skilldrop-cli
49
+ skilldrop install --pack dev-team
50
+ ```
51
+
42
52
  The install prints a starter prompt for the pack. For `dev-team`, ask your agent:
43
53
 
44
54
  ```text
@@ -53,6 +63,7 @@ In Claude Code you can use the plugin marketplace instead: run `/plugin marketpl
53
63
 
54
64
  - **A skill produces one file you own.** Each ships a quality bar, named anti-patterns and acceptance evals, so the output is an artifact, not a conversation. See [every skill, by category](guides/reference/skill-catalogue.md).
55
65
  - **Loops order skills, and a gate decides when work moves on.** Five loops cover the lifecycle (`discover`, `design`, `build`, `release`, `operate`), plus the `ship-a-draft` wrapper for any document. Each gate is a script, a review panel or a person, chosen by how expensive the mistake is to undo. See [the loops, stage by stage](guides/reference/loops.md) and [why loops](guides/explanation/loops.md).
66
+ - **Measured, not asserted.** `run_bench.py` runs each acceptance eval with the skill and without it, in a sandbox, and reports the lift, a blind preference from two judges, and the cost per run. The results are published whether or not they flatter the skills. See [how skills are checked](https://sananthanarayan.github.io/skilldrop/evals/) and [RFC-0040](docs/rfcs/0040-skill-benchmark.md).
56
67
  - **Copy, never transform.** A skill is a plain `SKILL.md` folder in the [Agent Skills](https://agentskills.io) format, at `packs/<pack>/skills/<name>/`. What runs in your agent is byte-identical to what is reviewed here. There is no runtime, and the tooling has zero dependencies.
57
68
  - **Updates keep your edits.** `npx skilldrop-cli update` replaces files you haven't touched. For any file you changed, it leaves the new version beside it as `<file>.upstream`. See [upgrade installed skills](guides/how-to/upgrade-skills.md).
58
69
 
package/catalogue.json CHANGED
@@ -55,6 +55,8 @@
55
55
  "sonar-review",
56
56
  "test-plan-generator",
57
57
  "bug-triage",
58
+ "pr-description-writer",
59
+ "tech-debt-register",
58
60
  "agents-md-generator",
59
61
  "llm-eval-harness",
60
62
  "eval-harness-generator",
@@ -172,7 +174,8 @@
172
174
  "skills": [
173
175
  "dpia",
174
176
  "risk-register",
175
- "soc2-evidence-map"
177
+ "soc2-evidence-map",
178
+ "security-questionnaire-response"
176
179
  ]
177
180
  },
178
181
  "build-agent-skills": {
@@ -170,6 +170,18 @@
170
170
  "tier": "standard",
171
171
  "rationale": "Structured planning against a fixed shape (decision, sub-questions, methods, criteria, stopping rule); needs judgment to cut scope but no adversarial reasoning."
172
172
  },
173
+ "pr-description-writer": {
174
+ "tier": "standard",
175
+ "rationale": "Reads a diff and rewrites it for a reviewer. Bounded by the diff; the judgment is in picking what a reviewer needs first."
176
+ },
177
+ "tech-debt-register": {
178
+ "tier": "standard",
179
+ "rationale": "Structures and ranks items against stated evidence. Judgment is bounded: a cost with no evidence is recorded as unknown, not estimated."
180
+ },
181
+ "security-questionnaire-response": {
182
+ "tier": "standard",
183
+ "rationale": "Matches questions to evidence and writes short answers. The hard rule is mechanical: no evidence, no answer."
184
+ },
173
185
  "risk-register": {
174
186
  "tier": "standard",
175
187
  "rationale": "Structured writing against a fixed format and scale, with a script doing the checks and counts. Scoring needs some judgment but is bounded by the written scale."
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "skilldrop-cli",
3
- "version": "0.16.2",
4
- "description": "Portable AI-agent skills for the deliverables knowledge workers ship \u2014 diagrams, ADRs, PRDs, runbooks, decks, reviews \u2014 installable into Claude Code, Cursor, Kiro, and any IDE that takes custom instructions. One command per skill or per role-based pack.",
3
+ "version": "0.16.4",
4
+ "description": "Portable AI-agent skills, measured against the agent without them. ADRs, PRDs, runbooks, decks and reviews, installable into Claude Code, Cursor, Kiro, and any IDE that takes custom instructions. One command per skill or per role-based pack.",
5
5
  "author": {
6
6
  "name": "Sanjay Ananthanarayan",
7
7
  "url": "https://github.com/sananthanarayan"
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: pr-description-writer
3
+ description: Write the pull request title and description for one change, from the actual diff and commits: what changed and why, how it was tested with only the checks that were really run, what a reviewer should look at first, and the risk and rollback. It also reports what the diff contains that the commits don't mention, such as a dependency bump, a deleted test or a schema change. Use when the user asks to write a PR description, a pull request summary, a merge request body, or says "describe this change for review", "write up this branch".
4
+ ---
5
+
6
+ # pr-description-writer
7
+
8
+ You write the description a reviewer reads before the diff: a title, why the change exists,
9
+ what it does, how it was checked, where to look first, and what could go wrong. Everything in
10
+ it comes from the diff, the commits and what the user told you. One change, one description.
11
+
12
+ ## How to respond
13
+
14
+ 1. **Read the change itself.** In a git repository, find the base branch (the one the user
15
+ names, else `main` or `master`) and read `git diff <base>...HEAD` and
16
+ `git log <base>..HEAD`. If the user hands you a diff or patch file, read that. Read the
17
+ whole diff, not the commit subjects: the subjects are the author's summary and the diff is
18
+ the fact.
19
+
20
+ 2. **Work out the one reason for the change.** A linked issue, the user's words, or the
21
+ commit bodies. If none of them says why, write what the change does and put
22
+ `Why: not stated — add the reason or the issue link` at the top of the body. Don't
23
+ make up a motive, an issue number or a customer.
24
+
25
+ 3. **Write the title.** Imperative mood, 72 characters or fewer, naming the behaviour that
26
+ changes. ✅ "Retry webhook delivery on 5xx with backoff". ❌ "Fix bug". ❌ "Updates to
27
+ webhook.py". Keep a `feat:` / `fix:` prefix only if the repository's history uses one.
28
+
29
+ 4. **Write the body in this order**, leaving out a section that has nothing true to say:
30
+ - **Why**: one or two sentences. The problem, not the solution.
31
+ - **What changed**: three to six bullets on behaviour, grouped by what a user or caller
32
+ would notice. No file-by-file tour.
33
+ - **How it was tested**: only checks that were run, with the command or the evidence
34
+ ("`pytest tests/webhooks -q`: 14 passed", "new test `test_retry_on_503`"). If you ran
35
+ nothing and were told nothing, write `Not run` and list the checks worth running.
36
+ - **Where to look first**: the one or two places a reviewer should spend their time, and
37
+ why. Name files and functions.
38
+ - **Risk and rollback**: what breaks if this is wrong, who notices, and how to undo it
39
+ (revert, feature flag, migration down). Say plainly when a rollback is not clean.
40
+ - **Not in this PR**: follow-ups the diff deliberately leaves out, if the commits or the
41
+ user mention any.
42
+
43
+ 5. **Report what the diff has that the commits don't mention.** Check for: a dependency or
44
+ lockfile change, a deleted or skipped test, a schema or data migration, a changed public
45
+ API or config default, a new secret or environment variable, files unrelated to the
46
+ stated purpose, and leftover debug output. Put each one under **Reviewer notes** in the
47
+ body, and repeat the list in your reply. A breaking change goes in the title or the first
48
+ line of the body.
49
+
50
+ 6. **Say when it should be more than one PR.** Two unrelated purposes in one diff, or a
51
+ refactor mixed with a behaviour change, gets one line: what to split and where. Still
52
+ write the description for the change as it stands.
53
+
54
+ 7. **Hand it over.** Print the title and the body as Markdown, ready to paste. Don't open,
55
+ edit or push the pull request unless the user asked for that.
56
+
57
+ **With no change to read** (no repository, no diff, no description of the change), say so in
58
+ one line and ask for the diff or the branch. With a described change and no diff, write the
59
+ description from the description, and state at the top that it was written without the diff.
60
+
61
+ ## Quality bar
62
+
63
+ - **Everything traces to the diff, the commits or the user.** No invented issue numbers,
64
+ metrics, reviewers or test results.
65
+ - **The test section is honest.** A check that wasn't run is listed as not run.
66
+ - **A reviewer learns where to look.** The body names the riskiest part of the diff.
67
+ - **Surprises are surfaced.** A dependency bump, deleted test, migration or API change that
68
+ the commits skip appears under Reviewer notes.
69
+ - **Claims about the code are checked.** Before saying a test does or doesn't cover a
70
+ change, or that two changes are independent, work it through against the diff (run the
71
+ arithmetic, trace the call). If you can't confirm it, leave the claim out.
72
+ - **Short.** The body fits on one screen. A long PR gets a tighter summary, not a longer one.
73
+
74
+ ## When to use this skill
75
+
76
+ - ✅ A branch is ready and needs a pull or merge request description
77
+ - ✅ A diff or patch file needs writing up for review
78
+ - ✅ An existing thin description ("fixes stuff") needs replacing before review
79
+
80
+ ## When NOT to use this skill
81
+
82
+ - ❌ Notes for a whole release or a changelog entry across many changes. Use `release-notes`.
83
+ - ❌ Deciding whether the change is safe to merge. Use `pre-merge-review`.
84
+ - ❌ Designing the tests the change needs. Use `test-plan-generator`.
85
+
86
+ ## Anti-patterns to avoid
87
+
88
+ - ❌ **Restating commit subjects as bullets.** The reviewer can read `git log`.
89
+ - ❌ **"Tested locally."** Name the command and the result, or write "Not run".
90
+ - ❌ **A file-by-file tour.** Group by behaviour; the diff already lists the files.
91
+ - ❌ **Inventing the why.** A missing reason is stated as missing.
92
+ - ❌ **Hiding a breaking change in the middle.** It leads.
93
+ - ❌ **Speculative reviewer notes.** A note that turns out wrong costs the reviewer more
94
+ than no note. Report what the diff shows, not what it might imply.
95
+ - ❌ **Showing the machinery.** The description is for the reviewer: no mention of this
96
+ skill, its files, or how the description was produced.
@@ -0,0 +1,30 @@
1
+ [
2
+ {
3
+ "query": "Write the PR description for this branch",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "I need a pull request summary for these commits before I ask for review",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "Here's my diff, write the merge request body",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "My PR description just says 'fixes stuff'. Rewrite it properly from the diff.",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "Write the release notes for everything merged since v2.3.0",
20
+ "should_trigger": false
21
+ },
22
+ {
23
+ "query": "Is this branch safe to merge? Run the full review.",
24
+ "should_trigger": false
25
+ },
26
+ {
27
+ "query": "What tests should we write for the new refund flow?",
28
+ "should_trigger": false
29
+ }
30
+ ]
@@ -0,0 +1,43 @@
1
+ {
2
+ "skill_name": "pr-description-writer",
3
+ "evals": [
4
+ {
5
+ "id": 1,
6
+ "prompt": "Write the PR description for this change. The diff is in change.diff and the commit log is in commits.txt. It's for issue #482 (webhook deliveries silently dropped when the receiver returns 503).",
7
+ "assertions": [
8
+ "The title is imperative, 72 characters or fewer, and names the retry behaviour rather than a file",
9
+ "The body explains why (deliveries dropped on a 5xx) before what changed",
10
+ "It reports that the diff bumps the requests dependency, which no commit mentions",
11
+ "It reports that the diff deletes or skips an existing test (test_delivery_timeout), which no commit mentions",
12
+ "The test section names only tests visible in the diff or says checks were not run; it does not claim a passing test run",
13
+ "It names where a reviewer should look first (the retry loop or backoff in deliver())",
14
+ "It does not invent an issue number other than #482, a metric, or a reviewer"
15
+ ]
16
+ },
17
+ {
18
+ "id": 2,
19
+ "prompt": "Describe this change for review: fix.diff. I ran `pytest tests/billing -q` and got 31 passed.",
20
+ "assertions": [
21
+ "The title names the rounding fix for invoice totals, in the imperative, 72 characters or fewer",
22
+ "The test section reports `pytest tests/billing -q`: 31 passed as the user stated, and mentions the new test in the diff",
23
+ "The body says no reason or issue was given for the change, or states the reason only as far as the diff shows it",
24
+ "It notes the changed default (ROUNDING moved from ROUND_HALF_EVEN to ROUND_HALF_UP) as something existing invoices or callers may notice",
25
+ "The body fits on one screen: no file-by-file walk-through",
26
+ "It does not invent an issue link, a customer report, or affected-invoice counts"
27
+ ]
28
+ },
29
+ {
30
+ "id": 3,
31
+ "prompt": "Write up this branch for the PR. Diff is big.diff. Tests pass.",
32
+ "assertions": [
33
+ "The breaking change (the `limit` query parameter renamed to `page_size` on GET /orders) is in the title or the first lines of the body",
34
+ "It reports the database migration (new orders.archived_at column and index) and says how to roll it back or that the rollback drops the column",
35
+ "It says the diff mixes an unrelated logging refactor with the API change and suggests splitting it",
36
+ "It reports the new environment variable ORDERS_ARCHIVE_DAYS",
37
+ "The test section does not list specific passing tests or commands that the user did not give; it records only that the user says tests pass, or asks which were run",
38
+ "It names where a reviewer should look first",
39
+ "It does not invent an issue number, a deploy date, or performance figures"
40
+ ]
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,61 @@
1
+ diff --git a/requirements.txt b/requirements.txt
2
+ --- a/requirements.txt
3
+ +++ b/requirements.txt
4
+ @@ -1,3 +1,3 @@
5
+ flask==3.0.3
6
+ -requests==2.31.0
7
+ +requests==2.32.3
8
+ redis==5.0.4
9
+ diff --git a/webhooks/deliver.py b/webhooks/deliver.py
10
+ --- a/webhooks/deliver.py
11
+ +++ b/webhooks/deliver.py
12
+ @@ -1,14 +1,27 @@
13
+ import logging
14
+ +import time
15
+
16
+ import requests
17
+
18
+ log = logging.getLogger(__name__)
19
+ +MAX_ATTEMPTS = 4
20
+ +BACKOFF_SECONDS = (1, 4, 16)
21
+
22
+
23
+ def deliver(url, payload):
24
+ - resp = requests.post(url, json=payload, timeout=5)
25
+ - if resp.status_code >= 400:
26
+ - log.warning("delivery failed: %s", resp.status_code)
27
+ - return False
28
+ - return True
29
+ + for attempt in range(1, MAX_ATTEMPTS + 1):
30
+ + resp = requests.post(url, json=payload, timeout=5)
31
+ + if resp.status_code < 400:
32
+ + return True
33
+ + if resp.status_code < 500:
34
+ + log.warning("delivery rejected: %s", resp.status_code)
35
+ + return False
36
+ + log.warning("delivery failed (attempt %d): %s", attempt, resp.status_code)
37
+ + if attempt < MAX_ATTEMPTS:
38
+ + time.sleep(BACKOFF_SECONDS[attempt - 1])
39
+ + return False
40
+ diff --git a/tests/test_deliver.py b/tests/test_deliver.py
41
+ --- a/tests/test_deliver.py
42
+ +++ b/tests/test_deliver.py
43
+ @@ -10,12 +10,22 @@ def test_delivery_ok(mock_post):
44
+ assert deliver("https://example.test/hook", {"id": 1}) is True
45
+
46
+
47
+ -def test_delivery_timeout(mock_post):
48
+ - mock_post.side_effect = requests.Timeout
49
+ - with pytest.raises(requests.Timeout):
50
+ - deliver("https://example.test/hook", {"id": 1})
51
+ +def test_retry_on_503(mock_post, monkeypatch):
52
+ + monkeypatch.setattr("webhooks.deliver.time.sleep", lambda s: None)
53
+ + mock_post.side_effect = [Resp(503), Resp(503), Resp(200)]
54
+ + assert deliver("https://example.test/hook", {"id": 1}) is True
55
+ + assert mock_post.call_count == 3
56
+ +
57
+ +
58
+ +def test_no_retry_on_404(mock_post):
59
+ + mock_post.return_value = Resp(404)
60
+ + assert deliver("https://example.test/hook", {"id": 1}) is False
61
+ + assert mock_post.call_count == 1
@@ -0,0 +1,5 @@
1
+ a41c9e2 Retry webhook delivery on 5xx
2
+
3
+ Refs #482. Deliveries were dropped when the receiver returned 503.
4
+
5
+ 7be0d13 Add backoff between retries
@@ -0,0 +1,30 @@
1
+ diff --git a/billing/totals.py b/billing/totals.py
2
+ --- a/billing/totals.py
3
+ +++ b/billing/totals.py
4
+ @@ -1,13 +1,13 @@
5
+ -from decimal import Decimal, ROUND_HALF_EVEN
6
+ +from decimal import Decimal, ROUND_HALF_UP
7
+
8
+ -ROUNDING = ROUND_HALF_EVEN
9
+ +ROUNDING = ROUND_HALF_UP
10
+ CENT = Decimal("0.01")
11
+
12
+
13
+ def invoice_total(lines, tax_rate):
14
+ - subtotal = sum(Decimal(str(l.qty)) * l.unit_price for l in lines)
15
+ - tax = (subtotal * tax_rate).quantize(CENT, rounding=ROUNDING)
16
+ - return (subtotal + tax).quantize(CENT, rounding=ROUNDING)
17
+ + subtotal = sum((Decimal(str(l.qty)) * l.unit_price).quantize(CENT, rounding=ROUNDING) for l in lines)
18
+ + tax = (subtotal * tax_rate).quantize(CENT, rounding=ROUNDING)
19
+ + return subtotal + tax
20
+ diff --git a/tests/billing/test_totals.py b/tests/billing/test_totals.py
21
+ --- a/tests/billing/test_totals.py
22
+ +++ b/tests/billing/test_totals.py
23
+ @@ -22,3 +22,9 @@ def test_total_with_tax():
24
+ assert invoice_total(lines, Decimal("0.20")) == Decimal("24.00")
25
+ +
26
+ +
27
+ +def test_line_totals_round_before_summing():
28
+ + lines = [Line(qty=3, unit_price=Decimal("0.335")), Line(qty=3, unit_price=Decimal("0.335"))]
29
+ + # each line is 1.005 -> 1.01, so the subtotal is 2.02, not 2.01
30
+ + assert invoice_total(lines, Decimal("0")) == Decimal("2.02")
@@ -0,0 +1,90 @@
1
+ diff --git a/api/orders.py b/api/orders.py
2
+ --- a/api/orders.py
3
+ +++ b/api/orders.py
4
+ @@ -8,12 +8,16 @@ router = APIRouter()
5
+
6
+ @router.get("/orders")
7
+ -def list_orders(limit: int = 50, offset: int = 0, db=Depends(get_db)):
8
+ - return db.query(Order).order_by(Order.id).offset(offset).limit(limit).all()
9
+ +def list_orders(page_size: int = 50, offset: int = 0, include_archived: bool = False, db=Depends(get_db)):
10
+ + q = db.query(Order)
11
+ + if not include_archived:
12
+ + q = q.filter(Order.archived_at.is_(None))
13
+ + return q.order_by(Order.id).offset(offset).limit(page_size).all()
14
+ diff --git a/jobs/archive.py b/jobs/archive.py
15
+ new file mode 100644
16
+ --- /dev/null
17
+ +++ b/jobs/archive.py
18
+ @@ -0,0 +1,14 @@
19
+ +import os
20
+ +from datetime import datetime, timedelta, timezone
21
+ +
22
+ +from models import Order
23
+ +
24
+ +ARCHIVE_DAYS = int(os.environ["ORDERS_ARCHIVE_DAYS"])
25
+ +
26
+ +
27
+ +def archive_old_orders(db):
28
+ + cutoff = datetime.now(timezone.utc) - timedelta(days=ARCHIVE_DAYS)
29
+ + n = db.query(Order).filter(Order.closed_at < cutoff, Order.archived_at.is_(None)).update(
30
+ + {"archived_at": datetime.now(timezone.utc)})
31
+ + db.commit()
32
+ + return n
33
+ diff --git a/migrations/versions/0042_orders_archived_at.py b/migrations/versions/0042_orders_archived_at.py
34
+ new file mode 100644
35
+ --- /dev/null
36
+ +++ b/migrations/versions/0042_orders_archived_at.py
37
+ @@ -0,0 +1,16 @@
38
+ +import sqlalchemy as sa
39
+ +from alembic import op
40
+ +
41
+ +revision = "0042"
42
+ +down_revision = "0041"
43
+ +
44
+ +
45
+ +def upgrade():
46
+ + op.add_column("orders", sa.Column("archived_at", sa.DateTime(timezone=True), nullable=True))
47
+ + op.create_index("ix_orders_archived_at", "orders", ["archived_at"])
48
+ +
49
+ +
50
+ +def downgrade():
51
+ + op.drop_index("ix_orders_archived_at", "orders")
52
+ + op.drop_column("orders", "archived_at")
53
+ diff --git a/models.py b/models.py
54
+ --- a/models.py
55
+ +++ b/models.py
56
+ @@ -14,3 +14,4 @@ class Order(Base):
57
+ closed_at = Column(DateTime(timezone=True))
58
+ + archived_at = Column(DateTime(timezone=True), nullable=True, index=True)
59
+ diff --git a/core/logging.py b/core/logging.py
60
+ --- a/core/logging.py
61
+ +++ b/core/logging.py
62
+ @@ -1,9 +1,15 @@
63
+ -import logging
64
+ +import json
65
+ +import logging
66
+
67
+
68
+ -def setup():
69
+ - logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
70
+ +class JsonFormatter(logging.Formatter):
71
+ + def format(self, record):
72
+ + return json.dumps({"ts": self.formatTime(record), "level": record.levelname, "msg": record.getMessage()})
73
+ +
74
+ +
75
+ +def setup():
76
+ + handler = logging.StreamHandler()
77
+ + handler.setFormatter(JsonFormatter())
78
+ + logging.basicConfig(level=logging.INFO, handlers=[handler])
79
+ diff --git a/tests/test_orders.py b/tests/test_orders.py
80
+ --- a/tests/test_orders.py
81
+ +++ b/tests/test_orders.py
82
+ @@ -30,3 +30,10 @@ def test_list_orders_default(client):
83
+ assert len(client.get("/orders").json()) == 50
84
+ +
85
+ +
86
+ +def test_archived_orders_hidden_by_default(client, archived_order):
87
+ + ids = [o["id"] for o in client.get("/orders").json()]
88
+ + assert archived_order.id not in ids
89
+ + ids = [o["id"] for o in client.get("/orders?include_archived=true").json()]
90
+ + assert archived_order.id in ids
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "pr-description-writer",
3
+ "version": "0.1.0",
4
+ "description": "Write the pull request title and description for one change, from the actual diff and commits: what changed and why, how it was tested with only the checks that were really run, what a reviewer should look at first, and the risk and rollback. It also reports what the diff contains that the commits don't mention, such as a dependency bump, a deleted test or a schema change. Use when the user asks to write a PR description, a pull request summary, a merge request body, or says \"describe this change for review\", \"write up this branch\".",
5
+ "entrypoint": "SKILL.md",
6
+ "deps": {
7
+ "npm": [],
8
+ "pip": []
9
+ },
10
+ "env": {
11
+ "required": [],
12
+ "optional": []
13
+ },
14
+ "related": [
15
+ "pre-merge-review",
16
+ "release-notes",
17
+ "test-plan-generator"
18
+ ],
19
+ "tags": [
20
+ "pull-request",
21
+ "code-review",
22
+ "git",
23
+ "description",
24
+ "merge-request"
25
+ ],
26
+ "model": {
27
+ "tier": "standard",
28
+ "rationale": "Reads a diff and rewrites it for a reviewer. Bounded by the diff; the judgment is in picking what a reviewer needs first."
29
+ }
30
+ }
@@ -46,7 +46,7 @@ Converts commit history into two artifacts with different readers: **customer-fa
46
46
 
47
47
  ## When NOT to use this skill
48
48
 
49
- - ❌ A single PR description — that's one change, just write it
49
+ - ❌ A single PR description — that's one change; use `pr-description-writer`
50
50
  - ❌ Reviewing the changes for quality or risk — that's `devils-advocate` / `sonar-review`
51
51
  - ❌ Stakeholder narrative about *why* the release matters strategically — feed the notes to `exec-summary`
52
52
  - ❌ Generating commit messages going forward — this skill reads history, it doesn't write it
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "release-notes",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Turn git history between two refs into release notes — a customer-facing version written in reader benefits and an internal Keep-a-Changelog version with commit traceability, breaking changes always first. Use when the user wants release notes, a changelog, \"what's in this release\", a deploy announcement, or to summarize changes since the last tag.",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": { "npm": [], "pip": [] },
7
7
  "env": { "required": [], "optional": [] },
8
- "related": ["devils-advocate", "exec-summary", "sonar-review"],
8
+ "related": ["devils-advocate", "exec-summary", "pr-description-writer", "sonar-review"],
9
9
  "tags": ["release-notes", "changelog", "git", "release", "deploy", "communication"],
10
10
  "model": { "tier": "standard", "rationale": "Classifies commits and rewrites them for a reader. The audience translation needs judgment; escalate via large-input on very long ranges." }
11
11
  }
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: tech-debt-register
3
+ description: Turn a pile of technical-debt complaints, TODOs and incident notes into a ranked register: each item with where it lives, the cost it imposes today backed by evidence, the size of the fix, a decision (pay now, schedule, accept or watch) with the trigger that reopens it, and an owner if one was given. It ends with the few items worth paying down this quarter and why. Use when the user asks for a tech debt register, a debt inventory or backlog, "what debt should we pay down", "prioritise our tech debt", or wants to make the case for refactoring time.
4
+ ---
5
+
6
+ # tech-debt-register
7
+
8
+ You turn complaints about the codebase into a register a team can decide from. Debt is
9
+ something that costs the team now, every week, until it is fixed. The register says what
10
+ each item costs, what fixing it takes, and what to do about it, and it is short enough to
11
+ be read in a planning meeting.
12
+
13
+ ## How to respond
14
+
15
+ 1. **Gather the items from what you were given.** Notes, a TODO or FIXME list, incident
16
+ write-ups, a ticket export, the user's own list. In a repository you may also search for
17
+ `TODO`, `FIXME`, `HACK` and skipped tests, and say that you did. Merge items that
18
+ describe the same underlying problem and keep the count of what was merged.
19
+
20
+ 2. **Write each item as a thing with a place.** ✅ "Order export builds CSV by string
21
+ concatenation in `export/orders.py`; breaks on commas in customer names." ❌ "Code
22
+ quality is poor." ❌ "Needs refactoring." An item you cannot locate goes under
23
+ *Unlocated*, with the question that would locate it.
24
+
25
+ 3. **State the cost it imposes today, with the evidence.** This is the interest. Use what
26
+ the input shows: incidents caused, hours lost, a slow build time, a recurring bug, a
27
+ workaround every new joiner has to learn. Quote or cite it. When the input shows no
28
+ cost, write `no evidence of cost` and do not guess one. An item with no observable cost
29
+ is a preference; keep it in the register, at the bottom.
30
+
31
+ The user's own statement counts as evidence of a cost, marked `reported, not measured`:
32
+ "deploys take ages" and "tests are flaky" are costs someone is paying. Record them as
33
+ reported, say what measurement would confirm them, and still rank them.
34
+
35
+ 4. **Size the fix.** S (under two days), M (up to two weeks), L (more). Use the team's own
36
+ estimate when they gave one. Add what the fix depends on and what it puts at risk.
37
+
38
+ 5. **Decide, one of four, with a reason in a clause:**
39
+ - **Pay now**: high, evidenced cost and a fix the team can finish this quarter.
40
+ - **Schedule**: real cost, but the fix is large or blocked; name what it waits on.
41
+ - **Accept**: the cost is small or the code is going away; say for how long.
42
+ - **Watch**: no cost reported or shown; name the signal that would promote it.
43
+
44
+ Don't hide behind Watch. A list where every item is Watch is not a register. When costs
45
+ are reported but unmeasured, still pick an order and one or two candidates to pay down,
46
+ label the ranking `provisional`, and say which single measurement would change it.
47
+
48
+ Every item gets a **trigger**: the event that reopens the decision ("a second export
49
+ incident", "the service passes 50 req/s"). Not a date alone.
50
+
51
+ 6. **Rank by cost against size.** Evidenced, recurring cost with a small fix first. Do not
52
+ produce a numeric score that the input cannot support; order the list and say in one
53
+ line why the top items are on top.
54
+
55
+ 7. **Write the register.** A table with these columns: `ID`, `Item`, `Where`,
56
+ `Cost today (evidence)`, `Fix size`, `Decision`, `Trigger`, `Owner`. Owner is filled only
57
+ when the input names one; otherwise `unassigned`. When you can write files, save it as
58
+ `tech-debt-register.md` so the team can keep it, and show the lead-in and the table in
59
+ the reply as well. Add a CSV with the same columns when the user asks for one.
60
+
61
+ 8. **Lead with the decision.** Above the table, in this order: the three or fewer items to
62
+ pay down this quarter and the cost each removes; what was merged, and what was left
63
+ unlocated or without evidence; the one question whose answer would most change the
64
+ ranking.
65
+
66
+ **With nothing to work from** (no items, no repository, no notes), ask for the list in one
67
+ line. With a thin list, build the register from it, mark what is unknown as unknown, and
68
+ say what evidence would firm it up. Do not pad it with debt the input never mentioned.
69
+
70
+ ## Quality bar
71
+
72
+ - **Every cost has evidence or says it has none.** No invented hours, incident counts,
73
+ percentages or money.
74
+ - **Every item has a place** or is listed as unlocated.
75
+ - **Every item has a decision and a trigger.**
76
+ - **The top of the list is defensible**: evidenced cost, fix the team can finish.
77
+ - **It fits a planning meeting.** One table, one short lead-in.
78
+
79
+ ## When to use this skill
80
+
81
+ - ✅ Making the case for refactoring time with something better than "the code is messy"
82
+ - ✅ Quarterly planning: which debt to pay down and which to live with
83
+ - ✅ Turning a TODO grep, incident notes or a rant into a ranked, decided list
84
+
85
+ ## When NOT to use this skill
86
+
87
+ - ❌ Planning how to carry out one large change. Use `migration-plan`.
88
+ - ❌ Sorting incoming bug reports. Use `bug-triage`.
89
+ - ❌ Cleaning up a tracker backlog of mixed work. Use `backlog-triage`.
90
+ - ❌ Business or delivery risks that are not about the code. Use `risk-register`.
91
+
92
+ ## Anti-patterns to avoid
93
+
94
+ - ❌ **Invented numbers.** "Costs about 6 hours a week" with nothing behind it is a guess
95
+ that will be quoted in a planning deck.
96
+ - ❌ **Everything is high priority.** If most items are "pay now", none are.
97
+ - ❌ **Everything is Watch.** Declining to rank because nothing was measured hands the
98
+ decision back unmade.
99
+ - ❌ **Listing preferences as debt.** "Should be in TypeScript" with no cost is a wish.
100
+ - ❌ **A weighted score to two decimals.** False precision over thin evidence.
101
+ - ❌ **Padding.** Adding generic debt ("improve test coverage") that nobody raised.
102
+ - ❌ **Showing the machinery.** The register is for the team: no mention of this skill or
103
+ how it was produced.
@@ -0,0 +1,34 @@
1
+ [
2
+ {
3
+ "query": "Build a tech debt register from these notes",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "Which of our technical debt should we pay down this quarter?",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "I need to make the case to my director for refactoring time. Here's what's wrong with the codebase.",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "Turn this TODO/FIXME grep into a prioritised debt backlog",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "Plan the migration from our monolith's Postgres 12 to 16 with a rollback",
20
+ "should_trigger": false
21
+ },
22
+ {
23
+ "query": "Triage these 40 incoming bug reports by severity",
24
+ "should_trigger": false
25
+ },
26
+ {
27
+ "query": "Clean up our Jira backlog: find duplicates and stale tickets",
28
+ "should_trigger": false
29
+ },
30
+ {
31
+ "query": "Build a risk register for the office relocation project",
32
+ "should_trigger": false
33
+ }
34
+ ]
@@ -0,0 +1,45 @@
1
+ {
2
+ "skill_name": "tech-debt-register",
3
+ "evals": [
4
+ {
5
+ "id": 1,
6
+ "prompt": "We have planning next week and I want to argue for debt time. My notes are in debt-notes.md. Turn them into a register and tell me what to pay down this quarter.",
7
+ "assertions": [
8
+ "The two notes about the order export (CSV string concatenation and the comma bug) are merged into one item, and the merge is stated",
9
+ "The export item cites its evidence from the notes (three incidents in Q3, INC-311, INC-318 and INC-327) and is among the items to pay down this quarter",
10
+ "The 22-minute CI build is recorded with its evidence (the measured time and the 14 engineers waiting on it)",
11
+ "The 'rewrite the frontend in TypeScript' note is recorded as having no evidence of cost and is not ranked as pay-now",
12
+ "Every item has a decision (pay now, schedule, accept or watch) and a trigger",
13
+ "Owners are filled only where the notes name one (Priya for the export); others are unassigned",
14
+ "It does not invent hours lost, incident counts, money or percentages that are not in the notes",
15
+ "The lead-in names three or fewer items for this quarter, above the table"
16
+ ]
17
+ },
18
+ {
19
+ "id": 2,
20
+ "prompt": "Here's a TODO/FIXME grep of the payments service (todos.txt) and last quarter's incident list (incidents.md). Build a tech debt register.",
21
+ "assertions": [
22
+ "The retry-without-idempotency-key TODO in payments/charge.py is linked to the duplicate-charge incidents in incidents.md and ranked at or near the top",
23
+ "TODOs with no matching incident or stated cost are marked as having no evidence of cost, with decision watch or accept",
24
+ "Each item names a file or module from todos.txt",
25
+ "Duplicate or near-duplicate TODOs (the two about hard-coded currency) are merged",
26
+ "Fix sizes are given as S, M or L, without invented day counts beyond the scale",
27
+ "It does not invent incidents, dates or customer impact beyond what incidents.md states",
28
+ "Every item has a trigger that is an event, not only a date"
29
+ ]
30
+ },
31
+ {
32
+ "id": 3,
33
+ "prompt": "We have a lot of tech debt: the monolith's deploy takes ages, nobody understands the billing module, tests are flaky, and we're still on Python 3.8. Make me a register.",
34
+ "assertions": [
35
+ "It produces a register with the four items rather than refusing or asking a list of questions first",
36
+ "Each item's cost is marked as not evidenced or unknown, because the prompt gives no numbers",
37
+ "It does not invent deploy times, flake rates, hours lost or incident counts",
38
+ "The Python 3.8 item notes that 3.8 is past end of life as a known fact, or asks to confirm, without inventing a specific vulnerability or deadline",
39
+ "It states what evidence would firm up the ranking (for example deploy duration, flaky-test rate, incidents traced to billing)",
40
+ "It adds no debt items beyond the four the user listed",
41
+ "Owners are unassigned"
42
+ ]
43
+ }
44
+ ]
45
+ }
@@ -0,0 +1,9 @@
1
+ # Things that hurt (collected in retro, Sept)
2
+
3
+ - Order export builds the CSV with string concatenation (export/orders.py). Priya owns export.
4
+ - CI build takes 22 minutes on main (measured last week, `ci-timings.csv`). 14 engineers push to it. People batch PRs to avoid waiting.
5
+ - Export breaks when a customer name has a comma. Three incidents in Q3: INC-311, INC-318, INC-327. Each one was a manual re-export for finance.
6
+ - We should rewrite the frontend in TypeScript, it would be nicer.
7
+ - The `legacy_auth` module is still imported by the admin panel. It's scheduled for removal when admin moves to SSO in Q1.
8
+ - Feature flags are never cleaned up: 143 flags in `flags.yaml`, 61 of them at 100% for more than 6 months. New joiners ask which ones matter.
9
+ - Staging database is refreshed by hand by whoever remembers. Last refresh was 11 weeks ago; two bugs last month only reproduced in prod.
@@ -0,0 +1,7 @@
1
+ # Payments incidents, Q3 2026
2
+
3
+ | ID | Date | What happened |
4
+ |---|---|---|
5
+ | INC-402 | 2026-07-19 | 14 customers charged twice after a provider timeout triggered our retry. Refunded manually. |
6
+ | INC-417 | 2026-08-30 | 9 customers charged twice, same cause as INC-402. |
7
+ | INC-431 | 2026-09-12 | Monthly report job killed by the OOM killer; finance report delivered a day late. |
@@ -0,0 +1,7 @@
1
+ payments/charge.py:88: # TODO: retries don't send an idempotency key, provider may double-charge
2
+ payments/charge.py:140: # FIXME: currency hard-coded to USD
3
+ payments/refund.py:31: # TODO: currency is hard-coded, should come from the original charge
4
+ payments/webhooks.py:12: # HACK: sleep(2) to wait for the provider to settle
5
+ payments/ledger.py:207: # TODO: rename this function, the name is misleading
6
+ payments/reports.py:55: # FIXME: loads the whole month into memory
7
+ tests/test_charge.py:14: @pytest.mark.skip(reason="flaky, fix later")
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "tech-debt-register",
3
+ "version": "0.1.0",
4
+ "description": "Turn a pile of technical-debt complaints, TODOs and incident notes into a ranked register: each item with where it lives, the cost it imposes today backed by evidence, the size of the fix, a decision (pay now, schedule, accept or watch) with the trigger that reopens it, and an owner if one was given. It ends with the few items worth paying down this quarter and why. Use when the user asks for a tech debt register, a debt inventory or backlog, \"what debt should we pay down\", \"prioritise our tech debt\", or wants to make the case for refactoring time.",
5
+ "entrypoint": "SKILL.md",
6
+ "deps": {
7
+ "npm": [],
8
+ "pip": []
9
+ },
10
+ "env": {
11
+ "required": [],
12
+ "optional": []
13
+ },
14
+ "related": [
15
+ "backlog-triage",
16
+ "bug-triage",
17
+ "migration-plan",
18
+ "risk-register"
19
+ ],
20
+ "tags": [
21
+ "tech-debt",
22
+ "refactoring",
23
+ "planning",
24
+ "prioritisation",
25
+ "engineering"
26
+ ],
27
+ "model": {
28
+ "tier": "standard",
29
+ "rationale": "Structures and ranks items against stated evidence. Judgment is bounded: a cost with no evidence is recorded as unknown, not estimated."
30
+ }
31
+ }
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: security-questionnaire-response
3
+ description: Answer a customer's security questionnaire from your own evidence: each question gets a direct answer (Yes, No, Partial or Not applicable), a short narrative, and the document and section it rests on. Questions the evidence doesn't cover are marked as needing input instead of being answered, and conflicts between sources, stale evidence and answers that would commit you to future work are flagged for the security owner. Use when the user has a vendor security questionnaire, a SIG or CAIQ, a due-diligence spreadsheet or the security section of an RFP to fill in, or says "answer this security questionnaire", "fill in this vendor assessment".
4
+ ---
5
+
6
+ # security-questionnaire-response
7
+
8
+ You draft the answers to a customer's security questionnaire from the evidence the company
9
+ already has: policies, an audit report, earlier answers, architecture notes. Every answer
10
+ rests on a document you can point to. Where the evidence runs out you say so, because a
11
+ wrong "Yes" in a questionnaire becomes a contractual claim. The draft is for the security
12
+ owner to review and send; it is not a statement you make on the company's behalf.
13
+
14
+ ## How to respond
15
+
16
+ 1. **Read the questionnaire and the evidence before answering anything.** Take the
17
+ questions from the file or paste the user gives you, and the evidence from the files they
18
+ point to. Keep the customer's own question IDs and order.
19
+
20
+ 2. **Answer each question in four parts:**
21
+ - **Answer**: `Yes`, `No`, `Partial` or `Not applicable`, or `Needs input` when the
22
+ evidence does not settle it. A question that asks for a value ("what is your retention
23
+ period?", "how often?") gets the value from the evidence as its answer, not Yes or No.
24
+ - **Response**: one to three sentences a customer can read, saying what is true today.
25
+ Plain claims, no marketing. For `Partial`, say what is covered and what is not.
26
+ - **Evidence**: the document and the section, page or row it comes from.
27
+ - **Review flag**: blank, or one of the flags in step 4.
28
+
29
+ 3. **Never answer past the evidence.**
30
+ - Nothing in the evidence covers it → `Needs input`, and name who would know or what
31
+ document would answer it. Do not infer a "Yes" from a related control.
32
+ - A certification, audit or test is claimed only if the evidence shows it, with its date
33
+ and scope. "Working towards ISO 27001" is a `No` with that sentence as the response.
34
+ - Numbers (retention periods, RTO, patch windows) are copied from the evidence, never
35
+ rounded or supplied from common practice.
36
+
37
+ 4. **Flag what the security owner must look at:**
38
+ - `conflict`: two sources disagree. Quote both, with where each comes from. Do not pick.
39
+ - `stale`: the evidence is older than 12 months, or older than the period the question
40
+ asks about. Give its date.
41
+ - `commitment`: the honest answer promises future work ("planned for Q2"). Customers
42
+ treat these as commitments.
43
+ - `no`: the answer is `No` or `Partial` on something customers commonly require.
44
+ - `reused`: taken from a previous questionnaire without a primary source behind it.
45
+
46
+ 5. **Keep answers consistent.** The same underlying question asked twice gets the same
47
+ answer. If an earlier questionnaire answered it differently, that is a `conflict`.
48
+
49
+ 6. **Write the output in the customer's format, at the length they asked for.** The
50
+ customer's document is what gets sent back, so fill in their layout: answers under their
51
+ questions in a document, extra columns (`Answer`, `Response`, `Evidence`, `Review flag`)
52
+ in a spreadsheet. If they asked for "Yes or No with a short explanation", give exactly
53
+ that in the document and keep the evidence and flags in your reply and in a separate
54
+ review notes file. When the user says to fill in a file, fill in that file. Otherwise
55
+ write a copy next to it (`<name>-answers.csv` or `.md`) and leave the original alone.
56
+
57
+ A CSV must stay a valid CSV: quote every field that contains a comma, a quote or a line
58
+ break, and re-read the file you wrote to check that each row has the same number of
59
+ columns as the header.
60
+
61
+ 7. **Lead with what needs a person.** Above the answers: how many questions were answered
62
+ from evidence, how many need input, and how many are flagged; then the flagged and
63
+ needs-input questions as a short list, each with what would resolve it. Close with one
64
+ line saying the draft needs the security owner's review before it is sent.
65
+
66
+ **With a questionnaire and no evidence**, do not answer from general knowledge of what
67
+ companies usually do. Return every question as `Needs input`, grouped by the kind of
68
+ document that would answer it (access control policy, audit report, incident response
69
+ plan), so the user knows what to fetch. With no questionnaire, ask for it in one line.
70
+
71
+ ## Quality bar
72
+
73
+ - **Every Yes, No and Partial cites a document and a place in it.**
74
+ - **Nothing is claimed that the evidence does not show**: no certification, test, number or
75
+ control is invented or upgraded.
76
+ - **Gaps are visible.** Needs-input and flagged questions are counted and listed first.
77
+ - **Conflicts are shown, not resolved.**
78
+ - **The customer's IDs, order and wording of questions are preserved.**
79
+
80
+ ## When to use this skill
81
+
82
+ - ✅ A customer or prospect sends a security questionnaire, SIG, CAIQ or due-diligence sheet
83
+ - ✅ The security section of an RFP needs answers from existing policies and reports
84
+ - ✅ Re-answering last year's questionnaire and checking the old answers still hold
85
+
86
+ ## When NOT to use this skill
87
+
88
+ - ❌ Mapping your controls to SOC 2 criteria and the evidence an auditor wants. Use
89
+ `soc2-evidence-map`.
90
+ - ❌ Assessing a processing activity's risk to individuals under GDPR. Use `dpia`.
91
+ - ❌ Finding the threats in a system design. Use `threat-model`.
92
+ - ❌ Listing and scoring business risks. Use `risk-register`.
93
+
94
+ ## Anti-patterns to avoid
95
+
96
+ - ❌ **"Yes" because most companies do it.** No evidence, no Yes.
97
+ - ❌ **Upgrading the claim.** "Encrypted in transit" in the policy does not become
98
+ "encrypted in transit and at rest".
99
+ - ❌ **Smoothing over a conflict** by choosing the better-looking source.
100
+ - ❌ **Copying last year's answer** without checking it against a current document.
101
+ - ❌ **Long narratives.** A reviewer scanning 200 answers needs three sentences, not a page.
102
+ - ❌ **Handing back your own format.** The customer sent a form; return their form filled
103
+ in, not a separate report they have to transcribe from.
104
+ - ❌ **A broken spreadsheet.** An unquoted comma shifts every column after it.
105
+ - ❌ **Showing the machinery.** The answers are for the customer and the security owner: no
106
+ mention of this skill or how the draft was produced.
@@ -0,0 +1,34 @@
1
+ [
2
+ {
3
+ "query": "Answer this vendor security questionnaire from our policies",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "A customer sent us a SIG Lite to fill in. The evidence is in this folder.",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "Fill in the security section of this RFP using our SOC 2 summary",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "Check whether last year's due-diligence answers still match our current policies",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "Map our controls to the SOC 2 criteria and list the evidence the auditor will ask for",
20
+ "should_trigger": false
21
+ },
22
+ {
23
+ "query": "We're adding biometric clock-in. Do we need a DPIA and can you draft it?",
24
+ "should_trigger": false
25
+ },
26
+ {
27
+ "query": "Threat model this new file-upload service",
28
+ "should_trigger": false
29
+ },
30
+ {
31
+ "query": "Score these risks for the board heat map",
32
+ "should_trigger": false
33
+ }
34
+ ]
@@ -0,0 +1,43 @@
1
+ {
2
+ "skill_name": "security-questionnaire-response",
3
+ "evals": [
4
+ {
5
+ "id": 1,
6
+ "prompt": "Acme sent us their vendor security questionnaire (questionnaire.csv). Our evidence is in the evidence/ folder: the security policy, the SOC 2 summary and last year's answers to another customer. Draft the answers.",
7
+ "assertions": [
8
+ "Q4 (ISO 27001 certified?) is answered No, with the response noting it is not held; it is not answered Yes or Partial on the strength of SOC 2",
9
+ "Q7 (penetration test in the last 12 months) is flagged stale or answered No/Needs input, citing the test dated 2025-06, which is older than 12 months",
10
+ "Q9 (MFA for all staff) is answered Partial: enforced for engineering and admin access, not yet for all staff, citing the security policy",
11
+ "Q11 (data retention period) flags a conflict between the security policy (90 days) and last year's answers (30 days), quoting both and not choosing one",
12
+ "Q12 (cyber insurance) is marked Needs input because no evidence covers it",
13
+ "Every Yes, No or Partial answer cites a document and a section or row",
14
+ "The reply leads with counts of answered, needs-input and flagged questions and lists the flagged ones",
15
+ "The answers keep Acme's question IDs and order, and are written to a new file without overwriting questionnaire.csv"
16
+ ]
17
+ },
18
+ {
19
+ "id": 2,
20
+ "prompt": "A prospect asked these five questions. Can you answer them for us?\n1. Do you encrypt customer data at rest?\n2. Do you have a SOC 2 Type II report?\n3. How often do you run penetration tests?\n4. Where is customer data hosted?\n5. Do you have a documented incident response plan?",
21
+ "assertions": [
22
+ "All five questions are returned as Needs input (or the equivalent), because no evidence was supplied",
23
+ "It does not answer Yes, give a hosting region, a test frequency or a certification from general knowledge",
24
+ "It says what document would answer each question (for example the encryption standard, the SOC 2 report, the pen-test report, the architecture or hosting record, the incident response plan)",
25
+ "It keeps the prospect's numbering and wording",
26
+ "The reply is short and asks for the evidence in plain words"
27
+ ]
28
+ },
29
+ {
30
+ "id": 3,
31
+ "prompt": "Fill in the security section of this RFP (rfp-security.md) using evidence/security-policy.md. They want Yes/No with a short explanation.",
32
+ "assertions": [
33
+ "The question on encryption in transit is answered Yes citing the policy's TLS 1.2+ requirement, and the answer does not also claim encryption at rest unless the policy states it",
34
+ "The question on backups being tested is answered from the policy's quarterly restore test, with the frequency copied exactly",
35
+ "The question on a 24/7 security operations centre is answered No or Needs input, since the policy describes business-hours on-call only",
36
+ "The question whose honest answer is 'planned' (SSO for the customer portal, planned for Q1 2027) is flagged as a commitment",
37
+ "Each answer is one to three sentences",
38
+ "It does not invent certifications, tools, vendors or dates that are not in the policy",
39
+ "The reply states the draft needs the security owner's review before it is sent"
40
+ ]
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,5 @@
1
+ Customer,Date,Question,Answer
2
+ Adatum,2025-09-02,Do you have an information security policy?,Yes. Version 4.1 approved by the CTO.
3
+ Adatum,2025-09-02,What is your data retention period after termination?,Customer data is deleted 30 days after contract termination.
4
+ Adatum,2025-09-02,Do you perform penetration tests?,Yes. Annual independent penetration test; last completed June 2025.
5
+ Adatum,2025-09-02,Is MFA enforced?,Yes for engineering and production administrators.
@@ -0,0 +1,31 @@
1
+ # Northwind Health information security policy
2
+
3
+ Version 4.2, approved by the CTO on 2026-03-14. Reviewed annually.
4
+
5
+ ## 1. Scope
6
+ Applies to all staff, contractors and systems that store or process customer data.
7
+
8
+ ## 2. Encryption
9
+ 2.1 All customer data in transit is encrypted with TLS 1.2 or higher.
10
+ 2.2 Customer data at rest in the production database and object storage is encrypted with AES-256.
11
+
12
+ ## 3. Access control
13
+ 3.1 Multi-factor authentication is enforced for engineering staff and for all administrative access to production.
14
+ 3.2 Roll-out of multi-factor authentication to all remaining staff is in progress.
15
+ 3.3 Access is reviewed quarterly by system owners.
16
+
17
+ ## 4. Training
18
+ 4.1 All staff complete security awareness training on joining and annually after that.
19
+
20
+ ## 5. Incident response
21
+ 5.1 The incident response plan (IR-001) is maintained by the security lead and tested annually.
22
+ 5.2 Security on-call covers business hours, 08:00 to 18:00 UK time, Monday to Friday.
23
+
24
+ ## 6. Backups
25
+ 6.1 Production databases are backed up daily. Restores are tested quarterly.
26
+
27
+ ## 7. Retention
28
+ 7.1 Customer data is deleted 90 days after contract termination.
29
+
30
+ ## 8. Customer portal
31
+ 8.1 Single sign-on for the customer portal is planned for Q1 2027.
@@ -0,0 +1,9 @@
1
+ # SOC 2 summary
2
+
3
+ - Report type: SOC 2 Type II
4
+ - Period covered: 2025-07-01 to 2026-06-30
5
+ - Auditor: Fabrikam Assurance LLP
6
+ - Criteria: Security, Availability
7
+ - Opinion: unqualified
8
+ - Penetration test referenced in the report: independent test by Contoso Security, completed 2025-06-18.
9
+ - ISO 27001: not held. Not in scope of this report.
@@ -0,0 +1,13 @@
1
+ ID,Question
2
+ Q1,Do you have a documented information security policy approved by management?
3
+ Q2,Is customer data encrypted in transit?
4
+ Q3,Is customer data encrypted at rest?
5
+ Q4,Are you ISO 27001 certified?
6
+ Q5,Do you hold a current SOC 2 Type II report?
7
+ Q6,Are employees required to complete security awareness training annually?
8
+ Q7,Have you had an independent penetration test in the last 12 months?
9
+ Q8,Do you have a documented incident response plan?
10
+ Q9,Is multi-factor authentication enforced for all staff?
11
+ Q10,Are backups performed and tested?
12
+ Q11,What is your retention period for customer data after contract termination?
13
+ Q12,Do you carry cyber liability insurance?
@@ -0,0 +1,31 @@
1
+ # Northwind Health information security policy
2
+
3
+ Version 4.2, approved by the CTO on 2026-03-14. Reviewed annually.
4
+
5
+ ## 1. Scope
6
+ Applies to all staff, contractors and systems that store or process customer data.
7
+
8
+ ## 2. Encryption
9
+ 2.1 All customer data in transit is encrypted with TLS 1.2 or higher.
10
+ 2.2 Customer data at rest in the production database and object storage is encrypted with AES-256.
11
+
12
+ ## 3. Access control
13
+ 3.1 Multi-factor authentication is enforced for engineering staff and for all administrative access to production.
14
+ 3.2 Roll-out of multi-factor authentication to all remaining staff is in progress.
15
+ 3.3 Access is reviewed quarterly by system owners.
16
+
17
+ ## 4. Training
18
+ 4.1 All staff complete security awareness training on joining and annually after that.
19
+
20
+ ## 5. Incident response
21
+ 5.1 The incident response plan (IR-001) is maintained by the security lead and tested annually.
22
+ 5.2 Security on-call covers business hours, 08:00 to 18:00 UK time, Monday to Friday.
23
+
24
+ ## 6. Backups
25
+ 6.1 Production databases are backed up daily. Restores are tested quarterly.
26
+
27
+ ## 7. Retention
28
+ 7.1 Customer data is deleted 90 days after contract termination.
29
+
30
+ ## 8. Customer portal
31
+ 8.1 Single sign-on for the customer portal is planned for Q1 2027.
@@ -0,0 +1,10 @@
1
+ # RFP 2026-118, section 7: security
2
+
3
+ Answer Yes or No with a short explanation.
4
+
5
+ 7.1 Is data encrypted in transit?
6
+ 7.2 Are backups tested, and how often?
7
+ 7.3 Do you operate a 24/7 security operations centre?
8
+ 7.4 Do you support single sign-on for the customer portal?
9
+ 7.5 Is access to production reviewed periodically?
10
+ 7.6 Is security awareness training mandatory?
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "security-questionnaire-response",
3
+ "version": "0.1.0",
4
+ "description": "Answer a customer's security questionnaire from your own evidence: each question gets a direct answer (Yes, No, Partial or Not applicable), a short narrative, and the document and section it rests on. Questions the evidence doesn't cover are marked as needing input instead of being answered, and conflicts between sources, stale evidence and answers that would commit you to future work are flagged for the security owner. Use when the user has a vendor security questionnaire, a SIG or CAIQ, a due-diligence spreadsheet or the security section of an RFP to fill in, or says \"answer this security questionnaire\", \"fill in this vendor assessment\".",
5
+ "entrypoint": "SKILL.md",
6
+ "deps": {
7
+ "npm": [],
8
+ "pip": []
9
+ },
10
+ "env": {
11
+ "required": [],
12
+ "optional": []
13
+ },
14
+ "related": [
15
+ "dpia",
16
+ "risk-register",
17
+ "soc2-evidence-map",
18
+ "threat-model"
19
+ ],
20
+ "tags": [
21
+ "security-questionnaire",
22
+ "vendor-assessment",
23
+ "due-diligence",
24
+ "grc",
25
+ "rfp",
26
+ "compliance"
27
+ ],
28
+ "model": {
29
+ "tier": "standard",
30
+ "rationale": "Matches questions to evidence and writes short answers. The hard rule is mechanical: no evidence, no answer."
31
+ }
32
+ }