lazycodex-ai 5.0.0-beta.82 → 5.0.0-beta.83

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.
Files changed (104) hide show
  1. package/README.ja.md +2 -2
  2. package/README.ko.md +2 -2
  3. package/README.md +4 -4
  4. package/README.ru.md +2 -2
  5. package/README.zh-cn.md +2 -2
  6. package/dist/cli/index.js +392 -112
  7. package/dist/cli/install-native/index.d.ts +4 -0
  8. package/dist/cli/install-native/plan.d.ts +11 -0
  9. package/dist/cli/install-native/run-native-install.d.ts +23 -0
  10. package/dist/cli/install-native-dev/index.d.ts +4 -0
  11. package/dist/cli/native-dev-platform-flag.d.ts +5 -0
  12. package/dist/cli/native-edition-hint.d.ts +10 -0
  13. package/dist/cli/types.d.ts +3 -2
  14. package/dist/cli-node/index.js +392 -112
  15. package/package.json +1 -1
  16. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  17. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +84 -11
  18. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  19. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  20. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  21. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  22. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  23. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  25. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  26. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  27. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  28. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  30. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  31. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  32. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  33. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  34. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  36. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  38. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  39. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +24 -10
  40. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +8 -6
  41. package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.d.ts +2 -0
  42. package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.js +21 -4
  43. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  44. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  45. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +10 -5
  46. package/packages/omo-codex/plugin/components/ulw-loop/src/surface.ts +24 -6
  47. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard-native-reviewer.test.ts +207 -0
  48. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +6 -4
  49. package/packages/omo-codex/plugin/components/ulw-loop/test/surface.test.ts +43 -4
  50. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  71. package/packages/omo-codex/plugin/package-lock.json +12 -12
  72. package/packages/omo-codex/plugin/package.json +1 -1
  73. package/packages/omo-codex/plugin/skills/browser/ATTRIBUTION.md +25 -0
  74. package/packages/omo-codex/plugin/skills/browser/SKILL.md +89 -0
  75. package/packages/omo-codex/plugin/skills/browser/agents/openai.yaml +2 -0
  76. package/packages/omo-codex/plugin/skills/browser/references/commands.md +90 -0
  77. package/packages/omo-codex/plugin/skills/browser/references/install.md +71 -0
  78. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/README.md +42 -0
  79. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/frames-and-humans.md +39 -0
  80. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/ladder.md +59 -0
  81. package/packages/omo-codex/plugin/skills/browser/references/owned-engine/network.md +42 -0
  82. package/packages/omo-codex/plugin/skills/browser/references/recipes/1password.md +59 -0
  83. package/packages/omo-codex/plugin/skills/browser/references/remote.md +24 -0
  84. package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +41 -0
  85. package/packages/omo-codex/plugin/skills/browser/scripts/browser-env.mjs +41 -0
  86. package/packages/omo-codex/plugin/skills/browser/scripts/browser-install.mjs +51 -0
  87. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +1 -0
  88. package/packages/omo-codex/scripts/install-dist/install-local.mjs +90 -17
  89. package/packages/shared-skills/skills/browser/ATTRIBUTION.md +25 -0
  90. package/packages/shared-skills/skills/browser/SKILL.md +89 -0
  91. package/packages/shared-skills/skills/browser/references/commands.md +90 -0
  92. package/packages/shared-skills/skills/browser/references/install.md +71 -0
  93. package/packages/shared-skills/skills/browser/references/owned-engine/README.md +42 -0
  94. package/packages/shared-skills/skills/browser/references/owned-engine/frames-and-humans.md +39 -0
  95. package/packages/shared-skills/skills/browser/references/owned-engine/ladder.md +59 -0
  96. package/packages/shared-skills/skills/browser/references/owned-engine/network.md +42 -0
  97. package/packages/shared-skills/skills/browser/references/recipes/1password.md +59 -0
  98. package/packages/shared-skills/skills/browser/references/remote.md +24 -0
  99. package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +41 -0
  100. package/packages/shared-skills/skills/browser/scripts/browser-env.mjs +41 -0
  101. package/packages/shared-skills/skills/browser/scripts/browser-install.mjs +51 -0
  102. package/dist/cli/install-senpi/index.d.ts +0 -1
  103. package/dist/cli/senpi-edition-hint.d.ts +0 -10
  104. package/dist/cli/senpi-platform-flag.d.ts +0 -4
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit --with-ultrawork",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Checking Ulw-Loop Steering"
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking Ulw-Loop Steering"
11
11
  }
