@astrosheep/keiyaku 2.8.0 → 2.8.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.
@@ -20,11 +20,21 @@ function assertMintSlug(slug) {
20
20
  if (!normalized) {
21
21
  throw new ProjectionStateError("projection slug cannot be empty");
22
22
  }
23
- if (normalized.includes("/") || normalized.includes("\0")) {
23
+ if (!/^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*$/.test(normalized)) {
24
24
  throw new ProjectionStateError(`invalid projection slug: ${slug}`);
25
25
  }
26
26
  return normalized;
27
27
  }
28
+ function removeEmptyAkumaRoot(dir) {
29
+ try {
30
+ fs.rmdirSync(dir);
31
+ }
32
+ catch (error) {
33
+ if (isErrnoException(error) && ["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error.code ?? ""))
34
+ return;
35
+ throw error;
36
+ }
37
+ }
28
38
  /**
29
39
  * Validate mint inputs that can fail, then reserve one private staging directory.
30
40
  * Dot-prefixed staging is never a published projection address.
@@ -43,14 +53,19 @@ function reserveProjectionDirectory(input) {
43
53
  }
44
54
  const root = projectionRoot(input.cwd);
45
55
  fs.mkdirSync(root, { recursive: true });
56
+ const akumaRoot = path.join(root, slug);
57
+ const createdAkumaRoot = !fs.existsSync(akumaRoot);
58
+ fs.mkdirSync(akumaRoot, { recursive: true });
46
59
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
47
60
  const hex = (input.randomHex?.(attempt) ?? randomHex8()).toLowerCase();
48
61
  if (!/^[0-9a-f]{8}$/.test(hex)) {
62
+ if (createdAkumaRoot)
63
+ removeEmptyAkumaRoot(akumaRoot);
49
64
  throw new ProjectionStateError(`projection id random segment must be 8 hex chars, got ${hex}`);
50
65
  }
51
- const id = `${slug}-${hex}`;
66
+ const id = `${slug}/${hex}`;
52
67
  const dir = projectionDir(input.cwd, id);
53
- const stagingDir = path.join(root, `.mint-${id}`);
68
+ const stagingDir = path.join(akumaRoot, `.mint-${hex}`);
54
69
  if (fs.existsSync(dir))
55
70
  continue;
56
71
  try {
@@ -60,6 +75,8 @@ function reserveProjectionDirectory(input) {
60
75
  if (isErrnoException(error) && error.code === "EEXIST") {
61
76
  continue;
62
77
  }
78
+ if (createdAkumaRoot)
79
+ removeEmptyAkumaRoot(akumaRoot);
63
80
  throw error;
64
81
  }
65
82
  if (fs.existsSync(dir)) {
@@ -68,6 +85,8 @@ function reserveProjectionDirectory(input) {
68
85
  }
69
86
  return { id, dir, stagingDir };
70
87
  }
88
+ if (createdAkumaRoot)
89
+ removeEmptyAkumaRoot(akumaRoot);
71
90
  throw new ProjectionMintCollisionError(`exhausted ${maxAttempts} projection id attempts under ${PROJECTION_ROOT_SEGMENTS.join("/")} (PROJECTION_MINT_COLLISION)`);
72
91
  }
73
92
  /** Build the identity fields shared by both authority variants. */
@@ -107,6 +126,7 @@ function publishReservedProjection(pact, reserved) {
107
126
  if (!published) {
108
127
  // Failure removes only this mint's staging.
109
128
  fs.rmSync(reserved.stagingDir, { recursive: true, force: true });
129
+ removeEmptyAkumaRoot(path.dirname(reserved.stagingDir));
110
130
  }
111
131
  }
112
132
  }
@@ -0,0 +1,123 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { isErrnoException } from "../errno.js";
4
+ import { ProjectionStateError, randomHex8 } from "./atomic-publish.js";
5
+ import { projectionCoordinateRoot } from "./projection-coordinate.js";
6
+ import { isProjectionId } from "./projection-core.js";
7
+ export const PROJECTION_ALIAS_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
8
+ const MAX_ALIAS_REF_BYTES = 1024;
9
+ export function isProjectionAlias(value) {
10
+ return PROJECTION_ALIAS_PATTERN.test(value);
11
+ }
12
+ export function assertProjectionAlias(value) {
13
+ if (!isProjectionAlias(value)) {
14
+ throw new ProjectionStateError("projection alias must match ^[a-z0-9][a-z0-9-]{0,63}$");
15
+ }
16
+ return value;
17
+ }
18
+ export function projectionAliasRoot(cwd) {
19
+ return path.join(projectionCoordinateRoot(cwd), ".keiyaku", "projection-alias");
20
+ }
21
+ export function projectionAliasPath(cwd, alias) {
22
+ assertProjectionAlias(alias);
23
+ return path.join(projectionAliasRoot(cwd), alias);
24
+ }
25
+ function invalidAliasRef(filePath) {
26
+ return new ProjectionStateError(`invalid projection alias ref: ${filePath}`);
27
+ }
28
+ function readBoundedRegularAliasRef(filePath) {
29
+ let before;
30
+ try {
31
+ before = fs.lstatSync(filePath);
32
+ }
33
+ catch (error) {
34
+ if (isErrnoException(error) && error.code === "ENOENT")
35
+ return undefined;
36
+ throw error;
37
+ }
38
+ if (!before.isFile() || before.size > MAX_ALIAS_REF_BYTES)
39
+ throw invalidAliasRef(filePath);
40
+ let fd;
41
+ try {
42
+ fd = fs.openSync(filePath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
43
+ }
44
+ catch (error) {
45
+ if (isErrnoException(error) && error.code === "ELOOP")
46
+ throw invalidAliasRef(filePath);
47
+ throw error;
48
+ }
49
+ try {
50
+ const opened = fs.fstatSync(fd);
51
+ if (!opened.isFile() || opened.size > MAX_ALIAS_REF_BYTES)
52
+ throw invalidAliasRef(filePath);
53
+ const bytes = Buffer.alloc(opened.size);
54
+ const count = fs.readSync(fd, bytes, 0, bytes.length, 0);
55
+ if (count !== bytes.length)
56
+ throw invalidAliasRef(filePath);
57
+ return bytes.toString("utf8");
58
+ }
59
+ finally {
60
+ fs.closeSync(fd);
61
+ }
62
+ }
63
+ /** Undefined means no ref; malformed refs fail closed with their exact sidecar path. */
64
+ export function readProjectionAlias(cwd, alias) {
65
+ const filePath = projectionAliasPath(cwd, alias);
66
+ const content = readBoundedRegularAliasRef(filePath);
67
+ if (content === undefined)
68
+ return undefined;
69
+ if (!content.endsWith("\n") || content.indexOf("\n") !== content.length - 1)
70
+ throw invalidAliasRef(filePath);
71
+ const id = content.slice(0, -1);
72
+ if (!isProjectionId(id))
73
+ throw invalidAliasRef(filePath);
74
+ return id;
75
+ }
76
+ /** Atomically replace one alias ref. Aliases intentionally have no multi-ref transaction. */
77
+ export function moveProjectionAlias(cwd, alias, id) {
78
+ assertProjectionAlias(alias);
79
+ if (!isProjectionId(id))
80
+ throw new ProjectionStateError(`invalid projection id: ${id}`);
81
+ const previous = readProjectionAlias(cwd, alias);
82
+ const root = projectionAliasRoot(cwd);
83
+ fs.mkdirSync(root, { recursive: true });
84
+ const target = projectionAliasPath(cwd, alias);
85
+ let temp;
86
+ try {
87
+ for (let attempt = 0; attempt < 16; attempt += 1) {
88
+ const candidate = path.join(root, `.tmp.${randomHex8()}`);
89
+ try {
90
+ const fd = fs.openSync(candidate, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o600);
91
+ temp = candidate;
92
+ try {
93
+ fs.writeFileSync(fd, `${id}\n`, "utf8");
94
+ fs.fsyncSync(fd);
95
+ }
96
+ finally {
97
+ fs.closeSync(fd);
98
+ }
99
+ break;
100
+ }
101
+ catch (error) {
102
+ if (isErrnoException(error) && error.code === "EEXIST")
103
+ continue;
104
+ throw error;
105
+ }
106
+ }
107
+ if (!temp)
108
+ throw new ProjectionStateError(`failed to create projection alias temp: ${target}`);
109
+ fs.renameSync(temp, target);
110
+ const directoryFd = fs.openSync(root, fs.constants.O_RDONLY);
111
+ try {
112
+ fs.fsyncSync(directoryFd);
113
+ }
114
+ finally {
115
+ fs.closeSync(directoryFd);
116
+ }
117
+ }
118
+ finally {
119
+ if (temp)
120
+ fs.rmSync(temp, { force: true });
121
+ }
122
+ return previous;
123
+ }
@@ -2,13 +2,13 @@ import { spawnSync } from "node:child_process";
2
2
  import * as path from "node:path";
3
3
  import { ProjectionStateError } from "./atomic-publish.js";
4
4
  /**
5
- * Select the filesystem authority for repository projection state.
5
+ * Resolve projection storage root and authority from one Git probe.
6
6
  *
7
7
  * A non-bare repository uses the parent of its absolute common Git directory,
8
8
  * so every linked worktree shares one coordinate. Repo-free callers retain the
9
9
  * existing cwd-local authority until its replacement is legislated.
10
10
  */
11
- export function projectionCoordinateRoot(cwd) {
11
+ export function resolveProjectionCoordinate(cwd) {
12
12
  const result = spawnSync("git", ["rev-parse", "--is-bare-repository", "--path-format=absolute", "--git-common-dir"], {
13
13
  cwd,
14
14
  encoding: "utf8",
@@ -24,12 +24,28 @@ export function projectionCoordinateRoot(cwd) {
24
24
  }
25
25
  if (result.status !== 0) {
26
26
  const detail = result.stderr.trim();
27
- if (detail.includes("not a git repository"))
28
- return cwd;
27
+ if (detail.includes("not a git repository")) {
28
+ return { physicalRoot: cwd, authority: { kind: "user" } };
29
+ }
29
30
  throw new ProjectionStateError(`cannot resolve projection repository coordinate${detail ? `: ${detail}` : ` (git exited ${String(result.status)})`}`);
30
31
  }
31
32
  const [bare, commonDir] = result.stdout.trim().split(/\r?\n/);
32
- if (bare !== "false" || !commonDir || !path.isAbsolute(commonDir))
33
- return cwd;
34
- return path.dirname(commonDir);
33
+ if (bare !== "false" || !commonDir || !path.isAbsolute(commonDir)) {
34
+ return { physicalRoot: cwd, authority: { kind: "user" } };
35
+ }
36
+ const physicalRoot = path.dirname(commonDir);
37
+ return {
38
+ physicalRoot,
39
+ authority: { kind: "repository", stableRoot: physicalRoot },
40
+ };
41
+ }
42
+ /**
43
+ * Select the filesystem authority for repository projection state.
44
+ *
45
+ * A non-bare repository uses the parent of its absolute common Git directory,
46
+ * so every linked worktree shares one coordinate. Repo-free callers retain the
47
+ * existing cwd-local authority until its replacement is legislated.
48
+ */
49
+ export function projectionCoordinateRoot(cwd) {
50
+ return resolveProjectionCoordinate(cwd).physicalRoot;
35
51
  }
@@ -3,16 +3,22 @@ import * as path from "node:path";
3
3
  import { isErrnoException } from "../errno.js";
4
4
  import { projectionCoordinateRoot } from "./projection-coordinate.js";
5
5
  import { ProjectionStateError, } from "./atomic-publish.js";
6
- import { readProjectionIdentity } from "./projection-identity.js";
7
6
  export { atomicPublishFile, ProjectionStateError, randomHex8 } from "./atomic-publish.js";
8
7
  export { claimAllInbox, claimFencedTells, claimTellToInflight, demoteSubmittedTellsForReplay, isTellCausallyConsumed, listTellIds, markTellConsumed, markTellSubmitted, readTellOriginal, readTellSubmitted, snapshotPendingTells, writeInboxTell, } from "./projection-tells.js";
9
8
  /** Relative segments for `.keiyaku/projection`. */
10
9
  export const PROJECTION_ROOT_SEGMENTS = [".keiyaku", "projection"];
10
+ export const PROJECTION_ID_PATTERN = /^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*\/[0-9a-f]{8}$/;
11
+ export function isProjectionId(value) {
12
+ return PROJECTION_ID_PATTERN.test(value);
13
+ }
11
14
  export function projectionRoot(cwd) {
12
15
  return path.join(projectionCoordinateRoot(cwd), ...PROJECTION_ROOT_SEGMENTS);
13
16
  }
14
17
  export function projectionDir(cwd, id) {
15
- return path.join(projectionRoot(cwd), id);
18
+ if (!isProjectionId(id))
19
+ throw new ProjectionStateError(`invalid projection id: ${id}`);
20
+ const [akuma, hex] = id.split("/");
21
+ return path.join(projectionRoot(cwd), akuma, hex);
16
22
  }
17
23
  export function encodeProjectionJson(value) {
18
24
  return `${JSON.stringify(value)}\n`;
@@ -32,51 +38,6 @@ export function isPublishedProjection(dir) {
32
38
  return false;
33
39
  }
34
40
  }
35
- /** readdir-based address resolution. Atomic mint rename makes every visible non-dot directory published. */
36
- export function resolveProjectionPrefix(cwd, prefix, commissionId) {
37
- const needle = prefix.trim();
38
- if (!needle) {
39
- return { status: "none" };
40
- }
41
- const root = projectionRoot(cwd);
42
- let entries;
43
- try {
44
- entries = fs.readdirSync(root);
45
- }
46
- catch (error) {
47
- if (isErrnoException(error) && error.code === "ENOENT") {
48
- return { status: "none" };
49
- }
50
- throw error;
51
- }
52
- const publishedIds = entries
53
- .filter((id) => {
54
- if (id.startsWith("."))
55
- return false;
56
- const directory = path.join(root, id);
57
- return isPublishedProjection(directory);
58
- })
59
- .sort();
60
- const addressed = publishedIds.filter((id) => {
61
- if (id === needle || id.startsWith(needle))
62
- return true;
63
- const suffix = /-([0-9a-f]{8})$/.exec(id)?.[1];
64
- return suffix === needle;
65
- });
66
- const candidates = commissionId === undefined
67
- ? addressed
68
- : addressed.filter((id) => {
69
- const identity = readProjectionIdentity(path.join(root, id));
70
- return identity.binding.kind === "commission" && identity.binding.commissionId === commissionId;
71
- });
72
- if (candidates.length === 1) {
73
- return { status: "resolved", id: candidates[0] };
74
- }
75
- if (candidates.length === 0) {
76
- return { status: "none" };
77
- }
78
- return { status: "ambiguous", candidates };
79
- }
80
41
  const PROJECTION_EXECUTION_ID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;
81
42
  function assertProjectionExecutionId(executionId) {
82
43
  if (!PROJECTION_EXECUTION_ID_PATTERN.test(executionId)) {
@@ -19,11 +19,21 @@ function assertMintSlug(slug) {
19
19
  if (!normalized) {
20
20
  throw new ProjectionStateError("projection slug cannot be empty");
21
21
  }
22
- if (normalized.includes("/") || normalized.includes("\0")) {
22
+ if (!/^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*$/.test(normalized)) {
23
23
  throw new ProjectionStateError(`invalid projection slug: ${slug}`);
24
24
  }
25
25
  return normalized;
26
26
  }
27
+ function removeEmptyAkumaRoot(dir) {
28
+ try {
29
+ fs.rmdirSync(dir);
30
+ }
31
+ catch (error) {
32
+ if (isErrnoException(error) && ["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error.code ?? ""))
33
+ return;
34
+ throw error;
35
+ }
36
+ }
27
37
  /**
28
38
  * Validate mint inputs that can fail, then reserve one private staging directory.
29
39
  * Dot-prefixed staging is never a published projection address.
@@ -42,14 +52,19 @@ function reserveProjectionDirectory(input) {
42
52
  }
43
53
  const root = projectionRoot(input.cwd);
44
54
  fs.mkdirSync(root, { recursive: true });
55
+ const akumaRoot = path.join(root, slug);
56
+ const createdAkumaRoot = !fs.existsSync(akumaRoot);
57
+ fs.mkdirSync(akumaRoot, { recursive: true });
45
58
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
46
59
  const hex = (input.randomHex?.(attempt) ?? randomHex8()).toLowerCase();
47
60
  if (!/^[0-9a-f]{8}$/.test(hex)) {
61
+ if (createdAkumaRoot)
62
+ removeEmptyAkumaRoot(akumaRoot);
48
63
  throw new ProjectionStateError(`projection id random segment must be 8 hex chars, got ${hex}`);
49
64
  }
50
- const id = `${slug}-${hex}`;
65
+ const id = `${slug}/${hex}`;
51
66
  const dir = projectionDir(input.cwd, id);
52
- const stagingDir = path.join(root, `.mint-${id}`);
67
+ const stagingDir = path.join(akumaRoot, `.mint-${hex}`);
53
68
  if (fs.existsSync(dir))
54
69
  continue;
55
70
  try {
@@ -59,6 +74,8 @@ function reserveProjectionDirectory(input) {
59
74
  if (isErrnoException(error) && error.code === "EEXIST") {
60
75
  continue;
61
76
  }
77
+ if (createdAkumaRoot)
78
+ removeEmptyAkumaRoot(akumaRoot);
62
79
  throw error;
63
80
  }
64
81
  if (fs.existsSync(dir)) {
@@ -67,6 +84,8 @@ function reserveProjectionDirectory(input) {
67
84
  }
68
85
  return { id, dir, stagingDir };
69
86
  }
87
+ if (createdAkumaRoot)
88
+ removeEmptyAkumaRoot(akumaRoot);
70
89
  throw new ProjectionMintCollisionError(`exhausted ${maxAttempts} projection id attempts under ${PROJECTION_ROOT_SEGMENTS.join("/")} (PROJECTION_MINT_COLLISION)`);
71
90
  }
72
91
  /** Build the pact fields shared by both authority variants. */
@@ -104,6 +123,8 @@ function publishReservedProjection(pact, reserved) {
104
123
  finally {
105
124
  if (!published) {
106
125
  fs.rmSync(reserved.stagingDir, { recursive: true, force: true });
126
+ const parent = path.dirname(reserved.stagingDir);
127
+ removeEmptyAkumaRoot(parent);
107
128
  }
108
129
  }
109
130
  }
@@ -3,7 +3,7 @@ import * as path from "node:path";
3
3
  import { DatabaseSync } from "node:sqlite";
4
4
  export const PROJECTION_RUNNER_LOCK_DATABASE = "leash";
5
5
  const SQLITE_BUSY = 5;
6
- const SUPPORTED_PLATFORMS = new Set(["darwin", "linux"]);
6
+ const SUPPORTED_PLATFORMS = new Set(["darwin", "linux", "win32"]);
7
7
  export class ProjectionRunnerLockUnavailableError extends Error {
8
8
  code = "PROJECTION_RUNNER_LOCK_UNAVAILABLE";
9
9
  constructor(message, options) {
@@ -1,7 +1,8 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { isErrnoException } from "../errno.js";
4
- import { executionEventsPath, projectionRoot, } from "./projection-core.js";
4
+ import { executionEventsPath, isProjectionId, projectionRoot, } from "./projection-core.js";
5
+ import { readProjectionAlias } from "./projection-alias.js";
5
6
  import { ProjectionStateError } from "./atomic-publish.js";
6
7
  import { foldAgentActivity, } from "./projection-activity.js";
7
8
  import { readProjectionIdentity } from "./projection-identity.js";
@@ -12,6 +13,73 @@ import { observeProjectionLifeSnapshot } from "./projection-life-observer.js";
12
13
  import { selectCurrentGeneration } from "./projection-life-protocol.js";
13
14
  import { invalidActivityEventDiagnostic, selectValidStoredAgentEvents } from "./stored-agent-event.js";
14
15
  const MAX_STATUS_FILE_BYTES = 256 * 1024;
16
+ export const DEFAULT_PROJECTION_STATUS_ROW_BUDGET = 20;
17
+ const STATUS_GROUP = {
18
+ "startup-timeout": 0,
19
+ lost: 0,
20
+ unknown: 0,
21
+ minting: 1,
22
+ out: 1,
23
+ failed: 2,
24
+ dead: 2,
25
+ done: 3,
26
+ dismissed: 4,
27
+ };
28
+ function isOmissibleProjectionStatusState(state) {
29
+ return state === "failed" || state === "dead" || state === "done" || state === "dismissed";
30
+ }
31
+ function rowSortTime(row) {
32
+ if (row.activityAtMs !== undefined && Number.isFinite(row.activityAtMs)) {
33
+ return row.activityAtMs;
34
+ }
35
+ if (row.mintedAt !== undefined) {
36
+ const mintedAtMs = Date.parse(row.mintedAt);
37
+ if (Number.isFinite(mintedAtMs))
38
+ return mintedAtMs;
39
+ }
40
+ return Number.NEGATIVE_INFINITY;
41
+ }
42
+ function compareProjectionStatusRows(a, b) {
43
+ const groupDifference = STATUS_GROUP[a.state] - STATUS_GROUP[b.state];
44
+ if (groupDifference !== 0)
45
+ return groupDifference;
46
+ const aTime = rowSortTime(a);
47
+ const bTime = rowSortTime(b);
48
+ if (aTime !== bTime) {
49
+ if (aTime === Number.NEGATIVE_INFINITY)
50
+ return 1;
51
+ if (bTime === Number.NEGATIVE_INFINITY)
52
+ return -1;
53
+ return bTime - aTime;
54
+ }
55
+ return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
56
+ }
57
+ export function prioritizeProjectionStatusRows(rows, showAll) {
58
+ const ordered = [...rows].sort(compareProjectionStatusRows);
59
+ if (showAll)
60
+ return { rows: ordered };
61
+ const protectedCount = ordered.filter((row) => STATUS_GROUP[row.state] < 2).length;
62
+ let terminalBudget = Math.max(0, DEFAULT_PROJECTION_STATUS_ROW_BUDGET - protectedCount);
63
+ const visible = [];
64
+ const byState = {
65
+ failed: 0,
66
+ dead: 0,
67
+ done: 0,
68
+ dismissed: 0,
69
+ };
70
+ for (const row of ordered) {
71
+ if (STATUS_GROUP[row.state] < 2 || terminalBudget > 0) {
72
+ visible.push(row);
73
+ if (STATUS_GROUP[row.state] >= 2)
74
+ terminalBudget -= 1;
75
+ continue;
76
+ }
77
+ if (isOmissibleProjectionStatusState(row.state))
78
+ byState[row.state] += 1;
79
+ }
80
+ const total = Object.values(byState).reduce((sum, count) => sum + count, 0);
81
+ return total > 0 ? { rows: visible, omitted: { total, byState } } : { rows: visible };
82
+ }
15
83
  class ProjectionStatusFileError extends Error {
16
84
  diagnostic;
17
85
  constructor(kind, filePath) {
@@ -198,7 +266,7 @@ function readActivityFacts(projectionDir, executionId, nowMs, diagnostics, fallb
198
266
  : {}),
199
267
  };
200
268
  }
201
- function readProjectionRow(projectionDir, id, nowMs) {
269
+ function readProjectionRow(projectionDir, id, alias, nowMs) {
202
270
  const diagnostics = [];
203
271
  const identityFacts = readIdentityFacts(projectionDir, diagnostics);
204
272
  const tellCounts = identityFacts.identity
@@ -232,6 +300,7 @@ function readProjectionRow(projectionDir, id, nowMs) {
232
300
  const state = phase === "active" || phase === "quiescent" ? "out" : phase;
233
301
  return {
234
302
  id,
303
+ ...(alias ? { alias } : {}),
235
304
  state,
236
305
  akuma: identityFacts.akuma,
237
306
  provider: identityFacts.provider,
@@ -243,7 +312,7 @@ function readProjectionRow(projectionDir, id, nowMs) {
243
312
  diagnostics,
244
313
  };
245
314
  }
246
- export function readProjectionStatusBoard(cwd, nowMs) {
315
+ export function readProjectionStatusBoard(cwd, nowMs, options = {}) {
247
316
  const root = projectionRoot(cwd);
248
317
  try {
249
318
  if (!fs.lstatSync(root).isDirectory())
@@ -263,17 +332,47 @@ export function readProjectionStatusBoard(cwd, nowMs) {
263
332
  return null;
264
333
  throw error;
265
334
  }
266
- const rows = entries
267
- .filter((entry) => !entry.startsWith("."))
268
- .filter((entry) => {
335
+ const aliases = new Map();
336
+ const aliasRoot = path.join(path.dirname(path.dirname(root)), ".keiyaku", "projection-alias");
337
+ try {
338
+ for (const alias of fs.readdirSync(aliasRoot).sort()) {
339
+ if (alias.startsWith("."))
340
+ continue;
341
+ const id = readProjectionAlias(cwd, alias);
342
+ if (id)
343
+ aliases.set(id, alias);
344
+ }
345
+ }
346
+ catch (error) {
347
+ if (!(isErrnoException(error) && error.code === "ENOENT"))
348
+ throw error;
349
+ }
350
+ const rows = [];
351
+ for (const akuma of entries.sort()) {
352
+ if (akuma.startsWith("."))
353
+ continue;
354
+ const akumaDir = path.join(root, akuma);
269
355
  try {
270
- return fs.lstatSync(path.join(root, entry)).isDirectory();
356
+ if (!fs.lstatSync(akumaDir).isDirectory())
357
+ continue;
271
358
  }
272
359
  catch {
273
- return false;
360
+ continue;
274
361
  }
275
- })
276
- .sort()
277
- .map((id) => readProjectionRow(path.join(root, id), id, nowMs));
278
- return rows.length > 0 ? { rows } : null;
362
+ for (const hex of fs.readdirSync(akumaDir).sort()) {
363
+ const id = `${akuma}/${hex}`;
364
+ const dir = path.join(akumaDir, hex);
365
+ if (!isProjectionId(id))
366
+ continue;
367
+ try {
368
+ if (!fs.lstatSync(dir).isDirectory())
369
+ continue;
370
+ }
371
+ catch {
372
+ continue;
373
+ }
374
+ rows.push(readProjectionRow(dir, id, aliases.get(id), nowMs));
375
+ }
376
+ }
377
+ return rows.length > 0 ? prioritizeProjectionStatusRows(rows, options.showAll === true) : null;
279
378
  }
@@ -0,0 +1,5 @@
1
+ /** Neutral response-artifact identity grammar. Sole production regex for rsp_<ULID>. */
2
+ export const RESPONSE_ARTIFACT_ID_RE = /^rsp_[0-9A-HJKMNP-TV-Z]{26}$/;
3
+ export function isResponseArtifactId(value) {
4
+ return RESPONSE_ARTIFACT_ID_RE.test(value);
5
+ }
@@ -528,7 +528,7 @@ export async function readKanshiBoard(cwd, now, opts) {
528
528
  const claimReconciliation = await computeClaimReconciliation(cwd, defaultBranch);
529
529
  return {
530
530
  contracts: filteredContracts,
531
- projections: readProjectionStatusBoard(cwd, now),
531
+ projections: readProjectionStatusBoard(cwd, now, { showAll: opts?.showAllProjections }),
532
532
  queueLength,
533
533
  defaultBranch: defaultBranch?.branch,
534
534
  defaultHead: defaultBranch?.head,