diff --git a/apps/server/src/environment/ServerEnvironment.ts b/apps/server/src/environment/ServerEnvironment.ts index 6ee059e06..919e4b30e 100644 --- a/apps/server/src/environment/ServerEnvironment.ts +++ b/apps/server/src/environment/ServerEnvironment.ts @@ -214,6 +214,7 @@ export const make = Effect.gen(function* () { serverVersion: packageJson.version, orchestrationProtocolVersion: ORCHESTRATION_PROTOCOL_VERSION, capabilities: { + workQueueMetadata: true, repositoryIdentity: true, connectionProbe: true, worktreeDefaultBase: true, diff --git a/apps/server/src/jones/http/registration.ts b/apps/server/src/jones/http/registration.ts index 239638e95..ed427bc43 100644 --- a/apps/server/src/jones/http/registration.ts +++ b/apps/server/src/jones/http/registration.ts @@ -1,5 +1,7 @@ import * as ProjectionStoreV2 from "../../orchestration-v2/ProjectionStore.ts"; import * as ServerEnvironment from "../../environment/ServerEnvironment.ts"; +import * as WorkQueueMetadataService from "../workQueueMetadata/WorkQueueMetadataService.ts"; +import { workQueueMetadataHttpApiLayer } from "../workQueueMetadata/http.ts"; import * as Layer from "effect/Layer"; import { conversationLibraryHttpApiLayer } from "../../conversations/http.ts"; import * as HostStatus from "../../hostStatus/HostStatus.ts"; @@ -13,6 +15,9 @@ export { voiceReviewResponseHeadersLayer } from "../../voiceReview/http.ts"; export const provideConversationAndVoiceReview = (api: Layer.Layer) => api.pipe( + Layer.provide( + workQueueMetadataHttpApiLayer.pipe(Layer.provide(WorkQueueMetadataService.layer)), + ), Layer.provide(conversationLibraryHttpApiLayer), Layer.provide( voiceReviewHttpApiLayer.pipe( diff --git a/apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.test.ts b/apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.test.ts new file mode 100644 index 000000000..29d4e75b3 --- /dev/null +++ b/apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.test.ts @@ -0,0 +1,317 @@ +// @effect-diagnostics nodeBuiltinImport:off preferSchemaOverJson:off - Synthetic native JSON fixtures exercise malformed shapes, exact digest bytes, descriptor permissions, symlink rejection, and scoped cleanup at the reader boundary. +import { describe, expect, it } from "@effect/vitest"; +import { vi } from "vite-plus/test"; +import * as Effect from "effect/Effect"; +import * as Clock from "effect/Clock"; +import * as Fiber from "effect/Fiber"; +import * as Schema from "effect/Schema"; +import * as TestClock from "effect/testing/TestClock"; +import * as NodeCrypto from "node:crypto"; +import * as NodeFSP from "node:fs/promises"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; +import { WorkQueueMetadata, WORK_QUEUE_METADATA_MAX_BYTES } from "@t3tools/contracts"; +import * as Service from "./WorkQueueMetadataService.ts"; +import { workQueueMetadataConfig, type WorkQueueMetadataConfig } from "./config.ts"; + +const decodeMetadata = Schema.decodeUnknownSync(WorkQueueMetadata); +const sampledNow = 100_000; +const source = { + queue_id: "queue", + host_id: "host", + environment_ref: "environment", + exporter_instance_id: "exporter", +}; +function artifact(change: Record = {}) { + const value = { + schema: "codex.t3-work-queue-metadata/v1", + source, + observed_at_ms: sampledNow - 10, + coverage: "complete", + items: [], + authority_effect: "none", + ...change, + }; + const canonical = (input: unknown): string => + Array.isArray(input) + ? `[${input.map(canonical).join(",")}]` + : input !== null && typeof input === "object" + ? `{${Object.entries(input) + .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)) + .map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`) + .join(",")}}` + : JSON.stringify(input); + return { + ...value, + snapshot_token: NodeCrypto.createHash("sha256").update(canonical(value)).digest("hex"), + }; +} +const read = (config: WorkQueueMetadataConfig) => + Effect.flatMap(Service.WorkQueueMetadataService, (service) => service.snapshot).pipe( + Effect.provide(Service.layerWithConfig(config)), + ); +const fixture = ( + run: (path: string, config: WorkQueueMetadataConfig) => Effect.Effect, +) => + Effect.acquireUseRelease( + Effect.promise(() => + NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "work-queue-metadata-test-")), + ), + (directory) => + Effect.gen(function* () { + yield* TestClock.setTime(sampledNow); + const path = NodePath.join(directory, "snapshot.json"); + return yield* run(path, { status: "configured", path, source, maxAgeMs: 30_000 }); + }), + (directory) => Effect.promise(() => NodeFSP.rm(directory, { recursive: true, force: true })), + ); + +describe("configured queue metadata service", () => { + it.effect.each([ + [1_500, "ready"], + [500, "stale"], + ] as const)( + "classifies observation %i using the clock after asynchronous artifact reading", + ([observedAt, status]) => + fixture((path) => + Effect.gen(function* () { + const value = artifact({ observed_at_ms: observedAt }); + yield* Effect.promise(() => + NodeFSP.writeFile(path, JSON.stringify(value), { mode: 0o600 }), + ); + let now = 1_000; + const baseClock = yield* Clock.Clock; + const clock: Clock.Clock = { + ...baseClock, + currentTimeMillisUnsafe: () => now, + currentTimeMillis: Effect.sync(() => now), + }; + const result = yield* read({ status: "configured", path, source, maxAgeMs: 1_000 }).pipe( + Effect.provideService(Clock.Clock, clock), + Effect.forkChild({ startImmediately: true }), + ); + now = 2_000; + expect(yield* Fiber.join(result)).toEqual({ + status, + snapshot: value, + expires_at_ms: observedAt + 1_000, + }); + }), + ), + ); + it.effect( + "validates the exact Python producer bytes and digest while reporting their sample as stale", + () => + fixture((path) => + Effect.gen(function* () { + const bytes = yield* Effect.promise(() => + NodeFSP.readFile( + new URL( + "../../../../../packages/contracts/src/jones/fixtures/work_queue_metadata_v1.json.fixture", + import.meta.url, + ), + ), + ); + const snapshot = decodeMetadata(JSON.parse(bytes.toString("utf8"))); + yield* Effect.promise(() => NodeFSP.writeFile(path, bytes, { mode: 0o600 })); + expect( + yield* read({ status: "configured", path, source: snapshot.source, maxAgeMs: 30_000 }), + ).toEqual({ + status: "stale", + snapshot, + expires_at_ms: 30_100, + }); + }), + ), + ); + it.effect("returns explicit unconfigured and invalid configuration outcomes", () => + Effect.gen(function* () { + expect(yield* read(workQueueMetadataConfig({}))).toEqual({ + status: "unconfigured", + reason: "not_configured", + }); + for (const env of [ + { T3CODE_WORK_QUEUE_METADATA_PATH: "relative" }, + { + T3CODE_WORK_QUEUE_METADATA_PATH: "/synthetic", + T3CODE_WORK_QUEUE_METADATA_MAX_AGE_MS: "NaN", + }, + ]) + expect(yield* read(workQueueMetadataConfig(env))).toEqual({ + status: "unavailable", + reason: "invalid_configuration", + }); + }), + ); + it.effect( + "distinguishes complete, partial and stale samples and preserves observation identity", + () => + fixture((path, config) => + Effect.gen(function* () { + for (const [change, status] of [ + [{}, "ready"], + [{ coverage: "partial" }, "partial"], + [{ observed_at_ms: sampledNow - 60_000 }, "stale"], + ] as const) { + const value = artifact(change); + yield* Effect.promise(() => + NodeFSP.writeFile(path, JSON.stringify(value), { mode: 0o600 }), + ); + expect(yield* read(config)).toMatchObject({ + status, + snapshot: value, + expires_at_ms: value.observed_at_ms + 30_000, + }); + } + }), + ), + ); + it.effect("rejects wrong source, future sample, corrupted digest and private metadata", () => + fixture((path, config) => + Effect.gen(function* () { + for (const [value, reason] of [ + [artifact({ source: { ...source, host_id: "other" } }), "source_mismatch"], + [artifact({ observed_at_ms: sampledNow + 60_000 }), "future_sample"], + [{ ...artifact(), snapshot_token: "0".repeat(64) }, "invalid_artifact"], + [artifact({ prompt: "private text" }), "invalid_artifact"], + ] as const) { + yield* Effect.promise(() => + NodeFSP.writeFile(path, JSON.stringify(value), { mode: 0o600 }), + ); + expect(yield* read(config)).toEqual({ status: "unavailable", reason }); + } + }), + ), + ); + it.effect( + "never treats missing, malformed, oversized, symlinked or permissive files as an empty queue", + () => + fixture((path, config) => + Effect.gen(function* () { + expect(yield* read(config)).toEqual({ + status: "unavailable", + reason: "source_unavailable", + }); + yield* Effect.promise(() => NodeFSP.writeFile(path, "not json", { mode: 0o600 })); + expect(yield* read(config)).toEqual({ + status: "unavailable", + reason: "invalid_artifact", + }); + yield* Effect.promise(() => NodeFSP.writeFile(path, Buffer.from([0xff, 0xfe]))); + expect(yield* read(config)).toEqual({ + status: "unavailable", + reason: "invalid_artifact", + }); + yield* Effect.promise(() => + NodeFSP.writeFile(path, Buffer.alloc(WORK_QUEUE_METADATA_MAX_BYTES + 1)), + ); + expect(yield* read(config)).toEqual({ + status: "unavailable", + reason: "source_unavailable", + }); + yield* Effect.promise(() => NodeFSP.writeFile(path, JSON.stringify(artifact()))); + yield* Effect.promise(() => NodeFSP.chmod(path, 0o644)); + expect(yield* read(config)).toEqual({ + status: "unavailable", + reason: "source_unavailable", + }); + yield* Effect.promise(() => NodeFSP.chmod(path, 0o600)); + const link = `${path}.link`; + yield* Effect.promise(() => NodeFSP.symlink(path, link)); + expect( + yield* read({ status: "configured", path: link, source, maxAgeMs: 30_000 }), + ).toEqual({ status: "unavailable", reason: "source_unavailable" }); + const uid = process.getuid?.(); + if (uid !== undefined) { + yield* Effect.acquireUseRelease( + Effect.sync(() => vi.spyOn(process, "getuid").mockReturnValue(uid + 1)), + () => + read(config).pipe( + Effect.tap((result) => + Effect.sync(() => { + expect(result).toEqual({ + status: "unavailable", + reason: "source_unavailable", + }); + }), + ), + ), + (owner) => Effect.sync(() => owner.mockRestore()), + ); + } + const directory = `${path}.directory`; + yield* Effect.promise(() => NodeFSP.mkdir(directory)); + const directoryLink = `${path}.directory-link`; + yield* Effect.promise(() => NodeFSP.symlink(directory, directoryLink)); + yield* Effect.promise(() => + NodeFSP.writeFile( + NodePath.join(directory, "snapshot.json"), + JSON.stringify(artifact()), + { mode: 0o600 }, + ), + ); + expect( + yield* read({ + status: "configured", + path: NodePath.join(directoryLink, "snapshot.json"), + source, + maxAgeMs: 30_000, + }), + ).toEqual({ status: "unavailable", reason: "source_unavailable" }); + expect( + yield* read({ status: "configured", path: directory, source, maxAgeMs: 30_000 }), + ).toEqual({ status: "unavailable", reason: "source_unavailable" }); + const response = JSON.stringify( + yield* read({ + status: "configured", + path: `${path}.missing`, + source, + maxAgeMs: 30_000, + }), + ); + expect(response).not.toContain(path); + }), + ), + ); +}); + +describe("queue metadata configuration bounds", () => { + const configured = { + T3CODE_WORK_QUEUE_METADATA_PATH: "/synthetic/snapshot.json", + T3CODE_WORK_QUEUE_METADATA_QUEUE_ID: "queue", + T3CODE_WORK_QUEUE_METADATA_HOST_ID: "host", + T3CODE_WORK_QUEUE_METADATA_ENVIRONMENT_REF: "environment", + T3CODE_WORK_QUEUE_METADATA_EXPORTER_INSTANCE_ID: "exporter", + }; + it("uses the exact source environment names and the 30 second default", () => { + expect(workQueueMetadataConfig(configured)).toEqual({ + status: "configured", + path: "/synthetic/snapshot.json", + source, + maxAgeMs: 30_000, + }); + expect(workQueueMetadataConfig({})).toEqual({ status: "unconfigured" }); + for (const name of Object.keys(configured)) { + const missing: Record = { ...configured, [name]: undefined }; + expect(workQueueMetadataConfig(missing)).toEqual({ status: "invalid" }); + } + }); + it("accepts inclusive 1 millisecond and 24 hour bounds and rejects invalid ages", () => { + for (const age of ["1", "86400000"]) { + expect( + workQueueMetadataConfig({ + ...configured, + T3CODE_WORK_QUEUE_METADATA_MAX_AGE_MS: age, + }), + ).toMatchObject({ status: "configured", maxAgeMs: Number(age) }); + } + for (const age of ["", "0", "-1", "1.5", "01", "NaN", "86400001", "9007199254740992"]) { + expect( + workQueueMetadataConfig({ + ...configured, + T3CODE_WORK_QUEUE_METADATA_MAX_AGE_MS: age, + }), + ).toEqual({ status: "invalid" }); + } + }); +}); diff --git a/apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.ts b/apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.ts new file mode 100644 index 000000000..8adf9c939 --- /dev/null +++ b/apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.ts @@ -0,0 +1,139 @@ +// @effect-diagnostics nodeBuiltinImport:off - The native descriptor boundary requires O_NOFOLLOW, descriptor ownership and identity checks, and bounded reads from that same descriptor. +import { + WorkQueueMetadata, + WORK_QUEUE_METADATA_MAX_BYTES, + type WorkQueueMetadataResult, +} from "@t3tools/contracts"; +import * as NodeFS from "node:fs"; +import * as NodeFSP from "node:fs/promises"; +import * as NodePath from "node:path"; +import * as NodeCrypto from "node:crypto"; +import * as Clock from "effect/Clock"; +import * as Context from "effect/Context"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as Schema from "effect/Schema"; +import { workQueueMetadataConfig, type WorkQueueMetadataConfig } from "./config.ts"; + +export class WorkQueueMetadataService extends Context.Service< + WorkQueueMetadataService, + { + readonly snapshot: Effect.Effect; + } +>()("t3/jones/workQueueMetadata/WorkQueueMetadataService") {} + +const decodeMetadata = Schema.decodeUnknownSync(WorkQueueMetadata); + +function canonical(value: unknown): string { + if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; + if (value !== null && typeof value === "object") + return `{${Object.entries(value) + .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)) + .map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`) + .join(",")}}`; + return JSON.stringify(value); +} + +async function read( + config: WorkQueueMetadataConfig, + currentTimeMillis: () => number, +): Promise { + if (config.status === "unconfigured") return { status: "unconfigured", reason: "not_configured" }; + if (config.status !== "configured") + return { status: "unavailable", reason: "invalid_configuration" }; + let bytes: Uint8Array; + try { + // Reject symlinked ancestors as well as the final component; never follow an alternate source. + if ((await NodeFSP.realpath(config.path)) !== NodePath.resolve(config.path)) + throw new Error("Invalid artifact"); + const before = await NodeFSP.lstat(config.path); + const handle = await NodeFSP.open( + config.path, + NodeFS.constants.O_RDONLY | NodeFS.constants.O_NOFOLLOW | NodeFS.constants.O_NONBLOCK, + ); + try { + const opened = await handle.stat(); + const uid = process.getuid?.(); + if ( + !opened.isFile() || + uid === undefined || + opened.uid !== uid || + (opened.mode & 0o077) !== 0 || + opened.nlink !== 1 || + opened.size > WORK_QUEUE_METADATA_MAX_BYTES || + opened.dev !== before.dev || + opened.ino !== before.ino + ) + throw new Error("Invalid artifact"); + const buffer = Buffer.alloc(WORK_QUEUE_METADATA_MAX_BYTES + 1); + let length = 0; + while (length < buffer.length) { + const result = await handle.read(buffer, length, buffer.length - length, length); + if (result.bytesRead === 0) break; + length += result.bytesRead; + } + const after = await handle.stat(); + const current = await NodeFSP.lstat(config.path); + if ( + length > WORK_QUEUE_METADATA_MAX_BYTES || + length !== opened.size || + after.size !== opened.size || + after.mtimeMs !== opened.mtimeMs || + after.ctimeMs !== opened.ctimeMs || + current.dev !== opened.dev || + current.ino !== opened.ino || + current.size !== opened.size || + current.mtimeMs !== opened.mtimeMs || + current.ctimeMs !== opened.ctimeMs || + current.isSymbolicLink() + ) + throw new Error("Invalid artifact"); + if ((await NodeFSP.realpath(config.path)) !== NodePath.resolve(config.path)) + throw new Error("Invalid artifact"); + bytes = buffer.subarray(0, length); + } finally { + await handle.close(); + } + } catch { + return { status: "unavailable", reason: "source_unavailable" }; + } + let snapshot: WorkQueueMetadata; + try { + snapshot = decodeMetadata(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)), { + onExcessProperty: "error", + }); + const { snapshot_token, ...unsigned } = snapshot; + if ( + NodeCrypto.createHash("sha256").update(canonical(unsigned), "utf8").digest("hex") !== + snapshot_token + ) + throw new Error("Invalid digest"); + } catch { + return { status: "unavailable", reason: "invalid_artifact" }; + } + if ( + Object.entries(config.source).some( + ([key, value]) => snapshot.source[key as keyof typeof snapshot.source] !== value, + ) + ) + return { status: "unavailable", reason: "source_mismatch" }; + const now = currentTimeMillis(); + if (snapshot.observed_at_ms > now) return { status: "unavailable", reason: "future_sample" }; + const expires_at_ms = snapshot.observed_at_ms + config.maxAgeMs; + if (!Number.isSafeInteger(expires_at_ms)) + return { status: "unavailable", reason: "invalid_artifact" }; + return { + status: now >= expires_at_ms ? "stale" : snapshot.coverage === "partial" ? "partial" : "ready", + snapshot, + expires_at_ms, + }; +} + +export const layerWithConfig = (config: WorkQueueMetadataConfig) => + Layer.succeed(WorkQueueMetadataService, { + snapshot: Effect.gen(function* () { + const clock = yield* Clock.Clock; + return yield* Effect.promise(() => read(config, () => clock.currentTimeMillisUnsafe())); + }), + }); +export const layer = Layer.suspend(() => layerWithConfig(workQueueMetadataConfig(process.env))); diff --git a/apps/server/src/jones/workQueueMetadata/config.ts b/apps/server/src/jones/workQueueMetadata/config.ts new file mode 100644 index 000000000..fcab12df6 --- /dev/null +++ b/apps/server/src/jones/workQueueMetadata/config.ts @@ -0,0 +1,47 @@ +// @effect-diagnostics nodeBuiltinImport:off - Server-only configuration must validate absolute paths using the same native path semantics as the artifact reader. +import { WorkQueueMetadataSource } from "@t3tools/contracts"; +import * as Schema from "effect/Schema"; +import * as NodePath from "node:path"; + +export type WorkQueueMetadataConfig = + | { readonly status: "unconfigured" | "invalid" } + | { + readonly status: "configured"; + readonly path: string; + readonly source: WorkQueueMetadataSource; + readonly maxAgeMs: number; + }; + +const decodeSource = Schema.decodeUnknownSync(WorkQueueMetadataSource); + +export function workQueueMetadataConfig( + env: Readonly>, +): WorkQueueMetadataConfig { + const names = [ + "PATH", + "QUEUE_ID", + "HOST_ID", + "ENVIRONMENT_REF", + "EXPORTER_INSTANCE_ID", + "MAX_AGE_MS", + ] as const; + const values = names.map((name) => env[`T3CODE_WORK_QUEUE_METADATA_${name}`]); + if (values.every((value) => value === undefined)) return { status: "unconfigured" }; + const [path, queue_id, host_id, environment_ref, exporter_instance_id, age] = values; + try { + if (!path || !NodePath.isAbsolute(path) || (age !== undefined && !/^[1-9][0-9]*$/.test(age))) + return { status: "invalid" }; + const maxAgeMs = age === undefined ? 30_000 : Number(age); + if (!Number.isSafeInteger(maxAgeMs) || maxAgeMs < 1 || maxAgeMs > 86_400_000) + return { status: "invalid" }; + const source = decodeSource({ + queue_id, + host_id, + environment_ref, + exporter_instance_id, + }); + return { status: "configured", path, source, maxAgeMs }; + } catch { + return { status: "invalid" }; + } +} diff --git a/apps/server/src/jones/workQueueMetadata/http.test.ts b/apps/server/src/jones/workQueueMetadata/http.test.ts new file mode 100644 index 000000000..8fbcf4595 --- /dev/null +++ b/apps/server/src/jones/workQueueMetadata/http.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from "vite-plus/test"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as HttpApi from "effect/unstable/httpapi/HttpApi"; +import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder"; +import * as HttpRouter from "effect/unstable/http/HttpRouter"; +import * as HttpServer from "effect/unstable/http/HttpServer"; +import { + AuthSessionId, + EnvironmentAuthenticatedAuth, + EnvironmentAuthenticatedPrincipal, + EnvironmentAuthInvalidError, + EnvironmentWorkQueueMetadataHttpApi, + type AuthEnvironmentScope, +} from "@t3tools/contracts"; +import { workQueueMetadataHttpApiLayer } from "./http.ts"; +import * as Service from "./WorkQueueMetadataService.ts"; + +function fixture(scopes: AuthEnvironmentScope[] | null) { + let reads = 0; + const auth = Layer.succeed(EnvironmentAuthenticatedAuth, (effect) => + scopes === null + ? Effect.fail( + new EnvironmentAuthInvalidError({ + code: "auth_invalid", + reason: "missing_credential", + traceId: "fixture", + }), + ) + : effect.pipe( + Effect.provideService(EnvironmentAuthenticatedPrincipal, { + sessionId: AuthSessionId.make("fixture"), + subject: "fixture", + method: "bearer-access-token", + scopes: new Set(scopes), + }), + ), + ); + const service = Layer.succeed(Service.WorkQueueMetadataService, { + snapshot: Effect.sync(() => { + reads++; + return { status: "unconfigured" as const, reason: "not_configured" as const }; + }), + }); + const routes = HttpApiBuilder.layer( + HttpApi.make("environment").add(EnvironmentWorkQueueMetadataHttpApi), + ).pipe( + Layer.provide(workQueueMetadataHttpApiLayer.pipe(Layer.provide(service))), + Layer.provide(auth), + Layer.provide(HttpServer.layerServices), + ); + return { ...HttpRouter.toWebHandler(routes), reads: () => reads }; +} + +describe("queue metadata HTTP authority", () => { + it.each([ + [null, 401], + [[], 403], + [["orchestration:read"], 200], + ] as const)( + "enforces authentication and read scope before reading: %j", + async (scopes, status) => { + const server = fixture(scopes === null ? null : [...scopes]); + try { + const result = await server.handler( + new Request("http://localhost/api/work-queue/metadata"), + ); + expect(result.status).toBe(status); + expect(server.reads()).toBe(status === 200 ? 1 : 0); + if (status === 200) { + expect(await result.json()).toEqual({ status: "unconfigured", reason: "not_configured" }); + expect(result.headers.get("cache-control")).toBe("no-store"); + } + } finally { + await server.dispose(); + } + }, + ); + it("has no mutation operation or caller-selected source", async () => { + const server = fixture(["orchestration:read"]); + try { + const result = await server.handler( + new Request("http://localhost/api/work-queue/metadata?path=/private&host_id=other"), + ); + expect(result.status).toBe(200); + expect(await result.json()).toEqual({ status: "unconfigured", reason: "not_configured" }); + expect( + ( + await server.handler( + new Request("http://localhost/api/work-queue/metadata", { method: "POST" }), + ) + ).status, + ).toBe(404); + expect(server.reads()).toBe(1); + } finally { + await server.dispose(); + } + }); +}); diff --git a/apps/server/src/jones/workQueueMetadata/http.ts b/apps/server/src/jones/workQueueMetadata/http.ts new file mode 100644 index 000000000..e8265ef78 --- /dev/null +++ b/apps/server/src/jones/workQueueMetadata/http.ts @@ -0,0 +1,25 @@ +import { AuthOrchestrationReadScope, EnvironmentHttpApi } from "@t3tools/contracts"; +import * as Effect from "effect/Effect"; +import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder"; +import * as HttpEffect from "effect/unstable/http/HttpEffect"; +import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; +import { requireEnvironmentScope } from "../../auth/http.ts"; +import * as WorkQueueMetadataService from "./WorkQueueMetadataService.ts"; + +export const workQueueMetadataHttpApiLayer = HttpApiBuilder.group( + EnvironmentHttpApi, + "workQueueMetadata", + Effect.fnUntraced(function* (handlers) { + const service = yield* WorkQueueMetadataService.WorkQueueMetadataService; + return handlers.handle( + "snapshot", + Effect.fn("workQueueMetadata.snapshot")(function* () { + yield* requireEnvironmentScope(AuthOrchestrationReadScope); + yield* HttpEffect.appendPreResponseHandler((_request, response) => + Effect.succeed(HttpServerResponse.setHeader(response, "cache-control", "no-store")), + ); + return yield* service.snapshot; + }), + ); + }), +); diff --git a/apps/web/src/components/workQueue/WorkQueuePage.test.tsx b/apps/web/src/components/workQueue/WorkQueuePage.test.tsx index df11730ec..a11ab41dd 100644 --- a/apps/web/src/components/workQueue/WorkQueuePage.test.tsx +++ b/apps/web/src/components/workQueue/WorkQueuePage.test.tsx @@ -2,6 +2,11 @@ import { act } from "react"; import { createRoot, type Root } from "react-dom/client"; import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; +import type { WorkQueueMetadata, WorkQueueMetadataResult } from "@t3tools/contracts"; +import { + WorkQueueMetadataPanel, + type WorkQueueMetadataLoader, +} from "../../jones/workQueue/WorkQueueMetadataPanel"; import { WorkQueuePanel } from "./WorkQueuePanel"; import { WorkQueuePreview } from "./WorkQueuePreview"; import { @@ -484,3 +489,247 @@ describe("mock pause clock", () => { }); }); }); + +describe("submitted work metadata", () => { + let root: Root; + let container: HTMLDivElement; + const sample = (): WorkQueueMetadata => ({ + schema: "codex.t3-work-queue-metadata/v1", + source: { + queue_id: "queue", + host_id: "host", + environment_ref: "environment", + exporter_instance_id: "worker", + }, + observed_at_ms: Date.now(), + snapshot_token: "a".repeat(64), + coverage: "complete", + authority_effect: "none", + items: [ + { + request_id: "request-legacy", + workstream_id: "raw-legacy-id", + canonical_binding: null, + entry_kind: "ordinary", + request_kind: "initial", + lane: "normal", + queue_state: "unknown", + submitted_at_ms: null, + target: null, + dispatch_status: "unknown", + native_command_status: null, + finish_line: "not_tracked", + }, + { + request_id: "request-canonical", + workstream_id: "exact-canonical-id", + canonical_binding: { + owner_id: "owner", + server_generation: 1, + registry_version: 2, + membership_id: "membership", + native_reference_id: "reference", + source_instance_id: "instance", + native_thread_id: "thread", + authority_namespace: "namespace", + store_generation: 1, + expires_at: "2099-01-01T00:00:00Z", + }, + entry_kind: "flexible", + request_kind: "owner_followup", + lane: "high", + queue_state: "observed_terminal", + submitted_at_ms: null, + target: { host_id: "host", environment_ref: "environment", thread_id: "thread" }, + dispatch_status: "accepted", + native_command_status: "accepted", + finish_line: "not_tracked", + }, + ], + }); + beforeEach(() => { + vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); + container = document.createElement("div"); + document.body.append(container); + root = createRoot(container); + }); + afterEach(async () => { + await act(() => root.unmount()); + container.remove(); + vi.useRealTimers(); + vi.unstubAllGlobals(); + }); + async function render(load: WorkQueueMetadataLoader, key = "environment-a") { + await act(() => root.render()); + } + const ready = (): WorkQueueMetadataResult => ({ + status: "ready", + snapshot: sample(), + expires_at_ms: Date.now() + 60_000, + }); + it("shows raw and canonical identities, unknown outcomes and no mutation controls", async () => { + await render(async () => ready()); + const text = container.textContent; + for (const value of [ + "raw-legacy-id", + "exact-canonical-id", + "Legacy / unverified", + "Canonical binding verified at sample", + "membership", + "observed_terminal", + "Dispatch: unknown", + "Native command: Not observed", + "Not tracked", + "does not mean completed", + "Source: queue", + "Sampled", + ]) { + expect(text).toContain(value); + } + expect(container.querySelector("textarea,input")).toBeNull(); + expect([...container.querySelectorAll("button")].map((button) => button.textContent)).toEqual([ + "Refresh metadata", + ]); + }); + it.each(["partial", "stale"] as const)( + "keeps %s rows visible with their sample status", + async (status) => { + await render(async () => ({ + status, + snapshot: { ...sample(), coverage: "partial" }, + expires_at_ms: Date.now() + 60_000, + })); + expect(container.textContent).toContain( + status === "stale" ? "Stale sample" : "Partial sample", + ); + expect(container.textContent).toContain("Coverage: partial"); + expect(container.textContent).toContain("request-legacy"); + }, + ); + it.each([ + { status: "unconfigured", reason: "not_configured" }, + { status: "unavailable", reason: "future_sample" }, + { status: "unavailable", reason: "source_unavailable" }, + ] satisfies WorkQueueMetadataResult[])( + "does not turn $reason into an empty queue", + async (result) => { + await render(async () => result); + expect(container.textContent).toContain( + result.status === "unconfigured" + ? "no source is configured" + : result.reason === "future_sample" + ? "the sample timestamp is in the future" + : "the source cannot be read", + ); + expect(container.textContent).not.toContain("No submitted work"); + expect(container.querySelector("table")).toBeNull(); + }, + ); + it("marks a cached sample stale when its validity expires without polling", async () => { + vi.useFakeTimers(); + const load = vi.fn(async () => ready()); + await render(load); + expect(container.textContent).toContain("Ready sample"); + await act(() => vi.advanceTimersByTime(60_000)); + expect(container.textContent).toContain("Stale sample"); + expect(load).toHaveBeenCalledTimes(1); + }); + it("clears old data, aborts the old environment read and ignores its late result", async () => { + const oldRead = deferred(); + let oldSignal: AbortSignal | undefined; + await render((signal) => { + oldSignal = signal; + return oldRead.promise; + }); + await render( + async () => ({ status: "unconfigured", reason: "not_configured" }), + "environment-b", + ); + expect(oldSignal?.aborted).toBe(true); + await act(() => oldRead.resolve(ready())); + expect(container.textContent).toContain("no source is configured"); + expect(container.textContent).not.toContain("request-legacy"); + }); + it("refreshes explicitly and reports a failed read without displaying old rows as current", async () => { + const load = vi + .fn() + .mockResolvedValueOnce(ready()) + .mockRejectedValueOnce(new Error("offline")); + await render(load); + expect(container.textContent).toContain("request-legacy"); + await act(() => container.querySelector("button")!.click()); + expect(container.textContent).toContain("Queue metadata unavailable"); + expect(container.textContent).not.toContain("request-legacy"); + expect(container.textContent).not.toContain("No submitted work"); + }); +}); + +describe("submitted work page integration", () => { + const mockedModules = [ + "../../state/environments", + "../../jones/workQueue/useWorkQueueMetadata", + "@tanstack/react-router", + "../WorkspacePageHeader", + "../WorkspacePageContainer", + "../ui/sidebar", + ]; + let root: Root; + let container: HTMLDivElement; + beforeEach(() => { + vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); + container = document.createElement("div"); + document.body.append(container); + root = createRoot(container); + }); + afterEach(async () => { + await act(() => root.unmount()); + container.remove(); + for (const path of mockedModules) vi.doUnmock(path); + vi.unstubAllGlobals(); + }); + it.each([false, true])( + "negotiates metadata capability=%s while keeping the synthetic preview separate", + async (supported) => { + vi.resetModules(); + const load = vi.fn(async () => ({ + status: "unconfigured", + reason: "not_configured", + })); + const environment = { + environmentId: "fixture-environment", + label: "Fixture environment", + entry: { enabled: true }, + connection: { phase: "connected" }, + serverConfig: { + environment: { capabilities: supported ? { workQueueMetadata: true } : {} }, + }, + }; + vi.doMock("../../state/environments", () => ({ + useEnvironments: () => ({ environments: [environment] }), + usePrimaryEnvironmentId: () => environment.environmentId, + })); + vi.doMock("../../jones/workQueue/useWorkQueueMetadata", () => ({ + useWorkQueueMetadata: () => load, + })); + vi.doMock("@tanstack/react-router", () => ({ + useBlocker: () => ({ status: "idle" }), + })); + const wrapper = ({ children }: { children: import("react").ReactNode }) => ( +
{children}
+ ); + vi.doMock("../WorkspacePageHeader", () => ({ WorkspacePageHeader: wrapper })); + vi.doMock("../WorkspacePageContainer", () => ({ WorkspacePageContainer: wrapper })); + vi.doMock("../ui/sidebar", () => ({ SidebarInset: wrapper })); + const { WorkQueuePage } = await import("./WorkQueuePage"); + await act(() => root.render()); + expect(container.querySelector('[aria-label="Mock queue preview"]')?.textContent).toContain( + "Synthetic preview data is separate", + ); + expect(load).toHaveBeenCalledTimes(supported ? 1 : 0); + expect(container.querySelector('[aria-label="Queue metadata"]') !== null).toBe(supported); + expect(container.textContent).toContain( + supported ? "no source is configured" : "Queue metadata unsupported by this environment", + ); + }, + ); +}); diff --git a/apps/web/src/components/workQueue/WorkQueuePage.tsx b/apps/web/src/components/workQueue/WorkQueuePage.tsx index 2ab90f96a..d51b28c39 100644 --- a/apps/web/src/components/workQueue/WorkQueuePage.tsx +++ b/apps/web/src/components/workQueue/WorkQueuePage.tsx @@ -1,4 +1,8 @@ import { useState } from "react"; +import type { EnvironmentId } from "@t3tools/contracts"; +import { useEnvironments, usePrimaryEnvironmentId } from "../../state/environments"; +import { WorkQueueMetadataPanel } from "../../jones/workQueue/WorkQueueMetadataPanel"; +import { useWorkQueueMetadata } from "../../jones/workQueue/useWorkQueueMetadata"; import { useBlocker } from "@tanstack/react-router"; import { isElectron } from "../../env"; import { WorkspacePageHeader } from "../WorkspacePageHeader"; @@ -10,6 +14,13 @@ import { createMockWorkQueueSource } from "./mockWorkQueue"; import { WorkQueuePanel } from "./WorkQueuePanel"; export function WorkQueuePage() { + const { environments } = useEnvironments(); + const primaryId = usePrimaryEnvironmentId(); + const [requestedId, setRequestedId] = useState(null); + const selected = + environments.find((item) => item.environmentId === requestedId) ?? + environments.find((item) => item.environmentId === primaryId) ?? + environments[0]; const [source] = useState(createMockWorkQueueSource); const [dirty, setDirty] = useState(false); const blocker = useBlocker({ @@ -24,21 +35,66 @@ export function WorkQueuePage() {
- {blocker.status === "blocked" && ( - - Leave this preview? - Your unsaved draft will be discarded. - - - - - + + {!selected ? ( +

No environment is connected.

+ ) : !selected.entry.enabled ? ( +

Queue metadata unsupported: this environment is disabled.

+ ) : selected.connection.phase !== "connected" ? ( +

Queue metadata unavailable: this environment is not connected.

+ ) : selected.serverConfig?.environment.capabilities.workQueueMetadata !== true ? ( +

Queue metadata unsupported by this environment.

+ ) : ( + )} - +
+

Mock queue preview

+

+ Synthetic preview data is separate from the sampled queue metadata above. +

+ {blocker.status === "blocked" && ( + + Leave this preview? + Your unsaved draft will be discarded. + + + + + + )} + +
); } + +function EnvironmentWorkQueue({ environmentId }: { environmentId: EnvironmentId }) { + const load = useWorkQueueMetadata(environmentId); + return ; +} diff --git a/apps/web/src/jones/workQueue/WorkQueueMetadataPanel.tsx b/apps/web/src/jones/workQueue/WorkQueueMetadataPanel.tsx new file mode 100644 index 000000000..42350112f --- /dev/null +++ b/apps/web/src/jones/workQueue/WorkQueueMetadataPanel.tsx @@ -0,0 +1,217 @@ +import type { WorkQueueMetadataResult } from "@t3tools/contracts"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { Button } from "../../components/ui/button"; + +export type WorkQueueMetadataLoader = (signal: AbortSignal) => Promise; +const timeFormatter = new Intl.DateTimeFormat("en-US", { + year: "numeric", + month: "numeric", + day: "numeric", + hour: "numeric", + minute: "2-digit", + second: "2-digit", + timeZone: "America/New_York", + timeZoneName: "short", +}); +const formatTime = (time: number) => timeFormatter.format(time); +const unavailableMessages = { + source_unavailable: "the source cannot be read", + invalid_artifact: "the source data is invalid", + source_mismatch: "the sample does not match the configured source", + future_sample: "the sample timestamp is in the future", + invalid_configuration: "the source configuration is invalid", +}; +const bindingLabels: Record = { + owner_id: "Owner ID", + server_generation: "Server generation", + registry_version: "Registry version", + membership_id: "Membership ID", + native_reference_id: "Native reference ID", + source_instance_id: "Source instance ID", + native_thread_id: "Native thread ID", + authority_namespace: "Authority namespace", + store_generation: "Store generation", + expires_at: "Binding expires at", +}; + +export function WorkQueueMetadataPanel({ load }: { load: WorkQueueMetadataLoader }) { + const [result, setResult] = useState(null); + const [failed, setFailed] = useState(false); + const [loading, setLoading] = useState(true); + const [observedAt, setObservedAt] = useState(Date.now); + const active = useRef(null); + const refresh = useCallback(async () => { + active.current?.abort(); + const controller = new AbortController(); + active.current = controller; + setLoading(true); + setFailed(false); + setResult(null); + try { + const next = await load(controller.signal); + if (!controller.signal.aborted) { + setObservedAt(Date.now()); + setResult(next); + } + } catch { + if (!controller.signal.aborted) setFailed(true); + } finally { + if (!controller.signal.aborted) setLoading(false); + } + }, [load]); + useEffect(() => { + void refresh(); + return () => { + active.current?.abort(); + }; + }, [refresh]); + useEffect(() => { + if (!result || !("snapshot" in result) || result.expires_at_ms <= observedAt) return; + const remaining = result.expires_at_ms - Date.now(); + const timer = window.setTimeout( + () => setObservedAt(Date.now()), + Math.max(0, Math.min(remaining, 2_147_483_647)), + ); + return () => window.clearTimeout(timer); + }, [result, observedAt]); + const snapshot = result && "snapshot" in result ? result.snapshot : null; + const stale = + result?.status === "stale" || + (result !== null && "snapshot" in result && result.expires_at_ms <= observedAt); + return ( +
+
+
+

Submitted work

+

+ Read-only sampled queue metadata. Prompt text is not available. +

+
+ +
+

+ Accepted or delivered does not mean completed work. This view does not track whether the + work is complete. +

+
+ {loading &&

Loading queue metadata…

} + {failed &&

Queue metadata unavailable. Refresh to try again.

} + {result?.status === "unconfigured" && ( +

Queue metadata unsupported: no source is configured.

+ )} + {result?.status === "unavailable" && ( +

Queue metadata unavailable: {unavailableMessages[result.reason]}.

+ )} + {snapshot && ( + <> +

+ {stale + ? "Stale sample" + : result?.status === "partial" + ? "Partial sample" + : "Ready sample"}{" "} + · Coverage: {snapshot.coverage} +

+

Sampled {formatTime(snapshot.observed_at_ms)}

+

+ Source: {snapshot.source.queue_id} · Host: {snapshot.source.host_id} · Environment:{" "} + {snapshot.source.environment_ref} · Exporter: {snapshot.source.exporter_instance_id} +

+ + )} +
+ {snapshot && + (snapshot.items.length === 0 ? ( +

+ {snapshot.coverage === "partial" || stale + ? "No rows in this sample; the current queue may contain work." + : "No submitted work in this sample."} +

+ ) : ( +
+ + + + {[ + "Request / workstream", + "Identity", + "Lane / kinds", + "Queue state", + "Target", + "Dispatch / native command", + "Completion", + ].map((heading) => ( + + ))} + + + + {snapshot.items.map((item) => ( + + + + + + + + + + ))} + +
+ {heading} +
+
{item.request_id}
+
{item.workstream_id}
+
+ Submitted:{" "} + {item.submitted_at_ms === null + ? "Not available" + : formatTime(item.submitted_at_ms)} +
+
+ {item.canonical_binding ? ( + <> +
Canonical binding verified at sample
+ {Object.entries(item.canonical_binding).map(([key, value]) => ( +
+ {bindingLabels[key]}: {value} +
+ ))} + + ) : ( + <> +
Legacy / unverified identity
+
No canonical binding available
+ + )} +
+ {item.lane} +
+ {item.entry_kind} +
+ {item.request_kind} +
{item.queue_state} + {item.target ? ( + <> + {item.target.host_id} +
+ {item.target.environment_ref} +
+ {item.target.thread_id} + + ) : ( + "Not available" + )} +
+ Dispatch: {item.dispatch_status ?? "Not observed"} +
+ Native command: {item.native_command_status ?? "Not observed"} +
Not tracked
+
+ ))} +
+ ); +} diff --git a/apps/web/src/jones/workQueue/useWorkQueueMetadata.ts b/apps/web/src/jones/workQueue/useWorkQueueMetadata.ts new file mode 100644 index 000000000..c45ab318f --- /dev/null +++ b/apps/web/src/jones/workQueue/useWorkQueueMetadata.ts @@ -0,0 +1,50 @@ +import { EnvironmentSupervisor } from "@t3tools/client-runtime/connection"; +import { fetchWorkQueueMetadata } from "@t3tools/client-runtime/work-queue-metadata"; +import * as ManagedRelay from "@t3tools/client-runtime/relay"; +import { createEnvironmentCommand } from "@t3tools/client-runtime/state/runtime"; +import type { EnvironmentId } from "@t3tools/contracts"; +import * as Cause from "effect/Cause"; +import * as Effect from "effect/Effect"; +import * as Option from "effect/Option"; +import * as SubscriptionRef from "effect/SubscriptionRef"; +import { useCallback } from "react"; +import { connectionAtomRuntime } from "../../connection/runtime"; +import { useAtomCommand } from "../../state/use-atom-command"; + +const query = createEnvironmentCommand(connectionAtomRuntime, { + label: "web-work-queue:metadata", + execute: (signal: AbortSignal) => { + const aborted = Effect.callback((resume) => { + const abort = () => resume(Effect.interrupt); + if (signal.aborted) abort(); + else signal.addEventListener("abort", abort, { once: true }); + return Effect.sync(() => signal.removeEventListener("abort", abort)); + }); + return Effect.raceFirst( + Effect.gen(function* () { + if (signal.aborted) return yield* Effect.interrupt; + const supervisor = yield* EnvironmentSupervisor.EnvironmentSupervisor; + const prepared = yield* SubscriptionRef.get(supervisor.prepared); + if (Option.isNone(prepared)) + return yield* Effect.fail({ _tag: "WorkQueueMetadataUnavailableError" }); + const signer = yield* Effect.serviceOption( + ManagedRelay.ManagedRelay.ManagedRelayDpopSigner, + ); + return yield* fetchWorkQueueMetadata({ prepared: prepared.value, signer }); + }), + aborted, + ); + }, +}); + +export function useWorkQueueMetadata(environmentId: EnvironmentId) { + const run = useAtomCommand(query, { reportFailure: false, reportDefect: false }); + return useCallback( + async (signal: AbortSignal) => { + const result = await run({ environmentId, input: signal }); + if (result._tag === "Failure") throw Cause.squash(result.cause); + return result.value; + }, + [environmentId, run], + ); +} diff --git a/packages/client-runtime/package.json b/packages/client-runtime/package.json index 3808deca5..8c9ee6d3a 100644 --- a/packages/client-runtime/package.json +++ b/packages/client-runtime/package.json @@ -386,6 +386,10 @@ "./device/duo-control": { "types": "./src/device/duoControl.ts", "default": "./src/device/duoControl.ts" + }, + "./work-queue-metadata": { + "types": "./src/jones/workQueueMetadata.ts", + "default": "./src/jones/workQueueMetadata.ts" } }, "scripts": { diff --git a/packages/client-runtime/src/jones/workQueueMetadata.test.ts b/packages/client-runtime/src/jones/workQueueMetadata.test.ts new file mode 100644 index 000000000..292d46b18 --- /dev/null +++ b/packages/client-runtime/src/jones/workQueueMetadata.test.ts @@ -0,0 +1,74 @@ +import { expect, it } from "@effect/vitest"; +import { EnvironmentId } from "@t3tools/contracts"; +import * as Effect from "effect/Effect"; +import * as Option from "effect/Option"; +import { RelayConnectionTarget, type PreparedConnection } from "../connection/model.ts"; +import type { ManagedRelayDpopSigner } from "../relay/managedRelay.ts"; +import { remoteHttpClientLayer } from "../rpc/http.ts"; +import { fetchWorkQueueMetadata } from "./workQueueMetadata.ts"; + +function connection(id: string, cookie = false): PreparedConnection { + const environmentId = EnvironmentId.make(id); + return { + environmentId, + label: id, + target: new RelayConnectionTarget({ environmentId, label: id }), + httpBaseUrl: `https://${id}.test`, + socketUrl: `wss://${id}.test/ws`, + httpAuthorization: cookie ? null : { _tag: "Bearer", token: `synthetic-${id}` }, + }; +} +const signer = Option.none(); +it.effect("binds each metadata query to the selected environment and its credentials", () => + Effect.gen(function* () { + const calls: Array<{ url: string; init: RequestInit }> = []; + const layer = remoteHttpClientLayer(async (url, init) => { + calls.push({ url: String(url), init: init ?? {} }); + return Response.json({ status: "unconfigured", reason: "not_configured" }); + }); + for (const id of ["first", "second"]) + expect( + yield* fetchWorkQueueMetadata({ prepared: connection(id), signer }).pipe( + Effect.provide(layer), + ), + ).toEqual({ status: "unconfigured", reason: "not_configured" }); + expect(calls.map(({ url }) => url)).toEqual([ + "https://first.test/api/work-queue/metadata", + "https://second.test/api/work-queue/metadata", + ]); + expect(calls.map(({ init }) => new Headers(init.headers).get("authorization"))).toEqual([ + "Bearer synthetic-first", + "Bearer synthetic-second", + ]); + }), +); +it.effect("retains cookie authentication and rejects private or malformed response fields", () => + Effect.gen(function* () { + const layer = remoteHttpClientLayer(async (_url, init) => { + expect(init?.credentials).toBe("include"); + return Response.json({ + status: "unavailable", + reason: "source_unavailable", + path: "/private", + }); + }); + const error = yield* fetchWorkQueueMetadata({ + prepared: connection("local", true), + signer, + }).pipe(Effect.flip, Effect.provide(layer)); + expect(error._tag).toBe("RemoteEnvironmentAuthInvalidJsonError"); + }), +); +it.effect("keeps old or unreachable servers unavailable without returning mock or empty rows", () => + Effect.gen(function* () { + const layer = remoteHttpClientLayer(async () => new Response("Missing", { status: 404 })); + const error = yield* fetchWorkQueueMetadata({ prepared: connection("old"), signer }).pipe( + Effect.flip, + Effect.provide(layer), + ); + expect(error).toMatchObject({ + _tag: "RemoteEnvironmentAuthUndeclaredStatusError", + status: 404, + }); + }), +); diff --git a/packages/client-runtime/src/jones/workQueueMetadata.ts b/packages/client-runtime/src/jones/workQueueMetadata.ts new file mode 100644 index 000000000..98b0ecdef --- /dev/null +++ b/packages/client-runtime/src/jones/workQueueMetadata.ts @@ -0,0 +1,27 @@ +import * as Effect from "effect/Effect"; +import type * as Option from "effect/Option"; +import { RemoteEnvironmentAuthorization } from "../authorization/service.ts"; +import type { PreparedConnection } from "../connection/model.ts"; +import type { ManagedRelayDpopSigner } from "../relay/managedRelay.ts"; +import { makeEnvironmentHttpApiUrlBuilder } from "../rpc/http.ts"; +import { executeAuthenticatedEnvironmentHttpRequest } from "../state/environmentHttpAuth.ts"; + +export const fetchWorkQueueMetadata = Effect.fn("clientRuntime.environment.workQueueMetadata")( + function* (input: { + readonly prepared: PreparedConnection; + readonly signer: Option.Option; + readonly remoteAuthorization?: Option.Option; + readonly timeoutMs?: number; + }) { + return yield* executeAuthenticatedEnvironmentHttpRequest({ + ...input, + remoteAuthorization: + input.remoteAuthorization ?? (yield* Effect.serviceOption(RemoteEnvironmentAuthorization)), + group: "workQueueMetadata", + method: "GET", + timeoutMs: input.timeoutMs ?? 10_000, + url: (base) => makeEnvironmentHttpApiUrlBuilder(base).workQueueMetadata.snapshot(), + request: ({ client, headers }) => client.snapshot({ headers }), + }); + }, +); diff --git a/packages/contracts/src/environment.ts b/packages/contracts/src/environment.ts index 6571b3de5..441479651 100644 --- a/packages/contracts/src/environment.ts +++ b/packages/contracts/src/environment.ts @@ -112,6 +112,7 @@ export const NativeBootstrapCreationCapability = nativeBootstrapCapabilityStruct export type NativeBootstrapCreationCapability = typeof NativeBootstrapCreationCapability.Type; export const ExecutionEnvironmentCapabilities = Schema.Struct({ + workQueueMetadata: Schema.optionalKey(Schema.Boolean), queueDispatch: Schema.optionalKey(QueueDispatchCapability), threadCorpus: Schema.optionalKey(ThreadCorpusCapability), nativeBootstrapCreation: Schema.optionalKey(NativeBootstrapCreationCapability), diff --git a/packages/contracts/src/environmentHttp.ts b/packages/contracts/src/environmentHttp.ts index f0f7416a5..cce389acc 100644 --- a/packages/contracts/src/environmentHttp.ts +++ b/packages/contracts/src/environmentHttp.ts @@ -693,6 +693,7 @@ class EnvironmentPullRequestsHttpApi extends HttpApiGroup.make("pullRequests").a const { EnvironmentVoiceReviewHttpApi, EnvironmentHostStatusHttpApi, + EnvironmentWorkQueueMetadataHttpApi: WorkQueueMetadataHttpApi, EnvironmentConversationLibraryHttpApi: ConversationLibraryHttpApi, EnvironmentWorkstreamAppearanceHttpApi, } = makeJonesHttpGroups({ @@ -894,11 +895,14 @@ export class ProviderQueueHttpApi extends HttpApiGroup.make("providerQueue") export class EnvironmentConversationLibraryHttpApi extends ConversationLibraryHttpApi {} +export class EnvironmentWorkQueueMetadataHttpApi extends WorkQueueMetadataHttpApi {} + export class EnvironmentHttpApi extends HttpApi.make("environment") .add(ProviderQueueHttpApi) .add(EnvironmentQueueDispatchHttpApi) .add(EnvironmentVoiceReviewHttpApi) .add(EnvironmentHostStatusHttpApi) + .add(EnvironmentWorkQueueMetadataHttpApi) .add(EnvironmentMetadataHttpApi) .add(EnvironmentAuthHttpApi) .add(EnvironmentOrchestrationHttpApi) diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index af5096252..ff0cbc2ec 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -84,3 +84,5 @@ export * from "./hostStatus.ts"; export * from "./jones/workstreamAppearance.ts"; export * from "./jones/organizationMetadata.ts"; + +export * from "./jones/workQueueMetadata.ts"; diff --git a/packages/contracts/src/jones/environmentHttpGroups.test.ts b/packages/contracts/src/jones/environmentHttpGroups.test.ts index cd9ce50fc..2eab72ea6 100644 --- a/packages/contracts/src/jones/environmentHttpGroups.test.ts +++ b/packages/contracts/src/jones/environmentHttpGroups.test.ts @@ -12,6 +12,7 @@ describe("Jones environment HTTP registration", () => { "queueDispatch", "voiceReview", "hostStatus", + "workQueueMetadata", "metadata", "auth", "orchestration", @@ -28,6 +29,7 @@ describe("Jones environment HTTP registration", () => { const routes = [ ...Object.values(groups.voiceReview.endpoints), ...Object.values(groups.hostStatus.endpoints), + ...Object.values(groups.workQueueMetadata.endpoints), ...Object.values(groups.conversationLibrary.endpoints), ...Object.values(groups.workstreams.endpoints), ]; @@ -49,6 +51,7 @@ describe("Jones environment HTTP registration", () => { ["sendNow", "POST", "/api/voice-review/drafts/:id/send-now"], ["delete", "POST", "/api/voice-review/drafts/:id/delete"], ["snapshot", "GET", "/api/host-status"], + ["snapshot", "GET", "/api/work-queue/metadata"], ["conversationLibrary", "POST", "/api/conversation-library"], ["appearanceRead", "POST", "/api/workstreams/appearance/read"], ["appearanceSave", "POST", "/api/workstreams/appearance/write"], diff --git a/packages/contracts/src/jones/environmentHttpGroups.ts b/packages/contracts/src/jones/environmentHttpGroups.ts index 17e583d41..bffb87ed7 100644 --- a/packages/contracts/src/jones/environmentHttpGroups.ts +++ b/packages/contracts/src/jones/environmentHttpGroups.ts @@ -1,3 +1,4 @@ +import { WorkQueueMetadataResult } from "./workQueueMetadata.ts"; import { VoiceReviewRecentList, VoiceReviewDiagnostics, @@ -313,7 +314,16 @@ export const makeJonesHttpGroups = ({ }).middleware(EnvironmentAuthenticatedAuth), ) {} + class EnvironmentWorkQueueMetadataHttpApi extends HttpApiGroup.make("workQueueMetadata").add( + HttpApiEndpoint.get("snapshot", "/api/work-queue/metadata", { + headers: OptionalBearerHeaders, + success: WorkQueueMetadataResult, + error: [EnvironmentScopeRequiredError, EnvironmentInternalError], + }).middleware(EnvironmentAuthenticatedAuth), + ) {} + return { + EnvironmentWorkQueueMetadataHttpApi, EnvironmentVoiceReviewHttpApi, EnvironmentHostStatusHttpApi, EnvironmentConversationLibraryHttpApi, diff --git a/packages/contracts/src/jones/fixtures/work_queue_metadata_v1.json.fixture b/packages/contracts/src/jones/fixtures/work_queue_metadata_v1.json.fixture new file mode 100644 index 000000000..1ef26be58 --- /dev/null +++ b/packages/contracts/src/jones/fixtures/work_queue_metadata_v1.json.fixture @@ -0,0 +1 @@ +{"authority_effect":"none","coverage":"complete","items":[{"canonical_binding":{"authority_namespace":"namespace","expires_at":"1970-01-01T00:00:01Z","membership_id":"membership","native_reference_id":"native-ref","native_thread_id":"thread","owner_id":"owner","registry_version":3,"server_generation":2,"source_instance_id":"env","store_generation":4},"dispatch_status":null,"entry_kind":"ordinary","finish_line":"not_tracked","lane":"normal","native_command_status":null,"queue_state":"queued","request_id":"request","request_kind":"initial","submitted_at_ms":100,"target":{"environment_ref":"env","host_id":"vps","thread_id":"thread"},"workstream_id":"canonical-work"}],"observed_at_ms":100,"schema":"codex.t3-work-queue-metadata/v1","snapshot_token":"5a648884903f4aa9d8932802522ff8735c2d2b040c5ec32ede1235001a8015fb","source":{"environment_ref":"env","exporter_instance_id":"worker","host_id":"vps","queue_id":"queue"}} \ No newline at end of file diff --git a/packages/contracts/src/jones/workQueueMetadata.test.ts b/packages/contracts/src/jones/workQueueMetadata.test.ts new file mode 100644 index 000000000..f9b5e76bc --- /dev/null +++ b/packages/contracts/src/jones/workQueueMetadata.test.ts @@ -0,0 +1,138 @@ +import { describe, expect, it } from "vite-plus/test"; +import * as Schema from "effect/Schema"; +import producerRaw from "./fixtures/work_queue_metadata_v1.json.fixture?raw"; +import { WorkQueueMetadata, WorkQueueMetadataResult } from "./workQueueMetadata.ts"; + +const item = { + request_id: "request-1", + workstream_id: "canonical-id", + canonical_binding: null, + entry_kind: "ordinary", + request_kind: "initial", + lane: "normal", + queue_state: "unknown", + submitted_at_ms: null, + target: null, + dispatch_status: null, + native_command_status: null, + finish_line: "not_tracked", +}; +const fixture = { + schema: "codex.t3-work-queue-metadata/v1", + source: { + queue_id: "queue", + host_id: "host", + environment_ref: "environment", + exporter_instance_id: "exporter", + }, + observed_at_ms: 1_000, + snapshot_token: "a".repeat(64), + coverage: "complete", + items: [item], + authority_effect: "none", +}; +const decode = Schema.decodeUnknownSync(WorkQueueMetadata); +const decodeResult = Schema.decodeUnknownSync(WorkQueueMetadataResult); + +describe("work queue metadata contract", () => { + it("decodes the exact Python producer golden artifact without changing its identity", () => { + const bytes = new TextEncoder().encode(producerRaw); + expect(bytes.byteLength).toBe(923); + expect(bytes[bytes.length - 1]).toBe(125); + const parsed = JSON.parse(producerRaw); + const snapshot = decode(parsed); + expect(snapshot).toEqual(parsed); + expect(snapshot.snapshot_token).toBe( + "5a648884903f4aa9d8932802522ff8735c2d2b040c5ec32ede1235001a8015fb", + ); + expect(snapshot.items[0]).toMatchObject({ + workstream_id: "canonical-work", + canonical_binding: { + membership_id: "membership", + native_reference_id: "native-ref", + native_thread_id: "thread", + }, + finish_line: "not_tracked", + }); + }); + it("preserves raw identity and unknown state without synthesizing completion", () => { + expect(decode(fixture)).toEqual(fixture); + expect(() => decode({ ...fixture, items: [{ ...item, finish_line: "completed" }] })).toThrow(); + }); + it("rejects private and unknown fields at every object boundary with default decoding", () => { + const variants = [ + { ...fixture, prompt: "private" }, + { ...fixture, source: { ...fixture.source, path: "/private" } }, + { ...fixture, items: [{ ...item, body: "private" }] }, + { + ...fixture, + items: [ + { + ...item, + target: { + host_id: "host", + environment_ref: "env", + thread_id: "thread", + account: "private", + }, + }, + ], + }, + ]; + for (const value of variants) expect(() => decode(value)).toThrow(); + expect(() => + decodeResult({ + status: "unavailable", + reason: "source_unavailable", + error: "private", + }), + ).toThrow(); + }); + it("rejects unsafe times, excessive items, duplicate requests and invalid discriminants", () => { + for (const value of [ + { ...fixture, observed_at_ms: Number.MAX_SAFE_INTEGER + 1 }, + { ...fixture, observed_at_ms: -1 }, + { ...fixture, observed_at_ms: 1.5 }, + { ...fixture, schema: "other" }, + { + ...fixture, + items: Array.from({ length: 1001 }, (_, index) => ({ ...item, request_id: `r${index}` })), + }, + { ...fixture, items: [item, item] }, + { ...fixture, items: [{ ...item, queue_state: "success" }] }, + { ...fixture, items: [{ ...item, submitted_at_ms: 1_001 }] }, + ]) + expect(() => decode(value)).toThrow(); + }); + it("accepts only bounded, current-at-sample, exact-thread canonical bindings", () => { + const binding = { + owner_id: "owner", + server_generation: 1, + registry_version: 0, + membership_id: "member", + native_reference_id: "ref", + source_instance_id: "source", + native_thread_id: "thread", + authority_namespace: "namespace", + store_generation: 1, + expires_at: "2026-10-04T12:00:00.123456Z", + }; + const bound = { + ...item, + canonical_binding: binding, + target: { host_id: "host", environment_ref: "environment", thread_id: "thread" }, + }; + expect(decode({ ...fixture, items: [bound] }).items[0]?.canonical_binding).toEqual(binding); + for (const change of [ + { expires_at: "1970-01-01T00:00:00Z" }, + { expires_at: "2026-02-30T12:00:00Z" }, + { native_thread_id: "other" }, + { store_generation: 0 }, + { private_evidence: "secret" }, + ]) { + expect(() => + decode({ ...fixture, items: [{ ...bound, canonical_binding: { ...binding, ...change } }] }), + ).toThrow(); + } + }); +}); diff --git a/packages/contracts/src/jones/workQueueMetadata.ts b/packages/contracts/src/jones/workQueueMetadata.ts new file mode 100644 index 000000000..4b7270a63 --- /dev/null +++ b/packages/contracts/src/jones/workQueueMetadata.ts @@ -0,0 +1,118 @@ +import * as Schema from "effect/Schema"; +import * as DateTime from "effect/DateTime"; + +export const WORK_QUEUE_METADATA_MAX_BYTES = 1_048_576; +const closed = (fields: Fields) => { + const schema = Schema.Struct(fields); + return Schema.flip( + Schema.flip(schema).check( + Schema.makeFilter((value) => + Reflect.ownKeys(value).every((key) => Object.hasOwn(fields, key)), + ), + ), + ); +}; +const Id = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/)); +const NativeId = Schema.String.check( + Schema.isMinLength(1), + Schema.isMaxLength(512), + // oxlint-disable-next-line no-control-regex -- Native identifiers must exclude control characters. + Schema.isPattern(/^[^\u0000-\u001f\u007f]+$/), +); +const Integer = Schema.Number.check( + Schema.isInt(), + Schema.isBetween({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER }), +); +const Positive = Integer.check(Schema.isGreaterThanOrEqualTo(1)); +const Timestamp = Schema.String.check( + Schema.isPattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?Z$/), + Schema.makeFilter( + (value) => + Number.isFinite(Date.parse(value)) && + DateTime.formatIso(DateTime.makeUnsafe(value)).slice(0, 19) === value.slice(0, 19), + ), +); +export const WorkQueueMetadataSource = closed({ + queue_id: Id, + host_id: Id, + environment_ref: NativeId, + exporter_instance_id: Id, +}); +export type WorkQueueMetadataSource = typeof WorkQueueMetadataSource.Type; +const CanonicalBinding = closed({ + owner_id: Id, + server_generation: Positive, + registry_version: Integer, + membership_id: Id, + native_reference_id: Id, + source_instance_id: Id, + native_thread_id: NativeId, + authority_namespace: NativeId, + store_generation: Positive, + expires_at: Timestamp, +}); +const Item = closed({ + request_id: Id, + workstream_id: Id, + canonical_binding: Schema.NullOr(CanonicalBinding), + entry_kind: Schema.Literals(["ordinary", "flexible", "bootstrap"]), + request_kind: Schema.Literals(["initial", "owner_followup", "auto_continue", "bootstrap"]), + lane: Schema.Literals(["high", "normal"]), + queue_state: Schema.Literals([ + "queued", + "reserved", + "waiting_remote", + "observed_terminal", + "held", + "cancelled", + "reconciliation_required", + "unknown", + ]), + submitted_at_ms: Schema.NullOr(Integer), + target: Schema.NullOr(closed({ host_id: Id, environment_ref: NativeId, thread_id: NativeId })), + dispatch_status: Schema.NullOr(Schema.Literals(["accepted", "rejected", "unknown"])), + native_command_status: Schema.NullOr(Schema.Literals(["accepted", "rejected", "not_found"])), + finish_line: Schema.Literal("not_tracked"), +}); +export const WorkQueueMetadata = closed({ + schema: Schema.Literal("codex.t3-work-queue-metadata/v1"), + source: WorkQueueMetadataSource, + observed_at_ms: Integer, + snapshot_token: Schema.String.check(Schema.isPattern(/^[a-f0-9]{64}$/)), + coverage: Schema.Literals(["complete", "partial"]), + items: Schema.Array(Item).check(Schema.isMaxLength(1000)), + authority_effect: Schema.Literal("none"), +}).check( + Schema.makeFilter( + (value) => + new Set(value.items.map((item) => item.request_id)).size === value.items.length && + value.items.every( + (item) => + (item.submitted_at_ms === null || item.submitted_at_ms <= value.observed_at_ms) && + (item.canonical_binding === null || + (Date.parse(item.canonical_binding.expires_at) > value.observed_at_ms && + item.target !== null && + item.target.thread_id === item.canonical_binding.native_thread_id)), + ), + ), +); +export type WorkQueueMetadata = typeof WorkQueueMetadata.Type; +export const WorkQueueMetadataResult = Schema.Union([ + closed({ + status: Schema.Literals(["ready", "partial", "stale"]), + snapshot: WorkQueueMetadata, + expires_at_ms: Integer, + }), + closed({ status: Schema.Literal("unconfigured"), reason: Schema.Literal("not_configured") }), + closed({ + status: Schema.Literal("unavailable"), + reason: Schema.Literals([ + "invalid_configuration", + "source_unavailable", + "invalid_artifact", + "source_mismatch", + "future_sample", + ]), + }), +]); +export type WorkQueueMetadataResult = typeof WorkQueueMetadataResult.Type; diff --git a/scripts/jones/boundary/checks.json b/scripts/jones/boundary/checks.json index 626cf2588..1de73e628 100644 --- a/scripts/jones/boundary/checks.json +++ b/scripts/jones/boundary/checks.json @@ -187,6 +187,20 @@ "commands": [ "vp test run apps/server/src/jones/mcp/organizationMetadata/OrganizationMetadataMcpService.test.ts apps/server/src/jones/mcp/organizationMetadata/handlers.test.ts packages/contracts/src/jones/organizationMetadata.test.ts" ] + }, + { + "id": "jones-work-queue-metadata", + "tests": [ + "apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.test.ts", + "apps/server/src/jones/workQueueMetadata/http.test.ts", + "apps/web/src/components/workQueue/WorkQueuePage.test.tsx", + "packages/client-runtime/src/jones/workQueueMetadata.test.ts", + "packages/contracts/src/jones/environmentHttpGroups.test.ts", + "packages/contracts/src/jones/workQueueMetadata.test.ts" + ], + "commands": [ + "vp test run apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.test.ts apps/server/src/jones/workQueueMetadata/http.test.ts apps/web/src/components/workQueue/WorkQueuePage.test.tsx packages/client-runtime/src/jones/workQueueMetadata.test.ts packages/contracts/src/jones/environmentHttpGroups.test.ts packages/contracts/src/jones/workQueueMetadata.test.ts" + ] } ] } diff --git a/scripts/jones/boundary/features.json b/scripts/jones/boundary/features.json index 01e6da5a1..5482e3487 100644 --- a/scripts/jones/boundary/features.json +++ b/scripts/jones/boundary/features.json @@ -30,7 +30,7 @@ { "id": "work-queue-preview", "tests": ["apps/web/src/components/workQueue/WorkQueuePage.test.tsx"], - "scope": "Mock submitted-work preview. Rebuild #113 canonical queue metadata remains outside automatically accepted scope; queue dispatch compatibility and process placement have separate source test references." + "scope": "Mock submitted-work preview remains covered by WorkQueuePage.test.tsx; the adapted canonical metadata panel, service, HTTP route, contracts and shared client runtime are covered by work-queue-metadata." }, { "id": "boundary-inventory", @@ -152,6 +152,18 @@ "packages/contracts/src/jones/organizationMetadata.test.ts" ], "scope": "Native authenticated MCP invocation context and bounded organization thread metadata service, toolkit handlers, and Jones contract schemas; registration is provided by McpHttpServer." + }, + { + "id": "work-queue-metadata", + "tests": [ + "apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.test.ts", + "apps/server/src/jones/workQueueMetadata/http.test.ts", + "apps/web/src/components/workQueue/WorkQueuePage.test.tsx", + "packages/client-runtime/src/jones/workQueueMetadata.test.ts", + "packages/contracts/src/jones/environmentHttpGroups.test.ts", + "packages/contracts/src/jones/workQueueMetadata.test.ts" + ], + "scope": "Adapted rebuild #113 canonical artifact metadata: validated server configuration and artifact reads, authenticated HTTP registration, capability-gated shared client reads, and metadata panel integration in WorkQueuePage." } ] } diff --git a/scripts/jones/boundary/fragments/work-queue-metadata.json b/scripts/jones/boundary/fragments/work-queue-metadata.json new file mode 100644 index 000000000..a9f433247 --- /dev/null +++ b/scripts/jones/boundary/fragments/work-queue-metadata.json @@ -0,0 +1,23 @@ +{ + "schema": "jones.boundary-fragment/v1", + "feature": "work-queue-metadata", + "jonesOwnedPaths": [ + "apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.test.ts", + "apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.ts", + "apps/server/src/jones/workQueueMetadata/config.ts", + "apps/server/src/jones/workQueueMetadata/http.test.ts", + "apps/server/src/jones/workQueueMetadata/http.ts", + "apps/web/src/jones/workQueue/WorkQueueMetadataPanel.tsx", + "apps/web/src/jones/workQueue/useWorkQueueMetadata.ts", + "packages/client-runtime/src/jones/workQueueMetadata.test.ts", + "packages/client-runtime/src/jones/workQueueMetadata.ts" + ], + "hookPaths": [], + "tombstones": [], + "allowedImports": [ + { + "from": "packages/contracts/src/index.ts", + "to": "packages/contracts/src/jones/workQueueMetadata.ts" + } + ] +} diff --git a/scripts/jones/boundary/inventory.json b/scripts/jones/boundary/inventory.json index 0714b2312..a6e88f150 100644 --- a/scripts/jones/boundary/inventory.json +++ b/scripts/jones/boundary/inventory.json @@ -470,7 +470,7 @@ "apps/web/src/components/voiceReview/useVoiceReview.ts": "jones-owned", "apps/web/src/components/voiceReview/voiceReviewActions.test.ts": "jones-owned", "apps/web/src/components/voiceReview/voiceReviewActions.ts": "jones-owned", - "apps/web/src/components/workQueue/WorkQueuePage.test.tsx": "jones-owned", + "apps/web/src/components/workQueue/WorkQueuePage.test.tsx": "jones-tests-in-upstream", "apps/web/src/components/workQueue/WorkQueuePage.tsx": "jones-owned", "apps/web/src/components/workQueue/WorkQueuePanel.tsx": "jones-owned", "apps/web/src/components/workQueue/WorkQueuePreview.tsx": "jones-owned", @@ -681,7 +681,22 @@ "apps/server/src/jones/mcp/organizationMetadata/tools.ts": "jones-owned", "apps/server/src/mcp/McpHttpServer.ts": "hook", "packages/contracts/src/jones/organizationMetadata.test.ts": "jones-tests-in-upstream", - "packages/contracts/src/jones/organizationMetadata.ts": "jones-owned" + "packages/contracts/src/jones/organizationMetadata.ts": "jones-owned", + "apps/server/src/jones/http/registration.ts": "jones-owned", + "apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.test.ts": "jones-tests-in-upstream", + "apps/server/src/jones/workQueueMetadata/WorkQueueMetadataService.ts": "jones-owned", + "apps/server/src/jones/workQueueMetadata/config.ts": "jones-owned", + "apps/server/src/jones/workQueueMetadata/http.test.ts": "jones-tests-in-upstream", + "apps/server/src/jones/workQueueMetadata/http.ts": "jones-owned", + "apps/web/src/jones/workQueue/WorkQueueMetadataPanel.tsx": "jones-owned", + "apps/web/src/jones/workQueue/useWorkQueueMetadata.ts": "jones-owned", + "packages/client-runtime/src/jones/workQueueMetadata.test.ts": "jones-tests-in-upstream", + "packages/client-runtime/src/jones/workQueueMetadata.ts": "jones-owned", + "packages/contracts/src/jones/environmentHttpGroups.test.ts": "jones-tests-in-upstream", + "packages/contracts/src/jones/environmentHttpGroups.ts": "jones-owned", + "packages/contracts/src/jones/fixtures/work_queue_metadata_v1.json.fixture": "jones-owned", + "packages/contracts/src/jones/workQueueMetadata.test.ts": "jones-tests-in-upstream", + "packages/contracts/src/jones/workQueueMetadata.ts": "jones-owned" }, "jonesOwnedPaths": [ "apps/desktop/scripts/ui-evidence/",