@teamscala/service-boot 0.23.334 → 0.23.335

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.
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Lane BRIEF resolution for task-sync: a spawned lane PULLS its own brief. The
7
7
  * lane's CLI extension reads its own session id on load, resolves the task
8
- * assigned to that session, and reads the brief from that task's LATEST comment.
8
+ * assigned to that session, and reads the brief from that task's FIRST comment.
9
9
  * Nothing is pasted, typed, passed as an argument or sent over a channel
10
10
  * (`scala-docs/reference/action-emission-architecture.md` "The lane PULLS its
11
11
  * brief"). This file owns the readAssignedBrief ORPC handler + its pure helper;
@@ -34,7 +34,7 @@ import { type Context } from "./auth-router.ts";
34
34
  */
35
35
  export type BriefReadStatus = "ready" | "empty_brief" | "no_comment" | "unbound";
36
36
  /**
37
- * Shape the readAssignedBrief response from a resolved task + its latest
37
+ * Shape the readAssignedBrief response from a resolved task + its first
38
38
  * comment. Pure (no prisma) so the decision — "no task/comment ⇒ nulls, never a
39
39
  * verdict from no data" (`no-verdict-from-no-data`) — is unit-testable.
40
40
  *
@@ -64,13 +64,12 @@ export declare function shapeBriefResponse(task: {
64
64
  status: BriefReadStatus;
65
65
  };
66
66
  /**
67
- * Read the LATEST comment on a work_items task. The comment IS the brief for a
68
- * run: every spawn writes a NEW comment, and the description is never the brief,
69
- * so "what was this lane told" always has exactly one answer
70
- * (`assignment-as-dispatch`). Newest-first by created_at. Returns null when the
71
- * task has no comment yet — the extension treats that as "no brief to inject".
67
+ * Read the immutable FIRST comment on a work_items task. It is the launch brief;
68
+ * later comments are conversation and must never change the launch identity.
69
+ * Oldest-first by created_at with id as a deterministic tie-breaker. Returns null
70
+ * when the task has no comment yet.
72
71
  */
73
- export declare function readLatestTaskComment(prisma: Prisma, workItemId: string): Promise<CommentRow | null>;
72
+ export declare function readFirstTaskComment(prisma: Prisma, workItemId: string): Promise<CommentRow | null>;
74
73
  /**
75
74
  * readAssignedBrief ORPC handler. A lane's CLI extension calls this with its
76
75
  * own session id to pull its brief. Same transport as bindLane/readTaskList: the
@@ -5,11 +5,11 @@
5
5
  *
6
6
  * Unit tests for the self-brief leg: shapeBriefResponse (the pure
7
7
  * no-task/no-comment ⇒ nulls decision) + source-contract assertions that
8
- * readLatestTaskComment reads the LATEST comment and the handler resolves by
8
+ * readFirstTaskComment reads the FIRST launch comment and the handler resolves by
9
9
  * session. Covers `what-must-have-a-test` for the new procedure.
10
10
  */
11
11
  import { describe, it, expect } from "bun:test";
12
- import { shapeBriefResponse, readLatestTaskComment } from "./task-sync-brief.ts";
12
+ import { shapeBriefResponse, readFirstTaskComment } from "./task-sync-brief.ts";
13
13
 
14
14
  describe("shapeBriefResponse (no verdict from no data)", () => {
15
15
  it("classifies a missing task as unbound (work_item_id null)", () => {
@@ -76,14 +76,13 @@ describe("shapeBriefResponse (no verdict from no data)", () => {
76
76
  });
77
77
  });
78
78
 
79
- describe("readAssignedBrief reads the LATEST comment (source contract)", () => {
79
+ describe("readAssignedBrief reads the FIRST launch comment (source contract)", () => {
80
80
  const briefSrc = Bun.file(new URL("./task-sync-brief.ts", import.meta.url).pathname);
81
81
 
82
- it("queries comments newest-first by created_at on work_item_id", async () => {
83
- // The comment IS the brief for a run; ordering by created_at desc makes the
84
- // LATEST comment (this run's) the answer, never a stale prior-run comment.
82
+ it("queries comments oldest-first with an id tie-breaker", async () => {
83
+ // The first comment is immutable launch identity; later comments are dialogue.
85
84
  const text = await briefSrc.text();
86
- expect(text).toContain('orderBy: { created_at: "desc" }');
85
+ expect(text).toContain('orderBy: [{ created_at: "asc" }, { id: "asc" }]');
87
86
  expect(text).toContain("work_item_id: workItemId");
88
87
  });
89
88
 
@@ -111,12 +110,12 @@ describe("readAssignedBrief reads the LATEST comment (source contract)", () => {
111
110
  });
112
111
  });
113
112
 
114
- describe("readLatestTaskComment is a single latest-comment read", () => {
113
+ describe("readFirstTaskComment is a single deterministic launch-comment read", () => {
115
114
  it("returns null when the prisma client has no comments model (feature-detect)", async () => {
116
115
  // `comments` is a universal shared model, but the access is feature-detected
117
116
  // like the fleet_agent_runs reads so a client without it degrades to "no
118
117
  // brief" rather than throwing.
119
- const out = await readLatestTaskComment({} as never, "task-1");
118
+ const out = await readFirstTaskComment({} as never, "task-1");
120
119
  expect(out).toBe(null);
121
120
  });
122
121
  });
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Lane BRIEF resolution for task-sync: a spawned lane PULLS its own brief. The
7
7
  * lane's CLI extension reads its own session id on load, resolves the task
8
- * assigned to that session, and reads the brief from that task's LATEST comment.
8
+ * assigned to that session, and reads the brief from that task's FIRST comment.
9
9
  * Nothing is pasted, typed, passed as an argument or sent over a channel
10
10
  * (`scala-docs/reference/action-emission-architecture.md` "The lane PULLS its
11
11
  * brief"). This file owns the readAssignedBrief ORPC handler + its pure helper;
@@ -39,7 +39,7 @@ import { resolveLaneTask, resolveTaskById } from "./task-sync-lane.ts";
39
39
  export type BriefReadStatus = "ready" | "empty_brief" | "no_comment" | "unbound";
40
40
 
41
41
  /**
42
- * Shape the readAssignedBrief response from a resolved task + its latest
42
+ * Shape the readAssignedBrief response from a resolved task + its first
43
43
  * comment. Pure (no prisma) so the decision — "no task/comment ⇒ nulls, never a
44
44
  * verdict from no data" (`no-verdict-from-no-data`) — is unit-testable.
45
45
  *
@@ -90,13 +90,12 @@ export function shapeBriefResponse(
90
90
  }
91
91
 
92
92
  /**
93
- * Read the LATEST comment on a work_items task. The comment IS the brief for a
94
- * run: every spawn writes a NEW comment, and the description is never the brief,
95
- * so "what was this lane told" always has exactly one answer
96
- * (`assignment-as-dispatch`). Newest-first by created_at. Returns null when the
97
- * task has no comment yet — the extension treats that as "no brief to inject".
93
+ * Read the immutable FIRST comment on a work_items task. It is the launch brief;
94
+ * later comments are conversation and must never change the launch identity.
95
+ * Oldest-first by created_at with id as a deterministic tie-breaker. Returns null
96
+ * when the task has no comment yet.
98
97
  */
99
- export async function readLatestTaskComment(
98
+ export async function readFirstTaskComment(
100
99
  prisma: Prisma,
101
100
  workItemId: string,
102
101
  ): Promise<CommentRow | null> {
@@ -110,7 +109,7 @@ export async function readLatestTaskComment(
110
109
  if (!comments?.findFirst) return null;
111
110
  return comments.findFirst({
112
111
  where: { work_item_id: workItemId },
113
- orderBy: { created_at: "desc" },
112
+ orderBy: [{ created_at: "asc" }, { id: "asc" }],
114
113
  });
115
114
  }
116
115
 
@@ -147,6 +146,6 @@ export const readAssignedBriefHandler = authed.handler(async ({ input, context }
147
146
  task = (await resolveLaneTask(prisma, session)) as (WorkItem & { id: string }) | null;
148
147
  }
149
148
  if (!task) return shapeBriefResponse(null, null);
150
- const comment = await readLatestTaskComment(prisma, task.id);
149
+ const comment = await readFirstTaskComment(prisma, task.id);
151
150
  return shapeBriefResponse(task, comment);
152
151
  });