skilldrop-cli 1.1.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/catalogue.json +7 -8
- package/model-routing.json +0 -16
- package/package.json +1 -1
- package/packs/ai-engineering/loops/adopt/LOOP.md +120 -0
- package/packs/ai-engineering/loops/adopt/evals/eval_queries.json +10 -0
- package/packs/ai-engineering/loops/adopt/evals/evals.json +69 -0
- package/packs/ai-engineering/loops/adopt/evals/files/adopt-first-wave-to-sign-off/ai-ideas.md +15 -0
- package/packs/ai-engineering/loops/adopt/evals/files/adopt-first-wave-to-sign-off/org-context.md +27 -0
- package/packs/ai-engineering/loops/adopt/evals/files/adopt-first-wave-to-sign-off/staff-survey.csv +11 -0
- package/packs/ai-engineering/loops/adopt/evals/files/adopt-sponsor-slice-blocked-by-client-data/ai-ideas.md +13 -0
- package/packs/ai-engineering/loops/adopt/evals/files/adopt-sponsor-slice-blocked-by-client-data/firm-context.md +23 -0
- package/packs/ai-engineering/loops/adopt/loop.json +53 -0
- package/packs/ai-engineering/loops/ship-agent/LOOP.md +128 -0
- package/packs/ai-engineering/loops/ship-agent/evals/eval_queries.json +10 -0
- package/packs/ai-engineering/loops/ship-agent/evals/evals.json +70 -0
- package/packs/ai-engineering/loops/ship-agent/evals/files/ship-agent-auto-reply-exfil-open/agent-brief.md +33 -0
- package/packs/ai-engineering/loops/ship-agent/evals/files/ship-agent-changelog-config-reopens-path/agent-brief.md +33 -0
- package/packs/ai-engineering/loops/ship-agent/evals/files/ship-agent-changelog-config-reopens-path/claude-settings.json +25 -0
- package/packs/ai-engineering/loops/ship-agent/evals/files/ship-agent-changelog-config-reopens-path/sample-merged-prs.md +30 -0
- package/packs/ai-engineering/loops/ship-agent/loop.json +53 -0
- package/packs/ai-engineering/skills/ai-usage-report/examples/weekly-engineering-team.md +1 -1
- package/packs/ai-engineering/skills/ai-usage-report/templates/usage-event-schema.md +2 -2
- package/packs/converters/pack.json +3 -14
- package/packs/converters/skills/file-to-markdown/SKILL.md +28 -11
- package/packs/converters/skills/file-to-markdown/evals/evals.json +30 -0
- package/packs/converters/skills/file-to-markdown/evals/files/5/supplier_agreement_v3.docx +0 -0
- package/packs/converters/skills/file-to-markdown/evals/files/6/numbers.xlsx +0 -0
- package/packs/converters/skills/file-to-markdown/evals/files/6/offsite_q3.pptx +0 -0
- package/packs/converters/skills/file-to-markdown/examples/inputs/acme-brief.docx +0 -0
- package/packs/converters/skills/file-to-markdown/examples/inputs/northwind-roadmap.pptx +0 -0
- package/packs/converters/skills/file-to-markdown/examples/mixed-inputs.md +24 -16
- package/packs/converters/skills/file-to-markdown/manifest.json +2 -3
- package/packs/converters/skills/file-to-markdown/scripts/to_markdown.py +103 -10
- package/packs/converters/skills/md-to-confluence/SKILL.md +2 -2
- package/packs/converters/skills/md-to-confluence/manifest.json +2 -9
- package/packs/converters/skills/md-to-docx/SKILL.md +3 -4
- package/packs/converters/skills/md-to-docx/evals/evals.json +1 -1
- package/packs/converters/skills/md-to-docx/evals/files/1/brand/brand.json +1 -1
- package/packs/converters/skills/md-to-docx/manifest.json +4 -11
- package/packs/converters/skills/md-to-docx/reference.md +1 -1
- package/packs/converters/skills/md-to-docx/scripts/md_to_docx.py +1 -1
- package/packs/converters/skills/md-to-pptx/SKILL.md +3 -5
- package/packs/converters/skills/md-to-pptx/manifest.json +3 -16
- package/packs/converters/skills/md-to-pptx/scripts/md_to_pptx.py +1 -1
- package/packs/converters/skills/md-to-xlsx/SKILL.md +1 -1
- package/packs/converters/skills/md-to-xlsx/manifest.json +2 -2
- package/packs/core/skills/brief-intake/SKILL.md +2 -2
- package/packs/core/skills/brief-intake/manifest.json +2 -2
- package/packs/core/skills/brief-intake/templates/brief-deck.md +3 -3
- package/packs/core/skills/doc-critique/rubrics/deck.md +1 -1
- package/packs/design/pack.json +11 -21
- package/packs/design/skills/brand-kit/SKILL.md +6 -8
- package/packs/design/skills/brand-kit/manifest.json +3 -17
- package/packs/design/skills/brand-kit/templates/brand.json +1 -1
- package/packs/experience-design/loops/clarify/LOOP.md +114 -0
- package/packs/experience-design/loops/clarify/evals/eval_queries.json +10 -0
- package/packs/experience-design/loops/clarify/evals/evals.json +68 -0
- package/packs/experience-design/loops/clarify/evals/files/clarify-garden-waste-no-complaint-data/form-fields.csv +12 -0
- package/packs/experience-design/loops/clarify/evals/files/clarify-garden-waste-no-complaint-data/garden-waste-page.md +29 -0
- package/packs/experience-design/loops/clarify/evals/files/clarify-parking-permit-with-call-data/backoffice-notes.md +14 -0
- package/packs/experience-design/loops/clarify/evals/files/clarify-parking-permit-with-call-data/calls-q3.csv +8 -0
- package/packs/experience-design/loops/clarify/evals/files/clarify-parking-permit-with-call-data/form-fields.csv +12 -0
- package/packs/experience-design/loops/clarify/evals/files/clarify-parking-permit-with-call-data/permit-page.md +27 -0
- package/packs/experience-design/loops/clarify/loop.json +35 -0
- package/packs/infra-as-code/loops/provision/LOOP.md +128 -0
- package/packs/infra-as-code/loops/provision/evals/eval_queries.json +9 -0
- package/packs/infra-as-code/loops/provision/evals/evals.json +67 -0
- package/packs/infra-as-code/loops/provision/evals/files/provision-rds-no-plan-yet/.terraform.lock.hcl.fixture +24 -0
- package/packs/infra-as-code/loops/provision/evals/files/provision-rds-no-plan-yet/network.md +36 -0
- package/packs/infra-as-code/loops/provision/evals/files/provision-replica-plan-replaces-primary/main.tf +74 -0
- package/packs/infra-as-code/loops/provision/evals/files/provision-replica-plan-replaces-primary/plan.json +901 -0
- package/packs/infra-as-code/loops/provision/evals/files/provision-replica-plan-replaces-primary/variables.tf +47 -0
- package/packs/infra-as-code/loops/provision/loop.json +41 -0
- package/packs/solution-architect/skills/interactive-diagram/SKILL.md +1 -1
- package/packs/solution-architect/skills/interactive-diagram/manifest.json +1 -2
- package/packs/stakeholder-comms/skills/audience-profile/SKILL.md +3 -3
- package/packs/stakeholder-comms/skills/audience-profile/manifest.json +2 -2
- package/packs/stakeholder-comms/skills/audience-profile/reference.md +1 -1
- package/packs/converters/skills/md-to-html/SKILL.md +0 -108
- package/packs/converters/skills/md-to-html/evals/eval_queries.json +0 -34
- package/packs/converters/skills/md-to-html/evals/evals.json +0 -44
- package/packs/converters/skills/md-to-html/evals/files/1/brand/brand.json +0 -80
- package/packs/converters/skills/md-to-html/evals/files/1/brand/logo-white.png +0 -0
- package/packs/converters/skills/md-to-html/evals/files/1/brand/logo.png +0 -0
- package/packs/converters/skills/md-to-html/evals/files/1/brand/mark.png +0 -0
- package/packs/converters/skills/md-to-html/evals/files/1/docs/q3-review.md +0 -35
- package/packs/converters/skills/md-to-html/evals/files/1/img/dashboard.png +0 -0
- package/packs/converters/skills/md-to-html/evals/files/2/brand.json +0 -13
- package/packs/converters/skills/md-to-html/evals/files/2/img/kick-net.png +0 -0
- package/packs/converters/skills/md-to-html/evals/files/2/river-survey-guide.md +0 -53
- package/packs/converters/skills/md-to-html/evals/files/3/nanas-lentil-soup.md +0 -21
- package/packs/converters/skills/md-to-html/evals/files/3/pot.png +0 -0
- package/packs/converters/skills/md-to-html/examples/inputs/chart.svg +0 -8
- package/packs/converters/skills/md-to-html/examples/inputs/ops-review.md +0 -48
- package/packs/converters/skills/md-to-html/examples/ops-review.md +0 -83
- package/packs/converters/skills/md-to-html/manifest.json +0 -43
- package/packs/converters/skills/md-to-html/reference.md +0 -70
- package/packs/converters/skills/md-to-html/scripts/md_to_html.py +0 -662
- package/packs/converters/skills/mermaid-render/SKILL.md +0 -117
- package/packs/converters/skills/mermaid-render/evals/eval_queries.json +0 -30
- package/packs/converters/skills/mermaid-render/evals/evals.json +0 -43
- package/packs/converters/skills/mermaid-render/evals/files/1/docs/contoso-orders.md +0 -23
- package/packs/converters/skills/mermaid-render/evals/files/2/dispensing-diagrams.md +0 -39
- package/packs/converters/skills/mermaid-render/examples/broken-diagrams.md +0 -147
- package/packs/converters/skills/mermaid-render/examples/inputs/broken-flowchart.mmd +0 -8
- package/packs/converters/skills/mermaid-render/examples/inputs/broken-sequence.mmd +0 -11
- package/packs/converters/skills/mermaid-render/examples/inputs/checkout-flow.mmd +0 -9
- package/packs/converters/skills/mermaid-render/examples/inputs/design-notes.md +0 -35
- package/packs/converters/skills/mermaid-render/examples/inputs/fixed-flowchart.mmd +0 -9
- package/packs/converters/skills/mermaid-render/examples/inputs/typo.mmd +0 -2
- package/packs/converters/skills/mermaid-render/manifest.json +0 -50
- package/packs/converters/skills/mermaid-render/reference.md +0 -70
- package/packs/converters/skills/mermaid-render/scripts/render_mermaid.py +0 -487
- package/packs/design/skills/deck-builder/SKILL.md +0 -209
- package/packs/design/skills/deck-builder/evals/eval_queries.json +0 -14
- package/packs/design/skills/deck-builder/evals/evals.json +0 -67
- package/packs/design/skills/deck-builder/evals/files/4/ashby-vale-board-notes.md +0 -36
- package/packs/design/skills/deck-builder/examples/exec-board-update.md +0 -108
- package/packs/design/skills/deck-builder/examples/templated-brand-deck.md +0 -148
- package/packs/design/skills/deck-builder/manifest.json +0 -18
- package/packs/design/skills/deck-builder/reference.md +0 -180
- package/packs/design/skills/deck-builder/requirements.txt +0 -3
- package/packs/design/skills/deck-builder/scripts/build_deck.py +0 -1144
- package/packs/design/skills/deck-builder/templates/deck-spec.json +0 -168
- package/packs/design/skills/deck-builder/templates/palettes.json +0 -59
- package/packs/design/skills/marketing-flyer/SKILL.md +0 -113
- package/packs/design/skills/marketing-flyer/evals/eval_queries.json +0 -30
- package/packs/design/skills/marketing-flyer/evals/evals.json +0 -45
- package/packs/design/skills/marketing-flyer/evals/files/1/brand/brand.json +0 -80
- package/packs/design/skills/marketing-flyer/evals/files/1/brand/logo-white.png +0 -0
- package/packs/design/skills/marketing-flyer/evals/files/1/brand/logo.png +0 -0
- package/packs/design/skills/marketing-flyer/evals/files/1/brand/mark.png +0 -0
- package/packs/design/skills/marketing-flyer/evals/files/2/brand.json +0 -14
- package/packs/design/skills/marketing-flyer/evals/files/2/larchfield-logo.svg +0 -4
- package/packs/design/skills/marketing-flyer/evals/files/2/manager-email.txt +0 -6
- package/packs/design/skills/marketing-flyer/evals/files/2/open-day-brief.md +0 -15
- package/packs/design/skills/marketing-flyer/examples/community-health-fair.md +0 -68
- package/packs/design/skills/marketing-flyer/manifest.json +0 -40
- package/packs/design/skills/marketing-flyer/reference.md +0 -54
- package/packs/design/skills/marketing-flyer/scripts/build_flyer.py +0 -332
- package/packs/design/skills/marketing-flyer/templates/flyer-spec.json +0 -26
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brand-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Capture a brand once as a reusable brand.json — logo files (and an on-dark version), colour roles as hex codes, heading and body fonts, voice, imagery rules, a PowerPoint template, contact details and the legal footer — plus a one-page BRAND.md, with text contrast checked and every value traced to where it came from. Use when the user wants decks, flyers or documents in their branding, says \"use our brand\", \"here's our logo and colours\", \"set up our brand kit\", or hands over brand guidelines.",
|
|
5
5
|
"entrypoint": "SKILL.md",
|
|
6
6
|
"deps": {
|
|
@@ -12,22 +12,8 @@
|
|
|
12
12
|
"optional": []
|
|
13
13
|
},
|
|
14
14
|
"related": [
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
],
|
|
18
|
-
"handoff": [
|
|
19
|
-
{
|
|
20
|
-
"to": "deck-builder",
|
|
21
|
-
"when": "the brand kit is written and the user wants a deck",
|
|
22
|
-
"purpose": "Build the PowerPoint deck in this brand: build_deck.py --brand brand.json.",
|
|
23
|
-
"fallback": "If deck-builder is absent, give the user the brand.json path and the five hex codes to paste into whatever deck tool they use."
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"to": "marketing-flyer",
|
|
27
|
-
"when": "the brand kit is written and the user wants a flyer or one-page sheet",
|
|
28
|
-
"purpose": "Build a print-ready flyer in this brand from the flyer spec's brand field.",
|
|
29
|
-
"fallback": "If marketing-flyer is absent, give the user BRAND.md so a designer or tool can apply it by hand."
|
|
30
|
-
}
|
|
15
|
+
"md-to-pptx",
|
|
16
|
+
"md-to-docx"
|
|
31
17
|
],
|
|
32
18
|
"tags": [
|
|
33
19
|
"brand",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$comment": "brand-kit output.
|
|
2
|
+
"$comment": "brand-kit output. md-to-docx and md-to-pptx read this file; any field left out is asked for or defaulted by them. Paths are relative to this file. Every value carries where it came from in `sources`.",
|
|
3
3
|
"name": "Northwind Health",
|
|
4
4
|
"tagline": "Care that comes to you",
|
|
5
5
|
"logo": {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clarify
|
|
3
|
+
description: Take a service people struggle with from its page to the service behind it: a content spec and plain-language rewrite from what users need, every string on the form or flow, and a service blueprint that tests whether those fixes reach the fail points behind the complaints, with a gate that sends the work back to the page when it misses a fail point and out to product when the fix is backstage. Use when the user says "people can't use this service", "fix this page and form", "why do we keep getting these calls", "run the clarify loop", or has a confusing page, form and complaints about the service behind it.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# clarify
|
|
7
|
+
|
|
8
|
+
One service, from the words people read to the work behind them. A clearer page stops the calls
|
|
9
|
+
that come from confusion and none of the calls that come from a wait or a lost handoff. The
|
|
10
|
+
content spec and the strings fix the words. Then the blueprint traces each complaint to the cell
|
|
11
|
+
that causes it. That shows which complaints the new words reach and which need the service
|
|
12
|
+
itself to change. Neither writing skill can judge that about its own output.
|
|
13
|
+
|
|
14
|
+
## Stages
|
|
15
|
+
|
|
16
|
+
| # | Stage | Type | Skills | Gate |
|
|
17
|
+
|---|---|---|---|---|
|
|
18
|
+
| 1 | `page` | generate | [`content-design`](../../skills/content-design/SKILL.md) | — |
|
|
19
|
+
| 2 | `strings` | generate | [`ux-writing`](../../skills/ux-writing/SKILL.md) | — |
|
|
20
|
+
| 3 | `blueprint` | verify | [`service-blueprint`](../../skills/service-blueprint/SKILL.md) | **G14** review |
|
|
21
|
+
|
|
22
|
+
## How to run this loop
|
|
23
|
+
|
|
24
|
+
1. **List the complaints first.** From the calls, tickets, survey answers or anything else the
|
|
25
|
+
person brought, write `complaints.md`: each kind of complaint, how often if known, and one
|
|
26
|
+
quote. Every later stage reads it. Without complaints, the loop checks the words against
|
|
27
|
+
nothing.
|
|
28
|
+
2. **Write the page.** Run `content-design` on the current page, with the complaints as
|
|
29
|
+
evidence of what users need to know. The questions the page must answer come from the
|
|
30
|
+
complaints first. Save the spec and rewrite as `page.md`, with the readability before and
|
|
31
|
+
after.
|
|
32
|
+
3. **Write the strings.** Run `ux-writing` on the form or flow, using the terms the content spec
|
|
33
|
+
chose, so the page and the form call the same thing by the same name. Save the string table
|
|
34
|
+
as `strings.md`.
|
|
35
|
+
4. **Blueprint the service.** Run `service-blueprint` on the scenario the complaints describe,
|
|
36
|
+
from the page through to the outcome the person is waiting for. Trace every complaint in
|
|
37
|
+
`complaints.md` to the cell that causes it. Then mark each complaint as reached by the page,
|
|
38
|
+
reached by the strings, or caused backstage. Save it as `blueprint.md`.
|
|
39
|
+
5. **Judge the fixes at G14.** Ask three questions, then choose the verdict below:
|
|
40
|
+
- Is every complaint caused by the words reached by a specific line in `page.md` or
|
|
41
|
+
`strings.md`?
|
|
42
|
+
- Which complaints are caused backstage, and does the page at least tell people what
|
|
43
|
+
happens and how long it takes?
|
|
44
|
+
- Do the page, the strings and the blueprint use the same names for the same things?
|
|
45
|
+
6. **On `REVISE`, fix the words for the missed complaint.** Return to `page` with the complaint
|
|
46
|
+
and the cell that causes it. Then re-run `strings` when the change touches the form. Each
|
|
47
|
+
return counts against the cap.
|
|
48
|
+
7. **Hand over what changes and what can't change here.** Lead with the complaints the new page
|
|
49
|
+
and form should stop, and the ones they can't, with the backstage fix and its owner. The
|
|
50
|
+
page, the strings and the blueprint go underneath. Close with the gate on its own line,
|
|
51
|
+
`G14: <VERDICT>`, and one sentence of reasons.
|
|
52
|
+
|
|
53
|
+
## Verdicts
|
|
54
|
+
|
|
55
|
+
Defined in [`contracts/terminals.json`](../../../../contracts/terminals.json). G14 emits:
|
|
56
|
+
|
|
57
|
+
- `PROCEED`: every complaint caused by the words is reached by a named line, the names match
|
|
58
|
+
across all three, and no complaint is caused backstage.
|
|
59
|
+
- `PROCEED WITH CONDITIONS`: the words are right, but some complaints are caused backstage.
|
|
60
|
+
The page says what happens and how long it takes, and each backstage fix travels with its
|
|
61
|
+
owner as a condition.
|
|
62
|
+
- `REVISE`: a complaint caused by the words is reached by no line, or the page and form name
|
|
63
|
+
the same thing differently. Return to `page`.
|
|
64
|
+
- `RECONSIDER`: most of the complaints are caused backstage, so new words won't move them. The
|
|
65
|
+
fix is a change to the service, so the work goes to product as a requirement, with the
|
|
66
|
+
blueprint's ranked fixes, and the loop ends here.
|
|
67
|
+
- `BLOCKED`: no service or scenario can be identified, or there is no current page or form.
|
|
68
|
+
|
|
69
|
+
On the cap-th `REVISE`, emit `PROCEED WITH CONDITIONS` with the missed complaint named as open.
|
|
70
|
+
|
|
71
|
+
## Non-interactive runs
|
|
72
|
+
|
|
73
|
+
A missing reader or voice degrades to the skills' `[assumption]` lines. With no complaints at
|
|
74
|
+
all, tag the loop's output `[no complaint evidence]` and judge G14 on the blueprint's fail
|
|
75
|
+
points alone. With no service or scenario, `service-blueprint` emits
|
|
76
|
+
`BLOCKED: need the service and one customer scenario`; carry it through as the verdict.
|
|
77
|
+
|
|
78
|
+
## When a stage's skill is not installed
|
|
79
|
+
|
|
80
|
+
Name the missing skill and skilldrop as its source, do the minimal inline version of that
|
|
81
|
+
stage, and record that it ran degraded. A degraded `blueprint` still traces every complaint to
|
|
82
|
+
a frontstage, backstage or support cause, because G14 can't be judged without that trace.
|
|
83
|
+
Never skip G14.
|
|
84
|
+
|
|
85
|
+
## Run log (opt-in)
|
|
86
|
+
|
|
87
|
+
Only when the environment variable `SKILLDROP_LOOP_LOG` is set to a file path: after each
|
|
88
|
+
gate's verdict, append one JSON line to that file. Nothing else is recorded, and nothing is
|
|
89
|
+
sent anywhere. When the variable isn't set, or you can't read it, skip this section.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
printf '%s\n' '{"ts":"2026-11-03T10:15:00Z","loop":"clarify","stage":"blueprint","gate":"G14","verdict":"REVISE","round":1}' >> "$SKILLDROP_LOOP_LOG"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Fields: `ts` (UTC), `loop`, `stage`, `gate` (`null` for a stage without one), `verdict` (the
|
|
96
|
+
gate's exact word), and `round` (1 for the first attempt, counting returns under the cap).
|
|
97
|
+
The log holds verdicts only: never the page, the complaints or the quotes.
|
|
98
|
+
`skilldrop loop-stats` summarises the file.
|
|
99
|
+
|
|
100
|
+
## Quality bar
|
|
101
|
+
|
|
102
|
+
- **The complaints are written down before any rewrite.** Every later stage is judged against them.
|
|
103
|
+
- **Every complaint is traced to the cell that causes it,** and marked as reached by the page, reached by the strings, or caused backstage.
|
|
104
|
+
- **The page and the form use the same name for the same thing,** and the blueprint uses it too.
|
|
105
|
+
- **A backstage cause is named with its owner,** never written around with friendlier words.
|
|
106
|
+
- **The reply says which complaints the new words can't stop.**
|
|
107
|
+
|
|
108
|
+
## Anti-patterns to avoid
|
|
109
|
+
|
|
110
|
+
- ❌ **Rewriting the page from a style guide instead of the complaints.** The page reads better and the same calls keep coming.
|
|
111
|
+
- ❌ **Fixing a wait with reassuring copy.** "We're working on it" doesn't shorten a five-week wait; say how long it takes, and send the wait to its owner.
|
|
112
|
+
- ❌ **Writing the strings before the page.** The form then names things one way and the page another.
|
|
113
|
+
- ❌ **Passing G14 because readability improved.** A lower grade level is not a complaint reached.
|
|
114
|
+
- ❌ **Treating `RECONSIDER` as failure.** Finding that the service, not the page, causes the calls is the result.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[
|
|
2
|
+
{"query": "people can't use our permit service: fix the page and the form and find out why they keep calling", "should_trigger": true},
|
|
3
|
+
{"query": "run the clarify loop on the benefits application journey", "should_trigger": true},
|
|
4
|
+
{"query": "we keep getting 'where is my order' calls, rewrite the help page and check whether that will even fix it", "should_trigger": true},
|
|
5
|
+
{"query": "fix this page and form, here are last quarter's complaints", "should_trigger": true},
|
|
6
|
+
{"query": "rewrite this page in plain English", "should_trigger": false},
|
|
7
|
+
{"query": "write the error messages for our signup form", "should_trigger": false},
|
|
8
|
+
{"query": "build a service blueprint of our claims process", "should_trigger": false},
|
|
9
|
+
{"query": "reorganise our help centre navigation", "should_trigger": false}
|
|
10
|
+
]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"loop_name": "clarify",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": "words-and-backstage-split",
|
|
6
|
+
"prompt": "Run the clarify loop on Ashby Vale Council's parking permit service. The current page:\n\n\"Residents' Parking Permits. Eligibility for a residents' parking permit (RPP) is determined in accordance with the Traffic Management Order applicable to your zone. Applicants must furnish proof of residency (see Schedule 2) and vehicle registration documentation. Applications are processed within the statutory timeframe. Permits are issued digitally; no paper permit will be displayed.\"\n\nThe form asks for 'RPP zone', 'V5C reference' and 'Proof type (Sched. 2)'. Last quarter's calls: 412 'where is my permit', 188 'which documents count as proof', 97 'do I need to put anything in my windscreen', 60 'what zone am I in'. The team says applications sit in a shared inbox until someone checks documents, which takes 2 to 15 working days; nobody emails the applicant when the permit is live. Non-interactive: no follow-up questions.",
|
|
7
|
+
"assertions": [
|
|
8
|
+
"Writes down the four complaint types with their counts before rewriting anything",
|
|
9
|
+
"The content spec's questions come from the complaints: proof documents, zone, nothing in the windscreen, and how long it takes",
|
|
10
|
+
"The form strings use the same terms the page chose, and replace 'RPP zone', 'V5C reference' and 'Proof type (Sched. 2)' with plain words",
|
|
11
|
+
"The blueprint traces 'where is my permit' to the shared-inbox wait and the missing confirmation email, both backstage",
|
|
12
|
+
"Marks each complaint as reached by the page, reached by the strings, or caused backstage",
|
|
13
|
+
"Gives G14 PROCEED WITH CONDITIONS or RECONSIDER, not PROCEED, because the biggest complaint is caused backstage, and names the backstage fix with an owner role",
|
|
14
|
+
"The page tells applicants how long processing takes, using the 2 to 15 working days the team gave",
|
|
15
|
+
"Does not invent a processing time, owner name or statistic the prompt did not give"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "missed-complaint-sends-it-back",
|
|
20
|
+
"prompt": "Continuing the clarify loop. The blueprint traced 188 calls a quarter ('which documents count as proof') to the page, but the new page.md still just says 'Upload proof that you live at this address' with no list of what counts. Everything else lined up. What's the G14 verdict and what happens next?",
|
|
21
|
+
"assertions": [
|
|
22
|
+
"Gives G14 REVISE because a complaint caused by the words is reached by no line",
|
|
23
|
+
"Returns to page with that complaint, not to the blueprint",
|
|
24
|
+
"Says the strings stage re-runs if the change touches the form's proof upload field",
|
|
25
|
+
"Counts the next attempt as round 2 under the cap of 3",
|
|
26
|
+
"Does not invent the list of accepted documents; marks it for the team to supply if the input doesn't give it"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "blocked-without-a-service",
|
|
31
|
+
"prompt": "Run the clarify loop. Our users are confused. Non-interactive, no follow-up questions.",
|
|
32
|
+
"assertions": [
|
|
33
|
+
"Emits BLOCKED naming what is missing: the service, one customer scenario, and the current page or form",
|
|
34
|
+
"Does not invent a service, page or complaints",
|
|
35
|
+
"Does not produce a content spec, strings or blueprint",
|
|
36
|
+
"Says in plain words what it will produce once it has them"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "clarify-parking-permit-with-call-data",
|
|
41
|
+
"held_out": "2026-11-03",
|
|
42
|
+
"prompt": "People can't use our resident parking permit service and the phones never stop. The current page is permit-page.md, the form is form-fields.csv, last quarter's call reasons are in calls-q3.csv and I sat with the permits team and wrote up how it works behind the scenes (backoffice-notes.md). Run the clarify loop, I want to know what we can fix on the page and what we can't.",
|
|
43
|
+
"assertions": [
|
|
44
|
+
"Runs content design, then UX writing, then the service blueprint, then gate G14, and the string table reuses the content spec's terms (one name for proof of address, and 'registration number' or similar rather than 'VRM')",
|
|
45
|
+
"The content spec builds its user needs from the call reasons, lists the questions the page must answer in priority order, gives a plain-language rewrite of permit-page.md with a before/after readability check, and lists what to cut and why",
|
|
46
|
+
"The string table covers every field in form-fields.csv, and each error says what happened and what to do next, including the 4 MB limit and accepted formats on uploads and how to enter a registration number",
|
|
47
|
+
"The blueprint places each of the seven call reasons at a step and layer, marking as fail points with owners the Tuesday/Thursday evidence batches, Friday printing and second-class post, the 4 MB platform cap, the no-spaces registration check, and the double charge after pressing Back on a timed-out payment",
|
|
48
|
+
"Gate G14 sends 'Where is my permit' (batching and post) and 'Charged twice' out to product as backstage fixes, naming the Digital Services manager for the payment fault, rather than claiming the copy fixes them; the page may still set an honest expectation from the 13-working-day average",
|
|
49
|
+
"Any page-fixable fail point the first pass misses (such as the unlinked postcode zone lookup, the accepted and rejected proofs of address, or the 50-a-year visitor permit allowance) sends the work back to the page with a REVISE verdict, and the final mapping shows every page-fixable fail point covered",
|
|
50
|
+
"Does not invent call volumes, timescales, prices, rules or owners not in the inputs"
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "clarify-garden-waste-no-complaint-data",
|
|
55
|
+
"held_out": "2026-11-03",
|
|
56
|
+
"prompt": "People can't use our garden waste subscription page and sign-up form, everyone says so. Page is garden-waste-page.md, form fields are in form-fields.csv. I don't have any call data, the contact centre won't share it until next quarter, so just fix it with what's there.",
|
|
57
|
+
"assertions": [
|
|
58
|
+
"Runs content design, then UX writing, then the service blueprint, in that order, and still produces a content spec, plain-language rewrite and string table from the page and form",
|
|
59
|
+
"Says plainly that no complaint or call data was supplied, and labels the user needs and fail points it draws from the page and form as assumptions to test",
|
|
60
|
+
"The blueprint marks its fail points as hypotheses (for example, where to find the bin serial number, how long the permit sticker takes to arrive, the full-year charge for a late start) rather than as confirmed complaint drivers",
|
|
61
|
+
"Gate G14 does not report a pass on evidence it lacks: the verdict is PROCEED WITH CONDITIONS or NOT READY, naming the contact centre's call reasons as what is needed to confirm the fixes reach the fail points",
|
|
62
|
+
"The string table covers every field in form-fields.csv, and each error says what happened and what to do next",
|
|
63
|
+
"Delivers the work in this reply rather than stopping to ask for the call data",
|
|
64
|
+
"Does not invent call volumes or complaint themes presented as fact, or service details not on the page such as sticker delivery times, bin delivery times or collection days"
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
step,field,label,hint,error_message,rules
|
|
2
|
+
1,postcode,Postcode,,Invalid,Required; UK postcode format
|
|
3
|
+
1,address,Address,,Please select,Required; picked from postcode lookup
|
|
4
|
+
2,bins_required,Number of bins,,Invalid,Required; 1 to 3
|
|
5
|
+
2,existing_bin,Do you have an existing brown bin?,,Please answer,Required; Yes or No
|
|
6
|
+
2,bin_serial,Bin serial number,,Serial number not recognised,Required if existing_bin = Yes; 8 digits
|
|
7
|
+
3,start_date,Start date,DD/MM/YYYY,Invalid date,Required; between 1 March and 30 November
|
|
8
|
+
3,email,Email address,,Invalid,Required
|
|
9
|
+
3,phone,Telephone,,Invalid,Optional
|
|
10
|
+
4,terms,I accept the terms and conditions,,You must accept,Required checkbox; links to terms page
|
|
11
|
+
5,pay,Pay now,,Payment failed,Card only
|
|
12
|
+
6,confirmation,Thank you,Your subscription reference is {ref}.,,Shown after payment
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Garden Waste Collection Service
|
|
2
|
+
|
|
3
|
+
*Ashcombe Vale Council: ashcombevale.example/bins/garden-waste*
|
|
4
|
+
|
|
5
|
+
The Council provides a chargeable garden waste collection service to households within the district, in accordance with the Environmental Protection Act 1990 and the Council's Waste Collection Policy. Participation in the scheme is voluntary and subject to the terms and conditions set out below.
|
|
6
|
+
|
|
7
|
+
## Subscription
|
|
8
|
+
|
|
9
|
+
Subscriptions run from 1 March to 30 November inclusive and are payable annually. The annual charge is £52 per bin. Households may subscribe for up to three bins. Subscriptions taken out part-way through the year are charged at the full annual rate. Subscriptions are non-transferable and non-refundable.
|
|
10
|
+
|
|
11
|
+
## Collections
|
|
12
|
+
|
|
13
|
+
Collections are made fortnightly on the household's scheduled collection day. Please refer to your collection calendar. Collections are suspended from December to February.
|
|
14
|
+
|
|
15
|
+
## Bins and permits
|
|
16
|
+
|
|
17
|
+
New subscribers will be supplied with a brown wheeled bin, subject to availability. Existing bin holders must quote the serial number of their bin when subscribing. A permit sticker will be dispatched and must be affixed to the bin before collections can commence. Bins not displaying a valid permit sticker will not be emptied.
|
|
18
|
+
|
|
19
|
+
## Acceptable materials
|
|
20
|
+
|
|
21
|
+
Grass cuttings, hedge trimmings, leaves, flowers, weeds and small branches. Not accepted: soil, rubble, food waste, plastic bags, pet waste, Japanese knotweed.
|
|
22
|
+
|
|
23
|
+
## Renewal
|
|
24
|
+
|
|
25
|
+
Subscribers will be invited to renew in February each year. Failure to renew by the stated date will result in the suspension of collections.
|
|
26
|
+
|
|
27
|
+
## Terms and conditions
|
|
28
|
+
|
|
29
|
+
By subscribing, the householder agrees to the terms and conditions of the scheme, which may be varied by the Council from time to time.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Parking Services back office: how a resident permit gets issued
|
|
2
|
+
|
|
3
|
+
Notes from a walk-through with the permits team lead, October 2026.
|
|
4
|
+
|
|
5
|
+
1. Applications from the online form land in a shared inbox queue. Two permit officers work it.
|
|
6
|
+
2. Evidence is checked by hand, in batches, on Tuesdays and Thursdays. Anything that arrives on a Friday waits until Tuesday.
|
|
7
|
+
3. Proof of address we accept: current year council tax bill; tenancy agreement with at least 6 months left to run; utility bill dated within the last 3 months. We do not accept bank statements or mobile phone bills. About a third of applications send one of these and get returned by email.
|
|
8
|
+
4. Approved permits are printed on Fridays and posted second class. Permits are paper and go in the windscreen.
|
|
9
|
+
5. Average time from application to permit arriving: 13 working days. The council's target is 10.
|
|
10
|
+
6. The form platform caps uploads at 4 MB per file; that is a platform setting the permits team can't change. Phone photos are often 5 to 8 MB.
|
|
11
|
+
7. The registration field rejects spaces. Most people type their registration with a space (as it appears on the number plate) and get "Invalid input".
|
|
12
|
+
8. Zones: a postcode zone lookup exists at brindlemoor.example/parking/zones but the permit page doesn't link to it.
|
|
13
|
+
9. Visitor permits: each household can buy up to 50 one-day visitor permits a year, £2.50 each. This isn't written down anywhere public.
|
|
14
|
+
10. Payment: if the payment page times out and the resident presses Back and pays again, they are charged twice. Finance refunds within 10 working days. A ticket with the form platform supplier has been open since May 2026. The product owner for the form platform is the Digital Services manager.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
call_reason,calls_jul,calls_aug,calls_sep
|
|
2
|
+
Where is my permit,171,184,183
|
|
3
|
+
Unsure what proof of address to send,129,141,142
|
|
4
|
+
Upload rejected or file too large,88,97,102
|
|
5
|
+
Don't know which zone I live in,64,70,69
|
|
6
|
+
Vehicle registration not accepted,45,51,48
|
|
7
|
+
Visitor permits: how many can I buy,40,38,44
|
|
8
|
+
Charged twice,18,22,21
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
step,field,label,hint,error_message,rules
|
|
2
|
+
1,postcode,Postcode,,Invalid input,Required; UK postcode format
|
|
3
|
+
1,address,Select address,,Please select,Required; picked from postcode lookup
|
|
4
|
+
2,vrm,VRM,,Invalid input,Required; letters and digits only; no spaces allowed; max 7 characters
|
|
5
|
+
2,vehicle_make,Make,,Invalid input,Required
|
|
6
|
+
2,keeper,Are you the registered keeper?,,Please answer,Required; Yes or No
|
|
7
|
+
3,proof_of_residency,Upload satisfactory evidence of residency,,Upload failed,Required; JPG PNG or PDF; max 4 MB per file
|
|
8
|
+
3,proof_of_vehicle,Upload vehicle registration document or lease agreement,,Upload failed,Required; JPG PNG or PDF; max 4 MB per file
|
|
9
|
+
4,permit_start,Commencement date,DD/MM/YYYY,Invalid date,Required; today or later; no more than 30 days ahead
|
|
10
|
+
4,email,Email,,Invalid input,Required; valid email address
|
|
11
|
+
5,pay,Proceed,,Transaction error,Opens payment page; times out after 10 minutes
|
|
12
|
+
6,confirmation,Application received,Your reference is {ref}.,,Shown after payment
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Resident Parking Permits
|
|
2
|
+
|
|
3
|
+
*Brindlemoor District Council: brindlemoor.example/parking/resident-permits*
|
|
4
|
+
|
|
5
|
+
## Introduction
|
|
6
|
+
|
|
7
|
+
Brindlemoor District Council operates a number of Controlled Parking Zones (CPZs) within the district under the provisions of the Brindlemoor (Controlled Parking Zones) Traffic Management Order 2019, as amended. Within a CPZ, parking in designated resident bays is restricted to vehicles displaying a valid resident parking permit issued for the relevant zone during the hours of operation shown on the zone entry signage.
|
|
8
|
+
|
|
9
|
+
## Eligibility
|
|
10
|
+
|
|
11
|
+
Permits may be issued to residents whose principal place of residence is situated within a CPZ and who are the registered keeper of the vehicle, or who can otherwise demonstrate that the vehicle is in their care and control on an ongoing basis. Satisfactory evidence of residency and of the vehicle's details will be required. The Council reserves the right to request further evidence at its discretion.
|
|
12
|
+
|
|
13
|
+
## Charges
|
|
14
|
+
|
|
15
|
+
The permit charge is payable annually in advance. The charge for a first permit is £68. The charge for a second permit at the same address is £112. Permits are not transferable between vehicles except on application. Refunds are not normally given.
|
|
16
|
+
|
|
17
|
+
## Visitor permits
|
|
18
|
+
|
|
19
|
+
Residents within a CPZ are also entitled to purchase visitor permits, subject to annual limits.
|
|
20
|
+
|
|
21
|
+
## How to apply
|
|
22
|
+
|
|
23
|
+
Applications should be made using the online form. Applicants must ensure that all evidence submitted is legible and current. Incomplete applications cannot be processed and will be returned. Permits will be issued in due course following verification of the application.
|
|
24
|
+
|
|
25
|
+
## Further information
|
|
26
|
+
|
|
27
|
+
For further information please contact the Parking Services team on 0306 999 0142 or parking@brindlemoor.example.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "RFC-0069. A service people struggle with, from its page to the service behind it. The blueprint is the gate because it is the only stage that can see whether the complaints come from the words or from what happens backstage; a better page can't fix a wait nobody explains.",
|
|
3
|
+
"name": "clarify",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Take a service people struggle with from its page to the service behind it: a content spec and plain-language rewrite from what users need, every string on the form or flow, and a service blueprint that tests whether those fixes reach the fail points behind the complaints, with a gate that sends the work back to the page when it misses a fail point and out to product when the fix is backstage. Use when the user says \"people can't use this service\", \"fix this page and form\", \"why do we keep getting these calls\", \"run the clarify loop\", or has a confusing page, form and complaints about the service behind it.",
|
|
6
|
+
"entrypoint": "LOOP.md",
|
|
7
|
+
"kind": "loop",
|
|
8
|
+
"cap": 3,
|
|
9
|
+
"stages": [
|
|
10
|
+
{
|
|
11
|
+
"id": "page",
|
|
12
|
+
"type": "generate",
|
|
13
|
+
"intent": "Write the content spec from the user needs and the complaints: the questions the page must answer in order, its structure, and a plain-language rewrite with its readability before and after.",
|
|
14
|
+
"skills": ["content-design"]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "strings",
|
|
18
|
+
"type": "generate",
|
|
19
|
+
"intent": "Write every string on the form or flow, in the content spec's terms, with errors that say what happened and what to do next.",
|
|
20
|
+
"skills": ["ux-writing"]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "blueprint",
|
|
24
|
+
"type": "verify",
|
|
25
|
+
"intent": "Blueprint the service behind the page and form, trace each complaint to the cell that causes it, and judge whether the page and strings reach the fail points that cause the complaints.",
|
|
26
|
+
"skills": ["service-blueprint"],
|
|
27
|
+
"gate": {
|
|
28
|
+
"id": "G14",
|
|
29
|
+
"kind": "review",
|
|
30
|
+
"verdicts": ["PROCEED", "PROCEED WITH CONDITIONS", "REVISE", "RECONSIDER", "BLOCKED"],
|
|
31
|
+
"revise_to": "page"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: provision
|
|
3
|
+
description: Take an infrastructure change from a described need to a Terraform apply a person approved: a module with secure defaults, a review of the real plan that names every destroy, replace, widening and out-of-scope change, and a check right before apply that the plan being applied is the one reviewed, with a gate that sends the module back when the plan would destroy state or widen access unasked. Use when the user says "write the terraform and check it before we apply", "provision this safely", "run the provision loop", or wants new infrastructure from module to apply in one pass.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# provision
|
|
7
|
+
|
|
8
|
+
One infrastructure change, from a need to an apply. A module with secure defaults can still
|
|
9
|
+
produce a plan that replaces a database, because the plan depends on the state and the
|
|
10
|
+
environment, not only on the code. So the loop reviews the real plan against the scope written
|
|
11
|
+
when the module was, and checks at apply time that nothing moved between review and apply.
|
|
12
|
+
|
|
13
|
+
## Stages
|
|
14
|
+
|
|
15
|
+
| # | Stage | Type | Skills | Gate |
|
|
16
|
+
|---|---|---|---|---|
|
|
17
|
+
| 1 | `module` | generate | [`terraform-module`](../../skills/terraform-module/SKILL.md) | — |
|
|
18
|
+
| 2 | `plan` | verify | [`terraform-plan-review`](../../skills/terraform-plan-review/SKILL.md) | **G13** review |
|
|
19
|
+
| 3 | `apply` | gate | [`terraform-plan-review`](../../skills/terraform-plan-review/SKILL.md) | **G13.1** human |
|
|
20
|
+
|
|
21
|
+
## How to run this loop
|
|
22
|
+
|
|
23
|
+
1. **Write the module and the scope together.** Run `terraform-module` on the need. Beside it,
|
|
24
|
+
write `scope.md`: the resource addresses this change is meant to create, change or destroy,
|
|
25
|
+
and one sentence of intent. The scope is written now, before any plan exists, so the review
|
|
26
|
+
can't read the plan to fit it.
|
|
27
|
+
2. **Get a saved plan.** The person, or you where credentials allow, runs the commands below.
|
|
28
|
+
A plan printed without `-out` can't be applied as reviewed, so it doesn't count.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
terraform init
|
|
32
|
+
terraform plan -out=tfplan
|
|
33
|
+
terraform show -json tfplan > plan.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Record the planfile's SHA-256 in `scope.md`. Without credentials or state, stop and say
|
|
37
|
+
exactly which commands to run and what to bring back.
|
|
38
|
+
3. **Review the plan against the scope.** Run `terraform-plan-review` on `plan.json`, passing
|
|
39
|
+
every address in `scope.md` as the intended scope. Every change outside it is listed, even
|
|
40
|
+
when harmless. Save the review as `review.md`.
|
|
41
|
+
4. **Judge the plan at G13.** Map the review's verdict and findings to the gate:
|
|
42
|
+
- `SAFE TO APPLY` with nothing out of scope is `PROCEED`.
|
|
43
|
+
- `APPLY WITH CARE` is `PROCEED WITH CONDITIONS`, and the review's pre-apply checks are the
|
|
44
|
+
conditions.
|
|
45
|
+
- `DO NOT APPLY` caused by the code (a forced replacement, a widened rule, a missing
|
|
46
|
+
`prevent_destroy`) is `REVISE`.
|
|
47
|
+
- `DO NOT APPLY` caused by the change itself, such as a stateful resource that has to be
|
|
48
|
+
replaced to reach the goal, is `RECONSIDER`.
|
|
49
|
+
5. **On `REVISE`, fix the module, then re-plan.** Return to `module` with the forcing attribute
|
|
50
|
+
or the widened rule named. Re-plan from the fixed code; never edit `plan.json`. Each return
|
|
51
|
+
counts against the cap.
|
|
52
|
+
6. **Check the apply at G13.1.** Right before apply, confirm two things:
|
|
53
|
+
- the planfile's SHA-256 matches the one recorded in `scope.md`;
|
|
54
|
+
- `terraform show -json tfplan` still lists the same resource changes `review.md` reviewed.
|
|
55
|
+
|
|
56
|
+
Any difference is `REVISE` back to `plan`: re-plan and re-review. Then the person who will
|
|
57
|
+
run `terraform apply tfplan` reads every destroy and replace in `review.md` and decides.
|
|
58
|
+
Only they choose the verdict. Never run `terraform apply` yourself in this loop.
|
|
59
|
+
7. **Hand over the verdict and what apply will do.** Lead with what changes in plain words,
|
|
60
|
+
what is destroyed or replaced, and the checks to do first. The review goes underneath.
|
|
61
|
+
Close with each gate on its own line, `G13: <VERDICT>` and `G13.1: <VERDICT>` (or
|
|
62
|
+
`G13.1: awaiting <role>`), and one sentence of reasons.
|
|
63
|
+
|
|
64
|
+
## Verdicts
|
|
65
|
+
|
|
66
|
+
Defined in [`contracts/terminals.json`](../../../../contracts/terminals.json). G13 emits:
|
|
67
|
+
|
|
68
|
+
- `PROCEED`: every change is in scope, nothing stateful is destroyed or replaced, no access is
|
|
69
|
+
widened, and no unexplained drift.
|
|
70
|
+
- `PROCEED WITH CONDITIONS`: in-scope replacements of stateless resources, in-place changes to
|
|
71
|
+
stateful ones, or drift the plan overwrites. The review's pre-apply checks travel to G13.1.
|
|
72
|
+
- `REVISE`: the code causes a destroy, a replace or a widening nobody asked for. Return to
|
|
73
|
+
`module`.
|
|
74
|
+
- `RECONSIDER`: the change can only be made by replacing state. It needs a migration with a
|
|
75
|
+
backup and a rollback, not a module edit, so the loop ends here and names that work.
|
|
76
|
+
- `BLOCKED`: no plan to review, or a plan that errored.
|
|
77
|
+
|
|
78
|
+
G13.1 emits `PROCEED`, `REVISE` (back to `plan`, when the planfile or its changes moved) or
|
|
79
|
+
`BLOCKED` (no planfile, or nobody named to run apply).
|
|
80
|
+
|
|
81
|
+
On the cap-th `REVISE` at G13, emit `RECONSIDER`: the module isn't converging on a safe plan,
|
|
82
|
+
so the change needs a person to rethink it.
|
|
83
|
+
|
|
84
|
+
## Non-interactive runs
|
|
85
|
+
|
|
86
|
+
Missing provider or Terraform versions degrade to `terraform-module`'s `[assumption]` tags.
|
|
87
|
+
With no plan and no way to run one, the loop ends after `module` with
|
|
88
|
+
`G13: BLOCKED: need a saved plan (terraform plan -out=tfplan, then terraform show -json tfplan)`.
|
|
89
|
+
G13.1 always stops at the person who runs apply.
|
|
90
|
+
|
|
91
|
+
## When a stage's skill is not installed
|
|
92
|
+
|
|
93
|
+
Name the missing skill and skilldrop as its source, do the minimal inline version of that
|
|
94
|
+
stage, and record that it ran degraded. A degraded `plan` still lists every resource whose
|
|
95
|
+
actions include `delete`, every change to a security group, IAM policy or bucket policy, and
|
|
96
|
+
every address outside `scope.md`, because G13 can't be judged without them. Never skip G13 or
|
|
97
|
+
G13.1.
|
|
98
|
+
|
|
99
|
+
## Run log (opt-in)
|
|
100
|
+
|
|
101
|
+
Only when the environment variable `SKILLDROP_LOOP_LOG` is set to a file path: after each
|
|
102
|
+
gate's verdict, append one JSON line to that file. Nothing else is recorded, and nothing is
|
|
103
|
+
sent anywhere. When the variable isn't set, or you can't read it, skip this section.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
printf '%s\n' '{"ts":"2026-11-03T10:15:00Z","loop":"provision","stage":"plan","gate":"G13","verdict":"REVISE","round":1}' >> "$SKILLDROP_LOOP_LOG"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Fields: `ts` (UTC), `loop`, `stage`, `gate` (`null` for a stage without one), `verdict` (the
|
|
110
|
+
gate's exact word), and `round` (1 for the first attempt, counting returns under the cap).
|
|
111
|
+
The log holds verdicts only: never resource names, plan contents or values.
|
|
112
|
+
`skilldrop loop-stats` summarises the file.
|
|
113
|
+
|
|
114
|
+
## Quality bar
|
|
115
|
+
|
|
116
|
+
- **The scope is written before the plan exists,** as resource addresses. A scope written after reading the plan covers whatever the plan does.
|
|
117
|
+
- **Only a saved planfile is reviewed.** A plan printed without `-out` can't be applied as reviewed.
|
|
118
|
+
- **Every change outside the scope is listed,** even when harmless.
|
|
119
|
+
- **The plan applied is the plan reviewed.** G13.1 checks the planfile's hash and its resource changes.
|
|
120
|
+
- **The loop never runs `terraform apply`.** The person who runs it decides at G13.1.
|
|
121
|
+
|
|
122
|
+
## Anti-patterns to avoid
|
|
123
|
+
|
|
124
|
+
- ❌ **Reviewing the module instead of the plan.** Secure code can still replace a database when the state disagrees with it.
|
|
125
|
+
- ❌ **Fixing a bad plan by editing plan.json.** Fix the code and re-plan.
|
|
126
|
+
- ❌ **Passing G13 because the destroy is "probably fine".** A stateful destroy without a confirmed backup is `DO NOT APPLY`, and the loop maps it to `REVISE` or `RECONSIDER`.
|
|
127
|
+
- ❌ **Applying a fresh plan after reviewing an old one.** Anything merged in between is unreviewed.
|
|
128
|
+
- ❌ **Treating `RECONSIDER` as a bigger `REVISE`.** A change that has to replace state is a migration; another module edit won't make it safe.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
[
|
|
2
|
+
{"query": "write the terraform for an RDS read replica and check the plan before we apply", "should_trigger": true},
|
|
3
|
+
{"query": "run the provision loop on this new VPC", "should_trigger": true},
|
|
4
|
+
{"query": "provision a private bucket for backups safely, module to apply", "should_trigger": true},
|
|
5
|
+
{"query": "I'm about to apply the plan we reviewed yesterday, is it still the same one?", "should_trigger": true},
|
|
6
|
+
{"query": "review this terraform plan, is it safe to apply?", "should_trigger": false},
|
|
7
|
+
{"query": "turn this terraform into a reusable module", "should_trigger": false},
|
|
8
|
+
{"query": "plan the migration of our database to a new instance class", "should_trigger": false}
|
|
9
|
+
]
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"loop_name": "provision",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": "scope-before-plan",
|
|
6
|
+
"prompt": "Run the provision loop: we need a private S3 bucket for nightly Postgres backups from our billing service, versioned, encrypted with our existing KMS key alias/billing-backups, with objects moving to Glacier after 30 days and expiring after a year. AWS provider 5.x, Terraform 1.9. I'll run the plan myself and paste it back. Non-interactive: no follow-up questions.",
|
|
7
|
+
"assertions": [
|
|
8
|
+
"Runs terraform-module first and writes a module with typed, validated variables and public access blocked",
|
|
9
|
+
"Writes the intended scope as resource addresses before any plan exists",
|
|
10
|
+
"Asks for a saved plan (terraform plan -out=tfplan, then terraform show -json) and does not review a plan it doesn't have",
|
|
11
|
+
"Ends with G13: BLOCKED or an equivalent stop naming the exact commands to run and what to bring back",
|
|
12
|
+
"Does not run terraform apply",
|
|
13
|
+
"Does not invent an account id, bucket name or KMS key ARN the prompt did not give"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "out-of-scope-replace-sends-the-module-back",
|
|
18
|
+
"prompt": "Continuing the provision loop. Scope was: create module.backups.aws_s3_bucket.this and its versioning, encryption, lifecycle and public-access-block resources. Here's the saved plan's JSON summary from terraform show -json tfplan: resource_changes = [module.backups.aws_s3_bucket.this create; module.backups.aws_s3_bucket_versioning.this create; module.backups.aws_s3_bucket_server_side_encryption_configuration.this create; module.backups.aws_s3_bucket_lifecycle_configuration.this create; module.backups.aws_s3_bucket_public_access_block.this create; aws_db_instance.billing actions [delete, create], replace_paths [[\"engine_version\"]]]. The billing DB change wasn't in my PR.",
|
|
19
|
+
"assertions": [
|
|
20
|
+
"Flags the aws_db_instance.billing replace as stateful, out of scope, and forced by engine_version",
|
|
21
|
+
"Maps the review to DO NOT APPLY and gives G13 REVISE, not PROCEED WITH CONDITIONS",
|
|
22
|
+
"Says the replacement comes from code or state outside this change (for example a version drift or a stale branch) and names what to check",
|
|
23
|
+
"Says to re-plan after the fix and never to edit plan.json",
|
|
24
|
+
"Does not reach G13.1 or approve an apply"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "stale-planfile-at-apply",
|
|
29
|
+
"prompt": "Continuing the provision loop. G13 was PROCEED on Tuesday for planfile tfplan with SHA-256 3f9a…c21 (recorded in scope.md). It's Thursday and I'm about to apply. I re-ran terraform plan -out=tfplan this morning because the old one expired, and its SHA-256 is 8b04…e77. Can I go ahead?",
|
|
30
|
+
"assertions": [
|
|
31
|
+
"Notices the planfile hash differs from the reviewed one",
|
|
32
|
+
"Gives G13.1 REVISE back to plan and asks for the new plan to be reviewed before apply",
|
|
33
|
+
"Explains in one sentence why a fresh plan is unreviewed (anything merged or drifted since Tuesday)",
|
|
34
|
+
"Does not approve the apply on the person's behalf",
|
|
35
|
+
"Does not run terraform apply"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "provision-rds-no-plan-yet",
|
|
40
|
+
"held_out": "2026-11-03",
|
|
41
|
+
"prompt": "We need a Postgres database for the new timesheets app, staging first then prod. Network details and our tag conventions are in network.md, and the staging env's lock file is .terraform.lock.hcl.fixture. Write the terraform and check it before we apply. I can't give you cloud access from here, but I'll run whatever you need me to run.",
|
|
42
|
+
"assertions": [
|
|
43
|
+
"Runs the module stage first and writes main.tf, variables.tf, outputs.tf and versions.tf with typed, validated variables, a README with inputs and outputs tables, and a staging usage example",
|
|
44
|
+
"Secure defaults: storage encrypted with the existing alias/platform-rds key (no new KMS key), publicly_accessible false, deletion_protection true, backup retention 14 days, and the master password managed by RDS rather than passed as a variable",
|
|
45
|
+
"Allows port 5432 only from sg-04d5e6f7a8b9c0d12, with no CIDR ingress, places the database in the three private subnets from network.md, and turns multi-AZ on for prod only",
|
|
46
|
+
"Applies the five required tags from network.md through a merged tags variable, and the versions.tf provider constraint admits the locked hashicorp/aws 5.62.0 without requiring 6.x",
|
|
47
|
+
"At gate G13 says there is no real plan to review and does not pass the gate: gives the exact commands to produce one (terraform plan -out=<file>, then terraform show -json <file>) and says what the review will check",
|
|
48
|
+
"Never gives SAFE TO APPLY and does not reach the apply check; says apply waits on a review of the real plan and a person's approval",
|
|
49
|
+
"Does not invent plan output (resource counts, destroys, replaces) or IDs, ARNs and account details that are not in network.md"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "provision-replica-plan-replaces-primary",
|
|
54
|
+
"held_out": "2026-11-03",
|
|
55
|
+
"prompt": "Reporting team needs a read replica of the orders database. I've made the change on my branch (the module's main.tf and variables.tf are attached) and ran the prod plan, json is in plan.json. Can you run provision on it? I'd like to apply this afternoon.",
|
|
56
|
+
"assertions": [
|
|
57
|
+
"Runs the module stage on the change, then reviews plan.json at gate G13, rather than going straight to a verdict",
|
|
58
|
+
"Flags module.orders_db.aws_db_instance.primary as a replace forced by identifier (orders-prod to orders-prod-primary), says the production database would be destroyed and recreated, and notes that deletion_protection false and skip_final_snapshot true mean nothing blocks the destroy and no final snapshot is taken",
|
|
59
|
+
"Flags the db_ingress replace that widens port 5432 from 10.40.0.0/16 to 0.0.0.0/0 as access widening the request never asked for, noting that var.vpc_cidr is now unused",
|
|
60
|
+
"Flags the delete of aws_s3_bucket_lifecycle_configuration.exports (the 90-day expiry on exports/) as outside the read-replica scope",
|
|
61
|
+
"Gate G13 does not pass: the plan verdict is DO NOT APPLY and the loop sends the work back to the module stage, whose revision keeps the primary's existing identifier, restores ingress to var.vpc_cidr, and either restores the lifecycle rule or names it for its owner to decide",
|
|
62
|
+
"Treats the revised module as needing a fresh plan: does not reach the apply check or approve applying this afternoon, and gives the commands to re-plan",
|
|
63
|
+
"Does not invent facts not in the inputs, such as a recent manual snapshot existing, the replica needing public access, or changes plan.json does not contain"
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# This file is maintained automatically by "terraform init".
|
|
2
|
+
# Manual edits may be lost in future updates.
|
|
3
|
+
|
|
4
|
+
provider "registry.terraform.io/hashicorp/aws" {
|
|
5
|
+
version = "5.62.0"
|
|
6
|
+
constraints = ">= 5.40.0, < 6.0.0"
|
|
7
|
+
hashes = [
|
|
8
|
+
"h1:8qY3kG0a1mX2rT4vN6pB9cD5eF7hJ1kL3mN5pQ7rS9u=",
|
|
9
|
+
"zh:0a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9",
|
|
10
|
+
"zh:1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90a",
|
|
11
|
+
"zh:2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90a1b",
|
|
12
|
+
"zh:3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c",
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
provider "registry.terraform.io/hashicorp/random" {
|
|
17
|
+
version = "3.6.2"
|
|
18
|
+
constraints = "~> 3.6"
|
|
19
|
+
hashes = [
|
|
20
|
+
"h1:Rm4vY2kT9pB1cN3dF5gH7jK9lM1nP3qR5sT7uV9wX1y=",
|
|
21
|
+
"zh:4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d",
|
|
22
|
+
"zh:5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e",
|
|
23
|
+
]
|
|
24
|
+
}
|