Repository navigation
feat(ledger): add waits history portfolio and read-only panel #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5d62cdf
21beb0f
5334cb4
fd639fd
2d46bfa
5839be4
a3e9aa7
6f06ba8
be0c902
831f77e
31ac0d5
02123a8
ce3a72b
bad9a4e
44fc985
b53efdc
76f406b
17977f8
821b8ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| import { formatThreadLink, parseThreadLinkHref } from "@t3tools/shared/threadLinks"; | ||
| import * as Schema from "effect/Schema"; | ||
| import * as Effect from "effect/Effect"; | ||
| import * as SqlClient from "effect/sql/SqlClient"; | ||
|
|
||
| const linksCodec = Schema.fromJsonString(Schema.Array(Schema.String)); | ||
| const decodeLinks = Schema.decodeUnknownEffect(linksCodec); | ||
| const encodeLinks = Schema.encodeEffect(linksCodec); | ||
|
|
||
| export default Effect.gen(function* () { | ||
| const sql = yield* SqlClient.SqlClient; | ||
| yield* sql`ALTER TABLE work_ledger ADD COLUMN wait_reason TEXT`; | ||
| yield* sql`ALTER TABLE work_ledger ADD COLUMN next_check_at TEXT`; | ||
| yield* sql`ALTER TABLE work_ledger ADD COLUMN last_run_id TEXT`; | ||
| yield* sql`ALTER TABLE work_ledger ADD COLUMN last_run_status TEXT`; | ||
| yield* sql`ALTER TABLE work_ledger ADD COLUMN last_run_at TEXT`; | ||
| yield* sql`UPDATE work_ledger SET wait_reason = CASE | ||
| WHEN blocker GLOB 'WAIT_EXTERNAL*' THEN 'external' | ||
| WHEN blocker GLOB 'WAIT_NATURAL*' THEN 'natural' | ||
| WHEN blocker GLOB 'NEEDS_DECISION*' THEN 'decision' ELSE NULL END`; | ||
| const owners = yield* sql<{ work_key: string; owner_thread_id: string; links_json: string }>` | ||
| SELECT work_key, owner_thread_id, links_json FROM work_ledger WHERE owner_thread_id IS NOT NULL | ||
| `; | ||
| for (const owner of owners) { | ||
| const ref = parseThreadLinkHref(owner.owner_thread_id); | ||
| if (ref === null) continue; | ||
| if (ref.environmentId === null) { | ||
| yield* sql`UPDATE work_ledger SET owner_thread_id = ${ref.threadId} WHERE work_key = ${owner.work_key}`; | ||
| } else { | ||
| const links = yield* decodeLinks(owner.links_json); | ||
| const href = formatThreadLink(ref).slice(1, -1); | ||
| const encodedLinks = yield* encodeLinks([...new Set([...links, href])]); | ||
| yield* sql`UPDATE work_ledger SET owner_thread_id = NULL, | ||
| links_json = ${encodedLinks} WHERE work_key = ${owner.work_key}`; | ||
| } | ||
| } | ||
| yield* sql`CREATE INDEX work_ledger_due_idx ON work_ledger(next_check_at) WHERE next_check_at IS NOT NULL`; | ||
| yield* sql`CREATE TABLE work_ledger_history ( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT, work_key TEXT NOT NULL, | ||
| changed_at TEXT NOT NULL, changed_by TEXT NOT NULL, changes_json TEXT NOT NULL | ||
| )`; | ||
| yield* sql`CREATE INDEX work_ledger_history_key_idx ON work_ledger_history(work_key, id)`; | ||
| // Keep terminal-run metadata in the projection transaction, including recovery and replay. | ||
| for (const operation of ["INSERT", "UPDATE"] as const) { | ||
| yield* sql.unsafe(`CREATE TRIGGER work_ledger_run_${operation.toLowerCase()} | ||
| AFTER ${operation} ON orchestration_v2_projection_runs | ||
| WHEN NEW.status IN ('completed', 'failed', 'cancelled', 'interrupted', 'rolled_back') | ||
| AND CASE WHEN json_valid(NEW.payload_json) | ||
| THEN json_extract(NEW.payload_json, '$.clientRequestId') IS NOT NULL ELSE 0 END | ||
| BEGIN | ||
| UPDATE work_ledger SET last_run_id = NEW.run_id, last_run_status = NEW.status, | ||
| last_run_at = COALESCE(NEW.completed_at, NEW.requested_at), | ||
| updated_at = MAX(updated_at, COALESCE(NEW.completed_at, NEW.requested_at)) | ||
| WHERE work_key = json_extract(NEW.payload_json, '$.clientRequestId') | ||
| AND (last_run_at IS NULL OR last_run_at <= COALESCE(NEW.completed_at, NEW.requested_at)); | ||
|
Comment on lines
+51
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛡️ Codex Security Review
When Linked environments is enabled and a peer is paired below full access but above read-only, its agent can read ledger keys and launch a fenced thread with Useful? React with 👍 / 👎.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed: a restricted linked caller can correlate a terminal run by a known ledger key. Tracked as #39 with the caller/item authorization boundary and regression requirement. This P3 advisory does not change work status and is a separate authorization follow-up; no claim that clientRequestId proves ledger ownership. |
||
| END`); | ||
| } | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.