@danmoisan/drm-copilot-mcp 1.1.16 → 1.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/out/mcp-server.js +1139 -133
  2. package/package.json +4 -3
  3. package/resources/claude-customizations/.claude/agents/feature-review.md +10 -1
  4. package/resources/claude-customizations/.claude/agents/orchestrator.md +16 -3
  5. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +18 -6
  6. package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +3 -3
  7. package/resources/claude-customizations/.claude/hooks/{enforce-powershell-batch-budget-route.ps1 → enforce-batch-budget-route.ps1} +12 -10
  8. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +221 -0
  9. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +42 -66
  10. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +58 -10
  11. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +144 -0
  12. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +36 -47
  13. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +179 -27
  14. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +21 -38
  15. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +4 -4
  16. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +58 -10
  17. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +59 -9
  18. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +114 -37
  19. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +3 -3
  20. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +99 -64
  21. package/resources/claude-customizations/.claude/lib/bash/abandon-parallel-item.sh +176 -0
  22. package/resources/claude-customizations/.claude/lib/bash/parallel-lane-assertion.sh +4 -2
  23. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConflict.psm1 +193 -14
  24. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusGlob.psm1 +51 -28
  25. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +5 -8
  26. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -13
  27. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +375 -0
  28. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +22 -15
  29. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +337 -0
  30. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRoutingContract.psm1 +9 -1
  31. package/resources/claude-customizations/.claude/lib/parallel-drift/Invoke-ParallelDriftDetection.ps1 +336 -0
  32. package/resources/claude-customizations/.claude/lib/parallel-drift/ParallelDrift.psm1 +467 -0
  33. package/resources/claude-customizations/.claude/lib/parallel-drift/ParallelDriftHalt.psm1 +388 -0
  34. package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +53 -32
  35. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +19 -4
  36. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +497 -0
  37. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +119 -3
  38. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +21 -0
  39. package/resources/claude-customizations/.claude/rules/quality-tiers.md +1 -1
  40. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +8 -0
  41. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +2 -0
  42. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +11 -3
  43. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +11 -2
  44. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +169 -5
  45. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +1 -1
  46. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_report_records_lib.sh +6 -43
  47. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +4 -15
  48. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +15 -6
  49. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
  50. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +10 -0
  51. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +3 -1
  52. package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +4 -0
  53. package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +4 -0
  54. package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +6 -3
  55. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +33 -13
  56. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +50 -21
  57. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +10 -10
  58. package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +11 -35
  59. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +5 -1
  60. package/resources/claude-customizations/pack-manifests/core.json +10 -0
  61. package/resources/claude-customizations/pack-manifests/powershell.json +0 -1
  62. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +8 -0
  63. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +5 -3
  64. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
  65. package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  66. package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +30 -6
  67. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +3 -1
  68. package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +0 -1
  69. package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +2 -3
  70. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +30 -7
  71. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +41 -2
  72. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +18 -3
  73. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +10 -34
  74. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +1 -1
  75. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +2 -0
  76. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c1.toml +1 -0
  77. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c2.toml +1 -0
  78. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3-elevated.toml +1 -0
  79. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c3.toml +1 -0
  80. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer-c4.toml +1 -0
  81. package/resources/codex-and-agents-customizations/.codex/agents/feature-reviewer.toml +1 -0
  82. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +1 -1
  83. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +1 -1
  84. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +1 -1
  85. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +1 -1
  86. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +1 -1
  87. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +1 -1
  88. package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +2 -2
  89. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +3 -3
  90. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +3 -3
  91. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +3 -3
  92. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +3 -3
  93. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +3 -3
  94. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +3 -3
  95. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  96. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-batch-budget-route.ps1 +139 -0
  97. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +4 -124
  98. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +169 -79
  99. package/resources/codex-and-agents-customizations/pack-manifests/core.json +1 -0
  100. package/resources/customizations/.github/agents/python-orchestrator.agent.md +4 -4
  101. package/resources/customizations/.github/agents/python-typed-engineer.agent.md +7 -8
  102. package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +3 -3
  103. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +8 -0
  104. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +4 -0
  105. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +16 -1
package/out/mcp-server.js CHANGED
@@ -1146,9 +1146,9 @@ var require_util = __commonJS({
1146
1146
  }
1147
1147
  }
1148
1148
  exports2.eachItem = eachItem;
1149
- function makeMergeEvaluated({ mergeNames, mergeToName, mergeValues: mergeValues2, resultToName }) {
1149
+ function makeMergeEvaluated({ mergeNames, mergeToName, mergeValues: mergeValues3, resultToName }) {
1150
1150
  return (gen, from, to, toName) => {
1151
- const res = to === void 0 ? from : to instanceof codegen_1.Name ? (from instanceof codegen_1.Name ? mergeNames(gen, from, to) : mergeToName(gen, from, to), to) : from instanceof codegen_1.Name ? (mergeToName(gen, to, from), from) : mergeValues2(from, to);
1151
+ const res = to === void 0 ? from : to instanceof codegen_1.Name ? (from instanceof codegen_1.Name ? mergeNames(gen, from, to) : mergeToName(gen, from, to), to) : from instanceof codegen_1.Name ? (mergeToName(gen, to, from), from) : mergeValues3(from, to);
1152
1152
  return toName === codegen_1.Name && !(res instanceof codegen_1.Name) ? resultToName(gen, res) : res;
1153
1153
  };
1154
1154
  }
@@ -4150,13 +4150,14 @@ var require_fast_uri = __commonJS({
4150
4150
  if (!malformedIPLiteral) {
4151
4151
  malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4152
4152
  }
4153
- if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4154
- if (uri.indexOf("%") !== -1) {
4155
- if (parsed.host !== void 0 && !malformedIPLiteral) {
4156
- const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4157
- parsed.host = reescapeHostDelimiters(host, isIP);
4158
- }
4153
+ if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
4154
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4155
+ if (!isIP) {
4156
+ host = normalizePercentEncoding(host.toLowerCase());
4159
4157
  }
4158
+ parsed.host = reescapeHostDelimiters(host, isIP);
4159
+ }
4160
+ if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4160
4161
  if (parsed.path) {
4161
4162
  parsed.path = normalizePathEncoding(parsed.path);
4162
4163
  }
@@ -9273,13 +9274,13 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
9273
9274
  }
9274
9275
  return propValues;
9275
9276
  });
9276
- const isObject15 = isObject;
9277
+ const isObject16 = isObject;
9277
9278
  const catchall = def.catchall;
9278
9279
  let value;
