Skip to content

docs(skills): scope rafter security skills by surface, not task label#195

Merged
Rome-1 merged 1 commit into
mainfrom
fix/rafter-skill-scoping
Jul 9, 2026
Merged

docs(skills): scope rafter security skills by surface, not task label#195
Rome-1 merged 1 commit into
mainfrom
fix/rafter-skill-scoping

Conversation

@Rome-1

@Rome-1 Rome-1 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Problem

The Rafter skills are a security review gate, but their prompts were all-escalation with no scoping: "DO NOT stop at local", "an un-evaluated 'done' on security-relevant work is not done", "Default expectation: run rafter run" — and nowhere did they say what counts as security-relevant or when the gate does not apply.

Result: agents writing research / experimental / exploratory code with zero security surface (training scripts, data analysis, plotting, model eval, notebooks, pure computation over trusted local data) kept getting the gate in the way. Direct feedback from the repo owner: Rafter should scope itself.

Fix (prompting only — no executable code changes)

Added a "When this applies (and when it doesn't)" framing to the three relevant skills, driven by the security SURFACE of the diff, not the task's label.

  • ENGAGE fully (unchanged — not weakened): auth, credentials/secrets/tokens/sessions, user/untrusted input, SQL, shell/exec, file paths + uploads, deserialization, crypto, network-facing endpoints/outbound fetchers (SSRF), data deletion, dependency/manifest changes.
  • BACK OFF: research/experimental/local-only/throwaway code with none of that surface — a quick surface check suffices, and if there is genuinely no surface it is fine to proceed without the full rafter-code-review + rafter run.
  • The key rule, landed in every edited skill: judge by the actual security surface of the diff, not by whether the work is called "research." Research code that touches secrets/shell/network/untrusted input still fully engages.

Also surface-conditioned the overly-broad description: front-matter on each edited skill (that string drives auto-invocation), so auto-triggering follows the surface too.

Guardrail: reduces false engagement WITHOUT weakening the gate

The back-off branch fires only when none of the engage surfaces are present; any presence of a real surface re-engages the full gate. No auth/secret/injection/SSRF/dependency surface can slip past. This narrows over-engagement on no-surface code without removing or softening protection for genuine security surface.

rafter-skill-review is left unchanged on purpose: vetting third-party executable context before install is always a genuine security surface, so adding a back-off there would weaken a real gate.

Files changed / node ↔ python parity

Docs/prompt-only. Edited and kept byte-identical across both trees:

  • node/resources/skills/rafter/SKILL.md (router — scoping section near the top)
  • node/resources/skills/rafter-code-review/SKILL.md
  • node/resources/skills/rafter-secure-design/SKILL.md
  • ...and their mirrors under python/rafter_cli/resources/skills/<same-path>

Confirmed node vs python identical before and after via diff. Front-matter still parses cleanly through the existing registry parser (names/versions unchanged; descriptions remain single-line).

Refs: sable-hjd3


AI-assisted (per repo AI Policy): authored with Claude Opus 4.8.

🤖 Generated with Claude Code