12
12
  ]
13
13
  }
@@ -20,7 +20,7 @@
20
20
  "type": "command",
21
21
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use-spawn",
22
22
  "timeout": 5,
23
- "statusMessage": "(OmO 5.0.0-beta.82) Recording Spawn Admission"
23
+ "statusMessage": "(OmO 5.0.0-beta.83) Recording Spawn Admission"
24
24
  }
25
25
  ]
26
26
  }
@@ -33,7 +33,7 @@
33
33
  "type": "command",
34
34
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook pre-tool-use",
35
35
  "timeout": 5,
36
- "statusMessage": "(OmO 5.0.0-beta.82) Enforcing Unlimited Ulw-Loop Budget"
36
+ "statusMessage": "(OmO 5.0.0-beta.83) Enforcing Unlimited Ulw-Loop Budget"
37
37
  }
38
38
  ]
39
39
  },
@@ -44,7 +44,7 @@
44
44
  "type": "command",
45
45
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook pre-tool-use-spawn",
46
46
  "timeout": 5,
47
- "statusMessage": "(OmO 5.0.0-beta.82) Guarding Ulw-Loop Spawns"
47
+ "statusMessage": "(OmO 5.0.0-beta.83) Guarding Ulw-Loop Spawns"
48
48
  }
49
49
  ]
50
50
  }
@@ -56,7 +56,7 @@
56
56
  "type": "command",
57
57
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook stop",
58
58
  "timeout": 10,
59
- "statusMessage": "(OmO 5.0.0-beta.82) Checking Ulw-Loop Resume"
59
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking Ulw-Loop Resume"
60
60
  }
61
61
  ]
62
62
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-ulw-loop",
3
- "version": "5.0.0-beta.82",
3
+ "version": "5.0.0-beta.83",
4
4
  "description": "Codex plugin: durable repo-native multi-goal orchestration with embedded success criteria and observable evidence audit.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -10,7 +10,9 @@ import { atomicWriteJson, isNonEmptyFile, readAdmissionBreaker, readCount, readC
10
10
  import { spawnRoleDenial } from "./spawn-role-guard.js";
11
11
  import { isStateLockTimeout, type StateLockOptions, withStateLockSync } from "./state-lock.js";
