@webpieces/rules-config 0.4.775 → 0.4.777

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.
Files changed (43) hide show
  1. package/package.json +1 -1
  2. package/src/checklist-config.d.ts +37 -6
  3. package/src/checklist-config.js +55 -19
  4. package/src/checklist-config.js.map +1 -1
  5. package/src/checklist-docs-validator.d.ts +2 -2
  6. package/src/checklist-docs-validator.js +2 -2
  7. package/src/checklist-docs-validator.js.map +1 -1
  8. package/src/checklist-instructions.d.ts +11 -0
  9. package/src/checklist-instructions.js +43 -11
  10. package/src/checklist-instructions.js.map +1 -1
  11. package/src/checklist-validator.d.ts +13 -12
  12. package/src/checklist-validator.js +46 -34
  13. package/src/checklist-validator.js.map +1 -1
  14. package/src/constants.d.ts +6 -0
  15. package/src/constants.js +7 -1
  16. package/src/constants.js.map +1 -1
  17. package/src/index.d.ts +3 -3
  18. package/src/index.js +11 -6
  19. package/src/index.js.map +1 -1
  20. package/src/pr-gate-config.d.ts +9 -1
  21. package/src/pr-gate-config.js +15 -4
  22. package/src/pr-gate-config.js.map +1 -1
  23. package/src/pr-gate-section-validators.d.ts +17 -3
  24. package/src/pr-gate-section-validators.js +91 -30
  25. package/src/pr-gate-section-validators.js.map +1 -1
  26. package/src/retired-config-keys.js +8 -0
  27. package/src/retired-config-keys.js.map +1 -1
  28. package/src/review-json-data.d.ts +3 -2
  29. package/src/review-json-data.js +4 -4
  30. package/src/review-json-data.js.map +1 -1
  31. package/src/review-json.js +3 -3
  32. package/src/review-json.js.map +1 -1
  33. package/src/review-provenance.js +2 -2
  34. package/src/review-provenance.js.map +1 -1
  35. package/src/reviewer-instructions.d.ts +13 -9
  36. package/src/reviewer-instructions.js +27 -16
  37. package/src/reviewer-instructions.js.map +1 -1
  38. package/src/subagent-provenance.d.ts +30 -15
  39. package/src/subagent-provenance.js +100 -50
  40. package/src/subagent-provenance.js.map +1 -1
  41. package/src/validate-config.js +5 -2
  42. package/src/validate-config.js.map +1 -1
  43. package/templates/webpieces.review-checklists.md +50 -27
