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.
- package/README.md +12 -1
- package/catalogue.json +4 -1
- package/model-routing.json +12 -0
- package/package.json +2 -2
- package/packs/dev-team/skills/pr-description-writer/SKILL.md +96 -0
- package/packs/dev-team/skills/pr-description-writer/evals/eval_queries.json +30 -0
- package/packs/dev-team/skills/pr-description-writer/evals/evals.json +43 -0
- package/packs/dev-team/skills/pr-description-writer/evals/files/1/change.diff +61 -0
- package/packs/dev-team/skills/pr-description-writer/evals/files/1/commits.txt +5 -0
- package/packs/dev-team/skills/pr-description-writer/evals/files/2/fix.diff +30 -0
- package/packs/dev-team/skills/pr-description-writer/evals/files/3/big.diff +90 -0
- package/packs/dev-team/skills/pr-description-writer/manifest.json +30 -0
- package/packs/dev-team/skills/release-notes/SKILL.md +1 -1
- package/packs/dev-team/skills/release-notes/manifest.json +2 -2
- package/packs/dev-team/skills/tech-debt-register/SKILL.md +103 -0
- package/packs/dev-team/skills/tech-debt-register/evals/eval_queries.json +34 -0
- package/packs/dev-team/skills/tech-debt-register/evals/evals.json +45 -0
- package/packs/dev-team/skills/tech-debt-register/evals/files/1/debt-notes.md +9 -0
- package/packs/dev-team/skills/tech-debt-register/evals/files/2/incidents.md +7 -0
- package/packs/dev-team/skills/tech-debt-register/evals/files/2/todos.txt +7 -0
- package/packs/dev-team/skills/tech-debt-register/manifest.json +31 -0
- package/packs/grc/skills/security-questionnaire-response/SKILL.md +106 -0
- package/packs/grc/skills/security-questionnaire-response/evals/eval_queries.json +34 -0
- package/packs/grc/skills/security-questionnaire-response/evals/evals.json +43 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/1/evidence/previous-answers.csv +5 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/1/evidence/security-policy.md +31 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/1/evidence/soc2-summary.md +9 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/1/questionnaire.csv +13 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/3/evidence/security-policy.md +31 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/3/rfp-security.md +10 -0
- 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
|
[](https://www.npmjs.com/package/skilldrop-cli)
|
|
4
|
+
[](https://pypi.org/project/skilldrop-cli/)
|
|
4
5
|
[](LICENSE)
|
|
5
6
|
|
|
6
|
-
**Portable AI-agent skills
|
|
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": {
|
package/model-routing.json
CHANGED
|
@@ -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.
|
|
4
|
-
"description": "Portable AI-agent skills
|
|
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,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
|
|
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.
|
|
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
|
+
}
|
package/packs/grc/skills/security-questionnaire-response/evals/files/1/evidence/previous-answers.csv
ADDED
|
@@ -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.
|
package/packs/grc/skills/security-questionnaire-response/evals/files/1/evidence/security-policy.md
ADDED
|
@@ -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.
|
package/packs/grc/skills/security-questionnaire-response/evals/files/1/evidence/soc2-summary.md
ADDED
|
@@ -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?
|
package/packs/grc/skills/security-questionnaire-response/evals/files/3/evidence/security-policy.md
ADDED
|
@@ -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
|
+
}
|