12
12
  import {
13
+ canonicalReviewerAgentName,
13
14
  GATE_REVIEWER_AGENT_NAMES,
15
+ LEGACY_REVIEWER_AGENT_ALIASES,
14
16
  REVIEWER_ROLES_BY_SURFACE,
15
17
  resolveToolkitSurface,
16
18
  reviewerRolesFor,
@@ -28,11 +30,13 @@ const SPAWN_TOOL_TOKENS = new Set([
28
30
  ]);
29
31
  export const DEFAULT_FANOUT_LIMIT = 24;
30
32
  const DEFAULT_REVIEW_SPAWN_LIMIT = 3;
31
- const GATE_MESSAGE_PATTERN = /lazycodex-gate-reviewer|omo-senpi-gate-reviewer|final gate review/i;
33
+ const GATE_MESSAGE_PATTERN =
34
+ /lazycodex-gate-reviewer|omo-native-gate-reviewer|omo-senpi-gate-reviewer|final gate review/i;
32
35
  const REVIEW_AGENT_TYPES = [
33
36
  ...Object.values(REVIEWER_ROLES_BY_SURFACE).map((roles) => roles.gateReview),
34
37
  ...Object.values(REVIEWER_ROLES_BY_SURFACE).map((roles) => roles.codeReview),
35
38
  ...Object.values(REVIEWER_ROLES_BY_SURFACE).map((roles) => roles.manualQa),
39
+ ...Object.keys(LEGACY_REVIEWER_AGENT_ALIASES),
36
40
  ] as const;
37
41
  const REVIEW_AGENT_TYPE_SET = new Set<string>(REVIEW_AGENT_TYPES);
38
42
 
@@ -238,13 +242,14 @@ function reviewAgentType(toolInput: unknown): string | null {
238
242
  }
239
243
 
240
244
  function activeSurfaceReviewerAlias(reviewer: string): string {
245
+ const canonical = canonicalReviewerAgentName(reviewer);
241
246
  const activeRoles = reviewerRolesFor(resolveToolkitSurface());
242
247
  for (const roles of Object.values(REVIEWER_ROLES_BY_SURFACE)) {
243
- if (reviewer === roles.codeReview) return activeRoles.codeReview;
244
- if (reviewer === roles.manualQa) return activeRoles.manualQa;
245
- if (reviewer === roles.gateReview) return activeRoles.gateReview;
248
+ if (canonical === roles.codeReview) return activeRoles.codeReview;
249
+ if (canonical === roles.manualQa) return activeRoles.manualQa;
250
+ if (canonical === roles.gateReview) return activeRoles.gateReview;
246
251
  }
247
- return reviewer;
252
+ return canonical;
248
253
  }
249
254
 
250
255
  function deny(reason: string): string {
@@ -22,15 +22,33 @@ export const REVIEWER_ROLES_BY_SURFACE: Readonly<Record<UlwLoopToolkitSurface, U
22
22
  gateReview: "lazycodex-gate-reviewer",
23
23
  },
24
24
  "omo-senpi": {
25
- codeReview: "omo-senpi-code-reviewer",
26
- manualQa: "omo-senpi-qa-executor",
27
- gateReview: "omo-senpi-gate-reviewer",
25
+ codeReview: "omo-native-code-reviewer",
26
+ manualQa: "omo-native-qa-executor",
27
+ gateReview: "omo-native-gate-reviewer",
28
28
  },
29
29
  };
30
30
 
31
- export const GATE_REVIEWER_AGENT_NAMES: ReadonlySet<string> = new Set(
32
- Object.values(REVIEWER_ROLES_BY_SURFACE).map((roles) => roles.gateReview),
33
- );
31
+ // The reviewer agents were renamed omo-senpi-* -> omo-native-*. The retired spellings stay
32
+ // resolvable for one release line so a caller that still names one is recognized rather than
33
+ // silently treated as a non-review spawn.
34
+ export const LEGACY_REVIEWER_AGENT_ALIASES: Readonly<Record<string, string>> = {
35
+ "omo-senpi-code-reviewer": REVIEWER_ROLES_BY_SURFACE["omo-senpi"].codeReview,
36
+ "omo-senpi-qa-executor": REVIEWER_ROLES_BY_SURFACE["omo-senpi"].manualQa,
37
+ "omo-senpi-gate-reviewer": REVIEWER_ROLES_BY_SURFACE["omo-senpi"].gateReview,
38
+ };
39
+
40
+ export function canonicalReviewerAgentName(reviewer: string): string {
41
+ return LEGACY_REVIEWER_AGENT_ALIASES[reviewer] ?? reviewer;
42
+ }
43
+
44
+ const CANONICAL_GATE_REVIEW_NAMES = Object.values(REVIEWER_ROLES_BY_SURFACE).map((roles) => roles.gateReview);
45
+
46
+ export const GATE_REVIEWER_AGENT_NAMES: ReadonlySet<string> = new Set([
47
+ ...CANONICAL_GATE_REVIEW_NAMES,
48
+ ...Object.entries(LEGACY_REVIEWER_AGENT_ALIASES)
49
+ .filter(([, canonical]) => CANONICAL_GATE_REVIEW_NAMES.includes(canonical))
50
+ .map(([legacy]) => legacy),
51
+ ]);
34
52
 
35
53
  export type UlwLoopGateSection = "codeReview" | "manualQa" | "gateReview" | "iteration" | "criteriaCoverage";
36
54
 
@@ -0,0 +1,207 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { mkdtemp, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
+
7
+ import type { PreToolUsePayload } from "../src/codex-hook.ts";
8
+ import { applySpawnBudgetGuards as applySpawnGuards } from "../src/spawn-guard.ts";
9
+
10
+ // The agent resolver canonicalizes omo-senpi-* to omo-native-* BEFORE this guard sees a name, so
11
+ // the guard receives the canonical spelling. When it does not recognize that spelling
12
+ // `reviewAgentType` returns null, which makes the gate-artifact check and the reviewer quota both
13
+ // stop applying without any error.
14
+
15
+ let workDir: string;
16
+ let originalReviewLimit: string | undefined;
17
+ let originalToolkitSurface: string | undefined;
18
+ let originalPluginData: string | undefined;
19
+
20
+ beforeEach(async () => {
21
+ workDir = await mkdtemp(join(tmpdir(), "ulw-native-reviewer-"));
22
+ originalPluginData = process.env["PLUGIN_DATA"];
23
+ process.env["PLUGIN_DATA"] = join(workDir, "plugin-data");
24
+ originalReviewLimit = process.env["OMO_ULW_LOOP_REVIEW_SPAWN_LIMIT"];
25
+ originalToolkitSurface = process.env["OMO_AGENT_TOOLKIT_SURFACE"];
26
+ delete process.env["OMO_ULW_LOOP_REVIEW_SPAWN_LIMIT"];
27
+ delete process.env["OMO_AGENT_TOOLKIT_SURFACE"];
28
+ });
29
+
30
+ afterEach(async () => {
31
+ if (originalToolkitSurface === undefined) delete process.env["OMO_AGENT_TOOLKIT_SURFACE"];
32
+ else process.env["OMO_AGENT_TOOLKIT_SURFACE"] = originalToolkitSurface;
33
+ if (originalReviewLimit === undefined) delete process.env["OMO_ULW_LOOP_REVIEW_SPAWN_LIMIT"];
34
+ else process.env["OMO_ULW_LOOP_REVIEW_SPAWN_LIMIT"] = originalReviewLimit;
35
+ if (originalPluginData === undefined) delete process.env["PLUGIN_DATA"];
36
+ else process.env["PLUGIN_DATA"] = originalPluginData;
37
+ await rm(workDir, { recursive: true, force: true });
38
+ });
39
+
40
+ function payload(toolName: string, toolInput: Record<string, unknown>): PreToolUsePayload {
41
+ return {
42
+ hook_event_name: "PreToolUse",
43
+ session_id: "s1",
44
+ turn_id: "t1",
45
+ transcript_path: null,
46
+ cwd: workDir,
47
+ model: "gpt-5.6-sol",
48
+ permission_mode: "default",
49
+ tool_name: toolName,
50
+ tool_use_id: "tu1",
51
+ tool_input: toolInput,
52
+ };
53
+ }
54
+
55
+ function sessionDir(): string {
56
+ return join(workDir, ".omo", "ulw-loop", "s1");
57
+ }
58
+
59
+ function criterion(id: string): Record<string, unknown> {
60
+ return {
61
+ id,
62
+ scenario: `scenario ${id}`,
63
+ userModel: "happy",
64
+ expectedEvidence: "evidence",
65
+ capturedEvidence: "captured",
66
+ status: "pass",
67
+ capturedAt: "2026-07-11T00:00:00.000Z",
68
+ };
69
+ }
70
+
71
+ function writeGoals(): void {
72
+ mkdirSync(sessionDir(), { recursive: true });
73
+ writeFileSync(
74
+ join(sessionDir(), "goals.json"),
75
+ JSON.stringify({
76
+ version: 1,
77
+ createdAt: "2026-07-11T00:00:00.000Z",
78
+ updatedAt: "2026-07-11T00:00:00.000Z",
79
+ briefPath: ".omo/ulw-loop/s1/brief.md",
80
+ goalsPath: ".omo/ulw-loop/s1/goals.json",
81
+ ledgerPath: ".omo/ulw-loop/s1/ledger.jsonl",
82
+ codexGoalMode: "aggregate",
83
+ goals: [
84
+ {
85
+ id: "g1",
86
+ title: "Final goal",
87
+ objective: "Final goal",
88
+ status: "in_progress",
89
+ successCriteria: [criterion("C001"), criterion("C002")],
90
+ attempt: 1,
91
+ createdAt: "2026-07-11T00:00:00.000Z",
92
+ updatedAt: "2026-07-11T00:00:00.000Z",
93
+ },
94
+ ],
95
+ activeGoalId: "g1",
96
+ }),
97
+ );
98
+ }
99
+
100
+ function writeEvidence(name: string, body: string): void {
101
+ mkdirSync(join(workDir, ".omo", "evidence"), { recursive: true });
102
+ writeFileSync(join(workDir, ".omo", "evidence", name), body);
103
+ }
104
+
105
+ function denial(output: string): { permissionDecision: string; permissionDecisionReason: string } {
106
+ return JSON.parse(output).hookSpecificOutput;
107
+ }
108
+
109
+ describe("applySpawnGuards recognizes the canonical omo-native reviewer names", () => {
110
+ // Unmodified code returns "" for BOTH of the next two cases, so only the denial can fail. The
111
+ // allow case is its control: without it, a blanket denial would also pass.
112
+ it("#given senpi manual QA is absent #when the canonical gate reviewer spawns #then it is denied naming manual QA", () => {
113
+ // given
114
+ process.env["OMO_AGENT_TOOLKIT_SURFACE"] = "omo-senpi";
115
+ writeGoals();
116
+
117
+ // when
118
+ const output = applySpawnGuards(
119
+ payload("collaborationspawn_agent", {
120
+ agent_type: "omo-native-gate-reviewer",
121
+ message: "audit the artifacts",
122
+ }),
123
+ );
124
+
125
+ // then
126
+ expect(output, "an empty guard result means the spawn was allowed").not.toBe("");
127
+ const parsed = denial(output);
128
+ expect(parsed.permissionDecision).toBe("deny");
129
+ expect(parsed.permissionDecisionReason).toContain("g1-manual-qa.md");
130
+ });
131
+
132
+ it("#given senpi manual QA is present #when the canonical gate reviewer spawns #then it is allowed", () => {
133
+ // given
134
+ process.env["OMO_AGENT_TOOLKIT_SURFACE"] = "omo-senpi";
135
+ writeGoals();
136
+ writeEvidence("g1-manual-qa.md", "matrix\n");
137
+
138
+ // when
139
+ const output = applySpawnGuards(
140
+ payload("collaborationspawn_agent", {
141
+ agent_type: "omo-native-gate-reviewer",
142
+ message: "audit the artifacts",
143
+ }),
144
+ );
145
+
146
+ // then
147
+ expect(output).toBe("");
148
+ });
149
+
150
+ it("#given a message-only spawn naming the canonical gate reviewer #when guarded #then it is still recognized", () => {
151
+ // given
152
+ process.env["OMO_AGENT_TOOLKIT_SURFACE"] = "omo-senpi";
153
+ writeGoals();
154
+
155
+ // when
156
+ const output = applySpawnGuards(
157
+ payload("spawn_agent", { message: "Act as omo-native-gate-reviewer; audit the final evidence" }),
158
+ );
159
+
160
+ // then
161
+ expect(output, "an empty guard result means the spawn was allowed").not.toBe("");
162
+ expect(denial(output).permissionDecision).toBe("deny");
163
+ });
164
+
165
+ it("#given repeated canonical gate spawns #when the reviewer cap is exceeded #then the fourth is denied", () => {
166
+ // given
167
+ writeGoals();
168
+ process.env["OMO_AGENT_TOOLKIT_SURFACE"] = "omo-senpi";
169
+ writeEvidence("g1-code-review.md", "report\n");
170
+ writeEvidence("g1-manual-qa.md", "matrix\n");
171
+ const canonicalGate = payload("spawn_agent", {
172
+ agent_type: "omo-native-gate-reviewer",
173
+ message: "audit the final evidence",
174
+ });
175
+
176
+ // when
177
+ expect(applySpawnGuards(canonicalGate)).toBe("");
178
+ expect(applySpawnGuards(canonicalGate)).toBe("");
179
+ expect(applySpawnGuards(canonicalGate)).toBe("");
180
+ const fourthOutput = applySpawnGuards(canonicalGate);
181
+
182
+ // then
183
+ expect(fourthOutput, "an empty fourth result means the reviewer cap never counted").not.toBe("");
184
+ const fourth = denial(fourthOutput);
185
+ expect(fourth.permissionDecision).toBe("deny");
186
+ expect(fourth.permissionDecisionReason).toContain("4/3");
187
+ const counters = JSON.parse(readFileSync(join(sessionDir(), "review-spawn-counts.json"), "utf8"));
188
+ expect(counters["omo-native-gate-reviewer:g1:a1"]).toBe(3);
189
+ });
190
+
191
+ it("#given the legacy spelling #when the gate reviewer spawns without manual QA #then it is denied exactly as before", () => {
192
+ // given
193
+ process.env["OMO_AGENT_TOOLKIT_SURFACE"] = "omo-senpi";
194
+ writeGoals();
195
+
196
+ // when
197
+ const output = applySpawnGuards(
198
+ payload("collaborationspawn_agent", {
199
+ agent_type: "omo-senpi-gate-reviewer",
200
+ message: "audit the artifacts",
201
+ }),
202
+ );
203
+
204
+ // then
205
+ expect(denial(output).permissionDecisionReason).toContain("g1-manual-qa.md");
206
+ });
207
+ });
@@ -430,9 +430,10 @@ describe("applySpawnGuards gate-artifact guard", () => {
430
430
  expect(applySpawnGuards(genericGate)).toBe("");
431
431
 
432
432
  const fourth = deny(applySpawnGuards(explicitGate));
433
- expect(fourth.permissionDecisionReason).toContain("omo-senpi-gate-reviewer 4/3");
433
+ expect(fourth.permissionDecisionReason).toContain("omo-native-gate-reviewer 4/3");
434
434
  const counters = JSON.parse(readFileSync(join(sessionDir(), "review-spawn-counts.json"), "utf8"));
435
- expect(counters["omo-senpi-gate-reviewer:g1:a1"]).toBe(3);
435
+ expect(counters["omo-native-gate-reviewer:g1:a1"]).toBe(3);
436
+ expect(counters["omo-senpi-gate-reviewer:g1:a1"]).toBeUndefined();
436
437
  expect(counters["lazycodex-gate-reviewer:g1:a1"]).toBeUndefined();
437
438
  });
438
439
 
@@ -451,9 +452,10 @@ describe("applySpawnGuards gate-artifact guard", () => {
451
452
  expect(applySpawnGuards(senpiReviewer)).toBe("");
452
453
 
453
454
  const fourth = deny(applySpawnGuards(lazycodexReviewer));
454
- expect(fourth.permissionDecisionReason).toContain("omo-senpi-code-reviewer 4/3");
455
+ expect(fourth.permissionDecisionReason).toContain("omo-native-code-reviewer 4/3");
455
456
  const counters = JSON.parse(readFileSync(join(sessionDir(), "review-spawn-counts.json"), "utf8"));
456
- expect(counters["omo-senpi-code-reviewer:g1:a1"]).toBe(3);
457
+ expect(counters["omo-native-code-reviewer:g1:a1"]).toBe(3);
458
+ expect(counters["omo-senpi-code-reviewer:g1:a1"]).toBeUndefined();
457
459
  expect(counters["lazycodex-code-reviewer:g1:a1"]).toBeUndefined();
458
460
  });
459
461
 
@@ -3,7 +3,14 @@ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
5
 
6
- import { REVIEWER_ROLES_BY_SURFACE, resolveToolkitSurface, reviewerRolesFor } from "../src/surface.ts";
6
+ import {
7
+ canonicalReviewerAgentName,
8
+ GATE_REVIEWER_AGENT_NAMES,
9
+ LEGACY_REVIEWER_AGENT_ALIASES,
10
+ REVIEWER_ROLES_BY_SURFACE,
11
+ resolveToolkitSurface,
12
+ reviewerRolesFor,
13
+ } from "../src/surface.ts";
7
14
 
8
15
  let markerDir: string;
9
16
 
@@ -65,10 +72,42 @@ describe("reviewerRolesFor", () => {
65
72
  gateReview: "lazycodex-gate-reviewer",
66
73
  });
67
74
  expect(reviewerRolesFor("omo-senpi")).toEqual({
68
- codeReview: "omo-senpi-code-reviewer",
69
- manualQa: "omo-senpi-qa-executor",
70
- gateReview: "omo-senpi-gate-reviewer",
75
+ codeReview: "omo-native-code-reviewer",
76
+ manualQa: "omo-native-qa-executor",
77
+ gateReview: "omo-native-gate-reviewer",
71
78
  });
72
79
  expect(Object.keys(REVIEWER_ROLES_BY_SURFACE).sort()).toEqual(["lazycodex", "omo-senpi"]);
73
80
  });
