@danmoisan/drm-copilot-mcp 1.0.20 → 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 (37) hide show
  1. package/out/mcp-server.js +1687 -210
  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/agents/orchestrator-c1.toml +4 -0
  30. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +4 -0
  31. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +4 -0
  32. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +4 -0
  33. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +4 -0
  34. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +4 -0
  35. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  36. package/resources/config/orchestration-routing.json +22 -0
  37. 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({
@@ -3646,7 +3646,12 @@ var require_fast_uri = __commonJS({
3646
3646
  }
3647
3647
  function resolve4(baseURI, relativeURI, options) {
3648
3648
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3649
- const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3649
+ const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
3650
+ const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
3651
+ if (baseMalformed || relativeMalformed) {
3652
+ throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
3653
+ }
3654
+ const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
3650
3655
  schemelessOptions.skipEscape = true;
3651
3656
  return serialize(resolved, schemelessOptions);
3652
3657
  }
@@ -3772,6 +3777,7 @@ var require_fast_uri = __commonJS({
3772
3777
  }
3773
3778
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
3774
3779
  var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
3780
+ var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
3775
3781
  function getParseError(parsed, matches) {
3776
3782
  if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
3777
3783
  return 'URI path must start with "/" when authority is present.';
@@ -3806,6 +3812,20 @@ var require_fast_uri = __commonJS({
3806
3812
  parsed.error = "URI authority must not contain a literal backslash.";
3807
3813
  malformedAuthorityOrPort = true;
3808
3814
  }
3815
+ const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
3816
+ if (introducerMatch !== null) {
3817
+ const region = introducerMatch[1];
3818
+ const normalizedRegion = region.replace(/[\t\n\r]/g, "");
3819
+ if (normalizedRegion.length >= 2) {
3820
+ if (normalizedRegion.slice(0, 2) !== "//") {
3821
+ parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
3822
+ malformedAuthorityOrPort = true;
3823
+ } else if (region.length !== normalizedRegion.length) {
3824
+ parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
3825
+ malformedAuthorityOrPort = true;
3826
+ }
3827
+ }
3828
+ }
3809
3829
  const matches = uri.match(URI_PARSE);
3810
3830
  if (matches) {
3811
3831
  parsed.scheme = matches[1];
@@ -5226,10 +5246,10 @@ var require_additionalItems = __commonJS({
5226
5246
  cxt.pass((0, codegen_1._)`${len} <= ${items.length}`);
5227
5247
  } else if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it, schema)) {
5228
5248
  const valid = gen.var("valid", (0, codegen_1._)`${len} <= ${items.length}`);
5229
- gen.if((0, codegen_1.not)(valid), () => validateItems(valid));
5249
+ gen.if((0, codegen_1.not)(valid), () => validateItems2(valid));
5230
5250
  cxt.ok(valid);
5231
5251
  }
5232
- function validateItems(valid) {
5252
+ function validateItems2(valid) {
5233
5253
  gen.forRange("i", items.length, len, (i) => {
5234
5254
  cxt.subschema({ keyword, dataProp: i, dataPropType: util_1.Type.Num }, valid);
5235
5255
  if (!it.allErrors)
@@ -5401,7 +5421,7 @@ var require_contains = __commonJS({
5401
5421
  it.items = true;
5402
5422
  const valid = gen.name("valid");
5403
5423
  if (max === void 0 && min === 1) {
5404
- validateItems(valid, () => gen.if(valid, () => gen.break()));
5424
+ validateItems2(valid, () => gen.if(valid, () => gen.break()));
5405
5425
  } else if (min === 0) {
5406
5426
  gen.let(valid, true);
5407
5427
  if (max !== void 0)
@@ -5414,9 +5434,9 @@ var require_contains = __commonJS({
5414
5434
  function validateItemsWithCount() {
5415
5435
  const schValid = gen.name("_valid");
5416
5436
  const count = gen.let("count", 0);
5417
- validateItems(schValid, () => gen.if(schValid, () => checkLimits(count)));
5437
+ validateItems2(schValid, () => gen.if(schValid, () => checkLimits(count)));
5418
5438
  }
5419
- function validateItems(_valid, block) {
5439
+ function validateItems2(_valid, block) {
5420
5440
  gen.forRange("i", 0, len, (i) => {
5421
5441
  cxt.subschema({
5422
5442
  keyword: "contains",
@@ -8970,13 +8990,13 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
8970
8990
  }
8971
8991
  return propValues;
8972
8992
  });
8973
- const isObject14 = isObject;
8993
+ const isObject15 = isObject;
8974
8994
  const catchall = def.catchall;
8975
8995
  let value;
8976
8996
  inst._zod.parse = (payload, ctx) => {
8977
8997
  value ?? (value = _normalized.value);
8978
8998
  const input = payload.value;
8979
- if (!isObject14(input)) {
8999
+ if (!isObject15(input)) {
8980
9000
  payload.issues.push({
8981
9001
  expected: "object",
8982
9002
  code: "invalid_type",
@@ -9103,7 +9123,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
9103
9123
  return (payload, ctx) => fn(shape, payload, ctx);
9104
9124
  };
9105
9125
  let fastpass;
9106
- const isObject14 = isObject;
9126
+ const isObject15 = isObject;
9107
9127
  const jit = !globalConfig.jitless;
9108
9128
  const allowsEval2 = allowsEval;
9109
9129
  const fastEnabled = jit && allowsEval2.value;
@@ -9112,7 +9132,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
9112
9132
  inst._zod.parse = (payload, ctx) => {
9113
9133
  value ?? (value = _normalized.value);
9114
9134
  const input = payload.value;
9115
- if (!isObject14(input)) {
9135
+ if (!isObject15(input)) {
9116
9136
  payload.issues.push({
9117
9137
  expected: "object",
9118
9138
  code: "invalid_type",
@@ -15069,16 +15089,7 @@ var Server = class extends Protocol {
15069
15089
  if (!methodSchema) {
15070
15090
  throw new Error("Schema is missing a method literal");
15071
15091
  }
15072
- let methodValue;
15073
- if (isZ4Schema(methodSchema)) {
15074
- const v4Schema = methodSchema;
15075
- const v4Def = v4Schema._zod?.def;
15076
- methodValue = v4Def?.value ?? v4Schema.value;
15077
- } else {
15078
- const v3Schema = methodSchema;
15079
- const legacyDef = v3Schema._def;
15080
- methodValue = legacyDef?.value ?? v3Schema.value;
15081
- }
15092
+ const methodValue = getLiteralValue(methodSchema);
15082
15093
  if (typeof methodValue !== "string") {
15083
15094
  throw new Error("Schema method literal must be a string");
15084
15095
  }
@@ -15387,8 +15398,17 @@ var Server = class extends Protocol {
15387
15398
  var import_node_process = __toESM(require("node:process"), 1);
15388
15399
 
15389
15400
  // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
15401
+ var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
15390
15402
  var ReadBuffer = class {
15403
+ constructor(options) {
15404
+ this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
15405
+ }
15391
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
+ }
15392
15412
  this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
15393
15413
  }
15394
15414
  readMessage() {
@@ -15416,18 +15436,24 @@ function serializeMessage(message) {
15416
15436
 
15417
15437
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
15418
15438
  var StdioServerTransport = class {
15419
- 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) {
15420
15440
  this._stdin = _stdin;
15421
15441
  this._stdout = _stdout;
15422
- this._readBuffer = new ReadBuffer();
15423
15442
  this._started = false;
15424
15443
  this._ondata = (chunk) => {
15425
- this._readBuffer.append(chunk);
15426
- 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
+ }
15427
15452
  };
15428
15453
  this._onerror = (error2) => {
15429
15454
  this.onerror?.(error2);
15430
15455
  };
15456
+ this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
15431
15457
  }
15432
15458
  /**
15433
15459
  * Starts listening for messages on stdin.
@@ -16242,7 +16268,10 @@ var VALID_ARTIFACT_TYPES = /* @__PURE__ */ new Set([
16242
16268
  "orchestrator-state",
16243
16269
  "epic-orchestrator-state",
16244
16270
  "epic-planner-state",
16245
- "epic-kickoff"
16271
+ "epic-kickoff",
16272
+ "parallel-orchestrator-state",
16273
+ "parallel-planner-state",
16274
+ "parallel-kickoff"
16246
16275
  ]);
16247
16276
  function resolveValidateOrchestrationArtifactsToolInput(rawInput, fallbackWorkspaceRoot) {
16248
16277
  const args = asToolArgumentObject(rawInput);
@@ -16987,7 +17016,10 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
16987
17016
  "orchestrator-state",
16988
17017
  "epic-orchestrator-state",
16989
17018
  "epic-planner-state",
16990
- "epic-kickoff"
17019
+ "epic-kickoff",
17020
+ "parallel-orchestrator-state",
17021
+ "parallel-planner-state",
17022
+ "parallel-kickoff"
16991
17023
  ],
16992
17024
  description: "The type of orchestration artifact to validate."
16993
17025
  },
@@ -16997,7 +17029,7 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
16997
17029
  },
16998
17030
  require_complete: {
16999
17031
  type: "boolean",
17000
- 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."
17001
17033
  },
17002
17034
  require_model_routing: {
17003
17035
  type: "boolean",
@@ -17013,7 +17045,7 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
17013
17045
  },
17014
17046
  require_ready_for_execution: {
17015
17047
  type: "boolean",
17016
- 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."
17017
17049
  }
17018
17050
  },
17019
17051
  required: ["workspace_root", "artifact_type", "artifact_path"],
@@ -23307,7 +23339,10 @@ function validateCodexModelRoutingReceipts(value) {
23307
23339
  }
23308
23340
  function delegatedAgentNames(state) {
23309
23341
  const result = /* @__PURE__ */ new Set();
23310
- const receipts = state["delegation_receipts"];
23342
+ let receipts = state["delegation_receipts"];
23343
+ if (isObject4(receipts)) {
23344
+ receipts = receipts["agents"];
23345
+ }
23311
23346
  if (!Array.isArray(receipts)) {
23312
23347
  return result;
23313
23348
  }
@@ -23701,7 +23736,10 @@ function validateCodexTopologyReceipts(value) {
23701
23736
  }
23702
23737
  function delegatedAgentNames2(state) {
23703
23738
  const result = /* @__PURE__ */ new Set();
23704
- const receipts = state["delegation_receipts"];
23739
+ let receipts = state["delegation_receipts"];
23740
+ if (isObject5(receipts)) {
23741
+ receipts = receipts["agents"];
23742
+ }
23705
23743
  if (!Array.isArray(receipts)) {
23706
23744
  return result;
23707
23745
  }
@@ -25354,6 +25392,9 @@ function stateList(state, key, expected) {
25354
25392
  return value;
25355
25393
  }
25356
25394
  function listReceipts(receipts) {
25395
+ if (isObject8(receipts)) {
25396
+ receipts = receipts["agents"];
25397
+ }
25357
25398
  if (!Array.isArray(receipts)) {
25358
25399
  return [];
25359
25400
  }
@@ -25764,7 +25805,10 @@ function isObject12(value) {
25764
25805
  }
25765
25806
  function delegatedAgents(state) {
25766
25807
  const agents = /* @__PURE__ */ new Set();
25767
- const receipts = state["delegation_receipts"];
25808
+ let receipts = state["delegation_receipts"];
25809
+ if (isObject12(receipts)) {
25810
+ receipts = receipts["agents"];
25811
+ }
25768
25812
  if (Array.isArray(receipts)) {
25769
25813
  for (const receipt of receipts) {
25770
25814
  if (!isObject12(receipt)) {
@@ -25867,6 +25911,7 @@ var STEP_STATUS_KEYS = [
25867
25911
  "step9_status",
25868
25912
  "step10_status"
25869
25913
  ];
25914
+ var AGENT_RECEIPT_NAMESPACE_KEY = "agents";
25870
25915
  var STEP_SPECIFIC_EXTRA_STATUS = /* @__PURE__ */ new Map([
25871
25916
  ["step6_status", /* @__PURE__ */ new Set(["blocked_remediation_loop_limit"])],
25872
25917
  [
@@ -25915,12 +25960,22 @@ function validateListDelegationReceipts(receipts) {
25915
25960
  }
25916
25961
  function validateNamespacedDelegationReceipts(receipts) {
25917
25962
  const errors = [];
25918
- const unsupportedKeys = Object.keys(receipts).filter((key) => key !== PROMOTION_RECEIPT_NAMESPACE_KEY).sort();
25963
+ const unsupportedKeys = Object.keys(receipts).filter(
25964
+ (key) => key !== AGENT_RECEIPT_NAMESPACE_KEY && key !== PROMOTION_RECEIPT_NAMESPACE_KEY
25965
+ ).sort();
25919
25966
  for (const key of unsupportedKeys) {
25920
25967
  errors.push(
25921
25968
  `Checkpoint delegation_receipts object contains unsupported key: ${key}`
25922
25969
  );
25923
25970
  }
25971
+ if (AGENT_RECEIPT_NAMESPACE_KEY in receipts) {
25972
+ const agentReceipts = receipts[AGENT_RECEIPT_NAMESPACE_KEY];
25973
+ if (!Array.isArray(agentReceipts)) {
25974
+ errors.push("Checkpoint delegation_receipts.agents must be a list.");
25975
+ } else {
25976
+ errors.push(...validateListDelegationReceipts(agentReceipts));
25977
+ }
25978
+ }
25924
25979
  const promotionReceipts = receipts[PROMOTION_RECEIPT_NAMESPACE_KEY];
25925
25980
  if (promotionReceipts === void 0 || promotionReceipts === null) {
25926
25981
  return errors;
@@ -26045,203 +26100,1611 @@ function validateOrchestratorStateText(text, options = {}) {
26045
26100
  return errors;
26046
26101
  }
26047
26102
 
26048
- // ../../extensions/drm-copilot/src/lib/validate/policy-audit-artifact.ts
26049
- var COVERAGE_PERCENT_RE = /\b\d+(?:\.\d+)?%/;
26050
- var POLICY_AUDIT_REQUIRED_HEADINGS = [
26051
- "## Executive Summary",
26052
- "## 1. General Unit Test Policy Compliance",
26053
- "## 2. General Code Change Policy Compliance",
26054
- "## 3. Language-Specific Code Change Policy Compliance",
26055
- "## 4. Language-Specific Unit Test Policy Compliance",
26056
- "## 5. Test Coverage Detail",
26057
- "## 6. Test Execution Metrics",
26058
- "## 7. Code Quality Checks",
26059
- "## 8. Gaps and Exceptions",
26060
- "## 9. Summary of Changes",
26061
- "## 10. Compliance Verdict",
26062
- "## Appendix A: Test Inventory",
26063
- "## Appendix B: Toolchain Commands Reference"
26064
- ];
26065
- var POLICY_AUDIT_REQUIRED_CHECKLIST_LABELS = [
26066
- "TypeScript baseline coverage artifact:",
26067
- "TypeScript post-change coverage artifact:",
26068
- "PowerShell baseline coverage artifact:",
26069
- "PowerShell post-change coverage artifact:",
26070
- "Per-language comparison summary:"
26071
- ];
26072
- var POLICY_AUDIT_COMPARISON_HEADING = "### 1.2.1 Per-Language Coverage Comparison";
26073
- var PLACEHOLDER_MARKERS = [
26074
- "[n]",
26075
- "[path",
26076
- "[artifact",
26077
- "[section reference",
26078
- "[language]",
26079
- "tbd",
26080
- "unverified",
26081
- "missing"
26082
- ];
26083
- var TEMPLATE_RESOLVER_TOOL = "resolve_policy_audit_template_asset";
26084
- var TEMPLATE_RESOLVER_MISSING_MARKERS = [
26085
- "not exposed",
26086
- "was not exposed",
26087
- "missing resolver exposure",
26088
- "fallback template",
26089
- "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"
26090
26118
  ];
26091
- function hasNumericCoverage(value) {
26092
- return COVERAGE_PERCENT_RE.test(value);
26093
- }
26094
- function isNaValue(value) {
26095
- return value.trim().toLowerCase().startsWith("n/a");
26096
- }
26097
- function hasPlaceholderMarker(value) {
26098
- const lowered = value.toLowerCase();
26099
- return PLACEHOLDER_MARKERS.some((marker) => lowered.includes(marker));
26100
- }
26101
- function reportsMissingTemplateResolverSuccess(text) {
26102
- const lowered = text.toLowerCase();
26103
- if (!lowered.includes(TEMPLATE_RESOLVER_TOOL)) {
26104
- return false;
26105
- }
26106
- if (!TEMPLATE_RESOLVER_MISSING_MARKERS.some((m) => lowered.includes(m))) {
26107
- return false;
26108
- }
26109
- return /\b(pass|ready|readiness)\b/.test(lowered);
26110
- }
26111
- function extractPolicyAuditCoverageRows(text) {
26112
- const rows = [];
26113
- for (const line of text.split("\n")) {
26114
- if (!line.startsWith("|")) {
26115
- continue;
26116
- }
26117
- const segments = line.split("|");
26118
- const cells = segments.slice(1, -1).map((cell) => cell.trim());
26119
- if (cells.length !== 7) {
26120
- continue;
26121
- }
26122
- const language = cells[0] ?? "";
26123
- 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
+ }
26124
26140
  continue;
26125
26141
  }
26126
- if (language.length > 0 && /^-+$/.test(language)) {
26127
- continue;
26142
+ if (current !== void 0) {
26143
+ sections.get(current)?.push(line);
26128
26144
  }
26129
- rows.push({
26130
- Language: language,
26131
- "Files Changed": cells[1] ?? "",
26132
- Tests: cells[2] ?? "",
26133
- "Test Result": cells[3] ?? "",
26134
- "Baseline Coverage": cells[4] ?? "",
26135
- "Post-Change Coverage": cells[5] ?? "",
26136
- "New Code Coverage": cells[6] ?? ""
26137
- });
26138
26145
  }
26139
- return rows;
26140
- }
26141
- function findPolicyAuditChecklistLine(text, label) {
26142
- for (const line of text.split("\n")) {
26143
- const stripped = line.trim();
26144
- if (stripped.startsWith("- ") && stripped.includes(label)) {
26145
- 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
+ );
26146
26151
  }
26147
26152
  }
26148
- return null;
26153
+ return { sections, errors };
26149
26154
  }
26150
- function extractPolicyAuditComparisonLines(text) {
26151
- let inSection = false;
26152
- const comparisonLines = /* @__PURE__ */ new Map();
26153
- for (const line of text.split("\n")) {
26154
- const stripped = line.trim();
26155
- if (stripped === POLICY_AUDIT_COMPARISON_HEADING) {
26156
- inSection = true;
26157
- continue;
26158
- }
26159
- if (inSection && stripped.startsWith("### ")) {
26160
- break;
26161
- }
26162
- if (!inSection || !stripped.startsWith("- ")) {
26163
- continue;
26164
- }
26165
- const body = stripped.slice(2);
26166
- const colonIndex = body.indexOf(":");
26167
- if (colonIndex === -1) {
26168
- continue;
26169
- }
26170
- const language = body.slice(0, colonIndex);
26171
- 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;
26172
26159
  }
26173
- return comparisonLines;
26174
- }
26175
- function escapeRegExp3(value) {
26176
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26160
+ return stripped.slice(1, -1).split("|").map((cell) => cell.trim().replace(/^`|`$/g, ""));
26177
26161
  }
26178
- function comparisonLineHasLabelledPercentage(line, label) {
26179
- const pattern = new RegExp(`${escapeRegExp3(label)}.*?\\d+(?:\\.\\d+)?%`);
26180
- return pattern.test(line);
26162
+ function isSeparator2(cells) {
26163
+ return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
26181
26164
  }
26182
- 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] ?? "");
26183
26177
  const errors = [];
26184
- for (const label of POLICY_AUDIT_REQUIRED_CHECKLIST_LABELS) {
26185
- const line = findPolicyAuditChecklistLine(text, label);
26186
- if (line === null) {
26187
- errors.push(`Policy audit missing required checklist line: ${label}`);
26188
- continue;
26189
- }
26190
- if (hasPlaceholderMarker(line)) {
26191
- errors.push(
26192
- `Policy audit checklist line still contains placeholder text: ${label}`
26193
- );
26194
- }
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
+ );
26195
26182
  }
26196
- const coverageRows = extractPolicyAuditCoverageRows(text);
26197
- if (coverageRows.length === 0) {
26198
- 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.");
26199
26185
  }
26200
- 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) {
26201
26196
  errors.push(
26202
- `Policy audit missing required heading: ${POLICY_AUDIT_COMPARISON_HEADING}`
26197
+ "Parallel kickoff item table must contain at least one item row."
26203
26198
  );
26204
26199
  }
26205
- const comparisonLines = extractPolicyAuditComparisonLines(text);
26206
- for (const row of coverageRows) {
26207
- const language = row.Language;
26208
- const baseline = row["Baseline Coverage"];
26209
- const postChange = row["Post-Change Coverage"];
26210
- const newCode = row["New Code Coverage"];
26211
- const requiresCoverageComparison = [baseline, postChange, newCode].some(
26212
- (value) => !isNaValue(value)
26213
- );
26214
- if (!isNaValue(baseline) && !hasNumericCoverage(baseline)) {
26215
- errors.push(
26216
- `Policy audit missing numeric baseline coverage for ${language}.`
26217
- );
26218
- }
26219
- if (!isNaValue(postChange) && !hasNumericCoverage(postChange)) {
26220
- errors.push(
26221
- `Policy audit missing numeric post-change coverage for ${language}.`
26222
- );
26223
- }
26224
- if (!isNaValue(newCode) && !hasNumericCoverage(newCode)) {
26225
- errors.push(
26226
- `Policy audit missing numeric new/changed-code 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.`
26227
26210
  );
26211
+ return;
26228
26212
  }
26229
- if (!requiresCoverageComparison) {
26230
- continue;
26231
- }
26232
- const comparisonLine = comparisonLines.get(language.toLowerCase());
26233
- if (comparisonLine === void 0) {
26234
- errors.push(
26235
- `Policy audit missing per-language comparison line 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.`
26236
26217
  );
26237
- continue;
26218
+ return;
26238
26219
  }
26239
- if (!comparisonLineHasLabelledPercentage(comparisonLine, "Baseline:")) {
26240
- errors.push(
26241
- `Policy audit comparison line missing numeric baseline 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.`
26242
26224
  );
26243
26225
  }
26244
- if (!comparisonLineHasLabelledPercentage(comparisonLine, "Post-change:")) {
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) {
26277
+ continue;
26278
+ }
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());
27696
+ if (comparisonLine === void 0) {
27697
+ errors.push(
27698
+ `Policy audit missing per-language comparison line for ${language}.`
27699
+ );
27700
+ continue;
27701
+ }
27702
+ if (!comparisonLineHasLabelledPercentage(comparisonLine, "Baseline:")) {
27703
+ errors.push(
27704
+ `Policy audit comparison line missing numeric baseline for ${language}.`
27705
+ );
27706
+ }
27707
+ if (!comparisonLineHasLabelledPercentage(comparisonLine, "Post-change:")) {
26245
27708
  errors.push(
26246
27709
  `Policy audit comparison line missing numeric post-change coverage for ${language}.`
26247
27710
  );
@@ -26342,7 +27805,7 @@ function validatePlanText(text) {
26342
27805
  const seenPhases = [];
26343
27806
  const expectedTaskNum = /* @__PURE__ */ new Map();
26344
27807
  let foundTask = false;
26345
- const lines = text.split("\n");
27808
+ const lines = text.split(/\r\n|\n|\r/);
26346
27809
  lines.forEach((line, lineIndex) => {
26347
27810
  const lineNumber = lineIndex + 1;
26348
27811
  if (line.startsWith("### Phase ")) {
@@ -26449,6 +27912,20 @@ function validateArtifact(input) {
26449
27912
  }
26450
27913
  case "epic-kickoff":
26451
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);
26452
27929
  default:
26453
27930
  return [`Unsupported artifact type: ${input.artifactType}`];
26454
27931
  }