@biffo/cli 0.301.27 → 0.302.0

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
@@ -11965,8 +11965,8 @@ async function runOwnershipCheck(argv) {
11965
11965
  const { stdout } = await execa("git", ["diff", "--cached", "--name-status"], { cwd: root });
11966
11966
  ({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
11967
11967
  if (messageFile) {
11968
- const { readFileSync: readFileSync45, existsSync: existsSync55 } = await import("fs");
11969
- if (existsSync55(messageFile)) commitMessage = readFileSync45(messageFile, "utf8");
11968
+ const { readFileSync: readFileSync46, existsSync: existsSync56 } = await import("fs");
11969
+ if (existsSync56(messageFile)) commitMessage = readFileSync46(messageFile, "utf8");
11970
11970
  }
11971
11971
  } else {
11972
11972
  const base = process.env["GITHUB_BASE_REF"] ?? args[0];
@@ -12066,9 +12066,253 @@ ${BOLD}If the divergence is deliberate${OFF}
12066
12066
  process.exit(1);
12067
12067
  }
12068
12068
 
12069
- // src/lib/eventbridge-log-permission-guard.ts
12070
- import { readFileSync as readFileSync34, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
12069
+ // src/lib/distribution-inventory.ts
12070
+ import { existsSync as existsSync43, readFileSync as readFileSync34 } from "fs";
12071
12071
  import { join as join47 } from "path";
12072
+ var INVENTORY_FILENAME = "distribution-inventory.json";
12073
+ function loadDistributionInventory(root) {
12074
+ const path = join47(root, INVENTORY_FILENAME);
12075
+ if (!existsSync43(path)) {
12076
+ throw new Error(
12077
+ `${INVENTORY_FILENAME} not found at ${root} -- expected it beside core-manifest.json`
12078
+ );
12079
+ }
12080
+ return JSON.parse(readFileSync34(path, "utf8"));
12081
+ }
12082
+ function validateInventory(inventory) {
12083
+ const violations = [];
12084
+ const seenIds = /* @__PURE__ */ new Set();
12085
+ for (const entry of inventory.entries) {
12086
+ if (seenIds.has(entry.id)) {
12087
+ violations.push({
12088
+ entryId: entry.id,
12089
+ rule: "duplicate-id",
12090
+ detail: "id reused by another entry"
12091
+ });
12092
+ }
12093
+ seenIds.add(entry.id);
12094
+ const channel = inventory.channels[entry.channel];
12095
+ if (!channel) {
12096
+ violations.push({
12097
+ entryId: entry.id,
12098
+ rule: "unknown-channel",
12099
+ detail: `channel "${entry.channel}" is not declared in inventory.channels`
12100
+ });
12101
+ continue;
12102
+ }
12103
+ if (entry.status === "detected" && (!channel.detectorCommand || entry.channel === "none")) {
12104
+ violations.push({
12105
+ entryId: entry.id,
12106
+ rule: "detected-with-no-detector",
12107
+ detail: `status "detected" but channel "${entry.channel}" declares no detectorCommand`
12108
+ });
12109
+ }
12110
+ if (entry.status === "unregistered" && !channel.detectorCommand) {
12111
+ violations.push({
12112
+ entryId: entry.id,
12113
+ rule: "unregistered-with-no-mechanism",
12114
+ detail: `status "unregistered" claims a mechanism exists to register with, but channel "${entry.channel}" declares no detectorCommand -- this should be "unverified" instead`
12115
+ });
12116
+ }
12117
+ if (entry.status === "unverified" && entry.channel !== "none") {
12118
+ violations.push({
12119
+ entryId: entry.id,
12120
+ rule: "unverified-on-live-channel",
12121
+ detail: `status "unverified" but channel "${entry.channel}" has a real detector -- this is "unregistered" (mechanism exists, artifact not added to it), not "unverified" (no mechanism at all)`
12122
+ });
12123
+ }
12124
+ if ((entry.status === "unregistered" || entry.status === "unverified") && !entry.gapReason) {
12125
+ violations.push({
12126
+ entryId: entry.id,
12127
+ rule: "gap-with-no-reason",
12128
+ detail: `status "${entry.status}" requires a gapReason explaining what is missing and why`
12129
+ });
12130
+ }
12131
+ if (entry.evidence.length === 0) {
12132
+ violations.push({
12133
+ entryId: entry.id,
12134
+ rule: "no-evidence",
12135
+ detail: "evidence must name at least one issue or file"
12136
+ });
12137
+ }
12138
+ if (entry.targets.length === 0) {
12139
+ violations.push({
12140
+ entryId: entry.id,
12141
+ rule: "no-targets",
12142
+ detail: "targets must name at least one destination"
12143
+ });
12144
+ }
12145
+ for (const assertion of entry.remoteContentAssertions ?? []) {
12146
+ if (!assertion.repo || !assertion.path || !assertion.ref) {
12147
+ violations.push({
12148
+ entryId: entry.id,
12149
+ rule: "incomplete-remote-content-assertion",
12150
+ detail: `remoteContentAssertions entry missing repo/path/ref: ${JSON.stringify(assertion)}`
12151
+ });
12152
+ }
12153
+ if ((assertion.mustContain?.length ?? 0) === 0 && (assertion.mustNotContain?.length ?? 0) === 0) {
12154
+ violations.push({
12155
+ entryId: entry.id,
12156
+ rule: "empty-remote-content-assertion",
12157
+ detail: `remoteContentAssertions entry for ${assertion.repo}/${assertion.path} states neither mustContain nor mustNotContain -- checks nothing`
12158
+ });
12159
+ }
12160
+ }
12161
+ }
12162
+ return violations;
12163
+ }
12164
+ function checkRemoteContentAssertions(inventory, fetchedContent) {
12165
+ const violations = [];
12166
+ for (const entry of inventory.entries) {
12167
+ for (const assertion of entry.remoteContentAssertions ?? []) {
12168
+ const key = `${assertion.repo}
12169
+ ${assertion.path}
12170
+ ${assertion.ref}`;
12171
+ const content = fetchedContent.get(key);
12172
+ if (content === void 0) {
12173
+ violations.push({
12174
+ entryId: entry.id,
12175
+ assertion,
12176
+ rule: "fetch-failed",
12177
+ detail: `no fetched content supplied for ${key.replace(/\n/g, " @ ")}`
12178
+ });
12179
+ continue;
12180
+ }
12181
+ if (content === null) {
12182
+ violations.push({
12183
+ entryId: entry.id,
12184
+ assertion,
12185
+ rule: "fetch-failed",
12186
+ detail: `fetching ${assertion.repo}/${assertion.path}@${assertion.ref} failed`
12187
+ });
12188
+ continue;
12189
+ }
12190
+ for (const needle of assertion.mustContain ?? []) {
12191
+ if (!content.includes(needle)) {
12192
+ violations.push({
12193
+ entryId: entry.id,
12194
+ assertion,
12195
+ rule: "missing-required-substring",
12196
+ detail: `${assertion.repo}/${assertion.path}@${assertion.ref} no longer contains "${needle}" -- the entry's gapReason claims this is (still) the current state`
12197
+ });
12198
+ }
12199
+ }
12200
+ for (const needle of assertion.mustNotContain ?? []) {
12201
+ if (content.includes(needle)) {
12202
+ violations.push({
12203
+ entryId: entry.id,
12204
+ assertion,
12205
+ rule: "contains-forbidden-substring",
12206
+ detail: `${assertion.repo}/${assertion.path}@${assertion.ref} still contains "${needle}" -- the entry's gapReason claims this was removed/changed`
12207
+ });
12208
+ }
12209
+ }
12210
+ }
12211
+ }
12212
+ return violations;
12213
+ }
12214
+ async function fetchRemoteContentViaGh(repo, path, ref, execCommand) {
12215
+ const result = await execCommand("gh", [
12216
+ "api",
12217
+ `repos/${repo}/contents/${path}?ref=${ref}`,
12218
+ "--jq",
12219
+ ".content"
12220
+ ]);
12221
+ if (result.exitCode !== 0) return null;
12222
+ const base64 = result.stdout.trim();
12223
+ if (!base64) return null;
12224
+ try {
12225
+ return Buffer.from(base64, "base64").toString("utf8");
12226
+ } catch {
12227
+ return null;
12228
+ }
12229
+ }
12230
+
12231
+ // src/scripts/check-distribution-inventory.ts
12232
+ async function runDistributionInventoryCheck(root) {
12233
+ const repoRoot = root ?? (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12234
+ const inventory = loadDistributionInventory(repoRoot);
12235
+ const violations = validateInventory(inventory);
12236
+ console.log(
12237
+ `distribution-inventory: examined ${inventory.entries.length} inventory entr(ies) across ${Object.keys(inventory.channels).length} declared channel(s) in distribution-inventory.json (${violations.length} schema violation(s))`
12238
+ );
12239
+ if (violations.length === 0) {
12240
+ console.log("\u2713 distribution-inventory: every entry is well-formed and internally consistent");
12241
+ return;
12242
+ }
12243
+ console.error(
12244
+ `\u2717 distribution-inventory: ${violations.length} schema violation(s) \u2014 an entry is malformed, half-classified, or inconsistent with the channel it names:
12245
+ `
12246
+ );
12247
+ for (const v of violations) {
12248
+ console.error(` ${v.entryId} [${v.rule}]: ${v.detail}`);
12249
+ }
12250
+ process.exit(1);
12251
+ }
12252
+
12253
+ // src/scripts/check-distribution-remote-state.ts
12254
+ import { join as join48 } from "path";
12255
+ async function ghExecCommand(file, args) {
12256
+ const result = await execa(file, args, { reject: false });
12257
+ return { stdout: String(result.stdout ?? ""), exitCode: result.exitCode ?? null };
12258
+ }
12259
+ async function runDistributionRemoteStateCheck(root) {
12260
+ const repoRoot = root ?? (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12261
+ const inventory = loadDistributionInventory(repoRoot);
12262
+ const assertions = [];
12263
+ for (const entry of inventory.entries) {
12264
+ for (const assertion of entry.remoteContentAssertions ?? []) {
12265
+ assertions.push({ entryId: entry.id, assertion });
12266
+ }
12267
+ }
12268
+ console.log(
12269
+ `distribution-remote-state: examined ${assertions.length} remote content assertion(s) declared across ${inventory.entries.length} inventory entries (${join48(repoRoot, "distribution-inventory.json")})`
12270
+ );
12271
+ if (assertions.length === 0) {
12272
+ console.log("\u2713 distribution-remote-state: nothing declared to check");
12273
+ return;
12274
+ }
12275
+ const fetched = /* @__PURE__ */ new Map();
12276
+ let fetchFailures = 0;
12277
+ for (const { assertion } of assertions) {
12278
+ const key = `${assertion.repo}
12279
+ ${assertion.path}
12280
+ ${assertion.ref}`;
12281
+ if (fetched.has(key)) continue;
12282
+ const content = await fetchRemoteContentViaGh(
12283
+ assertion.repo,
12284
+ assertion.path,
12285
+ assertion.ref,
12286
+ ghExecCommand
12287
+ );
12288
+ fetched.set(key, content);
12289
+ if (content === null) fetchFailures += 1;
12290
+ }
12291
+ if (fetchFailures > 0) {
12292
+ console.error(
12293
+ `\u2717 distribution-remote-state: could not fetch ${fetchFailures} of ${fetched.size} distinct remote file(s) -- no BIFFO_GITHUB_TOKEN, network failure, or the file/ref no longer exists. Cannot tell whether the remaining claims hold.`
12294
+ );
12295
+ process.exit(2);
12296
+ }
12297
+ const violations = checkRemoteContentAssertions(inventory, fetched);
12298
+ if (violations.length === 0) {
12299
+ console.log(
12300
+ `\u2713 distribution-remote-state: all ${assertions.length} assertion(s) match real content`
12301
+ );
12302
+ return;
12303
+ }
12304
+ console.error(
12305
+ `\u2717 distribution-remote-state: ${violations.length} assertion(s) no longer match real content -- the affected entry's gapReason is stale (#1570/#1816):`
12306
+ );
12307
+ for (const v of violations) {
12308
+ console.error(` ${v.entryId} [${v.rule}]: ${v.detail}`);
12309
+ }
12310
+ process.exit(1);
12311
+ }
12312
+
12313
+ // src/lib/eventbridge-log-permission-guard.ts
12314
+ import { readFileSync as readFileSync35, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
12315
+ import { join as join49 } from "path";
12072
12316
  var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
12073
12317
  var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
12074
12318
  var LOG_RESOURCE_POLICY_TYPE = "aws_cloudwatch_log_resource_policy";
@@ -12145,7 +12389,7 @@ function walkTerraformFiles(root) {
12145
12389
  return;
12146
12390
  }
12147
12391
  for (const entry of entries) {
12148
- const p = join47(dir, entry);
12392
+ const p = join49(dir, entry);
12149
12393
  let st;
12150
12394
  try {
12151
12395
  st = statSync12(p);
@@ -12188,7 +12432,7 @@ function auditEventBridgeLogPermissions(root) {
12188
12432
  let rawEventTargetCount = 0;
12189
12433
  let rawLogPolicyCount = 0;
12190
12434
  for (const file of files) {
12191
- const text = readFileSync34(file, "utf8");
12435
+ const text = readFileSync35(file, "utf8");
12192
12436
  rawEventTargetCount += countRawResourceDeclarations(text, EVENT_TARGET_TYPE);
12193
12437
  rawLogPolicyCount += countRawResourceDeclarations(text, LOG_RESOURCE_POLICY_TYPE);
12194
12438
  eventTargetBlocks.push(...findResourceBlocks(text, file, EVENT_TARGET_TYPE));
@@ -12278,8 +12522,8 @@ async function runEventBridgeLogPermissionCheck() {
12278
12522
  }
12279
12523
 
12280
12524
  // src/scripts/check-instance-adoption.ts
12281
- import { existsSync as existsSync43 } from "fs";
12282
- import { join as join48 } from "path";
12525
+ import { existsSync as existsSync44 } from "fs";
12526
+ import { join as join50 } from "path";
12283
12527
  async function runInstanceAdoptionCheck(opts = {}) {
12284
12528
  if (!opts.instanceDir) {
12285
12529
  console.error(
@@ -12287,7 +12531,7 @@ async function runInstanceAdoptionCheck(opts = {}) {
12287
12531
  );
12288
12532
  process.exit(2);
12289
12533
  }
12290
- if (!existsSync43(opts.instanceDir)) {
12534
+ if (!existsSync44(opts.instanceDir)) {
12291
12535
  console.error(
12292
12536
  `\u2717 instance-adoption guard: --instance-dir ${opts.instanceDir} does not exist \u2014 cannot tell whether it is adopted, and that is not the same as a clean pass.`
12293
12537
  );
@@ -12295,7 +12539,7 @@ async function runInstanceAdoptionCheck(opts = {}) {
12295
12539
  }
12296
12540
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12297
12541
  const theirsDir = opts.theirsDir ?? root;
12298
- const instanceLabel = opts.instance ?? join48(opts.instanceDir).split("/").filter(Boolean).pop();
12542
+ const instanceLabel = opts.instance ?? join50(opts.instanceDir).split("/").filter(Boolean).pop();
12299
12543
  const report = checkInstanceAdoption(theirsDir, opts.instanceDir);
12300
12544
  console.log(
12301
12545
  `examined ${report.examinedInstances} instance (${instanceLabel}) against ${report.registeredPairs} registered pair(s), ${report.applicablePairs} applicable to this instance, against template tree ${theirsDir}`
@@ -12317,12 +12561,12 @@ async function runInstanceAdoptionCheck(opts = {}) {
12317
12561
  }
12318
12562
 
12319
12563
  // src/lib/lambda-output-guard.ts
12320
- import { readFileSync as readFileSync36 } from "fs";
12321
- import { join as join50 } from "path";
12564
+ import { readFileSync as readFileSync37 } from "fs";
12565
+ import { join as join52 } from "path";
12322
12566
 
12323
12567
  // src/lib/terraform-input-guard.ts
12324
- import { existsSync as existsSync44, readdirSync as readdirSync21, readFileSync as readFileSync35, statSync as statSync13 } from "fs";
12325
- import { join as join49 } from "path";
12568
+ import { existsSync as existsSync45, readdirSync as readdirSync21, readFileSync as readFileSync36, statSync as statSync13 } from "fs";
12569
+ import { join as join51 } from "path";
12326
12570
  var GUARDED_SUBCOMMANDS = [
12327
12571
  "init",
12328
12572
  "plan",
@@ -12336,7 +12580,7 @@ function stripComments2(source) {
12336
12580
  return source.split("\n").map((line) => line.replace(/(^|\s)#.*$/, "$1")).join("\n");
12337
12581
  }
12338
12582
  function vendoredPluginServiceDirs(repoRoot) {
12339
- const servicesDir = join49(repoRoot, "services");
12583
+ const servicesDir = join51(repoRoot, "services");
12340
12584
  const result = /* @__PURE__ */ new Set();
12341
12585
  let entries;
12342
12586
  try {
@@ -12345,9 +12589,9 @@ function vendoredPluginServiceDirs(repoRoot) {
12345
12589
  return result;
12346
12590
  }
12347
12591
  for (const entry of entries) {
12348
- const full = join49(servicesDir, entry);
12349
- if (!existsSync44(full) || !statSync13(full).isDirectory()) continue;
12350
- if (existsSync44(join49(full, "biffo.plugin.json"))) {
12592
+ const full = join51(servicesDir, entry);
12593
+ if (!existsSync45(full) || !statSync13(full).isDirectory()) continue;
12594
+ if (existsSync45(join51(full, "biffo.plugin.json"))) {
12351
12595
  result.add(entry);
12352
12596
  }
12353
12597
  }
@@ -12369,7 +12613,7 @@ function findWorkflowFiles(repoRoot) {
12369
12613
  if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
12370
12614
  continue;
12371
12615
  }
12372
- const full = join49(dir, entry);
12616
+ const full = join51(dir, entry);
12373
12617
  const rel = relative11 ? `${relative11}/${entry}` : entry;
12374
12618
  let isDir;
12375
12619
  try {
@@ -12419,7 +12663,7 @@ function checkWorkflowSource(file, rawSource) {
12419
12663
  }
12420
12664
  function checkTerraformInput(repoRoot) {
12421
12665
  return findWorkflowFiles(repoRoot).flatMap(
12422
- (file) => checkWorkflowSource(file, readFileSync35(join49(repoRoot, file), "utf8"))
12666
+ (file) => checkWorkflowSource(file, readFileSync36(join51(repoRoot, file), "utf8"))
12423
12667
  );
12424
12668
  }
12425
12669
 
@@ -12477,7 +12721,7 @@ function checkWorkflowSource2(file, rawSource) {
12477
12721
  }
12478
12722
  function checkLambdaOutput(repoRoot) {
12479
12723
  return findWorkflowFiles(repoRoot).flatMap(
12480
- (file) => checkWorkflowSource2(file, readFileSync36(join50(repoRoot, file), "utf8"))
12724
+ (file) => checkWorkflowSource2(file, readFileSync37(join52(repoRoot, file), "utf8"))
12481
12725
  );
12482
12726
  }
12483
12727
 
@@ -12505,8 +12749,8 @@ async function runLambdaOutputCheck() {
12505
12749
  }
12506
12750
 
12507
12751
  // src/scripts/check-migration-body-change.ts
12508
- import { existsSync as existsSync45 } from "fs";
12509
- import { join as join51 } from "path";
12752
+ import { existsSync as existsSync46 } from "fs";
12753
+ import { join as join53 } from "path";
12510
12754
 
12511
12755
  // src/lib/migration-body-change-guard.ts
12512
12756
  function checkMigrationBodyChangeMarkers(diffs) {
@@ -12572,7 +12816,7 @@ async function runMigrationBodyChangeCheck(argv) {
12572
12816
  );
12573
12817
  return;
12574
12818
  }
12575
- if (!existsSync45(join51(root, MIGRATIONS_VERSIONS_DIR))) {
12819
+ if (!existsSync46(join53(root, MIGRATIONS_VERSIONS_DIR))) {
12576
12820
  console.log(`\u2713 migration body-change guard: no ${MIGRATIONS_VERSIONS_DIR} in this repo.`);
12577
12821
  return;
12578
12822
  }
@@ -12674,7 +12918,7 @@ ${BOLD2}What to do${OFF2}
12674
12918
  }
12675
12919
 
12676
12920
  // src/scripts/check-orphan-ratchet.ts
12677
- import { existsSync as existsSync46 } from "fs";
12921
+ import { existsSync as existsSync47 } from "fs";
12678
12922
  import { basename as basename3 } from "path";
12679
12923
  function reportOrphan(entry, manifest) {
12680
12924
  console.error(` ${entry.path}`);
@@ -12697,7 +12941,7 @@ function reportOrphan(entry, manifest) {
12697
12941
  }
12698
12942
  }
12699
12943
  async function runOrphanRatchetCheck(opts = {}) {
12700
- if (opts.instanceDir !== void 0 && !existsSync46(opts.instanceDir)) {
12944
+ if (opts.instanceDir !== void 0 && !existsSync47(opts.instanceDir)) {
12701
12945
  console.error(
12702
12946
  `\u2717 orphan-ratchet guard: --instance-dir ${opts.instanceDir} does not exist \u2014 cannot tell whether it carries unsanctioned files, and that is not the same as a clean pass.`
12703
12947
  );
@@ -12739,8 +12983,8 @@ baseline was ${String(ratchet.baseline)}, now ${String(ratchet.count)}. See biff
12739
12983
  }
12740
12984
 
12741
12985
  // src/scripts/check-pipe-trap.ts
12742
- import { readFileSync as readFileSync37, readdirSync as readdirSync22 } from "fs";
12743
- import { join as join52, relative as relative9 } from "path";
12986
+ import { readFileSync as readFileSync38, readdirSync as readdirSync22 } from "fs";
12987
+ import { join as join54, relative as relative9 } from "path";
12744
12988
 
12745
12989
  // src/lib/pipe-trap-guard.ts
12746
12990
  var STATUS_BEARING = [
@@ -12836,7 +13080,7 @@ function findPipeTraps(source) {
12836
13080
  function shellFiles(root) {
12837
13081
  const out = [];
12838
13082
  for (const dir of ["scripts", ".githooks"]) {
12839
- const full = join52(root, dir);
13083
+ const full = join54(root, dir);
12840
13084
  let entries;
12841
13085
  try {
12842
13086
  entries = readdirSync22(full, { withFileTypes: true });
@@ -12846,7 +13090,7 @@ function shellFiles(root) {
12846
13090
  for (const entry of entries) {
12847
13091
  if (!entry.isFile()) continue;
12848
13092
  if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
12849
- out.push(join52(full, entry.name));
13093
+ out.push(join54(full, entry.name));
12850
13094
  }
12851
13095
  }
12852
13096
  return out;
@@ -12862,7 +13106,7 @@ async function runPipeTrapCheck() {
12862
13106
  process.exit(1);
12863
13107
  }
12864
13108
  const findings = files.flatMap(
12865
- (file) => findPipeTraps(readFileSync37(file, "utf8")).map(
13109
+ (file) => findPipeTraps(readFileSync38(file, "utf8")).map(
12866
13110
  (t) => `${relative9(root, file)}:${t.line} ${t.text}
12867
13111
  ${t.reason}`
12868
13112
  )
@@ -12879,8 +13123,8 @@ async function runPipeTrapCheck() {
12879
13123
  }
12880
13124
 
12881
13125
  // src/lib/plugin-allowlist-convention.ts
12882
- import { readFileSync as readFileSync38 } from "fs";
12883
- import { join as join53 } from "path";
13126
+ import { readFileSync as readFileSync39 } from "fs";
13127
+ import { join as join55 } from "path";
12884
13128
  var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
12885
13129
  var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
12886
13130
  var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
@@ -12891,7 +13135,7 @@ var PLUGIN = "<plugin>";
12891
13135
  var ACCOUNT = "<account>";
12892
13136
  function read(repoRoot, relative11) {
12893
13137
  try {
12894
- return readFileSync38(join53(repoRoot, relative11), "utf8");
13138
+ return readFileSync39(join55(repoRoot, relative11), "utf8");
12895
13139
  } catch {
12896
13140
  throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
12897
13141
  }
@@ -13012,32 +13256,32 @@ async function runPluginAllowlistConventionCheck() {
13012
13256
  }
13013
13257
 
13014
13258
  // src/scripts/check-plugin-collisions.ts
13015
- import { existsSync as existsSync48 } from "fs";
13016
- import { join as join55 } from "path";
13259
+ import { existsSync as existsSync49 } from "fs";
13260
+ import { join as join57 } from "path";
13017
13261
 
13018
13262
  // src/lib/plugin-collision-guard.ts
13019
- import { existsSync as existsSync47, readdirSync as readdirSync23, statSync as statSync14 } from "fs";
13020
- import { join as join54 } from "path";
13263
+ import { existsSync as existsSync48, readdirSync as readdirSync23, statSync as statSync14 } from "fs";
13264
+ import { join as join56 } from "path";
13021
13265
  var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
13022
13266
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
13023
13267
  function subdirectories(dir) {
13024
- if (!existsSync47(dir)) return [];
13268
+ if (!existsSync48(dir)) return [];
13025
13269
  return readdirSync23(dir).filter((entry) => {
13026
13270
  if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
13027
13271
  try {
13028
- return statSync14(join54(dir, entry)).isDirectory();
13272
+ return statSync14(join56(dir, entry)).isDirectory();
13029
13273
  } catch {
13030
13274
  return false;
13031
13275
  }
13032
13276
  });
13033
13277
  }
13034
13278
  function regularPackagesOf(pluginDir2) {
13035
- return subdirectories(pluginDir2).filter((name) => existsSync47(join54(pluginDir2, name, "__init__.py"))).sort();
13279
+ return subdirectories(pluginDir2).filter((name) => existsSync48(join56(pluginDir2, name, "__init__.py"))).sort();
13036
13280
  }
13037
13281
  function bareTestModulesOf(pluginDir2) {
13038
- const testsDir = join54(pluginDir2, "tests");
13039
- if (!existsSync47(testsDir)) return [];
13040
- if (existsSync47(join54(testsDir, "__init__.py"))) return [];
13282
+ const testsDir = join56(pluginDir2, "tests");
13283
+ if (!existsSync48(testsDir)) return [];
13284
+ if (existsSync48(join56(testsDir, "__init__.py"))) return [];
13041
13285
  return readdirSync23(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
13042
13286
  }
13043
13287
  function findCollisions(servicesDir, pluginDirs) {
@@ -13046,7 +13290,7 @@ function findCollisions(servicesDir, pluginDirs) {
13046
13290
  const gather = (kind, namesOf) => {
13047
13291
  const claims = /* @__PURE__ */ new Map();
13048
13292
  for (const plugin of plugins) {
13049
- for (const name of namesOf(join54(servicesDir, plugin))) {
13293
+ for (const name of namesOf(join56(servicesDir, plugin))) {
13050
13294
  claims.set(name, [...claims.get(name) ?? [], plugin]);
13051
13295
  }
13052
13296
  }
@@ -13084,8 +13328,8 @@ function formatCollisions(collisions) {
13084
13328
  // src/scripts/check-plugin-collisions.ts
13085
13329
  async function runPluginCollisionCheck() {
13086
13330
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13087
- const servicesDir = join55(root, "services");
13088
- if (!existsSync48(servicesDir)) {
13331
+ const servicesDir = join57(root, "services");
13332
+ if (!existsSync49(servicesDir)) {
13089
13333
  console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
13090
13334
  return;
13091
13335
  }
@@ -13102,8 +13346,8 @@ async function runPluginCollisionCheck() {
13102
13346
  }
13103
13347
 
13104
13348
  // src/lib/plugin-terraform-guard.ts
13105
- import { existsSync as existsSync49, readFileSync as readFileSync39, readdirSync as readdirSync24 } from "fs";
13106
- import { dirname as dirname10, join as join56, relative as relative10, sep as sep4 } from "path";
13349
+ import { existsSync as existsSync50, readFileSync as readFileSync40, readdirSync as readdirSync24 } from "fs";
13350
+ import { dirname as dirname10, join as join58, relative as relative10, sep as sep4 } from "path";
13107
13351
  var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
13108
13352
  var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
13109
13353
  function findPluginManifests(root) {
@@ -13118,9 +13362,9 @@ function findPluginManifests(root) {
13118
13362
  for (const entry of entries) {
13119
13363
  if (entry.isDirectory()) {
13120
13364
  if (SKIP_DIRS3.has(entry.name)) continue;
13121
- walk2(join56(dir, entry.name));
13365
+ walk2(join58(dir, entry.name));
13122
13366
  } else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
13123
- found.push(relative10(root, join56(dir, entry.name)).split(sep4).join("/"));
13367
+ found.push(relative10(root, join58(dir, entry.name)).split(sep4).join("/"));
13124
13368
  }
13125
13369
  }
13126
13370
  };
@@ -13130,7 +13374,7 @@ function findPluginManifests(root) {
13130
13374
  function readSubscriptions(absManifestPath) {
13131
13375
  let parsed;
13132
13376
  try {
13133
- parsed = JSON.parse(readFileSync39(absManifestPath, "utf8"));
13377
+ parsed = JSON.parse(readFileSync40(absManifestPath, "utf8"));
13134
13378
  } catch {
13135
13379
  return null;
13136
13380
  }
@@ -13145,14 +13389,14 @@ function readSubscriptions(absManifestPath) {
13145
13389
  }
13146
13390
  function checkPluginTerraform(root) {
13147
13391
  const violations = [];
13148
- const coreManifest = existsSync49(join56(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
13392
+ const coreManifest = existsSync50(join58(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
13149
13393
  for (const manifest of findPluginManifests(root)) {
13150
13394
  if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
13151
- const absManifest = join56(root, manifest);
13395
+ const absManifest = join58(root, manifest);
13152
13396
  const subscriptions = readSubscriptions(absManifest);
13153
13397
  if (subscriptions === null) continue;
13154
13398
  const pluginDir2 = dirname10(absManifest);
13155
- if (existsSync49(join56(pluginDir2, "terraform"))) continue;
13399
+ if (existsSync50(join58(pluginDir2, "terraform"))) continue;
13156
13400
  const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
13157
13401
  violations.push({
13158
13402
  manifest,
@@ -13183,12 +13427,12 @@ async function runPluginTerraformCheck() {
13183
13427
  }
13184
13428
 
13185
13429
  // src/scripts/check-plugin-tool-supply.ts
13186
- import { existsSync as existsSync51 } from "fs";
13187
- import { join as join58 } from "path";
13430
+ import { existsSync as existsSync52 } from "fs";
13431
+ import { join as join60 } from "path";
13188
13432
 
13189
13433
  // src/lib/plugin-tool-supply-audit.ts
13190
- import { existsSync as existsSync50, readFileSync as readFileSync40, readdirSync as readdirSync25, statSync as statSync15 } from "fs";
13191
- import { join as join57 } from "path";
13434
+ import { existsSync as existsSync51, readFileSync as readFileSync41, readdirSync as readdirSync25, statSync as statSync15 } from "fs";
13435
+ import { join as join59 } from "path";
13192
13436
 
13193
13437
  // src/lib/openrouter-model-snapshot.ts
13194
13438
  var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
@@ -13605,7 +13849,7 @@ function listDirs(root) {
13605
13849
  }
13606
13850
  return entries.filter((e) => {
13607
13851
  try {
13608
- return statSync15(join57(root, e)).isDirectory();
13852
+ return statSync15(join59(root, e)).isDirectory();
13609
13853
  } catch {
13610
13854
  return false;
13611
13855
  }
@@ -13621,7 +13865,7 @@ function walkFiles2(root, accept, skipDir) {
13621
13865
  return;
13622
13866
  }
13623
13867
  for (const entry of entries) {
13624
- const p = join57(dir, entry);
13868
+ const p = join59(dir, entry);
13625
13869
  let st;
13626
13870
  try {
13627
13871
  st = statSync15(p);
@@ -13647,14 +13891,14 @@ function pluginPythonFiles(pluginDir2) {
13647
13891
  );
13648
13892
  }
13649
13893
  function pluginTerraformFiles(pluginDir2) {
13650
- const tfDir = join57(pluginDir2, "terraform");
13894
+ const tfDir = join59(pluginDir2, "terraform");
13651
13895
  let entries;
13652
13896
  try {
13653
13897
  entries = readdirSync25(tfDir);
13654
13898
  } catch {
13655
13899
  return [];
13656
13900
  }
13657
- return entries.filter((e) => e.endsWith(".tf")).map((e) => join57(tfDir, e)).sort();
13901
+ return entries.filter((e) => e.endsWith(".tf")).map((e) => join59(tfDir, e)).sort();
13658
13902
  }
13659
13903
  function extractManifestTools(manifestText) {
13660
13904
  let parsed;
@@ -13906,8 +14150,8 @@ function isSnapshotStale(fetchedAt, now) {
13906
14150
  function normalizeModelId(id) {
13907
14151
  return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
13908
14152
  }
13909
- var CONFIG_PY_PATH = join57("services", "api", "src", "api", "config.py");
13910
- var ORCHESTRATION_SCHEMA_PATH = join57(
14153
+ var CONFIG_PY_PATH = join59("services", "api", "src", "api", "config.py");
14154
+ var ORCHESTRATION_SCHEMA_PATH = join59(
13911
14155
  "services",
13912
14156
  "api",
13913
14157
  "src",
@@ -13919,10 +14163,10 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
13919
14163
  const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
13920
14164
  const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
13921
14165
  const now = options.now ?? /* @__PURE__ */ new Date();
13922
- const configPath = join57(repoRoot, CONFIG_PY_PATH);
13923
- const orchestrationPath = join57(repoRoot, ORCHESTRATION_SCHEMA_PATH);
13924
- const configMissing = !existsSync50(configPath);
13925
- const orchestrationSchemaMissing = !existsSync50(orchestrationPath);
14166
+ const configPath = join59(repoRoot, CONFIG_PY_PATH);
14167
+ const orchestrationPath = join59(repoRoot, ORCHESTRATION_SCHEMA_PATH);
14168
+ const configMissing = !existsSync51(configPath);
14169
+ const orchestrationSchemaMissing = !existsSync51(orchestrationPath);
13926
14170
  const knownSet = new Set(knownModelIds);
13927
14171
  const snapshotEmpty = knownModelIds.length === 0;
13928
14172
  const snapshotStale = isSnapshotStale(snapshotFetchedAt, now);
@@ -13940,13 +14184,13 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
13940
14184
  };
13941
14185
  let settingsBlind = false;
13942
14186
  if (!configMissing) {
13943
- const settingsFields = extractSettingsModelFields(readFileSync40(configPath, "utf8"));
14187
+ const settingsFields = extractSettingsModelFields(readFileSync41(configPath, "utf8"));
13944
14188
  if (settingsFields.length === 0) settingsBlind = true;
13945
14189
  for (const { field, value } of settingsFields) record(`${CONFIG_PY_PATH}#${field}`, value);
13946
14190
  }
13947
14191
  let curatedFieldsBlind = false;
13948
14192
  if (!orchestrationSchemaMissing) {
13949
- const curated = extractCuratedModelFields(readFileSync40(orchestrationPath, "utf8"));
14193
+ const curated = extractCuratedModelFields(readFileSync41(orchestrationPath, "utf8"));
13950
14194
  if (curated.rawFieldCount > 0 && curated.fields.every((f) => f.defaultValue === null && f.optionValues.length === 0)) {
13951
14195
  curatedFieldsBlind = true;
13952
14196
  }
@@ -13993,7 +14237,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
13993
14237
  function discoverPluginDirs(pluginsRoot) {
13994
14238
  return listDirs(pluginsRoot).filter((name) => {
13995
14239
  try {
13996
- return statSync15(join57(pluginsRoot, name, "biffo.plugin.json")).isFile();
14240
+ return statSync15(join59(pluginsRoot, name, "biffo.plugin.json")).isFile();
13997
14241
  } catch {
13998
14242
  return false;
13999
14243
  }
@@ -14006,8 +14250,8 @@ function auditPluginToolSupply(pluginsRoot) {
14006
14250
  let terraformBlind = false;
14007
14251
  let totalDeclaredTools = 0;
14008
14252
  for (const name of pluginNames) {
14009
- const pluginDir2 = join57(pluginsRoot, name);
14010
- const manifestText = readFileSync40(join57(pluginDir2, "biffo.plugin.json"), "utf8");
14253
+ const pluginDir2 = join59(pluginsRoot, name);
14254
+ const manifestText = readFileSync41(join59(pluginDir2, "biffo.plugin.json"), "utf8");
14011
14255
  const manifest = extractManifestTools(manifestText);
14012
14256
  if (manifest.parseError) {
14013
14257
  findings.push({
@@ -14025,13 +14269,13 @@ function auditPluginToolSupply(pluginsRoot) {
14025
14269
  totalDeclaredTools += manifest.tools.length;
14026
14270
  const pySources = pluginPythonFiles(pluginDir2).map((f) => ({
14027
14271
  file: f,
14028
- text: readFileSync40(f, "utf8")
14272
+ text: readFileSync41(f, "utf8")
14029
14273
  }));
14030
14274
  const resolver = buildSymbolResolver(pySources);
14031
14275
  const registry = extractToolRegistryEntries(pySources, resolver);
14032
14276
  if (registry.rawToolDefinitionCount > 0 && registry.entries.length === 0) registryBlind = true;
14033
14277
  const tfFiles = pluginTerraformFiles(pluginDir2);
14034
- const tfText = tfFiles.map((f) => readFileSync40(f, "utf8")).join("\n");
14278
+ const tfText = tfFiles.map((f) => readFileSync41(f, "utf8")).join("\n");
14035
14279
  const terraform = extractTerraformEnvKeys(tfText);
14036
14280
  if (terraform.rawMarkerCount > 0 && terraform.resolvedBlockCount === 0) terraformBlind = true;
14037
14281
  for (const toolName of manifest.tools) {
@@ -14105,7 +14349,7 @@ function auditPluginToolSupply(pluginsRoot) {
14105
14349
  requiredEnvVars: envResult.envVars,
14106
14350
  missingEnvVars: anyWired ? [] : envResult.envVars,
14107
14351
  status: anyWired ? "ok" : "missing-env",
14108
- 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 ${join57(pluginDir2, "terraform")}, so this deployment can never supply it`
14352
+ 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 ${join59(pluginDir2, "terraform")}, so this deployment can never supply it`
14109
14353
  });
14110
14354
  }
14111
14355
  }
@@ -14138,8 +14382,8 @@ function auditPluginToolSupply(pluginsRoot) {
14138
14382
  async function runPluginToolSupplyCheck() {
14139
14383
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
14140
14384
  let allOk = true;
14141
- const pluginsRoot = join58(root, "services", "_plugins");
14142
- if (!existsSync51(pluginsRoot)) {
14385
+ const pluginsRoot = join60(root, "services", "_plugins");
14386
+ if (!existsSync52(pluginsRoot)) {
14143
14387
  console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
14144
14388
  } else {
14145
14389
  const report = auditPluginToolSupply(pluginsRoot);
@@ -14169,8 +14413,8 @@ async function runPluginToolSupplyCheck() {
14169
14413
  console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
14170
14414
  }
14171
14415
  }
14172
- const servicesApiRoot = join58(root, "services", "api");
14173
- if (!existsSync51(servicesApiRoot)) {
14416
+ const servicesApiRoot = join60(root, "services", "api");
14417
+ if (!existsSync52(servicesApiRoot)) {
14174
14418
  console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
14175
14419
  } else {
14176
14420
  const modelReport = auditDeclaredModelIds(root);
@@ -14344,10 +14588,10 @@ async function runReleaseSubjectCheck(argv) {
14344
14588
  }
14345
14589
 
14346
14590
  // src/scripts/check-shared-file-reduction.ts
14347
- import { readFileSync as readFileSync42 } from "fs";
14591
+ import { readFileSync as readFileSync43 } from "fs";
14348
14592
 
14349
14593
  // src/lib/shared-file-reduction-guard.ts
14350
- import { readFileSync as readFileSync41 } from "fs";
14594
+ import { readFileSync as readFileSync42 } from "fs";
14351
14595
  var LEAF_TEST_CALLS = /* @__PURE__ */ new Set(["it", "test"]);
14352
14596
  var SUITE_CALLS = /* @__PURE__ */ new Set(["describe", "suite"]);
14353
14597
  var TEST_FILE_PATTERN = /\.(test|spec)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
@@ -14474,7 +14718,7 @@ function formatReductionReport(report) {
14474
14718
  // src/scripts/check-shared-file-reduction.ts
14475
14719
  function readStdin() {
14476
14720
  try {
14477
- return readFileSync42(0, "utf8");
14721
+ return readFileSync43(0, "utf8");
14478
14722
  } catch {
14479
14723
  return "";
14480
14724
  }
@@ -14491,15 +14735,15 @@ function pairsFromTsv(tsv) {
14491
14735
  const [target, existingPath, incomingPath] = fields;
14492
14736
  pairs.push({
14493
14737
  target,
14494
- existing: readFileSync42(existingPath, "utf8"),
14495
- incoming: readFileSync42(incomingPath, "utf8")
14738
+ existing: readFileSync43(existingPath, "utf8"),
14739
+ incoming: readFileSync43(incomingPath, "utf8")
14496
14740
  });
14497
14741
  }
14498
14742
  return pairs;
14499
14743
  }
14500
14744
  function loadAccepted(manifestPath) {
14501
14745
  if (!manifestPath) return {};
14502
- const parsed = JSON.parse(readFileSync42(manifestPath, "utf8"));
14746
+ const parsed = JSON.parse(readFileSync43(manifestPath, "utf8"));
14503
14747
  return parsed.acceptedReductions ?? {};
14504
14748
  }
14505
14749
  async function runSharedFileReductionCheck(args) {
@@ -14507,13 +14751,13 @@ async function runSharedFileReductionCheck(args) {
14507
14751
  let accepted;
14508
14752
  try {
14509
14753
  if (args.pairs) {
14510
- pairs = pairsFromTsv(args.pairs === "-" ? readStdin() : readFileSync42(args.pairs, "utf8"));
14754
+ pairs = pairsFromTsv(args.pairs === "-" ? readStdin() : readFileSync43(args.pairs, "utf8"));
14511
14755
  } else if (args.target && args.existing && args.incoming) {
14512
14756
  pairs = [
14513
14757
  {
14514
14758
  target: args.target,
14515
- existing: readFileSync42(args.existing, "utf8"),
14516
- incoming: readFileSync42(args.incoming, "utf8")
14759
+ existing: readFileSync43(args.existing, "utf8"),
14760
+ incoming: readFileSync43(args.incoming, "utf8")
14517
14761
  }
14518
14762
  ];
14519
14763
  } else {
@@ -14547,12 +14791,12 @@ async function runSharedFileReductionCheck(args) {
14547
14791
  }
14548
14792
 
14549
14793
  // src/scripts/check-skeleton-drift.ts
14550
- import { existsSync as existsSync52, readdirSync as readdirSync27 } from "fs";
14551
- import { join as join60 } from "path";
14794
+ import { existsSync as existsSync53, readdirSync as readdirSync27 } from "fs";
14795
+ import { join as join62 } from "path";
14552
14796
 
14553
14797
  // src/lib/skeleton-drift-guard.ts
14554
- import { readFileSync as readFileSync43, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
14555
- import { join as join59 } from "path";
14798
+ import { readFileSync as readFileSync44, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
14799
+ import { join as join61 } from "path";
14556
14800
  var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
14557
14801
  var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
14558
14802
  var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
@@ -14616,7 +14860,7 @@ function walk(dir, base = dir) {
14616
14860
  }
14617
14861
  for (const entry of entries) {
14618
14862
  if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
14619
- const abs = join59(dir, entry);
14863
+ const abs = join61(dir, entry);
14620
14864
  let isDir;
14621
14865
  try {
14622
14866
  isDir = statSync16(abs).isDirectory();
@@ -14638,7 +14882,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
14638
14882
  if (!rule.appliesTo(rel)) continue;
14639
14883
  let contents;
14640
14884
  try {
14641
- contents = readFileSync43(join59(skeletonRoot, rel), "utf8");
14885
+ contents = readFileSync44(join61(skeletonRoot, rel), "utf8");
14642
14886
  } catch {
14643
14887
  continue;
14644
14888
  }
@@ -14667,23 +14911,23 @@ function formatViolations2(violations) {
14667
14911
 
14668
14912
  // src/scripts/check-skeleton-drift.ts
14669
14913
  function discoverSkeletons(root) {
14670
- const skeletonsDir = join60(root, "_skeletons");
14914
+ const skeletonsDir = join62(root, "_skeletons");
14671
14915
  let entries;
14672
14916
  try {
14673
14917
  entries = readdirSync27(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
14674
14918
  } catch {
14675
14919
  return [];
14676
14920
  }
14677
- return entries.filter((name) => existsSync52(join60(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
14921
+ return entries.filter((name) => existsSync53(join62(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
14678
14922
  }
14679
14923
  async function runSkeletonDriftCheck() {
14680
14924
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
14681
14925
  const skeletons = discoverSkeletons(root);
14682
14926
  let filesConsidered = 0;
14683
14927
  for (const name of skeletons) {
14684
- const skeletonRoot = join60(root, "_skeletons", name);
14928
+ const skeletonRoot = join62(root, "_skeletons", name);
14685
14929
  filesConsidered += findWorkflowFiles(skeletonRoot).length;
14686
- if (existsSync52(join60(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
14930
+ if (existsSync53(join62(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
14687
14931
  filesConsidered += 1;
14688
14932
  }
14689
14933
  }
@@ -14697,7 +14941,7 @@ async function runSkeletonDriftCheck() {
14697
14941
  process.exit(1);
14698
14942
  }
14699
14943
  const violations = skeletons.flatMap(
14700
- (name) => auditSkeleton(join60(root, "_skeletons", name), name)
14944
+ (name) => auditSkeleton(join62(root, "_skeletons", name), name)
14701
14945
  );
14702
14946
  if (violations.length > 0) {
14703
14947
  console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
@@ -14733,7 +14977,7 @@ async function runTerraformInputCheck() {
14733
14977
 
14734
14978
  // src/commands/check.ts
14735
14979
  var checkCommand = new Command24("check").description(
14736
- "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) run in CI and git hooks, plus out-of-band audits (branch protection, plugin-staleness)"
14980
+ "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)"
14737
14981
  );
14738
14982
  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 () => {
14739
14983
  await runOwnershipCheck(rawArgsAfter("ownership"));
@@ -14796,6 +15040,16 @@ checkCommand.command("instance-adoption").description(
14796
15040
  ).action(async (opts) => {
14797
15041
  await runInstanceAdoptionCheck(opts);
14798
15042
  });
15043
+ checkCommand.command("distribution-inventory").description(
15044
+ "Refuse a distribution-inventory.json entry that is malformed, half-classified, or inconsistent with the channel it names (#1570) -- validateInventory's own schema rules, self-checkable and network-free. Contrast distribution-remote-state below, its sibling, which needs a real cross-repo token to check a NAMED REMOTE repo's actual content and runs only on a schedule."
15045
+ ).action(async () => {
15046
+ await runDistributionInventoryCheck();
15047
+ });
15048
+ checkCommand.command("distribution-remote-state").description(
15049
+ "Refuse a distribution-inventory.json gapReason restating a REMOTE repo's content as current fact once that content has actually changed (#1816) -- fetches every declared remoteContentAssertion via `gh api` and compares against real content, generalising the one-off #1807 wording-regex guard to any entry that declares one. Needs a real cross-repo token (BIFFO_GITHUB_TOKEN in CI); exits 2 (cannot tell) rather than passing when a fetch fails."
15050
+ ).action(async () => {
15051
+ await runDistributionRemoteStateCheck();
15052
+ });
14799
15053
  checkCommand.command("orphan-ratchet").description(
14800
15054
  "Refuse an instance-written file under a template-owned path with no sanctioned carve-out (#1026), reusing planCoreUpgrade's classify() and checkOrphanRatchet \u2014 previously reachable only from inside `biffo core upgrade`, so drift was discovered only ~90 core versions later by the upgrade it blocks (#1714). --instance-dir defaults to a SELF-CHECK (this repo's own tree, unlike instance-adoption above) that can only ever report zero \u2014 see the entrypoint's own doc comment for why that default exists anyway. Pass a real instance checkout for a check that can actually find something."
14801
15055
  ).option(
@@ -14878,8 +15132,8 @@ function rawArgsAfter(subcommand) {
14878
15132
  }
14879
15133
 
14880
15134
  // src/commands/doctor.ts
14881
- import { existsSync as existsSync53, readFileSync as readFileSync44 } from "fs";
14882
- import { join as join61, resolve as resolve20 } from "path";
15135
+ import { existsSync as existsSync54, readFileSync as readFileSync45 } from "fs";
15136
+ import { join as join63, resolve as resolve20 } from "path";
14883
15137
  import chalk21 from "chalk";
14884
15138
  import { Command as Command25 } from "commander";
14885
15139
 
@@ -15274,10 +15528,10 @@ async function runDoctorFixBranches(cwd, facts, deps = { git: new GitAdapter(),
15274
15528
  return reapAllBareBranches(cwd, facts.branches, facts.worktrees, facts.currentBranch, deps);
15275
15529
  }
15276
15530
  function readLocalCoreVersion(cwd) {
15277
- const path = join61(cwd, INSTANCE_CORE_FILE);
15278
- if (!existsSync53(path)) return null;
15531
+ const path = join63(cwd, INSTANCE_CORE_FILE);
15532
+ if (!existsSync54(path)) return null;
15279
15533
  try {
15280
- return extractVersionField(readFileSync44(path, "utf8"));
15534
+ return extractVersionField(readFileSync45(path, "utf8"));
15281
15535
  } catch {
15282
15536
  return null;
15283
15537
  }
@@ -15297,10 +15551,10 @@ function extractVersionField(contents) {
15297
15551
  return match?.[1] ?? null;
15298
15552
  }
15299
15553
  function readFossil(cwd) {
15300
- const path = join61(cwd, CORE_VERSION_FILE);
15301
- if (!existsSync53(path)) return null;
15554
+ const path = join63(cwd, CORE_VERSION_FILE);
15555
+ if (!existsSync54(path)) return null;
15302
15556
  try {
15303
- const value = readFileSync44(path, "utf8").trim();
15557
+ const value = readFileSync45(path, "utf8").trim();
15304
15558
  return value === "" ? null : value;
15305
15559
  } catch {
15306
15560
  return null;
@@ -15829,13 +16083,13 @@ import { fileURLToPath as fileURLToPath6 } from "url";
15829
16083
  import { Command as Command27 } from "commander";
15830
16084
 
15831
16085
  // src/lib/packaged-scripts.ts
15832
- import { existsSync as existsSync54 } from "fs";
15833
- import { dirname as dirname11, join as join62 } from "path";
16086
+ import { existsSync as existsSync55 } from "fs";
16087
+ import { dirname as dirname11, join as join64 } from "path";
15834
16088
  function findPackagedScript(startDir, relativePath) {
15835
16089
  let dir = startDir;
15836
16090
  for (; ; ) {
15837
- const candidate = join62(dir, relativePath);
15838
- if (existsSync54(candidate)) return candidate;
16091
+ const candidate = join64(dir, relativePath);
16092
+ if (existsSync55(candidate)) return candidate;
15839
16093
  const parent = dirname11(dir);
15840
16094
  if (parent === dir) return null;
15841
16095
  dir = parent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.301.27",
3
+ "version": "0.302.0",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/scripts/verify.sh CHANGED
@@ -1421,6 +1421,7 @@ if [ -f scripts/biffo.sh ]; then
1421
1421
  run_check claim-invocation sh scripts/biffo.sh check claim-invocation
1422
1422
  run_check terraform-input sh scripts/biffo.sh check terraform-input
1423
1423
  run_check plugin-allowlist sh scripts/biffo.sh check plugin-allowlist-convention
1424
+ run_check distribution-inventory sh scripts/biffo.sh check distribution-inventory
1424
1425
  else
1425
1426
  skip biffo-guards "no scripts/biffo.sh in this repo"
1426
1427
  fi