74
81
  });
82
+
83
+ describe("canonicalReviewerAgentName", () => {
84
+ it("#given a retired reviewer spelling #when canonicalized #then it maps to the omo-native name", () => {
85
+ expect(canonicalReviewerAgentName("omo-senpi-gate-reviewer")).toBe("omo-native-gate-reviewer");
86
+ expect(canonicalReviewerAgentName("omo-senpi-code-reviewer")).toBe("omo-native-code-reviewer");
87
+ expect(canonicalReviewerAgentName("omo-senpi-qa-executor")).toBe("omo-native-qa-executor");
88
+ });
89
+
90
+ it("#given a canonical or unrelated name #when canonicalized #then it is returned unchanged", () => {
91
+ expect(canonicalReviewerAgentName("omo-native-gate-reviewer")).toBe("omo-native-gate-reviewer");
92
+ expect(canonicalReviewerAgentName("lazycodex-gate-reviewer")).toBe("lazycodex-gate-reviewer");
93
+ expect(canonicalReviewerAgentName("explore")).toBe("explore");
94
+ });
95
+
96
+ it("#given every retired alias #when mapped #then each target is a live reviewer role", () => {
97
+ const liveRoles = new Set(
98
+ Object.values(REVIEWER_ROLES_BY_SURFACE).flatMap((roles) => [
99
+ roles.codeReview,
100
+ roles.manualQa,
101
+ roles.gateReview,
102
+ ]),
103
+ );
104
+ for (const canonical of Object.values(LEGACY_REVIEWER_AGENT_ALIASES)) expect(liveRoles.has(canonical)).toBe(true);
105
+ });
106
+
107
+ it("#given the gate reviewer name set #when read #then it carries both the canonical and the retired spelling", () => {
108
+ expect(GATE_REVIEWER_AGENT_NAMES.has("omo-native-gate-reviewer")).toBe(true);
109
+ expect(GATE_REVIEWER_AGENT_NAMES.has("omo-senpi-gate-reviewer")).toBe(true);
110
+ expect(GATE_REVIEWER_AGENT_NAMES.has("lazycodex-gate-reviewer")).toBe(true);
111
+ expect(GATE_REVIEWER_AGENT_NAMES.has("omo-native-code-reviewer")).toBe(false);
112
+ });
113
+ });
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/git-bash/dist/cli.js\" hook post-compact",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Resetting Git Bash MCP Reminder",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Resetting Git Bash MCP Reminder",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\git-bash\\dist\\cli.js\" hook post-compact"
12
12
  }