The rafter skill prompts were all-escalation with no scoping ("DO NOT
stop at local", "an un-evaluated 'done' is not done", "default: run
rafter run") and never said WHAT counts as security-relevant or when the
gate does NOT apply. Agents writing research/experimental code with zero
security surface (training scripts, data analysis, plotting, model eval,
notebooks, pure computation over trusted local data) kept getting the
gate in the way.

Fix (prompting only — no executable code changes): add a "When this
applies (and when it doesn't)" framing driven by the security SURFACE of
the diff, not the task's label.

- ENGAGE fully (unchanged, not weakened): auth, credentials/secrets/
  tokens/sessions, user/untrusted input, SQL, shell/exec, file paths +
  uploads, deserialization, crypto, network-facing endpoints, data
  deletion, dependency/manifest changes.
- BACK OFF: research/experimental/local-only/throwaway code with NONE of
  that surface — a quick surface check suffices, and with genuinely no
  surface it is fine to proceed without the full rafter-code-review +
  rafter run.
- Key rule landed in every edited skill: judge by the actual security
  surface of the diff, not by whether the work is called "research."
  Research code that touches secrets/shell/network/untrusted input still
  fully engages.

Also surface-conditioned the overly-broad `description:` front-matter
(that string drives auto-invocation).

Files: node/resources/skills/{rafter,rafter-code-review,
rafter-secure-design}/SKILL.md and their byte-identical python mirrors
under python/rafter_cli/resources/skills/. rafter-skill-review is left
unchanged on purpose: vetting third-party executable context before
install is always a genuine security surface, so a back-off there would
weaken a real gate.

This REDUCES false engagement on no-surface code without removing or
softening protection for any genuine security surface.

Refs: sable-hjd3

AI-assisted (per repo AI Policy): authored with Claude Opus 4.8.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Rome-1
Rome-1 merged commit d7317a9 into main Jul 9, 2026
@Rome-1
Rome-1 deleted the fix/rafter-skill-scoping branch July 9, 2026 02:43
Rome-1 added a commit that referenced this pull request Jul 9, 2026
…el (sable-m9n6) (#196)

`rafter agent init` injects RAFTER_INSTRUCTION_BLOCK into agents' instruction
files (CLAUDE.md, and ~/.codex/AGENTS.md — a GLOBAL Codex instructions file).
The block was a blanket gate ("A security-relevant task is not complete until
reviewed... Stop and invoke before continuing") with no scoping. Because Codex
reads ~/.codex/AGENTS.md as global instructions, every Codex session was ordered
to treat its task as rafter-gated — agents went to read rafter's SKILL.md instead
of doing the requested (often no-surface, research) work. Instruction-level
hijacking.

This applies the same surface-based scoping that PR #195 gave the skill prompts,
to the injected block itself, so block and skills tell the same story:

- Engage the gate when the change touches a real security surface (auth, secrets/
  tokens, untrusted input, SQL/query, shell/exec, file paths, deserialization,
  crypto, network endpoints/SSRF, data deletion, dependencies).
- Back off when NONE of those are present — research/experimental/local-only/
  throwaway code (training scripts, analysis, plotting, model eval, notebooks,
  pure computation over trusted local data): a quick surface check is enough.
- Decisive rule: judge by the diff's actual surface, not the "research" label —
  research code that reads a secret, shells out, hits the network, parses
  untrusted input, or bumps a dependency still gets the full gate.

Guardrail: this reduces false engagement on no-surface code without removing or
softening protection for any listed surface — the routing bullets were expanded
(crypto, network/SSRF, data deletion, dependencies) so coverage strengthens, not
weakens. node and python RAFTER_INSTRUCTION_BLOCK remain byte-identical.

Also (sable-h0ah): narrow the Codex .codex/hooks.json PostToolUse matcher from
`.*` to `Bash|apply_patch` so posttool stops firing on every Read/MCP call
(log noise + latency), matching Codex's own PreToolUse surface and the
claude-code posttool scoping. Applied in init.ts, components.ts, and the python
mirror; the one test asserting the old value is updated.

AI-assisted (Claude Opus 4.8) per repo AI Policy.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Rome-1 added a commit that referenced this pull request Jul 12, 2026
#195 added the surface-scoping section and grew the rafter router SKILL.md from
118 -> 142 lines, blowing BOTH line budgets (node <120, python <130). With no CI,
those two tests have been red on main since it merged.

- Condense SKILL.md 142 -> 128 lines: the 11-bullet engage list becomes a compact
  inline list (every surface kept) and the tier section is tightened. No content
  dropped — the scoping rule stays in the router, since it decides whether the
  skill is entered at all and can't be pushed into a sub-doc.
- Align the node budget 120 -> 130 to match python's. The limits had drifted:
  python was bumped 120 -> 130 for the scanner-scope section; node never was.

Tests: node brief.test.ts 28 passed; python test_brief.py 35 passed.
node/python SKILL.md byte-identical.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@Rome-1 Rome-1 mentioned this pull request Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant