@danmoisan/drm-copilot-mcp 1.0.21 → 1.0.22

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 (31) hide show
  1. package/out/mcp-server.js +1624 -190
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/MEMORY.md +5 -1
  4. package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/feedback_commit_push_memory_before_pr.md +48 -2
  5. package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/feedback_no_sendmessage_tool.md +35 -0
  6. package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/feedback_worktree_isolation_branches_from_main.md +45 -0
  7. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +238 -0
  8. package/resources/claude-customizations/.claude/agents/parallel-planner.md +149 -0
  9. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +23 -11
  10. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +259 -0
  11. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +499 -0
  12. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate-helpers.ps1 +302 -0
  13. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +359 -0
  14. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +244 -0
  15. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +379 -0
  16. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConfig.psm1 +491 -0
  17. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +490 -0
  18. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusGlob.psm1 +429 -0
  19. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusValidation.psm1 +366 -0
  20. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +184 -0
  21. package/resources/claude-customizations/.claude/settings.json +25 -0
  22. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +148 -0
  23. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +93 -0
  24. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +960 -0
  25. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +420 -0
  26. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +176 -0
  27. package/resources/claude-customizations/.claude/skills/parallel-run/SKILL.md +56 -0
  28. package/resources/claude-customizations/pack-manifests/core.json +19 -1
  29. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  30. package/resources/config/orchestration-routing.json +22 -0
  31. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +29 -0