13
13
  ],
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/lsp/dist/cli.js\" hook post-compact",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Resetting LSP Diagnostics Cache",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Resetting LSP Diagnostics Cache",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\lsp\\dist\\cli.js\" hook post-compact"
12
12
  }
13
13
  ],
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/rules/dist/cli.js\" hook post-compact",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Resetting Project Rule Cache",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Resetting Project Rule Cache",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\rules\\dist\\cli.js\" hook post-compact"
12
12
  }
13
13
  ],
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/comment-checker/dist/cli.js\" hook post-tool-use",
9
9
  "timeout": 30,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Checking Comments",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking Comments",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\comment-checker\\dist\\cli.js\" hook post-tool-use"
12
12
  }
13
13
  ],
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/lsp/dist/cli.js\" hook post-tool-use",
9
9
  "timeout": 60,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Checking LSP Diagnostics",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking LSP Diagnostics",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\lsp\\dist\\cli.js\" hook post-tool-use"
12
12
  }
13
13
  ],
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/components/teammode/dist/cli.js\" hook post-tool-use",
10
10
  "timeout": 10,
11
- "statusMessage": "(OmO 5.0.0-beta.82) Checking Thread Title Hygiene",
11
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking Thread Title Hygiene",
12
12
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\teammode\\dist\\cli.js\" hook post-tool-use"
13
13
  }
