@basou/cli 0.67.0 → 0.68.1

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/program.js CHANGED
@@ -828,33 +828,190 @@ function printNoApprovals(options) {
828
828
  }
829
829
 
830
830
  // src/commands/board.ts
831
- import { lstat, readFile as readFile2, realpath as realpath2 } from "fs/promises";
832
- import { basename as basename3, dirname as dirname3, join as join3, relative, resolve as resolve3, sep } from "path";
831
+ import { lstat, readdir as readdir2, readFile as readFile2, realpath as realpath3 } from "fs/promises";
832
+ import { basename as basename4, dirname as dirname5, join as join4, relative, resolve as resolve3, sep as sep2 } from "path";
833
833
  import {
834
834
  assertBasouRootSafe as assertBasouRootSafe3,
835
+ BOARD_INIT_SAVE,
835
836
  basouPaths as basouPaths4,
837
+ boardGuide,
838
+ boardInitText,
836
839
  buildRecord,
837
840
  byCodePoint,
838
841
  diffCells,
839
842
  diffObserved,
840
843
  displayPath as displayPath3,
844
+ effortStartOf,
841
845
  findErrorCode as findErrorCode3,
846
+ isBoardTimeZone,
842
847
  measureBoard,
843
848
  measureBoardLive,
844
849
  NO_PREVIOUS_RECORDS,
845
850
  parseBoardDeclaration,
846
851
  parseRecordInput,
847
- readManifest as readManifest3,
852
+ readManifest as readManifest4,
848
853
  readPreviousRecords,
854
+ recordIds,
855
+ resolveViewLanguage,
856
+ shellWord,
849
857
  writeRecord
850
858
  } from "@basou/core";
851
859
 
860
+ // src/lib/foreign-workspace-warn.ts
861
+ import { realpath as realpath2 } from "fs/promises";
862
+ import { dirname } from "path";
863
+ import {
864
+ isPositionBoardLine,
865
+ loadPortfolioConfig as loadPortfolioConfig2,
866
+ PortfolioConfigMissingError,
867
+ readMarkdownFile
868
+ } from "@basou/core";
869
+
870
+ // src/lib/foreign-workspace-scan.ts
871
+ import { homedir as osHomedir } from "os";
872
+ import { basename as basename2, normalize, sep } from "path";
873
+ var MIN_TOKEN_LENGTH = 4;
874
+ function stripTrailingSep(p) {
875
+ return p.length > 1 && p.endsWith(sep) ? p.slice(0, -sep.length) : p;
876
+ }
877
+ function toTildePath(absPath, home) {
878
+ if (absPath === home) return "~";
879
+ if (absPath.startsWith(home + sep)) return `~${absPath.slice(home.length)}`;
880
+ return null;
881
+ }
882
+ function encodedSpelling(name) {
883
+ return name.replace(/[^a-zA-Z0-9]/g, "-");
884
+ }
885
+ function directoryNameSpellings(dir) {
886
+ const paired = /^(.+)-(planning|workspace)$/.exec(dir);
887
+ const plain = paired !== null ? [`${paired[1]}-planning`, `${paired[1]}-workspace`] : [dir, `${dir}-workspace`];
888
+ return [...plain, ...plain.map(encodedSpelling)];
889
+ }
890
+ function nameTokensFor(workspacePath, home) {
891
+ const abs = stripTrailingSep(normalize(workspacePath));
892
+ const tokens = /* @__PURE__ */ new Set([abs]);
893
+ const tilde = toTildePath(abs, home);
894
+ if (tilde !== null) tokens.add(tilde);
895
+ for (const name of directoryNameSpellings(basename2(abs))) tokens.add(name);
896
+ return [...tokens].filter((t) => t.length >= MIN_TOKEN_LENGTH);
897
+ }
898
+ function isSpellingOfSelf(workspacePath, selfPath) {
899
+ const own = new Set(directoryNameSpellings(basename2(stripTrailingSep(normalize(selfPath)))));
900
+ return directoryNameSpellings(basename2(stripTrailingSep(normalize(workspacePath)))).some(
901
+ (s) => own.has(s)
902
+ );
903
+ }
904
+ function scanForeignWorkspaceNames(input) {
905
+ if (input.text.length === 0 || input.workspacePaths.length === 0) return [];
906
+ const home = input.homedir ?? osHomedir();
907
+ const own = input.selfPath === void 0 ? [] : nameTokensFor(input.selfPath, home);
908
+ const lines = input.text.split("\n");
909
+ const hits = [];
910
+ for (const workspacePath of input.workspacePaths) {
911
+ if (input.selfPath !== void 0 && isSpellingOfSelf(workspacePath, input.selfPath)) continue;
912
+ const tokens = nameTokensFor(workspacePath, home).filter(
913
+ (token) => !own.some((ownToken) => ownToken.includes(token))
914
+ );
915
+ if (tokens.length === 0) continue;
916
+ const matched = /* @__PURE__ */ new Set();
917
+ const matchedLines = [];
918
+ for (const [index, line] of lines.entries()) {
919
+ let lineMatched = false;
920
+ for (const token of tokens) {
921
+ if (line.includes(token)) {
922
+ matched.add(token);
923
+ lineMatched = true;
924
+ }
925
+ }
926
+ if (lineMatched) matchedLines.push(index + 1);
927
+ }
928
+ if (matchedLines.length === 0) continue;
929
+ hits.push({
930
+ workspacePath,
931
+ tokens: [...matched].sort((a, b) => b.length - a.length || a.localeCompare(b)),
932
+ lines: matchedLines
933
+ });
934
+ }
935
+ return hits;
936
+ }
937
+
938
+ // src/lib/foreign-workspace-warn.ts
939
+ var MAX_LISTED_LINES = 5;
940
+ async function canonicalize(path) {
941
+ try {
942
+ return await realpath2(path);
943
+ } catch {
944
+ return path;
945
+ }
946
+ }
947
+ async function findForeignWorkspaceNames(args) {
948
+ let workspacePaths;
949
+ let selfPath;
950
+ try {
951
+ const workspaces = await loadPortfolioConfig2(args.configPath);
952
+ workspacePaths = await Promise.all(workspaces.map((w) => canonicalize(w.path)));
953
+ selfPath = args.selfPath === void 0 ? void 0 : await canonicalize(args.selfPath);
954
+ } catch {
955
+ return null;
956
+ }
957
+ const text = args.text.split("\n").map((line) => isPositionBoardLine(line) ? "" : line).join("\n");
958
+ const hits = scanForeignWorkspaceNames({ text, workspacePaths, selfPath });
959
+ if (hits.length === 0) return null;
960
+ const lines = [...new Set(hits.flatMap((h) => h.lines))].sort((a, b) => a - b);
961
+ return { workspaceCount: hits.length, lines };
962
+ }
963
+ async function findForeignWorkspaceFields(args) {
964
+ let workspacePaths;
965
+ let selfPath;
966
+ try {
967
+ const workspaces = await loadPortfolioConfig2(args.configPath);
968
+ workspacePaths = await Promise.all(workspaces.map((w) => canonicalize(w.path)));
969
+ selfPath = await canonicalize(args.selfPath);
970
+ } catch (error) {
971
+ return error instanceof PortfolioConfigMissingError ? { status: "no_registry" } : { status: "unreadable" };
972
+ }
973
+ const at = args.fields.filter(
974
+ (field) => scanForeignWorkspaceNames({ text: field.text, workspacePaths, selfPath }).length > 0
975
+ ).map((field) => field.at);
976
+ return { status: "checked", at };
977
+ }
978
+ function describeForeignWorkspaceLines(lines) {
979
+ const listed = lines.slice(0, MAX_LISTED_LINES).join(", ");
980
+ const rest = lines.length - MAX_LISTED_LINES;
981
+ const numbers = rest > 0 ? `${listed} and ${rest} more` : listed;
982
+ return `${lines.length === 1 ? "line" : "lines"} ${numbers}`;
983
+ }
984
+ function positionForeignWorkspaceWarning(report, where) {
985
+ const subject = report.workspaceCount === 1 ? "names another registered workspace" : `names ${report.workspaceCount} other registered workspaces`;
986
+ return `basou: this workspace's position ${subject} (${where}, ${describeForeignWorkspaceLines(report.lines)}). The position is handed to the agent session running in this workspace, so those names travel with it. Advisory only: nothing was withheld.`;
987
+ }
988
+ function protocolForeignWorkspaceWarning(report) {
989
+ const subject = report.workspaceCount === 1 ? "names a registered workspace" : `names ${report.workspaceCount} registered workspaces`;
990
+ return `basou: the protocol block ${subject} (block ${describeForeignWorkspaceLines(report.lines)}, counted from the block's first line, not the file's). Standing protocols are written to the user-global CLAUDE.md, which every project on this machine loads, so a workspace-specific name there reaches every workspace's sessions. Only the basou-managed block is checked; the rest of that file is not basou's to inspect. Advisory only: nothing was withheld.`;
991
+ }
992
+ async function warnIfPositionNamesOtherWorkspaces(args) {
993
+ try {
994
+ const body = await readMarkdownFile(args.paths.files.orientation);
995
+ if (body === null) return;
996
+ const report = await findForeignWorkspaceNames({
997
+ text: body,
998
+ selfPath: dirname(args.paths.root),
999
+ configPath: args.configPath
1000
+ });
1001
+ if (report !== null) {
1002
+ console.error(positionForeignWorkspaceWarning(report, args.paths.files.orientation));
1003
+ }
1004
+ } catch {
1005
+ }
1006
+ }
1007
+
852
1008
  // src/lib/provenance-actions.ts
