promptwheel 0.3.0 → 0.4.0

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
@@ -76,6 +76,7 @@ npx promptwheel insights
76
76
  # EXPERIMENTAL (Phase 5): the earned playbook + where the next attempt should go
77
77
  npx promptwheel playbook # decayed, evidence-gated claims distilled from the record
78
78
  npx promptwheel suggest # UCB over the lever scores — proven levers vs under-explored arms
79
+ npx promptwheel backfill -n 30 # cold start: seed the record from git history (cohort-tagged, commit types → labels)
79
80
  ```
80
81
 
81
82
  **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.
@@ -365,7 +365,7 @@ function gate(repo, opts) {
365
365
  base: short(repo, base), head: short(repo, head), repeat, mode: opts.working ? 'working' : 'refs',
366
366
  // learning-substrate fields (Phase 5): cohort segments reliability by environment,
367
367
  // label attributes a change-type, subsystems fingerprint WHERE the change landed.
368
- cohort: process.env.CI ? 'ci' : 'local',
368
+ cohort: opts.cohort ?? (process.env.CI ? 'ci' : 'local'),
369
369
  ...(opts.label ? { label: opts.label } : {}),
370
370
  subsystems: subsystemsOf(repo, base, head),
371
371
  verdict, metrics,
@@ -582,6 +582,34 @@ function suggest(argv) {
582
582
  console.log('\n score = lever + exploration bonus — high scores are either proven levers or under-explored arms.\n');
583
583
  }
584
584
 
585
+ // backfill — seed the ledger from git history: replay past commits through the CURRENT
586
+ // metrics (LEARNING.md harvest path 1). Deterministic, no LLM. Rows are cohort-tagged
587
+ // 'backfill' — historical human commits are NOT live agent-loop evidence; the cohort
588
+ // machinery segments them and flags disagreement rather than averaging it away. The
589
+ // conventional-commit type (fix/feat/refactor/…) becomes the change-type label for free.
590
+ function backfill(argv) {
591
+ const args = parseArgs(argv);
592
+ const repo = git(['rev-parse', '--show-toplevel'], process.cwd());
593
+ const listArgs = ['rev-list', '--no-merges'];
594
+ if (args.since) listArgs.push(`${args.since}..HEAD`); else listArgs.push('-n', String(args.n ?? 30), 'HEAD');
595
+ const commits = git(listArgs, repo).split('\n').filter(Boolean).reverse(); // oldest first → ledger stays chronological, decay stays honest
596
+ if (!commits.length) { console.error('no commits to backfill'); process.exit(2); }
597
+ const seen = new Set(readLedger(repo).filter((r) => r.cohort === 'backfill').map((r) => r.head));
598
+ let done = 0, skipped = 0;
599
+ console.log(`\nbackfilling ${commits.length} commits through the current metrics (gaming detection ${args.dgExplicit && args.detectGaming ? 'ON' : 'off — pass --detect-gaming to audit history too'})\n`);
600
+ for (const c of commits) {
601
+ try { git(['rev-parse', `${c}~1`], repo); } catch { skipped++; continue; } // root commit
602
+ if (seen.has(short(repo, c))) { skipped++; continue; } // already recorded
603
+ const subj = git(['log', '-1', '--format=%s', c], repo);
604
+ const type = /^(feat|fix|docs|test|chore|refactor|perf|ci|build|style)\b/i.exec(subj)?.[1]?.toLowerCase();
605
+ const report = gate(repo, { base: `${c}~1`, head: c, repeat: args.repeat, detectGaming: args.dgExplicit ? args.detectGaming : false, label: type, cohort: 'backfill' });
606
+ console.log(` ${report.head} ${report.verdict.toUpperCase().padEnd(6)} ${type ? `#${type}`.padEnd(10) : ''.padEnd(10)} ${subj.slice(0, 56)}`);
607
+ done++;
608
+ }
609
+ console.log(`\n backfilled ${done}${skipped ? ` (skipped ${skipped}: root or already recorded)` : ''} — old commits that no longer build record as unmeasurable, never faked.`);
610
+ console.log(' next: promptwheel playbook · promptwheel suggest\n');
611
+ }
612
+
585
613
  // ---------------------------------------------------------------------------
586
614
  // init — write a starter config so a newcomer isn't staring at a blank page
587
615
  // ---------------------------------------------------------------------------
@@ -745,8 +773,10 @@ function parseArgs(argv) {
745
773
  else if (argv[i] === '--repeat') a.repeat = parseInt(argv[++i], 10);
746
774
  else if (argv[i] === '--working') a.working = true;
747
775
  else if (argv[i] === '--no-record') a.noRecord = true;
748
- else if (argv[i] === '--detect-gaming' || argv[i] === '--antihack') a.detectGaming = true;
749
- else if (argv[i] === '--no-detect-gaming' || argv[i] === '--no-antihack') a.detectGaming = false;
776
+ else if (argv[i] === '--detect-gaming' || argv[i] === '--antihack') { a.detectGaming = true; a.dgExplicit = true; }
777
+ else if (argv[i] === '--no-detect-gaming' || argv[i] === '--no-antihack') { a.detectGaming = false; a.dgExplicit = true; }
778
+ else if (argv[i] === '--since') a.since = argv[++i];
779
+ else if (argv[i] === '-n' || argv[i] === '--limit') a.n = parseInt(argv[++i], 10);
750
780
  else if (argv[i] === '--attempt') a.attempt = argv[++i];
751
781
  else if (argv[i] === '--label') a.label = argv[++i];
752
782
  else if (argv[i] === '--json') a.json = true;
@@ -766,6 +796,7 @@ function main() {
766
796
  else if (cmd === 'insights') insights(rest);
767
797
  else if (cmd === 'playbook') playbook(rest);
768
798
  else if (cmd === 'suggest') suggest(rest);
799
+ else if (cmd === 'backfill') backfill(rest);
769
800
  else if (cmd === 'init') init(rest);
770
801
  else if (cmd === 'guards') guards(rest);
771
802
  else {
@@ -782,6 +813,7 @@ function main() {
782
813
  ' promptwheel insights which metrics actually respond (raw counts)',
783
814
  ' promptwheel playbook [--json] the earned playbook: decayed, evidence-gated claims distilled from the record',
784
815
  ' promptwheel suggest [--json] UCB work-discovery: where the next attempt should go (experimental)',
816
+ ' promptwheel backfill [-n N | --since <ref>] seed the ledger from git history (cohort-tagged; commit types become labels)',
785
817
  ' promptwheel guards show the effective guardrails (incl. inherited) + flag record',
786
818
  '',
787
819
  'Loop it: while promptwheel improve --attempt "$AGENT"; do :; done # stops on plateau/regression',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptwheel",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Catch your agent cheating — a deterministic, zero-LLM CLI that flags when an AI coding agent gamed its own metric (re-proves the win from the agent's source edits alone). Built on an outcome gate; runs in CI or a Claude Code hook.",
5
5
  "type": "module",
6
6
  "bin": {