@basou/cli 0.32.0 → 0.34.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
@@ -10552,12 +10552,17 @@ async function inspectRepo(repoPath) {
10552
10552
  async function checkPortfolioSafety(workspaces) {
10553
10553
  const findings = [];
10554
10554
  let monitoredReposChecked = 0;
10555
+ const records = [];
10555
10556
  for (const ws of workspaces) {
10556
10557
  const wsReal = await canonical(ws.repoRoot);
10557
10558
  let sourceRoots = [];
10559
+ let viewPath;
10560
+ let isMaster = false;
10558
10561
  try {
10559
10562
  const manifest = await readManifest12(ws.paths);
10560
10563
  sourceRoots = manifest.import?.source_roots ?? [];
10564
+ viewPath = manifest.workspace.view;
10565
+ isMaster = true;
10561
10566
  } catch (error) {
10562
10567
  if (error instanceof Error && error.message === "YAML file not found") {
10563
10568
  sourceRoots = [];
@@ -10569,6 +10574,7 @@ async function checkPortfolioSafety(workspaces) {
10569
10574
  kind: "unverifiable",
10570
10575
  detail: "the workspace manifest is present but unreadable \u2014 cannot determine which repos it monitors; treat as unsafe"
10571
10576
  });
10577
+ records.push({ ws, real: wsReal, isMaster: false, claimed: /* @__PURE__ */ new Set() });
10572
10578
  continue;
10573
10579
  }
10574
10580
  }
@@ -10578,6 +10584,12 @@ async function checkPortfolioSafety(workspaces) {
10578
10584
  const real = await canonical(display);
10579
10585
  if (real !== wsReal) monitored.set(real, display);
10580
10586
  }
10587
+ const claimed = new Set(monitored.keys());
10588
+ if (isMaster && viewPath !== void 0) {
10589
+ const viewReal = await canonical(resolve11(ws.repoRoot, viewPath));
10590
+ if (viewReal !== wsReal) claimed.add(viewReal);
10591
+ }
10592
+ records.push({ ws, real: wsReal, isMaster, claimed });
10581
10593
  for (const [real, display] of monitored) {
10582
10594
  monitoredReposChecked++;
10583
10595
  if (isInside(wsReal, real)) {
@@ -10601,8 +10613,39 @@ async function checkPortfolioSafety(workspaces) {
10601
10613
  }
10602
10614
  }
10603
10615
  }
10616
+ findings.push(...detectRedundantEntries(records));
10604
10617
  return { findings, workspacesChecked: workspaces.length, monitoredReposChecked };
10605
10618
  }
10619
+ function detectRedundantEntries(records) {
10620
+ const masterIdentity = (r) => {
10621
+ if (r.isMaster) return r.real;
10622
+ const owner = records.find((o) => o.isMaster && o.claimed.has(r.real));
10623
+ return owner !== void 0 ? owner.real : r.real;
10624
+ };
10625
+ const groups = /* @__PURE__ */ new Map();
10626
+ for (const r of records) {
10627
+ const key = masterIdentity(r);
10628
+ const group = groups.get(key);
10629
+ if (group !== void 0) group.push(r);
10630
+ else groups.set(key, [r]);
10631
+ }
10632
+ const findings = [];
10633
+ for (const [identity, group] of groups) {
10634
+ if (group.length < 2) continue;
10635
+ const primary = group.find((r) => r.real === identity) ?? group[0];
10636
+ for (const r of group) {
10637
+ if (r === primary) continue;
10638
+ findings.push({
10639
+ workspaceLabel: r.ws.label,
10640
+ workspaceRoot: r.ws.repoRoot,
10641
+ monitoredRepo: r.ws.repoRoot,
10642
+ kind: "redundant",
10643
+ detail: `resolves to the same workspace as portfolio entry "${primary.ws.label}" (${primary.ws.repoRoot}) and shows a duplicate card \u2014 register only the planning master (the anchor that owns .basou), not its workspace view or a source-root repo; remove this entry from the registry`
10644
+ });
10645
+ }
10646
+ }
10647
+ return findings;
10648
+ }
10606
10649
  function formatSafetyReport(result) {
10607
10650
  if (result.findings.length === 0) {
10608
10651
  if (result.monitoredReposChecked === 0) {
@@ -10614,13 +10657,23 @@ function formatSafetyReport(result) {
10614
10657
  `Portfolio safety: OK. ${result.workspacesChecked} workspace(s), ${result.monitoredReposChecked} monitored repo(s) checked \u2014 no .basou footprint, no overlap.`
10615
10658
  ];
10616
10659
  }
10617
- const lines = [`Portfolio safety: DANGER \u2014 ${result.findings.length} finding(s):`];
10660
+ const kinds = new Set(result.findings.map((f) => f.kind));
10661
+ const hasWriteRisk = kinds.has("footprint") || kinds.has("overlap") || kinds.has("unverifiable");
10662
+ const severity = hasWriteRisk ? "DANGER" : "WARNING";
10663
+ const lines = [`Portfolio safety: ${severity} \u2014 ${result.findings.length} finding(s):`];
10618
10664
  for (const f of result.findings) {
10619
10665
  lines.push(` [${f.kind}] ${f.monitoredRepo} (workspace "${f.workspaceLabel}"): ${f.detail}`);
10620
10666
  }
10621
- lines.push(
10622
- "A monitored repo must have no basou footprint. Use a separate workspace repo whose source_roots point at the monitored repo as a sibling; never 'basou init' / 'run' / 'exec' inside a monitored repo."
10623
- );
10667
+ if (hasWriteRisk) {
10668
+ lines.push(
10669
+ "A monitored repo must have no basou footprint. Use a separate workspace repo whose source_roots point at the monitored repo as a sibling; never 'basou init' / 'run' / 'exec' inside a monitored repo."
10670
+ );
10671
+ }
10672
+ if (kinds.has("redundant")) {
10673
+ lines.push(
10674
+ "A redundant entry is a workspace already reached through another registered planning master. Register only the planning master (the anchor that owns .basou), never its workspace view or its member / source-root repos \u2014 those resolve back to the same workspace and produce duplicate cards."
10675
+ );
10676
+ }
10624
10677
  return lines;
10625
10678
  }
10626
10679
 
@@ -11855,7 +11908,7 @@ async function doRunView(options, ctx) {
11855
11908
  }
11856
11909
  if (result.findings.length > 0) {
11857
11910
  console.error(
11858
- `Portfolio safety: ${result.findings.length} unverifiable item(s) \u2014 the read-only view will still open; run 'basou view --check' for detail.`
11911
+ `Portfolio safety: ${result.findings.length} non-blocking finding(s) \u2014 the read-only view will still open; run 'basou view --check' for detail.`
11859
11912
  );
11860
11913
  }
11861
11914
  }