Files
Paweł Płatek 07bce8a2c8 Add goal-prompt plugin for copy-ready /goal commands (#248)
* Add goal-prompt plugin for copy-ready Codex /goal commands

Migrated from trailofbits/codex-skills#6, converted from the Codex
sidecar layout (.codex-plugin/, .agents/marketplace.json) to this
repo's canonical Claude plugin structure, which Codex loads through
marketplace compatibility. Both loadability checks pass.

The skill drafts a goal-mode objective and pipes it through a
deterministic stdlib-only formatter that collapses whitespace to one
line and rejects output over the 4,000-character /goal cap, with a
pytest suite covering normalization and both failure modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Add goal-prompt plugin for copy-ready /goal commands

Migrated from trailofbits/codex-skills#6, converted from the Codex
sidecar layout (.codex-plugin/, .agents/marketplace.json) to this
repo's canonical Claude plugin structure, which Codex loads through
marketplace compatibility. Works with goal mode in both Claude Code
and Codex; both loadability checks pass.

The skill drafts a goal-mode objective and pipes it through a
deterministic stdlib-only formatter that collapses whitespace to one
line and rejects output over the 4,000-character /goal cap, with a
pytest suite covering normalization and both failure modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: encode goal-mode limits and termination-contract guidance

Researched both harnesses' official docs. Claude Code's /goal condition
and Codex's stored objective are each capped at 4,000 characters, and
Claude Code's evaluator is a transcript-only small model that cannot run
tools — so a condition is judgeable only when the agent runs the check
and shows the output.

SKILL.md now separates the plugin's two jobs: draft a termination
contract (end state not activity, stated check with transcript-visible
proof, invariants including never weakening the gate, stop bound or
blocked clause, AND not "or"), then format it. Platform mechanics with
sources live in references/goal-mode.md. The formatter gains one
deterministic non-fatal check: it warns when the objective has no
numeric stop bound and no blocked clause, the documented top failure
mode for goal loops. Tests cover the new detection both ways.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: fold in Trail of Bits field guidance from codex-config

The codex-config README's /goal section adds three things the plugin
was missing. The drafting checklist gains the work-order fields it
lacked: scope to read first, and for multi-checkpoint goals a final
evidence deliverable plus a progress log file for durable state. A
when-to-use heuristic (an instruction repeated three turns in a row
belongs in the goal; chain small goals rather than one giant one). And
a security-research section hardening audit goals against reward
hacking: neutral wording, threat-model scoping, demonstrated attacker
preconditions, known-findings checks, per-finding human review, and
second-pass validation.

references/goal-mode.md gains the full work-order template, the codex
exec caveat, and the missing official links.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: tighten SKILL.md and README.md

Same content, less prose: SKILL.md drops the meta-commentary and keeps
the checklist, security hardening, formatter contract, and example;
README.md explains the two jobs in two paragraphs for a human reader.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: never invent missing goal elements

Every checklist element must be grounded in the user's request, the
conversation, or the repository (look up the real check command, don't
guess one). When nothing grounds an element, the skill still optimizes
and formats what the user provided, but reports the gap in a Missing:
list after the fenced block instead of fabricating a success condition
that would terminate the goal on the wrong contract. The example now
shows both the grounded case and the flag-the-gaps case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: fold goal-mode reference into SKILL.md

The reference file mostly restated the checklist. What survives into
SKILL.md: the official doc links, the Codex feature flag and
interactive-only caveat, and the Claude Code resume caveat (turn
bounds silently extend across resumes), placed next to the stop-bound
rule it affects. Everything else was lifecycle and mechanics detail
the drafting job does not need.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: close easy-outs, keep goals small

Two additions that pull in opposite directions, stated together so
neither wins by default. Before formatting, reread the condition as a
lazy model would and close the cheapest ways to satisfy the letter
without the intent: delete-or-stub, pass-on-a-subset, game-the-gate,
claim-without-running. But every constraint narrows the state space
the model can explore, so prefer pairing existing checks over adding
constraints, collapse to one terminating criterion when possible, and
drop non-goals. Outs that cannot be closed from grounded information
go in the Missing: list as warnings, never as invented constraints.

The security section now leads with the collapsed pattern: one
criterion referencing a THREATMODEL.md that carries scope, attacker
powers, severity baseline, and known findings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: strip links and config detail from SKILL.md

Skill bodies are for the drafting agent: the doc links, the Codex
feature-flag and codex-exec caveats, and the fenced security-goal
example added context without changing behavior. The never-invent rule
loses its check-command specificity. The security section keeps only
what changes the drafted text: one criterion, scoping file, neutral
wording, demonstrated preconditions, per-finding review, second-pass
validation. The Example section stays — Anthropic's authoring
checklist calls for concrete input/output examples, and this skill's
output shape is the point.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: replace README wall-of-prose with a capability list

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: set Paweł Płatek as author, trim CODEOWNERS

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: add with/without ablation evals

Three cases, each the same bare "Improve this goal: ..." prompt run
with and without the plugin so the score delta isolates what the skill
adds: grounded-migration (single-line output, check command grounded
in the fixture's package.json, stop clause), ungrounded-vague (no
invented metrics or benchmark commands when nothing grounds "faster";
gaps flagged back), easy-out-closed (the user's grep-only success
check is deletable-code-satisfiable; the goal must pair it with the
fixture's real test suite).

Graders judge the returned artifact against fixture contents: regex
for the mechanical stop clause, LLM graders for grounding, invention,
and easy-out closure. Also adds "improve" to the skill's trigger list
since the eval prompts (and users) phrase it that way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: fix eval graders to judge only the /goal line

Live ablation runs exposed the grader bug AGENTS.md warns about: the
haiku judges failed correct answers because the Missing: list's
illustrative examples ("e.g. p95 under 300ms") were read as inventions
and as extra command candidates. Graders now scope judgment to the
single line inside the fenced block and explicitly exempt the gap
list; the README pins --judge-model sonnet since haiku cannot follow
that scoping.

Measured results (4 runs/arm/case): plugin arm 100% across all
graders; bare arm bimodal — the deterministic stop-clause regex alone
failed half its grounded-migration runs. On strong models the plugin's
demonstrated value is consistency, recorded as such in the README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: record baseline contamination in eval results

Checked our eval logs against trailofbits/skills-internal#546 and we
were hit: baseline-arm responses cite the fixture's absolute repo path
and reproduce SKILL.md sentences verbatim ("scope to read first",
"terminates on the wrong contract"), so the no-plugin arm read the
plugin under test off disk and imitated it. Our runs were more exposed
than the issue's report — the baseline had full Bash, not just ungated
Read/Glob. The README now marks measured deltas as lower bounds, notes
the uncontaminated baselines scored 0, and prescribes --keep-temp plus
a leakage audit of baseline traces until the harness can deny the
baseline Read access to the plugin directory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* goal-prompt: isolate eval fixtures from the repo, gate on contamination

The ablation baseline could read the skill under test: add_dirs handed
every agent an absolute path into this repo, one directory walk from
SKILL.md and the graders, and baseline responses reproduced SKILL.md
sentences verbatim. Contaminated baselines imitate the skill, deflating
the delta to near zero.

Fixtures are now generated inside the eval's temp scaffold by each
case's scaffold.sh (run with --scaffold), so no agent sees a repo path;
a kept-temp probe confirmed the scaffold gets its own home/, config/,
and cwd/ with zero repo paths in the baseline trace.
check_contamination.py fails a run whose baseline responses contain the
plugin path, script name, or verbatim SKILL.md phrases, and fails when
it has nothing to inspect; its pytest suite proves both directions.

Clean rerun: plugin arm 1.00 everywhere; baselines 0.40/0.29/0.25;
mean delta +0.69 (was +0.04 contaminated). The checker flags the old
contaminated result and passes the new one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix owner

* goal-prompt: run scripts through uv, fix the dead contamination marker

Two fixes on top of #248.

The skill's only operative command was `python3 {baseDir}/scripts/
format_goal_prompt.py`. The modern-python plugin in this same marketplace
ships PATH shims that reject a bare `python3 <script>`, so the Format
step failed for anyone who has it installed — and #255's narrowing does
not help, because a bare script run is exactly what `uv run` replaces
and stays intercepted by design. Now `uv run --no-project`, matching the
form the Makefile already uses in all four of its invocations. Verified
with the shim on PATH: byte-for-byte identical output to the old command
run shim-free. Same fix in evals/README.md.

The `scope to read first` contamination marker could never fire: SKILL.md
writes `**Scope to read first**` and the match was case-sensitive. Every
existing test quoted the marker's own lowercase spelling rather than the
file's, so 22 tests passed over a dead marker. Matching is now
case-folded, the markers are split into path and phrase groups, and two
tests guard the recurrence — one asserts every phrase marker is still
present in SKILL.md, the other quotes SKILL.md verbatim. Both were
mutation-tested: reverting either fix fails exactly one of them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Dan Guido <dan@trailofbits.com>
2026-08-19 01:02:42 -04:00
..

goal-prompt

Turns a task description into a copy-paste-ready /goal command for goal mode in Claude Code or Codex.

What It Does

A goal prompt has to do two things at once: fit the format (/goal is a single line, max 4,000 characters in both harnesses) and actually terminate (goal mode keeps looping until the condition is judged met — a vague condition burns turns forever).

The skill:

  • drafts the objective as a termination contract: measurable end state, scope to read first, the command that proves completion, invariants, explicit stop bound
  • closes the easy-outs a lazy model would take: deleting code to silence a check, passing on a subset, gaming the gate
  • keeps the goal small — every constraint narrows what the model can explore
  • never invents missing elements — anything it cannot ground in your request or the repo comes back as a Missing: warning
  • hardens security-audit goals against reward hacking, following trailofbits/codex-config
  • formats deterministically to one line, warning on a missing stop clause and rejecting output over 4,000 characters instead of truncating

Example

User: turn this into a /goal command:
      Migrate the auth module:
        - replace legacyAuth() with auth.verify()
        - make sure the tests still work

Assistant:
/goal All legacyAuth() call sites use auth.verify(): `rg "legacyAuth\(" -t ts` prints nothing AND `npm test` exits 0 (run both, show the output), without modifying vendor/ or weakening any test. If blocked, stop and report attempted paths and the blocker, or stop after 20 turns.

Components

  • skills/goal-prompt/SKILL.md — drafting checklist and output contract
  • skills/goal-prompt/scripts/format_goal_prompt.py — stdlib-only formatter (--fenced, --objective-only, --max-chars)
  • evals/ — with/without-plugin ablation cases (claude plugin eval goal-prompt --ablation with-without)