@basou/cli 0.32.0 → 0.33.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 +58 -5
- package/dist/index.js.map +1 -1
- package/dist/program.js +58 -5
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.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
|
|
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
|
-
|
|
10622
|
-
|
|
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}
|
|
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
|
}
|