@@ -1 +1 @@
1
- {"version":3,"file":"retired-config-keys.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/retired-config-keys.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Retired webpieces.config.json keys — the ONE place in the codebase where a dead config key may be named.\n *\n * ## THE POLICY: webpieces.config.json is never released backwards-compatible\n *\n * When a config key moves, is renamed, or is deleted, the loader REJECTS the old shape with an error that\n * names the destination. It does NOT quietly accept both. There is no fallback, no alias table applied\n * before validation, no \"still accepted for back-compat until every consumer migrates\".\n *\n * This is safe *here* in a way it is not for a normal library, and the reason is the consumer: every reader\n * of this file is a coding agent. The config is validated on startup, the agent is handed the exact edit,\n * and it applies it in one pass — so the upgrade is seamless without shipping a compatibility layer. That\n * makes a hard failure strictly cheaper than duality: two accepted shapes means two code paths, two sets of\n * defaults and two sets of error messages to keep honest forever, and the stale shape then lives in\n * consumer configs indefinitely because nothing ever forces the edit.\n *\n * A hard rejection here is always self-recoverable, which is what makes it safe to do:\n * - editing webpieces.config.json is ALWAYS permitted, even while the config is invalid (see the banner\n * in config-file.ts and the guard matrix — a Write/Edit targeting this file is an unconditional PASS),\n * - and `PRUNE_UNKNOWN_COMMAND` is an L0 CURE, so the mechanical cleanup of keys no validator knows runs\n * from inside the block too.\n * So rejecting an old shape can never wedge a repo. \"It would deadlock the consumer\" is not a reason to add\n * a fallback; it is not true.\n *\n * NOT `pnpm install`. This paragraph used to offer it as the escape hatch that \"fixes the far more common\n * cause of a validation failure: an installed validator lagging the config by a release\". It is permitted\n * (installer bypass), but it is not the cure for anything you can read here: the shim's version-drift guard\n * compares the pin against the installed version and denies every tool call BEFORE the validator runs, so a\n * validation error on screen is proof that package.json and node_modules already agree. The banner from this\n * same package now says \"Do NOT run `pnpm install` — it cannot help\", and a docstring teaching the opposite\n * is how that advice leaked back out.\n *\n * ## Adding an entry\n *\n * When you retire a key, add it here and DELETE its read path — do not leave a `??` fallback behind. The\n * `instruction` is the whole product: it is read by an agent that will act on it verbatim, so give the\n * mechanical edit (\"rename X to Y\", \"move A into B\"), not a description of the change. Saying where a thing\n * moved to is exactly the point of this table; it is what makes the fallback unnecessary.\n *\n * retired-config-keys.spec.ts asserts every entry below actually FAILS the load. That spec is the guard: a\n * future fallback that silently swallows a retired key turns it red.\n */\n\nimport { RETIRED_KEY_MARKER } from './config-error-banner';\nimport { PRUNE_UNKNOWN_COMMAND } from './constants';\n\n// A retired key is matched one of two ways, because config keys live at two very different levels.\n// RULE — a rule/guard NAME, which may sit under either `rules` or `hookGuards`, so it is matched by bare\n// name rather than by a fixed path.\n// KEY — a plain key inside a known section, matched by name within that section.\nexport const RETIRED_SCOPE_RULE = 'rule';\nexport const RETIRED_SCOPE_KEY = 'key';\n\n/** One retired config key and the mechanical edit that replaces it. Data-only (per CLAUDE.md). */\nexport class RetiredConfigKey {\n scope: string;\n // The key exactly as it appears in webpieces.config.json.\n key: string;\n // Where the value goes now. Empty string when the key is deleted outright with no replacement.\n movedTo: string;\n // The imperative fix, written for the agent that will apply it.\n instruction: string;\n // Bracketed label leading the error, matching the `[rule-name]` / `[section]` convention in this package.\n label: string;\n /**\n * True when DELETING the key from webpieces.config.json is the entire edit — nothing in THIS file\n * replaces it, so `PRUNE_UNKNOWN_COMMAND` may strip it mechanically. `whole-repo-build-guard` is the\n * worked example: its switch moved OUT of the repo config into `~/.webpieces/config.json`, so the\n * value has nowhere to go here.\n *\n * False for a rename or an in-file move: deleting those would DISCARD a value the reader still needs,\n * so they keep their migration instruction and the pruner leaves them alone. Required, not defaulted —\n * a defaulted `false` would let a future deletion-only retirement silently opt out of the mechanical\n * cure and land back in \"the reader decides while every Bash call is blocked\".\n */\n prunable: boolean;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(scope: string, key: string, movedTo: string, instruction: string, label: string, prunable: boolean) {\n this.scope = scope;\n this.key = key;\n this.movedTo = movedTo;\n this.instruction = instruction;\n this.label = label;\n this.prunable = prunable;\n }\n}\n\n/**\n * Every retired key. Keep the newest at the bottom with the release that retired it, so the list reads as a\n * changelog an agent can walk when a config is several versions behind.\n */\nexport const RETIRED_CONFIG_KEYS: readonly RetiredConfigKey[] = [\n // --- Guard renames. Previously applied SILENTLY before validation (a DEPRECATED_RULE_ALIASES table in\n // load-config.ts rewrote the key so no validator ever saw it). That hid the rename from the config file\n // forever: the old name kept working, so no consumer ever updated, and the alias table could never be\n // deleted. Now each is a hard error with the new name.\n //\n // These three RE-POINTED when hookGuards collapsed to one key per policy. Their old destinations\n // (`pr-merge-guard`, `pr-creation-or-push-guard`, `read-stale-guard`) are themselves retired now,\n // and a retirement whose `movedTo` names a dead key teaches the removed API — the same defect one\n // level out. Each therefore names the POLICY key it lands on today, in one hop.\n new RetiredConfigKey(\n RETIRED_SCOPE_RULE, 'pr-merge-cleanup', 'pr-lifecycle-guard',\n 'Rename the key to \"pr-lifecycle-guard\" (merging its value into that entry if you already have ' +\n 'one — the four PR/merge guards share one key now). Its mode and escape hatches carry over.',\n '[pr-merge-cleanup]', false,\n ),\n new RetiredConfigKey(\n RETIRED_SCOPE_RULE, 'pr-creation-guard', 'pr-lifecycle-guard',\n 'Rename the key to \"pr-lifecycle-guard\" (merging its value into that entry if you already have ' +\n 'one — the four PR/merge guards share one key now). Its mode and escape hatches carry over; the ' +\n 'old \"upsertPrCommand\" field does NOT — that string lives only in commands.guardHints.prCreationOrPush.',\n '[pr-creation-guard]', false,\n ),\n new RetiredConfigKey(\n RETIRED_SCOPE_RULE, 'main-stale-guard', 'branch-state-guard',\n 'Rename the key to \"branch-state-guard\" (merging its value into that entry if you already have ' +\n 'one — the four branch-state guards share one key now). Its mode and escape hatches carry over. ' +\n 'Note the widened scope: that key arms the Write, Read AND Bash halves of the branch-state ' +\n 'policy, not just the Read block this key used to name.',\n '[main-stale-guard]', false,\n ),\n\n // --- The two flat guard-hint strings, superseded by commands.guardHints so that every guard-facing\n // command string sits in one named sub-object instead of loose beside the gate config.\n new RetiredConfigKey(\n RETIRED_SCOPE_KEY, 'upsertPr', 'commands.guardHints.prCreationOrPush',\n 'Move the value to \"guardHints\": { \"prCreationOrPush\": <value> } inside the same \"commands\" ' +\n 'section, then delete \"upsertPr\".',\n '[commands]', false,\n ),\n new RetiredConfigKey(\n RETIRED_SCOPE_KEY, 'mergeComplete', 'commands.guardHints.mergeInProgress',\n 'Move the value to \"guardHints\": { \"mergeInProgress\": <value> } inside the same \"commands\" ' +\n 'section, then delete \"mergeComplete\".',\n '[commands]', false,\n ),\n\n // --- The two-list excludePaths object. The split never earned its keep (every consumer set both lists\n // to the same value), and the one case that would need them to differ is served better by a rule's own\n // `excludePaths`. Retired as a SHAPE: `rules` is the entry validateExcludePaths reports for either key.\n new RetiredConfigKey(\n RETIRED_SCOPE_KEY, 'rules', 'excludePaths (one flat array)',\n 'Replace the whole { \"rules\": [...], \"guards\": [...] } object with ONE array holding the union of ' +\n 'both lists, de-duplicated — e.g. \"excludePaths\": [\"repositories/**\"]. A path is governed by ' +\n 'webpieces or it is not, so there is no longer a per-engine split. To exclude a path from one rule ' +\n 'only, use that rule\\'s own \"excludePaths\" inside its config block instead.',\n '[excludePaths]', false,\n ),\n\n // --- whole-repo-build-guard, retired as a REPO-CONFIG key one release after it was added. It shipped\n // as a conventional validated guard (mode ON by default, entry REQUIRED under hookGuards), so every\n // consumer that upgraded hit fault Y — every Bash call blocked — for a feature nobody had opted into.\n // The REQUIRED KEY was the fault, not the default: the failure was at config LOAD, before any command\n // was judged. The switch now lives ONLY in the optional machine-local ~/.webpieces/config.json, where\n // nothing has to be added to be in the default state — and the default is OFF, as every experimental\n // flag's is, so deleting this entry loses nothing.\n new RetiredConfigKey(\n RETIRED_SCOPE_RULE, 'whole-repo-build-guard', '~/.webpieces/config.json → experimental.whole-repo-build-guard',\n 'DELETE this entry from webpieces.config.json — no repo config key controls this guard any more. ' +\n 'The guard is OFF by default for everyone, with no file and no key required. To turn it ON for ' +\n 'YOUR machine only, put {\"experimental\": {\"whole-repo-build-guard\": true}} in ' +\n '~/.webpieces/config.json — that file is optional and is tracked by no repo.',\n '[whole-repo-build-guard]', true,\n ),\n\n // --- THE 9 → 3 COLLAPSE of `hookGuards`: one key per POLICY, not one per implementation CLASS.\n //\n // Eight class-named keys become two policy keys. The CLASSES are untouched — `feature-branch-guard`\n // is still the rule name in every decision-log line and every deny report — so nothing an operator\n // greps for moved. What moved is the SWITCH, because nine keys let a consumer configure HALF a\n // policy: `read-stale-guard: OFF` beside `merged-branch-bash-guard: ON` is \"read the file, yes;\n // `cat` the same file, no\", which nobody chose and the config made reachable.\n //\n // EVERY ONE OF THESE IS `prunable: false`, and that is not bookkeeping. `PRUNABLE_SECTIONS` in\n // ConfigPruner includes `hookGuards`, and the validation banner actively RECOMMENDS running the\n // pruner. A `prunable: true` here would mean the recommended cure silently DELETES four configured\n // guards with no destination named — a config loss, which is worse than a block. False keeps the\n // migration instruction and makes the pruner return null for these keys.\n //\n // 4 → 1 is legal for this table (nothing requires `movedTo` to be unique), but it is NOT legal for\n // a naive 1:1 migrator: see migrateRetiredRuleNames in ai-hook-rules' setup.ts, which unions the\n // four old entries into the destination and then fills any missing required field, rather than\n // renaming the first and deleting the rest.\n ...branchStateRetirements(),\n ...prLifecycleRetirements(),\n];\n\n// The four branch-state classes. Split into a helper purely to keep the table above readable; the\n// instruction is per-key because the fields that carry over differ (only feature-branch-guard had\n// `branchNamingConvention`).\n// webpieces-disable no-function-outside-class -- table data for RETIRED_CONFIG_KEYS, beside the array it feeds\nfunction branchStateRetirements(): RetiredConfigKey[] {\n const merged = 'MERGE it into ONE \"branch-state-guard\" entry under \"hookGuards\"';\n const shared =\n `${merged}. All four of feature-branch-guard, read-stale-guard, stale-main-bash-guard and ` +\n 'merged-branch-bash-guard now read that single entry — they are one policy (\"may I work here, ' +\n 'and is what I read current?\") implemented by four classes, and configuring them separately let ' +\n 'you switch on half of it. Keep \"mode\", \"turnOffRuleUntilEpoch\" and \"turnOffRuleWhileOnBranch\"; ' +\n 'the ONE \"hangTimeoutMinutes\" survives (there was only ever one refresher and one cache, so at ' +\n 'most one of the four values could ever take effect); \"branchNamingConvention\" survives from ' +\n 'feature-branch-guard. If the four disagreed on \"mode\", pick the one you meant — \"ON\" arms the ' +\n 'whole policy including the Read block. Then DELETE this key.';\n return [\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'feature-branch-guard', 'branch-state-guard', shared, '[feature-branch-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'read-stale-guard', 'branch-state-guard', shared, '[read-stale-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'stale-main-bash-guard', 'branch-state-guard', shared, '[stale-main-bash-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'merged-branch-bash-guard', 'branch-state-guard', shared, '[merged-branch-bash-guard]', false),\n ];\n}\n\n// The four PR-lifecycle classes.\n// webpieces-disable no-function-outside-class -- table data for RETIRED_CONFIG_KEYS, beside the array it feeds\nfunction prLifecycleRetirements(): RetiredConfigKey[] {\n const shared =\n 'MERGE it into ONE \"pr-lifecycle-guard\" entry under \"hookGuards\". All four of ' +\n 'pr-creation-or-push-guard, merge-in-progress-guard, pr-merge-guard and ' +\n 'redirect-how-to-merge-main now read that single entry — they are one policy (\"do PRs and ' +\n 'merges go through the gated flow?\"). Keep \"mode\", \"turnOffRuleUntilEpoch\" and ' +\n '\"turnOffRuleWhileOnBranch\". Know what \"mode\": \"OFF\" now means: it releases the ' +\n 'unvalidated-merge gate as well as the PR/push blocks, because merge-in-progress-guard is under ' +\n 'this key too. The per-guard \"upsertPrCommand\" / \"mergeCompleteCommand\" fields are gone — those ' +\n 'strings live ONLY in commands.guardHints.prCreationOrPush / .mergeInProgress. Then DELETE this key.';\n return [\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'pr-creation-or-push-guard', 'pr-lifecycle-guard', shared, '[pr-creation-or-push-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'merge-in-progress-guard', 'pr-lifecycle-guard', shared, '[merge-in-progress-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'pr-merge-guard', 'pr-lifecycle-guard', shared, '[pr-merge-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'redirect-how-to-merge-main', 'pr-lifecycle-guard', shared, '[redirect-how-to-merge-main]', false),\n ];\n}\n\n/**\n * The shared message. Leads with the retirement, then the destination, then the edit — an agent reading this\n * should not have to infer anything.\n *\n * RETIRED_KEY_MARKER is the phrase the banner classifier keys off to call this error DEFINITIVE (no\n * install can revive a key this table names), so the marker is IMPORTED rather than re-typed here.\n */\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this package\nexport function retiredKeyError(entry: RetiredConfigKey): string {\n const destination = entry.movedTo === ''\n ? 'It was removed with no replacement.'\n : `It moved to \"${entry.movedTo}\".`;\n // Deletion-only retirements get the mechanical cure named right here, so the reader never has to\n // decide whether removing a key is safe while the guard is denying every Bash call.\n const mechanical = entry.prunable\n ? ` Deleting it is the WHOLE fix — \\`${PRUNE_UNKNOWN_COMMAND}\\` does it for you.`\n : '';\n return `${entry.label} \"${entry.key}\" ${RETIRED_KEY_MARKER}. ${destination} ${entry.instruction}${mechanical}`;\n}\n\n/**\n * The retired entry for a rule/guard NAME, or null. Callers must consult this BEFORE reporting a name as an\n * unknown rule: the generic unknown-rule message can only say \"delete it\", while this table knows WHERE the\n * setting went — a rename that must carry its value over, or a move to `~/.webpieces/config.json`. The\n * destination is the whole product, and a bare \"delete it\" would throw it away.\n */\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this package\nexport function retiredRuleFor(ruleName: string): RetiredConfigKey | null {\n return RETIRED_CONFIG_KEYS.find(e => e.scope === RETIRED_SCOPE_RULE && e.key === ruleName) ?? null;\n}\n\n/**\n * Whether `key` is retired within `label`'s section. Unknown-key rejection consults this so a retired key\n * gets its migration message instead of a generic \"unknown key, delete it\" — the migration instruction is\n * the entire reason this table exists.\n */\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this package\nexport function isRetiredKey(key: string, label: string): boolean {\n return RETIRED_CONFIG_KEYS.some(e => e.scope === RETIRED_SCOPE_KEY && e.label === label && e.key === key);\n}\n\n/**\n * The retired entry for `key` within `label`'s section. For a retirement that is a SHAPE rather than a\n * single key (the excludePaths object), the validator looks the entry up directly so one table row remains\n * the single source of the message.\n */\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this package\nexport function retiredEntry(key: string, label: string): RetiredConfigKey | null {\n return RETIRED_CONFIG_KEYS.find(e => e.scope === RETIRED_SCOPE_KEY && e.label === label && e.key === key) ?? null;\n}\n\n/** Errors for every retired plain key present in `section`. `label` scopes the lookup to that section. */\n// webpieces-disable no-any-unknown -- `section` is opaque consumer JSON; only key PRESENCE is read\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this package\nexport function retiredKeyErrorsIn(section: Record<string, unknown>, label: string): string[] {\n return RETIRED_CONFIG_KEYS\n .filter(e => e.scope === RETIRED_SCOPE_KEY && e.label === label && e.key in section)\n .map(e => retiredKeyError(e));\n}\n"]}
1
+ {"version":3,"file":"retired-config-keys.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/retired-config-keys.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Retired webpieces.config.json keys — the ONE place in the codebase where a dead config key may be named.\n *\n * ## THE POLICY: webpieces.config.json is never released backwards-compatible\n *\n * When a config key moves, is renamed, or is deleted, the loader REJECTS the old shape with an error that\n * names the destination. It does NOT quietly accept both. There is no fallback, no alias table applied\n * before validation, no \"still accepted for back-compat until every consumer migrates\".\n *\n * This is safe *here* in a way it is not for a normal library, and the reason is the consumer: every reader\n * of this file is a coding agent. The config is validated on startup, the agent is handed the exact edit,\n * and it applies it in one pass — so the upgrade is seamless without shipping a compatibility layer. That\n * makes a hard failure strictly cheaper than duality: two accepted shapes means two code paths, two sets of\n * defaults and two sets of error messages to keep honest forever, and the stale shape then lives in\n * consumer configs indefinitely because nothing ever forces the edit.\n *\n * A hard rejection here is always self-recoverable, which is what makes it safe to do:\n * - editing webpieces.config.json is ALWAYS permitted, even while the config is invalid (see the banner\n * in config-file.ts and the guard matrix — a Write/Edit targeting this file is an unconditional PASS),\n * - and `PRUNE_UNKNOWN_COMMAND` is an L0 CURE, so the mechanical cleanup of keys no validator knows runs\n * from inside the block too.\n * So rejecting an old shape can never wedge a repo. \"It would deadlock the consumer\" is not a reason to add\n * a fallback; it is not true.\n *\n * NOT `pnpm install`. This paragraph used to offer it as the escape hatch that \"fixes the far more common\n * cause of a validation failure: an installed validator lagging the config by a release\". It is permitted\n * (installer bypass), but it is not the cure for anything you can read here: the shim's version-drift guard\n * compares the pin against the installed version and denies every tool call BEFORE the validator runs, so a\n * validation error on screen is proof that package.json and node_modules already agree. The banner from this\n * same package now says \"Do NOT run `pnpm install` — it cannot help\", and a docstring teaching the opposite\n * is how that advice leaked back out.\n *\n * ## Adding an entry\n *\n * When you retire a key, add it here and DELETE its read path — do not leave a `??` fallback behind. The\n * `instruction` is the whole product: it is read by an agent that will act on it verbatim, so give the\n * mechanical edit (\"rename X to Y\", \"move A into B\"), not a description of the change. Saying where a thing\n * moved to is exactly the point of this table; it is what makes the fallback unnecessary.\n *\n * retired-config-keys.spec.ts asserts every entry below actually FAILS the load. That spec is the guard: a\n * future fallback that silently swallows a retired key turns it red.\n */\n\nimport { RETIRED_KEY_MARKER } from './config-error-banner';\nimport { PRUNE_UNKNOWN_COMMAND } from './constants';\n\n// A retired key is matched one of two ways, because config keys live at two very different levels.\n// RULE — a rule/guard NAME, which may sit under either `rules` or `hookGuards`, so it is matched by bare\n// name rather than by a fixed path.\n// KEY — a plain key inside a known section, matched by name within that section.\nexport const RETIRED_SCOPE_RULE = 'rule';\nexport const RETIRED_SCOPE_KEY = 'key';\n\n/** One retired config key and the mechanical edit that replaces it. Data-only (per CLAUDE.md). */\nexport class RetiredConfigKey {\n scope: string;\n // The key exactly as it appears in webpieces.config.json.\n key: string;\n // Where the value goes now. Empty string when the key is deleted outright with no replacement.\n movedTo: string;\n // The imperative fix, written for the agent that will apply it.\n instruction: string;\n // Bracketed label leading the error, matching the `[rule-name]` / `[section]` convention in this package.\n label: string;\n /**\n * True when DELETING the key from webpieces.config.json is the entire edit — nothing in THIS file\n * replaces it, so `PRUNE_UNKNOWN_COMMAND` may strip it mechanically. `whole-repo-build-guard` is the\n * worked example: its switch moved OUT of the repo config into `~/.webpieces/config.json`, so the\n * value has nowhere to go here.\n *\n * False for a rename or an in-file move: deleting those would DISCARD a value the reader still needs,\n * so they keep their migration instruction and the pruner leaves them alone. Required, not defaulted —\n * a defaulted `false` would let a future deletion-only retirement silently opt out of the mechanical\n * cure and land back in \"the reader decides while every Bash call is blocked\".\n */\n prunable: boolean;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(scope: string, key: string, movedTo: string, instruction: string, label: string, prunable: boolean) {\n this.scope = scope;\n this.key = key;\n this.movedTo = movedTo;\n this.instruction = instruction;\n this.label = label;\n this.prunable = prunable;\n }\n}\n\n/**\n * Every retired key. Keep the newest at the bottom with the release that retired it, so the list reads as a\n * changelog an agent can walk when a config is several versions behind.\n */\nexport const RETIRED_CONFIG_KEYS: readonly RetiredConfigKey[] = [\n // --- Guard renames. Previously applied SILENTLY before validation (a DEPRECATED_RULE_ALIASES table in\n // load-config.ts rewrote the key so no validator ever saw it). That hid the rename from the config file\n // forever: the old name kept working, so no consumer ever updated, and the alias table could never be\n // deleted. Now each is a hard error with the new name.\n //\n // These three RE-POINTED when hookGuards collapsed to one key per policy. Their old destinations\n // (`pr-merge-guard`, `pr-creation-or-push-guard`, `read-stale-guard`) are themselves retired now,\n // and a retirement whose `movedTo` names a dead key teaches the removed API — the same defect one\n // level out. Each therefore names the POLICY key it lands on today, in one hop.\n new RetiredConfigKey(\n RETIRED_SCOPE_RULE, 'pr-merge-cleanup', 'pr-lifecycle-guard',\n 'Rename the key to \"pr-lifecycle-guard\" (merging its value into that entry if you already have ' +\n 'one — the four PR/merge guards share one key now). Its mode and escape hatches carry over.',\n '[pr-merge-cleanup]', false,\n ),\n new RetiredConfigKey(\n RETIRED_SCOPE_RULE, 'pr-creation-guard', 'pr-lifecycle-guard',\n 'Rename the key to \"pr-lifecycle-guard\" (merging its value into that entry if you already have ' +\n 'one — the four PR/merge guards share one key now). Its mode and escape hatches carry over; the ' +\n 'old \"upsertPrCommand\" field does NOT — that string lives only in commands.guardHints.prCreationOrPush.',\n '[pr-creation-guard]', false,\n ),\n new RetiredConfigKey(\n RETIRED_SCOPE_RULE, 'main-stale-guard', 'branch-state-guard',\n 'Rename the key to \"branch-state-guard\" (merging its value into that entry if you already have ' +\n 'one — the four branch-state guards share one key now). Its mode and escape hatches carry over. ' +\n 'Note the widened scope: that key arms the Write, Read AND Bash halves of the branch-state ' +\n 'policy, not just the Read block this key used to name.',\n '[main-stale-guard]', false,\n ),\n\n // --- The two flat guard-hint strings, superseded by commands.guardHints so that every guard-facing\n // command string sits in one named sub-object instead of loose beside the gate config.\n new RetiredConfigKey(\n RETIRED_SCOPE_KEY, 'upsertPr', 'commands.guardHints.prCreationOrPush',\n 'Move the value to \"guardHints\": { \"prCreationOrPush\": <value> } inside the same \"commands\" ' +\n 'section, then delete \"upsertPr\".',\n '[commands]', false,\n ),\n new RetiredConfigKey(\n RETIRED_SCOPE_KEY, 'mergeComplete', 'commands.guardHints.mergeInProgress',\n 'Move the value to \"guardHints\": { \"mergeInProgress\": <value> } inside the same \"commands\" ' +\n 'section, then delete \"mergeComplete\".',\n '[commands]', false,\n ),\n\n // --- The two-list excludePaths object. The split never earned its keep (every consumer set both lists\n // to the same value), and the one case that would need them to differ is served better by a rule's own\n // `excludePaths`. Retired as a SHAPE: `rules` is the entry validateExcludePaths reports for either key.\n new RetiredConfigKey(\n RETIRED_SCOPE_KEY, 'rules', 'excludePaths (one flat array)',\n 'Replace the whole { \"rules\": [...], \"guards\": [...] } object with ONE array holding the union of ' +\n 'both lists, de-duplicated — e.g. \"excludePaths\": [\"repositories/**\"]. A path is governed by ' +\n 'webpieces or it is not, so there is no longer a per-engine split. To exclude a path from one rule ' +\n 'only, use that rule\\'s own \"excludePaths\" inside its config block instead.',\n '[excludePaths]', false,\n ),\n\n // --- whole-repo-build-guard, retired as a REPO-CONFIG key one release after it was added. It shipped\n // as a conventional validated guard (mode ON by default, entry REQUIRED under hookGuards), so every\n // consumer that upgraded hit fault Y — every Bash call blocked — for a feature nobody had opted into.\n // The REQUIRED KEY was the fault, not the default: the failure was at config LOAD, before any command\n // was judged. The switch now lives ONLY in the optional machine-local ~/.webpieces/config.json, where\n // nothing has to be added to be in the default state — and the default is OFF, as every experimental\n // flag's is, so deleting this entry loses nothing.\n new RetiredConfigKey(\n RETIRED_SCOPE_RULE, 'whole-repo-build-guard', '~/.webpieces/config.json → experimental.whole-repo-build-guard',\n 'DELETE this entry from webpieces.config.json — no repo config key controls this guard any more. ' +\n 'The guard is OFF by default for everyone, with no file and no key required. To turn it ON for ' +\n 'YOUR machine only, put {\"experimental\": {\"whole-repo-build-guard\": true}} in ' +\n '~/.webpieces/config.json — that file is optional and is tracked by no repo.',\n '[whole-repo-build-guard]', true,\n ),\n\n // --- THE 9 → 3 COLLAPSE of `hookGuards`: one key per POLICY, not one per implementation CLASS.\n //\n // Eight class-named keys become two policy keys. The CLASSES are untouched — `feature-branch-guard`\n // is still the rule name in every decision-log line and every deny report — so nothing an operator\n // greps for moved. What moved is the SWITCH, because nine keys let a consumer configure HALF a\n // policy: `read-stale-guard: OFF` beside `merged-branch-bash-guard: ON` is \"read the file, yes;\n // `cat` the same file, no\", which nobody chose and the config made reachable.\n //\n // EVERY ONE OF THESE IS `prunable: false`, and that is not bookkeeping. `PRUNABLE_SECTIONS` in\n // ConfigPruner includes `hookGuards`, and the validation banner actively RECOMMENDS running the\n // pruner. A `prunable: true` here would mean the recommended cure silently DELETES four configured\n // guards with no destination named — a config loss, which is worse than a block. False keeps the\n // migration instruction and makes the pruner return null for these keys.\n //\n // 4 → 1 is legal for this table (nothing requires `movedTo` to be unique), but it is NOT legal for\n // a naive 1:1 migrator: see migrateRetiredRuleNames in ai-hook-rules' setup.ts, which unions the\n // four old entries into the destination and then fills any missing required field, rather than\n // renaming the first and deleting the rest.\n ...branchStateRetirements(),\n ...prLifecycleRetirements(),\n\n // --- The per-checklist reviewer agent (issue #938). A checklist used to name its own agent type in\n // `subagent`, which also served as its id; every checklist is now reviewed by the ONE agent type that\n // `commands.pr-gate.reviewerAgentName` names, so the key that is left is only the checklist's NAME.\n // A rename, so `prunable: false` — deleting it would discard the id every verdict file is keyed by.\n new RetiredConfigKey(\n RETIRED_SCOPE_KEY, 'subagent', 'id',\n 'Rename \"subagent\" to \"id\" in EVERY commands.pr-gate.checklists entry, keeping its value (it still ' +\n 'names the checklist and keys review-<id>.json). Checklists no longer choose an agent type: every one ' +\n 'is reviewed by the agent commands.pr-gate.reviewerAgentName names — add ' +\n '\"reviewerAgentName\": \"webpieces-reviewer\" there if it is missing.',\n '[pr-gate.checklists]', false,\n ),\n];\n\n// The four branch-state classes. Split into a helper purely to keep the table above readable; the\n// instruction is per-key because the fields that carry over differ (only feature-branch-guard had\n// `branchNamingConvention`).\n// webpieces-disable no-function-outside-class -- table data for RETIRED_CONFIG_KEYS, beside the array it feeds\nfunction branchStateRetirements(): RetiredConfigKey[] {\n const merged = 'MERGE it into ONE \"branch-state-guard\" entry under \"hookGuards\"';\n const shared =\n `${merged}. All four of feature-branch-guard, read-stale-guard, stale-main-bash-guard and ` +\n 'merged-branch-bash-guard now read that single entry — they are one policy (\"may I work here, ' +\n 'and is what I read current?\") implemented by four classes, and configuring them separately let ' +\n 'you switch on half of it. Keep \"mode\", \"turnOffRuleUntilEpoch\" and \"turnOffRuleWhileOnBranch\"; ' +\n 'the ONE \"hangTimeoutMinutes\" survives (there was only ever one refresher and one cache, so at ' +\n 'most one of the four values could ever take effect); \"branchNamingConvention\" survives from ' +\n 'feature-branch-guard. If the four disagreed on \"mode\", pick the one you meant — \"ON\" arms the ' +\n 'whole policy including the Read block. Then DELETE this key.';\n return [\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'feature-branch-guard', 'branch-state-guard', shared, '[feature-branch-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'read-stale-guard', 'branch-state-guard', shared, '[read-stale-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'stale-main-bash-guard', 'branch-state-guard', shared, '[stale-main-bash-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'merged-branch-bash-guard', 'branch-state-guard', shared, '[merged-branch-bash-guard]', false),\n ];\n}\n\n// The four PR-lifecycle classes.\n// webpieces-disable no-function-outside-class -- table data for RETIRED_CONFIG_KEYS, beside the array it feeds\nfunction prLifecycleRetirements(): RetiredConfigKey[] {\n const shared =\n 'MERGE it into ONE \"pr-lifecycle-guard\" entry under \"hookGuards\". All four of ' +\n 'pr-creation-or-push-guard, merge-in-progress-guard, pr-merge-guard and ' +\n 'redirect-how-to-merge-main now read that single entry — they are one policy (\"do PRs and ' +\n 'merges go through the gated flow?\"). Keep \"mode\", \"turnOffRuleUntilEpoch\" and ' +\n '\"turnOffRuleWhileOnBranch\". Know what \"mode\": \"OFF\" now means: it releases the ' +\n 'unvalidated-merge gate as well as the PR/push blocks, because merge-in-progress-guard is under ' +\n 'this key too. The per-guard \"upsertPrCommand\" / \"mergeCompleteCommand\" fields are gone — those ' +\n 'strings live ONLY in commands.guardHints.prCreationOrPush / .mergeInProgress. Then DELETE this key.';\n return [\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'pr-creation-or-push-guard', 'pr-lifecycle-guard', shared, '[pr-creation-or-push-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'merge-in-progress-guard', 'pr-lifecycle-guard', shared, '[merge-in-progress-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'pr-merge-guard', 'pr-lifecycle-guard', shared, '[pr-merge-guard]', false),\n new RetiredConfigKey(RETIRED_SCOPE_RULE, 'redirect-how-to-merge-main', 'pr-lifecycle-guard', shared, '[redirect-how-to-merge-main]', false),\n ];\n}\n\n/**\n * The shared message. Leads with the retirement, then the destination, then the edit — an agent reading this\n * should not have to infer anything.\n *\n * RETIRED_KEY_MARKER is the phrase the banner classifier keys off to call this error DEFINITIVE (no\n * install can revive a key this table names), so the marker is IMPORTED rather than re-typed here.\n */\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this package\nexport function retiredKeyError(entry: RetiredConfigKey): string {\n const destination = entry.movedTo === ''\n ? 'It was removed with no replacement.'\n : `It moved to \"${entry.movedTo}\".`;\n // Deletion-only retirements get the mechanical cure named right here, so the reader never has to\n // decide whether removing a key is safe while the guard is denying every Bash call.\n const mechanical = entry.prunable\n ? ` Deleting it is the WHOLE fix — \\`${PRUNE_UNKNOWN_COMMAND}\\` does it for you.`\n : '';\n return `${entry.label} \"${entry.key}\" ${RETIRED_KEY_MARKER}. ${destination} ${entry.instruction}${mechanical}`;\n}\n\n/**\n * The retired entry for a rule/guard NAME, or null. Callers must consult this BEFORE reporting a name as an\n * unknown rule: the generic unknown-rule message can only say \"delete it\", while this table knows WHERE the\n * setting went — a rename that must carry its value over, or a move to `~/.webpieces/config.json`. The\n * destination is the whole product, and a bare \"delete it\" would throw it away.\n */\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this package\nexport function retiredRuleFor(ruleName: string): RetiredConfigKey | null {\n return RETIRED_CONFIG_KEYS.find(e => e.scope === RETIRED_SCOPE_RULE && e.key === ruleName) ?? null;\n}\n\n/**\n * Whether `key` is retired within `label`'s section. Unknown-key rejection consults this so a retired key\n * gets its migration message instead of a generic \"unknown key, delete it\" — the migration instruction is\n * the entire reason this table exists.\n */\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this package\nexport function isRetiredKey(key: string, label: string): boolean {\n return RETIRED_CONFIG_KEYS.some(e => e.scope === RETIRED_SCOPE_KEY && e.label === label && e.key === key);\n}\n\n/**\n * The retired entry for `key` within `label`'s section. For a retirement that is a SHAPE rather than a\n * single key (the excludePaths object), the validator looks the entry up directly so one table row remains\n * the single source of the message.\n */\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this package\nexport function retiredEntry(key: string, label: string): RetiredConfigKey | null {\n return RETIRED_CONFIG_KEYS.find(e => e.scope === RETIRED_SCOPE_KEY && e.label === label && e.key === key) ?? null;\n}\n\n/** Errors for every retired plain key present in `section`. `label` scopes the lookup to that section. */\n// webpieces-disable no-any-unknown -- `section` is opaque consumer JSON; only key PRESENCE is read\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this package\nexport function retiredKeyErrorsIn(section: Record<string, unknown>, label: string): string[] {\n return RETIRED_CONFIG_KEYS\n .filter(e => e.scope === RETIRED_SCOPE_KEY && e.label === label && e.key in section)\n .map(e => retiredKeyError(e));\n}\n"]}
@@ -6,6 +6,7 @@
6
6
  * kind (data vs behaviour): review-json.ts re-exports every name below, so `from './review-json'` and
