promptwheel 0.7.1 → 0.7.2

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 CHANGED
@@ -104,6 +104,15 @@ npx promptwheel suggest # UCB over the lever scores, proven le
104
104
  npx promptwheel backfill -n 30 # cold start: seed the record from git history (cohort-tagged, commit types → labels)
105
105
  ```
106
106
 
107
+ ## Give your agent the gate
108
+
109
+ `promptwheel init` writes this into your `AGENTS.md` for you (and `CLAUDE.md` / `.cursorrules` if present; `--no-agent-block` to skip), so a coding agent (or a loop) runs the gate itself, not just you. To paste it yourself:
110
+
111
+ After a change that should move a measurable metric (tests, lint, coverage, an eval), run
112
+ `npx promptwheel run --working`. Exit 2 (verdict GAMED) means the win came from editing a test,
113
+ config, or grader, not the source, so revert and earn it in the source. Exit 1 means a guarded
114
+ metric regressed, revert that too. Never weaken, skip, or delete a test to go green.
115
+
107
116
  **The consequence ledger.** git records *what changed*; PromptWheel records *what the change did*, same trust model (local, deterministic, append-only, no server, no LLM in the verdict). `playbook` and `suggest` are pure re-derivations over that ledger: every rendered line was measured by the gate, decays unless re-earned, and stays hidden below an evidence threshold. No compounding claim is made for them until the A/B acceptance test (`bench/compounding-ab.mjs`) passes on real usage data.
108
117
 
109
118
  **Footprint:** it never touches your working tree, every measurement runs in a throwaway git worktree **in your system temp dir** (one at a time; configured `linkDirs` are symlinked not copied, `node_modules` by default, `.venv` for Python, etc.), removed when the run finishes. The only thing PromptWheel writes to your repo is the optional `.promptwheel/outcomes.jsonl` record, commit it to build the per-repo "what moves what" history, or `.gitignore` it (`--no-record` to skip entirely). A hard-killed run can't leave clutter behind: the next run **self-heals** any orphaned worktree (stale registry entry + abandoned temp checkout).
@@ -256,4 +265,4 @@ The engine is one importable file; pure helpers are exported for unit tests, the
256
265
  - [x] zero-config: `run` and `check()` auto-detect the gate with no committed config, `init` becomes optional (0.6.0)
257
266
  - [x] declared override (`Sawbuck-Allow:` trailer / `--allow`) + `init` build-setup detection, so a build-gated test measures instead of reading inert (0.7.0)
258
267
 
259
- > Status: **published** (npm `promptwheel`, v0.7.0), all core phases built. Lineage: CommandLayer → BlockSpool → PromptWheel (orchestrator, archived) → **PromptWheel (outcome gate)**.
268
+ > Status: **published** (npm `promptwheel`, v0.7.1), all core phases built. Lineage: CommandLayer → BlockSpool → PromptWheel (orchestrator, archived) → **PromptWheel (outcome gate)**.
@@ -970,10 +970,34 @@ function init(argv) {
970
970
  if (envCfg.linkDirs) note += ` · linking ${envCfg.linkDirs.join(', ')}`;
971
971
  writeFileSync(out, JSON.stringify({ repeat: 1, ...envCfg, ...(evaluatorPaths ? { evaluatorPaths } : {}), metrics }, null, 2) + '\n');
972
972
  console.log(`✓ wrote promptwheel.config.json, ${note}`);
973
+ if (!argv.includes('--no-agent-block')) {
974
+ const wrote = writeAgentBlock(repo);
975
+ if (wrote.length) console.log(`✓ wrote the agent guide (when to run the gate) to ${wrote.join(', ')}`);
976
+ }
973
977
  console.log('\n next: promptwheel run --working # gate your uncommitted changes');
974
978
  console.log(' promptwheel init --list # other presets (llm-eval, bundle-size, …)');
975
979
  }
976
980
 
981
+ // Write (or refresh) a short agent-orienting block so the coding AGENT runs the gate itself, not just
982
+ // the human. Goes into AGENTS.md (the cross-tool convention; created if absent) and into CLAUDE.md /
983
+ // .cursorrules when those already exist. Idempotent + safe: only the sentinel-delimited block is
984
+ // touched, everything else in the file is left exactly as-is. `--no-agent-block` skips it.
985
+ function writeAgentBlock(repo) {
986
+ const START = '<!-- promptwheel:gate -->', END = '<!-- /promptwheel:gate -->';
987
+ const block = `${START}\n## PromptWheel gate\n\nAfter a change that should move a measurable metric (tests, lint, coverage, an eval), run \`npx promptwheel run --working\`. Exit 2 (verdict GAMED) means the win came from editing a test, config, or grader, not the source, so revert and earn it in the source. Exit 1 means a guarded metric regressed, revert that too. Never weaken, skip, or delete a test to go green.\n${END}\n`;
988
+ const targets = ['AGENTS.md', ...['CLAUDE.md', '.cursorrules'].filter((f) => existsSync(join(repo, f)))];
989
+ const done = [];
990
+ for (const f of targets) {
991
+ const p = join(repo, f);
992
+ let text = existsSync(p) ? readFileSync(p, 'utf8') : '';
993
+ const s = text.indexOf(START);
994
+ if (s > -1) { const e = text.indexOf(END, s); text = (e > -1 ? text.slice(0, s) + block + text.slice(e + END.length).replace(/^\n+/, '\n') : text.slice(0, s) + block); }
995
+ else { text = (text.trim() ? text.replace(/\n*$/, '\n\n') : '') + block; }
996
+ try { writeFileSync(p, text); done.push(f); } catch { /* best-effort */ }
997
+ }
998
+ return done;
999
+ }
1000
+
977
1001
  // observability: list the EFFECTIVE guardrails (incl. inherited) + each one's flag record
978
1002
  function guards(argv) {
979
1003
  const args = parseArgs(argv);
@@ -1288,7 +1312,7 @@ function main() {
1288
1312
  console.log([
1289
1313
  'PromptWheel, catch your agent cheating. Prove a change moved a real metric (and that the agent earned it, not gamed it). The per-turn reward + source-only audit for AI coding loops.',
1290
1314
  '',
1291
- ' promptwheel init [--preset <name> | --list] write a starter config (OPTIONAL: run works zero-config, auto-detecting your test command)',
1315
+ ' promptwheel init [--preset <name> | --list] [--no-agent-block] write a starter config + an AGENTS.md guide so your agent runs the gate (OPTIONAL: run works zero-config, auto-detecting your test command)',
1292
1316
  ' promptwheel run [--base R] [--head R] [--repeat N] [--json|--markdown]',
1293
1317
  ' catches reward-hacking BY DEFAULT, re-proves each win from SOURCE edits alone (verdict GAMED,',
1294
1318
  ' exit 2, when a metric only moved by editing tests/config/grader). Use --no-detect-gaming for the bare gate.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptwheel",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "A deterministic, zero-LLM check for your coding loop. It proves every change earned its win by re-running the result from the agent's source edits alone, so a green that came from editing the test reads GAMED. Runs in CI or a Claude Code hook, or import { check } in process.",
5
5
  "type": "module",
6
6
  "bin": {