@gluecharm-lab/easyspecs-cli 0.3.5 → 0.3.6

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 (25) hide show
  1. package/commands.md +4 -4
  2. package/dist/main.cjs +1655 -513
  3. package/dist/main.cjs.map +4 -4
  4. package/package.json +1 -1
  5. package/resources/opencode-agents/agent-list-infrastructure-components.md +187 -0
  6. package/resources/opencode-agents/agent-list-infrastructure.md +222 -0
  7. package/resources/opencode-agents/agent-list-qa-test-cases.md +187 -0
  8. package/resources/opencode-agents/agent-list-qa.md +222 -0
  9. package/resources/opencode-agents/agent-md-infrastructure-component-detail.md +165 -0
  10. package/resources/opencode-agents/agent-md-infrastructure-detail.md +95 -0
  11. package/resources/opencode-agents/agent-md-qa-detail.md +95 -0
  12. package/resources/opencode-agents/agent-md-qa-test-case-detail.md +165 -0
  13. package/resources/opencode-agents/agent-review-infrastructure-components-list.md +34 -0
  14. package/resources/opencode-agents/agent-review-infrastructure-list.md +60 -0
  15. package/resources/opencode-agents/agent-review-qa-list.md +60 -0
  16. package/resources/opencode-agents/agent-review-qa-test-cases-list.md +34 -0
  17. package/resources/schemas/context-lists/features-list.schema.json +2 -2
  18. package/resources/schemas/context-lists/infrastructure-components-list.schema.json +175 -0
  19. package/resources/schemas/context-lists/infrastructure-list.schema.json +179 -0
  20. package/resources/schemas/context-lists/qa-list.schema.json +187 -0
  21. package/resources/schemas/context-lists/qa-test-cases-list.schema.json +180 -0
  22. package/resources/schemas/context-lists/zero-reference-classifier-record.schema.json +2 -0
  23. package/resources/schemas/context-lists/zero-reference-routing.schema.json +2 -0
  24. package/resources/schemas/context-lists/zero-reference-triage-record.schema.json +4 -4
  25. package/resources/schemas/index-application-context.schema.json +9 -3