14
14
  ]
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/rules/dist/cli.js\" hook post-tool-use",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Matching Project Rules",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Matching Project Rules",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\rules\\dist\\cli.js\" hook post-tool-use"
12
12
  }
13
13
  ],
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/components/ulw-loop/dist/cli.js\" hook post-tool-use-spawn",
10
10
  "timeout": 5,
11
- "statusMessage": "(OmO 5.0.0-beta.82) Recording Spawn Admission",
11
+ "statusMessage": "(OmO 5.0.0-beta.83) Recording Spawn Admission",
12
12
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\ulw-loop\\dist\\cli.js\" hook post-tool-use-spawn"
13
13
  }
14
14
  ]
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/ulw-loop/dist/cli.js\" hook pre-tool-use",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Enforcing Unlimited Goal Budget",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Enforcing Unlimited Goal Budget",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\ulw-loop\\dist\\cli.js\" hook pre-tool-use"
12
12
  }
13
13
  ],
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/components/ulw-loop/dist/cli.js\" hook pre-tool-use-spawn",
10
10
  "timeout": 5,
11
- "statusMessage": "(OmO 5.0.0-beta.82) Guarding Ulw-Loop Spawns",
11
+ "statusMessage": "(OmO 5.0.0-beta.83) Guarding Ulw-Loop Spawns",
12
12
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\ulw-loop\\dist\\cli.js\" hook pre-tool-use-spawn"
13
13
  }