package/out/mcp-server.js CHANGED
@@ -1796,13 +1796,13 @@ var require_code2 = __commonJS({
1796
1796
  const valid = gen.name("valid");
1797
1797
  if (it.allErrors) {
1798
1798
  const validArr = gen.let("valid", true);
1799
- validateItems(() => gen.assign(validArr, false));
1799
+ validateItems2(() => gen.assign(validArr, false));
1800
1800
  return validArr;
1801
1801
  }
1802
1802
  gen.var(valid, true);
1803
- validateItems(() => gen.break());
1803
+ validateItems2(() => gen.break());
1804
1804
  return valid;
1805
- function validateItems(notValid) {
1805
+ function validateItems2(notValid) {
1806
1806
  const len = gen.const("len", (0, codegen_1._)`${data}.length`);
1807
1807
  gen.forRange("i", 0, len, (i) => {
1808
1808
  cxt.subschema({
@@ -5246,10 +5246,10 @@ var require_additionalItems = __commonJS({
5246
5246
  cxt.pass((0, codegen_1._)`${len} <= ${items.length}`);
5247
5247
  } else if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it, schema)) {
5248
5248
  const valid = gen.var("valid", (0, codegen_1._)`${len} <= ${items.length}`);
5249
- gen.if((0, codegen_1.not)(valid), () => validateItems(valid));
5249
+ gen.if((0, codegen_1.not)(valid), () => validateItems2(valid));
5250
5250
  cxt.ok(valid);
5251
5251
  }
5252
- function validateItems(valid) {
5252
+ function validateItems2(valid) {
5253
5253
  gen.forRange("i", items.length, len, (i) => {
5254
5254
  cxt.subschema({ keyword, dataProp: i, dataPropType: util_1.Type.Num }, valid);
5255
5255
  if (!it.allErrors)
@@ -5421,7 +5421,7 @@ var require_contains = __commonJS({
5421
5421
  it.items = true;
5422
5422
  const valid = gen.name("valid");
5423
5423
  if (max === void 0 && min === 1) {
5424
- validateItems(valid, () => gen.if(valid, () => gen.break()));
5424
+ validateItems2(valid, () => gen.if(valid, () => gen.break()));
5425
5425
  } else if (min === 0) {
5426
5426
  gen.let(valid, true);
5427
5427
  if (max !== void 0)
@@ -5434,9 +5434,9 @@ var require_contains = __commonJS({
5434
5434
  function validateItemsWithCount() {
5435
5435
  const schValid = gen.name("_valid");
5436
5436
  const count = gen.let("count", 0);
5437
- validateItems(schValid, () => gen.if(schValid, () => checkLimits(count)));
5437
+ validateItems2(schValid, () => gen.if(schValid, () => checkLimits(count)));
5438
5438
  }
5439
- function validateItems(_valid, block) {
5439
+ function validateItems2(_valid, block) {
5440
5440
  gen.forRange("i", 0, len, (i) => {
5441
5441
  cxt.subschema({
5442
5442
  keyword: "contains",
@@ -8990,13 +8990,13 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
8990
8990
  }
8991
8991
  return propValues;
8992
8992
  });
8993
- const isObject14 = isObject;
8993
+ const isObject15 = isObject;
8994
8994
  const catchall = def.catchall;
8995
8995
  let value;
8996
8996
  inst._zod.parse = (payload, ctx) => {
8997
8997
  value ?? (value = _normalized.value);
8998
8998
  const input = payload.value;
8999
- if (!isObject14(input)) {
8999
+ if (!isObject15(input)) {
9000
9000
  payload.issues.push({
9001
9001
  expected: "object",
9002
9002
  code: "invalid_type",
@@ -9123,7 +9123,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
9123
9123
  return (payload, ctx) => fn(shape, payload, ctx);
9124
9124
  };
9125
9125
  let fastpass;
9126
- const isObject14 = isObject;
9126
+ const isObject15 = isObject;
9127
9127
  const jit = !globalConfig.jitless;
9128
9128
  const allowsEval2 = allowsEval;
9129
9129
  const fastEnabled = jit && allowsEval2.value;
@@ -9132,7 +9132,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
9132
9132
  inst._zod.parse = (payload, ctx) => {
9133
9133
  value ?? (value = _normalized.value);
9134
9134
  const input = payload.value;
9135
- if (!isObject14(input)) {
9135
+ if (!isObject15(input)) {
9136
9136
  payload.issues.push({
9137
9137
  expected: "object",
9138
9138
  code: "invalid_type",
@@ -15089,16 +15089,7 @@ var Server = class extends Protocol {
15089
15089
  if (!methodSchema) {
15090
15090
  throw new Error("Schema is missing a method literal");
15091
15091
  }
15092
- let methodValue;
15093
- if (isZ4Schema(methodSchema)) {
15094
- const v4Schema = methodSchema;
15095
- const v4Def = v4Schema._zod?.def;
15096
- methodValue = v4Def?.value ?? v4Schema.value;
15097
- } else {
15098
- const v3Schema = methodSchema;
15099
- const legacyDef = v3Schema._def;
15100
- methodValue = legacyDef?.value ?? v3Schema.value;
15101
- }
15092
+ const methodValue = getLiteralValue(methodSchema);
15102
15093
  if (typeof methodValue !== "string") {
15103
15094
  throw new Error("Schema method literal must be a string");
15104
15095
  }
@@ -15407,8 +15398,17 @@ var Server = class extends Protocol {
15407
15398
  var import_node_process = __toESM(require("node:process"), 1);
15408
15399
 
15409
15400
  // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
15401
+ var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
15410
15402
  var ReadBuffer = class {
15403
+ constructor(options) {
15404
+ this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
15405
+ }
15411
15406
  append(chunk) {
15407
+ const newSize = (this._buffer?.length ?? 0) + chunk.length;
15408
+ if (newSize > this._maxBufferSize) {
15409
+ this.clear();
15410
+ throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
15411
+ }
15412
15412
  this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
15413
15413
  }
15414
15414
  readMessage() {
@@ -15436,18 +15436,24 @@ function serializeMessage(message) {
15436
15436
 
15437
15437
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
15438
15438
  var StdioServerTransport = class {
15439
- constructor(_stdin = import_node_process.default.stdin, _stdout = import_node_process.default.stdout) {
15439
+ constructor(_stdin = import_node_process.default.stdin, _stdout = import_node_process.default.stdout, options) {
15440
15440
  this._stdin = _stdin;
15441
15441
  this._stdout = _stdout;
15442
- this._readBuffer = new ReadBuffer();
15443
15442
  this._started = false;
15444
15443
  this._ondata = (chunk) => {
15445
- this._readBuffer.append(chunk);
15446
- this.processReadBuffer();
15444
+ try {
15445
+ this._readBuffer.append(chunk);
15446
+ this.processReadBuffer();
15447
+ } catch (error2) {
15448
+ this.onerror?.(error2);
15449
+ this.close().catch(() => {
15450
+ });
15451
+ }
15447
15452
  };
15448
15453
  this._onerror = (error2) => {
15449
15454
  this.onerror?.(error2);
15450
15455
  };
15456
+ this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
15451
15457
  }
15452
15458
  /**
15453
15459
  * Starts listening for messages on stdin.
@@ -16262,7 +16268,10 @@ var VALID_ARTIFACT_TYPES = /* @__PURE__ */ new Set([
16262
16268
  "orchestrator-state",
16263
16269
  "epic-orchestrator-state",
16264
16270
  "epic-planner-state",
16265
- "epic-kickoff"
16271
+ "epic-kickoff",
16272
+ "parallel-orchestrator-state",
16273
+ "parallel-planner-state",
16274
+ "parallel-kickoff"
16266
16275
  ]);
16267
16276
  function resolveValidateOrchestrationArtifactsToolInput(rawInput, fallbackWorkspaceRoot) {
16268
16277
  const args = asToolArgumentObject(rawInput);
@@ -17007,7 +17016,10 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
17007
17016
  "orchestrator-state",
17008
17017
  "epic-orchestrator-state",
17009
17018
  "epic-planner-state",
17010
- "epic-kickoff"
17019
+ "epic-kickoff",
17020
+ "parallel-orchestrator-state",
17021
+ "parallel-planner-state",
17022
+ "parallel-kickoff"
17011
17023
  ],
17012
17024
  description: "The type of orchestration artifact to validate."
17013
17025
  },
@@ -17017,7 +17029,7 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
17017
17029
  },
17018
17030
  require_complete: {
17019
17031
  type: "boolean",
17020
- description: "When true and artifact_type is 'orchestrator-state' or 'epic-orchestrator-state', require all phases to be complete."
17032
+ description: "When true and artifact_type is 'orchestrator-state', 'epic-orchestrator-state', or 'parallel-orchestrator-state', require all phases to be complete."
17021
17033
  },
17022
17034
  require_model_routing: {
17023
17035
  type: "boolean",
@@ -17033,7 +17045,7 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
17033
17045
  },
17034
17046
  require_ready_for_execution: {
17035
17047
  type: "boolean",
17036
- description: "When true and artifact_type is 'epic-planner-state', require every child to be prepared and preflight-cleared."
17048
+ description: "When true and artifact_type is 'epic-planner-state' or 'parallel-planner-state', require every child to be prepared and preflight-cleared."
17037
17049
  }
17038
17050
  },
17039
17051
  required: ["workspace_root", "artifact_type", "artifact_path"],
@@ -26088,191 +26100,1599 @@ function validateOrchestratorStateText(text, options = {}) {
26088
26100
  return errors;
26089
26101
  }
26090
26102
 
26091
- // ../../extensions/drm-copilot/src/lib/validate/policy-audit-artifact.ts
26092
- var COVERAGE_PERCENT_RE = /\b\d+(?:\.\d+)?%/;
26093
- var POLICY_AUDIT_REQUIRED_HEADINGS = [
26094
- "## Executive Summary",
26095
- "## 1. General Unit Test Policy Compliance",
26096
- "## 2. General Code Change Policy Compliance",
26097
- "## 3. Language-Specific Code Change Policy Compliance",
26098
- "## 4. Language-Specific Unit Test Policy Compliance",
26099
- "## 5. Test Coverage Detail",
26100
- "## 6. Test Execution Metrics",
26101
- "## 7. Code Quality Checks",
26102
- "## 8. Gaps and Exceptions",
26103
- "## 9. Summary of Changes",
26104
- "## 10. Compliance Verdict",
26105
- "## Appendix A: Test Inventory",
26106
- "## Appendix B: Toolchain Commands Reference"
26107
- ];
26108
- var POLICY_AUDIT_REQUIRED_CHECKLIST_LABELS = [
26109
- "TypeScript baseline coverage artifact:",
26110
- "TypeScript post-change coverage artifact:",
26111
- "PowerShell baseline coverage artifact:",
26112
- "PowerShell post-change coverage artifact:",
26113
- "Per-language comparison summary:"
26114
- ];
26115
- var POLICY_AUDIT_COMPARISON_HEADING = "### 1.2.1 Per-Language Coverage Comparison";
26116
- var PLACEHOLDER_MARKERS = [
26117
- "[n]",
26118
- "[path",
26119
- "[artifact",
26120
- "[section reference",
26121
- "[language]",
26122
- "tbd",
26123
- "unverified",
26124
- "missing"
26125
- ];
26126
- var TEMPLATE_RESOLVER_TOOL = "resolve_policy_audit_template_asset";
26127
- var TEMPLATE_RESOLVER_MISSING_MARKERS = [
26128
- "not exposed",
26129
- "was not exposed",
26130
- "missing resolver exposure",
26131
- "fallback template",
26132
- "fallback-template"
26103
+ // ../../extensions/drm-copilot/src/lib/validate/parallel-kickoff-artifact.ts
26104
+ var KICKOFF_HEADING_RE2 = /^# Parallel Kickoff: (?<slug>[a-z0-9][a-z0-9-]*)$/;
26105
+ var PARALLEL_RUN_RE = /Run `\/parallel-run (?<slug>[a-z0-9][a-z0-9-]*)`/;
26106
+ var MANIFEST_RE2 = /docs\/features\/parallel\/[a-z0-9][a-z0-9-]*\/parallel\.md/;
26107
+ var PLAN_BRANCH_RE = /parallel\/[a-z0-9][a-z0-9-]*-plan/;
26108
+ var RESUME_RE2 = /(?:Every item|Each item|items)\s+resumes?\s+at atomic execution\s+from\s+(?:its|their)\s+committed plan-path\s+on\s+(?:its|their)\s+own\s+(?:pushed\s+)?feature branch/i;
26109
+ var INTEGRITY_COMMIT_RE2 = /^(?:-\s*)?planning_commit:\s*`?(?<commit>[0-9a-fA-F]{7,64})`?\s*$/;
26110
+ var LINE_SPLIT_RE = /\r\n|\r|\n/u;
26111
+ var ITEM_HEADERS = [
26112
+ "issue_num",
26113
+ "feature_folder",
26114
+ "cohort",
26115
+ "complexity",
26116
+ "branch",
26117
+ "plan-path"
26133
26118
  ];
26134
- function hasNumericCoverage(value) {
26135
- return COVERAGE_PERCENT_RE.test(value);
26136
- }
26137
- function isNaValue(value) {
26138
- return value.trim().toLowerCase().startsWith("n/a");
26139
- }
26140
- function hasPlaceholderMarker(value) {
26141
- const lowered = value.toLowerCase();
26142
- return PLACEHOLDER_MARKERS.some((marker) => lowered.includes(marker));
26143
- }
26144
- function reportsMissingTemplateResolverSuccess(text) {
26145
- const lowered = text.toLowerCase();
26146
- if (!lowered.includes(TEMPLATE_RESOLVER_TOOL)) {
26147
- return false;
26148
- }
26149
- if (!TEMPLATE_RESOLVER_MISSING_MARKERS.some((m) => lowered.includes(m))) {
26150
- return false;
26151
- }
26152
- return /\b(pass|ready|readiness)\b/.test(lowered);
26153
- }
26154
- function extractPolicyAuditCoverageRows(text) {
26155
- const rows = [];
26156
- for (const line of text.split("\n")) {
26157
- if (!line.startsWith("|")) {
26158
- continue;
26159
- }
26160
- const segments = line.split("|");
26161
- const cells = segments.slice(1, -1).map((cell) => cell.trim());
26162
- if (cells.length !== 7) {
26163
- continue;
26164
- }
26165
- const language = cells[0] ?? "";
26166
- if (language === "Language" || language === "----------" || !language) {
26119
+ var HASH_HEADERS2 = /* @__PURE__ */ new Set([
26120
+ "plan-hash",
26121
+ "plan_hash",
26122
+ "git-blob-sha",
26123
+ "git_blob_sha"
26124
+ ]);
26125
+ var COMPLEXITY_BANDS2 = /* @__PURE__ */ new Set(["C1", "C2", "C3", "C4"]);
26126
+ function splitSections3(text) {
26127
+ const sections = /* @__PURE__ */ new Map();
26128
+ const errors = [];
26129
+ let current;
26130
+ for (const line of text.split(LINE_SPLIT_RE).slice(1)) {
26131
+ if (line.startsWith("## ")) {
26132
+ current = line.slice(3).trim();
26133
+ if (sections.has(current)) {
26134
+ errors.push(
26135
+ `Parallel kickoff contains duplicate section: ## ${current}`
26136
+ );
26137
+ } else {
26138
+ sections.set(current, []);
26139
+ }
26167
26140
  continue;
26168
26141
  }
26169
- if (language.length > 0 && /^-+$/.test(language)) {
26170
- continue;
26142
+ if (current !== void 0) {
26143
+ sections.get(current)?.push(line);
26171
26144
  }
26172
- rows.push({
26173
- Language: language,
26174
- "Files Changed": cells[1] ?? "",
26175
- Tests: cells[2] ?? "",
26176
- "Test Result": cells[3] ?? "",
26177
- "Baseline Coverage": cells[4] ?? "",
26178
- "Post-Change Coverage": cells[5] ?? "",
26179
- "New Code Coverage": cells[6] ?? ""
26180
- });
26181
26145
  }
26182
- return rows;
26183
- }
26184
- function findPolicyAuditChecklistLine(text, label) {
26185
- for (const line of text.split("\n")) {
26186
- const stripped = line.trim();
26187
- if (stripped.startsWith("- ") && stripped.includes(label)) {
26188
- return stripped;
26146
+ for (const required2 of ["Invocation Prompt", "Item Summary"]) {
26147
+ if (!sections.has(required2)) {
26148
+ errors.push(
26149
+ `Parallel kickoff is missing required section: ## ${required2}`
26150
+ );
26189
26151
  }
26190
26152
  }
26191
- return null;
26153
+ return { sections, errors };
26192
26154
  }
26193
- function extractPolicyAuditComparisonLines(text) {
26194
- let inSection = false;
26195
- const comparisonLines = /* @__PURE__ */ new Map();
26196
- for (const line of text.split("\n")) {
26197
- const stripped = line.trim();
26198
- if (stripped === POLICY_AUDIT_COMPARISON_HEADING) {
26199
- inSection = true;
26200
- continue;
26201
- }
26202
- if (inSection && stripped.startsWith("### ")) {
26203
- break;
26204
- }
26205
- if (!inSection || !stripped.startsWith("- ")) {
26206
- continue;
26207
- }
26208
- const body = stripped.slice(2);
26209
- const colonIndex = body.indexOf(":");
26210
- if (colonIndex === -1) {
26211
- continue;
26212
- }
26213
- const language = body.slice(0, colonIndex);
26214
- comparisonLines.set(language.trim().toLowerCase(), stripped);
26155
+ function parseCells2(line) {
26156
+ const stripped = line.trim();
26157
+ if (!stripped.startsWith("|") || !stripped.endsWith("|")) {
26158
+ return void 0;
26215
26159
  }
26216
- return comparisonLines;
26217
- }
26218
- function escapeRegExp3(value) {
26219
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26160
+ return stripped.slice(1, -1).split("|").map((cell) => cell.trim().replace(/^`|`$/g, ""));
26220
26161
  }
26221
- function comparisonLineHasLabelledPercentage(line, label) {
26222
- const pattern = new RegExp(`${escapeRegExp3(label)}.*?\\d+(?:\\.\\d+)?%`);
26223
- return pattern.test(line);
26162
+ function isSeparator2(cells) {
26163
+ return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
26224
26164
  }
26225
- function validatePolicyAuditSubstantiveRequirements(text) {
26165
+ function tableRows2(lines, expectedHeaders) {
26166
+ const nonempty = lines.filter((line) => line.trim().length > 0);
26167
+ if (nonempty.length < 2) {
26168
+ return {
26169
+ rows: [],
26170
+ errors: [
26171
+ "Parallel kickoff table is missing its header or separator row."
26172
+ ]
26173
+ };
26174
+ }
26175
+ const headers = parseCells2(nonempty[0] ?? "");
26176
+ const separator = parseCells2(nonempty[1] ?? "");
26226
26177
  const errors = [];
26227
- for (const label of POLICY_AUDIT_REQUIRED_CHECKLIST_LABELS) {
26228
- const line = findPolicyAuditChecklistLine(text, label);
26229
- if (line === null) {
26230
- errors.push(`Policy audit missing required checklist line: ${label}`);
26231
- continue;
26232
- }
26233
- if (hasPlaceholderMarker(line)) {
26234
- errors.push(
26235
- `Policy audit checklist line still contains placeholder text: ${label}`
26236
- );
26237
- }
26178
+ if (headers === void 0 || headers.length !== expectedHeaders.length || headers.some((value, index) => value !== expectedHeaders[index])) {
26179
+ errors.push(
26180
+ `Parallel kickoff item table headers must be: ${expectedHeaders.join(" | ")}`
26181
+ );
26238
26182
  }
26239
- const coverageRows = extractPolicyAuditCoverageRows(text);
26240
- if (coverageRows.length === 0) {
26241
- errors.push("Policy audit missing coverage metrics table rows.");
26183
+ if (separator === void 0 || separator.length !== expectedHeaders.length || !isSeparator2(separator)) {
26184
+ errors.push("Parallel kickoff table separator row is invalid.");
26242
26185
  }
26243
- if (!text.includes(POLICY_AUDIT_COMPARISON_HEADING)) {
26186
+ const rows = [];
26187
+ for (const line of nonempty.slice(2)) {
26188
+ const cells = parseCells2(line);
26189
+ if (cells === void 0 || cells.length !== expectedHeaders.length) {
26190
+ errors.push(`Parallel kickoff table row is invalid: ${line}`);
26191
+ } else {
26192
+ rows.push(cells);
26193
+ }
26194
+ }
26195
+ if (rows.length === 0) {
26244
26196
  errors.push(
26245
- `Policy audit missing required heading: ${POLICY_AUDIT_COMPARISON_HEADING}`
26197
+ "Parallel kickoff item table must contain at least one item row."
26246
26198
  );
26247
26199
  }
26248
- const comparisonLines = extractPolicyAuditComparisonLines(text);
26249
- for (const row of coverageRows) {
26250
- const language = row.Language;
26251
- const baseline = row["Baseline Coverage"];
26252
- const postChange = row["Post-Change Coverage"];
26253
- const newCode = row["New Code Coverage"];
26254
- const requiresCoverageComparison = [baseline, postChange, newCode].some(
26255
- (value) => !isNaValue(value)
26256
- );
26257
- if (!isNaValue(baseline) && !hasNumericCoverage(baseline)) {
26258
- errors.push(
26259
- `Policy audit missing numeric baseline coverage for ${language}.`
26200
+ return { rows, errors };
26201
+ }
26202
+ function parseItems(lines) {
26203
+ const table = tableRows2(lines, ITEM_HEADERS);
26204
+ const items = [];
26205
+ table.rows.forEach((row, index) => {
26206
+ const issueNum = Number(row[0]);
26207
+ if (!Number.isInteger(issueNum)) {
26208
+ table.errors.push(
26209
+ `Parallel kickoff item row ${String(index)} issue_num must be an integer.`
26260
26210
  );
26211
+ return;
26261
26212
  }
26262
- if (!isNaValue(postChange) && !hasNumericCoverage(postChange)) {
26263
- errors.push(
26264
- `Policy audit missing numeric post-change coverage for ${language}.`
26213
+ const cohort = Number(row[2]);
26214
+ if (!Number.isInteger(cohort)) {
26215
+ table.errors.push(
26216
+ `Parallel kickoff item row ${String(index)} cohort must be an integer.`
26265
26217
  );
26218
+ return;
26266
26219
  }
26267
- if (!isNaValue(newCode) && !hasNumericCoverage(newCode)) {
26268
- errors.push(
26269
- `Policy audit missing numeric new/changed-code coverage for ${language}.`
26220
+ const complexity = row[3] ?? "";
26221
+ if (!COMPLEXITY_BANDS2.has(complexity)) {
26222
+ table.errors.push(
26223
+ `Parallel kickoff item row ${String(index)} complexity must be C1-C4.`
26270
26224
  );
26271
26225
  }
26272
- if (!requiresCoverageComparison) {
26226
+ items.push({
26227
+ issueNum,
26228
+ featureFolder: row[1] ?? "",
26229
+ cohort,
26230
+ complexity,
26231
+ branch: row[4] ?? "",
26232
+ planPath: row[5] ?? ""
26233
+ });
26234
+ });
26235
+ return { items, errors: table.errors };
26236
+ }
26237
+ function parseIntegrityTable(tableLines) {
26238
+ const planHashes = {};
26239
+ const errors = [];
26240
+ const header = parseCells2(tableLines[0] ?? "");
26241
+ if (header === void 0 || header.length !== 2 || header[0] !== "plan-path" || !HASH_HEADERS2.has(header[1] ?? "")) {
26242
+ errors.push(
26243
+ "Parallel kickoff integrity table headers must be plan-path and plan-hash."
26244
+ );
26245
+ } else if (tableLines.length < 2) {
26246
+ errors.push(
26247
+ "Parallel kickoff integrity table is missing its separator row."
26248
+ );
26249
+ } else {
26250
+ const separator = parseCells2(tableLines[1] ?? "");
26251
+ if (separator === void 0 || separator.length !== 2 || !isSeparator2(separator)) {
26252
+ errors.push("Parallel kickoff integrity table separator row is invalid.");
26253
+ }
26254
+ for (const line of tableLines.slice(2)) {
26255
+ const cells = parseCells2(line);
26256
+ if (cells === void 0 || cells.length !== 2 || !/^[0-9a-fA-F]{40,64}$/.test(cells[1] ?? "")) {
26257
+ errors.push(`Parallel kickoff integrity table row is invalid: ${line}`);
26258
+ continue;
26259
+ }
26260
+ const planPath = cells[0] ?? "";
26261
+ if (planHashes[planPath] !== void 0) {
26262
+ errors.push(
26263
+ `Parallel kickoff integrity repeats plan path: '${planPath}'.`
26264
+ );
26265
+ }
26266
+ planHashes[planPath] = (cells[1] ?? "").toLowerCase();
26267
+ }
26268
+ }
26269
+ return { planHashes, errors };
26270
+ }
26271
+ function parseIntegrity2(lines) {
26272
+ let planningCommit;
26273
+ const errors = [];
26274
+ const tableLines = [];
26275
+ for (const line of lines) {
26276
+ if (line.trim().length === 0) {
26273
26277
  continue;
26274
26278
  }
26275
- const comparisonLine = comparisonLines.get(language.toLowerCase());
26279
+ const match = INTEGRITY_COMMIT_RE2.exec(line.trim());
26280
+ if (match?.groups?.["commit"] !== void 0) {
26281
+ if (planningCommit !== void 0) {
26282
+ errors.push(
26283
+ "Parallel kickoff integrity has duplicate planning_commit fields."
26284
+ );
26285
+ }
26286
+ planningCommit = match.groups["commit"].toLowerCase();
26287
+ } else if (line.trim().startsWith("|")) {
26288
+ tableLines.push(line);
26289
+ } else {
26290
+ errors.push(`Parallel kickoff integrity line is invalid: ${line}`);
26291
+ }
26292
+ }
26293
+ let planHashes = {};
26294
+ if (tableLines.length > 0) {
26295
+ const table = parseIntegrityTable(tableLines);
26296
+ planHashes = table.planHashes;
26297
+ errors.push(...table.errors);
26298
+ }
26299
+ return {
26300
+ ...planningCommit === void 0 ? {} : { planningCommit },
26301
+ planHashes,
26302
+ errors
26303
+ };
26304
+ }
26305
+ function parseParallelKickoff(text) {
26306
+ const lines = text.split(LINE_SPLIT_RE);
26307
+ if (text.length === 0) {
26308
+ return { errors: ["Parallel kickoff is empty."] };
26309
+ }
26310
+ const heading = KICKOFF_HEADING_RE2.exec(lines[0] ?? "");
26311
+ if (heading?.groups?.["slug"] === void 0) {
26312
+ return {
26313
+ errors: [
26314
+ "Parallel kickoff first line must match '# Parallel Kickoff: <slug>'."
26315
+ ]
26316
+ };
26317
+ }
26318
+ const sectionResult = splitSections3(text);
26319
+ const invocation = (sectionResult.sections.get("Invocation Prompt") ?? []).join("\n");
26320
+ const invocationSlug = PARALLEL_RUN_RE.exec(invocation)?.groups?.["slug"];
26321
+ const manifestMatch = MANIFEST_RE2.exec(invocation);
26322
+ const branchMatch = PLAN_BRANCH_RE.exec(invocation);
26323
+ const resumeMatch = RESUME_RE2.exec(invocation);
26324
+ if (invocationSlug === void 0) {
26325
+ sectionResult.errors.push(
26326
+ "Parallel kickoff invocation must contain `Run /parallel-run <slug>`."
26327
+ );
26328
+ }
26329
+ if (manifestMatch?.[0] === void 0 || branchMatch?.[0] === void 0 || resumeMatch === null) {
26330
+ sectionResult.errors.push(
26331
+ "Parallel kickoff invocation must structurally name the manifest, plan-home branch, and atomic-execution resume boundary."
26332
+ );
26333
+ }
26334
+ const itemResult = parseItems(
26335
+ sectionResult.sections.get("Item Summary") ?? []
26336
+ );
26337
+ sectionResult.errors.push(...itemResult.errors);
26338
+ const integrity = parseIntegrity2(
26339
+ sectionResult.sections.get("Integrity") ?? []
26340
+ );
26341
+ sectionResult.errors.push(...integrity.errors);
26342
+ if (sectionResult.errors.length > 0 || invocationSlug === void 0 || manifestMatch?.[0] === void 0 || branchMatch?.[0] === void 0 || resumeMatch === null) {
26343
+ return { errors: sectionResult.errors };
26344
+ }
26345
+ return {
26346
+ parsed: {
26347
+ slug: heading.groups["slug"],
26348
+ invocationSlug,
26349
+ manifestPath: manifestMatch[0],
26350
+ planHomeBranch: branchMatch[0],
26351
+ items: itemResult.items,
26352
+ ...integrity.planningCommit === void 0 ? {} : { planningCommit: integrity.planningCommit },
26353
+ planHashes: integrity.planHashes
26354
+ },
26355
+ errors: []
26356
+ };
26357
+ }
26358
+ function validateParallelKickoffText(text) {
26359
+ return parseParallelKickoff(text).errors;
26360
+ }
26361
+
26362
+ // ../../extensions/drm-copilot/src/lib/validate/parallel-state-shared.ts
26363
+ function words(text) {
26364
+ return text.split(" ");
26365
+ }
26366
+ var VALID_ITEM_STATES = words(
26367
+ "proposed admitted prepared scheduled in_flight merged withdrawn blocked"
26368
+ );
26369
+ var VALID_MERGE_STATUS2 = words(
26370
+ "not_started worktree_created pr_open ci_green merged worktree_removed blocked_drift blocked_ci_loop_limit"
26371
+ );
26372
+ var VALID_SOURCES = words("derived declared observed");
26373
+ var VALID_KINDS = words("feature bug");
26374
+ var VALID_MODES = words("closed open");
26375
+ var VALID_MUTATION_OPS = words("add remove close requeue");
26376
+ var VALID_DISPOSITIONS = words("detach abandon");
26377
+ var VALID_EDGE_REASONS = words(
26378
+ "path_overlap module_overlap shared_surface_overlap contract_dependency"
26379
+ );
26380
+ var VALID_DRIFT_ACTIONS = words(
26381
+ "raised_blocking_finding halted_later_started_item"
26382
+ );
26383
+ var MERGED_MERGE_STATUSES = words("merged worktree_removed");
26384
+ var BLOCKED_MERGE_STATUSES = words(
26385
+ "blocked_drift blocked_ci_loop_limit"
26386
+ );
26387
+ var BLAST_RADIUS_LIST_FIELDS = words(
26388
+ "paths modules shared_surfaces contracts"
26389
+ );
26390
+ var PROHIBITED_ANY_LEVEL_KEYS = words(
26391
+ "depends_on integration_branch epic_merge_pr"
26392
+ );
26393
+ var ROOT_PATH = "<root>";
26394
+ function isObject14(value) {
26395
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26396
+ }
26397
+ function pythonRepr9(value) {
26398
+ if (value === null || value === void 0) {
26399
+ return "None";
26400
+ }
26401
+ if (typeof value === "boolean") {
26402
+ return value ? "True" : "False";
26403
+ }
26404
+ if (typeof value === "string") {
26405
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
26406
+ }
26407
+ if (Array.isArray(value)) {
26408
+ return `[${value.map(pythonRepr9).join(", ")}]`;
26409
+ }
26410
+ if (isObject14(value)) {
26411
+ const pairs = Object.entries(value).map(
26412
+ ([key, item]) => `${pythonRepr9(key)}: ${pythonRepr9(item)}`
26413
+ );
26414
+ return `{${pairs.join(", ")}}`;
26415
+ }
26416
+ return String(value);
26417
+ }
26418
+ function pythonStr2(value) {
26419
+ return typeof value === "string" ? value : pythonRepr9(value);
26420
+ }
26421
+ function isEnumMember(members, value) {
26422
+ return typeof value === "string" && members.includes(value);
26423
+ }
26424
+ function isNonEmptyString2(value) {
26425
+ return typeof value === "string" && value.trim().length > 0;
26426
+ }
26427
+ function isIntegral(value) {
26428
+ return typeof value === "number" && Number.isInteger(value);
26429
+ }
26430
+ function isPositiveInteger(value) {
26431
+ return isIntegral(value) && value > 0;
26432
+ }
26433
+ function isNonNegativeInteger(value) {
26434
+ return isIntegral(value) && value >= 0;
26435
+ }
26436
+ function isStringList(value) {
26437
+ return Array.isArray(value) && value.every(isNonEmptyString2);
26438
+ }
26439
+ function inBoundedRange(value, minimum, maximum) {
26440
+ return isIntegral(value) && value >= minimum && value <= maximum;
26441
+ }
26442
+ function enumError(context, field, members, value) {
26443
+ return `${context} ${field} must be one of ${members.join(", ")}; found: ${pythonRepr9(value)}.`;
26444
+ }
26445
+ function itemContext(context, index) {
26446
+ return `${context} items[${index}]`;
26447
+ }
26448
+ function validateBlastRadiusBlock(radius, context) {
26449
+ if (!isObject14(radius)) {
26450
+ return [`${context} blast_radius must be an object.`];
26451
+ }
26452
+ const errors = [];
26453
+ for (const field of BLAST_RADIUS_LIST_FIELDS) {
26454
+ if (!isStringList(radius[field])) {
26455
+ errors.push(
26456
+ `${context} blast_radius.${field} must be a list of non-empty strings.`
26457
+ );
26458
+ }
26459
+ }
26460
+ const source = radius["source"];
26461
+ if (!isEnumMember(VALID_SOURCES, source)) {
26462
+ errors.push(
26463
+ enumError(context, "blast_radius.source", VALID_SOURCES, source)
26464
+ );
26465
+ }
26466
+ if (!isNonEmptyString2(radius["computed_at"])) {
26467
+ errors.push(
26468
+ `${context} blast_radius.computed_at must be a non-empty string.`
26469
+ );
26470
+ }
26471
+ return errors;
26472
+ }
26473
+ function validateMergeStatus(record2, context, state) {
26474
+ if (!("merge_status" in record2)) {
26475
+ return [];
26476
+ }
26477
+ const mergeStatus = record2["merge_status"];
26478
+ if (!isEnumMember(VALID_MERGE_STATUS2, mergeStatus)) {
26479
+ return [
26480
+ enumError(context, "merge_status", VALID_MERGE_STATUS2, mergeStatus)
26481
+ ];
26482
+ }
26483
+ if (isEnumMember(MERGED_MERGE_STATUSES, mergeStatus) && state !== "merged") {
26484
+ return [
26485
+ `${context} merge_status ${pythonRepr9(mergeStatus)} requires state 'merged'; found: ${pythonRepr9(state)}.`
26486
+ ];
26487
+ }
26488
+ if (isEnumMember(BLOCKED_MERGE_STATUSES, mergeStatus) && state !== "blocked") {
26489
+ return [
26490
+ `${context} merge_status ${pythonRepr9(mergeStatus)} requires state 'blocked'; found: ${pythonRepr9(state)}.`
26491
+ ];
26492
+ }
26493
+ return [];
26494
+ }
26495
+ function validateItemRecord(item, context, requireKind = false) {
26496
+ if (!isObject14(item)) {
26497
+ return [`${context} must be an object.`];
26498
+ }
26499
+ const errors = [];
26500
+ const issueNum = item["issue_num"];
26501
+ if (!isPositiveInteger(issueNum)) {
26502
+ errors.push(
26503
+ `${context} issue_num must be a positive integer; found: ${pythonRepr9(issueNum)}.`
26504
+ );
26505
+ }
26506
+ if (!isNonEmptyString2(item["feature_folder"])) {
26507
+ errors.push(`${context} feature_folder must be a non-empty string.`);
26508
+ }
26509
+ const state = item["state"];
26510
+ if (!isEnumMember(VALID_ITEM_STATES, state)) {
26511
+ errors.push(enumError(context, "state", VALID_ITEM_STATES, state));
26512
+ }
26513
+ if (requireKind) {
26514
+ const kind = item["kind"];
26515
+ if (!isEnumMember(VALID_KINDS, kind)) {
26516
+ errors.push(enumError(context, "kind", VALID_KINDS, kind));
26517
+ }
26518
+ }
26519
+ errors.push(...validateMergeStatus(item, context, state));
26520
+ errors.push(...validateBlastRadiusBlock(item["blast_radius"], context));
26521
+ return errors;
26522
+ }
26523
+ function validateItems(items, context, requireKind = false) {
26524
+ if (!Array.isArray(items)) {
26525
+ return [`${context} items must be a list.`];
26526
+ }
26527
+ const errors = [];
26528
+ const seen = /* @__PURE__ */ new Set();
26529
+ const duplicates = /* @__PURE__ */ new Set();
26530
+ items.forEach((entry, index) => {
26531
+ errors.push(
26532
+ ...validateItemRecord(entry, itemContext(context, index), requireKind)
26533
+ );
26534
+ if (!isObject14(entry)) {
26535
+ return;
26536
+ }
26537
+ const issueNum = entry["issue_num"];
26538
+ if (typeof issueNum !== "number" || !Number.isInteger(issueNum)) {
26539
+ return;
26540
+ }
26541
+ if (seen.has(issueNum)) {
26542
+ duplicates.add(issueNum);
26543
+ }
26544
+ seen.add(issueNum);
26545
+ });
26546
+ for (const issueNum of [...duplicates].sort((left, right) => left - right)) {
26547
+ errors.push(`${context} has duplicate items[].issue_num: ${issueNum}.`);
26548
+ }
26549
+ return errors;
26550
+ }
26551
+ function prohibitedKeyErrors(value, path11, context, keys) {
26552
+ const errors = [];
26553
+ if (isObject14(value)) {
26554
+ for (const [key, child] of Object.entries(value)) {
26555
+ if (keys.includes(key)) {
26556
+ errors.push(`${context} carries prohibited key '${key}' at ${path11}.`);
26557
+ }
26558
+ const childPath = path11 === ROOT_PATH ? key : `${path11}.${key}`;
26559
+ errors.push(...prohibitedKeyErrors(child, childPath, context, keys));
26560
+ }
26561
+ } else if (Array.isArray(value)) {
26562
+ value.forEach((entry, index) => {
26563
+ errors.push(
26564
+ ...prohibitedKeyErrors(entry, `${path11}[${index}]`, context, keys)
26565
+ );
26566
+ });
26567
+ }
26568
+ return errors;
26569
+ }
26570
+ function scanProhibitedKeys(root, context, deepKeys = PROHIBITED_ANY_LEVEL_KEYS, topLevelKeys = []) {
26571
+ const errors = prohibitedKeyErrors(root, ROOT_PATH, context, deepKeys);
26572
+ if (isObject14(root) && topLevelKeys.length > 0) {
26573
+ for (const key of topLevelKeys) {
26574
+ if (key in root) {
26575
+ errors.push(
26576
+ `${context} carries prohibited key '${key}' at ${ROOT_PATH}.`
26577
+ );
26578
+ }
26579
+ }
26580
+ }
26581
+ return errors;
26582
+ }
26583
+
26584
+ // ../../extensions/drm-copilot/src/lib/validate/parallel-orchestrator-state-cohort-barrier.ts
26585
+ var VIOLATION_PREFIX = "PARALLEL_COHORT_BARRIER_VIOLATION";
26586
+ var ITEM_START_TIMESTAMP_FIELD = "worktree_created_at";
26587
+ var MERGE_CONFIRMATION_TIMESTAMP_FIELD = "merged_at";
26588
+ var NOT_STARTED_MERGE_STATUS = "not_started";
26589
+ var GATING_KEYS = ["conflict_edges", "cohorts"];
26590
+ var FOLDER_HINT_PREFIXES = [
26591
+ "docs/features/active/",
26592
+ "docs/features/completed/",
26593
+ "active/",
26594
+ "completed/"
26595
+ ];
26596
+ function normalizeFolderHint2(value) {
26597
+ for (const prefix of FOLDER_HINT_PREFIXES) {
26598
+ if (value.startsWith(prefix)) {
26599
+ return value.slice(prefix.length);
26600
+ }
26601
+ }
26602
+ return value;
26603
+ }
26604
+ function buildReferenceIndex(items) {
26605
+ const records = /* @__PURE__ */ new Map();
26606
+ const byFolderHint = /* @__PURE__ */ new Map();
26607
+ if (!Array.isArray(items)) {
26608
+ return { records, byFolderHint };
26609
+ }
26610
+ for (const entry of items) {
26611
+ if (!isObject14(entry)) {
26612
+ continue;
26613
+ }
26614
+ const issueNum = entry["issue_num"];
26615
+ if (typeof issueNum !== "number" || !isPositiveInteger(issueNum)) {
26616
+ continue;
26617
+ }
26618
+ if (!records.has(issueNum)) {
26619
+ records.set(issueNum, entry);
26620
+ }
26621
+ const folder = entry["feature_folder"];
26622
+ if (typeof folder === "string" && folder.trim().length > 0) {
26623
+ const hint = normalizeFolderHint2(folder);
26624
+ if (!byFolderHint.has(hint)) {
26625
+ byFolderHint.set(hint, issueNum);
26626
+ }
26627
+ }
26628
+ }
26629
+ return { records, byFolderHint };
26630
+ }
26631
+ function resolveReference(reference, references) {
26632
+ if (typeof reference === "string") {
26633
+ return references.byFolderHint.get(normalizeFolderHint2(reference)) ?? null;
26634
+ }
26635
+ if (typeof reference === "number" && isPositiveInteger(reference) && references.records.has(reference)) {
26636
+ return reference;
26637
+ }
26638
+ return null;
26639
+ }
26640
+ function cohortIndexByItem(cohorts, recolorGeneration, references) {
26641
+ const assignments = /* @__PURE__ */ new Map();
26642
+ if (!Array.isArray(cohorts) || !isNonNegativeInteger(recolorGeneration)) {
26643
+ return assignments;
26644
+ }
26645
+ for (const entry of cohorts) {
26646
+ if (!isObject14(entry) || entry["generation"] !== recolorGeneration) {
26647
+ continue;
26648
+ }
26649
+ const cohortIndex = entry["index"];
26650
+ const itemKeys = entry["item_keys"];
26651
+ if (typeof cohortIndex !== "number" || !isNonNegativeInteger(cohortIndex) || !Array.isArray(itemKeys)) {
26652
+ continue;
26653
+ }
26654
+ for (const reference of itemKeys) {
26655
+ const key = resolveReference(reference, references);
26656
+ if (key !== null && !assignments.has(key)) {
26657
+ assignments.set(key, cohortIndex);
26658
+ }
26659
+ }
26660
+ }
26661
+ return assignments;
26662
+ }
26663
+ function hasStarted(record2) {
26664
+ const start = record2[ITEM_START_TIMESTAMP_FIELD];
26665
+ if (typeof start === "string" && start.trim().length > 0) {
26666
+ return true;
26667
+ }
26668
+ const mergeStatus = record2["merge_status"];
26669
+ return typeof mergeStatus === "string" && mergeStatus !== NOT_STARTED_MERGE_STATUS;
26670
+ }
26671
+ function satisfiesBarrier(record2) {
26672
+ return isEnumMember(MERGED_MERGE_STATUSES, record2["merge_status"]);
26673
+ }
26674
+ function mergeConfirmedAfterStart(earlier, later) {
26675
+ const confirmed = earlier[MERGE_CONFIRMATION_TIMESTAMP_FIELD];
26676
+ const started = later[ITEM_START_TIMESTAMP_FIELD];
26677
+ if (typeof confirmed !== "string" || typeof started !== "string") {
26678
+ return false;
26679
+ }
26680
+ return confirmed > started;
26681
+ }
26682
+ function violationEndpoints(first, second, assignments, records) {
26683
+ const firstIndex = assignments.get(first);
26684
+ const secondIndex = assignments.get(second);
26685
+ if (firstIndex !== void 0 && firstIndex === secondIndex) {
26686
+ return [first, second];
26687
+ }
26688
+ if (firstIndex === void 0 || secondIndex === void 0) {
26689
+ return null;
26690
+ }
26691
+ const [earlierKey, laterKey] = firstIndex < secondIndex ? [first, second] : [second, first];
26692
+ const earlier = records.get(earlierKey);
26693
+ const later = records.get(laterKey);
26694
+ if (earlier === void 0 || later === void 0) {
26695
+ return null;
26696
+ }
26697
+ const statusViolation = hasStarted(later) && !satisfiesBarrier(earlier);
26698
+ if (statusViolation || mergeConfirmedAfterStart(earlier, later)) {
26699
+ return [earlierKey, laterKey];
26700
+ }
26701
+ return null;
26702
+ }
26703
+ function validateCohortBarrierOrdering(state) {
26704
+ if (GATING_KEYS.some((key) => !(key in state))) {
26705
+ return [];
26706
+ }
26707
+ const edges = state["conflict_edges"];
26708
+ if (!Array.isArray(edges)) {
26709
+ return [];
26710
+ }
26711
+ const references = buildReferenceIndex(state["items"]);
26712
+ const assignments = cohortIndexByItem(
26713
+ state["cohorts"],
26714
+ state["recolor_generation"],
26715
+ references
26716
+ );
26717
+ const errors = [];
26718
+ for (const entry of edges) {
26719
+ if (!isObject14(entry)) {
26720
+ continue;
26721
+ }
26722
+ const first = resolveReference(entry["a"], references);
26723
+ const second = resolveReference(entry["b"], references);
26724
+ if (first === null || second === null || first === second) {
26725
+ continue;
26726
+ }
26727
+ const endpoints = violationEndpoints(
26728
+ first,
26729
+ second,
26730
+ assignments,
26731
+ references.records
26732
+ );
26733
+ if (endpoints !== null) {
26734
+ errors.push(
26735
+ `${VIOLATION_PREFIX}: ${endpoints[0]} ran concurrently with conflicting ${endpoints[1]}`
26736
+ );
26737
+ }
26738
+ }
26739
+ return errors;
26740
+ }
26741
+
26742
+ // ../../extensions/drm-copilot/src/lib/validate/parallel-state-records.ts
26743
+ var OPS_REQUIRING_ITEM_KEY = ["add", "remove", "requeue"];
26744
+ var OPS_REQUIRING_NULL_PRIOR_STATE = ["add", "close"];
26745
+ var OPS_REQUIRING_NULL_NEW_STATE = ["close"];
26746
+ function isNone(value) {
26747
+ return value === null || value === void 0;
26748
+ }
26749
+ function resolves(value, issueNums) {
26750
+ return typeof value === "number" && Number.isInteger(value) && issueNums.has(value);
26751
+ }
26752
+ function validateMutationItemKey(entry, entryContext, op, issueNums) {
26753
+ const itemKey = entry["item_key"];
26754
+ if (op === "close") {
26755
+ if (!isNone(itemKey)) {
26756
+ return [
26757
+ `${entryContext} item_key must be null for op 'close'; found: ${pythonRepr9(itemKey)}.`
26758
+ ];
26759
+ }
26760
+ return [];
26761
+ }
26762
+ if (isEnumMember(OPS_REQUIRING_ITEM_KEY, op) && !resolves(itemKey, issueNums)) {
26763
+ return [
26764
+ `${entryContext} item_key ${pythonRepr9(itemKey)} does not resolve to an items[].issue_num.`
26765
+ ];
26766
+ }
26767
+ return [];
26768
+ }
26769
+ function validateMutationStateField(entry, entryContext, field, op, nullOps) {
26770
+ const value = entry[field];
26771
+ if (isNone(value)) {
26772
+ return [];
26773
+ }
26774
+ if (isEnumMember(nullOps, op)) {
26775
+ return [
26776
+ `${entryContext} ${field} must be null for op ${pythonRepr9(op)}; found: ${pythonRepr9(value)}.`
26777
+ ];
26778
+ }
26779
+ if (!isEnumMember(VALID_ITEM_STATES, value)) {
26780
+ return [
26781
+ `${entryContext} ${field} must be null or one of ${VALID_ITEM_STATES.join(", ")}; found: ${pythonRepr9(value)}.`
26782
+ ];
26783
+ }
26784
+ return [];
26785
+ }
26786
+ function validateMutationDisposition(entry, entryContext, op) {
26787
+ const disposition = entry["disposition"];
26788
+ if (op === "remove" && entry["prior_state"] === "in_flight") {
26789
+ if (!isEnumMember(VALID_DISPOSITIONS, disposition)) {
26790
+ return [
26791
+ `${entryContext} disposition must be one of ${VALID_DISPOSITIONS.join(", ")} for an in-flight removal; found: ${pythonRepr9(disposition)}.`
26792
+ ];
26793
+ }
26794
+ return [];
26795
+ }
26796
+ if (!isNone(disposition)) {
26797
+ return [
26798
+ `${entryContext} disposition must be null unless op is 'remove' with prior_state 'in_flight'; found: ${pythonRepr9(disposition)}.`
26799
+ ];
26800
+ }
26801
+ return [];
26802
+ }
26803
+ function validateMutationGeneration(entry, entryContext, recolorGeneration) {
26804
+ const generation = entry["recolor_generation"];
26805
+ if (!isNonNegativeInteger(generation)) {
26806
+ return [
26807
+ `${entryContext} recolor_generation must be a non-negative integer; found: ${pythonRepr9(generation)}.`
26808
+ ];
26809
+ }
26810
+ if (isNonNegativeInteger(recolorGeneration) && typeof generation === "number" && typeof recolorGeneration === "number" && generation > recolorGeneration) {
26811
+ return [
26812
+ `${entryContext} recolor_generation ${generation} must not exceed recolor_generation ${recolorGeneration}.`
26813
+ ];
26814
+ }
26815
+ return [];
26816
+ }
26817
+ function validateMutations(mutations, issueNums, recolorGeneration, context) {
26818
+ if (!Array.isArray(mutations)) {
26819
+ return [`${context} mutations must be a list.`];
26820
+ }
26821
+ const errors = [];
26822
+ mutations.forEach((entry, position) => {
26823
+ const entryContext = `${context} mutations[${position}]`;
26824
+ if (!isObject14(entry)) {
26825
+ errors.push(`${entryContext} must be an object.`);
26826
+ return;
26827
+ }
26828
+ const op = entry["op"];
26829
+ if (!isEnumMember(VALID_MUTATION_OPS, op)) {
26830
+ errors.push(enumError(entryContext, "op", VALID_MUTATION_OPS, op));
26831
+ }
26832
+ errors.push(...validateMutationItemKey(entry, entryContext, op, issueNums));
26833
+ if (!isNonEmptyString2(entry["at"])) {
26834
+ errors.push(`${entryContext} at must be a non-empty string.`);
26835
+ }
26836
+ errors.push(
26837
+ ...validateMutationStateField(
26838
+ entry,
26839
+ entryContext,
26840
+ "prior_state",
26841
+ op,
26842
+ OPS_REQUIRING_NULL_PRIOR_STATE
26843
+ )
26844
+ );
26845
+ errors.push(
26846
+ ...validateMutationStateField(
26847
+ entry,
26848
+ entryContext,
26849
+ "new_state",
26850
+ op,
26851
+ OPS_REQUIRING_NULL_NEW_STATE
26852
+ )
26853
+ );
26854
+ errors.push(...validateMutationDisposition(entry, entryContext, op));
26855
+ errors.push(
26856
+ ...validateMutationGeneration(entry, entryContext, recolorGeneration)
26857
+ );
26858
+ });
26859
+ return errors;
26860
+ }
26861
+ function validateDriftEvents(events, issueNums, context) {
26862
+ if (!Array.isArray(events)) {
26863
+ return [`${context} drift_events must be a list.`];
26864
+ }
26865
+ const errors = [];
26866
+ events.forEach((entry, position) => {
26867
+ const entryContext = `${context} drift_events[${position}]`;
26868
+ if (!isObject14(entry)) {
26869
+ errors.push(`${entryContext} must be an object.`);
26870
+ return;
26871
+ }
26872
+ const itemKey = entry["item_key"];
26873
+ if (!resolves(itemKey, issueNums)) {
26874
+ errors.push(
26875
+ `${entryContext} item_key ${pythonRepr9(itemKey)} does not resolve to an items[].issue_num.`
26876
+ );
26877
+ }
26878
+ for (const field of ["declared", "observed"]) {
26879
+ if (!isStringList(entry[field])) {
26880
+ errors.push(
26881
+ `${entryContext} ${field} must be a list of non-empty strings.`
26882
+ );
26883
+ }
26884
+ }
26885
+ const escapedPaths = entry["escaped_paths"];
26886
+ if (!isStringList(escapedPaths) || !Array.isArray(escapedPaths) || escapedPaths.length === 0) {
26887
+ errors.push(
26888
+ `${entryContext} escaped_paths must be a non-empty list of non-empty strings.`
26889
+ );
26890
+ }
26891
+ if (!isNonEmptyString2(entry["at"])) {
26892
+ errors.push(`${entryContext} at must be a non-empty string.`);
26893
+ }
26894
+ const action = entry["action"];
26895
+ if (!isEnumMember(VALID_DRIFT_ACTIONS, action)) {
26896
+ errors.push(
26897
+ enumError(entryContext, "action", VALID_DRIFT_ACTIONS, action)
26898
+ );
26899
+ }
26900
+ });
26901
+ return errors;
26902
+ }
26903
+
26904
+ // ../../extensions/drm-copilot/src/lib/validate/parallel-state-structures.ts
26905
+ var COHORT_COVERAGE_EXEMPT_STATES = "withdrawn merged blocked".split(" ");
26906
+ var RECEIPT_ARRAY_KEYS = "delegation_receipts skill_receipts mcp_call_receipts".split(" ");
26907
+ function collectItemKeysAndStates(items) {
26908
+ const pairs = [];
26909
+ if (!Array.isArray(items)) {
26910
+ return pairs;
26911
+ }
26912
+ for (const entry of items) {
26913
+ if (!isObject14(entry)) {
26914
+ continue;
26915
+ }
26916
+ const issueNum = entry["issue_num"];
26917
+ if (typeof issueNum === "number" && Number.isInteger(issueNum) && issueNum > 0) {
26918
+ pairs.push({ issueNum, state: entry["state"] });
26919
+ }
26920
+ }
26921
+ return pairs;
26922
+ }
26923
+ function collectIssueNumbers(items) {
26924
+ return new Set(collectItemKeysAndStates(items).map((pair) => pair.issueNum));
26925
+ }
26926
+ function validateCohortEntry(entry, entryContext, issueNums, recolorGeneration) {
26927
+ const errors = [];
26928
+ const index = entry["index"];
26929
+ if (!isNonNegativeInteger(index)) {
26930
+ errors.push(
26931
+ `${entryContext} index must be a non-negative integer; found: ${pythonRepr9(index)}.`
26932
+ );
26933
+ }
26934
+ const generation = entry["generation"];
26935
+ if (!isNonNegativeInteger(generation)) {
26936
+ errors.push(
26937
+ `${entryContext} generation must be a non-negative integer; found: ${pythonRepr9(generation)}.`
26938
+ );
26939
+ } else if (recolorGeneration !== null && typeof generation === "number" && generation > recolorGeneration) {
26940
+ errors.push(
26941
+ `${entryContext} generation ${generation} must not exceed recolor_generation ${recolorGeneration}.`
26942
+ );
26943
+ }
26944
+ const itemKeys = entry["item_keys"];
26945
+ if (!Array.isArray(itemKeys)) {
26946
+ errors.push(`${entryContext} item_keys must be a list.`);
26947
+ return errors;
26948
+ }
26949
+ for (const key of itemKeys) {
26950
+ if (typeof key !== "number" || !Number.isInteger(key) || !issueNums.has(key)) {
26951
+ errors.push(
26952
+ `${entryContext} item_keys entry ${pythonRepr9(key)} does not resolve to an items[].issue_num.`
26953
+ );
26954
+ }
26955
+ }
26956
+ return errors;
26957
+ }
26958
+ function validateCohortShapes(cohorts, issueNums, recolorGeneration, context) {
26959
+ const errors = [];
26960
+ const generationOk = isNonNegativeInteger(recolorGeneration);
26961
+ if (!generationOk) {
26962
+ errors.push(
26963
+ `${context} recolor_generation must be a non-negative integer; found: ${pythonRepr9(recolorGeneration)}.`
26964
+ );
26965
+ }
26966
+ if (!Array.isArray(cohorts)) {
26967
+ errors.push(`${context} cohorts must be a list.`);
26968
+ return errors;
26969
+ }
26970
+ const bound = generationOk && typeof recolorGeneration === "number" ? recolorGeneration : null;
26971
+ cohorts.forEach((entry, position) => {
26972
+ const entryContext = `${context} cohorts[${position}]`;
26973
+ if (!isObject14(entry)) {
26974
+ errors.push(`${entryContext} must be an object.`);
26975
+ return;
26976
+ }
26977
+ errors.push(...validateCohortEntry(entry, entryContext, issueNums, bound));
26978
+ });
26979
+ return errors;
26980
+ }
26981
+ function currentGenerationCohorts(cohorts, recolorGeneration) {
26982
+ if (!isNonNegativeInteger(recolorGeneration) || !Array.isArray(cohorts)) {
26983
+ return [];
26984
+ }
26985
+ return cohorts.filter(isObject14).filter((entry) => entry["generation"] === recolorGeneration);
26986
+ }
26987
+ function validateCurrentGenerationCohorts(cohorts, items, recolorGeneration, context) {
26988
+ const current = currentGenerationCohorts(cohorts, recolorGeneration);
26989
+ const errors = [];
26990
+ const seenIndices = /* @__PURE__ */ new Set();
26991
+ const duplicateIndices = /* @__PURE__ */ new Set();
26992
+ const membershipCounts = /* @__PURE__ */ new Map();
26993
+ for (const entry of current) {
26994
+ const index = entry["index"];
26995
+ if (isNonNegativeInteger(index) && typeof index === "number") {
26996
+ if (seenIndices.has(index)) {
26997
+ duplicateIndices.add(index);
26998
+ }
26999
+ seenIndices.add(index);
27000
+ }
27001
+ const itemKeys = entry["item_keys"];
27002
+ if (!Array.isArray(itemKeys)) {
27003
+ continue;
27004
+ }
27005
+ for (const key of itemKeys) {
27006
+ if (typeof key === "number" && Number.isInteger(key)) {
27007
+ membershipCounts.set(key, (membershipCounts.get(key) ?? 0) + 1);
27008
+ }
27009
+ }
27010
+ }
27011
+ for (const index of [...duplicateIndices].sort(
27012
+ (left, right) => left - right
27013
+ )) {
27014
+ errors.push(
27015
+ `${context} has duplicate current-generation cohorts[].index: ${index}.`
27016
+ );
27017
+ }
27018
+ const pairs = [...collectItemKeysAndStates(items)].sort(
27019
+ (left, right) => left.issueNum - right.issueNum
27020
+ );
27021
+ for (const { issueNum, state } of pairs) {
27022
+ const count = membershipCounts.get(issueNum) ?? 0;
27023
+ if (count === 1) {
27024
+ continue;
27025
+ }
27026
+ if (count === 0 && isEnumMember(COHORT_COVERAGE_EXEMPT_STATES, state)) {
27027
+ continue;
27028
+ }
27029
+ errors.push(
27030
+ `${context} item ${issueNum} in state ${pythonRepr9(state)} must appear in exactly one current-generation cohort; found ${count}.`
27031
+ );
27032
+ }
27033
+ return errors;
27034
+ }
27035
+ function validateCurrentCohortBound(currentCohort, cohorts, recolorGeneration, context) {
27036
+ if (!isNonNegativeInteger(currentCohort) || typeof currentCohort !== "number") {
27037
+ return [
27038
+ `${context} current_cohort must be a non-negative integer; found: ${pythonRepr9(currentCohort)}.`
27039
+ ];
27040
+ }
27041
+ const indices = [];
27042
+ for (const entry of currentGenerationCohorts(cohorts, recolorGeneration)) {
27043
+ const index = entry["index"];
27044
+ if (isNonNegativeInteger(index) && typeof index === "number") {
27045
+ indices.push(index);
27046
+ }
27047
+ }
27048
+ if (indices.length === 0) {
27049
+ return [];
27050
+ }
27051
+ const highest = Math.max(...indices);
27052
+ if (currentCohort > highest) {
27053
+ return [
27054
+ `${context} current_cohort ${currentCohort} must not exceed the maximum current-generation cohorts[].index ${highest}.`
27055
+ ];
27056
+ }
27057
+ return [];
27058
+ }
27059
+ function validateEdgeEndpoints(entry, entryContext, issueNums) {
27060
+ const errors = [];
27061
+ const endpoints = /* @__PURE__ */ new Map();
27062
+ for (const field of ["a", "b"]) {
27063
+ const value = entry[field];
27064
+ if (typeof value === "number" && Number.isInteger(value) && issueNums.has(value)) {
27065
+ endpoints.set(field, value);
27066
+ } else {
27067
+ errors.push(
27068
+ `${entryContext} ${field} ${pythonRepr9(value)} does not resolve to an items[].issue_num.`
27069
+ );
27070
+ }
27071
+ }
27072
+ if (endpoints.size !== 2) {
27073
+ return { errors, pair: null };
27074
+ }
27075
+ const first = endpoints.get("a") ?? 0;
27076
+ const second = endpoints.get("b") ?? 0;
27077
+ if (first === second) {
27078
+ errors.push(`${entryContext} endpoints must be distinct; found: ${first}.`);
27079
+ return { errors, pair: null };
27080
+ }
27081
+ if (first > second) {
27082
+ errors.push(
27083
+ `${entryContext} must be normalized with a < b; found: (${first}, ${second}).`
27084
+ );
27085
+ return { errors, pair: null };
27086
+ }
27087
+ return { errors, pair: [first, second] };
27088
+ }
27089
+ function validateConflictEdges(edges, issueNums, context) {
27090
+ if (!Array.isArray(edges)) {
27091
+ return [`${context} conflict_edges must be a list.`];
27092
+ }
27093
+ const errors = [];
27094
+ const seen = /* @__PURE__ */ new Set();
27095
+ const duplicates = /* @__PURE__ */ new Map();
27096
+ edges.forEach((entry, position) => {
27097
+ const entryContext = `${context} conflict_edges[${position}]`;
27098
+ if (!isObject14(entry)) {
27099
+ errors.push(`${entryContext} must be an object.`);
27100
+ return;
27101
+ }
27102
+ const { errors: endpointErrors, pair } = validateEdgeEndpoints(
27103
+ entry,
27104
+ entryContext,
27105
+ issueNums
27106
+ );
27107
+ errors.push(...endpointErrors);
27108
+ const reason = entry["reason"];
27109
+ if (!isEnumMember(VALID_EDGE_REASONS, reason)) {
27110
+ errors.push(
27111
+ enumError(entryContext, "reason", VALID_EDGE_REASONS, reason)
27112
+ );
27113
+ }
27114
+ if (pair === null) {
27115
+ return;
27116
+ }
27117
+ const key = `${pair[0]},${pair[1]}`;
27118
+ if (seen.has(key)) {
27119
+ duplicates.set(key, pair);
27120
+ }
27121
+ seen.add(key);
27122
+ });
27123
+ const ordered = [...duplicates.values()].sort(
27124
+ (left, right) => left[0] - right[0] || left[1] - right[1]
27125
+ );
27126
+ for (const pair of ordered) {
27127
+ errors.push(
27128
+ `${context} has duplicate conflict_edges[] pair: (${pair[0]}, ${pair[1]}).`
27129
+ );
27130
+ }
27131
+ return errors;
27132
+ }
27133
+ function validateReceiptArrays(state, context) {
27134
+ const errors = [];
27135
+ for (const key of RECEIPT_ARRAY_KEYS) {
27136
+ if (key in state && !Array.isArray(state[key])) {
27137
+ errors.push(`${context} ${key} must be a list when present.`);
27138
+ }
27139
+ }
27140
+ return errors;
27141
+ }
27142
+
27143
+ // ../../extensions/drm-copilot/src/lib/validate/parallel-orchestrator-state-core.ts
27144
+ var CONTEXT = "Parallel checkpoint";
27145
+ var EXPECTED_ROUTE_ID2 = "parallel";
27146
+ var MIN_CONCURRENCY = 1;
27147
+ var MAX_CONCURRENCY = 8;
27148
+ var REQUIRED_KEYS4 = [
27149
+ "objective",
27150
+ "completed_steps",
27151
+ "next_step",
27152
+ "last_updated",
27153
+ "route_id",
27154
+ "parallel_slug",
27155
+ "parallel_manifest_path",
27156
+ "parallel_status_doc_path",
27157
+ "mode",
27158
+ "max_concurrency",
27159
+ "current_cohort",
27160
+ "recolor_generation",
27161
+ "cohorts",
27162
+ "items",
27163
+ "conflict_edges",
27164
+ "mutations",
27165
+ "drift_events"
27166
+ ];
27167
+ function missingRequiredKeys(state) {
27168
+ return REQUIRED_KEYS4.filter((key) => !(key in state)).map(
27169
+ (key) => `${CONTEXT} missing required key: ${key}.`
27170
+ );
27171
+ }
27172
+ function validateIdentity(state) {
27173
+ const errors = [];
27174
+ const routeId = state["route_id"];
27175
+ if ("route_id" in state && routeId !== EXPECTED_ROUTE_ID2) {
27176
+ errors.push(
27177
+ `${CONTEXT} route_id must be '${EXPECTED_ROUTE_ID2}'; found: ${pythonRepr9(routeId)}.`
27178
+ );
27179
+ }
27180
+ const mode = state["mode"];
27181
+ if ("mode" in state && !isEnumMember(VALID_MODES, mode)) {
27182
+ errors.push(enumError(CONTEXT, "mode", VALID_MODES, mode));
27183
+ }
27184
+ const concurrency = state["max_concurrency"];
27185
+ if ("max_concurrency" in state && !inBoundedRange(concurrency, MIN_CONCURRENCY, MAX_CONCURRENCY)) {
27186
+ errors.push(
27187
+ `${CONTEXT} max_concurrency must be an integer from ${MIN_CONCURRENCY} through ${MAX_CONCURRENCY}; found: ${pythonRepr9(concurrency)}.`
27188
+ );
27189
+ }
27190
+ return errors;
27191
+ }
27192
+ function validateCollections(state) {
27193
+ const items = state["items"];
27194
+ const issueNums = collectIssueNumbers(items);
27195
+ const generation = state["recolor_generation"];
27196
+ const cohorts = state["cohorts"];
27197
+ const errors = [];
27198
+ if ("items" in state) {
27199
+ errors.push(...validateItems(items, CONTEXT));
27200
+ }
27201
+ if ("cohorts" in state) {
27202
+ errors.push(
27203
+ ...validateCohortShapes(cohorts, issueNums, generation, CONTEXT)
27204
+ );
27205
+ errors.push(
27206
+ ...validateCurrentGenerationCohorts(cohorts, items, generation, CONTEXT)
27207
+ );
27208
+ }
27209
+ if ("current_cohort" in state) {
27210
+ errors.push(
27211
+ ...validateCurrentCohortBound(
27212
+ state["current_cohort"],
27213
+ cohorts,
27214
+ generation,
27215
+ CONTEXT
27216
+ )
27217
+ );
27218
+ }
27219
+ if ("conflict_edges" in state) {
27220
+ errors.push(
27221
+ ...validateConflictEdges(state["conflict_edges"], issueNums, CONTEXT)
27222
+ );
27223
+ }
27224
+ if ("mutations" in state) {
27225
+ errors.push(
27226
+ ...validateMutations(state["mutations"], issueNums, generation, CONTEXT)
27227
+ );
27228
+ }
27229
+ if ("drift_events" in state) {
27230
+ errors.push(
27231
+ ...validateDriftEvents(state["drift_events"], issueNums, CONTEXT)
27232
+ );
27233
+ }
27234
+ errors.push(...validateReceiptArrays(state, CONTEXT));
27235
+ return errors;
27236
+ }
27237
+ function recordsCloseMutation(state) {
27238
+ const mutations = state["mutations"];
27239
+ if (!Array.isArray(mutations)) {
27240
+ return false;
27241
+ }
27242
+ return mutations.some(
27243
+ (entry) => isObject14(entry) && entry["op"] === "close"
27244
+ );
27245
+ }
27246
+ function validateCompletion2(state) {
27247
+ const errors = [];
27248
+ const items = state["items"];
27249
+ const entries = Array.isArray(items) ? items : [];
27250
+ entries.forEach((entry, index) => {
27251
+ if (!isObject14(entry)) {
27252
+ return;
27253
+ }
27254
+ if (entry["state"] === "withdrawn") {
27255
+ return;
27256
+ }
27257
+ const mergeStatus = entry["merge_status"];
27258
+ if (!isEnumMember(MERGED_MERGE_STATUSES, mergeStatus)) {
27259
+ errors.push(
27260
+ `${itemContext(CONTEXT, index)} completion validation failed: merge_status is not merged or worktree_removed; found: ${pythonRepr9(mergeStatus)}.`
27261
+ );
27262
+ }
27263
+ });
27264
+ if (state["mode"] === "open" && !recordsCloseMutation(state)) {
27265
+ errors.push(
27266
+ `${CONTEXT} completion validation failed: open mode requires a mutations[] entry with op 'close'.`
27267
+ );
27268
+ }
27269
+ return errors;
27270
+ }
27271
+ function validateParallelOrchestratorStateText(text, options = {}) {
27272
+ let state;
27273
+ try {
27274
+ state = JSON.parse(text);
27275
+ } catch (error2) {
27276
+ const detail = error2 instanceof Error ? error2.message : String(error2);
27277
+ return [`${CONTEXT} is not valid JSON: ${detail}.`];
27278
+ }
27279
+ if (!isObject14(state)) {
27280
+ return [`${CONTEXT} root must be a JSON object.`];
27281
+ }
27282
+ const errors = [];
27283
+ errors.push(...missingRequiredKeys(state));
27284
+ errors.push(...validateIdentity(state));
27285
+ errors.push(...scanProhibitedKeys(state, CONTEXT));
27286
+ errors.push(...validateCollections(state));
27287
+ errors.push(...validateCohortBarrierOrdering(state));
27288
+ if (options.requireComplete === true) {
27289
+ errors.push(...validateCompletion2(state));
27290
+ }
27291
+ return errors;
27292
+ }
27293
+
27294
+ // ../../extensions/drm-copilot/src/lib/validate/parallel-planner-state-core.ts
27295
+ var CONTEXT2 = "Parallel planner checkpoint";
27296
+ var MIN_CONCURRENCY2 = 1;
27297
+ var MAX_CONCURRENCY2 = 8;
27298
+ var REQUIRED_KEYS5 = [
27299
+ "objective",
27300
+ "parallel_slug",
27301
+ "parallel_manifest_path",
27302
+ "mode",
27303
+ "max_concurrency",
27304
+ "items",
27305
+ "cohorts",
27306
+ "conflict_edges",
27307
+ "recolor_generation",
27308
+ "completed_steps",
27309
+ "next_step",
27310
+ "last_updated"
27311
+ ];
27312
+ var REQUIRED_ITEM_KEYS = [
27313
+ "issue_num",
27314
+ "feature_folder",
27315
+ "kind",
27316
+ "state",
27317
+ "blast_radius",
27318
+ "preparation_status",
27319
+ "research_path",
27320
+ "plan_path",
27321
+ "preflight_status"
27322
+ ];
27323
+ var VALID_COMPLEXITY_BANDS = [
27324
+ "C1",
27325
+ "C2",
27326
+ "C3",
27327
+ "C4"
27328
+ ];
27329
+ var READY_NEXT_STEP2 = "PARALLEL_EXECUTION_READY";
27330
+ var READY_PREPARATION_STATUS = "prepared";
27331
+ var READY_PREFLIGHT_STATUS = "PREFLIGHT: ALL CLEAR";
27332
+ var READY_RADIUS_SOURCE = "declared";
27333
+ var MINIMUM_READY_ITEMS = 2;
27334
+ var READY_ITEM_PATH_KEYS = ["research_path", "plan_path"];
27335
+ function kickoffPathFor(slug) {
27336
+ return `artifacts/orchestration/parallel-kickoff-${pythonStr2(slug)}.md`;
27337
+ }
27338
+ function missingRequiredKeys2(state) {
27339
+ return REQUIRED_KEYS5.filter((key) => !(key in state)).map(
27340
+ (key) => `${CONTEXT2} missing required key: ${key}.`
27341
+ );
27342
+ }
27343
+ function validateIdentity2(state) {
27344
+ const errors = [];
27345
+ for (const key of ["parallel_slug", "parallel_manifest_path"]) {
27346
+ if (key in state && !isNonEmptyString2(state[key])) {
27347
+ errors.push(`${CONTEXT2} ${key} must be a non-empty string.`);
27348
+ }
27349
+ }
27350
+ const mode = state["mode"];
27351
+ if ("mode" in state && !isEnumMember(VALID_MODES, mode)) {
27352
+ errors.push(enumError(CONTEXT2, "mode", VALID_MODES, mode));
27353
+ }
27354
+ const concurrency = state["max_concurrency"];
27355
+ if ("max_concurrency" in state && !inBoundedRange(concurrency, MIN_CONCURRENCY2, MAX_CONCURRENCY2)) {
27356
+ errors.push(
27357
+ `${CONTEXT2} max_concurrency must be an integer from ${MIN_CONCURRENCY2} through ${MAX_CONCURRENCY2}; found: ${pythonRepr9(concurrency)}.`
27358
+ );
27359
+ }
27360
+ return errors;
27361
+ }
27362
+ function itemRecords(items) {
27363
+ if (!Array.isArray(items)) {
27364
+ return [];
27365
+ }
27366
+ const records = [];
27367
+ items.forEach((entry, index) => {
27368
+ if (isObject14(entry)) {
27369
+ records.push({ index, record: entry });
27370
+ }
27371
+ });
27372
+ return records;
27373
+ }
27374
+ function validateItemContract(items) {
27375
+ const errors = [];
27376
+ for (const { index, record: record2 } of itemRecords(items)) {
27377
+ const entryContext = itemContext(CONTEXT2, index);
27378
+ for (const key of REQUIRED_ITEM_KEYS) {
27379
+ if (!(key in record2)) {
27380
+ errors.push(`${entryContext} missing required key: ${key}.`);
27381
+ }
27382
+ }
27383
+ const band = record2["complexity_band"];
27384
+ if ("complexity_band" in record2 && !isEnumMember(VALID_COMPLEXITY_BANDS, band)) {
27385
+ errors.push(
27386
+ enumError(
27387
+ entryContext,
27388
+ "complexity_band",
27389
+ VALID_COMPLEXITY_BANDS,
27390
+ band
27391
+ )
27392
+ );
27393
+ }
27394
+ }
27395
+ return errors;
27396
+ }
27397
+ function validateCollections2(state) {
27398
+ const items = state["items"];
27399
+ const issueNums = collectIssueNumbers(items);
27400
+ const generation = state["recolor_generation"];
27401
+ const cohorts = state["cohorts"];
27402
+ const errors = [];
27403
+ if ("items" in state) {
27404
+ errors.push(...validateItems(items, CONTEXT2, true));
27405
+ errors.push(...validateItemContract(items));
27406
+ }
27407
+ if ("cohorts" in state) {
27408
+ errors.push(
27409
+ ...validateCohortShapes(cohorts, issueNums, generation, CONTEXT2)
27410
+ );
27411
+ errors.push(
27412
+ ...validateCurrentGenerationCohorts(cohorts, items, generation, CONTEXT2)
27413
+ );
27414
+ }
27415
+ if ("current_cohort" in state) {
27416
+ errors.push(
27417
+ ...validateCurrentCohortBound(
27418
+ state["current_cohort"],
27419
+ cohorts,
27420
+ generation,
27421
+ CONTEXT2
27422
+ )
27423
+ );
27424
+ }
27425
+ if ("conflict_edges" in state) {
27426
+ errors.push(
27427
+ ...validateConflictEdges(state["conflict_edges"], issueNums, CONTEXT2)
27428
+ );
27429
+ }
27430
+ return errors;
27431
+ }
27432
+ function validateReadyItem(record2, entryContext) {
27433
+ const errors = [];
27434
+ const preparationStatus = record2["preparation_status"];
27435
+ if (preparationStatus !== READY_PREPARATION_STATUS) {
27436
+ errors.push(
27437
+ `${entryContext} preparation_status must be ${pythonRepr9(READY_PREPARATION_STATUS)}; found: ${pythonRepr9(preparationStatus)}.`
27438
+ );
27439
+ }
27440
+ const preflightStatus = record2["preflight_status"];
27441
+ if (preflightStatus !== READY_PREFLIGHT_STATUS) {
27442
+ errors.push(
27443
+ `${entryContext} preflight_status must be ${pythonRepr9(READY_PREFLIGHT_STATUS)}; found: ${pythonRepr9(preflightStatus)}.`
27444
+ );
27445
+ }
27446
+ for (const key of READY_ITEM_PATH_KEYS) {
27447
+ if (!isNonEmptyString2(record2[key])) {
27448
+ errors.push(`${entryContext} ${key} must be a non-empty string.`);
27449
+ }
27450
+ }
27451
+ const radius = record2["blast_radius"];
27452
+ const source = isObject14(radius) ? radius["source"] : null;
27453
+ if (source !== READY_RADIUS_SOURCE) {
27454
+ errors.push(
27455
+ `${entryContext} blast_radius.source must be ${pythonRepr9(READY_RADIUS_SOURCE)} for execution readiness; found: ${pythonRepr9(source)}.`
27456
+ );
27457
+ }
27458
+ return errors;
27459
+ }
27460
+ function validateReadyGate(state) {
27461
+ const errors = [];
27462
+ const items = state["items"];
27463
+ if (Array.isArray(items)) {
27464
+ const count = items.length;
27465
+ if (count < MINIMUM_READY_ITEMS) {
27466
+ errors.push(
27467
+ `${CONTEXT2} requires at least ${MINIMUM_READY_ITEMS} items for execution readiness; found: ${count}.`
27468
+ );
27469
+ }
27470
+ for (const { index, record: record2 } of itemRecords(items)) {
27471
+ errors.push(...validateReadyItem(record2, itemContext(CONTEXT2, index)));
27472
+ }
27473
+ }
27474
+ const nextStep = state["next_step"];
27475
+ if (nextStep !== READY_NEXT_STEP2) {
27476
+ errors.push(
27477
+ `${CONTEXT2} next_step must be ${pythonRepr9(READY_NEXT_STEP2)}; found: ${pythonRepr9(nextStep)}.`
27478
+ );
27479
+ }
27480
+ const expectedKickoff = kickoffPathFor(state["parallel_slug"]);
27481
+ const kickoffPromptPath = state["kickoff_prompt_path"];
27482
+ if (kickoffPromptPath !== expectedKickoff) {
27483
+ errors.push(
27484
+ `${CONTEXT2} kickoff_prompt_path must be ${pythonRepr9(expectedKickoff)}; found: ${pythonRepr9(kickoffPromptPath)}.`
27485
+ );
27486
+ }
27487
+ return errors;
27488
+ }
27489
+ function validateParallelPlannerStateText(text, options = {}) {
27490
+ let state;
27491
+ try {
27492
+ state = JSON.parse(text);
27493
+ } catch (error2) {
27494
+ const detail = error2 instanceof Error ? error2.message : String(error2);
27495
+ return [`${CONTEXT2} is not valid JSON: ${detail}.`];
27496
+ }
27497
+ if (!isObject14(state)) {
27498
+ return [`${CONTEXT2} root must be a JSON object.`];
27499
+ }
27500
+ const errors = [];
27501
+ errors.push(...missingRequiredKeys2(state));
27502
+ errors.push(...validateIdentity2(state));
27503
+ errors.push(...scanProhibitedKeys(state, CONTEXT2));
27504
+ errors.push(...validateCollections2(state));
27505
+ if (options.requireReadyForExecution === true) {
27506
+ errors.push(...validateReadyGate(state));
27507
+ }
27508
+ return errors;
27509
+ }
27510
+
27511
+ // ../../extensions/drm-copilot/src/lib/validate/policy-audit-artifact.ts
27512
+ var COVERAGE_PERCENT_RE = /\b\d+(?:\.\d+)?%/;
27513
+ var POLICY_AUDIT_REQUIRED_HEADINGS = [
27514
+ "## Executive Summary",
27515
+ "## 1. General Unit Test Policy Compliance",
27516
+ "## 2. General Code Change Policy Compliance",
27517
+ "## 3. Language-Specific Code Change Policy Compliance",
27518
+ "## 4. Language-Specific Unit Test Policy Compliance",
27519
+ "## 5. Test Coverage Detail",
27520
+ "## 6. Test Execution Metrics",
27521
+ "## 7. Code Quality Checks",
27522
+ "## 8. Gaps and Exceptions",
27523
+ "## 9. Summary of Changes",
27524
+ "## 10. Compliance Verdict",
27525
+ "## Appendix A: Test Inventory",
27526
+ "## Appendix B: Toolchain Commands Reference"
27527
+ ];
27528
+ var POLICY_AUDIT_REQUIRED_CHECKLIST_LABELS = [
27529
+ "TypeScript baseline coverage artifact:",
27530
+ "TypeScript post-change coverage artifact:",
27531
+ "PowerShell baseline coverage artifact:",
27532
+ "PowerShell post-change coverage artifact:",
27533
+ "Per-language comparison summary:"
27534
+ ];
27535
+ var POLICY_AUDIT_COMPARISON_HEADING = "### 1.2.1 Per-Language Coverage Comparison";
27536
+ var PLACEHOLDER_MARKERS = [
27537
+ "[n]",
27538
+ "[path",
27539
+ "[artifact",
27540
+ "[section reference",
27541
+ "[language]",
27542
+ "tbd",
27543
+ "unverified",
27544
+ "missing"
27545
+ ];
27546
+ var TEMPLATE_RESOLVER_TOOL = "resolve_policy_audit_template_asset";
27547
+ var TEMPLATE_RESOLVER_MISSING_MARKERS = [
27548
+ "not exposed",
27549
+ "was not exposed",
27550
+ "missing resolver exposure",
27551
+ "fallback template",
27552
+ "fallback-template"
27553
+ ];
27554
+ function hasNumericCoverage(value) {
27555
+ return COVERAGE_PERCENT_RE.test(value);
27556
+ }
27557
+ function isNaValue(value) {
27558
+ return value.trim().toLowerCase().startsWith("n/a");
27559
+ }
27560
+ function hasPlaceholderMarker(value) {
27561
+ const lowered = value.toLowerCase();
27562
+ return PLACEHOLDER_MARKERS.some((marker) => lowered.includes(marker));
27563
+ }
27564
+ function reportsMissingTemplateResolverSuccess(text) {
27565
+ const lowered = text.toLowerCase();
27566
+ if (!lowered.includes(TEMPLATE_RESOLVER_TOOL)) {
27567
+ return false;
27568
+ }
27569
+ if (!TEMPLATE_RESOLVER_MISSING_MARKERS.some((m) => lowered.includes(m))) {
27570
+ return false;
27571
+ }
27572
+ return /\b(pass|ready|readiness)\b/.test(lowered);
27573
+ }
27574
+ function extractPolicyAuditCoverageRows(text) {
27575
+ const rows = [];
27576
+ for (const line of text.split("\n")) {
27577
+ if (!line.startsWith("|")) {
27578
+ continue;
27579
+ }
27580
+ const segments = line.split("|");
27581
+ const cells = segments.slice(1, -1).map((cell) => cell.trim());
27582
+ if (cells.length !== 7) {
27583
+ continue;
27584
+ }
27585
+ const language = cells[0] ?? "";
27586
+ if (language === "Language" || language === "----------" || !language) {
27587
+ continue;
27588
+ }
27589
+ if (language.length > 0 && /^-+$/.test(language)) {
27590
+ continue;
27591
+ }
27592
+ rows.push({
27593
+ Language: language,
27594
+ "Files Changed": cells[1] ?? "",
27595
+ Tests: cells[2] ?? "",
27596
+ "Test Result": cells[3] ?? "",
27597
+ "Baseline Coverage": cells[4] ?? "",
27598
+ "Post-Change Coverage": cells[5] ?? "",
27599
+ "New Code Coverage": cells[6] ?? ""
27600
+ });
27601
+ }
27602
+ return rows;
27603
+ }
27604
+ function findPolicyAuditChecklistLine(text, label) {
27605
+ for (const line of text.split("\n")) {
27606
+ const stripped = line.trim();
27607
+ if (stripped.startsWith("- ") && stripped.includes(label)) {
27608
+ return stripped;
27609
+ }
27610
+ }
27611
+ return null;
27612
+ }
27613
+ function extractPolicyAuditComparisonLines(text) {
27614
+ let inSection = false;
27615
+ const comparisonLines = /* @__PURE__ */ new Map();
27616
+ for (const line of text.split("\n")) {
27617
+ const stripped = line.trim();
27618
+ if (stripped === POLICY_AUDIT_COMPARISON_HEADING) {
27619
+ inSection = true;
27620
+ continue;
27621
+ }
27622
+ if (inSection && stripped.startsWith("### ")) {
27623
+ break;
27624
+ }
27625
+ if (!inSection || !stripped.startsWith("- ")) {
27626
+ continue;
27627
+ }
27628
+ const body = stripped.slice(2);
27629
+ const colonIndex = body.indexOf(":");
27630
+ if (colonIndex === -1) {
27631
+ continue;
27632
+ }
27633
+ const language = body.slice(0, colonIndex);
27634
+ comparisonLines.set(language.trim().toLowerCase(), stripped);
27635
+ }
27636
+ return comparisonLines;
27637
+ }
27638
+ function escapeRegExp3(value) {
27639
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27640
+ }
27641
+ function comparisonLineHasLabelledPercentage(line, label) {
27642
+ const pattern = new RegExp(`${escapeRegExp3(label)}.*?\\d+(?:\\.\\d+)?%`);
27643
+ return pattern.test(line);
27644
+ }
27645
+ function validatePolicyAuditSubstantiveRequirements(text) {
27646
+ const errors = [];
27647
+ for (const label of POLICY_AUDIT_REQUIRED_CHECKLIST_LABELS) {
27648
+ const line = findPolicyAuditChecklistLine(text, label);
27649
+ if (line === null) {
27650
+ errors.push(`Policy audit missing required checklist line: ${label}`);
27651
+ continue;
27652
+ }
27653
+ if (hasPlaceholderMarker(line)) {
27654
+ errors.push(
27655
+ `Policy audit checklist line still contains placeholder text: ${label}`
27656
+ );
27657
+ }
27658
+ }
27659
+ const coverageRows = extractPolicyAuditCoverageRows(text);
27660
+ if (coverageRows.length === 0) {
27661
+ errors.push("Policy audit missing coverage metrics table rows.");
27662
+ }
27663
+ if (!text.includes(POLICY_AUDIT_COMPARISON_HEADING)) {
27664
+ errors.push(
27665
+ `Policy audit missing required heading: ${POLICY_AUDIT_COMPARISON_HEADING}`
27666
+ );
27667
+ }
27668
+ const comparisonLines = extractPolicyAuditComparisonLines(text);
27669
+ for (const row of coverageRows) {
27670
+ const language = row.Language;
27671
+ const baseline = row["Baseline Coverage"];
27672
+ const postChange = row["Post-Change Coverage"];
27673
+ const newCode = row["New Code Coverage"];
27674
+ const requiresCoverageComparison = [baseline, postChange, newCode].some(
27675
+ (value) => !isNaValue(value)
27676
+ );
27677
+ if (!isNaValue(baseline) && !hasNumericCoverage(baseline)) {
27678
+ errors.push(
27679
+ `Policy audit missing numeric baseline coverage for ${language}.`
27680
+ );
27681
+ }
27682
+ if (!isNaValue(postChange) && !hasNumericCoverage(postChange)) {
27683
+ errors.push(
27684
+ `Policy audit missing numeric post-change coverage for ${language}.`
27685
+ );
27686
+ }
27687
+ if (!isNaValue(newCode) && !hasNumericCoverage(newCode)) {
27688
+ errors.push(
27689
+ `Policy audit missing numeric new/changed-code coverage for ${language}.`
27690
+ );
27691
+ }
27692
+ if (!requiresCoverageComparison) {
27693
+ continue;
27694
+ }
27695
+ const comparisonLine = comparisonLines.get(language.toLowerCase());
26276
27696
  if (comparisonLine === void 0) {
26277
27697
  errors.push(
26278
27698
  `Policy audit missing per-language comparison line for ${language}.`
@@ -26492,6 +27912,20 @@ function validateArtifact(input) {
26492
27912
  }
26493
27913
  case "epic-kickoff":
26494
27914
  return validateEpicKickoffText(input.text);
27915
+ case "parallel-orchestrator-state": {
27916
+ const options = {
27917
+ ...input.requireComplete === void 0 ? {} : { requireComplete: input.requireComplete }
27918
+ };
27919
+ return validateParallelOrchestratorStateText(input.text, options);
27920
+ }
27921
+ case "parallel-planner-state": {
27922
+ const options = {
27923
+ ...input.requireReadyForExecution === void 0 ? {} : { requireReadyForExecution: input.requireReadyForExecution }
27924
+ };
27925
+ return validateParallelPlannerStateText(input.text, options);
27926
+ }
27927
+ case "parallel-kickoff":
27928
+ return validateParallelKickoffText(input.text);
26495
27929
  default:
26496
27930
  return [`Unsupported artifact type: ${input.artifactType}`];
26497
27931
  }