oh-my-opencode 5.0.0-beta.66 → 5.0.0-beta.68
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/cli/index.js +15 -15
- package/dist/cli-node/index.js +15 -15
- package/dist/index.js +14 -14
- package/package.json +14 -14
- package/packages/lsp-core/src/lsp/client-diagnostics-concurrency.integration.test.ts +9 -1
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +43 -8
- package/packages/lsp-core/src/lsp/controlled-clock-test-support.ts +29 -0
- package/packages/lsp-core/src/lsp/workspace-document-state.ts +5 -0
- package/packages/lsp-daemon/dist/cli.js +2 -0
- package/packages/lsp-daemon/dist/client.js +2 -0
- package/packages/lsp-daemon/dist/index.js +2 -0
- package/packages/lsp-tools-mcp/dist/cli.js +2 -0
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +2 -0
- package/packages/lsp-tools-mcp/dist/mcp.js +2 -0
- package/packages/lsp-tools-mcp/dist/tools.js +2 -0
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +2 -0
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +26 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/ledger.js +6 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-commit.js +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-log.d.ts +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-log.js +30 -1
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/ledger.ts +6 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-commit.ts +9 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-log.ts +30 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/plan-log-legacy-projection.test.ts +228 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "5.0.0-beta.
|
|
4
|
-
"inputDigest": "sha256:
|
|
3
|
+
"version": "5.0.0-beta.68",
|
|
4
|
+
"inputDigest": "sha256:e94fa1eb1e1363a3c1c13ccfeec67863452fdbfcb2ed88d4a2b2e2cb8fe6581f",
|
|
5
5
|
"outputs": [
|
|
6
6
|
{
|
|
7
7
|
"path": "cli.d.ts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "cli.js",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "b8d0f009e0031d015280244ddd2e5abb8db076ffb324c7e500d29d437a345446"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": "codex-hook-cli.d.ts",
|
|
@@ -1920,6 +1920,8 @@ class WorkspaceDocumentState {
|
|
|
1920
1920
|
const state = this.openByUri.get(normalizeDocumentUri(snapshot.uri));
|
|
1921
1921
|
if (!state || timeoutMs <= 0)
|
|
1922
1922
|
return Promise.resolve();
|
|
1923
|
+
if (state.publishGeneration !== snapshot.publishGeneration)
|
|
1924
|
+
return Promise.resolve();
|
|
1923
1925
|
return new Promise((resolveActivity) => {
|
|
1924
1926
|
let settled = false;
|
|
1925
1927
|
const finish = () => {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "command",
|
|
9
9
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
|
|
10
10
|
"timeout": 60,
|
|
11
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
11
|
+
"statusMessage": "(OmO 5.0.0-beta.68) Checking LSP Diagnostics"
|
|
12
12
|
}
|
|
13
13
|
]
|
|
14
14
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"type": "command",
|
|
22
22
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
|
|
23
23
|
"timeout": 5,
|
|
24
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
24
|
+
"statusMessage": "(OmO 5.0.0-beta.68) Resetting LSP Diagnostics Cache"
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"type": "command",
|
|
8
8
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
|
|
9
9
|
"timeout": 10,
|
|
10
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
10
|
+
"statusMessage": "(OmO 5.0.0-beta.68) Loading Project Rules"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"type": "command",
|
|
20
20
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
|
|
21
21
|
"timeout": 10,
|
|
22
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
22
|
+
"statusMessage": "(OmO 5.0.0-beta.68) Loading Project Rules"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"type": "command",
|
|
33
33
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
|
|
34
34
|
"timeout": 10,
|
|
35
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
35
|
+
"statusMessage": "(OmO 5.0.0-beta.68) Matching Project Rules"
|
|
36
36
|
}
|
|
37
37
|
]
|
|
38
38
|
}
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"type": "command",
|
|
46
46
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
|
|
47
47
|
"timeout": 10,
|
|
48
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
48
|
+
"statusMessage": "(OmO 5.0.0-beta.68) Resetting Project Rule Cache"
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
51
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "command",
|
|
9
9
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
|
|
10
10
|
"timeout": 10,
|
|
11
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
11
|
+
"statusMessage": "(OmO 5.0.0-beta.68) Checking Thread Title Hygiene"
|
|
12
12
|
}
|
|
13
13
|
]
|
|
14
14
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/codex-telemetry",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.68",
|
|
4
4
|
"description": "Codex plugin component that emits omo-codex anonymous daily-active telemetry on SessionStart.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@11.12.1",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"type": "command",
|
|
8
8
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
|
|
9
9
|
"timeout": 5,
|
|
10
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
10
|
+
"statusMessage": "(OmO 5.0.0-beta.68) Checking Ultrawork Trigger"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/codex-ultrawork",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.68",
|
|
4
4
|
"description": "Codex plugin that injects the ultrawork orchestration directive and ships LazyCodex planning, review, QA, and gate agent roles.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@11.12.1",
|
|
@@ -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.
|
|
10
|
+
"statusMessage": "(OmO 5.0.0-beta.68) Checking Ulw-Execute Continuation"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## [0.1.0] - unreleased
|
|
4
4
|
|
|
5
|
+
- **Fix:** a `goals.json` written outside the commit log (the removed `omo_agent_toolkit` tool path rewrote it wholesale, never stamping `revision`) no longer loses to an older `revisions/` snapshot when it holds goals that snapshot lacks; the cache wins, is stamped with the snapshot's revision, and the next publish folds it into revision N+1 instead of colliding on an existing record. Raw `ledger.jsonl` lines appended after a published revision are attributed to that revision rather than to the pre-store past a later `ledgerResetRevision` discards. Locked reads and every commit now refuse to rewrite `goals.json` while the reconciled ledger names a `goal_added` goal the projection lacks (`ULW_LOOP_PROJECTION_TRUNCATED`, `details.missingGoalIds`) instead of silently truncating the plan (code-yeongyu/oh-my-openagent#8328).
|
|
5
6
|
- Status nextActions and missing-plan recovery now follow the calling surface: omo-senpi teaches the eval SDK methods and current attempt directory without asking for a driver snapshot; LazyCodex retains its existing CLI guidance byte-for-byte.
|
|
6
7
|
|
|
7
8
|
- Async `.state.lock` holders now keep their file descriptor open and refresh a 30-second lease through it. An expired lease can be reclaimed even while its process remains alive; sync hooks and older lease-less records retain dead-pid-only ownership. Windows filesystems that refuse unlinking an open lock fail closed. Interrupted kernels can recover after lease expiry without deleting a live owner's lock.
|
|
@@ -456,7 +456,28 @@ function reconcilePlan(dir) {
|
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
458
|
const latest = readNewestRecord(dir);
|
|
459
|
-
|
|
459
|
+
if (latest === undefined)
|
|
460
|
+
return cached;
|
|
461
|
+
if (cached === undefined)
|
|
462
|
+
return latest.plan;
|
|
463
|
+
if ((cached.revision ?? 0) > latest.revision)
|
|
464
|
+
return cached;
|
|
465
|
+
if (cached.revision === undefined || cached.revision === latest.revision) {
|
|
466
|
+
const published = new Set(latest.plan.goals.map((goal) => goal.id));
|
|
467
|
+
if (Array.isArray(cached.goals) && cached.goals.some((goal) => !published.has(goal.id)))
|
|
468
|
+
return { ...cached, revision: latest.revision };
|
|
469
|
+
}
|
|
470
|
+
return latest.plan;
|
|
471
|
+
}
|
|
472
|
+
function assertProjectionComplete(plan, ledger) {
|
|
473
|
+
const present = new Set(plan.goals.map((goal) => goal.id));
|
|
474
|
+
const missing = new Set;
|
|
475
|
+
for (const entry of ledger)
|
|
476
|
+
if (entry.kind === "goal_added" && entry.goalId !== undefined && !present.has(entry.goalId))
|
|
477
|
+
missing.add(entry.goalId);
|
|
478
|
+
if (missing.size === 0)
|
|
479
|
+
return;
|
|
480
|
+
throw new UlwLoopError(`Refusing to rewrite goals.json: the ledger records goals the plan projection lacks (${[...missing].join(", ")}). Restore those goals into the newest revisions/ record (keeping plan.revision equal to its file number) before mutating this run.`, "ULW_LOOP_PROJECTION_TRUNCATED", { details: { missingGoalIds: [...missing] } });
|
|
460
481
|
}
|
|
461
482
|
function planExists(repoRoot, scope) {
|
|
462
483
|
const dir = ulwLoopDir(repoRoot, scope);
|
|
@@ -467,12 +488,14 @@ function planExists(repoRoot, scope) {
|
|
|
467
488
|
function readLedgerAt(dir) {
|
|
468
489
|
const entries = new Map;
|
|
469
490
|
const lines = (readOptional(join3(dir, "ledger.jsonl")) ?? "").split(/\r?\n/);
|
|
491
|
+
let reached = 0;
|
|
470
492
|
for (const [index, line] of lines.entries()) {
|
|
471
493
|
if (line.trim().length === 0)
|
|
472
494
|
continue;
|
|
473
495
|
try {
|
|
474
496
|
const entry = JSON.parse(line);
|
|
475
|
-
entry.revision
|
|
497
|
+
entry.revision = entry.revision || reached;
|
|
498
|
+
reached = Math.max(reached, entry.revision);
|
|
476
499
|
entry.id ??= `legacy-${index + 1}`;
|
|
477
500
|
entries.set(entry.id, entry);
|
|
478
501
|
} catch (error) {
|
|
@@ -959,6 +982,7 @@ function viewContents(dir) {
|
|
|
959
982
|
if (plan === undefined || readRecords(dir).length === 0)
|
|
960
983
|
return [];
|
|
961
984
|
const ledger = readLedgerAt(dir);
|
|
985
|
+
assertProjectionComplete(plan, ledger);
|
|
962
986
|
const views = [
|
|
963
987
|
["goals.json", `${JSON.stringify(plan, null, 2)}
|
|
964
988
|
`],
|
|
@@ -4,12 +4,17 @@ import { readOptional, readRecords, reconcilePlan } from "./plan-log.js";
|
|
|
4
4
|
export function readLedgerAt(dir) {
|
|
5
5
|
const entries = new Map();
|
|
6
6
|
const lines = (readOptional(join(dir, "ledger.jsonl")) ?? "").split(/\r?\n/);
|
|
7
|
+
// A materialized view is sorted by revision, so a line that carries no revision (or revision 0)
|
|
8
|
+
// AFTER a published revision was appended out-of-band (the removed tool path) and belongs to the
|
|
9
|
+
// newest revision before it - not to the pre-store past a later ledgerResetRevision would discard.
|
|
10
|
+
let reached = 0;
|
|
7
11
|
for (const [index, line] of lines.entries()) {
|
|
8
12
|
if (line.trim().length === 0)
|
|
9
13
|
continue;
|
|
10
14
|
try {
|
|
11
15
|
const entry = JSON.parse(line);
|
|
12
|
-
entry.revision
|
|
16
|
+
entry.revision = entry.revision || reached;
|
|
17
|
+
reached = Math.max(reached, entry.revision);
|
|
13
18
|
entry.id ??= `legacy-${index + 1}`;
|
|
14
19
|
entries.set(entry.id, entry);
|
|
15
20
|
}
|
|
@@ -6,7 +6,7 @@ import { join } from "node:path";
|
|
|
6
6
|
import { readLedgerAt } from "./ledger.js";
|
|
7
7
|
import { ulwLoopDir, ulwLoopStateLockPath } from "./paths.js";
|
|
8
8
|
import { assertStateLockOwned, migrationEntries } from "./plan-io.js";
|
|
9
|
-
import { hasCode, readOptional, readRecords, reconcilePlan } from "./plan-log.js";
|
|
9
|
+
import { assertProjectionComplete, hasCode, readOptional, readRecords, reconcilePlan, } from "./plan-log.js";
|
|
10
10
|
import { UlwLoopError } from "./types.js";
|
|
11
11
|
const hooks = new AsyncLocalStorage();
|
|
12
12
|
export function withCommitHooks(value, fn) {
|
|
@@ -72,6 +72,7 @@ function viewContents(dir) {
|
|
|
72
72
|
if (plan === undefined || readRecords(dir).length === 0)
|
|
73
73
|
return [];
|
|
74
74
|
const ledger = readLedgerAt(dir);
|
|
75
|
+
assertProjectionComplete(plan, ledger);
|
|
75
76
|
const views = [
|
|
76
77
|
["goals.json", `${JSON.stringify(plan, null, 2)}\n`],
|
|
77
78
|
["ledger.jsonl", ledger.length === 0 ? "" : `${ledger.map((entry) => JSON.stringify(entry)).join("\n")}\n`],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type UlwLoopScope } from "./paths.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type UlwLoopLedgerEntry, type UlwLoopPlan } from "./types.js";
|
|
3
3
|
export interface PlanCommitRecord {
|
|
4
4
|
readonly version: 1;
|
|
5
5
|
readonly revision: number;
|
|
@@ -12,4 +12,5 @@ export declare function logNames(dir: string): string[];
|
|
|
12
12
|
export declare function readRecords(dir: string): PlanCommitRecord[];
|
|
13
13
|
export declare function readNewestRecord(dir: string): PlanCommitRecord | undefined;
|
|
14
14
|
export declare function reconcilePlan(dir: string): UlwLoopPlan | undefined;
|
|
15
|
+
export declare function assertProjectionComplete(plan: UlwLoopPlan, ledger: readonly UlwLoopLedgerEntry[]): void;
|
|
15
16
|
export declare function planExists(repoRoot: string, scope?: UlwLoopScope): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { ulwLoopDir } from "./paths.js";
|
|
4
|
+
import { UlwLoopError } from "./types.js";
|
|
4
5
|
export function hasCode(error, code) {
|
|
5
6
|
return error instanceof Error && "code" in error && error.code === code;
|
|
6
7
|
}
|
|
@@ -78,7 +79,35 @@ export function reconcilePlan(dir) {
|
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
const latest = readNewestRecord(dir);
|
|
81
|
-
|
|
82
|
+
if (latest === undefined)
|
|
83
|
+
return cached;
|
|
84
|
+
if (cached === undefined)
|
|
85
|
+
return latest.plan;
|
|
86
|
+
if ((cached.revision ?? 0) > latest.revision)
|
|
87
|
+
return cached;
|
|
88
|
+
// A goals.json written outside the commit log (the removed tool path rewrote it wholesale, never
|
|
89
|
+
// stamping `revision`) can hold goals no snapshot has. Serving the snapshot would drop them, so the
|
|
90
|
+
// cache wins and is stamped with the snapshot's revision: the next publish derives N+1 from it and
|
|
91
|
+
// folds the whole plan into a real record instead of colliding with an existing revision file.
|
|
92
|
+
// A cache that names an OLDER revision is a lagging view of a force-recreate and never wins.
|
|
93
|
+
if (cached.revision === undefined || cached.revision === latest.revision) {
|
|
94
|
+
const published = new Set(latest.plan.goals.map((goal) => goal.id));
|
|
95
|
+
if (Array.isArray(cached.goals) && cached.goals.some((goal) => !published.has(goal.id)))
|
|
96
|
+
return { ...cached, revision: latest.revision };
|
|
97
|
+
}
|
|
98
|
+
return latest.plan;
|
|
99
|
+
}
|
|
100
|
+
// Goals are never removed from a plan, so every `goal_added` the reconciled ledger still carries must
|
|
101
|
+
// have a goal in the projection. A projection that lacks one is truncated and must not become goals.json.
|
|
102
|
+
export function assertProjectionComplete(plan, ledger) {
|
|
103
|
+
const present = new Set(plan.goals.map((goal) => goal.id));
|
|
104
|
+
const missing = new Set();
|
|
105
|
+
for (const entry of ledger)
|
|
106
|
+
if (entry.kind === "goal_added" && entry.goalId !== undefined && !present.has(entry.goalId))
|
|
107
|
+
missing.add(entry.goalId);
|
|
108
|
+
if (missing.size === 0)
|
|
109
|
+
return;
|
|
110
|
+
throw new UlwLoopError(`Refusing to rewrite goals.json: the ledger records goals the plan projection lacks (${[...missing].join(", ")}). Restore those goals into the newest revisions/ record (keeping plan.revision equal to its file number) before mutating this run.`, "ULW_LOOP_PROJECTION_TRUNCATED", { details: { missingGoalIds: [...missing] } });
|
|
82
111
|
}
|
|
83
112
|
export function planExists(repoRoot, scope) {
|
|
84
113
|
const dir = ulwLoopDir(repoRoot, scope);
|
|
@@ -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.
|
|
10
|
+
"statusMessage": "(OmO 5.0.0-beta.68) 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.
|
|
23
|
+
"statusMessage": "(OmO 5.0.0-beta.68) 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.
|
|
36
|
+
"statusMessage": "(OmO 5.0.0-beta.68) 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.
|
|
47
|
+
"statusMessage": "(OmO 5.0.0-beta.68) 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.
|
|
59
|
+
"statusMessage": "(OmO 5.0.0-beta.68) 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.
|
|
3
|
+
"version": "5.0.0-beta.68",
|
|
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",
|
|
@@ -6,11 +6,16 @@ import type { UlwLoopLedgerEntry } from "./types.js";
|
|
|
6
6
|
export function readLedgerAt(dir: string): UlwLoopLedgerEntry[] {
|
|
7
7
|
const entries = new Map<string, UlwLoopLedgerEntry>();
|
|
8
8
|
const lines = (readOptional(join(dir, "ledger.jsonl")) ?? "").split(/\r?\n/);
|
|
9
|
+
// A materialized view is sorted by revision, so a line that carries no revision (or revision 0)
|
|
10
|
+
// AFTER a published revision was appended out-of-band (the removed tool path) and belongs to the
|
|
11
|
+
// newest revision before it - not to the pre-store past a later ledgerResetRevision would discard.
|
|
12
|
+
let reached = 0;
|
|
9
13
|
for (const [index, line] of lines.entries()) {
|
|
10
14
|
if (line.trim().length === 0) continue;
|
|
11
15
|
try {
|
|
12
16
|
const entry: UlwLoopLedgerEntry = JSON.parse(line);
|
|
13
|
-
entry.revision
|
|
17
|
+
entry.revision = entry.revision || reached;
|
|
18
|
+
reached = Math.max(reached, entry.revision);
|
|
14
19
|
entry.id ??= `legacy-${index + 1}`;
|
|
15
20
|
entries.set(entry.id, entry);
|
|
16
21
|
} catch (error) {
|
|
@@ -6,7 +6,14 @@ import { join } from "node:path";
|
|
|
6
6
|
import { readLedgerAt } from "./ledger.js";
|
|
7
7
|
import { type UlwLoopScope, ulwLoopDir, ulwLoopStateLockPath } from "./paths.js";
|
|
8
8
|
import { assertStateLockOwned, migrationEntries } from "./plan-io.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
assertProjectionComplete,
|
|
11
|
+
hasCode,
|
|
12
|
+
type PlanCommitRecord,
|
|
13
|
+
readOptional,
|
|
14
|
+
readRecords,
|
|
15
|
+
reconcilePlan,
|
|
16
|
+
} from "./plan-log.js";
|
|
10
17
|
import { UlwLoopError, type UlwLoopLedgerEntry, type UlwLoopPlan } from "./types.js";
|
|
11
18
|
|
|
12
19
|
interface CommitHooks {
|
|
@@ -91,6 +98,7 @@ function viewContents(dir: string): [string, string][] {
|
|
|
91
98
|
const plan = reconcilePlan(dir);
|
|
92
99
|
if (plan === undefined || readRecords(dir).length === 0) return [];
|
|
93
100
|
const ledger = readLedgerAt(dir);
|
|
101
|
+
assertProjectionComplete(plan, ledger);
|
|
94
102
|
const views: [string, string][] = [
|
|
95
103
|
["goals.json", `${JSON.stringify(plan, null, 2)}\n`],
|
|
96
104
|
["ledger.jsonl", ledger.length === 0 ? "" : `${ledger.map((entry) => JSON.stringify(entry)).join("\n")}\n`],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { type UlwLoopScope, ulwLoopDir } from "./paths.js";
|
|
4
|
-
import type
|
|
4
|
+
import { UlwLoopError, type UlwLoopLedgerEntry, type UlwLoopPlan } from "./types.js";
|
|
5
5
|
|
|
6
6
|
export interface PlanCommitRecord {
|
|
7
7
|
readonly version: 1;
|
|
@@ -76,7 +76,35 @@ export function reconcilePlan(dir: string): UlwLoopPlan | undefined {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
const latest = readNewestRecord(dir);
|
|
79
|
-
|
|
79
|
+
if (latest === undefined) return cached;
|
|
80
|
+
if (cached === undefined) return latest.plan;
|
|
81
|
+
if ((cached.revision ?? 0) > latest.revision) return cached;
|
|
82
|
+
// A goals.json written outside the commit log (the removed tool path rewrote it wholesale, never
|
|
83
|
+
// stamping `revision`) can hold goals no snapshot has. Serving the snapshot would drop them, so the
|
|
84
|
+
// cache wins and is stamped with the snapshot's revision: the next publish derives N+1 from it and
|
|
85
|
+
// folds the whole plan into a real record instead of colliding with an existing revision file.
|
|
86
|
+
// A cache that names an OLDER revision is a lagging view of a force-recreate and never wins.
|
|
87
|
+
if (cached.revision === undefined || cached.revision === latest.revision) {
|
|
88
|
+
const published = new Set(latest.plan.goals.map((goal) => goal.id));
|
|
89
|
+
if (Array.isArray(cached.goals) && cached.goals.some((goal) => !published.has(goal.id)))
|
|
90
|
+
return { ...cached, revision: latest.revision };
|
|
91
|
+
}
|
|
92
|
+
return latest.plan;
|
|
93
|
+
}
|
|
94
|
+
// Goals are never removed from a plan, so every `goal_added` the reconciled ledger still carries must
|
|
95
|
+
// have a goal in the projection. A projection that lacks one is truncated and must not become goals.json.
|
|
96
|
+
export function assertProjectionComplete(plan: UlwLoopPlan, ledger: readonly UlwLoopLedgerEntry[]): void {
|
|
97
|
+
const present = new Set(plan.goals.map((goal) => goal.id));
|
|
98
|
+
const missing = new Set<string>();
|
|
99
|
+
for (const entry of ledger)
|
|
100
|
+
if (entry.kind === "goal_added" && entry.goalId !== undefined && !present.has(entry.goalId))
|
|
101
|
+
missing.add(entry.goalId);
|
|
102
|
+
if (missing.size === 0) return;
|
|
103
|
+
throw new UlwLoopError(
|
|
104
|
+
`Refusing to rewrite goals.json: the ledger records goals the plan projection lacks (${[...missing].join(", ")}). Restore those goals into the newest revisions/ record (keeping plan.revision equal to its file number) before mutating this run.`,
|
|
105
|
+
"ULW_LOOP_PROJECTION_TRUNCATED",
|
|
106
|
+
{ details: { missingGoalIds: [...missing] } },
|
|
107
|
+
);
|
|
80
108
|
}
|
|
81
109
|
export function planExists(repoRoot: string, scope?: UlwLoopScope): boolean {
|
|
82
110
|
const dir = ulwLoopDir(repoRoot, scope);
|