Compare commits

...
2 Commits
Author SHA1 Message Date
mathiasandClaude Opus 5 21be3ea245 feat(skills): add secret-hygiene skill
release / tag (push) Successful in 0s
Was a loose secret-hygiene.md at the root of ~/.claude/skills — not
dir/SKILL.md, so no harness ever loaded it. Promoted to a real skill so
it installs to Claude Code, Crush, Antigravity and Vibe alike.

Frontmatter description carries load-before triggers (kubectl exec/env,
whole-file SOPS decrypt, token rotation) so it fires ahead of the command
that would leak, not after.

Bump-Type: minor

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
2026-08-23 07:50:42 +02:00
mathias 85e125703e feat(own-the-task): add delegation-ownership skill (#21)
release / tag (push) Successful in 1s
2026-08-05 19:09:38 +00:00
3 changed files with 134 additions and 0 deletions
+4
View File
@@ -23,7 +23,9 @@ This index lists all available engineering skills. Load the full SKILL.md on dem
| `session-retrospective` | Surface non-obvious learnings from a session log | After a coding session ends, before context is lost |
| `trainer` | Two-phase brain curation (score candidates, write past quality gate) | Periodic brain pruning and curation |
| `grill-me` | Structured plan interrogation — Quick Poke, Full Grill, Pre-mortem | Stress-testing a plan before committing; end of Diamond 1; before promoting to pre-prod |
| `own-the-task` | Delegation ownership — Intake → Brief → Plan → Execute | A task is handed over open-ended; "take this off my plate"; before a plan exists |
| `telos-load` | Load TELOS intention substrate at session start | Starting any koala session; before architectural decisions; CAD pipeline entry |
| `secret-hygiene` | Stop credentials rendering into the session transcript; safe rotation chain when one leaks | Before any kubectl/SOPS/token command; after a confirmed leak |
| `regulatory-risk-assessment` | Structured risk register for regulated-industry features | Filing a CAD issue (needs Risk: level); features touching payments, auth, external APIs, user data |
| `discovery-framing` | Problem-before-solution discovery/framing method | Framing/validating a problem before any solution; opportunity sizing |
| `stage-gate-review` | Stage-gate product governance (go/no-go council) | Preparing a Product Council ask; building a go/no-go checklist; gate review |
@@ -90,6 +92,7 @@ code-review ──→ refactoring ──→ clean-code
session-retrospective ──→ trainer ──→ brain (via brain_write)
(planning & convergence)
own-the-task ──→ grill-me (intent first, then stress-test the plan)
grill-me ──→ planning (after plan is sharpened)
──→ spec-driven-dev (after hypothesis is validated)
```
@@ -113,5 +116,6 @@ grill-me ──→ planning (after plan is sharpened)
| "Grill me / stress-test this / is this ready?" | `grill-me` |
| "End of Diamond 1 — should we build this?" | `grill-me` (Full Grill) |
| "About to promote to pre-prod" | `grill-me` (Pre-mortem) |
| "Take this off my plate / own this / act as my AI employee" | `own-the-task` |
| "What are the risks?" / "compliance gate" / "risk register" | `regulatory-risk-assessment` |
| "Start of session" / "load TELOS" / "what are we optimizing toward?" | `telos-load` |
+54
View File
@@ -0,0 +1,54 @@
---
name: own-the-task
description: Take ownership of a delegated task like a hired employee — interview first, write a handover brief, propose a plan, then execute. Use whenever the user says "take this off my plate", "own this", "act as my AI employee", "interview me first", "ask me questions before you start", or delegates anything open-ended where the real goal, the why, and the definition of done are not yet on the table. Trigger even when "interview" is never said — any delegation where getting the intent wrong would waste the whole effort belongs here.
---
# Own the task
The user is delegating, not dictating. Value comes from the questions that change the shape of the work, not from a fast answer to the surface request.
**Intake → Brief → Plan → Execute.** Don't skip ahead. A task looking obvious usually means the real goal hasn't surfaced yet.
## 1. Intake
Ask the 5 highest-signal questions — the ones whose answers would most change your approach. **One per turn, wait for the answer.** Skip anything you could infer or look up. Follow up on any gap, contradiction, or opportunity; follow-ups don't count against the five.
Cover:
1. **Real goal** — the outcome underneath the stated deliverable.
2. **Why** it matters to them.
3. **Attention budget** — what they want to be *asked* about vs *told* about afterwards, plus hard constraints (time, money, people, politics, tooling, taste). This work fails by pulling them back in, not by missing a spec.
4. **Definition of done** — observable. Then always follow up: *what changes for you once that's true?* Cold, that question gets a shrug; after the observable answer, it surfaces criteria they can't state directly.
5. **Failure modes** — what bad looks like, or what previous attempts got wrong. Success criteria are rehearsed; being burned isn't.
Weak → strong:
- "What's your deadline?" → "If this were half-finished on the deadline, what's the half you'd want done?"
- "How involved do you want to be?" → "What would you be annoyed to be asked about — and what would you be annoyed to find out afterwards?"
- "Any constraints?" → "What's been tried here before, and what made it not stick?"
If they answer thinly or say "you decide", take the decision, state the assumption, move on.
## 2. Handover brief
Write it as if you had amnesia and were briefing your future self — a stranger should be able to pick the task up cold.
```
**Task:** / **Real goal:** / **Why:**
**Constraints:** [hard vs soft, marked]
**Attention budget:** [what comes back to them, what doesn't]
**Definition of done:** / **What a win feels like:**
**Failure modes to avoid:**
**Open assumptions:** [what you decided for them, so they can correct it]
```
## 3. Plan
Short: numbered steps with what each produces, the first checkpoint, and what you'll decide yourself (drawn from the attention budget, not guessed). Then ask for a go-ahead — one question, not a menu.
## 4. Execute
Decide the small things. Surface only what changes the outcome. If reality contradicts the brief, re-propose early rather than quietly bending the work. Deliver against the definition of done and say plainly where you fell short.
## Skip this
For small, well-specified, or factual requests. If the task is nearly specified, ask one or two questions and say why you're going short.
+76
View File
@@ -0,0 +1,76 @@
---
name: secret-hygiene
description: Prevent agent sessions from rendering credentials into the transcript, and recover safely when one leaks. Load BEFORE any command that touches credentials - kubectl exec/env/describe, kubectl get secret, whole-file SOPS decryption, token generation or rotation, envFrom inspection, or edits to encrypted config. Also load when a credential has already been exposed and a rotation chain is needed. Triggers - "rotate this token", "decrypt the config", "check the env var", "leaked credential", "did that print a secret", "why is this secret wrong".
---
# Secret hygiene for agent sessions
Goal: stop the recurring leak pattern where a diagnostic command renders a
SOPS-encrypted value or a Kubernetes Secret value into the session
transcript, forcing a rotation.
**Confirmed past leaks** (see `mathias/infra#39` rotation umbrella):
- `POSTGRES_PASSWORD` — `kubectl exec ... env` rendered envFrom value
- `INFRA_MCP_TOKEN` — `sops -d ingress.enc.yaml` rendered the bearer in the configuration-snippet
- `brain_app` postgres DSN — pre-fix ingestion pod log (hyperguild#20)
Three are pattern, not bug.
## Rules — never do these
| Forbidden | Why | Use instead |
|---|---|---|
| `kubectl exec <pod> -- env` (or `printenv`) | Renders every envFrom + valueFrom.secretKeyRef plaintext | `kubectl describe pod <pod>` — k8s redacts secretKeyRef in describe |
| `sops -d <file>` to inspect | Whole-file decrypt dumps everything | `sops --extract '["key"]' <file>` for pinpoint reads |
| `kubectl get secret ... -o jsonpath='{.data}'` (whole map) | Returns base64 of every key; trivial to b64-decode the lot | `... -o jsonpath='{.data.SPECIFIC_KEY}'` for the one key needed |
| Echoing a generated token to verify length / content | Renders the new token into transcript, defeating the rotation | Verify functionally: `curl ... -H "Authorization: Bearer $(cat /tmp/tokfile)"` — never echo `$NEW_TOK` |
| Storing a token in a shell variable then running `echo` / `set` / `env` | Same as above | Pipe through file (mode 600) or stdin to the next consumer |
## Rules — do these instead
- **Verify by side-effect, not by value.** If a token rotation should make
the old token return 401 and the new one return 200, test those two
HTTP responses. Never check `$OLD == $LEAKED` by printing it.
- **Pinpoint SOPS reads.** When you need ONE key from a SOPS file, always
`sops --extract '["..."]'`. The shape of the key path is safe to log;
the value isn't.
- **Stash tokens in `/dev/shm/<unique>` mode 600** and pass via filename,
not env. `shred -u` after use. Avoid `/tmp` if the system has `/dev/shm`.
- **If you must dump a secret to confirm wiring**, immediately rotate it
in the same commit train. Treat the dump as "this is now compromised".
## Rotation chain pattern when a token is already exposed
If a token was leaked in a prior step of the same session, chain two
rotations:
1. **T1**: replace the leaked value with a new one. The verification step
may also leak T1 (acceptable risk).
2. **T2**: replace T1 with a fresh value. Run this without any decrypt
step that emits to stdout. The new value lives only in the encrypted
file + the consumer's secret store (`~/.llmkeys`, the k8s Secret).
Example T2 path (Python, never decrypts to stdout):
```python
new_t2 = subprocess.check_output(["openssl","rand","-hex","32"]).decode().strip()
plaintext = subprocess.check_output(["sops","-d", path]).decode() # in-memory only
patched = re.sub(r'Bearer [a-f0-9]+(?=")', f"Bearer {new_t2}", plaintext)
# write patched via /dev/shm tmp + sops -e → write back
# DO NOT print(patched), DO NOT print(new_t2)
```
After T2 lands via GitOps + Flux, update consumers from the file stash
without echoing.
## Detection ideas (not blocking — telemetry)
- Add a `bash` pre-hook in Claude harness that refuses obvious patterns
(`kubectl exec * env*`, `sops -d *.enc.yaml`). Filed as a follow-up.
- Run a periodic grep over `~/.claude/projects/*/sessions/*.jsonl` for
high-entropy strings matching `[a-f0-9]{32,}` to catch new leak surface.
## Reference
- `gitea.d-ma.be/mathias/infra#39` — umbrella credential rotation issue with confirmed-leak roster.
- `gitea.d-ma.be/mathias/infra#44` — POSTGRES_PASSWORD rotation procedure (template for future incidents).