@teamscala/service-boot 0.23.345 → 0.23.346

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/package.json +1064 -1093
  2. package/src/two-phase-boot.ts +2 -1
  3. package/src/agreement-gate.d.ts +0 -19
  4. package/src/asset-cache-key.d.ts +0 -11
  5. package/src/asset-optimize.d.ts +0 -17
  6. package/src/asset-proxy.d.ts +0 -11
  7. package/src/asset-social-card.d.ts +0 -17
  8. package/src/attach-agreement-magic.d.ts +0 -20
  9. package/src/attach-portal-members.d.ts +0 -23
  10. package/src/attach-proposal-accept.d.ts +0 -26
  11. package/src/attach-proposal-magic.d.ts +0 -18
  12. package/src/attach-transfers-upload.d.ts +0 -27
  13. package/src/auth-gate.d.ts +0 -29
  14. package/src/bind-page-handler.d.ts +0 -18
  15. package/src/build-client-assets/input-hashing.d.ts +0 -25
  16. package/src/build-client-assets/source-candidates.d.ts +0 -27
  17. package/src/build-client-assets.d.ts +0 -50
  18. package/src/compose-page-title.d.ts +0 -41
  19. package/src/content-gate/access.d.ts +0 -15
  20. package/src/content-gate/config-types.d.ts +0 -99
  21. package/src/content-gate/gate.d.ts +0 -13
  22. package/src/content-gate/parse-cookie.d.ts +0 -9
  23. package/src/content-gate/token.d.ts +0 -48
  24. package/src/content-page-floor.d.ts +0 -39
  25. package/src/discover-blocks.d.ts +0 -28
  26. package/src/expand-roster.d.ts +0 -34
  27. package/src/fn-routers/agreement-completion-email.d.ts +0 -14
  28. package/src/fn-routers/agreement-send-router.d.ts +0 -82
  29. package/src/fn-routers/ai-health-router.d.ts +0 -35
  30. package/src/fn-routers/auth-router.d.ts +0 -47
  31. package/src/fn-routers/cache-admin-router.d.ts +0 -57
  32. package/src/fn-routers/chat-session-router.d.ts +0 -219
  33. package/src/fn-routers/email-defaults.d.ts +0 -1
  34. package/src/fn-routers/email-link-ttl.d.ts +0 -1
  35. package/src/fn-routers/index.d.ts +0 -18
  36. package/src/fn-routers/orchestrator-registration-router.d.ts +0 -60
  37. package/src/fn-routers/orchestrator-registration-router.ts +0 -77
  38. package/src/fn-routers/orchestrator-registration.live.test.ts +0 -70
  39. package/src/fn-routers/orchestrator-registration.test.ts +0 -23
  40. package/src/fn-routers/panel-data-router.d.ts +0 -19
  41. package/src/fn-routers/persist-chat-messages.d.ts +0 -36
  42. package/src/fn-routers/plans-router.d.ts +0 -25
  43. package/src/fn-routers/proposal-send-router.d.ts +0 -58
  44. package/src/fn-routers/public-agreement-router.d.ts +0 -289
  45. package/src/fn-routers/public-assessment-router.d.ts +0 -68
  46. package/src/fn-routers/public-gpt-session-router.d.ts +0 -143
  47. package/src/fn-routers/sign-in-flow-router.d.ts +0 -107
  48. package/src/fn-routers/task-sync-assign.d.ts +0 -307
  49. package/src/fn-routers/task-sync-brief.d.ts +0 -98
  50. package/src/fn-routers/task-sync-close.d.ts +0 -41
  51. package/src/fn-routers/task-sync-dispatch.d.ts +0 -47
  52. package/src/fn-routers/task-sync-dispatch.ts +0 -81
  53. package/src/fn-routers/task-sync-lane.d.ts +0 -49
  54. package/src/fn-routers/task-sync-lane.ts +0 -114
  55. package/src/fn-routers/task-sync-projection.d.ts +0 -144
  56. package/src/fn-routers/task-sync-rename.d.ts +0 -91
  57. package/src/fn-routers/task-sync-router.d.ts +0 -455
  58. package/src/fn-routers/task-sync-router.ts +0 -1075
  59. package/src/fn-routers/tasks-router.d.ts +0 -25
  60. package/src/fn-routers/user-preferences-router.d.ts +0 -77
  61. package/src/fn-routers/work-items-router.d.ts +0 -102
  62. package/src/gate-ttls.d.ts +0 -6
  63. package/src/gpt-gate.d.ts +0 -25
  64. package/src/handlers/bun-stable-watch.d.ts +0 -1
  65. package/src/handlers/pane-outbox-drain.d.ts +0 -1
  66. package/src/handlers/projects-digest.d.ts +0 -26
  67. package/src/handlers/prune-stale-data.d.ts +0 -30
  68. package/src/handlers/registry-stale-inactive-sweep.d.ts +0 -1
  69. package/src/handlers/scala-tools-currency-watch.d.ts +0 -1
  70. package/src/handlers/singleton-tools-currency-watch.d.ts +0 -1
  71. package/src/handlers/sweep-event-log.d.ts +0 -1
  72. package/src/handlers/sweep-host-cache.d.ts +0 -24
  73. package/src/handlers/upstream-issue-sync.d.ts +0 -1
  74. package/src/handlers/whatsapp-liveness-logic.d.ts +0 -34
  75. package/src/handlers/whatsapp-liveness.d.ts +0 -4
  76. package/src/hono-error-handler.d.ts +0 -25
  77. package/src/install-harness.d.ts +0 -45
  78. package/src/install-logging.d.ts +0 -34
  79. package/src/load-service-install-row.d.ts +0 -24
  80. package/src/load-service-prisma-client.d.ts +0 -18
  81. package/src/mount-orpc-router.d.ts +0 -15
  82. package/src/mount-server-packages.d.ts +0 -29
  83. package/src/mount.d.ts +0 -9
  84. package/src/mutable-app-ref.d.ts +0 -44
  85. package/src/page-route-shell-types.d.ts +0 -12
  86. package/src/page-server-orpc/audit-revert-router.d.ts +0 -37
  87. package/src/page-server-orpc/hub-mcp-client.d.ts +0 -12
  88. package/src/page-server-orpc/portal-diagnostics-router.d.ts +0 -49
  89. package/src/page-server-orpc/puck-pages-router.d.ts +0 -58
  90. package/src/page-server-orpc/server-client.d.ts +0 -16
  91. package/src/page-server-orpc/templates-router.d.ts +0 -34
  92. package/src/page-server-orpc/user-preferences-router.d.ts +0 -79
  93. package/src/pg-boss-handler-registry.d.ts +0 -27
  94. package/src/pg-boss-mount.d.ts +0 -42
  95. package/src/proposal-authorized-emails.d.ts +0 -28
  96. package/src/proposal-chat-inject.d.ts +0 -17
  97. package/src/proposal-gate.d.ts +0 -18
  98. package/src/read-html-template.d.ts +0 -38
  99. package/src/read-viewport-from-request.d.ts +0 -18
  100. package/src/readiness-gate.d.ts +0 -16
  101. package/src/register-api-routes.d.ts +0 -18
  102. package/src/register-crash-handlers.d.ts +0 -15
  103. package/src/register-page-routes.d.ts +0 -27
  104. package/src/register-page-server-client.d.ts +0 -11
  105. package/src/register-registry-page-route/ai-chat-prefetch.d.ts +0 -13
  106. package/src/register-registry-page-route/graph-cache.d.ts +0 -30
  107. package/src/register-registry-page-route/meta-title.d.ts +0 -2
  108. package/src/register-registry-page-route/proposal-approval-email-token.d.ts +0 -14
  109. package/src/register-registry-page-route/proposal-brand-stamping.d.ts +0 -28
  110. package/src/register-registry-page-route/resolve-page-data.d.ts +0 -1
  111. package/src/register-registry-page-route.d.ts +0 -27
  112. package/src/register-static-assets.d.ts +0 -13
  113. package/src/register-ws-routes.d.ts +0 -28
  114. package/src/resolve-app-base-url.d.ts +0 -13
  115. package/src/resolve-brand-favicon.d.ts +0 -28
  116. package/src/resolve-from-loader-config.d.ts +0 -32
  117. package/src/resolve-service-context.d.ts +0 -17
  118. package/src/resolve-service-port.d.ts +0 -75
  119. package/src/resolve-theme-mode.d.ts +0 -42
  120. package/src/resolvers/fetch-org-brand.d.ts +0 -2
  121. package/src/resolvers/portal-route-paths.d.ts +0 -39
  122. package/src/resolvers/website-by-host-map.d.ts +0 -12
  123. package/src/resource-chat-inject.d.ts +0 -46
  124. package/src/resource-not-found.d.ts +0 -60
  125. package/src/run-boot-hook-list.d.ts +0 -19
  126. package/src/run-boot-hooks.d.ts +0 -31
  127. package/src/scala-tools-currency-watchdog.d.ts +0 -10
  128. package/src/security-headers.d.ts +0 -12
  129. package/src/serve/serve.d.ts +0 -46
  130. package/src/serve/stamp-transport-identity.d.ts +0 -39
  131. package/src/server-error-page.d.ts +0 -29
  132. package/src/start-scheduled-jobs.d.ts +0 -18
  133. package/src/two-phase-boot.d.ts +0 -176
  134. package/src/types.d.ts +0 -46
  135. package/src/ui-service.d.ts +0 -46
  136. package/src/ws-boot-self-test.d.ts +0 -19