1009
+ import { dirname as dirname4 } from "path";
853
1010
  import {
854
1011
  assertApprovalStoreSafe,
855
1012
  assertLockStoreSafe as assertLockStoreSafe3,
856
1013
  assertTaskStoreSafe,
857
- readMarkdownFile,
1014
+ readMarkdownFile as readMarkdownFile2,
858
1015
  renderDecisions,
859
1016
  renderHandoff,
860
1017
  renderOrientation,
@@ -866,7 +1023,7 @@ import {
866
1023
  import { createReadStream } from "fs";
867
1024
  import { readdir, readFile, rm, stat as stat2 } from "fs/promises";
868
1025
  import { homedir } from "os";
869
- import { basename as basename2, dirname as dirname2, join as join2, resolve as resolve2 } from "path";
1026
+ import { basename as basename3, dirname as dirname3, join as join2, resolve as resolve2 } from "path";
870
1027
  import { createInterface } from "readline";
871
1028
  import {
872
1029
  AGENT_INFRA_DIRS,
@@ -893,7 +1050,7 @@ import {
893
1050
  } from "@basou/core";
894
1051
 
895
1052
  // src/lib/observation-warn.ts
896
- import { dirname } from "path";
1053
+ import { dirname as dirname2 } from "path";
897
1054
  import { assertObservationsDirSafe, displayPath } from "@basou/core";
898
1055
  async function warnIfObservationsRefused(paths) {
899
1056
  try {
@@ -901,7 +1058,7 @@ async function warnIfObservationsRefused(paths) {
901
1058
  } catch (error) {
902
1059
  const reason = error instanceof Error ? error.message : String(error);
903
1060
  console.error(
904
- `basou: ${reason} (in ${displayPath(dirname(paths.root))}). The files a session changes through the shell are not observed there, so a session imported now records only the files its transcript names.`
1061
+ `basou: ${reason} (in ${displayPath(dirname2(paths.root))}). The files a session changes through the shell are not observed there, so a session imported now records only the files its transcript names.`
905
1062
  );
906
1063
  }
907
1064
  }
@@ -978,7 +1135,7 @@ async function doRunImportClaudeCode(options, ctx) {
978
1135
  const files = await selectTranscriptFiles(projectsRoot, projectPaths, options);
979
1136
  const projectSet = new Set(projectPaths);
980
1137
  const candidates = files.map((file) => {
981
- const externalId = basename2(file, ".jsonl");
1138
+ const externalId = basename3(file, ".jsonl");
982
1139
  return {
983
1140
  externalId,
984
1141
  sourcePath: file,
@@ -1077,7 +1234,7 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
1077
1234
  files: payload.session.related_files ?? [],
1078
1235
  workingDirectory: payload.session.working_directory,
1079
1236
  sourceRoots: projectPaths,
1080
- masterRoot: dirname2(paths.root),
1237
+ masterRoot: dirname3(paths.root),
1081
1238
  extraInRoot: AGENT_INFRA_DIRS
1082
1239
  });
1083
1240
  if (scope.outOfRoot.length > 0) crossProject.push({ externalId, outOfRoot: scope.outOfRoot });
@@ -1515,6 +1672,19 @@ async function assertWorkspaceInitialized2(basouRoot) {
1515
1672
  }
1516
1673
  }
1517
1674
 
1675
+ // src/lib/board-glance.ts
1676
+ import { join as join3 } from "path";
1677
+ import { glanceBoard, readManifest as readManifest3 } from "@basou/core";
1678
+ async function glanceDefaultBoard(root, paths) {
1679
+ try {
1680
+ const own = (await readManifest3(paths)).repos?.find((repo) => repo.path === ".");
1681
+ if (own?.visibility !== "private") return null;
1682
+ } catch {
1683
+ return null;
1684
+ }
1685
+ return glanceBoard(join3(root, DEFAULT_BOARD_PATH));
1686
+ }
1687
+
1518
1688
  // src/lib/provenance-actions.ts
1519
1689
  var capturing = Promise.resolve();
1520
1690
  function captureImportJson(fn) {
@@ -1599,7 +1769,7 @@ function importCodex(options, ctx) {
1599
1769
  }
1600
1770
  async function regenerateHandoff(paths, nowIso, callbacks) {
1601
1771
  const result = await renderHandoff({ paths, nowIso, ...callbacks });
1602
- const existing = await readMarkdownFile(paths.files.handoff);
1772
+ const existing = await readMarkdownFile2(paths.files.handoff);
1603
1773
  await writeMarkdownFile(
1604
1774
  paths.files.handoff,
1605
1775
  renderWithMarkers(existing, result.body, "handoff.md")
@@ -1613,7 +1783,7 @@ async function regenerateHandoff(paths, nowIso, callbacks) {
1613
1783
  }
1614
1784
  async function regenerateDecisions(paths, nowIso, callbacks) {
1615
1785
  const result = await renderDecisions({ paths, nowIso, ...callbacks });
1616
- const existing = await readMarkdownFile(paths.files.decisions);
1786
+ const existing = await readMarkdownFile2(paths.files.decisions);
1617
1787
  await writeMarkdownFile(
1618
1788
  paths.files.decisions,
1619
1789
  renderWithMarkers(existing, result.body, "decisions.md")
@@ -1621,7 +1791,12 @@ async function regenerateDecisions(paths, nowIso, callbacks) {
1621
1791
  return { decisionCount: result.decisionCount };
1622
1792
  }
1623
1793
  async function regenerateOrientation(paths, nowIso, callbacks) {
1624
- const result = await renderOrientation({ paths, nowIso, ...callbacks });
1794
+ const result = await renderOrientation({
1795
+ paths,
1796
+ nowIso,
1797
+ board: await glanceDefaultBoard(dirname4(paths.root), paths),
1798
+ ...callbacks
1799
+ });
1625
1800
  await writeMarkdownFile(paths.files.orientation, `${result.body}
1626
1801
  `);
1627
1802
  return {
@@ -1739,8 +1914,9 @@ read (nothing is printed on stdout).`
1739
1914
  "after",
1740
1915
  `
1741
1916
  The input is a JSON object: measure_digest (the digest of the measurement the
1742
- judgement saw), observed, cells (every lane at every stage), prose, judged_by
1743
- and axis_review. The board is measured again, and nothing is written when the
1917
+ judgement saw), observed (exactly the observations the board declares, when it
1918
+ declares any), cells (every lane at every stage), prose, judged_by and
1919
+ axis_review. The board is measured again, and nothing is written when the
1744
1920
  digest differs. Records are never written under a .basou/ directory. Exit
1745
1921
  codes: 0 when the record was written, or with --dry-run when everything
1746
1922
  checked out (nothing is written then); 1 when it was refused (nothing is
@@ -1748,6 +1924,191 @@ written; the reasons are on stderr).`
1748
1924
  ).action(async (options) => {
1749
1925
  await runBoardRecord(options);
1750
1926
  });
1927
+ board.command("guide").description(
1928
+ "Print the steps an agent follows to judge the board and record it (writes nothing, sends nothing)"
1929
+ ).option("-v, --verbose", "Show error causes").addHelpText(
1930
+ "after",
1931
+ `
1932
+ The steps are filled in with this workspace's board (${DEFAULT_BOARD_PATH},
1933
+ read only when the manifest declares the workspace's own repo private): where
1934
+ it is and its last record, the commands that measure, observe, record and show
1935
+ it, what each stage is judged by, and the record's input to fill in, with the
1936
+ previous record's state of each cell beside it. With no board declared yet, it
1937
+ says how to declare one. The text is for an agent to read; its shape may change
1938
+ at any release. Exit codes: 0 when the steps were printed; 1 when the workspace,
1939
+ its manifest or its board.yaml cannot be read, or its own repo is not declared
1940
+ private (the reason is on stderr).`
1941
+ ).action(async (options) => {
1942
+ await runBoardGuide(options);
1943
+ });
1944
+ board.command("init").description("Print a board.yaml to start a board from (writes nothing)").option("-v, --verbose", "Show error causes").addHelpText(
1945
+ "after",
1946
+ `
1947
+ It prints a declaration of the newest board_version, in the language the
1948
+ manifest declares for the workspace's own repo: a title from the workspace's
1949
+ name, the six stages with a meaning each, one lane to cut again, nothing to
1950
+ observe, no component registered, an axis of version 1, and the effort from
1951
+ the day of the first session, in this host's time zone. It writes nothing:
1952
+ save it, from the workspace's own repo, only where no board is, through a file
1953
+ of its own:
1954
+
1955
+ ${INIT_SAVE}
1956
+
1957
+ Never print it straight into ${DEFAULT_BOARD_PATH}: the shell empties that file
1958
+ before init runs, so a board already there would be lost.
1959
+
1960
+ Exit codes: 0 when it was printed; 1 when the manifest does not declare the
1961
+ workspace's own repo private (a board's records hold what the trail holds),
1962
+ when ${DEFAULT_BOARD_PATH} is already there, or when the workspace cannot be
1963
+ read (the reason is on stderr, and nothing is printed on stdout).`
1964
+ ).action(async (options) => {
1965
+ await runBoardInit(options);
1966
+ });
1967
+ }
1968
+ var INIT_SAVE = BOARD_INIT_SAVE;
1969
+ async function runBoardInit(options, ctx = {}) {
1970
+ try {
1971
+ await doRunBoardInit(options, ctx);
1972
+ } catch (error) {
1973
+ renderCliError(error, { verbose: isVerbose(options) });
1974
+ process.exitCode = 1;
1975
+ }
1976
+ }
1977
+ async function doRunBoardInit(_options, ctx) {
1978
+ const { root, paths, manifest } = await privateWorkspace(ctx, "board init");
1979
+ const cwd = ctx.cwd ?? process.cwd();
1980
+ if (await realpath3(cwd).catch(() => cwd) !== await realpath3(root).catch(() => root)) {
1981
+ throw new Error(
1982
+ `Run basou board init from the top of the workspace's own repo, where its .basou/ is: the way to save what it prints writes ${DEFAULT_BOARD_PATH} in the current directory.`
1983
+ );
1984
+ }
1985
+ const board = join4(root, DEFAULT_BOARD_PATH);
1986
+ try {
1987
+ await lstat(board);
1988
+ throw new Error(
1989
+ `${DEFAULT_BOARD_PATH} is already there, so there is no board to start: change that one (through an axis review when it changes the lanes or the stages).`
1990
+ );
1991
+ } catch (error) {
1992
+ if (!findErrorCode3(error, "ENOENT")) throw error;
1993
+ }
1994
+ const { start, timeZone, sessionsRead } = await effortStartOf(
1995
+ paths,
1996
+ ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()
1997
+ );
1998
+ if (timeZone === void 0) {
1999
+ console.error(
2000
+ "basou: this host's time zone has no name, so effort.start is today in UTC and effort.time_zone is left out; write both."
2001
+ );
2002
+ } else if (!sessionsRead) {
2003
+ console.error(
2004
+ "basou: the sessions could not be read, so effort.start is today; set it to the day the work began."
2005
+ );
2006
+ } else if (!isBoardTimeZone(timeZone)) {
2007
+ console.error(
2008
+ "basou: this host's time zone has no name a board can declare, so effort.time_zone is left out; write one."
2009
+ );
2010
+ }
2011
+ const text = boardInitText({
2012
+ name: manifest.workspace.name,
2013
+ language: resolveViewLanguage(manifest),
2014
+ start,
2015
+ startIsFirstSession: timeZone !== void 0,
2016
+ // An offset such as +00:00, which some hosts name their zone, is not
2017
+ // one a board can declare.
2018
+ timeZone: timeZone !== void 0 && isBoardTimeZone(timeZone) ? timeZone : void 0
2019
+ });
2020
+ process.stdout.write(text);
2021
+ return text;
2022
+ }
2023
+ async function privateWorkspace(ctx, command) {
2024
+ const cwd = ctx.cwd ?? process.cwd();
2025
+ const root = await resolveBasouRootForCommand(
2026
+ cwd,
2027
+ command,
2028
+ ctx.portfolioConfigPath === void 0 ? {} : { portfolioConfigPath: ctx.portfolioConfigPath }
2029
+ );
2030
+ const paths = basouPaths4(root);
2031
+ await assertWorkspaceInitialized3(paths.root);
2032
+ const manifest = await readManifest4(paths);
2033
+ const own = (manifest.repos ?? []).find((repo) => repo.path === ".");
2034
+ if (own?.visibility !== "private") {
2035
+ throw new Error(
2036
+ `The board is kept at ${DEFAULT_BOARD_PATH} only in a workspace whose manifest declares its own repo (path: .) private, since a record holds what the trail holds (open tracks, time worked, model names). This one does not, so it has no judged board; basou view's /board page shows what is measured without one.`
2037
+ );
2038
+ }
2039
+ return { root, paths, manifest };
2040
+ }
2041
+ async function runBoardGuide(options, ctx = {}) {
2042
+ try {
2043
+ await doRunBoardGuide(options, ctx);
2044
+ } catch (error) {
2045
+ renderCliError(error, { verbose: isVerbose(options) });
2046
+ process.exitCode = 1;
2047
+ }
2048
+ }
2049
+ async function doRunBoardGuide(_options, ctx) {
2050
+ const { root, manifest } = await privateWorkspace(ctx, "board guide");
2051
+ const board = {
2052
+ path: join4(root, DEFAULT_BOARD_PATH),
2053
+ shown: DEFAULT_BOARD_PATH,
2054
+ given: false
2055
+ };
2056
+ let text;
2057
+ try {
2058
+ text = await readDeclaration(board);
2059
+ } catch (error) {
2060
+ if (!findErrorCode3(error, "ENOENT")) throw error;
2061
+ if (await lstat(board.path).then(
2062
+ () => true,
2063
+ () => false
2064
+ )) {
2065
+ throw new Error(
2066
+ `${DEFAULT_BOARD_PATH} is there but cannot be read (a link to nothing?). Fix it before the board is updated.`
2067
+ );
2068
+ }
2069
+ text = null;
2070
+ }
2071
+ const otherRepos = (manifest.repos ?? []).map((repo) => repo.path).filter((p) => p !== ".");
2072
+ if ([root, ...otherRepos].some((path) => displayPath3(path) !== path)) {
2073
+ throw new Error(
2074
+ "The workspace's path, or a repo path the manifest declares, holds a control character, which the guide's commands cannot carry safely. Rename it, or follow the steps by hand."
2075
+ );
2076
+ }
2077
+ const common = {
2078
+ // As basou resolves it, the root measure resolves the repos' paths against.
2079
+ anchor: root,
2080
+ otherRepos,
2081
+ language: resolveViewLanguage(manifest),
2082
+ basouCommand: ctx.basouCommand ?? [process.execPath, process.argv[1] ?? ""].filter((part) => part !== "").map(shellWord).join(" "),
2083
+ basouVersion: BASOU_CLI_VERSION,
2084
+ now: ctx.nowProvider?.() ?? /* @__PURE__ */ new Date()
2085
+ };
2086
+ let guide;
2087
+ if (text === null) {
2088
+ guide = boardGuide({ ...common, board: { status: "undeclared" } });
2089
+ } else {
2090
+ const parsed = parseBoardDeclaration(text, {
2091
+ manifestRepoPaths: (manifest.repos ?? []).map((repo) => repo.path)
2092
+ });
2093
+ if (!parsed.ok) {
2094
+ throw new Error(
2095
+ `${DEFAULT_BOARD_PATH} is not a valid board declaration, so there are no steps to print until it reads (fix it through an axis review when it changes the lanes or the stages):
2096
+ ${parsed.errors.map((e) => ` - ${displayPath3(e)}`).join("\n")}`
2097
+ );
2098
+ }
2099
+ const recordsDir = join4(dirname5(board.path), "records");
2100
+ guide = boardGuide({
2101
+ ...common,
2102
+ board: {
2103
+ status: "declared",
2104
+ declaration: parsed.declaration,
2105
+ recordCount: recordIds(await readdir2(recordsDir).catch(() => [])).length,
2106
+ previous: await readPreviousRecords(recordsDir)
2107
+ }
2108
+ });
2109
+ }
2110
+ console.log(guide.trimEnd());
2111
+ return guide;
1751
2112
  }
1752
2113
  async function runBoardMeasure(options, ctx = {}) {
1753
2114
  try {
@@ -1794,8 +2155,8 @@ Nothing was written.`, { cause: error.cause });
1794
2155
  async function checkAndRecord(options, ctx) {
1795
2156
  const cwd = ctx.cwd ?? process.cwd();
1796
2157
  const loaded = await loadBoard(options, ctx, "board record");
1797
- const beside = (...parts) => loaded.board.given ? join3(dirname3(loaded.board.shown), ...parts) : relative(cwd, join3(dirname3(loaded.board.path), ...parts));
1798
- const recordsDir = join3(dirname3(loaded.board.path), "records");
2158
+ const beside = (...parts) => loaded.board.given ? join4(dirname5(loaded.board.shown), ...parts) : relative(cwd, join4(dirname5(loaded.board.path), ...parts));
2159
+ const recordsDir = join4(dirname5(loaded.board.path), "records");
1799
2160
  await checkRecordsPlace(loaded, recordsDir, beside("records"), options.notPrivate === true);
1800
2161
  const text = await readRecordInput(options, ctx);
1801
2162
  let value;
@@ -1806,6 +2167,7 @@ async function checkAndRecord(options, ctx) {
1806
2167
  cause: error
1807
2168
  });
1808
2169
  }
2170
+ await refuseOtherWorkspaces(value, loaded.root, ctx);
1809
2171
  const parsed = parseRecordInput(value, loaded.declaration);
1810
2172
  if (!parsed.ok) {
1811
2173
  throw new RecordRefusal(
@@ -1862,11 +2224,90 @@ ${parsed.errors.map((e) => ` - ${displayPath3(e)}`).join("\n")}`
1862
2224
  diff
1863
2225
  };
1864
2226
  }
2227
+ var INPUT_KEYS = /* @__PURE__ */ new Set([
2228
+ "measure_digest",
2229
+ "observed",
2230
+ "cells",
2231
+ "prose",
2232
+ "judged_by",
2233
+ "axis_review",
2234
+ "value",
2235
+ "observed_at",
2236
+ "source",
2237
+ "error",
2238
+ "lane",
2239
+ "stage",
2240
+ "state",
2241
+ "reason",
2242
+ "summary",
2243
+ "lanes",
2244
+ "operator_turns",
2245
+ "footnotes",
2246
+ "text",
2247
+ "model",
2248
+ "self_reported",
2249
+ "triggers"
2250
+ ]);
2251
+ var SHAPED_PLACES = [
2252
+ /^measure_digest$/,
2253
+ /^observed \(a named entry\)\.observed_at$/,
2254
+ /^cells\[\d+\]\.state$/,
2255
+ /^axis_review\.triggers$/
2256
+ ];
2257
+ var NAMED_MAPS = /* @__PURE__ */ new Set(["observed", "prose.lanes"]);
2258
+ function inputTexts(value) {
2259
+ const out = [];
2260
+ const stack = [{ value, at: "" }];
2261
+ for (let next = stack.pop(); next !== void 0; next = stack.pop()) {
2262
+ const { value: v, at } = next;
2263
+ if (SHAPED_PLACES.some((place) => place.test(at))) continue;
2264
+ if (typeof v === "string" || typeof v === "number") {
2265
+ out.push({ at: at === "" ? "(top level)" : at, text: String(v) });
2266
+ } else if (Array.isArray(v)) {
2267
+ for (let i = v.length - 1; i >= 0; i--) stack.push({ value: v[i], at: `${at}[${i}]` });
2268
+ } else if (typeof v === "object" && v !== null) {
2269
+ const named = NAMED_MAPS.has(at);
2270
+ const where = at === "" ? "(top level)" : at;
2271
+ const children = [];
2272
+ for (const [k, child] of Object.entries(v)) {
2273
+ if (!named && INPUT_KEYS.has(k)) {
2274
+ children.push({ value: child, at: at === "" ? k : `${at}.${k}` });
2275
+ } else {
2276
+ out.push({ at: `${where}: a name`, text: k });
2277
+ children.push({ value: child, at: `${where} (a named entry)` });
2278
+ }
2279
+ }
2280
+ stack.push(...children.reverse());
2281
+ }
2282
+ }
2283
+ return out;
2284
+ }
2285
+ async function refuseOtherWorkspaces(input, root, ctx) {
2286
+ const found = await findForeignWorkspaceFields({
2287
+ fields: inputTexts(input),
2288
+ selfPath: root,
2289
+ configPath: ctx.portfolioConfigPath
2290
+ });
2291
+ if (found.status === "unreadable") {
2292
+ console.error(
2293
+ "basou: ~/.basou/portfolio.yaml could not be read, or does not list workspaces as basou reads them, so the record's input was not checked for the names of other registered workspaces."
2294
+ );
2295
+ return;
2296
+ }
2297
+ if (found.status === "checked" && found.at.length > 0) {
2298
+ throw new RecordRefusal(
2299
+ `The record's input names another workspace the portfolio registers, and a board holds this workspace's own work only; nothing was written:
2300
+ ${[
2301
+ ...new Set(found.at)
2302
+ ].map((where) => ` - ${displayPath3(where)}`).join("\n")}`
2303
+ );
2304
+ }
2305
+ }
1865
2306
  function hasRecords(loaded) {
1866
- return basename3(loaded.board.path) === "board.yaml";
2307
+ return basename4(loaded.board.path) === "board.yaml";
1867
2308
  }
1868
2309
  async function previousOf(loaded) {
1869
- return hasRecords(loaded) ? readPreviousRecords(join3(dirname3(loaded.board.path), "records")) : NO_PREVIOUS_RECORDS;
2310
+ return hasRecords(loaded) ? readPreviousRecords(join4(dirname5(loaded.board.path), "records")) : NO_PREVIOUS_RECORDS;
1870
2311
  }
1871
2312
  function errorCode(error) {
1872
2313
  const code = error?.code;
@@ -1874,21 +2315,21 @@ function errorCode(error) {
1874
2315
  }
1875
2316
  async function checkRecordsPlace(loaded, recordsDir, shown, notPrivate) {
1876
2317
  const { board } = loaded;
1877
- if (basename3(board.path) !== "board.yaml") {
2318
+ if (basename4(board.path) !== "board.yaml") {
1878
2319
  throw new RecordRefusal(
1879
2320
  `${displayPath3(board.shown)} is not named board.yaml: basou board record reads only a file of that name, so that the records/ beside it holds one board's records; nothing was written.`
1880
2321
  );
1881
2322
  }
1882
2323
  let dir;
1883
2324
  try {
1884
- dir = await realpath2(dirname3(board.path));
2325
+ dir = await realpath3(dirname5(board.path));
1885
2326
  } catch (error) {
1886
2327
  throw new RecordRefusal(
1887
2328
  `The directory of ${displayPath3(board.shown)} could not be resolved; nothing was written.`,
1888
2329
  { cause: error }
1889
2330
  );
1890
2331
  }
1891
- if (dir.split(sep).includes(".basou")) {
2332
+ if (dir.split(sep2).includes(".basou")) {
1892
2333
  throw new RecordRefusal(
1893
2334
  `${displayPath3(shown)} would be under a .basou/ directory, where basou board record writes nothing; nothing was written.`
1894
2335
  );
@@ -1896,8 +2337,8 @@ async function checkRecordsPlace(loaded, recordsDir, shown, notPrivate) {
1896
2337
  if (!notPrivate) {
1897
2338
  let holder;
1898
2339
  for (const repo of loaded.manifest.repos ?? []) {
1899
- const real = await realpath2(resolve3(loaded.root, repo.path)).catch(() => void 0);
1900
- if (real === void 0 || dir !== real && !dir.startsWith(`${real}${sep}`)) continue;
2340
+ const real = await realpath3(resolve3(loaded.root, repo.path)).catch(() => void 0);
2341
+ if (real === void 0 || dir !== real && !dir.startsWith(`${real}${sep2}`)) continue;
1901
2342
  if (holder === void 0 || real.length > holder.real.length) {
1902
2343
  holder = { real, private: repo.visibility === "private" };
1903
2344
  }
@@ -1997,7 +2438,7 @@ async function loadBoard(options, ctx, command) {
1997
2438
  );
1998
2439
  const paths = basouPaths4(root);
1999
2440
  await assertWorkspaceInitialized3(paths.root);
2000
- const manifest = await readManifest3(paths);
2441
+ const manifest = await readManifest4(paths);
2001
2442
  const board = boardPath(options, cwd, root, manifest);
2002
2443
  const text = await readDeclaration(board);
2003
2444
  const repoPaths = (manifest.repos ?? []).map((repo) => repo.path);
@@ -2036,7 +2477,7 @@ async function measureLiveBoard(root, ctx) {
2036
2477
  let repos = ["."];
2037
2478
  let manifestUnread = false;
2038
2479
  try {
2039
- const declared = ((await readManifest3(paths)).repos ?? []).map((repo) => repo.path);
2480
+ const declared = ((await readManifest4(paths)).repos ?? []).map((repo) => repo.path);
2040
2481
  if (declared.length > 0) repos = declared;
2041
2482
  } catch {
2042
2483
  manifestUnread = true;
@@ -2075,7 +2516,7 @@ function boardPath(options, cwd, root, manifest) {
2075
2516
  `No --board given, and the default ${DEFAULT_BOARD_PATH} is used only when the manifest declares this workspace's own repo (path: .) private. Pass --board <path to board.yaml>.`
2076
2517
  );
2077
2518
  }
2078
- return { path: join3(root, DEFAULT_BOARD_PATH), shown: DEFAULT_BOARD_PATH, given: false };
2519
+ return { path: join4(root, DEFAULT_BOARD_PATH), shown: DEFAULT_BOARD_PATH, given: false };
2079
2520
  }
2080
2521
  async function readDeclaration(board) {
2081
2522
  try {
@@ -2307,19 +2748,19 @@ function printMeasurementText(m, hasRecords2) {
2307
2748
 
2308
2749
  // src/lib/context-channel.ts
2309
2750
  import { homedir as homedir2 } from "os";
2310
- import { join as join5 } from "path";
2751
+ import { join as join6 } from "path";
2311
2752
  import {
2312
2753
  ORIENTATION_END,
2313
2754
  ORIENTATION_START,
2314
2755
  parseMarkers,
2315
- readMarkdownFile as readMarkdownFile2,
2756
+ readMarkdownFile as readMarkdownFile3,
2316
2757
  removeMarkerSection
2317
2758
  } from "@basou/core";
2318
2759
 
2319
2760
  // src/lib/durable-write.ts
2320
2761
  import { randomUUID } from "crypto";
2321
2762
  import { lstat as lstat2, open, rename, stat as stat3, unlink as unlink2 } from "fs/promises";
2322
- import { basename as basename4, dirname as dirname4, join as join4 } from "path";
2763
+ import { basename as basename5, dirname as dirname6, join as join5 } from "path";
2323
2764
  async function assertNotSymlink(targetPath) {
2324
2765
  try {
2325
2766
  const st = await lstat2(targetPath);
@@ -2334,8 +2775,8 @@ async function assertNotSymlink(targetPath) {
2334
2775
  }
2335
2776
  }
2336
2777
  async function writeFileDurable(targetPath, content) {
2337
- const dir = dirname4(targetPath);
2338
- const tmpPath = join4(dir, `.${basename4(targetPath)}.tmp.${randomUUID()}`);
2778
+ const dir = dirname6(targetPath);
2779
+ const tmpPath = join5(dir, `.${basename5(targetPath)}.tmp.${randomUUID()}`);
2339
2780
  let mode = 420;
2340
2781
  try {
2341
2782
  mode = (await stat3(targetPath)).mode & 511;
@@ -2370,7 +2811,7 @@ async function writeFileDurable(targetPath, content) {
2370
2811
  }
2371
2812
 
2372
2813
  // src/lib/context-channel.ts
2373
- var CODEX_TARGET_PATH = join5(homedir2(), ".codex", "AGENTS.md");
2814
+ var CODEX_TARGET_PATH = join6(homedir2(), ".codex", "AGENTS.md");
2374
2815
  var ORIENTATION_MARKERS = { start: ORIENTATION_START, end: ORIENTATION_END };
2375
2816
  function buildTargetBody(existing, block, markers) {
2376
2817
  const wrapped = `${markers.start}
@@ -2394,20 +2835,20 @@ ${block}${markers.end}${section.after}`;
2394
2835
  }
2395
2836
  async function backupOnce(target, existing) {
2396
2837
  const bak = `${target}.basou-bak`;
2397
- const already = await readMarkdownFile2(bak);
2838
+ const already = await readMarkdownFile3(bak);
2398
2839
  if (already !== null) return;
2399
2840
  await writeFileDurable(bak, existing ?? "");
2400
2841
  }
2401
2842
  async function syncMarkerBlock(opts) {
2402
2843
  const { target, markers, block } = opts;
2403
2844
  await assertNotSymlink(target);
2404
- const existing = await readMarkdownFile2(target);
2845
+ const existing = await readMarkdownFile3(target);
2405
2846
  const newBody = buildTargetBody(existing, block, markers);
2406
2847
  if (newBody === existing) return { action: "unchanged" };
2407
2848
  const hadBlock = existing !== null && parseMarkers(existing, markers).kind === "ok";
2408
2849
  const action = hadBlock ? "updated" : "installed";
2409
2850
  if (opts.dryRun === true) return { action };
2410
- const recheck = await readMarkdownFile2(target);
2851
+ const recheck = await readMarkdownFile3(target);
2411
2852
  if (recheck !== existing) {
2412
2853
  throw new Error(
2413
2854
  "The target changed during sync; aborting so a concurrent edit is not overwritten. Re-run the command."
@@ -2429,12 +2870,12 @@ function assertNoMarkerLine(body, markers) {
2429
2870
  async function removeMarkerBlock(opts) {
2430
2871
  const { target, markers, fileLabel } = opts;
2431
2872
  await assertNotSymlink(target);
2432
- const existing = await readMarkdownFile2(target);
2873
+ const existing = await readMarkdownFile3(target);
2433
2874
  if (existing === null) return { removed: false };
2434
2875
  const newBody = removeMarkerSection(existing, fileLabel, markers);
2435
2876
  if (newBody === existing) return { removed: false };
2436
2877
  if (opts.dryRun === true) return { removed: true };
2437
- const recheck = await readMarkdownFile2(target);
2878
+ const recheck = await readMarkdownFile3(target);
2438
2879
  if (recheck !== existing) {
2439
2880
  throw new Error(
2440
2881
  "The target changed during unsync; aborting so a concurrent edit is not overwritten. Re-run the command."
@@ -2515,7 +2956,7 @@ async function doRunChannelClear(face, options) {
2515
2956
  // src/commands/decision.ts
2516
2957
  import { readFile as readFile3 } from "fs/promises";
2517
2958
  import { homedir as homedir3 } from "os";
2518
- import { join as join6, resolve as resolve4 } from "path";
2959
+ import { join as join7, resolve as resolve4 } from "path";
2519
2960
  import {
2520
2961
  AGENT_INFRA_DIRS as AGENT_INFRA_DIRS2,
2521
2962
  acquireLock as acquireLock2,
@@ -2531,7 +2972,7 @@ import {
2531
2972
  LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE2,
2532
2973
  loadSessionEntries,
2533
2974
  prefixedUlid as prefixedUlid2,
2534
- readManifest as readManifest4,
2975
+ readManifest as readManifest5,
2535
2976
  replayEvents as replayEvents2,
2536
2977
  resolveSessionId,
2537
2978
  sanitizePath
@@ -2794,7 +3235,7 @@ async function runDecisionRecord(options, ctx = {}) {
2794
3235
  async function warnLinkedFilesOutsideRoots(input) {
2795
3236
  if (input.linkedFiles.length === 0) return;
2796
3237
  try {
2797
- const manifest = await readManifest4(input.paths);
3238
+ const manifest = await readManifest5(input.paths);
2798
3239
  if ((manifest.import?.source_roots?.length ?? 0) === 0) return;
2799
3240
  const scope = await classifyFilesBySourceRoot2({
2800
3241
  files: input.linkedFiles,
@@ -2860,7 +3301,7 @@ async function doRunDecisionRecord(options, ctx) {
2860
3301
  });
2861
3302
  return;
2862
3303
  }
2863
- const manifest = await readManifest4(paths);
3304
+ const manifest = await readManifest5(paths);
2864
3305
  const adHoc = await createAdHocSessionWithEvent({
2865
3306
  paths,
2866
3307
  manifest,
@@ -2924,7 +3365,7 @@ async function doRunDecisionCapture(options, ctx) {
2924
3365
  const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
2925
3366
  const occurredAt = now.toISOString();
2926
3367
  const decisionIds = decisions.map(() => prefixedUlid2("decision"));
2927
- const manifest = await readManifest4(paths);
3368
+ const manifest = await readManifest5(paths);
2928
3369
  const invocationArgs = options.file !== void 0 ? [
2929
3370
  "--file",
2930
3371
  sanitizePath(resolve4(cwd, options.file), {
@@ -3030,7 +3471,7 @@ async function doRunDecisionVoid(decisionId, options, ctx) {
3030
3471
  });
3031
3472
  return;
3032
3473
  }
3033
- const manifest = await readManifest4(paths);
3474
+ const manifest = await readManifest5(paths);
3034
3475
  const adHoc = await createAdHocSessionWithEvent({
3035
3476
  paths,
3036
3477
  manifest,
@@ -3066,7 +3507,7 @@ function isDecisionId(value) {
3066
3507
  async function decisionExists(paths, decisionId) {
3067
3508
  const entries = await loadSessionEntries(paths, { now: /* @__PURE__ */ new Date() });
3068
3509
  for (const entry of entries) {
3069
- const sessionDir = join6(paths.sessions, entry.sessionId);
3510
+ const sessionDir = join7(paths.sessions, entry.sessionId);
3070
3511
  try {
3071
3512
  for await (const ev of replayEvents2(sessionDir, {})) {
3072
3513
  if (ev.type === "decision_recorded" && ev.decision_id === decisionId) return true;
@@ -3478,7 +3919,7 @@ import {
3478
3919
  assertBasouRootSafe as assertBasouRootSafe5,
3479
3920
  basouPaths as basouPaths7,
3480
3921
  findErrorCode as findErrorCode5,
3481
- readMarkdownFile as readMarkdownFile3,
3922
+ readMarkdownFile as readMarkdownFile4,
3482
3923
  renderDecisions as renderDecisions2,
3483
3924
  renderWithMarkers as renderWithMarkers2,
3484
3925
  writeMarkdownFile as writeMarkdownFile2
@@ -3510,7 +3951,7 @@ async function doRunDecisionsGenerate(options, ctx) {
3510
3951
  onWarning: (w, sid) => printReplayWarning(w, sid),
3511
3952
  onSessionSkip: (sid, reason) => printSessionSkip(sid, reason)
3512
3953
  });
3513
- const existing = await readMarkdownFile3(paths.files.decisions);
3954
+ const existing = await readMarkdownFile4(paths.files.decisions);
3514
3955
  const finalBody = renderWithMarkers2(existing, result.body, "decisions.md");
3515
3956
  await writeMarkdownFile2(paths.files.decisions, finalBody);
3516
3957
  console.log(`Generated .basou/decisions.md (decisions: ${result.decisionCount})`);
@@ -3529,7 +3970,7 @@ async function assertWorkspaceInitialized5(basouRoot) {
3529
3970
  // src/commands/exec.ts
3530
3971
  import { mkdir as mkdir2, rm as rm2 } from "fs/promises";
3531
3972
  import { homedir as homedir4 } from "os";
3532
- import { join as join7 } from "path";
3973
+ import { join as join8 } from "path";
3533
3974
  import {
3534
3975
  acquireLock as acquireLock3,
3535
3976
  assertBasouRootSafe as assertBasouRootSafe6,
@@ -3543,7 +3984,7 @@ import {
3543
3984
  overwriteYamlFile,
3544
3985
  parseDuration,
3545
3986
  prefixedUlid as prefixedUlid3,
3546
- readManifest as readManifest5,
3987
+ readManifest as readManifest6,
3547
3988
  readYamlFile as readYamlFile2,
3548
3989
  SESSION_SCHEMA_VERSION,
3549
3990
  SessionSchema,
@@ -3570,15 +4011,15 @@ async function runExec(command, args, options, ctx = {}) {
3570
4011
  const repoRoot = await resolveRepositoryRootInPlace(cwd, "exec");
3571
4012
  const paths = basouPaths8(repoRoot);
3572
4013
  await assertBasouRootSafe6(paths.root);
3573
- const manifest = await readManifest5(paths);
4014
+ const manifest = await readManifest6(paths);
3574
4015
  await assertSessionStoreSafe2(paths);
3575
4016
  const sessionId = prefixedUlid3("ses");
3576
- const sessionDir = join7(paths.sessions, sessionId);
4017
+ const sessionDir = join8(paths.sessions, sessionId);
3577
4018
  const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
3578
4019
  await coreAppendChainedEvent(paths, sessionId, event);
3579
4020
  });
3580
4021
  const startedAt = now().toISOString();
3581
- const sessionYamlPath = join7(sessionDir, "session.yaml");
4022
+ const sessionYamlPath = join8(sessionDir, "session.yaml");
3582
4023
  const session = buildInitialSession({
3583
4024
  id: sessionId,
3584
4025
  command,
@@ -3871,7 +4312,7 @@ import {
3871
4312
  assertBasouRootSafe as assertBasouRootSafe7,
3872
4313
  basouPaths as basouPaths9,
3873
4314
  findErrorCode as findErrorCode6,
3874
- readMarkdownFile as readMarkdownFile4,
4315
+ readMarkdownFile as readMarkdownFile5,
3875
4316
  renderHandoff as renderHandoff2,
3876
4317
  renderWithMarkers as renderWithMarkers3,
3877
4318
  writeMarkdownFile as writeMarkdownFile3
@@ -3904,7 +4345,7 @@ async function doRunHandoffGenerate(options, ctx) {
3904
4345
  onSessionSkip: (sid, reason) => printSessionSkip(sid, reason),
3905
4346
  onTaskSkip: (taskId, reason) => printTaskSkip(taskId, reason)
3906
4347
  });
3907
- const existing = await readMarkdownFile4(paths.files.handoff);
4348
+ const existing = await readMarkdownFile5(paths.files.handoff);
3908
4349
  const finalBody = renderWithMarkers3(existing, result.body, "handoff.md");
3909
4350
  await writeMarkdownFile3(paths.files.handoff, finalBody);
3910
4351
  console.log(
@@ -3939,7 +4380,7 @@ async function assertWorkspaceInitialized6(basouRoot) {
3939
4380
  import { execFile } from "child_process";
3940
4381
  import { open as open2, readFile as readFile4, realpath as realpath4, stat as stat4 } from "fs/promises";
3941
4382
  import { homedir as homedir7 } from "os";
3942
- import { join as join10 } from "path";
4383
+ import { join as join11 } from "path";
3943
4384
  import { fileURLToPath } from "url";
3944
4385
  import { promisify } from "util";
3945
4386
  import {
@@ -3966,7 +4407,7 @@ import {
3966
4407
  parseProtocolStamp,
3967
4408
  protocolSectionsFrom,
3968
4409
  protocolUpdateToken,
3969
- readManifest as readManifest6,
4410
+ readManifest as readManifest7,
3970
4411
  readMarkdownFile as readMarkdownFile6,
3971
4412
  recordSessionBaseline,
3972
4413
  removeClaudeSessionStartHook,
@@ -3993,12 +4434,12 @@ function commandHandlerFields(handler) {
3993
4434
  out.additionalContextLimit = handler.additionalContextLimit;
3994
4435
  return out;
3995
4436
  }
3996
- function canonicalize(value) {
3997
- if (Array.isArray(value)) return value.map(canonicalize);
4437
+ function canonicalize2(value) {
4438
+ if (Array.isArray(value)) return value.map(canonicalize2);
3998
4439
  if (typeof value === "object" && value !== null) {
3999
4440
  const src = value;
4000
4441
  const out = {};
4001
- for (const key of Object.keys(src).sort()) out[key] = canonicalize(src[key]);
4442
+ for (const key of Object.keys(src).sort()) out[key] = canonicalize2(src[key]);
4002
4443
  return out;
4003
4444
  }
4004
4445
  return value;
@@ -4017,7 +4458,7 @@ function computeCodexHookIdentityHash(input) {
4017
4458
  }
4018
4459
  const identity = { event_name: input.eventKey, hooks: [normalized] };
4019
4460
  if (input.matcher !== void 0) identity.matcher = input.matcher;
4020
- const blob = JSON.stringify(canonicalize(identity));
4461
+ const blob = JSON.stringify(canonicalize2(identity));
4021
4462
  return `sha256:${createHash("sha256").update(blob, "utf8").digest("hex")}`;
4022
4463
  }
4023
4464
  function codexHookStateKey(hooksPath, eventKey, groupIndex, handlerIndex) {
@@ -4098,148 +4539,17 @@ function describeCodexHookTrust(trust) {
4098
4539
  }
4099
4540
  }
4100
4541
 
4101
- // src/lib/foreign-workspace-warn.ts
4102
- import { realpath as realpath3 } from "fs/promises";
4103
- import { dirname as dirname5 } from "path";
4104
- import { loadPortfolioConfig as loadPortfolioConfig2, readMarkdownFile as readMarkdownFile5 } from "@basou/core";
4105
-
4106
- // src/lib/foreign-workspace-scan.ts
4107
- import { homedir as osHomedir } from "os";
4108
- import { basename as basename5, normalize, sep as sep2 } from "path";
4109
- var MIN_TOKEN_LENGTH = 4;
4110
- function stripTrailingSep(p) {
4111
- return p.length > 1 && p.endsWith(sep2) ? p.slice(0, -sep2.length) : p;
4112
- }
4113
- function toTildePath(absPath, home) {
4114
- if (absPath === home) return "~";
4115
- if (absPath.startsWith(home + sep2)) return `~${absPath.slice(home.length)}`;
4116
- return null;
4117
- }
4118
- function encodedSpelling(name) {
4119
- return name.replace(/[^a-zA-Z0-9]/g, "-");
4120
- }
4121
- function directoryNameSpellings(dir) {
4122
- const paired = /^(.+)-(planning|workspace)$/.exec(dir);
4123
- const plain = paired !== null ? [`${paired[1]}-planning`, `${paired[1]}-workspace`] : [dir, `${dir}-workspace`];
4124
- return [...plain, ...plain.map(encodedSpelling)];
4125
- }
4126
- function nameTokensFor(workspacePath, home) {
4127
- const abs = stripTrailingSep(normalize(workspacePath));
4128
- const tokens = /* @__PURE__ */ new Set([abs]);
4129
- const tilde = toTildePath(abs, home);
4130
- if (tilde !== null) tokens.add(tilde);
4131
- for (const name of directoryNameSpellings(basename5(abs))) tokens.add(name);
4132
- return [...tokens].filter((t) => t.length >= MIN_TOKEN_LENGTH);
4133
- }
4134
- function isSpellingOfSelf(workspacePath, selfPath) {
4135
- const own = new Set(directoryNameSpellings(basename5(stripTrailingSep(normalize(selfPath)))));
4136
- return directoryNameSpellings(basename5(stripTrailingSep(normalize(workspacePath)))).some(
4137
- (s) => own.has(s)
4138
- );
4139
- }
4140
- function scanForeignWorkspaceNames(input) {
4141
- if (input.text.length === 0 || input.workspacePaths.length === 0) return [];
4142
- const home = input.homedir ?? osHomedir();
4143
- const own = input.selfPath === void 0 ? [] : nameTokensFor(input.selfPath, home);
4144
- const lines = input.text.split("\n");
4145
- const hits = [];
4146
- for (const workspacePath of input.workspacePaths) {
4147
- if (input.selfPath !== void 0 && isSpellingOfSelf(workspacePath, input.selfPath)) continue;
4148
- const tokens = nameTokensFor(workspacePath, home).filter(
4149
- (token) => !own.some((ownToken) => ownToken.includes(token))
4150
- );
4151
- if (tokens.length === 0) continue;
4152
- const matched = /* @__PURE__ */ new Set();
4153
- const matchedLines = [];
4154
- for (const [index, line] of lines.entries()) {
4155
- let lineMatched = false;
4156
- for (const token of tokens) {
4157
- if (line.includes(token)) {
4158
- matched.add(token);
4159
- lineMatched = true;
4160
- }
4161
- }
4162
- if (lineMatched) matchedLines.push(index + 1);
4163
- }
4164
- if (matchedLines.length === 0) continue;
4165
- hits.push({
4166
- workspacePath,
4167
- tokens: [...matched].sort((a, b) => b.length - a.length || a.localeCompare(b)),
4168
- lines: matchedLines
4169
- });
4170
- }
4171
- return hits;
4172
- }
4173
-
4174
- // src/lib/foreign-workspace-warn.ts
4175
- var MAX_LISTED_LINES = 5;
4176
- async function canonicalize2(path) {
4177
- try {
4178
- return await realpath3(path);
4179
- } catch {
4180
- return path;
4181
- }
4182
- }
4183
- async function findForeignWorkspaceNames(args) {
4184
- let workspacePaths;
4185
- let selfPath;
4186
- try {
4187
- const workspaces = await loadPortfolioConfig2(args.configPath);
4188
- workspacePaths = await Promise.all(workspaces.map((w) => canonicalize2(w.path)));
4189
- selfPath = args.selfPath === void 0 ? void 0 : await canonicalize2(args.selfPath);
4190
- } catch {
4191
- return null;
4192
- }
4193
- const hits = scanForeignWorkspaceNames({
4194
- text: args.text,
4195
- workspacePaths,
4196
- selfPath
4197
- });
4198
- if (hits.length === 0) return null;
4199
- const lines = [...new Set(hits.flatMap((h) => h.lines))].sort((a, b) => a - b);
4200
- return { workspaceCount: hits.length, lines };
4201
- }
4202
- function describeForeignWorkspaceLines(lines) {
4203
- const listed = lines.slice(0, MAX_LISTED_LINES).join(", ");
4204
- const rest = lines.length - MAX_LISTED_LINES;
4205
- const numbers = rest > 0 ? `${listed} and ${rest} more` : listed;
4206
- return `${lines.length === 1 ? "line" : "lines"} ${numbers}`;
4207
- }
4208
- function positionForeignWorkspaceWarning(report, where) {
4209
- const subject = report.workspaceCount === 1 ? "names another registered workspace" : `names ${report.workspaceCount} other registered workspaces`;
4210
- return `basou: this workspace's position ${subject} (${where}, ${describeForeignWorkspaceLines(report.lines)}). The position is handed to the agent session running in this workspace, so those names travel with it. Advisory only: nothing was withheld.`;
4211
- }
4212
- function protocolForeignWorkspaceWarning(report) {
4213
- const subject = report.workspaceCount === 1 ? "names a registered workspace" : `names ${report.workspaceCount} registered workspaces`;
4214
- return `basou: the protocol block ${subject} (block ${describeForeignWorkspaceLines(report.lines)}, counted from the block's first line, not the file's). Standing protocols are written to the user-global CLAUDE.md, which every project on this machine loads, so a workspace-specific name there reaches every workspace's sessions. Only the basou-managed block is checked; the rest of that file is not basou's to inspect. Advisory only: nothing was withheld.`;
4215
- }
4216
- async function warnIfPositionNamesOtherWorkspaces(args) {
4217
- try {
4218
- const body = await readMarkdownFile5(args.paths.files.orientation);
4219
- if (body === null) return;
4220
- const report = await findForeignWorkspaceNames({
4221
- text: body,
4222
- selfPath: dirname5(args.paths.root),
4223
- configPath: args.configPath
4224
- });
4225
- if (report !== null) {
4226
- console.error(positionForeignWorkspaceWarning(report, args.paths.files.orientation));
4227
- }
4228
- } catch {
4229
- }
4230
- }
4231
-
4232
4542
  // src/lib/protocols-config.ts
4233
4543
  import { homedir as homedir5 } from "os";
4234
- import { isAbsolute, join as join8, resolve as resolve5 } from "path";
4544
+ import { isAbsolute, join as join9, resolve as resolve5 } from "path";
4235
4545
  import { readYamlFile as readYamlFile3 } from "@basou/core";
4236
- var DEFAULT_PROTOCOLS_CONFIG_PATH = join8(homedir5(), ".basou", "protocols.yaml");
4237
- var DEFAULT_TARGET_PATH = join8(homedir5(), ".claude", "CLAUDE.md");
4546
+ var DEFAULT_PROTOCOLS_CONFIG_PATH = join9(homedir5(), ".basou", "protocols.yaml");
4547
+ var DEFAULT_TARGET_PATH = join9(homedir5(), ".claude", "CLAUDE.md");
4238
4548
  var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set(["version", "protocols"]);
4239
4549
  var ALLOWED_ENTRY_KEYS = /* @__PURE__ */ new Set(["source", "title"]);
4240
4550
  function expandTilde(p) {
4241
4551
  if (p === "~") return homedir5();
4242
- if (p.startsWith("~/")) return join8(homedir5(), p.slice(2));
4552
+ if (p.startsWith("~/")) return join9(homedir5(), p.slice(2));
4243
4553
  return p;
4244
4554
  }
4245
4555
  function isRecord(value) {
@@ -4317,12 +4627,12 @@ import {
4317
4627
 
4318
4628
  // src/lib/hosts-config.ts
4319
4629
  import { homedir as homedir6 } from "os";
4320
- import { isAbsolute as isAbsolute2, join as join9, resolve as resolve6 } from "path";
4630
+ import { isAbsolute as isAbsolute2, join as join10, resolve as resolve6 } from "path";
4321
4631
  import { readYamlFile as readYamlFile4 } from "@basou/core";
4322
- var DEFAULT_HOSTS_CONFIG_PATH = join9(homedir6(), ".basou", "hosts.yaml");
4632
+ var DEFAULT_HOSTS_CONFIG_PATH = join10(homedir6(), ".basou", "hosts.yaml");
4323
4633
  function expandTilde2(p) {
4324
4634
  if (p === "~") return homedir6();
4325
- if (p.startsWith("~/")) return join9(homedir6(), p.slice(2));
4635
+ if (p.startsWith("~/")) return join10(homedir6(), p.slice(2));
4326
4636
  return p;
4327
4637
  }
4328
4638
  function isRecord2(value) {
@@ -4442,6 +4752,7 @@ async function renderOrientationForRoot(repositoryRoot, options, ctx, behaviour)
4442
4752
  paths,
4443
4753
  nowIso,
4444
4754
  staleness,
4755
+ board: await glanceDefaultBoard(repositoryRoot, paths),
4445
4756
  verbose: options.verbose === true,
4446
4757
  federatedRoots,
4447
4758
  onWarning: (w, sid) => printReplayWarning(w, sid),
@@ -4843,7 +5154,7 @@ async function observeSessionFromPayload(fields, pass, portfolioConfigPath) {
4843
5154
  });
4844
5155
  return;
4845
5156
  }
4846
- const manifest = await readManifest6(paths);
5157
+ const manifest = await readManifest7(paths);
4847
5158
  await recordSessionBaseline({
4848
5159
  observationsDir: paths.observations,
4849
5160
  repoRoots: observedRepoRoots(root, manifest),
@@ -4905,7 +5216,7 @@ function parseMinEdits(raw) {
4905
5216
  if (raw === void 0 || !/^\d+$/.test(raw)) return void 0;
4906
5217
  return Number(raw);
4907
5218
  }
4908
- var DEFAULT_CLAUDE_SETTINGS_PATH = join10(homedir7(), ".claude", "settings.json");
5219
+ var DEFAULT_CLAUDE_SETTINGS_PATH = join11(homedir7(), ".claude", "settings.json");
4909
5220
  function resolveCliEntry() {
4910
5221
  return fileURLToPath(import.meta.url);
4911
5222
  }
@@ -5262,7 +5573,7 @@ function describeHookMode(tiers) {
5262
5573
  const gates = tiers.review ? "capture + review" : "capture";
5263
5574
  return `${enforcement}, ${gates}`;
5264
5575
  }
5265
- var DEFAULT_CODEX_FACE_PATH = join10(homedir7(), ".codex", "AGENTS.md");
5576
+ var DEFAULT_CODEX_FACE_PATH = join11(homedir7(), ".codex", "AGENTS.md");
5266
5577
  var LEFTOVER_FACE_NOTE = (label) => `${label} still carries an orientation block rendered by an earlier basou (0.39 or before); every Codex session on this machine reads it. \`basou channel clear codex\` removes it.`;
5267
5578
  async function faceHasLeftoverOrientationBlock(facePath) {
5268
5579
  try {
@@ -5274,8 +5585,8 @@ async function faceHasLeftoverOrientationBlock(facePath) {
5274
5585
  return false;
5275
5586
  }
5276
5587
  }
5277
- var DEFAULT_CODEX_HOOKS_PATH = join10(homedir7(), ".codex", "hooks.json");
5278
- var DEFAULT_CODEX_CONFIG_PATH = join10(homedir7(), ".codex", "config.toml");
5588
+ var DEFAULT_CODEX_HOOKS_PATH = join11(homedir7(), ".codex", "hooks.json");
5589
+ var DEFAULT_CODEX_CONFIG_PATH = join11(homedir7(), ".codex", "config.toml");
5279
5590
  async function readHooksFile(path) {
5280
5591
  let raw;
5281
5592
  try {
@@ -5533,7 +5844,7 @@ import {
5533
5844
  EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION4,
5534
5845
  findErrorCode as findErrorCode8,
5535
5846
  LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE3,
5536
- readManifest as readManifest7,
5847
+ readManifest as readManifest8,
5537
5848
  resolveSessionId as resolveSessionId2,
5538
5849
  sanitizePath as sanitizePath2
5539
5850
  } from "@basou/core";
@@ -5645,7 +5956,7 @@ ${HEREDOC_EXAMPLE}`
5645
5956
  });
5646
5957
  return;
5647
5958
  }
5648
- const manifest = await readManifest7(paths);
5959
+ const manifest = await readManifest8(paths);
5649
5960
  const adHoc = await createAdHocSessionWithEvent2({
5650
5961
  paths,
5651
5962
  manifest,
@@ -5893,7 +6204,7 @@ import {
5893
6204
  writeFileSync,
5894
6205
  writeSync
5895
6206
  } from "fs";
5896
- import { basename as basename7, dirname as dirname6, isAbsolute as isAbsolute3, join as join11, relative as relative3, resolve as resolve9 } from "path";
6207
+ import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute3, join as join12, relative as relative3, resolve as resolve9 } from "path";
5897
6208
  import {
5898
6209
  appendBasouGitignore as appendBasouGitignore2,
5899
6210
  basouPaths as basouPaths13,
@@ -5911,7 +6222,7 @@ import {
5911
6222
  planRename,
5912
6223
  planRosterAdoption,
5913
6224
  planWorkspaceView,
5914
- readManifest as readManifest8,
6225
+ readManifest as readManifest9,
5915
6226
  readMarkdownFile as readMarkdownFile7,
5916
6227
  reconcileSourceRoots,
5917
6228
  removeMarkerSection as removeMarkerSection2,
@@ -6059,7 +6370,7 @@ async function doRunProjectCheck(options, ctx) {
6059
6370
  const cwd = ctx.cwd ?? process.cwd();
6060
6371
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project check");
6061
6372
  const paths = basouPaths13(repositoryRoot);
6062
- const manifest = await readManifest8(paths);
6373
+ const manifest = await readManifest9(paths);
6063
6374
  const roster = summarizeRosterDrift({
6064
6375
  ...manifest.repos !== void 0 ? { repos: manifest.repos } : {},
6065
6376
  sourceRoots: effectiveSourceRoots(manifest)
@@ -6194,7 +6505,7 @@ async function doRunProjectSync(options, ctx) {
6194
6505
  const cwd = ctx.cwd ?? process.cwd();
6195
6506
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project sync");
6196
6507
  const paths = basouPaths13(repositoryRoot);
6197
- const manifest = await readManifest8(paths);
6508
+ const manifest = await readManifest9(paths);
6198
6509
  const hasRoster = manifest.repos !== void 0 && manifest.repos.length > 0;
6199
6510
  const reconcile = reconcileSourceRoots({
6200
6511
  ...manifest.repos !== void 0 ? { repos: manifest.repos } : {},
@@ -6274,13 +6585,13 @@ function classifySourceRoot(repositoryRoot, declaredPath) {
6274
6585
  } catch {
6275
6586
  return { path: declaredPath, kind: "unresolved" };
6276
6587
  }
6277
- return { path: declaredPath, kind: existsSync(join11(real, ".git")) ? "repo" : "non-repo" };
6588
+ return { path: declaredPath, kind: existsSync(join12(real, ".git")) ? "repo" : "non-repo" };
6278
6589
  }
6279
6590
  async function doRunProjectAdopt(options, ctx) {
6280
6591
  const cwd = ctx.cwd ?? process.cwd();
6281
6592
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project adopt");
6282
6593
  const paths = basouPaths13(repositoryRoot);
6283
- const manifest = await readManifest8(paths);
6594
+ const manifest = await readManifest9(paths);
6284
6595
  const alreadyDeclared = manifest.repos !== void 0 && manifest.repos.length > 0;
6285
6596
  const candidates = effectiveSourceRoots(manifest).map(
6286
6597
  (r) => classifySourceRoot(repositoryRoot, r)
@@ -6378,7 +6689,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
6378
6689
  } catch {
6379
6690
  return { ...base, reachable: false, instructionFiles: [] };
6380
6691
  }
6381
- if (!existsSync(join11(real, ".git"))) {
6692
+ if (!existsSync(join12(real, ".git"))) {
6382
6693
  return { ...base, reachable: false, instructionFiles: [] };
6383
6694
  }
6384
6695
  try {
@@ -6386,7 +6697,7 @@ async function gatherRepoWiring(repositoryRoot, entry) {
6386
6697
  for (const name of INSTRUCTION_FILES) {
6387
6698
  let present = true;
6388
6699
  try {
6389
- lstatSync(join11(real, name));
6700
+ lstatSync(join12(real, name));
6390
6701
  } catch {
6391
6702
  present = false;
6392
6703
  }
@@ -6402,7 +6713,7 @@ async function doRunProjectWiring(options, ctx) {
6402
6713
  const cwd = ctx.cwd ?? process.cwd();
6403
6714
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project wiring");
6404
6715
  const paths = basouPaths13(repositoryRoot);
6405
- const manifest = await readManifest8(paths);
6716
+ const manifest = await readManifest9(paths);
6406
6717
  const roster = manifest.repos ?? [];
6407
6718
  const facts = [];
6408
6719
  for (const entry of roster) facts.push(await gatherRepoWiring(repositoryRoot, entry));
@@ -6497,10 +6808,10 @@ function gatherRepoGitignore(repositoryRoot, entry) {
6497
6808
  } catch {
6498
6809
  return { ...base, reachable: false, currentLines: [] };
6499
6810
  }
6500
- if (!existsSync(join11(real, ".git"))) {
6811
+ if (!existsSync(join12(real, ".git"))) {
6501
6812
  return { ...base, reachable: false, currentLines: [] };
6502
6813
  }
6503
- return { ...base, reachable: true, currentLines: readGitignoreLines(join11(real, ".gitignore")) };
6814
+ return { ...base, reachable: true, currentLines: readGitignoreLines(join12(real, ".gitignore")) };
6504
6815
  }
6505
6816
  function hasErrorCode(error) {
6506
6817
  return error instanceof Error && typeof error.code === "string";
@@ -6514,7 +6825,7 @@ function readGitignoreLines(file) {
6514
6825
  }
6515
6826
  }
6516
6827
  function applyGitignorePlan(repositoryRoot, plan) {
6517
- const file = join11(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
6828
+ const file = join12(realpathSync(resolve9(repositoryRoot, plan.path)), ".gitignore");
6518
6829
  let existing = "";
6519
6830
  try {
6520
6831
  existing = readFileSync(file, "utf8");
@@ -6535,7 +6846,7 @@ async function doRunProjectGitignore(options, ctx) {
6535
6846
  const cwd = ctx.cwd ?? process.cwd();
6536
6847
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project gitignore");
6537
6848
  const paths = basouPaths13(repositoryRoot);
6538
- const manifest = await readManifest8(paths);
6849
+ const manifest = await readManifest9(paths);
6539
6850
  const roster = manifest.repos ?? [];
6540
6851
  const facts = roster.map((entry) => gatherRepoGitignore(repositoryRoot, entry));
6541
6852
  const summary = planGitignore({ repos: facts, required: [...INSTRUCTION_FILES] });
@@ -6654,7 +6965,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
6654
6965
  return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
6655
6966
  }
6656
6967
  if (real === anchorReal) {
6657
- const anchorCanonical = join11(real, CANONICAL_FILE);
6968
+ const anchorCanonical = join12(real, CANONICAL_FILE);
6658
6969
  const anchorState = anchorCanonicalState(anchorCanonical);
6659
6970
  if (anchorState === "absent") {
6660
6971
  return { ...base, isAnchor: true, reachable: true, canonicalPresent: false, files: [] };
@@ -6674,7 +6985,7 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
6674
6985
  anchorCanonical,
6675
6986
  "self"
6676
6987
  ).map((spec) => {
6677
- const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
6988
+ const { state, actualTarget } = inspectSymlink(join12(real, spec.name), spec.target);
6678
6989
  return {
6679
6990
  name: spec.name,
6680
6991
  expectedTarget: spec.target,
@@ -6691,16 +7002,16 @@ function gatherRepoSymlinks(repositoryRoot, anchorReal, entry) {
6691
7002
  files: anchorFiles
6692
7003
  };
6693
7004
  }
6694
- if (!existsSync(join11(real, ".git"))) {
7005
+ if (!existsSync(join12(real, ".git"))) {
6695
7006
  return { ...base, isAnchor: false, reachable: false, canonicalPresent: false, files: [] };
6696
7007
  }
6697
- const canonicalFile = isSelf ? join11(real, CANONICAL_FILE) : join11(anchorReal, "agents", basename7(real), CANONICAL_FILE);
7008
+ const canonicalFile = isSelf ? join12(real, CANONICAL_FILE) : join12(anchorReal, "agents", basename7(real), CANONICAL_FILE);
6698
7009
  if (!existsSync(canonicalFile)) {
6699
7010
  return { ...base, isAnchor: false, reachable: true, canonicalPresent: false, files: [] };
6700
7011
  }
6701
7012
  const files = expectedSymlinkTargets(real, canonicalFile, mode).map(
6702
7013
  (spec) => {
6703
- const { state, actualTarget } = inspectSymlink(join11(real, spec.name), spec.target);
7014
+ const { state, actualTarget } = inspectSymlink(join12(real, spec.name), spec.target);
6704
7015
  return {
6705
7016
  name: spec.name,
6706
7017
  expectedTarget: spec.target,
@@ -6729,9 +7040,9 @@ function applySymlinkPlan(repositoryRoot, plan) {
6729
7040
  const created = [];
6730
7041
  const failed = [];
6731
7042
  for (const { name, target } of plan.toCreate) {
6732
- const filePath = join11(real, name);
7043
+ const filePath = join12(real, name);
6733
7044
  try {
6734
- mkdirSync(dirname6(filePath), { recursive: true });
7045
+ mkdirSync(dirname7(filePath), { recursive: true });
6735
7046
  symlinkSync(target, filePath);
6736
7047
  created.push(name);
6737
7048
  } catch (error) {
@@ -6763,7 +7074,7 @@ function gatherViewSymlinks(repositoryRoot, anchorReal, roster, viewDir) {
6763
7074
  if (!existsSync(canonicalFile)) return { kind: "missing-canonical", viewName };
6764
7075
  const files = expectedSymlinkTargets(viewDir, canonicalFile, "hub").map(
6765
7076
  (spec) => {
6766
- const { state, actualTarget } = inspectSymlink(join11(viewDir, spec.name), spec.target);
7077
+ const { state, actualTarget } = inspectSymlink(join12(viewDir, spec.name), spec.target);
6767
7078
  return {
6768
7079
  name: spec.name,
6769
7080
  expectedTarget: spec.target,
@@ -6779,9 +7090,9 @@ function applyViewSymlinks(viewDir, files) {
6779
7090
  const failed = [];
6780
7091
  for (const f of files) {
6781
7092
  if (f.state !== "missing") continue;
6782
- const filePath = join11(viewDir, f.name);
7093
+ const filePath = join12(viewDir, f.name);
6783
7094
  try {
6784
- mkdirSync(dirname6(filePath), { recursive: true });
7095
+ mkdirSync(dirname7(filePath), { recursive: true });
6785
7096
  symlinkSync(f.expectedTarget, filePath);
6786
7097
  created.push(f.name);
6787
7098
  } catch (error) {
@@ -6794,7 +7105,7 @@ async function doRunProjectSymlinks(options, ctx) {
6794
7105
  const cwd = ctx.cwd ?? process.cwd();
6795
7106
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project symlinks");
6796
7107
  const paths = basouPaths13(repositoryRoot);
6797
- const manifest = await readManifest8(paths);
7108
+ const manifest = await readManifest9(paths);
6798
7109
  const roster = manifest.repos ?? [];
6799
7110
  const anchorReal = realpathSync(repositoryRoot);
6800
7111
  const facts = roster.map((entry) => gatherRepoSymlinks(repositoryRoot, anchorReal, entry));
@@ -7001,7 +7312,7 @@ function resolveViewDir(repositoryRoot, viewPath) {
7001
7312
  return realpathSync(abs);
7002
7313
  } catch {
7003
7314
  try {
7004
- return join11(realpathSync(dirname6(abs)), basename7(abs));
7315
+ return join12(realpathSync(dirname7(abs)), basename7(abs));
7005
7316
  } catch {
7006
7317
  return abs;
7007
7318
  }
@@ -7019,7 +7330,7 @@ function gatherViewRepo(repositoryRoot, viewDir, entry) {
7019
7330
  return { path: entry.path, reachable: false };
7020
7331
  }
7021
7332
  const linkName = basename7(repoReal);
7022
- const { state, actualTarget } = inspectSymlink(join11(viewDir, linkName), expectedTarget);
7333
+ const { state, actualTarget } = inspectSymlink(join12(viewDir, linkName), expectedTarget);
7023
7334
  return {
7024
7335
  path: entry.path,
7025
7336
  reachable: true,
@@ -7033,9 +7344,9 @@ function applyViewPlan(viewDir, toCreate) {
7033
7344
  const created = [];
7034
7345
  const failed = [];
7035
7346
  for (const { name, target } of toCreate) {
7036
- const filePath = join11(viewDir, name);
7347
+ const filePath = join12(viewDir, name);
7037
7348
  try {
7038
- mkdirSync(dirname6(filePath), { recursive: true });
7349
+ mkdirSync(dirname7(filePath), { recursive: true });
7039
7350
  symlinkSync(target, filePath);
7040
7351
  created.push(name);
7041
7352
  } catch (error) {
@@ -7048,7 +7359,7 @@ var TOP_LEVEL_INSTRUCTION_FILES_LOWER = new Set(
7048
7359
  INSTRUCTION_FILES.filter((f) => !f.includes("/")).map((f) => f.toLowerCase())
7049
7360
  );
7050
7361
  function classifyViewLink(viewDir, name, rosterRealpaths) {
7051
- const filePath = join11(viewDir, name);
7362
+ const filePath = join12(viewDir, name);
7052
7363
  let isLink;
7053
7364
  try {
7054
7365
  isLink = lstatSync(filePath).isSymbolicLink();
@@ -7077,7 +7388,7 @@ function classifyViewLink(viewDir, name, rosterRealpaths) {
7077
7388
  if (!isDir) {
7078
7389
  return { target, kind: existsSync(resolved) ? "non-repo" : "broken" };
7079
7390
  }
7080
- return { target, kind: existsSync(join11(resolved, ".git")) ? "repo" : "non-repo" };
7391
+ return { target, kind: existsSync(join12(resolved, ".git")) ? "repo" : "non-repo" };
7081
7392
  }
7082
7393
  function gatherExistingViewLinks(viewDir, rosterRealpaths) {
7083
7394
  let names;
@@ -7102,7 +7413,7 @@ function pruneViewLinks(viewDir, toPrune, rosterRealpaths) {
7102
7413
  const pruned = [];
7103
7414
  const failed = [];
7104
7415
  for (const { name } of toPrune) {
7105
- const filePath = join11(viewDir, name);
7416
+ const filePath = join12(viewDir, name);
7106
7417
  const c = classifyViewLink(viewDir, name, rosterRealpaths);
7107
7418
  if (c === null || c.kind !== "repo") {
7108
7419
  failed.push({
@@ -7124,7 +7435,7 @@ async function doRunProjectWorkspace(options, ctx) {
7124
7435
  const cwd = ctx.cwd ?? process.cwd();
7125
7436
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project workspace");
7126
7437
  const paths = basouPaths13(repositoryRoot);
7127
- const manifest = await readManifest8(paths);
7438
+ const manifest = await readManifest9(paths);
7128
7439
  const viewPath = manifest.workspace.view;
7129
7440
  const roster = manifest.repos ?? [];
7130
7441
  let result;
@@ -7315,10 +7626,10 @@ async function runProjectPreset(options, ctx = {}) {
7315
7626
  }
7316
7627
  }
7317
7628
  function canonicalFileFor(anchorReal, canonicalName) {
7318
- return join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7629
+ return join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7319
7630
  }
7320
7631
  function canonicalLabelFor(canonicalName) {
7321
- return join11("agents", canonicalName, CANONICAL_FILE);
7632
+ return join12("agents", canonicalName, CANONICAL_FILE);
7322
7633
  }
7323
7634
  async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
7324
7635
  const declared = {
@@ -7339,7 +7650,7 @@ async function gatherRepoPreset(repositoryRoot, anchorReal, entry) {
7339
7650
  if (real === anchorReal) {
7340
7651
  return { ...declared, isAnchor: true, reachable: true, canonicalPresent: false };
7341
7652
  }
7342
- if (!existsSync(join11(real, ".git"))) {
7653
+ if (!existsSync(join12(real, ".git"))) {
7343
7654
  return { ...declared, isAnchor: false, reachable: false, canonicalPresent: false };
7344
7655
  }
7345
7656
  const canonicalName = basename7(real);
@@ -7453,7 +7764,7 @@ async function applyViewPreset(anchorReal, outcome) {
7453
7764
  isLink = false;
7454
7765
  }
7455
7766
  if (isLink) throw new Error(`Canonical is a symlink in ${label}`);
7456
- if (outcome.action === "create") mkdirSync(dirname6(file), { recursive: true });
7767
+ if (outcome.action === "create") mkdirSync(dirname7(file), { recursive: true });
7457
7768
  const existing = await readMarkdownFile7(file);
7458
7769
  await writeMarkdownFile5(file, renderWithMarkers4(existing, outcome.block, label));
7459
7770
  }
@@ -7467,7 +7778,7 @@ async function applyPresetPlan(anchorReal, plan) {
7467
7778
  isLink = false;
7468
7779
  }
7469
7780
  if (isLink) throw new Error(`Canonical is a symlink in ${label}`);
7470
- if (plan.action === "create") mkdirSync(dirname6(file), { recursive: true });
7781
+ if (plan.action === "create") mkdirSync(dirname7(file), { recursive: true });
7471
7782
  const existing = await readMarkdownFile7(file);
7472
7783
  await writeMarkdownFile5(file, renderWithMarkers4(existing, plan.desiredBlock, label));
7473
7784
  }
@@ -7484,7 +7795,7 @@ async function doRunProjectPreset(options, ctx) {
7484
7795
  const cwd = ctx.cwd ?? process.cwd();
7485
7796
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project preset");
7486
7797
  const paths = basouPaths13(repositoryRoot);
7487
- const manifest = await readManifest8(paths);
7798
+ const manifest = await readManifest9(paths);
7488
7799
  const roster = manifest.repos ?? [];
7489
7800
  const anchorReal = realpathSync(repositoryRoot);
7490
7801
  const facts = [];
@@ -7721,24 +8032,24 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
7721
8032
  const instructionFiles = [];
7722
8033
  for (const name of INSTRUCTION_FILES) {
7723
8034
  try {
7724
- lstatSync(join11(real, name));
8035
+ lstatSync(join12(real, name));
7725
8036
  instructionFiles.push(name);
7726
8037
  } catch {
7727
8038
  }
7728
8039
  }
7729
8040
  let ignored;
7730
8041
  try {
7731
- ignored = new Set(readGitignoreLines(join11(real, ".gitignore")).map((l) => l.trim()));
8042
+ ignored = new Set(readGitignoreLines(join12(real, ".gitignore")).map((l) => l.trim()));
7732
8043
  } catch {
7733
8044
  ignored = /* @__PURE__ */ new Set();
7734
8045
  }
7735
8046
  const gitignorePatterns = INSTRUCTION_FILES.filter((p) => ignored.has(p) || ignored.has(`/${p}`));
7736
- const canonical2 = existsSync(join11(anchorReal, "agents", canonicalName, CANONICAL_FILE));
8047
+ const canonical2 = existsSync(join12(anchorReal, "agents", canonicalName, CANONICAL_FILE));
7737
8048
  let viewLink = false;
7738
8049
  const viewPath = manifest.workspace.view;
7739
8050
  if (viewPath !== void 0) {
7740
8051
  try {
7741
- lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), canonicalName));
8052
+ lstatSync(join12(resolveViewDir(repositoryRoot, viewPath), canonicalName));
7742
8053
  viewLink = true;
7743
8054
  } catch {
7744
8055
  }
@@ -7752,11 +8063,11 @@ function gatherArchiveTeardown(repositoryRoot, manifest, target) {
7752
8063
  };
7753
8064
  }
7754
8065
  function teardownExpectedTargets(repoReal, anchorReal, canonicalName) {
7755
- const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8066
+ const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7756
8067
  return expectedSymlinkTargets(repoReal, canonicalFile);
7757
8068
  }
7758
8069
  function viewLinkPointsAt(viewDir, name, repoReal) {
7759
- const filePath = join11(viewDir, name);
8070
+ const filePath = join12(viewDir, name);
7760
8071
  try {
7761
8072
  if (!lstatSync(filePath).isSymbolicLink()) return false;
7762
8073
  const target = readlinkSync(filePath);
@@ -7767,7 +8078,7 @@ function viewLinkPointsAt(viewDir, name, repoReal) {
7767
8078
  }
7768
8079
  }
7769
8080
  function viewLinkPointsAtPath(viewDir, name, expectedRepoPath) {
7770
- const filePath = join11(viewDir, name);
8081
+ const filePath = join12(viewDir, name);
7771
8082
  try {
7772
8083
  if (!lstatSync(filePath).isSymbolicLink()) return false;
7773
8084
  const target = readlinkSync(filePath);
@@ -7818,7 +8129,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
7818
8129
  if (!isAnchor) {
7819
8130
  if (repoReal !== void 0) {
7820
8131
  for (const spec of teardownExpectedTargets(repoReal, anchorReal, canonicalName)) {
7821
- const { state, actualTarget } = inspectSymlink(join11(repoReal, spec.name), spec.target);
8132
+ const { state, actualTarget } = inspectSymlink(join12(repoReal, spec.name), spec.target);
7822
8133
  if (isSelf) {
7823
8134
  if (state !== "missing")
7824
8135
  items.push({
@@ -7855,7 +8166,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
7855
8166
  }
7856
8167
  let ignored;
7857
8168
  try {
7858
- ignored = new Set(readGitignoreLines(join11(repoReal, ".gitignore")).map((l) => l.trim()));
8169
+ ignored = new Set(readGitignoreLines(join12(repoReal, ".gitignore")).map((l) => l.trim()));
7859
8170
  for (const p of INSTRUCTION_FILES) {
7860
8171
  if (ignored.has(p) || ignored.has(`/${p}`)) {
7861
8172
  items.push({
@@ -7878,7 +8189,7 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
7878
8189
  const viewPath = manifest.workspace.view;
7879
8190
  if (viewPath !== void 0) {
7880
8191
  const viewDir = resolveViewDir(repositoryRoot, viewPath);
7881
- const linkPath = join11(viewDir, canonicalName);
8192
+ const linkPath = join12(viewDir, canonicalName);
7882
8193
  let isLink = false;
7883
8194
  try {
7884
8195
  isLink = lstatSync(linkPath).isSymbolicLink();
@@ -7904,8 +8215,8 @@ function gatherRepoTeardown(repositoryRoot, manifest, target) {
7904
8215
  else items.push({ kind: "view-symlink", label: canonicalName, state: "removable" });
7905
8216
  }
7906
8217
  }
7907
- const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
7908
- const canonicalLabel = join11("agents", canonicalName, CANONICAL_FILE);
8218
+ const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8219
+ const canonicalLabel = join12("agents", canonicalName, CANONICAL_FILE);
7909
8220
  let canonicalIsLink = false;
7910
8221
  try {
7911
8222
  canonicalIsLink = lstatSync(canonicalFile).isSymbolicLink();
@@ -8009,12 +8320,12 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8009
8320
  );
8010
8321
  for (const item of removable.filter((i) => i.kind === "instruction-symlink")) {
8011
8322
  const expected = expectedByName.get(item.label);
8012
- if (repoReal === null || expected === void 0 || inspectSymlink(join11(repoReal, item.label), expected).state !== "correct") {
8323
+ if (repoReal === null || expected === void 0 || inspectSymlink(join12(repoReal, item.label), expected).state !== "correct") {
8013
8324
  changed(item.label);
8014
8325
  continue;
8015
8326
  }
8016
8327
  try {
8017
- unlinkSync(join11(repoReal, item.label));
8328
+ unlinkSync(join12(repoReal, item.label));
8018
8329
  removed.push(item.label);
8019
8330
  } catch (error) {
8020
8331
  failed.push({ label: item.label, message: failureReason(error) });
@@ -8033,7 +8344,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8033
8344
  continue;
8034
8345
  }
8035
8346
  try {
8036
- unlinkSync(join11(viewDir, item.label));
8347
+ unlinkSync(join12(viewDir, item.label));
8037
8348
  removed.push(`view/${item.label}`);
8038
8349
  } catch (error) {
8039
8350
  failed.push({ label: `view/${item.label}`, message: failureReason(error) });
@@ -8041,7 +8352,7 @@ function applyRepoTeardown(repositoryRoot, manifest, plan) {
8041
8352
  }
8042
8353
  const NOFOLLOW = fsConstants.O_NOFOLLOW ?? 0;
8043
8354
  for (const item of removable.filter((i) => i.kind === "canonical-block")) {
8044
- const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8355
+ const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8045
8356
  try {
8046
8357
  if (lstatSync(canonicalFile).isSymbolicLink()) {
8047
8358
  changed(item.label);
@@ -8143,7 +8454,7 @@ async function doRunProjectTeardown(target, options, ctx = {}) {
8143
8454
  const cwd = ctx.cwd ?? process.cwd();
8144
8455
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project teardown");
8145
8456
  const paths = basouPaths13(repositoryRoot);
8146
- const manifest = await readManifest8(paths);
8457
+ const manifest = await readManifest9(paths);
8147
8458
  const plan = gatherRepoTeardown(repositoryRoot, manifest, target);
8148
8459
  const willApply = options.apply === true && !plan.isAnchor && plan.removableCount > 0;
8149
8460
  const { removed, failed } = willApply ? applyRepoTeardown(repositoryRoot, manifest, plan) : { removed: [], failed: [] };
@@ -8185,7 +8496,7 @@ async function doRunProjectArchive(target, options, ctx) {
8185
8496
  const cwd = ctx.cwd ?? process.cwd();
8186
8497
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project archive");
8187
8498
  const paths = basouPaths13(repositoryRoot);
8188
- const manifest = await readManifest8(paths);
8499
+ const manifest = await readManifest9(paths);
8189
8500
  const roster = manifest.repos ?? [];
8190
8501
  let targetIsAnchor = false;
8191
8502
  try {
@@ -8315,12 +8626,12 @@ function gatherRenameWiring(repositoryRoot, manifest, oldBasename) {
8315
8626
  } catch {
8316
8627
  return { canonicalDirOld: false, viewLinkOld: false };
8317
8628
  }
8318
- const canonicalDirOld = existsSync(join11(anchorReal, "agents", oldBasename));
8629
+ const canonicalDirOld = existsSync(join12(anchorReal, "agents", oldBasename));
8319
8630
  let viewLinkOld = false;
8320
8631
  const viewPath = manifest.workspace.view;
8321
8632
  if (viewPath !== void 0) {
8322
8633
  try {
8323
- lstatSync(join11(resolveViewDir(repositoryRoot, viewPath), oldBasename));
8634
+ lstatSync(join12(resolveViewDir(repositoryRoot, viewPath), oldBasename));
8324
8635
  viewLinkOld = true;
8325
8636
  } catch {
8326
8637
  }
@@ -8342,7 +8653,7 @@ async function doRunProjectRename(oldPath, newPath, options, ctx) {
8342
8653
  const cwd = ctx.cwd ?? process.cwd();
8343
8654
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project rename");
8344
8655
  const paths = basouPaths13(repositoryRoot);
8345
- const manifest = await readManifest8(paths);
8656
+ const manifest = await readManifest9(paths);
8346
8657
  const roster = manifest.repos ?? [];
8347
8658
  let oldIsAnchor = false;
8348
8659
  try {
@@ -8616,7 +8927,7 @@ async function doRunProjectDerive(options, ctx) {
8616
8927
  const cwd = ctx.cwd ?? process.cwd();
8617
8928
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project derive");
8618
8929
  const paths = basouPaths13(repositoryRoot);
8619
- const manifest = await readManifest8(paths);
8930
+ const manifest = await readManifest9(paths);
8620
8931
  if (manifest.repos === void 0 || manifest.repos.length === 0) {
8621
8932
  console.log(
8622
8933
  "# Generate project wiring in one pass (declaration \u2192 wiring)\n\n\u2139\uFE0F No repo roster declared (manifest `repos`). Declare one first with `basou project new` (new project) or `basou project adopt` (bootstrap from existing source_roots)."
@@ -8657,7 +8968,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
8657
8968
  const cwd = ctx.cwd ?? process.cwd();
8658
8969
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project derive");
8659
8970
  const paths = basouPaths13(repositoryRoot);
8660
- const manifest = await readManifest8(paths);
8971
+ const manifest = await readManifest9(paths);
8661
8972
  const roster = manifest.repos ?? [];
8662
8973
  console.log("# Anchor instruction-file seed (the planning master's own AGENTS.md)");
8663
8974
  console.log("");
@@ -8665,7 +8976,7 @@ async function doRunProjectSeedAnchor(options, ctx) {
8665
8976
  console.log("\u2139\uFE0F No repo roster declared \u2014 nothing to seed.");
8666
8977
  return;
8667
8978
  }
8668
- const anchorDoc = join11(repositoryRoot, CANONICAL_FILE);
8979
+ const anchorDoc = join12(repositoryRoot, CANONICAL_FILE);
8669
8980
  if (pathPresent(anchorDoc)) {
8670
8981
  console.log(
8671
8982
  `\u2705 The anchor's own \`${CANONICAL_FILE}\` already exists \u2014 hand-maintained, left untouched.`
@@ -8727,7 +9038,7 @@ function regularFileSpokes(repoReal) {
8727
9038
  const out = [];
8728
9039
  for (const spoke of ["CLAUDE.md", ".github/copilot-instructions.md"]) {
8729
9040
  try {
8730
- const st = lstatSync(join11(repoReal, spoke));
9041
+ const st = lstatSync(join12(repoReal, spoke));
8731
9042
  if (!st.isSymbolicLink() && st.isFile()) out.push(spoke);
8732
9043
  } catch {
8733
9044
  }
@@ -8773,8 +9084,8 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
8773
9084
  };
8774
9085
  }
8775
9086
  const isAnchor = argReal === anchorReal;
8776
- const reachable = existsSync(join11(argReal, ".git"));
8777
- const canonicalFile = join11(anchorReal, "agents", canonicalName, CANONICAL_FILE);
9087
+ const reachable = existsSync(join12(argReal, ".git"));
9088
+ const canonicalFile = join12(anchorReal, "agents", canonicalName, CANONICAL_FILE);
8778
9089
  return {
8779
9090
  path,
8780
9091
  declared,
@@ -8783,15 +9094,15 @@ function gatherRetrofit(repositoryRoot, anchorReal, roster, argAbs, argReal, vie
8783
9094
  reachable,
8784
9095
  canonicalName,
8785
9096
  ...viewCanonicalName !== void 0 ? { viewCanonicalName } : {},
8786
- agentsState: inspectAgentsState(join11(argReal, CANONICAL_FILE)),
9097
+ agentsState: inspectAgentsState(join12(argReal, CANONICAL_FILE)),
8787
9098
  canonicalExists: pathPresent(canonicalFile),
8788
9099
  regularSpokes: regularFileSpokes(argReal)
8789
9100
  };
8790
9101
  }
8791
9102
  function relocateAgentsFile(repoReal, canonicalFile) {
8792
- const agentsFile = join11(repoReal, CANONICAL_FILE);
9103
+ const agentsFile = join12(repoReal, CANONICAL_FILE);
8793
9104
  try {
8794
- mkdirSync(dirname6(canonicalFile), { recursive: true });
9105
+ mkdirSync(dirname7(canonicalFile), { recursive: true });
8795
9106
  } catch (error) {
8796
9107
  return { ok: false, message: failureReason(error), partial: false };
8797
9108
  }
@@ -8851,7 +9162,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
8851
9162
  const cwd = ctx.cwd ?? process.cwd();
8852
9163
  const repositoryRoot = await resolveBasouRootForCommand(cwd, "project retrofit");
8853
9164
  const paths = basouPaths13(repositoryRoot);
8854
- const manifest = await readManifest8(paths);
9165
+ const manifest = await readManifest9(paths);
8855
9166
  const roster = manifest.repos ?? [];
8856
9167
  const anchorReal = realpathSync(repositoryRoot);
8857
9168
  const viewPath = manifest.workspace.view;
@@ -8905,7 +9216,7 @@ async function doRunProjectRetrofit(repo, options, ctx) {
8905
9216
  let failure;
8906
9217
  let partial = false;
8907
9218
  if (options.apply === true && plan.action === "relocate" && argReal !== void 0) {
8908
- const canonicalFile = join11(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
9219
+ const canonicalFile = join12(anchorReal, "agents", plan.canonicalName, CANONICAL_FILE);
8909
9220
  const res = relocateAgentsFile(argReal, canonicalFile);
8910
9221
  if (res.ok) {
8911
9222
  applied = true;
@@ -9290,14 +9601,14 @@ import {
9290
9601
  basouPaths as basouPaths14,
9291
9602
  findErrorCode as findErrorCode10,
9292
9603
  loadPortfolioConfig as loadPortfolioConfig5,
9293
- readManifest as readManifest9
9604
+ readManifest as readManifest10
9294
9605
  } from "@basou/core";
9295
9606
  import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
9296
9607
 
9297
9608
  // src/commands/refresh-watch.ts
9298
- import { readdir as readdir2, stat as stat6 } from "fs/promises";
9609
+ import { readdir as readdir3, stat as stat6 } from "fs/promises";
9299
9610
  import { homedir as homedir9 } from "os";
9300
- import { join as join12 } from "path";
9611
+ import { join as join13 } from "path";
9301
9612
  import {
9302
9613
  assertApprovalStoreSafe as assertApprovalStoreSafe2,
9303
9614
  assertLockStoreSafe as assertLockStoreSafe4,
@@ -9309,8 +9620,8 @@ var MIN_WATCH_INTERVAL_SEC = 5;
9309
9620
  var MAX_WATCH_INTERVAL_SEC = 86400;
9310
9621
  function watchedRoots(ctx) {
9311
9622
  return [
9312
- ctx.codexSessionsDir ?? join12(homedir9(), ".codex", "sessions"),
9313
- ctx.claudeProjectsDir ?? join12(homedir9(), ".claude", "projects")
9623
+ ctx.codexSessionsDir ?? join13(homedir9(), ".codex", "sessions"),
9624
+ ctx.claudeProjectsDir ?? join13(homedir9(), ".claude", "projects")
9314
9625
  ];
9315
9626
  }
9316
9627
  async function scanSourceLogs(roots) {
@@ -9318,13 +9629,13 @@ async function scanSourceLogs(roots) {
9318
9629
  const walk = async (dir) => {
9319
9630
  let entries;
9320
9631
  try {
9321
- entries = await readdir2(dir, { withFileTypes: true });
9632
+ entries = await readdir3(dir, { withFileTypes: true });
9322
9633
  } catch (error) {
9323
9634
  if (findErrorCode9(error, "ENOENT") || findErrorCode9(error, "ENOTDIR")) return;
9324
9635
  throw new Error("Failed to read a source log directory", { cause: error });
9325
9636
  }
9326
9637
  for (const entry of entries) {
9327
- const full = join12(dir, entry.name);
9638
+ const full = join13(dir, entry.name);
9328
9639
  if (entry.isDirectory()) {
9329
9640
  await walk(full);
9330
9641
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -9607,7 +9918,7 @@ async function warnPosition(paths, result, ctx) {
9607
9918
  }
9608
9919
  async function retiredChannelNotice(paths) {
9609
9920
  try {
9610
- const manifest = await readManifest9(paths);
9921
+ const manifest = await readManifest10(paths);
9611
9922
  if (manifest.channels?.codex !== true) return null;
9612
9923
  return "codex channel: retired \u2014 the manifest's channels.codex is ignored; a Codex session now receives this workspace's position from the SessionStart hook (see `basou hook status codex`), and nothing is written to the user-global ~/.codex/AGENTS.md";
9613
9924
  } catch {
@@ -9750,7 +10061,7 @@ import {
9750
10061
  findErrorCode as findErrorCode12,
9751
10062
  findUnbindableRepos,
9752
10063
  parseReviewRecordInput,
9753
- readManifest as readManifest10,
10064
+ readManifest as readManifest11,
9754
10065
  resolveRepoRoot,
9755
10066
  sanitizePath as sanitizePath3
9756
10067
  } from "@basou/core";
@@ -9837,7 +10148,7 @@ async function doRunReviewRecord(options, ctx) {
9837
10148
  }
9838
10149
  const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
9839
10150
  const occurredAt = now.toISOString();
9840
- const manifest = await readManifest10(paths);
10151
+ const manifest = await readManifest11(paths);
9841
10152
  const invocationArgs = options.file !== void 0 ? [
9842
10153
  "--file",
9843
10154
  sanitizePath3(resolve11(cwd, options.file), {
@@ -10177,7 +10488,7 @@ function unattachedLines(u) {
10177
10488
  // src/commands/run.ts
10178
10489
  import { mkdir as mkdir3, readFile as readFile8, rm as rm3 } from "fs/promises";
10179
10490
  import { homedir as homedir11 } from "os";
10180
- import { join as join13 } from "path";
10491
+ import { join as join14 } from "path";
10181
10492
  import {
10182
10493
  acquireLock as acquireLock5,
10183
10494
  assertBasouRootSafe as assertBasouRootSafe13,
@@ -10194,7 +10505,7 @@ import {
10194
10505
  getSnapshot as getSnapshot2,
10195
10506
  overwriteYamlFile as overwriteYamlFile2,
10196
10507
  prefixedUlid as prefixedUlid4,
10197
- readManifest as readManifest11,
10508
+ readManifest as readManifest12,
10198
10509
  readYamlFile as readYamlFile5,
10199
10510
  resolveClaudeCodeCommand,
10200
10511
  resolveCodexCommand,
@@ -10255,15 +10566,15 @@ async function runTrackedTool(args, options, ctx, adapter) {
10255
10566
  const repoRoot = await resolveRepositoryRootInPlace(cwd, "run");
10256
10567
  const paths = basouPaths18(repoRoot);
10257
10568
  await assertBasouRootSafe13(paths.root);
10258
- const manifest = await readManifest11(paths);
10569
+ const manifest = await readManifest12(paths);
10259
10570
  await assertSessionStoreSafe3(paths);
10260
10571
  const sessionId = prefixedUlid4("ses");
10261
- const sessionDir = join13(paths.sessions, sessionId);
10572
+ const sessionDir = join14(paths.sessions, sessionId);
10262
10573
  const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
10263
10574
  await coreAppendChainedEvent2(paths, sessionId, event);
10264
10575
  });
10265
10576
  const startedAt = now().toISOString();
10266
- const sessionYamlPath = join13(sessionDir, "session.yaml");
10577
+ const sessionYamlPath = join14(sessionDir, "session.yaml");
10267
10578
  const session = buildInitialSession2({
10268
10579
  id: sessionId,
10269
10580
  command,
@@ -10640,7 +10951,7 @@ async function noteCodexHookStatusPreSpawn(_cwd, ctx) {
10640
10951
 
10641
10952
  // src/commands/session.ts
10642
10953
  import { readFile as readFile9 } from "fs/promises";
10643
- import { basename as basename8, isAbsolute as isAbsolute5, join as join14, relative as relative4, sep as sep3 } from "path";
10954
+ import { basename as basename8, isAbsolute as isAbsolute5, join as join15, relative as relative4, sep as sep3 } from "path";
10644
10955
  import {
10645
10956
  acquireLock as acquireLock6,
10646
10957
  appendEventToExistingSession as appendEventToExistingSession3,
@@ -10656,7 +10967,7 @@ import {
10656
10967
  loadSessionEntries as loadSessionEntries2,
10657
10968
  normalizeSessionTimestamps,
10658
10969
  readAllEvents,
10659
- readManifest as readManifest12,
10970
+ readManifest as readManifest13,
10660
10971
  readObservedDuration,
10661
10972
  readYamlFile as readYamlFile6,
10662
10973
  rechainSessionInPlace,
@@ -10772,8 +11083,8 @@ async function doRunSessionShow(idInput, options, ctx) {
10772
11083
  const paths = basouPaths19(repositoryRoot);
10773
11084
  await assertWorkspaceInitialized12(paths.root);
10774
11085
  const sessionId = await resolveSessionId3(paths, idInput);
10775
- const sessionDir = join14(paths.sessions, sessionId);
10776
- const sessionYamlPath = join14(sessionDir, "session.yaml");
11086
+ const sessionDir = join15(paths.sessions, sessionId);
11087
+ const sessionYamlPath = join15(sessionDir, "session.yaml");
10777
11088
  let session;
10778
11089
  try {
10779
11090
  const raw = await readYamlFile6(sessionYamlPath);
@@ -11083,7 +11394,7 @@ async function doRunSessionImport(options, ctx) {
11083
11394
  const repositoryRoot = await resolveRepositoryRootForSession(cwd, "import");
11084
11395
  const paths = basouPaths19(repositoryRoot);
11085
11396
  await assertWorkspaceInitialized12(paths.root);
11086
- const manifest = await readManifest12(paths);
11397
+ const manifest = await readManifest13(paths);
11087
11398
  const rawBody = await readInputFile(options.from);
11088
11399
  const json = parseJsonStrict(rawBody);
11089
11400
  const parsed = SessionImportPayloadSchema2.safeParse(json);
@@ -11469,7 +11780,7 @@ import {
11469
11780
  basouPaths as basouPaths21,
11470
11781
  buildStatusSnapshot,
11471
11782
  findErrorCode as findErrorCode15,
11472
- readManifest as readManifest13,
11783
+ readManifest as readManifest14,
11473
11784
  writeStatus
11474
11785
  } from "@basou/core";
11475
11786
  function registerStatusCommand(program) {
@@ -11499,7 +11810,7 @@ async function doRunStatus(options, ctx) {
11499
11810
  }
11500
11811
  let manifest;
11501
11812
  try {
11502
- manifest = await readManifest13(paths);
11813
+ manifest = await readManifest14(paths);
11503
11814
  } catch (error) {
11504
11815
  if (findErrorCode15(error, "ENOENT")) {
11505
11816
  throw new Error("Workspace not initialized. Run 'basou init' first.");
@@ -11540,7 +11851,7 @@ function formatVersionGateMessage(error) {
11540
11851
 
11541
11852
  // src/commands/task.ts
11542
11853
  import { readFile as readFile10 } from "fs/promises";
11543
- import { join as join15 } from "path";
11854
+ import { join as join16 } from "path";
11544
11855
  import {
11545
11856
  archiveTask,
11546
11857
  assertBasouRootSafe as assertBasouRootSafe17,
@@ -11554,7 +11865,7 @@ import {
11554
11865
  loadSessionEntries as loadSessionEntries3,
11555
11866
  loadTaskEntries,
11556
11867
  prefixedUlid as prefixedUlid5,
11557
- readManifest as readManifest14,
11868
+ readManifest as readManifest15,
11558
11869
  readTaskFile,
11559
11870
  readTaskFileWithArchiveFallback,
11560
11871
  reconcileAllTasks,
@@ -11681,7 +11992,7 @@ async function doRunTaskNew(options, ctx) {
11681
11992
  });
11682
11993
  return;
11683
11994
  }
11684
- const manifest = await readManifest14(paths);
11995
+ const manifest = await readManifest15(paths);
11685
11996
  const result = await createTaskWithEvent({
11686
11997
  mode: "ad-hoc",
11687
11998
  paths,
@@ -11867,7 +12178,7 @@ async function doRunTaskShow(idInput, options, ctx) {
11867
12178
  const events = [];
11868
12179
  const linkedSessionIds = new Set(doc.task.task.linked_sessions);
11869
12180
  for (const s of sessions) {
11870
- const sessionDir = join15(paths.sessions, s.sessionId);
12181
+ const sessionDir = join16(paths.sessions, s.sessionId);
11871
12182
  try {
11872
12183
  for await (const ev of replayEvents3(sessionDir, {
11873
12184
  onWarning: (w) => printReplayWarning(w, s.sessionId)
@@ -12029,7 +12340,7 @@ async function doRunTaskStatus(taskIdInput, newStatusInput, options, ctx) {
12029
12340
  });
12030
12341
  return;
12031
12342
  }
12032
- const manifest = await readManifest14(paths);
12343
+ const manifest = await readManifest15(paths);
12033
12344
  const result = await updateTaskStatusWithEvent({
12034
12345
  mode: "ad-hoc",
12035
12346
  paths,
@@ -12083,7 +12394,7 @@ async function doRunTaskReconcile(options, ctx) {
12083
12394
  const paths = basouPaths22(repositoryRoot);
12084
12395
  await assertWorkspaceInitialized14(paths.root);
12085
12396
  await assertLockStoreSafe6(paths);
12086
- const manifest = await readManifest14(paths);
12397
+ const manifest = await readManifest15(paths);
12087
12398
  const nowProvider = ctx.nowProvider ?? (() => /* @__PURE__ */ new Date());
12088
12399
  const write = options.write === true;
12089
12400
  const verbose = isVerbose(options);
@@ -12263,7 +12574,7 @@ async function doRunTaskRefreshLinkage(taskIdInput, options, ctx) {
12263
12574
  const repositoryRoot = await resolveRepositoryRootForTask(cwd, "refresh-linkage");
12264
12575
  const paths = basouPaths22(repositoryRoot);
12265
12576
  await assertWorkspaceInitialized14(paths.root);
12266
- const manifest = await readManifest14(paths);
12577
+ const manifest = await readManifest15(paths);
12267
12578
  const taskId = await resolveTaskId2(paths, taskIdInput);
12268
12579
  const nowProvider = ctx.nowProvider ?? (() => /* @__PURE__ */ new Date());
12269
12580
  const write = options.write === true;
@@ -12343,7 +12654,7 @@ async function doRunTaskEdit(taskIdInput, options, ctx) {
12343
12654
  const repositoryRoot = await resolveRepositoryRootForTask(cwd, "edit");
12344
12655
  const paths = basouPaths22(repositoryRoot);
12345
12656
  await assertWorkspaceInitialized14(paths.root);
12346
- const manifest = await readManifest14(paths);
12657
+ const manifest = await readManifest15(paths);
12347
12658
  const taskId = await resolveTaskId2(paths, taskIdInput);
12348
12659
  const now = ctx.nowProvider !== void 0 ? ctx.nowProvider() : /* @__PURE__ */ new Date();
12349
12660
  const occurredAt = now.toISOString();
@@ -12399,7 +12710,7 @@ async function doRunTaskDelete(taskIdInput, options, ctx) {
12399
12710
  const repositoryRoot = await resolveRepositoryRootForTask(cwd, "delete");
12400
12711
  const paths = basouPaths22(repositoryRoot);
12401
12712
  await assertWorkspaceInitialized14(paths.root);
12402
- const manifest = await readManifest14(paths);
12713
+ const manifest = await readManifest15(paths);
12403
12714
  const taskId = await resolveTaskId2(paths, taskIdInput);
12404
12715
  if (options.yes !== true) {
12405
12716
  await confirmDestructiveAction("delete", taskId);
@@ -12444,7 +12755,7 @@ async function doRunTaskArchive(taskIdInput, options, ctx) {
12444
12755
  const repositoryRoot = await resolveRepositoryRootForTask(cwd, "archive");
12445
12756
  const paths = basouPaths22(repositoryRoot);
12446
12757
  await assertWorkspaceInitialized14(paths.root);
12447
- const manifest = await readManifest14(paths);
12758
+ const manifest = await readManifest15(paths);
12448
12759
  const taskId = await resolveTaskId2(paths, taskIdInput);
12449
12760
  if (options.yes !== true) {
12450
12761
  await confirmDestructiveAction("archive", taskId);
@@ -12762,23 +13073,23 @@ async function assertWorkspaceInitialized15(basouRoot) {
12762
13073
  // src/commands/view.ts
12763
13074
  import { spawn } from "child_process";
12764
13075
  import { createHash as createHash2 } from "crypto";
12765
- import { basename as basename11, dirname as dirname8, join as join19, resolve as resolve14 } from "path";
13076
+ import { basename as basename11, dirname as dirname9, join as join20, resolve as resolve14 } from "path";
12766
13077
  import {
12767
13078
  assertBasouRootSafe as assertBasouRootSafe19,
12768
13079
  basouPaths as basouPaths25,
12769
13080
  findErrorCode as findErrorCode19,
12770
13081
  loadPortfolioConfig as loadPortfolioConfig6,
12771
- readManifest as readManifest18
13082
+ readManifest as readManifest19
12772
13083
  } from "@basou/core";
12773
13084
  import { InvalidArgumentError as InvalidArgumentError8 } from "commander";
12774
13085
 
12775
13086
  // src/lib/portfolio-coverage.ts
12776
13087
  import { createReadStream as createReadStream2 } from "fs";
12777
- import { readdir as readdir3, stat as stat7 } from "fs/promises";
13088
+ import { readdir as readdir4, stat as stat7 } from "fs/promises";
12778
13089
  import { homedir as homedir12 } from "os";
12779
- import { basename as basename9, dirname as dirname7, join as join16 } from "path";
13090
+ import { basename as basename9, dirname as dirname8, join as join17 } from "path";
12780
13091
  import { createInterface as createInterface2 } from "readline";
12781
- import { basouPaths as basouPaths24, readManifest as readManifest15 } from "@basou/core";
13092
+ import { basouPaths as basouPaths24, readManifest as readManifest16 } from "@basou/core";
12782
13093
  function uncapturedTotal(result) {
12783
13094
  return result.groups.reduce((sum, g) => sum + g.logs, 0) + result.unplaceable;
12784
13095
  }
@@ -12788,8 +13099,8 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
12788
13099
  ctx.portfolioConfigPath
12789
13100
  );
12790
13101
  const listedDirs = new Set([...roots].map((root) => encodeProjectDir(root)));
12791
- const claudeProjectsDir = ctx.claudeProjectsDir ?? join16(homedir12(), ".claude", "projects");
12792
- const codexSessionsDir = ctx.codexSessionsDir ?? join16(homedir12(), ".codex", "sessions");
13102
+ const claudeProjectsDir = ctx.claudeProjectsDir ?? join17(homedir12(), ".claude", "projects");
13103
+ const codexSessionsDir = ctx.codexSessionsDir ?? join17(homedir12(), ".codex", "sessions");
12793
13104
  const tally = new Tally(roots);
12794
13105
  const absentTrees = [];
12795
13106
  const claudeFiles = await listClaudeTranscripts(claudeProjectsDir);
@@ -12800,7 +13111,7 @@ async function checkPortfolioCoverage(workspaces, ctx = {}) {
12800
13111
  file,
12801
13112
  "claude-code",
12802
13113
  claudeTranscriptCwd,
12803
- listedDirs.has(basename9(dirname7(file)))
13114
+ listedDirs.has(basename9(dirname8(file)))
12804
13115
  );
12805
13116
  }
12806
13117
  }
@@ -12827,7 +13138,7 @@ async function collectDeclaredRoots(workspaces, portfolioConfigPath) {
12827
13138
  }
12828
13139
  let resolved;
12829
13140
  try {
12830
- const manifest = await readManifest15(basouPaths24(importRoot));
13141
+ const manifest = await readManifest16(basouPaths24(importRoot));
12831
13142
  resolved = resolveSourceRoots({
12832
13143
  projectFlags: [],
12833
13144
  manifest,
@@ -12927,7 +13238,7 @@ async function isDirEntry(parent, entry) {
12927
13238
  if (entry.isDirectory()) return true;
12928
13239
  if (!entry.isSymbolicLink()) return false;
12929
13240
  try {
12930
- return (await stat7(join16(parent, entry.name))).isDirectory();
13241
+ return (await stat7(join17(parent, entry.name))).isDirectory();
12931
13242
  } catch {
12932
13243
  return false;
12933
13244
  }
@@ -12935,22 +13246,22 @@ async function isDirEntry(parent, entry) {
12935
13246
  async function listClaudeTranscripts(projectsRoot) {
12936
13247
  let entries;
12937
13248
  try {
12938
- entries = await readdir3(projectsRoot, { withFileTypes: true });
13249
+ entries = await readdir4(projectsRoot, { withFileTypes: true });
12939
13250
  } catch {
12940
13251
  return void 0;
12941
13252
  }
12942
13253
  const files = [];
12943
13254
  for (const entry of entries) {
12944
13255
  if (!await isDirEntry(projectsRoot, entry)) continue;
12945
- const full = join16(projectsRoot, entry.name);
13256
+ const full = join17(projectsRoot, entry.name);
12946
13257
  let names;
12947
13258
  try {
12948
- names = await readdir3(full);
13259
+ names = await readdir4(full);
12949
13260
  } catch {
12950
13261
  continue;
12951
13262
  }
12952
13263
  for (const name of names) {
12953
- if (name.endsWith(".jsonl")) files.push(join16(full, name));
13264
+ if (name.endsWith(".jsonl")) files.push(join17(full, name));
12954
13265
  }
12955
13266
  }
12956
13267
  return files.sort();
@@ -12960,12 +13271,12 @@ async function listCodexRollouts(sessionsRoot) {
12960
13271
  const walk = async (dir) => {
12961
13272
  let entries;
12962
13273
  try {
12963
- entries = await readdir3(dir, { withFileTypes: true });
13274
+ entries = await readdir4(dir, { withFileTypes: true });
12964
13275
  } catch {
12965
13276
  return;
12966
13277
  }
12967
13278
  for (const entry of entries) {
12968
- const full = join16(dir, entry.name);
13279
+ const full = join17(dir, entry.name);
12969
13280
  if (entry.isDirectory()) await walk(full);
12970
13281
  else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
12971
13282
  found.push(full);
@@ -12973,7 +13284,7 @@ async function listCodexRollouts(sessionsRoot) {
12973
13284
  }
12974
13285
  };
12975
13286
  try {
12976
- await readdir3(sessionsRoot);
13287
+ await readdir4(sessionsRoot);
12977
13288
  } catch {
12978
13289
  return void 0;
12979
13290
  }
@@ -13096,9 +13407,9 @@ function inertLines(result) {
13096
13407
  // src/lib/portfolio-safety.ts
13097
13408
  import { execFile as execFile2 } from "child_process";
13098
13409
  import { lstat as lstat3, realpath as realpath5 } from "fs/promises";
13099
- import { isAbsolute as isAbsolute6, join as join17, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
13410
+ import { isAbsolute as isAbsolute6, join as join18, relative as relative5, resolve as resolve12, sep as sep4 } from "path";
13100
13411
  import { promisify as promisify2 } from "util";
13101
- import { readManifest as readManifest16 } from "@basou/core";
13412
+ import { readManifest as readManifest17 } from "@basou/core";
13102
13413
  var execFileAsync2 = promisify2(execFile2);
13103
13414
  function errorCode2(error) {
13104
13415
  return error instanceof Error ? error.code : void 0;
@@ -13120,7 +13431,7 @@ function isBasouPath(p) {
13120
13431
  async function inspectRepo(repoPath) {
13121
13432
  let hasEntry = false;
13122
13433
  try {
13123
- await lstat3(join17(repoPath, ".basou"));
13434
+ await lstat3(join18(repoPath, ".basou"));
13124
13435
  hasEntry = true;
13125
13436
  } catch (error) {
13126
13437
  if (errorCode2(error) !== "ENOENT") {
@@ -13154,7 +13465,7 @@ async function checkPortfolioSafety(workspaces) {
13154
13465
  let viewPath;
13155
13466
  let isMaster = false;
13156
13467
  try {
13157
- const manifest = await readManifest16(ws.paths);
13468
+ const manifest = await readManifest17(ws.paths);
13158
13469
  sourceRoots = manifest.import?.source_roots ?? [];
13159
13470
  viewPath = manifest.workspace.view;
13160
13471
  isMaster = true;
@@ -13274,7 +13585,7 @@ function formatSafetyReport(result) {
13274
13585
 
13275
13586
  // src/lib/view-server.ts
13276
13587
  import { createServer } from "http";
13277
- import { basename as basename10, join as join18, resolve as resolve13 } from "path";
13588
+ import { basename as basename10, join as join19, resolve as resolve13 } from "path";
13278
13589
  import {
13279
13590
  boardLivePage,
13280
13591
  boardPage,
@@ -13289,7 +13600,7 @@ import {
13289
13600
  loadSessionEntries as loadSessionEntries4,
13290
13601
  loadTaskEntries as loadTaskEntries2,
13291
13602
  readAllEvents as readAllEvents2,
13292
- readManifest as readManifest17,
13603
+ readManifest as readManifest18,
13293
13604
  readMarkdownFile as readMarkdownFile9,
13294
13605
  readSessionYaml as readSessionYaml3,
13295
13606
  readTaskFile as readTaskFile2,
@@ -13961,13 +14272,23 @@ var BOARD_HTML = `<!doctype html>
13961
14272
  var f = b.footnotes;
13962
14273
  var items = f.notes.map(function (n) { return el('li', null, [prose(n, 'span'), reported()]); });
13963
14274
  var last = f.axis.last_review;
13964
- var review = !f.axis.last_review_known ? S.footnotes.reviewUnknown
13965
- : last === null ? S.footnotes.noReview
13966
- : last.from === 'record' ? fill(S.footnotes.lastReviewFromRecord, { date: last.date, model: last.model, record: last.record || '' })
13967
- : fill(S.footnotes.lastReview, { date: last.date, model: last.model });
14275
+ // The last review and the need were measured before the record was
14276
+ // written: when it reviews the axis itself, they are the ones before it.
14277
+ var own = f.axis.this_review || null;
14278
+ var review = !f.axis.last_review_known ? (own ? S.footnotes.previousReviewUnknown : S.footnotes.reviewUnknown)
14279
+ : last === null ? (own ? S.footnotes.noPreviousReview : S.footnotes.noReview)
14280
+ : last.from === 'record' ? fill(own ? S.footnotes.previousReviewFromRecord : S.footnotes.lastReviewFromRecord, { date: last.date, model: last.model, record: last.record || '' })
14281
+ : fill(own ? S.footnotes.previousReview : S.footnotes.lastReview, { date: last.date, model: last.model });
13968
14282
  var answer = f.axis.review_needed === true ? S.footnotes.reviewNeededYes
13969
14283
  : f.axis.review_needed === false ? S.footnotes.reviewNeededNo : S.footnotes.reviewNeededUnknown;
13970
- items.push(el('li', { text: fill(S.footnotes.axis, { version: f.axis.version }) + ' / ' + review + ' / ' + fill(S.footnotes.reviewNeeded, { answer: answer }) }));
14284
+ items.push(el('li', { text: fill(S.footnotes.axis, { version: f.axis.version }) + ' / ' + review + ' / ' + fill(own ? S.footnotes.reviewNeededBefore : S.footnotes.reviewNeeded, { answer: answer }) }));
14285
+ if (own) {
14286
+ items.push(el('li', null, [
14287
+ el('span', { text: fill(S.footnotes.thisReview, { triggers: own.triggers.join(', ') }) }),
14288
+ prose(own.summary, 'span'),
14289
+ reported()
14290
+ ]));
14291
+ }
13971
14292
  items.push(el('li', { text: fill(S.footnotes.judgedBy, { model: f.model }) }));
13972
14293
  items.push(el('li', { text: S.footnotes.reportedNote }));
13973
14294
  if (f.not_found.length > 0) {
@@ -15213,7 +15534,7 @@ async function captureStaleness(ws, nowIso) {
15213
15534
  async function overview(ws, nowProvider, resolveRemoteUrl) {
15214
15535
  let manifest;
15215
15536
  try {
15216
- manifest = await readManifest17(ws.paths);
15537
+ manifest = await readManifest18(ws.paths);
15217
15538
  } catch (error) {
15218
15539
  if (findErrorCode18(error, "ENOENT")) {
15219
15540
  return { initialized: false, repoRoot: ws.repoRoot };
@@ -15298,7 +15619,7 @@ async function sessionDetail(ws, sessionId) {
15298
15619
  throw error;
15299
15620
  }
15300
15621
  try {
15301
- const events = await readAllEvents2(join18(ws.paths.sessions, sessionId));
15622
+ const events = await readAllEvents2(join19(ws.paths.sessions, sessionId));
15302
15623
  return { session, events };
15303
15624
  } catch {
15304
15625
  return { session, events: [], degraded: true };
@@ -15558,12 +15879,12 @@ function sharedWhileRunning(run) {
15558
15879
  async function viewBoardOf(root, paths) {
15559
15880
  let own;
15560
15881
  try {
15561
- own = (await readManifest18(paths)).repos?.find((repo) => repo.path === ".");
15882
+ own = (await readManifest19(paths)).repos?.find((repo) => repo.path === ".");
15562
15883
  } catch {
15563
15884
  own = void 0;
15564
15885
  }
15565
15886
  if (own?.visibility !== "private") return { why: "no_board" };
15566
- return { recordsDir: join19(root, dirname8(DEFAULT_BOARD_PATH), "records") };
15887
+ return { recordsDir: join20(root, dirname9(DEFAULT_BOARD_PATH), "records") };
15567
15888
  }
15568
15889
  async function buildPortfolioDeps(workspaceFlags, ctx, cwd) {
15569
15890
  const specs = workspaceFlags.length > 0 ? workspaceFlags.map((p) => ({ path: resolve14(cwd, p) })) : await loadPortfolioConfig6(ctx.portfolioConfigPath);
@@ -15596,7 +15917,7 @@ async function buildWorkspaceEntry(repoRoot, ctx, labelOverride) {
15596
15917
  ...ctx.codexSessionsDir !== void 0 ? { codexSessionsDir: ctx.codexSessionsDir } : {}
15597
15918
  };
15598
15919
  try {
15599
- const manifest = await readManifest18(paths);
15920
+ const manifest = await readManifest19(paths);
15600
15921
  return {
15601
15922
  key: manifest.workspace.id,
15602
15923
  label: labelOverride ?? manifest.workspace.name,
@@ -15717,7 +16038,7 @@ function installExperimentalNotice(program, names = EXPERIMENTAL_COMMANDS, write
15717
16038
  function readBuildStamp() {
15718
16039
  if (false) return void 0;
15719
16040
  try {
15720
- return JSON.parse('{"version":"0.67.0","commit":"c4c4ebb","committedAt":"2026-10-08T23:33:17+09:00"}');
16041
+ return JSON.parse('{"version":"0.68.1","commit":"e413cb0","committedAt":"2026-10-10T15:13:41+09:00"}');
15721
16042
  } catch {
15722
16043
  return void 0;
15723
16044
  }