@webpieces/rules-config 0.4.598 → 0.4.600
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 +1 -1
- package/src/aged-tree-sweep.d.ts +6 -6
- package/src/aged-tree-sweep.js +6 -6
- package/src/aged-tree-sweep.js.map +1 -1
- package/src/index.d.ts +0 -2
- package/src/index.js +12 -22
- package/src/index.js.map +1 -1
- package/src/machine-state-home.d.ts +0 -75
- package/src/machine-state-home.js +0 -152
- package/src/machine-state-home.js.map +0 -1
- package/src/pr-body-store.d.ts +0 -143
- package/src/pr-body-store.js +0 -301
- package/src/pr-body-store.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/rules-config",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.600",
|
|
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",
|
package/src/aged-tree-sweep.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* How long a file survives an AGED-TREE SWEEP without being rewritten — the policy `cleanTmp` applies
|
|
3
|
-
* to
|
|
3
|
+
* to `{repo}/.webpieces`, which is the only root webpieces writes state under.
|
|
4
4
|
*
|
|
5
5
|
* NOT the same knob as `DEFAULT_RETENTION_DAYS` in review-provenance.ts, which happens to be 30 as well.
|
|
6
6
|
* That one is a CONFIGURABLE ceiling on reviewer transcripts, overridable per repo in settings; this one
|
|
@@ -19,11 +19,11 @@ export declare class SweepCount {
|
|
|
19
19
|
/**
|
|
20
20
|
* The 30-day depth-first reap, as ONE implementation.
|
|
21
21
|
*
|
|
22
|
-
* It was written inside `CleanTmp` for `<primary>/.webpieces`, and
|
|
23
|
-
* (
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* It was written inside `CleanTmp` for `<primary>/.webpieces`, and extracted here when a second root
|
|
23
|
+
* (the machine-global PR-body store) needed the identical policy. That second root is gone — the gated
|
|
24
|
+
* merge body is the PR's own description now, see `decisions/0005` — so today there is one caller. The
|
|
25
|
+
* loop stays extracted anyway: it is the sweep policy, testable on its own root, and `CleanTmp` reads
|
|
26
|
+
* as the command it is rather than as a deletion algorithm.
|
|
27
27
|
*
|
|
28
28
|
* `onDelete` is a reporter, not a policy hook: it is told what was removed AFTER the decision, so a
|
|
29
29
|
* caller can print a line and cannot change the outcome.
|
package/src/aged-tree-sweep.js
CHANGED
|
@@ -7,7 +7,7 @@ const path = tslib_1.__importStar(require("path"));
|
|
|
7
7
|
const inversify_1 = require("inversify");
|
|
8
8
|
/**
|
|
9
9
|
* How long a file survives an AGED-TREE SWEEP without being rewritten — the policy `cleanTmp` applies
|
|
10
|
-
* to
|
|
10
|
+
* to `{repo}/.webpieces`, which is the only root webpieces writes state under.
|
|
11
11
|
*
|
|
12
12
|
* NOT the same knob as `DEFAULT_RETENTION_DAYS` in review-provenance.ts, which happens to be 30 as well.
|
|
13
13
|
* That one is a CONFIGURABLE ceiling on reviewer transcripts, overridable per repo in settings; this one
|
|
@@ -35,11 +35,11 @@ exports.SweepCount = SweepCount;
|
|
|
35
35
|
/**
|
|
36
36
|
* The 30-day depth-first reap, as ONE implementation.
|
|
37
37
|
*
|
|
38
|
-
* It was written inside `CleanTmp` for `<primary>/.webpieces`, and
|
|
39
|
-
* (
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
38
|
+
* It was written inside `CleanTmp` for `<primary>/.webpieces`, and extracted here when a second root
|
|
39
|
+
* (the machine-global PR-body store) needed the identical policy. That second root is gone — the gated
|
|
40
|
+
* merge body is the PR's own description now, see `decisions/0005` — so today there is one caller. The
|
|
41
|
+
* loop stays extracted anyway: it is the sweep policy, testable on its own root, and `CleanTmp` reads
|
|
42
|
+
* as the command it is rather than as a deletion algorithm.
|
|
43
43
|
*
|
|
44
44
|
* `onDelete` is a reporter, not a policy hook: it is told what was removed AFTER the decision, so a
|
|
45
45
|
* caller can print a line and cannot change the outcome.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aged-tree-sweep.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/aged-tree-sweep.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAC7B,yCAA2D;AAE3D;;;;;;;;GAQG;AACU,QAAA,cAAc,GAAG,EAAE,CAAC;AAEjC,wGAAwG;AACxG,MAAa,UAAU;IACnB,KAAK,CAAS;IACd,IAAI,CAAS;IAEb,YAAY,KAAK,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC;QAC3B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,GAAG,CAAC,KAAiB;QACjB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED,IAAI,KAAK;QACL,OAAO,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC;IAC/C,CAAC;CACJ;AAjBD,gCAiBC;AAED;;;;;;;;;;;GAWG;AAEI,IAAM,eAAe,GAArB,MAAM,eAAe;IACxB;;;;;;OAMG;IACH,KAAK,CAAC,IAAY,EAAE,QAAgB,EAAE,WAAsD,GAAS,EAAE,GAAE,CAAC;QACtG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,UAAU,EAAE,CAAC;QAClD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IACjE,CAAC;IAED,mGAAmG;IACnG,wDAAwD;IACxD,yDAAyD;IACjD,IAAI,CAAC,GAAW,EAAE,GAAW,EAAE,QAAgB,EAAE,MAAe,EAAE,QAAmD;QACzH,MAAM,KAAK,GAAG,IAAI,UAAU,EAAE,CAAC;QAC/B,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACnC,6FAA6F;YAC7F,wFAAwF;YACxF,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACrB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC/D,CAAC;iBAAM,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,QAAQ,EAAE,CAAC;gBACxC,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBACjC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBACtB,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;YACrB,CAAC;QACL,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9C,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAClB,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACpB,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;QACpB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ,CAAA;AAtCY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,eAAe,CAsC3B","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\n/**\n * How long a file survives an AGED-TREE SWEEP without being rewritten — the policy `cleanTmp` applies\n * to
|
|
1
|
+
{"version":3,"file":"aged-tree-sweep.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/aged-tree-sweep.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAC7B,yCAA2D;AAE3D;;;;;;;;GAQG;AACU,QAAA,cAAc,GAAG,EAAE,CAAC;AAEjC,wGAAwG;AACxG,MAAa,UAAU;IACnB,KAAK,CAAS;IACd,IAAI,CAAS;IAEb,YAAY,KAAK,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC;QAC3B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,GAAG,CAAC,KAAiB;QACjB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED,IAAI,KAAK;QACL,OAAO,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC;IAC/C,CAAC;CACJ;AAjBD,gCAiBC;AAED;;;;;;;;;;;GAWG;AAEI,IAAM,eAAe,GAArB,MAAM,eAAe;IACxB;;;;;;OAMG;IACH,KAAK,CAAC,IAAY,EAAE,QAAgB,EAAE,WAAsD,GAAS,EAAE,GAAE,CAAC;QACtG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,UAAU,EAAE,CAAC;QAClD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IACjE,CAAC;IAED,mGAAmG;IACnG,wDAAwD;IACxD,yDAAyD;IACjD,IAAI,CAAC,GAAW,EAAE,GAAW,EAAE,QAAgB,EAAE,MAAe,EAAE,QAAmD;QACzH,MAAM,KAAK,GAAG,IAAI,UAAU,EAAE,CAAC;QAC/B,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACnC,6FAA6F;YAC7F,wFAAwF;YACxF,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACrB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC/D,CAAC;iBAAM,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,QAAQ,EAAE,CAAC;gBACxC,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBACjC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBACtB,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;YACrB,CAAC;QACL,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9C,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAClB,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACpB,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;QACpB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ,CAAA;AAtCY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,eAAe,CAsC3B","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\n/**\n * How long a file survives an AGED-TREE SWEEP without being rewritten — the policy `cleanTmp` applies\n * to `{repo}/.webpieces`, which is the only root webpieces writes state under.\n *\n * NOT the same knob as `DEFAULT_RETENTION_DAYS` in review-provenance.ts, which happens to be 30 as well.\n * That one is a CONFIGURABLE ceiling on reviewer transcripts, overridable per repo in settings; this one\n * is the fixed sweep cutoff and nothing reads it from config. Same number today, different decisions,\n * so folding them would couple a repo's transcript-retention preference to when scratch dirs are reaped.\n */\nexport const RETENTION_DAYS = 30;\n\n/** What one sweep reaped: files deleted for age, and directories pruned because they ended up empty. */\nexport class SweepCount {\n files: number;\n dirs: number;\n\n constructor(files = 0, dirs = 0) {\n this.files = files;\n this.dirs = dirs;\n }\n\n add(other: SweepCount): void {\n this.files += other.files;\n this.dirs += other.dirs;\n }\n\n get empty(): boolean {\n return this.files === 0 && this.dirs === 0;\n }\n}\n\n/**\n * The 30-day depth-first reap, as ONE implementation.\n *\n * It was written inside `CleanTmp` for `<primary>/.webpieces`, and extracted here when a second root\n * (the machine-global PR-body store) needed the identical policy. That second root is gone — the gated\n * merge body is the PR's own description now, see `decisions/0005` — so today there is one caller. The\n * loop stays extracted anyway: it is the sweep policy, testable on its own root, and `CleanTmp` reads\n * as the command it is rather than as a deletion algorithm.\n *\n * `onDelete` is a reporter, not a policy hook: it is told what was removed AFTER the decision, so a\n * caller can print a line and cannot change the outcome.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class AgedTreeSweeper {\n /**\n * Delete every file under `root` older than `cutoffMs`, then prune every directory left empty.\n * `root` itself is swept INTO but never removed — every writer `mkdirSync`s its target, so an empty\n * root is harmless, whereas removing it can race a concurrent writer.\n *\n * A missing `root` is a no-op, not an error.\n */\n sweep(root: string, cutoffMs: number, onDelete: (removed: string, isDir: boolean) => void = (): void => {}): SweepCount {\n if (!fs.existsSync(root)) return new SweepCount();\n return this.walk(root, Date.now(), cutoffMs, true, onDelete);\n }\n\n // Depth-first, POST-order: children first, so a directory whose last file just aged out is seen as\n // empty on the way back up and pruned in the same pass.\n // eslint-disable-next-line @typescript-eslint/max-params\n private walk(dir: string, now: number, cutoffMs: number, isRoot: boolean, onDelete: (removed: string, isDir: boolean) => void): SweepCount {\n const count = new SweepCount();\n for (const entry of fs.readdirSync(dir)) {\n const full = path.join(dir, entry);\n // lstat, never stat: a symlink is a leaf that ages out like a file and is NEVER followed, so\n // a sweep can never wander outside `dir` or delete somebody else's file through a link.\n const stat = fs.lstatSync(full);\n if (stat.isDirectory()) {\n count.add(this.walk(full, now, cutoffMs, false, onDelete));\n } else if (now - stat.mtimeMs >= cutoffMs) {\n fs.rmSync(full, { force: true });\n onDelete(full, false);\n count.files += 1;\n }\n }\n if (!isRoot && fs.readdirSync(dir).length === 0) {\n fs.rmdirSync(dir);\n onDelete(dir, true);\n count.dirs += 1;\n }\n return count;\n }\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -11,9 +11,7 @@ export { findConfigFile, CONFIG_FILENAME, ConfigFile, ConfigParseAttempt, CONFIG
|
|
|
11
11
|
export { RepoRootFinder, INSTRUCT_AI_DIR, INSTRUCT_AI_LEAF } from './repo-root';
|
|
12
12
|
export { DotWebpieces, dotWebpieces, GitDirs, WORKTREE_STATE_DIR, LOGS_STATE_DIR, HOOKS_STATE_DIR } from './state-dir';
|
|
13
13
|
export { StateDirMigrator, StateMigrationReport } from './state-dir-migration';
|
|
14
|
-
export { MachineStateHome, machineStateHome, StateHome, WEBPIECES_STATE_HOME_ENV } from './machine-state-home';
|
|
15
14
|
export { AgedTreeSweeper, SweepCount, RETENTION_DAYS } from './aged-tree-sweep';
|
|
16
|
-
export { PrBodyStore, PrBodyLocation, PrBodyOrigin, RepoSlug, PRS_STATE_DIR, MERGE_BODY_FILE, PR_ORIGIN_FILE, } from './pr-body-store';
|
|
17
15
|
export { ClaudeEnv, claudeEnv, CLAUDE_PROJECT_DIR_ENV, CLAUDE_PROJECT_DIR_UNSET } from './claude-env';
|
|
18
16
|
export { AtomicFile } from './atomic-file';
|
|
19
17
|
export { atRoot } from './at-root';
|
package/src/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
5
|
-
exports.
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
8
|
-
exports.
|
|
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 =
|
|
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;
|
|
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; } });
|
|
@@ -60,25 +60,15 @@ Object.defineProperty(exports, "HOOKS_STATE_DIR", { enumerable: true, get: funct
|
|
|
60
60
|
var state_dir_migration_1 = require("./state-dir-migration");
|
|
61
61
|
Object.defineProperty(exports, "StateDirMigrator", { enumerable: true, get: function () { return state_dir_migration_1.StateDirMigrator; } });
|
|
62
62
|
Object.defineProperty(exports, "StateMigrationReport", { enumerable: true, get: function () { return state_dir_migration_1.StateMigrationReport; } });
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Object.defineProperty(exports, "StateHome", { enumerable: true, get: function () { return machine_state_home_1.StateHome; } });
|
|
69
|
-
Object.defineProperty(exports, "WEBPIECES_STATE_HOME_ENV", { enumerable: true, get: function () { return machine_state_home_1.WEBPIECES_STATE_HOME_ENV; } });
|
|
63
|
+
// There is NO machine-global state root. `MachineStateHome`/`StateHome`/`WEBPIECES_STATE_HOME` and the
|
|
64
|
+
// `PrBodyStore` that used them are DELETED: the one artifact that needed a scope above the clone was the
|
|
65
|
+
// gated squash body, and GitHub holds it now (it IS the PR description). Every `.webpieces` path a
|
|
66
|
+
// webpieces tool writes is `{repo}/.webpieces`, resolved through `DotWebpieces` above. See
|
|
67
|
+
// `decisions/0005-the-pr-description-is-the-merge-body.md`.
|
|
70
68
|
var aged_tree_sweep_1 = require("./aged-tree-sweep");
|
|
71
69
|
Object.defineProperty(exports, "AgedTreeSweeper", { enumerable: true, get: function () { return aged_tree_sweep_1.AgedTreeSweeper; } });
|
|
72
70
|
Object.defineProperty(exports, "SweepCount", { enumerable: true, get: function () { return aged_tree_sweep_1.SweepCount; } });
|
|
73
71
|
Object.defineProperty(exports, "RETENTION_DAYS", { enumerable: true, get: function () { return aged_tree_sweep_1.RETENTION_DAYS; } });
|
|
74
|
-
var pr_body_store_1 = require("./pr-body-store");
|
|
75
|
-
Object.defineProperty(exports, "PrBodyStore", { enumerable: true, get: function () { return pr_body_store_1.PrBodyStore; } });
|
|
76
|
-
Object.defineProperty(exports, "PrBodyLocation", { enumerable: true, get: function () { return pr_body_store_1.PrBodyLocation; } });
|
|
77
|
-
Object.defineProperty(exports, "PrBodyOrigin", { enumerable: true, get: function () { return pr_body_store_1.PrBodyOrigin; } });
|
|
78
|
-
Object.defineProperty(exports, "RepoSlug", { enumerable: true, get: function () { return pr_body_store_1.RepoSlug; } });
|
|
79
|
-
Object.defineProperty(exports, "PRS_STATE_DIR", { enumerable: true, get: function () { return pr_body_store_1.PRS_STATE_DIR; } });
|
|
80
|
-
Object.defineProperty(exports, "MERGE_BODY_FILE", { enumerable: true, get: function () { return pr_body_store_1.MERGE_BODY_FILE; } });
|
|
81
|
-
Object.defineProperty(exports, "PR_ORIGIN_FILE", { enumerable: true, get: function () { return pr_body_store_1.PR_ORIGIN_FILE; } });
|
|
82
72
|
var claude_env_1 = require("./claude-env");
|
|
83
73
|
Object.defineProperty(exports, "ClaudeEnv", { enumerable: true, get: function () { return claude_env_1.ClaudeEnv; } });
|
|
84
74
|
Object.defineProperty(exports, "claudeEnv", { enumerable: true, get: function () { return claude_env_1.claudeEnv; } });
|
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":"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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, HOOKS_STATE_DIR } from './state-dir';\nexport { StateDirMigrator, StateMigrationReport } from './state-dir-migration';\n// MACHINE-GLOBAL state — the scope ABOVE the clone. Used only for artifacts whose identity is bigger\n// than a clone (today: the gated squash body, keyed by the PR's global identity). See pr-body-store.ts.\nexport { MachineStateHome, machineStateHome, StateHome, WEBPIECES_STATE_HOME_ENV } from './machine-state-home';\nexport { AgedTreeSweeper, SweepCount, RETENTION_DAYS } from './aged-tree-sweep';\nexport {\n PrBodyStore, PrBodyLocation, PrBodyOrigin, RepoSlug,\n PRS_STATE_DIR, MERGE_BODY_FILE, PR_ORIGIN_FILE,\n} from './pr-body-store';\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, 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,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The environment variable that takes over completely. See {@link MachineStateHome} and
|
|
3
|
-
* `decisions/0001-tree-identity-and-governance.md` § D3.
|
|
4
|
-
*/
|
|
5
|
-
export declare const WEBPIECES_STATE_HOME_ENV = "WEBPIECES_STATE_HOME";
|
|
6
|
-
/**
|
|
7
|
-
* Where the MACHINE-GLOBAL state root resolved to, and whether it is really machine-global.
|
|
8
|
-
*
|
|
9
|
-
* Data-only (classes for data, per CLAUDE.md). `degraded` is the whole reason this is a class rather
|
|
10
|
-
* than a bare string: a caller that fell back to the clone has NOT written a machine-global artifact,
|
|
11
|
-
* and any message it prints about "this machine" would be a lie. `reason` is the sentence to print.
|
|
12
|
-
*/
|
|
13
|
-
export declare class StateHome {
|
|
14
|
-
/** The directory that IS the root. Callers append their own namespace beneath it. */
|
|
15
|
-
readonly root: string;
|
|
16
|
-
/** TRUE when this is the per-clone fallback, i.e. NOT visible from another clone. */
|
|
17
|
-
readonly degraded: boolean;
|
|
18
|
-
/** '' when not degraded; otherwise one human-readable clause naming what went wrong. */
|
|
19
|
-
readonly reason: string;
|
|
20
|
-
constructor(root: string, degraded?: boolean, reason?: string);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* The ONE resolver for state that belongs to the MACHINE rather than to a clone or a worktree.
|
|
24
|
-
*
|
|
25
|
-
* ─── Why this exists at all ────────────────────────────────────────────────────────────────────────
|
|
26
|
-
* `DotWebpieces` answers "where does state for THIS TREE live" and has two scopes, shared (repo-wide)
|
|
27
|
-
* and local (this worktree). Both are anchored inside the primary clone, which is correct for every
|
|
28
|
-
* fact whose scope is a clone: branches, worktrees, in-flight merges.
|
|
29
|
-
*
|
|
30
|
-
* It is WRONG for a fact whose scope is larger than a clone. `wp-finish-upsert-pr` renders the compact
|
|
31
|
-
* squash-commit body for PR #N; `wp-land-pr` must pass those exact bytes to `gh pr merge --body-file`.
|
|
32
|
-
* PR #N is a fact of the REMOTE repository — the same object seen from every clone and every worktree
|
|
33
|
-
* on this machine — so storing its receipt under a per-worktree namespace made landing work only while
|
|
34
|
-
* the branch never changed trees. It did (the flow ran in the primary clone, landing happened from a
|
|
35
|
-
* linked worktree), landing found nothing, and the artifact got copied across by hand.
|
|
36
|
-
*
|
|
37
|
-
* ─── D3: `WEBPIECES_STATE_HOME` is a FULL override, not a prefix ────────────────────────────────────
|
|
38
|
-
* Point it at a directory and that directory IS the root — no `.webpieces` appended, no per-repo
|
|
39
|
-
* nesting. That is the escape for a container/sandbox with no writable `$HOME`, and it is also "put it
|
|
40
|
-
* back inside the tree" for anyone who wants that.
|
|
41
|
-
*
|
|
42
|
-
* ─── Never throws ──────────────────────────────────────────────────────────────────────────────────
|
|
43
|
-
* D3 again: this sits under code that runs on a hook's blocking path, so an unwritable or absent `$HOME`
|
|
44
|
-
* DEGRADES to `<primary clone>/.webpieces` and says so, rather than raising. A degraded home is not a
|
|
45
|
-
* silent one: {@link StateHome.degraded} travels with the path, and every consumer prints the reason —
|
|
46
|
-
* because a receipt written into a clone is exactly the thing that was broken to begin with, and a
|
|
47
|
-
* reader who is not told will spend the next incident looking for it on the wrong machine.
|
|
48
|
-
*/
|
|
49
|
-
export declare class MachineStateHome {
|
|
50
|
-
private readonly cache;
|
|
51
|
-
/**
|
|
52
|
-
* The machine-global state root.
|
|
53
|
-
*
|
|
54
|
-
* @param cloneFallback the primary clone's root, used ONLY when neither the override nor `$HOME`
|
|
55
|
-
* is usable. Pass the repo root you already resolved; it is never consulted
|
|
56
|
-
* on the happy path.
|
|
57
|
-
*/
|
|
58
|
-
resolve(cloneFallback: string): StateHome;
|
|
59
|
-
private resolveUncached;
|
|
60
|
-
/**
|
|
61
|
-
* `$HOME` FIRST, `os.homedir()` second — deliberately not the other way round.
|
|
62
|
-
*
|
|
63
|
-
* On macOS `os.homedir()` falls back to the PASSWORD DATABASE when `$HOME` is unset, so it happily
|
|
64
|
-
* returns the real home of a process that was launched with a scrubbed environment on purpose. It
|
|
65
|
-
* also makes `HOME=<tmp>` untestable, and a state root nothing can point at a temp directory is a
|
|
66
|
-
* state root nothing has real tests for.
|
|
67
|
-
*/
|
|
68
|
-
private homeDir;
|
|
69
|
-
private usable;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Process-wide instance for the many non-DI call sites, mirroring `dotWebpieces`. Inversify still
|
|
73
|
-
* injects the singleton wherever a container is in play.
|
|
74
|
-
*/
|
|
75
|
-
export declare const machineStateHome: MachineStateHome;
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.machineStateHome = exports.MachineStateHome = exports.StateHome = exports.WEBPIECES_STATE_HOME_ENV = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
-
const os = tslib_1.__importStar(require("os"));
|
|
7
|
-
const path = tslib_1.__importStar(require("path"));
|
|
8
|
-
const inversify_1 = require("inversify");
|
|
9
|
-
const constants_1 = require("./constants");
|
|
10
|
-
const to_error_1 = require("./to-error");
|
|
11
|
-
/**
|
|
12
|
-
* The environment variable that takes over completely. See {@link MachineStateHome} and
|
|
13
|
-
* `decisions/0001-tree-identity-and-governance.md` § D3.
|
|
14
|
-
*/
|
|
15
|
-
exports.WEBPIECES_STATE_HOME_ENV = 'WEBPIECES_STATE_HOME';
|
|
16
|
-
/**
|
|
17
|
-
* Where the MACHINE-GLOBAL state root resolved to, and whether it is really machine-global.
|
|
18
|
-
*
|
|
19
|
-
* Data-only (classes for data, per CLAUDE.md). `degraded` is the whole reason this is a class rather
|
|
20
|
-
* than a bare string: a caller that fell back to the clone has NOT written a machine-global artifact,
|
|
21
|
-
* and any message it prints about "this machine" would be a lie. `reason` is the sentence to print.
|
|
22
|
-
*/
|
|
23
|
-
class StateHome {
|
|
24
|
-
/** The directory that IS the root. Callers append their own namespace beneath it. */
|
|
25
|
-
root;
|
|
26
|
-
/** TRUE when this is the per-clone fallback, i.e. NOT visible from another clone. */
|
|
27
|
-
degraded;
|
|
28
|
-
/** '' when not degraded; otherwise one human-readable clause naming what went wrong. */
|
|
29
|
-
reason;
|
|
30
|
-
constructor(root, degraded = false, reason = '') {
|
|
31
|
-
this.root = root;
|
|
32
|
-
this.degraded = degraded;
|
|
33
|
-
this.reason = reason;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
exports.StateHome = StateHome;
|
|
37
|
-
/**
|
|
38
|
-
* The ONE resolver for state that belongs to the MACHINE rather than to a clone or a worktree.
|
|
39
|
-
*
|
|
40
|
-
* ─── Why this exists at all ────────────────────────────────────────────────────────────────────────
|
|
41
|
-
* `DotWebpieces` answers "where does state for THIS TREE live" and has two scopes, shared (repo-wide)
|
|
42
|
-
* and local (this worktree). Both are anchored inside the primary clone, which is correct for every
|
|
43
|
-
* fact whose scope is a clone: branches, worktrees, in-flight merges.
|
|
44
|
-
*
|
|
45
|
-
* It is WRONG for a fact whose scope is larger than a clone. `wp-finish-upsert-pr` renders the compact
|
|
46
|
-
* squash-commit body for PR #N; `wp-land-pr` must pass those exact bytes to `gh pr merge --body-file`.
|
|
47
|
-
* PR #N is a fact of the REMOTE repository — the same object seen from every clone and every worktree
|
|
48
|
-
* on this machine — so storing its receipt under a per-worktree namespace made landing work only while
|
|
49
|
-
* the branch never changed trees. It did (the flow ran in the primary clone, landing happened from a
|
|
50
|
-
* linked worktree), landing found nothing, and the artifact got copied across by hand.
|
|
51
|
-
*
|
|
52
|
-
* ─── D3: `WEBPIECES_STATE_HOME` is a FULL override, not a prefix ────────────────────────────────────
|
|
53
|
-
* Point it at a directory and that directory IS the root — no `.webpieces` appended, no per-repo
|
|
54
|
-
* nesting. That is the escape for a container/sandbox with no writable `$HOME`, and it is also "put it
|
|
55
|
-
* back inside the tree" for anyone who wants that.
|
|
56
|
-
*
|
|
57
|
-
* ─── Never throws ──────────────────────────────────────────────────────────────────────────────────
|
|
58
|
-
* D3 again: this sits under code that runs on a hook's blocking path, so an unwritable or absent `$HOME`
|
|
59
|
-
* DEGRADES to `<primary clone>/.webpieces` and says so, rather than raising. A degraded home is not a
|
|
60
|
-
* silent one: {@link StateHome.degraded} travels with the path, and every consumer prints the reason —
|
|
61
|
-
* because a receipt written into a clone is exactly the thing that was broken to begin with, and a
|
|
62
|
-
* reader who is not told will spend the next incident looking for it on the wrong machine.
|
|
63
|
-
*/
|
|
64
|
-
let MachineStateHome = class MachineStateHome {
|
|
65
|
-
// Resolved roots, keyed by the fallback that was offered. Probing writability costs a mkdir + a
|
|
66
|
-
// stat, and every path lookup in a command goes through here.
|
|
67
|
-
cache = new Map();
|
|
68
|
-
/**
|
|
69
|
-
* The machine-global state root.
|
|
70
|
-
*
|
|
71
|
-
* @param cloneFallback the primary clone's root, used ONLY when neither the override nor `$HOME`
|
|
72
|
-
* is usable. Pass the repo root you already resolved; it is never consulted
|
|
73
|
-
* on the happy path.
|
|
74
|
-
*/
|
|
75
|
-
resolve(cloneFallback) {
|
|
76
|
-
const cached = this.cache.get(cloneFallback);
|
|
77
|
-
if (cached !== undefined)
|
|
78
|
-
return cached;
|
|
79
|
-
const home = this.resolveUncached(cloneFallback);
|
|
80
|
-
this.cache.set(cloneFallback, home);
|
|
81
|
-
return home;
|
|
82
|
-
}
|
|
83
|
-
resolveUncached(cloneFallback) {
|
|
84
|
-
const override = (process.env[exports.WEBPIECES_STATE_HOME_ENV] ?? '').trim();
|
|
85
|
-
if (override !== '') {
|
|
86
|
-
// FULL override: this directory IS the root. No `<key>` nesting, no `.webpieces` suffix.
|
|
87
|
-
if (this.usable(override))
|
|
88
|
-
return new StateHome(path.resolve(override));
|
|
89
|
-
return new StateHome(path.join(cloneFallback, constants_1.WEBPIECES_TMP_DIR), true, `${exports.WEBPIECES_STATE_HOME_ENV}=${override} is not a writable directory`);
|
|
90
|
-
}
|
|
91
|
-
const home = this.homeDir();
|
|
92
|
-
if (home !== '') {
|
|
93
|
-
const root = path.join(home, constants_1.WEBPIECES_TMP_DIR);
|
|
94
|
-
if (this.usable(root))
|
|
95
|
-
return new StateHome(root);
|
|
96
|
-
return new StateHome(path.join(cloneFallback, constants_1.WEBPIECES_TMP_DIR), true, `${root} could not be created or written to`);
|
|
97
|
-
}
|
|
98
|
-
return new StateHome(path.join(cloneFallback, constants_1.WEBPIECES_TMP_DIR), true, 'this process has no home directory ($HOME is unset and os.homedir() gave nothing)');
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* `$HOME` FIRST, `os.homedir()` second — deliberately not the other way round.
|
|
102
|
-
*
|
|
103
|
-
* On macOS `os.homedir()` falls back to the PASSWORD DATABASE when `$HOME` is unset, so it happily
|
|
104
|
-
* returns the real home of a process that was launched with a scrubbed environment on purpose. It
|
|
105
|
-
* also makes `HOME=<tmp>` untestable, and a state root nothing can point at a temp directory is a
|
|
106
|
-
* state root nothing has real tests for.
|
|
107
|
-
*/
|
|
108
|
-
homeDir() {
|
|
109
|
-
const fromEnv = (process.env['HOME'] ?? '').trim();
|
|
110
|
-
if (fromEnv !== '')
|
|
111
|
-
return fromEnv;
|
|
112
|
-
// webpieces-disable no-unmanaged-exceptions -- chokepoint: os.homedir() throws on some sandboxes; '' is the honest answer
|
|
113
|
-
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
114
|
-
try {
|
|
115
|
-
return (os.homedir() ?? '').trim();
|
|
116
|
-
}
|
|
117
|
-
catch (err) {
|
|
118
|
-
const error = (0, to_error_1.toError)(err);
|
|
119
|
-
void error;
|
|
120
|
-
return '';
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
// Can we actually create and write inside `dir`? Probed by creating it and writing a marker rather
|
|
124
|
-
// than by reading mode bits: a read-only mount, an SELinux/sandbox denial and a plain permission
|
|
125
|
-
// error all present differently in `stat`, and all of them fail the write identically.
|
|
126
|
-
usable(dir) {
|
|
127
|
-
// webpieces-disable no-unmanaged-exceptions -- chokepoint: any filesystem refusal means "not usable", never a crash
|
|
128
|
-
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
129
|
-
try {
|
|
130
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
131
|
-
const probe = path.join(dir, '.writable-probe');
|
|
132
|
-
fs.writeFileSync(probe, '');
|
|
133
|
-
fs.rmSync(probe, { force: true });
|
|
134
|
-
return true;
|
|
135
|
-
}
|
|
136
|
-
catch (err) {
|
|
137
|
-
const error = (0, to_error_1.toError)(err);
|
|
138
|
-
void error;
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
exports.MachineStateHome = MachineStateHome;
|
|
144
|
-
exports.MachineStateHome = MachineStateHome = tslib_1.__decorate([
|
|
145
|
-
(0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton)
|
|
146
|
-
], MachineStateHome);
|
|
147
|
-
/**
|
|
148
|
-
* Process-wide instance for the many non-DI call sites, mirroring `dotWebpieces`. Inversify still
|
|
149
|
-
* injects the singleton wherever a container is in play.
|
|
150
|
-
*/
|
|
151
|
-
exports.machineStateHome = new MachineStateHome();
|
|
152
|
-
//# sourceMappingURL=machine-state-home.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"machine-state-home.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/machine-state-home.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { WEBPIECES_TMP_DIR } from './constants';\nimport { toError } from './to-error';\n\n/**\n * The environment variable that takes over completely. See {@link MachineStateHome} and\n * `decisions/0001-tree-identity-and-governance.md` § D3.\n */\nexport const WEBPIECES_STATE_HOME_ENV = 'WEBPIECES_STATE_HOME';\n\n/**\n * Where the MACHINE-GLOBAL state root resolved to, and whether it is really machine-global.\n *\n * Data-only (classes for data, per CLAUDE.md). `degraded` is the whole reason this is a class rather\n * than a bare string: a caller that fell back to the clone has NOT written a machine-global artifact,\n * and any message it prints about \"this machine\" would be a lie. `reason` is the sentence to print.\n */\nexport class StateHome {\n /** The directory that IS the root. Callers append their own namespace beneath it. */\n readonly root: string;\n /** TRUE when this is the per-clone fallback, i.e. NOT visible from another clone. */\n readonly degraded: boolean;\n /** '' when not degraded; otherwise one human-readable clause naming what went wrong. */\n readonly reason: string;\n\n constructor(root: string, degraded = false, reason = '') {\n this.root = root;\n this.degraded = degraded;\n this.reason = reason;\n }\n}\n\n/**\n * The ONE resolver for state that belongs to the MACHINE rather than to a clone or a worktree.\n *\n * ─── Why this exists at all ────────────────────────────────────────────────────────────────────────\n * `DotWebpieces` answers \"where does state for THIS TREE live\" and has two scopes, shared (repo-wide)\n * and local (this worktree). Both are anchored inside the primary clone, which is correct for every\n * fact whose scope is a clone: branches, worktrees, in-flight merges.\n *\n * It is WRONG for a fact whose scope is larger than a clone. `wp-finish-upsert-pr` renders the compact\n * squash-commit body for PR #N; `wp-land-pr` must pass those exact bytes to `gh pr merge --body-file`.\n * PR #N is a fact of the REMOTE repository — the same object seen from every clone and every worktree\n * on this machine — so storing its receipt under a per-worktree namespace made landing work only while\n * the branch never changed trees. It did (the flow ran in the primary clone, landing happened from a\n * linked worktree), landing found nothing, and the artifact got copied across by hand.\n *\n * ─── D3: `WEBPIECES_STATE_HOME` is a FULL override, not a prefix ────────────────────────────────────\n * Point it at a directory and that directory IS the root — no `.webpieces` appended, no per-repo\n * nesting. That is the escape for a container/sandbox with no writable `$HOME`, and it is also \"put it\n * back inside the tree\" for anyone who wants that.\n *\n * ─── Never throws ──────────────────────────────────────────────────────────────────────────────────\n * D3 again: this sits under code that runs on a hook's blocking path, so an unwritable or absent `$HOME`\n * DEGRADES to `<primary clone>/.webpieces` and says so, rather than raising. A degraded home is not a\n * silent one: {@link StateHome.degraded} travels with the path, and every consumer prints the reason —\n * because a receipt written into a clone is exactly the thing that was broken to begin with, and a\n * reader who is not told will spend the next incident looking for it on the wrong machine.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class MachineStateHome {\n // Resolved roots, keyed by the fallback that was offered. Probing writability costs a mkdir + a\n // stat, and every path lookup in a command goes through here.\n private readonly cache = new Map<string, StateHome>();\n\n /**\n * The machine-global state root.\n *\n * @param cloneFallback the primary clone's root, used ONLY when neither the override nor `$HOME`\n * is usable. Pass the repo root you already resolved; it is never consulted\n * on the happy path.\n */\n resolve(cloneFallback: string): StateHome {\n const cached = this.cache.get(cloneFallback);\n if (cached !== undefined) return cached;\n const home = this.resolveUncached(cloneFallback);\n this.cache.set(cloneFallback, home);\n return home;\n }\n\n private resolveUncached(cloneFallback: string): StateHome {\n const override = (process.env[WEBPIECES_STATE_HOME_ENV] ?? '').trim();\n if (override !== '') {\n // FULL override: this directory IS the root. No `<key>` nesting, no `.webpieces` suffix.\n if (this.usable(override)) return new StateHome(path.resolve(override));\n return new StateHome(\n path.join(cloneFallback, WEBPIECES_TMP_DIR), true,\n `${WEBPIECES_STATE_HOME_ENV}=${override} is not a writable directory`);\n }\n\n const home = this.homeDir();\n if (home !== '') {\n const root = path.join(home, WEBPIECES_TMP_DIR);\n if (this.usable(root)) return new StateHome(root);\n return new StateHome(\n path.join(cloneFallback, WEBPIECES_TMP_DIR), true,\n `${root} could not be created or written to`);\n }\n\n return new StateHome(\n path.join(cloneFallback, WEBPIECES_TMP_DIR), true,\n 'this process has no home directory ($HOME is unset and os.homedir() gave nothing)');\n }\n\n /**\n * `$HOME` FIRST, `os.homedir()` second — deliberately not the other way round.\n *\n * On macOS `os.homedir()` falls back to the PASSWORD DATABASE when `$HOME` is unset, so it happily\n * returns the real home of a process that was launched with a scrubbed environment on purpose. It\n * also makes `HOME=<tmp>` untestable, and a state root nothing can point at a temp directory is a\n * state root nothing has real tests for.\n */\n private homeDir(): string {\n const fromEnv = (process.env['HOME'] ?? '').trim();\n if (fromEnv !== '') return fromEnv;\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: os.homedir() throws on some sandboxes; '' is the honest answer\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return (os.homedir() ?? '').trim();\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return '';\n }\n }\n\n // Can we actually create and write inside `dir`? Probed by creating it and writing a marker rather\n // than by reading mode bits: a read-only mount, an SELinux/sandbox denial and a plain permission\n // error all present differently in `stat`, and all of them fail the write identically.\n private usable(dir: string): boolean {\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: any filesystem refusal means \"not usable\", never a crash\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n fs.mkdirSync(dir, { recursive: true });\n const probe = path.join(dir, '.writable-probe');\n fs.writeFileSync(probe, '');\n fs.rmSync(probe, { force: true });\n return true;\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return false;\n }\n }\n}\n\n/**\n * Process-wide instance for the many non-DI call sites, mirroring `dotWebpieces`. Inversify still\n * injects the singleton wherever a container is in play.\n */\nexport const machineStateHome = new MachineStateHome();\n"]}
|
package/src/pr-body-store.d.ts
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { AgedTreeSweeper, SweepCount } from './aged-tree-sweep';
|
|
2
|
-
import { MachineStateHome, StateHome } from './machine-state-home';
|
|
3
|
-
import { DotWebpieces } from './state-dir';
|
|
4
|
-
/** The namespace under the machine-global state root. Everything PR-shaped lives beneath it. */
|
|
5
|
-
export declare const PRS_STATE_DIR = "prs";
|
|
6
|
-
/** The gated squash-commit body `wp-finish-upsert-pr` renders and `wp-land-pr` passes to `gh`. */
|
|
7
|
-
export declare const MERGE_BODY_FILE = "merge-commit-body.md";
|
|
8
|
-
/** The provenance sidecar — also the `origin.json` marker `decisions/0001` § O3 asks for. */
|
|
9
|
-
export declare const PR_ORIGIN_FILE = "origin.json";
|
|
10
|
-
/**
|
|
11
|
-
* The REMOTE repository a PR belongs to: `github.com` / `deanhiller` / `webpieces-ts30`. Data-only.
|
|
12
|
-
*
|
|
13
|
-
* `owner` is a PATH, not one token — GitLab subgroups and Azure DevOps project paths are legitimately
|
|
14
|
-
* several segments deep, and collapsing them would merge two repos into one directory.
|
|
15
|
-
*/
|
|
16
|
-
export declare class RepoSlug {
|
|
17
|
-
readonly host: string;
|
|
18
|
-
readonly owner: string;
|
|
19
|
-
readonly repo: string;
|
|
20
|
-
constructor(host: string, owner: string, repo: string);
|
|
21
|
-
/** FALSE when the remote could not be read or parsed — the caller must then not pretend to store. */
|
|
22
|
-
get known(): boolean;
|
|
23
|
-
/** The directory segments, in order. `owner` may contribute more than one. */
|
|
24
|
-
segments(): string[];
|
|
25
|
-
toString(): string;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* WHO wrote a PR body, and from WHERE. Data-only, serialized beside the body as `origin.json`.
|
|
29
|
-
*
|
|
30
|
-
* `treeRoot` is the load-bearing field: `wp-land-pr` compares it against the tree it is standing in to
|
|
31
|
-
* decide whether the tree-bound bookkeeping (archive the pre-squash tip, promote merge-info, reap the
|
|
32
|
-
* worktree) belongs to it. See `land-pr-command.ts`.
|
|
33
|
-
*/
|
|
34
|
-
export declare class PrBodyOrigin {
|
|
35
|
-
treeRoot: string;
|
|
36
|
-
primaryRoot: string;
|
|
37
|
-
branch: string;
|
|
38
|
-
feature: string;
|
|
39
|
-
prNumber: string;
|
|
40
|
-
prUrl: string;
|
|
41
|
-
writtenAt: string;
|
|
42
|
-
}
|
|
43
|
-
/** A stored body: where it is, and the provenance beside it (`null` origin ⇒ no readable sidecar). */
|
|
44
|
-
export declare class PrBodyLocation {
|
|
45
|
-
readonly dir: string;
|
|
46
|
-
readonly bodyFile: string;
|
|
47
|
-
readonly origin: PrBodyOrigin | null;
|
|
48
|
-
constructor(dir: string, bodyFile: string, origin: PrBodyOrigin | null);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* The gated squash-commit body for a PR, stored by the PR's GLOBAL identity rather than by the tree
|
|
52
|
-
* that happened to render it:
|
|
53
|
-
*
|
|
54
|
-
* ~/.webpieces/prs/<host>/<owner>/<repo>/<prNumber>/merge-commit-body.md
|
|
55
|
-
*
|
|
56
|
-
* ─── The bug this fixes ─────────────────────────────────────────────────────────────────────────────
|
|
57
|
-
* The body used to live at `<primary>/.webpieces/worktrees/<name>/pr-review/<branch>/`, i.e. in
|
|
58
|
-
* PER-WORKTREE state. But `pr-review/<branch>/` is keyed by BRANCH, and a branch is a REPO-WIDE fact —
|
|
59
|
-
* git forbids two worktrees checking out one branch — so a per-tree home only worked while the branch
|
|
60
|
-
* never moved trees. The gated flow ran in the primary clone, landing happened from a linked worktree,
|
|
61
|
-
* `wp-land-pr` looked under the worktree's namespace and printed "Nothing to land". That is a SCOPE
|
|
62
|
-
* MISMATCH, not a missing fallback, and the fix is to give the artifact the scope of its identity.
|
|
63
|
-
*
|
|
64
|
-
* ─── Why keyed by the remote, when `decisions/0001` § D2 forbids exactly that ───────────────────────
|
|
65
|
-
* D2 rejects the remote URL as the key for CLONE state, because two clones of one repo have different
|
|
66
|
-
* branches, worktrees and in-flight merges and must NOT share. That reasoning is intact and this is its
|
|
67
|
-
* mirror image: a PR NUMBER is a fact of the remote, the same object seen from every clone, and here
|
|
68
|
-
* sharing is the REQUIREMENT — landing must work from any tree, and from a second clone of the same
|
|
69
|
-
* repo, as long as the PR was posted from this machine. The rule underneath both is one rule: key an
|
|
70
|
-
* artifact by the scope of the fact it describes.
|
|
71
|
-
*
|
|
72
|
-
* ─── Why nested segments and not D2's flattening ────────────────────────────────────────────────────
|
|
73
|
-
* D2 flattens because it keys on an absolute PATH, whose separators must survive as data. Here each
|
|
74
|
-
* segment is already a single opaque token (`github.com`, `deanhiller`, `webpieces-ts30`, `604`), the
|
|
75
|
-
* hierarchy is real, and it buys two things a flattened key cannot: it is browsable by a human
|
|
76
|
-
* debugging a landing, and the retention sweep prunes the empty parents of a reaped PR for free.
|
|
77
|
-
*
|
|
78
|
-
* ─── What is NOT stored here ────────────────────────────────────────────────────────────────────────
|
|
79
|
-
* Everything else under `pr-review/<branch>/` — review.json, the per-checklist verdicts, pr-context.json
|
|
80
|
-
* and the stage snapshots. Those describe work in progress in ONE tree and correctly stay local. Only
|
|
81
|
-
* the finished receipt for a posted PR is machine-global.
|
|
82
|
-
*
|
|
83
|
-
* ─── Never regenerated ──────────────────────────────────────────────────────────────────────────────
|
|
84
|
-
* `wp-land-pr` READS this and never re-renders it. The bytes that land are the bytes finish produced;
|
|
85
|
-
* re-deriving them at land time would be a second authoritative gate whose result nobody reads. See
|
|
86
|
-
* `land-pr-command.ts`.
|
|
87
|
-
*/
|
|
88
|
-
export declare class PrBodyStore {
|
|
89
|
-
private readonly stateHome;
|
|
90
|
-
private readonly dotDir;
|
|
91
|
-
private readonly sweeper;
|
|
92
|
-
private readonly slugByDir;
|
|
93
|
-
constructor(stateHome?: MachineStateHome, dotDir?: DotWebpieces, sweeper?: AgedTreeSweeper);
|
|
94
|
-
/** The machine-global root this store writes under, plus whether it degraded to the clone. */
|
|
95
|
-
home(startDir: string): StateHome;
|
|
96
|
-
/** `<state home>/prs` — the sweep root, and the directory a human is pointed at when landing fails. */
|
|
97
|
-
prsRoot(startDir: string): string;
|
|
98
|
-
/**
|
|
99
|
-
* The remote this working tree pushes to. Read from `origin`, because that is the remote `gh` itself
|
|
100
|
-
* resolves a PR against — asking a different remote would key the body under a repo the PR is not on.
|
|
101
|
-
* An unknown slug (no remote, detached tooling, a URL shape we decline to guess at) is returned as
|
|
102
|
-
* `known === false`; it is never fabricated.
|
|
103
|
-
*/
|
|
104
|
-
slugFor(startDir: string): RepoSlug;
|
|
105
|
-
/** The directory for one PR, or '' when the remote is unknown or `prNumber` is not a number. */
|
|
106
|
-
dirFor(startDir: string, prNumber: string): string;
|
|
107
|
-
/**
|
|
108
|
-
* Store the gated body for `prNumber` and return where it went — `null` when it cannot be stored at
|
|
109
|
-
* all (unknown remote, or a PR whose number could not be read back from GitHub). A `null` here is
|
|
110
|
-
* NOT fatal for the caller that is about to merge in the same process: it still holds the bytes. It
|
|
111
|
-
* IS the reason a later `wp-land-pr` will report the PR as not found on this machine, which is the
|
|
112
|
-
* honest answer — no durable receipt was ever filed.
|
|
113
|
-
*/
|
|
114
|
-
write(startDir: string, prNumber: string, body: string, origin: PrBodyOrigin): PrBodyLocation | null;
|
|
115
|
-
/** The stored body for `prNumber`, or `null` when this machine has none. */
|
|
116
|
-
read(startDir: string, prNumber: string): PrBodyLocation | null;
|
|
117
|
-
/**
|
|
118
|
-
* The 30-day reap of `prs/`, run from the same place every other `.webpieces` sweep runs.
|
|
119
|
-
*
|
|
120
|
-
* `decisions/0001` § O3: these directories outlive every clone, so nothing else will ever delete
|
|
121
|
-
* them. A landed PR's body is never rewritten, so it ages out on schedule; a PR still being pushed
|
|
122
|
-
* to gets a fresh mtime on every `wp-finish-upsert-pr`, so an open PR is never reaped out from under
|
|
123
|
-
* a landing.
|
|
124
|
-
*/
|
|
125
|
-
sweep(startDir: string, onDelete?: (removed: string, isDir: boolean) => void): SweepCount;
|
|
126
|
-
private readOrigin;
|
|
127
|
-
private str;
|
|
128
|
-
private remoteUrl;
|
|
129
|
-
/**
|
|
130
|
-
* Remote URL → host/owner/repo, for the three shapes git actually hands out:
|
|
131
|
-
*
|
|
132
|
-
* git@github.com:owner/repo.git scp-like
|
|
133
|
-
* ssh://git@github.com:22/owner/repo.git url with a port
|
|
134
|
-
* https://user@host/owner/sub/repo.git url, possibly with credentials and subgroups
|
|
135
|
-
*
|
|
136
|
-
* Anything else returns an UNKNOWN slug rather than a guess. A wrong key here silently files the
|
|
137
|
-
* receipt under a repo the PR is not on, which reads to the next reader as "the body was never
|
|
138
|
-
* written" — the exact failure being fixed. Not guessing is the only safe default.
|
|
139
|
-
*/
|
|
140
|
-
private parseRemote;
|
|
141
|
-
private urlParts;
|
|
142
|
-
private safe;
|
|
143
|
-
}
|
package/src/pr-body-store.js
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PrBodyStore = exports.PrBodyLocation = exports.PrBodyOrigin = exports.RepoSlug = exports.PR_ORIGIN_FILE = exports.MERGE_BODY_FILE = exports.PRS_STATE_DIR = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const child_process_1 = require("child_process");
|
|
6
|
-
const fs = tslib_1.__importStar(require("fs"));
|
|
7
|
-
const path = tslib_1.__importStar(require("path"));
|
|
8
|
-
const inversify_1 = require("inversify");
|
|
9
|
-
const aged_tree_sweep_1 = require("./aged-tree-sweep");
|
|
10
|
-
const machine_state_home_1 = require("./machine-state-home");
|
|
11
|
-
const state_dir_1 = require("./state-dir");
|
|
12
|
-
const to_error_1 = require("./to-error");
|
|
13
|
-
/** The namespace under the machine-global state root. Everything PR-shaped lives beneath it. */
|
|
14
|
-
exports.PRS_STATE_DIR = 'prs';
|
|
15
|
-
/** The gated squash-commit body `wp-finish-upsert-pr` renders and `wp-land-pr` passes to `gh`. */
|
|
16
|
-
exports.MERGE_BODY_FILE = 'merge-commit-body.md';
|
|
17
|
-
/** The provenance sidecar — also the `origin.json` marker `decisions/0001` § O3 asks for. */
|
|
18
|
-
exports.PR_ORIGIN_FILE = 'origin.json';
|
|
19
|
-
// Every path segment is a single opaque token taken from a remote URL, so anything that is not an
|
|
20
|
-
// ordinary name character is folded away. `.` and `..` are rejected outright rather than folded,
|
|
21
|
-
// because folding them to `-`/`--` would silently key two different repos to one directory.
|
|
22
|
-
const UNSAFE_SEGMENT_CHARS = /[^A-Za-z0-9._-]+/g;
|
|
23
|
-
/**
|
|
24
|
-
* The REMOTE repository a PR belongs to: `github.com` / `deanhiller` / `webpieces-ts30`. Data-only.
|
|
25
|
-
*
|
|
26
|
-
* `owner` is a PATH, not one token — GitLab subgroups and Azure DevOps project paths are legitimately
|
|
27
|
-
* several segments deep, and collapsing them would merge two repos into one directory.
|
|
28
|
-
*/
|
|
29
|
-
class RepoSlug {
|
|
30
|
-
host;
|
|
31
|
-
owner;
|
|
32
|
-
repo;
|
|
33
|
-
constructor(host, owner, repo) {
|
|
34
|
-
this.host = host;
|
|
35
|
-
this.owner = owner;
|
|
36
|
-
this.repo = repo;
|
|
37
|
-
}
|
|
38
|
-
/** FALSE when the remote could not be read or parsed — the caller must then not pretend to store. */
|
|
39
|
-
get known() {
|
|
40
|
-
return this.host !== '' && this.owner !== '' && this.repo !== '';
|
|
41
|
-
}
|
|
42
|
-
/** The directory segments, in order. `owner` may contribute more than one. */
|
|
43
|
-
segments() {
|
|
44
|
-
return [this.host, ...this.owner.split('/'), this.repo];
|
|
45
|
-
}
|
|
46
|
-
toString() {
|
|
47
|
-
return `${this.host}/${this.owner}/${this.repo}`;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
exports.RepoSlug = RepoSlug;
|
|
51
|
-
/**
|
|
52
|
-
* WHO wrote a PR body, and from WHERE. Data-only, serialized beside the body as `origin.json`.
|
|
53
|
-
*
|
|
54
|
-
* `treeRoot` is the load-bearing field: `wp-land-pr` compares it against the tree it is standing in to
|
|
55
|
-
* decide whether the tree-bound bookkeeping (archive the pre-squash tip, promote merge-info, reap the
|
|
56
|
-
* worktree) belongs to it. See `land-pr-command.ts`.
|
|
57
|
-
*/
|
|
58
|
-
class PrBodyOrigin {
|
|
59
|
-
treeRoot = '';
|
|
60
|
-
primaryRoot = '';
|
|
61
|
-
branch = '';
|
|
62
|
-
feature = '';
|
|
63
|
-
prNumber = '';
|
|
64
|
-
prUrl = '';
|
|
65
|
-
writtenAt = '';
|
|
66
|
-
}
|
|
67
|
-
exports.PrBodyOrigin = PrBodyOrigin;
|
|
68
|
-
/** A stored body: where it is, and the provenance beside it (`null` origin ⇒ no readable sidecar). */
|
|
69
|
-
class PrBodyLocation {
|
|
70
|
-
dir;
|
|
71
|
-
bodyFile;
|
|
72
|
-
origin;
|
|
73
|
-
constructor(dir, bodyFile, origin) {
|
|
74
|
-
this.dir = dir;
|
|
75
|
-
this.bodyFile = bodyFile;
|
|
76
|
-
this.origin = origin;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
exports.PrBodyLocation = PrBodyLocation;
|
|
80
|
-
/**
|
|
81
|
-
* The gated squash-commit body for a PR, stored by the PR's GLOBAL identity rather than by the tree
|
|
82
|
-
* that happened to render it:
|
|
83
|
-
*
|
|
84
|
-
* ~/.webpieces/prs/<host>/<owner>/<repo>/<prNumber>/merge-commit-body.md
|
|
85
|
-
*
|
|
86
|
-
* ─── The bug this fixes ─────────────────────────────────────────────────────────────────────────────
|
|
87
|
-
* The body used to live at `<primary>/.webpieces/worktrees/<name>/pr-review/<branch>/`, i.e. in
|
|
88
|
-
* PER-WORKTREE state. But `pr-review/<branch>/` is keyed by BRANCH, and a branch is a REPO-WIDE fact —
|
|
89
|
-
* git forbids two worktrees checking out one branch — so a per-tree home only worked while the branch
|
|
90
|
-
* never moved trees. The gated flow ran in the primary clone, landing happened from a linked worktree,
|
|
91
|
-
* `wp-land-pr` looked under the worktree's namespace and printed "Nothing to land". That is a SCOPE
|
|
92
|
-
* MISMATCH, not a missing fallback, and the fix is to give the artifact the scope of its identity.
|
|
93
|
-
*
|
|
94
|
-
* ─── Why keyed by the remote, when `decisions/0001` § D2 forbids exactly that ───────────────────────
|
|
95
|
-
* D2 rejects the remote URL as the key for CLONE state, because two clones of one repo have different
|
|
96
|
-
* branches, worktrees and in-flight merges and must NOT share. That reasoning is intact and this is its
|
|
97
|
-
* mirror image: a PR NUMBER is a fact of the remote, the same object seen from every clone, and here
|
|
98
|
-
* sharing is the REQUIREMENT — landing must work from any tree, and from a second clone of the same
|
|
99
|
-
* repo, as long as the PR was posted from this machine. The rule underneath both is one rule: key an
|
|
100
|
-
* artifact by the scope of the fact it describes.
|
|
101
|
-
*
|
|
102
|
-
* ─── Why nested segments and not D2's flattening ────────────────────────────────────────────────────
|
|
103
|
-
* D2 flattens because it keys on an absolute PATH, whose separators must survive as data. Here each
|
|
104
|
-
* segment is already a single opaque token (`github.com`, `deanhiller`, `webpieces-ts30`, `604`), the
|
|
105
|
-
* hierarchy is real, and it buys two things a flattened key cannot: it is browsable by a human
|
|
106
|
-
* debugging a landing, and the retention sweep prunes the empty parents of a reaped PR for free.
|
|
107
|
-
*
|
|
108
|
-
* ─── What is NOT stored here ────────────────────────────────────────────────────────────────────────
|
|
109
|
-
* Everything else under `pr-review/<branch>/` — review.json, the per-checklist verdicts, pr-context.json
|
|
110
|
-
* and the stage snapshots. Those describe work in progress in ONE tree and correctly stay local. Only
|
|
111
|
-
* the finished receipt for a posted PR is machine-global.
|
|
112
|
-
*
|
|
113
|
-
* ─── Never regenerated ──────────────────────────────────────────────────────────────────────────────
|
|
114
|
-
* `wp-land-pr` READS this and never re-renders it. The bytes that land are the bytes finish produced;
|
|
115
|
-
* re-deriving them at land time would be a second authoritative gate whose result nobody reads. See
|
|
116
|
-
* `land-pr-command.ts`.
|
|
117
|
-
*/
|
|
118
|
-
let PrBodyStore = class PrBodyStore {
|
|
119
|
-
stateHome;
|
|
120
|
-
dotDir;
|
|
121
|
-
sweeper;
|
|
122
|
-
// startDir → the remote slug. `git remote get-url` is a process spawn; commands ask more than once.
|
|
123
|
-
slugByDir = new Map();
|
|
124
|
-
constructor(stateHome = machine_state_home_1.machineStateHome, dotDir = state_dir_1.dotWebpieces, sweeper = new aged_tree_sweep_1.AgedTreeSweeper()) {
|
|
125
|
-
this.stateHome = stateHome;
|
|
126
|
-
this.dotDir = dotDir;
|
|
127
|
-
this.sweeper = sweeper;
|
|
128
|
-
}
|
|
129
|
-
/** The machine-global root this store writes under, plus whether it degraded to the clone. */
|
|
130
|
-
home(startDir) {
|
|
131
|
-
return this.stateHome.resolve(this.dotDir.primaryRoot(startDir));
|
|
132
|
-
}
|
|
133
|
-
/** `<state home>/prs` — the sweep root, and the directory a human is pointed at when landing fails. */
|
|
134
|
-
prsRoot(startDir) {
|
|
135
|
-
return path.join(this.home(startDir).root, exports.PRS_STATE_DIR);
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* The remote this working tree pushes to. Read from `origin`, because that is the remote `gh` itself
|
|
139
|
-
* resolves a PR against — asking a different remote would key the body under a repo the PR is not on.
|
|
140
|
-
* An unknown slug (no remote, detached tooling, a URL shape we decline to guess at) is returned as
|
|
141
|
-
* `known === false`; it is never fabricated.
|
|
142
|
-
*/
|
|
143
|
-
slugFor(startDir) {
|
|
144
|
-
const cached = this.slugByDir.get(startDir);
|
|
145
|
-
if (cached !== undefined)
|
|
146
|
-
return cached;
|
|
147
|
-
const slug = this.parseRemote(this.remoteUrl(startDir));
|
|
148
|
-
this.slugByDir.set(startDir, slug);
|
|
149
|
-
return slug;
|
|
150
|
-
}
|
|
151
|
-
/** The directory for one PR, or '' when the remote is unknown or `prNumber` is not a number. */
|
|
152
|
-
dirFor(startDir, prNumber) {
|
|
153
|
-
const slug = this.slugFor(startDir);
|
|
154
|
-
if (!slug.known || !/^\d+$/.test(prNumber.trim()))
|
|
155
|
-
return '';
|
|
156
|
-
return path.join(this.prsRoot(startDir), ...slug.segments(), prNumber.trim());
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Store the gated body for `prNumber` and return where it went — `null` when it cannot be stored at
|
|
160
|
-
* all (unknown remote, or a PR whose number could not be read back from GitHub). A `null` here is
|
|
161
|
-
* NOT fatal for the caller that is about to merge in the same process: it still holds the bytes. It
|
|
162
|
-
* IS the reason a later `wp-land-pr` will report the PR as not found on this machine, which is the
|
|
163
|
-
* honest answer — no durable receipt was ever filed.
|
|
164
|
-
*/
|
|
165
|
-
write(startDir, prNumber, body, origin) {
|
|
166
|
-
const dir = this.dirFor(startDir, prNumber);
|
|
167
|
-
if (dir === '')
|
|
168
|
-
return null;
|
|
169
|
-
// webpieces-disable no-unmanaged-exceptions -- chokepoint: a store that cannot be written degrades to null, never a crash mid-PR-post
|
|
170
|
-
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
171
|
-
try {
|
|
172
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
173
|
-
const bodyFile = path.join(dir, exports.MERGE_BODY_FILE);
|
|
174
|
-
fs.writeFileSync(bodyFile, body);
|
|
175
|
-
fs.writeFileSync(path.join(dir, exports.PR_ORIGIN_FILE), JSON.stringify(origin, null, 2) + '\n');
|
|
176
|
-
return new PrBodyLocation(dir, bodyFile, origin);
|
|
177
|
-
}
|
|
178
|
-
catch (err) {
|
|
179
|
-
const error = (0, to_error_1.toError)(err);
|
|
180
|
-
void error;
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
/** The stored body for `prNumber`, or `null` when this machine has none. */
|
|
185
|
-
read(startDir, prNumber) {
|
|
186
|
-
const dir = this.dirFor(startDir, prNumber);
|
|
187
|
-
if (dir === '')
|
|
188
|
-
return null;
|
|
189
|
-
const bodyFile = path.join(dir, exports.MERGE_BODY_FILE);
|
|
190
|
-
if (!fs.existsSync(bodyFile))
|
|
191
|
-
return null;
|
|
192
|
-
return new PrBodyLocation(dir, bodyFile, this.readOrigin(dir));
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* The 30-day reap of `prs/`, run from the same place every other `.webpieces` sweep runs.
|
|
196
|
-
*
|
|
197
|
-
* `decisions/0001` § O3: these directories outlive every clone, so nothing else will ever delete
|
|
198
|
-
* them. A landed PR's body is never rewritten, so it ages out on schedule; a PR still being pushed
|
|
199
|
-
* to gets a fresh mtime on every `wp-finish-upsert-pr`, so an open PR is never reaped out from under
|
|
200
|
-
* a landing.
|
|
201
|
-
*/
|
|
202
|
-
sweep(startDir, onDelete = () => { }) {
|
|
203
|
-
return this.sweeper.sweep(this.prsRoot(startDir), aged_tree_sweep_1.RETENTION_DAYS * 24 * 60 * 60 * 1000, onDelete);
|
|
204
|
-
}
|
|
205
|
-
readOrigin(dir) {
|
|
206
|
-
const file = path.join(dir, exports.PR_ORIGIN_FILE);
|
|
207
|
-
if (!fs.existsSync(file))
|
|
208
|
-
return null;
|
|
209
|
-
// webpieces-disable no-unmanaged-exceptions -- chokepoint: an unreadable sidecar means "no provenance", never a crash
|
|
210
|
-
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
211
|
-
try {
|
|
212
|
-
// webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed field-by-field below
|
|
213
|
-
const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
214
|
-
const origin = new PrBodyOrigin();
|
|
215
|
-
origin.treeRoot = this.str(raw['treeRoot']);
|
|
216
|
-
origin.primaryRoot = this.str(raw['primaryRoot']);
|
|
217
|
-
origin.branch = this.str(raw['branch']);
|
|
218
|
-
origin.feature = this.str(raw['feature']);
|
|
219
|
-
origin.prNumber = this.str(raw['prNumber']);
|
|
220
|
-
origin.prUrl = this.str(raw['prUrl']);
|
|
221
|
-
origin.writtenAt = this.str(raw['writtenAt']);
|
|
222
|
-
return origin;
|
|
223
|
-
}
|
|
224
|
-
catch (err) {
|
|
225
|
-
const error = (0, to_error_1.toError)(err);
|
|
226
|
-
void error;
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
// webpieces-disable no-any-unknown -- one opaque JSON field, narrowed to string
|
|
231
|
-
str(value) {
|
|
232
|
-
return typeof value === 'string' ? value : '';
|
|
233
|
-
}
|
|
234
|
-
remoteUrl(startDir) {
|
|
235
|
-
const result = (0, child_process_1.spawnSync)('git', ['-C', startDir, 'remote', 'get-url', 'origin'], { encoding: 'utf8' });
|
|
236
|
-
if (result.status !== 0)
|
|
237
|
-
return '';
|
|
238
|
-
return (result.stdout ?? '').trim();
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Remote URL → host/owner/repo, for the three shapes git actually hands out:
|
|
242
|
-
*
|
|
243
|
-
* git@github.com:owner/repo.git scp-like
|
|
244
|
-
* ssh://git@github.com:22/owner/repo.git url with a port
|
|
245
|
-
* https://user@host/owner/sub/repo.git url, possibly with credentials and subgroups
|
|
246
|
-
*
|
|
247
|
-
* Anything else returns an UNKNOWN slug rather than a guess. A wrong key here silently files the
|
|
248
|
-
* receipt under a repo the PR is not on, which reads to the next reader as "the body was never
|
|
249
|
-
* written" — the exact failure being fixed. Not guessing is the only safe default.
|
|
250
|
-
*/
|
|
251
|
-
parseRemote(url) {
|
|
252
|
-
const unknown = new RepoSlug('', '', '');
|
|
253
|
-
if (url === '')
|
|
254
|
-
return unknown;
|
|
255
|
-
const scp = /^(?:[^@/]+@)?([^:/@]+):(?!\/)(.+)$/.exec(url);
|
|
256
|
-
const parts = scp !== null
|
|
257
|
-
? new RemoteParts(scp[1] ?? '', scp[2] ?? '')
|
|
258
|
-
: this.urlParts(url);
|
|
259
|
-
if (parts === null)
|
|
260
|
-
return unknown;
|
|
261
|
-
const segments = parts.pathPart.replace(/\.git$/, '').split('/').filter((s) => s !== '');
|
|
262
|
-
if (segments.length < 2)
|
|
263
|
-
return unknown;
|
|
264
|
-
const repo = this.safe(segments[segments.length - 1] ?? '');
|
|
265
|
-
const owner = segments.slice(0, -1).map((s) => this.safe(s)).join('/');
|
|
266
|
-
const host = this.safe(parts.host);
|
|
267
|
-
if (repo === '' || owner.includes('//') || owner === '' || host === '')
|
|
268
|
-
return unknown;
|
|
269
|
-
return new RepoSlug(host, owner, repo);
|
|
270
|
-
}
|
|
271
|
-
urlParts(url) {
|
|
272
|
-
const match = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/(?:[^@/]+@)?([^/:]+)(?::\d+)?\/(.+)$/.exec(url);
|
|
273
|
-
if (match === null)
|
|
274
|
-
return null;
|
|
275
|
-
return new RemoteParts(match[1] ?? '', match[2] ?? '');
|
|
276
|
-
}
|
|
277
|
-
// One path segment, reduced to characters that cannot escape the directory they name. `.` and `..`
|
|
278
|
-
// are refused rather than folded: folding them would key two distinct repos to one directory.
|
|
279
|
-
safe(segment) {
|
|
280
|
-
if (segment === '.' || segment === '..')
|
|
281
|
-
return '';
|
|
282
|
-
return segment.replace(UNSAFE_SEGMENT_CHARS, '-');
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
exports.PrBodyStore = PrBodyStore;
|
|
286
|
-
exports.PrBodyStore = PrBodyStore = tslib_1.__decorate([
|
|
287
|
-
(0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton),
|
|
288
|
-
tslib_1.__metadata("design:paramtypes", [machine_state_home_1.MachineStateHome,
|
|
289
|
-
state_dir_1.DotWebpieces,
|
|
290
|
-
aged_tree_sweep_1.AgedTreeSweeper])
|
|
291
|
-
], PrBodyStore);
|
|
292
|
-
/** Data-only split of a remote URL into the two pieces the slug needs. */
|
|
293
|
-
class RemoteParts {
|
|
294
|
-
host;
|
|
295
|
-
pathPart;
|
|
296
|
-
constructor(host, pathPart) {
|
|
297
|
-
this.host = host;
|
|
298
|
-
this.pathPart = pathPart;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
//# sourceMappingURL=pr-body-store.js.map
|
package/src/pr-body-store.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pr-body-store.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/pr-body-store.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 { AgedTreeSweeper, RETENTION_DAYS, SweepCount } from './aged-tree-sweep';\nimport { MachineStateHome, StateHome, machineStateHome } from './machine-state-home';\nimport { DotWebpieces, dotWebpieces } from './state-dir';\nimport { toError } from './to-error';\n\n/** The namespace under the machine-global state root. Everything PR-shaped lives beneath it. */\nexport const PRS_STATE_DIR = 'prs';\n/** The gated squash-commit body `wp-finish-upsert-pr` renders and `wp-land-pr` passes to `gh`. */\nexport const MERGE_BODY_FILE = 'merge-commit-body.md';\n/** The provenance sidecar — also the `origin.json` marker `decisions/0001` § O3 asks for. */\nexport const PR_ORIGIN_FILE = 'origin.json';\n\n// Every path segment is a single opaque token taken from a remote URL, so anything that is not an\n// ordinary name character is folded away. `.` and `..` are rejected outright rather than folded,\n// because folding them to `-`/`--` would silently key two different repos to one directory.\nconst UNSAFE_SEGMENT_CHARS = /[^A-Za-z0-9._-]+/g;\n\n/**\n * The REMOTE repository a PR belongs to: `github.com` / `deanhiller` / `webpieces-ts30`. Data-only.\n *\n * `owner` is a PATH, not one token — GitLab subgroups and Azure DevOps project paths are legitimately\n * several segments deep, and collapsing them would merge two repos into one directory.\n */\nexport class RepoSlug {\n readonly host: string;\n readonly owner: string;\n readonly repo: string;\n\n constructor(host: string, owner: string, repo: string) {\n this.host = host;\n this.owner = owner;\n this.repo = repo;\n }\n\n /** FALSE when the remote could not be read or parsed — the caller must then not pretend to store. */\n get known(): boolean {\n return this.host !== '' && this.owner !== '' && this.repo !== '';\n }\n\n /** The directory segments, in order. `owner` may contribute more than one. */\n segments(): string[] {\n return [this.host, ...this.owner.split('/'), this.repo];\n }\n\n toString(): string {\n return `${this.host}/${this.owner}/${this.repo}`;\n }\n}\n\n/**\n * WHO wrote a PR body, and from WHERE. Data-only, serialized beside the body as `origin.json`.\n *\n * `treeRoot` is the load-bearing field: `wp-land-pr` compares it against the tree it is standing in to\n * decide whether the tree-bound bookkeeping (archive the pre-squash tip, promote merge-info, reap the\n * worktree) belongs to it. See `land-pr-command.ts`.\n */\nexport class PrBodyOrigin {\n treeRoot = '';\n primaryRoot = '';\n branch = '';\n feature = '';\n prNumber = '';\n prUrl = '';\n writtenAt = '';\n}\n\n/** A stored body: where it is, and the provenance beside it (`null` origin ⇒ no readable sidecar). */\nexport class PrBodyLocation {\n readonly dir: string;\n readonly bodyFile: string;\n readonly origin: PrBodyOrigin | null;\n\n constructor(dir: string, bodyFile: string, origin: PrBodyOrigin | null) {\n this.dir = dir;\n this.bodyFile = bodyFile;\n this.origin = origin;\n }\n}\n\n/**\n * The gated squash-commit body for a PR, stored by the PR's GLOBAL identity rather than by the tree\n * that happened to render it:\n *\n * ~/.webpieces/prs/<host>/<owner>/<repo>/<prNumber>/merge-commit-body.md\n *\n * ─── The bug this fixes ─────────────────────────────────────────────────────────────────────────────\n * The body used to live at `<primary>/.webpieces/worktrees/<name>/pr-review/<branch>/`, i.e. in\n * PER-WORKTREE state. But `pr-review/<branch>/` is keyed by BRANCH, and a branch is a REPO-WIDE fact —\n * git forbids two worktrees checking out one branch — so a per-tree home only worked while the branch\n * never moved trees. The gated flow ran in the primary clone, landing happened from a linked worktree,\n * `wp-land-pr` looked under the worktree's namespace and printed \"Nothing to land\". That is a SCOPE\n * MISMATCH, not a missing fallback, and the fix is to give the artifact the scope of its identity.\n *\n * ─── Why keyed by the remote, when `decisions/0001` § D2 forbids exactly that ───────────────────────\n * D2 rejects the remote URL as the key for CLONE state, because two clones of one repo have different\n * branches, worktrees and in-flight merges and must NOT share. That reasoning is intact and this is its\n * mirror image: a PR NUMBER is a fact of the remote, the same object seen from every clone, and here\n * sharing is the REQUIREMENT — landing must work from any tree, and from a second clone of the same\n * repo, as long as the PR was posted from this machine. The rule underneath both is one rule: key an\n * artifact by the scope of the fact it describes.\n *\n * ─── Why nested segments and not D2's flattening ────────────────────────────────────────────────────\n * D2 flattens because it keys on an absolute PATH, whose separators must survive as data. Here each\n * segment is already a single opaque token (`github.com`, `deanhiller`, `webpieces-ts30`, `604`), the\n * hierarchy is real, and it buys two things a flattened key cannot: it is browsable by a human\n * debugging a landing, and the retention sweep prunes the empty parents of a reaped PR for free.\n *\n * ─── What is NOT stored here ────────────────────────────────────────────────────────────────────────\n * Everything else under `pr-review/<branch>/` — review.json, the per-checklist verdicts, pr-context.json\n * and the stage snapshots. Those describe work in progress in ONE tree and correctly stay local. Only\n * the finished receipt for a posted PR is machine-global.\n *\n * ─── Never regenerated ──────────────────────────────────────────────────────────────────────────────\n * `wp-land-pr` READS this and never re-renders it. The bytes that land are the bytes finish produced;\n * re-deriving them at land time would be a second authoritative gate whose result nobody reads. See\n * `land-pr-command.ts`.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class PrBodyStore {\n // startDir → the remote slug. `git remote get-url` is a process spawn; commands ask more than once.\n private readonly slugByDir = new Map<string, RepoSlug>();\n\n constructor(\n private readonly stateHome: MachineStateHome = machineStateHome,\n private readonly dotDir: DotWebpieces = dotWebpieces,\n private readonly sweeper: AgedTreeSweeper = new AgedTreeSweeper(),\n ) {}\n\n /** The machine-global root this store writes under, plus whether it degraded to the clone. */\n home(startDir: string): StateHome {\n return this.stateHome.resolve(this.dotDir.primaryRoot(startDir));\n }\n\n /** `<state home>/prs` — the sweep root, and the directory a human is pointed at when landing fails. */\n prsRoot(startDir: string): string {\n return path.join(this.home(startDir).root, PRS_STATE_DIR);\n }\n\n /**\n * The remote this working tree pushes to. Read from `origin`, because that is the remote `gh` itself\n * resolves a PR against — asking a different remote would key the body under a repo the PR is not on.\n * An unknown slug (no remote, detached tooling, a URL shape we decline to guess at) is returned as\n * `known === false`; it is never fabricated.\n */\n slugFor(startDir: string): RepoSlug {\n const cached = this.slugByDir.get(startDir);\n if (cached !== undefined) return cached;\n const slug = this.parseRemote(this.remoteUrl(startDir));\n this.slugByDir.set(startDir, slug);\n return slug;\n }\n\n /** The directory for one PR, or '' when the remote is unknown or `prNumber` is not a number. */\n dirFor(startDir: string, prNumber: string): string {\n const slug = this.slugFor(startDir);\n if (!slug.known || !/^\\d+$/.test(prNumber.trim())) return '';\n return path.join(this.prsRoot(startDir), ...slug.segments(), prNumber.trim());\n }\n\n /**\n * Store the gated body for `prNumber` and return where it went — `null` when it cannot be stored at\n * all (unknown remote, or a PR whose number could not be read back from GitHub). A `null` here is\n * NOT fatal for the caller that is about to merge in the same process: it still holds the bytes. It\n * IS the reason a later `wp-land-pr` will report the PR as not found on this machine, which is the\n * honest answer — no durable receipt was ever filed.\n */\n write(startDir: string, prNumber: string, body: string, origin: PrBodyOrigin): PrBodyLocation | null {\n const dir = this.dirFor(startDir, prNumber);\n if (dir === '') return null;\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: a store that cannot be written degrades to null, never a crash mid-PR-post\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n fs.mkdirSync(dir, { recursive: true });\n const bodyFile = path.join(dir, MERGE_BODY_FILE);\n fs.writeFileSync(bodyFile, body);\n fs.writeFileSync(path.join(dir, PR_ORIGIN_FILE), JSON.stringify(origin, null, 2) + '\\n');\n return new PrBodyLocation(dir, bodyFile, origin);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return null;\n }\n }\n\n /** The stored body for `prNumber`, or `null` when this machine has none. */\n read(startDir: string, prNumber: string): PrBodyLocation | null {\n const dir = this.dirFor(startDir, prNumber);\n if (dir === '') return null;\n const bodyFile = path.join(dir, MERGE_BODY_FILE);\n if (!fs.existsSync(bodyFile)) return null;\n return new PrBodyLocation(dir, bodyFile, this.readOrigin(dir));\n }\n\n /**\n * The 30-day reap of `prs/`, run from the same place every other `.webpieces` sweep runs.\n *\n * `decisions/0001` § O3: these directories outlive every clone, so nothing else will ever delete\n * them. A landed PR's body is never rewritten, so it ages out on schedule; a PR still being pushed\n * to gets a fresh mtime on every `wp-finish-upsert-pr`, so an open PR is never reaped out from under\n * a landing.\n */\n sweep(startDir: string, onDelete: (removed: string, isDir: boolean) => void = (): void => {}): SweepCount {\n return this.sweeper.sweep(this.prsRoot(startDir), RETENTION_DAYS * 24 * 60 * 60 * 1000, onDelete);\n }\n\n private readOrigin(dir: string): PrBodyOrigin | null {\n const file = path.join(dir, PR_ORIGIN_FILE);\n if (!fs.existsSync(file)) return null;\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: an unreadable sidecar means \"no provenance\", never a crash\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed field-by-field below\n const raw = JSON.parse(fs.readFileSync(file, 'utf8')) as Record<string, unknown>;\n const origin = new PrBodyOrigin();\n origin.treeRoot = this.str(raw['treeRoot']);\n origin.primaryRoot = this.str(raw['primaryRoot']);\n origin.branch = this.str(raw['branch']);\n origin.feature = this.str(raw['feature']);\n origin.prNumber = this.str(raw['prNumber']);\n origin.prUrl = this.str(raw['prUrl']);\n origin.writtenAt = this.str(raw['writtenAt']);\n return origin;\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return null;\n }\n }\n\n // webpieces-disable no-any-unknown -- one opaque JSON field, narrowed to string\n private str(value: unknown): string {\n return typeof value === 'string' ? value : '';\n }\n\n private remoteUrl(startDir: string): string {\n const result = spawnSync('git', ['-C', startDir, 'remote', 'get-url', 'origin'], { encoding: 'utf8' });\n if (result.status !== 0) return '';\n return (result.stdout ?? '').trim();\n }\n\n /**\n * Remote URL → host/owner/repo, for the three shapes git actually hands out:\n *\n * git@github.com:owner/repo.git scp-like\n * ssh://git@github.com:22/owner/repo.git url with a port\n * https://user@host/owner/sub/repo.git url, possibly with credentials and subgroups\n *\n * Anything else returns an UNKNOWN slug rather than a guess. A wrong key here silently files the\n * receipt under a repo the PR is not on, which reads to the next reader as \"the body was never\n * written\" — the exact failure being fixed. Not guessing is the only safe default.\n */\n private parseRemote(url: string): RepoSlug {\n const unknown = new RepoSlug('', '', '');\n if (url === '') return unknown;\n const scp = /^(?:[^@/]+@)?([^:/@]+):(?!\\/)(.+)$/.exec(url);\n const parts = scp !== null\n ? new RemoteParts(scp[1] ?? '', scp[2] ?? '')\n : this.urlParts(url);\n if (parts === null) return unknown;\n\n const segments = parts.pathPart.replace(/\\.git$/, '').split('/').filter((s: string): boolean => s !== '');\n if (segments.length < 2) return unknown;\n const repo = this.safe(segments[segments.length - 1] ?? '');\n const owner = segments.slice(0, -1).map((s: string): string => this.safe(s)).join('/');\n const host = this.safe(parts.host);\n if (repo === '' || owner.includes('//') || owner === '' || host === '') return unknown;\n return new RepoSlug(host, owner, repo);\n }\n\n private urlParts(url: string): RemoteParts | null {\n const match = /^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\/(?:[^@/]+@)?([^/:]+)(?::\\d+)?\\/(.+)$/.exec(url);\n if (match === null) return null;\n return new RemoteParts(match[1] ?? '', match[2] ?? '');\n }\n\n // One path segment, reduced to characters that cannot escape the directory they name. `.` and `..`\n // are refused rather than folded: folding them would key two distinct repos to one directory.\n private safe(segment: string): string {\n if (segment === '.' || segment === '..') return '';\n return segment.replace(UNSAFE_SEGMENT_CHARS, '-');\n }\n}\n\n/** Data-only split of a remote URL into the two pieces the slug needs. */\nclass RemoteParts {\n readonly host: string;\n readonly pathPart: string;\n\n constructor(host: string, pathPart: string) {\n this.host = host;\n this.pathPart = pathPart;\n }\n}\n"]}
|