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 +10 -1
- package/bin/promptwheel.mjs +25 -1
- package/package.json +1 -1
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.
|
|
268
|
+
> Status: **published** (npm `promptwheel`, v0.7.1), all core phases built. Lineage: CommandLayer → BlockSpool → PromptWheel (orchestrator, archived) → **PromptWheel (outcome gate)**.
|
package/bin/promptwheel.mjs
CHANGED
|
@@ -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.
|
|
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": {
|