7
7
  * `from '@webpieces/rules-config'` keep resolving exactly as before and no consumer changes.
8
8
  */
9
+ import { ReviewerAgentPolicy } from './checklist-config';
9
10
  import { ChecklistOverride } from './checklist-override';
10
11
  export declare const VERDICT_GREEN = "green";
11
12
  export declare const VERDICT_YELLOW = "yellow";
@@ -34,12 +35,12 @@ export declare class ChecklistResult {
34
35
  */
35
36
  export declare class RequiredChecklist {
36
37
  id: string;
37
- subagent: string;
38
+ reviewer: ReviewerAgentPolicy;
38
39
  doc: string;
39
40
  matchedFiles: string[];
40
41
  matchedPatterns: string[];
41
42
  required: boolean;
42
- constructor(id: string, subagent: string, doc: string, matchedFiles: string[], matchedPatterns?: string[], required?: boolean);
43
+ constructor(id: string, reviewer: ReviewerAgentPolicy, doc: string, matchedFiles: string[], matchedPatterns?: string[], required?: boolean);
43
44
  }
44
45
  /**
45
46
  * The per-PR facts every reviewer subagent needs GIVEN to it, alongside its own checklist: the exact base
@@ -72,8 +72,8 @@ exports.ChecklistResult = ChecklistResult;
72
72
  * Whether the reviewer must actually run is the {@link RequiredChecklist.required} field below.
73
73
  */
74
74
  class RequiredChecklist {
75
- id; // = subagent name; keys review-<id>.json
76
- subagent; // reviewer agent that must run (agentType the harness stamps)
75
+ id; // the checklist's name; keys review-<id>.json and its instructions file
76
+ reviewer; // the agent type to spawn (agentType the harness stamps) + the round cap
77
77
  doc; // REPO-RELATIVE guidance doc the reviewer reads ('' → it just reads the diff)
78
78
  matchedFiles; // the changed files that matched it (for the dashboard + hint)
79
79
  // Which of the checklist's OWN globs actually fired. Printed so a reviewer can judge how coarse the
@@ -85,12 +85,12 @@ class RequiredChecklist {
85
85
  // and the set that is reported cannot disagree about which of the two a checklist is.
86
86
  required;
87
87
  // eslint-disable-next-line @typescript-eslint/max-params
88
- constructor(id, subagent, doc, matchedFiles, matchedPatterns = [],
88
+ constructor(id, reviewer, doc, matchedFiles, matchedPatterns = [],
89
89
  // Defaulted to the BLOCKING value so any construction that forgets it fails closed — a test or a
90
90
  // future call site that silently produced an optional checklist would be a hole in the gate.
91
91
  required = true) {
92
92
  this.id = id;
93
- this.subagent = subagent;
93
+ this.reviewer = reviewer;
94
94
  this.doc = doc;
95
95
  this.matchedFiles = matchedFiles;
96
96
  this.matchedPatterns = matchedPatterns;
@@ -1 +1 @@
1
- {"version":3,"file":"review-json-data.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/review-json-data.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The DATA-ONLY classes and status constants of the PR review system — the verdict a reviewer writes, the\n * checklist that demanded it, the resolved outcome, and the diff context handed to reviewers.\n *\n * Split out of review-json.ts, which holds the SERVICE that reads and writes them. The split is purely by\n * kind (data vs behaviour): review-json.ts re-exports every name below, so `from './review-json'` and\n * `from '@webpieces/rules-config'` keep resolving exactly as before and no consumer changes.\n */\n\nimport { ChecklistOverride } from './checklist-override';\n\n// The three colors a reviewer subagent may report in `review-<id>.json`. A TRI-state, not a boolean,\n// because the boolean it replaced gave a reviewer no way to say \"this passes, but a human should look at\n// X\" — the only way to raise a concern was to FAIL the PR and then override your own failure, which reads\n// on the dashboard as a deliberately-accepted defect rather than as a note.\nexport const VERDICT_GREEN = 'green';\nexport const VERDICT_YELLOW = 'yellow';\nexport const VERDICT_RED = 'red';\nexport const VERDICT_STATUSES = [VERDICT_GREEN, VERDICT_YELLOW, VERDICT_RED] as const;\n\n/** The durable instruction embedded in single-round review.json files for the fixing/coordinating AI. */\nexport const SINGLE_ROUND_MAIN_AGENT_INSTRUCTIONS =\n 'DO NOT RERUN this reviewer. If red, fix every finding and change each addressed red result to yellow; ' +\n 'yellow is acceptable. If you genuinely disagree, leave it red and flag the human for a decision BEFORE ' +\n 'posting the PR. A remaining red overrides any instruction to continue or land on main.';\n\n// The verdict a reviewer SUBAGENT writes into `.webpieces/pr-review/<featureSlug>/review-<id>.json`, one per\n// matched checklist. One file per checklist so N concurrent reviewer subagents never clobber a shared\n// file. It records the OUTCOME:\n// status:'green' → PASS\n// status:'yellow' → WARN (passes; the concern is published on the PR, nothing is blocked)\n// status:'red' + an override-<id>.json → OVERRIDDEN (pass; the human's stated reason reaches the PR)\n// status:'red' + no override file → FAIL (refuse; `output` is printed verbatim)\n//\n// THE `override` FIELD IS GONE FROM THIS FILE, and there is no compatibility mode. The ship-anyway\n// justification used to live here as free text, which made the ONE participant who hears the human — the\n// coordinating agent — the one participant that could not record it, because editing a reviewer's verdict\n// file is (correctly) refused by the harness. It now lives in its own `override-<id>.json`; see\n// ChecklistOverride. A verdict file still carrying an `override` key is reported through `problem` with the\n// destination named, exactly as the removed `success` field is. Data-only (per CLAUDE.md).\nexport class ChecklistResult {\n agent: string;\n model: string;\n id: string;\n status: string; // one of VERDICT_STATUSES; anything else is reported via `problem`\n output: string; // what the reviewer found; printed verbatim when the checklist fails\n // The HUMAN's authorization loaded from `override-<id>.json` beside this verdict, or null when there is\n // none. Loaded alongside the verdict so `resolveVerdict` needs no second read of disk and every command\n // resolves the same outcome from the same bytes.\n override: ChecklistOverride | null;\n // '' = a well-formed verdict. Non-empty = the file exists and parses but its verdict cannot be READ\n // (most often: it still uses the removed `success` field, or the moved `override` field). Carried as\n // data rather than thrown so the complaint can be reported by BOTH wp-review-upsert-pr and\n // wp-finish-upsert-pr in identical words, and so a legacy file is never silently mistaken for a missing one.\n problem: string;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(agent: string, model: string, id: string, status: string, output: string, override: ChecklistOverride | null, problem = '') {\n this.agent = agent;\n this.model = model;\n this.id = id;\n this.status = status;\n this.output = output;\n this.override = override;\n this.problem = problem;\n }\n}\n\n/**\n * What the pr-gate command computed from the diff: a checklist this branch MATCHED (its patterns hit the\n * diff, so its reviewer subagent is in scope). Drives review-<id>.json enforcement, provenance, the schema\n * hint, and the dashboard. Data-only.\n *\n * NAME NOTE: \"Required\" here means MATCHED, not mandatory — it predates `required` by a long way and is\n * the shared shape across review-json, the detector, the briefing builder, provenance and the dashboard.\n * Whether the reviewer must actually run is the {@link RequiredChecklist.required} field below.\n */\nexport class RequiredChecklist {\n id: string; // = subagent name; keys review-<id>.json\n subagent: string; // reviewer agent that must run (agentType the harness stamps)\n doc: string; // REPO-RELATIVE guidance doc the reviewer reads ('' → it just reads the diff)\n matchedFiles: string[]; // the changed files that matched it (for the dashboard + hint)\n // Which of the checklist's OWN globs actually fired. Printed so a reviewer can judge how coarse the\n // match was — a precise `db/migrations/**` hit means something different from a blanket `**` — and the\n // template tells reviewers that matching IS deliberately coarse. [] = no patterns (matches every PR).\n matchedPatterns: string[];\n // Straight from the checklist's config `required`. true = blocking; false = the human is offered it and\n // may decline. Carried on the MATCH rather than looked up from config downstream so the set that gates\n // and the set that is reported cannot disagree about which of the two a checklist is.\n required: boolean;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n id: string, subagent: string, doc: string, matchedFiles: string[], matchedPatterns: string[] = [],\n // Defaulted to the BLOCKING value so any construction that forgets it fails closed — a test or a\n // future call site that silently produced an optional checklist would be a hole in the gate.\n required = true,\n ) {\n this.id = id;\n this.subagent = subagent;\n this.doc = doc;\n this.matchedFiles = matchedFiles;\n this.matchedPatterns = matchedPatterns;\n this.required = required;\n }\n}\n\n/**\n * The per-PR facts every reviewer subagent needs GIVEN to it, alongside its own checklist: the exact base\n * sha the gate diffs against and the file holding the complete changed-file set. Both used to live only in\n * a doc the printed instruction told the AI to go read, one indirection away from the instruction to hand\n * them over — so the printed block could not stand on its own. Data-only; empty = omit those lines.\n */\nexport class ChecklistReviewContext {\n baseSha: string; // the 3-point merge-base sha\n prContextPath: string; // path of pr-context.json — the AUTHORITATIVE full changed-file set\n /**\n * The exact command that reproduces ONE file's diff, with a `-- <file>` tail — NOT assembled by the\n * caller. This used to be hardcoded as `git diff <baseSha> HEAD -- <file>`, which returns NOTHING on a\n * dirty tree because the changed-file set is computed base→working-tree. See DiffBasis, which derives\n * this string from the same range the file set came from.\n */\n fileDiffCommand: string;\n diffDir: string; // dir of the MATERIALIZED diff (diff/ALL.diff + diff/files/…); '' when not written\n dirty: boolean; // true ⇒ the range includes uncommitted + untracked work, and must be said out loud\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(baseSha = '', prContextPath = '', fileDiffCommand = '', diffDir = '', dirty = false) {\n this.baseSha = baseSha;\n this.prContextPath = prContextPath;\n this.fileDiffCommand = fileDiffCommand;\n this.diffDir = diffDir;\n this.dirty = dirty;\n }\n}\n\n// The AI-authored review for a PR. The AI writes review.json itself between `wp-start-upsert-pr` (which\n// prints the schema) and `wp-finish-upsert-pr` (which reads it); reviewer subagents write the per-checklist\n// review-<id>.json files. Data-only (per CLAUDE.md).\nexport class ReviewJson {\n agent: string;\n model: string;\n title: string; // human PR title describing the change; used as the `gh pr` title (empty → caller falls back)\n riskScore: number; // 0–100, drives the risk bar\n riskLevel: string; // 'green' | 'yellow' | 'red'\n riskEmoji: string; // '🟢' | '🟡' | '🔴' — derived from riskLevel when omitted\n summary: string; // rendered in the dashboard Summary section\n violations: string[]; // pattern/architecture violations; length = the Pattern Violations count\n risks: string[];\n filesToReview: string[];\n results: ChecklistResult[]; // resolved per-checklist verdicts (from review-<id>.json); [] when none\n mainAgentInstructions: string; // non-empty only for the opt-in single-round experiment\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(agent: string, model: string,\n title: string,\n riskScore: number,\n riskLevel: string,\n riskEmoji: string,\n summary: string,\n violations: string[],\n risks: string[],\n filesToReview: string[],\n results: ChecklistResult[] = [],\n mainAgentInstructions = '',\n ) {\n this.agent = agent;\n this.model = model;\n this.title = title;\n this.riskScore = riskScore;\n this.riskLevel = riskLevel;\n this.riskEmoji = riskEmoji;\n this.summary = summary;\n this.violations = violations;\n this.risks = risks;\n this.filesToReview = filesToReview;\n this.results = results;\n this.mainAgentInstructions = mainAgentInstructions;\n }\n}\n\n// A checklist's resolved outcome, shared by review.json enforcement and the dashboard so both agree.\n// PASS, WARN and OVERRIDDEN all ship; FAIL, MISSING and BAD_FORMAT all refuse the PR.\nexport const CK_PASS = 'pass'; // review-<id>.json status:'green'\nexport const CK_WARN = 'warn'; // review-<id>.json status:'yellow' → 🟡 passes WITH concerns\nexport const CK_OVERRIDDEN = 'overridden'; // status:'red' + a human's override-<id>.json → 🟠\nexport const CK_FAIL = 'fail'; // review-<id>.json status:'red' + no override → refuse\nexport const CK_MISSING = 'missing'; // no review-<id>.json written → refuse\nexport const CK_BAD_FORMAT = 'bad-format'; // written, but its verdict is unreadable (e.g. legacy `success`)\n\nexport class ChecklistVerdict {\n id: string;\n status: string; // one of CK_PASS | CK_WARN | CK_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_BAD_FORMAT\n detail: string; // reviewer output / override justification / format complaint (dashboard + errors)\n\n constructor(id: string, status: string, detail: string) {\n this.id = id;\n this.status = status;\n this.detail = detail;\n }\n}\n\n// The PR's diff context, written by wp-start-upsert-pr into `.webpieces/pr-review/<featureSlug>/pr-context.json`\n// so a reviewer subagent knows the exact 3-point base the gate used and the full changed-file set — then\n// reads any file's actual diff with `git diff <base> HEAD -- <file>`. This is what lets a checklist match\n// coarsely by path (in the config) while the subagent makes the fine, content-level judgment. Data-only.\nexport class PrContext {\n base: string; // the 3-point merge-base sha the gate diffs against\n /**\n * The real HEAD sha. This was once the literal string 'HEAD', which is not a fact — it cannot be\n * compared later to detect that the tree moved under a review, and it reads as a range that was never\n * actually diffed. Its only reader (reviewContextFor) takes `base`, so recording the sha is free.\n */\n head: string;\n changedFiles: string[]; // every file changed in the range (NOT tsOnly — includes .sql/.gql/Dockerfile/…)\n dirty: boolean; // true ⇒ changedFiles includes uncommitted + untracked work\n dirtyFiles: string[]; // exactly which paths are uncommitted/untracked — why `dirty` is true\n diffCommand: string; // the command that reproduces the WHOLE diff (see DiffBasis; correct when dirty)\n diffDir: string; // dir holding the materialized per-file diffs + ALL.diff; '' when not materialized\n generatedAt: string; // ISO timestamp, so a stale context is detectable rather than silently trusted\n /**\n * Main's head as this clone last saw it — the THIRD hash point, matching the trio the 3-point merge\n * records in `merge-info/<branch>/updatemain-hashes.json`. `base`/`head` above are points A and B\n * under the review side's older names.\n *\n * The review side used to record only A and B, so nothing could answer \"did main move while this was\n * under review?\" — the question you most want answered when a review looks stale. '' when origin/main\n * is unresolvable. Purely informational; nothing gates on it.\n */\n hashMainHead: string;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n base: string, head: string, changedFiles: string[],\n dirty = false, dirtyFiles: string[] = [], diffCommand = '', diffDir = '', generatedAt = '',\n hashMainHead = '',\n ) {\n this.hashMainHead = hashMainHead;\n this.base = base;\n this.head = head;\n this.changedFiles = changedFiles;\n this.dirty = dirty;\n this.dirtyFiles = dirtyFiles;\n this.diffCommand = diffCommand;\n this.diffDir = diffDir;\n this.generatedAt = generatedAt;\n }\n}\n"]}
1
+ {"version":3,"file":"review-json-data.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/review-json-data.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The DATA-ONLY classes and status constants of the PR review system — the verdict a reviewer writes, the\n * checklist that demanded it, the resolved outcome, and the diff context handed to reviewers.\n *\n * Split out of review-json.ts, which holds the SERVICE that reads and writes them. The split is purely by\n * kind (data vs behaviour): review-json.ts re-exports every name below, so `from './review-json'` and\n * `from '@webpieces/rules-config'` keep resolving exactly as before and no consumer changes.\n */\n\nimport { ReviewerAgentPolicy } from './checklist-config';\nimport { ChecklistOverride } from './checklist-override';\n\n// The three colors a reviewer subagent may report in `review-<id>.json`. A TRI-state, not a boolean,\n// because the boolean it replaced gave a reviewer no way to say \"this passes, but a human should look at\n// X\" — the only way to raise a concern was to FAIL the PR and then override your own failure, which reads\n// on the dashboard as a deliberately-accepted defect rather than as a note.\nexport const VERDICT_GREEN = 'green';\nexport const VERDICT_YELLOW = 'yellow';\nexport const VERDICT_RED = 'red';\nexport const VERDICT_STATUSES = [VERDICT_GREEN, VERDICT_YELLOW, VERDICT_RED] as const;\n\n/** The durable instruction embedded in single-round review.json files for the fixing/coordinating AI. */\nexport const SINGLE_ROUND_MAIN_AGENT_INSTRUCTIONS =\n 'DO NOT RERUN this reviewer. If red, fix every finding and change each addressed red result to yellow; ' +\n 'yellow is acceptable. If you genuinely disagree, leave it red and flag the human for a decision BEFORE ' +\n 'posting the PR. A remaining red overrides any instruction to continue or land on main.';\n\n// The verdict a reviewer SUBAGENT writes into `.webpieces/pr-review/<featureSlug>/review-<id>.json`, one per\n// matched checklist. One file per checklist so N concurrent reviewer subagents never clobber a shared\n// file. It records the OUTCOME:\n// status:'green' → PASS\n// status:'yellow' → WARN (passes; the concern is published on the PR, nothing is blocked)\n// status:'red' + an override-<id>.json → OVERRIDDEN (pass; the human's stated reason reaches the PR)\n// status:'red' + no override file → FAIL (refuse; `output` is printed verbatim)\n//\n// THE `override` FIELD IS GONE FROM THIS FILE, and there is no compatibility mode. The ship-anyway\n// justification used to live here as free text, which made the ONE participant who hears the human — the\n// coordinating agent — the one participant that could not record it, because editing a reviewer's verdict\n// file is (correctly) refused by the harness. It now lives in its own `override-<id>.json`; see\n// ChecklistOverride. A verdict file still carrying an `override` key is reported through `problem` with the\n// destination named, exactly as the removed `success` field is. Data-only (per CLAUDE.md).\nexport class ChecklistResult {\n agent: string;\n model: string;\n id: string;\n status: string; // one of VERDICT_STATUSES; anything else is reported via `problem`\n output: string; // what the reviewer found; printed verbatim when the checklist fails\n // The HUMAN's authorization loaded from `override-<id>.json` beside this verdict, or null when there is\n // none. Loaded alongside the verdict so `resolveVerdict` needs no second read of disk and every command\n // resolves the same outcome from the same bytes.\n override: ChecklistOverride | null;\n // '' = a well-formed verdict. Non-empty = the file exists and parses but its verdict cannot be READ\n // (most often: it still uses the removed `success` field, or the moved `override` field). Carried as\n // data rather than thrown so the complaint can be reported by BOTH wp-review-upsert-pr and\n // wp-finish-upsert-pr in identical words, and so a legacy file is never silently mistaken for a missing one.\n problem: string;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(agent: string, model: string, id: string, status: string, output: string, override: ChecklistOverride | null, problem = '') {\n this.agent = agent;\n this.model = model;\n this.id = id;\n this.status = status;\n this.output = output;\n this.override = override;\n this.problem = problem;\n }\n}\n\n/**\n * What the pr-gate command computed from the diff: a checklist this branch MATCHED (its patterns hit the\n * diff, so its reviewer subagent is in scope). Drives review-<id>.json enforcement, provenance, the schema\n * hint, and the dashboard. Data-only.\n *\n * NAME NOTE: \"Required\" here means MATCHED, not mandatory — it predates `required` by a long way and is\n * the shared shape across review-json, the detector, the briefing builder, provenance and the dashboard.\n * Whether the reviewer must actually run is the {@link RequiredChecklist.required} field below.\n */\nexport class RequiredChecklist {\n id: string; // the checklist's name; keys review-<id>.json and its instructions file\n reviewer: ReviewerAgentPolicy; // the agent type to spawn (agentType the harness stamps) + the round cap\n doc: string; // REPO-RELATIVE guidance doc the reviewer reads ('' → it just reads the diff)\n matchedFiles: string[]; // the changed files that matched it (for the dashboard + hint)\n // Which of the checklist's OWN globs actually fired. Printed so a reviewer can judge how coarse the\n // match was — a precise `db/migrations/**` hit means something different from a blanket `**` — and the\n // template tells reviewers that matching IS deliberately coarse. [] = no patterns (matches every PR).\n matchedPatterns: string[];\n // Straight from the checklist's config `required`. true = blocking; false = the human is offered it and\n // may decline. Carried on the MATCH rather than looked up from config downstream so the set that gates\n // and the set that is reported cannot disagree about which of the two a checklist is.\n required: boolean;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n id: string, reviewer: ReviewerAgentPolicy, doc: string, matchedFiles: string[], matchedPatterns: string[] = [],\n // Defaulted to the BLOCKING value so any construction that forgets it fails closed — a test or a\n // future call site that silently produced an optional checklist would be a hole in the gate.\n required = true,\n ) {\n this.id = id;\n this.reviewer = reviewer;\n this.doc = doc;\n this.matchedFiles = matchedFiles;\n this.matchedPatterns = matchedPatterns;\n this.required = required;\n }\n}\n\n/**\n * The per-PR facts every reviewer subagent needs GIVEN to it, alongside its own checklist: the exact base\n * sha the gate diffs against and the file holding the complete changed-file set. Both used to live only in\n * a doc the printed instruction told the AI to go read, one indirection away from the instruction to hand\n * them over — so the printed block could not stand on its own. Data-only; empty = omit those lines.\n */\nexport class ChecklistReviewContext {\n baseSha: string; // the 3-point merge-base sha\n prContextPath: string; // path of pr-context.json — the AUTHORITATIVE full changed-file set\n /**\n * The exact command that reproduces ONE file's diff, with a `-- <file>` tail — NOT assembled by the\n * caller. This used to be hardcoded as `git diff <baseSha> HEAD -- <file>`, which returns NOTHING on a\n * dirty tree because the changed-file set is computed base→working-tree. See DiffBasis, which derives\n * this string from the same range the file set came from.\n */\n fileDiffCommand: string;\n diffDir: string; // dir of the MATERIALIZED diff (diff/ALL.diff + diff/files/…); '' when not written\n dirty: boolean; // true ⇒ the range includes uncommitted + untracked work, and must be said out loud\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(baseSha = '', prContextPath = '', fileDiffCommand = '', diffDir = '', dirty = false) {\n this.baseSha = baseSha;\n this.prContextPath = prContextPath;\n this.fileDiffCommand = fileDiffCommand;\n this.diffDir = diffDir;\n this.dirty = dirty;\n }\n}\n\n// The AI-authored review for a PR. The AI writes review.json itself between `wp-start-upsert-pr` (which\n// prints the schema) and `wp-finish-upsert-pr` (which reads it); reviewer subagents write the per-checklist\n// review-<id>.json files. Data-only (per CLAUDE.md).\nexport class ReviewJson {\n agent: string;\n model: string;\n title: string; // human PR title describing the change; used as the `gh pr` title (empty → caller falls back)\n riskScore: number; // 0–100, drives the risk bar\n riskLevel: string; // 'green' | 'yellow' | 'red'\n riskEmoji: string; // '🟢' | '🟡' | '🔴' — derived from riskLevel when omitted\n summary: string; // rendered in the dashboard Summary section\n violations: string[]; // pattern/architecture violations; length = the Pattern Violations count\n risks: string[];\n filesToReview: string[];\n results: ChecklistResult[]; // resolved per-checklist verdicts (from review-<id>.json); [] when none\n mainAgentInstructions: string; // non-empty only for the opt-in single-round experiment\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(agent: string, model: string,\n title: string,\n riskScore: number,\n riskLevel: string,\n riskEmoji: string,\n summary: string,\n violations: string[],\n risks: string[],\n filesToReview: string[],\n results: ChecklistResult[] = [],\n mainAgentInstructions = '',\n ) {\n this.agent = agent;\n this.model = model;\n this.title = title;\n this.riskScore = riskScore;\n this.riskLevel = riskLevel;\n this.riskEmoji = riskEmoji;\n this.summary = summary;\n this.violations = violations;\n this.risks = risks;\n this.filesToReview = filesToReview;\n this.results = results;\n this.mainAgentInstructions = mainAgentInstructions;\n }\n}\n\n// A checklist's resolved outcome, shared by review.json enforcement and the dashboard so both agree.\n// PASS, WARN and OVERRIDDEN all ship; FAIL, MISSING and BAD_FORMAT all refuse the PR.\nexport const CK_PASS = 'pass'; // review-<id>.json status:'green'\nexport const CK_WARN = 'warn'; // review-<id>.json status:'yellow' → 🟡 passes WITH concerns\nexport const CK_OVERRIDDEN = 'overridden'; // status:'red' + a human's override-<id>.json → 🟠\nexport const CK_FAIL = 'fail'; // review-<id>.json status:'red' + no override → refuse\nexport const CK_MISSING = 'missing'; // no review-<id>.json written → refuse\nexport const CK_BAD_FORMAT = 'bad-format'; // written, but its verdict is unreadable (e.g. legacy `success`)\n\nexport class ChecklistVerdict {\n id: string;\n status: string; // one of CK_PASS | CK_WARN | CK_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_BAD_FORMAT\n detail: string; // reviewer output / override justification / format complaint (dashboard + errors)\n\n constructor(id: string, status: string, detail: string) {\n this.id = id;\n this.status = status;\n this.detail = detail;\n }\n}\n\n// The PR's diff context, written by wp-start-upsert-pr into `.webpieces/pr-review/<featureSlug>/pr-context.json`\n// so a reviewer subagent knows the exact 3-point base the gate used and the full changed-file set — then\n// reads any file's actual diff with `git diff <base> HEAD -- <file>`. This is what lets a checklist match\n// coarsely by path (in the config) while the subagent makes the fine, content-level judgment. Data-only.\nexport class PrContext {\n base: string; // the 3-point merge-base sha the gate diffs against\n /**\n * The real HEAD sha. This was once the literal string 'HEAD', which is not a fact — it cannot be\n * compared later to detect that the tree moved under a review, and it reads as a range that was never\n * actually diffed. Its only reader (reviewContextFor) takes `base`, so recording the sha is free.\n */\n head: string;\n changedFiles: string[]; // every file changed in the range (NOT tsOnly — includes .sql/.gql/Dockerfile/…)\n dirty: boolean; // true ⇒ changedFiles includes uncommitted + untracked work\n dirtyFiles: string[]; // exactly which paths are uncommitted/untracked — why `dirty` is true\n diffCommand: string; // the command that reproduces the WHOLE diff (see DiffBasis; correct when dirty)\n diffDir: string; // dir holding the materialized per-file diffs + ALL.diff; '' when not materialized\n generatedAt: string; // ISO timestamp, so a stale context is detectable rather than silently trusted\n /**\n * Main's head as this clone last saw it — the THIRD hash point, matching the trio the 3-point merge\n * records in `merge-info/<branch>/updatemain-hashes.json`. `base`/`head` above are points A and B\n * under the review side's older names.\n *\n * The review side used to record only A and B, so nothing could answer \"did main move while this was\n * under review?\" — the question you most want answered when a review looks stale. '' when origin/main\n * is unresolvable. Purely informational; nothing gates on it.\n */\n hashMainHead: string;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n base: string, head: string, changedFiles: string[],\n dirty = false, dirtyFiles: string[] = [], diffCommand = '', diffDir = '', generatedAt = '',\n hashMainHead = '',\n ) {\n this.hashMainHead = hashMainHead;\n this.base = base;\n this.head = head;\n this.changedFiles = changedFiles;\n this.dirty = dirty;\n this.dirtyFiles = dirtyFiles;\n this.diffCommand = diffCommand;\n this.diffDir = diffDir;\n this.generatedAt = generatedAt;\n }\n}\n"]}
@@ -386,14 +386,14 @@ let ReviewJsonService = class ReviewJsonService {
386
386
  // eslint-disable-next-line @typescript-eslint/max-params
387
387
  refusalError(req, verdict, reviewJsonFilePath, archivedPath = '') {
388
388
  const finding = `${verdict.detail.split('\n').join('\n ')}\n`;
389
- const head = `Checklist "${req.id}" FAILED review (status:"${review_json_data_1.VERDICT_RED}"). The reviewer (${req.subagent}) wrote:\n ` + finding;
389
+ const head = `Checklist "${req.id}" FAILED review (status:"${review_json_data_1.VERDICT_RED}"). The reviewer (${req.reviewer.agentName}) wrote:\n ` + finding;
390
390
  const retired = archivedPath === ''
391
391
  ? ' Fix it, then re-run.\n'
392
392
  // Re-spawning is said only after the finding, because an instruction to spawn a subagent is the
393
393
  // one line an AI acts on first — see refusedChecklists for what that cost.
394
394
  : ` That verdict has been RETIRED to ${archivedPath} (audit only — it is not a live verdict).\n` +
395
395
  ` A FRESH ${this.checklistFileName(req.id)} is now required. Fix the finding first, then have the ` +
396
- `"${req.subagent}" subagent review again and write a new verdict.\n`;
396
+ `"${req.reviewer.agentName}" subagent review again and write a new verdict.\n`;
397
397
  return head + retired + this.overrideRoute(req, reviewJsonFilePath);
398
398
  }
399
399
  /**
@@ -494,7 +494,7 @@ let ReviewJsonService = class ReviewJsonService {
494
494
  if (!req.required)
495
495
  continue;
496
496
  const doc = req.doc.trim() !== '' ? ` Read: ${req.doc}.` : '';
497
- errors.push(`Checklist "${req.id}" MATCHED this diff but has no verdict. Spawn the "${req.subagent}" subagent to review it, ` +
497
+ errors.push(`Checklist "${req.id}" MATCHED this diff but has no verdict. Spawn the "${req.reviewer.agentName}" subagent to review it, ` +
498
498
  `then write ${this.checklistFileName(req.id)} with ` +
499
499
  `{"id":"${req.id}","status":"${review_json_data_1.VERDICT_GREEN}","agent":"unknown","model":"unknown","output":"…"}.${doc}`);
500
500
  }