@webpieces/rules-config 0.4.601 → 0.4.603

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/rules-config",
3
- "version": "0.4.601",
3
+ "version": "0.4.603",
4
4
  "description": "Shared webpieces.config.json loader. Single source of truth for validation rule configuration consumed by @webpieces/ai-hook-rules, @webpieces/code-rules, and @webpieces/nx-webpieces-rules.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -35,9 +35,18 @@ export declare class BranchMutationLog {
35
35
  * logs in the same directory: Claude Code runs every matching PreToolUse hook IN PARALLEL, and
36
36
  * several agents and sessions can share one tree, so a per-worktree name there had several
37
37
  * concurrent writers. Those names now carry a `<sessionId>-<agentId|coordinator>-<hook>-` prefix
38
- * (ai-hook-rules' LogStream). This file keeps a bare name deliberately LogStream lives in
39
- * ai-hook-rules, which DEPENDS on rules-config, so the import direction forbids reusing it here.
40
- * If this log ever gains a second concurrent writer, it needs its own stream identity first.
38
+ * (ai-hook-rules' LogStream), and the rejection DETAIL files sit in a directory of that same name.
39
+ *
40
+ * This file keeps a bare name deliberately, for TWO independent reasons, and neither has gone away
41
+ * (re-checked 2026-08-07). First, LogStream lives in ai-hook-rules, which DEPENDS on rules-config,
42
+ * so the import direction forbids reusing it here. Second — and this is the one that would still
43
+ * bite after moving the class down — the `wp-*` bins that write this file have NO session or agent
44
+ * identity to prefix with: those ids reach a PreToolUse hook on its JSON payload and a plain Bash
45
+ * tool call never sees them. Routing this through LogStream today would therefore produce
46
+ * `unknown-coordinator-hook-branch-mutations.log`: one shared file, exactly as now, under a longer
47
+ * name, plus a rename of a log people already grep. If this log ever gains a second concurrent
48
+ * writer, it needs a real stream identity FIRST — the prefix is only worth having when it
49
+ * discriminates.
41
50
  *
42
51
  * Nothing is lost by keeping it local: under the `worktrees/<name>/` layout the log lives in the
43
52
  * PRIMARY clone, so it survives `git worktree remove`, and the whole history is one glob —
@@ -72,9 +72,18 @@ let BranchMutationLog = class BranchMutationLog {
72
72
  * logs in the same directory: Claude Code runs every matching PreToolUse hook IN PARALLEL, and
73
73
  * several agents and sessions can share one tree, so a per-worktree name there had several
74
74
  * concurrent writers. Those names now carry a `<sessionId>-<agentId|coordinator>-<hook>-` prefix
75
- * (ai-hook-rules' LogStream). This file keeps a bare name deliberately LogStream lives in
76
- * ai-hook-rules, which DEPENDS on rules-config, so the import direction forbids reusing it here.
77
- * If this log ever gains a second concurrent writer, it needs its own stream identity first.
75
+ * (ai-hook-rules' LogStream), and the rejection DETAIL files sit in a directory of that same name.
76
+ *
77
+ * This file keeps a bare name deliberately, for TWO independent reasons, and neither has gone away
78
+ * (re-checked 2026-08-07). First, LogStream lives in ai-hook-rules, which DEPENDS on rules-config,
79
+ * so the import direction forbids reusing it here. Second — and this is the one that would still
80
+ * bite after moving the class down — the `wp-*` bins that write this file have NO session or agent
81
+ * identity to prefix with: those ids reach a PreToolUse hook on its JSON payload and a plain Bash
82
+ * tool call never sees them. Routing this through LogStream today would therefore produce
83
+ * `unknown-coordinator-hook-branch-mutations.log`: one shared file, exactly as now, under a longer
84
+ * name, plus a rename of a log people already grep. If this log ever gains a second concurrent
85
+ * writer, it needs a real stream identity FIRST — the prefix is only worth having when it
86
+ * discriminates.
78
87
  *
79
88
  * Nothing is lost by keeping it local: under the `worktrees/<name>/` layout the log lives in the
80
89
  * PRIMARY clone, so it survives `git worktree remove`, and the whole history is one glob —
@@ -1 +1 @@
1
- {"version":3,"file":"branch-mutation-log.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/branch-mutation-log.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { DotWebpieces, dotWebpieces } from './state-dir';\nimport { toError } from './to-error';\n\n// The BRANCH-MUTATION log — an audit trail for every workflow verb that RENAMES or MOVES branches.\n// Records START / each phase boundary / END-with-outcome so the next agent (or a human) can\n// reconstruct what the tooling did to the branches. Writes to `.webpieces/logs/branch-mutations.log`\n// (in a linked worktree: `.webpieces/worktrees/<name>/logs/`) — see LOGS_STATE_DIR for why every\n// webpieces log lives under `logs/` and no longer beside the non-log state in `hooks/`.\n// Lives in rules-config (the shared dep of pr-gate) so the pr-gate scripts can call it directly.\n\nconst LOG_FILE = 'branch-mutations.log';\nconst LOG_FILE_PREV = 'branch-mutations.1.log';\nconst MAX_LOG_BYTES = 512 * 1024; // 512 KB — rotate when exceeded (mirrors the other webpieces logs)\nconst MAX_DETAIL_LEN = 400;\n\n// The workflow verb whose branch mutation is being logged (the bin the AI/human invoked).\n// `auto-reap` is the odd one out: no human invoked it — it is the detached background refresher\n// (sync-main.ts) deleting dead branches on its own. It gets a verb precisely BECAUSE it is\n// unattended: a deletion nobody watched happen is the one that most needs an audit line.\nexport type MutationVerb =\n | 'wp-start-update' | 'wp-finish-update' | 'wp-start-upsert-pr' | 'wp-review-upsert-pr'\n | 'wp-finish-upsert-pr' | 'wp-cleanup' | 'wp-land-pr' | 'auto-reap';\n\n// A boundary within a verb's execution. START/END bracket the whole run; the middle phases mark each\n// irreversible git step so an interrupt leaves a breadcrumb at the last phase reached.\n// REAP is a whole mutation in one line (a branch delete has no phases) — see BranchReaper.\n// REAP_WORKTREE is its worktree twin: archive → `git worktree remove` → `git branch -D`, all three of\n// which succeed or fail as one act. It is a SEPARATE phase, not just a REAP with a path, so that\n// `grep REAP_WORKTREE` answers \"what directories did the tooling delete?\" — a strictly scarier\n// question than \"what refs did it delete?\", since a worktree removal takes real files with it.\nexport type MutationPhase =\n | 'START' | 'BACKUP' | 'CHECKOUT_MAIN' | 'PULL' | 'SQUASH' | 'RENAME'\n | 'FINALIZE' | 'CONFLICT' | 'INTERRUPTED' | 'END' | 'REAP' | 'REAP_WORKTREE';\n\n// Data-only record of one branch-mutation event (per CLAUDE.md: classes for data, explicit construction).\nexport class BranchMutationEvent {\n verb: MutationVerb;\n phase: MutationPhase;\n fromBranch: string = '';\n toBranch: string = '';\n oldMain: string = '';\n newMain: string = '';\n conflict: boolean = false;\n conflictFiles: string[] = [];\n outcome: string = '';\n artifacts: string[] = [];\n // The commit a DELETED branch pointed at, captured immediately before the delete. This is what\n // makes a reap auditable AND reversible: the work is already in main, and the pre-delete tip is\n // still addressable by hash (the reflog holds it ~90 days), so formatDetail renders a literal\n // `recover=git branch <name> <sha>` next to it. Empty for mutations that delete nothing.\n sha: string = '';\n // The `archive/<date>/<branch>` tag written immediately BEFORE a REAP deleted the branch. When set,\n // formatDetail renders `recover=` against the TAG instead of the sha: a tag is a permanent ref that\n // survives `gc` and reflog expiry and can be pushed, whereas a bare sha is only recoverable while\n // this clone's reflog still holds it. Empty when nothing was tagged (retention policy 'delete').\n archiveTag: string = '';\n // The directory a REAP_WORKTREE removed. When set, `recover=` becomes the WORKTREE form\n // (`git worktree add -b <branch> <path> <ref>`) rather than the bare `git branch` form: putting the\n // ref back does not put the directory back, and a recover line that restores half of what was\n // destroyed is worse than none — it reads as done. Empty for every mutation that removes no directory.\n worktreePath: string = '';\n\n constructor(verb: MutationVerb, phase: MutationPhase) {\n this.verb = verb;\n this.phase = phase;\n }\n}\n\n/** Appends branch-mutation audit lines. `@injectable(bindingScopeValues.Singleton)` so it's injectable + drawn in the design. */\n@injectable(bindingScopeValues.Singleton)\nexport class BranchMutationLog {\n constructor(private readonly dotDir: DotWebpieces = dotWebpieces) {}\n\n /**\n * LOCAL scope, deliberately — one log per worktree, not one per repo.\n *\n * A SHARED append-only log would genuinely corrupt. `O_APPEND` makes a write indivisible only up to\n * PIPE_BUF, which is 512 bytes on macOS, and a REAP_WORKTREE line carrying\n * `recover=git worktree add -b <branch> <absolute-path> <tag>` exceeds that — concurrent appenders\n * from seven worktrees would interleave into an unrecoverable audit trail, which is the one thing\n * this file exists not to be. Per-worktree, THIS log has one writer and cannot tear.\n *\n * That last claim is narrower than it looks, so do not generalise it (2026-08-06). It holds here\n * because the wp-* bins write this file one command at a time. It does NOT hold for the ai-hook\n * logs in the same directory: Claude Code runs every matching PreToolUse hook IN PARALLEL, and\n * several agents and sessions can share one tree, so a per-worktree name there had several\n * concurrent writers. Those names now carry a `<sessionId>-<agentId|coordinator>-<hook>-` prefix\n * (ai-hook-rules' LogStream). This file keeps a bare name deliberately — LogStream lives in\n * ai-hook-rules, which DEPENDS on rules-config, so the import direction forbids reusing it here.\n * If this log ever gains a second concurrent writer, it needs its own stream identity first.\n *\n * Nothing is lost by keeping it local: under the `worktrees/<name>/` layout the log lives in the\n * PRIMARY clone, so it survives `git worktree remove`, and the whole history is one glob —\n * `<primary>/.webpieces/worktrees/*/logs/branch-mutations.log`.\n */\n branchMutationLogPath(root: string): string {\n return this.dotDir.logsFile(root, LOG_FILE);\n }\n\n /**\n * Append one tab-separated line per branch-mutation event to\n * `.webpieces/logs/branch-mutations.log`. Swallows all errors — logging must NEVER block or fail\n * the workflow it is observing.\n */\n logBranchMutation(root: string, event: BranchMutationEvent): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const timestamp = new Date().toISOString();\n const logsDir = this.dotDir.logs(root);\n fs.mkdirSync(logsDir, { recursive: true });\n\n const logPath = path.join(logsDir, LOG_FILE);\n this.rotateLogFile(logPath, path.join(logsDir, LOG_FILE_PREV));\n\n const line = [\n `[${timestamp}]`,\n event.verb,\n event.phase,\n this.oneLine(this.formatDetail(event)),\n ].join('\\t') + '\\n';\n fs.appendFileSync(logPath, line);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n\n // Render only the fields this event actually set, as `key=value` tokens — greppable on one line.\n private formatDetail(event: BranchMutationEvent): string {\n const parts: string[] = [];\n // A rename/move has both ends; a REAP has only the branch it destroyed. Printing `to=?` for\n // the latter reads like a lost destination rather than \"there was never one\".\n if (event.fromBranch !== '' && event.toBranch !== '') parts.push(`from=${event.fromBranch} to=${event.toBranch}`);\n else if (event.fromBranch !== '') parts.push(`branch=${event.fromBranch}`);\n else if (event.toBranch !== '') parts.push(`from=? to=${event.toBranch}`);\n if (event.oldMain !== '' || event.newMain !== '') parts.push(`oldMain=${event.oldMain || '?'} newMain=${event.newMain || '?'}`);\n if (event.conflict) parts.push('conflict=true');\n if (event.conflictFiles.length > 0) parts.push(`conflictFiles=${event.conflictFiles.length}(${event.conflictFiles.join(',')})`);\n if (event.outcome !== '') parts.push(`outcome=${event.outcome}`);\n // Emitted as one unit so the hash is never separated from the command that undoes the delete.\n // Prefer the archive TAG as the recover ref when there is one — it does not expire.\n if (event.worktreePath !== '') {\n parts.push(this.worktreeDetail(event));\n } else if (event.sha !== '' && event.archiveTag !== '') {\n parts.push(\n `sha=${event.sha} archiveTag=${event.archiveTag} ` +\n `recover=git checkout -b ${event.fromBranch || '?'} ${event.archiveTag}`,\n );\n } else if (event.sha !== '') {\n parts.push(`sha=${event.sha} recover=git branch ${event.fromBranch || '?'} ${event.sha}`);\n }\n for (const artifact of event.artifacts) parts.push(`artifact=${artifact}`);\n return parts.join(' ');\n }\n\n /**\n * The worktree flavour of the sha/recover token: path, tip, archive tag and the ONE command that\n * puts the directory AND the branch back together.\n *\n * `git worktree add -b <branch> <path> <ref>` is verified by hand and in worktree-reaper.spec.ts —\n * plain `git worktree add <path> <tag>` would restore the files at a DETACHED HEAD, silently losing\n * the branch name the reap destroyed. Falls back to the sha when nothing was archived (retention\n * 'delete'), and to a bare `git worktree add <path>` when there was no branch at all (detached).\n */\n private worktreeDetail(event: BranchMutationEvent): string {\n const ref = event.archiveTag !== '' ? event.archiveTag : event.sha;\n const tokens = [`worktree=${event.worktreePath}`];\n if (event.sha !== '') tokens.push(`sha=${event.sha}`);\n if (event.archiveTag !== '') tokens.push(`archiveTag=${event.archiveTag}`);\n if (ref === '') return tokens.join(' ');\n const recover = event.fromBranch !== ''\n ? `git worktree add -b ${event.fromBranch} ${event.worktreePath} ${ref}`\n : `git worktree add ${event.worktreePath} ${ref}`;\n tokens.push(`recover=${recover}`);\n return tokens.join(' ');\n }\n\n // Collapse newlines/tabs and cap length so one event is always exactly one log line.\n private oneLine(value: string): string {\n const flat = value.replace(/[\\t\\r\\n]+/g, ' ').trim();\n return flat.length <= MAX_DETAIL_LEN ? flat : flat.slice(0, MAX_DETAIL_LEN) + '…';\n }\n\n private rotateLogFile(logPath: string, prevPath: string): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const stat = fs.statSync(logPath);\n if (stat.size > MAX_LOG_BYTES) {\n if (fs.existsSync(prevPath)) fs.unlinkSync(prevPath);\n fs.renameSync(logPath, prevPath);\n }\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n}\n\n// Temporary migration delegators to BranchMutationLog — removed once consumers inject it.\nconst branchMutationLogSvc = new BranchMutationLog();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to BranchMutationLog; removed once consumers inject it\nexport function branchMutationLogPath(root: string): string {\n return branchMutationLogSvc.branchMutationLogPath(root);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to BranchMutationLog; removed once consumers inject it\nexport function logBranchMutation(root: string, event: BranchMutationEvent): void {\n branchMutationLogSvc.logBranchMutation(root, event);\n}\n"]}
1
+ {"version":3,"file":"branch-mutation-log.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/branch-mutation-log.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { DotWebpieces, dotWebpieces } from './state-dir';\nimport { toError } from './to-error';\n\n// The BRANCH-MUTATION log — an audit trail for every workflow verb that RENAMES or MOVES branches.\n// Records START / each phase boundary / END-with-outcome so the next agent (or a human) can\n// reconstruct what the tooling did to the branches. Writes to `.webpieces/logs/branch-mutations.log`\n// (in a linked worktree: `.webpieces/worktrees/<name>/logs/`) — see LOGS_STATE_DIR for why every\n// webpieces log lives under `logs/` and no longer beside the non-log state in `hooks/`.\n// Lives in rules-config (the shared dep of pr-gate) so the pr-gate scripts can call it directly.\n\nconst LOG_FILE = 'branch-mutations.log';\nconst LOG_FILE_PREV = 'branch-mutations.1.log';\nconst MAX_LOG_BYTES = 512 * 1024; // 512 KB — rotate when exceeded (mirrors the other webpieces logs)\nconst MAX_DETAIL_LEN = 400;\n\n// The workflow verb whose branch mutation is being logged (the bin the AI/human invoked).\n// `auto-reap` is the odd one out: no human invoked it — it is the detached background refresher\n// (sync-main.ts) deleting dead branches on its own. It gets a verb precisely BECAUSE it is\n// unattended: a deletion nobody watched happen is the one that most needs an audit line.\nexport type MutationVerb =\n | 'wp-start-update' | 'wp-finish-update' | 'wp-start-upsert-pr' | 'wp-review-upsert-pr'\n | 'wp-finish-upsert-pr' | 'wp-cleanup' | 'wp-land-pr' | 'auto-reap';\n\n// A boundary within a verb's execution. START/END bracket the whole run; the middle phases mark each\n// irreversible git step so an interrupt leaves a breadcrumb at the last phase reached.\n// REAP is a whole mutation in one line (a branch delete has no phases) — see BranchReaper.\n// REAP_WORKTREE is its worktree twin: archive → `git worktree remove` → `git branch -D`, all three of\n// which succeed or fail as one act. It is a SEPARATE phase, not just a REAP with a path, so that\n// `grep REAP_WORKTREE` answers \"what directories did the tooling delete?\" — a strictly scarier\n// question than \"what refs did it delete?\", since a worktree removal takes real files with it.\nexport type MutationPhase =\n | 'START' | 'BACKUP' | 'CHECKOUT_MAIN' | 'PULL' | 'SQUASH' | 'RENAME'\n | 'FINALIZE' | 'CONFLICT' | 'INTERRUPTED' | 'END' | 'REAP' | 'REAP_WORKTREE';\n\n// Data-only record of one branch-mutation event (per CLAUDE.md: classes for data, explicit construction).\nexport class BranchMutationEvent {\n verb: MutationVerb;\n phase: MutationPhase;\n fromBranch: string = '';\n toBranch: string = '';\n oldMain: string = '';\n newMain: string = '';\n conflict: boolean = false;\n conflictFiles: string[] = [];\n outcome: string = '';\n artifacts: string[] = [];\n // The commit a DELETED branch pointed at, captured immediately before the delete. This is what\n // makes a reap auditable AND reversible: the work is already in main, and the pre-delete tip is\n // still addressable by hash (the reflog holds it ~90 days), so formatDetail renders a literal\n // `recover=git branch <name> <sha>` next to it. Empty for mutations that delete nothing.\n sha: string = '';\n // The `archive/<date>/<branch>` tag written immediately BEFORE a REAP deleted the branch. When set,\n // formatDetail renders `recover=` against the TAG instead of the sha: a tag is a permanent ref that\n // survives `gc` and reflog expiry and can be pushed, whereas a bare sha is only recoverable while\n // this clone's reflog still holds it. Empty when nothing was tagged (retention policy 'delete').\n archiveTag: string = '';\n // The directory a REAP_WORKTREE removed. When set, `recover=` becomes the WORKTREE form\n // (`git worktree add -b <branch> <path> <ref>`) rather than the bare `git branch` form: putting the\n // ref back does not put the directory back, and a recover line that restores half of what was\n // destroyed is worse than none — it reads as done. Empty for every mutation that removes no directory.\n worktreePath: string = '';\n\n constructor(verb: MutationVerb, phase: MutationPhase) {\n this.verb = verb;\n this.phase = phase;\n }\n}\n\n/** Appends branch-mutation audit lines. `@injectable(bindingScopeValues.Singleton)` so it's injectable + drawn in the design. */\n@injectable(bindingScopeValues.Singleton)\nexport class BranchMutationLog {\n constructor(private readonly dotDir: DotWebpieces = dotWebpieces) {}\n\n /**\n * LOCAL scope, deliberately — one log per worktree, not one per repo.\n *\n * A SHARED append-only log would genuinely corrupt. `O_APPEND` makes a write indivisible only up to\n * PIPE_BUF, which is 512 bytes on macOS, and a REAP_WORKTREE line carrying\n * `recover=git worktree add -b <branch> <absolute-path> <tag>` exceeds that — concurrent appenders\n * from seven worktrees would interleave into an unrecoverable audit trail, which is the one thing\n * this file exists not to be. Per-worktree, THIS log has one writer and cannot tear.\n *\n * That last claim is narrower than it looks, so do not generalise it (2026-08-06). It holds here\n * because the wp-* bins write this file one command at a time. It does NOT hold for the ai-hook\n * logs in the same directory: Claude Code runs every matching PreToolUse hook IN PARALLEL, and\n * several agents and sessions can share one tree, so a per-worktree name there had several\n * concurrent writers. Those names now carry a `<sessionId>-<agentId|coordinator>-<hook>-` prefix\n * (ai-hook-rules' LogStream), and the rejection DETAIL files sit in a directory of that same name.\n *\n * This file keeps a bare name deliberately, for TWO independent reasons, and neither has gone away\n * (re-checked 2026-08-07). First, LogStream lives in ai-hook-rules, which DEPENDS on rules-config,\n * so the import direction forbids reusing it here. Second — and this is the one that would still\n * bite after moving the class down — the `wp-*` bins that write this file have NO session or agent\n * identity to prefix with: those ids reach a PreToolUse hook on its JSON payload and a plain Bash\n * tool call never sees them. Routing this through LogStream today would therefore produce\n * `unknown-coordinator-hook-branch-mutations.log`: one shared file, exactly as now, under a longer\n * name, plus a rename of a log people already grep. If this log ever gains a second concurrent\n * writer, it needs a real stream identity FIRST — the prefix is only worth having when it\n * discriminates.\n *\n * Nothing is lost by keeping it local: under the `worktrees/<name>/` layout the log lives in the\n * PRIMARY clone, so it survives `git worktree remove`, and the whole history is one glob —\n * `<primary>/.webpieces/worktrees/*/logs/branch-mutations.log`.\n */\n branchMutationLogPath(root: string): string {\n return this.dotDir.logsFile(root, LOG_FILE);\n }\n\n /**\n * Append one tab-separated line per branch-mutation event to\n * `.webpieces/logs/branch-mutations.log`. Swallows all errors — logging must NEVER block or fail\n * the workflow it is observing.\n */\n logBranchMutation(root: string, event: BranchMutationEvent): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const timestamp = new Date().toISOString();\n const logsDir = this.dotDir.logs(root);\n fs.mkdirSync(logsDir, { recursive: true });\n\n const logPath = path.join(logsDir, LOG_FILE);\n this.rotateLogFile(logPath, path.join(logsDir, LOG_FILE_PREV));\n\n const line = [\n `[${timestamp}]`,\n event.verb,\n event.phase,\n this.oneLine(this.formatDetail(event)),\n ].join('\\t') + '\\n';\n fs.appendFileSync(logPath, line);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n\n // Render only the fields this event actually set, as `key=value` tokens — greppable on one line.\n private formatDetail(event: BranchMutationEvent): string {\n const parts: string[] = [];\n // A rename/move has both ends; a REAP has only the branch it destroyed. Printing `to=?` for\n // the latter reads like a lost destination rather than \"there was never one\".\n if (event.fromBranch !== '' && event.toBranch !== '') parts.push(`from=${event.fromBranch} to=${event.toBranch}`);\n else if (event.fromBranch !== '') parts.push(`branch=${event.fromBranch}`);\n else if (event.toBranch !== '') parts.push(`from=? to=${event.toBranch}`);\n if (event.oldMain !== '' || event.newMain !== '') parts.push(`oldMain=${event.oldMain || '?'} newMain=${event.newMain || '?'}`);\n if (event.conflict) parts.push('conflict=true');\n if (event.conflictFiles.length > 0) parts.push(`conflictFiles=${event.conflictFiles.length}(${event.conflictFiles.join(',')})`);\n if (event.outcome !== '') parts.push(`outcome=${event.outcome}`);\n // Emitted as one unit so the hash is never separated from the command that undoes the delete.\n // Prefer the archive TAG as the recover ref when there is one — it does not expire.\n if (event.worktreePath !== '') {\n parts.push(this.worktreeDetail(event));\n } else if (event.sha !== '' && event.archiveTag !== '') {\n parts.push(\n `sha=${event.sha} archiveTag=${event.archiveTag} ` +\n `recover=git checkout -b ${event.fromBranch || '?'} ${event.archiveTag}`,\n );\n } else if (event.sha !== '') {\n parts.push(`sha=${event.sha} recover=git branch ${event.fromBranch || '?'} ${event.sha}`);\n }\n for (const artifact of event.artifacts) parts.push(`artifact=${artifact}`);\n return parts.join(' ');\n }\n\n /**\n * The worktree flavour of the sha/recover token: path, tip, archive tag and the ONE command that\n * puts the directory AND the branch back together.\n *\n * `git worktree add -b <branch> <path> <ref>` is verified by hand and in worktree-reaper.spec.ts —\n * plain `git worktree add <path> <tag>` would restore the files at a DETACHED HEAD, silently losing\n * the branch name the reap destroyed. Falls back to the sha when nothing was archived (retention\n * 'delete'), and to a bare `git worktree add <path>` when there was no branch at all (detached).\n */\n private worktreeDetail(event: BranchMutationEvent): string {\n const ref = event.archiveTag !== '' ? event.archiveTag : event.sha;\n const tokens = [`worktree=${event.worktreePath}`];\n if (event.sha !== '') tokens.push(`sha=${event.sha}`);\n if (event.archiveTag !== '') tokens.push(`archiveTag=${event.archiveTag}`);\n if (ref === '') return tokens.join(' ');\n const recover = event.fromBranch !== ''\n ? `git worktree add -b ${event.fromBranch} ${event.worktreePath} ${ref}`\n : `git worktree add ${event.worktreePath} ${ref}`;\n tokens.push(`recover=${recover}`);\n return tokens.join(' ');\n }\n\n // Collapse newlines/tabs and cap length so one event is always exactly one log line.\n private oneLine(value: string): string {\n const flat = value.replace(/[\\t\\r\\n]+/g, ' ').trim();\n return flat.length <= MAX_DETAIL_LEN ? flat : flat.slice(0, MAX_DETAIL_LEN) + '…';\n }\n\n private rotateLogFile(logPath: string, prevPath: string): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const stat = fs.statSync(logPath);\n if (stat.size > MAX_LOG_BYTES) {\n if (fs.existsSync(prevPath)) fs.unlinkSync(prevPath);\n fs.renameSync(logPath, prevPath);\n }\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n}\n\n// Temporary migration delegators to BranchMutationLog — removed once consumers inject it.\nconst branchMutationLogSvc = new BranchMutationLog();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to BranchMutationLog; removed once consumers inject it\nexport function branchMutationLogPath(root: string): string {\n return branchMutationLogSvc.branchMutationLogPath(root);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to BranchMutationLog; removed once consumers inject it\nexport function logBranchMutation(root: string, event: BranchMutationEvent): void {\n branchMutationLogSvc.logBranchMutation(root, event);\n}\n"]}
package/src/index.d.ts CHANGED
@@ -9,7 +9,7 @@ export { loadAndValidate, LoadedConfig, ConfigLoader } from './load-config';
9
9
  export { formatConfigErrorsBanner, RETIRED_KEY_MARKER, RETIRED_TOP_LEVEL_MARKER, SECTION_PLACEMENT_MARKER, } from './config-error-banner';
10
10
  export { findConfigFile, CONFIG_FILENAME, ConfigFile, ConfigParseAttempt, CONFIG_PARSE_ATTEMPTS, CONFIG_PARSE_RETRY_MILLIS } from './config-file';
11
11
  export { RepoRootFinder, INSTRUCT_AI_DIR, INSTRUCT_AI_LEAF } from './repo-root';
12
- export { DotWebpieces, dotWebpieces, GitDirs, WORKTREE_STATE_DIR, LOGS_STATE_DIR, HOOKS_STATE_DIR } from './state-dir';
12
+ export { DotWebpieces, dotWebpieces, GitDirs, WORKTREE_STATE_DIR, LOGS_STATE_DIR } from './state-dir';
13
13
  export { StateDirMigrator, StateMigrationReport } from './state-dir-migration';
14
14
  export { AgedTreeSweeper, SweepCount, RETENTION_DAYS } from './aged-tree-sweep';
15
15
  export { ClaudeEnv, claudeEnv, CLAUDE_PROJECT_DIR_ENV, CLAUDE_PROJECT_DIR_UNSET } from './claude-env';
package/src/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ExcludePaths = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.RulesConfigDesign = exports.atRoot = exports.AtomicFile = exports.CLAUDE_PROJECT_DIR_UNSET = exports.CLAUDE_PROJECT_DIR_ENV = exports.claudeEnv = exports.ClaudeEnv = exports.RETENTION_DAYS = exports.SweepCount = exports.AgedTreeSweeper = exports.StateMigrationReport = exports.StateDirMigrator = exports.HOOKS_STATE_DIR = exports.LOGS_STATE_DIR = exports.WORKTREE_STATE_DIR = exports.GitDirs = exports.dotWebpieces = exports.DotWebpieces = exports.INSTRUCT_AI_LEAF = exports.INSTRUCT_AI_DIR = exports.RepoRootFinder = exports.CONFIG_PARSE_RETRY_MILLIS = exports.CONFIG_PARSE_ATTEMPTS = exports.ConfigParseAttempt = exports.ConfigFile = exports.CONFIG_FILENAME = exports.findConfigFile = exports.SECTION_PLACEMENT_MARKER = exports.RETIRED_TOP_LEVEL_MARKER = exports.RETIRED_KEY_MARKER = exports.formatConfigErrorsBanner = exports.ConfigLoader = exports.LoadedConfig = exports.loadAndValidate = exports.toError = exports.runMain = exports.CliArgs = exports.CliArgsCheck = exports.CliArgSet = exports.CliFlag = exports.CliUsage = exports.CliExitError = exports.RuleFailError = exports.InformAiError = exports.ResolvedRuleConfig = exports.ResolvedConfig = void 0;
4
- exports.getFileDiff = exports.getChangedFiles = exports.resolveBase = exports.detectBase = exports.getCurrentBranch = exports.shouldSkipRule = exports.FieldDef = exports.sectionForRule = exports.isHookGuard = exports.HOOK_GUARD_NAMES = exports.DEFAULT_MATCH_RULES = exports.renderMatchRuleMessage = exports.compileMatchRulePatterns = exports.isMatchRuleAllowedPath = exports.findMatchRuleViolations = exports.MatchRuleViolation = exports.MatchRuleConfig = exports.validateChecklistDocs = exports.retiredRuleFor = exports.retiredKeyErrorsIn = exports.retiredKeyError = exports.retiredEntry = exports.isRetiredKey = exports.RetiredConfigKey = exports.RETIRED_SCOPE_RULE = exports.RETIRED_SCOPE_KEY = exports.RETIRED_CONFIG_KEYS = exports.COMMENT_KEY_SUFFIX = exports.validateTopLevelKeys = exports.isCommentKey = exports.unknownKeyErrors = exports.validateCommandsSection = exports.seedEntryForRule = exports.recommendedSeedModeFor = exports.recommendedSeedMode = exports.allRuleNames = exports.validateMatchRulesSection = exports.validateExcludePaths = exports.validateSectionPlacement = exports.validateChecklistsSection = exports.validatePrGateSection = exports.validateWebpiecesConfig = exports.TemplateWriter = exports.writeTemplate = exports.writeTemplateIfMissing = exports.loadTemplate = exports.defaultRulesDir = exports.defaultRules = exports.matchesAnyGlob = exports.isPathExcluded = void 0;
5
- exports.RedirectHowToMergeMainConfig = exports.PrMergeGuardConfig = exports.MergeInProgressGuardConfig = exports.PrCreationOrPushGuardConfig = exports.BranchCreationGuardConfig = exports.RoleTagConfig = exports.FrameworkTagConfig = exports.InjectAnnotationNotNeededForConcreteClassConfig = exports.NoFunctionOutsideClassConfig = exports.NoProcessExitOutsideMainConfig = exports.NoCustomCssConfig = exports.NoSymbolDiTokensConfig = exports.AngularNoDirectApiInResolverConfig = exports.ThrowCauseRequiredConfig = exports.CatchErrorPatternConfig = exports.NoUnmanagedExceptionsConfig = exports.NoDestructureConfig = exports.PrismaConverterConfig = exports.PrismaValidateDtosConfig = exports.NoImplicitAnyConfig = exports.NoAnyUnknownConfig = exports.NoInlineTypeLiteralsConfig = exports.RequireReturnTypeConfig = exports.MaxFileLinesConfig = exports.MaxMethodLinesConfig = exports.WP_FINISH_PUSH_DEV = exports.WP_PUSH_DEV = exports.WP_FINISH_UPSERT_PR = exports.WP_START_UPSERT_PR = exports.WP_FINISH_UPDATE = exports.WP_START_UPDATE = exports.SyncFlowGuidance = exports.WebpiecesRulesConfig = exports.PUSH_DEV_STATE_FILE = exports.MERGE_EXPLANATION_FILE = exports.MERGE_IN_PROGRESS_FILE = exports.PR_REVIEW_DIR = exports.MERGE_INFO_DIR = exports.WEBPIECES_TMP_DIR = exports.hasDisable = exports.RULE_NAMES = exports.WEBPIECES_DISABLE = exports.AbstractRule = exports.ChangedFilesOptions = exports.DiffRange = exports.DiffScope = exports.isNewOrModified = exports.hasChangesInRange = exports.findNewMethodSignaturesInDiff = exports.getChangedLineNumbers = void 0;
6
- exports.toChecklist = exports.ChecklistDefinition = exports.MERGE_MODES = exports.MERGE_MODE_NONE = exports.MERGE_MODE_AUTO = exports.buildDevDeployConfig = exports.buildLandPrConfig = exports.buildPrGateConfig = exports.defaultDevDeployConfig = exports.defaultLandPrConfig = exports.defaultPrGateConfig = exports.defaultGates = exports.ReviewContextEntry = exports.DEFAULT_DEV_BRANCH = exports.DEFAULT_DEV_BRANCH_NAMESPACE = exports.DevDeployConfig = exports.LandPrConfig = exports.PrGateConfig = exports.GateDefinition = exports.StaleMainBashGuardConfig = exports.MergedBranchBashGuardConfig = exports.ReadStaleGuardConfig = exports.FeatureBranchGuardConfig = exports.CLIENT_CREATION_SEVERITIES = exports.NoClientCreationOutsideServerOrClientConfig = exports.VALIDATE_TS_MODES = exports.STRUCTURAL_MODES = exports.ON_OFF_MODES = exports.THROW_CAUSE_MODES = exports.DIRECT_API_RESOLVER_MODES = exports.PRISMA_CONVERTER_MODES = exports.PRISMA_DTOS_MODES = exports.PROJECT_MODES = exports.MODIFIED_CODE_MODES = exports.INLINE_TYPE_MODES = exports.RETURN_TYPE_MODES = exports.FILE_LIMIT_MODES = exports.METHOD_LIMIT_MODES = exports.BaseRuleConfig = exports.ValidateEslintSyncConfig = exports.ValidateVersionsLockedConfig = exports.ValidatePackageJsonConfig = exports.ValidateNoArchitectureCyclesConfig = exports.ValidateArchitectureUnchangedConfig = exports.ValidateTsInSrcConfig = exports.NoJsFilesConfig = exports.DiGraphConfig = exports.NxWiringConfig = exports.RuntimeArchitectureConfig = exports.NoFileImportCyclesConfig = void 0;
7
- exports.reviewJsonPath = exports.prDirFor = exports.loadReviewJson = exports.ReviewJsonService = exports.ChecklistReviewContext = exports.RequiredChecklist = exports.VERDICT_STATUSES = exports.VERDICT_RED = exports.VERDICT_YELLOW = exports.VERDICT_GREEN = exports.CK_BAD_FORMAT = exports.CK_MISSING = exports.CK_FAIL = exports.CK_OVERRIDDEN = exports.CK_WARN = exports.CK_PASS = exports.ChecklistVerdict = exports.ChecklistResult = exports.PrContext = exports.ReviewJson = exports.DEFAULT_RETENTION_DAYS = exports.ProvenanceWriteRequest = exports.OfferedContext = exports.ReviewerPaths = exports.ReviewerTranscript = exports.ReviewProvenance = exports.ReviewProvenanceService = exports.PROVENANCE_SKIPPED = exports.PROVENANCE_MISSING = exports.PROVENANCE_OK = exports.ProvenanceResult = exports.TranscriptScan = exports.EvidenceRequest = exports.ReviewerEvidence = exports.SubagentProvenanceService = exports.verifyGateToken = exports.extractGateToken = exports.gateTokenMarker = exports.computeGateToken = exports.GateTokenService = exports.ALL_DIFF_ONE_READ_LINES = exports.READ_TRUNCATION_LINES = exports.ContextEntry = exports.BriefedFile = exports.ReviewerBriefing = exports.ReviewerInstructionsService = exports.ChecklistInstructionsService = exports.ChecklistValidator = exports.formatFileList = exports.normalizeChecklistDoc = void 0;
8
- exports.BRANCH_RETENTIONS = exports.ARCHIVE_TAG_PREFIX = exports.ArchiveResult = exports.BranchArchiver = exports.PROMPTABLE_CLASSIFICATIONS = exports.CLASSIFICATION_DETACHED = exports.CLASSIFICATION_CURRENT = exports.CLASSIFICATION_LOCKED = exports.CLASSIFICATION_PRUNABLE = exports.CLASSIFICATION_IN_USE = exports.CLASSIFICATION_NEVER_PROPOSED = exports.CLASSIFICATION_CONTENT_IN_MAIN = exports.CLASSIFICATION_SUPERSEDED = exports.CLASSIFICATION_NO_COMMITS = exports.CLASSIFICATION_BACKUP_OF_LIVE = exports.CLASSIFICATION_BACKUP_OF_MERGED = exports.CLASSIFICATION_MERGED_PR = exports.CACHE_STALE_AFTER_MS = exports.CacheFreshness = exports.MergedBranchesService = exports.MergedBranchesCache = exports.DeletableWorktree = exports.DeletableBranch = exports.MergedBranch = exports.squashRecoverySteps = exports.stampCleanMainSyncStatus = exports.computeMainSyncStatus = exports.finishedLock = exports.inProcessLock = exports.tryAcquireMainSyncLock = exports.isRefreshInProgress = exports.isLockStale = exports.writeMainSyncLock = exports.readMainSyncLock = exports.computeAllMainSyncStatuses = exports.writeMainSyncStatusFile = exports.writeMainSyncStatus = exports.readMainSyncStatusFile = exports.readMainSyncStatus = exports.mainSyncLockPath = exports.mainSyncStatusPath = exports.DEFAULT_HANG_TIMEOUT_MINUTES = exports.MainSyncStatusService = exports.MainSyncLock = exports.MAIN_SYNC_STATUS_VERSION = exports.PullRequestIndex = exports.MainSyncFileStore = exports.MainSyncStatusFile = exports.MainSyncStatus = exports.reviewJsonSchemaHint = void 0;
9
- exports.DEFAULT_MERGE_COMPLETE_COMMAND = exports.DEFAULT_UPSERT_PR_COMMAND = exports.buildCommandsConfig = exports.CommandsConfig = exports.logBranchMutation = exports.branchMutationLogPath = exports.BranchMutationLog = exports.BranchMutationEvent = exports.WorktreeReaper = exports.WorktreeReapResult = exports.ReapedWorktree = exports.BranchReaper = exports.ReapResult = exports.ReapedBranch = exports.WorktreeService = exports.Worktree = exports.BRANCH_RETENTION_KEEP = exports.BRANCH_RETENTION_ARCHIVE_TAG = exports.BRANCH_RETENTION_DELETE = void 0;
3
+ exports.isPathExcluded = exports.ExcludePaths = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.RulesConfigDesign = exports.atRoot = exports.AtomicFile = exports.CLAUDE_PROJECT_DIR_UNSET = exports.CLAUDE_PROJECT_DIR_ENV = exports.claudeEnv = exports.ClaudeEnv = exports.RETENTION_DAYS = exports.SweepCount = exports.AgedTreeSweeper = exports.StateMigrationReport = exports.StateDirMigrator = exports.LOGS_STATE_DIR = exports.WORKTREE_STATE_DIR = exports.GitDirs = exports.dotWebpieces = exports.DotWebpieces = exports.INSTRUCT_AI_LEAF = exports.INSTRUCT_AI_DIR = exports.RepoRootFinder = exports.CONFIG_PARSE_RETRY_MILLIS = exports.CONFIG_PARSE_ATTEMPTS = exports.ConfigParseAttempt = exports.ConfigFile = exports.CONFIG_FILENAME = exports.findConfigFile = exports.SECTION_PLACEMENT_MARKER = exports.RETIRED_TOP_LEVEL_MARKER = exports.RETIRED_KEY_MARKER = exports.formatConfigErrorsBanner = exports.ConfigLoader = exports.LoadedConfig = exports.loadAndValidate = exports.toError = exports.runMain = exports.CliArgs = exports.CliArgsCheck = exports.CliArgSet = exports.CliFlag = exports.CliUsage = exports.CliExitError = exports.RuleFailError = exports.InformAiError = exports.ResolvedRuleConfig = exports.ResolvedConfig = void 0;
4
+ exports.getChangedLineNumbers = exports.getFileDiff = exports.getChangedFiles = exports.resolveBase = exports.detectBase = exports.getCurrentBranch = exports.shouldSkipRule = exports.FieldDef = exports.sectionForRule = exports.isHookGuard = exports.HOOK_GUARD_NAMES = exports.DEFAULT_MATCH_RULES = exports.renderMatchRuleMessage = exports.compileMatchRulePatterns = exports.isMatchRuleAllowedPath = exports.findMatchRuleViolations = exports.MatchRuleViolation = exports.MatchRuleConfig = exports.validateChecklistDocs = exports.retiredRuleFor = exports.retiredKeyErrorsIn = exports.retiredKeyError = exports.retiredEntry = exports.isRetiredKey = exports.RetiredConfigKey = exports.RETIRED_SCOPE_RULE = exports.RETIRED_SCOPE_KEY = exports.RETIRED_CONFIG_KEYS = exports.COMMENT_KEY_SUFFIX = exports.validateTopLevelKeys = exports.isCommentKey = exports.unknownKeyErrors = exports.validateCommandsSection = exports.seedEntryForRule = exports.recommendedSeedModeFor = exports.recommendedSeedMode = exports.allRuleNames = exports.validateMatchRulesSection = exports.validateExcludePaths = exports.validateSectionPlacement = exports.validateChecklistsSection = exports.validatePrGateSection = exports.validateWebpiecesConfig = exports.TemplateWriter = exports.writeTemplate = exports.writeTemplateIfMissing = exports.loadTemplate = exports.defaultRulesDir = exports.defaultRules = exports.matchesAnyGlob = void 0;
5
+ exports.NoFileImportCyclesConfig = exports.RedirectHowToMergeMainConfig = exports.PrMergeGuardConfig = exports.MergeInProgressGuardConfig = exports.PrCreationOrPushGuardConfig = exports.BranchCreationGuardConfig = exports.RoleTagConfig = exports.FrameworkTagConfig = exports.InjectAnnotationNotNeededForConcreteClassConfig = exports.NoFunctionOutsideClassConfig = exports.NoProcessExitOutsideMainConfig = exports.NoCustomCssConfig = exports.NoSymbolDiTokensConfig = exports.AngularNoDirectApiInResolverConfig = exports.ThrowCauseRequiredConfig = exports.CatchErrorPatternConfig = exports.NoUnmanagedExceptionsConfig = exports.NoDestructureConfig = exports.PrismaConverterConfig = exports.PrismaValidateDtosConfig = exports.NoImplicitAnyConfig = exports.NoAnyUnknownConfig = exports.NoInlineTypeLiteralsConfig = exports.RequireReturnTypeConfig = exports.MaxFileLinesConfig = exports.MaxMethodLinesConfig = exports.WP_FINISH_PUSH_DEV = exports.WP_PUSH_DEV = exports.WP_FINISH_UPSERT_PR = exports.WP_START_UPSERT_PR = exports.WP_FINISH_UPDATE = exports.WP_START_UPDATE = exports.SyncFlowGuidance = exports.WebpiecesRulesConfig = exports.PUSH_DEV_STATE_FILE = exports.MERGE_EXPLANATION_FILE = exports.MERGE_IN_PROGRESS_FILE = exports.PR_REVIEW_DIR = exports.MERGE_INFO_DIR = exports.WEBPIECES_TMP_DIR = exports.hasDisable = exports.RULE_NAMES = exports.WEBPIECES_DISABLE = exports.AbstractRule = exports.ChangedFilesOptions = exports.DiffRange = exports.DiffScope = exports.isNewOrModified = exports.hasChangesInRange = exports.findNewMethodSignaturesInDiff = void 0;
6
+ exports.normalizeChecklistDoc = exports.toChecklist = exports.ChecklistDefinition = exports.MERGE_MODES = exports.MERGE_MODE_NONE = exports.MERGE_MODE_AUTO = exports.buildDevDeployConfig = exports.buildLandPrConfig = exports.buildPrGateConfig = exports.defaultDevDeployConfig = exports.defaultLandPrConfig = exports.defaultPrGateConfig = exports.defaultGates = exports.ReviewContextEntry = exports.DEFAULT_DEV_BRANCH = exports.DEFAULT_DEV_BRANCH_NAMESPACE = exports.DevDeployConfig = exports.LandPrConfig = exports.PrGateConfig = exports.GateDefinition = exports.StaleMainBashGuardConfig = exports.MergedBranchBashGuardConfig = exports.ReadStaleGuardConfig = exports.FeatureBranchGuardConfig = exports.CLIENT_CREATION_SEVERITIES = exports.NoClientCreationOutsideServerOrClientConfig = exports.VALIDATE_TS_MODES = exports.STRUCTURAL_MODES = exports.ON_OFF_MODES = exports.THROW_CAUSE_MODES = exports.DIRECT_API_RESOLVER_MODES = exports.PRISMA_CONVERTER_MODES = exports.PRISMA_DTOS_MODES = exports.PROJECT_MODES = exports.MODIFIED_CODE_MODES = exports.INLINE_TYPE_MODES = exports.RETURN_TYPE_MODES = exports.FILE_LIMIT_MODES = exports.METHOD_LIMIT_MODES = exports.BaseRuleConfig = exports.ValidateEslintSyncConfig = exports.ValidateVersionsLockedConfig = exports.ValidatePackageJsonConfig = exports.ValidateNoArchitectureCyclesConfig = exports.ValidateArchitectureUnchangedConfig = exports.ValidateTsInSrcConfig = exports.NoJsFilesConfig = exports.DiGraphConfig = exports.NxWiringConfig = exports.RuntimeArchitectureConfig = void 0;
7
+ exports.reviewJsonSchemaHint = exports.reviewJsonPath = exports.prDirFor = exports.loadReviewJson = exports.ReviewJsonService = exports.ChecklistReviewContext = exports.RequiredChecklist = exports.VERDICT_STATUSES = exports.VERDICT_RED = exports.VERDICT_YELLOW = exports.VERDICT_GREEN = exports.CK_BAD_FORMAT = exports.CK_MISSING = exports.CK_FAIL = exports.CK_OVERRIDDEN = exports.CK_WARN = exports.CK_PASS = exports.ChecklistVerdict = exports.ChecklistResult = exports.PrContext = exports.ReviewJson = exports.DEFAULT_RETENTION_DAYS = exports.ProvenanceWriteRequest = exports.OfferedContext = exports.ReviewerPaths = exports.ReviewerTranscript = exports.ReviewProvenance = exports.ReviewProvenanceService = exports.PROVENANCE_SKIPPED = exports.PROVENANCE_MISSING = exports.PROVENANCE_OK = exports.ProvenanceResult = exports.TranscriptScan = exports.EvidenceRequest = exports.ReviewerEvidence = exports.SubagentProvenanceService = exports.verifyGateToken = exports.extractGateToken = exports.gateTokenMarker = exports.computeGateToken = exports.GateTokenService = exports.ALL_DIFF_ONE_READ_LINES = exports.READ_TRUNCATION_LINES = exports.ContextEntry = exports.BriefedFile = exports.ReviewerBriefing = exports.ReviewerInstructionsService = exports.ChecklistInstructionsService = exports.ChecklistValidator = exports.formatFileList = void 0;
8
+ exports.BRANCH_RETENTION_DELETE = exports.BRANCH_RETENTIONS = exports.ARCHIVE_TAG_PREFIX = exports.ArchiveResult = exports.BranchArchiver = exports.PROMPTABLE_CLASSIFICATIONS = exports.CLASSIFICATION_DETACHED = exports.CLASSIFICATION_CURRENT = exports.CLASSIFICATION_LOCKED = exports.CLASSIFICATION_PRUNABLE = exports.CLASSIFICATION_IN_USE = exports.CLASSIFICATION_NEVER_PROPOSED = exports.CLASSIFICATION_CONTENT_IN_MAIN = exports.CLASSIFICATION_SUPERSEDED = exports.CLASSIFICATION_NO_COMMITS = exports.CLASSIFICATION_BACKUP_OF_LIVE = exports.CLASSIFICATION_BACKUP_OF_MERGED = exports.CLASSIFICATION_MERGED_PR = exports.CACHE_STALE_AFTER_MS = exports.CacheFreshness = exports.MergedBranchesService = exports.MergedBranchesCache = exports.DeletableWorktree = exports.DeletableBranch = exports.MergedBranch = exports.squashRecoverySteps = exports.stampCleanMainSyncStatus = exports.computeMainSyncStatus = exports.finishedLock = exports.inProcessLock = exports.tryAcquireMainSyncLock = exports.isRefreshInProgress = exports.isLockStale = exports.writeMainSyncLock = exports.readMainSyncLock = exports.computeAllMainSyncStatuses = exports.writeMainSyncStatusFile = exports.writeMainSyncStatus = exports.readMainSyncStatusFile = exports.readMainSyncStatus = exports.mainSyncLockPath = exports.mainSyncStatusPath = exports.DEFAULT_HANG_TIMEOUT_MINUTES = exports.MainSyncStatusService = exports.MainSyncLock = exports.MAIN_SYNC_STATUS_VERSION = exports.PullRequestIndex = exports.MainSyncFileStore = exports.MainSyncStatusFile = exports.MainSyncStatus = void 0;
9
+ exports.DEFAULT_MERGE_COMPLETE_COMMAND = exports.DEFAULT_UPSERT_PR_COMMAND = exports.buildCommandsConfig = exports.CommandsConfig = exports.logBranchMutation = exports.branchMutationLogPath = exports.BranchMutationLog = exports.BranchMutationEvent = exports.WorktreeReaper = exports.WorktreeReapResult = exports.ReapedWorktree = exports.BranchReaper = exports.ReapResult = exports.ReapedBranch = exports.WorktreeService = exports.Worktree = exports.BRANCH_RETENTION_KEEP = exports.BRANCH_RETENTION_ARCHIVE_TAG = void 0;
10
10
  var types_1 = require("./types");
11
11
  Object.defineProperty(exports, "ResolvedConfig", { enumerable: true, get: function () { return types_1.ResolvedConfig; } });
12
12
  Object.defineProperty(exports, "ResolvedRuleConfig", { enumerable: true, get: function () { return types_1.ResolvedRuleConfig; } });
@@ -56,7 +56,6 @@ Object.defineProperty(exports, "dotWebpieces", { enumerable: true, get: function
56
56
  Object.defineProperty(exports, "GitDirs", { enumerable: true, get: function () { return state_dir_1.GitDirs; } });
57
57
  Object.defineProperty(exports, "WORKTREE_STATE_DIR", { enumerable: true, get: function () { return state_dir_1.WORKTREE_STATE_DIR; } });
58
58
  Object.defineProperty(exports, "LOGS_STATE_DIR", { enumerable: true, get: function () { return state_dir_1.LOGS_STATE_DIR; } });
59
- Object.defineProperty(exports, "HOOKS_STATE_DIR", { enumerable: true, get: function () { return state_dir_1.HOOKS_STATE_DIR; } });
60
59
  var state_dir_migration_1 = require("./state-dir-migration");
61
60
  Object.defineProperty(exports, "StateDirMigrator", { enumerable: true, get: function () { return state_dir_migration_1.StateDirMigrator; } });
62
61
  Object.defineProperty(exports, "StateMigrationReport", { enumerable: true, get: function () { return state_dir_migration_1.StateMigrationReport; } });
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/index.ts"],"names":[],"mappings":";;;;;;;;;AAAA,iCAA0E;AAAjE,uGAAA,cAAc,OAAA;AAAE,2GAAA,kBAAkB,OAAA;AAC3C,qDAAkD;AAAzC,gHAAA,aAAa,OAAA;AACtB,qDAAkD;AAAzC,gHAAA,aAAa,OAAA;AACtB,mDAAgD;AAAvC,8GAAA,YAAY,OAAA;AACrB,uCAAiF;AAAxE,oGAAA,QAAQ,OAAA;AAAE,mGAAA,OAAO,OAAA;AAAE,qGAAA,SAAS,OAAA;AAAE,wGAAA,YAAY,OAAA;AAAE,mGAAA,OAAO,OAAA;AAC5D,uCAAqC;AAA5B,mGAAA,OAAO,OAAA;AAChB,uCAAqC;AAA5B,mGAAA,OAAO,OAAA;AAChB,6CAA4E;AAAnE,8GAAA,eAAe,OAAA;AAAE,2GAAA,YAAY,OAAA;AAAE,2GAAA,YAAY,OAAA;AACpD,iGAAiG;AACjG,sCAAsC;AACtC,6DAK+B;AAJ3B,+HAAA,wBAAwB,OAAA;AACxB,yHAAA,kBAAkB,OAAA;AAClB,+HAAA,wBAAwB,OAAA;AACxB,+HAAA,wBAAwB,OAAA;AAE5B,6CAAkJ;AAAzI,6GAAA,cAAc,OAAA;AAAE,8GAAA,eAAe,OAAA;AAAE,yGAAA,UAAU,OAAA;AAAE,iHAAA,kBAAkB,OAAA;AAAE,oHAAA,qBAAqB,OAAA;AAAE,wHAAA,yBAAyB,OAAA;AAC1H,yCAAgF;AAAvE,2GAAA,cAAc,OAAA;AAAE,4GAAA,eAAe,OAAA;AAAE,6GAAA,gBAAgB,OAAA;AAC1D,wGAAwG;AACxG,8FAA8F;AAC9F,yCAAuH;AAA9G,yGAAA,YAAY,OAAA;AAAE,yGAAA,YAAY,OAAA;AAAE,oGAAA,OAAO,OAAA;AAAE,+GAAA,kBAAkB,OAAA;AAAE,2GAAA,cAAc,OAAA;AAAE,4GAAA,eAAe,OAAA;AACjG,6DAA+E;AAAtE,uHAAA,gBAAgB,OAAA;AAAE,2HAAA,oBAAoB,OAAA;AAC/C,uGAAuG;AACvG,yGAAyG;AACzG,mGAAmG;AACnG,2FAA2F;AAC3F,4DAA4D;AAC5D,qDAAgF;AAAvE,kHAAA,eAAe,OAAA;AAAE,6GAAA,UAAU,OAAA;AAAE,iHAAA,cAAc,OAAA;AACpD,2CAAsG;AAA7F,uGAAA,SAAS,OAAA;AAAE,uGAAA,SAAS,OAAA;AAAE,oHAAA,sBAAsB,OAAA;AAAE,sHAAA,wBAAwB,OAAA;AAC/E,6CAA2C;AAAlC,yGAAA,UAAU,OAAA;AACnB,iGAAiG;AACjG,gGAAgG;AAChG,qCAAmC;AAA1B,iGAAA,MAAM,OAAA;AACf,6DAA0D;AAAjD,wHAAA,iBAAiB,OAAA;AAC1B,2BAA8E;AAArE,oGAAA,cAAc,OAAA;AAAE,sGAAA,gBAAgB,OAAA;AAAE,0GAAA,oBAAoB,OAAA;AAC/D,2DAAoD;AAA3C,kHAAA,YAAY,OAAA;AACrB,iDAAiE;AAAxD,+GAAA,cAAc,OAAA;AAAE,+GAAA,cAAc,OAAA;AACvC,iDAAgE;AAAvD,6GAAA,YAAY,OAAA;AAAE,gHAAA,eAAe,OAAA;AACtC,iDAAsG;AAA7F,6GAAA,YAAY,OAAA;AAAE,uHAAA,sBAAsB,OAAA;AAAE,8GAAA,aAAa,OAAA;AAAE,+GAAA,cAAc,OAAA;AAC5E,qDAAsQ;AAA7P,0HAAA,uBAAuB,OAAA;AAAE,wHAAA,qBAAqB,OAAA;AAAE,4HAAA,yBAAyB,OAAA;AAAE,2HAAA,wBAAwB,OAAA;AAAE,uHAAA,oBAAoB,OAAA;AAAE,4HAAA,yBAAyB,OAAA;AAAE,+GAAA,YAAY,OAAA;AAAE,sHAAA,mBAAmB,OAAA;AAAE,yHAAA,sBAAsB,OAAA;AAAE,mHAAA,gBAAgB,OAAA;AAC1O,6EAAwE;AAA/D,sIAAA,uBAAuB,OAAA;AAChC,uDAA8G;AAArG,oHAAA,gBAAgB,OAAA;AAAE,gHAAA,YAAY,OAAA;AAAE,wHAAA,oBAAoB,OAAA;AAAE,sHAAA,kBAAkB,OAAA;AACjF,uGAAuG;AACvG,wEAAwE;AACxE,6DAAsM;AAA7L,0HAAA,mBAAmB,OAAA;AAAE,wHAAA,iBAAiB,OAAA;AAAE,yHAAA,kBAAkB,OAAA;AAAE,uHAAA,gBAAgB,OAAA;AAAE,mHAAA,YAAY,OAAA;AAAE,mHAAA,YAAY,OAAA;AAAE,sHAAA,eAAe,OAAA;AAAE,yHAAA,kBAAkB,OAAA;AAAE,qHAAA,cAAc,OAAA;AACtK,uEAAmE;AAA1D,iIAAA,qBAAqB,OAAA;AAC9B,2DAQ8B;AAP1B,qHAAA,eAAe,OAAA;AACf,wHAAA,kBAAkB,OAAA;AAClB,6HAAA,uBAAuB,OAAA;AACvB,4HAAA,sBAAsB,OAAA;AACtB,8HAAA,wBAAwB,OAAA;AACxB,4HAAA,sBAAsB,OAAA;AACtB,yHAAA,mBAAmB,OAAA;AAGvB,uCAA2E;AAAlE,4GAAA,gBAAgB,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,0GAAA,cAAc,OAAA;AACtD,yCAAuC;AAA9B,qGAAA,QAAQ,OAAA;AAEjB,yCAA+D;AAAtD,2GAAA,cAAc,OAAA;AAAE,6GAAA,gBAAgB,OAAA;AAEzC,2CAYsB;AAXlB,wGAAA,UAAU,OAAA;AACV,yGAAA,WAAW,OAAA;AACX,6GAAA,eAAe,OAAA;AACf,yGAAA,WAAW,OAAA;AACX,mHAAA,qBAAqB,OAAA;AACrB,2HAAA,6BAA6B,OAAA;AAC7B,+GAAA,iBAAiB,OAAA;AACjB,6GAAA,eAAe,OAAA;AACf,uGAAA,SAAS,OAAA;AACT,uGAAA,SAAS,OAAA;AACT,iHAAA,mBAAmB,OAAA;AAEvB,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AACrB,yCAUqB;AATjB,8GAAA,iBAAiB,OAAA;AACjB,uGAAA,UAAU,OAAA;AACV,uGAAA,UAAU,OAAA;AACV,8GAAA,iBAAiB,OAAA;AACjB,2GAAA,cAAc,OAAA;AACd,0GAAA,aAAa,OAAA;AACb,mHAAA,sBAAsB,OAAA;AACtB,mHAAA,sBAAsB,OAAA;AACtB,gHAAA,mBAAmB,OAAA;AAEvB,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAC7B,2DAQ8B;AAP1B,sHAAA,gBAAgB,OAAA;AAChB,qHAAA,eAAe,OAAA;AACf,sHAAA,gBAAgB,OAAA;AAChB,wHAAA,kBAAkB,OAAA;AAClB,yHAAA,mBAAmB,OAAA;AACnB,iHAAA,WAAW,OAAA;AACX,wHAAA,kBAAkB,OAAA;AAEtB,+CAsCwB;AArCpB,oHAAA,oBAAoB,OAAA;AACpB,kHAAA,kBAAkB,OAAA;AAClB,uHAAA,uBAAuB,OAAA;AACvB,0HAAA,0BAA0B,OAAA;AAC1B,kHAAA,kBAAkB,OAAA;AAClB,mHAAA,mBAAmB,OAAA;AACnB,wHAAA,wBAAwB,OAAA;AACxB,qHAAA,qBAAqB,OAAA;AACrB,mHAAA,mBAAmB,OAAA;AACnB,2HAAA,2BAA2B,OAAA;AAC3B,uHAAA,uBAAuB,OAAA;AACvB,wHAAA,wBAAwB,OAAA;AACxB,kIAAA,kCAAkC,OAAA;AAClC,sHAAA,sBAAsB,OAAA;AACtB,iHAAA,iBAAiB,OAAA;AACjB,8HAAA,8BAA8B,OAAA;AAC9B,4HAAA,4BAA4B,OAAA;AAC5B,+IAAA,+CAA+C,OAAA;AAC/C,kHAAA,kBAAkB,OAAA;AAClB,6GAAA,aAAa,OAAA;AACb,yHAAA,yBAAyB,OAAA;AACzB,2HAAA,2BAA2B,OAAA;AAC3B,0HAAA,0BAA0B,OAAA;AAC1B,kHAAA,kBAAkB,OAAA;AAClB,4HAAA,4BAA4B,OAAA;AAC5B,wHAAA,wBAAwB,OAAA;AACxB,yHAAA,yBAAyB,OAAA;AACzB,8GAAA,cAAc,OAAA;AACd,6GAAA,aAAa,OAAA;AACb,+GAAA,eAAe,OAAA;AACf,qHAAA,qBAAqB,OAAA;AACrB,mIAAA,mCAAmC,OAAA;AACnC,kIAAA,kCAAkC,OAAA;AAClC,yHAAA,yBAAyB,OAAA;AACzB,4HAAA,4BAA4B,OAAA;AAC5B,wHAAA,wBAAwB,OAAA;AACxB,8GAAA,cAAc,OAAA;AAElB,wFAAwF;AACxF,+CAcwB;AAbpB,kHAAA,kBAAkB,OAAA;AAClB,gHAAA,gBAAgB,OAAA;AAChB,iHAAA,iBAAiB,OAAA;AACjB,iHAAA,iBAAiB,OAAA;AACjB,mHAAA,mBAAmB,OAAA;AACnB,6GAAA,aAAa,OAAA;AACb,iHAAA,iBAAiB,OAAA;AACjB,sHAAA,sBAAsB,OAAA;AACtB,yHAAA,yBAAyB,OAAA;AACzB,iHAAA,iBAAiB,OAAA;AACjB,4GAAA,YAAY,OAAA;AACZ,gHAAA,gBAAgB,OAAA;AAChB,iHAAA,iBAAiB,OAAA;AAErB,yEAGqC;AAFjC,wJAAA,2CAA2C,OAAA;AAC3C,uIAAA,0BAA0B,OAAA;AAkB9B,qEAKmC;AAJ/B,mIAAA,wBAAwB,OAAA;AACxB,+HAAA,oBAAoB,OAAA;AACpB,sIAAA,2BAA2B,OAAA;AAC3B,mIAAA,wBAAwB,OAAA;AAE5B,mDAkB0B;AAjBtB,gHAAA,cAAc,OAAA;AACd,8GAAA,YAAY,OAAA;AACZ,8GAAA,YAAY,OAAA;AACZ,iHAAA,eAAe,OAAA;AACf,8HAAA,4BAA4B,OAAA;AAC5B,oHAAA,kBAAkB,OAAA;AAClB,oHAAA,kBAAkB,OAAA;AAClB,8GAAA,YAAY,OAAA;AACZ,qHAAA,mBAAmB,OAAA;AACnB,qHAAA,mBAAmB,OAAA;AACnB,wHAAA,sBAAsB,OAAA;AACtB,mHAAA,iBAAiB,OAAA;AACjB,mHAAA,iBAAiB,OAAA;AACjB,sHAAA,oBAAoB,OAAA;AACpB,iHAAA,eAAe,OAAA;AACf,iHAAA,eAAe,OAAA;AACf,6GAAA,WAAW,OAAA;AAEf,uDAK4B;AAJxB,uHAAA,mBAAmB,OAAA;AACnB,+GAAA,WAAW,OAAA;AACX,yHAAA,qBAAqB,OAAA;AACrB,kHAAA,cAAc,OAAA;AAGlB,6DAA2D;AAAlD,yHAAA,kBAAkB,OAAA;AAC3B,mEAAwE;AAA/D,sIAAA,4BAA4B,OAAA;AACrC,iEAOiC;AAN7B,oIAAA,2BAA2B,OAAA;AAC3B,yHAAA,gBAAgB,OAAA;AAChB,oHAAA,WAAW,OAAA;AACX,qHAAA,YAAY,OAAA;AACZ,8HAAA,qBAAqB,OAAA;AACrB,gIAAA,uBAAuB,OAAA;AAE3B,2CAMsB;AALlB,8GAAA,gBAAgB,OAAA;AAChB,8GAAA,gBAAgB,OAAA;AAChB,6GAAA,eAAe,OAAA;AACf,8GAAA,gBAAgB,OAAA;AAChB,6GAAA,eAAe,OAAA;AAEnB,6DAS+B;AAR3B,gIAAA,yBAAyB,OAAA;AACzB,uHAAA,gBAAgB,OAAA;AAChB,sHAAA,eAAe,OAAA;AACf,qHAAA,cAAc,OAAA;AACd,uHAAA,gBAAgB,OAAA;AAChB,oHAAA,aAAa,OAAA;AACb,yHAAA,kBAAkB,OAAA;AAClB,yHAAA,kBAAkB,OAAA;AAEtB,yDAQ6B;AAPzB,4HAAA,uBAAuB,OAAA;AACvB,qHAAA,gBAAgB,OAAA;AAChB,uHAAA,kBAAkB,OAAA;AAClB,kHAAA,aAAa,OAAA;AACb,mHAAA,cAAc,OAAA;AACd,2HAAA,sBAAsB,OAAA;AACtB,2HAAA,sBAAsB,OAAA;AAE1B,6CAsBuB;AArBnB,yGAAA,UAAU,OAAA;AACV,wGAAA,SAAS,OAAA;AACT,8GAAA,eAAe,OAAA;AACf,+GAAA,gBAAgB,OAAA;AAChB,sGAAA,OAAO,OAAA;AACP,sGAAA,OAAO,OAAA;AACP,4GAAA,aAAa,OAAA;AACb,sGAAA,OAAO,OAAA;AACP,yGAAA,UAAU,OAAA;AACV,4GAAA,aAAa,OAAA;AACb,4GAAA,aAAa,OAAA;AACb,6GAAA,cAAc,OAAA;AACd,0GAAA,WAAW,OAAA;AACX,+GAAA,gBAAgB,OAAA;AAChB,gHAAA,iBAAiB,OAAA;AACjB,qHAAA,sBAAsB,OAAA;AACtB,gHAAA,iBAAiB,OAAA;AACjB,6GAAA,cAAc,OAAA;AACd,uGAAA,QAAQ,OAAA;AACR,6GAAA,cAAc,OAAA;AACd,mHAAA,oBAAoB,OAAA;AAExB,mDAM0B;AALtB,gHAAA,cAAc,OAAA;AACd,oHAAA,kBAAkB,OAAA;AAClB,mHAAA,iBAAiB,OAAA;AACjB,kHAAA,gBAAgB,OAAA;AAChB,0HAAA,wBAAwB,OAAA;AAE5B,uDAqB4B;AApBxB,gHAAA,YAAY,OAAA;AACZ,yHAAA,qBAAqB,OAAA;AACrB,gIAAA,4BAA4B,OAAA;AAC5B,sHAAA,kBAAkB,OAAA;AAClB,oHAAA,gBAAgB,OAAA;AAChB,sHAAA,kBAAkB,OAAA;AAClB,0HAAA,sBAAsB,OAAA;AACtB,uHAAA,mBAAmB,OAAA;AACnB,2HAAA,uBAAuB,OAAA;AACvB,8HAAA,0BAA0B,OAAA;AAC1B,oHAAA,gBAAgB,OAAA;AAChB,qHAAA,iBAAiB,OAAA;AACjB,+GAAA,WAAW,OAAA;AACX,uHAAA,mBAAmB,OAAA;AACnB,0HAAA,sBAAsB,OAAA;AACtB,iHAAA,aAAa,OAAA;AACb,gHAAA,YAAY,OAAA;AACZ,yHAAA,qBAAqB,OAAA;AACrB,4HAAA,wBAAwB,OAAA;AACxB,uHAAA,mBAAmB,OAAA;AAEvB,qDAqB2B;AApBvB,+GAAA,YAAY,OAAA;AACZ,kHAAA,eAAe,OAAA;AACf,oHAAA,iBAAiB,OAAA;AACjB,sHAAA,mBAAmB,OAAA;AACnB,wHAAA,qBAAqB,OAAA;AACrB,iHAAA,cAAc,OAAA;AACd,uHAAA,oBAAoB,OAAA;AACpB,2HAAA,wBAAwB,OAAA;AACxB,kIAAA,+BAA+B,OAAA;AAC/B,gIAAA,6BAA6B,OAAA;AAC7B,4HAAA,yBAAyB,OAAA;AACzB,4HAAA,yBAAyB,OAAA;AACzB,iIAAA,8BAA8B,OAAA;AAC9B,gIAAA,6BAA6B,OAAA;AAC7B,wHAAA,qBAAqB,OAAA;AACrB,0HAAA,uBAAuB,OAAA;AACvB,wHAAA,qBAAqB,OAAA;AACrB,yHAAA,sBAAsB,OAAA;AACtB,0HAAA,uBAAuB,OAAA;AACvB,6HAAA,0BAA0B,OAAA;AAE9B,qDAQ2B;AAPvB,iHAAA,cAAc,OAAA;AACd,gHAAA,aAAa,OAAA;AACb,qHAAA,kBAAkB,OAAA;AAClB,oHAAA,iBAAiB,OAAA;AACjB,0HAAA,uBAAuB,OAAA;AACvB,+HAAA,4BAA4B,OAAA;AAC5B,wHAAA,qBAAqB,OAAA;AAEzB,yCAGqB;AAFjB,qGAAA,QAAQ,OAAA;AACR,4GAAA,eAAe,OAAA;AAEnB,iDAIyB;AAHrB,6GAAA,YAAY,OAAA;AACZ,2GAAA,UAAU,OAAA;AACV,6GAAA,YAAY,OAAA;AAEhB,qDAI2B;AAHvB,iHAAA,cAAc,OAAA;AACd,qHAAA,kBAAkB,OAAA;AAClB,iHAAA,cAAc,OAAA;AAGlB,6DAK+B;AAJ3B,0HAAA,mBAAmB,OAAA;AACnB,wHAAA,iBAAiB,OAAA;AACjB,4HAAA,qBAAqB,OAAA;AACrB,wHAAA,iBAAiB,OAAA;AAErB,qDAK2B;AAJvB,iHAAA,cAAc,OAAA;AACd,sHAAA,mBAAmB,OAAA;AACnB,4HAAA,yBAAyB,OAAA;AACzB,iIAAA,8BAA8B,OAAA","sourcesContent":["export { ResolvedConfig, ResolvedRuleConfig, RuleOptions } from './types';\nexport { InformAiError } from './inform-ai-error';\nexport { RuleFailError } from './rule-fail-error';\nexport { CliExitError } from './cli-exit-error';\nexport { CliUsage, CliFlag, CliArgSet, CliArgsCheck, CliArgs } from './cli-args';\nexport { runMain } from './run-main';\nexport { toError } from './to-error';\nexport { loadAndValidate, LoadedConfig, ConfigLoader } from './load-config';\n// The validation-failure banner: ONE cure (edit the file), plus the marker phrases the validator\n// messages embed rather than re-type.\nexport {\n formatConfigErrorsBanner,\n RETIRED_KEY_MARKER,\n RETIRED_TOP_LEVEL_MARKER,\n SECTION_PLACEMENT_MARKER,\n} from './config-error-banner';\nexport { findConfigFile, CONFIG_FILENAME, ConfigFile, ConfigParseAttempt, CONFIG_PARSE_ATTEMPTS, CONFIG_PARSE_RETRY_MILLIS } from './config-file';\nexport { RepoRootFinder, INSTRUCT_AI_DIR, INSTRUCT_AI_LEAF } from './repo-root';\n// The scoped `.webpieces` resolver. EVERY reader/writer of `.webpieces/...` goes through one of its two\n// named methods so the call site declares whether the state is repo-wide or worktree-private.\nexport { DotWebpieces, dotWebpieces, GitDirs, WORKTREE_STATE_DIR, LOGS_STATE_DIR, HOOKS_STATE_DIR } from './state-dir';\nexport { StateDirMigrator, StateMigrationReport } from './state-dir-migration';\n// There is NO machine-global state root. `MachineStateHome`/`StateHome`/`WEBPIECES_STATE_HOME` and the\n// `PrBodyStore` that used them are DELETED: the one artifact that needed a scope above the clone was the\n// gated squash body, and GitHub holds it now (it IS the PR description). Every `.webpieces` path a\n// webpieces tool writes is `{repo}/.webpieces`, resolved through `DotWebpieces` above. See\n// `decisions/0005-the-pr-description-is-the-merge-body.md`.\nexport { AgedTreeSweeper, SweepCount, RETENTION_DAYS } from './aged-tree-sweep';\nexport { ClaudeEnv, claudeEnv, CLAUDE_PROJECT_DIR_ENV, CLAUDE_PROJECT_DIR_UNSET } from './claude-env';\nexport { AtomicFile } from './atomic-file';\n// The ONE formatter for a remedy that must run in a named directory: `cd '<root>' && <command>`.\n// Single-quoted so a repo path containing a space is still runnable (and still un-smuggleable).\nexport { atRoot } from './at-root';\nexport { RulesConfigDesign } from './rules-config-design';\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './di';\nexport { ExcludePaths } from './exclude-hook-paths';\nexport { isPathExcluded, matchesAnyGlob } from './exclude-paths';\nexport { defaultRules, defaultRulesDir } from './default-rules';\nexport { loadTemplate, writeTemplateIfMissing, writeTemplate, TemplateWriter } from './load-template';\nexport { validateWebpiecesConfig, validatePrGateSection, validateChecklistsSection, validateSectionPlacement, validateExcludePaths, validateMatchRulesSection, allRuleNames, recommendedSeedMode, recommendedSeedModeFor, seedEntryForRule } from './validate-config';\nexport { validateCommandsSection } from './commands-section-validators';\nexport { unknownKeyErrors, isCommentKey, validateTopLevelKeys, COMMENT_KEY_SUFFIX } from './config-key-rules';\n// The retired-key table + the no-back-compat policy it enforces. Exported so the installer can migrate\n// what the errors instruct, and so consumers can enumerate retirements.\nexport { RETIRED_CONFIG_KEYS, RETIRED_SCOPE_KEY, RETIRED_SCOPE_RULE, RetiredConfigKey, isRetiredKey, retiredEntry, retiredKeyError, retiredKeyErrorsIn, retiredRuleFor } from './retired-config-keys';\nexport { validateChecklistDocs } from './checklist-docs-validator';\nexport {\n MatchRuleConfig,\n MatchRuleViolation,\n findMatchRuleViolations,\n isMatchRuleAllowedPath,\n compileMatchRulePatterns,\n renderMatchRuleMessage,\n DEFAULT_MATCH_RULES,\n} from './match-rules-config';\nexport type { ConfigSection } from './sections';\nexport { HOOK_GUARD_NAMES, isHookGuard, sectionForRule } from './sections';\nexport { FieldDef } from './field-def';\nexport type { SchemaShape } from './field-def';\nexport { shouldSkipRule, getCurrentBranch } from './skip-rule';\nexport type { SkipRuleResult } from './skip-rule';\nexport {\n detectBase,\n resolveBase,\n getChangedFiles,\n getFileDiff,\n getChangedLineNumbers,\n findNewMethodSignaturesInDiff,\n hasChangesInRange,\n isNewOrModified,\n DiffScope,\n DiffRange,\n ChangedFilesOptions,\n} from './diff-scope';\nexport { AbstractRule } from './abstract-rule';\nexport {\n WEBPIECES_DISABLE,\n RULE_NAMES,\n hasDisable,\n WEBPIECES_TMP_DIR,\n MERGE_INFO_DIR,\n PR_REVIEW_DIR,\n MERGE_IN_PROGRESS_FILE,\n MERGE_EXPLANATION_FILE,\n PUSH_DEV_STATE_FILE,\n} from './constants';\nexport { WebpiecesRulesConfig } from './WebpiecesRulesConfig';\nexport {\n SyncFlowGuidance,\n WP_START_UPDATE,\n WP_FINISH_UPDATE,\n WP_START_UPSERT_PR,\n WP_FINISH_UPSERT_PR,\n WP_PUSH_DEV,\n WP_FINISH_PUSH_DEV,\n} from './sync-flow-guidance';\nexport {\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n NoUnmanagedExceptionsConfig,\n CatchErrorPatternConfig,\n ThrowCauseRequiredConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoCustomCssConfig,\n NoProcessExitOutsideMainConfig,\n NoFunctionOutsideClassConfig,\n InjectAnnotationNotNeededForConcreteClassConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n BranchCreationGuardConfig,\n PrCreationOrPushGuardConfig,\n MergeInProgressGuardConfig,\n PrMergeGuardConfig,\n RedirectHowToMergeMainConfig,\n NoFileImportCyclesConfig,\n RuntimeArchitectureConfig,\n NxWiringConfig,\n DiGraphConfig,\n NoJsFilesConfig,\n ValidateTsInSrcConfig,\n ValidateArchitectureUnchangedConfig,\n ValidateNoArchitectureCyclesConfig,\n ValidatePackageJsonConfig,\n ValidateVersionsLockedConfig,\n ValidateEslintSyncConfig,\n BaseRuleConfig,\n} from './rule-configs';\n// Mode unions + their value arrays — the single source of truth shared with code-rules.\nexport {\n METHOD_LIMIT_MODES,\n FILE_LIMIT_MODES,\n RETURN_TYPE_MODES,\n INLINE_TYPE_MODES,\n MODIFIED_CODE_MODES,\n PROJECT_MODES,\n PRISMA_DTOS_MODES,\n PRISMA_CONVERTER_MODES,\n DIRECT_API_RESOLVER_MODES,\n THROW_CAUSE_MODES,\n ON_OFF_MODES,\n STRUCTURAL_MODES,\n VALIDATE_TS_MODES,\n} from './rule-configs';\nexport {\n NoClientCreationOutsideServerOrClientConfig,\n CLIENT_CREATION_SEVERITIES,\n} from './no-client-creation-config';\nexport type { ClientCreationSeverity } from './no-client-creation-config';\nexport type {\n MethodLimitMode,\n FileLimitMode,\n ReturnTypeMode,\n InlineTypeMode,\n ModifiedCodeMode,\n ProjectMode,\n PrismaValidateDtosMode,\n PrismaConverterMode,\n DirectApiResolverMode,\n ThrowCauseMode,\n OnOffMode,\n StructuralMode,\n ValidateTsMode,\n} from './rule-configs';\nexport {\n FeatureBranchGuardConfig,\n ReadStaleGuardConfig,\n MergedBranchBashGuardConfig,\n StaleMainBashGuardConfig,\n} from './main-sync-guard-configs';\nexport {\n GateDefinition,\n PrGateConfig,\n LandPrConfig,\n DevDeployConfig,\n DEFAULT_DEV_BRANCH_NAMESPACE,\n DEFAULT_DEV_BRANCH,\n ReviewContextEntry,\n defaultGates,\n defaultPrGateConfig,\n defaultLandPrConfig,\n defaultDevDeployConfig,\n buildPrGateConfig,\n buildLandPrConfig,\n buildDevDeployConfig,\n MERGE_MODE_AUTO,\n MERGE_MODE_NONE,\n MERGE_MODES,\n} from './pr-gate-config';\nexport {\n ChecklistDefinition,\n toChecklist,\n normalizeChecklistDoc,\n formatFileList,\n} from './checklist-config';\nexport type { RawChecklistItem } from './checklist-config';\nexport { ChecklistValidator } from './checklist-validator';\nexport { ChecklistInstructionsService } from './checklist-instructions';\nexport {\n ReviewerInstructionsService,\n ReviewerBriefing,\n BriefedFile,\n ContextEntry,\n READ_TRUNCATION_LINES,\n ALL_DIFF_ONE_READ_LINES,\n} from './reviewer-instructions';\nexport {\n GateTokenService,\n computeGateToken,\n gateTokenMarker,\n extractGateToken,\n verifyGateToken,\n} from './gate-token';\nexport {\n SubagentProvenanceService,\n ReviewerEvidence,\n EvidenceRequest,\n TranscriptScan,\n ProvenanceResult,\n PROVENANCE_OK,\n PROVENANCE_MISSING,\n PROVENANCE_SKIPPED,\n} from './subagent-provenance';\nexport {\n ReviewProvenanceService,\n ReviewProvenance,\n ReviewerTranscript,\n ReviewerPaths,\n OfferedContext,\n ProvenanceWriteRequest,\n DEFAULT_RETENTION_DAYS,\n} from './review-provenance';\nexport {\n ReviewJson,\n PrContext,\n ChecklistResult,\n ChecklistVerdict,\n CK_PASS,\n CK_WARN,\n CK_OVERRIDDEN,\n CK_FAIL,\n CK_MISSING,\n CK_BAD_FORMAT,\n VERDICT_GREEN,\n VERDICT_YELLOW,\n VERDICT_RED,\n VERDICT_STATUSES,\n RequiredChecklist,\n ChecklistReviewContext,\n ReviewJsonService,\n loadReviewJson,\n prDirFor,\n reviewJsonPath,\n reviewJsonSchemaHint,\n} from './review-json';\nexport {\n MainSyncStatus,\n MainSyncStatusFile,\n MainSyncFileStore,\n PullRequestIndex,\n MAIN_SYNC_STATUS_VERSION,\n} from './main-sync-file';\nexport {\n MainSyncLock,\n MainSyncStatusService,\n DEFAULT_HANG_TIMEOUT_MINUTES,\n mainSyncStatusPath,\n mainSyncLockPath,\n readMainSyncStatus,\n readMainSyncStatusFile,\n writeMainSyncStatus,\n writeMainSyncStatusFile,\n computeAllMainSyncStatuses,\n readMainSyncLock,\n writeMainSyncLock,\n isLockStale,\n isRefreshInProgress,\n tryAcquireMainSyncLock,\n inProcessLock,\n finishedLock,\n computeMainSyncStatus,\n stampCleanMainSyncStatus,\n squashRecoverySteps,\n} from './main-sync-status';\nexport {\n MergedBranch,\n DeletableBranch,\n DeletableWorktree,\n MergedBranchesCache,\n MergedBranchesService,\n CacheFreshness,\n CACHE_STALE_AFTER_MS,\n CLASSIFICATION_MERGED_PR,\n CLASSIFICATION_BACKUP_OF_MERGED,\n CLASSIFICATION_BACKUP_OF_LIVE,\n CLASSIFICATION_NO_COMMITS,\n CLASSIFICATION_SUPERSEDED,\n CLASSIFICATION_CONTENT_IN_MAIN,\n CLASSIFICATION_NEVER_PROPOSED,\n CLASSIFICATION_IN_USE,\n CLASSIFICATION_PRUNABLE,\n CLASSIFICATION_LOCKED,\n CLASSIFICATION_CURRENT,\n CLASSIFICATION_DETACHED,\n PROMPTABLE_CLASSIFICATIONS,\n} from './merged-branches';\nexport {\n BranchArchiver,\n ArchiveResult,\n ARCHIVE_TAG_PREFIX,\n BRANCH_RETENTIONS,\n BRANCH_RETENTION_DELETE,\n BRANCH_RETENTION_ARCHIVE_TAG,\n BRANCH_RETENTION_KEEP,\n} from './branch-archiver';\nexport {\n Worktree,\n WorktreeService,\n} from './worktrees';\nexport {\n ReapedBranch,\n ReapResult,\n BranchReaper,\n} from './branch-reaper';\nexport {\n ReapedWorktree,\n WorktreeReapResult,\n WorktreeReaper,\n} from './worktree-reaper';\nexport type { MutationVerb, MutationPhase } from './branch-mutation-log';\nexport {\n BranchMutationEvent,\n BranchMutationLog,\n branchMutationLogPath,\n logBranchMutation,\n} from './branch-mutation-log';\nexport {\n CommandsConfig,\n buildCommandsConfig,\n DEFAULT_UPSERT_PR_COMMAND,\n DEFAULT_MERGE_COMPLETE_COMMAND,\n} from './commands-config';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["export { ResolvedConfig, ResolvedRuleConfig, RuleOptions } from './types';\nexport { InformAiError } from './inform-ai-error';\nexport { RuleFailError } from './rule-fail-error';\nexport { CliExitError } from './cli-exit-error';\nexport { CliUsage, CliFlag, CliArgSet, CliArgsCheck, CliArgs } from './cli-args';\nexport { runMain } from './run-main';\nexport { toError } from './to-error';\nexport { loadAndValidate, LoadedConfig, ConfigLoader } from './load-config';\n// The validation-failure banner: ONE cure (edit the file), plus the marker phrases the validator\n// messages embed rather than re-type.\nexport {\n formatConfigErrorsBanner,\n RETIRED_KEY_MARKER,\n RETIRED_TOP_LEVEL_MARKER,\n SECTION_PLACEMENT_MARKER,\n} from './config-error-banner';\nexport { findConfigFile, CONFIG_FILENAME, ConfigFile, ConfigParseAttempt, CONFIG_PARSE_ATTEMPTS, CONFIG_PARSE_RETRY_MILLIS } from './config-file';\nexport { RepoRootFinder, INSTRUCT_AI_DIR, INSTRUCT_AI_LEAF } from './repo-root';\n// The scoped `.webpieces` resolver. EVERY reader/writer of `.webpieces/...` goes through one of its two\n// named methods so the call site declares whether the state is repo-wide or worktree-private.\nexport { DotWebpieces, dotWebpieces, GitDirs, WORKTREE_STATE_DIR, LOGS_STATE_DIR } from './state-dir';\nexport { StateDirMigrator, StateMigrationReport } from './state-dir-migration';\n// There is NO machine-global state root. `MachineStateHome`/`StateHome`/`WEBPIECES_STATE_HOME` and the\n// `PrBodyStore` that used them are DELETED: the one artifact that needed a scope above the clone was the\n// gated squash body, and GitHub holds it now (it IS the PR description). Every `.webpieces` path a\n// webpieces tool writes is `{repo}/.webpieces`, resolved through `DotWebpieces` above. See\n// `decisions/0005-the-pr-description-is-the-merge-body.md`.\nexport { AgedTreeSweeper, SweepCount, RETENTION_DAYS } from './aged-tree-sweep';\nexport { ClaudeEnv, claudeEnv, CLAUDE_PROJECT_DIR_ENV, CLAUDE_PROJECT_DIR_UNSET } from './claude-env';\nexport { AtomicFile } from './atomic-file';\n// The ONE formatter for a remedy that must run in a named directory: `cd '<root>' && <command>`.\n// Single-quoted so a repo path containing a space is still runnable (and still un-smuggleable).\nexport { atRoot } from './at-root';\nexport { RulesConfigDesign } from './rules-config-design';\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './di';\nexport { ExcludePaths } from './exclude-hook-paths';\nexport { isPathExcluded, matchesAnyGlob } from './exclude-paths';\nexport { defaultRules, defaultRulesDir } from './default-rules';\nexport { loadTemplate, writeTemplateIfMissing, writeTemplate, TemplateWriter } from './load-template';\nexport { validateWebpiecesConfig, validatePrGateSection, validateChecklistsSection, validateSectionPlacement, validateExcludePaths, validateMatchRulesSection, allRuleNames, recommendedSeedMode, recommendedSeedModeFor, seedEntryForRule } from './validate-config';\nexport { validateCommandsSection } from './commands-section-validators';\nexport { unknownKeyErrors, isCommentKey, validateTopLevelKeys, COMMENT_KEY_SUFFIX } from './config-key-rules';\n// The retired-key table + the no-back-compat policy it enforces. Exported so the installer can migrate\n// what the errors instruct, and so consumers can enumerate retirements.\nexport { RETIRED_CONFIG_KEYS, RETIRED_SCOPE_KEY, RETIRED_SCOPE_RULE, RetiredConfigKey, isRetiredKey, retiredEntry, retiredKeyError, retiredKeyErrorsIn, retiredRuleFor } from './retired-config-keys';\nexport { validateChecklistDocs } from './checklist-docs-validator';\nexport {\n MatchRuleConfig,\n MatchRuleViolation,\n findMatchRuleViolations,\n isMatchRuleAllowedPath,\n compileMatchRulePatterns,\n renderMatchRuleMessage,\n DEFAULT_MATCH_RULES,\n} from './match-rules-config';\nexport type { ConfigSection } from './sections';\nexport { HOOK_GUARD_NAMES, isHookGuard, sectionForRule } from './sections';\nexport { FieldDef } from './field-def';\nexport type { SchemaShape } from './field-def';\nexport { shouldSkipRule, getCurrentBranch } from './skip-rule';\nexport type { SkipRuleResult } from './skip-rule';\nexport {\n detectBase,\n resolveBase,\n getChangedFiles,\n getFileDiff,\n getChangedLineNumbers,\n findNewMethodSignaturesInDiff,\n hasChangesInRange,\n isNewOrModified,\n DiffScope,\n DiffRange,\n ChangedFilesOptions,\n} from './diff-scope';\nexport { AbstractRule } from './abstract-rule';\nexport {\n WEBPIECES_DISABLE,\n RULE_NAMES,\n hasDisable,\n WEBPIECES_TMP_DIR,\n MERGE_INFO_DIR,\n PR_REVIEW_DIR,\n MERGE_IN_PROGRESS_FILE,\n MERGE_EXPLANATION_FILE,\n PUSH_DEV_STATE_FILE,\n} from './constants';\nexport { WebpiecesRulesConfig } from './WebpiecesRulesConfig';\nexport {\n SyncFlowGuidance,\n WP_START_UPDATE,\n WP_FINISH_UPDATE,\n WP_START_UPSERT_PR,\n WP_FINISH_UPSERT_PR,\n WP_PUSH_DEV,\n WP_FINISH_PUSH_DEV,\n} from './sync-flow-guidance';\nexport {\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n NoUnmanagedExceptionsConfig,\n CatchErrorPatternConfig,\n ThrowCauseRequiredConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoCustomCssConfig,\n NoProcessExitOutsideMainConfig,\n NoFunctionOutsideClassConfig,\n InjectAnnotationNotNeededForConcreteClassConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n BranchCreationGuardConfig,\n PrCreationOrPushGuardConfig,\n MergeInProgressGuardConfig,\n PrMergeGuardConfig,\n RedirectHowToMergeMainConfig,\n NoFileImportCyclesConfig,\n RuntimeArchitectureConfig,\n NxWiringConfig,\n DiGraphConfig,\n NoJsFilesConfig,\n ValidateTsInSrcConfig,\n ValidateArchitectureUnchangedConfig,\n ValidateNoArchitectureCyclesConfig,\n ValidatePackageJsonConfig,\n ValidateVersionsLockedConfig,\n ValidateEslintSyncConfig,\n BaseRuleConfig,\n} from './rule-configs';\n// Mode unions + their value arrays — the single source of truth shared with code-rules.\nexport {\n METHOD_LIMIT_MODES,\n FILE_LIMIT_MODES,\n RETURN_TYPE_MODES,\n INLINE_TYPE_MODES,\n MODIFIED_CODE_MODES,\n PROJECT_MODES,\n PRISMA_DTOS_MODES,\n PRISMA_CONVERTER_MODES,\n DIRECT_API_RESOLVER_MODES,\n THROW_CAUSE_MODES,\n ON_OFF_MODES,\n STRUCTURAL_MODES,\n VALIDATE_TS_MODES,\n} from './rule-configs';\nexport {\n NoClientCreationOutsideServerOrClientConfig,\n CLIENT_CREATION_SEVERITIES,\n} from './no-client-creation-config';\nexport type { ClientCreationSeverity } from './no-client-creation-config';\nexport type {\n MethodLimitMode,\n FileLimitMode,\n ReturnTypeMode,\n InlineTypeMode,\n ModifiedCodeMode,\n ProjectMode,\n PrismaValidateDtosMode,\n PrismaConverterMode,\n DirectApiResolverMode,\n ThrowCauseMode,\n OnOffMode,\n StructuralMode,\n ValidateTsMode,\n} from './rule-configs';\nexport {\n FeatureBranchGuardConfig,\n ReadStaleGuardConfig,\n MergedBranchBashGuardConfig,\n StaleMainBashGuardConfig,\n} from './main-sync-guard-configs';\nexport {\n GateDefinition,\n PrGateConfig,\n LandPrConfig,\n DevDeployConfig,\n DEFAULT_DEV_BRANCH_NAMESPACE,\n DEFAULT_DEV_BRANCH,\n ReviewContextEntry,\n defaultGates,\n defaultPrGateConfig,\n defaultLandPrConfig,\n defaultDevDeployConfig,\n buildPrGateConfig,\n buildLandPrConfig,\n buildDevDeployConfig,\n MERGE_MODE_AUTO,\n MERGE_MODE_NONE,\n MERGE_MODES,\n} from './pr-gate-config';\nexport {\n ChecklistDefinition,\n toChecklist,\n normalizeChecklistDoc,\n formatFileList,\n} from './checklist-config';\nexport type { RawChecklistItem } from './checklist-config';\nexport { ChecklistValidator } from './checklist-validator';\nexport { ChecklistInstructionsService } from './checklist-instructions';\nexport {\n ReviewerInstructionsService,\n ReviewerBriefing,\n BriefedFile,\n ContextEntry,\n READ_TRUNCATION_LINES,\n ALL_DIFF_ONE_READ_LINES,\n} from './reviewer-instructions';\nexport {\n GateTokenService,\n computeGateToken,\n gateTokenMarker,\n extractGateToken,\n verifyGateToken,\n} from './gate-token';\nexport {\n SubagentProvenanceService,\n ReviewerEvidence,\n EvidenceRequest,\n TranscriptScan,\n ProvenanceResult,\n PROVENANCE_OK,\n PROVENANCE_MISSING,\n PROVENANCE_SKIPPED,\n} from './subagent-provenance';\nexport {\n ReviewProvenanceService,\n ReviewProvenance,\n ReviewerTranscript,\n ReviewerPaths,\n OfferedContext,\n ProvenanceWriteRequest,\n DEFAULT_RETENTION_DAYS,\n} from './review-provenance';\nexport {\n ReviewJson,\n PrContext,\n ChecklistResult,\n ChecklistVerdict,\n CK_PASS,\n CK_WARN,\n CK_OVERRIDDEN,\n CK_FAIL,\n CK_MISSING,\n CK_BAD_FORMAT,\n VERDICT_GREEN,\n VERDICT_YELLOW,\n VERDICT_RED,\n VERDICT_STATUSES,\n RequiredChecklist,\n ChecklistReviewContext,\n ReviewJsonService,\n loadReviewJson,\n prDirFor,\n reviewJsonPath,\n reviewJsonSchemaHint,\n} from './review-json';\nexport {\n MainSyncStatus,\n MainSyncStatusFile,\n MainSyncFileStore,\n PullRequestIndex,\n MAIN_SYNC_STATUS_VERSION,\n} from './main-sync-file';\nexport {\n MainSyncLock,\n MainSyncStatusService,\n DEFAULT_HANG_TIMEOUT_MINUTES,\n mainSyncStatusPath,\n mainSyncLockPath,\n readMainSyncStatus,\n readMainSyncStatusFile,\n writeMainSyncStatus,\n writeMainSyncStatusFile,\n computeAllMainSyncStatuses,\n readMainSyncLock,\n writeMainSyncLock,\n isLockStale,\n isRefreshInProgress,\n tryAcquireMainSyncLock,\n inProcessLock,\n finishedLock,\n computeMainSyncStatus,\n stampCleanMainSyncStatus,\n squashRecoverySteps,\n} from './main-sync-status';\nexport {\n MergedBranch,\n DeletableBranch,\n DeletableWorktree,\n MergedBranchesCache,\n MergedBranchesService,\n CacheFreshness,\n CACHE_STALE_AFTER_MS,\n CLASSIFICATION_MERGED_PR,\n CLASSIFICATION_BACKUP_OF_MERGED,\n CLASSIFICATION_BACKUP_OF_LIVE,\n CLASSIFICATION_NO_COMMITS,\n CLASSIFICATION_SUPERSEDED,\n CLASSIFICATION_CONTENT_IN_MAIN,\n CLASSIFICATION_NEVER_PROPOSED,\n CLASSIFICATION_IN_USE,\n CLASSIFICATION_PRUNABLE,\n CLASSIFICATION_LOCKED,\n CLASSIFICATION_CURRENT,\n CLASSIFICATION_DETACHED,\n PROMPTABLE_CLASSIFICATIONS,\n} from './merged-branches';\nexport {\n BranchArchiver,\n ArchiveResult,\n ARCHIVE_TAG_PREFIX,\n BRANCH_RETENTIONS,\n BRANCH_RETENTION_DELETE,\n BRANCH_RETENTION_ARCHIVE_TAG,\n BRANCH_RETENTION_KEEP,\n} from './branch-archiver';\nexport {\n Worktree,\n WorktreeService,\n} from './worktrees';\nexport {\n ReapedBranch,\n ReapResult,\n BranchReaper,\n} from './branch-reaper';\nexport {\n ReapedWorktree,\n WorktreeReapResult,\n WorktreeReaper,\n} from './worktree-reaper';\nexport type { MutationVerb, MutationPhase } from './branch-mutation-log';\nexport {\n BranchMutationEvent,\n BranchMutationLog,\n branchMutationLogPath,\n logBranchMutation,\n} from './branch-mutation-log';\nexport {\n CommandsConfig,\n buildCommandsConfig,\n DEFAULT_UPSERT_PR_COMMAND,\n DEFAULT_MERGE_COMPLETE_COMMAND,\n} from './commands-config';\n"]}
@@ -48,18 +48,6 @@ export declare class StateDirMigrator {
48
48
  * dir does not exist / is not a real directory (a symlink means migration already happened).
49
49
  */
50
50
  migrate(legacyDir: string, targetDir: string): StateMigrationReport;
51
- /**
52
- * Relocate every `*.log` FILE sitting directly in `hooksDir` into `logsDir` — the hooks/ → logs/
53
- * split (see LOGS_STATE_DIR). Same shape and the same safety rule as {@link migrate}: `rename`
54
- * first, an occupied destination is LEFT ALONE and reported, nothing is ever deleted or
55
- * overwritten. Deliberately NON-recursive and extension-scoped, because the rest of `hooks/` is
56
- * not log data — the dated `hooks/<YYYY-MM-DD>/writeInfo-*.md` rejection details stay exactly
57
- * where they are, and a recursive sweep would drag them along.
58
- *
59
- * `hooksDir` normally still exists after this (it holds those dated dirs); it is removed only if
60
- * draining the logs left it genuinely empty.
61
- */
62
- migrateLogFiles(hooksDir: string, logsDir: string): StateMigrationReport;
63
51
  /**
64
52
  * Move everything under `<legacyRoot>/<relative>` to `<targetRoot>/<relative>`.
65
53
  *
@@ -78,48 +78,6 @@ let StateDirMigrator = class StateDirMigrator {
78
78
  this.announce(legacyDir, targetDir, report);
79
79
  return report;
80
80
  }
81
- /**
82
- * Relocate every `*.log` FILE sitting directly in `hooksDir` into `logsDir` — the hooks/ → logs/
83
- * split (see LOGS_STATE_DIR). Same shape and the same safety rule as {@link migrate}: `rename`
84
- * first, an occupied destination is LEFT ALONE and reported, nothing is ever deleted or
85
- * overwritten. Deliberately NON-recursive and extension-scoped, because the rest of `hooks/` is
86
- * not log data — the dated `hooks/<YYYY-MM-DD>/writeInfo-*.md` rejection details stay exactly
87
- * where they are, and a recursive sweep would drag them along.
88
- *
89
- * `hooksDir` normally still exists after this (it holds those dated dirs); it is removed only if
90
- * draining the logs left it genuinely empty.
91
- */
92
- migrateLogFiles(hooksDir, logsDir) {
93
- const report = new StateMigrationReport();
94
- if (path.resolve(hooksDir) === path.resolve(logsDir))
95
- return report;
96
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
97
- try {
98
- if (!this.isRealDirectory(hooksDir))
99
- return report;
100
- const logs = fs.readdirSync(hooksDir, { withFileTypes: true })
101
- .filter((entry) => entry.isFile() && entry.name.endsWith('.log'));
102
- if (logs.length === 0)
103
- return report;
104
- fs.mkdirSync(logsDir, { recursive: true });
105
- for (const entry of logs) {
106
- const destination = path.join(logsDir, entry.name);
107
- if (fs.existsSync(destination)) {
108
- report.kept.push(entry.name);
109
- continue;
110
- }
111
- this.relocate(path.join(hooksDir, entry.name), destination, entry.name, report);
112
- }
113
- this.removeIfEmpty(hooksDir);
114
- }
115
- catch (err) {
116
- const error = (0, to_error_1.toError)(err);
117
- this.warn(`could not move logs from ${hooksDir} into ${logsDir}: ${error.message}`);
118
- return report;
119
- }
120
- this.announce(hooksDir, logsDir, report);
121
- return report;
122
- }
123
81
  /**
124
82
  * Move everything under `<legacyRoot>/<relative>` to `<targetRoot>/<relative>`.
125
83
  *
@@ -1 +1 @@
1
- {"version":3,"file":"state-dir-migration.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/state-dir-migration.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { toError } from './to-error';\n\n/**\n * What a migration did. Data-only (per CLAUDE.md: classes for data, explicit construction).\n *\n * `kept` is the important half: an entry that could NOT be moved because the destination already holds\n * something at that path. Nothing in `kept` is ever deleted — it stays where it is, for a human.\n */\nexport class StateMigrationReport {\n moved: string[] = [];\n kept: string[] = [];\n\n get movedAnything(): boolean {\n return this.moved.length > 0;\n }\n}\n\n/**\n * Moves a LEGACY per-worktree `.webpieces/` REAL DIRECTORY into the worktree's namespace inside the\n * primary clone (`<primary>/.webpieces/worktrees/<name>/`).\n *\n * NO SYMLINK IS EVER CREATED in its place — an earlier draft of this design planned one and this\n * comment still said so, which is worse than silence: it told the reader to expect an indirection\n * that does not exist. `DotWebpieces` resolves the namespace path EXPLICITLY (see its \"Why two\n * explicit methods and not a symlink\" section, which records why the link was rejected: lazy creation\n * with three failure modes, a Windows hazard, and `rename(2)` silently REPLACING the link). After a\n * migration the legacy `<worktree>/.webpieces/` is simply gone; nothing takes its place.\n *\n * WHY it must exist: those directories hold REAL in-flight state — a half-finished 3-point merge under\n * `merge-info/staged/<branch>/`, a written-but-not-yet-posted `pr-review/<branch>/review.json`. The\n * first invocation under the new scheme must not orphan them; a merge an agent is standing in the\n * middle of is not recoverable by re-running anything.\n *\n * WHY a plain move suffices: the destination namespace is created FOR this worktree, so it is empty in\n * the ordinary case and the whole tree moves in one `rename`. Where something is already there (a\n * repeat run, or an old PUBLISHED build having written to the legacy path again mid-transition), we\n * descend and move only what is free.\n *\n * SAFETY RULE, absolute: this never deletes or overwrites anything that holds content. If a\n * destination path is occupied, the legacy copy is LEFT WHERE IT IS and reported loudly on stderr.\n * Empty directories are removed as they drain, which is how a fully-migrated legacy `.webpieces/`\n * disappears on its own and lets the symlink be created.\n *\n * It is also the answer to the PUBLISHED-vs-LOCAL transition window. The `wp-*` bins and the hooks run\n * the PUBLISHED package, so for a while some invocations still create a REAL `<worktree>/.webpieces`\n * directory. Because this runs on the first state-dir resolution of EVERY new-code process, anything an\n * old-code invocation deposited is swept into the worktree namespace before any reader looks — and a\n * reader running old code finds it through the symlink either way. The two schemes converge instead of\n * splitting; nothing is lost in either direction.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class StateDirMigrator {\n /**\n * Drain `legacyDir` into `targetDir`. A no-op when they are the same directory or when the legacy\n * dir does not exist / is not a real directory (a symlink means migration already happened).\n */\n migrate(legacyDir: string, targetDir: string): StateMigrationReport {\n const report = new StateMigrationReport();\n if (path.resolve(legacyDir) === path.resolve(targetDir)) return report;\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n if (!this.isRealDirectory(legacyDir)) return report;\n fs.mkdirSync(targetDir, { recursive: true });\n this.drain(legacyDir, targetDir, '', report);\n this.removeIfEmpty(legacyDir);\n } catch (err: unknown) {\n const error = toError(err);\n this.warn(`could not migrate ${legacyDir} into ${targetDir}: ${error.message}`);\n return report;\n }\n\n this.announce(legacyDir, targetDir, report);\n return report;\n }\n\n /**\n * Relocate every `*.log` FILE sitting directly in `hooksDir` into `logsDir` — the hooks/ → logs/\n * split (see LOGS_STATE_DIR). Same shape and the same safety rule as {@link migrate}: `rename`\n * first, an occupied destination is LEFT ALONE and reported, nothing is ever deleted or\n * overwritten. Deliberately NON-recursive and extension-scoped, because the rest of `hooks/` is\n * not log data — the dated `hooks/<YYYY-MM-DD>/writeInfo-*.md` rejection details stay exactly\n * where they are, and a recursive sweep would drag them along.\n *\n * `hooksDir` normally still exists after this (it holds those dated dirs); it is removed only if\n * draining the logs left it genuinely empty.\n */\n migrateLogFiles(hooksDir: string, logsDir: string): StateMigrationReport {\n const report = new StateMigrationReport();\n if (path.resolve(hooksDir) === path.resolve(logsDir)) return report;\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n if (!this.isRealDirectory(hooksDir)) return report;\n const logs = fs.readdirSync(hooksDir, { withFileTypes: true })\n .filter((entry: fs.Dirent): boolean => entry.isFile() && entry.name.endsWith('.log'));\n if (logs.length === 0) return report;\n\n fs.mkdirSync(logsDir, { recursive: true });\n for (const entry of logs) {\n const destination = path.join(logsDir, entry.name);\n if (fs.existsSync(destination)) {\n report.kept.push(entry.name);\n continue;\n }\n this.relocate(path.join(hooksDir, entry.name), destination, entry.name, report);\n }\n this.removeIfEmpty(hooksDir);\n } catch (err: unknown) {\n const error = toError(err);\n this.warn(`could not move logs from ${hooksDir} into ${logsDir}: ${error.message}`);\n return report;\n }\n\n this.announce(hooksDir, logsDir, report);\n return report;\n }\n\n /**\n * Move everything under `<legacyRoot>/<relative>` to `<targetRoot>/<relative>`.\n *\n * A whole subtree whose destination is free moves in ONE `rename` — which is what keeps an\n * in-flight `merge-info/staged/<branch>/` intact rather than copying it file by file and risking a\n * half-moved merge. Only when the destination is an existing DIRECTORY do we descend and consider\n * its children individually.\n */\n private drain(legacyRoot: string, targetRoot: string, relative: string, report: StateMigrationReport): void {\n const from = path.join(legacyRoot, relative);\n for (const entry of fs.readdirSync(from, { withFileTypes: true })) {\n const childRelative = path.join(relative, entry.name);\n const source = path.join(legacyRoot, childRelative);\n const destination = path.join(targetRoot, childRelative);\n\n if (!fs.existsSync(destination)) {\n this.relocate(source, destination, childRelative, report);\n continue;\n }\n if (entry.isDirectory() && fs.statSync(destination).isDirectory()) {\n this.drain(legacyRoot, targetRoot, childRelative, report);\n this.removeIfEmpty(source);\n continue;\n }\n report.kept.push(childRelative);\n }\n }\n\n // Move one file or subtree to a free destination. `rename` first (atomic, instant, and the only\n // form that cannot half-move a merge); a cross-device rename falls back to copy-then-remove.\n private relocate(source: string, destination: string, relative: string, report: StateMigrationReport): void {\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n fs.renameSync(source, destination);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n fs.cpSync(source, destination, { recursive: true });\n fs.rmSync(source, { recursive: true, force: true });\n }\n report.moved.push(relative);\n }\n\n // A real directory, not a symlink to one — `lstat` on purpose. A symlink here is the NEW scheme\n // already in place, and following it would make the migrator try to drain a directory into itself.\n private isRealDirectory(dir: string): boolean {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return fs.lstatSync(dir).isDirectory();\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return false;\n }\n }\n\n // Remove a directory only when it is genuinely empty. Never recursive — a non-empty legacy dir is\n // state a human still needs.\n private removeIfEmpty(dir: string): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n if (!fs.existsSync(dir)) return;\n if (fs.readdirSync(dir).length > 0) return;\n fs.rmdirSync(dir);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n\n // Migration is invisible by design when it goes cleanly; anything LEFT BEHIND must be shouted about,\n // because that is the only case where a human has to do something.\n private announce(legacyDir: string, targetDir: string, report: StateMigrationReport): void {\n if (report.kept.length === 0) return;\n this.warn(\n `${String(report.kept.length)} item(s) in ${legacyDir} could NOT move into ${targetDir} because ` +\n `that path is already occupied. NOTHING was deleted — resolve by hand if any of these still ` +\n `matters (an in-flight merge, or log history you have not read): ${report.kept.join(', ')}`,\n );\n }\n\n private warn(message: string): void {\n process.stderr.write(`[webpieces] .webpieces state migration: ${message}\\n`);\n }\n}\n"]}
1
+ {"version":3,"file":"state-dir-migration.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/state-dir-migration.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAC7B,yCAA2D;AAE3D,yCAAqC;AAErC;;;;;GAKG;AACH,MAAa,oBAAoB;IAC7B,KAAK,GAAa,EAAE,CAAC;IACrB,IAAI,GAAa,EAAE,CAAC;IAEpB,IAAI,aAAa;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACjC,CAAC;CACJ;AAPD,oDAOC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEI,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IACzB;;;OAGG;IACH,OAAO,CAAC,SAAiB,EAAE,SAAiB;QACxC,MAAM,MAAM,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC1C,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;YAAE,OAAO,MAAM,CAAC;QAEvE,8DAA8D;QAC9D,IAAI,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC;gBAAE,OAAO,MAAM,CAAC;YACpD,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7C,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;YAC7C,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,IAAI,CAAC,IAAI,CAAC,qBAAqB,SAAS,SAAS,SAAS,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAChF,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAC5C,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,UAAkB,EAAE,UAAkB,EAAE,QAAgB,EAAE,MAA4B;QAChG,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC7C,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAChE,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACtD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;YACpD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;YAEzD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC9B,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;gBAC1D,SAAS;YACb,CAAC;YACD,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;gBAChE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;gBAC1D,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAC3B,SAAS;YACb,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACpC,CAAC;IACL,CAAC;IAED,gGAAgG;IAChG,6FAA6F;IACrF,QAAQ,CAAC,MAAc,EAAE,WAAmB,EAAE,QAAgB,EAAE,MAA4B;QAChG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,8DAA8D;QAC9D,IAAI,CAAC;YACD,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,KAAK,KAAK,CAAC;YACX,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,gGAAgG;IAChG,mGAAmG;IAC3F,eAAe,CAAC,GAAW;QAC/B,8DAA8D;QAC9D,IAAI,CAAC;YACD,OAAO,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3C,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,KAAK,KAAK,CAAC;YACX,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IAED,kGAAkG;IAClG,6BAA6B;IACrB,aAAa,CAAC,GAAW;QAC7B,8DAA8D;QAC9D,IAAI,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,OAAO;YAChC,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO;YAC3C,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,KAAK,KAAK,CAAC;QACf,CAAC;IACL,CAAC;IAED,qGAAqG;IACrG,mEAAmE;IAC3D,QAAQ,CAAC,SAAiB,EAAE,SAAiB,EAAE,MAA4B;QAC/E,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACrC,IAAI,CAAC,IAAI,CACL,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,SAAS,wBAAwB,SAAS,WAAW;YACjG,6FAA6F;YAC7F,mEAAmE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9F,CAAC;IACN,CAAC;IAEO,IAAI,CAAC,OAAe;QACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,OAAO,IAAI,CAAC,CAAC;IACjF,CAAC;CACJ,CAAA;AA9GY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,gBAAgB,CA8G5B","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { toError } from './to-error';\n\n/**\n * What a migration did. Data-only (per CLAUDE.md: classes for data, explicit construction).\n *\n * `kept` is the important half: an entry that could NOT be moved because the destination already holds\n * something at that path. Nothing in `kept` is ever deleted — it stays where it is, for a human.\n */\nexport class StateMigrationReport {\n moved: string[] = [];\n kept: string[] = [];\n\n get movedAnything(): boolean {\n return this.moved.length > 0;\n }\n}\n\n/**\n * Moves a LEGACY per-worktree `.webpieces/` REAL DIRECTORY into the worktree's namespace inside the\n * primary clone (`<primary>/.webpieces/worktrees/<name>/`).\n *\n * NO SYMLINK IS EVER CREATED in its place — an earlier draft of this design planned one and this\n * comment still said so, which is worse than silence: it told the reader to expect an indirection\n * that does not exist. `DotWebpieces` resolves the namespace path EXPLICITLY (see its \"Why two\n * explicit methods and not a symlink\" section, which records why the link was rejected: lazy creation\n * with three failure modes, a Windows hazard, and `rename(2)` silently REPLACING the link). After a\n * migration the legacy `<worktree>/.webpieces/` is simply gone; nothing takes its place.\n *\n * WHY it must exist: those directories hold REAL in-flight state — a half-finished 3-point merge under\n * `merge-info/staged/<branch>/`, a written-but-not-yet-posted `pr-review/<branch>/review.json`. The\n * first invocation under the new scheme must not orphan them; a merge an agent is standing in the\n * middle of is not recoverable by re-running anything.\n *\n * WHY a plain move suffices: the destination namespace is created FOR this worktree, so it is empty in\n * the ordinary case and the whole tree moves in one `rename`. Where something is already there (a\n * repeat run, or an old PUBLISHED build having written to the legacy path again mid-transition), we\n * descend and move only what is free.\n *\n * SAFETY RULE, absolute: this never deletes or overwrites anything that holds content. If a\n * destination path is occupied, the legacy copy is LEFT WHERE IT IS and reported loudly on stderr.\n * Empty directories are removed as they drain, which is how a fully-migrated legacy `.webpieces/`\n * disappears on its own and lets the symlink be created.\n *\n * It is also the answer to the PUBLISHED-vs-LOCAL transition window. The `wp-*` bins and the hooks run\n * the PUBLISHED package, so for a while some invocations still create a REAL `<worktree>/.webpieces`\n * directory. Because this runs on the first state-dir resolution of EVERY new-code process, anything an\n * old-code invocation deposited is swept into the worktree namespace before any reader looks — and a\n * reader running old code finds it through the symlink either way. The two schemes converge instead of\n * splitting; nothing is lost in either direction.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class StateDirMigrator {\n /**\n * Drain `legacyDir` into `targetDir`. A no-op when they are the same directory or when the legacy\n * dir does not exist / is not a real directory (a symlink means migration already happened).\n */\n migrate(legacyDir: string, targetDir: string): StateMigrationReport {\n const report = new StateMigrationReport();\n if (path.resolve(legacyDir) === path.resolve(targetDir)) return report;\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n if (!this.isRealDirectory(legacyDir)) return report;\n fs.mkdirSync(targetDir, { recursive: true });\n this.drain(legacyDir, targetDir, '', report);\n this.removeIfEmpty(legacyDir);\n } catch (err: unknown) {\n const error = toError(err);\n this.warn(`could not migrate ${legacyDir} into ${targetDir}: ${error.message}`);\n return report;\n }\n\n this.announce(legacyDir, targetDir, report);\n return report;\n }\n\n /**\n * Move everything under `<legacyRoot>/<relative>` to `<targetRoot>/<relative>`.\n *\n * A whole subtree whose destination is free moves in ONE `rename` — which is what keeps an\n * in-flight `merge-info/staged/<branch>/` intact rather than copying it file by file and risking a\n * half-moved merge. Only when the destination is an existing DIRECTORY do we descend and consider\n * its children individually.\n */\n private drain(legacyRoot: string, targetRoot: string, relative: string, report: StateMigrationReport): void {\n const from = path.join(legacyRoot, relative);\n for (const entry of fs.readdirSync(from, { withFileTypes: true })) {\n const childRelative = path.join(relative, entry.name);\n const source = path.join(legacyRoot, childRelative);\n const destination = path.join(targetRoot, childRelative);\n\n if (!fs.existsSync(destination)) {\n this.relocate(source, destination, childRelative, report);\n continue;\n }\n if (entry.isDirectory() && fs.statSync(destination).isDirectory()) {\n this.drain(legacyRoot, targetRoot, childRelative, report);\n this.removeIfEmpty(source);\n continue;\n }\n report.kept.push(childRelative);\n }\n }\n\n // Move one file or subtree to a free destination. `rename` first (atomic, instant, and the only\n // form that cannot half-move a merge); a cross-device rename falls back to copy-then-remove.\n private relocate(source: string, destination: string, relative: string, report: StateMigrationReport): void {\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n fs.renameSync(source, destination);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n fs.cpSync(source, destination, { recursive: true });\n fs.rmSync(source, { recursive: true, force: true });\n }\n report.moved.push(relative);\n }\n\n // A real directory, not a symlink to one — `lstat` on purpose. A symlink here is the NEW scheme\n // already in place, and following it would make the migrator try to drain a directory into itself.\n private isRealDirectory(dir: string): boolean {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return fs.lstatSync(dir).isDirectory();\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return false;\n }\n }\n\n // Remove a directory only when it is genuinely empty. Never recursive — a non-empty legacy dir is\n // state a human still needs.\n private removeIfEmpty(dir: string): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n if (!fs.existsSync(dir)) return;\n if (fs.readdirSync(dir).length > 0) return;\n fs.rmdirSync(dir);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n\n // Migration is invisible by design when it goes cleanly; anything LEFT BEHIND must be shouted about,\n // because that is the only case where a human has to do something.\n private announce(legacyDir: string, targetDir: string, report: StateMigrationReport): void {\n if (report.kept.length === 0) return;\n this.warn(\n `${String(report.kept.length)} item(s) in ${legacyDir} could NOT move into ${targetDir} because ` +\n `that path is already occupied. NOTHING was deleted — resolve by hand if any of these still ` +\n `matters (an in-flight merge, or log history you have not read): ${report.kept.join(', ')}`,\n );\n }\n\n private warn(message: string): void {\n process.stderr.write(`[webpieces] .webpieces state migration: ${message}\\n`);\n }\n}\n"]}
@@ -1,7 +1,6 @@
1
1
  import { StateDirMigrator } from './state-dir-migration';
2
2
  export declare const WORKTREE_STATE_DIR = "worktrees";
3
3
  export declare const LOGS_STATE_DIR = "logs";
4
- export declare const HOOKS_STATE_DIR = "hooks";
5
4
  /**
6
5
  * Data-only carrier for the two paths git is asked for. Per CLAUDE.md: classes for data.
7
6
  *
@@ -87,7 +86,6 @@ export declare class DotWebpieces {
87
86
  private readonly migrator;
88
87
  private readonly gitDirsByRoot;
89
88
  private readonly migrated;
90
- private readonly logsMigrated;
91
89
  constructor(migrator?: StateDirMigrator);
92
90
  /**
93
91
  * REPO-WIDE state: `<primary>/.webpieces`. Use ONLY for facts about the repo rather than about one
@@ -110,13 +108,14 @@ export declare class DotWebpieces {
110
108
  /** A path beneath this worktree's private state dir. */
111
109
  localFile(startDir: string, ...segments: string[]): string;
112
110
  /**
113
- * THE log directory — `<local()>/logs` — and the only place a webpieces `.log` may be written, in
114
- * the primary clone and in every worktree namespace alike.
111
+ * THE log directory — `<local()>/logs` — and the only place webpieces writes a `.log` or a log's
112
+ * detail files, in the primary clone and in every worktree namespace alike.
115
113
  *
116
- * The first call per tree also RELOCATES any `hooks/*.log` written by an older release (or by the
117
- * still-published build during the transition window) into `logs/`, so upgrading does not orphan
118
- * the history a human is mid-way through reading. Same safety rule as StateDirMigrator.migrate:
119
- * an occupied destination is never overwritten the old copy is left where it is.
114
+ * There is NO migration here, deliberately. A lazy once-per-process relocation of an older
115
+ * release's `hooks/*.log` used to run on this path, and it was itself the defect it claimed to
116
+ * cure: it only fires in the trees a process happens to re-enter, so it guarantees two answers to
117
+ * "where are the logs" indefinitely rather than converging on one. New writes go to `logs/` and
118
+ * nothing else; a stale `hooks/` left by an older release is inert and can be deleted by hand.
120
119
  */
121
120
  logs(startDir: string): string;
122
121
  /** A path beneath the log directory — `dotWebpieces.logsFile(root, 'guard-invocations.log')`. */
@@ -139,7 +138,6 @@ export declare class DotWebpieces {
139
138
  */
140
139
  legacyDir(treeRoot: string): string;
141
140
  private migrateOnce;
142
- private migrateLogsOnce;
143
141
  private gitDirs;
144
142
  private gitToplevel;
145
143
  private revParse;
package/src/state-dir.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dotWebpieces = exports.DotWebpieces = exports.GitDirs = exports.HOOKS_STATE_DIR = exports.LOGS_STATE_DIR = exports.WORKTREE_STATE_DIR = void 0;
3
+ exports.dotWebpieces = exports.DotWebpieces = exports.GitDirs = exports.LOGS_STATE_DIR = exports.WORKTREE_STATE_DIR = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const child_process_1 = require("child_process");
6
6
  const fs = tslib_1.__importStar(require("fs"));
@@ -12,19 +12,23 @@ const state_dir_migration_1 = require("./state-dir-migration");
12
12
  // lives at `<primary>/.webpieces/worktrees/<worktreeName>/`; the primary clone keeps using
13
13
  // `<primary>/.webpieces/` directly, exactly as it always has.
14
14
  exports.WORKTREE_STATE_DIR = 'worktrees';
15
- // The ONE directory every webpieces `.log` lives in — `<state>/logs/`, for the primary clone and for
16
- // each worktree namespace alike. It used to be `hooks/`, which also holds NON-log state (the dated
17
- // `hooks/<YYYY-MM-DD>/writeInfo-*.md` rejection details), while the L0 sh shim wrote to `logs/`. Two
18
- // directories, one of them mixed — so "where are the logs?" had two answers and neither was complete.
15
+ // The ONE and ONLY directory webpieces writes observability state into — `<state>/logs/`, for the
16
+ // primary clone and for each worktree namespace alike. There is no second state directory and no
17
+ // split: the L1 binary's logs, the L0 sh shim's log, and the rejection DETAIL files (which are not
18
+ // `.log`s but belong to the log that indexes them) are all here.
19
+ //
20
+ // It used to be two directories — `hooks/`, holding the binary's logs mixed in with dated
21
+ // `hooks/<YYYY-MM-DD>/writeInfo-*.md` rejection details, and `logs/`, where the sh shim wrote — so
22
+ // "where are the logs?" had two answers and neither was complete. `hooks/` is gone entirely; nothing
23
+ // reads or writes it.
24
+ //
19
25
  // Every writer resolves its DIRECTORY through dotWebpieces.logs()/logsFile(), so the layout cannot
20
26
  // drift apart again. The FILENAME is a separate question: ai-hook-rules' LogStream prefixes it with
21
27
  // <sessionId>-<agentId|coordinator>-<hook>- so that concurrent writers (Claude Code runs all matching
22
- // PreToolUse hooks IN PARALLEL, and subagents/windows share a tree) never append to one file. This dir
23
- // is still the one home for all of them.
28
+ // PreToolUse hooks IN PARALLEL, and subagents/windows share a tree) never append to one file. The
29
+ // rejection details sit in a DIRECTORY carrying that identical prefix, beside the log that points at
30
+ // them, which gives them the same one-writer-per-name property.
24
31
  exports.LOGS_STATE_DIR = 'logs';
25
- // Non-log hook state: the dated rejection-detail directories. Named here (rather than re-spelled in
26
- // each consumer) so the hooks/logs split has exactly one definition.
27
- exports.HOOKS_STATE_DIR = 'hooks';
28
32
  // git prints the shared git dir as `<primary>/.git` for a conventional clone. Anything else (a bare
29
33
  // repo, `--separate-git-dir`) is a layout we decline to derive a working tree from.
30
34
  const GIT_DIR_NAME = '.git';
@@ -122,8 +126,6 @@ let DotWebpieces = class DotWebpieces {
122
126
  gitDirsByRoot = new Map();
123
127
  // Roots whose legacy per-worktree `.webpieces/` has already been considered for migration.
124
128
  migrated = new Set();
125
- // Roots whose legacy `hooks/*.log` files have already been considered for relocation into `logs/`.
126
- logsMigrated = new Set();
127
129
  constructor(migrator = new state_dir_migration_1.StateDirMigrator()) {
128
130
  this.migrator = migrator;
129
131
  }
@@ -161,18 +163,17 @@ let DotWebpieces = class DotWebpieces {
161
163
  return path.join(this.local(startDir), ...segments);
162
164
  }
163
165
  /**
164
- * THE log directory — `<local()>/logs` — and the only place a webpieces `.log` may be written, in
165
- * the primary clone and in every worktree namespace alike.
166
+ * THE log directory — `<local()>/logs` — and the only place webpieces writes a `.log` or a log's
167
+ * detail files, in the primary clone and in every worktree namespace alike.
166
168
  *
167
- * The first call per tree also RELOCATES any `hooks/*.log` written by an older release (or by the
168
- * still-published build during the transition window) into `logs/`, so upgrading does not orphan
169
- * the history a human is mid-way through reading. Same safety rule as StateDirMigrator.migrate:
170
- * an occupied destination is never overwritten the old copy is left where it is.
169
+ * There is NO migration here, deliberately. A lazy once-per-process relocation of an older
170
+ * release's `hooks/*.log` used to run on this path, and it was itself the defect it claimed to
171
+ * cure: it only fires in the trees a process happens to re-enter, so it guarantees two answers to
172
+ * "where are the logs" indefinitely rather than converging on one. New writes go to `logs/` and
173
+ * nothing else; a stale `hooks/` left by an older release is inert and can be deleted by hand.
171
174
  */
172
175
  logs(startDir) {
173
- const target = path.join(this.local(startDir), exports.LOGS_STATE_DIR);
174
- this.migrateLogsOnce(startDir, target);
175
- return target;
176
+ return path.join(this.local(startDir), exports.LOGS_STATE_DIR);
176
177
  }
177
178
  /** A path beneath the log directory — `dotWebpieces.logsFile(root, 'guard-invocations.log')`. */
178
179
  logsFile(startDir, ...segments) {
@@ -227,14 +228,6 @@ let DotWebpieces = class DotWebpieces {
227
228
  return;
228
229
  this.migrator.migrate(this.legacyDir(toplevel), target);
229
230
  }
230
- // Drain `hooks/*.log` into `logs/`, at most once per tree per process. Idempotent, but it touches
231
- // the filesystem on the hook's blocking path, so it must not run per log line.
232
- migrateLogsOnce(startDir, target) {
233
- if (this.logsMigrated.has(startDir))
234
- return;
235
- this.logsMigrated.add(startDir);
236
- this.migrator.migrateLogFiles(path.join(this.local(startDir), exports.HOOKS_STATE_DIR), target);
237
- }
238
231
  // Both git dirs for `startDir`, cached, or null when this is not a git repo / git is unavailable.
239
232
  // `status !== 0` IS the expected "not a repo" answer (spawnSync does not throw on a non-zero exit),
240
233
  // so there is no try/catch here swallowing a real git crash. Mirrors RepoRootFinder.gitToplevel.
@@ -1 +1 @@
1
- {"version":3,"file":"state-dir.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/state-dir.ts"],"names":[],"mappings":";;;;AAAA,iDAA0C;AAC1C,+CAAyB;AACzB,mDAA6B;AAC7B,yCAA2D;AAE3D,2CAAgD;AAChD,+DAAyD;AAEzD,uGAAuG;AACvG,2FAA2F;AAC3F,8DAA8D;AACjD,QAAA,kBAAkB,GAAG,WAAW,CAAC;AAE9C,qGAAqG;AACrG,mGAAmG;AACnG,qGAAqG;AACrG,sGAAsG;AACtG,mGAAmG;AACnG,oGAAoG;AACpG,sGAAsG;AACtG,uGAAuG;AACvG,yCAAyC;AAC5B,QAAA,cAAc,GAAG,MAAM,CAAC;AAErC,oGAAoG;AACpG,qEAAqE;AACxD,QAAA,eAAe,GAAG,OAAO,CAAC;AAEvC,oGAAoG;AACpG,oFAAoF;AACpF,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B;;;;;;GAMG;AACH,MAAa,OAAO;IACP,MAAM,CAAS;IACf,SAAS,CAAS;IAE3B,YAAY,MAAc,EAAE,SAAiB;QACzC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;IAED,IAAI,gBAAgB;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtE,CAAC;CACJ;AAZD,0BAYC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;IASQ;IAR7B,kGAAkG;IAClG,mCAAmC;IAClB,aAAa,GAAG,IAAI,GAAG,EAA0B,CAAC;IACnE,2FAA2F;IAC1E,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9C,mGAAmG;IAClF,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IAElD,YAA6B,WAA6B,IAAI,sCAAgB,EAAE;QAAnD,aAAQ,GAAR,QAAQ,CAA2C;IAAG,CAAC;IAEpF;;;;OAIG;IACH,MAAM,CAAC,QAAgB;QACnB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,6BAAiB,CAAC,CAAC;IACpE,CAAC;IAED,8CAA8C;IAC9C,UAAU,CAAC,QAAgB,EAAE,GAAG,QAAkB;QAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC;IACzD,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,QAAgB;QAClB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,gBAAgB;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE1E,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,0BAAkB,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAChG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACnC,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,wDAAwD;IACxD,SAAS,CAAC,QAAgB,EAAE,GAAG,QAAkB;QAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC;IACxD,CAAC;IAED;;;;;;;;OAQG;IACH,IAAI,CAAC,QAAgB;QACjB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,sBAAc,CAAC,CAAC;QAC/D,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,iGAAiG;IACjG,QAAQ,CAAC,QAAgB,EAAE,GAAG,QAAkB;QAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC;IACvD,CAAC;IAED,oFAAoF;IACpF,gBAAgB,CAAC,QAAgB;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpC,OAAO,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,gBAAgB,CAAC;IAClD,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,QAAgB;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,gBAAgB;YAAE,OAAO,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,iGAAiG;IACjG,WAAW,CAAC,QAAgB;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,QAAQ,CAAC;QACnC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,YAAY;YAAE,OAAO,QAAQ,CAAC;QACpE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7C,OAAO,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;IACvD,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,QAAgB;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,6BAAiB,CAAC,CAAC;IAClD,CAAC;IAED,kGAAkG;IAClG,mGAAmG;IAC3F,WAAW,CAAC,QAAgB,EAAE,MAAc;QAChD,iGAAiG;QACjG,2FAA2F;QAC3F,gFAAgF;QAChF,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO;QACxC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO;QAC9B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5D,CAAC;IAED,kGAAkG;IAClG,+EAA+E;IACvE,eAAe,CAAC,QAAgB,EAAE,MAAc;QACpD,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO;QAC5C,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,uBAAe,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5F,CAAC;IAED,kGAAkG;IAClG,oGAAoG;IACpG,iGAAiG;IACzF,OAAO,CAAC,QAAgB;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QAExC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QAC9D,MAAM,IAAI,GAAG,MAAM,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC3F,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IAChB,CAAC;IAEO,WAAW,CAAC,QAAgB;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;IACtD,CAAC;IAED,kGAAkG;IAClG,gFAAgF;IACxE,QAAQ,CAAC,GAAW,EAAE,IAAY;QACtC,MAAM,MAAM,GAAG,IAAA,yBAAS,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QACtF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,IAAI,OAAO,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QAChC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;CACJ,CAAA;AAxJY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;6CAUE,sCAAgB;GAT9C,YAAY,CAwJxB;AAED,sGAAsG;AACtG,sGAAsG;AACtG,uFAAuF;AAC1E,QAAA,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC","sourcesContent":["import { spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { WEBPIECES_TMP_DIR } from './constants';\nimport { StateDirMigrator } from './state-dir-migration';\n\n// The per-worktree namespace inside the primary clone's `.webpieces/`. A LINKED worktree's local state\n// lives at `<primary>/.webpieces/worktrees/<worktreeName>/`; the primary clone keeps using\n// `<primary>/.webpieces/` directly, exactly as it always has.\nexport const WORKTREE_STATE_DIR = 'worktrees';\n\n// The ONE directory every webpieces `.log` lives in — `<state>/logs/`, for the primary clone and for\n// each worktree namespace alike. It used to be `hooks/`, which also holds NON-log state (the dated\n// `hooks/<YYYY-MM-DD>/writeInfo-*.md` rejection details), while the L0 sh shim wrote to `logs/`. Two\n// directories, one of them mixed — so \"where are the logs?\" had two answers and neither was complete.\n// Every writer resolves its DIRECTORY through dotWebpieces.logs()/logsFile(), so the layout cannot\n// drift apart again. The FILENAME is a separate question: ai-hook-rules' LogStream prefixes it with\n// <sessionId>-<agentId|coordinator>-<hook>- so that concurrent writers (Claude Code runs all matching\n// PreToolUse hooks IN PARALLEL, and subagents/windows share a tree) never append to one file. This dir\n// is still the one home for all of them.\nexport const LOGS_STATE_DIR = 'logs';\n\n// Non-log hook state: the dated rejection-detail directories. Named here (rather than re-spelled in\n// each consumer) so the hooks/logs split has exactly one definition.\nexport const HOOKS_STATE_DIR = 'hooks';\n\n// git prints the shared git dir as `<primary>/.git` for a conventional clone. Anything else (a bare\n// repo, `--separate-git-dir`) is a layout we decline to derive a working tree from.\nconst GIT_DIR_NAME = '.git';\n\n/**\n * Data-only carrier for the two paths git is asked for. Per CLAUDE.md: classes for data.\n *\n * `gitDir` is the PER-WORKTREE git dir (`<primary>/.git/worktrees/<name>` in a linked worktree,\n * `<primary>/.git` in the primary clone). `commonDir` is the SHARED one — always `<primary>/.git`.\n * They differ if and only if this is a linked worktree; that is git's own canonical test.\n */\nexport class GitDirs {\n readonly gitDir: string;\n readonly commonDir: string;\n\n constructor(gitDir: string, commonDir: string) {\n this.gitDir = gitDir;\n this.commonDir = commonDir;\n }\n\n get isLinkedWorktree(): boolean {\n return path.resolve(this.gitDir) !== path.resolve(this.commonDir);\n }\n}\n\n/**\n * WHERE a piece of `.webpieces/` state belongs — the ONE resolver every reader and writer must go\n * through, with TWO named methods because there are exactly two answers and a call site must DECLARE\n * which one it means.\n *\n * dotWebpieces.shared(dir) → <primary>/.webpieces (repo-wide facts)\n * dotWebpieces.local(dir) → <primary>/.webpieces/worktrees/<name> (this worktree only)\n * → <primary>/.webpieces (…in the primary clone)\n *\n * ─── The bug ───────────────────────────────────────────────────────────────────────────────────────\n * `.webpieces/` is gitignored, and it was anchored at the directory holding webpieces.config.json —\n * which in a linked worktree is the WORKTREE. So a repo with seven worktrees had SEVEN independent\n * copies of files that describe the WHOLE REPO. `merged-branches.json` holds verdicts for every branch\n * AND every worktree in the repo; N copies is N divergent truths, and the guards read them as fact.\n * Observed in the field: branch-creation-guard asserted \"8 parked local branches\" while\n * `git branch --list` showed ONE — it was reading a cache written before deletions performed from a\n * DIFFERENT worktree — and it then blocked a legitimate `git worktree add` on that fiction.\n *\n * ─── Why two explicit methods and not a symlink ────────────────────────────────────────────────────\n * A `<worktree>/.webpieces` → `<primary>/…/worktrees/<name>` symlink would have left every call site\n * untouched, which is seductive and wrong. Nothing hooks `git worktree add`, so the link needs LAZY\n * creation that also has to handle \"link already exists\", \"link points somewhere else\", and \"a real\n * directory is already there\" — invisible filesystem magic with a Windows failure mode. Worse, the safe\n * way to write a shared file under concurrency is temp-file-then-`rename()`, and `rename(2)` acts on\n * the PATH, not the link: it REPLACES a symlink with a real file. That silently works for one writer\n * and diverges for everyone else — the exact bug being fixed, but invisible. An explicit call is\n * greppable, testable, and forces each site to say which scope it means.\n *\n * ─── Which scope is which (the scope assignment is deliberate, not incidental) ─────────────────────\n * shared():\n * • merged-branches.json — verdicts for every branch and worktree in the repo. Atomically written.\n * • main-sync-status.json AND main-sync.lock.json — there is one `main` and one `.git`, so there is\n * one refresher. A lock inside a per-worktree directory locks nothing.\n * local():\n * • logs/*.log, INCLUDING branch-mutations.log. A shared append-only log genuinely corrupts:\n * `O_APPEND` writes are indivisible only under PIPE_BUF, which is 512 bytes on macOS, and a\n * `recover=git worktree add -b <branch> <abs-path> <tag>` line with real paths exceeds that. A\n * per-worktree log has exactly ONE writer and cannot tear, and under this layout it already\n * survives the worktree's deletion — recovery is one glob over\n * `<primary>/.webpieces/worktrees/*/logs/branch-mutations.log`.\n * • merge-info/staged|merged/<branch>/ and its index.json, pr-review/<branch>/, instruct-ai/, and\n * every other per-tree scratch file.\n *\n * ─── The boundary that must not be blurred ─────────────────────────────────────────────────────────\n * ONLY the gitignored `.webpieces/` STATE relocates. `webpieces.config.json` is TRACKED IN GIT and is\n * therefore part of the BRANCH: a branch may legitimately change its own rules and that must keep\n * working. Config resolution is untouched — still per-worktree, via findConfigFile /\n * RepoRootFinder.resolveRepoRoot. Nothing in this class reads or moves config.\n *\n * ─── Why `--git-dir` / `--git-common-dir`, and not one of the existing services ────────────────────\n * They are git's own answers, from any subdirectory, in one cheap local call, with no `.git`-file\n * parsing by hand (which gets `--separate-git-dir` and submodules wrong). Two existing mechanisms do\n * distinguish primary from linked, and neither is the right authority here:\n * • `WorktreeService` answers \"what worktrees exist and what do they hold\" — a repo-wide ENUMERATION\n * (`git worktree list --porcelain`) for the caps and the reaper. Using it for a path lookup would\n * run an enumeration on the hook's blocking path for every state access, and it fails SOFT to `[]`,\n * which here would read as \"there is no primary clone\" on exactly the degraded repo where the\n * answer matters most. It also does not expose the worktree's git NAME, which is the namespace key.\n * • `EffectiveTreeResolver` (#524) answers \"which tree does this COMMAND act on\" — it takes a command\n * string and is a policy input to the bash guards, not a filesystem-path resolver.\n * Both remain authoritative for their own questions. This asks the narrowest one — two path strings —\n * and lives in rules-config, UNDER both, which is where a primitive that pr-gate, ai-hook-rules and\n * code-rules all need has to sit.\n *\n * Fails CLOSED to the pre-change behaviour: when git cannot answer, every path here collapses to\n * `<startDir-root>/.webpieces` exactly as before. Degrading to merely-suboptimal beats throwing on a\n * hook's blocking path.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class DotWebpieces {\n // treeRoot → git's answer. One `git rev-parse` pair per root per process; every later path lookup\n // in that invocation is a Map hit.\n private readonly gitDirsByRoot = new Map<string, GitDirs | null>();\n // Roots whose legacy per-worktree `.webpieces/` has already been considered for migration.\n private readonly migrated = new Set<string>();\n // Roots whose legacy `hooks/*.log` files have already been considered for relocation into `logs/`.\n private readonly logsMigrated = new Set<string>();\n\n constructor(private readonly migrator: StateDirMigrator = new StateDirMigrator()) {}\n\n /**\n * REPO-WIDE state: `<primary>/.webpieces`. Use ONLY for facts about the repo rather than about one\n * worktree — today that is merged-branches.json and the main-sync status + lock. Identical from\n * every worktree, and never behind an indirection, so an atomic `rename()` into it is safe.\n */\n shared(startDir: string): string {\n return path.join(this.primaryRoot(startDir), WEBPIECES_TMP_DIR);\n }\n\n /** A path beneath the repo-wide state dir. */\n sharedFile(startDir: string, ...segments: string[]): string {\n return path.join(this.shared(startDir), ...segments);\n }\n\n /**\n * THIS WORKTREE's private state: `<primary>/.webpieces/worktrees/<name>` for a linked worktree, and\n * `<primary>/.webpieces` for the primary clone, which keeps its state exactly where it has always\n * been. Fully isolated — two worktrees never write the same path, so nothing here needs a lock.\n *\n * The first call for a linked worktree also MIGRATES a legacy real `<worktree>/.webpieces/`\n * directory into the namespace, so in-flight merge / pr-review state written under the old scheme\n * (or by an older PUBLISHED build during the transition) is picked up rather than orphaned.\n */\n local(startDir: string): string {\n const dirs = this.gitDirs(startDir);\n if (dirs === null || !dirs.isLinkedWorktree) return this.shared(startDir);\n\n const target = path.join(this.shared(startDir), WORKTREE_STATE_DIR, path.basename(dirs.gitDir));\n this.migrateOnce(startDir, target);\n return target;\n }\n\n /** A path beneath this worktree's private state dir. */\n localFile(startDir: string, ...segments: string[]): string {\n return path.join(this.local(startDir), ...segments);\n }\n\n /**\n * THE log directory — `<local()>/logs` — and the only place a webpieces `.log` may be written, in\n * the primary clone and in every worktree namespace alike.\n *\n * The first call per tree also RELOCATES any `hooks/*.log` written by an older release (or by the\n * still-published build during the transition window) into `logs/`, so upgrading does not orphan\n * the history a human is mid-way through reading. Same safety rule as StateDirMigrator.migrate:\n * an occupied destination is never overwritten — the old copy is left where it is.\n */\n logs(startDir: string): string {\n const target = path.join(this.local(startDir), LOGS_STATE_DIR);\n this.migrateLogsOnce(startDir, target);\n return target;\n }\n\n /** A path beneath the log directory — `dotWebpieces.logsFile(root, 'guard-invocations.log')`. */\n logsFile(startDir: string, ...segments: string[]): string {\n return path.join(this.logs(startDir), ...segments);\n }\n\n /** True when `startDir` sits in a LINKED worktree rather than the primary clone. */\n isLinkedWorktree(startDir: string): boolean {\n const dirs = this.gitDirs(startDir);\n return dirs !== null && dirs.isLinkedWorktree;\n }\n\n /**\n * git's own name for this linked worktree — the basename of `<primary>/.git/worktrees/<name>`, and\n * the namespace key under `worktrees/`. Empty for the primary clone. git's name rather than the\n * directory's basename, so two worktrees checked out into same-named directories under different\n * parents cannot collide.\n */\n worktreeName(startDir: string): string {\n const dirs = this.gitDirs(startDir);\n if (dirs === null || !dirs.isLinkedWorktree) return '';\n return path.basename(dirs.gitDir);\n }\n\n /** The primary clone's root, from any worktree. Falls back to git's toplevel-less best guess. */\n primaryRoot(startDir: string): string {\n const dirs = this.gitDirs(startDir);\n if (dirs === null) return startDir;\n if (path.basename(dirs.commonDir) !== GIT_DIR_NAME) return startDir;\n const primary = path.dirname(dirs.commonDir);\n return fs.existsSync(primary) ? primary : startDir;\n }\n\n /**\n * The PRE-change location, `<treeRoot>/.webpieces` — what every call site used to compute. Public\n * because the migrator and its specs must be able to name the thing being migrated FROM, and\n * because the transition-window fallback readers need it.\n */\n legacyDir(treeRoot: string): string {\n return path.join(treeRoot, WEBPIECES_TMP_DIR);\n }\n\n // Drain a legacy per-worktree `.webpieces/` into this worktree's namespace, at most once per tree\n // per process. Migration is idempotent, but it touches the filesystem on the hook's blocking path.\n private migrateOnce(startDir: string, target: string): void {\n // Guard on the CHEAP key first. `local()` is called many times per invocation, and resolving the\n // worktree toplevel costs a `git rev-parse` — doing that before the once-check would put a\n // process spawn on the hook's blocking path for every single state-path lookup.\n if (this.migrated.has(startDir)) return;\n this.migrated.add(startDir);\n const toplevel = this.gitToplevel(startDir);\n if (toplevel === null) return;\n this.migrator.migrate(this.legacyDir(toplevel), target);\n }\n\n // Drain `hooks/*.log` into `logs/`, at most once per tree per process. Idempotent, but it touches\n // the filesystem on the hook's blocking path, so it must not run per log line.\n private migrateLogsOnce(startDir: string, target: string): void {\n if (this.logsMigrated.has(startDir)) return;\n this.logsMigrated.add(startDir);\n this.migrator.migrateLogFiles(path.join(this.local(startDir), HOOKS_STATE_DIR), target);\n }\n\n // Both git dirs for `startDir`, cached, or null when this is not a git repo / git is unavailable.\n // `status !== 0` IS the expected \"not a repo\" answer (spawnSync does not throw on a non-zero exit),\n // so there is no try/catch here swallowing a real git crash. Mirrors RepoRootFinder.gitToplevel.\n private gitDirs(startDir: string): GitDirs | null {\n const cached = this.gitDirsByRoot.get(startDir);\n if (cached !== undefined) return cached;\n\n const gitDir = this.revParse(startDir, '--git-dir');\n const commonDir = this.revParse(startDir, '--git-common-dir');\n const dirs = gitDir === null || commonDir === null ? null : new GitDirs(gitDir, commonDir);\n this.gitDirsByRoot.set(startDir, dirs);\n return dirs;\n }\n\n private gitToplevel(startDir: string): string | null {\n return this.revParse(startDir, '--show-toplevel');\n }\n\n // One `git rev-parse <flag>`, resolved to an absolute path (git prints a bare `.git`, relative to\n // the tree, in the primary clone, and an absolute path from a linked worktree).\n private revParse(cwd: string, flag: string): string | null {\n const result = spawnSync('git', ['-C', cwd, 'rev-parse', flag], { encoding: 'utf8' });\n if (result.status !== 0) return null;\n const printed = (result.stdout ?? '').trim();\n if (printed === '') return null;\n return path.resolve(cwd, printed);\n }\n}\n\n// Process-wide instance for the many non-DI call sites (hooks, detached refreshers, wp-* bins, eslint\n// rules). Sharing one instance is what makes the git-resolution cache and the once-per-tree migration\n// worth having; inversify still injects the singleton wherever a container is in play.\nexport const dotWebpieces = new DotWebpieces();\n"]}
1
+ {"version":3,"file":"state-dir.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/state-dir.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { WEBPIECES_TMP_DIR } from './constants';\nimport { StateDirMigrator } from './state-dir-migration';\n\n// The per-worktree namespace inside the primary clone's `.webpieces/`. A LINKED worktree's local state\n// lives at `<primary>/.webpieces/worktrees/<worktreeName>/`; the primary clone keeps using\n// `<primary>/.webpieces/` directly, exactly as it always has.\nexport const WORKTREE_STATE_DIR = 'worktrees';\n\n// The ONE and ONLY directory webpieces writes observability state into — `<state>/logs/`, for the\n// primary clone and for each worktree namespace alike. There is no second state directory and no\n// split: the L1 binary's logs, the L0 sh shim's log, and the rejection DETAIL files (which are not\n// `.log`s but belong to the log that indexes them) are all here.\n//\n// It used to be two directories — `hooks/`, holding the binary's logs mixed in with dated\n// `hooks/<YYYY-MM-DD>/writeInfo-*.md` rejection details, and `logs/`, where the sh shim wrote — so\n// \"where are the logs?\" had two answers and neither was complete. `hooks/` is gone entirely; nothing\n// reads or writes it.\n//\n// Every writer resolves its DIRECTORY through dotWebpieces.logs()/logsFile(), so the layout cannot\n// drift apart again. The FILENAME is a separate question: ai-hook-rules' LogStream prefixes it with\n// <sessionId>-<agentId|coordinator>-<hook>- so that concurrent writers (Claude Code runs all matching\n// PreToolUse hooks IN PARALLEL, and subagents/windows share a tree) never append to one file. The\n// rejection details sit in a DIRECTORY carrying that identical prefix, beside the log that points at\n// them, which gives them the same one-writer-per-name property.\nexport const LOGS_STATE_DIR = 'logs';\n\n// git prints the shared git dir as `<primary>/.git` for a conventional clone. Anything else (a bare\n// repo, `--separate-git-dir`) is a layout we decline to derive a working tree from.\nconst GIT_DIR_NAME = '.git';\n\n/**\n * Data-only carrier for the two paths git is asked for. Per CLAUDE.md: classes for data.\n *\n * `gitDir` is the PER-WORKTREE git dir (`<primary>/.git/worktrees/<name>` in a linked worktree,\n * `<primary>/.git` in the primary clone). `commonDir` is the SHARED one — always `<primary>/.git`.\n * They differ if and only if this is a linked worktree; that is git's own canonical test.\n */\nexport class GitDirs {\n readonly gitDir: string;\n readonly commonDir: string;\n\n constructor(gitDir: string, commonDir: string) {\n this.gitDir = gitDir;\n this.commonDir = commonDir;\n }\n\n get isLinkedWorktree(): boolean {\n return path.resolve(this.gitDir) !== path.resolve(this.commonDir);\n }\n}\n\n/**\n * WHERE a piece of `.webpieces/` state belongs — the ONE resolver every reader and writer must go\n * through, with TWO named methods because there are exactly two answers and a call site must DECLARE\n * which one it means.\n *\n * dotWebpieces.shared(dir) → <primary>/.webpieces (repo-wide facts)\n * dotWebpieces.local(dir) → <primary>/.webpieces/worktrees/<name> (this worktree only)\n * → <primary>/.webpieces (…in the primary clone)\n *\n * ─── The bug ───────────────────────────────────────────────────────────────────────────────────────\n * `.webpieces/` is gitignored, and it was anchored at the directory holding webpieces.config.json —\n * which in a linked worktree is the WORKTREE. So a repo with seven worktrees had SEVEN independent\n * copies of files that describe the WHOLE REPO. `merged-branches.json` holds verdicts for every branch\n * AND every worktree in the repo; N copies is N divergent truths, and the guards read them as fact.\n * Observed in the field: branch-creation-guard asserted \"8 parked local branches\" while\n * `git branch --list` showed ONE — it was reading a cache written before deletions performed from a\n * DIFFERENT worktree — and it then blocked a legitimate `git worktree add` on that fiction.\n *\n * ─── Why two explicit methods and not a symlink ────────────────────────────────────────────────────\n * A `<worktree>/.webpieces` → `<primary>/…/worktrees/<name>` symlink would have left every call site\n * untouched, which is seductive and wrong. Nothing hooks `git worktree add`, so the link needs LAZY\n * creation that also has to handle \"link already exists\", \"link points somewhere else\", and \"a real\n * directory is already there\" — invisible filesystem magic with a Windows failure mode. Worse, the safe\n * way to write a shared file under concurrency is temp-file-then-`rename()`, and `rename(2)` acts on\n * the PATH, not the link: it REPLACES a symlink with a real file. That silently works for one writer\n * and diverges for everyone else — the exact bug being fixed, but invisible. An explicit call is\n * greppable, testable, and forces each site to say which scope it means.\n *\n * ─── Which scope is which (the scope assignment is deliberate, not incidental) ─────────────────────\n * shared():\n * • merged-branches.json — verdicts for every branch and worktree in the repo. Atomically written.\n * • main-sync-status.json AND main-sync.lock.json — there is one `main` and one `.git`, so there is\n * one refresher. A lock inside a per-worktree directory locks nothing.\n * local():\n * • logs/*.log, INCLUDING branch-mutations.log. A shared append-only log genuinely corrupts:\n * `O_APPEND` writes are indivisible only under PIPE_BUF, which is 512 bytes on macOS, and a\n * `recover=git worktree add -b <branch> <abs-path> <tag>` line with real paths exceeds that. A\n * per-worktree log has exactly ONE writer and cannot tear, and under this layout it already\n * survives the worktree's deletion — recovery is one glob over\n * `<primary>/.webpieces/worktrees/*/logs/branch-mutations.log`.\n * • merge-info/staged|merged/<branch>/ and its index.json, pr-review/<branch>/, instruct-ai/, and\n * every other per-tree scratch file.\n *\n * ─── The boundary that must not be blurred ─────────────────────────────────────────────────────────\n * ONLY the gitignored `.webpieces/` STATE relocates. `webpieces.config.json` is TRACKED IN GIT and is\n * therefore part of the BRANCH: a branch may legitimately change its own rules and that must keep\n * working. Config resolution is untouched — still per-worktree, via findConfigFile /\n * RepoRootFinder.resolveRepoRoot. Nothing in this class reads or moves config.\n *\n * ─── Why `--git-dir` / `--git-common-dir`, and not one of the existing services ────────────────────\n * They are git's own answers, from any subdirectory, in one cheap local call, with no `.git`-file\n * parsing by hand (which gets `--separate-git-dir` and submodules wrong). Two existing mechanisms do\n * distinguish primary from linked, and neither is the right authority here:\n * • `WorktreeService` answers \"what worktrees exist and what do they hold\" — a repo-wide ENUMERATION\n * (`git worktree list --porcelain`) for the caps and the reaper. Using it for a path lookup would\n * run an enumeration on the hook's blocking path for every state access, and it fails SOFT to `[]`,\n * which here would read as \"there is no primary clone\" on exactly the degraded repo where the\n * answer matters most. It also does not expose the worktree's git NAME, which is the namespace key.\n * • `EffectiveTreeResolver` (#524) answers \"which tree does this COMMAND act on\" — it takes a command\n * string and is a policy input to the bash guards, not a filesystem-path resolver.\n * Both remain authoritative for their own questions. This asks the narrowest one — two path strings —\n * and lives in rules-config, UNDER both, which is where a primitive that pr-gate, ai-hook-rules and\n * code-rules all need has to sit.\n *\n * Fails CLOSED to the pre-change behaviour: when git cannot answer, every path here collapses to\n * `<startDir-root>/.webpieces` exactly as before. Degrading to merely-suboptimal beats throwing on a\n * hook's blocking path.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class DotWebpieces {\n // treeRoot → git's answer. One `git rev-parse` pair per root per process; every later path lookup\n // in that invocation is a Map hit.\n private readonly gitDirsByRoot = new Map<string, GitDirs | null>();\n // Roots whose legacy per-worktree `.webpieces/` has already been considered for migration.\n private readonly migrated = new Set<string>();\n\n constructor(private readonly migrator: StateDirMigrator = new StateDirMigrator()) {}\n\n /**\n * REPO-WIDE state: `<primary>/.webpieces`. Use ONLY for facts about the repo rather than about one\n * worktree — today that is merged-branches.json and the main-sync status + lock. Identical from\n * every worktree, and never behind an indirection, so an atomic `rename()` into it is safe.\n */\n shared(startDir: string): string {\n return path.join(this.primaryRoot(startDir), WEBPIECES_TMP_DIR);\n }\n\n /** A path beneath the repo-wide state dir. */\n sharedFile(startDir: string, ...segments: string[]): string {\n return path.join(this.shared(startDir), ...segments);\n }\n\n /**\n * THIS WORKTREE's private state: `<primary>/.webpieces/worktrees/<name>` for a linked worktree, and\n * `<primary>/.webpieces` for the primary clone, which keeps its state exactly where it has always\n * been. Fully isolated — two worktrees never write the same path, so nothing here needs a lock.\n *\n * The first call for a linked worktree also MIGRATES a legacy real `<worktree>/.webpieces/`\n * directory into the namespace, so in-flight merge / pr-review state written under the old scheme\n * (or by an older PUBLISHED build during the transition) is picked up rather than orphaned.\n */\n local(startDir: string): string {\n const dirs = this.gitDirs(startDir);\n if (dirs === null || !dirs.isLinkedWorktree) return this.shared(startDir);\n\n const target = path.join(this.shared(startDir), WORKTREE_STATE_DIR, path.basename(dirs.gitDir));\n this.migrateOnce(startDir, target);\n return target;\n }\n\n /** A path beneath this worktree's private state dir. */\n localFile(startDir: string, ...segments: string[]): string {\n return path.join(this.local(startDir), ...segments);\n }\n\n /**\n * THE log directory — `<local()>/logs` — and the only place webpieces writes a `.log` or a log's\n * detail files, in the primary clone and in every worktree namespace alike.\n *\n * There is NO migration here, deliberately. A lazy once-per-process relocation of an older\n * release's `hooks/*.log` used to run on this path, and it was itself the defect it claimed to\n * cure: it only fires in the trees a process happens to re-enter, so it guarantees two answers to\n * \"where are the logs\" indefinitely rather than converging on one. New writes go to `logs/` and\n * nothing else; a stale `hooks/` left by an older release is inert and can be deleted by hand.\n */\n logs(startDir: string): string {\n return path.join(this.local(startDir), LOGS_STATE_DIR);\n }\n\n /** A path beneath the log directory — `dotWebpieces.logsFile(root, 'guard-invocations.log')`. */\n logsFile(startDir: string, ...segments: string[]): string {\n return path.join(this.logs(startDir), ...segments);\n }\n\n /** True when `startDir` sits in a LINKED worktree rather than the primary clone. */\n isLinkedWorktree(startDir: string): boolean {\n const dirs = this.gitDirs(startDir);\n return dirs !== null && dirs.isLinkedWorktree;\n }\n\n /**\n * git's own name for this linked worktree — the basename of `<primary>/.git/worktrees/<name>`, and\n * the namespace key under `worktrees/`. Empty for the primary clone. git's name rather than the\n * directory's basename, so two worktrees checked out into same-named directories under different\n * parents cannot collide.\n */\n worktreeName(startDir: string): string {\n const dirs = this.gitDirs(startDir);\n if (dirs === null || !dirs.isLinkedWorktree) return '';\n return path.basename(dirs.gitDir);\n }\n\n /** The primary clone's root, from any worktree. Falls back to git's toplevel-less best guess. */\n primaryRoot(startDir: string): string {\n const dirs = this.gitDirs(startDir);\n if (dirs === null) return startDir;\n if (path.basename(dirs.commonDir) !== GIT_DIR_NAME) return startDir;\n const primary = path.dirname(dirs.commonDir);\n return fs.existsSync(primary) ? primary : startDir;\n }\n\n /**\n * The PRE-change location, `<treeRoot>/.webpieces` — what every call site used to compute. Public\n * because the migrator and its specs must be able to name the thing being migrated FROM, and\n * because the transition-window fallback readers need it.\n */\n legacyDir(treeRoot: string): string {\n return path.join(treeRoot, WEBPIECES_TMP_DIR);\n }\n\n // Drain a legacy per-worktree `.webpieces/` into this worktree's namespace, at most once per tree\n // per process. Migration is idempotent, but it touches the filesystem on the hook's blocking path.\n private migrateOnce(startDir: string, target: string): void {\n // Guard on the CHEAP key first. `local()` is called many times per invocation, and resolving the\n // worktree toplevel costs a `git rev-parse` — doing that before the once-check would put a\n // process spawn on the hook's blocking path for every single state-path lookup.\n if (this.migrated.has(startDir)) return;\n this.migrated.add(startDir);\n const toplevel = this.gitToplevel(startDir);\n if (toplevel === null) return;\n this.migrator.migrate(this.legacyDir(toplevel), target);\n }\n\n // Both git dirs for `startDir`, cached, or null when this is not a git repo / git is unavailable.\n // `status !== 0` IS the expected \"not a repo\" answer (spawnSync does not throw on a non-zero exit),\n // so there is no try/catch here swallowing a real git crash. Mirrors RepoRootFinder.gitToplevel.\n private gitDirs(startDir: string): GitDirs | null {\n const cached = this.gitDirsByRoot.get(startDir);\n if (cached !== undefined) return cached;\n\n const gitDir = this.revParse(startDir, '--git-dir');\n const commonDir = this.revParse(startDir, '--git-common-dir');\n const dirs = gitDir === null || commonDir === null ? null : new GitDirs(gitDir, commonDir);\n this.gitDirsByRoot.set(startDir, dirs);\n return dirs;\n }\n\n private gitToplevel(startDir: string): string | null {\n return this.revParse(startDir, '--show-toplevel');\n }\n\n // One `git rev-parse <flag>`, resolved to an absolute path (git prints a bare `.git`, relative to\n // the tree, in the primary clone, and an absolute path from a linked worktree).\n private revParse(cwd: string, flag: string): string | null {\n const result = spawnSync('git', ['-C', cwd, 'rev-parse', flag], { encoding: 'utf8' });\n if (result.status !== 0) return null;\n const printed = (result.stdout ?? '').trim();\n if (printed === '') return null;\n return path.resolve(cwd, printed);\n }\n}\n\n// Process-wide instance for the many non-DI call sites (hooks, detached refreshers, wp-* bins, eslint\n// rules). Sharing one instance is what makes the git-resolution cache and the once-per-tree migration\n// worth having; inversify still injects the singleton wherever a container is in play.\nexport const dotWebpieces = new DotWebpieces();\n"]}