9279
9280
  inst._zod.parse = (payload, ctx) => {
9280
9281
  value ?? (value = _normalized.value);
9281
9282
  const input = payload.value;
9282
- if (!isObject15(input)) {
9283
+ if (!isObject16(input)) {
9283
9284
  payload.issues.push({
9284
9285
  expected: "object",
9285
9286
  code: "invalid_type",
@@ -9406,7 +9407,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
9406
9407
  return (payload, ctx) => fn(shape, payload, ctx);
9407
9408
  };
9408
9409
  let fastpass;
9409
- const isObject15 = isObject;
9410
+ const isObject16 = isObject;
9410
9411
  const jit = !globalConfig.jitless;
9411
9412
  const allowsEval2 = allowsEval;
9412
9413
  const fastEnabled = jit && allowsEval2.value;
@@ -9415,7 +9416,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
9415
9416
  inst._zod.parse = (payload, ctx) => {
9416
9417
  value ?? (value = _normalized.value);
9417
9418
  const input = payload.value;
9418
- if (!isObject15(input)) {
9419
+ if (!isObject16(input)) {
9419
9420
  payload.issues.push({
9420
9421
  expected: "object",
9421
9422
  code: "invalid_type",
@@ -19984,6 +19985,237 @@ var BlastRadiusDeriveFileSystem = class {
19984
19985
  }
19985
19986
  };
19986
19987
 
19988
+ // ../../extensions/drm-copilot/src/lib/push-down/claude-blast-radius-overlay.ts
19989
+ var BLAST_RADIUS_OVERLAY_RELATIVE_PATH = "config/blast-radius.local.json";
19990
+ var STRING_LIST_KEYS = /* @__PURE__ */ new Set([
19991
+ "shared_surfaces",
19992
+ "shared_surface_globs",
19993
+ "mandate_reads",
19994
+ "mergeable_paths",
19995
+ "path_roots"
19996
+ ]);
19997
+ var MODULES_KEY = "modules";
19998
+ var VERSION_KEY = "version";
19999
+ var BlastRadiusOverlayError = class extends Error {
20000
+ /** Relative path of the document that could not be composed. */
20001
+ path;
20002
+ /**
20003
+ * @param path Relative path of the offending document.
20004
+ * @param detail Reason appended to the message.
20005
+ */
20006
+ constructor(path16, detail) {
20007
+ super(
20008
+ `Blast-radius overlay ${path16} was not applied; ${BLAST_RADIUS_RELATIVE_PATH} was not written: ${detail}`
20009
+ );
20010
+ this.name = "BlastRadiusOverlayError";
20011
+ this.path = path16;
20012
+ }
20013
+ };
20014
+ function isJsonObject(value) {
20015
+ return value !== null && typeof value === "object" && !Array.isArray(value);
20016
+ }
20017
+ function isStringList(value) {
20018
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
20019
+ }
20020
+ function parseObject(text2, path16) {
20021
+ let parsed;
20022
+ try {
20023
+ parsed = JSON.parse(text2);
20024
+ } catch (error2) {
20025
+ const detail = error2 instanceof Error ? error2.message : String(error2);
20026
+ throw new BlastRadiusOverlayError(path16, `not valid JSON (${detail})`);
20027
+ }
20028
+ if (!isJsonObject(parsed)) {
20029
+ throw new BlastRadiusOverlayError(
20030
+ path16,
20031
+ "document root is not a JSON object"
20032
+ );
20033
+ }
20034
+ return parsed;
20035
+ }
20036
+ function unionStrings(base, overlay) {
20037
+ const composed = [...base];
20038
+ const seen = new Set(base.filter((item) => typeof item === "string"));
20039
+ for (const item of overlay) {
20040
+ if (!seen.has(item)) {
20041
+ seen.add(item);
20042
+ composed.push(item);
20043
+ }
20044
+ }
20045
+ return composed;
20046
+ }
20047
+ function composeStringList(key, base, overlay, overlayPath) {
20048
+ if (!isStringList(overlay)) {
20049
+ throw new BlastRadiusOverlayError(
20050
+ overlayPath,
20051
+ `key "${key}" must be a list of strings`
20052
+ );
20053
+ }
20054
+ return unionStrings(Array.isArray(base) ? base : [], overlay);
20055
+ }
20056
+ function composeModules(base, overlay, overlayPath) {
20057
+ if (!isJsonObject(overlay) || !Object.values(overlay).every((globs) => isStringList(globs))) {
20058
+ throw new BlastRadiusOverlayError(
20059
+ overlayPath,
20060
+ `key "${MODULES_KEY}" must map module names to lists of strings`
20061
+ );
20062
+ }
20063
+ const merged = { ...isJsonObject(base) ? base : {} };
20064
+ for (const [name, globs] of Object.entries(overlay)) {
20065
+ merged[name] = globs;
20066
+ }
20067
+ const composed = {};
20068
+ for (const name of Object.keys(merged).sort(compareOrdinal)) {
20069
+ const globs = merged[name] ?? [];
20070
+ composed[name] = globs;
20071
+ for (const glob of Array.isArray(globs) ? globs : []) {
20072
+ if (typeof glob === "string" && FORBIDDEN_GLOBS.includes(glob)) {
20073
+ throw new BlastRadiusGuardError(name, glob);
20074
+ }
20075
+ }
20076
+ }
20077
+ return composed;
20078
+ }
20079
+ function mergeValues2(base, overlay, dottedKey, overlayPath) {
20080
+ if (isJsonObject(base) && isJsonObject(overlay)) {
20081
+ const merged = {};
20082
+ for (const [key, value] of Object.entries(base)) {
20083
+ const overlayValue = overlay[key];
20084
+ merged[key] = overlayValue === void 0 ? value : mergeValues2(
20085
+ value,
20086
+ overlayValue,
20087
+ `${dottedKey}.${key}`,
20088
+ overlayPath
20089
+ );
20090
+ }
20091
+ for (const [key, value] of Object.entries(overlay)) {
20092
+ if (!(key in merged)) {
20093
+ merged[key] = value;
20094
+ }
20095
+ }
20096
+ return merged;
20097
+ }
20098
+ if (Array.isArray(base) && Array.isArray(overlay)) {
20099
+ if (!isStringList(overlay)) {
20100
+ throw new BlastRadiusOverlayError(
20101
+ overlayPath,
20102
+ `key "${dottedKey}" must be a list of strings`
20103
+ );
20104
+ }
20105
+ return unionStrings(base, overlay);
20106
+ }
20107
+ return overlay;
20108
+ }
20109
+ function composeKey(key, base, overlay, overlayPath) {
20110
+ if (STRING_LIST_KEYS.has(key)) {
20111
+ return composeStringList(key, base, overlay, overlayPath);
20112
+ }
20113
+ if (key === MODULES_KEY) {
20114
+ return composeModules(base, overlay, overlayPath);
20115
+ }
20116
+ return base === void 0 ? overlay : mergeValues2(base, overlay, key, overlayPath);
20117
+ }
20118
+ function composeBlastRadiusOverlay(baseText, overlayText, overlayPath) {
20119
+ if (overlayText === null) {
20120
+ return baseText;
20121
+ }
20122
+ const overlay = parseObject(overlayText, overlayPath);
20123
+ const base = parseObject(baseText, BLAST_RADIUS_RELATIVE_PATH);
20124
+ if (VERSION_KEY in overlay && overlay[VERSION_KEY] !== base[VERSION_KEY]) {
20125
+ throw new BlastRadiusOverlayError(
20126
+ overlayPath,
20127
+ `key "${VERSION_KEY}" does not match the base version`
20128
+ );
20129
+ }
20130
+ const composed = {};
20131
+ for (const [key, value] of Object.entries(base)) {
20132
+ const overlayValue = overlay[key];
20133
+ composed[key] = key === VERSION_KEY || overlayValue === void 0 ? value : composeKey(key, value, overlayValue, overlayPath);
20134
+ }
20135
+ for (const [key, value] of Object.entries(overlay)) {
20136
+ if (!(key in composed)) {
20137
+ composed[key] = composeKey(key, void 0, value, overlayPath);
20138
+ }
20139
+ }
20140
+ return `${JSON.stringify(composed, null, 2)}
20141
+ `;
20142
+ }
20143
+ function normalizePosix6(value) {
20144
+ return value.replace(/\\/g, "/").replace(/\/+$/, "");
20145
+ }
20146
+ function relativeToPosix6(path16, root) {
20147
+ const normalizedPath = normalizePosix6(path16);
20148
+ const normalizedRoot = normalizePosix6(root);
20149
+ if (normalizedPath === normalizedRoot) {
20150
+ return "";
20151
+ }
20152
+ const prefix = `${normalizedRoot}/`;
20153
+ return normalizedPath.startsWith(prefix) ? normalizedPath.slice(prefix.length) : null;
20154
+ }
20155
+ var BlastRadiusOverlayFileSystem = class {
20156
+ inner;
20157
+ destinationRoot;
20158
+ overlayRelativePath;
20159
+ targetRelativePath;
20160
+ /**
20161
+ * @param inner The wrapped adapter performing real I/O.
20162
+ * @param destinationRoot Destination workspace root (POSIX path).
20163
+ * @param overlayRelativePath Destination-relative overlay path.
20164
+ * @param targetRelativePath Destination-relative path the overlay composes
20165
+ * onto.
20166
+ */
20167
+ constructor(inner, destinationRoot, overlayRelativePath = BLAST_RADIUS_OVERLAY_RELATIVE_PATH, targetRelativePath = BLAST_RADIUS_RELATIVE_PATH) {
20168
+ this.inner = inner;
20169
+ this.destinationRoot = normalizePosix6(destinationRoot);
20170
+ this.overlayRelativePath = overlayRelativePath;
20171
+ this.targetRelativePath = targetRelativePath;
20172
+ }
20173
+ /** @inheritdoc */
20174
+ listFiles(root) {
20175
+ return this.inner.listFiles(root);
20176
+ }
20177
+ /** @inheritdoc */
20178
+ isDir(path16) {
20179
+ return this.inner.isDir(path16);
20180
+ }
20181
+ /** @inheritdoc */
20182
+ isFile(path16) {
20183
+ return this.inner.isFile(path16);
20184
+ }
20185
+ /** @inheritdoc */
20186
+ readTextFile(path16) {
20187
+ return this.inner.readTextFile(path16);
20188
+ }
20189
+ /** @inheritdoc */
20190
+ ensureDir(path16) {
20191
+ this.inner.ensureDir(path16);
20192
+ }
20193
+ /**
20194
+ * Write a file, composing the destination overlay onto the target path.
20195
+ *
20196
+ * @param path Absolute destination POSIX path.
20197
+ * @param content Content the layers above want to publish.
20198
+ * @throws BlastRadiusOverlayError When the overlay cannot be composed; the
20199
+ * destination target keeps its prior bytes.
20200
+ * @throws BlastRadiusGuardError When a composed module carries a forbidden
20201
+ * glob; the destination target keeps its prior bytes.
20202
+ */
20203
+ writeTextFile(path16, content) {
20204
+ if (relativeToPosix6(path16, this.destinationRoot) !== this.targetRelativePath) {
20205
+ this.inner.writeTextFile(path16, content);
20206
+ return;
20207
+ }
20208
+ const overlayPath = `${this.destinationRoot}/${this.overlayRelativePath}`;
20209
+ const overlayText = this.inner.isFile(overlayPath) ? this.inner.readTextFile(overlayPath) : null;
20210
+ const composed = composeBlastRadiusOverlay(
20211
+ content,
20212
+ overlayText,
20213
+ this.overlayRelativePath
20214
+ );
20215
+ this.inner.writeTextFile(path16, composed);
20216
+ }
20217
+ };
20218
+
19987
20219
  // ../../extensions/drm-copilot/src/lib/push-down/claude-gitignore-merge.ts
19988
20220
  var CLAUDE_GITIGNORE_RELATIVE_PATH = ".gitignore";
19989
20221
  var CLAUDE_GITIGNORE_BEGIN_SENTINEL = "# BEGIN drm-copilot managed ignores";
@@ -20043,12 +20275,363 @@ function toDocument(lines) {
20043
20275
  return `${lines.join(LINE_SEPARATOR)}${LINE_SEPARATOR}`;
20044
20276
  }
20045
20277
 
20278
+ // ../../extensions/drm-copilot/src/lib/push-down/claude-exclusion-manifest.ts
20279
+ var EXCLUSION_MANIFEST_RELATIVE_PATH = ".push-down-exclusions";
20280
+ var ExclusionManifestError = class extends Error {
20281
+ /** Destination-relative manifest path. */
20282
+ path;
20283
+ /** 1-based offending line, or `undefined` when the whole file is at fault. */
20284
+ line;
20285
+ /**
20286
+ * @param path Destination-relative manifest path.
20287
+ * @param line Offending line, or `undefined` for a whole-file condition.
20288
+ * @param reason Human-readable reason.
20289
+ */
20290
+ constructor(path16, line, reason) {
20291
+ const location = line === void 0 ? path16 : `${path16} line ${String(line)}`;
20292
+ super(`Malformed push-down exclusion manifest ${location}: ${reason}`);
20293
+ this.name = "ExclusionManifestError";
20294
+ this.path = path16;
20295
+ this.line = line;
20296
+ }
20297
+ };
20298
+ var BYTE_ORDER_MARK = "\uFEFF";
20299
+ var REPLACEMENT_CHARACTER = "\uFFFD";
20300
+ var LINE_SPLIT = /\r?\n/;
20301
+ var REPEATED_SLASH = /\/{2,}/g;
20302
+ var DRIVE_PREFIX = /^[A-Za-z]:/;
20303
+ function hasWildcard(normalized) {
20304
+ return normalized.includes("*") || normalized.includes("?");
20305
+ }
20306
+ function escapeRegexCharacter(character) {
20307
+ return /[\\^$.*+?()[\]{}|/-]/.test(character) ? `\\${character}` : character;
20308
+ }
20309
+ function globToRegexText(pattern) {
20310
+ const parts = [];
20311
+ let index = 0;
20312
+ while (index < pattern.length) {
20313
+ if (pattern.startsWith("**", index)) {
20314
+ parts.push(".*");
20315
+ index += 2;
20316
+ continue;
20317
+ }
20318
+ const character = pattern.charAt(index);
20319
+ if (character === "*") {
20320
+ parts.push("[^/]*");
20321
+ } else if (character === "?") {
20322
+ parts.push("[^/]");
20323
+ } else {
20324
+ parts.push(escapeRegexCharacter(character));
20325
+ }
20326
+ index += 1;
20327
+ }
20328
+ return parts.join("");
20329
+ }
20330
+ function matchesGlob(pattern, candidate) {
20331
+ return new RegExp(`^(?:${globToRegexText(pattern)})$`).test(candidate);
20332
+ }
20333
+ function normalizeExclusionEntry(raw) {
20334
+ let text2 = raw.trim().replace(/\\/g, "/");
20335
+ if (text2.startsWith("./")) {
20336
+ text2 = text2.slice(2);
20337
+ }
20338
+ return text2.replace(REPEATED_SLASH, "/");
20339
+ }
20340
+ function malformedReason(normalized) {
20341
+ const checks = [
20342
+ [normalized === "", "entry is empty after normalization"],
20343
+ [
20344
+ normalized.startsWith("/") || DRIVE_PREFIX.test(normalized),
20345
+ "entry is an absolute path"
20346
+ ],
20347
+ [normalized.split("/").includes(".."), "entry contains a '..' segment"],
20348
+ [normalized.startsWith("!"), "negation entries are not supported"],
20349
+ [
20350
+ normalized.includes("[") || normalized.includes("]"),
20351
+ "character classes are not supported"
20352
+ ],
20353
+ [
20354
+ hasWildcard(normalized) && normalized.endsWith("/"),
20355
+ "a wildcard entry must not end with '/'"
20356
+ ],
20357
+ [normalized.includes(REPLACEMENT_CHARACTER), "text is not valid UTF-8"]
20358
+ ];
20359
+ const failed = checks.find(([isFailed]) => isFailed);
20360
+ return failed === void 0 ? void 0 : failed[1];
20361
+ }
20362
+ function classify(normalized) {
20363
+ if (hasWildcard(normalized)) {
20364
+ return "glob";
20365
+ }
20366
+ return normalized.endsWith("/") ? "directory" : "exact";
20367
+ }
20368
+ function parseExclusionManifest(text2, path16) {
20369
+ const body = text2.startsWith(BYTE_ORDER_MARK) ? text2.slice(BYTE_ORDER_MARK.length) : text2;
20370
+ const entries = [];
20371
+ body.split(LINE_SPLIT).forEach((raw, offset) => {
20372
+ const line = offset + 1;
20373
+ const stripped = raw.trim();
20374
+ if (stripped === "" || stripped.startsWith("#")) {
20375
+ return;
20376
+ }
20377
+ const normalized = normalizeExclusionEntry(raw);
20378
+ const reason = malformedReason(normalized);
20379
+ if (reason !== void 0) {
20380
+ throw new ExclusionManifestError(path16, line, reason);
20381
+ }
20382
+ entries.push({ raw, normalized, kind: classify(normalized), line });
20383
+ });
20384
+ return { path: path16, entries };
20385
+ }
20386
+ function matchesExclusionEntry(entry, candidate) {
20387
+ if (entry.kind === "glob") {
20388
+ return matchesGlob(entry.normalized, candidate);
20389
+ }
20390
+ const base = entry.normalized.replace(/\/+$/, "");
20391
+ return candidate === base || candidate.startsWith(`${base}/`);
20392
+ }
20393
+ function planExclusions(payloadRelativePaths, manifest, destinationExists) {
20394
+ const kept = [];
20395
+ const skipped = [];
20396
+ for (const relativePath2 of payloadRelativePaths) {
20397
+ const entry = manifest.entries.find(
20398
+ (candidate) => matchesExclusionEntry(candidate, relativePath2)
20399
+ );
20400
+ if (entry === void 0) {
20401
+ kept.push(relativePath2);
20402
+ continue;
20403
+ }
20404
+ skipped.push({
20405
+ relativePath: relativePath2,
20406
+ entry: entry.normalized,
20407
+ line: entry.line,
20408
+ destinationStatus: destinationExists(relativePath2) ? "present" : "absent"
20409
+ });
20410
+ }
20411
+ const claimed = new Set(
20412
+ skipped.map((skip) => `${String(skip.line)}\0${skip.entry}`)
20413
+ );
20414
+ const unmatchedEntries = manifest.entries.filter(
20415
+ (entry) => !claimed.has(`${String(entry.line)}\0${entry.normalized}`)
20416
+ );
20417
+ return { kept, skipped, unmatchedEntries };
20418
+ }
20419
+ function assertManifestPathIsRootLevel(relativePath2, rootFolders) {
20420
+ if (relativePath2.includes("/")) {
20421
+ throw new Error(
20422
+ `Exclusion manifest path must be root-level: ${relativePath2}`
20423
+ );
20424
+ }
20425
+ const root = rootFolders.find((name) => relativePath2.startsWith(name));
20426
+ if (root !== void 0) {
20427
+ throw new Error(
20428
+ `Exclusion manifest path must not begin with published root ${root}: ${relativePath2}`
20429
+ );
20430
+ }
20431
+ }
20432
+
20433
+ // ../../extensions/drm-copilot/src/lib/push-down/claude-exclusion-filter.ts
20434
+ var EXCLUSION_CONFLICT_LINE_PREFIX = "push-down exclusion conflict:";
20435
+ var DEFAULT_ARTIFACT_DIRECTORY = "artifacts/claude-customizations";
20436
+ var ExclusionViolationError = class extends Error {
20437
+ /**
20438
+ * @param message Description of the refused write.
20439
+ */
20440
+ constructor(message) {
20441
+ super(message);
20442
+ this.name = "ExclusionViolationError";
20443
+ }
20444
+ };
20445
+ function normalizePosix7(value) {
20446
+ return value.replace(/\\/g, "/").replace(/\/+$/, "");
20447
+ }
20448
+ function relativeToPosix7(path16, root) {
20449
+ const normalizedPath = normalizePosix7(path16);
20450
+ const prefix = `${normalizePosix7(root)}/`;
20451
+ return normalizedPath.startsWith(prefix) ? normalizedPath.slice(prefix.length) : void 0;
20452
+ }
20453
+ function findFirstMatch(manifest, candidate) {
20454
+ return manifest.entries.find(
20455
+ (entry) => matchesExclusionEntry(entry, candidate)
20456
+ );
20457
+ }
20458
+ var ExclusionFilterFileSystem = class {
20459
+ /** Skip records in enumeration order. */
20460
+ skipped = [];
20461
+ inner;
20462
+ sourceRoot;
20463
+ destinationRoot;
20464
+ manifest;
20465
+ artifactPrefix;
20466
+ /**
20467
+ * @param inner Adapter the engine would otherwise use.
20468
+ * @param sourceRoot Root the engine enumerates payload files under.
20469
+ * @param destinationRoot Destination workspace root.
20470
+ * @param manifest Parsed manifest.
20471
+ * @param artifactDirectory Destination-relative summary-artifact directory
20472
+ * exempt from the write guard.
20473
+ */
20474
+ constructor(inner, sourceRoot, destinationRoot, manifest, artifactDirectory = DEFAULT_ARTIFACT_DIRECTORY) {
20475
+ this.inner = inner;
20476
+ this.sourceRoot = normalizePosix7(sourceRoot);
20477
+ this.destinationRoot = normalizePosix7(destinationRoot);
20478
+ this.manifest = manifest;
20479
+ this.artifactPrefix = `${normalizePosix7(artifactDirectory)}/`;
20480
+ }
20481
+ listFiles(root) {
20482
+ const listed = this.inner.listFiles(root);
20483
+ const relativeByPath = listed.map(
20484
+ (path16) => [path16, relativeToPosix7(path16, this.sourceRoot)]
20485
+ );
20486
+ const candidates = relativeByPath.flatMap(
20487
+ ([, relative2]) => relative2 === void 0 ? [] : [relative2]
20488
+ );
20489
+ const plan = planExclusions(
20490
+ candidates,
20491
+ this.manifest,
20492
+ (relative2) => this.inner.isFile(`${this.destinationRoot}/${relative2}`)
20493
+ );
20494
+ this.skipped.push(...plan.skipped);
20495
+ const kept = new Set(plan.kept);
20496
+ return relativeByPath.filter(([, relative2]) => relative2 === void 0 || kept.has(relative2)).map(([path16]) => path16);
20497
+ }
20498
+ isDir(path16) {
20499
+ return this.inner.isDir(path16);
20500
+ }
20501
+ isFile(path16) {
20502
+ return this.inner.isFile(path16);
20503
+ }
20504
+ readTextFile(path16) {
20505
+ return this.inner.readTextFile(path16);
20506
+ }
20507
+ ensureDir(path16) {
20508
+ this.inner.ensureDir(path16);
20509
+ }
20510
+ /**
20511
+ * Write through the inner adapter unless the target is excluded.
20512
+ *
20513
+ * @param path Target path.
20514
+ * @param content Text to write.
20515
+ * @throws ExclusionViolationError When the destination-relative target is
20516
+ * the manifest path or matches a manifest entry.
20517
+ */
20518
+ writeTextFile(path16, content) {
20519
+ const relative2 = relativeToPosix7(path16, this.destinationRoot);
20520
+ if (relative2 !== void 0 && !relative2.startsWith(this.artifactPrefix)) {
20521
+ if (relative2 === EXCLUSION_MANIFEST_RELATIVE_PATH) {
20522
+ throw new ExclusionViolationError(
20523
+ `Refusing to write the push-down exclusion manifest: ${relative2}`
20524
+ );
20525
+ }
20526
+ const entry = findFirstMatch(this.manifest, relative2);
20527
+ if (entry !== void 0) {
20528
+ throw new ExclusionViolationError(
20529
+ `Refusing to write excluded path ${relative2} (entry ${entry.normalized}, line ${String(entry.line)})`
20530
+ );
20531
+ }
20532
+ }
20533
+ this.inner.writeTextFile(path16, content);
20534
+ }
20535
+ };
20536
+ function readExclusionManifest(fs12, destinationRoot) {
20537
+ const manifestPath = `${normalizePosix7(destinationRoot)}/${EXCLUSION_MANIFEST_RELATIVE_PATH}`;
20538
+ if (!fs12.isFile(manifestPath)) {
20539
+ if (fs12.isDir(manifestPath)) {
20540
+ throw new ExclusionManifestError(
20541
+ EXCLUSION_MANIFEST_RELATIVE_PATH,
20542
+ void 0,
20543
+ "path is not a regular file"
20544
+ );
20545
+ }
20546
+ return void 0;
20547
+ }
20548
+ return parseExclusionManifest(
20549
+ fs12.readTextFile(manifestPath),
20550
+ EXCLUSION_MANIFEST_RELATIVE_PATH
20551
+ );
20552
+ }
20553
+ function buildExclusionReport(manifest, skipped) {
20554
+ const claimed = new Set(
20555
+ skipped.map((skip) => `${String(skip.line)}\0${skip.entry}`)
20556
+ );
20557
+ return {
20558
+ manifestPath: manifest.path,
20559
+ entries: manifest.entries,
20560
+ skipped: [...skipped],
20561
+ unmatchedEntries: manifest.entries.filter(
20562
+ (entry) => !claimed.has(`${String(entry.line)}\0${entry.normalized}`)
20563
+ )
20564
+ };
20565
+ }
20566
+ function renderExclusionLines(report) {
20567
+ const skippedLines = report.skipped.map((skip) => {
20568
+ const suffix2 = `${skip.relativePath} (entry ${skip.entry}, line ${String(skip.line)})`;
20569
+ return skip.destinationStatus === "present" ? `${EXCLUSION_CONFLICT_LINE_PREFIX} destination file present, not overwritten: ${suffix2}` : `push-down exclusion: skipped ${suffix2}`;
20570
+ });
20571
+ const unmatchedLines = report.unmatchedEntries.map(
20572
+ (entry) => `push-down exclusion: entry matched no payload path: ${entry.normalized} (line ${String(entry.line)})`
20573
+ );
20574
+ return [...skippedLines, ...unmatchedLines];
20575
+ }
20576
+ function exclusionReportPayload(report) {
20577
+ return {
20578
+ conflict_count: report.skipped.filter(
20579
+ (skip) => skip.destinationStatus === "present"
20580
+ ).length,
20581
+ entries: report.entries.map((entry) => entry.normalized),
20582
+ manifest_path: report.manifestPath,
20583
+ skipped: report.skipped.map((skip) => ({
20584
+ destination_status: skip.destinationStatus,
20585
+ entry: skip.entry,
20586
+ line: skip.line,
20587
+ relative_path: skip.relativePath
20588
+ })),
20589
+ skipped_count: report.skipped.length,
20590
+ unmatched_entries: report.unmatchedEntries.map((entry) => entry.normalized)
20591
+ };
20592
+ }
20593
+ function appendExclusionsToArtifact(fs12, artifactPath, report) {
20594
+ const payload = JSON.parse(fs12.readTextFile(artifactPath));
20595
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
20596
+ throw new Error(`Summary artifact is not a JSON object: ${artifactPath}`);
20597
+ }
20598
+ const document = {
20599
+ ...payload,
20600
+ exclusions: exclusionReportPayload(report)
20601
+ };
20602
+ fs12.writeTextFile(artifactPath, stringifySorted(document, 2));
20603
+ }
20604
+
20046
20605
  // ../../extensions/drm-copilot/src/lib/push-down/claude-customizations.ts
20047
20606
  var ARTIFACT_DIRECTORY4 = "artifacts/claude-customizations";
20048
20607
  var ROOT_FOLDERS2 = [".claude", "config"];
20608
+ assertManifestPathIsRootLevel(EXCLUSION_MANIFEST_RELATIVE_PATH, ROOT_FOLDERS2);
20049
20609
  var ROUTING_MERGE_RELATIVE_PATH = "config/orchestration-routing.json";
20050
20610
  var EXCLUDED_RELATIVE_PATHS = [
20051
- ".claude/settings.local.json"
20611
+ ".claude/settings.local.json",
20612
+ "config/blast-radius.local.json"
20613
+ ];
20614
+ var DESTINATION_WRITE_DECORATORS = [
20615
+ {
20616
+ relativePath: ROUTING_MERGE_RELATIVE_PATH,
20617
+ wrap: (inner, destinationRoot) => new RoutingMergeFileSystem(
20618
+ inner,
20619
+ destinationRoot,
20620
+ ROUTING_MERGE_RELATIVE_PATH
20621
+ )
20622
+ },
20623
+ {
20624
+ relativePath: BLAST_RADIUS_RELATIVE_PATH,
20625
+ wrap: (inner, destinationRoot) => new BlastRadiusOverlayFileSystem(inner, destinationRoot)
20626
+ },
20627
+ {
20628
+ relativePath: BLAST_RADIUS_RELATIVE_PATH,
20629
+ wrap: (inner, destinationRoot, { listEntries }) => listEntries === void 0 ? new BlastRadiusDeriveFileSystem(inner, destinationRoot) : new BlastRadiusDeriveFileSystem(
20630
+ inner,
20631
+ destinationRoot,
20632
+ listEntries
20633
+ )
20634
+ }
20052
20635
  ];
20053
20636
  var BUNDLE_ROOT_RELATIVE_DIR = "extensions/drm-copilot/resources/claude-customizations";
20054
20637
  var PACK_MANIFEST_SUBDIR2 = "pack-manifests";
@@ -20091,23 +20674,19 @@ function pushDownCustomizations4(options) {
20091
20674
  } = options;
20092
20675
  const effectiveSource = sourceRoot ?? repoRoot;
20093
20676
  const effectiveBundle = bundleRoot ?? joinPosix7(effectiveSource, BUNDLE_ROOT_RELATIVE_DIR);
20677
+ const manifest = readExclusionManifest(fs12, destinationRoot);
20094
20678
  const publishedPaths = resolvePublishedPaths2(
20095
20679
  packs ?? null,
20096
20680
  effectiveBundle,
20097
20681
  fs12
20098
20682
  );
20099
- const mergingFs = new RoutingMergeFileSystem(
20100
- fs12,
20101
- destinationRoot,
20102
- ROUTING_MERGE_RELATIVE_PATH
20103
- );
20104
- const derivingFs = listEntries === void 0 ? new BlastRadiusDeriveFileSystem(mergingFs, destinationRoot) : new BlastRadiusDeriveFileSystem(
20105
- mergingFs,
20106
- destinationRoot,
20107
- listEntries
20683
+ const decoratorOptions = listEntries === void 0 ? {} : { listEntries };
20684
+ const decoratedFs = DESTINATION_WRITE_DECORATORS.reduce(
20685
+ (inner, decorator) => decorator.wrap(inner, destinationRoot, decoratorOptions),
20686
+ fs12
20108
20687
  );
20109
20688
  const excludingFs = new ExcludingFileSystem(
20110
- derivingFs,
20689
+ decoratedFs,
20111
20690
  repoRoot,
20112
20691
  EXCLUDED_RELATIVE_PATHS,
20113
20692
  {
@@ -20119,10 +20698,17 @@ function pushDownCustomizations4(options) {
20119
20698
  variantRoot: effectiveBundle
20120
20699
  }
20121
20700
  );
20701
+ const exclusionFs = manifest === void 0 ? void 0 : new ExclusionFilterFileSystem(
20702
+ excludingFs,
20703
+ effectiveSource,
20704
+ destinationRoot,
20705
+ manifest,
20706
+ ARTIFACT_DIRECTORY4
20707
+ );
20122
20708
  const summary = pushDownCustomizations({
20123
20709
  repoRoot,
20124
20710
  destinationRoot,
20125
- fs: excludingFs,
20711
+ fs: exclusionFs ?? excludingFs,
20126
20712
  ...sourceRoot === void 0 ? {} : { sourceRoot },
20127
20713
  ...artifactRoot === void 0 ? {} : { artifactRoot },
20128
20714
  rootFolders: ROOT_FOLDERS2,
@@ -20130,19 +20716,41 @@ function pushDownCustomizations4(options) {
20130
20716
  rewriteReferences: passthroughRewrite2,
20131
20717
  ...clock === void 0 ? {} : { clock }
20132
20718
  });
20133
- deliverDestinationGitignore(fs12, destinationRoot);
20134
- return summary;
20719
+ const gitignoreSkip = deliverDestinationGitignore(
20720
+ fs12,
20721
+ destinationRoot,
20722
+ manifest
20723
+ );
20724
+ if (manifest === void 0 || exclusionFs === void 0) {
20725
+ return summary;
20726
+ }
20727
+ const skipped = gitignoreSkip === void 0 ? exclusionFs.skipped : [...exclusionFs.skipped, gitignoreSkip];
20728
+ const report = buildExclusionReport(manifest, skipped);
20729
+ appendExclusionsToArtifact(fs12, summary.artifactPath, report);
20730
+ return { ...summary, exclusions: report };
20135
20731
  }
20136
- function deliverDestinationGitignore(fs12, destinationRoot) {
20732
+ function deliverDestinationGitignore(fs12, destinationRoot, manifest) {
20137
20733
  const destinationPath = joinPosix7(
20138
20734
  destinationRoot,
20139
20735
  CLAUDE_GITIGNORE_RELATIVE_PATH
20140
20736
  );
20737
+ const entry = manifest?.entries.find(
20738
+ (candidate) => matchesExclusionEntry(candidate, CLAUDE_GITIGNORE_RELATIVE_PATH)
20739
+ );
20740
+ if (entry !== void 0) {
20741
+ return {
20742
+ relativePath: CLAUDE_GITIGNORE_RELATIVE_PATH,
20743
+ entry: entry.normalized,
20744
+ line: entry.line,
20745
+ destinationStatus: fs12.isFile(destinationPath) ? "present" : "absent"
20746
+ };
20747
+ }
20141
20748
  const currentText = fs12.isFile(destinationPath) ? fs12.readTextFile(destinationPath) : "";
20142
20749
  const mergedText = mergeClaudeGitignore(currentText);
20143
20750
  if (mergedText !== currentText) {
20144
20751
  fs12.writeTextFile(destinationPath, mergedText);
20145
20752
  }
20753
+ return void 0;
20146
20754
  }
20147
20755
 
20148
20756
  // ../../extensions/drm-copilot/src/lib/push-down/push-down-service-call.ts
@@ -20216,11 +20824,16 @@ function pushDownClaudeCustomizationsServiceCall(input) {
20216
20824
  ...input.memoryMode === void 0 ? {} : { memoryMode: input.memoryMode },
20217
20825
  ...input.clock === void 0 ? {} : { clock: input.clock }
20218
20826
  });
20827
+ const lines = summary.exclusions === void 0 ? [] : renderExclusionLines(summary.exclusions);
20828
+ for (const line of lines) {
20829
+ input.log?.(line);
20830
+ }
20219
20831
  return {
20220
20832
  tool: "push_down_claude_customizations",
20221
20833
  workspaceRoot: input.workspaceRoot,
20222
20834
  summary: "Pushed bundled Claude Code customizations into the destination workspace.",
20223
- artifacts: [normalizeGeneratedPath(summary.artifactPath)]
20835
+ artifacts: [normalizeGeneratedPath(summary.artifactPath)],
20836
+ ...lines.length > 0 ? { warnings: lines } : {}
20224
20837
  };
20225
20838
  }
20226
20839
 
@@ -25127,6 +25740,7 @@ var MERGED_STATUSES = /* @__PURE__ */ new Set([
25127
25740
  "merged",
25128
25741
  "worktree_removed"
25129
25742
  ]);
25743
+ var NOT_STARTED_MERGE_STATUS = "not_started";
25130
25744
  function isObject6(value) {
25131
25745
  return typeof value === "object" && value !== null && !Array.isArray(value);
25132
25746
  }
@@ -25207,6 +25821,9 @@ function validateMergeStatusEnum(features) {
25207
25821
  }
25208
25822
  return errors;
25209
25823
  }
25824
+ function hasStarted(feature) {
25825
+ return typeof feature["worktree_created_at"] === "string" || feature["merge_status"] !== NOT_STARTED_MERGE_STATUS;
25826
+ }
25210
25827
  function validateWaveBarrierOrdering(features) {
25211
25828
  const errors = [];
25212
25829
  const byFolder = /* @__PURE__ */ new Map();
@@ -25223,6 +25840,9 @@ function validateWaveBarrierOrdering(features) {
25223
25840
  if (typeof folder !== "string" || !Array.isArray(dependsOn)) {
25224
25841
  continue;
25225
25842
  }
25843
+ if (!hasStarted(feature)) {
25844
+ continue;
25845
+ }
25226
25846
  const worktreeCreatedAt = feature["worktree_created_at"];
25227
25847
  for (const dependency of dependsOn) {
25228
25848
  const resolved = resolveFeatureReference(dependency, index);
@@ -25234,9 +25854,13 @@ function validateWaveBarrierOrdering(features) {
25234
25854
  const depConfirmedAt = dependencyFeature["merge_confirmed_at"];
25235
25855
  const statusViolation = typeof depMergeStatus !== "string" || !MERGED_STATUSES.has(depMergeStatus);
25236
25856
  const timingViolation = typeof depConfirmedAt === "string" && typeof worktreeCreatedAt === "string" && depConfirmedAt > worktreeCreatedAt;
25237
- if (statusViolation || timingViolation) {
25857
+ if (statusViolation) {
25238
25858
  errors.push(
25239
- `EPIC_WAVE_BARRIER_VIOLATION: ${folder} started before dependency ${String(dependency)} merged`
25859
+ `EPIC_WAVE_BARRIER_VIOLATION: ${folder} is treated as started while dependency ${String(dependency)} is not merged`
25860
+ );
25861
+ } else if (timingViolation) {
25862
+ errors.push(
25863
+ `EPIC_WAVE_BARRIER_VIOLATION: ${folder} worktree_created_at precedes dependency ${String(dependency)} merge_confirmed_at`
25240
25864
  );
25241
25865
  }
25242
25866
  }
@@ -26582,6 +27206,201 @@ function validateEpicPlannerStateText(text2, options = {}) {
26582
27206
  return errors;
26583
27207
  }
26584
27208
 
27209
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-blocked-reason.ts
27210
+ var MECHANICAL_BLOCKED_REASONS = /* @__PURE__ */ new Set([
27211
+ "spawn_agent_unavailable",
27212
+ "delegation_launch_failed",
27213
+ "delegate_no_receipt",
27214
+ "delegate_contract_incomplete",
27215
+ "validator_failed",
27216
+ "user_requested_stop"
27217
+ ]);
27218
+ var NON_MECHANICAL_BLOCKED_REASONS = /* @__PURE__ */ new Set([
27219
+ "premise_falsified",
27220
+ "external_dependency",
27221
+ "policy_hold",
27222
+ "awaiting_ci",
27223
+ "human_decision_required"
27224
+ ]);
27225
+ var VALID_BLOCKED_REASONS = /* @__PURE__ */ new Set([
27226
+ "none",
27227
+ ...MECHANICAL_BLOCKED_REASONS,
27228
+ ...NON_MECHANICAL_BLOCKED_REASONS
27229
+ ]);
27230
+
27231
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-promotion-tools.ts
27232
+ var FEATURE_PROMOTION_ENTRY_TOOL = "new_potential_entry";
27233
+ var BUG_PROMOTION_ENTRY_TOOL = "new_potential_bug_entry";
27234
+ var BUG_PROMOTION_TYPE = "bug";
27235
+ var PROMOTION_TYPE_KEY = "promotion-type";
27236
+ function resolvePromotionEntryTools(requiredMcpTools, state) {
27237
+ if (state[PROMOTION_TYPE_KEY] !== BUG_PROMOTION_TYPE) {
27238
+ return [...requiredMcpTools];
27239
+ }
27240
+ return requiredMcpTools.map(
27241
+ (tool) => tool === FEATURE_PROMOTION_ENTRY_TOOL ? BUG_PROMOTION_ENTRY_TOOL : tool
27242
+ );
27243
+ }
27244
+
27245
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-issue-adoption.ts
27246
+ var ISSUE_ADOPTION_KEY = "issue_adoption";
27247
+ var POTENTIAL_TO_ISSUE_TOOL = "potential_to_issue";
27248
+ var ORIGIN_VALUES = /* @__PURE__ */ new Set([
27249
+ "transferred",
27250
+ "filed_before_orchestration",
27251
+ "epic_decomposition"
27252
+ ]);
27253
+ var VERIFIED_VIA_VALUES = /* @__PURE__ */ new Set([
27254
+ "gh_issue_view",
27255
+ "gh_api_get",
27256
+ "github_mcp_issue_read"
27257
+ ]);
27258
+ var WAIVABLE_TOOLS = /* @__PURE__ */ new Set([
27259
+ POTENTIAL_TO_ISSUE_TOOL,
27260
+ FEATURE_PROMOTION_ENTRY_TOOL,
27261
+ BUG_PROMOTION_ENTRY_TOOL
27262
+ ]);
27263
+ var PROMOTION_ENTRY_TOOLS = /* @__PURE__ */ new Set([
27264
+ FEATURE_PROMOTION_ENTRY_TOOL,
27265
+ BUG_PROMOTION_ENTRY_TOOL
27266
+ ]);
27267
+ var POTENTIAL_RECORD_PREFIX = "docs/features/potential/";
27268
+ var POTENTIAL_RECORD_SUFFIX = ".md";
27269
+ var ISSUE_NUM_PATTERN = /^[1-9][0-9]*$/;
27270
+ var ERROR_NOT_OBJECT = "Checkpoint issue_adoption must be an object when present.";
27271
+ var ERROR_ISSUE_NUM_FORMAT = "Checkpoint issue_adoption.issue_num must be a string of decimal digits without a leading zero.";
27272
+ var ERROR_ISSUE_NUM_MISMATCH = "Checkpoint issue_adoption.issue_num must equal the checkpoint issue-num.";
27273
+ var ERROR_ISSUE_URL = "Checkpoint issue_adoption.issue_url must end with /issues/ followed by issue_num.";
27274
+ var ERROR_ORIGIN = "Checkpoint issue_adoption.origin must be one of transferred, filed_before_orchestration, epic_decomposition.";
27275
+ var ERROR_VERIFIED_VIA = "Checkpoint issue_adoption.verified_via must be one of gh_issue_view, gh_api_get, github_mcp_issue_read.";
27276
+ var ERROR_VERIFIED_AT = "Checkpoint issue_adoption.verified_at must be present.";
27277
+ var ERROR_EVIDENCE = "Checkpoint issue_adoption.evidence must be a non-empty string.";
27278
+ var ERROR_WAIVED_TOOLS_SHAPE = "Checkpoint issue_adoption.waived_tools must be a non-empty list of tool names.";
27279
+ var ERROR_WAIVED_TOOLS_INCLUDE = "Checkpoint issue_adoption.waived_tools must include potential_to_issue.";
27280
+ function isPlainObject3(value) {
27281
+ return typeof value === "object" && value !== null && !Array.isArray(value);
27282
+ }
27283
+ function isNonBlankString(value) {
27284
+ return typeof value === "string" && value.trim().length > 0;
27285
+ }
27286
+ function validIssueNum(value) {
27287
+ return typeof value === "string" && ISSUE_NUM_PATTERN.test(value) ? value : null;
27288
+ }
27289
+ function issueIdentityErrors(adoption, state) {
27290
+ const errors = [];
27291
+ const issueNum = validIssueNum(adoption["issue_num"]);
27292
+ if (issueNum === null) {
27293
+ errors.push(ERROR_ISSUE_NUM_FORMAT);
27294
+ } else {
27295
+ const checkpointIssueNum = state["issue-num"];
27296
+ if (typeof checkpointIssueNum === "string" && checkpointIssueNum !== issueNum) {
27297
+ errors.push(ERROR_ISSUE_NUM_MISMATCH);
27298
+ }
27299
+ }
27300
+ const issueUrl = adoption["issue_url"];
27301
+ if (issueNum === null || typeof issueUrl !== "string" || !issueUrl.endsWith(`/issues/${issueNum}`)) {
27302
+ errors.push(ERROR_ISSUE_URL);
27303
+ }
27304
+ return errors;
27305
+ }
27306
+ function provenanceErrors(adoption) {
27307
+ const errors = [];
27308
+ const origin = adoption["origin"];
27309
+ if (typeof origin !== "string" || !ORIGIN_VALUES.has(origin)) {
27310
+ errors.push(ERROR_ORIGIN);
27311
+ }
27312
+ const verifiedVia = adoption["verified_via"];
27313
+ if (typeof verifiedVia !== "string" || !VERIFIED_VIA_VALUES.has(verifiedVia)) {
27314
+ errors.push(ERROR_VERIFIED_VIA);
27315
+ }
27316
+ const verifiedAt = adoption["verified_at"];
27317
+ if (verifiedAt === void 0 || verifiedAt === null || typeof verifiedAt === "string" && verifiedAt.trim().length === 0) {
27318
+ errors.push(ERROR_VERIFIED_AT);
27319
+ }
27320
+ if (!isNonBlankString(adoption["evidence"])) {
27321
+ errors.push(ERROR_EVIDENCE);
27322
+ }
27323
+ return errors;
27324
+ }
27325
+ function waivedToolList(value) {
27326
+ if (!Array.isArray(value)) {
27327
+ return null;
27328
+ }
27329
+ const items = value;
27330
+ if (items.length === 0 || !items.every((item) => isNonBlankString(item))) {
27331
+ return null;
27332
+ }
27333
+ return items;
27334
+ }
27335
+ function waivedToolErrors(waived, options) {
27336
+ const errors = [];
27337
+ const seen = /* @__PURE__ */ new Set();
27338
+ for (const tool of waived) {
27339
+ if (seen.has(tool)) {
27340
+ errors.push(
27341
+ `Checkpoint issue_adoption.waived_tools lists a tool more than once: ${tool}.`
27342
+ );
27343
+ } else if (!WAIVABLE_TOOLS.has(tool)) {
27344
+ errors.push(
27345
+ `Checkpoint issue_adoption.waived_tools names a tool that cannot be waived: ${tool}.`
27346
+ );
27347
+ } else if (!options.requiredMcpTools.includes(tool)) {
27348
+ errors.push(
27349
+ `Checkpoint issue_adoption.waived_tools names a tool that is not required by route ${options.routeId}: ${tool}.`
27350
+ );
27351
+ } else if (options.successfulTools.has(tool)) {
27352
+ errors.push(
27353
+ `Checkpoint issue_adoption.waived_tools names a tool that has a successful MCP receipt: ${tool}.`
27354
+ );
27355
+ }
27356
+ seen.add(tool);
27357
+ }
27358
+ if (!seen.has(POTENTIAL_TO_ISSUE_TOOL)) {
27359
+ errors.push(ERROR_WAIVED_TOOLS_INCLUDE);
27360
+ }
27361
+ return errors;
27362
+ }
27363
+ function potentialRecordErrors(waived, potentialRecord) {
27364
+ if (typeof potentialRecord === "string" && potentialRecord.startsWith(POTENTIAL_RECORD_PREFIX) && potentialRecord.endsWith(POTENTIAL_RECORD_SUFFIX)) {
27365
+ return [];
27366
+ }
27367
+ const errors = [];
27368
+ const reported = /* @__PURE__ */ new Set();
27369
+ for (const tool of waived) {
27370
+ if (PROMOTION_ENTRY_TOOLS.has(tool) && !reported.has(tool)) {
27371
+ errors.push(
27372
+ `Checkpoint issue_adoption.potential_record must name a markdown file under docs/features/potential/ when waiving ${tool}.`
27373
+ );
27374
+ reported.add(tool);
27375
+ }
27376
+ }
27377
+ return errors;
27378
+ }
27379
+ function resolveIssueAdoption(state, options) {
27380
+ if (!Object.prototype.hasOwnProperty.call(state, ISSUE_ADOPTION_KEY)) {
27381
+ return { errors: [], waivedTools: /* @__PURE__ */ new Set() };
27382
+ }
27383
+ const adoption = state[ISSUE_ADOPTION_KEY];
27384
+ if (!isPlainObject3(adoption)) {
27385
+ return { errors: [ERROR_NOT_OBJECT], waivedTools: /* @__PURE__ */ new Set() };
27386
+ }
27387
+ const errors = [
27388
+ ...issueIdentityErrors(adoption, state),
27389
+ ...provenanceErrors(adoption)
27390
+ ];
27391
+ const waived = waivedToolList(adoption["waived_tools"]);
27392
+ if (waived === null) {
27393
+ errors.push(ERROR_WAIVED_TOOLS_SHAPE);
27394
+ return { errors, waivedTools: /* @__PURE__ */ new Set() };
27395
+ }
27396
+ errors.push(...waivedToolErrors(waived, options));
27397
+ errors.push(...potentialRecordErrors(waived, adoption["potential_record"]));
27398
+ if (errors.length > 0) {
27399
+ return { errors, waivedTools: /* @__PURE__ */ new Set() };
27400
+ }
27401
+ return { errors: [], waivedTools: new Set(waived) };
27402
+ }
27403
+
26585
27404
  // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-routing.ts
26586
27405
  var ROUTING_MATRIX_RELATIVE_PATH = "config/orchestration-routing.json";
26587
27406
  var MANDATORY_ROUTE_PHASES = {
@@ -26770,7 +27589,10 @@ function validateRoutingContract(state, options = {}) {
26770
27589
  const errors = [];
26771
27590
  const requiredAgents = routeList(rawRoute, "required_agents");
26772
27591
  const requiredSkills = routeList(rawRoute, "required_skills");
26773
- const requiredMcpTools = routeList(rawRoute, "required_mcp_tools");
27592
+ const requiredMcpTools = resolvePromotionEntryTools(
27593
+ routeList(rawRoute, "required_mcp_tools"),
27594
+ state
27595
+ );
26774
27596
  if (stateList(state, "required_agents", requiredAgents) === null) {
26775
27597
  errors.push(
26776
27598
  `Checkpoint required_agents must match routing matrix for route ${routeId}.`
@@ -26799,11 +27621,20 @@ function validateRoutingContract(state, options = {}) {
26799
27621
  }
26800
27622
  }
26801
27623
  const actualTools = mcpTools(state);
27624
+ const adoption = resolveIssueAdoption(state, {
27625
+ routeId,
27626
+ requiredMcpTools,
27627
+ successfulTools: actualTools
27628
+ });
26802
27629
  for (const tool of requiredMcpTools) {
27630
+ if (adoption.waivedTools.has(tool)) {
27631
+ continue;
27632
+ }
26803
27633
  if (!actualTools.has(tool)) {
26804
27634
  errors.push(`Checkpoint missing successful MCP receipt: ${tool}.`);
26805
27635
  }
26806
27636
  }
27637
+ errors.push(...adoption.errors);
26807
27638
  errors.push(...validateEmptyListField(state, "local_execution_overrides"));
26808
27639
  errors.push(...validateEmptyListField(state, "delegation_bypasses"));
26809
27640
  errors.push(...validateLifecycleOperations(state));
@@ -26949,14 +27780,137 @@ function validateHumanInteraction(humanInteraction) {
26949
27780
  return errors;
26950
27781
  }
26951
27782
 
27783
+ // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-remediation-accounting.ts
27784
+ var REVIEW_OUTCOMES_KEY = "review_outcomes";
27785
+ var REVIEW_VERDICTS = [
27786
+ "PASS",
27787
+ "REMEDIATION_REQUIRED",
27788
+ "HALT_NON_REMEDIABLE",
27789
+ "AWAITING_CI"
27790
+ ];
27791
+ var REMEDIABILITY_CLASSES = [
27792
+ "autonomous",
27793
+ "external_dependency",
27794
+ "policy_hold",
27795
+ "awaiting_ci",
27796
+ "human_decision_required"
27797
+ ];
27798
+ var HALT_CLASSES = /* @__PURE__ */ new Set([
27799
+ "external_dependency",
27800
+ "policy_hold",
27801
+ "human_decision_required"
27802
+ ]);
27803
+ var REMEDIABLE_CLASS = "autonomous";
27804
+ function isObject11(value) {
27805
+ return typeof value === "object" && value !== null && !Array.isArray(value);
27806
+ }
27807
+ function pythonRepr8(value) {
27808
+ if (value === null || value === void 0) {
27809
+ return "None";
27810
+ }
27811
+ if (value === true) {
27812
+ return "True";
27813
+ }
27814
+ if (value === false) {
27815
+ return "False";
27816
+ }
27817
+ return String(value);
27818
+ }
27819
+ function deriveReviewVerdict(remediabilities) {
27820
+ for (const value of remediabilities) {
27821
+ if (!REMEDIABILITY_CLASSES.includes(value)) {
27822
+ throw new RangeError(`invalid remediability: ${value}`);
27823
+ }
27824
+ }
27825
+ if (remediabilities.length === 0) {
27826
+ return "PASS";
27827
+ }
27828
+ if (remediabilities.includes(REMEDIABLE_CLASS)) {
27829
+ return "REMEDIATION_REQUIRED";
27830
+ }
27831
+ if (remediabilities.some((value) => HALT_CLASSES.has(value))) {
27832
+ return "HALT_NON_REMEDIABLE";
27833
+ }
27834
+ return "AWAITING_CI";
27835
+ }
27836
+ function validateReviewOutcome(index, outcome) {
27837
+ const errors = [];
27838
+ const prefix = `Checkpoint remediation review outcome #${index}`;
27839
+ const verdict = outcome["verdict"];
27840
+ if (typeof verdict !== "string" || !REVIEW_VERDICTS.includes(verdict)) {
27841
+ errors.push(
27842
+ `${prefix} verdict must be one of PASS, REMEDIATION_REQUIRED, HALT_NON_REMEDIABLE, AWAITING_CI; got: ${pythonRepr8(verdict)}`
27843
+ );
27844
+ }
27845
+ const classes = [];
27846
+ const findings = outcome["findings"];
27847
+ if (!Array.isArray(findings)) {
27848
+ errors.push(`${prefix} findings must be a list.`);
27849
+ } else {
27850
+ findings.forEach((finding, findingIndex) => {
27851
+ if (!isObject11(finding)) {
27852
+ errors.push(`${prefix} finding #${findingIndex} must be an object.`);
27853
+ return;
27854
+ }
27855
+ const remediability = finding["remediability"];
27856
+ if (typeof remediability !== "string" || !REMEDIABILITY_CLASSES.includes(remediability)) {
27857
+ errors.push(
27858
+ `${prefix} finding #${findingIndex} remediability must be one of autonomous, external_dependency, policy_hold, awaiting_ci, human_decision_required; got: ${pythonRepr8(remediability)}`
27859
+ );
27860
+ return;
27861
+ }
27862
+ classes.push(remediability);
27863
+ });
27864
+ }
27865
+ if (errors.length === 0) {
27866
+ const expected = deriveReviewVerdict(classes);
27867
+ if (verdict !== expected) {
27868
+ errors.push(
27869
+ `${prefix} verdict ${pythonRepr8(verdict)} does not match its findings (expected ${expected}).`
27870
+ );
27871
+ }
27872
+ }
27873
+ return errors;
27874
+ }
27875
+ function validateReviewOutcomes(loop) {
27876
+ if (!Object.prototype.hasOwnProperty.call(loop, REVIEW_OUTCOMES_KEY)) {
27877
+ return [];
27878
+ }
27879
+ const outcomes = loop[REVIEW_OUTCOMES_KEY];
27880
+ if (!Array.isArray(outcomes)) {
27881
+ return ["Checkpoint remediation_loop review_outcomes must be a list."];
27882
+ }
27883
+ const errors = [];
27884
+ outcomes.forEach((outcome, index) => {
27885
+ if (!isObject11(outcome)) {
27886
+ errors.push(
27887
+ `Checkpoint remediation review outcome #${index} must be an object.`
27888
+ );
27889
+ return;
27890
+ }
27891
+ errors.push(...validateReviewOutcome(index, outcome));
27892
+ });
27893
+ return errors;
27894
+ }
27895
+
26952
27896
  // ../../extensions/drm-copilot/src/lib/validate/orchestrator-state-remediation.ts
26953
27897
  var REMEDIATION_LOOP_KEY = "remediation_loop";
26954
27898
  var REMEDIATION_CYCLES_KEY = "cycles";
26955
27899
  var EXECUTION_STATUSES_REQUIRING_CLEAR_PREFLIGHT = /* @__PURE__ */ new Set(["in_progress", "complete", "failed"]);
26956
27900
  var PREFLIGHT_CLEARED_STATUS = "clear";
26957
- function isObject11(value) {
27901
+ var COMPLETED_ATTEMPTS_KEY = "completed_attempts";
27902
+ var CANDIDATE_APPLIED_KEY = "candidate_applied";
27903
+ var OPENED_BY_REVIEW_KEY = "opened_by_review";
27904
+ var COMPLETED_EXECUTION_STATUS = "complete";
27905
+ function isObject12(value) {
26958
27906
  return typeof value === "object" && value !== null && !Array.isArray(value);
26959
27907
  }
27908
+ function isStrictInteger(value) {
27909
+ return typeof value === "number" && Number.isInteger(value);
27910
+ }
27911
+ function hasKey(value, key) {
27912
+ return Object.prototype.hasOwnProperty.call(value, key);
27913
+ }
26960
27914
  function validateRemediationCycle(index, cycle) {
26961
27915
  const errors = [];
26962
27916
  const planPath = cycle["plan_path"];
@@ -26968,7 +27922,7 @@ function validateRemediationCycle(index, cycle) {
26968
27922
  const executionStatus = cycle["execution_status"];
26969
27923
  if (typeof executionStatus === "string" && EXECUTION_STATUSES_REQUIRING_CLEAR_PREFLIGHT.has(executionStatus)) {
26970
27924
  const preflight = cycle["preflight"];
26971
- const preflightStatus = isObject11(preflight) ? preflight["final_status"] : void 0;
27925
+ const preflightStatus = isObject12(preflight) ? preflight["final_status"] : void 0;
26972
27926
  if (preflightStatus !== PREFLIGHT_CLEARED_STATUS) {
26973
27927
  errors.push(
26974
27928
  `Checkpoint remediation cycle #${index} execution_status is ${executionStatus} but preflight.final_status is not 'clear'.`
@@ -26982,22 +27936,79 @@ function validateRemediationCycle(index, cycle) {
26982
27936
  }
26983
27937
  return errors;
26984
27938
  }
27939
+ function opensRemediationReview(openedByReview, outcomes) {
27940
+ if (!isStrictInteger(openedByReview) || !Array.isArray(outcomes)) {
27941
+ return false;
27942
+ }
27943
+ if (openedByReview < 0 || openedByReview >= outcomes.length) {
27944
+ return false;
27945
+ }
27946
+ const target = outcomes[openedByReview];
27947
+ return isObject12(target) && target["verdict"] === "REMEDIATION_REQUIRED";
27948
+ }
27949
+ function validateRemediationAccounting(loop, cycleList) {
27950
+ const errors = [];
27951
+ const outcomes = loop[REVIEW_OUTCOMES_KEY];
27952
+ let appliedCount = 0;
27953
+ (cycleList ?? []).forEach((cycle, index) => {
27954
+ if (!isObject12(cycle)) {
27955
+ return;
27956
+ }
27957
+ const prefix = `Checkpoint remediation cycle #${index}`;
27958
+ if (hasKey(cycle, CANDIDATE_APPLIED_KEY)) {
27959
+ const candidateApplied = cycle[CANDIDATE_APPLIED_KEY];
27960
+ if (typeof candidateApplied !== "boolean") {
27961
+ errors.push(`${prefix} candidate_applied must be a boolean.`);
27962
+ } else if (candidateApplied) {
27963
+ appliedCount += 1;
27964
+ if (cycle["execution_status"] !== COMPLETED_EXECUTION_STATUS) {
27965
+ errors.push(
27966
+ `${prefix} candidate_applied is true but execution_status is not 'complete'.`
27967
+ );
27968
+ }
27969
+ }
27970
+ }
27971
+ if (hasKey(cycle, OPENED_BY_REVIEW_KEY) && !opensRemediationReview(cycle[OPENED_BY_REVIEW_KEY], outcomes)) {
27972
+ errors.push(
27973
+ `${prefix} opened_by_review must reference a review outcome whose verdict is REMEDIATION_REQUIRED.`
27974
+ );
27975
+ }
27976
+ });
27977
+ if (hasKey(loop, COMPLETED_ATTEMPTS_KEY)) {
27978
+ const completedAttempts = loop[COMPLETED_ATTEMPTS_KEY];
27979
+ if (!isStrictInteger(completedAttempts) || completedAttempts < 0) {
27980
+ errors.push(
27981
+ "Checkpoint remediation_loop completed_attempts must be a non-negative integer."
27982
+ );
27983
+ } else if (completedAttempts !== appliedCount) {
27984
+ errors.push(
27985
+ `Checkpoint remediation_loop completed_attempts is ${completedAttempts} but ${appliedCount} cycles have candidate_applied true.`
27986
+ );
27987
+ }
27988
+ }
27989
+ return errors;
27990
+ }
26985
27991
  function validateRemediationLoop(remediationLoop) {
26986
27992
  const errors = [];
26987
- if (!isObject11(remediationLoop)) {
27993
+ if (!isObject12(remediationLoop)) {
26988
27994
  return errors;
26989
27995
  }
26990
27996
  const cycles = remediationLoop[REMEDIATION_CYCLES_KEY];
26991
- if (!Array.isArray(cycles)) {
26992
- return errors;
27997
+ let cycleList = null;
27998
+ if (Array.isArray(cycles)) {
27999
+ cycleList = cycles;
28000
+ cycles.forEach((cycle, index) => {
28001
+ if (!isObject12(cycle)) {
28002
+ errors.push(
28003
+ `Checkpoint remediation cycle #${index} must be an object.`
28004
+ );
28005
+ return;
28006
+ }
28007
+ errors.push(...validateRemediationCycle(index, cycle));
28008
+ });
26993
28009
  }
26994
- cycles.forEach((cycle, index) => {
26995
- if (!isObject11(cycle)) {
26996
- errors.push(`Checkpoint remediation cycle #${index} must be an object.`);
26997
- return;
26998
- }
26999
- errors.push(...validateRemediationCycle(index, cycle));
27000
- });
28010
+ errors.push(...validateReviewOutcomes(remediationLoop));
28011
+ errors.push(...validateRemediationAccounting(remediationLoop, cycleList));
27001
28012
  return errors;
27002
28013
  }
27003
28014
 
@@ -27011,7 +28022,7 @@ var OUT_OF_SCOPE_STEP_KEYS = [
27011
28022
  "step9_status",
27012
28023
  "step10_status"
27013
28024
  ];
27014
- function pythonRepr8(value) {
28025
+ function pythonRepr9(value) {
27015
28026
  if (value === void 0 || value === null) {
27016
28027
  return "None";
27017
28028
  }
@@ -27026,11 +28037,11 @@ function pythonRepr8(value) {
27026
28037
  return String(value);
27027
28038
  }
27028
28039
  if (Array.isArray(value)) {
27029
- return `[${value.map(pythonRepr8).join(", ")}]`;
28040
+ return `[${value.map(pythonRepr9).join(", ")}]`;
27030
28041
  }
27031
28042
  if (typeof value === "object") {
27032
28043
  const entries = Object.entries(value).map(
27033
- ([key, item]) => `${pythonRepr8(key)}: ${pythonRepr8(item)}`
28044
+ ([key, item]) => `${pythonRepr9(key)}: ${pythonRepr9(item)}`
27034
28045
  );
27035
28046
  return `{${entries.join(", ")}}`;
27036
28047
  }
@@ -27044,13 +28055,13 @@ function validatePreparationTerminalContract(state) {
27044
28055
  const errors = [];
27045
28056
  if (state["next_step"] !== EXPECTED_NEXT_STEP) {
27046
28057
  errors.push(
27047
- `Preparation checkpoint next_step must be ${pythonRepr8(EXPECTED_NEXT_STEP)}, found ${pythonRepr8(state["next_step"])}.`
28058
+ `Preparation checkpoint next_step must be ${pythonRepr9(EXPECTED_NEXT_STEP)}, found ${pythonRepr9(state["next_step"])}.`
27048
28059
  );
27049
28060
  }
27050
28061
  for (const key of OUT_OF_SCOPE_STEP_KEYS) {
27051
28062
  if (state[key] !== "not-applicable") {
27052
28063
  errors.push(
27053
- `Preparation checkpoint ${key} must be 'not-applicable', found ${pythonRepr8(state[key])}.`
28064
+ `Preparation checkpoint ${key} must be 'not-applicable', found ${pythonRepr9(state[key])}.`
27054
28065
  );
27055
28066
  }
27056
28067
  }
@@ -27066,18 +28077,18 @@ var DELEGATING_AGENTS = /* @__PURE__ */ new Set([
27066
28077
  "prd-feature",
27067
28078
  "pr-author"
27068
28079
  ]);
27069
- function isObject12(value) {
28080
+ function isObject13(value) {
27070
28081
  return typeof value === "object" && value !== null && !Array.isArray(value);
27071
28082
  }
27072
28083
  function delegatedAgents(state) {
27073
28084
  const agents = /* @__PURE__ */ new Set();
27074
28085
  let receipts = state["delegation_receipts"];
27075
- if (isObject12(receipts)) {
28086
+ if (isObject13(receipts)) {
27076
28087
  receipts = receipts["agents"];
27077
28088
  }
27078
28089
  if (Array.isArray(receipts)) {
27079
28090
  for (const receipt of receipts) {
27080
- if (!isObject12(receipt)) {
28091
+ if (!isObject13(receipt)) {
27081
28092
  continue;
27082
28093
  }
27083
28094
  const agentName = receipt["agent_name"];
@@ -27101,7 +28112,7 @@ function validateModelRoutingExistence(state) {
27101
28112
  const receipts = state["model_routing_receipts"];
27102
28113
  if (Array.isArray(receipts)) {
27103
28114
  for (const receipt of receipts) {
27104
- if (!isObject12(receipt)) {
28115
+ if (!isObject13(receipt)) {
27105
28116
  continue;
27106
28117
  }
27107
28118
  const agent = receipt["agent"];
@@ -27150,15 +28161,6 @@ var VALID_STEP_STATUS = /* @__PURE__ */ new Set([
27150
28161
  "in_progress",
27151
28162
  "completed"
27152
28163
  ]);
27153
- var VALID_BLOCKED_REASONS = /* @__PURE__ */ new Set([
27154
- "none",
27155
- "spawn_agent_unavailable",
27156
- "delegation_launch_failed",
27157
- "delegate_no_receipt",
27158
- "delegate_contract_incomplete",
27159
- "validator_failed",
27160
- "user_requested_stop"
27161
- ]);
27162
28164
  var REQUIRED_RECEIPT_KEYS = [
27163
28165
  "step",
27164
28166
  "agent_name",
@@ -27198,13 +28200,13 @@ function isValidStepStatus(key, value) {
27198
28200
  }
27199
28201
  return STEP_SPECIFIC_EXTRA_STATUS.get(key)?.has(value) === true;
27200
28202
  }
27201
- function isObject13(value) {
28203
+ function isObject14(value) {
27202
28204
  return typeof value === "object" && value !== null && !Array.isArray(value);
27203
28205
  }
27204
28206
  function validateListDelegationReceipts(receipts) {
27205
28207
  const errors = [];
27206
28208
  receipts.forEach((receipt, index) => {
27207
- if (!isObject13(receipt)) {
28209
+ if (!isObject14(receipt)) {
27208
28210
  errors.push(`Checkpoint delegation receipt #${index} must be an object.`);
27209
28211
  return;
27210
28212
  }
@@ -27246,7 +28248,7 @@ function validateNamespacedDelegationReceipts(receipts) {
27246
28248
  if (promotionReceipts === void 0 || promotionReceipts === null) {
27247
28249
  return errors;
27248
28250
  }
27249
- if (!isObject13(promotionReceipts)) {
28251
+ if (!isObject14(promotionReceipts)) {
27250
28252
  errors.push(
27251
28253
  "Checkpoint delegation_receipts.promotion must be an object namespace."
27252
28254
  );
@@ -27281,7 +28283,7 @@ function validateOrchestratorStateText(text2, options = {}) {
27281
28283
  `Checkpoint is not valid JSON: ${exc instanceof Error ? exc.message : String(exc)}`
27282
28284
  ];
27283
28285
  }
27284
- if (!isObject13(state)) {
28286
+ if (!isObject14(state)) {
27285
28287
  return ["Checkpoint root must be a JSON object."];
27286
28288
  }
27287
28289
  const stateMap = state;
@@ -27306,7 +28308,7 @@ function validateOrchestratorStateText(text2, options = {}) {
27306
28308
  if (receipts !== void 0 && receipts !== null) {
27307
28309
  if (Array.isArray(receipts)) {
27308
28310
  errors.push(...validateListDelegationReceipts(receipts));
27309
- } else if (isObject13(receipts)) {
28311
+ } else if (isObject14(receipts)) {
27310
28312
  errors.push(...validateNamespacedDelegationReceipts(receipts));
27311
28313
  } else {
27312
28314
  errors.push(
@@ -28343,10 +29345,10 @@ var PROHIBITED_ANY_LEVEL_KEYS = words(
28343
29345
  "depends_on integration_branch epic_merge_pr"
28344
29346
  );
28345
29347
  var ROOT_PATH = "<root>";
28346
- function isObject14(value) {
29348
+ function isObject15(value) {
28347
29349
  return typeof value === "object" && value !== null && !Array.isArray(value);
28348
29350
  }
28349
- function pythonRepr9(value) {
29351
+ function pythonRepr10(value) {
28350
29352
  if (value === null || value === void 0) {
28351
29353
  return "None";
28352
29354
  }
@@ -28357,18 +29359,18 @@ function pythonRepr9(value) {
28357
29359
  return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
28358
29360
  }
28359
29361
  if (Array.isArray(value)) {
28360
- return `[${value.map(pythonRepr9).join(", ")}]`;
29362
+ return `[${value.map(pythonRepr10).join(", ")}]`;
28361
29363
  }
28362
- if (isObject14(value)) {
29364
+ if (isObject15(value)) {
28363
29365
  const pairs = Object.entries(value).map(
28364
- ([key, item]) => `${pythonRepr9(key)}: ${pythonRepr9(item)}`
29366
+ ([key, item]) => `${pythonRepr10(key)}: ${pythonRepr10(item)}`
28365
29367
  );
28366
29368
  return `{${pairs.join(", ")}}`;
28367
29369
  }
28368
29370
  return String(value);
28369
29371
  }
28370
29372
  function pythonStr2(value) {
28371
- return typeof value === "string" ? value : pythonRepr9(value);
29373
+ return typeof value === "string" ? value : pythonRepr10(value);
28372
29374
  }
28373
29375
  function isEnumMember(members, value) {
28374
29376
  return typeof value === "string" && members.includes(value);
@@ -28385,25 +29387,25 @@ function isPositiveInteger(value) {
28385
29387
  function isNonNegativeInteger(value) {
28386
29388
  return isIntegral(value) && value >= 0;
28387
29389
  }
28388
- function isStringList(value) {
29390
+ function isStringList2(value) {
28389
29391
  return Array.isArray(value) && value.every(isNonEmptyString2);
28390
29392
  }
28391
29393
  function inBoundedRange(value, minimum, maximum) {
28392
29394
  return isIntegral(value) && value >= minimum && value <= maximum;
28393
29395
  }
28394
29396
  function enumError(context, field, members, value) {
28395
- return `${context} ${field} must be one of ${members.join(", ")}; found: ${pythonRepr9(value)}.`;
29397
+ return `${context} ${field} must be one of ${members.join(", ")}; found: ${pythonRepr10(value)}.`;
28396
29398
  }
28397
29399
  function itemContext(context, index) {
28398
29400
  return `${context} items[${index}]`;
28399
29401
  }
28400
29402
  function validateBlastRadiusBlock(radius, context) {
28401
- if (!isObject14(radius)) {
29403
+ if (!isObject15(radius)) {
28402
29404
  return [`${context} blast_radius must be an object.`];
28403
29405
  }
28404
29406
  const errors = [];
28405
29407
  for (const field of BLAST_RADIUS_LIST_FIELDS) {
28406
- if (!isStringList(radius[field])) {
29408
+ if (!isStringList2(radius[field])) {
28407
29409
  errors.push(
28408
29410
  `${context} blast_radius.${field} must be a list of non-empty strings.`
28409
29411
  );
@@ -28434,25 +29436,25 @@ function validateMergeStatus(record3, context, state) {
28434
29436
  }
28435
29437
  if (isEnumMember(MERGED_MERGE_STATUSES, mergeStatus) && state !== "merged") {
28436
29438
  return [
28437
- `${context} merge_status ${pythonRepr9(mergeStatus)} requires state 'merged'; found: ${pythonRepr9(state)}.`
29439
+ `${context} merge_status ${pythonRepr10(mergeStatus)} requires state 'merged'; found: ${pythonRepr10(state)}.`
28438
29440
  ];
28439
29441
  }
28440
29442
  if (isEnumMember(BLOCKED_MERGE_STATUSES, mergeStatus) && state !== "blocked") {
28441
29443
  return [
28442
- `${context} merge_status ${pythonRepr9(mergeStatus)} requires state 'blocked'; found: ${pythonRepr9(state)}.`
29444
+ `${context} merge_status ${pythonRepr10(mergeStatus)} requires state 'blocked'; found: ${pythonRepr10(state)}.`
28443
29445
  ];
28444
29446
  }
28445
29447
  return [];
28446
29448
  }
28447
29449
  function validateItemRecord(item, context, requireKind = false) {
28448
- if (!isObject14(item)) {
29450
+ if (!isObject15(item)) {
28449
29451
  return [`${context} must be an object.`];
28450
29452
  }
28451
29453
  const errors = [];
28452
29454
  const issueNum = item["issue_num"];
28453
29455
  if (!isPositiveInteger(issueNum)) {
28454
29456
  errors.push(
28455
- `${context} issue_num must be a positive integer; found: ${pythonRepr9(issueNum)}.`
29457
+ `${context} issue_num must be a positive integer; found: ${pythonRepr10(issueNum)}.`
28456
29458
  );
28457
29459
  }
28458
29460
  if (!isNonEmptyString2(item["feature_folder"])) {
@@ -28483,7 +29485,7 @@ function validateItems(items, context, requireKind = false) {
28483
29485
  errors.push(
28484
29486
  ...validateItemRecord(entry, itemContext(context, index), requireKind)
28485
29487
  );
28486
- if (!isObject14(entry)) {
29488
+ if (!isObject15(entry)) {
28487
29489
  return;
28488
29490
  }
28489
29491
  const issueNum = entry["issue_num"];
@@ -28502,7 +29504,7 @@ function validateItems(items, context, requireKind = false) {
28502
29504
  }
28503
29505
  function prohibitedKeyErrors(value, path16, context, keys) {
28504
29506
  const errors = [];
28505
- if (isObject14(value)) {
29507
+ if (isObject15(value)) {
28506
29508
  for (const [key, child] of Object.entries(value)) {
28507
29509
  if (keys.includes(key)) {
28508
29510
  errors.push(`${context} carries prohibited key '${key}' at ${path16}.`);
@@ -28521,7 +29523,7 @@ function prohibitedKeyErrors(value, path16, context, keys) {
28521
29523
  }
28522
29524
  function scanProhibitedKeys(root, context, deepKeys = PROHIBITED_ANY_LEVEL_KEYS, topLevelKeys = []) {
28523
29525
  const errors = prohibitedKeyErrors(root, ROOT_PATH, context, deepKeys);
28524
- if (isObject14(root) && topLevelKeys.length > 0) {
29526
+ if (isObject15(root) && topLevelKeys.length > 0) {
28525
29527
  for (const key of topLevelKeys) {
28526
29528
  if (key in root) {
28527
29529
  errors.push(
@@ -28537,7 +29539,7 @@ function scanProhibitedKeys(root, context, deepKeys = PROHIBITED_ANY_LEVEL_KEYS,
28537
29539
  var VIOLATION_PREFIX = "PARALLEL_COHORT_BARRIER_VIOLATION";
28538
29540
  var ITEM_START_TIMESTAMP_FIELD = "worktree_created_at";
28539
29541
  var MERGE_CONFIRMATION_TIMESTAMP_FIELD = "merged_at";
28540
- var NOT_STARTED_MERGE_STATUS = "not_started";
29542
+ var NOT_STARTED_MERGE_STATUS2 = "not_started";
28541
29543
  var GATING_KEYS = ["conflict_edges", "cohorts"];
28542
29544
  var FOLDER_HINT_PREFIXES = [
28543
29545
  "docs/features/active/",
@@ -28560,7 +29562,7 @@ function buildReferenceIndex(items) {
28560
29562
  return { records, byFolderHint };
28561
29563
  }
28562
29564
  for (const entry of items) {
28563
- if (!isObject14(entry)) {
29565
+ if (!isObject15(entry)) {
28564
29566
  continue;
28565
29567
  }
28566
29568
  const issueNum = entry["issue_num"];
@@ -28595,7 +29597,7 @@ function cohortIndexByItem(cohorts, recolorGeneration, references) {
28595
29597
  return assignments;
28596
29598
  }
28597
29599
  for (const entry of cohorts) {
28598
- if (!isObject14(entry) || entry["generation"] !== recolorGeneration) {
29600
+ if (!isObject15(entry) || entry["generation"] !== recolorGeneration) {
28599
29601
  continue;
28600
29602
  }
28601
29603
  const cohortIndex = entry["index"];
@@ -28612,13 +29614,13 @@ function cohortIndexByItem(cohorts, recolorGeneration, references) {
28612
29614
  }
28613
29615
  return assignments;
28614
29616
  }
28615
- function hasStarted(record3) {
29617
+ function hasStarted2(record3) {
28616
29618
  const start = record3[ITEM_START_TIMESTAMP_FIELD];
28617
29619
  if (typeof start === "string" && start.trim().length > 0) {
28618
29620
  return true;
28619
29621
  }
28620
29622
  const mergeStatus = record3["merge_status"];
28621
- return typeof mergeStatus === "string" && mergeStatus !== NOT_STARTED_MERGE_STATUS;
29623
+ return typeof mergeStatus === "string" && mergeStatus !== NOT_STARTED_MERGE_STATUS2;
28622
29624
  }
28623
29625
  function satisfiesBarrier(record3) {
28624
29626
  return isEnumMember(MERGED_MERGE_STATUSES, record3["merge_status"]);
@@ -28646,7 +29648,7 @@ function violationEndpoints(first, second, assignments, records) {
28646
29648
  if (earlier === void 0 || later === void 0) {
28647
29649
  return null;
28648
29650
  }
28649
- const statusViolation = hasStarted(later) && !satisfiesBarrier(earlier);
29651
+ const statusViolation = hasStarted2(later) && !satisfiesBarrier(earlier);
28650
29652
  if (statusViolation || mergeConfirmedAfterStart(earlier, later)) {
28651
29653
  return [earlierKey, laterKey];
28652
29654
  }
@@ -28668,7 +29670,7 @@ function validateCohortBarrierOrdering(state) {
28668
29670
  );
28669
29671
  const errors = [];
28670
29672
  for (const entry of edges) {
28671
- if (!isObject14(entry)) {
29673
+ if (!isObject15(entry)) {
28672
29674
  continue;
28673
29675
  }
28674
29676
  const first = resolveReference(entry["a"], references);
@@ -28706,14 +29708,14 @@ function validateMutationItemKey(entry, entryContext, op, issueNums) {
28706
29708
  if (op === "close") {
28707
29709
  if (!isNone(itemKey)) {
28708
29710
  return [
28709
- `${entryContext} item_key must be null for op 'close'; found: ${pythonRepr9(itemKey)}.`
29711
+ `${entryContext} item_key must be null for op 'close'; found: ${pythonRepr10(itemKey)}.`
28710
29712
  ];
28711
29713
  }
28712
29714
  return [];
28713
29715
  }
28714
29716
  if (isEnumMember(OPS_REQUIRING_ITEM_KEY, op) && !resolves(itemKey, issueNums)) {
28715
29717
  return [
28716
- `${entryContext} item_key ${pythonRepr9(itemKey)} does not resolve to an items[].issue_num.`
29718
+ `${entryContext} item_key ${pythonRepr10(itemKey)} does not resolve to an items[].issue_num.`
28717
29719
  ];
28718
29720
  }
28719
29721
  return [];
@@ -28725,12 +29727,12 @@ function validateMutationStateField(entry, entryContext, field, op, nullOps) {
28725
29727
  }
28726
29728
  if (isEnumMember(nullOps, op)) {
28727
29729
  return [
28728
- `${entryContext} ${field} must be null for op ${pythonRepr9(op)}; found: ${pythonRepr9(value)}.`
29730
+ `${entryContext} ${field} must be null for op ${pythonRepr10(op)}; found: ${pythonRepr10(value)}.`
28729
29731
  ];
28730
29732
  }
28731
29733
  if (!isEnumMember(VALID_ITEM_STATES, value)) {
28732
29734
  return [
28733
- `${entryContext} ${field} must be null or one of ${VALID_ITEM_STATES.join(", ")}; found: ${pythonRepr9(value)}.`
29735
+ `${entryContext} ${field} must be null or one of ${VALID_ITEM_STATES.join(", ")}; found: ${pythonRepr10(value)}.`
28734
29736
  ];
28735
29737
  }
28736
29738
  return [];
@@ -28740,14 +29742,14 @@ function validateMutationDisposition(entry, entryContext, op) {
28740
29742
  if (op === "remove" && entry["prior_state"] === "in_flight") {
28741
29743
  if (!isEnumMember(VALID_DISPOSITIONS, disposition)) {
28742
29744
  return [
28743
- `${entryContext} disposition must be one of ${VALID_DISPOSITIONS.join(", ")} for an in-flight removal; found: ${pythonRepr9(disposition)}.`
29745
+ `${entryContext} disposition must be one of ${VALID_DISPOSITIONS.join(", ")} for an in-flight removal; found: ${pythonRepr10(disposition)}.`
28744
29746
  ];
28745
29747
  }
28746
29748
  return [];
28747
29749
  }
28748
29750
  if (!isNone(disposition)) {
28749
29751
  return [
28750
- `${entryContext} disposition must be null unless op is 'remove' with prior_state 'in_flight'; found: ${pythonRepr9(disposition)}.`
29752
+ `${entryContext} disposition must be null unless op is 'remove' with prior_state 'in_flight'; found: ${pythonRepr10(disposition)}.`
28751
29753
  ];
28752
29754
  }
28753
29755
  return [];
@@ -28756,7 +29758,7 @@ function validateMutationGeneration(entry, entryContext, recolorGeneration) {
28756
29758
  const generation = entry["recolor_generation"];
28757
29759
  if (!isNonNegativeInteger(generation)) {
28758
29760
  return [
28759
- `${entryContext} recolor_generation must be a non-negative integer; found: ${pythonRepr9(generation)}.`
29761
+ `${entryContext} recolor_generation must be a non-negative integer; found: ${pythonRepr10(generation)}.`
28760
29762
  ];
28761
29763
  }
28762
29764
  if (isNonNegativeInteger(recolorGeneration) && typeof generation === "number" && typeof recolorGeneration === "number" && generation > recolorGeneration) {
@@ -28773,7 +29775,7 @@ function validateMutations(mutations, issueNums, recolorGeneration, context) {
28773
29775
  const errors = [];
28774
29776
  mutations.forEach((entry, position) => {
28775
29777
  const entryContext = `${context} mutations[${position}]`;
28776
- if (!isObject14(entry)) {
29778
+ if (!isObject15(entry)) {
28777
29779
  errors.push(`${entryContext} must be an object.`);
28778
29780
  return;
28779
29781
  }
@@ -28817,25 +29819,25 @@ function validateDriftEvents(events, issueNums, context) {
28817
29819
  const errors = [];
28818
29820
  events.forEach((entry, position) => {
28819
29821
  const entryContext = `${context} drift_events[${position}]`;
28820
- if (!isObject14(entry)) {
29822
+ if (!isObject15(entry)) {
28821
29823
  errors.push(`${entryContext} must be an object.`);
28822
29824
  return;
28823
29825
  }
28824
29826
  const itemKey = entry["item_key"];
28825
29827
  if (!resolves(itemKey, issueNums)) {
28826
29828
  errors.push(
28827
- `${entryContext} item_key ${pythonRepr9(itemKey)} does not resolve to an items[].issue_num.`
29829
+ `${entryContext} item_key ${pythonRepr10(itemKey)} does not resolve to an items[].issue_num.`
28828
29830
  );
28829
29831
  }
28830
29832
  for (const field of ["declared", "observed"]) {
28831
- if (!isStringList(entry[field])) {
29833
+ if (!isStringList2(entry[field])) {
28832
29834
  errors.push(
28833
29835
  `${entryContext} ${field} must be a list of non-empty strings.`
28834
29836
  );
28835
29837
  }
28836
29838
  }
28837
29839
  const escapedPaths = entry["escaped_paths"];
28838
- if (!isStringList(escapedPaths) || !Array.isArray(escapedPaths) || escapedPaths.length === 0) {
29840
+ if (!isStringList2(escapedPaths) || !Array.isArray(escapedPaths) || escapedPaths.length === 0) {
28839
29841
  errors.push(
28840
29842
  `${entryContext} escaped_paths must be a non-empty list of non-empty strings.`
28841
29843
  );
@@ -28862,7 +29864,7 @@ function collectItemKeysAndStates(items) {
28862
29864
  return pairs;
28863
29865
  }
28864
29866
  for (const entry of items) {
28865
- if (!isObject14(entry)) {
29867
+ if (!isObject15(entry)) {
28866
29868
  continue;
28867
29869
  }
28868
29870
  const issueNum = entry["issue_num"];
@@ -28880,13 +29882,13 @@ function validateCohortEntry(entry, entryContext, issueNums, recolorGeneration)
28880
29882
  const index = entry["index"];
28881
29883
  if (!isNonNegativeInteger(index)) {
28882
29884
  errors.push(
28883
- `${entryContext} index must be a non-negative integer; found: ${pythonRepr9(index)}.`
29885
+ `${entryContext} index must be a non-negative integer; found: ${pythonRepr10(index)}.`
28884
29886
  );
28885
29887
  }
28886
29888
  const generation = entry["generation"];
28887
29889
  if (!isNonNegativeInteger(generation)) {
28888
29890
  errors.push(
28889
- `${entryContext} generation must be a non-negative integer; found: ${pythonRepr9(generation)}.`
29891
+ `${entryContext} generation must be a non-negative integer; found: ${pythonRepr10(generation)}.`
28890
29892
  );
28891
29893
  } else if (recolorGeneration !== null && typeof generation === "number" && generation > recolorGeneration) {
28892
29894
  errors.push(
@@ -28901,7 +29903,7 @@ function validateCohortEntry(entry, entryContext, issueNums, recolorGeneration)
28901
29903
  for (const key of itemKeys) {
28902
29904
  if (typeof key !== "number" || !Number.isInteger(key) || !issueNums.has(key)) {
28903
29905
  errors.push(
28904
- `${entryContext} item_keys entry ${pythonRepr9(key)} does not resolve to an items[].issue_num.`
29906
+ `${entryContext} item_keys entry ${pythonRepr10(key)} does not resolve to an items[].issue_num.`
28905
29907
  );
28906
29908
  }
28907
29909
  }
@@ -28912,7 +29914,7 @@ function validateCohortShapes(cohorts, issueNums, recolorGeneration, context) {
28912
29914
  const generationOk = isNonNegativeInteger(recolorGeneration);
28913
29915
  if (!generationOk) {
28914
29916
  errors.push(
28915
- `${context} recolor_generation must be a non-negative integer; found: ${pythonRepr9(recolorGeneration)}.`
29917
+ `${context} recolor_generation must be a non-negative integer; found: ${pythonRepr10(recolorGeneration)}.`
28916
29918
  );
28917
29919
  }
28918
29920
  if (!Array.isArray(cohorts)) {
@@ -28922,7 +29924,7 @@ function validateCohortShapes(cohorts, issueNums, recolorGeneration, context) {
28922
29924
  const bound = generationOk && typeof recolorGeneration === "number" ? recolorGeneration : null;
28923
29925
  cohorts.forEach((entry, position) => {
28924
29926
  const entryContext = `${context} cohorts[${position}]`;
28925
- if (!isObject14(entry)) {
29927
+ if (!isObject15(entry)) {
28926
29928
  errors.push(`${entryContext} must be an object.`);
28927
29929
  return;
28928
29930
  }
@@ -28934,7 +29936,7 @@ function currentGenerationCohorts(cohorts, recolorGeneration) {
28934
29936
  if (!isNonNegativeInteger(recolorGeneration) || !Array.isArray(cohorts)) {
28935
29937
  return [];
28936
29938
  }
28937
- return cohorts.filter(isObject14).filter((entry) => entry["generation"] === recolorGeneration);
29939
+ return cohorts.filter(isObject15).filter((entry) => entry["generation"] === recolorGeneration);
28938
29940
  }
28939
29941
  function validateCurrentGenerationCohorts(cohorts, items, recolorGeneration, context) {
28940
29942
  const current = currentGenerationCohorts(cohorts, recolorGeneration);
@@ -28979,7 +29981,7 @@ function validateCurrentGenerationCohorts(cohorts, items, recolorGeneration, con
28979
29981
  continue;
28980
29982
  }
28981
29983
  errors.push(
28982
- `${context} item ${issueNum} in state ${pythonRepr9(state)} must appear in exactly one current-generation cohort; found ${count}.`
29984
+ `${context} item ${issueNum} in state ${pythonRepr10(state)} must appear in exactly one current-generation cohort; found ${count}.`
28983
29985
  );
28984
29986
  }
28985
29987
  return errors;
@@ -28987,7 +29989,7 @@ function validateCurrentGenerationCohorts(cohorts, items, recolorGeneration, con
28987
29989
  function validateCurrentCohortBound(currentCohort, cohorts, recolorGeneration, context) {
28988
29990
  if (!isNonNegativeInteger(currentCohort) || typeof currentCohort !== "number") {
28989
29991
  return [
28990
- `${context} current_cohort must be a non-negative integer; found: ${pythonRepr9(currentCohort)}.`
29992
+ `${context} current_cohort must be a non-negative integer; found: ${pythonRepr10(currentCohort)}.`
28991
29993
  ];
28992
29994
  }
28993
29995
  const indices = [];
@@ -29017,7 +30019,7 @@ function validateEdgeEndpoints(entry, entryContext, issueNums) {
29017
30019
  endpoints.set(field, value);
29018
30020
  } else {
29019
30021
  errors.push(
29020
- `${entryContext} ${field} ${pythonRepr9(value)} does not resolve to an items[].issue_num.`
30022
+ `${entryContext} ${field} ${pythonRepr10(value)} does not resolve to an items[].issue_num.`
29021
30023
  );
29022
30024
  }
29023
30025
  }
@@ -29047,7 +30049,7 @@ function validateConflictEdges(edges, issueNums, context) {
29047
30049
  const duplicates = /* @__PURE__ */ new Map();
29048
30050
  edges.forEach((entry, position) => {
29049
30051
  const entryContext = `${context} conflict_edges[${position}]`;
29050
- if (!isObject14(entry)) {
30052
+ if (!isObject15(entry)) {
29051
30053
  errors.push(`${entryContext} must be an object.`);
29052
30054
  return;
29053
30055
  }
@@ -29126,7 +30128,7 @@ function validateIdentity(state) {
29126
30128
  const routeId = state["route_id"];
29127
30129
  if ("route_id" in state && routeId !== EXPECTED_ROUTE_ID2) {
29128
30130
  errors.push(
29129
- `${CONTEXT} route_id must be '${EXPECTED_ROUTE_ID2}'; found: ${pythonRepr9(routeId)}.`
30131
+ `${CONTEXT} route_id must be '${EXPECTED_ROUTE_ID2}'; found: ${pythonRepr10(routeId)}.`
29130
30132
  );
29131
30133
  }
29132
30134
  const mode = state["mode"];
@@ -29136,7 +30138,7 @@ function validateIdentity(state) {
29136
30138
  const concurrency = state["max_concurrency"];
29137
30139
  if ("max_concurrency" in state && !inBoundedRange(concurrency, MIN_CONCURRENCY, MAX_CONCURRENCY)) {
29138
30140
  errors.push(
29139
- `${CONTEXT} max_concurrency must be an integer from ${MIN_CONCURRENCY} through ${MAX_CONCURRENCY}; found: ${pythonRepr9(concurrency)}.`
30141
+ `${CONTEXT} max_concurrency must be an integer from ${MIN_CONCURRENCY} through ${MAX_CONCURRENCY}; found: ${pythonRepr10(concurrency)}.`
29140
30142
  );
29141
30143
  }
29142
30144
  return errors;
@@ -29192,7 +30194,7 @@ function recordsCloseMutation(state) {
29192
30194
  return false;
29193
30195
  }
29194
30196
  return mutations.some(
29195
- (entry) => isObject14(entry) && entry["op"] === "close"
30197
+ (entry) => isObject15(entry) && entry["op"] === "close"
29196
30198
  );
29197
30199
  }
29198
30200
  function validateCompletion2(state) {
@@ -29200,7 +30202,7 @@ function validateCompletion2(state) {
29200
30202
  const items = state["items"];
29201
30203
  const entries = Array.isArray(items) ? items : [];
29202
30204
  entries.forEach((entry, index) => {
29203
- if (!isObject14(entry)) {
30205
+ if (!isObject15(entry)) {
29204
30206
  return;
29205
30207
  }
29206
30208
  if (entry["state"] === "withdrawn") {
@@ -29209,7 +30211,7 @@ function validateCompletion2(state) {
29209
30211
  const mergeStatus = entry["merge_status"];
29210
30212
  if (!isEnumMember(MERGED_MERGE_STATUSES, mergeStatus)) {
29211
30213
  errors.push(
29212
- `${itemContext(CONTEXT, index)} completion validation failed: merge_status is not merged or worktree_removed; found: ${pythonRepr9(mergeStatus)}.`
30214
+ `${itemContext(CONTEXT, index)} completion validation failed: merge_status is not merged or worktree_removed; found: ${pythonRepr10(mergeStatus)}.`
29213
30215
  );
29214
30216
  }
29215
30217
  });
@@ -29228,7 +30230,7 @@ function validateParallelOrchestratorStateText(text2, options = {}) {
29228
30230
  const detail = error2 instanceof Error ? error2.message : String(error2);
29229
30231
  return [`${CONTEXT} is not valid JSON: ${detail}.`];
29230
30232
  }
29231
- if (!isObject14(state)) {
30233
+ if (!isObject15(state)) {
29232
30234
  return [`${CONTEXT} root must be a JSON object.`];
29233
30235
  }
29234
30236
  const errors = [];
@@ -29306,7 +30308,7 @@ function validateIdentity2(state) {
29306
30308
  const concurrency = state["max_concurrency"];
29307
30309
  if ("max_concurrency" in state && !inBoundedRange(concurrency, MIN_CONCURRENCY2, MAX_CONCURRENCY2)) {
29308
30310
  errors.push(
29309
- `${CONTEXT2} max_concurrency must be an integer from ${MIN_CONCURRENCY2} through ${MAX_CONCURRENCY2}; found: ${pythonRepr9(concurrency)}.`
30311
+ `${CONTEXT2} max_concurrency must be an integer from ${MIN_CONCURRENCY2} through ${MAX_CONCURRENCY2}; found: ${pythonRepr10(concurrency)}.`
29310
30312
  );
29311
30313
  }
29312
30314
  return errors;
@@ -29317,7 +30319,7 @@ function itemRecords(items) {
29317
30319
  }
29318
30320
  const records = [];
29319
30321
  items.forEach((entry, index) => {
29320
- if (isObject14(entry)) {
30322
+ if (isObject15(entry)) {
29321
30323
  records.push({ index, record: entry });
29322
30324
  }
29323
30325
  });
@@ -29386,13 +30388,13 @@ function validateReadyItem(record3, entryContext) {
29386
30388
  const preparationStatus = record3["preparation_status"];
29387
30389
  if (preparationStatus !== READY_PREPARATION_STATUS) {
29388
30390
  errors.push(
29389
- `${entryContext} preparation_status must be ${pythonRepr9(READY_PREPARATION_STATUS)}; found: ${pythonRepr9(preparationStatus)}.`
30391
+ `${entryContext} preparation_status must be ${pythonRepr10(READY_PREPARATION_STATUS)}; found: ${pythonRepr10(preparationStatus)}.`
29390
30392
  );
29391
30393
  }
29392
30394
  const preflightStatus = record3["preflight_status"];
29393
30395
  if (preflightStatus !== READY_PREFLIGHT_STATUS) {
29394
30396
  errors.push(
29395
- `${entryContext} preflight_status must be ${pythonRepr9(READY_PREFLIGHT_STATUS)}; found: ${pythonRepr9(preflightStatus)}.`
30397
+ `${entryContext} preflight_status must be ${pythonRepr10(READY_PREFLIGHT_STATUS)}; found: ${pythonRepr10(preflightStatus)}.`
29396
30398
  );
29397
30399
  }
29398
30400
  for (const key of READY_ITEM_PATH_KEYS) {
@@ -29401,10 +30403,10 @@ function validateReadyItem(record3, entryContext) {
29401
30403
  }
29402
30404
  }
29403
30405
  const radius = record3["blast_radius"];
29404
- const source = isObject14(radius) ? radius["source"] : null;
30406
+ const source = isObject15(radius) ? radius["source"] : null;
29405
30407
  if (source !== READY_RADIUS_SOURCE) {
29406
30408
  errors.push(
29407
- `${entryContext} blast_radius.source must be ${pythonRepr9(READY_RADIUS_SOURCE)} for execution readiness; found: ${pythonRepr9(source)}.`
30409
+ `${entryContext} blast_radius.source must be ${pythonRepr10(READY_RADIUS_SOURCE)} for execution readiness; found: ${pythonRepr10(source)}.`
29408
30410
  );
29409
30411
  }
29410
30412
  return errors;
@@ -29426,14 +30428,14 @@ function validateReadyGate(state) {
29426
30428
  const nextStep = state["next_step"];
29427
30429
  if (nextStep !== READY_NEXT_STEP2) {
29428
30430
  errors.push(
29429
- `${CONTEXT2} next_step must be ${pythonRepr9(READY_NEXT_STEP2)}; found: ${pythonRepr9(nextStep)}.`
30431
+ `${CONTEXT2} next_step must be ${pythonRepr10(READY_NEXT_STEP2)}; found: ${pythonRepr10(nextStep)}.`
29430
30432
  );
29431
30433
  }
29432
30434
  const expectedKickoff = kickoffPathFor(state["parallel_slug"]);
29433
30435
  const kickoffPromptPath = state["kickoff_prompt_path"];
29434
30436
  if (kickoffPromptPath !== expectedKickoff) {
29435
30437
  errors.push(
29436
- `${CONTEXT2} kickoff_prompt_path must be ${pythonRepr9(expectedKickoff)}; found: ${pythonRepr9(kickoffPromptPath)}.`
30438
+ `${CONTEXT2} kickoff_prompt_path must be ${pythonRepr10(expectedKickoff)}; found: ${pythonRepr10(kickoffPromptPath)}.`
29437
30439
  );
29438
30440
  }
29439
30441
  return errors;
@@ -29446,7 +30448,7 @@ function validateParallelPlannerStateText(text2, options = {}) {
29446
30448
  const detail = error2 instanceof Error ? error2.message : String(error2);
29447
30449
  return [`${CONTEXT2} is not valid JSON: ${detail}.`];
29448
30450
  }
29449
- if (!isObject14(state)) {
30451
+ if (!isObject15(state)) {
29450
30452
  return [`${CONTEXT2} root must be a JSON object.`];
29451
30453
  }
29452
30454
  const errors = [];
@@ -33418,7 +34420,7 @@ function resolveReadinessSignal(fs12, featureDir) {
33418
34420
  }
33419
34421
  return [null, null];
33420
34422
  }
33421
- function relativeToPosix6(root, path16) {
34423
+ function relativeToPosix8(root, path16) {
33422
34424
  const normalizedRoot = toPosixPath2(root).replace(/\/+$/u, "");
33423
34425
  const normalized = toPosixPath2(path16);
33424
34426
  if (normalized.startsWith(`${normalizedRoot}/`)) {
@@ -33451,7 +34453,7 @@ function discoverCanonicalEvidenceFiles(fs12, root, feature) {
33451
34453
  for (const pattern of CANONICAL_GLOBS) {
33452
34454
  for (const candidate of fs12.glob(featureRoot, pattern)) {
33453
34455
  if (fs12.isFile(candidate)) {
33454
- discovered.add(relativeToPosix7(normalizedRoot, candidate));
34456
+ discovered.add(relativeToPosix9(normalizedRoot, candidate));
33455
34457
  }
33456
34458
  }
33457
34459
  }
@@ -33544,7 +34546,7 @@ function parseIntegerStrict(value) {
33544
34546
  }
33545
34547
  return Number.parseInt(value, 10);
33546
34548
  }
33547
- function relativeToPosix7(root, absolute) {
34549
+ function relativeToPosix9(root, absolute) {
33548
34550
  const normalized = toPosixPath2(absolute);
33549
34551
  if (normalized.startsWith(`${root}/`)) {
33550
34552
  return normalized.slice(root.length + 1);
@@ -33732,11 +34734,11 @@ function buildContextFiles(params) {
33732
34734
  const contextFiles = [];
33733
34735
  for (const path16 of candidatePaths) {
33734
34736
  if (fs12.exists(path16)) {
33735
- contextFiles.push(relativeToPosix6(root, path16));
34737
+ contextFiles.push(relativeToPosix8(root, path16));
33736
34738
  }
33737
34739
  }
33738
34740
  if (readinessSource !== null) {
33739
- contextFiles.push(relativeToPosix6(root, readinessSource));
34741
+ contextFiles.push(relativeToPosix8(root, readinessSource));
33740
34742
  }
33741
34743
  const evidenceContextFiles = discoverCanonicalEvidenceFiles(
33742
34744
  fs12,
@@ -35261,6 +36263,10 @@ function promotePotential(options) {
35261
36263
  filesystem.ensureDir(promotedDir);
35262
36264
  const destPath = posixJoin(promotedDir, posixBasename(resolved));
35263
36265
  filesystem.move(resolved, destPath);
36266
+ if (!filesystem.exists(destPath)) {
36267
+ emitLine(`Promoted file missing after move: ${destPath}`);
36268
+ return { exitCode: 1, messages };
36269
+ }
35264
36270
  emitLine(`Moved potential file to promoted folder: ${destPath}`);
35265
36271
  return { exitCode: 0, messages, destination: destPath };
35266
36272
  }