@biffo/cli 0.303.1 → 0.303.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10920,6 +10920,239 @@ Already accepted? List it in docs/ADR/${ALLOWLIST_FILENAME} instead of leaving t
10920
10920
  console.log("\u2713 ADR numbering guard: OK");
10921
10921
  }
10922
10922
 
10923
+ // src/lib/api-gateway-integration-guard.ts
10924
+ import { readFileSync as readFileSync30, readdirSync as readdirSync16, statSync as statSync9 } from "fs";
10925
+ import { join as join41 } from "path";
10926
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
10927
+ var MODULE_TYPE = "module";
10928
+ var INTEGRATION_TYPE = "aws_apigatewayv2_integration";
10929
+ function lineNumber(text, index) {
10930
+ let count = 1;
10931
+ for (let i = 0; i < index && i < text.length; i++) {
10932
+ if (text[i] === "\n") count += 1;
10933
+ }
10934
+ return count;
10935
+ }
10936
+ function balancedBraceSpan(text, openIndex) {
10937
+ let depth = 0;
10938
+ for (let i = openIndex; i < text.length; i++) {
10939
+ if (text[i] === "{") depth += 1;
10940
+ else if (text[i] === "}") {
10941
+ depth -= 1;
10942
+ if (depth === 0) return i;
10943
+ }
10944
+ }
10945
+ return null;
10946
+ }
10947
+ function findResourceBlocks(text, file, resourceType) {
10948
+ const blocks = [];
10949
+ const pattern = new RegExp(`resource\\s+"${resourceType}"\\s+"([\\w-]+)"\\s*\\{`, "g");
10950
+ let m;
10951
+ while ((m = pattern.exec(text)) !== null) {
10952
+ const name = m[1];
10953
+ const line = lineNumber(text, m.index);
10954
+ const openIndex = m.index + m[0].length - 1;
10955
+ const closeIndex = balancedBraceSpan(text, openIndex);
10956
+ if (closeIndex === null) {
10957
+ blocks.push({ file, line, type: resourceType, name, body: null });
10958
+ continue;
10959
+ }
10960
+ blocks.push({
10961
+ file,
10962
+ line,
10963
+ type: resourceType,
10964
+ name,
10965
+ body: text.slice(openIndex + 1, closeIndex)
10966
+ });
10967
+ }
10968
+ return blocks;
10969
+ }
10970
+ function findModuleBlocks(text, file) {
10971
+ const blocks = [];
10972
+ const pattern = /module\s+"([\w-]+)"\s*\{/g;
10973
+ let m;
10974
+ while ((m = pattern.exec(text)) !== null) {
10975
+ const name = m[1];
10976
+ const line = lineNumber(text, m.index);
10977
+ const openIndex = m.index + m[0].length - 1;
10978
+ const closeIndex = balancedBraceSpan(text, openIndex);
10979
+ if (closeIndex === null) {
10980
+ blocks.push({ file, line, type: MODULE_TYPE, name, body: null });
10981
+ continue;
10982
+ }
10983
+ blocks.push({
10984
+ file,
10985
+ line,
10986
+ type: MODULE_TYPE,
10987
+ name,
10988
+ body: text.slice(openIndex + 1, closeIndex)
10989
+ });
10990
+ }
10991
+ return blocks;
10992
+ }
10993
+ function countRawResourceDeclarations(text, resourceType) {
10994
+ const needle = resourceType === MODULE_TYPE ? 'module "' : `resource "${resourceType}"`;
10995
+ return text.split(needle).length - 1;
10996
+ }
10997
+ function walkTerraformFiles(root) {
10998
+ const out = [];
10999
+ const walk2 = (dir) => {
11000
+ let entries;
11001
+ try {
11002
+ entries = readdirSync16(dir);
11003
+ } catch {
11004
+ return;
11005
+ }
11006
+ for (const entry of entries) {
11007
+ const p = join41(dir, entry);
11008
+ let st;
11009
+ try {
11010
+ st = statSync9(p);
11011
+ } catch {
11012
+ continue;
11013
+ }
11014
+ if (st.isDirectory()) {
11015
+ if (SKIP_DIRS.has(entry)) continue;
11016
+ walk2(p);
11017
+ continue;
11018
+ }
11019
+ if (entry.endsWith(".tf")) out.push(p);
11020
+ }
11021
+ };
11022
+ walk2(root);
11023
+ return out.sort();
11024
+ }
11025
+ function isApiGatewaySource(body) {
11026
+ const m = /source\s*=\s*"([^"]+)"/.exec(body);
11027
+ if (!m) return false;
11028
+ return /(^|\/)modules\/cloud\/aws\/api-gateway\/?$/.test(m[1].trim());
11029
+ }
11030
+ function protectedFunctionModuleName(body) {
11031
+ const m = /lambda_function_arn\s*=\s*module\.([\w-]+)\.function_arn\b/.exec(body);
11032
+ return m ? m[1] : null;
11033
+ }
11034
+ function integrationUriOf(body) {
11035
+ const m = /integration_uri\s*=\s*(.+)/.exec(body);
11036
+ if (!m) return null;
11037
+ return m[1].trim().replace(/^"\$\{(.+)\}"$/, "$1").replace(/^"(.+)"$/, "$1").trim();
11038
+ }
11039
+ function auditApiGatewayIntegrations(root) {
11040
+ const files = walkTerraformFiles(root);
11041
+ if (files.length === 0) {
11042
+ throw new Error(
11043
+ `auditApiGatewayIntegrations: no .tf files found under ${root} \u2014 the guard cannot verify anything against input it cannot see. This is a hard failure, not "0 violations": fix the path, do not treat an empty scan as a clean pass.`
11044
+ );
11045
+ }
11046
+ const moduleBlocks = [];
11047
+ const integrationBlocks = [];
11048
+ let rawModuleCount = 0;
11049
+ let rawIntegrationCount = 0;
11050
+ for (const file of files) {
11051
+ const text = readFileSync30(file, "utf8");
11052
+ rawModuleCount += countRawResourceDeclarations(text, MODULE_TYPE);
11053
+ rawIntegrationCount += countRawResourceDeclarations(text, INTEGRATION_TYPE);
11054
+ moduleBlocks.push(...findModuleBlocks(text, file));
11055
+ integrationBlocks.push(...findResourceBlocks(text, file, INTEGRATION_TYPE));
11056
+ }
11057
+ const unterminatedBlocks = [...moduleBlocks, ...integrationBlocks].filter((b) => b.body === null);
11058
+ const moduleBlind = rawModuleCount > 0 && moduleBlocks.length === 0;
11059
+ const integrationBlind = rawIntegrationCount > 0 && integrationBlocks.length === 0;
11060
+ const protectedLambdas = [];
11061
+ for (const block of moduleBlocks) {
11062
+ if (block.body === null) continue;
11063
+ if (!isApiGatewaySource(block.body)) continue;
11064
+ if (block.file.split("/").includes("_skeletons")) continue;
11065
+ const functionModuleName = protectedFunctionModuleName(block.body);
11066
+ if (functionModuleName === null) continue;
11067
+ protectedLambdas.push({
11068
+ functionModuleName,
11069
+ gatewayModuleName: block.name,
11070
+ file: block.file,
11071
+ line: block.line
11072
+ });
11073
+ }
11074
+ const protectedNames = new Set(protectedLambdas.map((p) => p.functionModuleName));
11075
+ const integrations = [];
11076
+ for (const block of integrationBlocks) {
11077
+ if (block.body === null) continue;
11078
+ integrations.push({
11079
+ file: block.file,
11080
+ line: block.line,
11081
+ name: block.name,
11082
+ integrationUri: integrationUriOf(block.body)
11083
+ });
11084
+ }
11085
+ const violations = [];
11086
+ for (const integration of integrations) {
11087
+ if (integration.integrationUri === null) continue;
11088
+ const m = /^module\.([\w-]+)\.function_arn$/.exec(integration.integrationUri);
11089
+ if (!m) continue;
11090
+ const targetModuleName = m[1];
11091
+ if (!protectedNames.has(targetModuleName)) continue;
11092
+ const owner = protectedLambdas.find((p) => p.functionModuleName === targetModuleName);
11093
+ violations.push({
11094
+ file: integration.file,
11095
+ line: integration.line,
11096
+ integrationName: integration.name,
11097
+ targetModuleName,
11098
+ reason: `aws_apigatewayv2_integration.${integration.name} targets module.${targetModuleName}.function_arn (unqualified), but module.${owner?.gatewayModuleName ?? "<gateway>"} already grants that Lambda's "live" alias \u2014 not the unqualified function \u2014 invoke access (#1747). This integration will fail closed with a generic API Gateway 500 at runtime, invisible to terraform plan/apply on either side. Use module.${owner?.gatewayModuleName ?? "<gateway>"}.lambda_integration_uri instead (biffo-template#1900).`
11099
+ });
11100
+ }
11101
+ const ok = !moduleBlind && !integrationBlind && unterminatedBlocks.length === 0 && violations.length === 0;
11102
+ const summary = `scanned ${files.length} .tf file(s) under ${root}; ${moduleBlocks.length} module block(s) found (${protectedLambdas.length} api-gateway instance(s) fronting an alias-qualified Lambda), ${integrationBlocks.length} ${INTEGRATION_TYPE} block(s) found; ${violations.length} mis-qualified, ${unterminatedBlocks.length} unterminated.`;
11103
+ return {
11104
+ filesScanned: files.length,
11105
+ moduleBlocksFound: moduleBlocks.length,
11106
+ integrationBlocksFound: integrationBlocks.length,
11107
+ protectedLambdas,
11108
+ integrations,
11109
+ unterminatedBlocks,
11110
+ violations,
11111
+ moduleBlind,
11112
+ integrationBlind,
11113
+ ok,
11114
+ summary
11115
+ };
11116
+ }
11117
+
11118
+ // src/scripts/check-api-gateway-integration.ts
11119
+ async function runApiGatewayIntegrationCheck() {
11120
+ const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11121
+ let report;
11122
+ try {
11123
+ report = auditApiGatewayIntegrations(root);
11124
+ } catch (err) {
11125
+ console.error("\u2717 API Gateway integration-qualifier guard: could not run\n");
11126
+ console.error(err instanceof Error ? err.message : String(err));
11127
+ process.exit(1);
11128
+ }
11129
+ console.log(`scanned ${report.filesScanned} .tf file(s) under ${root}`);
11130
+ if (!report.ok) {
11131
+ console.error("\u2717 API Gateway integration-qualifier guard: mis-qualified integration(s) found\n");
11132
+ if (report.moduleBlind) {
11133
+ console.error(
11134
+ ' BLIND (module): raw source contains `module "..."` blocks but the block extractor found none \u2014 the extractor broke, this is not evidence there are no modules.'
11135
+ );
11136
+ }
11137
+ if (report.integrationBlind) {
11138
+ console.error(
11139
+ " BLIND (aws_apigatewayv2_integration): raw source contains this resource type but the block extractor found none \u2014 the extractor broke, this is not evidence there are no integrations."
11140
+ );
11141
+ }
11142
+ for (const b of report.unterminatedBlocks) {
11143
+ console.error(
11144
+ ` UNTERMINATED ${b.file}:${b.line} ${b.type} "${b.name}" never closes \u2014 cannot verify what it targets or grants.`
11145
+ );
11146
+ }
11147
+ for (const v of report.violations) {
11148
+ console.error(` MIS-QUALIFIED ${v.file}:${v.line} ${v.reason}`);
11149
+ }
11150
+ console.error("\nSee biffo-template#1900.");
11151
+ process.exit(1);
11152
+ }
11153
+ console.log(`\u2713 API Gateway integration-qualifier guard: ${report.summary}`);
11154
+ }
11155
+
10923
11156
  // src/scripts/check-branch-protection.ts