14
14
  ]
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/git-bash/dist/cli.js\" hook pre-tool-use",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Recommending Git Bash MCP",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Recommending Git Bash MCP",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\git-bash\\dist\\cli.js\" hook pre-tool-use"
12
12
  }
13
13
  ],
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/scripts/auto-update.mjs\" hook session-start",
9
9
  "timeout": 15,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Checking Auto Update",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking Auto Update",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\scripts\\auto-update.mjs\" hook session-start"
12
12
  }
13
13
  ],
@@ -8,7 +8,7 @@
8
8
  "command": "node \"${PLUGIN_ROOT}/components/bootstrap/dist/cli.js\" hook session-start",
9
9
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\bootstrap.ps1\"",
10
10
  "timeout": 30,
11
- "statusMessage": "(OmO 5.0.0-beta.82) Checking Bootstrap Provisioning"
11
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking Bootstrap Provisioning"
12
12
  }
13
13
  ]
14
14
  }
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/rules/dist/cli.js\" hook session-start",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Loading Project Rules",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Loading Project Rules",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\rules\\dist\\cli.js\" hook session-start"
12
12
  }
13
13
  ]
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/telemetry/dist/cli.js\" hook session-start",
9
9
  "timeout": 15,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Recording Session Telemetry",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Recording Session Telemetry",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\telemetry\\dist\\cli.js\" hook session-start"
12
12
  }
13
13
  ]
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/ulw-execute-continuation/dist/cli.js\" hook stop",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Checking Ulw-Execute Continuation",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking Ulw-Execute Continuation",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\ulw-execute-continuation\\dist\\cli.js\" hook stop"
12
12
  }
13
13
  ]
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/ulw-loop/dist/cli.js\" hook stop",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Checking Ulw-Loop Resume",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking Ulw-Loop Resume",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\ulw-loop\\dist\\cli.js\" hook stop"
12
12
  }
13
13
  ]
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/lazycodex-executor-verify/dist/cli.js\" hook subagent-stop",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Verifying LazyCodex Executor Evidence",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Verifying LazyCodex Executor Evidence",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\lazycodex-executor-verify\\dist\\cli.js\" hook subagent-stop"
12
12
  }
13
13
  ],
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/ultrawork/dist/cli.js\" hook user-prompt-submit",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Checking Ultrawork Trigger",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking Ultrawork Trigger",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\ultrawork\\dist\\cli.js\" hook user-prompt-submit"
12
12
  }
13
13
  ]
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/ulw-loop/dist/cli.js\" hook user-prompt-submit",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.0.0-beta.82) Checking Ulw-Loop Steering",
10
+ "statusMessage": "(OmO 5.0.0-beta.83) Checking Ulw-Loop Steering",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\ulw-loop\\dist\\cli.js\" hook user-prompt-submit"
12
12
  }
13
13
  ]