package/dist/main.cjs CHANGED
@@ -6,9 +6,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
9
12
  var __commonJS = (cb, mod) => function __require() {
10
13
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
14
  };
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
12
19
  var __copyProps = (to, from, except, desc) => {
13
20
  if (from && typeof from === "object" || typeof from === "function") {
14
21
  for (let key of __getOwnPropNames(from))
@@ -959,8 +966,8 @@ var require_command = __commonJS({
959
966
  "node_modules/commander/lib/command.js"(exports2) {
960
967
  var EventEmitter = require("node:events").EventEmitter;
961
968
  var childProcess = require("node:child_process");
962
- var path62 = require("node:path");
963
- var fs64 = require("node:fs");
969
+ var path64 = require("node:path");
970
+ var fs65 = require("node:fs");
964
971
  var process2 = require("node:process");
965
972
  var { Argument: Argument2, humanReadableArgName } = require_argument();
966
973
  var { CommanderError: CommanderError2 } = require_error();
@@ -1892,11 +1899,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1892
1899
  let launchWithNode = false;
1893
1900
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1894
1901
  function findFile(baseDir, baseName) {
1895
- const localBin = path62.resolve(baseDir, baseName);
1896
- if (fs64.existsSync(localBin)) return localBin;
1897
- if (sourceExt.includes(path62.extname(baseName))) return void 0;
1902
+ const localBin = path64.resolve(baseDir, baseName);
1903
+ if (fs65.existsSync(localBin)) return localBin;
1904
+ if (sourceExt.includes(path64.extname(baseName))) return void 0;
1898
1905
  const foundExt = sourceExt.find(
1899
- (ext) => fs64.existsSync(`${localBin}${ext}`)
1906
+ (ext) => fs65.existsSync(`${localBin}${ext}`)
1900
1907
  );
1901
1908
  if (foundExt) return `${localBin}${foundExt}`;
1902
1909
  return void 0;
@@ -1908,21 +1915,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
1908
1915
  if (this._scriptPath) {
1909
1916
  let resolvedScriptPath;
1910
1917
  try {
1911
- resolvedScriptPath = fs64.realpathSync(this._scriptPath);
1918
+ resolvedScriptPath = fs65.realpathSync(this._scriptPath);
1912
1919
  } catch (err) {
1913
1920
  resolvedScriptPath = this._scriptPath;
1914
1921
  }
1915
- executableDir = path62.resolve(
1916
- path62.dirname(resolvedScriptPath),
1922
+ executableDir = path64.resolve(
1923
+ path64.dirname(resolvedScriptPath),
1917
1924
  executableDir
1918
1925
  );
1919
1926
  }
1920
1927
  if (executableDir) {
1921
1928
  let localFile = findFile(executableDir, executableFile);
1922
1929
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1923
- const legacyName = path62.basename(
1930
+ const legacyName = path64.basename(
1924
1931
  this._scriptPath,
1925
- path62.extname(this._scriptPath)
1932
+ path64.extname(this._scriptPath)
1926
1933
  );
1927
1934
  if (legacyName !== this._name) {
1928
1935
  localFile = findFile(
@@ -1933,7 +1940,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1933
1940
  }
1934
1941
  executableFile = localFile || executableFile;
1935
1942
  }
1936
- launchWithNode = sourceExt.includes(path62.extname(executableFile));
1943
+ launchWithNode = sourceExt.includes(path64.extname(executableFile));
1937
1944
  let proc;
1938
1945
  if (process2.platform !== "win32") {
1939
1946
  if (launchWithNode) {
@@ -2773,7 +2780,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2773
2780
  * @return {Command}
2774
2781
  */
2775
2782
  nameFromFilename(filename) {
2776
- this._name = path62.basename(filename, path62.extname(filename));
2783
+ this._name = path64.basename(filename, path64.extname(filename));
2777
2784
  return this;
2778
2785
  }
2779
2786
  /**
@@ -2787,9 +2794,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2787
2794
  * @param {string} [path]
2788
2795
  * @return {(string|null|Command)}
2789
2796
  */
2790
- executableDir(path63) {
2791
- if (path63 === void 0) return this._executableDir;
2792
- this._executableDir = path63;
2797
+ executableDir(path65) {
2798
+ if (path65 === void 0) return this._executableDir;
2799
+ this._executableDir = path65;
2793
2800
  return this;
2794
2801
  }
2795
2802
  /**
@@ -5993,7 +6000,7 @@ var require_compile = __commonJS({
5993
6000
  const schOrFunc = root.refs[ref];
5994
6001
  if (schOrFunc)
5995
6002
  return schOrFunc;
5996
- let _sch = resolve24.call(this, root, ref);
6003
+ let _sch = resolve25.call(this, root, ref);
5997
6004
  if (_sch === void 0) {
5998
6005
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
5999
6006
  const { schemaId } = this.opts;
@@ -6020,7 +6027,7 @@ var require_compile = __commonJS({
6020
6027
  function sameSchemaEnv(s1, s2) {
6021
6028
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
6022
6029
  }
6023
- function resolve24(root, ref) {
6030
+ function resolve25(root, ref) {
6024
6031
  let sch;
6025
6032
  while (typeof (sch = this.refs[ref]) == "string")
6026
6033
  ref = sch;
@@ -6235,8 +6242,8 @@ var require_utils = __commonJS({
6235
6242
  }
6236
6243
  return ind;
6237
6244
  }
6238
- function removeDotSegments(path62) {
6239
- let input = path62;
6245
+ function removeDotSegments(path64) {
6246
+ let input = path64;
6240
6247
  const output = [];
6241
6248
  let nextSlash = -1;
6242
6249
  let len = 0;
@@ -6435,8 +6442,8 @@ var require_schemes = __commonJS({
6435
6442
  wsComponent.secure = void 0;
6436
6443
  }
6437
6444
  if (wsComponent.resourceName) {
6438
- const [path62, query] = wsComponent.resourceName.split("?");
6439
- wsComponent.path = path62 && path62 !== "/" ? path62 : void 0;
6445
+ const [path64, query] = wsComponent.resourceName.split("?");
6446
+ wsComponent.path = path64 && path64 !== "/" ? path64 : void 0;
6440
6447
  wsComponent.query = query;
6441
6448
  wsComponent.resourceName = void 0;
6442
6449
  }
@@ -6595,7 +6602,7 @@ var require_fast_uri = __commonJS({
6595
6602
  }
6596
6603
  return uri;
6597
6604
  }
6598
- function resolve24(baseURI, relativeURI, options) {
6605
+ function resolve25(baseURI, relativeURI, options) {
6599
6606
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
6600
6607
  const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
6601
6608
  schemelessOptions.skipEscape = true;
@@ -6822,7 +6829,7 @@ var require_fast_uri = __commonJS({
6822
6829
  var fastUri = {
6823
6830
  SCHEMES,
6824
6831
  normalize: normalize9,
6825
- resolve: resolve24,
6832
+ resolve: resolve25,
6826
6833
  resolveComponent,
6827
6834
  equal,
6828
6835
  serialize,
@@ -10622,12 +10629,12 @@ var require_dist = __commonJS({
10622
10629
  throw new Error(`Unknown format "${name}"`);
10623
10630
  return f;
10624
10631
  };
10625
- function addFormats2(ajv2, list, fs64, exportName) {
10632
+ function addFormats2(ajv2, list, fs65, exportName) {
10626
10633
  var _a;
10627
10634
  var _b;
10628
10635
  (_a = (_b = ajv2.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
10629
10636
  for (const f of list)
10630
- ajv2.addFormat(f, fs64[f]);
10637
+ ajv2.addFormat(f, fs65[f]);
10631
10638
  }
10632
10639
  module2.exports = exports2 = formatsPlugin;
10633
10640
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -10635,9 +10642,160 @@ var require_dist = __commonJS({
10635
10642
  }
10636
10643
  });
10637
10644
 
10645
+ // src/analysis/migrateSrs52Context.ts
10646
+ var migrateSrs52Context_exports = {};
10647
+ __export(migrateSrs52Context_exports, {
10648
+ migrateSrs52Context: () => migrateSrs52Context
10649
+ });
10650
+ function readJson6(p) {
10651
+ try {
10652
+ return JSON.parse(fs63.readFileSync(p, "utf-8"));
10653
+ } catch {
10654
+ return null;
10655
+ }
10656
+ }
10657
+ function writeJson2(p, data, dryRun) {
10658
+ if (dryRun) {
10659
+ return;
10660
+ }
10661
+ fs63.mkdirSync(path62.dirname(p), { recursive: true });
10662
+ fs63.writeFileSync(p, `${JSON.stringify(data, null, 2)}
10663
+ `, "utf-8");
10664
+ }
10665
+ function nextCode(prefix, existing) {
10666
+ let n = 1;
10667
+ while (existing.has(`${prefix}-${String(n).padStart(2, "0")}`)) {
10668
+ n += 1;
10669
+ }
10670
+ const code = `${prefix}-${String(n).padStart(2, "0")}`;
10671
+ existing.add(code);
10672
+ return code;
10673
+ }
10674
+ function migrateSrs52Context(opts) {
10675
+ const contextDir2 = path62.resolve(opts.contextDir);
10676
+ const featuresPath = path62.join(contextDir2, "features-list.json");
10677
+ const fl = readJson6(featuresPath);
10678
+ if (!fl?.features) {
10679
+ return { ok: false, migratedInfrastructure: 0, migratedQa: 0, errors: ["features-list.json missing or invalid"] };
10680
+ }
10681
+ const infraPath = path62.join(contextDir2, "infrastructure-list.json");
10682
+ const qaPath = path62.join(contextDir2, "qa-list.json");
10683
+ const infraFile = readJson6(infraPath) ?? {
10684
+ infrastructure: [],
10685
+ revisionLog: []
10686
+ };
10687
+ const qaFile = readJson6(qaPath) ?? { qa: [], revisionLog: [] };
10688
+ const inCodes = new Set(
10689
+ (Array.isArray(infraFile.infrastructure) ? infraFile.infrastructure : []).map((r) => r.code).filter((c) => typeof c === "string")
10690
+ );
10691
+ const qaCodes = new Set(
10692
+ (Array.isArray(qaFile.qa) ? qaFile.qa : []).map((r) => r.code).filter((c) => typeof c === "string")
10693
+ );
10694
+ const errors = [];
10695
+ let migratedInfrastructure = 0;
10696
+ let migratedQa = 0;
10697
+ const kept = [];
10698
+ for (const row2 of fl.features) {
10699
+ const kind = typeof row2.featureKind === "string" ? row2.featureKind : "product";
10700
+ const feCode = typeof row2.code === "string" ? row2.code : "";
10701
+ if (!FE3.test(feCode)) {
10702
+ kept.push(row2);
10703
+ continue;
10704
+ }
10705
+ if (kind === "infrastructure") {
10706
+ const inCode = nextCode("IN", inCodes);
10707
+ const slug = typeof row2.slug === "string" ? row2.slug : "migrated";
10708
+ infraFile.infrastructure = Array.isArray(infraFile.infrastructure) ? infraFile.infrastructure : [];
10709
+ infraFile.infrastructure.push({
10710
+ code: inCode,
10711
+ name: row2.name ?? inCode,
10712
+ slug,
10713
+ sourceReferences: row2.sourceReferences ?? [],
10714
+ description: row2.description
10715
+ });
10716
+ const ucPath = path62.join(contextDir2, `${feCode}-use-cases-list.json`);
10717
+ const ucFile = readJson6(ucPath);
10718
+ if (ucFile?.useCases?.length) {
10719
+ const compPath = path62.join(contextDir2, `${inCode}-components-list.json`);
10720
+ const components = ucFile.useCases.filter((u) => typeof u.code === "string" && UC3.test(u.code)).map((u, i) => ({
10721
+ code: `IC-${String(i + 1).padStart(2, "0")}`,
10722
+ name: u.name ?? u.code,
10723
+ slug: u.slug ?? `ic-${i + 1}`,
10724
+ sourceReferences: u.sourceReferences ?? []
10725
+ }));
10726
+ writeJson2(
10727
+ compPath,
10728
+ {
10729
+ infrastructureCode: inCode,
10730
+ components,
10731
+ revisionLog: [{ summary: `Migrated from ${feCode} use cases (SRS-52)` }]
10732
+ },
10733
+ Boolean(opts.dryRun)
10734
+ );
10735
+ }
10736
+ migratedInfrastructure += 1;
10737
+ continue;
10738
+ }
10739
+ if (kind === "testing") {
10740
+ const qaCode = nextCode("QA", qaCodes);
10741
+ const covers = Array.isArray(row2.coversCodes) ? row2.coversCodes.filter((c) => typeof c === "string") : [];
10742
+ if (!covers.length && !opts.force) {
10743
+ errors.push(`${feCode}: cannot derive coversCodes for QA migration (use --force or --mapping-json)`);
10744
+ kept.push(row2);
10745
+ continue;
10746
+ }
10747
+ qaFile.qa = Array.isArray(qaFile.qa) ? qaFile.qa : [];
10748
+ qaFile.qa.push({
10749
+ code: qaCode,
10750
+ name: row2.name ?? qaCode,
10751
+ slug: typeof row2.slug === "string" ? row2.slug : "migrated",
10752
+ coversCodes: covers.length ? covers : ["FE-01"],
10753
+ sourceReferences: row2.sourceReferences ?? []
10754
+ });
10755
+ migratedQa += 1;
10756
+ continue;
10757
+ }
10758
+ kept.push(row2);
10759
+ }
10760
+ if (errors.length && !opts.force) {
10761
+ return { ok: false, migratedInfrastructure, migratedQa, errors };
10762
+ }
10763
+ fl.features = kept;
10764
+ fl.revisionLog = Array.isArray(fl.revisionLog) ? fl.revisionLog : [];
10765
+ fl.revisionLog.push({
10766
+ summary: `SRS-52 migration: moved ${migratedInfrastructure} infrastructure and ${migratedQa} QA rows`
10767
+ });
10768
+ infraFile.revisionLog = Array.isArray(infraFile.revisionLog) ? infraFile.revisionLog : [];
10769
+ if (migratedInfrastructure) {
10770
+ infraFile.revisionLog.push({ summary: `SRS-52 migration: added ${migratedInfrastructure} row(s) from features` });
10771
+ }
10772
+ qaFile.revisionLog = Array.isArray(qaFile.revisionLog) ? qaFile.revisionLog : [];
10773
+ if (migratedQa) {
10774
+ qaFile.revisionLog.push({ summary: `SRS-52 migration: added ${migratedQa} row(s) from features` });
10775
+ }
10776
+ writeJson2(featuresPath, fl, Boolean(opts.dryRun));
10777
+ if (migratedInfrastructure || infraFile.infrastructure?.length) {
10778
+ writeJson2(infraPath, infraFile, Boolean(opts.dryRun));
10779
+ }
10780
+ if (migratedQa || qaFile.qa?.length) {
10781
+ writeJson2(qaPath, qaFile, Boolean(opts.dryRun));
10782
+ }
10783
+ return { ok: true, migratedInfrastructure, migratedQa, errors };
10784
+ }
10785
+ var fs63, path62, FE3, UC3;
10786
+ var init_migrateSrs52Context = __esm({
10787
+ "src/analysis/migrateSrs52Context.ts"() {
10788
+ "use strict";
10789
+ fs63 = __toESM(require("fs"));
10790
+ path62 = __toESM(require("path"));
10791
+ FE3 = /^FE-\d+$/;
10792
+ UC3 = /^UC-\d+$/;
10793
+ }
10794
+ });
10795
+
10638
10796
  // src/cli/main.ts
10639
- var fs63 = __toESM(require("node:fs"));
10640
- var path61 = __toESM(require("node:path"));
10797
+ var fs64 = __toESM(require("node:fs"));
10798
+ var path63 = __toESM(require("node:path"));
10641
10799
 
10642
10800
  // src/cli/exitCodes.ts
10643
10801
  var OsExit = {
@@ -10823,7 +10981,7 @@ function createEasyspecsCliProgram() {
10823
10981
  program2.command("help").description("Show help");
10824
10982
  program2.command("version").description("Print CLI version");
10825
10983
  const workstation = program2.command("workstation").description("Host inventory for AI workstation sizing");
10826
- workstation.command("info").description("Print CPU, RAM, and SRS-69 N_sys (macOS and Linux only; use global --json)").allowUnknownOption(false);
10984
+ workstation.command("info").description("Print CPU, RAM, and SRS-75 N_sys (macOS and Linux only; use global --json)").allowUnknownOption(false);
10827
10985
  program2.command("doctor").description("Check readiness and/or inspect configuration").option("--readiness", "Print readiness summary (default)").option("--inspect-config", "Print redacted merged configuration");
10828
10986
  const auth = program2.command("auth").description("Authentication");
10829
10987
  auth.command("login").description("Log in").option("--email <email>", "Email address").option("--password <password>", "Password").option("--session-path <path>", "Session JSON path override for this run").option("--ci", "Use easyspecs.auth.ciLogin from config when argv credentials are omitted");
@@ -11366,14 +11524,23 @@ var import_node_child_process2 = require("node:child_process");
11366
11524
  var ABSOLUTE_MAX_CONCURRENT_AI = 64;
11367
11525
 
11368
11526
  // src/config/machineConcurrentAiFromHost.ts
11369
- var AGENTS_PER_PHYSICAL_CORE = 1;
11370
- var RAM_GIB_PER_AGENT = 8;
11527
+ var ANCHOR_A_N_SYS = 15;
11528
+ var ANCHOR_B_P = 11;
11529
+ var ANCHOR_B_N_SYS = 100;
11530
+ var CPU_SLOPE_PER_CORE = 8.5;
11531
+ var GIB_PER_CORE_REF = 4;
11371
11532
  function clampProductConcurrentLimits(n) {
11372
11533
  if (!Number.isFinite(n)) {
11373
11534
  return DEFAULT_MAX_CONCURRENT_AI;
11374
11535
  }
11375
11536
  return Math.min(ABSOLUTE_MAX_CONCURRENT_AI, Math.max(1, Math.floor(n)));
11376
11537
  }
11538
+ function nCpuFloatFromPhysicalCpus(p) {
11539
+ if (p <= ANCHOR_B_P) {
11540
+ return ANCHOR_A_N_SYS + CPU_SLOPE_PER_CORE * (p - 1);
11541
+ }
11542
+ return ANCHOR_B_N_SYS + CPU_SLOPE_PER_CORE * (p - ANCHOR_B_P);
11543
+ }
11377
11544
  function machineConcurrentAiCapFromHost(profile) {
11378
11545
  const memBytes = Number.isFinite(profile.totalMemBytes) && profile.totalMemBytes > 0 ? profile.totalMemBytes : 0;
11379
11546
  const rGib = memBytes / 2 ** 30;
@@ -11384,8 +11551,8 @@ function machineConcurrentAiCapFromHost(profile) {
11384
11551
  if (!Number.isFinite(rGib) || rGib <= 0) {
11385
11552
  return 1;
11386
11553
  }
11387
- const nCpuFloat = AGENTS_PER_PHYSICAL_CORE * p;
11388
- const ramRefGib = RAM_GIB_PER_AGENT * p;
11554
+ const nCpuFloat = nCpuFloatFromPhysicalCpus(p);
11555
+ const ramRefGib = GIB_PER_CORE_REF * p;
11389
11556
  const ramFactor = Math.min(1, rGib / ramRefGib);
11390
11557
  const nFloat = nCpuFloat * ramFactor;
11391
11558
  const rounded = Math.round(nFloat);
@@ -12694,7 +12861,7 @@ function stringifyForSrs13Debug(value) {
12694
12861
  return truncateForSrs13DebugLog(String(value));
12695
12862
  }
12696
12863
  }
12697
- function logSrs13HttpDebug(log, path62, method, requestBody, error) {
12864
+ function logSrs13HttpDebug(log, path64, method, requestBody, error) {
12698
12865
  if (!log) {
12699
12866
  return;
12700
12867
  }
@@ -12704,7 +12871,7 @@ function logSrs13HttpDebug(log, path62, method, requestBody, error) {
12704
12871
  } catch {
12705
12872
  bodyBytes = 0;
12706
12873
  }
12707
- log(`[pipeline:upload] debug ${method} ${path62} \u2014 request body ~${bodyBytes} bytes (JSON below):`);
12874
+ log(`[pipeline:upload] debug ${method} ${path64} \u2014 request body ~${bodyBytes} bytes (JSON below):`);
12708
12875
  log(stringifyForSrs13Debug(requestBody));
12709
12876
  const raw = httpApiResponseBody(error);
12710
12877
  const status = error && typeof error === "object" && "status" in error ? Number(error.status) : void 0;
@@ -12808,18 +12975,18 @@ async function runParallelSinglesIntoAccum(requestJson, applicationId, paths, on
12808
12975
  const recentWaveMs = [];
12809
12976
  let idx = 0;
12810
12977
  const runOne = async (absPath) => {
12811
- const basename17 = path12.basename(absPath);
12978
+ const basename18 = path12.basename(absPath);
12812
12979
  let content;
12813
12980
  try {
12814
12981
  content = fs12.readFileSync(absPath, "utf8");
12815
12982
  } catch (e) {
12816
12983
  const msg = errorMessage(e);
12817
- logSrs13Failure(log, `read file file=${basename17}`, msg);
12984
+ logSrs13Failure(log, `read file file=${basename18}`, msg);
12818
12985
  accum.failed.push({ path: absPath, message: msg });
12819
12986
  return;
12820
12987
  }
12821
12988
  const existingId = resolveExistingId(absPath);
12822
- const payload = buildSrsDiscoverySavePayload(applicationId, basename17, content, existingId);
12989
+ const payload = buildSrsDiscoverySavePayload(applicationId, basename18, content, existingId);
12823
12990
  try {
12824
12991
  await postSingleCreate(requestJson, payload, log);
12825
12992
  accum.succeeded.push(absPath);
@@ -12833,7 +13000,7 @@ async function runParallelSinglesIntoAccum(requestJson, applicationId, paths, on
12833
13000
  throw e;
12834
13001
  }
12835
13002
  const msg = errorMessage(e);
12836
- logSrs13Failure(log, `POST /api/content/srs_discovery file=${basename17}`, msg);
13003
+ logSrs13Failure(log, `POST /api/content/srs_discovery file=${basename18}`, msg);
12837
13004
  accum.failed.push({ path: absPath, message: msg });
12838
13005
  }
12839
13006
  };
@@ -12915,18 +13082,18 @@ async function executeContextSrsDiscoveryUploadPhase(filePaths, phaseOpts, accum
12915
13082
  const items = [];
12916
13083
  const chunkPaths = [];
12917
13084
  for (const absPath of chunk) {
12918
- const basename17 = path12.basename(absPath);
13085
+ const basename18 = path12.basename(absPath);
12919
13086
  let content;
12920
13087
  try {
12921
13088
  content = fs12.readFileSync(absPath, "utf8");
12922
13089
  } catch (e) {
12923
13090
  const msg = errorMessage(e);
12924
- logSrs13Failure(log, `read file file=${basename17}`, msg);
13091
+ logSrs13Failure(log, `read file file=${basename18}`, msg);
12925
13092
  accum.failed.push({ path: absPath, message: msg });
12926
13093
  continue;
12927
13094
  }
12928
13095
  const existingId = resolveExistingId(absPath);
12929
- items.push(buildSrsDiscoverySavePayloadForBatch(applicationId, basename17, content, existingId));
13096
+ items.push(buildSrsDiscoverySavePayloadForBatch(applicationId, basename18, content, existingId));
12930
13097
  chunkPaths.push(absPath);
12931
13098
  }
12932
13099
  if (items.length === 0) {
@@ -13317,7 +13484,7 @@ function createFileBackedWorkspaceState(repoRoot) {
13317
13484
 
13318
13485
  // src/pipelines/synthesis/synthesisPipeline.ts
13319
13486
  var fs37 = __toESM(require("fs"));
13320
- var path32 = __toESM(require("path"));
13487
+ var path33 = __toESM(require("path"));
13321
13488
 
13322
13489
  // src/analysis/analysisDynamicTestSteps.ts
13323
13490
  var fs14 = __toESM(require("fs"));
@@ -13342,6 +13509,10 @@ var BH_ROW = /^BH-\d+$/;
13342
13509
  var SV_ROW = /^SV-\d+$/;
13343
13510
  var ME_ROW = /^ME-\d+$/;
13344
13511
  var TS_ROW = /^TS-\d+$/;
13512
+ var IN_ROW = /^IN-\d+$/;
13513
+ var IC_ROW = /^IC-\d+$/;
13514
+ var QA_ROW = /^QA-\d+$/;
13515
+ var TC_ROW = /^TC-\d+$/;
13345
13516
  function safeCoordStr(v) {
13346
13517
  return typeof v === "string" ? v.trim() : "";
13347
13518
  }
@@ -13428,6 +13599,88 @@ function discoverServiceTreeRows(contextDir2) {
13428
13599
  }
13429
13600
  return out;
13430
13601
  }
13602
+ function discoverInfrastructureRows(contextDir2) {
13603
+ const p = path14.join(contextDir2, "infrastructure-list.json");
13604
+ const file = readJson(p);
13605
+ const rows = Array.isArray(file?.infrastructure) ? file.infrastructure : [];
13606
+ const out = [];
13607
+ for (const row2 of rows) {
13608
+ const code = safeCoordStr(row2.code);
13609
+ if (!IN_ROW.test(code)) {
13610
+ continue;
13611
+ }
13612
+ const slug = slugOrFallbackCoord(row2);
13613
+ if (!slug) {
13614
+ continue;
13615
+ }
13616
+ out.push({ code, name: safeCoordStr(row2.name) || code, slug });
13617
+ }
13618
+ return out;
13619
+ }
13620
+ function discoverInfrastructureComponentTreeRows(contextDir2) {
13621
+ const parents = discoverInfrastructureRows(contextDir2);
13622
+ const out = [];
13623
+ for (const parent of parents) {
13624
+ const clPath = path14.join(contextDir2, `${parent.code}-components-list.json`);
13625
+ const clFile = readJson(clPath);
13626
+ const comps = Array.isArray(clFile?.components) ? clFile.components : [];
13627
+ const row2 = { ...parent, components: [] };
13628
+ for (const c of comps) {
13629
+ const icCode = safeCoordStr(c.code);
13630
+ if (!IC_ROW.test(icCode)) {
13631
+ continue;
13632
+ }
13633
+ const slug = slugOrFallbackCoord(c);
13634
+ if (!slug) {
13635
+ continue;
13636
+ }
13637
+ row2.components.push({ code: icCode, name: safeCoordStr(c.name) || icCode, slug });
13638
+ }
13639
+ out.push(row2);
13640
+ }
13641
+ return out;
13642
+ }
13643
+ function discoverQaRows(contextDir2) {
13644
+ const p = path14.join(contextDir2, "qa-list.json");
13645
+ const file = readJson(p);
13646
+ const rows = Array.isArray(file?.qa) ? file.qa : [];
13647
+ const out = [];
13648
+ for (const row2 of rows) {
13649
+ const code = safeCoordStr(row2.code);
13650
+ if (!QA_ROW.test(code)) {
13651
+ continue;
13652
+ }
13653
+ const slug = slugOrFallbackCoord(row2);
13654
+ if (!slug) {
13655
+ continue;
13656
+ }
13657
+ out.push({ code, name: safeCoordStr(row2.name) || code, slug });
13658
+ }
13659
+ return out;
13660
+ }
13661
+ function discoverQaTestCaseTreeRows(contextDir2) {
13662
+ const parents = discoverQaRows(contextDir2);
13663
+ const out = [];
13664
+ for (const parent of parents) {
13665
+ const tcPath = path14.join(contextDir2, `${parent.code}-test-cases-list.json`);
13666
+ const tcFile = readJson(tcPath);
13667
+ const tcs = Array.isArray(tcFile?.testCases) ? tcFile.testCases : [];
13668
+ const row2 = { ...parent, testCases: [] };
13669
+ for (const t of tcs) {
13670
+ const tcCode = safeCoordStr(t.code);
13671
+ if (!TC_ROW.test(tcCode)) {
13672
+ continue;
13673
+ }
13674
+ const slug = slugOrFallbackCoord(t);
13675
+ if (!slug) {
13676
+ continue;
13677
+ }
13678
+ row2.testCases.push({ code: tcCode, name: safeCoordStr(t.name) || tcCode, slug });
13679
+ }
13680
+ out.push(row2);
13681
+ }
13682
+ return out;
13683
+ }
13431
13684
  function discoverTechStackToolRows(contextDir2) {
13432
13685
  const tsPath = path14.join(contextDir2, "tech-stack-list.json");
13433
13686
  const tsFile = readJson(tsPath);
@@ -13466,12 +13719,14 @@ var ANALYSIS_STATIC_CONTEXT_OUTPUTS = [
13466
13719
  "experiences-list.json",
13467
13720
  "services-list.json",
13468
13721
  "data-model-list.json",
13469
- "tech-stack-list.json"
13722
+ "tech-stack-list.json",
13723
+ "infrastructure-list.json",
13724
+ "qa-list.json"
13470
13725
  ];
13471
13726
  function discoverDynamicAnalysisTestSteps(contextDir2) {
13472
- const staticOutputs = ANALYSIS_STATIC_CONTEXT_OUTPUTS.map((basename17) => ({
13473
- basename: basename17,
13474
- exists: nonEmptyContextFile(path14.join(contextDir2, basename17))
13727
+ const staticOutputs = ANALYSIS_STATIC_CONTEXT_OUTPUTS.map((basename18) => ({
13728
+ basename: basename18,
13729
+ exists: nonEmptyContextFile(path14.join(contextDir2, basename18))
13475
13730
  }));
13476
13731
  const featuresPath = path14.join(contextDir2, "features-list.json");
13477
13732
  const featuresData = readJson(featuresPath);
@@ -13642,8 +13897,8 @@ function aceCurationPath(contextDir2, agentStem, runId) {
13642
13897
  function aceConsolidatedSessionsJsonlPath(contextDir2) {
13643
13898
  return path15.join(aceLearningsRoot(contextDir2), ACE_CONSOLIDATED_SESSIONS_JSONL);
13644
13899
  }
13645
- function opencodeAceSchemaPath(worktreeRoot, basename17) {
13646
- return path15.join(worktreeRoot, ".opencode", "schemas", "ace", basename17);
13900
+ function opencodeAceSchemaPath(worktreeRoot, basename18) {
13901
+ return path15.join(worktreeRoot, ".opencode", "schemas", "ace", basename18);
13647
13902
  }
13648
13903
 
13649
13904
  // src/analysis/aceJsonValidate.ts
@@ -14060,7 +14315,7 @@ function materializeOpenCodeAgentsWithAce(extensionResourcesAgents, analysisChec
14060
14315
 
14061
14316
  // src/workstations/aiWorkstation.ts
14062
14317
  var fs30 = __toESM(require("fs"));
14063
- var path26 = __toESM(require("path"));
14318
+ var path27 = __toESM(require("path"));
14064
14319
 
14065
14320
  // src/analysis/promptTemplates.ts
14066
14321
  function fillMarkdownPrompt(p) {
@@ -14180,7 +14435,15 @@ var CONTEXT_SYNTHESIS_TEST_STEP_ORDER = [
14180
14435
  "listUseCases",
14181
14436
  "reviewUseCasesList",
14182
14437
  "listScenarios",
14183
- "reviewScenariosList"
14438
+ "reviewScenariosList",
14439
+ "listInfrastructure",
14440
+ "reviewInfrastructureList",
14441
+ "listQa",
14442
+ "reviewQaList",
14443
+ "listInfrastructureComponents",
14444
+ "reviewInfrastructureComponentsList",
14445
+ "listQaTestCases",
14446
+ "reviewQaTestCasesList"
14184
14447
  ];
14185
14448
  function synthesisStepLabel(step, ctx) {
14186
14449
  if (step === "listUseCases" || step === "reviewUseCasesList") {
@@ -14192,6 +14455,12 @@ function synthesisStepLabel(step, ctx) {
14192
14455
  if (step === "listEntityFields" || step === "reviewEntityFieldsList") {
14193
14456
  return ctx?.entityCode ? `${ctx.entityCode}-fields-list.json` : "DM-<nn>-fields-list.json (per entity)";
14194
14457
  }
14458
+ if (step === "listInfrastructureComponents" || step === "reviewInfrastructureComponentsList") {
14459
+ return ctx?.infrastructureCode ? `${ctx.infrastructureCode}-components-list.json` : "IN-<nn>-components-list.json (per infrastructure)";
14460
+ }
14461
+ if (step === "listQaTestCases" || step === "reviewQaTestCasesList") {
14462
+ return ctx?.qaCode ? `${ctx.qaCode}-test-cases-list.json` : "QA-<nn>-test-cases-list.json (per QA)";
14463
+ }
14195
14464
  const m = {
14196
14465
  docsProject: "project.md",
14197
14466
  architecture: "architecture.md",
@@ -14205,7 +14474,11 @@ function synthesisStepLabel(step, ctx) {
14205
14474
  listDataModel: "data-model-list.json",
14206
14475
  reviewDataModelList: "data-model-list.json",
14207
14476
  listTechStack: "tech-stack-list.json",
14208
- reviewTechStackList: "tech-stack-list.json"
14477
+ reviewTechStackList: "tech-stack-list.json",
14478
+ listInfrastructure: "infrastructure-list.json",
14479
+ reviewInfrastructureList: "infrastructure-list.json",
14480
+ listQa: "qa-list.json",
14481
+ reviewQaList: "qa-list.json"
14209
14482
  };
14210
14483
  return m[step];
14211
14484
  }
@@ -14401,9 +14674,9 @@ function parsePsRssDarwinKilobytes(stdout) {
14401
14674
  return n;
14402
14675
  }
14403
14676
  function readLinuxPidRssBytes(pid) {
14404
- const path62 = `/proc/${pid}/status`;
14677
+ const path64 = `/proc/${pid}/status`;
14405
14678
  try {
14406
- const content = fs18.readFileSync(path62, "utf8");
14679
+ const content = fs18.readFileSync(path64, "utf8");
14407
14680
  const kb = parseVmRssKilobytesFromProcStatus(content);
14408
14681
  if (kb === null) {
14409
14682
  return { kind: "unknown", reason: "VmRSS_missing" };
@@ -14600,9 +14873,9 @@ function runOpenCodeAgent(cwd, args, options) {
14600
14873
  log?.(`${procTag} command: ${formatCliCommandForLog(cmd, args)}`);
14601
14874
  log?.(`${procTag} cwd: ${JSON.stringify(cwd)}`);
14602
14875
  log?.(`${procTag} argv: ${JSON.stringify(args)}`);
14603
- return new Promise((resolve24) => {
14876
+ return new Promise((resolve25) => {
14604
14877
  if (sig?.aborted) {
14605
- resolve24({ ok: false, message: "Stopped by user.", cancelled: true });
14878
+ resolve25({ ok: false, message: "Stopped by user.", cancelled: true });
14606
14879
  return;
14607
14880
  }
14608
14881
  const spawnEnv = options?.childEnv && Object.keys(options.childEnv).length > 0 ? { ...process.env, ...options.childEnv } : process.env;
@@ -14697,7 +14970,7 @@ ${truncateForDiag(outBody, DIAG_STDOUT_MAX)}`);
14697
14970
  if (diag) {
14698
14971
  finishDiag(diag.label, diag.code, diag.dumpStreams);
14699
14972
  }
14700
- resolve24(result);
14973
+ resolve25(result);
14701
14974
  };
14702
14975
  let onAbort;
14703
14976
  const clearAbortHandler = () => {
@@ -15671,7 +15944,75 @@ async function runAceTracePhase(params) {
15671
15944
 
15672
15945
  // src/analysis/coordinationListJsonValidate.ts
15673
15946
  var fs24 = __toESM(require("fs"));
15947
+ var path22 = __toESM(require("path"));
15674
15948
  var import__4 = __toESM(require__());
15949
+
15950
+ // src/analysis/qaCoversCodesAnchor.ts
15951
+ var FE_ROOT = /^FE-[0-9]+$/;
15952
+ var FE_UC = /^FE-[0-9]+_UC-[0-9]+$/;
15953
+ var FE_UC_SC = /^FE-[0-9]+_UC-[0-9]+_SC-[0-9]+$/;
15954
+ var SV_ROOT = /^SV-[0-9]+$/;
15955
+ var SV_ME = /^SV-[0-9]+_ME-[0-9]+$/;
15956
+ function isFeatureSubtreeCode(code) {
15957
+ const s = code.trim();
15958
+ return FE_ROOT.test(s) || FE_UC.test(s) || FE_UC_SC.test(s);
15959
+ }
15960
+ function isServiceSubtreeCode(code) {
15961
+ const s = code.trim();
15962
+ return SV_ROOT.test(s) || SV_ME.test(s);
15963
+ }
15964
+ function hasQaCoversCodesAnchor(coversCodes) {
15965
+ return coversCodes.some((c) => isFeatureSubtreeCode(c) || isServiceSubtreeCode(c));
15966
+ }
15967
+ function validateQaCoversCodesAnchors(coversCodes) {
15968
+ if (!Array.isArray(coversCodes)) {
15969
+ return { ok: false, errors: ["coversCodes must be a non-empty array"] };
15970
+ }
15971
+ const codes = coversCodes.filter((c) => typeof c === "string" && c.trim().length > 0);
15972
+ if (codes.length === 0) {
15973
+ return { ok: false, errors: ["coversCodes must contain at least one non-empty string (minItems: 1)"] };
15974
+ }
15975
+ if (!hasQaCoversCodesAnchor(codes)) {
15976
+ return {
15977
+ ok: false,
15978
+ errors: [
15979
+ "coversCodes must include at least one Feature-subtree code (FE-*, FE-*_UC-*, FE-*_UC-*_SC-*) or Service-subtree code (SV-*, SV-*_ME-*)"
15980
+ ]
15981
+ };
15982
+ }
15983
+ return { ok: true };
15984
+ }
15985
+ function validateQaListDocumentAnchors(data, basename18) {
15986
+ if (data === null || typeof data !== "object" || Array.isArray(data)) {
15987
+ return { ok: true };
15988
+ }
15989
+ const o = data;
15990
+ const errors = [];
15991
+ if (basename18 === "qa-list.json") {
15992
+ const rows = Array.isArray(o.qa) ? o.qa : [];
15993
+ for (let i = 0; i < rows.length; i++) {
15994
+ const row2 = rows[i];
15995
+ const code = typeof row2?.code === "string" ? row2.code : `qa[${i}]`;
15996
+ const v = validateQaCoversCodesAnchors(row2?.coversCodes);
15997
+ if (!v.ok) {
15998
+ errors.push(`${code}: ${v.errors.join("; ")}`);
15999
+ }
16000
+ }
16001
+ } else if (/^QA-[0-9]+-test-cases-list\.json$/i.test(basename18)) {
16002
+ const rows = Array.isArray(o.testCases) ? o.testCases : [];
16003
+ for (let i = 0; i < rows.length; i++) {
16004
+ const row2 = rows[i];
16005
+ const code = typeof row2?.code === "string" ? row2.code : `testCases[${i}]`;
16006
+ const v = validateQaCoversCodesAnchors(row2?.coversCodes);
16007
+ if (!v.ok) {
16008
+ errors.push(`${code}: ${v.errors.join("; ")}`);
16009
+ }
16010
+ }
16011
+ }
16012
+ return errors.length ? { ok: false, errors } : { ok: true };
16013
+ }
16014
+
16015
+ // src/analysis/coordinationListJsonValidate.ts
15675
16016
  function stripUtf8Bom3(s) {
15676
16017
  return s.length > 0 && s.charCodeAt(0) === 65279 ? s.slice(1) : s;
15677
16018
  }
@@ -15806,6 +16147,19 @@ function validateCoordinationListJson(jsonAbsolutePath, schemaAbsolutePath) {
15806
16147
  }
15807
16148
  };
15808
16149
  }
16150
+ const basename18 = path22.basename(jsonAbsolutePath);
16151
+ const qaAnchor = validateQaListDocumentAnchors(data, basename18);
16152
+ if (!qaAnchor.ok) {
16153
+ const errorsText = qaAnchor.errors.map((e) => `- ${e}`).join("\n");
16154
+ return {
16155
+ ok: false,
16156
+ failure: {
16157
+ kind: "qa_covers",
16158
+ message: "QA coversCodes anchor validation failed (SRS-52 \xA73.1).",
16159
+ errorsText
16160
+ }
16161
+ };
16162
+ }
15809
16163
  return { ok: true };
15810
16164
  }
15811
16165
  var FILE_PREVIEW_MAX = 4e3;
@@ -15829,11 +16183,19 @@ function coordinationListSchemaBasenameForOutputBasename(outputBasename) {
15829
16183
  "experiences-list.json": "experiences-list.schema.json",
15830
16184
  "services-list.json": "services-list.schema.json",
15831
16185
  "data-model-list.json": "data-model-list.schema.json",
15832
- "tech-stack-list.json": "tech-stack-list.schema.json"
16186
+ "tech-stack-list.json": "tech-stack-list.schema.json",
16187
+ "infrastructure-list.json": "infrastructure-list.schema.json",
16188
+ "qa-list.json": "qa-list.schema.json"
15833
16189
  };
15834
16190
  if (exact[outputBasename]) {
15835
16191
  return exact[outputBasename];
15836
16192
  }
16193
+ if (/^IN-\d+-components-list\.json$/i.test(outputBasename)) {
16194
+ return "infrastructure-components-list.schema.json";
16195
+ }
16196
+ if (/^QA-\d+-test-cases-list\.json$/i.test(outputBasename)) {
16197
+ return "qa-test-cases-list.schema.json";
16198
+ }
15837
16199
  if (/^FE-\d+-use-cases-list\.json$/.test(outputBasename)) {
15838
16200
  return "use-cases-list.schema.json";
15839
16201
  }
@@ -15876,6 +16238,8 @@ function formatCoordinationJsonRepairAppendix(outputBasename, failure, rawFilePr
15876
16238
  lines.push(failure.errorsText, "");
15877
16239
  } else if (failure.kind === "forbidden_ref") {
15878
16240
  lines.push("**Forbidden sourceReferences path:**", "", failure.message, "");
16241
+ } else if (failure.kind === "qa_covers") {
16242
+ lines.push(failure.message, "", failure.errorsText, "");
15879
16243
  } else {
15880
16244
  lines.push(`**Read error:** ${failure.message}`, "");
15881
16245
  }
@@ -15888,12 +16252,12 @@ function formatCoordinationJsonRepairAppendix(outputBasename, failure, rawFilePr
15888
16252
 
15889
16253
  // src/analysis/coordinationListStableWrite.ts
15890
16254
  var fs26 = __toESM(require("fs"));
15891
- var path23 = __toESM(require("path"));
16255
+ var path24 = __toESM(require("path"));
15892
16256
 
15893
16257
  // src/analysis/coordinationListFileLock.ts
15894
16258
  var fs25 = __toESM(require("fs"));
15895
16259
  var os3 = __toESM(require("os"));
15896
- var path22 = __toESM(require("path"));
16260
+ var path23 = __toESM(require("path"));
15897
16261
  var DEFAULT_COORDINATION_LIST_LOCK_TIMEOUT_MS = 12e4;
15898
16262
  var DEFAULT_COORDINATION_LIST_LOCK_STALE_MS = 30 * 60 * 1e3;
15899
16263
  function sleepMs(ms) {
@@ -15963,7 +16327,7 @@ async function withCoordinationListFileLock(listJsonAbsolutePath, options, fn) {
15963
16327
  fs25.writeFileSync(lockAbs, `${JSON.stringify(body, null, 0)}
15964
16328
  `, { encoding: "utf-8", flag: "wx" });
15965
16329
  acquired = true;
15966
- diagnosticLog?.(`[coord-lock] acquired ${path22.basename(lockAbs)}`);
16330
+ diagnosticLog?.(`[coord-lock] acquired ${path23.basename(lockAbs)}`);
15967
16331
  } catch (e) {
15968
16332
  const err = e;
15969
16333
  if (err.code !== "EEXIST") {
@@ -15985,7 +16349,7 @@ async function withCoordinationListFileLock(listJsonAbsolutePath, options, fn) {
15985
16349
  } finally {
15986
16350
  try {
15987
16351
  fs25.unlinkSync(lockAbs);
15988
- diagnosticLog?.(`[coord-lock] released ${path22.basename(lockAbs)}`);
16352
+ diagnosticLog?.(`[coord-lock] released ${path23.basename(lockAbs)}`);
15989
16353
  } catch {
15990
16354
  }
15991
16355
  }
@@ -16082,6 +16446,20 @@ function coordinationListProfileForBasename(outputBasename) {
16082
16446
  codeRe: /^TS-\d+$/i,
16083
16447
  slugField: "slug",
16084
16448
  nestedOnPrimary: []
16449
+ },
16450
+ "infrastructure-list.json": {
16451
+ id: "infrastructure",
16452
+ primaryKey: "infrastructure",
16453
+ codeRe: /^IN-\d+$/i,
16454
+ slugField: "slug",
16455
+ nestedOnPrimary: []
16456
+ },
16457
+ "qa-list.json": {
16458
+ id: "qa",
16459
+ primaryKey: "qa",
16460
+ codeRe: /^QA-\d+$/i,
16461
+ slugField: "slug",
16462
+ nestedOnPrimary: []
16085
16463
  }
16086
16464
  };
16087
16465
  if (exact[outputBasename]) {
@@ -16113,6 +16491,24 @@ function coordinationListProfileForBasename(outputBasename) {
16113
16491
  nestedOnPrimary: []
16114
16492
  };
16115
16493
  }
16494
+ if (/^IN-\d+-components-list\.json$/i.test(outputBasename)) {
16495
+ return {
16496
+ id: "infrastructure-components",
16497
+ primaryKey: "components",
16498
+ codeRe: /^IC-\d+$/i,
16499
+ slugField: "slug",
16500
+ nestedOnPrimary: []
16501
+ };
16502
+ }
16503
+ if (/^QA-\d+-test-cases-list\.json$/i.test(outputBasename)) {
16504
+ return {
16505
+ id: "qa-test-cases",
16506
+ primaryKey: "testCases",
16507
+ codeRe: /^TC-\d+$/i,
16508
+ slugField: "slug",
16509
+ nestedOnPrimary: []
16510
+ };
16511
+ }
16116
16512
  return void 0;
16117
16513
  }
16118
16514
  function extractDroppedCodesFromRevisionLog(agent) {
@@ -16357,7 +16753,7 @@ function mergeRowWithNested(pre, agent, slugField, slugAuth, nestedOnPrimary, dr
16357
16753
  function buildCascadePathsForDroppedCodes(listBasename, droppedCodes, contextDirAbs) {
16358
16754
  const paths = [];
16359
16755
  const tryPush = (p) => {
16360
- const abs = path23.join(contextDirAbs, p);
16756
+ const abs = path24.join(contextDirAbs, p);
16361
16757
  if (fs26.existsSync(abs)) {
16362
16758
  paths.push(abs);
16363
16759
  }
@@ -16377,19 +16773,19 @@ function buildCascadePathsForDroppedCodes(listBasename, droppedCodes, contextDir
16377
16773
  const prefix = `${code}-`;
16378
16774
  for (const f of listDir()) {
16379
16775
  if (f.startsWith(prefix) && f.endsWith(".md")) {
16380
- paths.push(path23.join(contextDirAbs, f));
16776
+ paths.push(path24.join(contextDirAbs, f));
16381
16777
  }
16382
16778
  }
16383
16779
  tryPush(`${code}-use-cases-list.json`);
16384
16780
  for (const f of listDir()) {
16385
16781
  if (f.startsWith(`${code}_UC-`) && f.endsWith("-scenarios-list.json")) {
16386
- paths.push(path23.join(contextDirAbs, f));
16782
+ paths.push(path24.join(contextDirAbs, f));
16387
16783
  }
16388
16784
  if (f.startsWith(`${code}_UC-`) && f.includes("_SC-") && f.endsWith(".md")) {
16389
- paths.push(path23.join(contextDirAbs, f));
16785
+ paths.push(path24.join(contextDirAbs, f));
16390
16786
  }
16391
16787
  if (f.startsWith(`${code}_UC-`) && f.endsWith(".md") && !f.includes("-scenarios-list")) {
16392
- paths.push(path23.join(contextDirAbs, f));
16788
+ paths.push(path24.join(contextDirAbs, f));
16393
16789
  }
16394
16790
  }
16395
16791
  }
@@ -16410,7 +16806,7 @@ function buildCascadePathsForDroppedCodes(listBasename, droppedCodes, contextDir
16410
16806
  tryPush(`${stem}-scenarios-list.json`);
16411
16807
  for (const f of listDir()) {
16412
16808
  if (f.startsWith(`${stem}_SC-`) && f.endsWith(".md")) {
16413
- paths.push(path23.join(contextDirAbs, f));
16809
+ paths.push(path24.join(contextDirAbs, f));
16414
16810
  }
16415
16811
  }
16416
16812
  }
@@ -16442,7 +16838,7 @@ function buildCascadePathsForDroppedCodes(listBasename, droppedCodes, contextDir
16442
16838
  }
16443
16839
  for (const f of listDir()) {
16444
16840
  if (f.startsWith(`${dm}_${code}-`) && f.endsWith(".md")) {
16445
- paths.push(path23.join(contextDirAbs, f));
16841
+ paths.push(path24.join(contextDirAbs, f));
16446
16842
  }
16447
16843
  }
16448
16844
  }
@@ -16891,10 +17287,10 @@ function formatMarkdownEvidenceRepairAppendix(outputFileAbsolute, kind = "empty"
16891
17287
 
16892
17288
  // src/analysis/openQuestionResolution.ts
16893
17289
  var fs28 = __toESM(require("fs"));
16894
- var path24 = __toESM(require("path"));
17290
+ var path25 = __toESM(require("path"));
16895
17291
  var OPEN_QUESTION_RESOLUTION_JSON_BASENAME = "open-question-resolution.json";
16896
17292
  function openQuestionResolutionJsonAbsolute(worktreeRoot) {
16897
- return path24.join(worktreeRoot, ".opencode", "_run", OPEN_QUESTION_RESOLUTION_JSON_BASENAME);
17293
+ return path25.join(worktreeRoot, ".opencode", "_run", OPEN_QUESTION_RESOLUTION_JSON_BASENAME);
16898
17294
  }
16899
17295
  function asTrimmedString(v) {
16900
17296
  if (typeof v !== "string") {
@@ -16951,7 +17347,7 @@ function deleteOpenQuestionResolutionFile(worktreeRoot) {
16951
17347
 
16952
17348
  // src/analysis/openQuestionsSectionValidate.ts
16953
17349
  var fs29 = __toESM(require("fs"));
16954
- var path25 = __toESM(require("path"));
17350
+ var path26 = __toESM(require("path"));
16955
17351
  var CANONICAL_NORMALIZED = "open questions";
16956
17352
  function normalizeOpenQuestionsHeadingTitle(raw) {
16957
17353
  const t = raw.trim().replace(/\s+/gu, " ");
@@ -17068,9 +17464,9 @@ function parseOpenQuestionsSection(content) {
17068
17464
  return { kind: "hasResidualQuestions", questions, startLine1Based };
17069
17465
  }
17070
17466
  function isContextMarkdownPathForOpenQuestions(worktreeRoot, absolutePath) {
17071
- const ctx = path25.join(path25.resolve(worktreeRoot), ".gluecharm", "context");
17072
- const abs = path25.resolve(absolutePath);
17073
- const ctxNorm = ctx + path25.sep;
17467
+ const ctx = path26.join(path26.resolve(worktreeRoot), ".gluecharm", "context");
17468
+ const abs = path26.resolve(absolutePath);
17469
+ const ctxNorm = ctx + path26.sep;
17074
17470
  return abs === ctx || abs.startsWith(ctxNorm);
17075
17471
  }
17076
17472
  function validateOpenQuestionsSectionFile(absolutePath, scope) {
@@ -17207,10 +17603,10 @@ var CITATION_EXAMPLE = "`src/example.ts:42` or `src/example.ts:10-25`";
17207
17603
  var SRS50_COORD_LIST_APPEND_HINT = " **SRS-50 \u2014 stable writes:** Append **new** coded rows at the **end** of each array (never insert new codes between existing ones).";
17208
17604
  var SRS50_COORD_LIST_REVIEW_HINT = ' **SRS-50 \u2014 stable writes:** Preserve existing **code** order; append net-new at the end. To **remove** a row, add **revisionLog** with **droppedCodes** (string[]). To change **slug** without changing **code**, add **slugRenames** ([{ "code", "from", "to" }]).';
17209
17605
  function contextDir(worktreeRoot) {
17210
- return path26.join(worktreeRoot, ".gluecharm", "context");
17606
+ return path27.join(worktreeRoot, ".gluecharm", "context");
17211
17607
  }
17212
17608
  function schemaRef(worktreeRoot, schemaBasename) {
17213
- return path26.join(worktreeRoot, ".opencode", "schemas", "context-lists", schemaBasename);
17609
+ return path27.join(worktreeRoot, ".opencode", "schemas", "context-lists", schemaBasename);
17214
17610
  }
17215
17611
  function expandArgvTemplate3(template, vars) {
17216
17612
  return template.map((part) => {
@@ -17232,6 +17628,12 @@ function buildCoordStepLabel(step, listTarget) {
17232
17628
  if (listTarget?.entityCode) {
17233
17629
  parts.push(listTarget.entityCode);
17234
17630
  }
17631
+ if (listTarget?.infrastructureCode) {
17632
+ parts.push(listTarget.infrastructureCode);
17633
+ }
17634
+ if (listTarget?.qaCode) {
17635
+ parts.push(listTarget.qaCode);
17636
+ }
17235
17637
  return parts.join("\xB7");
17236
17638
  }
17237
17639
  function applyOpenCodeStewardshipToExpandedArgv(expandedArgv, p) {
@@ -17300,7 +17702,7 @@ function buildPromptBody(step, worktreeRoot, workspaceLabel, outputFileAbsolute,
17300
17702
  agentId: "ctx-review-features-list",
17301
17703
  agentDisplayName: "Features list \u2014 semantic review",
17302
17704
  worktreeRoot,
17303
- listTaskDescription: "Semantically review the **existing** features list at the output path: merge duplicates, remove false features, split overloaded rows, consolidate test-only duplicates of product capabilities (e.g. test login vs login), set **featureKind** per row (**product** | **infrastructure** | **testing**); every feature row **must** have **sourceReferences** with **minItems: 1** (add evidence or **drop** the row). Renumber **FE-** only when splitting (new codes at end). Append **revisionLog** entries for substantive edits. Follow the bundled agent instructions." + SRS50_COORD_LIST_REVIEW_HINT,
17705
+ listTaskDescription: "Semantically review the **existing** features list at the output path: merge duplicates, remove false features, split overloaded rows, consolidate test-only duplicates of product capabilities (e.g. test login vs login); **drop** rows that belong in **infrastructure-list.json** or **qa-list.json** (do not use featureKind infrastructure/testing). Every surviving feature row **must** have **sourceReferences** with **minItems: 1**. Renumber **FE-** only when splitting (new codes at end). Append **revisionLog** entries for substantive edits. Follow the bundled agent instructions." + SRS50_COORD_LIST_REVIEW_HINT,
17304
17706
  listSchemaRef: schemaRef(worktreeRoot, "features-list.schema.json"),
17305
17707
  outputFileAbsolute,
17306
17708
  outputBasename,
@@ -17454,6 +17856,166 @@ function buildPromptBody(step, worktreeRoot, workspaceLabel, outputFileAbsolute,
17454
17856
  }),
17455
17857
  listSchemaFile: "tech-stack-list.schema.json"
17456
17858
  };
17859
+ case "listInfrastructure":
17860
+ return {
17861
+ body: fillListJsonPrompt({
17862
+ agentId: "ctx-list-infrastructure",
17863
+ agentDisplayName: "Infrastructure list",
17864
+ worktreeRoot,
17865
+ listTaskDescription: `Enumerate infrastructure / developer-delivery surfaces (IN-*) with stable codes, names, slugs, and **sourceReferences** (**minItems: 1**). CI/CD, IaC, scripts, hooks, bootstrap \u2014 not product features (FE-*) or runtime catalogue tools (TS-*).${SRS50_COORD_LIST_APPEND_HINT}`,
17866
+ listSchemaRef: schemaRef(worktreeRoot, "infrastructure-list.schema.json"),
17867
+ outputFileAbsolute,
17868
+ outputBasename,
17869
+ allowJsonFence: false,
17870
+ parentContextBlock: "",
17871
+ repairAppendix: listRepairAppendix
17872
+ }),
17873
+ listSchemaFile: "infrastructure-list.schema.json"
17874
+ };
17875
+ case "reviewInfrastructureList":
17876
+ return {
17877
+ body: fillListJsonPrompt({
17878
+ agentId: "ctx-review-infrastructure-list",
17879
+ agentDisplayName: "Infrastructure list \u2014 semantic review",
17880
+ worktreeRoot,
17881
+ listTaskDescription: "Semantically review **infrastructure-list.json**: merge duplicates, remove false rows, stable **IN-*** codes; every row **sourceReferences** minItems 1. Overwrite; follow bundled agent." + SRS50_COORD_LIST_REVIEW_HINT,
17882
+ listSchemaRef: schemaRef(worktreeRoot, "infrastructure-list.schema.json"),
17883
+ outputFileAbsolute,
17884
+ outputBasename,
17885
+ allowJsonFence: false,
17886
+ parentContextBlock: "\n## Parent context\n- File from **listInfrastructure**; **overwrite** with reviewed canonical list.\n",
17887
+ repairAppendix: listRepairAppendix
17888
+ }),
17889
+ listSchemaFile: "infrastructure-list.schema.json"
17890
+ };
17891
+ case "listQa":
17892
+ return {
17893
+ body: fillListJsonPrompt({
17894
+ agentId: "ctx-list-qa",
17895
+ agentDisplayName: "QA list",
17896
+ worktreeRoot,
17897
+ listTaskDescription: `Enumerate QA / verification surfaces (QA-*) with **coversCodes** (minItems 1, \u22651 FE-* or SV-* anchor per SRS-52 \xA73.1), names, slugs, **sourceReferences**. Not product features.${SRS50_COORD_LIST_APPEND_HINT}`,
17898
+ listSchemaRef: schemaRef(worktreeRoot, "qa-list.schema.json"),
17899
+ outputFileAbsolute,
17900
+ outputBasename,
17901
+ allowJsonFence: false,
17902
+ parentContextBlock: "\n## Parent context\n- **features-list.json** is already reviewed; reference real **FE-*** / **SV-*** codes in **coversCodes**.\n",
17903
+ repairAppendix: listRepairAppendix
17904
+ }),
17905
+ listSchemaFile: "qa-list.schema.json"
17906
+ };
17907
+ case "reviewQaList":
17908
+ return {
17909
+ body: fillListJsonPrompt({
17910
+ agentId: "ctx-review-qa-list",
17911
+ agentDisplayName: "QA list \u2014 semantic review",
17912
+ worktreeRoot,
17913
+ listTaskDescription: "Semantically review **qa-list.json**: enforce **coversCodes** anchor rule; merge duplicates; stable **QA-*** codes; **sourceReferences** minItems 1. Overwrite." + SRS50_COORD_LIST_REVIEW_HINT,
17914
+ listSchemaRef: schemaRef(worktreeRoot, "qa-list.schema.json"),
17915
+ outputFileAbsolute,
17916
+ outputBasename,
17917
+ allowJsonFence: false,
17918
+ parentContextBlock: "\n## Parent context\n- File from **listQa**; **overwrite** with reviewed canonical list.\n",
17919
+ repairAppendix: listRepairAppendix
17920
+ }),
17921
+ listSchemaFile: "qa-list.schema.json"
17922
+ };
17923
+ case "listInfrastructureComponents": {
17924
+ const inCode = listTarget?.infrastructureCode;
17925
+ if (!inCode) {
17926
+ throw new Error("listInfrastructureComponents requires listTarget.infrastructureCode");
17927
+ }
17928
+ return {
17929
+ body: fillListJsonPrompt({
17930
+ agentId: "ctx-list-infrastructure-components",
17931
+ agentDisplayName: "Infrastructure components list",
17932
+ worktreeRoot,
17933
+ listTaskDescription: `For infrastructure **${inCode}**, list components (IC-*) with stable codes, names, slugs, **sourceReferences** (minItems 1). Set infrastructureCode **${inCode}**.${SRS50_COORD_LIST_APPEND_HINT}`,
17934
+ listSchemaRef: schemaRef(worktreeRoot, "infrastructure-components-list.schema.json"),
17935
+ outputFileAbsolute,
17936
+ outputBasename,
17937
+ allowJsonFence: false,
17938
+ parentContextBlock: `
17939
+ ## Parent context
17940
+ - infrastructureCode: **${inCode}**
17941
+ `,
17942
+ repairAppendix: listRepairAppendix
17943
+ }),
17944
+ listSchemaFile: "infrastructure-components-list.schema.json"
17945
+ };
17946
+ }
17947
+ case "reviewInfrastructureComponentsList": {
17948
+ const inCode = listTarget?.infrastructureCode;
17949
+ if (!inCode) {
17950
+ throw new Error("reviewInfrastructureComponentsList requires listTarget.infrastructureCode");
17951
+ }
17952
+ return {
17953
+ body: fillListJsonPrompt({
17954
+ agentId: "ctx-review-infrastructure-components-list",
17955
+ agentDisplayName: "Infrastructure components \u2014 semantic review",
17956
+ worktreeRoot,
17957
+ listTaskDescription: `Review **${inCode}-components-list.json**: normalize IC rows; **sourceReferences** minItems 1; stable codes. Overwrite.${SRS50_COORD_LIST_REVIEW_HINT}`,
17958
+ listSchemaRef: schemaRef(worktreeRoot, "infrastructure-components-list.schema.json"),
17959
+ outputFileAbsolute,
17960
+ outputBasename,
17961
+ allowJsonFence: false,
17962
+ parentContextBlock: `
17963
+ ## Parent context
17964
+ - infrastructureCode: **${inCode}**
17965
+ `,
17966
+ repairAppendix: listRepairAppendix
17967
+ }),
17968
+ listSchemaFile: "infrastructure-components-list.schema.json"
17969
+ };
17970
+ }
17971
+ case "listQaTestCases": {
17972
+ const qaCode = listTarget?.qaCode;
17973
+ if (!qaCode) {
17974
+ throw new Error("listQaTestCases requires listTarget.qaCode");
17975
+ }
17976
+ return {
17977
+ body: fillListJsonPrompt({
17978
+ agentId: "ctx-list-qa-test-cases",
17979
+ agentDisplayName: "QA test cases list",
17980
+ worktreeRoot,
17981
+ listTaskDescription: `For QA **${qaCode}**, list test cases (TC-*) with **coversCodes** (anchor rule), names, slugs, **sourceReferences**. Set qaCode **${qaCode}**.${SRS50_COORD_LIST_APPEND_HINT}`,
17982
+ listSchemaRef: schemaRef(worktreeRoot, "qa-test-cases-list.schema.json"),
17983
+ outputFileAbsolute,
17984
+ outputBasename,
17985
+ allowJsonFence: false,
17986
+ parentContextBlock: `
17987
+ ## Parent context
17988
+ - qaCode: **${qaCode}**
17989
+ `,
17990
+ repairAppendix: listRepairAppendix
17991
+ }),
17992
+ listSchemaFile: "qa-test-cases-list.schema.json"
17993
+ };
17994
+ }
17995
+ case "reviewQaTestCasesList": {
17996
+ const qaCode = listTarget?.qaCode;
17997
+ if (!qaCode) {
17998
+ throw new Error("reviewQaTestCasesList requires listTarget.qaCode");
17999
+ }
18000
+ return {
18001
+ body: fillListJsonPrompt({
18002
+ agentId: "ctx-review-qa-test-cases-list",
18003
+ agentDisplayName: "QA test cases \u2014 semantic review",
18004
+ worktreeRoot,
18005
+ listTaskDescription: `Review **${qaCode}-test-cases-list.json**: enforce **coversCodes**; normalize TC rows; **sourceReferences** minItems 1. Overwrite.${SRS50_COORD_LIST_REVIEW_HINT}`,
18006
+ listSchemaRef: schemaRef(worktreeRoot, "qa-test-cases-list.schema.json"),
18007
+ outputFileAbsolute,
18008
+ outputBasename,
18009
+ allowJsonFence: false,
18010
+ parentContextBlock: `
18011
+ ## Parent context
18012
+ - qaCode: **${qaCode}**
18013
+ `,
18014
+ repairAppendix: listRepairAppendix
18015
+ }),
18016
+ listSchemaFile: "qa-test-cases-list.schema.json"
18017
+ };
18018
+ }
17457
18019
  case "listUseCases": {
17458
18020
  const fe = listTarget?.featureCode;
17459
18021
  if (!fe) {
@@ -17626,7 +18188,15 @@ var STEP_OPEN_CODE_AGENT_IDS = {
17626
18188
  listUseCases: "agent-list-use-cases",
17627
18189
  reviewUseCasesList: "agent-review-use-cases-list",
17628
18190
  listScenarios: "agent-list-scenarios",
17629
- reviewScenariosList: "agent-review-scenarios-list"
18191
+ reviewScenariosList: "agent-review-scenarios-list",
18192
+ listInfrastructure: "agent-list-infrastructure",
18193
+ reviewInfrastructureList: "agent-review-infrastructure-list",
18194
+ listInfrastructureComponents: "agent-list-infrastructure-components",
18195
+ reviewInfrastructureComponentsList: "agent-review-infrastructure-components-list",
18196
+ listQa: "agent-list-qa",
18197
+ reviewQaList: "agent-review-qa-list",
18198
+ listQaTestCases: "agent-list-qa-test-cases",
18199
+ reviewQaTestCasesList: "agent-review-qa-test-cases-list"
17630
18200
  };
17631
18201
  var RESOLVE_OPEN_QUESTION_AGENT_STEM = "agent-resolve-open-question";
17632
18202
  function clampMarkdownOpenQuestionIterations(raw) {
@@ -17654,46 +18224,86 @@ function outputPaths(step, worktreeRoot, listTarget) {
17654
18224
  const ctx = contextDir(worktreeRoot);
17655
18225
  switch (step) {
17656
18226
  case "docsProject":
17657
- return { absolute: path26.join(contextDir(worktreeRoot), "project.md"), basename: "project.md" };
18227
+ return { absolute: path27.join(contextDir(worktreeRoot), "project.md"), basename: "project.md" };
17658
18228
  case "architecture":
17659
- return { absolute: path26.join(ctx, "architecture.md"), basename: "architecture.md" };
18229
+ return { absolute: path27.join(ctx, "architecture.md"), basename: "architecture.md" };
17660
18230
  case "listFeatures":
17661
- return { absolute: path26.join(ctx, "features-list.json"), basename: "features-list.json" };
18231
+ return { absolute: path27.join(ctx, "features-list.json"), basename: "features-list.json" };
17662
18232
  case "reviewFeaturesList":
17663
- return { absolute: path26.join(ctx, "features-list.json"), basename: "features-list.json" };
18233
+ return { absolute: path27.join(ctx, "features-list.json"), basename: "features-list.json" };
17664
18234
  case "listRepoSurface":
17665
- return { absolute: path26.join(ctx, "repo-surface-scan.json"), basename: "repo-surface-scan.json" };
18235
+ return { absolute: path27.join(ctx, "repo-surface-scan.json"), basename: "repo-surface-scan.json" };
17666
18236
  case "listExperiences":
17667
- return { absolute: path26.join(ctx, "experiences-list.json"), basename: "experiences-list.json" };
18237
+ return { absolute: path27.join(ctx, "experiences-list.json"), basename: "experiences-list.json" };
17668
18238
  case "reviewExperiencesList":
17669
- return { absolute: path26.join(ctx, "experiences-list.json"), basename: "experiences-list.json" };
18239
+ return { absolute: path27.join(ctx, "experiences-list.json"), basename: "experiences-list.json" };
17670
18240
  case "listServices":
17671
- return { absolute: path26.join(ctx, "services-list.json"), basename: "services-list.json" };
18241
+ return { absolute: path27.join(ctx, "services-list.json"), basename: "services-list.json" };
17672
18242
  case "reviewServicesList":
17673
- return { absolute: path26.join(ctx, "services-list.json"), basename: "services-list.json" };
18243
+ return { absolute: path27.join(ctx, "services-list.json"), basename: "services-list.json" };
17674
18244
  case "listDataModel":
17675
- return { absolute: path26.join(ctx, "data-model-list.json"), basename: "data-model-list.json" };
18245
+ return { absolute: path27.join(ctx, "data-model-list.json"), basename: "data-model-list.json" };
17676
18246
  case "reviewDataModelList":
17677
- return { absolute: path26.join(ctx, "data-model-list.json"), basename: "data-model-list.json" };
18247
+ return { absolute: path27.join(ctx, "data-model-list.json"), basename: "data-model-list.json" };
17678
18248
  case "listTechStack":
17679
- return { absolute: path26.join(ctx, "tech-stack-list.json"), basename: "tech-stack-list.json" };
18249
+ return { absolute: path27.join(ctx, "tech-stack-list.json"), basename: "tech-stack-list.json" };
17680
18250
  case "reviewTechStackList":
17681
- return { absolute: path26.join(ctx, "tech-stack-list.json"), basename: "tech-stack-list.json" };
18251
+ return { absolute: path27.join(ctx, "tech-stack-list.json"), basename: "tech-stack-list.json" };
18252
+ case "listInfrastructure":
18253
+ return { absolute: path27.join(ctx, "infrastructure-list.json"), basename: "infrastructure-list.json" };
18254
+ case "reviewInfrastructureList":
18255
+ return { absolute: path27.join(ctx, "infrastructure-list.json"), basename: "infrastructure-list.json" };
18256
+ case "listQa":
18257
+ return { absolute: path27.join(ctx, "qa-list.json"), basename: "qa-list.json" };
18258
+ case "reviewQaList":
18259
+ return { absolute: path27.join(ctx, "qa-list.json"), basename: "qa-list.json" };
18260
+ case "listInfrastructureComponents": {
18261
+ const inCode = listTarget?.infrastructureCode;
18262
+ if (!inCode) {
18263
+ throw new Error("listInfrastructureComponents requires listTarget.infrastructureCode");
18264
+ }
18265
+ const basename18 = `${inCode}-components-list.json`;
18266
+ return { absolute: path27.join(ctx, basename18), basename: basename18 };
18267
+ }
18268
+ case "reviewInfrastructureComponentsList": {
18269
+ const inCode = listTarget?.infrastructureCode;
18270
+ if (!inCode) {
18271
+ throw new Error("reviewInfrastructureComponentsList requires listTarget.infrastructureCode");
18272
+ }
18273
+ const basename18 = `${inCode}-components-list.json`;
18274
+ return { absolute: path27.join(ctx, basename18), basename: basename18 };
18275
+ }
18276
+ case "listQaTestCases": {
18277
+ const qaCode = listTarget?.qaCode;
18278
+ if (!qaCode) {
18279
+ throw new Error("listQaTestCases requires listTarget.qaCode");
18280
+ }
18281
+ const basename18 = `${qaCode}-test-cases-list.json`;
18282
+ return { absolute: path27.join(ctx, basename18), basename: basename18 };
18283
+ }
18284
+ case "reviewQaTestCasesList": {
18285
+ const qaCode = listTarget?.qaCode;
18286
+ if (!qaCode) {
18287
+ throw new Error("reviewQaTestCasesList requires listTarget.qaCode");
18288
+ }
18289
+ const basename18 = `${qaCode}-test-cases-list.json`;
18290
+ return { absolute: path27.join(ctx, basename18), basename: basename18 };
18291
+ }
17682
18292
  case "listUseCases": {
17683
18293
  const fe = listTarget?.featureCode;
17684
18294
  if (!fe) {
17685
18295
  throw new Error("listUseCases requires listTarget.featureCode");
17686
18296
  }
17687
- const basename17 = `${fe}-use-cases-list.json`;
17688
- return { absolute: path26.join(ctx, basename17), basename: basename17 };
18297
+ const basename18 = `${fe}-use-cases-list.json`;
18298
+ return { absolute: path27.join(ctx, basename18), basename: basename18 };
17689
18299
  }
17690
18300
  case "reviewUseCasesList": {
17691
18301
  const fe = listTarget?.featureCode;
17692
18302
  if (!fe) {
17693
18303
  throw new Error("reviewUseCasesList requires listTarget.featureCode");
17694
18304
  }
17695
- const basename17 = `${fe}-use-cases-list.json`;
17696
- return { absolute: path26.join(ctx, basename17), basename: basename17 };
18305
+ const basename18 = `${fe}-use-cases-list.json`;
18306
+ return { absolute: path27.join(ctx, basename18), basename: basename18 };
17697
18307
  }
17698
18308
  case "listScenarios": {
17699
18309
  const fe = listTarget?.featureCode;
@@ -17701,8 +18311,8 @@ function outputPaths(step, worktreeRoot, listTarget) {
17701
18311
  if (!fe || !uc) {
17702
18312
  throw new Error("listScenarios requires listTarget.featureCode and useCaseCode");
17703
18313
  }
17704
- const basename17 = `${fe}_${uc}-scenarios-list.json`;
17705
- return { absolute: path26.join(ctx, basename17), basename: basename17 };
18314
+ const basename18 = `${fe}_${uc}-scenarios-list.json`;
18315
+ return { absolute: path27.join(ctx, basename18), basename: basename18 };
17706
18316
  }
17707
18317
  case "reviewScenariosList": {
17708
18318
  const fe = listTarget?.featureCode;
@@ -17710,24 +18320,24 @@ function outputPaths(step, worktreeRoot, listTarget) {
17710
18320
  if (!fe || !uc) {
17711
18321
  throw new Error("reviewScenariosList requires listTarget.featureCode and useCaseCode");
17712
18322
  }
17713
- const basename17 = `${fe}_${uc}-scenarios-list.json`;
17714
- return { absolute: path26.join(ctx, basename17), basename: basename17 };
18323
+ const basename18 = `${fe}_${uc}-scenarios-list.json`;
18324
+ return { absolute: path27.join(ctx, basename18), basename: basename18 };
17715
18325
  }
17716
18326
  case "listEntityFields": {
17717
18327
  const dm = listTarget?.entityCode;
17718
18328
  if (!dm) {
17719
18329
  throw new Error("listEntityFields requires listTarget.entityCode");
17720
18330
  }
17721
- const basename17 = `${dm}-fields-list.json`;
17722
- return { absolute: path26.join(ctx, basename17), basename: basename17 };
18331
+ const basename18 = `${dm}-fields-list.json`;
18332
+ return { absolute: path27.join(ctx, basename18), basename: basename18 };
17723
18333
  }
17724
18334
  case "reviewEntityFieldsList": {
17725
18335
  const dm = listTarget?.entityCode;
17726
18336
  if (!dm) {
17727
18337
  throw new Error("reviewEntityFieldsList requires listTarget.entityCode");
17728
18338
  }
17729
- const basename17 = `${dm}-fields-list.json`;
17730
- return { absolute: path26.join(ctx, basename17), basename: basename17 };
18339
+ const basename18 = `${dm}-fields-list.json`;
18340
+ return { absolute: path27.join(ctx, basename18), basename: basename18 };
17731
18341
  }
17732
18342
  default: {
17733
18343
  const _u = step;
@@ -17736,7 +18346,7 @@ function outputPaths(step, worktreeRoot, listTarget) {
17736
18346
  }
17737
18347
  }
17738
18348
  async function runOpenQuestionResolutionWorkstation(opts) {
17739
- const runDir = path26.join(opts.worktreeRoot, ".opencode", "_run");
18349
+ const runDir = path27.join(opts.worktreeRoot, ".opencode", "_run");
17740
18350
  fs30.mkdirSync(runDir, { recursive: true });
17741
18351
  const resolutionJsonAbsolute = openQuestionResolutionJsonAbsolute(opts.worktreeRoot);
17742
18352
  const body = fillResolveOpenQuestionPrompt({
@@ -17746,7 +18356,7 @@ async function runOpenQuestionResolutionWorkstation(opts) {
17746
18356
  questionText: opts.questionText,
17747
18357
  resolutionJsonAbsolute
17748
18358
  });
17749
- const promptPath = path26.join(runDir, `resolve-open-question-${Date.now()}.prompt.txt`);
18359
+ const promptPath = path27.join(runDir, `resolve-open-question-${Date.now()}.prompt.txt`);
17750
18360
  fs30.writeFileSync(promptPath, body, "utf-8");
17751
18361
  const vars = {
17752
18362
  promptFile: promptPath,
@@ -18001,7 +18611,7 @@ async function runSynthesisMarkdownRemediationChain(common, step, workspaceLabel
18001
18611
  if (common.abortSignal?.aborted) {
18002
18612
  return { ok: false, cancelled: true, message: "Remediation stopped.", promptPath: "" };
18003
18613
  }
18004
- const runDir = path26.join(common.worktreeRoot, ".opencode", "_run");
18614
+ const runDir = path27.join(common.worktreeRoot, ".opencode", "_run");
18005
18615
  const r = await runOpenCodeMarkdownEvidenceRepairLoop({
18006
18616
  worktreeRoot: common.worktreeRoot,
18007
18617
  runDir,
@@ -18030,7 +18640,7 @@ async function runSynthesisMarkdownRemediationChain(common, step, workspaceLabel
18030
18640
  void 0,
18031
18641
  appendix
18032
18642
  ).body,
18033
- promptPathForAttempt: (attemptIndex) => path26.join(runDir, `${step}-oqrem-a${String(attemptIndex)}-${Date.now()}.prompt.txt`),
18643
+ promptPathForAttempt: (attemptIndex) => path27.join(runDir, `${step}-oqrem-a${String(attemptIndex)}-${Date.now()}.prompt.txt`),
18034
18644
  argvVarsForPrompt: (promptPath) => ({
18035
18645
  promptFile: promptPath,
18036
18646
  agentId: STEP_OPEN_CODE_AGENT_IDS[step],
@@ -18051,7 +18661,7 @@ async function runDetailMarkdownRemediationChain(common, target, initialMarkdown
18051
18661
  if (common.abortSignal?.aborted) {
18052
18662
  return { ok: false, cancelled: true, message: "Remediation stopped.", promptPath: "" };
18053
18663
  }
18054
- const runDir = path26.join(common.worktreeRoot, ".opencode", "_run");
18664
+ const runDir = path27.join(common.worktreeRoot, ".opencode", "_run");
18055
18665
  const safeStem = target.outputBasename.replace(/[^A-Za-z0-9_.-]+/g, "_").slice(0, 120);
18056
18666
  const r = await runOpenCodeMarkdownEvidenceRepairLoop({
18057
18667
  worktreeRoot: common.worktreeRoot,
@@ -18081,7 +18691,7 @@ async function runDetailMarkdownRemediationChain(common, target, initialMarkdown
18081
18691
  citationFormatExample: CITATION_EXAMPLE,
18082
18692
  repairAppendix: appendix
18083
18693
  }),
18084
- promptPathForAttempt: (attemptIndex) => path26.join(
18694
+ promptPathForAttempt: (attemptIndex) => path27.join(
18085
18695
  runDir,
18086
18696
  `markdownDetail-${target.openCodeAgentStem}-${safeStem}-oqrem-a${String(attemptIndex)}-${Date.now()}.prompt.txt`
18087
18697
  ),
@@ -18359,8 +18969,8 @@ Run **Materialize agents** first (copies list schemas into this folder\u2019s \`
18359
18969
  }
18360
18970
  }
18361
18971
  }
18362
- fs30.mkdirSync(path26.dirname(outputFileAbsolute), { recursive: true });
18363
- const runDir = path26.join(opts.worktreeRoot, ".opencode", "_run");
18972
+ fs30.mkdirSync(path27.dirname(outputFileAbsolute), { recursive: true });
18973
+ const runDir = path27.join(opts.worktreeRoot, ".opencode", "_run");
18364
18974
  fs30.mkdirSync(runDir, { recursive: true });
18365
18975
  const scopeSuffix = (opts.step === "listUseCases" || opts.step === "reviewUseCasesList") && opts.listTarget?.featureCode ? `-${opts.listTarget.featureCode}` : (opts.step === "listScenarios" || opts.step === "reviewScenariosList") && opts.listTarget?.featureCode && opts.listTarget?.useCaseCode ? `-${opts.listTarget.featureCode}_${opts.listTarget.useCaseCode}` : (opts.step === "listEntityFields" || opts.step === "reviewEntityFieldsList") && opts.listTarget?.entityCode ? `-${opts.listTarget.entityCode}` : "";
18366
18976
  const rawRepair = opts.listJsonSchemaRepairAttempts;
@@ -18441,7 +19051,7 @@ Run **Materialize agents** first (copies list schemas into this folder\u2019s \`
18441
19051
  void 0,
18442
19052
  appendix
18443
19053
  ).body,
18444
- promptPathForAttempt: (attemptIndex) => path26.join(runDir, `${opts.step}${scopeSuffix}-a${String(attemptIndex)}-${Date.now()}.prompt.txt`),
19054
+ promptPathForAttempt: (attemptIndex) => path27.join(runDir, `${opts.step}${scopeSuffix}-a${String(attemptIndex)}-${Date.now()}.prompt.txt`),
18445
19055
  argvVarsForPrompt: (promptPath) => ({
18446
19056
  promptFile: promptPath,
18447
19057
  agentId: STEP_OPEN_CODE_AGENT_IDS[opts.step],
@@ -18568,7 +19178,7 @@ Run **Materialize agents** first (copies list schemas into this folder\u2019s \`
18568
19178
  effectiveListSchemaFile ? repairAppendix : void 0,
18569
19179
  effectiveListSchemaFile ? void 0 : markdownEvidenceRepairAppendix
18570
19180
  );
18571
- const promptPath = path26.join(runDir, `${opts.step}${scopeSuffix}-a${attempt}-${Date.now()}.prompt.txt`);
19181
+ const promptPath = path27.join(runDir, `${opts.step}${scopeSuffix}-a${attempt}-${Date.now()}.prompt.txt`);
18572
19182
  lastPromptPath = promptPath;
18573
19183
  fs30.writeFileSync(promptPath, body, "utf-8");
18574
19184
  const vars = {
@@ -18754,7 +19364,7 @@ Expected output: ${outputFileAbsolute}`;
18754
19364
  const orderSet = new Set(CONTEXT_SYNTHESIS_TEST_STEP_ORDER);
18755
19365
  if (orderSet.has(opts.step)) {
18756
19366
  const ctx = contextDir(opts.worktreeRoot);
18757
- const rel = path26.relative(ctx, outputFileAbsolute).split(path26.sep).join("/");
19367
+ const rel = path27.relative(ctx, outputFileAbsolute).split(path27.sep).join("/");
18758
19368
  opts.onRemediationPhase?.("aceTracing");
18759
19369
  try {
18760
19370
  await runAceTracePhase({
@@ -18801,7 +19411,7 @@ Expected output: ${outputFileAbsolute}`;
18801
19411
  var DETAIL_MARKDOWN_WORKSTATION_KIND = "markdownDetail";
18802
19412
  async function runDetailMarkdownWorkstation(opts) {
18803
19413
  const ctx = contextDir(opts.worktreeRoot);
18804
- const outputFileAbsolute = path26.join(ctx, opts.target.outputBasename);
19414
+ const outputFileAbsolute = path27.join(ctx, opts.target.outputBasename);
18805
19415
  const outputBasename = opts.target.outputBasename;
18806
19416
  const rawMdEv = opts.markdownEvidenceRepairAttempts;
18807
19417
  let maxMarkdownEvidenceRepairs = rawMdEv === void 0 ? 2 : Math.floor(Number(rawMdEv));
@@ -18810,8 +19420,8 @@ async function runDetailMarkdownWorkstation(opts) {
18810
19420
  }
18811
19421
  maxMarkdownEvidenceRepairs = Math.min(5, Math.max(0, maxMarkdownEvidenceRepairs));
18812
19422
  const totalMarkdownRuns = 1 + maxMarkdownEvidenceRepairs;
18813
- fs30.mkdirSync(path26.dirname(outputFileAbsolute), { recursive: true });
18814
- const runDir = path26.join(opts.worktreeRoot, ".opencode", "_run");
19423
+ fs30.mkdirSync(path27.dirname(outputFileAbsolute), { recursive: true });
19424
+ const runDir = path27.join(opts.worktreeRoot, ".opencode", "_run");
18815
19425
  fs30.mkdirSync(runDir, { recursive: true });
18816
19426
  const safeStem = opts.target.outputBasename.replace(/[^A-Za-z0-9_.-]+/g, "_").slice(0, 120);
18817
19427
  let lastPromptPath = "";
@@ -18864,7 +19474,7 @@ async function runDetailMarkdownWorkstation(opts) {
18864
19474
  citationFormatExample: CITATION_EXAMPLE,
18865
19475
  repairAppendix: appendix
18866
19476
  }),
18867
- promptPathForAttempt: (attemptIndex) => path26.join(
19477
+ promptPathForAttempt: (attemptIndex) => path27.join(
18868
19478
  runDir,
18869
19479
  `markdownDetail-${opts.target.openCodeAgentStem}-${safeStem}-a${String(attemptIndex)}-${Date.now()}.prompt.txt`
18870
19480
  ),
@@ -19009,7 +19619,7 @@ Expected output: ${outputFileAbsolute}`;
19009
19619
  }
19010
19620
  const ok = cliProcessOk && artefactOk && validationOk;
19011
19621
  if (ok && opts.aceEnabled && sessionIdForAceTrace) {
19012
- const rel = path26.relative(ctx, outputFileAbsolute).split(path26.sep).join("/");
19622
+ const rel = path27.relative(ctx, outputFileAbsolute).split(path27.sep).join("/");
19013
19623
  opts.onRemediationPhase?.("aceTracing");
19014
19624
  try {
19015
19625
  await runAceTracePhase({
@@ -19057,7 +19667,7 @@ Expected output: ${outputFileAbsolute}`;
19057
19667
  var import_child_process4 = require("child_process");
19058
19668
  var fs31 = __toESM(require("fs"));
19059
19669
  var os4 = __toESM(require("os"));
19060
- var path27 = __toESM(require("path"));
19670
+ var path28 = __toESM(require("path"));
19061
19671
 
19062
19672
  // src/analysis/gitWorktreeStaleRecovery.ts
19063
19673
  var import_child_process3 = require("child_process");
@@ -19113,7 +19723,7 @@ function allocateEasyspecsWorktreePath(parentDir) {
19113
19723
  const start = maxEasyspecsWorktreeIndex(parentDir);
19114
19724
  for (let i = start + 1; i < start + 1e4; i++) {
19115
19725
  const name = formatEasyspecsWorktreeFolderName(i);
19116
- const wt = path27.join(parentDir, name);
19726
+ const wt = path28.join(parentDir, name);
19117
19727
  if (!fs31.existsSync(wt)) {
19118
19728
  return wt;
19119
19729
  }
@@ -19133,7 +19743,7 @@ function readHeadBranchShort(repoRoot) {
19133
19743
  return b.length > 0 ? b : void 0;
19134
19744
  }
19135
19745
  function createInPlaceWorktreeHandle(repositoryRootAbs) {
19136
- const root = path27.resolve(repositoryRootAbs);
19746
+ const root = path28.resolve(repositoryRootAbs);
19137
19747
  return {
19138
19748
  path: root,
19139
19749
  repoRoot: root,
@@ -19142,16 +19752,16 @@ function createInPlaceWorktreeHandle(repositoryRootAbs) {
19142
19752
  };
19143
19753
  }
19144
19754
  function resolveAnalysisCheckoutHandle(worktreePath, repositoryRoot) {
19145
- const wt = path27.resolve(worktreePath);
19146
- const repo = path27.resolve(repositoryRoot);
19755
+ const wt = path28.resolve(worktreePath);
19756
+ const repo = path28.resolve(repositoryRoot);
19147
19757
  if (wt === repo) {
19148
19758
  return createInPlaceWorktreeHandle(repo);
19149
19759
  }
19150
19760
  return attachWorktreeHandle(worktreePath, repositoryRoot);
19151
19761
  }
19152
19762
  function attachWorktreeHandle(worktreePath, repositoryRoot) {
19153
- const wt = path27.resolve(worktreePath);
19154
- const repo = path27.resolve(repositoryRoot);
19763
+ const wt = path28.resolve(worktreePath);
19764
+ const repo = path28.resolve(repositoryRoot);
19155
19765
  return {
19156
19766
  path: wt,
19157
19767
  repoRoot: repo,
@@ -19169,7 +19779,7 @@ function attachWorktreeHandle(worktreePath, repositoryRoot) {
19169
19779
  };
19170
19780
  }
19171
19781
  function createAnalysisWorktree(repoRoot, options) {
19172
- const parent = path27.join(os4.tmpdir(), "easyspecs-analysis");
19782
+ const parent = path28.join(os4.tmpdir(), "easyspecs-analysis");
19173
19783
  const wt = allocateEasyspecsWorktreePath(parent);
19174
19784
  if (!wt) {
19175
19785
  return {
@@ -19200,7 +19810,7 @@ function createAnalysisWorktree(repoRoot, options) {
19200
19810
  if (attempt === 0 && isMissingRegisteredWorktreeError(fullMsg)) {
19201
19811
  const pr = gitWorktreePruneSync(repoRoot);
19202
19812
  if (pr.ok) {
19203
- const wtName = path27.basename(wt);
19813
+ const wtName = path28.basename(wt);
19204
19814
  options?.log?.(`[worktree] git worktree prune \u2014 cleared stale registration for ${wtName} (${wt})`);
19205
19815
  continue;
19206
19816
  }
@@ -19298,7 +19908,7 @@ function noteOpenCodeAgentLaunched() {
19298
19908
 
19299
19909
  // src/contextIndexAssembler.ts
19300
19910
  var fs32 = __toESM(require("fs"));
19301
- var path28 = __toESM(require("path"));
19911
+ var path29 = __toESM(require("path"));
19302
19912
 
19303
19913
  // src/srsModel.ts
19304
19914
  var APPLICATION_CONTEXT_KIND = "easyspecs.application-context";
@@ -19330,7 +19940,7 @@ function tryAttachMarkdown(node, contextDir2, relativePath) {
19330
19940
  if (!norm || norm.includes("..")) {
19331
19941
  return false;
19332
19942
  }
19333
- const full = path28.join(contextDir2, norm);
19943
+ const full = path29.join(contextDir2, norm);
19334
19944
  try {
19335
19945
  if (!fs32.existsSync(full) || !fs32.statSync(full).isFile()) {
19336
19946
  return false;
@@ -19388,7 +19998,7 @@ function mergePersistedIndexUploadMetadata(previous, next) {
19388
19998
  walkScopes3(next, apply);
19389
19999
  }
19390
20000
  function tryReadExistingIndexForMerge(contextDir2) {
19391
- const p = path28.join(contextDir2, "index-application-context.json");
20001
+ const p = path29.join(contextDir2, "index-application-context.json");
19392
20002
  try {
19393
20003
  if (!fs32.existsSync(p)) {
19394
20004
  return null;
@@ -19447,7 +20057,7 @@ function applyDetailMarkdownToDocument(doc, contextDir2) {
19447
20057
  }
19448
20058
  }
19449
20059
  }
19450
- const exp = readJson2(path28.join(contextDir2, "experiences-list.json"));
20060
+ const exp = readJson2(path29.join(contextDir2, "experiences-list.json"));
19451
20061
  if (doc.scopes.Experience?.nodes && exp?.views) {
19452
20062
  for (const vNode of doc.scopes.Experience.nodes) {
19453
20063
  if (vNode.type !== "View" || !vNode.code) {
@@ -19473,7 +20083,7 @@ function applyDetailMarkdownToDocument(doc, contextDir2) {
19473
20083
  }
19474
20084
  }
19475
20085
  }
19476
- const svcData = readJson2(path28.join(contextDir2, "services-list.json"));
20086
+ const svcData = readJson2(path29.join(contextDir2, "services-list.json"));
19477
20087
  if (doc.scopes.Service?.nodes && svcData?.services) {
19478
20088
  for (const sNode of doc.scopes.Service.nodes) {
19479
20089
  if (sNode.type !== "Service" || !sNode.code) {
@@ -19499,7 +20109,7 @@ function applyDetailMarkdownToDocument(doc, contextDir2) {
19499
20109
  }
19500
20110
  }
19501
20111
  }
19502
- const dmData = readJson2(path28.join(contextDir2, "data-model-list.json"));
20112
+ const dmData = readJson2(path29.join(contextDir2, "data-model-list.json"));
19503
20113
  if (doc.scopes.DataModel?.nodes && dmData?.entities) {
19504
20114
  for (const eNode of doc.scopes.DataModel.nodes) {
19505
20115
  if (eNode.type !== "Entity" || !eNode.code) {
@@ -19531,7 +20141,7 @@ function applyDetailMarkdownToDocument(doc, contextDir2) {
19531
20141
  }
19532
20142
  }
19533
20143
  }
19534
- const tsData = readJson2(path28.join(contextDir2, "tech-stack-list.json"));
20144
+ const tsData = readJson2(path29.join(contextDir2, "tech-stack-list.json"));
19535
20145
  if (doc.scopes.TechStack?.nodes && tsData?.tools) {
19536
20146
  for (const tNode of doc.scopes.TechStack.nodes) {
19537
20147
  if (tNode.type !== "Tool" || !tNode.code) {
@@ -19544,7 +20154,70 @@ function applyDetailMarkdownToDocument(doc, contextDir2) {
19544
20154
  }
19545
20155
  }
19546
20156
  }
19547
- doc.version = anyLink ? 2 : 1;
20157
+ const infraData = readJson2(path29.join(contextDir2, "infrastructure-list.json"));
20158
+ if (doc.scopes.Infrastructure?.nodes && infraData?.infrastructure) {
20159
+ for (const inNode of doc.scopes.Infrastructure.nodes) {
20160
+ if (inNode.type !== "Infrastructure" || !inNode.code) {
20161
+ continue;
20162
+ }
20163
+ const inRow = infraData.infrastructure.find((r) => r.code === inNode.code);
20164
+ const inSlug = inRow ? slugOrFallback(inRow) : slugOrFallback({ code: inNode.code, name: inNode.name });
20165
+ if (inSlug && tryAttachMarkdown(inNode, contextDir2, `${inNode.code}-${inSlug}.md`)) {
20166
+ anyLink = true;
20167
+ }
20168
+ const cl = readJson2(
20169
+ path29.join(contextDir2, `${inNode.code}-components-list.json`)
20170
+ );
20171
+ if (!inNode.children || !cl?.components) {
20172
+ continue;
20173
+ }
20174
+ for (const icNode of inNode.children) {
20175
+ if (icNode.type !== "Component" || !icNode.code) {
20176
+ continue;
20177
+ }
20178
+ const icRow = cl.components.find((c) => c.code === icNode.code);
20179
+ const icSlug = icRow ? slugOrFallback(icRow) : slugOrFallback({ code: icNode.code, name: icNode.name });
20180
+ if (icSlug && tryAttachMarkdown(icNode, contextDir2, `${inNode.code}_${icNode.code}-${icSlug}.md`)) {
20181
+ anyLink = true;
20182
+ }
20183
+ }
20184
+ }
20185
+ }
20186
+ const qaData = readJson2(path29.join(contextDir2, "qa-list.json"));
20187
+ if (doc.scopes.QA?.nodes && qaData?.qa) {
20188
+ for (const qaNode of doc.scopes.QA.nodes) {
20189
+ if (qaNode.type !== "QA" || !qaNode.code) {
20190
+ continue;
20191
+ }
20192
+ const qaRow = qaData.qa.find((r) => r.code === qaNode.code);
20193
+ const qaSlug = qaRow ? slugOrFallback(qaRow) : slugOrFallback({ code: qaNode.code, name: qaNode.name });
20194
+ if (qaSlug && tryAttachMarkdown(qaNode, contextDir2, `${qaNode.code}-${qaSlug}.md`)) {
20195
+ anyLink = true;
20196
+ }
20197
+ const tc = readJson2(path29.join(contextDir2, `${qaNode.code}-test-cases-list.json`));
20198
+ if (!qaNode.children || !tc?.testCases) {
20199
+ continue;
20200
+ }
20201
+ for (const tcNode of qaNode.children) {
20202
+ if (tcNode.type !== "TestCase" || !tcNode.code) {
20203
+ continue;
20204
+ }
20205
+ const tcRow = tc.testCases.find((t) => t.code === tcNode.code);
20206
+ const tcSlug = tcRow ? slugOrFallback(tcRow) : slugOrFallback({ code: tcNode.code, name: tcNode.name });
20207
+ if (tcSlug && tryAttachMarkdown(tcNode, contextDir2, `${qaNode.code}_${tcNode.code}-${tcSlug}.md`)) {
20208
+ anyLink = true;
20209
+ }
20210
+ }
20211
+ }
20212
+ }
20213
+ const hasInfraOrQaScope = Boolean(doc.scopes.Infrastructure?.nodes?.length || doc.scopes.QA?.nodes?.length);
20214
+ if (hasInfraOrQaScope) {
20215
+ doc.version = 3;
20216
+ } else if (anyLink) {
20217
+ doc.version = 2;
20218
+ } else {
20219
+ doc.version = 1;
20220
+ }
19548
20221
  }
19549
20222
  function readJson2(filePath) {
19550
20223
  try {
@@ -19555,7 +20228,7 @@ function readJson2(filePath) {
19555
20228
  }
19556
20229
  }
19557
20230
  function safeReadFeaturesList(contextDir2) {
19558
- const p = path28.join(contextDir2, "features-list.json");
20231
+ const p = path29.join(contextDir2, "features-list.json");
19559
20232
  const data = readJson2(p);
19560
20233
  if (!data?.features?.length) {
19561
20234
  return null;
@@ -19563,7 +20236,7 @@ function safeReadFeaturesList(contextDir2) {
19563
20236
  return data;
19564
20237
  }
19565
20238
  function safeReadUseCasesList(contextDir2, feCode) {
19566
- const p = path28.join(contextDir2, `${feCode}-use-cases-list.json`);
20239
+ const p = path29.join(contextDir2, `${feCode}-use-cases-list.json`);
19567
20240
  const data = readJson2(p);
19568
20241
  if (!data?.useCases?.length) {
19569
20242
  return null;
@@ -19571,7 +20244,7 @@ function safeReadUseCasesList(contextDir2, feCode) {
19571
20244
  return data;
19572
20245
  }
19573
20246
  function scenariosListPath(contextDir2, feCode, ucCode) {
19574
- return path28.join(contextDir2, `${feCode}_${ucCode}-scenarios-list.json`);
20247
+ return path29.join(contextDir2, `${feCode}_${ucCode}-scenarios-list.json`);
19575
20248
  }
19576
20249
  function buildFeatureScope(contextDir2) {
19577
20250
  const fl = safeReadFeaturesList(contextDir2);
@@ -19608,7 +20281,7 @@ function buildFeatureScope(contextDir2) {
19608
20281
  return nodes;
19609
20282
  }
19610
20283
  function buildExperienceScope(contextDir2) {
19611
- const data = readJson2(path28.join(contextDir2, "experiences-list.json"));
20284
+ const data = readJson2(path29.join(contextDir2, "experiences-list.json"));
19612
20285
  if (!data?.views?.length) {
19613
20286
  return [];
19614
20287
  }
@@ -19620,7 +20293,7 @@ function buildExperienceScope(contextDir2) {
19620
20293
  }));
19621
20294
  }
19622
20295
  function buildServiceScope(contextDir2) {
19623
- const data = readJson2(path28.join(contextDir2, "services-list.json"));
20296
+ const data = readJson2(path29.join(contextDir2, "services-list.json"));
19624
20297
  if (!data?.services?.length) {
19625
20298
  return [];
19626
20299
  }
@@ -19632,13 +20305,13 @@ function buildServiceScope(contextDir2) {
19632
20305
  }));
19633
20306
  }
19634
20307
  function readEntityFieldsList(contextDir2, dmCode) {
19635
- const p = path28.join(contextDir2, `${dmCode}-fields-list.json`);
20308
+ const p = path29.join(contextDir2, `${dmCode}-fields-list.json`);
19636
20309
  const file = readJson2(p);
19637
20310
  const rows = Array.isArray(file?.fields) ? file.fields : [];
19638
20311
  return rows.filter((f) => Boolean(f?.code && f?.name));
19639
20312
  }
19640
20313
  function buildDataModelScope(contextDir2) {
19641
- const data = readJson2(path28.join(contextDir2, "data-model-list.json"));
20314
+ const data = readJson2(path29.join(contextDir2, "data-model-list.json"));
19642
20315
  if (!data?.entities?.length) {
19643
20316
  return [];
19644
20317
  }
@@ -19656,12 +20329,58 @@ function buildDataModelScope(contextDir2) {
19656
20329
  });
19657
20330
  }
19658
20331
  function buildTechStackScope(contextDir2) {
19659
- const data = readJson2(path28.join(contextDir2, "tech-stack-list.json"));
20332
+ const data = readJson2(path29.join(contextDir2, "tech-stack-list.json"));
19660
20333
  if (!data?.tools?.length) {
19661
20334
  return [];
19662
20335
  }
19663
20336
  return data.tools.map((t) => ({ type: "Tool", code: t.code, name: t.name }));
19664
20337
  }
20338
+ function buildInfrastructureScope(contextDir2) {
20339
+ const data = readJson2(path29.join(contextDir2, "infrastructure-list.json"));
20340
+ if (!data?.infrastructure?.length) {
20341
+ return [];
20342
+ }
20343
+ const nodes = [];
20344
+ for (const row2 of data.infrastructure) {
20345
+ const cl = readJson2(
20346
+ path29.join(contextDir2, `${row2.code}-components-list.json`)
20347
+ );
20348
+ const icNodes = cl?.components?.map((c) => ({
20349
+ type: "Component",
20350
+ code: c.code,
20351
+ name: c.name
20352
+ })) ?? [];
20353
+ nodes.push({
20354
+ type: "Infrastructure",
20355
+ code: row2.code,
20356
+ name: row2.name,
20357
+ children: icNodes.length ? icNodes : void 0
20358
+ });
20359
+ }
20360
+ return nodes;
20361
+ }
20362
+ function buildQaScope(contextDir2) {
20363
+ const data = readJson2(path29.join(contextDir2, "qa-list.json"));
20364
+ if (!data?.qa?.length) {
20365
+ return [];
20366
+ }
20367
+ const nodes = [];
20368
+ for (const row2 of data.qa) {
20369
+ const tc = readJson2(path29.join(contextDir2, `${row2.code}-test-cases-list.json`));
20370
+ const tcNodes = tc?.testCases?.map((t) => ({
20371
+ type: "TestCase",
20372
+ code: t.code,
20373
+ name: t.name
20374
+ })) ?? [];
20375
+ nodes.push({
20376
+ type: "QA",
20377
+ code: row2.code,
20378
+ name: row2.name,
20379
+ children: tcNodes.length ? tcNodes : void 0
20380
+ });
20381
+ }
20382
+ return nodes;
20383
+ }
19665
20384
  function countDriftNodes(nodes) {
19666
20385
  let n = 0;
19667
20386
  for (const node of nodes) {
@@ -19683,6 +20402,8 @@ function assembleApplicationContextIndex(contextDir2, title = "Application conte
19683
20402
  const serviceNodes = buildServiceScope(contextDir2);
19684
20403
  const dataModelNodes = buildDataModelScope(contextDir2);
19685
20404
  const techNodes = buildTechStackScope(contextDir2);
20405
+ const infrastructureNodes = buildInfrastructureScope(contextDir2);
20406
+ const qaNodes = buildQaScope(contextDir2);
19686
20407
  const scopes = {};
19687
20408
  if (featureNodes.length) {
19688
20409
  scopes.Feature = { nodes: featureNodes };
@@ -19699,10 +20420,17 @@ function assembleApplicationContextIndex(contextDir2, title = "Application conte
19699
20420
  if (techNodes.length) {
19700
20421
  scopes.TechStack = { nodes: techNodes };
19701
20422
  }
19702
- const driftCount = countScopeDrift(featureNodes) + countScopeDrift(experienceNodes) + countScopeDrift(serviceNodes) + countScopeDrift(dataModelNodes) + countScopeDrift(techNodes);
20423
+ if (infrastructureNodes.length) {
20424
+ scopes.Infrastructure = { nodes: infrastructureNodes };
20425
+ }
20426
+ if (qaNodes.length) {
20427
+ scopes.QA = { nodes: qaNodes };
20428
+ }
20429
+ const driftCount = countScopeDrift(featureNodes) + countScopeDrift(experienceNodes) + countScopeDrift(serviceNodes) + countScopeDrift(dataModelNodes) + countScopeDrift(techNodes) + countScopeDrift(infrastructureNodes) + countScopeDrift(qaNodes);
20430
+ const hasInfraOrQa = infrastructureNodes.length > 0 || qaNodes.length > 0;
19703
20431
  return {
19704
20432
  kind: APPLICATION_CONTEXT_KIND,
19705
- version: 1,
20433
+ version: hasInfraOrQa ? 3 : 1,
19706
20434
  title,
19707
20435
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
19708
20436
  driftCount,
@@ -19725,7 +20453,7 @@ function writeIndexApplicationContext(contextDir2, title, options) {
19725
20453
  if (!validation.ok) {
19726
20454
  throw new Error(`index-application-context.json schema validation failed: ${validation.errors.join("; ")}`);
19727
20455
  }
19728
- const target = path28.join(contextDir2, "index-application-context.json");
20456
+ const target = path29.join(contextDir2, "index-application-context.json");
19729
20457
  const tmp = `${target}.${process.pid}.tmp`;
19730
20458
  try {
19731
20459
  fs32.writeFileSync(tmp, JSON.stringify(doc, null, 2), "utf-8");
@@ -19742,7 +20470,7 @@ function writeIndexApplicationContext(contextDir2, title, options) {
19742
20470
 
19743
20471
  // src/pipelines/linkMapping/linkMappingPipeline.ts
19744
20472
  var fs33 = __toESM(require("node:fs"));
19745
- var path29 = __toESM(require("node:path"));
20473
+ var path30 = __toESM(require("node:path"));
19746
20474
  var SLUG2 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
19747
20475
  function safeStr2(v) {
19748
20476
  return typeof v === "string" ? v.trim() : "";
@@ -19780,13 +20508,13 @@ var HEADING_CHILD_DOCUMENTS = "## EasySpecs \u2014 Child documents";
19780
20508
  var HEADING_PARENT_DOCUMENTS = "## EasySpecs \u2014 Parent documents";
19781
20509
  var NONE_LINE = "*None.*";
19782
20510
  function scenariosListPath2(contextDir2, feCode, ucCode) {
19783
- return path29.join(contextDir2, `${feCode}_${ucCode}-scenarios-list.json`);
20511
+ return path30.join(contextDir2, `${feCode}_${ucCode}-scenarios-list.json`);
19784
20512
  }
19785
20513
  function useCasesListPath(contextDir2, feCode) {
19786
- return path29.join(contextDir2, `${feCode}-use-cases-list.json`);
20514
+ return path30.join(contextDir2, `${feCode}-use-cases-list.json`);
19787
20515
  }
19788
20516
  function readEntityFieldsList2(contextDir2, dmCode) {
19789
- const p = path29.join(contextDir2, `${dmCode}-fields-list.json`);
20517
+ const p = path30.join(contextDir2, `${dmCode}-fields-list.json`);
19790
20518
  const file = readJson3(p);
19791
20519
  const rows = Array.isArray(file?.fields) ? file.fields : [];
19792
20520
  return rows.filter((f) => Boolean(f?.code && f?.name));
@@ -19796,7 +20524,7 @@ function existsContextFile(contextDir2, rel) {
19796
20524
  if (!norm || norm.includes("..")) {
19797
20525
  return false;
19798
20526
  }
19799
- const full = path29.join(contextDir2, norm);
20527
+ const full = path30.join(contextDir2, norm);
19800
20528
  try {
19801
20529
  return fs33.existsSync(full) && fs33.statSync(full).isFile();
19802
20530
  } catch {
@@ -19847,11 +20575,39 @@ function toolDetailBasename(t) {
19847
20575
  const slug = slugOrFallback2(t);
19848
20576
  return slug ? `${t.code}-${slug}.md` : null;
19849
20577
  }
20578
+ function readInfrastructureComponentsList(contextDir2, inCode) {
20579
+ const p = path30.join(contextDir2, `${inCode}-components-list.json`);
20580
+ const file = readJson3(p);
20581
+ const rows = Array.isArray(file?.components) ? file.components : [];
20582
+ return rows.filter((c) => Boolean(c?.code && c?.name));
20583
+ }
20584
+ function readQaTestCasesList(contextDir2, qaCode) {
20585
+ const p = path30.join(contextDir2, `${qaCode}-test-cases-list.json`);
20586
+ const file = readJson3(p);
20587
+ const rows = Array.isArray(file?.testCases) ? file.testCases : [];
20588
+ return rows.filter((tc) => Boolean(tc?.code && tc?.name));
20589
+ }
20590
+ function infrastructureDetailBasename(item) {
20591
+ const slug = slugOrFallback2(item);
20592
+ return slug ? `${item.code}-${slug}.md` : null;
20593
+ }
20594
+ function componentDetailBasename(inCode, c) {
20595
+ const slug = slugOrFallback2(c);
20596
+ return slug ? `${inCode}_${c.code}-${slug}.md` : null;
20597
+ }
20598
+ function qaDetailBasename(item) {
20599
+ const slug = slugOrFallback2(item);
20600
+ return slug ? `${item.code}-${slug}.md` : null;
20601
+ }
20602
+ function testCaseDetailBasename(qaCode, tc) {
20603
+ const slug = slugOrFallback2(tc);
20604
+ return slug ? `${qaCode}_${tc.code}-${slug}.md` : null;
20605
+ }
19850
20606
  function markdownRelativeHref(fromFileAbs, toFileAbs) {
19851
- const dir = path29.dirname(fromFileAbs);
19852
- let rel = path29.relative(dir, toFileAbs);
20607
+ const dir = path30.dirname(fromFileAbs);
20608
+ let rel = path30.relative(dir, toFileAbs);
19853
20609
  rel = rel.replace(/\\/g, "/");
19854
- if (!rel.startsWith(".") && !path29.isAbsolute(rel)) {
20610
+ if (!rel.startsWith(".") && !path30.isAbsolute(rel)) {
19855
20611
  rel = `./${rel}`;
19856
20612
  }
19857
20613
  return rel;
@@ -19900,7 +20656,7 @@ function collectMarkdownFilesRecursive(dir, acc) {
19900
20656
  return;
19901
20657
  }
19902
20658
  for (const e of entries) {
19903
- const p = path29.join(dir, e.name);
20659
+ const p = path30.join(dir, e.name);
19904
20660
  if (e.isDirectory()) {
19905
20661
  collectMarkdownFilesRecursive(p, acc);
19906
20662
  } else if (e.isFile() && e.name.endsWith(".md")) {
@@ -19940,13 +20696,13 @@ function validateEasyspecsNavLinksInContext(contextDirAbs) {
19940
20696
  if (!href || href.startsWith("http://") || href.startsWith("https://") || href.startsWith("#")) {
19941
20697
  continue;
19942
20698
  }
19943
- const target = path29.normalize(path29.join(path29.dirname(abs), href));
20699
+ const target = path30.normalize(path30.join(path30.dirname(abs), href));
19944
20700
  try {
19945
20701
  if (!fs33.existsSync(target) || !fs33.statSync(target).isFile()) {
19946
- broken.push(`${path29.relative(contextDirAbs, abs)} \u2192 ${href}`);
20702
+ broken.push(`${path30.relative(contextDirAbs, abs)} \u2192 ${href}`);
19947
20703
  }
19948
20704
  } catch {
19949
- broken.push(`${path29.relative(contextDirAbs, abs)} \u2192 ${href}`);
20705
+ broken.push(`${path30.relative(contextDirAbs, abs)} \u2192 ${href}`);
19950
20706
  }
19951
20707
  }
19952
20708
  scan = scan.slice(end + NAV_CHILDREN_END.length);
@@ -19966,13 +20722,13 @@ function validateEasyspecsNavLinksInContext(contextDirAbs) {
19966
20722
  if (!href || href.startsWith("http://") || href.startsWith("https://") || href.startsWith("#")) {
19967
20723
  continue;
19968
20724
  }
19969
- const target = path29.normalize(path29.join(path29.dirname(abs), href));
20725
+ const target = path30.normalize(path30.join(path30.dirname(abs), href));
19970
20726
  try {
19971
20727
  if (!fs33.existsSync(target) || !fs33.statSync(target).isFile()) {
19972
- broken.push(`${path29.relative(contextDirAbs, abs)} \u2192 ${href}`);
20728
+ broken.push(`${path30.relative(contextDirAbs, abs)} \u2192 ${href}`);
19973
20729
  }
19974
20730
  } catch {
19975
- broken.push(`${path29.relative(contextDirAbs, abs)} \u2192 ${href}`);
20731
+ broken.push(`${path30.relative(contextDirAbs, abs)} \u2192 ${href}`);
19976
20732
  }
19977
20733
  }
19978
20734
  scan = scan.slice(end + NAV_PARENTS_END.length);
@@ -19996,7 +20752,7 @@ function buildNavEdges(contextDir2) {
19996
20752
  const projectBasename = "project.md";
19997
20753
  const archBasename = "architecture.md";
19998
20754
  addParent(archBasename, "EasySpecs project", projectBasename);
19999
- const fl = readJson3(path29.join(contextDir2, "features-list.json"));
20755
+ const fl = readJson3(path30.join(contextDir2, "features-list.json"));
20000
20756
  if (fl?.features?.length) {
20001
20757
  for (const fe of fl.features) {
20002
20758
  const feBase = featureDetailBasename(fe);
@@ -20033,7 +20789,7 @@ function buildNavEdges(contextDir2) {
20033
20789
  }
20034
20790
  }
20035
20791
  }
20036
- const exp = readJson3(path29.join(contextDir2, "experiences-list.json"));
20792
+ const exp = readJson3(path30.join(contextDir2, "experiences-list.json"));
20037
20793
  if (exp?.views?.length) {
20038
20794
  for (const v of exp.views) {
20039
20795
  const vBase = viewDetailBasename(v);
@@ -20055,7 +20811,7 @@ function buildNavEdges(contextDir2) {
20055
20811
  }
20056
20812
  }
20057
20813
  }
20058
- const svcData = readJson3(path29.join(contextDir2, "services-list.json"));
20814
+ const svcData = readJson3(path30.join(contextDir2, "services-list.json"));
20059
20815
  if (svcData?.services?.length) {
20060
20816
  for (const s of svcData.services) {
20061
20817
  const sBase = serviceDetailBasename(s);
@@ -20077,7 +20833,7 @@ function buildNavEdges(contextDir2) {
20077
20833
  }
20078
20834
  }
20079
20835
  }
20080
- const dmData = readJson3(path29.join(contextDir2, "data-model-list.json"));
20836
+ const dmData = readJson3(path30.join(contextDir2, "data-model-list.json"));
20081
20837
  if (dmData?.entities?.length) {
20082
20838
  for (const e of dmData.entities) {
20083
20839
  const eBase = entityDetailBasename(e);
@@ -20107,7 +20863,7 @@ function buildNavEdges(contextDir2) {
20107
20863
  }
20108
20864
  }
20109
20865
  }
20110
- const tsData = readJson3(path29.join(contextDir2, "tech-stack-list.json"));
20866
+ const tsData = readJson3(path30.join(contextDir2, "tech-stack-list.json"));
20111
20867
  if (tsData?.tools?.length) {
20112
20868
  for (const t of tsData.tools) {
20113
20869
  const tBase = toolDetailBasename(t);
@@ -20117,11 +20873,51 @@ function buildNavEdges(contextDir2) {
20117
20873
  addParent(tBase, "EasySpecs project", projectBasename);
20118
20874
  }
20119
20875
  }
20876
+ const inData = readJson3(path30.join(contextDir2, "infrastructure-list.json"));
20877
+ if (inData?.infrastructure?.length) {
20878
+ for (const item of inData.infrastructure) {
20879
+ const inBase = infrastructureDetailBasename(item);
20880
+ if (!inBase || !existsContextFile(contextDir2, inBase)) {
20881
+ continue;
20882
+ }
20883
+ addParent(inBase, "EasySpecs project", projectBasename);
20884
+ const components = readInfrastructureComponentsList(contextDir2, item.code);
20885
+ for (const c of components) {
20886
+ const cBase = componentDetailBasename(item.code, c);
20887
+ if (!cBase || !existsContextFile(contextDir2, cBase)) {
20888
+ continue;
20889
+ }
20890
+ addChild(inBase, c.name || c.code, cBase);
20891
+ addParent(cBase, item.name || item.code, inBase);
20892
+ addParent(cBase, "EasySpecs project", projectBasename);
20893
+ }
20894
+ }
20895
+ }
20896
+ const qaData = readJson3(path30.join(contextDir2, "qa-list.json"));
20897
+ if (qaData?.qa?.length) {
20898
+ for (const item of qaData.qa) {
20899
+ const qaBase = qaDetailBasename(item);
20900
+ if (!qaBase || !existsContextFile(contextDir2, qaBase)) {
20901
+ continue;
20902
+ }
20903
+ addParent(qaBase, "EasySpecs project", projectBasename);
20904
+ const testCases = readQaTestCasesList(contextDir2, item.code);
20905
+ for (const tc of testCases) {
20906
+ const tcBase = testCaseDetailBasename(item.code, tc);
20907
+ if (!tcBase || !existsContextFile(contextDir2, tcBase)) {
20908
+ continue;
20909
+ }
20910
+ addChild(qaBase, tc.name || tc.code, tcBase);
20911
+ addParent(tcBase, item.name || item.code, qaBase);
20912
+ addParent(tcBase, "EasySpecs project", projectBasename);
20913
+ }
20914
+ }
20915
+ }
20120
20916
  return { children, parents };
20121
20917
  }
20122
20918
  function renderProjectMdSections(contextDir2) {
20123
20919
  const chunks = [];
20124
- const fl = readJson3(path29.join(contextDir2, "features-list.json"));
20920
+ const fl = readJson3(path30.join(contextDir2, "features-list.json"));
20125
20921
  const featureLines = [];
20126
20922
  if (fl?.features?.length) {
20127
20923
  for (const fe of fl.features) {
@@ -20132,7 +20928,7 @@ function renderProjectMdSections(contextDir2) {
20132
20928
  }
20133
20929
  }
20134
20930
  chunks.push("### Features", "", featureLines.length ? featureLines.join("\n") : NONE_LINE, "");
20135
- const exp = readJson3(path29.join(contextDir2, "experiences-list.json"));
20931
+ const exp = readJson3(path30.join(contextDir2, "experiences-list.json"));
20136
20932
  const xpLines = [];
20137
20933
  if (exp?.views?.length) {
20138
20934
  for (const v of exp.views) {
@@ -20143,7 +20939,7 @@ function renderProjectMdSections(contextDir2) {
20143
20939
  }
20144
20940
  }
20145
20941
  chunks.push("### Experiences", "", xpLines.length ? xpLines.join("\n") : NONE_LINE, "");
20146
- const svcData = readJson3(path29.join(contextDir2, "services-list.json"));
20942
+ const svcData = readJson3(path30.join(contextDir2, "services-list.json"));
20147
20943
  const svLines = [];
20148
20944
  if (svcData?.services?.length) {
20149
20945
  for (const s of svcData.services) {
@@ -20154,7 +20950,7 @@ function renderProjectMdSections(contextDir2) {
20154
20950
  }
20155
20951
  }
20156
20952
  chunks.push("### Services", "", svLines.length ? svLines.join("\n") : NONE_LINE, "");
20157
- const tsData = readJson3(path29.join(contextDir2, "tech-stack-list.json"));
20953
+ const tsData = readJson3(path30.join(contextDir2, "tech-stack-list.json"));
20158
20954
  const tsLines = [];
20159
20955
  if (tsData?.tools?.length) {
20160
20956
  for (const t of tsData.tools) {
@@ -20165,7 +20961,29 @@ function renderProjectMdSections(contextDir2) {
20165
20961
  }
20166
20962
  }
20167
20963
  chunks.push("### Tech stack", "", tsLines.length ? tsLines.join("\n") : NONE_LINE, "");
20168
- const dmData = readJson3(path29.join(contextDir2, "data-model-list.json"));
20964
+ const inData = readJson3(path30.join(contextDir2, "infrastructure-list.json"));
20965
+ const inLines = [];
20966
+ if (inData?.infrastructure?.length) {
20967
+ for (const item of inData.infrastructure) {
20968
+ const inBase = infrastructureDetailBasename(item);
20969
+ if (inBase && existsContextFile(contextDir2, inBase)) {
20970
+ inLines.push(bulletLink(item.name || item.code, `./${inBase}`));
20971
+ }
20972
+ }
20973
+ }
20974
+ chunks.push("### Infrastructure", "", inLines.length ? inLines.join("\n") : NONE_LINE, "");
20975
+ const qaData = readJson3(path30.join(contextDir2, "qa-list.json"));
20976
+ const qaLines = [];
20977
+ if (qaData?.qa?.length) {
20978
+ for (const item of qaData.qa) {
20979
+ const qaBase = qaDetailBasename(item);
20980
+ if (qaBase && existsContextFile(contextDir2, qaBase)) {
20981
+ qaLines.push(bulletLink(item.name || item.code, `./${qaBase}`));
20982
+ }
20983
+ }
20984
+ }
20985
+ chunks.push("### QA", "", qaLines.length ? qaLines.join("\n") : NONE_LINE, "");
20986
+ const dmData = readJson3(path30.join(contextDir2, "data-model-list.json"));
20169
20987
  const dmLines = [];
20170
20988
  if (dmData?.entities?.length) {
20171
20989
  for (const e of dmData.entities) {
@@ -20175,15 +20993,15 @@ function renderProjectMdSections(contextDir2) {
20175
20993
  }
20176
20994
  }
20177
20995
  }
20178
- const dmListPath = path29.join(contextDir2, "data-model-list.json");
20996
+ const dmListPath = path30.join(contextDir2, "data-model-list.json");
20179
20997
  if (dmLines.length === 0 && fs33.existsSync(dmListPath)) {
20180
20998
  dmLines.push(bulletLink("Data model registry (JSON)", "./data-model-list.json"));
20181
20999
  }
20182
21000
  chunks.push("### Data model", "", dmLines.length ? dmLines.join("\n") : NONE_LINE, "");
20183
21001
  return chunks;
20184
21002
  }
20185
- function patchOneFile(contextDir2, basename17, innerChildren, innerParents, opts) {
20186
- const abs = path29.join(contextDir2, basename17);
21003
+ function patchOneFile(contextDir2, basename18, innerChildren, innerParents, opts) {
21004
+ const abs = path30.join(contextDir2, basename18);
20187
21005
  if (!fs33.existsSync(abs)) {
20188
21006
  return;
20189
21007
  }
@@ -20191,7 +21009,7 @@ function patchOneFile(contextDir2, basename17, innerChildren, innerParents, opts
20191
21009
  try {
20192
21010
  body = fs33.readFileSync(abs, "utf8");
20193
21011
  } catch (e) {
20194
- opts.log?.(`[pipeline:link-mapping] skip read ${basename17}: ${e instanceof Error ? e.message : String(e)}`);
21012
+ opts.log?.(`[pipeline:link-mapping] skip read ${basename18}: ${e instanceof Error ? e.message : String(e)}`);
20195
21013
  return;
20196
21014
  }
20197
21015
  let next = replaceOrAppendRegion(body, NAV_CHILDREN_START, NAV_CHILDREN_END, innerChildren);
@@ -20199,13 +21017,13 @@ function patchOneFile(contextDir2, basename17, innerChildren, innerParents, opts
20199
21017
  fs33.writeFileSync(abs, next, "utf8");
20200
21018
  }
20201
21019
  function runLinkMappingPipeline(contextDirAbs, opts = {}) {
20202
- const contextDir2 = path29.resolve(contextDirAbs);
21020
+ const contextDir2 = path30.resolve(contextDirAbs);
20203
21021
  if (!fs33.existsSync(contextDir2) || !fs33.statSync(contextDir2).isDirectory()) {
20204
21022
  return { ok: false, error: `Context directory does not exist: ${contextDir2}` };
20205
21023
  }
20206
21024
  const edges = buildNavEdges(contextDir2);
20207
- const projectAbs = path29.join(contextDir2, "project.md");
20208
- const archAbs = path29.join(contextDir2, "architecture.md");
21025
+ const projectAbs = path30.join(contextDir2, "project.md");
21026
+ const archAbs = path30.join(contextDir2, "architecture.md");
20209
21027
  const projectChunks = renderProjectMdSections(contextDir2);
20210
21028
  const projectChildrenInner = buildInnerChildren(true, projectChunks);
20211
21029
  patchOneFile(contextDir2, "project.md", projectChildrenInner, buildInnerParents([]), opts);
@@ -20223,28 +21041,28 @@ function runLinkMappingPipeline(contextDirAbs, opts = {}) {
20223
21041
  };
20224
21042
  visitEdgeBasenames(edges.children);
20225
21043
  visitEdgeBasenames(edges.parents);
20226
- for (const basename17 of managedBasenames) {
20227
- if (basename17 === "project.md" || basename17 === "architecture.md") {
21044
+ for (const basename18 of managedBasenames) {
21045
+ if (basename18 === "project.md" || basename18 === "architecture.md") {
20228
21046
  continue;
20229
21047
  }
20230
- const abs = path29.join(contextDir2, basename17);
21048
+ const abs = path30.join(contextDir2, basename18);
20231
21049
  if (!fs33.existsSync(abs)) {
20232
21050
  continue;
20233
21051
  }
20234
- const childRows = edges.children.get(basename17) ?? [];
20235
- const parentRows = edges.parents.get(basename17) ?? [];
21052
+ const childRows = edges.children.get(basename18) ?? [];
21053
+ const parentRows = edges.parents.get(basename18) ?? [];
20236
21054
  const childLines = childRows.length === 0 ? NONE_LINE : childRows.map(
20237
- (c) => bulletLink(c.title, markdownRelativeHref(abs, path29.join(contextDir2, c.basename)))
21055
+ (c) => bulletLink(c.title, markdownRelativeHref(abs, path30.join(contextDir2, c.basename)))
20238
21056
  ).join("\n");
20239
21057
  const parentLines = parentRows.length === 0 ? NONE_LINE : parentRows.map(
20240
- (p) => bulletLink(p.title, markdownRelativeHref(abs, path29.join(contextDir2, p.basename)))
21058
+ (p) => bulletLink(p.title, markdownRelativeHref(abs, path30.join(contextDir2, p.basename)))
20241
21059
  ).join("\n");
20242
21060
  const innerChildren = `${HEADING_CHILD_DOCUMENTS}
20243
21061
 
20244
21062
  ${childLines}
20245
21063
  `;
20246
21064
  const innerParents = buildInnerParents(parentLines.split("\n").filter(Boolean));
20247
- patchOneFile(contextDir2, basename17, innerChildren, innerParents, opts);
21065
+ patchOneFile(contextDir2, basename18, innerChildren, innerParents, opts);
20248
21066
  }
20249
21067
  const validation = validateEasyspecsNavLinksInContext(contextDir2);
20250
21068
  if (!validation.ok) {
@@ -20258,7 +21076,7 @@ ${validation.brokenLinks.join("\n")}`;
20258
21076
  }
20259
21077
 
20260
21078
  // src/analysis/aceAnalysisConfig.ts
20261
- var path30 = __toESM(require("node:path"));
21079
+ var path31 = __toESM(require("node:path"));
20262
21080
  var vscode2 = __toESM(require_vscode_stub());
20263
21081
 
20264
21082
  // src/config/easyspecsAceConfigRead.ts
@@ -20336,7 +21154,7 @@ function setAceCliHeadlessMode(enabled) {
20336
21154
  cliHeadlessMode = enabled;
20337
21155
  }
20338
21156
  function normFs(p) {
20339
- return path30.resolve(p.trim());
21157
+ return path31.resolve(p.trim());
20340
21158
  }
20341
21159
  function getAceAnalysisEnabledForCheckout(analysisCheckoutRoot) {
20342
21160
  const seen = /* @__PURE__ */ new Set();
@@ -20407,13 +21225,13 @@ function getAceOfflineLearnAfterSameSessionTrace(analysisCheckoutRoot) {
20407
21225
 
20408
21226
  // src/analysis/repoSurfaceAssessment.ts
20409
21227
  var fs35 = __toESM(require("fs"));
20410
- var path31 = __toESM(require("path"));
21228
+ var path32 = __toESM(require("path"));
20411
21229
  var REPO_SURFACE_SCAN_BASENAME = "repo-surface-scan.json";
20412
21230
  function stripBom(s) {
20413
21231
  return s.length > 0 && s.charCodeAt(0) === 65279 ? s.slice(1) : s;
20414
21232
  }
20415
21233
  function readRepoSurfaceAssessment(contextDir2) {
20416
- const p = path31.join(contextDir2, REPO_SURFACE_SCAN_BASENAME);
21234
+ const p = path32.join(contextDir2, REPO_SURFACE_SCAN_BASENAME);
20417
21235
  try {
20418
21236
  const raw = stripBom(fs35.readFileSync(p, "utf-8"));
20419
21237
  const data = JSON.parse(raw);
@@ -20435,8 +21253,8 @@ function readRepoSurfaceAssessment(contextDir2) {
20435
21253
  }
20436
21254
  }
20437
21255
  function writeEmptyExperiencesListJson(contextDir2) {
20438
- const p = path31.join(contextDir2, "experiences-list.json");
20439
- fs35.mkdirSync(path31.dirname(p), { recursive: true });
21256
+ const p = path32.join(contextDir2, "experiences-list.json");
21257
+ fs35.mkdirSync(path32.dirname(p), { recursive: true });
20440
21258
  fs35.writeFileSync(
20441
21259
  p,
20442
21260
  `${JSON.stringify(
@@ -20455,6 +21273,7 @@ function writeEmptyExperiencesListJson(contextDir2) {
20455
21273
 
20456
21274
  // src/config/cpuPressureSample.ts
20457
21275
  var os5 = __toESM(require("node:os"));
21276
+ var DEFAULT_CPU_PRESSURE_THRESHOLD = 0.85;
20458
21277
  function createDefaultCpuPressureSampler() {
20459
21278
  const n = Math.max(1, os5.cpus()?.length ?? 1);
20460
21279
  return () => {
@@ -20466,7 +21285,7 @@ function createDefaultCpuPressureSampler() {
20466
21285
  };
20467
21286
  }
20468
21287
  function nextAdaptiveMaxAfterCpuSample(p) {
20469
- const threshold = p.threshold ?? 0.96;
21288
+ const threshold = p.threshold ?? DEFAULT_CPU_PRESSURE_THRESHOLD;
20470
21289
  if (p.activeRunningCount < 1 || !Number.isFinite(p.ratio) || p.ratio <= threshold) {
20471
21290
  return { adaptiveMax: p.priorAdaptiveMax, changed: false };
20472
21291
  }
@@ -20770,6 +21589,10 @@ function createHostPoolTick(deps) {
20770
21589
 
20771
21590
  // src/pipelines/synthesis/synthesisPipeline.ts
20772
21591
  var FE = /^FE-\d+$/;
21592
+ var IN = /^IN-\d+$/;
21593
+ var IC = /^IC-\d+$/;
21594
+ var QA = /^QA-\d+$/;
21595
+ var TC = /^TC-\d+$/;
20773
21596
  var UC = /^UC-\d+$/;
20774
21597
  var SC = /^SC-\d+$/;
20775
21598
  var DM = /^DM-\d+$/;
@@ -20825,7 +21648,11 @@ var ROOT_WORKSTATION_DEFS = [
20825
21648
  { id: "root:listDataModel", step: "listDataModel", parentIds: [] },
20826
21649
  { id: "root:reviewDataModelList", step: "reviewDataModelList", parentIds: ["root:listDataModel"] },
20827
21650
  { id: "root:listTechStack", step: "listTechStack", parentIds: [] },
20828
- { id: "root:reviewTechStackList", step: "reviewTechStackList", parentIds: ["root:listTechStack"] }
21651
+ { id: "root:reviewTechStackList", step: "reviewTechStackList", parentIds: ["root:listTechStack"] },
21652
+ { id: "root:listInfrastructure", step: "listInfrastructure", parentIds: [] },
21653
+ { id: "root:reviewInfrastructureList", step: "reviewInfrastructureList", parentIds: ["root:listInfrastructure"] },
21654
+ { id: "root:listQa", step: "listQa", parentIds: ["root:reviewFeaturesList"] },
21655
+ { id: "root:reviewQaList", step: "reviewQaList", parentIds: ["root:listQa"] }
20829
21656
  ];
20830
21657
  function buildRootItems() {
20831
21658
  const out = {};
@@ -20868,91 +21695,135 @@ function mdItem(id, parentIds, t) {
20868
21695
  };
20869
21696
  }
20870
21697
  function featureDetailTarget(contextDir2, code, name, slug) {
20871
- const basename17 = `${code}-${slug}.md`;
21698
+ const basename18 = `${code}-${slug}.md`;
20872
21699
  return {
20873
21700
  openCodeAgentStem: "agent-md-feature-detail",
20874
21701
  agentId: "ctx-md-feature-detail",
20875
21702
  displayName: "Feature detail",
20876
- outputBasename: basename17,
21703
+ outputBasename: basename18,
20877
21704
  taskDescription: `Document feature **${code}** (**${name}**, slug **${slug}**) per \`.gluecharm/context/features-list.json\`: scope, behaviour, main dependencies, entry points, and links to code under the worktree. Write exactly one markdown file at the output path; cite substantive claims with file and line (or range).`,
20878
- exists: fs37.existsSync(path32.join(contextDir2, basename17)) && fs37.statSync(path32.join(contextDir2, basename17)).size > 0
21705
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
20879
21706
  };
20880
21707
  }
20881
21708
  function viewDetailTarget(row2, contextDir2) {
20882
- const basename17 = `${row2.code}-${row2.slug}.md`;
21709
+ const basename18 = `${row2.code}-${row2.slug}.md`;
20883
21710
  return {
20884
21711
  openCodeAgentStem: "agent-md-view-detail",
20885
21712
  agentId: "ctx-md-view-detail",
20886
21713
  displayName: "View detail",
20887
- outputBasename: basename17,
21714
+ outputBasename: basename18,
20888
21715
  taskDescription: `Describe view **${row2.code}** (**${row2.name}**, slug **${row2.slug}**) per \`.gluecharm/context/experiences-list.json\`: purpose, layout, controls, navigation, data shown; ground in components and routes.`,
20889
- exists: fs37.existsSync(path32.join(contextDir2, basename17)) && fs37.statSync(path32.join(contextDir2, basename17)).size > 0
21716
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
20890
21717
  };
20891
21718
  }
20892
21719
  function interactionDetailTarget(viewCode, ix, contextDir2) {
20893
- const basename17 = `${viewCode}_${ix.code}-${ix.slug}.md`;
21720
+ const basename18 = `${viewCode}_${ix.code}-${ix.slug}.md`;
20894
21721
  return {
20895
21722
  openCodeAgentStem: "agent-md-interaction-detail",
20896
21723
  agentId: "ctx-md-interaction-detail",
20897
21724
  displayName: "Interaction detail",
20898
- outputBasename: basename17,
21725
+ outputBasename: basename18,
20899
21726
  taskDescription: `Document interaction **${ix.code}** (**${ix.name}**) on view **${viewCode}** per \`.gluecharm/context/experiences-list.json\`.`,
20900
- exists: fs37.existsSync(path32.join(contextDir2, basename17)) && fs37.statSync(path32.join(contextDir2, basename17)).size > 0
21727
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
20901
21728
  };
20902
21729
  }
20903
21730
  function serviceDetailTarget(row2, contextDir2) {
20904
- const basename17 = `${row2.code}-${row2.slug}.md`;
21731
+ const basename18 = `${row2.code}-${row2.slug}.md`;
20905
21732
  return {
20906
21733
  openCodeAgentStem: "agent-md-service-detail",
20907
21734
  agentId: "ctx-md-service-detail",
20908
21735
  displayName: "Service detail",
20909
- outputBasename: basename17,
21736
+ outputBasename: basename18,
20910
21737
  taskDescription: `Describe service **${row2.code}** (**${row2.name}**, slug **${row2.slug}**) per \`.gluecharm/context/services-list.json\`: responsibilities, consumers, errors, integration points.`,
20911
- exists: fs37.existsSync(path32.join(contextDir2, basename17)) && fs37.statSync(path32.join(contextDir2, basename17)).size > 0
21738
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
20912
21739
  };
20913
21740
  }
20914
21741
  function methodDetailTarget(svc, m, contextDir2) {
20915
- const basename17 = `${svc.code}_${m.code}-${m.slug}.md`;
21742
+ const basename18 = `${svc.code}_${m.code}-${m.slug}.md`;
20916
21743
  return {
20917
21744
  openCodeAgentStem: "agent-md-method-detail",
20918
21745
  agentId: "ctx-md-method-detail",
20919
21746
  displayName: "Method detail",
20920
- outputBasename: basename17,
21747
+ outputBasename: basename18,
20921
21748
  taskDescription: `Document method **${m.code}** (**${m.name}**) on service **${svc.code}** per \`.gluecharm/context/services-list.json\`.`,
20922
- exists: fs37.existsSync(path32.join(contextDir2, basename17)) && fs37.statSync(path32.join(contextDir2, basename17)).size > 0
21749
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
20923
21750
  };
20924
21751
  }
20925
21752
  function entityDetailTarget(dmCode, ename, slug, contextDir2) {
20926
- const basename17 = `${dmCode}-${slug}.md`;
21753
+ const basename18 = `${dmCode}-${slug}.md`;
20927
21754
  return {
20928
21755
  openCodeAgentStem: "agent-md-entity-detail",
20929
21756
  agentId: "ctx-md-entity-detail",
20930
21757
  displayName: "Entity detail",
20931
- outputBasename: basename17,
21758
+ outputBasename: basename18,
20932
21759
  taskDescription: `Describe entity **${dmCode}** (**${ename}**, slug **${slug}**) per \`.gluecharm/context/data-model-list.json\`: lifecycle, invariants, storage, ORM/schema mapping.`,
20933
- exists: fs37.existsSync(path32.join(contextDir2, basename17)) && fs37.statSync(path32.join(contextDir2, basename17)).size > 0
21760
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
20934
21761
  };
20935
21762
  }
20936
21763
  function fieldDetailTarget(dmCode, fdCode, fname, fSlug, contextDir2) {
20937
- const basename17 = `${dmCode}_${fdCode}-${fSlug}.md`;
21764
+ const basename18 = `${dmCode}_${fdCode}-${fSlug}.md`;
20938
21765
  return {
20939
21766
  openCodeAgentStem: "agent-md-field-detail",
20940
21767
  agentId: "ctx-md-field-detail",
20941
21768
  displayName: "Field detail",
20942
- outputBasename: basename17,
21769
+ outputBasename: basename18,
20943
21770
  taskDescription: `Document field **${fdCode}** (**${fname}**) on entity **${dmCode}** per \`.gluecharm/context/${dmCode}-fields-list.json\`. Cite types and constraints with file and line.`,
20944
- exists: fs37.existsSync(path32.join(contextDir2, basename17)) && fs37.statSync(path32.join(contextDir2, basename17)).size > 0
21771
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
20945
21772
  };
20946
21773
  }
20947
21774
  function toolDetailTarget(row2, contextDir2) {
20948
- const basename17 = `${row2.code}-${row2.slug}.md`;
21775
+ const basename18 = `${row2.code}-${row2.slug}.md`;
20949
21776
  return {
20950
21777
  openCodeAgentStem: "agent-md-tool-detail",
20951
21778
  agentId: "ctx-md-tool-detail",
20952
21779
  displayName: "Tool detail",
20953
- outputBasename: basename17,
21780
+ outputBasename: basename18,
20954
21781
  taskDescription: `Describe tool **${row2.code}** (**${row2.name}**, slug **${row2.slug}**) per \`.gluecharm/context/tech-stack-list.json\`: role, version hints, configuration, boundaries.`,
20955
- exists: fs37.existsSync(path32.join(contextDir2, basename17)) && fs37.statSync(path32.join(contextDir2, basename17)).size > 0
21782
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
21783
+ };
21784
+ }
21785
+ function infrastructureDetailTarget(contextDir2, code, name, slug) {
21786
+ const basename18 = `${code}-${slug}.md`;
21787
+ return {
21788
+ openCodeAgentStem: "agent-md-infrastructure-detail",
21789
+ agentId: "ctx-md-infrastructure-detail",
21790
+ displayName: "Infrastructure detail",
21791
+ outputBasename: basename18,
21792
+ taskDescription: `Document infrastructure surface **${code}** (**${name}**, slug **${slug}**) per \`.gluecharm/context/infrastructure-list.json\`: delivery/DX role, automation, dependencies, and evidence.`,
21793
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
21794
+ };
21795
+ }
21796
+ function infrastructureComponentDetailTarget(inCode, ic, contextDir2) {
21797
+ const basename18 = `${inCode}_${ic.code}-${ic.slug}.md`;
21798
+ return {
21799
+ openCodeAgentStem: "agent-md-infrastructure-component-detail",
21800
+ agentId: "ctx-md-infrastructure-component-detail",
21801
+ displayName: "Infrastructure component detail",
21802
+ outputBasename: basename18,
21803
+ taskDescription: `Document component **${ic.code}** (**${ic.name}**) under infrastructure **${inCode}** per \`.gluecharm/context/${inCode}-components-list.json\`.`,
21804
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
21805
+ };
21806
+ }
21807
+ function qaDetailTarget(contextDir2, code, name, slug) {
21808
+ const basename18 = `${code}-${slug}.md`;
21809
+ return {
21810
+ openCodeAgentStem: "agent-md-qa-detail",
21811
+ agentId: "ctx-md-qa-detail",
21812
+ displayName: "QA detail",
21813
+ outputBasename: basename18,
21814
+ taskDescription: `Document QA surface **${code}** (**${name}**, slug **${slug}**) per \`.gluecharm/context/qa-list.json\`: verification scope, harness, and **coversCodes** anchors.`,
21815
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
21816
+ };
21817
+ }
21818
+ function qaTestCaseDetailTarget(qaCode, tc, contextDir2) {
21819
+ const basename18 = `${qaCode}_${tc.code}-${tc.slug}.md`;
21820
+ return {
21821
+ openCodeAgentStem: "agent-md-qa-test-case-detail",
21822
+ agentId: "ctx-md-qa-test-case-detail",
21823
+ displayName: "QA test case detail",
21824
+ outputBasename: basename18,
21825
+ taskDescription: `Document test case **${tc.code}** (**${tc.name}**) under QA **${qaCode}** per \`.gluecharm/context/${qaCode}-test-cases-list.json\`; include **coversCodes** anchors.`,
21826
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
20956
21827
  };
20957
21828
  }
20958
21829
  function useCaseDetailTarget(feCode, ucCode, ucName, ucSlug, contextDir2) {
@@ -20965,18 +21836,18 @@ function useCaseDetailTarget(feCode, ucCode, ucName, ucSlug, contextDir2) {
20965
21836
  taskDescription: `Document use case **${ucCode}** (**${ucName}**) under feature **${feCode}** per \`.gluecharm/context/${feCode}-use-cases-list.json\`. Do not change stable codes.
20966
21837
 
20967
21838
  Follow bundled agent **agent-md-use-case-detail**: include **## Data inputs and validation** (concrete fields, mandatory vs optional, rules, failure behaviour). **## Code flow** must trace **implementation order** (entrypoint \u2192 validation \u2192 business rules \u2192 persistence \u2192 response) with **real** symbols and files\u2014not generic \u201Cuser does X\u201D. Add a **Mermaid** fenced diagram under Code flow when there are **two or more** implementation stages. **## Evidence index** must cite **each major stage** (validation, persistence, response), not only the outer entrypoint.`,
20968
- exists: fs37.existsSync(path32.join(contextDir2, ucBase)) && fs37.statSync(path32.join(contextDir2, ucBase)).size > 0
21839
+ exists: fs37.existsSync(path33.join(contextDir2, ucBase)) && fs37.statSync(path33.join(contextDir2, ucBase)).size > 0
20969
21840
  };
20970
21841
  }
20971
21842
  function scenarioDetailTarget(feCode, ucCode, scCode, scName, contextDir2) {
20972
- const basename17 = `${feCode}_${ucCode}_${scCode}.md`;
21843
+ const basename18 = `${feCode}_${ucCode}_${scCode}.md`;
20973
21844
  return {
20974
21845
  openCodeAgentStem: "agent-md-scenario-detail",
20975
21846
  agentId: "ctx-md-scenario-detail",
20976
21847
  displayName: "Scenario detail",
20977
- outputBasename: basename17,
21848
+ outputBasename: basename18,
20978
21849
  taskDescription: `Document scenario **${scCode}** (**${scName}**) for **${feCode}** / **${ucCode}** per \`.gluecharm/context/${feCode}_${ucCode}-scenarios-list.json\`. Cite steps with file and line where possible.`,
20979
- exists: fs37.existsSync(path32.join(contextDir2, basename17)) && fs37.statSync(path32.join(contextDir2, basename17)).size > 0
21850
+ exists: fs37.existsSync(path33.join(contextDir2, basename18)) && fs37.statSync(path33.join(contextDir2, basename18)).size > 0
20980
21851
  };
20981
21852
  }
20982
21853
  function parentsDone(item, byId) {
@@ -21029,7 +21900,7 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
21029
21900
  case "listFeatures":
21030
21901
  break;
21031
21902
  case "reviewFeaturesList": {
21032
- const flPath = path32.join(contextDir2, "features-list.json");
21903
+ const flPath = path33.join(contextDir2, "features-list.json");
21033
21904
  const fl = readJson4(flPath);
21034
21905
  const features = Array.isArray(fl?.features) ? fl.features : [];
21035
21906
  for (const row2 of features) {
@@ -21068,7 +21939,7 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
21068
21939
  case "listDataModel":
21069
21940
  break;
21070
21941
  case "reviewDataModelList": {
21071
- const dmPath = path32.join(contextDir2, "data-model-list.json");
21942
+ const dmPath = path33.join(contextDir2, "data-model-list.json");
21072
21943
  const dmFile = readJson4(dmPath);
21073
21944
  const ents = Array.isArray(dmFile?.entities) ? dmFile.entities : [];
21074
21945
  for (const erow of ents) {
@@ -21095,6 +21966,100 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
21095
21966
  }
21096
21967
  break;
21097
21968
  }
21969
+ case "listInfrastructure":
21970
+ break;
21971
+ case "reviewInfrastructureList": {
21972
+ for (const row2 of discoverInfrastructureRows(contextDir2)) {
21973
+ const mdId = `md:${row2.code}-${row2.slug}.md`;
21974
+ add(mdItem(mdId, [completed.id], infrastructureDetailTarget(contextDir2, row2.code, row2.name, row2.slug)));
21975
+ add(
21976
+ coordItem(`coord:ic:${row2.code}`, [completed.id], "listInfrastructureComponents", {
21977
+ infrastructureCode: row2.code
21978
+ })
21979
+ );
21980
+ }
21981
+ break;
21982
+ }
21983
+ case "listInfrastructureComponents": {
21984
+ const inCode = listTarget?.infrastructureCode;
21985
+ if (!inCode || !IN.test(inCode)) {
21986
+ break;
21987
+ }
21988
+ add(
21989
+ coordItem(`coord:reviewIc:${inCode}`, [completed.id], "reviewInfrastructureComponentsList", {
21990
+ infrastructureCode: inCode
21991
+ })
21992
+ );
21993
+ break;
21994
+ }
21995
+ case "reviewInfrastructureComponentsList": {
21996
+ const inCode = listTarget?.infrastructureCode;
21997
+ if (!inCode || !IN.test(inCode)) {
21998
+ break;
21999
+ }
22000
+ const clPath = path33.join(contextDir2, `${inCode}-components-list.json`);
22001
+ const clFile = readJson4(clPath);
22002
+ const comps = Array.isArray(clFile?.components) ? clFile.components : [];
22003
+ for (const crow of comps) {
22004
+ const icCode = safeStr3(crow.code);
22005
+ if (!IC.test(icCode)) {
22006
+ continue;
22007
+ }
22008
+ const slug = safeStr3(crow.slug);
22009
+ if (!slug || !SLUG3.test(slug)) {
22010
+ continue;
22011
+ }
22012
+ const cname = safeStr3(crow.name) || icCode;
22013
+ const mdId = `md:${inCode}_${icCode}-${slug}.md`;
22014
+ add(mdItem(mdId, [completed.id], infrastructureComponentDetailTarget(inCode, { code: icCode, name: cname, slug }, contextDir2)));
22015
+ }
22016
+ break;
22017
+ }
22018
+ case "listQa":
22019
+ break;
22020
+ case "reviewQaList": {
22021
+ for (const row2 of discoverQaRows(contextDir2)) {
22022
+ const mdId = `md:${row2.code}-${row2.slug}.md`;
22023
+ add(mdItem(mdId, [completed.id], qaDetailTarget(contextDir2, row2.code, row2.name, row2.slug)));
22024
+ add(coordItem(`coord:tc:${row2.code}`, [completed.id], "listQaTestCases", { qaCode: row2.code }));
22025
+ }
22026
+ break;
22027
+ }
22028
+ case "listQaTestCases": {
22029
+ const qaCode = listTarget?.qaCode;
22030
+ if (!qaCode || !QA.test(qaCode)) {
22031
+ break;
22032
+ }
22033
+ add(
22034
+ coordItem(`coord:reviewTc:${qaCode}`, [completed.id], "reviewQaTestCasesList", {
22035
+ qaCode
22036
+ })
22037
+ );
22038
+ break;
22039
+ }
22040
+ case "reviewQaTestCasesList": {
22041
+ const qaCode = listTarget?.qaCode;
22042
+ if (!qaCode || !QA.test(qaCode)) {
22043
+ break;
22044
+ }
22045
+ const tcPath = path33.join(contextDir2, `${qaCode}-test-cases-list.json`);
22046
+ const tcFile = readJson4(tcPath);
22047
+ const tcs = Array.isArray(tcFile?.testCases) ? tcFile.testCases : [];
22048
+ for (const trow of tcs) {
22049
+ const tcCode = safeStr3(trow.code);
22050
+ if (!TC.test(tcCode)) {
22051
+ continue;
22052
+ }
22053
+ const slug = safeStr3(trow.slug);
22054
+ if (!slug || !SLUG3.test(slug)) {
22055
+ continue;
22056
+ }
22057
+ const tname = safeStr3(trow.name) || tcCode;
22058
+ const mdId = `md:${qaCode}_${tcCode}-${slug}.md`;
22059
+ add(mdItem(mdId, [completed.id], qaTestCaseDetailTarget(qaCode, { code: tcCode, name: tname, slug }, contextDir2)));
22060
+ }
22061
+ break;
22062
+ }
21098
22063
  case "listUseCases": {
21099
22064
  const fe = listTarget?.featureCode;
21100
22065
  if (!fe || !FE.test(fe)) {
@@ -21108,7 +22073,7 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
21108
22073
  if (!fe || !FE.test(fe)) {
21109
22074
  break;
21110
22075
  }
21111
- const ucListPath = path32.join(contextDir2, `${fe}-use-cases-list.json`);
22076
+ const ucListPath = path33.join(contextDir2, `${fe}-use-cases-list.json`);
21112
22077
  const ucFile = readJson4(ucListPath);
21113
22078
  const ucs = Array.isArray(ucFile?.useCases) ? ucFile.useCases : [];
21114
22079
  for (const ucRow of ucs) {
@@ -21151,7 +22116,7 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
21151
22116
  if (!fe || !uc || !FE.test(fe) || !UC.test(uc)) {
21152
22117
  break;
21153
22118
  }
21154
- const scPath = path32.join(contextDir2, `${fe}_${uc}-scenarios-list.json`);
22119
+ const scPath = path33.join(contextDir2, `${fe}_${uc}-scenarios-list.json`);
21155
22120
  const scFile = readJson4(scPath);
21156
22121
  const scs = Array.isArray(scFile?.scenarios) ? scFile.scenarios : [];
21157
22122
  for (const scRow of scs) {
@@ -21178,7 +22143,7 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
21178
22143
  if (!dm || !DM.test(dm)) {
21179
22144
  break;
21180
22145
  }
21181
- const flPath = path32.join(contextDir2, `${dm}-fields-list.json`);
22146
+ const flPath = path33.join(contextDir2, `${dm}-fields-list.json`);
21182
22147
  const flFile = readJson4(flPath);
21183
22148
  const flds = Array.isArray(flFile?.fields) ? flFile.fields : [];
21184
22149
  for (const frow of flds) {
@@ -21202,15 +22167,15 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
21202
22167
  return;
21203
22168
  }
21204
22169
  if (completed.kind === "markdown") {
21205
- const basename17 = completed.payload.outputBasename;
22170
+ const basename18 = completed.payload.outputBasename;
21206
22171
  const mdId = completed.id;
21207
- const feFromFeatureMd = basename17.match(/^(FE-\d+)-[^/]+\.md$/);
22172
+ const feFromFeatureMd = basename18.match(/^(FE-\d+)-[^/]+\.md$/);
21208
22173
  if (feFromFeatureMd) {
21209
22174
  const fe = feFromFeatureMd[1];
21210
22175
  add(coordItem(`coord:uc:${fe}`, [mdId], "listUseCases", { featureCode: fe }));
21211
22176
  return;
21212
22177
  }
21213
- const xpFromView = basename17.match(/^(XP-\d+)-[^/]+\.md$/);
22178
+ const xpFromView = basename18.match(/^(XP-\d+)-[^/]+\.md$/);
21214
22179
  if (xpFromView) {
21215
22180
  const xp = xpFromView[1];
21216
22181
  const row2 = discoverExperienceTreeRows(contextDir2).find((r) => r.code === xp);
@@ -21222,7 +22187,7 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
21222
22187
  }
21223
22188
  return;
21224
22189
  }
21225
- const svFromSvc = basename17.match(/^(SV-\d+)-[^/]+\.md$/);
22190
+ const svFromSvc = basename18.match(/^(SV-\d+)-[^/]+\.md$/);
21226
22191
  if (svFromSvc) {
21227
22192
  const sv = svFromSvc[1];
21228
22193
  const srow = discoverServiceTreeRows(contextDir2).find((r) => r.code === sv);
@@ -21234,7 +22199,7 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
21234
22199
  }
21235
22200
  return;
21236
22201
  }
21237
- const dmFromEnt = basename17.match(/^(DM-\d+)-[^/]+\.md$/);
22202
+ const dmFromEnt = basename18.match(/^(DM-\d+)-[^/]+\.md$/);
21238
22203
  if (dmFromEnt) {
21239
22204
  const dm = dmFromEnt[1];
21240
22205
  add(coordItem(`coord:ef:${dm}`, [mdId], "listEntityFields", { entityCode: dm }));
@@ -21278,7 +22243,7 @@ async function runWorkstationAgent(item, worktreeRoot, workspaceLabel, oc, log,
21278
22243
  };
21279
22244
  if (item.kind === "coord") {
21280
22245
  const { step, listTarget } = item.payload;
21281
- const contextDir2 = path32.join(worktreeRoot, ".gluecharm", "context");
22246
+ const contextDir2 = path33.join(worktreeRoot, ".gluecharm", "context");
21282
22247
  if (step === "listExperiences") {
21283
22248
  const assessment = readRepoSurfaceAssessment(contextDir2);
21284
22249
  if (assessment === null) {
@@ -21421,8 +22386,8 @@ async function drainWorkstationPool(p) {
21421
22386
  const artefactRunId = readArtefactRunSnapshot(storageContext)?.runId ?? "unknown-run";
21422
22387
  let active = 0;
21423
22388
  let wake;
21424
- const waitTurn = () => new Promise((resolve24) => {
21425
- wake = resolve24;
22389
+ const waitTurn = () => new Promise((resolve25) => {
22390
+ wake = resolve25;
21426
22391
  });
21427
22392
  const persist = async () => {
21428
22393
  const items = {};
@@ -21592,7 +22557,7 @@ async function prepareSynthesisWorktree(storageContext, repoRoot, log, options)
21592
22557
  await clearArtefactRun(storageContext);
21593
22558
  }
21594
22559
  if (options?.inPlace === true) {
21595
- const repoResolved = path32.resolve(repoRoot);
22560
+ const repoResolved = path33.resolve(repoRoot);
21596
22561
  log(`[worktree] in-place analysis \u2014 using repository root ${repoResolved}`);
21597
22562
  const handle2 = createInPlaceWorktreeHandle(repoResolved);
21598
22563
  const sourceBranchAtCreation = readHeadBranchShort(repoResolved);
@@ -21639,7 +22604,7 @@ async function runSynthesisPipelineDrainFromPreparedWorktree(storageContext, rep
21639
22604
  aceEnabled: getAceAnalysisEnabledForCheckout(handle.path),
21640
22605
  offlineLearnAfterSameSessionTrace: getAceOfflineLearnAfterSameSessionTrace(handle.path)
21641
22606
  };
21642
- const contextDir2 = path32.join(handle.path, ".gluecharm", "context");
22607
+ const contextDir2 = path33.join(handle.path, ".gluecharm", "context");
21643
22608
  fs37.mkdirSync(contextDir2, { recursive: true });
21644
22609
  const initialItems = buildRootItems();
21645
22610
  await startArtefactRun(storageContext, repoRoot, handle.path, initialItems);
@@ -21742,10 +22707,10 @@ async function runSynthesisPipeline(storageContext, repoRoot, workspaceLabel, ex
21742
22707
 
21743
22708
  // src/workspaceContextPromote.ts
21744
22709
  var fs38 = __toESM(require("fs"));
21745
- var path33 = __toESM(require("path"));
22710
+ var path34 = __toESM(require("path"));
21746
22711
  function promoteContextDirectoryToWorkspaceFs(sourceContextDir, workspaceRootFs) {
21747
- const dest = path33.join(workspaceRootFs, ".gluecharm", "context");
21748
- fs38.mkdirSync(path33.join(workspaceRootFs, ".gluecharm"), { recursive: true });
22712
+ const dest = path34.join(workspaceRootFs, ".gluecharm", "context");
22713
+ fs38.mkdirSync(path34.join(workspaceRootFs, ".gluecharm"), { recursive: true });
21749
22714
  fs38.mkdirSync(dest, { recursive: true });
21750
22715
  let names;
21751
22716
  try {
@@ -21755,12 +22720,12 @@ function promoteContextDirectoryToWorkspaceFs(sourceContextDir, workspaceRootFs)
21755
22720
  }
21756
22721
  let filesCopied = 0;
21757
22722
  for (const name of names) {
21758
- const srcPath = path33.join(sourceContextDir, name);
22723
+ const srcPath = path34.join(sourceContextDir, name);
21759
22724
  if (!fs38.statSync(srcPath).isFile()) {
21760
22725
  continue;
21761
22726
  }
21762
22727
  const buf = fs38.readFileSync(srcPath);
21763
- const target = path33.join(dest, name);
22728
+ const target = path34.join(dest, name);
21764
22729
  fs38.writeFileSync(target, buf);
21765
22730
  filesCopied += 1;
21766
22731
  }
@@ -21783,23 +22748,23 @@ var FACTORY_PIPELINE_EXIT_CONDITIONS = {
21783
22748
 
21784
22749
  // src/readiness/factoryReadiness.ts
21785
22750
  var fs39 = __toESM(require("node:fs"));
21786
- var path35 = __toESM(require("node:path"));
22751
+ var path36 = __toESM(require("node:path"));
21787
22752
 
21788
22753
  // src/readiness/openCodeConfigCandidates.ts
21789
22754
  var os6 = __toESM(require("node:os"));
21790
- var path34 = __toESM(require("node:path"));
22755
+ var path35 = __toESM(require("node:path"));
21791
22756
  function openCodeUserConfigDir() {
21792
22757
  if (process.platform === "win32") {
21793
- const base = process.env.APPDATA || path34.join(os6.homedir(), "AppData", "Roaming");
21794
- return path34.join(base, "opencode");
22758
+ const base = process.env.APPDATA || path35.join(os6.homedir(), "AppData", "Roaming");
22759
+ return path35.join(base, "opencode");
21795
22760
  }
21796
22761
  const xdg = process.env.XDG_CONFIG_HOME?.trim();
21797
- const cfgRoot = xdg && xdg.length > 0 ? xdg : path34.join(os6.homedir(), ".config");
21798
- return path34.join(cfgRoot, "opencode");
22762
+ const cfgRoot = xdg && xdg.length > 0 ? xdg : path35.join(os6.homedir(), ".config");
22763
+ return path35.join(cfgRoot, "opencode");
21799
22764
  }
21800
22765
  function openCodeMacUserApplicationSupportDir() {
21801
22766
  if (process.platform === "darwin") {
21802
- return path34.join(os6.homedir(), "Library", "Application Support", "opencode");
22767
+ return path35.join(os6.homedir(), "Library", "Application Support", "opencode");
21803
22768
  }
21804
22769
  return void 0;
21805
22770
  }
@@ -21807,7 +22772,7 @@ function openCodeWindowsLocalAppDataDir() {
21807
22772
  if (process.platform === "win32") {
21808
22773
  const la = process.env.LOCALAPPDATA?.trim();
21809
22774
  if (la && la.length > 0) {
21810
- return path34.join(la, "opencode");
22775
+ return path35.join(la, "opencode");
21811
22776
  }
21812
22777
  }
21813
22778
  return void 0;
@@ -21817,25 +22782,25 @@ function managedOpenCodeConfigDir() {
21817
22782
  return "/Library/Application Support/opencode";
21818
22783
  }
21819
22784
  if (process.platform === "linux") {
21820
- return path34.join("/etc", "opencode");
22785
+ return path35.join("/etc", "opencode");
21821
22786
  }
21822
22787
  if (process.platform === "win32") {
21823
22788
  const pd = process.env.ProgramData?.trim() || "C:\\ProgramData";
21824
- return path34.join(pd, "opencode");
22789
+ return path35.join(pd, "opencode");
21825
22790
  }
21826
22791
  return void 0;
21827
22792
  }
21828
22793
  function pushConfigBasenames(out, dir) {
21829
- const d = path34.normalize(dir);
21830
- out.push(path34.join(d, "opencode.json"));
21831
- out.push(path34.join(d, "opencode.jsonc"));
21832
- out.push(path34.join(d, "config.json"));
22794
+ const d = path35.normalize(dir);
22795
+ out.push(path35.join(d, "opencode.json"));
22796
+ out.push(path35.join(d, "opencode.jsonc"));
22797
+ out.push(path35.join(d, "config.json"));
21833
22798
  }
21834
22799
  function dedupeStablePaths(paths) {
21835
22800
  const seen = /* @__PURE__ */ new Set();
21836
22801
  const out = [];
21837
22802
  for (const p of paths) {
21838
- const n = path34.normalize(p.trim());
22803
+ const n = path35.normalize(p.trim());
21839
22804
  if (n.length === 0 || seen.has(n)) {
21840
22805
  continue;
21841
22806
  }
@@ -21845,11 +22810,11 @@ function dedupeStablePaths(paths) {
21845
22810
  return out;
21846
22811
  }
21847
22812
  function openCodeJsonModelFileCandidates(analysisRootAbs) {
21848
- const root = path34.resolve(analysisRootAbs);
22813
+ const root = path35.resolve(analysisRootAbs);
21849
22814
  const acc = [];
21850
22815
  const oc = process.env.OPENCODE_CONFIG?.trim();
21851
22816
  if (oc) {
21852
- acc.push(path34.resolve(oc));
22817
+ acc.push(path35.resolve(oc));
21853
22818
  }
21854
22819
  pushConfigBasenames(acc, openCodeUserConfigDir());
21855
22820
  const macAs = openCodeMacUserApplicationSupportDir();
@@ -21860,13 +22825,13 @@ function openCodeJsonModelFileCandidates(analysisRootAbs) {
21860
22825
  if (winLoc) {
21861
22826
  pushConfigBasenames(acc, winLoc);
21862
22827
  }
21863
- pushConfigBasenames(acc, path34.join(os6.homedir(), ".opencode"));
22828
+ pushConfigBasenames(acc, path35.join(os6.homedir(), ".opencode"));
21864
22829
  const ocd = process.env.OPENCODE_CONFIG_DIR?.trim();
21865
22830
  if (ocd) {
21866
- pushConfigBasenames(acc, path34.resolve(ocd));
22831
+ pushConfigBasenames(acc, path35.resolve(ocd));
21867
22832
  }
21868
22833
  pushConfigBasenames(acc, root);
21869
- pushConfigBasenames(acc, path34.join(root, ".opencode"));
22834
+ pushConfigBasenames(acc, path35.join(root, ".opencode"));
21870
22835
  const managed = managedOpenCodeConfigDir();
21871
22836
  if (managed) {
21872
22837
  pushConfigBasenames(acc, managed);
@@ -22031,7 +22996,7 @@ function buildReadinessProbeFacts(ctx) {
22031
22996
  });
22032
22997
  const openCodeConfigFiles = buildOpenCodeConfigInventory(ctx.analysisRootAbs);
22033
22998
  const mandatoryOk = MANDATORY_OPEN_CODE_CONFIG_REL_PATHS.every((rel) => {
22034
- const abs = path35.join(path35.resolve(ctx.analysisRootAbs), rel);
22999
+ const abs = path36.join(path36.resolve(ctx.analysisRootAbs), rel);
22035
23000
  return fs39.existsSync(abs);
22036
23001
  });
22037
23002
  const model = resolveModelFromContext(ctx);
@@ -22347,7 +23312,7 @@ function sleepUntilAborted(ms, signal) {
22347
23312
  if (signal.aborted) {
22348
23313
  return Promise.reject(new DOMException("The operation was aborted.", "AbortError"));
22349
23314
  }
22350
- return new Promise((resolve24, reject) => {
23315
+ return new Promise((resolve25, reject) => {
22351
23316
  const onAbort = () => {
22352
23317
  clearTimeout(t);
22353
23318
  signal.removeEventListener("abort", onAbort);
@@ -22355,7 +23320,7 @@ function sleepUntilAborted(ms, signal) {
22355
23320
  };
22356
23321
  const t = setTimeout(() => {
22357
23322
  signal.removeEventListener("abort", onAbort);
22358
- resolve24();
23323
+ resolve25();
22359
23324
  }, ms);
22360
23325
  signal.addEventListener("abort", onAbort, { once: true });
22361
23326
  });
@@ -22902,7 +23867,7 @@ async function runGenerateContextFactory(deps) {
22902
23867
 
22903
23868
  // src/factory/generateContextFactoryHeadlessHost.ts
22904
23869
  var fs48 = __toESM(require("node:fs"));
22905
- var path45 = __toESM(require("node:path"));
23870
+ var path46 = __toESM(require("node:path"));
22906
23871
 
22907
23872
  // src/stores/pipelineRunStore.ts
22908
23873
  var STORAGE_KEY2 = SRS53_PIPELINE_RUN_KEY_V2;
@@ -23003,11 +23968,11 @@ async function noteAgentsMaterialized(context) {
23003
23968
 
23004
23969
  // src/pipelines/remediation/missingWorkstations.ts
23005
23970
  var fs41 = __toESM(require("fs"));
23006
- var path37 = __toESM(require("path"));
23971
+ var path38 = __toESM(require("path"));
23007
23972
 
23008
23973
  // src/analysis/analysisDetailMarkdownDiscovery.ts
23009
23974
  var fs40 = __toESM(require("fs"));
23010
- var path36 = __toESM(require("path"));
23975
+ var path37 = __toESM(require("path"));
23011
23976
  var SLUG4 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
23012
23977
  var FE2 = /^FE-\d+$/;
23013
23978
  var UC2 = /^UC-\d+$/;
@@ -23024,7 +23989,17 @@ var STEM_TO_AGENT = {
23024
23989
  "agent-md-method-detail": { agentId: "ctx-md-method-detail", displayName: "Method detail" },
23025
23990
  "agent-md-entity-detail": { agentId: "ctx-md-entity-detail", displayName: "Entity detail" },
23026
23991
  "agent-md-field-detail": { agentId: "ctx-md-field-detail", displayName: "Field detail" },
23027
- "agent-md-tool-detail": { agentId: "ctx-md-tool-detail", displayName: "Tool detail" }
23992
+ "agent-md-tool-detail": { agentId: "ctx-md-tool-detail", displayName: "Tool detail" },
23993
+ "agent-md-infrastructure-detail": {
23994
+ agentId: "ctx-md-infrastructure-detail",
23995
+ displayName: "Infrastructure detail"
23996
+ },
23997
+ "agent-md-infrastructure-component-detail": {
23998
+ agentId: "ctx-md-infrastructure-component-detail",
23999
+ displayName: "Infrastructure component detail"
24000
+ },
24001
+ "agent-md-qa-detail": { agentId: "ctx-md-qa-detail", displayName: "QA detail" },
24002
+ "agent-md-qa-test-case-detail": { agentId: "ctx-md-qa-test-case-detail", displayName: "QA test case detail" }
23028
24003
  };
23029
24004
  function readJson5(filePath) {
23030
24005
  try {
@@ -23068,8 +24043,8 @@ function expectedFeatureDetailBasenameFromRow(row2) {
23068
24043
  }
23069
24044
  return `${code}-${slug}.md`;
23070
24045
  }
23071
- function ctxPath(contextDir2, basename17) {
23072
- return path36.join(contextDir2, basename17);
24046
+ function ctxPath(contextDir2, basename18) {
24047
+ return path37.join(contextDir2, basename18);
23073
24048
  }
23074
24049
  function pushTarget(targets, stem, outputBasename, taskDescription, contextDir2) {
23075
24050
  const meta = STEM_TO_AGENT[stem];
@@ -23085,7 +24060,7 @@ function pushTarget(targets, stem, outputBasename, taskDescription, contextDir2)
23085
24060
  function discoverDetailMarkdownGroups(contextDir2) {
23086
24061
  const groups = [];
23087
24062
  const featureTargets = [];
23088
- const flPath = path36.join(contextDir2, "features-list.json");
24063
+ const flPath = path37.join(contextDir2, "features-list.json");
23089
24064
  const fl = readJson5(flPath);
23090
24065
  const features = Array.isArray(fl?.features) ? fl.features : [];
23091
24066
  for (const row2 of features) {
@@ -23098,11 +24073,11 @@ function discoverDetailMarkdownGroups(contextDir2) {
23098
24073
  continue;
23099
24074
  }
23100
24075
  const name = safeStr4(row2.name) || code;
23101
- const basename17 = `${code}-${slug}.md`;
24076
+ const basename18 = `${code}-${slug}.md`;
23102
24077
  pushTarget(
23103
24078
  featureTargets,
23104
24079
  "agent-md-feature-detail",
23105
- basename17,
24080
+ basename18,
23106
24081
  `Document feature **${code}** (**${name}**, slug **${slug}**) per \`.gluecharm/context/features-list.json\`: scope, behaviour, main dependencies, entry points, and links to code under the worktree. Write exactly one markdown file at the output path; cite substantive claims with file and line (or range).`,
23107
24082
  contextDir2
23108
24083
  );
@@ -23115,7 +24090,7 @@ function discoverDetailMarkdownGroups(contextDir2) {
23115
24090
  if (!FE2.test(feCode)) {
23116
24091
  continue;
23117
24092
  }
23118
- const ucListPath = path36.join(contextDir2, `${feCode}-use-cases-list.json`);
24093
+ const ucListPath = path37.join(contextDir2, `${feCode}-use-cases-list.json`);
23119
24094
  const ucFile = readJson5(ucListPath);
23120
24095
  const ucs = Array.isArray(ucFile?.useCases) ? ucFile.useCases : [];
23121
24096
  for (const ucRow of ucs) {
@@ -23135,7 +24110,7 @@ function discoverDetailMarkdownGroups(contextDir2) {
23135
24110
  Follow bundled agent **agent-md-use-case-detail**: include **## Data inputs and validation** (concrete fields, mandatory vs optional, rules, failure behaviour). **## Code flow** must trace **implementation order** (entrypoint \u2192 validation \u2192 business rules \u2192 persistence \u2192 response) with **real** symbols and files\u2014not generic \u201Cuser does X\u201D. Add a **Mermaid** fenced diagram under Code flow when there are **two or more** implementation stages. **## Evidence index** must cite **each major stage** (validation, persistence, response), not only the outer entrypoint.`,
23136
24111
  contextDir2
23137
24112
  );
23138
- const scPath = path36.join(contextDir2, `${feCode}_${ucCode}-scenarios-list.json`);
24113
+ const scPath = path37.join(contextDir2, `${feCode}_${ucCode}-scenarios-list.json`);
23139
24114
  const scFile = readJson5(scPath);
23140
24115
  const scs = Array.isArray(scFile?.scenarios) ? scFile.scenarios : [];
23141
24116
  for (const scRow of scs) {
@@ -23206,7 +24181,7 @@ Follow bundled agent **agent-md-use-case-detail**: include **## Data inputs and
23206
24181
  groups.push({ id: "detail-services", label: "Detail markdown \u2014 Services", targets: svcTargets });
23207
24182
  groups.push({ id: "detail-methods", label: "Detail markdown \u2014 Methods", targets: meTargets });
23208
24183
  const entTargets = [];
23209
- const dmPath = path36.join(contextDir2, "data-model-list.json");
24184
+ const dmPath = path37.join(contextDir2, "data-model-list.json");
23210
24185
  const dmFile = readJson5(dmPath);
23211
24186
  const ents = Array.isArray(dmFile?.entities) ? dmFile.entities : [];
23212
24187
  for (const erow of ents) {
@@ -23235,7 +24210,7 @@ Follow bundled agent **agent-md-use-case-detail**: include **## Data inputs and
23235
24210
  if (!DM2.test(dmCode)) {
23236
24211
  continue;
23237
24212
  }
23238
- const flPath2 = path36.join(contextDir2, `${dmCode}-fields-list.json`);
24213
+ const flPath2 = path37.join(contextDir2, `${dmCode}-fields-list.json`);
23239
24214
  const flFile = readJson5(flPath2);
23240
24215
  const flds = Array.isArray(flFile?.fields) ? flFile.fields : [];
23241
24216
  for (const frow of flds) {
@@ -23271,6 +24246,58 @@ Follow bundled agent **agent-md-use-case-detail**: include **## Data inputs and
23271
24246
  );
23272
24247
  }
23273
24248
  groups.push({ id: "detail-tools", label: "Detail markdown \u2014 Tech stack", targets: toolTargets });
24249
+ const infraTargets = [];
24250
+ const icTargets = [];
24251
+ for (const row2 of discoverInfrastructureComponentTreeRows(contextDir2)) {
24252
+ const inBase = `${row2.code}-${row2.slug}.md`;
24253
+ pushTarget(
24254
+ infraTargets,
24255
+ "agent-md-infrastructure-detail",
24256
+ inBase,
24257
+ `Document infrastructure **${row2.code}** (**${row2.name}**, slug **${row2.slug}**) per \`.gluecharm/context/infrastructure-list.json\`.`,
24258
+ contextDir2
24259
+ );
24260
+ for (const comp of row2.components) {
24261
+ const icBase = `${row2.code}_${comp.code}-${comp.slug}.md`;
24262
+ pushTarget(
24263
+ icTargets,
24264
+ "agent-md-infrastructure-component-detail",
24265
+ icBase,
24266
+ `Document component **${comp.code}** (**${comp.name}**) under infrastructure **${row2.code}** per \`.gluecharm/context/${row2.code}-components-list.json\`.`,
24267
+ contextDir2
24268
+ );
24269
+ }
24270
+ }
24271
+ groups.push({ id: "detail-infrastructure", label: "Detail markdown \u2014 Infrastructure", targets: infraTargets });
24272
+ groups.push({
24273
+ id: "detail-infrastructure-components",
24274
+ label: "Detail markdown \u2014 Infrastructure components",
24275
+ targets: icTargets
24276
+ });
24277
+ const qaTargets = [];
24278
+ const tcTargets = [];
24279
+ for (const row2 of discoverQaTestCaseTreeRows(contextDir2)) {
24280
+ const qaBase = `${row2.code}-${row2.slug}.md`;
24281
+ pushTarget(
24282
+ qaTargets,
24283
+ "agent-md-qa-detail",
24284
+ qaBase,
24285
+ `Document QA area **${row2.code}** (**${row2.name}**, slug **${row2.slug}**) per \`.gluecharm/context/qa-list.json\`.`,
24286
+ contextDir2
24287
+ );
24288
+ for (const tc of row2.testCases) {
24289
+ const tcBase = `${row2.code}_${tc.code}-${tc.slug}.md`;
24290
+ pushTarget(
24291
+ tcTargets,
24292
+ "agent-md-qa-test-case-detail",
24293
+ tcBase,
24294
+ `Document test case **${tc.code}** (**${tc.name}**) under QA **${row2.code}**; include **coversCodes** anchors per \`.gluecharm/context/${row2.code}-test-cases-list.json\`.`,
24295
+ contextDir2
24296
+ );
24297
+ }
24298
+ }
24299
+ groups.push({ id: "detail-qa", label: "Detail markdown \u2014 QA", targets: qaTargets });
24300
+ groups.push({ id: "detail-qa-test-cases", label: "Detail markdown \u2014 QA test cases", targets: tcTargets });
23274
24301
  return groups;
23275
24302
  }
23276
24303
  function collectExpectedDetailMarkdownBasenames(contextDir2) {
@@ -23294,7 +24321,7 @@ function fileAndValidationFromKind(kind) {
23294
24321
  return { filePresentYesNo: "no", validationYesNo: "na" };
23295
24322
  }
23296
24323
  function classifyMissingWorkstationOutputFileStatus(row2, contextDir2, worktreeRoot) {
23297
- const abs = path37.join(contextDir2, row2.relativePath);
24324
+ const abs = path38.join(contextDir2, row2.relativePath);
23298
24325
  try {
23299
24326
  const st = fs41.statSync(abs);
23300
24327
  if (!st.isFile()) {
@@ -23333,7 +24360,7 @@ function classifyMissingWorkstationOutputFileStatus(row2, contextDir2, worktreeR
23333
24360
  detail: "Unknown coordination list basename \u2014 no JSON Schema mapping."
23334
24361
  };
23335
24362
  }
23336
- const schemaAbs = path37.join(worktreeRoot, ".opencode", "schemas", "context-lists", schemaBn);
24363
+ const schemaAbs = path38.join(worktreeRoot, ".opencode", "schemas", "context-lists", schemaBn);
23337
24364
  if (!fs41.existsSync(schemaAbs)) {
23338
24365
  return {
23339
24366
  kind: "invalid",
@@ -23363,7 +24390,7 @@ function classifyWorkstationOutputOnDisk(item, contextDir2, worktreeRoot) {
23363
24390
  return classifyMissingWorkstationOutputFileStatus(row2, contextDir2, worktreeRoot).kind;
23364
24391
  }
23365
24392
  function isWorkstationRunSnapshotForWorktree(snapshot, worktreeRoot) {
23366
- return !!(snapshot && snapshot.overallStatus !== "running" && snapshot.overallStatus !== "idle" && path37.resolve(snapshot.analysisRoot) === path37.resolve(worktreeRoot));
24393
+ return !!(snapshot && snapshot.overallStatus !== "running" && snapshot.overallStatus !== "idle" && path38.resolve(snapshot.analysisRoot) === path38.resolve(worktreeRoot));
23367
24394
  }
23368
24395
  function expectedBasenameForCoordPayload(p) {
23369
24396
  const { step, listTarget } = p;
@@ -23420,6 +24447,22 @@ function expectedBasenameForCoordPayload(p) {
23420
24447
  const dm = listTarget?.entityCode?.trim();
23421
24448
  return dm ? `${dm}-fields-list.json` : null;
23422
24449
  }
24450
+ case "listInfrastructure":
24451
+ case "reviewInfrastructureList":
24452
+ return "infrastructure-list.json";
24453
+ case "listQa":
24454
+ case "reviewQaList":
24455
+ return "qa-list.json";
24456
+ case "listInfrastructureComponents":
24457
+ case "reviewInfrastructureComponentsList": {
24458
+ const inCode = listTarget?.infrastructureCode?.trim();
24459
+ return inCode ? `${inCode}-components-list.json` : null;
24460
+ }
24461
+ case "listQaTestCases":
24462
+ case "reviewQaTestCasesList": {
24463
+ const qaCode = listTarget?.qaCode?.trim();
24464
+ return qaCode ? `${qaCode}-test-cases-list.json` : null;
24465
+ }
23423
24466
  default:
23424
24467
  return null;
23425
24468
  }
@@ -23432,15 +24475,17 @@ function labelContextForCoordPayload(p) {
23432
24475
  return {
23433
24476
  ...lt.featureCode ? { featureCode: lt.featureCode } : {},
23434
24477
  ...lt.useCaseCode ? { useCaseCode: lt.useCaseCode } : {},
23435
- ...lt.entityCode ? { entityCode: lt.entityCode } : {}
24478
+ ...lt.entityCode ? { entityCode: lt.entityCode } : {},
24479
+ ...lt.infrastructureCode ? { infrastructureCode: lt.infrastructureCode } : {},
24480
+ ...lt.qaCode ? { qaCode: lt.qaCode } : {}
23436
24481
  };
23437
24482
  }
23438
24483
  function contextPathForWorkItem(item, contextDir2) {
23439
24484
  if (item.kind === "markdown") {
23440
- return path37.join(contextDir2, item.payload.outputBasename);
24485
+ return path38.join(contextDir2, item.payload.outputBasename);
23441
24486
  }
23442
24487
  const bn = expectedBasenameForCoordPayload(item.payload);
23443
- return bn ? path37.join(contextDir2, bn) : null;
24488
+ return bn ? path38.join(contextDir2, bn) : null;
23444
24489
  }
23445
24490
  function syntheticRunnerId(key) {
23446
24491
  return `remediation:${key}`;
@@ -23450,7 +24495,7 @@ function rowFromSkippedItem(item, contextDir2) {
23450
24495
  return null;
23451
24496
  }
23452
24497
  const abs = contextPathForWorkItem(item, contextDir2);
23453
- const rel = abs ? path37.basename(abs) : item.kind === "markdown" ? item.payload.outputBasename : item.id;
24498
+ const rel = abs ? path38.basename(abs) : item.kind === "markdown" ? item.payload.outputBasename : item.id;
23454
24499
  if (abs && nonEmptyContextFile(abs)) {
23455
24500
  return null;
23456
24501
  }
@@ -23521,7 +24566,9 @@ function rowsFromDynamicPayload(contextDir2, p) {
23521
24566
  "experiences-list.json": "listExperiences",
23522
24567
  "services-list.json": "listServices",
23523
24568
  "data-model-list.json": "listDataModel",
23524
- "tech-stack-list.json": "listTechStack"
24569
+ "tech-stack-list.json": "listTechStack",
24570
+ "infrastructure-list.json": "listInfrastructure",
24571
+ "qa-list.json": "listQa"
23525
24572
  };
23526
24573
  const step = stepMap[bn];
23527
24574
  if (step) {
@@ -23557,6 +24604,24 @@ function rowsFromDynamicPayload(contextDir2, p) {
23557
24604
  );
23558
24605
  }
23559
24606
  }
24607
+ for (const row2 of discoverInfrastructureComponentTreeRows(contextDir2)) {
24608
+ const rel = `${row2.code}-components-list.json`;
24609
+ const exists = nonEmptyContextFile(path38.join(contextDir2, rel));
24610
+ if (!exists) {
24611
+ pushCoord("listInfrastructureComponents", rel, rel, synthesisStepLabel("listInfrastructureComponents", { infrastructureCode: row2.code }), {
24612
+ infrastructureCode: row2.code
24613
+ });
24614
+ }
24615
+ }
24616
+ for (const row2 of discoverQaTestCaseTreeRows(contextDir2)) {
24617
+ const rel = `${row2.code}-test-cases-list.json`;
24618
+ const exists = nonEmptyContextFile(path38.join(contextDir2, rel));
24619
+ if (!exists) {
24620
+ pushCoord("listQaTestCases", rel, rel, synthesisStepLabel("listQaTestCases", { qaCode: row2.code }), {
24621
+ qaCode: row2.code
24622
+ });
24623
+ }
24624
+ }
23560
24625
  return out;
23561
24626
  }
23562
24627
  function listMissingWorkstations(contextDir2, worktreeRoot, snapshot) {
@@ -23625,7 +24690,7 @@ function toMissingWorkstationUiRows(rows, contextDir2, worktreeRoot) {
23625
24690
  }
23626
24691
 
23627
24692
  // src/pipelines/remediation/missingWorkstationsPool.ts
23628
- var path38 = __toESM(require("path"));
24693
+ var path39 = __toESM(require("path"));
23629
24694
  function reconcileSkippedWorkItemsWithDisk(byId, contextDir2, worktreeRoot) {
23630
24695
  for (const item of byId.values()) {
23631
24696
  if (item.status !== "skipped") {
@@ -23685,7 +24750,7 @@ async function runRemediationPipelineMissingPass(p) {
23685
24750
  onItemComplete,
23686
24751
  sourceBranchAtWorktreeCreation
23687
24752
  } = p;
23688
- const contextDir2 = path38.join(worktreeRoot, ".gluecharm", "context");
24753
+ const contextDir2 = path39.join(worktreeRoot, ".gluecharm", "context");
23689
24754
  const snap = readArtefactRunSnapshot(storageContext);
23690
24755
  const snapOk = isWorkstationRunSnapshotForWorktree(snap, worktreeRoot);
23691
24756
  let byId;
@@ -23752,17 +24817,17 @@ async function runRemediationPipelineMissingPass(p) {
23752
24817
  // src/pipelines/coverage/coveragePipeline.ts
23753
24818
  var import_child_process5 = require("child_process");
23754
24819
  var fs43 = __toESM(require("fs"));
23755
- var path40 = __toESM(require("path"));
24820
+ var path41 = __toESM(require("path"));
23756
24821
 
23757
24822
  // src/analysis/coverageReferenceValidationSchemaValidate.ts
23758
24823
  var fs42 = __toESM(require("fs"));
23759
- var path39 = __toESM(require("path"));
24824
+ var path40 = __toESM(require("path"));
23760
24825
  var import__5 = __toESM(require__());
23761
24826
  function stripUtf8Bom4(s) {
23762
24827
  return s.length > 0 && s.charCodeAt(0) === 65279 ? s.slice(1) : s;
23763
24828
  }
23764
24829
  function bundledCoverageReferenceValidationSchemaPath() {
23765
- return path39.join(
24830
+ return path40.join(
23766
24831
  resolveRepoResourcesRoot(),
23767
24832
  "schemas",
23768
24833
  "context-lists",
@@ -23842,8 +24907,8 @@ var DEFAULT_IGNORE_DIR_BASENAMES = [
23842
24907
  ];
23843
24908
  var GIT_LS_FILES_MAX_BUFFER = 64 * 1024 * 1024;
23844
24909
  function tryLoadGitNonIgnoredPathSet(repositoryRootAbs) {
23845
- const root = path40.resolve(repositoryRootAbs);
23846
- if (!fs43.existsSync(path40.join(root, ".git"))) {
24910
+ const root = path41.resolve(repositoryRootAbs);
24911
+ if (!fs43.existsSync(path41.join(root, ".git"))) {
23847
24912
  return null;
23848
24913
  }
23849
24914
  const env = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
@@ -23911,12 +24976,12 @@ var REFERENCE_COVERAGE_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
23911
24976
  ".emf"
23912
24977
  ]);
23913
24978
  function isReferenceCoverageExcludedImagePath(relPosix) {
23914
- const ext = path40.extname(relPosix).toLowerCase();
24979
+ const ext = path41.extname(relPosix).toLowerCase();
23915
24980
  return ext.length > 0 && REFERENCE_COVERAGE_IMAGE_EXTENSIONS.has(ext);
23916
24981
  }
23917
24982
  var REFERENCE_COVERAGE_EXCLUDED_FILE_BASENAMES = /* @__PURE__ */ new Set([".gitignore"]);
23918
24983
  function isReferenceCoverageExcludedDefaultBasename(relPosix) {
23919
- const base = path40.basename(relPosix.replace(/\\/g, "/")).toLowerCase();
24984
+ const base = path41.basename(relPosix.replace(/\\/g, "/")).toLowerCase();
23920
24985
  return REFERENCE_COVERAGE_EXCLUDED_FILE_BASENAMES.has(base);
23921
24986
  }
23922
24987
  function decodeBufferForLineCount(buf) {
@@ -23953,12 +25018,12 @@ function normalizeRepoRelativePath(repoRoot, raw) {
23953
25018
  if (!trimmed || trimmed.includes("\0")) {
23954
25019
  return null;
23955
25020
  }
23956
- const abs = path40.resolve(repoRoot, trimmed);
23957
- const rel = path40.relative(repoRoot, abs);
23958
- if (rel.startsWith("..") || path40.isAbsolute(rel)) {
25021
+ const abs = path41.resolve(repoRoot, trimmed);
25022
+ const rel = path41.relative(repoRoot, abs);
25023
+ if (rel.startsWith("..") || path41.isAbsolute(rel)) {
23959
25024
  return null;
23960
25025
  }
23961
- return rel.split(path40.sep).join("/");
25026
+ return rel.split(path41.sep).join("/");
23962
25027
  }
23963
25028
  function collectJsonSourceReferences(node, out) {
23964
25029
  if (node === null || typeof node !== "object") {
@@ -24003,7 +25068,7 @@ function listContextFilesRecursive(dir, acc) {
24003
25068
  return;
24004
25069
  }
24005
25070
  for (const e of entries) {
24006
- const full = path40.join(dir, e.name);
25071
+ const full = path41.join(dir, e.name);
24007
25072
  if (e.isDirectory()) {
24008
25073
  listContextFilesRecursive(full, acc);
24009
25074
  } else if (e.isFile()) {
@@ -24024,8 +25089,8 @@ function walkRepositoryFiles(repoRoot, ignoreBasenames, maxBytes, excludedOut, g
24024
25089
  if (ignoreBasenames.has(e.name)) {
24025
25090
  continue;
24026
25091
  }
24027
- const full = path40.join(dir, e.name);
24028
- const rel = path40.relative(repoRoot, full).split(path40.sep).join("/");
25092
+ const full = path41.join(dir, e.name);
25093
+ const rel = path41.relative(repoRoot, full).split(path41.sep).join("/");
24029
25094
  if (rel.startsWith("..")) {
24030
25095
  continue;
24031
25096
  }
@@ -24083,9 +25148,9 @@ function collectReferencesFromContext(contextDirAbs, repositoryRootAbs, warnings
24083
25148
  const allFiles = [];
24084
25149
  listContextFilesRecursive(contextDirAbs, allFiles);
24085
25150
  for (const abs of allFiles) {
24086
- const ext = path40.extname(abs).toLowerCase();
24087
- const base = path40.basename(abs);
24088
- const sourceArtefact = path40.relative(contextDirAbs, abs).split(path40.sep).join("/");
25151
+ const ext = path41.extname(abs).toLowerCase();
25152
+ const base = path41.basename(abs);
25153
+ const sourceArtefact = path41.relative(contextDirAbs, abs).split(path41.sep).join("/");
24089
25154
  if (ext === ".json") {
24090
25155
  if (SKIP_CONTEXT_JSON.has(base)) {
24091
25156
  continue;
@@ -24167,12 +25232,12 @@ function sortReferenceRows(a, b) {
24167
25232
  return (a.startLine ?? 0) - (b.startLine ?? 0);
24168
25233
  }
24169
25234
  function buildCoverageReferenceValidationDocument(repositoryRootAbs, contextDirAbs, options) {
24170
- const repoRoot = path40.resolve(repositoryRootAbs);
25235
+ const repoRoot = path41.resolve(repositoryRootAbs);
24171
25236
  const warnings = [];
24172
25237
  const ignoreDirs = [...DEFAULT_IGNORE_DIR_BASENAMES];
24173
25238
  const ignoreSet = new Set(ignoreDirs);
24174
25239
  const maxBytes = options?.maxFileBytes ?? MAX_FILE_BYTES;
24175
- const references = collectReferencesFromContext(path40.resolve(contextDirAbs), repoRoot, warnings);
25240
+ const references = collectReferencesFromContext(path41.resolve(contextDirAbs), repoRoot, warnings);
24176
25241
  references.sort(sortReferenceRows);
24177
25242
  const referencedSet = /* @__PURE__ */ new Set();
24178
25243
  for (const r of references) {
@@ -24180,7 +25245,7 @@ function buildCoverageReferenceValidationDocument(repositoryRootAbs, contextDirA
24180
25245
  }
24181
25246
  const excludedFiles = [];
24182
25247
  const gitNonIgnoredPaths = tryLoadGitNonIgnoredPathSet(repoRoot);
24183
- if (fs43.existsSync(path40.join(repoRoot, ".git")) && gitNonIgnoredPaths === null) {
25248
+ if (fs43.existsSync(path41.join(repoRoot, ".git")) && gitNonIgnoredPaths === null) {
24184
25249
  warnings.push(
24185
25250
  "Repository has .git but git ls-files failed or git is unavailable; .gitignore / exclude-standard not applied."
24186
25251
  );
@@ -24229,8 +25294,8 @@ function buildCoverageReferenceValidationDocument(repositoryRootAbs, contextDirA
24229
25294
  return { document: doc, warnings };
24230
25295
  }
24231
25296
  function runCoveragePipeline(opts) {
24232
- const repoRoot = path40.resolve(opts.repositoryRootAbs);
24233
- const contextDir2 = path40.resolve(opts.contextDirAbs);
25297
+ const repoRoot = path41.resolve(opts.repositoryRootAbs);
25298
+ const contextDir2 = path41.resolve(opts.contextDirAbs);
24234
25299
  if (!fs43.existsSync(repoRoot)) {
24235
25300
  return { ok: false, error: `Repository root does not exist: ${repoRoot}` };
24236
25301
  }
@@ -24252,7 +25317,7 @@ ${schemaCheck.errors.join("\n")}`,
24252
25317
  warnings
24253
25318
  };
24254
25319
  }
24255
- const outPath = path40.join(contextDir2, COVERAGE_REFERENCE_VALIDATION_BASENAME);
25320
+ const outPath = path41.join(contextDir2, COVERAGE_REFERENCE_VALIDATION_BASENAME);
24256
25321
  if (opts.write) {
24257
25322
  try {
24258
25323
  const payload = stableStringifyCoverageDocument(document);
@@ -24271,11 +25336,11 @@ ${schemaCheck.errors.join("\n")}`,
24271
25336
  // src/pipelines/remediation/zeroReferenceWorkstationChain.ts
24272
25337
  var crypto = __toESM(require("crypto"));
24273
25338
  var fs45 = __toESM(require("fs"));
24274
- var path42 = __toESM(require("path"));
25339
+ var path43 = __toESM(require("path"));
24275
25340
 
24276
25341
  // src/analysis/zeroReferenceRemediationSchemaValidate.ts
24277
25342
  var fs44 = __toESM(require("fs"));
24278
- var path41 = __toESM(require("path"));
25343
+ var path42 = __toESM(require("path"));
24279
25344
  var import__6 = __toESM(require__());
24280
25345
  function stripUtf8Bom5(s) {
24281
25346
  return s.length > 0 && s.charCodeAt(0) === 65279 ? s.slice(1) : s;
@@ -24294,8 +25359,8 @@ function formatAjvErrors6(errors) {
24294
25359
  });
24295
25360
  }
24296
25361
  var ajv = new import__6.default({ allErrors: true, strict: false });
24297
- function compileSchema(basename17) {
24298
- const schemaPath = path41.join(schemasDir(), basename17);
25362
+ function compileSchema(basename18) {
25363
+ const schemaPath = path42.join(schemasDir(), basename18);
24299
25364
  const schemaRaw = stripUtf8Bom5(fs44.readFileSync(schemaPath, "utf-8"));
24300
25365
  const schema = JSON.parse(schemaRaw);
24301
25366
  return ajv.compile(schema);
@@ -24506,7 +25571,9 @@ var COORD_SCOPE_TO_TOP_ROUTING = {
24506
25571
  use_case: "feature",
24507
25572
  scenario: "feature",
24508
25573
  behavior: "experiences",
24509
- field_domain: "data_models"
25574
+ field_domain: "data_models",
25575
+ infrastructure: "infrastructure",
25576
+ qa: "qa"
24510
25577
  };
24511
25578
  function normalizeClassifierRouting(record) {
24512
25579
  const raw = record.routing;
@@ -24542,6 +25609,16 @@ function normalizeClassifierRouting(record) {
24542
25609
  delete record.coordinationScope;
24543
25610
  return;
24544
25611
  }
25612
+ if (r === "infrastructure") {
25613
+ record.routing = "infrastructure";
25614
+ delete record.coordinationScope;
25615
+ return;
25616
+ }
25617
+ if (r === "qa") {
25618
+ record.routing = "qa";
25619
+ delete record.coordinationScope;
25620
+ return;
25621
+ }
24545
25622
  record.routing = r;
24546
25623
  }
24547
25624
  function normalizeClassifierStagingRecord(record) {
@@ -24563,6 +25640,12 @@ function resolveListTriageKindFromStaging(staging) {
24563
25640
  if (routing === "data_models") {
24564
25641
  return "data_model";
24565
25642
  }
25643
+ if (routing === "infrastructure") {
25644
+ return "infrastructure";
25645
+ }
25646
+ if (routing === "qa") {
25647
+ return "qa";
25648
+ }
24566
25649
  if (routing === "coordination_follow_up") {
24567
25650
  const top = COORD_SCOPE_TO_TOP_ROUTING[scope] || "feature";
24568
25651
  if (top === "experiences") {
@@ -24574,6 +25657,12 @@ function resolveListTriageKindFromStaging(staging) {
24574
25657
  if (top === "data_models") {
24575
25658
  return "data_model";
24576
25659
  }
25660
+ if (top === "infrastructure") {
25661
+ return "infrastructure";
25662
+ }
25663
+ if (top === "qa") {
25664
+ return "qa";
25665
+ }
24577
25666
  return "feature";
24578
25667
  }
24579
25668
  return null;
@@ -24590,17 +25679,17 @@ function expandArgvTemplate4(template, vars) {
24590
25679
  });
24591
25680
  }
24592
25681
  function schemaRef2(worktreeRoot, schemaBasename) {
24593
- return path42.join(worktreeRoot, ".opencode", "schemas", "context-lists", schemaBasename);
25682
+ return path43.join(worktreeRoot, ".opencode", "schemas", "context-lists", schemaBasename);
24594
25683
  }
24595
25684
  function stagingDir(contextDirAbs) {
24596
- return path42.join(contextDirAbs, "_zero-ref-staging");
25685
+ return path43.join(contextDirAbs, "_zero-ref-staging");
24597
25686
  }
24598
25687
  function stagingPathForTarget(contextDirAbs, kind, targetFilePathPosix) {
24599
25688
  const h = crypto.createHash("sha256").update(targetFilePathPosix, "utf8").digest("hex").slice(0, 16);
24600
- return path42.join(stagingDir(contextDirAbs), `${kind}-${h}.json`);
25689
+ return path43.join(stagingDir(contextDirAbs), `${kind}-${h}.json`);
24601
25690
  }
24602
25691
  function readNonReferencedFilesFromRepositoryRoot(repositoryRootAbs) {
24603
- const p = path42.join(repositoryRootAbs, ".gluecharm", "context", "coverage-reference-validation.json");
25692
+ const p = path43.join(repositoryRootAbs, ".gluecharm", "context", "coverage-reference-validation.json");
24604
25693
  const r = readAndValidateCoverageReferenceValidationFile(p);
24605
25694
  if (!r.ok) {
24606
25695
  return { ok: false, error: r.errors.join("; ") };
@@ -24693,7 +25782,7 @@ function slugifyFeatureLabel(s) {
24693
25782
  return t.length > 0 ? t : "feature";
24694
25783
  }
24695
25784
  function lineCountRepoFile(worktreeRootAbs, relPosix) {
24696
- const abs = path42.join(worktreeRootAbs, ...relPosix.split("/"));
25785
+ const abs = path43.join(worktreeRootAbs, ...relPosix.split("/"));
24697
25786
  let buf;
24698
25787
  try {
24699
25788
  buf = fs45.readFileSync(abs);
@@ -24732,24 +25821,42 @@ var LIST_MERGE_CFG = {
24732
25821
  arrayKey: "entities",
24733
25822
  codeRe: /^DM-(\d+)$/,
24734
25823
  prefix: "DM"
25824
+ },
25825
+ infrastructure: {
25826
+ listBasename: "infrastructure-list.json",
25827
+ schemaBasename: "infrastructure-list.schema.json",
25828
+ arrayKey: "infrastructure",
25829
+ codeRe: /^IN-(\d+)$/,
25830
+ prefix: "IN"
25831
+ },
25832
+ qa: {
25833
+ listBasename: "qa-list.json",
25834
+ schemaBasename: "qa-list.schema.json",
25835
+ arrayKey: "qa",
25836
+ codeRe: /^QA-(\d+)$/,
25837
+ prefix: "QA"
24735
25838
  }
24736
25839
  };
24737
25840
  var DETAIL_MD_BASENAME_FOR_LIST_KIND = {
24738
25841
  feature: /^FE-\d+-.+\.md$/i,
24739
25842
  experience: /^XP-\d+-.+\.md$/i,
24740
25843
  service: /^SV-\d+-.+\.md$/i,
24741
- data_model: /^DM-\d+-.+\.md$/i
25844
+ data_model: /^DM-\d+-.+\.md$/i,
25845
+ infrastructure: /^IN-\d+-.+\.md$/i,
25846
+ qa: /^QA-\d+-.+\.md$/i
24742
25847
  };
24743
25848
  var LIST_STEP_HINT = {
24744
25849
  feature: "listFeatures",
24745
25850
  experience: "listExperiences",
24746
25851
  service: "listServices",
24747
- data_model: "listDataModel"
25852
+ data_model: "listDataModel",
25853
+ infrastructure: "listInfrastructure",
25854
+ qa: "listQa"
24748
25855
  };
24749
25856
  async function applyCoordinationListTriageMerge(input) {
24750
25857
  const cfg = LIST_MERGE_CFG[input.listKind];
24751
- const listPath = path42.join(input.contextDirAbs, cfg.listBasename);
24752
- const schemaPath = path42.join(resolveContextListSchemasDir(), cfg.schemaBasename);
25858
+ const listPath = path43.join(input.contextDirAbs, cfg.listBasename);
25859
+ const schemaPath = path43.join(resolveContextListSchemasDir(), cfg.schemaBasename);
24753
25860
  if (!fs45.existsSync(listPath)) {
24754
25861
  return {
24755
25862
  ok: false,
@@ -24853,10 +25960,10 @@ async function applyCoordinationListTriageMerge(input) {
24853
25960
  }
24854
25961
  async function runClassifierAgent(common, contextDirAbs, targetFilePathPosix, workspaceLabel) {
24855
25962
  const outAbs = stagingPathForTarget(contextDirAbs, "classifier", targetFilePathPosix);
24856
- fs45.mkdirSync(path42.dirname(outAbs), { recursive: true });
24857
- const runDir = path42.join(common.worktreeRoot, ".opencode", "_run");
25963
+ fs45.mkdirSync(path43.dirname(outAbs), { recursive: true });
25964
+ const runDir = path43.join(common.worktreeRoot, ".opencode", "_run");
24858
25965
  fs45.mkdirSync(runDir, { recursive: true });
24859
- const outputBasename = path42.basename(outAbs);
25966
+ const outputBasename = path43.basename(outAbs);
24860
25967
  const listTaskDescription = [
24861
25968
  `Target file (repo-relative, POSIX): **${targetFilePathPosix}**`,
24862
25969
  `Workspace label hint: **${workspaceLabel}**.`,
@@ -24892,7 +25999,7 @@ async function runClassifierAgent(common, contextDirAbs, targetFilePathPosix, wo
24892
25999
  parentContextBlock: "",
24893
26000
  ...repairAppendix ? { repairAppendix } : {}
24894
26001
  });
24895
- const promptPath = path42.join(runDir, `zero-ref-classify-a${attempt}-${Date.now()}.prompt.txt`);
26002
+ const promptPath = path43.join(runDir, `zero-ref-classify-a${attempt}-${Date.now()}.prompt.txt`);
24896
26003
  fs45.writeFileSync(promptPath, body, "utf-8");
24897
26004
  const argv = expandArgvTemplate4(common.argvTemplate, {
24898
26005
  promptFile: promptPath,
@@ -25020,13 +26127,25 @@ var TRIAGE_SCOPE_META = {
25020
26127
  codeExample: "DM-01",
25021
26128
  codeKind: "DM-* (**entities**)",
25022
26129
  agentLabel: "data_model"
26130
+ },
26131
+ infrastructure: {
26132
+ listFile: "infrastructure-list.json",
26133
+ codeExample: "IN-01",
26134
+ codeKind: "IN-*",
26135
+ agentLabel: "infrastructure"
26136
+ },
26137
+ qa: {
26138
+ listFile: "qa-list.json",
26139
+ codeExample: "QA-01",
26140
+ codeKind: "QA-*",
26141
+ agentLabel: "qa"
25023
26142
  }
25024
26143
  };
25025
26144
  async function runMarkdownReferenceAgent(common, contextDirAbs, targetFilePathPosix, routingSummary, which) {
25026
26145
  const mdBasename = which === "project" ? "project.md" : "architecture.md";
25027
- const mdAbs = path42.join(contextDirAbs, mdBasename);
26146
+ const mdAbs = path43.join(contextDirAbs, mdBasename);
25028
26147
  const agentStem = which === "project" ? ZERO_REF_ADD_REF_PROJECT_AGENT_STEM : ZERO_REF_ADD_REF_ARCH_AGENT_STEM;
25029
- const runDir = path42.join(common.worktreeRoot, ".opencode", "_run");
26148
+ const runDir = path43.join(common.worktreeRoot, ".opencode", "_run");
25030
26149
  fs45.mkdirSync(runDir, { recursive: true });
25031
26150
  const lines = [
25032
26151
  `# SRS-30 \u2014 Add reference to ${mdBasename}`,
@@ -25042,7 +26161,7 @@ async function runMarkdownReferenceAgent(common, contextDirAbs, targetFilePathPo
25042
26161
  `- If the file is short, \`${targetFilePathPosix}:1-<lastLine>\` is acceptable.`,
25043
26162
  `- Do not remove unrelated content; append or extend the most appropriate section.`
25044
26163
  ];
25045
- const promptPath = path42.join(runDir, `zero-ref-md-${which}-${Date.now()}.prompt.txt`);
26164
+ const promptPath = path43.join(runDir, `zero-ref-md-${which}-${Date.now()}.prompt.txt`);
25046
26165
  fs45.writeFileSync(promptPath, lines.join("\n"), "utf-8");
25047
26166
  const argv = expandArgvTemplate4(common.argvTemplate, {
25048
26167
  promptFile: promptPath,
@@ -25070,10 +26189,10 @@ async function runMarkdownReferenceAgent(common, contextDirAbs, targetFilePathPo
25070
26189
  async function runCoordinationTriageAgent(common, contextDirAbs, targetFilePathPosix, routingSummary, workspaceLabel, triageScope) {
25071
26190
  const meta = TRIAGE_SCOPE_META[triageScope];
25072
26191
  const outAbs = stagingPathForTarget(contextDirAbs, "triage", targetFilePathPosix);
25073
- fs45.mkdirSync(path42.dirname(outAbs), { recursive: true });
25074
- const runDir = path42.join(common.worktreeRoot, ".opencode", "_run");
26192
+ fs45.mkdirSync(path43.dirname(outAbs), { recursive: true });
26193
+ const runDir = path43.join(common.worktreeRoot, ".opencode", "_run");
25075
26194
  fs45.mkdirSync(runDir, { recursive: true });
25076
- const outputBasename = path42.basename(outAbs);
26195
+ const outputBasename = path43.basename(outAbs);
25077
26196
  const listTaskDescription = [
25078
26197
  `Target file: **${targetFilePathPosix}** (${workspaceLabel})`,
25079
26198
  "**Classifier summary:**",
@@ -25103,7 +26222,7 @@ async function runCoordinationTriageAgent(common, contextDirAbs, targetFilePathP
25103
26222
  parentContextBlock: "",
25104
26223
  ...repairAppendix ? { repairAppendix } : {}
25105
26224
  });
25106
- const promptPath = path42.join(runDir, `zero-ref-triage-a${attempt}-${Date.now()}.prompt.txt`);
26225
+ const promptPath = path43.join(runDir, `zero-ref-triage-a${attempt}-${Date.now()}.prompt.txt`);
25107
26226
  fs45.writeFileSync(promptPath, body, "utf-8");
25108
26227
  const argv = expandArgvTemplate4(common.argvTemplate, {
25109
26228
  promptFile: promptPath,
@@ -25218,8 +26337,8 @@ async function runCoordinationTriageAgent(common, contextDirAbs, targetFilePathP
25218
26337
  return { ok: false, message: lastFailureMessage, stagingPath: outAbs };
25219
26338
  }
25220
26339
  async function runOneUnreferencedFilePipeline(p) {
25221
- const routingAbs = path42.join(p.contextDirAbs, ZERO_REF_ROUTING_BASENAME);
25222
- const triageAbs = path42.join(p.contextDirAbs, ZERO_REF_TRIAGE_BASENAME);
26340
+ const routingAbs = path43.join(p.contextDirAbs, ZERO_REF_ROUTING_BASENAME);
26341
+ const triageAbs = path43.join(p.contextDirAbs, ZERO_REF_TRIAGE_BASENAME);
25223
26342
  const key = p.targetFilePathPosix;
25224
26343
  const cr = await runClassifierAgent(p, p.contextDirAbs, key, p.workspaceLabel);
25225
26344
  if (!cr.ok) {
@@ -25243,7 +26362,7 @@ async function runOneUnreferencedFilePipeline(p) {
25243
26362
  if (!s.ok) {
25244
26363
  throw new Error(s.errors.join("; "));
25245
26364
  }
25246
- fs45.mkdirSync(path42.dirname(routingAbs), { recursive: true });
26365
+ fs45.mkdirSync(path43.dirname(routingAbs), { recursive: true });
25247
26366
  fs45.writeFileSync(routingAbs, s.json, "utf-8");
25248
26367
  });
25249
26368
  const routing = stagingParsed.routing;
@@ -25292,7 +26411,7 @@ async function runOneUnreferencedFilePipeline(p) {
25292
26411
  if (!s.ok) {
25293
26412
  throw new Error(s.errors.join("; "));
25294
26413
  }
25295
- fs45.mkdirSync(path42.dirname(triageAbs), { recursive: true });
26414
+ fs45.mkdirSync(path43.dirname(triageAbs), { recursive: true });
25296
26415
  fs45.writeFileSync(triageAbs, s.json, "utf-8");
25297
26416
  });
25298
26417
  const decision = triageParsed.decision;
@@ -25320,11 +26439,11 @@ async function runOneUnreferencedFilePipeline(p) {
25320
26439
  return { ok: true, message: `Triage ${decision}.` };
25321
26440
  }
25322
26441
  async function runRemediationPipelineZeroRefPass(p) {
25323
- const contextDirAbs = path42.join(p.worktreeRoot, ".gluecharm", "context");
26442
+ const contextDirAbs = path43.join(p.worktreeRoot, ".gluecharm", "context");
25324
26443
  const covRoot = p.coverageRepositoryRootAbs ?? p.workspaceRootAbs;
25325
26444
  const cov = readNonReferencedFilesFromRepositoryRoot(covRoot);
25326
26445
  const coverageAt = cov.ok ? cov.generatedAt : void 0;
25327
- const workspaceLabel = path42.basename(p.worktreeRoot);
26446
+ const workspaceLabel = path43.basename(p.worktreeRoot);
25328
26447
  const routingMutex = new AsyncMutex();
25329
26448
  const triageMutex = new AsyncMutex();
25330
26449
  const paths = [...p.paths];
@@ -25386,7 +26505,7 @@ async function runRemediationPipelineZeroRefPass(p) {
25386
26505
 
25387
26506
  // src/pipelines/coverage/coverageExecutionReport.ts
25388
26507
  var fs46 = __toESM(require("fs"));
25389
- var path43 = __toESM(require("path"));
26508
+ var path44 = __toESM(require("path"));
25390
26509
  var REFERENCE_COVERAGE_EXECUTION_REPORT_BASENAME = "reference-coverage-execution-report.md";
25391
26510
  function inlineMdText(s) {
25392
26511
  const t = s.replace(/\r\n/g, "\n").replace(/\n/g, " ").trim();
@@ -25527,10 +26646,10 @@ async function runCoverageExecutionReport(p) {
25527
26646
  if (p.abortSignal?.aborted) {
25528
26647
  return { ok: false, error: "Stopped.", cancelled: true };
25529
26648
  }
25530
- const contextDirAbs = path43.join(p.repositoryRootAbs, ".gluecharm", "context");
25531
- const coverageAbs = path43.join(contextDirAbs, "coverage-reference-validation.json");
25532
- const routingAbs = path43.join(contextDirAbs, "zero-reference-routing.json");
25533
- const outAbs = path43.join(contextDirAbs, REFERENCE_COVERAGE_EXECUTION_REPORT_BASENAME);
26649
+ const contextDirAbs = path44.join(p.repositoryRootAbs, ".gluecharm", "context");
26650
+ const coverageAbs = path44.join(contextDirAbs, "coverage-reference-validation.json");
26651
+ const routingAbs = path44.join(contextDirAbs, "zero-reference-routing.json");
26652
+ const outAbs = path44.join(contextDirAbs, REFERENCE_COVERAGE_EXECUTION_REPORT_BASENAME);
25534
26653
  const cov = readAndValidateCoverageReferenceValidationFile(coverageAbs);
25535
26654
  if (!cov.ok) {
25536
26655
  return { ok: false, error: `Coverage JSON: ${cov.errors.join("; ")}` };
@@ -25557,7 +26676,7 @@ async function runCoverageExecutionReport(p) {
25557
26676
  previous = void 0;
25558
26677
  }
25559
26678
  try {
25560
- fs46.mkdirSync(path43.dirname(outAbs), { recursive: true });
26679
+ fs46.mkdirSync(path44.dirname(outAbs), { recursive: true });
25561
26680
  fs46.writeFileSync(outAbs, md, "utf-8");
25562
26681
  } catch (e) {
25563
26682
  const msg = e instanceof Error ? e.message : String(e);
@@ -25575,7 +26694,7 @@ async function runCoverageExecutionReport(p) {
25575
26694
 
25576
26695
  // src/gluecharm/minimalGluecharmLayout.ts
25577
26696
  var fs47 = __toESM(require("node:fs"));
25578
- var path44 = __toESM(require("node:path"));
26697
+ var path45 = __toESM(require("node:path"));
25579
26698
  var MINIMAL_GLUECHARM_RELATIVE_DIRS = [
25580
26699
  [".gluecharm", "docs", "srs"],
25581
26700
  [".gluecharm", "content"],
@@ -25583,9 +26702,9 @@ var MINIMAL_GLUECHARM_RELATIVE_DIRS = [
25583
26702
  [".gluecharm", "context"]
25584
26703
  ];
25585
26704
  function ensureMinimalGluecharmLayoutNode(repoRootAbs) {
25586
- const root = path44.resolve(repoRootAbs);
26705
+ const root = path45.resolve(repoRootAbs);
25587
26706
  for (const segments of MINIMAL_GLUECHARM_RELATIVE_DIRS) {
25588
- const dir = path44.join(root, ...segments);
26707
+ const dir = path45.join(root, ...segments);
25589
26708
  try {
25590
26709
  fs47.mkdirSync(dir, { recursive: true });
25591
26710
  } catch (e) {
@@ -25720,7 +26839,7 @@ function buildFactoryDepsHeadless(input) {
25720
26839
  if (!layout.ok) {
25721
26840
  return { ok: false, error: layout.error };
25722
26841
  }
25723
- const ctxDir = path45.join(ar, ".gluecharm", "context");
26842
+ const ctxDir = path46.join(ar, ".gluecharm", "context");
25724
26843
  const snap = readArtefactRunSnapshot(storageContext);
25725
26844
  const rows = listMissingWorkstations(ctxDir, ar, snap);
25726
26845
  if (rows.length === 0) {
@@ -25734,7 +26853,7 @@ function buildFactoryDepsHeadless(input) {
25734
26853
  storageContext,
25735
26854
  repositoryRoot: repoRoot,
25736
26855
  worktreeRoot: ar,
25737
- workspaceLabel: path45.basename(ar),
26856
+ workspaceLabel: path46.basename(ar),
25738
26857
  oc: {
25739
26858
  ...oc,
25740
26859
  aceEnabled: getAceAnalysisEnabledForCheckout(ar),
@@ -25762,7 +26881,7 @@ function buildFactoryDepsHeadless(input) {
25762
26881
  }
25763
26882
  try {
25764
26883
  await startPipelineRun(storageContext, repoRoot);
25765
- const folderName = path45.basename(repoRoot);
26884
+ const folderName = path46.basename(repoRoot);
25766
26885
  const oc = buildOpenCodeOptions(handle.path);
25767
26886
  const result = await runSynthesisPipelineDrainFromPreparedWorktree(
25768
26887
  storageContext,
@@ -25819,7 +26938,7 @@ function buildFactoryDepsHeadless(input) {
25819
26938
  if (!ar) {
25820
26939
  return 0;
25821
26940
  }
25822
- const ctxDir = path45.join(ar, ".gluecharm", "context");
26941
+ const ctxDir = path46.join(ar, ".gluecharm", "context");
25823
26942
  const snap = readArtefactRunSnapshot(storageContext);
25824
26943
  return listMissingWorkstations(ctxDir, ar, snap).length;
25825
26944
  };
@@ -25832,7 +26951,7 @@ function buildFactoryDepsHeadless(input) {
25832
26951
  if (!layout.ok) {
25833
26952
  return { ok: false, message: layout.error };
25834
26953
  }
25835
- const contextDir2 = path45.join(ar, ".gluecharm", "context");
26954
+ const contextDir2 = path46.join(ar, ".gluecharm", "context");
25836
26955
  log(`[factory] reference coverage (worktree) \u2014 ${ar}`);
25837
26956
  const res = runCoveragePipeline({
25838
26957
  repositoryRootAbs: ar,
@@ -25895,7 +27014,7 @@ function buildFactoryDepsHeadless(input) {
25895
27014
  ...remaining ? { remainingRemediationFailures: remaining } : {}
25896
27015
  });
25897
27016
  if (res.ok) {
25898
- return { ok: true, message: `Report: ${path45.basename(res.outputAbsolutePath)}` };
27017
+ return { ok: true, message: `Report: ${path46.basename(res.outputAbsolutePath)}` };
25899
27018
  }
25900
27019
  if (res.cancelled) {
25901
27020
  return { ok: false, cancelled: true, message: "Report generation stopped." };
@@ -25911,7 +27030,7 @@ function buildFactoryDepsHeadless(input) {
25911
27030
  if (!lmLayout.ok) {
25912
27031
  return { ok: false, message: lmLayout.error };
25913
27032
  }
25914
- const contextDir2 = path45.join(snap.adHocWorktreePath, ".gluecharm", "context");
27033
+ const contextDir2 = path46.join(snap.adHocWorktreePath, ".gluecharm", "context");
25915
27034
  const linkGraph = runLinkMappingPipeline(contextDir2, { log });
25916
27035
  if (!linkGraph.ok) {
25917
27036
  return { ok: false, message: linkGraph.error };
@@ -25927,7 +27046,7 @@ function buildFactoryDepsHeadless(input) {
25927
27046
  if (!idxLayout.ok) {
25928
27047
  return { ok: false, message: idxLayout.error };
25929
27048
  }
25930
- const contextDir2 = path45.join(snap.adHocWorktreePath, ".gluecharm", "context");
27049
+ const contextDir2 = path46.join(snap.adHocWorktreePath, ".gluecharm", "context");
25931
27050
  try {
25932
27051
  writeIndexApplicationContext(contextDir2, void 0, {
25933
27052
  sourceBranchAtWorktreeCreation: snap.adHocSourceBranchAtCreation
@@ -25983,13 +27102,13 @@ function buildFactoryDepsHeadless(input) {
25983
27102
  },
25984
27103
  runPrepareAnalysisWorktree: async (resume) => {
25985
27104
  if (resume) {
25986
- if (adHocWorktree && fs48.existsSync(path45.join(adHocWorktree.path, ".git"))) {
27105
+ if (adHocWorktree && fs48.existsSync(path46.join(adHocWorktree.path, ".git"))) {
25987
27106
  return { ok: true };
25988
27107
  }
25989
27108
  const snap = readAnalysisWorkspaceSnapshot(storageContext);
25990
27109
  const wtPath = snap?.adHocWorktreePath?.trim();
25991
27110
  const repo = snap?.adHocRepositoryRoot?.trim() || repoRoot;
25992
- if (wtPath && fs48.existsSync(path45.join(wtPath, ".git"))) {
27111
+ if (wtPath && fs48.existsSync(path46.join(wtPath, ".git"))) {
25993
27112
  adHocWorktree = resolveAnalysisCheckoutHandle(wtPath, repo);
25994
27113
  macroSourceBranch = snap?.adHocSourceBranchAtCreation;
25995
27114
  macroFinalize = () => {
@@ -26207,11 +27326,11 @@ function stderrLinesForFactoryFailures(failures, exitCode) {
26207
27326
  }
26208
27327
 
26209
27328
  // src/factory/updateContext/runUpdateContextFactory.ts
26210
- var path50 = __toESM(require("node:path"));
27329
+ var path51 = __toESM(require("node:path"));
26211
27330
 
26212
27331
  // src/factory/updateContext/updateContextBaseline.ts
26213
27332
  var fs49 = __toESM(require("node:fs"));
26214
- var path46 = __toESM(require("node:path"));
27333
+ var path47 = __toESM(require("node:path"));
26215
27334
  function isValidIso(s) {
26216
27335
  const t = Date.parse(s);
26217
27336
  return Number.isFinite(t);
@@ -26226,7 +27345,7 @@ function maxMtimeRegularFilesUnderDir(dirAbs) {
26226
27345
  return;
26227
27346
  }
26228
27347
  for (const e of entries) {
26229
- const p = path46.join(d, e.name);
27348
+ const p = path47.join(d, e.name);
26230
27349
  if (e.isDirectory()) {
26231
27350
  walk(p);
26232
27351
  } else if (e.isFile()) {
@@ -26252,7 +27371,7 @@ function resolveUpdateContextBaseline(repoRootAbs, repoConfig) {
26252
27371
  if (last.length > 0 && isValidIso(last)) {
26253
27372
  return { baselineIsoUtc: new Date(last).toISOString(), source: "lastRunAt" };
26254
27373
  }
26255
- const ctxDir = path46.join(repoRootAbs, ".gluecharm", "context");
27374
+ const ctxDir = path47.join(repoRootAbs, ".gluecharm", "context");
26256
27375
  if (!fs49.existsSync(ctxDir)) {
26257
27376
  return null;
26258
27377
  }
@@ -26277,7 +27396,7 @@ function persistUpdateContextLastRunAt(repoRootAbs, isoUtc) {
26277
27396
  // src/factory/updateContext/updateContextGitWindow.ts
26278
27397
  var import_node_child_process3 = require("node:child_process");
26279
27398
  var fs50 = __toESM(require("node:fs"));
26280
- var path47 = __toESM(require("node:path"));
27399
+ var path48 = __toESM(require("node:path"));
26281
27400
  var GIT_ENV = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
26282
27401
  function gitLines(repoRootAbs, args) {
26283
27402
  const r = (0, import_node_child_process3.execFileSync)("git", ["-c", "core.quotepath=false", ...args], {
@@ -26301,8 +27420,8 @@ function parseGitLogIso(line) {
26301
27420
  return { hash, iso };
26302
27421
  }
26303
27422
  function discoverCommitWindowAndTouchedPaths(repoRootAbs, baselineIsoUtc) {
26304
- const root = path47.resolve(repoRootAbs);
26305
- if (!fs50.existsSync(path47.join(root, ".git"))) {
27423
+ const root = path48.resolve(repoRootAbs);
27424
+ if (!fs50.existsSync(path48.join(root, ".git"))) {
26306
27425
  return { ok: false, error: "Not a git repository (missing .git)." };
26307
27426
  }
26308
27427
  const baselineMs = Date.parse(baselineIsoUtc);
@@ -26361,11 +27480,11 @@ function discoverCommitWindowAndTouchedPaths(repoRootAbs, baselineIsoUtc) {
26361
27480
  };
26362
27481
  }
26363
27482
  function filterPathsExistingInWorktree(worktreeRootAbs, pathsPosix) {
26364
- const root = path47.resolve(worktreeRootAbs);
27483
+ const root = path48.resolve(worktreeRootAbs);
26365
27484
  const out = [];
26366
27485
  for (const p of pathsPosix) {
26367
27486
  const rel = p.replace(/\\/g, "/");
26368
- const abs = path47.join(root, ...rel.split("/"));
27487
+ const abs = path48.join(root, ...rel.split("/"));
26369
27488
  try {
26370
27489
  if (fs50.existsSync(abs) && fs50.statSync(abs).isFile()) {
26371
27490
  out.push(rel);
@@ -26378,7 +27497,7 @@ function filterPathsExistingInWorktree(worktreeRootAbs, pathsPosix) {
26378
27497
 
26379
27498
  // src/factory/updateContext/updateContextReport.ts
26380
27499
  var fs51 = __toESM(require("node:fs"));
26381
- var path48 = __toESM(require("node:path"));
27500
+ var path49 = __toESM(require("node:path"));
26382
27501
  var CHANGES_SINCE_DATE_BASENAME = "changes-since-date.md";
26383
27502
  function renderChangesSinceDateMarkdown(p) {
26384
27503
  const lines = [
@@ -26420,7 +27539,7 @@ function renderChangesSinceDateMarkdown(p) {
26420
27539
  function writeChangesSinceDateReport(contextDirAbs, body) {
26421
27540
  try {
26422
27541
  fs51.mkdirSync(contextDirAbs, { recursive: true });
26423
- const target = path48.join(contextDirAbs, CHANGES_SINCE_DATE_BASENAME);
27542
+ const target = path49.join(contextDirAbs, CHANGES_SINCE_DATE_BASENAME);
26424
27543
  fs51.writeFileSync(target, body, "utf-8");
26425
27544
  return { ok: true };
26426
27545
  } catch (e) {
@@ -26430,7 +27549,7 @@ function writeChangesSinceDateReport(contextDirAbs, body) {
26430
27549
 
26431
27550
  // src/factory/updateContext/updateContextSeedCheck.ts
26432
27551
  var fs52 = __toESM(require("node:fs"));
26433
- var path49 = __toESM(require("node:path"));
27552
+ var path50 = __toESM(require("node:path"));
26434
27553
  var INDEX_BASENAME = "index-application-context.json";
26435
27554
  var CHANGES_REPORT = "changes-since-date.md";
26436
27555
  function tryParseJsonFile(abs) {
@@ -26446,7 +27565,7 @@ function isWorktreeContextSeeded(contextDirAbs) {
26446
27565
  if (!fs52.existsSync(contextDirAbs)) {
26447
27566
  return false;
26448
27567
  }
26449
- const indexAbs = path49.join(contextDirAbs, INDEX_BASENAME);
27568
+ const indexAbs = path50.join(contextDirAbs, INDEX_BASENAME);
26450
27569
  if (fs52.existsSync(indexAbs) && fs52.statSync(indexAbs).isFile() && tryParseJsonFile(indexAbs)) {
26451
27570
  return true;
26452
27571
  }
@@ -26464,7 +27583,7 @@ function isWorktreeContextSeeded(contextDirAbs) {
26464
27583
  if (!name.endsWith(".md") && !name.endsWith(".json")) {
26465
27584
  continue;
26466
27585
  }
26467
- const p = path49.join(contextDirAbs, name);
27586
+ const p = path50.join(contextDirAbs, name);
26468
27587
  try {
26469
27588
  if (fs52.statSync(p).isFile()) {
26470
27589
  distinct.add(name);
@@ -26478,7 +27597,7 @@ function isWorktreeContextSeeded(contextDirAbs) {
26478
27597
  // src/factory/updateContext/runUpdateContextFactory.ts
26479
27598
  var REMEDIATION_CHUNK_MAX = 40;
26480
27599
  function contextDirUnderRoot(wtRoot) {
26481
- return path50.join(wtRoot, ".gluecharm", "context");
27600
+ return path51.join(wtRoot, ".gluecharm", "context");
26482
27601
  }
26483
27602
  async function runUpdateContextFactory(deps) {
26484
27603
  const inPlace = deps.inPlace === true;
@@ -26809,11 +27928,11 @@ async function runUpdateContextFactory(deps) {
26809
27928
 
26810
27929
  // src/factory/contextDrift/runContextDriftFactory.ts
26811
27930
  var fs58 = __toESM(require("node:fs"));
26812
- var path55 = __toESM(require("node:path"));
27931
+ var path56 = __toESM(require("node:path"));
26813
27932
 
26814
27933
  // src/factory/contextDrift/contextDriftManifest.ts
26815
27934
  var fs53 = __toESM(require("node:fs"));
26816
- var path51 = __toESM(require("node:path"));
27935
+ var path52 = __toESM(require("node:path"));
26817
27936
  var MAX_REFERENCE_BYTES = 256 * 1024;
26818
27937
  var MAX_EVIDENCE_FILES = 300;
26819
27938
  var MAX_EVIDENCE_READ = 512 * 1024;
@@ -26833,16 +27952,16 @@ function collectEvidencePaths(repoRoot) {
26833
27952
  const out = [];
26834
27953
  const roots = ["src", "test", "tests", "packages", ".gluecharm", "scripts"];
26835
27954
  for (const rel of roots) {
26836
- const abs = path51.join(repoRoot, rel);
27955
+ const abs = path52.join(repoRoot, rel);
26837
27956
  if (!fs53.existsSync(abs)) {
26838
27957
  continue;
26839
27958
  }
26840
27959
  walkFiles(abs, repoRoot, out, 0);
26841
27960
  }
26842
27961
  for (const leaf of ["package.json", "tsconfig.json"]) {
26843
- const abs = path51.join(repoRoot, leaf);
27962
+ const abs = path52.join(repoRoot, leaf);
26844
27963
  if (fs53.existsSync(abs) && fs53.statSync(abs).isFile()) {
26845
- const r = path51.relative(repoRoot, abs).split(path51.sep).join("/");
27964
+ const r = path52.relative(repoRoot, abs).split(path52.sep).join("/");
26846
27965
  out.push(r);
26847
27966
  }
26848
27967
  }
@@ -26867,13 +27986,13 @@ function walkFiles(dir, repoRoot, out, depth) {
26867
27986
  if (e.name === "node_modules" || e.name === ".git" || e.name === "dist" || e.name === "out") {
26868
27987
  continue;
26869
27988
  }
26870
- const full = path51.join(dir, e.name);
27989
+ const full = path52.join(dir, e.name);
26871
27990
  if (e.isDirectory()) {
26872
27991
  walkFiles(full, repoRoot, out, depth + 1);
26873
27992
  } else if (e.isFile()) {
26874
- const ext = path51.extname(e.name).toLowerCase();
27993
+ const ext = path52.extname(e.name).toLowerCase();
26875
27994
  if ([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yaml", ".yml"].includes(ext) || e.name === "Dockerfile") {
26876
- const rel = path51.relative(repoRoot, full).split(path51.sep).join("/");
27995
+ const rel = path52.relative(repoRoot, full).split(path52.sep).join("/");
26877
27996
  out.push(rel);
26878
27997
  }
26879
27998
  }
@@ -26883,7 +28002,7 @@ function buildComparisonManifest(args) {
26883
28002
  const references = [];
26884
28003
  let referenceTruncated = false;
26885
28004
  for (const abs of args.bundleAbsFiles) {
26886
- const rel = path51.relative(args.worktreeRoot, abs).split(path51.sep).join("/");
28005
+ const rel = path52.relative(args.worktreeRoot, abs).split(path52.sep).join("/");
26887
28006
  const { text, truncated } = readFileLimited(abs, MAX_REFERENCE_BYTES);
26888
28007
  references.push({ path: rel, content: text, truncated });
26889
28008
  if (truncated) {
@@ -26895,7 +28014,7 @@ function buildComparisonManifest(args) {
26895
28014
  const omitted = Math.max(0, allEvidence.length - evidencePathsTrimmed.length);
26896
28015
  const evidenceFiles = [];
26897
28016
  for (const rel of evidencePathsTrimmed) {
26898
- const abs = path51.join(args.worktreeRoot, ...rel.split("/"));
28017
+ const abs = path52.join(args.worktreeRoot, ...rel.split("/"));
26899
28018
  const st = fs53.existsSync(abs) ? fs53.statSync(abs) : null;
26900
28019
  const size = st && st.isFile() ? st.size : 0;
26901
28020
  const { text, truncated } = readFileLimited(abs, MAX_EVIDENCE_READ);
@@ -26906,7 +28025,7 @@ function buildComparisonManifest(args) {
26906
28025
  }
26907
28026
  return {
26908
28027
  runDate: args.runDate,
26909
- referenceRelPaths: args.bundleAbsFiles.map((a) => path51.relative(args.worktreeRoot, a).split(path51.sep).join("/")),
28028
+ referenceRelPaths: args.bundleAbsFiles.map((a) => path52.relative(args.worktreeRoot, a).split(path52.sep).join("/")),
26910
28029
  references,
26911
28030
  evidenceFiles,
26912
28031
  evidencePathsOnly: [],
@@ -26919,7 +28038,7 @@ function buildComparisonManifest(args) {
26919
28038
 
26920
28039
  // src/factory/contextDrift/contextDriftAgent.ts
26921
28040
  var fs54 = __toESM(require("node:fs"));
26922
- var path52 = __toESM(require("node:path"));
28041
+ var path53 = __toESM(require("node:path"));
26923
28042
 
26924
28043
  // src/factory/contextDrift/contextDriftPayload.ts
26925
28044
  function isNonEmptyString2(v) {
@@ -27064,16 +28183,16 @@ function buildDriftPrompt(args) {
27064
28183
  ].join("\n");
27065
28184
  }
27066
28185
  async function runDriftComparisonOpenCode(args) {
27067
- const runDir = path52.join(args.worktreeRoot, ".opencode", "_run");
28186
+ const runDir = path53.join(args.worktreeRoot, ".opencode", "_run");
27068
28187
  fs54.mkdirSync(runDir, { recursive: true });
27069
- const manifestPath = path52.join(runDir, "context-drift-manifest.json");
27070
- const outputPath = path52.join(runDir, "context-drift-payload.json");
28188
+ const manifestPath = path53.join(runDir, "context-drift-manifest.json");
28189
+ const outputPath = path53.join(runDir, "context-drift-payload.json");
27071
28190
  fs54.writeFileSync(manifestPath, `${JSON.stringify(args.manifestObject, null, 2)}
27072
28191
  `, "utf8");
27073
28192
  if (fs54.existsSync(outputPath)) {
27074
28193
  fs54.unlinkSync(outputPath);
27075
28194
  }
27076
- const promptPath = path52.join(runDir, `context-drift-${Date.now()}.prompt.txt`);
28195
+ const promptPath = path53.join(runDir, `context-drift-${Date.now()}.prompt.txt`);
27077
28196
  fs54.writeFileSync(
27078
28197
  promptPath,
27079
28198
  buildDriftPrompt({
@@ -27091,7 +28210,7 @@ async function runDriftComparisonOpenCode(args) {
27091
28210
  });
27092
28211
  const title = buildOpenCodeSessionTitle({
27093
28212
  runId: "context-drift",
27094
- workItemId: path52.basename(args.worktreeRoot).slice(0, 24) || "wt",
28213
+ workItemId: path53.basename(args.worktreeRoot).slice(0, 24) || "wt",
27095
28214
  stepLabel: "context-drift"
27096
28215
  });
27097
28216
  const argv = injectPrimaryOpenCodeSessionArgv(expanded, title);
@@ -27134,8 +28253,8 @@ async function runDriftComparisonOpenCode(args) {
27134
28253
  // src/factory/contextDrift/contextDriftPaths.ts
27135
28254
  var crypto2 = __toESM(require("node:crypto"));
27136
28255
  var fs55 = __toESM(require("node:fs"));
27137
- var path53 = __toESM(require("node:path"));
27138
- var DRIFT_CONTEXT_SUBDIR = path53.join(".gluecharm", "context", "drift");
28256
+ var path54 = __toESM(require("node:path"));
28257
+ var DRIFT_CONTEXT_SUBDIR = path54.join(".gluecharm", "context", "drift");
27139
28258
  function sanitizeSlug(raw) {
27140
28259
  const s = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
27141
28260
  return s.length > 0 ? s : "drift";
@@ -27144,9 +28263,9 @@ function utcDateString(d) {
27144
28263
  return d.toISOString().slice(0, 10);
27145
28264
  }
27146
28265
  function resolveInsideRepo(repoRootAbs, userPath) {
27147
- const abs = path53.isAbsolute(userPath) ? path53.normalize(userPath) : path53.resolve(repoRootAbs, userPath);
27148
- const rel = path53.relative(repoRootAbs, abs);
27149
- if (rel.startsWith("..") || path53.isAbsolute(rel)) {
28266
+ const abs = path54.isAbsolute(userPath) ? path54.normalize(userPath) : path54.resolve(repoRootAbs, userPath);
28267
+ const rel = path54.relative(repoRootAbs, abs);
28268
+ if (rel.startsWith("..") || path54.isAbsolute(rel)) {
27150
28269
  return { ok: false };
27151
28270
  }
27152
28271
  return { ok: true, abs };
@@ -27156,7 +28275,7 @@ function computeSlug(args) {
27156
28275
  return sanitizeSlug(args.label.trim());
27157
28276
  }
27158
28277
  const r = resolveInsideRepo(args.repoRootAbs, args.referencePath);
27159
- const base = r.ok ? path53.basename(r.abs) : path53.basename(args.referencePath);
28278
+ const base = r.ok ? path54.basename(r.abs) : path54.basename(args.referencePath);
27160
28279
  return sanitizeSlug(base);
27161
28280
  }
27162
28281
  function driftFilename(slug, runDate) {
@@ -27198,7 +28317,7 @@ function discoverReferenceBundle(worktreeRoot, referenceAbsInWorktree) {
27198
28317
  return;
27199
28318
  }
27200
28319
  for (const e of entries) {
27201
- const full = path53.join(dir, e.name);
28320
+ const full = path54.join(dir, e.name);
27202
28321
  if (e.isDirectory()) {
27203
28322
  if (e.name === "node_modules" || e.name === ".git") {
27204
28323
  continue;
@@ -27228,7 +28347,7 @@ function pickReferenceRootDocument(args) {
27228
28347
  }
27229
28348
  const dir = args.referenceAbsInWorktree;
27230
28349
  for (const name of ["index.md", "README.md"]) {
27231
- const p = path53.join(dir, name);
28350
+ const p = path54.join(dir, name);
27232
28351
  if (fs55.existsSync(p) && fs55.statSync(p).isFile()) {
27233
28352
  return { ok: true, path: p };
27234
28353
  }
@@ -27239,10 +28358,10 @@ function pickReferenceRootDocument(args) {
27239
28358
  };
27240
28359
  }
27241
28360
  function toWorktreeRelative(worktreeRoot, absolute) {
27242
- return path53.relative(worktreeRoot, absolute).split(path53.sep).join("/");
28361
+ return path54.relative(worktreeRoot, absolute).split(path54.sep).join("/");
27243
28362
  }
27244
28363
  function toPosixPath(p) {
27245
- return p.split(path53.sep).join("/");
28364
+ return p.split(path54.sep).join("/");
27246
28365
  }
27247
28366
 
27248
28367
  // src/factory/contextDrift/contextDriftIndex.ts
@@ -27285,19 +28404,19 @@ function escapeRe(s) {
27285
28404
 
27286
28405
  // src/factory/contextDrift/contextDriftPromote.ts
27287
28406
  var fs57 = __toESM(require("node:fs"));
27288
- var path54 = __toESM(require("node:path"));
28407
+ var path55 = __toESM(require("node:path"));
27289
28408
  function copyWorktreeFileToWorkspace(args) {
27290
- const wt = path54.resolve(args.worktreeRoot);
27291
- const ws = path54.resolve(args.workspaceRoot);
28409
+ const wt = path55.resolve(args.worktreeRoot);
28410
+ const ws = path55.resolve(args.workspaceRoot);
27292
28411
  if (wt === ws) {
27293
28412
  return;
27294
28413
  }
27295
- const src = path54.join(args.worktreeRoot, ...args.relativePosix.split("/"));
27296
- const dest = path54.join(args.workspaceRoot, ...args.relativePosix.split("/"));
27297
- if (path54.resolve(src) === path54.resolve(dest)) {
28414
+ const src = path55.join(args.worktreeRoot, ...args.relativePosix.split("/"));
28415
+ const dest = path55.join(args.workspaceRoot, ...args.relativePosix.split("/"));
28416
+ if (path55.resolve(src) === path55.resolve(dest)) {
27298
28417
  return;
27299
28418
  }
27300
- fs57.mkdirSync(path54.dirname(dest), { recursive: true });
28419
+ fs57.mkdirSync(path55.dirname(dest), { recursive: true });
27301
28420
  fs57.copyFileSync(src, dest);
27302
28421
  }
27303
28422
 
@@ -27353,7 +28472,7 @@ async function runContextDriftFactory(deps) {
27353
28472
  ok: true,
27354
28473
  code: "DRY_RUN",
27355
28474
  ...baseMeta,
27356
- referenceRootDocument: toPosixPath(path55.relative(deps.repoRootAbs, refAbsWorkspace)) + (fs58.statSync(refAbsWorkspace).isDirectory() ? "/" : ""),
28475
+ referenceRootDocument: toPosixPath(path56.relative(deps.repoRootAbs, refAbsWorkspace)) + (fs58.statSync(refAbsWorkspace).isDirectory() ? "/" : ""),
27357
28476
  driftReportPath: null,
27358
28477
  analysisWorktreeRoot: "",
27359
28478
  promoted: false,
@@ -27410,8 +28529,8 @@ async function runContextDriftFactory(deps) {
27410
28529
  const handle = prep.handle;
27411
28530
  const finalizeWt = prep.finalize;
27412
28531
  const wt = handle.path;
27413
- const refRel = path55.relative(deps.repoRootAbs, refAbsWorkspace).split(path55.sep).join("/");
27414
- const refAbsWt = path55.join(wt, ...refRel.split("/"));
28532
+ const refRel = path56.relative(deps.repoRootAbs, refAbsWorkspace).split(path56.sep).join("/");
28533
+ const refAbsWt = path56.join(wt, ...refRel.split("/"));
27415
28534
  try {
27416
28535
  materializeOpenCodeAgentsWithAce(deps.agentsDirFs, wt, {
27417
28536
  enabled: getAceAnalysisEnabledForCheckout(wt),
@@ -27450,7 +28569,7 @@ async function runContextDriftFactory(deps) {
27450
28569
  ...baseMeta
27451
28570
  };
27452
28571
  }
27453
- const indexWt = indexOverrideAbsWorkspace ? path55.join(wt, ...path55.relative(deps.repoRootAbs, indexOverrideAbsWorkspace).split(path55.sep)) : void 0;
28572
+ const indexWt = indexOverrideAbsWorkspace ? path56.join(wt, ...path56.relative(deps.repoRootAbs, indexOverrideAbsWorkspace).split(path56.sep)) : void 0;
27454
28573
  const rootPick = pickReferenceRootDocument({
27455
28574
  referencePathIsFile: fs58.statSync(refAbsWt).isFile(),
27456
28575
  referenceAbsInWorktree: refAbsWt,
@@ -27478,9 +28597,9 @@ async function runContextDriftFactory(deps) {
27478
28597
  let slug = computeSlug({ label: deps.label, referencePath: deps.referencePathArg, repoRootAbs: deps.repoRootAbs });
27479
28598
  slug = maybeDedupeSlug(slug, refRel);
27480
28599
  const driftBase = driftFilename(slug, runDate);
27481
- const driftDirWt = path55.join(wt, DRIFT_CONTEXT_SUBDIR);
28600
+ const driftDirWt = path56.join(wt, DRIFT_CONTEXT_SUBDIR);
27482
28601
  fs58.mkdirSync(driftDirWt, { recursive: true });
27483
- const driftAbsWt = path55.join(driftDirWt, driftBase);
28602
+ const driftAbsWt = path56.join(driftDirWt, driftBase);
27484
28603
  let payload;
27485
28604
  if (deps.testOnlyFixturePayload) {
27486
28605
  payload = deps.testOnlyFixturePayload;
@@ -27529,7 +28648,7 @@ async function runContextDriftFactory(deps) {
27529
28648
  ...baseMeta
27530
28649
  };
27531
28650
  }
27532
- const driftRelFromRootDir = path55.relative(path55.dirname(refRootAbsWt), driftAbsWt).split(path55.sep).join("/");
28651
+ const driftRelFromRootDir = path56.relative(path56.dirname(refRootAbsWt), driftAbsWt).split(path56.sep).join("/");
27533
28652
  const linkRes = patchReferenceIndexWithDriftLink({
27534
28653
  referenceRootAbsolute: refRootAbsWt,
27535
28654
  relativeLinkFromRootToDrift: driftRelFromRootDir,
@@ -27548,8 +28667,8 @@ async function runContextDriftFactory(deps) {
27548
28667
  ...baseMeta
27549
28668
  };
27550
28669
  }
27551
- const driftRelPosix = toPosixPath(path55.relative(wt, driftAbsWt));
27552
- const rootRelPosix = toPosixPath(path55.relative(wt, refRootAbsWt));
28670
+ const driftRelPosix = toPosixPath(path56.relative(wt, driftAbsWt));
28671
+ const rootRelPosix = toPosixPath(path56.relative(wt, refRootAbsWt));
27553
28672
  const promoteEffective = deps.merged.promoteContextToWorkspace !== false;
27554
28673
  if (promoteEffective && !inPlace) {
27555
28674
  try {
@@ -27579,7 +28698,7 @@ async function runContextDriftFactory(deps) {
27579
28698
  } else if (promoteEffective && inPlace) {
27580
28699
  deps.log("[context-drift] in-place analysis \u2014 promote skipped (artefacts already under repository root)");
27581
28700
  }
27582
- const driftReportPathFs = promoteEffective ? path55.join(deps.repoRootAbs, ...driftRelPosix.split("/")) : driftAbsWt;
28701
+ const driftReportPathFs = promoteEffective ? path56.join(deps.repoRootAbs, ...driftRelPosix.split("/")) : driftAbsWt;
27583
28702
  return {
27584
28703
  exitOk: true,
27585
28704
  ok: true,
@@ -27598,14 +28717,16 @@ async function runContextDriftFactory(deps) {
27598
28717
 
27599
28718
  // src/analysis/coordinationDuplicatesDiagnosis.ts
27600
28719
  var fs59 = __toESM(require("fs"));
27601
- var path56 = __toESM(require("path"));
28720
+ var path57 = __toESM(require("path"));
27602
28721
  var import__7 = __toESM(require__());
27603
28722
  var COORDINATION_DUPLICATES_REPORT_BASENAME = "coordination-duplicates-report.json";
27604
28723
  var COORDINATION_LIST_SCAN_ENTRIES = [
27605
28724
  { basename: "features-list.json", arrayKey: "features" },
27606
28725
  { basename: "experiences-list.json", arrayKey: "views" },
27607
28726
  { basename: "services-list.json", arrayKey: "services" },
27608
- { basename: "data-model-list.json", arrayKey: "entities" }
28727
+ { basename: "data-model-list.json", arrayKey: "entities" },
28728
+ { basename: "infrastructure-list.json", arrayKey: "infrastructure" },
28729
+ { basename: "qa-list.json", arrayKey: "qa" }
27609
28730
  ];
27610
28731
  var STAPLE_CONTEXT_MARKDOWN_BASENAMES = /* @__PURE__ */ new Set([
27611
28732
  "project.md",
@@ -27623,17 +28744,21 @@ var RE_MD_SV = /^SV-\d+-.+\.md$/i;
27623
28744
  var RE_MD_DM_FD = /^DM-\d+_FD-\d+-.+\.md$/i;
27624
28745
  var RE_MD_DM = /^DM-\d+-.+\.md$/i;
27625
28746
  var RE_MD_TS = /^TS-\d+-.+\.md$/i;
27626
- function looksLikeCoordinationDetailMarkdownBasename(basename17) {
27627
- if (!basename17 || basename17 !== path56.basename(basename17) || !/\.md$/i.test(basename17)) {
28747
+ var RE_MD_IN_IC = /^IN-\d+_IC-\d+-.+\.md$/i;
28748
+ var RE_MD_IN = /^IN-\d+-.+\.md$/i;
28749
+ var RE_MD_QA_TC = /^QA-\d+_TC-\d+-.+\.md$/i;
28750
+ var RE_MD_QA = /^QA-\d+-.+\.md$/i;
28751
+ function looksLikeCoordinationDetailMarkdownBasename(basename18) {
28752
+ if (!basename18 || basename18 !== path57.basename(basename18) || !/\.md$/i.test(basename18)) {
27628
28753
  return false;
27629
28754
  }
27630
- if (STAPLE_CONTEXT_MARKDOWN_BASENAMES.has(basename17)) {
28755
+ if (STAPLE_CONTEXT_MARKDOWN_BASENAMES.has(basename18)) {
27631
28756
  return false;
27632
28757
  }
27633
- return RE_MD_FE_UC_SC.test(basename17) || RE_MD_FE_UC_SLUG.test(basename17) || RE_MD_FE_UC_PLAIN.test(basename17) || RE_MD_FE_FEATURE.test(basename17) || RE_MD_XP_BH.test(basename17) || RE_MD_XP_VIEW.test(basename17) || RE_MD_SV_ME.test(basename17) || RE_MD_SV.test(basename17) || RE_MD_DM_FD.test(basename17) || RE_MD_DM.test(basename17) || RE_MD_TS.test(basename17);
28758
+ return RE_MD_FE_UC_SC.test(basename18) || RE_MD_FE_UC_SLUG.test(basename18) || RE_MD_FE_UC_PLAIN.test(basename18) || RE_MD_FE_FEATURE.test(basename18) || RE_MD_XP_BH.test(basename18) || RE_MD_XP_VIEW.test(basename18) || RE_MD_SV_ME.test(basename18) || RE_MD_SV.test(basename18) || RE_MD_DM_FD.test(basename18) || RE_MD_DM.test(basename18) || RE_MD_TS.test(basename18) || RE_MD_IN_IC.test(basename18) || RE_MD_IN.test(basename18) || RE_MD_QA_TC.test(basename18) || RE_MD_QA.test(basename18);
27634
28759
  }
27635
28760
  function loadRawFeatureRows(contextDirAbs) {
27636
- const p = path56.join(contextDirAbs, "features-list.json");
28761
+ const p = path57.join(contextDirAbs, "features-list.json");
27637
28762
  if (!fs59.existsSync(p)) {
27638
28763
  return [];
27639
28764
  }
@@ -27645,8 +28770,8 @@ function loadRawFeatureRows(contextDirAbs) {
27645
28770
  return [];
27646
28771
  }
27647
28772
  }
27648
- function hintForOrphanFeatureMarkdown(basename17, featureRows) {
27649
- const m = /^FE-(\d+)-(.+)\.md$/i.exec(basename17);
28773
+ function hintForOrphanFeatureMarkdown(basename18, featureRows) {
28774
+ const m = /^FE-(\d+)-(.+)\.md$/i.exec(basename18);
27650
28775
  if (!m) {
27651
28776
  return void 0;
27652
28777
  }
@@ -27658,8 +28783,8 @@ function hintForOrphanFeatureMarkdown(basename17, featureRows) {
27658
28783
  continue;
27659
28784
  }
27660
28785
  const expected = expectedFeatureDetailBasenameFromRow(row2);
27661
- if (expected && expected !== basename17) {
27662
- return `features-list row ${code} currently implies detail file ${expected} (slug/name changed \u2014 ${basename17} may be stale).`;
28786
+ if (expected && expected !== basename18) {
28787
+ return `features-list row ${code} currently implies detail file ${expected} (slug/name changed \u2014 ${basename18} may be stale).`;
27663
28788
  }
27664
28789
  if (!expected) {
27665
28790
  return `features-list row ${code} has no resolvable slug for a detail markdown basename.`;
@@ -27926,7 +29051,7 @@ function buildCoordinationDuplicatesReport(input) {
27926
29051
  const lists = [];
27927
29052
  const duplicateGroups = [];
27928
29053
  for (const entry of COORDINATION_LIST_SCAN_ENTRIES) {
27929
- const filePath = path56.join(input.contextDirAbsolute, entry.basename);
29054
+ const filePath = path57.join(input.contextDirAbsolute, entry.basename);
27930
29055
  if (!fs59.existsSync(filePath)) {
27931
29056
  lists.push({ basename: entry.basename, status: "missing" });
27932
29057
  continue;
@@ -27981,7 +29106,7 @@ var validateReportCompiled;
27981
29106
  function validateReportData(data) {
27982
29107
  if (!validateReportCompiled) {
27983
29108
  const ajv2 = new import__7.default({ allErrors: true, strict: false });
27984
- const schemaPath = path56.join(resolveContextListSchemasDir(), "coordination-duplicates-report.schema.json");
29109
+ const schemaPath = path57.join(resolveContextListSchemasDir(), "coordination-duplicates-report.schema.json");
27985
29110
  const schemaRaw = stripUtf8Bom6(fs59.readFileSync(schemaPath, "utf-8"));
27986
29111
  validateReportCompiled = ajv2.compile(JSON.parse(schemaRaw));
27987
29112
  }
@@ -28000,7 +29125,7 @@ function runCoordinationDuplicatesDiagnosis(input) {
28000
29125
  if (!v.ok) {
28001
29126
  return { ok: false, message: `Report validation failed: ${v.errors.join("; ")}` };
28002
29127
  }
28003
- const outPath = path56.join(input.contextDirAbsolute, COORDINATION_DUPLICATES_REPORT_BASENAME);
29128
+ const outPath = path57.join(input.contextDirAbsolute, COORDINATION_DUPLICATES_REPORT_BASENAME);
28004
29129
  const payload = `${JSON.stringify(report, null, 2)}
28005
29130
  `;
28006
29131
  const tmp = `${outPath}.tmp.${process.pid}`;
@@ -28048,7 +29173,7 @@ function runCoordinationDuplicatesDiagnosis(input) {
28048
29173
 
28049
29174
  // src/pipelines/download/downloadPipeline.ts
28050
29175
  var fs60 = __toESM(require("node:fs"));
28051
- var path57 = __toESM(require("node:path"));
29176
+ var path58 = __toESM(require("node:path"));
28052
29177
  var SRS_DISCOVERY_BATCH_GET_CHUNK_SIZE = 200;
28053
29178
  function isRecord7(v) {
28054
29179
  return Boolean(v) && typeof v === "object" && !Array.isArray(v);
@@ -28138,9 +29263,9 @@ function resolveSafeContextOutputPath(contextDirAbs, nameRaw) {
28138
29263
  return null;
28139
29264
  }
28140
29265
  }
28141
- const resolved = path57.resolve(contextDirAbs, ...segments);
28142
- const rel = path57.relative(contextDirAbs, resolved);
28143
- if (rel.startsWith("..") || path57.isAbsolute(rel)) {
29266
+ const resolved = path58.resolve(contextDirAbs, ...segments);
29267
+ const rel = path58.relative(contextDirAbs, resolved);
29268
+ if (rel.startsWith("..") || path58.isAbsolute(rel)) {
28144
29269
  return null;
28145
29270
  }
28146
29271
  return resolved;
@@ -28156,7 +29281,7 @@ function clearContextDirectoryForCloudReplace(contextDirAbs) {
28156
29281
  if (!fs60.existsSync(contextDirAbs) || !fs60.statSync(contextDirAbs).isDirectory()) {
28157
29282
  return { filesRemoved: 0 };
28158
29283
  }
28159
- const preserveAbs = path57.resolve(contextDirAbs, UPLOAD_TARGET_FILENAME);
29284
+ const preserveAbs = path58.resolve(contextDirAbs, UPLOAD_TARGET_FILENAME);
28160
29285
  const preserveSet = /* @__PURE__ */ new Set();
28161
29286
  if (fs60.existsSync(preserveAbs) && fs60.statSync(preserveAbs).isFile()) {
28162
29287
  preserveSet.add(preserveAbs);
@@ -28170,7 +29295,7 @@ function clearContextDirectoryForCloudReplace(contextDirAbs) {
28170
29295
  return;
28171
29296
  }
28172
29297
  for (const e of entries) {
28173
- const full = path57.join(dir, e.name);
29298
+ const full = path58.join(dir, e.name);
28174
29299
  if (e.isDirectory()) {
28175
29300
  walkRm(full);
28176
29301
  try {
@@ -28178,7 +29303,7 @@ function clearContextDirectoryForCloudReplace(contextDirAbs) {
28178
29303
  } catch {
28179
29304
  }
28180
29305
  } else if (e.isFile()) {
28181
- const abs = path57.resolve(full);
29306
+ const abs = path58.resolve(full);
28182
29307
  if (preserveSet.has(abs)) {
28183
29308
  continue;
28184
29309
  }
@@ -28251,7 +29376,7 @@ async function runDownloadPipeline(opts) {
28251
29376
  failed.push({ id, message: "Missing name on srs_discovery node." });
28252
29377
  continue;
28253
29378
  }
28254
- if (name === UPLOAD_TARGET_FILENAME || path57.basename(name) === UPLOAD_TARGET_FILENAME) {
29379
+ if (name === UPLOAD_TARGET_FILENAME || path58.basename(name) === UPLOAD_TARGET_FILENAME) {
28255
29380
  skipped += 1;
28256
29381
  log?.(`[pipeline:download] skip ${name} (upload target row).`);
28257
29382
  continue;
@@ -28262,7 +29387,7 @@ async function runDownloadPipeline(opts) {
28262
29387
  failed.push({ id, name, message: "Unsafe or invalid name for local path." });
28263
29388
  continue;
28264
29389
  }
28265
- fs60.mkdirSync(path57.dirname(outAbs), { recursive: true });
29390
+ fs60.mkdirSync(path58.dirname(outAbs), { recursive: true });
28266
29391
  const exists = fs60.existsSync(outAbs);
28267
29392
  if (exists && !opts.force && !opts.replaceFromCloud) {
28268
29393
  skipped += 1;
@@ -28345,12 +29470,12 @@ function toFetchErrorMessage(e) {
28345
29470
 
28346
29471
  // src/auth/gluecharmContentNegotiation.ts
28347
29472
  var GLUECHARM_WS_LEGACY_JSON = "application/vnd.gluecharm.v1.ws-legacy+json";
28348
- function pathWithoutQuery(path62) {
28349
- const q = path62.indexOf("?");
28350
- return q === -1 ? path62 : path62.slice(0, q);
29473
+ function pathWithoutQuery(path64) {
29474
+ const q = path64.indexOf("?");
29475
+ return q === -1 ? path64 : path64.slice(0, q);
28351
29476
  }
28352
- function isGluecharmContentApiPath(path62) {
28353
- const p = pathWithoutQuery(path62);
29477
+ function isGluecharmContentApiPath(path64) {
29478
+ const p = pathWithoutQuery(path64);
28354
29479
  return p.startsWith("/api/content/") || p.startsWith("/api/batch/content/");
28355
29480
  }
28356
29481
  function gluecharmContentHeaders(method) {
@@ -28388,7 +29513,7 @@ async function fetchWithTimeout2(url, init, fetchImpl, timeoutMs, externalSignal
28388
29513
  }
28389
29514
  function createAuthenticatedRequestJson(deps) {
28390
29515
  const fetchImpl = deps.fetchImpl ?? fetch;
28391
- async function requestJson(path62, options = {}) {
29516
+ async function requestJson(path64, options = {}) {
28392
29517
  const base = deps.getApiBaseUrl();
28393
29518
  if (!base) {
28394
29519
  const err = { status: 0, message: "easyspecs.apiBaseUrl is not configured." };
@@ -28396,7 +29521,7 @@ function createAuthenticatedRequestJson(deps) {
28396
29521
  }
28397
29522
  const method = options.method ?? "GET";
28398
29523
  const headers = { ...options.headers };
28399
- if (isGluecharmContentApiPath(path62)) {
29524
+ if (isGluecharmContentApiPath(path64)) {
28400
29525
  Object.assign(headers, gluecharmContentHeaders(method));
28401
29526
  } else {
28402
29527
  if (!headers.Accept) {
@@ -28410,7 +29535,7 @@ function createAuthenticatedRequestJson(deps) {
28410
29535
  if (options.withAuth !== false && access) {
28411
29536
  headers.Authorization = `Bearer ${access}`;
28412
29537
  }
28413
- const url = `${base}${path62}`;
29538
+ const url = `${base}${path64}`;
28414
29539
  const timeoutMs = options.timeoutMs ?? API_TIMEOUT_MS;
28415
29540
  const response = await fetchWithTimeout2(
28416
29541
  url,
@@ -28431,7 +29556,7 @@ function createAuthenticatedRequestJson(deps) {
28431
29556
  if (shouldRetryUnauthorized) {
28432
29557
  const refreshed = await deps.refreshSession();
28433
29558
  if (refreshed) {
28434
- return requestJson(path62, { ...options, retryOnUnauthorized: false });
29559
+ return requestJson(path64, { ...options, retryOnUnauthorized: false });
28435
29560
  }
28436
29561
  }
28437
29562
  const fallback = payload == null ? `${response.statusText || "HTTP error"} (response body empty or not JSON)` : "Request failed.";
@@ -28443,13 +29568,13 @@ function createAuthenticatedRequestJson(deps) {
28443
29568
  // src/cli/cliSession.ts
28444
29569
  var fs61 = __toESM(require("node:fs"));
28445
29570
  var os7 = __toESM(require("node:os"));
28446
- var path58 = __toESM(require("node:path"));
29571
+ var path59 = __toESM(require("node:path"));
28447
29572
  function defaultSessionPath() {
28448
- return path58.join(os7.homedir(), ".easyspecs", "cli-session.json");
29573
+ return path59.join(os7.homedir(), ".easyspecs", "cli-session.json");
28449
29574
  }
28450
29575
  var configSessionPath;
28451
29576
  function setCliSessionPathFromConfig(absPath) {
28452
- configSessionPath = absPath?.trim() ? path58.resolve(absPath) : void 0;
29577
+ configSessionPath = absPath?.trim() ? path59.resolve(absPath) : void 0;
28453
29578
  }
28454
29579
  function applyCliSessionPathFromRepoConfig(repoRoot, cfg) {
28455
29580
  const raw = cfg.easyspecs?.cliSessionPath?.trim();
@@ -28457,7 +29582,7 @@ function applyCliSessionPathFromRepoConfig(repoRoot, cfg) {
28457
29582
  setCliSessionPathFromConfig(void 0);
28458
29583
  return;
28459
29584
  }
28460
- const abs = path58.isAbsolute(raw) ? raw : path58.join(repoRoot, raw);
29585
+ const abs = path59.isAbsolute(raw) ? raw : path59.join(repoRoot, raw);
28461
29586
  setCliSessionPathFromConfig(abs);
28462
29587
  }
28463
29588
  function normalizeCliSessionPathForConfig(repoRoot, raw) {
@@ -28465,15 +29590,15 @@ function normalizeCliSessionPathForConfig(repoRoot, raw) {
28465
29590
  if (!t) {
28466
29591
  return "";
28467
29592
  }
28468
- const resolvedRepo = path58.resolve(repoRoot);
28469
- const abs = path58.isAbsolute(t) ? path58.normalize(t) : path58.resolve(resolvedRepo, t);
28470
- const rel = path58.relative(resolvedRepo, abs);
29593
+ const resolvedRepo = path59.resolve(repoRoot);
29594
+ const abs = path59.isAbsolute(t) ? path59.normalize(t) : path59.resolve(resolvedRepo, t);
29595
+ const rel = path59.relative(resolvedRepo, abs);
28471
29596
  if (rel === "") {
28472
29597
  return abs;
28473
29598
  }
28474
- const underRepo = !rel.startsWith("..") && !path58.isAbsolute(rel);
29599
+ const underRepo = !rel.startsWith("..") && !path59.isAbsolute(rel);
28475
29600
  if (underRepo) {
28476
- return rel.split(path58.sep).join("/");
29601
+ return rel.split(path59.sep).join("/");
28477
29602
  }
28478
29603
  return abs;
28479
29604
  }
@@ -28503,7 +29628,7 @@ function readCliSession() {
28503
29628
  }
28504
29629
  function writeCliSession(s) {
28505
29630
  const p = effectiveCliSessionPath();
28506
- fs61.mkdirSync(path58.dirname(p), { recursive: true });
29631
+ fs61.mkdirSync(path59.dirname(p), { recursive: true });
28507
29632
  fs61.writeFileSync(p, `${JSON.stringify(s, null, 2)}
28508
29633
  `, "utf8");
28509
29634
  }
@@ -28517,7 +29642,7 @@ function clearCliSession() {
28517
29642
 
28518
29643
  // src/analysis/acePendingTraces.ts
28519
29644
  var fs62 = __toESM(require("fs"));
28520
- var path59 = __toESM(require("path"));
29645
+ var path60 = __toESM(require("path"));
28521
29646
  function normalizeAceTraceRelativePath(rel) {
28522
29647
  return rel.split(/[/\\]/).join("/");
28523
29648
  }
@@ -28567,7 +29692,7 @@ function listPendingAceTraceFiles(contextDir2, worktreeRoot) {
28567
29692
  const allAbs = listAceTraceFiles(contextDir2);
28568
29693
  const pending = [];
28569
29694
  for (const abs of allAbs) {
28570
- const rel = normalizeAceTraceRelativePath(path59.relative(contextDir2, abs));
29695
+ const rel = normalizeAceTraceRelativePath(path60.relative(contextDir2, abs));
28571
29696
  const v = validateAceJsonFile(abs, traceSchema);
28572
29697
  if (!v.ok) {
28573
29698
  continue;
@@ -28582,7 +29707,7 @@ function listPendingAceTraceFiles(contextDir2, worktreeRoot) {
28582
29707
  }
28583
29708
 
28584
29709
  // src/analysis/aceAutoLearnPool.ts
28585
- var path60 = __toESM(require("path"));
29710
+ var path61 = __toESM(require("path"));
28586
29711
  function clampConcurrency2(n) {
28587
29712
  if (!Number.isFinite(n)) {
28588
29713
  return DEFAULT_MAX_CONCURRENT_AI;
@@ -28617,8 +29742,8 @@ async function runAceAutoLearnPool(p) {
28617
29742
  };
28618
29743
  const currentCap = () => Math.min(staticMaxC, adaptiveMax);
28619
29744
  let wake;
28620
- const waitTurn = () => new Promise((resolve24) => {
28621
- wake = resolve24;
29745
+ const waitTurn = () => new Promise((resolve25) => {
29746
+ wake = resolve25;
28622
29747
  });
28623
29748
  const pump = () => {
28624
29749
  wake?.();
@@ -28640,7 +29765,7 @@ async function runAceAutoLearnPool(p) {
28640
29765
  poolAbortListenerRegistered = true;
28641
29766
  }
28642
29767
  }
28643
- const traceRel = (abs) => path60.relative(contextDir2, abs).split(path60.sep).join("/");
29768
+ const traceRel = (abs) => path61.relative(contextDir2, abs).split(path61.sep).join("/");
28644
29769
  const runOne = async (traceAbs) => {
28645
29770
  if (abortSignal?.aborted) {
28646
29771
  active -= 1;
@@ -29173,15 +30298,15 @@ function formatCliStderrLine(line, useAnsi) {
29173
30298
  // src/cli/main.ts
29174
30299
  function resolveCliPackageVersion() {
29175
30300
  if (true) {
29176
- return "0.3.5";
30301
+ return "0.3.6";
29177
30302
  }
29178
30303
  const candidates = [
29179
- path61.join(__dirname, "..", "package.json"),
29180
- path61.join(__dirname, "..", "..", "packages", "cli", "package.json")
30304
+ path63.join(__dirname, "..", "package.json"),
30305
+ path63.join(__dirname, "..", "..", "packages", "cli", "package.json")
29181
30306
  ];
29182
30307
  for (const pkgPath of candidates) {
29183
30308
  try {
29184
- const raw = fs63.readFileSync(pkgPath, "utf8");
30309
+ const raw = fs64.readFileSync(pkgPath, "utf8");
29185
30310
  const j = JSON.parse(raw);
29186
30311
  if (typeof j.version === "string") {
29187
30312
  return j.version;
@@ -29333,23 +30458,23 @@ function resolveAnalysisRoot(repoRoot, rootKind, worktreePath) {
29333
30458
  return repoRoot;
29334
30459
  }
29335
30460
  const wt = worktreePath?.trim();
29336
- if (wt && fs63.existsSync(path61.join(wt, ".git"))) {
29337
- return path61.resolve(wt);
30461
+ if (wt && fs64.existsSync(path63.join(wt, ".git"))) {
30462
+ return path63.resolve(wt);
29338
30463
  }
29339
30464
  throw new Error("worktree mode requires --worktree <path> to an existing analysis checkout.");
29340
30465
  }
29341
30466
  function resolveAdHocCheckoutRoot(_repoRoot, storage, worktreeFlag) {
29342
30467
  const w = worktreeFlag?.trim();
29343
30468
  if (w) {
29344
- const abs = path61.resolve(w);
29345
- if (fs63.existsSync(path61.join(abs, ".git"))) {
30469
+ const abs = path63.resolve(w);
30470
+ if (fs64.existsSync(path63.join(abs, ".git"))) {
29346
30471
  return abs;
29347
30472
  }
29348
30473
  throw new Error(`Invalid --worktree (not a git checkout): ${abs}`);
29349
30474
  }
29350
30475
  const snap = readAnalysisWorkspaceSnapshot(storage);
29351
30476
  const p = snap?.adHocWorktreePath?.trim();
29352
- if (p && fs63.existsSync(path61.join(p, ".git"))) {
30477
+ if (p && fs64.existsSync(path63.join(p, ".git"))) {
29353
30478
  return p;
29354
30479
  }
29355
30480
  throw new Error("No analysis checkout: run `easyspecs-cli run synthesis` first or pass `--worktree <path>`.");
@@ -29376,7 +30501,7 @@ async function runResumeRemediationPool(storage, repoRoot, analysisRoot, merged,
29376
30501
  requireOpenCode(merged, flags);
29377
30502
  const agentsDir = resolveOpenCodeAgentsDir(repoRoot, repoConfig);
29378
30503
  assertAgentsDirExists(agentsDir);
29379
- const ctxDir = path61.join(analysisRoot, ".gluecharm", "context");
30504
+ const ctxDir = path63.join(analysisRoot, ".gluecharm", "context");
29380
30505
  const snap = readArtefactRunSnapshot(storage);
29381
30506
  const rows = listMissingWorkstations(ctxDir, analysisRoot, snap);
29382
30507
  if (rows.length === 0) {
@@ -29394,7 +30519,7 @@ async function runResumeRemediationPool(storage, repoRoot, analysisRoot, merged,
29394
30519
  storageContext: storage,
29395
30520
  repositoryRoot: repoRoot,
29396
30521
  worktreeRoot: analysisRoot,
29397
- workspaceLabel: path61.basename(analysisRoot),
30522
+ workspaceLabel: path63.basename(analysisRoot),
29398
30523
  oc,
29399
30524
  log: (line) => logErr(flags, line),
29400
30525
  abortSignal: void 0,
@@ -29571,17 +30696,17 @@ async function main() {
29571
30696
  { easyspecs: { defaultGitRemoteUrl: url } },
29572
30697
  { warnMigration: (m) => logErr(flags, `[EasySpecs] ${m}`) }
29573
30698
  );
29574
- const path62 = easyspecsConfigPath(repoRoot);
30699
+ const path64 = easyspecsConfigPath(repoRoot);
29575
30700
  if (flags.json) {
29576
30701
  printJsonLine({
29577
30702
  command: "config set-git-remote",
29578
30703
  durationMs: Date.now() - t0,
29579
30704
  ok: true,
29580
- path: path62,
30705
+ path: path64,
29581
30706
  defaultGitRemoteUrl: cfg.easyspecs?.defaultGitRemoteUrl ?? ""
29582
30707
  });
29583
30708
  } else {
29584
- console.log(`Updated ${path62} \u2014 easyspecs.defaultGitRemoteUrl`);
30709
+ console.log(`Updated ${path64} \u2014 easyspecs.defaultGitRemoteUrl`);
29585
30710
  }
29586
30711
  process.exit(ExitCode.ok);
29587
30712
  } catch (e) {
@@ -29614,7 +30739,7 @@ async function main() {
29614
30739
  applyCliSessionPathFromRepoConfig(repoRoot, repoConfig);
29615
30740
  if (flags.sessionPath?.trim()) {
29616
30741
  const sp = flags.sessionPath.trim();
29617
- const abs = path61.isAbsolute(sp) ? path61.normalize(sp) : path61.resolve(repoRoot, sp);
30742
+ const abs = path63.isAbsolute(sp) ? path63.normalize(sp) : path63.resolve(repoRoot, sp);
29618
30743
  setCliSessionPathFromConfig(abs);
29619
30744
  }
29620
30745
  const apiResolved = initApiBaseUrlForCli(repoRoot, flags, repoConfig);
@@ -29855,7 +30980,7 @@ async function main() {
29855
30980
  const result = await runSynthesisPipeline(
29856
30981
  storage,
29857
30982
  repoRoot,
29858
- path61.basename(repoRoot),
30983
+ path63.basename(repoRoot),
29859
30984
  agentsDir,
29860
30985
  {
29861
30986
  ...merged.pipelineOpenCode
@@ -29910,7 +31035,7 @@ async function main() {
29910
31035
  if (sub === "reference-coverage") {
29911
31036
  const rootAbs = resolveAnalysisRoot(repoRoot, rootKind, worktree);
29912
31037
  requireMinimalGluecharmLayoutAt(rootAbs);
29913
- const contextDir2 = path61.join(rootAbs, ".gluecharm", "context");
31038
+ const contextDir2 = path63.join(rootAbs, ".gluecharm", "context");
29914
31039
  const res = runCoveragePipeline({
29915
31040
  repositoryRootAbs: rootAbs,
29916
31041
  contextDirAbs: contextDir2,
@@ -29933,7 +31058,7 @@ async function main() {
29933
31058
  if (sub === "coordination-duplicates") {
29934
31059
  const rootAbs = resolveAnalysisRoot(repoRoot, rootKind, worktree);
29935
31060
  requireMinimalGluecharmLayoutAt(rootAbs);
29936
- const contextDir2 = path61.join(rootAbs, ".gluecharm", "context");
31061
+ const contextDir2 = path63.join(rootAbs, ".gluecharm", "context");
29937
31062
  const res = runCoordinationDuplicatesDiagnosis({
29938
31063
  contextDirAbsolute: contextDir2,
29939
31064
  sourceRoot: rootKind === "worktree" ? "worktree" : "workspace"
@@ -29969,7 +31094,7 @@ async function main() {
29969
31094
  if (sub === "missing-artefacts") {
29970
31095
  const rootAbs = resolveAnalysisRoot(repoRoot, rootKind, worktree);
29971
31096
  requireMinimalGluecharmLayoutAt(rootAbs);
29972
- const ctxDir = path61.join(rootAbs, ".gluecharm", "context");
31097
+ const ctxDir = path63.join(rootAbs, ".gluecharm", "context");
29973
31098
  const storage = createFileBackedWorkspaceState(repoRoot);
29974
31099
  const snap = readArtefactRunSnapshot(storage);
29975
31100
  const rows = listMissingWorkstations(ctxDir, rootAbs, snap);
@@ -30019,7 +31144,7 @@ async function main() {
30019
31144
  });
30020
31145
  const bad = poolRes.cancelled || poolRes.failures > 0;
30021
31146
  if (!bad) {
30022
- const ctxDir = path61.join(analysisRoot, ".gluecharm", "context");
31147
+ const ctxDir = path63.join(analysisRoot, ".gluecharm", "context");
30023
31148
  const lg = runLinkMappingPipeline(ctxDir, { log: (line) => logErr(flags, line) });
30024
31149
  if (!lg.ok) {
30025
31150
  finish(OsExit.diagnoseZeroReferenceLinkMapping, {
@@ -30042,6 +31167,23 @@ async function main() {
30042
31167
  }
30043
31168
  finish(ExitCode.usage, { ok: false, error: `Unknown diagnose subcommand: ${sub ?? ""}` });
30044
31169
  }
31170
+ if (pos[0] === "context" && pos[1] === "migrate-srs52") {
31171
+ const tail = pos.slice(2);
31172
+ const { rootKind, worktree: wtFromRoot } = parseTailFlags(tail);
31173
+ const { worktree: wtExplicit } = parseWorktreeFlag(tail);
31174
+ const worktree = wtExplicit ?? wtFromRoot;
31175
+ const rootAbs = resolveAnalysisRoot(repoRoot, rootKind, worktree);
31176
+ requireMinimalGluecharmLayoutAt(rootAbs);
31177
+ const contextDir2 = path63.join(rootAbs, ".gluecharm", "context");
31178
+ const dryRun = tail.includes("--dry-run");
31179
+ const force = tail.includes("--force");
31180
+ const { migrateSrs52Context: migrateSrs52Context2 } = await Promise.resolve().then(() => (init_migrateSrs52Context(), migrateSrs52Context_exports));
31181
+ const res = migrateSrs52Context2({ contextDir: contextDir2, dryRun, force });
31182
+ if (!res.ok) {
31183
+ finish(ExitCode.usage, { contextDir: contextDir2, dryRun, ...res });
31184
+ }
31185
+ finish(ExitCode.ok, { contextDir: contextDir2, dryRun, ...res });
31186
+ }
30045
31187
  if (pos[0] === "context" && pos[1] === "link-graph") {
30046
31188
  const tail = pos.slice(2);
30047
31189
  const { rootKind, worktree: wtFromRoot } = parseTailFlags(tail);
@@ -30049,7 +31191,7 @@ async function main() {
30049
31191
  const worktree = wtExplicit ?? wtFromRoot;
30050
31192
  const rootAbs = resolveAnalysisRoot(repoRoot, rootKind, worktree);
30051
31193
  requireMinimalGluecharmLayoutAt(rootAbs);
30052
- const contextDir2 = path61.join(rootAbs, ".gluecharm", "context");
31194
+ const contextDir2 = path63.join(rootAbs, ".gluecharm", "context");
30053
31195
  const res = runLinkMappingPipeline(contextDir2, {
30054
31196
  log: (line) => logErr(flags, line)
30055
31197
  });
@@ -30246,13 +31388,13 @@ async function main() {
30246
31388
  const snap = readAnalysisWorkspaceSnapshot(storage);
30247
31389
  const wt = snap?.adHocWorktreePath?.trim();
30248
31390
  if (wt) {
30249
- const sourceCtx = path61.join(wt, ".gluecharm", "context");
30250
- if (path61.resolve(wt) === path61.resolve(repoRoot)) {
31391
+ const sourceCtx = path63.join(wt, ".gluecharm", "context");
31392
+ if (path63.resolve(wt) === path63.resolve(repoRoot)) {
30251
31393
  logErr(
30252
31394
  flags,
30253
31395
  "[pipeline:analysis] promote skipped (in-place: analysis checkout is the repository root)"
30254
31396
  );
30255
- } else if (fs63.existsSync(sourceCtx)) {
31397
+ } else if (fs64.existsSync(sourceCtx)) {
30256
31398
  const n = promoteContextDirectoryToWorkspaceFs(sourceCtx, repoRoot);
30257
31399
  logErr(flags, `[pipeline:analysis] promoted ${String(n.filesCopied)} file(s) \u2192 ${repoRoot}`);
30258
31400
  } else {
@@ -30350,10 +31492,10 @@ async function main() {
30350
31492
  finish(ExitCode.usage, { ok: false, error: `unknown download context flag: ${a}` });
30351
31493
  }
30352
31494
  requireMinimalGluecharmLayoutAt(repoRoot);
30353
- const ctxDir = path61.resolve(path61.join(repoRoot, ".gluecharm", "context"));
30354
- const gluecharmParent = path61.dirname(ctxDir);
30355
- if (path61.basename(gluecharmParent) === ".gluecharm" && path61.basename(ctxDir) === "context") {
30356
- requireMinimalGluecharmLayoutAt(path61.dirname(gluecharmParent));
31495
+ const ctxDir = path63.resolve(path63.join(repoRoot, ".gluecharm", "context"));
31496
+ const gluecharmParent = path63.dirname(ctxDir);
31497
+ if (path63.basename(gluecharmParent) === ".gluecharm" && path63.basename(ctxDir) === "context") {
31498
+ requireMinimalGluecharmLayoutAt(path63.dirname(gluecharmParent));
30357
31499
  }
30358
31500
  const appIdRaw = getEasyspecsProjectIdFromRepoConfig(repoConfig)?.trim();
30359
31501
  if (!appIdRaw) {
@@ -30436,16 +31578,16 @@ async function main() {
30436
31578
  }
30437
31579
  const sess = sessRaw;
30438
31580
  requireMinimalGluecharmLayoutAt(repoRoot);
30439
- let ctxDir = path61.join(repoRoot, ".gluecharm", "context");
31581
+ let ctxDir = path63.join(repoRoot, ".gluecharm", "context");
30440
31582
  if (pos[1] === "republish") {
30441
31583
  const fromCfg = repoConfig.easyspecs?.upload?.contextDirectory?.trim();
30442
- const resolvedOverride = fromCfg && fromCfg.length > 0 ? path61.isAbsolute(fromCfg) ? path61.normalize(fromCfg) : path61.resolve(repoRoot, fromCfg) : "";
30443
- if (resolvedOverride && fs63.existsSync(path61.join(resolvedOverride, ".."))) {
31584
+ const resolvedOverride = fromCfg && fromCfg.length > 0 ? path63.isAbsolute(fromCfg) ? path63.normalize(fromCfg) : path63.resolve(repoRoot, fromCfg) : "";
31585
+ if (resolvedOverride && fs64.existsSync(path63.join(resolvedOverride, ".."))) {
30444
31586
  ctxDir = resolvedOverride;
30445
31587
  } else {
30446
31588
  const storage = createFileBackedWorkspaceState(repoRoot);
30447
31589
  const snap = readAnalysisWorkspaceSnapshot(storage);
30448
- const wt = snap?.adHocWorktreePath && fs63.existsSync(path61.join(snap.adHocWorktreePath, ".gluecharm", "context")) ? path61.join(snap.adHocWorktreePath, ".gluecharm", "context") : "";
31590
+ const wt = snap?.adHocWorktreePath && fs64.existsSync(path63.join(snap.adHocWorktreePath, ".gluecharm", "context")) ? path63.join(snap.adHocWorktreePath, ".gluecharm", "context") : "";
30449
31591
  if (!wt) {
30450
31592
  finish(ExitCode.misconfiguration, {
30451
31593
  ok: false,
@@ -30455,10 +31597,10 @@ async function main() {
30455
31597
  ctxDir = wt;
30456
31598
  }
30457
31599
  }
30458
- const ctxResolved = path61.resolve(ctxDir);
30459
- const gluecharmParent = path61.dirname(ctxResolved);
30460
- if (path61.basename(gluecharmParent) === ".gluecharm" && path61.basename(ctxResolved) === "context") {
30461
- requireMinimalGluecharmLayoutAt(path61.dirname(gluecharmParent));
31600
+ const ctxResolved = path63.resolve(ctxDir);
31601
+ const gluecharmParent = path63.dirname(ctxResolved);
31602
+ if (path63.basename(gluecharmParent) === ".gluecharm" && path63.basename(ctxResolved) === "context") {
31603
+ requireMinimalGluecharmLayoutAt(path63.dirname(gluecharmParent));
30462
31604
  }
30463
31605
  const appIdRaw = getEasyspecsProjectIdFromRepoConfig(repoConfig)?.trim();
30464
31606
  if (!appIdRaw) {
@@ -30562,18 +31704,18 @@ async function main() {
30562
31704
  finish(failed ? ExitCode.upload : ExitCode.ok, primary);
30563
31705
  }
30564
31706
  if (pos[0] === "ace" && pos[1] === "clear") {
30565
- const learnings = path61.join(repoRoot, ".gluecharm", "context", "learnings");
30566
- if (!fs63.existsSync(learnings)) {
31707
+ const learnings = path63.join(repoRoot, ".gluecharm", "context", "learnings");
31708
+ if (!fs64.existsSync(learnings)) {
30567
31709
  finish(ExitCode.ok, { ok: true, message: "nothing to clear" });
30568
31710
  }
30569
- fs63.rmSync(learnings, { recursive: true, force: true });
31711
+ fs64.rmSync(learnings, { recursive: true, force: true });
30570
31712
  finish(ExitCode.ok, { ok: true, message: `cleared ${learnings}` });
30571
31713
  }
30572
31714
  if (pos[0] === "ace" && pos[1] === "learn") {
30573
31715
  requireOpenCode(merged, flags);
30574
31716
  const { worktree } = parseWorktreeFlag(pos.slice(2));
30575
- const root = worktree && fs63.existsSync(path61.join(worktree, ".opencode", "schemas", "ace")) ? worktree : repoRoot;
30576
- const contextDir2 = path61.join(root, ".gluecharm", "context");
31717
+ const root = worktree && fs64.existsSync(path63.join(worktree, ".opencode", "schemas", "ace")) ? worktree : repoRoot;
31718
+ const contextDir2 = path63.join(root, ".gluecharm", "context");
30577
31719
  const traces = listAceTraceFiles(contextDir2);
30578
31720
  if (traces.length === 0) {
30579
31721
  finish(ExitCode.ok, { ok: true, message: "no traces", traceCount: 0 });
@@ -30597,8 +31739,8 @@ async function main() {
30597
31739
  if (pos[0] === "ace" && pos[1] === "auto-learn") {
30598
31740
  requireOpenCode(merged, flags);
30599
31741
  const { worktree } = parseWorktreeFlag(pos.slice(2));
30600
- const root = worktree && fs63.existsSync(path61.join(worktree, ".git")) ? worktree : repoRoot;
30601
- const contextDir2 = path61.join(root, ".gluecharm", "context");
31742
+ const root = worktree && fs64.existsSync(path63.join(worktree, ".git")) ? worktree : repoRoot;
31743
+ const contextDir2 = path63.join(root, ".gluecharm", "context");
30602
31744
  const pending = listPendingAceTraceFiles(contextDir2, root);
30603
31745
  if (pending.length === 0) {
30604
31746
  finish(ExitCode.ok, { ok: true, pending: 0 });