@@ -1,41 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Lane-close write-through for task-sync. The session-owning fleet runtime
7
- * calls this ORPC procedure before releasing an ACP lane, so work_items remains
8
- * the durable lifecycle source without a second raw-SQL writer in the host.
9
- */
10
- import * as v from "valibot";
11
- import { type Context } from "./auth-router.ts";
12
- import type { WorkItem } from "./task-sync-router.ts";
13
- export declare const closeLaneInput: v.ObjectSchema<{
14
- readonly lane_session: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, "lane_session is required">]>;
15
- readonly disposition: v.PicklistSchema<["completed", "needs_retry", "failed", "orchestrator_closed", "orphaned"], undefined>;
16
- }, undefined>;
17
- export interface ClosedTaskState {
18
- task_id: string;
19
- task_status: "done" | "archived";
20
- completed: boolean;
21
- kind_data: Record<string, unknown>;
22
- }
23
- export declare function closedTaskState(task: Pick<WorkItem, "id" | "kind_data">, disposition: v.InferOutput<typeof closeLaneInput>["disposition"]): ClosedTaskState;
24
- export declare const closeLaneHandler: import("@orpc/server").DecoratedProcedure<import("@orpc/server").MergedInitialContext<Context & Record<never, never>, Context, Context>, import("@orpc/server").MergedCurrentContext<Context, {
25
- headers: Headers;
26
- resolveUser?: (headers: Headers, prisma: any) => Promise<import("./auth-router.ts").User | null>;
27
- prisma: any;
28
- user: import("./auth-router.ts").User;
29
- orgId: string | null | undefined;
30
- }>, v.ObjectSchema<{
31
- readonly lane_session: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, "lane_session is required">]>;
32
- readonly disposition: v.PicklistSchema<["completed", "needs_retry", "failed", "orchestrator_closed", "orphaned"], undefined>;
33
- }, undefined>, import("@orpc/contract").Schema<{
34
- task_id: string;
35
- task_status: "archived" | "done";
36
- reconciled: boolean;
37
- }, {
38
- task_id: string;
39
- task_status: "archived" | "done";
40
- reconciled: boolean;
41
- }>, Record<never, never>, Record<never, never>>;
@@ -1,47 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * DB→AGENT task projection emission. A work_items change is delivered by one
7
- * canonical comments row addressed to the active cli_sessions runtime; the
8
- * database emission wakes that runtime adapter, which re-reads readTaskList.
9
- * This module owns no pane transport, terminal fallback, or second message
10
- * queue.
11
- */
12
- export interface TaskChangeComment {
13
- work_item_id: string;
14
- content: string;
15
- system_key: string;
16
- source_cli_session_id: string;
17
- sender_role: "system";
18
- sender_name: "TaskSync";
19
- }
20
- export declare function buildTaskChangeComment(taskId: string, sessionId: string, eventKey?: string): TaskChangeComment;
21
- export interface TaskDispatchPrisma {
22
- cli_sessions: {
23
- findFirst(args: unknown): Promise<{
24
- id: string;
25
- } | null>;
26
- };
27
- comments: {
28
- upsert(args: {
29
- where: {
30
- work_item_id_system_key: {
31
- work_item_id: string;
32
- system_key: string;
33
- };
34
- };
35
- create: TaskChangeComment;
36
- update: Record<string, never>;
37
- }): Promise<{
38
- id: string;
39
- }>;
40
- };
41
- }
42
- /** Write the durable task-change comment for the active runtime, if present. */
43
- export declare function notifyLaneOfTaskChange(prisma: TaskDispatchPrisma, taskId: string, eventKey?: string): Promise<{
44
- delivered: boolean;
45
- sessionId: string | null;
46
- commentId: string | null;
47
- }>;
@@ -1,81 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * DB→AGENT task projection emission. A work_items change is delivered by one
7
- * canonical comments row addressed to the active cli_sessions runtime; the
8
- * database emission wakes that runtime adapter, which re-reads readTaskList.
9
- * This module owns no pane transport, terminal fallback, or second message
10
- * queue.
11
- */
12
-
13
- import { getAppLogger } from "@teamscala/logger/app-loggers";
14
-
15
- export interface TaskChangeComment {
16
- work_item_id: string;
17
- content: string;
18
- system_key: string;
19
- source_cli_session_id: string;
20
- sender_role: "system";
21
- sender_name: "TaskSync";
22
- }
23
-
24
- export function buildTaskChangeComment(
25
- taskId: string,
26
- sessionId: string,
27
- eventKey: string = "refresh",
28
- ): TaskChangeComment {
29
- return {
30
- work_item_id: taskId,
31
- content: `TaskSync: work_items task ${taskId} changed; re-read fn.taskSync.readTaskList.`,
32
- system_key: `task-sync:${taskId}:${sessionId}:${eventKey}`,
33
- source_cli_session_id: sessionId,
34
- sender_role: "system",
35
- sender_name: "TaskSync",
36
- };
37
- }
38
-
39
- export interface TaskDispatchPrisma {
40
- cli_sessions: {
41
- findFirst(args: unknown): Promise<{ id: string } | null>;
42
- };
43
- comments: {
44
- upsert(args: {
45
- where: { work_item_id_system_key: { work_item_id: string; system_key: string } };
46
- create: TaskChangeComment;
47
- update: Record<string, never>;
48
- }): Promise<{ id: string }>;
49
- };
50
- }
51
-
52
- /** Write the durable task-change comment for the active runtime, if present. */
53
- export async function notifyLaneOfTaskChange(
54
- prisma: TaskDispatchPrisma,
55
- taskId: string,
56
- eventKey: string = "refresh",
57
- ): Promise<{ delivered: boolean; sessionId: string | null; commentId: string | null }> {
58
- const log = getAppLogger();
59
- const session = await prisma.cli_sessions.findFirst({
60
- where: { work_item_id: taskId, closed_at: null, lifecycle_state: { not: "closed" } },
61
- orderBy: { created_at: "desc" },
62
- select: { id: true },
63
- });
64
- if (!session) return { delivered: false, sessionId: null, commentId: null };
65
- try {
66
- const data = buildTaskChangeComment(taskId, session.id, eventKey);
67
- const comment = await prisma.comments.upsert({
68
- where: { work_item_id_system_key: { work_item_id: taskId, system_key: data.system_key } },
69
- create: data,
70
- update: {},
71
- });
72
- return { delivered: true, sessionId: session.id, commentId: comment.id };
73
- } catch (error) {
74
- log.warn("[task-sync] durable task comment emission failed", {
75
- taskId,
76
- sessionId: session.id,
77
- error: error instanceof Error ? error.message : String(error),
78
- });
79
- return { delivered: false, sessionId: session.id, commentId: null };
80
- }
81
- }
@@ -1,49 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Lane RESOLUTION for task-sync: given a session or a pane, which work_items
7
- * task IS this lane, and what is it called.
8
- *
9
- * Split out of task-sync-router.ts, which had reached 998 of its 1000-line
10
- * budget — two lines from frozen. That is not tidiness: the budget is a commit
11
- * gate, so at 998 lines ANY change to task-sync was blocked for every lane, and
12
- * the four-line fix that needed to land could not. The router header records
13
- * being split once before for exactly this reason; it grew back.
14
- *
15
- * The seam matches the one that split the projection. Everything here answers
16
- * "which lane, called what" from an identity, and none of it touches the ORPC
17
- * surface or decides what a task list should become — which is why these move
18
- * together rather than one at a time: resolveLaneTask alone has seven callers.
19
- *
20
- * CAPABILITY HARVEST (capability-harvest-on-split): APP-LOCAL, not a capability
21
- * candidate. Checked against the 14 platform singletons and the package
22
- * catalogue; the doctrine test fails plainly, because every export is bound to
23
- * the work_items schema and to task-sync own kind_data.lane convention, so no
24
- * unrelated app could install this unmodified.
25
- */
26
- import type { Prisma, WorkItem } from "./task-sync-router.ts";
27
- export declare function resolveLaneWorkName(prisma: Prisma, pane: string): Promise<string | null>;
28
- export declare function resolveLaneTask(prisma: Prisma, session: string): Promise<WorkItem | null>;
29
- /**
30
- * Resolve a lane's task by its AUTHORITATIVE work_item id — the id the spawn
31
- * minted (createBriefTask) and exported into the pane as SCALA_LANE_WORK_ITEM.
32
- *
33
- * WHY this exists alongside the session resolution: readAssignedBrief used to
34
- * resolve ONLY by kind_data.lane.session, which is written by the lane's OWN
35
- * bindLane hook at boot. That bind is a fire-and-forget POST racing the brief
36
- * read, so a fleet lane whose bind had not yet commit read "no task" and the
37
- * self-brief hook silently exited 0 — absent-input-reads-as-success at the
38
- * dispatch boundary (a lane booted, was told its brief was loaded, had none,
39
- * ran nothing, and was archived having done nothing — measured 2026-08-02).
40
- *
41
- * The work_item id is known to the spawn before the lane even starts
42
- * (mintBriefTask returns it; the spawn REFUSES if minting fails for a capture
43
- * CLI), so resolving by it CANNOT race: the row exists because the spawn
44
- * created it. The session bind stays for task-list writes (upsertFromTaskList)
45
- * — it is no longer load-bearing for brief delivery. Any status is matched
46
- * (active OR archived) because the brief is the brief regardless of the run's
47
- * state — closing a run archives the task, not the brief.
48
- */
49
- export declare function resolveTaskById(prisma: Prisma, workItemId: string): Promise<WorkItem | null>;
@@ -1,114 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Lane RESOLUTION for task-sync: given a session or a pane, which work_items
7
- * task IS this lane, and what is it called.
8
- *
9
- * Split out of task-sync-router.ts, which had reached 998 of its 1000-line
10
- * budget — two lines from frozen. That is not tidiness: the budget is a commit
11
- * gate, so at 998 lines ANY change to task-sync was blocked for every lane, and
12
- * the four-line fix that needed to land could not. The router header records
13
- * being split once before for exactly this reason; it grew back.
14
- *
15
- * The seam matches the one that split the projection. Everything here answers
16
- * "which lane, called what" from an identity, and none of it touches the ORPC
17
- * surface or decides what a task list should become — which is why these move
18
- * together rather than one at a time: resolveLaneTask alone has seven callers.
19
- *
20
- * CAPABILITY HARVEST (capability-harvest-on-split): APP-LOCAL, not a capability
21
- * candidate. Checked against the 14 platform singletons and the package
22
- * catalogue; the doctrine test fails plainly, because every export is bound to
23
- * the work_items schema and to task-sync own kind_data.lane convention, so no
24
- * unrelated app could install this unmodified.
25
- */
26
-
27
- import type { Prisma, WorkItem } from "./task-sync-router.ts";
28
-
29
- export async function resolveLaneWorkName(prisma: Prisma, pane: string): Promise<string | null> {
30
- try {
31
- const session = await prisma.cli_sessions.findFirst({
32
- where: {
33
- closed_at: null,
34
- lifecycle_state: { not: "closed" },
35
- host_route: { path: ["pane_id"], equals: pane },
36
- },
37
- orderBy: { created_at: "desc" },
38
- select: { id: true, label: true },
39
- });
40
- const name = session?.label?.trim();
41
- return name ? name : null;
42
- } catch {
43
- return null;
44
- }
45
- }
46
-
47
- export async function resolveLaneTask(prisma: Prisma, session: string): Promise<WorkItem | null> {
48
- const active = await prisma.work_items.findFirst({
49
- where: {
50
- kind: "task",
51
- status: "active",
52
- kind_data: { path: ["lane", "session"], equals: session },
53
- },
54
- // Deterministic by oldest. An unordered findFirst returns whichever row
55
- // Postgres happens to hand back, so with two parents for one lane (the
56
- // bindLane race, below) one turn wrote the lane's todos under parent A
57
- // and the next read parent B and found nothing — the lane's task list
58
- // appeared to empty itself, and the watchdog accused lanes that were
59
- // holding a populated list. Migration 109 makes the duplicate
60
- // unwritable; this makes the read stable regardless.
61
- orderBy: { created_at: "asc" },
62
- });
63
- if (active) return active;
64
-
65
- // FALL BACK TO AN ARCHIVED PARENT FOR THE SAME SESSION.
66
- //
67
- // Closing a run archives the lane's parent (close-run.ts) specifically to
68
- // drop it from the active match set above. That is correct when the LANE is
69
- // gone, and wrong whenever the run record closes while the lane keeps
70
- // running — which happens on a tmux-server restart (runs are reaped, panes
71
- // are restored) and whenever an orchestrator closes a run on a stale read.
72
- // The lane then has a full task list that this function cannot see, so
73
- // callers mint a FRESH parent, the old list is orphaned, and the compliance
74
- // watchdog reports "working with NO task list" at a lane holding 85 tasks.
75
- // That is the "KNOWN ADJACENT DEFECT" recorded on the lane-working-has-task-list
76
- // guard row, observed live 2026-07-31.
77
- //
78
- // Safe because a session id is minted per lane spawn and never reused, so an
79
- // archived parent under this session belongs to THIS lane. Newest-first.
80
- return prisma.work_items.findFirst({
81
- where: {
82
- kind: "task",
83
- status: "archived",
84
- kind_data: { path: ["lane", "session"], equals: session },
85
- },
86
- orderBy: { created_at: "desc" },
87
- });
88
- }
89
-
90
- /**
91
- * Resolve a lane's task by its AUTHORITATIVE work_item id — the id the spawn
92
- * minted (createBriefTask) and exported into the pane as SCALA_LANE_WORK_ITEM.
93
- *
94
- * WHY this exists alongside the session resolution: readAssignedBrief used to
95
- * resolve ONLY by kind_data.lane.session, which is written by the lane's OWN
96
- * bindLane hook at boot. That bind is a fire-and-forget POST racing the brief
97
- * read, so a fleet lane whose bind had not yet commit read "no task" and the
98
- * self-brief hook silently exited 0 — absent-input-reads-as-success at the
99
- * dispatch boundary (a lane booted, was told its brief was loaded, had none,
100
- * ran nothing, and was archived having done nothing — measured 2026-08-02).
101
- *
102
- * The work_item id is known to the spawn before the lane even starts
103
- * (mintBriefTask returns it; the spawn REFUSES if minting fails for a capture
104
- * CLI), so resolving by it CANNOT race: the row exists because the spawn
105
- * created it. The session bind stays for task-list writes (upsertFromTaskList)
106
- * — it is no longer load-bearing for brief delivery. Any status is matched
107
- * (active OR archived) because the brief is the brief regardless of the run's
108
- * state — closing a run archives the task, not the brief.
109
- */
110
- export async function resolveTaskById(prisma: Prisma, workItemId: string): Promise<WorkItem | null> {
111
- return prisma.work_items.findFirst({
112
- where: { id: workItemId, kind: "task" },
113
- });
114
- }
@@ -1,144 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The task-sync PROJECTION: how a CLI's task list maps onto `work_items` rows,
7
- * and what reconciling one against the other should do.
8
- *
9
- * Split out of `task-sync-router.ts`, which had grown past the 1000-line budget
10
- * and so could not be committed by anyone touching it. The seam is real rather
11
- * than arbitrary: everything here is PURE and decides WHAT should change, while
12
- * the router owns the ORPC surface and performs the writes. That is exactly why
13
- * this half is directly testable with no database.
14
- *
15
- * CAPABILITY HARVEST (capability-harvest-on-split): APP-LOCAL, not a capability
16
- * candidate. Checked against the 14 platform singletons and the package catalog
17
- * — nothing generic to fold into, and the doctrine test fails: no unrelated app
18
- * could install this unmodified, because every export is bound to the
19
- * `work_items` schema and task-sync's own contract (STATUS_TO_WORK_ITEM encodes
20
- * the work_items status enum, subtaskKey is this projection's key convention,
21
- * planReconcile diffs against work_items subtask rows).
22
- *
23
- * The near-miss is worth naming so the next reader does not re-derive it: the
24
- * SHAPE — diff a desired list against existing rows into create/update/delete —
25
- * is generic, and `document-signing` is precedent for lifting a pure domain half
26
- * out of service-boot into a package. It stays here because it has exactly one
27
- * consumer; extracting it now would be speculative packaging, which
28
- * platform-capabilities treats as drift. Revisit if a SECOND surface needs
29
- * list-against-rows reconciliation — then the generic diff core (not the
30
- * work_items-specific mapping) is what earns a package.
31
- */
32
- import type { TaskListItem, WorkItem } from "./task-sync-router.ts";
33
- /** CLI task-list status → work_items.status. The canonical forward direction. */
34
- export declare const STATUS_TO_WORK_ITEM: Record<"completed" | "in_progress" | "pending", string>;
35
- export declare function normalizeStatus(s: string | null): TaskListItem["status"];
36
- export interface ProgressSubtask {
37
- id: string;
38
- status: string | null;
39
- kind_data?: Record<string, unknown> | null;
40
- }
41
- export interface ProgressAcceptanceEvidence {
42
- work_item_id: string | null;
43
- sender_role: string | null;
44
- system_key: string | null;
45
- }
46
- export interface EvidenceProgress {
47
- total: number;
48
- accepted: number;
49
- percentage: number;
50
- }
51
- /**
52
- * Derive progress from the canonical checklist and independent acceptance
53
- * comments. A done status without an Orchestrator acceptance comment is not
54
- * evidence of completion; prose percentages and context gauges never enter
55
- * this calculation. Subtasks are required by default, with an explicit
56
- * `kind_data.pm_progress.required: false` opt-out for non-gating notes.
57
- */
58
- export declare function deriveEvidenceProgress(subtasks: ProgressSubtask[], evidence: ProgressAcceptanceEvidence[]): EvidenceProgress;
59
- /**
60
- * Decide the status to WRITE for an existing row, given what the CLI list says.
61
- *
62
- * The CLI task list has no `blocked` state, so a blocked row round-trips as
63
- * `pending` (via `normalizeStatus`, which collapses unknown/internal statuses)
64
- * and then maps back to `todo` — silently destroying the block. That is not a
65
- * cosmetic drift: the stop gate reads these rows, so un-blocking one re-blocks
66
- * the LANE from stopping, and the lane's only apparent fix is to touch the list
67
- * again, which destroys it again.
68
- *
69
- * A `todo` arriving from the CLI therefore carries NO information: it is what
70
- * the projection emits for every status it cannot represent, not an assertion
71
- * that the work became un-blocked. `in-progress` and `done` are different —
72
- * those are real transitions the CLI CAN express, so they win normally.
73
- *
74
- * Measured 2026-07-30: a lane set a row to `blocked` with a named blocker, the
75
- * next TaskUpdate reset it to `todo`, and the stop gate reported the lane
76
- * unfinished again — three turns of ping-pong before the cause was found.
77
- */
78
- export declare function resolveProjectedStatus(currentStatus: string | null | undefined, incomingStatus: string): string;
79
- export declare function subtaskKey(item: {
80
- mark?: string;
81
- content: string;
82
- title: string;
83
- }): string;
84
- /**
85
- * Pure decision for a TaskUpdate/TaskDelete: which existing subtask does this
86
- * event address, and what should happen when none does. Extracted for the same
87
- * reason planReconcile was — the handler should only EXECUTE, so the matching
88
- * rule is unit-testable.
89
- *
90
- * Three resolutions:
91
- * - `update` — matched by mark, or (mark absent/unstamped) by exact title.
92
- * - `heal` — nothing matched but the event carries a subject, so the row can
93
- * be reconstructed. Covers a TaskCreate that silently produced no
94
- * subtask, which previously orphaned the task forever.
95
- * - `discard` — nothing matched and there is no subject to rebuild a title from.
96
- *
97
- * Measured 2026-07-28: marks "1" and "3" were stamped for one lane while mark
98
- * "2" was never written at all, and every later update for it was dropped as
99
- * `no-matching-subtask` — 26 of 30 tasksync discards in six hours, across seven
100
- * distinct lane sessions, were that one reason.
101
- */
102
- export declare function resolveTaskUpdateTarget(existing: Array<{
103
- id: string;
104
- title: string;
105
- kind_data?: Record<string, unknown> | null;
106
- }>, evt: {
107
- taskId: string;
108
- subject?: string;
109
- }): {
110
- op: "update";
111
- id: string;
112
- } | {
113
- op: "heal";
114
- title: string;
115
- } | {
116
- op: "discard";
117
- };
118
- /**
119
- * Pure reconciliation plan: given the lane's EXISTING subtasks and its INCOMING
120
- * (current) task list, compute the create/update/delete ops that make the
121
- * subtask set match the list — WITHOUT touching prisma. Extracted so the
122
- * matching/status/positioning logic is unit-testable (the handler only executes
123
- * the plan). Matching is by stable mark when the CLI provides one, else by
124
- * content. Position follows incoming index, so a reorder is an update.
125
- */
126
- export interface ReconcilePlan {
127
- toCreate: Array<{
128
- content: string;
129
- status: string;
130
- completed: boolean;
131
- position: string;
132
- kind_data: Record<string, unknown>;
133
- }>;
134
- toUpdate: Array<{
135
- id: string;
136
- content: string;
137
- status: string;
138
- completed: boolean;
139
- position: string;
140
- kind_data: Record<string, unknown>;
141
- }>;
142
- toDelete: string[];
143
- }
144
- export declare function planReconcile(existing: WorkItem[], incoming: TaskListItem[], source?: string): ReconcilePlan;
@@ -1,91 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Lane RENAME back-write for task-sync: a tab rename EMITS to update the bound
7
- * work_items task's title in the DB, by CLI session id. This is the DB half of
8
- * `tab-rename-emits-back-to-task-title`: the operator's tab bar is how they see
9
- * what is being worked on (`lane-labels-and-task-lists`), so the tmux window
10
- * name and the task title must never diverge silently. Renaming a tab from
11
- * `scala-tools session rename` (or the lane renaming itself through the same
12
- * surface) updates the task title so the project board agrees with the tab bar.
13
- *
14
- * THE EMISSION, not a poll (`actions-are-rows-then-emissions`): a caller renames
15
- * the window (a row/state change) and that change emits the title update — the
16
- * task-sync owner reacts by updating the row. State lives in the row, never in a
17
- * return value a caller may never receive.
18
- *
19
- * Bound by CLI SESSION ID, never pane id (`lane-identity-is-cli-session-id`): a
20
- * pane OUTLIVES the session inside it, so matching on a pane would let a dead
21
- * session's task answer for a live lane. The session is the stable identity a
22
- * rename resolves to (the daemon resolves the pane's tab → its stored session id
23
- * and passes that here), so the rename lands on the lane that owns the tab.
24
- *
25
- * Split out of task-sync-router.ts for the same reason task-sync-brief/lane/
26
- * assign exist: the router sits at its 1000-line commit gate, so every cohesive
27
- * task-sync unit that can live in a sibling does. This owns the rename handler +
28
- * its pure outcome decision.
29
- *
30
- * CAPABILITY HARVEST (capability-harvest-on-split): APP-LOCAL, not a capability
31
- * candidate. Bound to the work_items schema and to task-sync's own
32
- * kind_data.lane.session convention; no unrelated app could install this
33
- * unmodified.
34
- */
35
- import * as v from "valibot";
36
- import { type Context } from "./auth-router.ts";
37
- /**
38
- * renameLaneTask ORPC input. `lane_session` is the lane's CLI-native session id
39
- * (kind_data.lane.session) — the stable identity a pane rename resolves to.
40
- * `title` is the new task title (the renamed tab's label). Both required:
41
- * oRPC rejects a call missing either at the signature
42
- * (`prevention-over-detection`), so a malformed rename is a 400, never a silent
43
- * no-op that leaves the title stale.
44
- */
45
- export declare const renameLaneTaskInput: v.ObjectSchema<{
46
- readonly lane_session: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, "lane_session is required">]>;
47
- readonly title: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, "title is required">]>;
48
- }, undefined>;
49
- /**
50
- * The rename outcome from a resolved lane task — pure so the "no task bound ⇒
51
- * not updated, never an error" decision is unit-testable with no DB. A rename
52
- * whose lane has no task is best-effort (returns updated:false): every spawn
53
- * now mints a task, so this is the rare case (a tab whose task was archived and
54
- * pruned), not a path the operator hits. Failing loudly here would refuse a
55
- * rename of the WINDOW for a DB miss, which couples two surfaces that must not
56
- * be — the tmux rename already succeeded before this is called.
57
- */
58
- export declare function renameOutcome(task: {
59
- id: string;
60
- } | null): {
61
- task_id: string | null;
62
- updated: boolean;
63
- };
64
- /**
65
- * renameLaneTask ORPC handler. The session-picker daemon calls this from
66
- * `/rpc/rename` after it renames the tmux window: it resolves the pane's tab to
67
- * its session id and posts it here. Same transport as bindLane/readTaskList:
68
- * page-gate-exempt (fn.taskSync), localhost super-admin. Resolves the lane's
69
- * task BY SESSION (never pane) and updates its title. Idempotent: renaming to
70
- * the same title writes the same row.
71
- *
72
- * An archived parent is matched too (resolveLaneTask falls back to the archived
73
- * lane task for the session), so a rename of a tab whose run closed still lands
74
- * on its task — the title is the title regardless of run state.
75
- */
76
- export declare const renameLaneTaskHandler: import("@orpc/server").DecoratedProcedure<import("@orpc/server").MergedInitialContext<Context & Record<never, never>, Context, Context>, import("@orpc/server").MergedCurrentContext<Context, {
77
- headers: Headers;
78
- resolveUser?: (headers: Headers, prisma: any) => Promise<import("./auth-router.ts").User | null>;
79
- prisma: any;
80
- user: import("./auth-router.ts").User;
81
- orgId: string | null | undefined;
82
- }>, v.ObjectSchema<{
83
- readonly lane_session: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, "lane_session is required">]>;
84
- readonly title: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, "title is required">]>;
85
- }, undefined>, import("@orpc/contract").Schema<{
86
- task_id: string | null;
87
- updated: boolean;
88
- }, {
89
- task_id: string | null;
90
- updated: boolean;
91
- }>, Record<never, never>, Record<never, never>>;