10924
11157
  import { Octokit as Octokit2 } from "@octokit/rest";
10925
11158
 
@@ -11180,18 +11413,18 @@ async function runBranchProtectionCheck(explicitRepo, options = {}) {
11180
11413
  }
11181
11414
 
11182
11415
  // src/lib/claim-invocation-parity.ts
11183
- import { existsSync as existsSync40, readFileSync as readFileSync30, readdirSync as readdirSync16 } from "fs";
11184
- import { join as join41 } from "path";
11416
+ import { existsSync as existsSync40, readFileSync as readFileSync31, readdirSync as readdirSync17 } from "fs";
11417
+ import { join as join42 } from "path";
11185
11418
  function distributedAgentsDocs(root) {
11186
11419
  const docs = [];
11187
- const own = join41(root, "AGENTS.md");
11188
- if (existsSync40(own)) docs.push({ path: "AGENTS.md", text: readFileSync30(own, "utf8") });
11189
- const skeletons = join41(root, "_skeletons");
11420
+ const own = join42(root, "AGENTS.md");
11421
+ if (existsSync40(own)) docs.push({ path: "AGENTS.md", text: readFileSync31(own, "utf8") });
11422
+ const skeletons = join42(root, "_skeletons");
11190
11423
  if (existsSync40(skeletons)) {
11191
- for (const name of readdirSync16(skeletons).sort()) {
11192
- const abs = join41(skeletons, name, "AGENTS.md");
11424
+ for (const name of readdirSync17(skeletons).sort()) {
11425
+ const abs = join42(skeletons, name, "AGENTS.md");
11193
11426
  if (!existsSync40(abs)) continue;
11194
- docs.push({ path: `_skeletons/${name}/AGENTS.md`, text: readFileSync30(abs, "utf8") });
11427
+ docs.push({ path: `_skeletons/${name}/AGENTS.md`, text: readFileSync31(abs, "utf8") });
11195
11428
  }
11196
11429
  }
11197
11430
  return docs;
@@ -11324,12 +11557,12 @@ async function runClaimInvocationCheck() {
11324
11557
 
11325
11558
  // src/scripts/check-codeql-suppression.ts
11326
11559
  import { existsSync as existsSync41 } from "fs";
11327
- import { join as join43, relative as relative8 } from "path";
11560
+ import { join as join44, relative as relative8 } from "path";
11328
11561
 
11329
11562
  // src/lib/codeql-suppression-guard.ts
11330
- import { readdirSync as readdirSync17, readFileSync as readFileSync31, statSync as statSync9 } from "fs";
11331
- import { join as join42 } from "path";
11332
- var SKIP_DIRS = /* @__PURE__ */ new Set([
11563
+ import { readdirSync as readdirSync18, readFileSync as readFileSync32, statSync as statSync10 } from "fs";
11564
+ import { join as join43 } from "path";
11565
+ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
11333
11566
  ".git",
11334
11567
  ".worktrees",
11335
11568
  "node_modules",
@@ -11354,20 +11587,20 @@ function walkSourceFiles(root) {
11354
11587
  const walk2 = (dir) => {
11355
11588
  let entries;
11356
11589
  try {
11357
- entries = readdirSync17(dir);
11590
+ entries = readdirSync18(dir);
11358
11591
  } catch {
11359
11592
  return;
11360
11593
  }
11361
11594
  for (const entry of entries) {
11362
- const p = join42(dir, entry);
11595
+ const p = join43(dir, entry);
11363
11596
  let st;
11364
11597
  try {
11365
- st = statSync9(p);
11598
+ st = statSync10(p);
11366
11599
  } catch {
11367
11600
  continue;
11368
11601
  }
11369
11602
  if (st.isDirectory()) {
11370
- if (SKIP_DIRS.has(entry)) continue;
11603
+ if (SKIP_DIRS2.has(entry)) continue;
11371
11604
  walk2(p);
11372
11605
  continue;
11373
11606
  }
@@ -11385,7 +11618,7 @@ function countSourceFiles(root) {
11385
11618
  function sweepCodeqlSuppressionComments(root) {
11386
11619
  const hits = [];
11387
11620
  for (const path of walkSourceFiles(root)) {
11388
- const text = readFileSync31(path, "utf8");
11621
+ const text = readFileSync32(path, "utf8");
11389
11622
  for (const line of findCodeqlSuppressionComments(text)) {
11390
11623
  hits.push({ path, line, text: text.split("\n")[line - 1] ?? "" });
11391
11624
  }
@@ -11396,7 +11629,7 @@ function sweepCodeqlSuppressionComments(root) {
11396
11629
  // src/scripts/check-codeql-suppression.ts
11397
11630
  async function runCodeqlSuppressionCheck() {
11398
11631
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11399
- const scanRoot = join43(root, "cli", "src");
11632
+ const scanRoot = join44(root, "cli", "src");
11400
11633
  if (!existsSync41(scanRoot)) {
11401
11634
  console.log(
11402
11635
  "\u2014 codeql-suppression guard: skipped \u2014 no cli/src in this repo, so there is no CLI source to scan."
@@ -11421,8 +11654,8 @@ async function runCodeqlSuppressionCheck() {
11421
11654
  }
11422
11655
 
11423
11656
  // src/lib/cognito-invite-template-guard.ts
11424
- import { readdirSync as readdirSync18, readFileSync as readFileSync32, statSync as statSync10 } from "fs";
11425
- import { join as join44 } from "path";
11657
+ import { readdirSync as readdirSync19, readFileSync as readFileSync33, statSync as statSync11 } from "fs";
11658
+ import { join as join45 } from "path";
11426
11659
  var REQUIRED_INVITE_MEMBERS = ["email_subject", "email_message", "sms_message"];
11427
11660
  var REQUIRED_INVITE_PLACEHOLDERS = ["{username}", "{####}"];
11428
11661
  var PLACEHOLDER_MEMBERS = ["email_message", "sms_message"];
@@ -11499,18 +11732,18 @@ function findModuleTerraformFiles(repoRoot) {
11499
11732
  const walk2 = (dir, relative11) => {
11500
11733
  let entries;
11501
11734
  try {
11502
- entries = readdirSync18(dir);
11735
+ entries = readdirSync19(dir);
11503
11736
  } catch {
11504
11737
  return;
11505
11738
  }
11506
11739
  for (const entry of entries) {
11507
11740
  if (entry === "node_modules" || entry === ".git" || entry === ".worktrees" || entry === ".venv")
11508
11741
  continue;
11509
- const full = join44(dir, entry);
11742
+ const full = join45(dir, entry);
11510
11743
  const rel = `${relative11}/${entry}`;
11511
11744
  let isDir;
11512
11745
  try {
11513
- isDir = statSync10(full).isDirectory();
11746
+ isDir = statSync11(full).isDirectory();
11514
11747
  } catch {
11515
11748
  continue;
11516
11749
  }
@@ -11521,12 +11754,12 @@ function findModuleTerraformFiles(repoRoot) {
11521
11754
  }
11522
11755
  }
11523
11756
  };
11524
- walk2(join44(repoRoot, "modules"), "modules");
11757
+ walk2(join45(repoRoot, "modules"), "modules");
11525
11758
  return found.sort();
11526
11759
  }
11527
11760
  function checkCognitoInviteTemplates(repoRoot) {
11528
11761
  return findModuleTerraformFiles(repoRoot).flatMap(
11529
- (file) => checkInviteTemplateSource(file, readFileSync32(join44(repoRoot, file), "utf8"))
11762
+ (file) => checkInviteTemplateSource(file, readFileSync33(join45(repoRoot, file), "utf8"))
11530
11763
  );
11531
11764
  }
11532
11765
 
@@ -11554,11 +11787,11 @@ async function runCognitoInviteTemplateCheck() {
11554
11787
  }
11555
11788
 
11556
11789
  // src/scripts/check-core-direct-paths.ts
11557
- import { join as join46 } from "path";
11790
+ import { join as join47 } from "path";
11558
11791
 
11559
11792
  // src/lib/core-direct-paths-audit.ts
11560
- import { existsSync as existsSync42, readFileSync as readFileSync33, readdirSync as readdirSync19, statSync as statSync11 } from "fs";
11561
- import { join as join45 } from "path";
11793
+ import { existsSync as existsSync42, readFileSync as readFileSync34, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
11794
+ import { join as join46 } from "path";
11562
11795
  var EXTERNAL_BASE_IDENTIFIERS = ["CORE_API_URL"];
11563
11796
  var API_ROUTE_PREFIX = "/api/v1";
11564
11797
  var TEST_FILE_SUFFIXES = [".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx"];
@@ -11605,7 +11838,7 @@ function* templateLiterals(text) {
11605
11838
  i = j + 1;
11606
11839
  }
11607
11840
  }
11608
- function lineNumber(text, index) {
11841
+ function lineNumber2(text, index) {
11609
11842
  let count = 1;
11610
11843
  for (let i = 0; i < index && i < text.length; i++) {
11611
11844
  if (text[i] === "\n") count += 1;
@@ -11673,7 +11906,7 @@ function extractCoreDirectPaths(rawText, file, externalBases = EXTERNAL_BASE_IDE
11673
11906
  if (touchesConcatenation(text, start - 1, end + 1)) {
11674
11907
  found.push({
11675
11908
  file,
11676
- line: lineNumber(text, start),
11909
+ line: lineNumber2(text, start),
11677
11910
  raw,
11678
11911
  normalized: null,
11679
11912
  unresolvedReason: "built with string concatenation (+) \u2014 the extracted literal is a fragment, not the whole path",
@@ -11684,7 +11917,7 @@ function extractCoreDirectPaths(rawText, file, externalBases = EXTERNAL_BASE_IDE
11684
11917
  if (isNestedTemplateLiteralArtifact(rest)) {
11685
11918
  found.push({
11686
11919
  file,
11687
- line: lineNumber(text, start),
11920
+ line: lineNumber2(text, start),
11688
11921
  raw,
11689
11922
  normalized: null,
11690
11923
  unresolvedReason: "contains a template literal nested inside its own interpolation \u2014 this extractor's scanner would otherwise truncate at the inner backtick, producing a corrupted path rather than a real mismatch",
@@ -11695,7 +11928,7 @@ function extractCoreDirectPaths(rawText, file, externalBases = EXTERNAL_BASE_IDE
11695
11928
  const normalized = stripQueryString(resolveInterpolations(rest));
11696
11929
  found.push({
11697
11930
  file,
11698
- line: lineNumber(text, start),
11931
+ line: lineNumber2(text, start),
11699
11932
  raw,
11700
11933
  normalized,
11701
11934
  unresolvedReason: null,
@@ -11717,15 +11950,15 @@ function walkFiles(root, accept, skipDir) {
11717
11950
  const walk2 = (dir) => {
11718
11951
  let entries;
11719
11952
  try {
11720
- entries = readdirSync19(dir);
11953
+ entries = readdirSync20(dir);
11721
11954
  } catch {
11722
11955
  return;
11723
11956
  }
11724
11957
  for (const entry of entries) {
11725
- const p = join45(dir, entry);
11958
+ const p = join46(dir, entry);
11726
11959
  let st;
11727
11960
  try {
11728
- st = statSync11(p);
11961
+ st = statSync12(p);
11729
11962
  } catch {
11730
11963
  continue;
11731
11964
  }
@@ -11752,7 +11985,7 @@ function auditFrontendExtraction(frontendSrcDir, externalBases = EXTERNAL_BASE_I
11752
11985
  const extracted = [];
11753
11986
  let rawTotal = 0;
11754
11987
  for (const file of files) {
11755
- const text = readFileSync33(file, "utf8");
11988
+ const text = readFileSync34(file, "utf8");
11756
11989
  rawTotal += countRawExternalOccurrences(text, externalBases);
11757
11990
  extracted.push(...extractCoreDirectPaths(text, file, externalBases));
11758
11991
  }
@@ -11801,7 +12034,7 @@ function auditCoreRouteExtraction(apiSrcDir) {
11801
12034
  const prefixSet = /* @__PURE__ */ new Set();
11802
12035
  let rawApiRouterCount = 0;
11803
12036
  for (const file of files) {
11804
- const text = readFileSync33(file, "utf8");
12037
+ const text = readFileSync34(file, "utf8");
11805
12038
  const extraction = extractCoreRoutePrefixes(text);
11806
12039
  rawApiRouterCount += extraction.rawApiRouterCount;
11807
12040
  for (const p of extraction.prefixes) prefixSet.add(normalizePrefix(p));
@@ -11817,10 +12050,10 @@ function pathMatchesAnyCorePrefix(normalized, corePrefixes, apiRoutePrefix = API
11817
12050
  }
11818
12051
  function resolveSiblingCoreSrc(params) {
11819
12052
  const { estateDir, sibling } = params;
11820
- const configPath = join45(estateDir, sibling, "biffo.sibling.json");
12053
+ const configPath = join46(estateDir, sibling, "biffo.sibling.json");
11821
12054
  let raw;
11822
12055
  try {
11823
- raw = readFileSync33(configPath, "utf8");
12056
+ raw = readFileSync34(configPath, "utf8");
11824
12057
  } catch (err) {
11825
12058
  throw new Error(
11826
12059
  `cannot resolve ${sibling}'s core: ${configPath} does not exist or is unreadable (${err.message}) -- refusing to guess which core serves this sibling.`
@@ -11840,7 +12073,7 @@ function resolveSiblingCoreSrc(params) {
11840
12073
  `cannot resolve ${sibling}'s core: ${configPath} has no non-empty "core_project" field.`
11841
12074
  );
11842
12075
  }
11843
- const coreApiSrcDir = join45(estateDir, coreProject, "services", "api", "src");
12076
+ const coreApiSrcDir = join46(estateDir, coreProject, "services", "api", "src");
11844
12077
  if (!existsSync42(coreApiSrcDir)) {
11845
12078
  throw new Error(
11846
12079
  `cannot resolve ${sibling}'s core: biffo.sibling.json names core_project "${coreProject}", but ${coreApiSrcDir} does not exist -- the instance is missing from this estate checkout, not merely unmatched. Refusing to silently skip ${sibling} and shrink the audit's denominator.`
@@ -11884,7 +12117,7 @@ function auditSiblingCoreDirectPaths(params) {
11884
12117
  async function runCoreDirectPathsCheck(opts = {}) {
11885
12118
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11886
12119
  const sibling = opts.sibling ?? "sibling-template (self-check)";
11887
- const frontendSrcDir = opts.frontendSrc ?? join46(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
12120
+ const frontendSrcDir = opts.frontendSrc ?? join47(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
11888
12121
  let coreApiSrcDir;
11889
12122
  let coreProject = null;
11890
12123
  if (opts.coreSrc) {
@@ -11900,7 +12133,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
11900
12133
  coreApiSrcDir = resolution.coreApiSrcDir;
11901
12134
  coreProject = resolution.coreProject;
11902
12135
  } else {
11903
- coreApiSrcDir = join46(root, "services", "api", "src");
12136
+ coreApiSrcDir = join47(root, "services", "api", "src");
11904
12137
  }
11905
12138
  const report = auditSiblingCoreDirectPaths({ sibling, frontendSrcDir, coreApiSrcDir });
11906
12139
  console.log(
@@ -11965,8 +12198,8 @@ async function runOwnershipCheck(argv) {
11965
12198
  const { stdout } = await execa("git", ["diff", "--cached", "--name-status"], { cwd: root });
11966
12199
  ({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
11967
12200
  if (messageFile) {
11968
- const { readFileSync: readFileSync46, existsSync: existsSync57 } = await import("fs");
11969
- if (existsSync57(messageFile)) commitMessage = readFileSync46(messageFile, "utf8");
12201
+ const { readFileSync: readFileSync47, existsSync: existsSync57 } = await import("fs");
12202
+ if (existsSync57(messageFile)) commitMessage = readFileSync47(messageFile, "utf8");
11970
12203
  }
11971
12204
  } else {
11972
12205
  const base = process.env["GITHUB_BASE_REF"] ?? args[0];
@@ -12068,20 +12301,20 @@ ${BOLD}If the divergence is deliberate${OFF}
12068
12301
 
12069
12302
  // src/scripts/check-distribution-inventory.ts
12070
12303
  import { existsSync as existsSync44 } from "fs";
12071
- import { join as join48 } from "path";
12304
+ import { join as join49 } from "path";
12072
12305
 
12073
12306
  // src/lib/distribution-inventory.ts
12074
- import { existsSync as existsSync43, readFileSync as readFileSync34 } from "fs";
12075
- import { join as join47 } from "path";
12307
+ import { existsSync as existsSync43, readFileSync as readFileSync35 } from "fs";
12308
+ import { join as join48 } from "path";
12076
12309
  var INVENTORY_FILENAME = "distribution-inventory.json";
12077
12310
  function loadDistributionInventory(root) {
12078
- const path = join47(root, INVENTORY_FILENAME);
12311
+ const path = join48(root, INVENTORY_FILENAME);
12079
12312
  if (!existsSync43(path)) {
12080
12313
  throw new Error(
12081
12314
  `${INVENTORY_FILENAME} not found at ${root} -- expected it beside core-manifest.json`
12082
12315
  );
12083
12316
  }
12084
- return JSON.parse(readFileSync34(path, "utf8"));
12317
+ return JSON.parse(readFileSync35(path, "utf8"));
12085
12318
  }
12086
12319
  function validateInventory(inventory) {
12087
12320
  const violations = [];
@@ -12241,7 +12474,7 @@ async function runDistributionInventoryCheck(root) {
12241
12474
  );
12242
12475
  return;
12243
12476
  }
12244
- if (!existsSync44(join48(repoRoot, INVENTORY_FILENAME))) {
12477
+ if (!existsSync44(join49(repoRoot, INVENTORY_FILENAME))) {
12245
12478
  console.log(
12246
12479
  `\u2713 distribution-inventory: skipped \u2014 no ${INVENTORY_FILENAME} at ${repoRoot}. This is a template-only registry; a checkout that legitimately has none has nothing for this guard to check.`
12247
12480
  );
@@ -12267,7 +12500,7 @@ async function runDistributionInventoryCheck(root) {
12267
12500
  }
12268
12501
 
12269
12502
  // src/scripts/check-distribution-remote-state.ts
12270
- import { join as join49 } from "path";
12503
+ import { join as join50 } from "path";
12271
12504
  async function ghExecCommand(file, args) {
12272
12505
  const result = await execa(file, args, { reject: false });
12273
12506
  return { stdout: String(result.stdout ?? ""), exitCode: result.exitCode ?? null };
@@ -12282,7 +12515,7 @@ async function runDistributionRemoteStateCheck(root) {
12282
12515
  }
12283
12516
  }
12284
12517
  console.log(
12285
- `distribution-remote-state: examined ${assertions.length} remote content assertion(s) declared across ${inventory.entries.length} inventory entries (${join49(repoRoot, "distribution-inventory.json")})`
12518
+ `distribution-remote-state: examined ${assertions.length} remote content assertion(s) declared across ${inventory.entries.length} inventory entries (${join50(repoRoot, "distribution-inventory.json")})`
12286
12519
  );
12287
12520
  if (assertions.length === 0) {
12288
12521
  console.log("\u2713 distribution-remote-state: nothing declared to check");
@@ -12327,9 +12560,9 @@ ${assertion.ref}`;
12327
12560
  }
12328
12561
 
12329
12562
  // src/lib/eventbridge-log-permission-guard.ts
12330
- import { readFileSync as readFileSync35, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
12331
- import { join as join50 } from "path";
12332
- var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
12563
+ import { readFileSync as readFileSync36, readdirSync as readdirSync21, statSync as statSync13 } from "fs";
12564
+ import { join as join51 } from "path";
12565
+ var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
12333
12566
  var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
12334
12567
  var LOG_RESOURCE_POLICY_TYPE = "aws_cloudwatch_log_resource_policy";
12335
12568
  var LOG_GROUP_ARN_REF = /aws_cloudwatch_log_group\.([\w-]+)(?:\[[^\]]*\])?\.arn/g;
@@ -12350,14 +12583,14 @@ function servicePrincipalsIn(body) {
12350
12583
  function grantsPrincipal(body, principal) {
12351
12584
  return servicePrincipalsIn(body).includes(principal);
12352
12585
  }
12353
- function lineNumber2(text, index) {
12586
+ function lineNumber3(text, index) {
12354
12587
  let count = 1;
12355
12588
  for (let i = 0; i < index && i < text.length; i++) {
12356
12589
  if (text[i] === "\n") count += 1;
12357
12590
  }
12358
12591
  return count;
12359
12592
  }
12360
- function balancedBraceSpan(text, openIndex) {
12593
+ function balancedBraceSpan2(text, openIndex) {
12361
12594
  let depth = 0;
12362
12595
  for (let i = openIndex; i < text.length; i++) {
12363
12596
  if (text[i] === "{") depth += 1;
@@ -12368,15 +12601,15 @@ function balancedBraceSpan(text, openIndex) {
12368
12601
  }
12369
12602
  return null;
12370
12603
  }
12371
- function findResourceBlocks(text, file, resourceType) {
12604
+ function findResourceBlocks2(text, file, resourceType) {
12372
12605
  const blocks = [];
12373
12606
  const pattern = new RegExp(`resource\\s+"${resourceType}"\\s+"([\\w-]+)"\\s*\\{`, "g");
12374
12607
  let m;
12375
12608
  while ((m = pattern.exec(text)) !== null) {
12376
12609
  const name = m[1];
12377
- const line = lineNumber2(text, m.index);
12610
+ const line = lineNumber3(text, m.index);
12378
12611
  const openIndex = m.index + m[0].length - 1;
12379
- const closeIndex = balancedBraceSpan(text, openIndex);
12612
+ const closeIndex = balancedBraceSpan2(text, openIndex);
12380
12613
  if (closeIndex === null) {
12381
12614
  blocks.push({ file, line, type: resourceType, name, body: null });
12382
12615
  continue;
@@ -12391,29 +12624,29 @@ function findResourceBlocks(text, file, resourceType) {
12391
12624
  }
12392
12625
  return blocks;
12393
12626
  }
12394
- function countRawResourceDeclarations(text, resourceType) {
12627
+ function countRawResourceDeclarations2(text, resourceType) {
12395
12628
  const needle = `resource "${resourceType}"`;
12396
12629
  return text.split(needle).length - 1;
12397
12630
  }
12398
- function walkTerraformFiles(root) {
12631
+ function walkTerraformFiles2(root) {
12399
12632
  const out = [];
12400
12633
  const walk2 = (dir) => {
12401
12634
  let entries;
12402
12635
  try {
12403
- entries = readdirSync20(dir);
12636
+ entries = readdirSync21(dir);
12404
12637
  } catch {
12405
12638
  return;
12406
12639
  }
12407
12640
  for (const entry of entries) {
12408
- const p = join50(dir, entry);
12641
+ const p = join51(dir, entry);
12409
12642
  let st;
12410
12643
  try {
12411
- st = statSync12(p);
12644
+ st = statSync13(p);
12412
12645
  } catch {
12413
12646
  continue;
12414
12647
  }
12415
12648
  if (st.isDirectory()) {
12416
- if (SKIP_DIRS2.has(entry)) continue;
12649
+ if (SKIP_DIRS3.has(entry)) continue;
12417
12650
  walk2(p);
12418
12651
  continue;
12419
12652
  }
@@ -12437,7 +12670,7 @@ function logGroupNamesReferencedIn(body) {
12437
12670
  return names;
12438
12671
  }
12439
12672
  function auditEventBridgeLogPermissions(root) {
12440
- const files = walkTerraformFiles(root);
12673
+ const files = walkTerraformFiles2(root);
12441
12674
  if (files.length === 0) {
12442
12675
  throw new Error(
12443
12676
  `auditEventBridgeLogPermissions: no .tf files found under ${root} \u2014 the guard cannot verify anything against input it cannot see. This is a hard failure, not "0 violations": fix the path, do not treat an empty scan as a clean pass.`
@@ -12448,11 +12681,11 @@ function auditEventBridgeLogPermissions(root) {
12448
12681
  let rawEventTargetCount = 0;
12449
12682
  let rawLogPolicyCount = 0;
12450
12683
  for (const file of files) {
12451
- const text = readFileSync35(file, "utf8");
12452
- rawEventTargetCount += countRawResourceDeclarations(text, EVENT_TARGET_TYPE);
12453
- rawLogPolicyCount += countRawResourceDeclarations(text, LOG_RESOURCE_POLICY_TYPE);
12454
- eventTargetBlocks.push(...findResourceBlocks(text, file, EVENT_TARGET_TYPE));
12455
- logResourcePolicyBlocks.push(...findResourceBlocks(text, file, LOG_RESOURCE_POLICY_TYPE));
12684
+ const text = readFileSync36(file, "utf8");
12685
+ rawEventTargetCount += countRawResourceDeclarations2(text, EVENT_TARGET_TYPE);
12686
+ rawLogPolicyCount += countRawResourceDeclarations2(text, LOG_RESOURCE_POLICY_TYPE);
12687
+ eventTargetBlocks.push(...findResourceBlocks2(text, file, EVENT_TARGET_TYPE));
12688
+ logResourcePolicyBlocks.push(...findResourceBlocks2(text, file, LOG_RESOURCE_POLICY_TYPE));
12456
12689
  }
12457
12690
  const unterminatedBlocks = [...eventTargetBlocks, ...logResourcePolicyBlocks].filter(
12458
12691
  (b) => b.body === null
@@ -12539,7 +12772,7 @@ async function runEventBridgeLogPermissionCheck() {
12539
12772
 
12540
12773
  // src/scripts/check-instance-adoption.ts
12541
12774
  import { existsSync as existsSync45 } from "fs";
12542
- import { join as join51 } from "path";
12775
+ import { join as join52 } from "path";
12543
12776
  async function runInstanceAdoptionCheck(opts = {}) {
12544
12777
  if (!opts.instanceDir) {
12545
12778
  console.error(
@@ -12555,7 +12788,7 @@ async function runInstanceAdoptionCheck(opts = {}) {
12555
12788
  }
12556
12789
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12557
12790
  const theirsDir = opts.theirsDir ?? root;
12558
- const instanceLabel = opts.instance ?? join51(opts.instanceDir).split("/").filter(Boolean).pop();
12791
+ const instanceLabel = opts.instance ?? join52(opts.instanceDir).split("/").filter(Boolean).pop();
12559
12792
  const report = checkInstanceAdoption(theirsDir, opts.instanceDir);
12560
12793
  console.log(
12561
12794
  `examined ${report.examinedInstances} instance (${instanceLabel}) against ${report.registeredPairs} registered pair(s), ${report.applicablePairs} applicable to this instance, against template tree ${theirsDir}`
@@ -12577,12 +12810,12 @@ async function runInstanceAdoptionCheck(opts = {}) {
12577
12810
  }
12578
12811
 
12579
12812
  // src/lib/lambda-output-guard.ts
12580
- import { readFileSync as readFileSync37 } from "fs";
12581
- import { join as join53 } from "path";
12813
+ import { readFileSync as readFileSync38 } from "fs";
12814
+ import { join as join54 } from "path";
12582
12815
 
12583
12816
  // src/lib/terraform-input-guard.ts
12584
- import { existsSync as existsSync46, readdirSync as readdirSync21, readFileSync as readFileSync36, statSync as statSync13 } from "fs";
12585
- import { join as join52 } from "path";
12817
+ import { existsSync as existsSync46, readdirSync as readdirSync22, readFileSync as readFileSync37, statSync as statSync14 } from "fs";
12818
+ import { join as join53 } from "path";
12586
12819
  var GUARDED_SUBCOMMANDS = [
12587
12820
  "init",
12588
12821
  "plan",
@@ -12596,18 +12829,18 @@ function stripComments2(source) {
12596
12829
  return source.split("\n").map((line) => line.replace(/(^|\s)#.*$/, "$1")).join("\n");
12597
12830
  }
12598
12831
  function vendoredPluginServiceDirs(repoRoot) {
12599
- const servicesDir = join52(repoRoot, "services");
12832
+ const servicesDir = join53(repoRoot, "services");
12600
12833
  const result = /* @__PURE__ */ new Set();
12601
12834
  let entries;
12602
12835
  try {
12603
- entries = readdirSync21(servicesDir);
12836
+ entries = readdirSync22(servicesDir);
12604
12837
  } catch {
12605
12838
  return result;
12606
12839
  }
12607
12840
  for (const entry of entries) {
12608
- const full = join52(servicesDir, entry);
12609
- if (!existsSync46(full) || !statSync13(full).isDirectory()) continue;
12610
- if (existsSync46(join52(full, "biffo.plugin.json"))) {
12841
+ const full = join53(servicesDir, entry);
12842
+ if (!existsSync46(full) || !statSync14(full).isDirectory()) continue;
12843
+ if (existsSync46(join53(full, "biffo.plugin.json"))) {
12611
12844
  result.add(entry);
12612
12845
  }
12613
12846
  }
@@ -12619,7 +12852,7 @@ function findWorkflowFiles(repoRoot) {
12619
12852
  const walk2 = (dir, relative11) => {
12620
12853
  let entries;
12621
12854
  try {
12622
- entries = readdirSync21(dir);
12855
+ entries = readdirSync22(dir);
12623
12856
  } catch {
12624
12857
  return;
12625
12858
  }
@@ -12629,11 +12862,11 @@ function findWorkflowFiles(repoRoot) {
12629
12862
  if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
12630
12863
  continue;
12631
12864
  }
12632
- const full = join52(dir, entry);
12865
+ const full = join53(dir, entry);
12633
12866
  const rel = relative11 ? `${relative11}/${entry}` : entry;
12634
12867
  let isDir;
12635
12868
  try {
12636
- isDir = statSync13(full).isDirectory();
12869
+ isDir = statSync14(full).isDirectory();
12637
12870
  } catch {
12638
12871
  continue;
12639
12872
  }
@@ -12679,7 +12912,7 @@ function checkWorkflowSource(file, rawSource) {
12679
12912
  }
12680
12913
  function checkTerraformInput(repoRoot) {
12681
12914
  return findWorkflowFiles(repoRoot).flatMap(
12682
- (file) => checkWorkflowSource(file, readFileSync36(join52(repoRoot, file), "utf8"))
12915
+ (file) => checkWorkflowSource(file, readFileSync37(join53(repoRoot, file), "utf8"))
12683
12916
  );
12684
12917
  }
12685
12918
 
@@ -12737,7 +12970,7 @@ function checkWorkflowSource2(file, rawSource) {
12737
12970
  }
12738
12971
  function checkLambdaOutput(repoRoot) {
12739
12972
  return findWorkflowFiles(repoRoot).flatMap(
12740
- (file) => checkWorkflowSource2(file, readFileSync37(join53(repoRoot, file), "utf8"))
12973
+ (file) => checkWorkflowSource2(file, readFileSync38(join54(repoRoot, file), "utf8"))
12741
12974
  );
12742
12975
  }
12743
12976
 
@@ -12766,7 +12999,7 @@ async function runLambdaOutputCheck() {
12766
12999
 
12767
13000
  // src/scripts/check-migration-body-change.ts
12768
13001
  import { existsSync as existsSync47 } from "fs";
12769
- import { join as join54 } from "path";
13002
+ import { join as join55 } from "path";
12770
13003
 
12771
13004
  // src/lib/migration-body-change-guard.ts
12772
13005
  function checkMigrationBodyChangeMarkers(diffs) {
@@ -12832,7 +13065,7 @@ async function runMigrationBodyChangeCheck(argv) {
12832
13065
  );
12833
13066
  return;
12834
13067
  }
12835
- if (!existsSync47(join54(root, MIGRATIONS_VERSIONS_DIR))) {
13068
+ if (!existsSync47(join55(root, MIGRATIONS_VERSIONS_DIR))) {
12836
13069
  console.log(`\u2713 migration body-change guard: no ${MIGRATIONS_VERSIONS_DIR} in this repo.`);
12837
13070
  return;
12838
13071
  }
@@ -12999,8 +13232,8 @@ baseline was ${String(ratchet.baseline)}, now ${String(ratchet.count)}. See biff
12999
13232
  }
13000
13233
 
13001
13234
  // src/scripts/check-pipe-trap.ts
13002
- import { readFileSync as readFileSync38, readdirSync as readdirSync22 } from "fs";
13003
- import { join as join55, relative as relative9 } from "path";
13235
+ import { readFileSync as readFileSync39, readdirSync as readdirSync23 } from "fs";
13236
+ import { join as join56, relative as relative9 } from "path";
13004
13237
 
13005
13238
  // src/lib/pipe-trap-guard.ts
13006
13239
  var STATUS_BEARING = [
@@ -13096,17 +13329,17 @@ function findPipeTraps(source) {
13096
13329
  function shellFiles(root) {
13097
13330
  const out = [];
13098
13331
  for (const dir of ["scripts", ".githooks"]) {
13099
- const full = join55(root, dir);
13332
+ const full = join56(root, dir);
13100
13333
  let entries;
13101
13334
  try {
13102
- entries = readdirSync22(full, { withFileTypes: true });
13335
+ entries = readdirSync23(full, { withFileTypes: true });
13103
13336
  } catch {
13104
13337
  continue;
13105
13338
  }
13106
13339
  for (const entry of entries) {
13107
13340
  if (!entry.isFile()) continue;
13108
13341
  if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
13109
- out.push(join55(full, entry.name));
13342
+ out.push(join56(full, entry.name));
13110
13343
  }
13111
13344
  }
13112
13345
  return out;
@@ -13122,7 +13355,7 @@ async function runPipeTrapCheck() {
13122
13355
  process.exit(1);
13123
13356
  }
13124
13357
  const findings = files.flatMap(
13125
- (file) => findPipeTraps(readFileSync38(file, "utf8")).map(
13358
+ (file) => findPipeTraps(readFileSync39(file, "utf8")).map(
13126
13359
  (t) => `${relative9(root, file)}:${t.line} ${t.text}
13127
13360
  ${t.reason}`
13128
13361
  )
@@ -13139,8 +13372,8 @@ async function runPipeTrapCheck() {
13139
13372
  }
13140
13373
 
13141
13374
  // src/lib/plugin-allowlist-convention.ts
13142
- import { readFileSync as readFileSync39 } from "fs";
13143
- import { join as join56 } from "path";
13375
+ import { readFileSync as readFileSync40 } from "fs";
13376
+ import { join as join57 } from "path";
13144
13377
  var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
13145
13378
  var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
13146
13379
  var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
@@ -13151,7 +13384,7 @@ var PLUGIN = "<plugin>";
13151
13384
  var ACCOUNT = "<account>";
13152
13385
  function read(repoRoot, relative11) {
13153
13386
  try {
13154
- return readFileSync39(join56(repoRoot, relative11), "utf8");
13387
+ return readFileSync40(join57(repoRoot, relative11), "utf8");
13155
13388
  } catch {
13156
13389
  throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
13157
13390
  }
@@ -13273,32 +13506,32 @@ async function runPluginAllowlistConventionCheck() {
13273
13506
 
13274
13507
  // src/scripts/check-plugin-collisions.ts
13275
13508
  import { existsSync as existsSync50 } from "fs";
13276
- import { join as join58 } from "path";
13509
+ import { join as join59 } from "path";
13277
13510
 
13278
13511
  // src/lib/plugin-collision-guard.ts
13279
- import { existsSync as existsSync49, readdirSync as readdirSync23, statSync as statSync14 } from "fs";
13280
- import { join as join57 } from "path";
13512
+ import { existsSync as existsSync49, readdirSync as readdirSync24, statSync as statSync15 } from "fs";
13513
+ import { join as join58 } from "path";
13281
13514
  var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
13282
13515
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
13283
13516
  function subdirectories(dir) {
13284
13517
  if (!existsSync49(dir)) return [];
13285
- return readdirSync23(dir).filter((entry) => {
13518
+ return readdirSync24(dir).filter((entry) => {
13286
13519
  if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
13287
13520
  try {
13288
- return statSync14(join57(dir, entry)).isDirectory();
13521
+ return statSync15(join58(dir, entry)).isDirectory();
13289
13522
  } catch {
13290
13523
  return false;
13291
13524
  }
13292
13525
  });
13293
13526
  }
13294
13527
  function regularPackagesOf(pluginDir2) {
13295
- return subdirectories(pluginDir2).filter((name) => existsSync49(join57(pluginDir2, name, "__init__.py"))).sort();
13528
+ return subdirectories(pluginDir2).filter((name) => existsSync49(join58(pluginDir2, name, "__init__.py"))).sort();
13296
13529
  }
13297
13530
  function bareTestModulesOf(pluginDir2) {
13298
- const testsDir = join57(pluginDir2, "tests");
13531
+ const testsDir = join58(pluginDir2, "tests");
13299
13532
  if (!existsSync49(testsDir)) return [];
13300
- if (existsSync49(join57(testsDir, "__init__.py"))) return [];
13301
- return readdirSync23(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
13533
+ if (existsSync49(join58(testsDir, "__init__.py"))) return [];
13534
+ return readdirSync24(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
13302
13535
  }
13303
13536
  function findCollisions(servicesDir, pluginDirs) {
13304
13537
  const plugins = (pluginDirs ?? subdirectories(servicesDir)).filter((name) => !name.startsWith("_")).filter((name) => name !== "api").sort();
@@ -13306,7 +13539,7 @@ function findCollisions(servicesDir, pluginDirs) {
13306
13539
  const gather = (kind, namesOf) => {
13307
13540
  const claims = /* @__PURE__ */ new Map();
13308
13541
  for (const plugin of plugins) {
13309
- for (const name of namesOf(join57(servicesDir, plugin))) {
13542
+ for (const name of namesOf(join58(servicesDir, plugin))) {
13310
13543
  claims.set(name, [...claims.get(name) ?? [], plugin]);
13311
13544
  }
13312
13545
  }
@@ -13344,7 +13577,7 @@ function formatCollisions(collisions) {
13344
13577
  // src/scripts/check-plugin-collisions.ts
13345
13578
  async function runPluginCollisionCheck() {
13346
13579
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13347
- const servicesDir = join58(root, "services");
13580
+ const servicesDir = join59(root, "services");
13348
13581
  if (!existsSync50(servicesDir)) {
13349
13582
  console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
13350
13583
  return;
@@ -13362,25 +13595,25 @@ async function runPluginCollisionCheck() {
13362
13595
  }
13363
13596
 
13364
13597
  // src/lib/plugin-terraform-guard.ts
13365
- import { existsSync as existsSync51, readFileSync as readFileSync40, readdirSync as readdirSync24 } from "fs";
13366
- import { dirname as dirname10, join as join59, relative as relative10, sep as sep4 } from "path";
13367
- var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
13598
+ import { existsSync as existsSync51, readFileSync as readFileSync41, readdirSync as readdirSync25 } from "fs";
13599
+ import { dirname as dirname10, join as join60, relative as relative10, sep as sep4 } from "path";
13600
+ var SKIP_DIRS4 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
13368
13601
  var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
13369
13602
  function findPluginManifests(root) {
13370
13603
  const found = [];
13371
13604
  const walk2 = (dir) => {
13372
13605
  let entries;
13373
13606
  try {
13374
- entries = readdirSync24(dir, { withFileTypes: true });
13607
+ entries = readdirSync25(dir, { withFileTypes: true });
13375
13608
  } catch {
13376
13609
  return;
13377
13610
  }
13378
13611
  for (const entry of entries) {
13379
13612
  if (entry.isDirectory()) {
13380
- if (SKIP_DIRS3.has(entry.name)) continue;
13381
- walk2(join59(dir, entry.name));
13613
+ if (SKIP_DIRS4.has(entry.name)) continue;
13614
+ walk2(join60(dir, entry.name));
13382
13615
  } else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
13383
- found.push(relative10(root, join59(dir, entry.name)).split(sep4).join("/"));
13616
+ found.push(relative10(root, join60(dir, entry.name)).split(sep4).join("/"));
13384
13617
  }
13385
13618
  }
13386
13619
  };
@@ -13390,7 +13623,7 @@ function findPluginManifests(root) {
13390
13623
  function readSubscriptions(absManifestPath) {
13391
13624
  let parsed;
13392
13625
  try {
13393
- parsed = JSON.parse(readFileSync40(absManifestPath, "utf8"));
13626
+ parsed = JSON.parse(readFileSync41(absManifestPath, "utf8"));
13394
13627
  } catch {
13395
13628
  return null;
13396
13629
  }
@@ -13405,14 +13638,14 @@ function readSubscriptions(absManifestPath) {
13405
13638
  }
13406
13639
  function checkPluginTerraform(root) {
13407
13640
  const violations = [];
13408
- const coreManifest = existsSync51(join59(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
13641
+ const coreManifest = existsSync51(join60(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
13409
13642
  for (const manifest of findPluginManifests(root)) {
13410
13643
  if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
13411
- const absManifest = join59(root, manifest);
13644
+ const absManifest = join60(root, manifest);
13412
13645
  const subscriptions = readSubscriptions(absManifest);
13413
13646
  if (subscriptions === null) continue;
13414
13647
  const pluginDir2 = dirname10(absManifest);
13415
- if (existsSync51(join59(pluginDir2, "terraform"))) continue;
13648
+ if (existsSync51(join60(pluginDir2, "terraform"))) continue;
13416
13649
  const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
13417
13650
  violations.push({
13418
13651
  manifest,
@@ -13444,11 +13677,11 @@ async function runPluginTerraformCheck() {
13444
13677
 
13445
13678
  // src/scripts/check-plugin-tool-supply.ts
13446
13679
  import { existsSync as existsSync53 } from "fs";
13447
- import { join as join61 } from "path";
13680
+ import { join as join62 } from "path";
13448
13681
 
13449
13682
  // src/lib/plugin-tool-supply-audit.ts
13450
- import { existsSync as existsSync52, readFileSync as readFileSync41, readdirSync as readdirSync25, statSync as statSync15 } from "fs";
13451
- import { join as join60 } from "path";
13683
+ import { existsSync as existsSync52, readFileSync as readFileSync42, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
13684
+ import { join as join61 } from "path";
13452
13685
 
13453
13686
  // src/lib/openrouter-model-snapshot.ts
13454
13687
  var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
@@ -13859,13 +14092,13 @@ var OPENROUTER_MODEL_IDS = [
13859
14092
  function listDirs(root) {
13860
14093
  let entries;
13861
14094
  try {
13862
- entries = readdirSync25(root);
14095
+ entries = readdirSync26(root);
13863
14096
  } catch {
13864
14097
  return [];
13865
14098
  }
13866
14099
  return entries.filter((e) => {
13867
14100
  try {
13868
- return statSync15(join60(root, e)).isDirectory();
14101
+ return statSync16(join61(root, e)).isDirectory();
13869
14102
  } catch {
13870
14103
  return false;
13871
14104
  }
@@ -13876,15 +14109,15 @@ function walkFiles2(root, accept, skipDir) {
13876
14109
  const walk2 = (dir) => {
13877
14110
  let entries;
13878
14111
  try {
13879
- entries = readdirSync25(dir);
14112
+ entries = readdirSync26(dir);
13880
14113
  } catch {
13881
14114
  return;
13882
14115
  }
13883
14116
  for (const entry of entries) {
13884
- const p = join60(dir, entry);
14117
+ const p = join61(dir, entry);
13885
14118
  let st;
13886
14119
  try {
13887
- st = statSync15(p);
14120
+ st = statSync16(p);
13888
14121
  } catch {
13889
14122
  continue;
13890
14123
  }
@@ -13907,14 +14140,14 @@ function pluginPythonFiles(pluginDir2) {
13907
14140
  );
13908
14141
  }
13909
14142
  function pluginTerraformFiles(pluginDir2) {
13910
- const tfDir = join60(pluginDir2, "terraform");
14143
+ const tfDir = join61(pluginDir2, "terraform");
13911
14144
  let entries;
13912
14145
  try {
13913
- entries = readdirSync25(tfDir);
14146
+ entries = readdirSync26(tfDir);
13914
14147
  } catch {
13915
14148
  return [];
13916
14149
  }
13917
- return entries.filter((e) => e.endsWith(".tf")).map((e) => join60(tfDir, e)).sort();
14150
+ return entries.filter((e) => e.endsWith(".tf")).map((e) => join61(tfDir, e)).sort();
13918
14151
  }
13919
14152
  function extractManifestTools(manifestText) {
13920
14153
  let parsed;
@@ -14166,8 +14399,8 @@ function isSnapshotStale(fetchedAt, now) {
14166
14399
  function normalizeModelId(id) {
14167
14400
  return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
14168
14401
  }
14169
- var CONFIG_PY_PATH = join60("services", "api", "src", "api", "config.py");
14170
- var ORCHESTRATION_SCHEMA_PATH = join60(
14402
+ var CONFIG_PY_PATH = join61("services", "api", "src", "api", "config.py");
14403
+ var ORCHESTRATION_SCHEMA_PATH = join61(
14171
14404
  "services",
14172
14405
  "api",
14173
14406
  "src",
@@ -14179,8 +14412,8 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
14179
14412
  const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
14180
14413
  const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
14181
14414
  const now = options.now ?? /* @__PURE__ */ new Date();
14182
- const configPath = join60(repoRoot, CONFIG_PY_PATH);
14183
- const orchestrationPath = join60(repoRoot, ORCHESTRATION_SCHEMA_PATH);
14415
+ const configPath = join61(repoRoot, CONFIG_PY_PATH);
14416
+ const orchestrationPath = join61(repoRoot, ORCHESTRATION_SCHEMA_PATH);
14184
14417
  const configMissing = !existsSync52(configPath);
14185
14418
  const orchestrationSchemaMissing = !existsSync52(orchestrationPath);
14186
14419
  const knownSet = new Set(knownModelIds);
@@ -14200,13 +14433,13 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
14200
14433
  };
14201
14434
  let settingsBlind = false;
14202
14435
  if (!configMissing) {
14203
- const settingsFields = extractSettingsModelFields(readFileSync41(configPath, "utf8"));
14436
+ const settingsFields = extractSettingsModelFields(readFileSync42(configPath, "utf8"));
14204
14437
  if (settingsFields.length === 0) settingsBlind = true;
14205
14438
  for (const { field, value } of settingsFields) record(`${CONFIG_PY_PATH}#${field}`, value);
14206
14439
  }
14207
14440
  let curatedFieldsBlind = false;
14208
14441
  if (!orchestrationSchemaMissing) {
14209
- const curated = extractCuratedModelFields(readFileSync41(orchestrationPath, "utf8"));
14442
+ const curated = extractCuratedModelFields(readFileSync42(orchestrationPath, "utf8"));
14210
14443
  if (curated.rawFieldCount > 0 && curated.fields.every((f) => f.defaultValue === null && f.optionValues.length === 0)) {
14211
14444
  curatedFieldsBlind = true;
14212
14445
  }
@@ -14253,7 +14486,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
14253
14486
  function discoverPluginDirs(pluginsRoot) {
14254
14487
  return listDirs(pluginsRoot).filter((name) => {
14255
14488
  try {
14256
- return statSync15(join60(pluginsRoot, name, "biffo.plugin.json")).isFile();
14489
+ return statSync16(join61(pluginsRoot, name, "biffo.plugin.json")).isFile();
14257
14490
  } catch {
14258
14491
  return false;
14259
14492
  }
@@ -14266,8 +14499,8 @@ function auditPluginToolSupply(pluginsRoot) {
14266
14499
  let terraformBlind = false;
14267
14500
  let totalDeclaredTools = 0;
14268
14501
  for (const name of pluginNames) {
14269
- const pluginDir2 = join60(pluginsRoot, name);
14270
- const manifestText = readFileSync41(join60(pluginDir2, "biffo.plugin.json"), "utf8");
14502
+ const pluginDir2 = join61(pluginsRoot, name);
14503
+ const manifestText = readFileSync42(join61(pluginDir2, "biffo.plugin.json"), "utf8");
14271
14504
  const manifest = extractManifestTools(manifestText);
14272
14505
  if (manifest.parseError) {
14273
14506
  findings.push({
@@ -14285,13 +14518,13 @@ function auditPluginToolSupply(pluginsRoot) {
14285
14518
  totalDeclaredTools += manifest.tools.length;
14286
14519
  const pySources = pluginPythonFiles(pluginDir2).map((f) => ({
14287
14520
  file: f,
14288
- text: readFileSync41(f, "utf8")
14521
+ text: readFileSync42(f, "utf8")
14289
14522
  }));
14290
14523
  const resolver = buildSymbolResolver(pySources);
14291
14524
  const registry = extractToolRegistryEntries(pySources, resolver);
14292
14525
  if (registry.rawToolDefinitionCount > 0 && registry.entries.length === 0) registryBlind = true;
14293
14526
  const tfFiles = pluginTerraformFiles(pluginDir2);
14294
- const tfText = tfFiles.map((f) => readFileSync41(f, "utf8")).join("\n");
14527
+ const tfText = tfFiles.map((f) => readFileSync42(f, "utf8")).join("\n");
14295
14528
  const terraform = extractTerraformEnvKeys(tfText);
14296
14529
  if (terraform.rawMarkerCount > 0 && terraform.resolvedBlockCount === 0) terraformBlind = true;
14297
14530
  for (const toolName of manifest.tools) {
@@ -14365,7 +14598,7 @@ function auditPluginToolSupply(pluginsRoot) {
14365
14598
  requiredEnvVars: envResult.envVars,
14366
14599
  missingEnvVars: anyWired ? [] : envResult.envVars,
14367
14600
  status: anyWired ? "ok" : "missing-env",
14368
- detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${join60(pluginDir2, "terraform")}, so this deployment can never supply it`
14601
+ detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${join61(pluginDir2, "terraform")}, so this deployment can never supply it`
14369
14602
  });
14370
14603
  }
14371
14604
  }
@@ -14398,7 +14631,7 @@ function auditPluginToolSupply(pluginsRoot) {
14398
14631
  async function runPluginToolSupplyCheck() {
14399
14632
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
14400
14633
  let allOk = true;
14401
- const pluginsRoot = join61(root, "services", "_plugins");
14634
+ const pluginsRoot = join62(root, "services", "_plugins");
14402
14635
  if (!existsSync53(pluginsRoot)) {
14403
14636
  console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
14404
14637
  } else {
@@ -14429,7 +14662,7 @@ async function runPluginToolSupplyCheck() {
14429
14662
  console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
14430
14663
  }
14431
14664
  }
14432
- const servicesApiRoot = join61(root, "services", "api");
14665
+ const servicesApiRoot = join62(root, "services", "api");
14433
14666
  if (!existsSync53(servicesApiRoot)) {
14434
14667
  console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
14435
14668
  } else {
@@ -14604,10 +14837,10 @@ async function runReleaseSubjectCheck(argv) {
14604
14837
  }
14605
14838
 
14606
14839
  // src/scripts/check-shared-file-reduction.ts
14607
- import { readFileSync as readFileSync43 } from "fs";
14840
+ import { readFileSync as readFileSync44 } from "fs";
14608
14841
 
14609
14842
  // src/lib/shared-file-reduction-guard.ts
14610
- import { readFileSync as readFileSync42 } from "fs";
14843
+ import { readFileSync as readFileSync43 } from "fs";
14611
14844
  var LEAF_TEST_CALLS = /* @__PURE__ */ new Set(["it", "test"]);
14612
14845
  var SUITE_CALLS = /* @__PURE__ */ new Set(["describe", "suite"]);
14613
14846
  var TEST_FILE_PATTERN = /\.(test|spec)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
@@ -14734,7 +14967,7 @@ function formatReductionReport(report) {
14734
14967
  // src/scripts/check-shared-file-reduction.ts
14735
14968
  function readStdin() {
14736
14969
  try {
14737
- return readFileSync43(0, "utf8");
14970
+ return readFileSync44(0, "utf8");
14738
14971
  } catch {
14739
14972
  return "";
14740
14973
  }
@@ -14751,15 +14984,15 @@ function pairsFromTsv(tsv) {
14751
14984
  const [target, existingPath, incomingPath] = fields;
14752
14985
  pairs.push({
14753
14986
  target,
14754
- existing: readFileSync43(existingPath, "utf8"),
14755
- incoming: readFileSync43(incomingPath, "utf8")
14987
+ existing: readFileSync44(existingPath, "utf8"),
14988
+ incoming: readFileSync44(incomingPath, "utf8")
14756
14989
  });
14757
14990
  }
14758
14991
  return pairs;
14759
14992
  }
14760
14993
  function loadAccepted(manifestPath) {
14761
14994
  if (!manifestPath) return {};
14762
- const parsed = JSON.parse(readFileSync43(manifestPath, "utf8"));
14995
+ const parsed = JSON.parse(readFileSync44(manifestPath, "utf8"));
14763
14996
  return parsed.acceptedReductions ?? {};
14764
14997
  }
14765
14998
  async function runSharedFileReductionCheck(args) {
@@ -14767,13 +15000,13 @@ async function runSharedFileReductionCheck(args) {
14767
15000
  let accepted;
14768
15001
  try {
14769
15002
  if (args.pairs) {
14770
- pairs = pairsFromTsv(args.pairs === "-" ? readStdin() : readFileSync43(args.pairs, "utf8"));
15003
+ pairs = pairsFromTsv(args.pairs === "-" ? readStdin() : readFileSync44(args.pairs, "utf8"));
14771
15004
  } else if (args.target && args.existing && args.incoming) {
14772
15005
  pairs = [
14773
15006
  {
14774
15007
  target: args.target,
14775
- existing: readFileSync43(args.existing, "utf8"),
14776
- incoming: readFileSync43(args.incoming, "utf8")
15008
+ existing: readFileSync44(args.existing, "utf8"),
15009
+ incoming: readFileSync44(args.incoming, "utf8")
14777
15010
  }
14778
15011
  ];
14779
15012
  } else {
@@ -14807,12 +15040,12 @@ async function runSharedFileReductionCheck(args) {
14807
15040
  }
14808
15041
 
14809
15042
  // src/scripts/check-skeleton-drift.ts
14810
- import { existsSync as existsSync54, readdirSync as readdirSync27 } from "fs";
14811
- import { join as join63 } from "path";
15043
+ import { existsSync as existsSync54, readdirSync as readdirSync28 } from "fs";
15044
+ import { join as join64 } from "path";
14812
15045
 
14813
15046
  // src/lib/skeleton-drift-guard.ts
14814
- import { readFileSync as readFileSync44, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
14815
- import { join as join62 } from "path";
15047
+ import { readFileSync as readFileSync45, readdirSync as readdirSync27, statSync as statSync17 } from "fs";
15048
+ import { join as join63 } from "path";
14816
15049
  var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
14817
15050
  var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
14818
15051
  var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
@@ -14870,16 +15103,16 @@ function walk(dir, base = dir) {
14870
15103
  const out = [];
14871
15104
  let entries;
14872
15105
  try {
14873
- entries = readdirSync26(dir);
15106
+ entries = readdirSync27(dir);
14874
15107
  } catch {
14875
15108
  return out;
14876
15109
  }
14877
15110
  for (const entry of entries) {
14878
15111
  if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
14879
- const abs = join62(dir, entry);
15112
+ const abs = join63(dir, entry);
14880
15113
  let isDir;
14881
15114
  try {
14882
- isDir = statSync16(abs).isDirectory();
15115
+ isDir = statSync17(abs).isDirectory();
14883
15116
  } catch {
14884
15117
  continue;
14885
15118
  }
@@ -14898,7 +15131,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
14898
15131
  if (!rule.appliesTo(rel)) continue;
14899
15132
  let contents;
14900
15133
  try {
14901
- contents = readFileSync44(join62(skeletonRoot, rel), "utf8");
15134
+ contents = readFileSync45(join63(skeletonRoot, rel), "utf8");
14902
15135
  } catch {
14903
15136
  continue;
14904
15137
  }
@@ -14927,23 +15160,23 @@ function formatViolations2(violations) {
14927
15160
 
14928
15161
  // src/scripts/check-skeleton-drift.ts
14929
15162
  function discoverSkeletons(root) {
14930
- const skeletonsDir = join63(root, "_skeletons");
15163
+ const skeletonsDir = join64(root, "_skeletons");
14931
15164
  let entries;
14932
15165
  try {
14933
- entries = readdirSync27(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
15166
+ entries = readdirSync28(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
14934
15167
  } catch {
14935
15168
  return [];
14936
15169
  }
14937
- return entries.filter((name) => existsSync54(join63(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
15170
+ return entries.filter((name) => existsSync54(join64(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
14938
15171
  }
14939
15172
  async function runSkeletonDriftCheck() {
14940
15173
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
14941
15174
  const skeletons = discoverSkeletons(root);
14942
15175
  let filesConsidered = 0;
14943
15176
  for (const name of skeletons) {
14944
- const skeletonRoot = join63(root, "_skeletons", name);
15177
+ const skeletonRoot = join64(root, "_skeletons", name);
14945
15178
  filesConsidered += findWorkflowFiles(skeletonRoot).length;
14946
- if (existsSync54(join63(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
15179
+ if (existsSync54(join64(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
14947
15180
  filesConsidered += 1;
14948
15181
  }
14949
15182
  }
@@ -14957,7 +15190,7 @@ async function runSkeletonDriftCheck() {
14957
15190
  process.exit(1);
14958
15191
  }
14959
15192
  const violations = skeletons.flatMap(
14960
- (name) => auditSkeleton(join63(root, "_skeletons", name), name)
15193
+ (name) => auditSkeleton(join64(root, "_skeletons", name), name)
14961
15194
  );
14962
15195
  if (violations.length > 0) {
14963
15196
  console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
@@ -14993,7 +15226,7 @@ async function runTerraformInputCheck() {
14993
15226
 
14994
15227
  // src/commands/check.ts
14995
15228
  var checkCommand = new Command24("check").description(
14996
- "Repo guards (ownership, release subject, plugin terraform, plugin collisions, eventbridge-log-permissions, plugin-tool-supply, core-direct-paths, instance-adoption, orphan-ratchet, cognito-invite-template, lambda-output, pipe-trap, codeql-suppression, skeleton-drift, terraform-input, plugin-allowlist-convention, migration-body-change, distribution-inventory, distribution-remote-state) run in CI and git hooks, plus out-of-band audits (branch protection, plugin-staleness)"
15229
+ "Repo guards (ownership, release subject, plugin terraform, plugin collisions, eventbridge-log-permissions, plugin-tool-supply, core-direct-paths, instance-adoption, orphan-ratchet, cognito-invite-template, lambda-output, pipe-trap, codeql-suppression, skeleton-drift, terraform-input, plugin-allowlist-convention, migration-body-change, distribution-inventory, distribution-remote-state, api-gateway-integration) run in CI and git hooks, plus out-of-band audits (branch protection, plugin-staleness)"
14997
15230
  );
14998
15231
  checkCommand.command("ownership").description("Refuse changes to template-owned paths in an instance (#370)").argument("[base]", "Base branch to diff against; defaults to $GITHUB_BASE_REF").option("--staged <messageFile>", "Check staged changes instead of a branch diff (commit hook)").allowExcessArguments(true).action(async () => {
14999
15232
  await runOwnershipCheck(rawArgsAfter("ownership"));
@@ -15117,6 +15350,11 @@ checkCommand.command("terraform-input").description(
15117
15350
  ).action(async () => {
15118
15351
  await runTerraformInputCheck();
15119
15352
  });
15353
+ checkCommand.command("api-gateway-integration").description(
15354
+ "Refuse an env-owned aws_apigatewayv2_integration targeting a Lambda's raw, unqualified function_arn when modules/cloud/aws/api-gateway already fronts that same Lambda with an alias-qualified aws_lambda_permission (#1747) \u2014 terraform validate/plan pass on both sides in isolation, so this is the only signal before a real deploy 500s (biffo-template#1900; tabsii-platform took 11 public routes down this exact way)."
15355
+ ).action(async () => {
15356
+ await runApiGatewayIntegrationCheck();
15357
+ });
15120
15358
  checkCommand.command("shared-file-reduction").description(
15121
15359
  "Refuse a shared-files.json overwrite that would DELETE tests from the satellite it lands in (#1577) \u2014 filesIfPresent is a one-way cp and nothing ever compared the two copies. Invoked by scripts/shared-sync.sh at the moment of the write, not by per-PR CI: both sides of the comparison only exist together inside a sync run. Compares TEST TITLES in TS/JS test files and nothing else \u2014 see the guard module for what it does not catch."
15122
15360
  ).option("--pairs <tsv>", "TSV of target<TAB>existing<TAB>incoming lines, or - for stdin").option("--target <path>", "Single pair: path inside the satellite").option("--existing <path>", "Single pair: the satellite's current copy").option("--incoming <path>", "Single pair: the canonical copy that would replace it").option("--manifest <path>", "shared-files.json, read for acceptedReductions").action(
@@ -15148,8 +15386,8 @@ function rawArgsAfter(subcommand) {
15148
15386
  }
15149
15387
 
15150
15388
  // src/commands/doctor.ts
15151
- import { existsSync as existsSync55, readFileSync as readFileSync45 } from "fs";
15152
- import { join as join64, resolve as resolve20 } from "path";
15389
+ import { existsSync as existsSync55, readFileSync as readFileSync46 } from "fs";
15390
+ import { join as join65, resolve as resolve20 } from "path";
15153
15391
  import chalk21 from "chalk";
15154
15392
  import { Command as Command25 } from "commander";
15155
15393
 
@@ -15544,10 +15782,10 @@ async function runDoctorFixBranches(cwd, facts, deps = { git: new GitAdapter(),
15544
15782
  return reapAllBareBranches(cwd, facts.branches, facts.worktrees, facts.currentBranch, deps);
15545
15783
  }
15546
15784
  function readLocalCoreVersion(cwd) {
15547
- const path = join64(cwd, INSTANCE_CORE_FILE);
15785
+ const path = join65(cwd, INSTANCE_CORE_FILE);
15548
15786
  if (!existsSync55(path)) return null;
15549
15787
  try {
15550
- return extractVersionField(readFileSync45(path, "utf8"));
15788
+ return extractVersionField(readFileSync46(path, "utf8"));
15551
15789
  } catch {
15552
15790
  return null;
15553
15791
  }
@@ -15567,10 +15805,10 @@ function extractVersionField(contents) {
15567
15805
  return match?.[1] ?? null;
15568
15806
  }
15569
15807
  function readFossil(cwd) {
15570
- const path = join64(cwd, CORE_VERSION_FILE);
15808
+ const path = join65(cwd, CORE_VERSION_FILE);
15571
15809
  if (!existsSync55(path)) return null;
15572
15810
  try {
15573
- const value = readFileSync45(path, "utf8").trim();
15811
+ const value = readFileSync46(path, "utf8").trim();
15574
15812
  return value === "" ? null : value;
15575
15813
  } catch {
15576
15814
  return null;
@@ -16093,18 +16331,18 @@ function resolveGithubToken4() {
16093
16331
 
16094
16332
  // src/lib/packaged-script-command.ts
16095
16333
  import { spawnSync } from "child_process";
16096
- import { chmodSync as chmodSync2, statSync as statSync17 } from "fs";
16334
+ import { chmodSync as chmodSync2, statSync as statSync18 } from "fs";
16097
16335
  import { dirname as dirname12 } from "path";
16098
16336
  import { fileURLToPath as fileURLToPath6 } from "url";
16099
16337
  import { Command as Command27 } from "commander";
16100
16338
 
16101
16339
  // src/lib/packaged-scripts.ts
16102
16340
  import { existsSync as existsSync56 } from "fs";
16103
- import { dirname as dirname11, join as join65 } from "path";
16341
+ import { dirname as dirname11, join as join66 } from "path";
16104
16342
  function findPackagedScript(startDir, relativePath) {
16105
16343
  let dir = startDir;
16106
16344
  for (; ; ) {
16107
- const candidate = join65(dir, relativePath);
16345
+ const candidate = join66(dir, relativePath);
16108
16346
  if (existsSync56(candidate)) return candidate;
16109
16347
  const parent = dirname11(dir);
16110
16348
  if (parent === dir) return null;
@@ -16119,7 +16357,7 @@ It ships with this package via cli/scripts/packaged-root-assets.mjs; if you are
16119
16357
  // src/lib/packaged-script-command.ts
16120
16358
  function ensureExecutable(script) {
16121
16359
  try {
16122
- const { mode } = statSync17(script);
16360
+ const { mode } = statSync18(script);
16123
16361
  if ((mode & 73) === 0) chmodSync2(script, mode | 493);
16124
16362
  } catch {
16125
16363
  }