dsh-live-teams 0.1.0

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 (69) hide show
  1. package/LICENSE +176 -0
  2. package/NOTICE +11 -0
  3. package/README.md +85 -0
  4. package/cordis.patch.yml +25 -0
  5. package/lib/binding.d.ts +18 -0
  6. package/lib/binding.js +42 -0
  7. package/lib/changed-paths.d.ts +26 -0
  8. package/lib/changed-paths.js +69 -0
  9. package/lib/client.js +6753 -0
  10. package/lib/command-queue.d.ts +60 -0
  11. package/lib/command-queue.js +185 -0
  12. package/lib/compatibility.js +109 -0
  13. package/lib/context-provider.d.ts +110 -0
  14. package/lib/context-provider.js +249 -0
  15. package/lib/dispatch.d.ts +174 -0
  16. package/lib/dispatch.js +624 -0
  17. package/lib/errors.d.ts +36 -0
  18. package/lib/errors.js +103 -0
  19. package/lib/git-artifacts.d.ts +50 -0
  20. package/lib/git-artifacts.js +242 -0
  21. package/lib/index.d.ts +14 -0
  22. package/lib/index.js +14 -0
  23. package/lib/mailbox.d.ts +274 -0
  24. package/lib/mailbox.js +721 -0
  25. package/lib/member-tools.d.ts +57 -0
  26. package/lib/member-tools.js +1265 -0
  27. package/lib/migrations.d.ts +17 -0
  28. package/lib/migrations.js +47 -0
  29. package/lib/plugin.d.ts +106 -0
  30. package/lib/plugin.js +1003 -0
  31. package/lib/roles.d.ts +35 -0
  32. package/lib/roles.js +284 -0
  33. package/lib/routes.d.ts +586 -0
  34. package/lib/routes.js +2816 -0
  35. package/lib/scope.d.ts +62 -0
  36. package/lib/scope.js +133 -0
  37. package/lib/session-bridge.d.ts +76 -0
  38. package/lib/session-bridge.js +147 -0
  39. package/lib/session-title.js +35 -0
  40. package/lib/storage.d.ts +9 -0
  41. package/lib/storage.js +65 -0
  42. package/lib/task-store.d.ts +729 -0
  43. package/lib/task-store.js +2205 -0
  44. package/lib/team-store.d.ts +216 -0
  45. package/lib/team-store.js +765 -0
  46. package/lib/tree-snapshot.d.ts +28 -0
  47. package/lib/tree-snapshot.js +80 -0
  48. package/lib/types/client/TeamView.d.ts +26 -0
  49. package/lib/types/client/TeamView.dom.test.d.ts +1 -0
  50. package/lib/types/client/api.d.ts +522 -0
  51. package/lib/types/client/api.test.d.ts +1 -0
  52. package/lib/types/client/attention.d.ts +65 -0
  53. package/lib/types/client/attention.test.d.ts +1 -0
  54. package/lib/types/client/index.d.ts +31 -0
  55. package/lib/types/client/locales.d.ts +577 -0
  56. package/lib/types/client/member-name.d.ts +14 -0
  57. package/lib/types/client/member-name.test.d.ts +1 -0
  58. package/lib/types/client/roster.d.ts +26 -0
  59. package/lib/types/client/roster.test.d.ts +1 -0
  60. package/lib/types/client/styles.d.ts +3 -0
  61. package/package.json +104 -0
  62. package/roles/builder.md +40 -0
  63. package/roles/delegate.md +36 -0
  64. package/roles/lead.md +46 -0
  65. package/roles/oracle.md +36 -0
  66. package/roles/researcher.md +37 -0
  67. package/roles/reviewer.md +45 -0
  68. package/roles/scout.md +36 -0
  69. package/roles/verifier.md +36 -0
@@ -0,0 +1,729 @@
1
+ import { Migration } from "./migrations.js";
2
+ import { TeamState } from "./team-store.js";
3
+ //#region src/task-store.d.ts
4
+ export type TaskKind = 'work' | 'research' | 'requirements' | 'implementation' | 'verification' | 'review' | 'repair' | 'integration';
5
+ export type TaskStatus = 'draft' | 'ready' | 'claimed' | 'in_progress' | 'waiting' | 'needs_human' | 'completed' | 'failed' | 'cancelled';
6
+ export type TaskRoute = {
7
+ type: 'report';
8
+ to: string | 'human';
9
+ } | {
10
+ type: 'review';
11
+ reviewer: string;
12
+ } | {
13
+ type: 'interactive';
14
+ then: TaskRoute;
15
+ } | {
16
+ type: 'custom';
17
+ description: string;
18
+ };
19
+ /** The durable task shape deliberately leaves room for later Phase 4 fields. */
20
+ export type HumanContact = 'none' | 'expected' | 'required';
21
+ export interface TeamTask {
22
+ id: string;
23
+ teamId: string;
24
+ title: string;
25
+ kind: TaskKind;
26
+ status: TaskStatus;
27
+ assignee?: string;
28
+ route?: TaskRoute;
29
+ humanContact?: HumanContact;
30
+ activeAttemptId?: string;
31
+ latestSubmissionId?: string;
32
+ acceptedSubmissionId?: string;
33
+ acceptance: string[];
34
+ roundLimit: number;
35
+ createdBy: string;
36
+ createdAt: number;
37
+ updatedAt: number;
38
+ revision: number;
39
+ /** Set after lead acceptance; the authored document then lives under archive/. */
40
+ archivedPath?: string;
41
+ /** Latest review round, populated by list projections when reviews exist. */
42
+ round?: number;
43
+ /**
44
+ * Tasks in this team that must be `completed` before this one may be claimed.
45
+ * Immutable in B26: changing them is contract editing (B27).
46
+ */
47
+ dependsOn?: string[];
48
+ /**
49
+ * Workspace-relative globs this task may touch (B30). The lead declares them; two
50
+ * writing tasks with intersecting declarations do not run at the same time, and a
51
+ * submission records what actually changed outside them.
52
+ */
53
+ paths?: string[];
54
+ /**
55
+ * Bumped only when the *work itself* is restated — creation, a contract update, an
56
+ * acceptance, a revision — never by claiming, acknowledging, submitting or revoking.
57
+ * A mark left by a human intervention is resolved by exactly that (B31), so a
58
+ * revocation cannot silently un-suppress a task whose contract nobody rewrote.
59
+ */
60
+ contractRevision?: number;
61
+ /**
62
+ * The authored narrative of the contract. It used to live only in the rendered
63
+ * document, which made the record a partial copy of its own contract and made
64
+ * editing impossible without losing it (B27).
65
+ */
66
+ why?: string;
67
+ body?: string;
68
+ checks?: string[];
69
+ /** True only in list projections when this task's authored document is absent. */
70
+ documentMissing?: boolean;
71
+ [key: string]: unknown;
72
+ /**
73
+ * Written just before an integration moves the branch, cleared by the write that finishes the
74
+ * acceptance. A process that dies in between leaves it behind, which is how the reconciler can tell
75
+ * "the branch moved but the task was never completed" from "nothing happened" (review finding 1).
76
+ */
77
+ integrationPending?: {
78
+ baseRef: string;
79
+ headSha: string;
80
+ baseTipSha: string;
81
+ at: number;
82
+ reportedAt?: number;
83
+ };
84
+ }
85
+ export interface TaskAttempt {
86
+ id: string;
87
+ taskId: string;
88
+ memberId: string;
89
+ sessionId: string;
90
+ bindingGeneration: number;
91
+ generation: number;
92
+ /**
93
+ * `revoking` is durable on purpose: the capability is already invalid and the
94
+ * session has been asked to stop, but quiescence has not been observed yet. A
95
+ * crash mid-transaction then leaves an honest record, not an active-looking
96
+ * attempt (B28).
97
+ */
98
+ status: 'active' | 'revoking' | 'revoked' | 'completed' | 'failed';
99
+ startedAt: number;
100
+ /**
101
+ * When a human wrote into this member's Session directly, mid-attempt (B31), and the
102
+ * task revision at that moment. The mark stops automatic dispatch until the contract
103
+ * is formally restated — an update, an acceptance or a revision bumps the task
104
+ * revision above this value, which is what resolves it.
105
+ */
106
+ humanIntervenedAt?: number;
107
+ humanIntervenedContractRevision?: number;
108
+ /**
109
+ * When the reconciler first noticed that this attempt is not moving: the assignment was never
110
+ * acknowledged, or the member's Session went idle with the work unfinished (F3 of the live pilot).
111
+ * Recorded on the attempt so the notice survives restarts and is written once, not every pass.
112
+ * Nothing is revoked on the strength of it — silence is not death, it is something to show.
113
+ */
114
+ stalled?: {
115
+ at: number;
116
+ reason: 'unacknowledged' | 'idle';
117
+ detail: string;
118
+ };
119
+ /**
120
+ * Phase 6: the branch and tip this attempt was based on, recorded when it claimed the task.
121
+ * It is the acceptance precondition — integration refuses if the base moved since.
122
+ */
123
+ gitBase?: {
124
+ ref: string;
125
+ baseTipSha: string;
126
+ };
127
+ revision: number;
128
+ }
129
+ export interface AttemptsState {
130
+ schemaVersion: number;
131
+ revision: number;
132
+ teamId: string;
133
+ attempts: TaskAttempt[];
134
+ [key: string]: unknown;
135
+ }
136
+ export interface ClaimTaskRequest {
137
+ taskId: string;
138
+ memberId: string;
139
+ sessionId: string;
140
+ bindingGeneration: number;
141
+ }
142
+ export interface ClaimTaskResult {
143
+ task: TeamTask;
144
+ attempt: TaskAttempt;
145
+ }
146
+ export interface AcknowledgeTaskRequest {
147
+ taskId: string;
148
+ memberId: string;
149
+ /** Required: the fence of ADR 0005 is checked against the binding the caller presents. */
150
+ sessionId: string;
151
+ bindingGeneration: number;
152
+ }
153
+ export interface TasksState {
154
+ schemaVersion: number;
155
+ revision: number;
156
+ teamId: string;
157
+ tasks: TeamTask[];
158
+ [key: string]: unknown;
159
+ }
160
+ /**
161
+ * A contract edit. An absent key means "leave it"; `null` clears a field that is
162
+ * allowed to be empty. Only the lead may send one, and only while the task is
163
+ * `draft` or `ready` — see the B27 contract.
164
+ */
165
+ export interface UpdateTaskRequest {
166
+ taskId: string;
167
+ actor: string;
168
+ expectedRevision?: number;
169
+ title?: string;
170
+ kind?: TaskKind;
171
+ assignee?: string | null;
172
+ route?: TaskRoute | null;
173
+ acceptance?: readonly string[];
174
+ checks?: readonly string[];
175
+ why?: string;
176
+ body?: string;
177
+ roundLimit?: number;
178
+ humanContact?: HumanContact;
179
+ dependsOn?: readonly string[] | null;
180
+ paths?: readonly string[] | null;
181
+ }
182
+ export type UpdateTaskResult = {
183
+ readonly task: TeamTask;
184
+ readonly document: string;
185
+ readonly changed: readonly string[];
186
+ };
187
+ /**
188
+ * Take work away from its current attempt (B28).
189
+ *
190
+ * `nextAssignee` moves ownership inside the same transaction; the contract is not
191
+ * touched, and the new owner claims the task themselves — the only path that has
192
+ * ever created an attempt.
193
+ */
194
+ export interface RevokeTaskRequest {
195
+ taskId: string;
196
+ actor: string;
197
+ reason: string;
198
+ nextAssignee?: string;
199
+ expectedRevision?: number;
200
+ }
201
+ export type RevokeTaskResult = {
202
+ readonly task: TeamTask;
203
+ readonly attempt: TaskAttempt;
204
+ readonly quiesced: boolean;
205
+ readonly reassigned: boolean;
206
+ };
207
+ export interface CreateTaskRequest {
208
+ title: string;
209
+ kind: TaskKind;
210
+ assignee?: string;
211
+ route?: TaskRoute;
212
+ acceptance: readonly string[];
213
+ humanContact?: HumanContact;
214
+ checks?: readonly string[];
215
+ why?: string;
216
+ body?: string;
217
+ roundLimit?: number;
218
+ dependsOn?: readonly string[];
219
+ paths?: readonly string[];
220
+ actor: string;
221
+ expectedRevision?: number;
222
+ }
223
+ export type SubmissionArtifact = {
224
+ type: 'report';
225
+ bodyRef: string;
226
+ bodyDigest: string;
227
+ /**
228
+ * Where the frozen bytes live, relative to the team directory. The digest proves *what* was
229
+ * submitted; this makes it possible to read it back after the workspace moved on — the author can
230
+ * rewrite the file at `bodyRef` the moment after submitting, and a reviewer that reads the path
231
+ * would see the new text while the digest still describes the old one.
232
+ */
233
+ blobPath?: string;
234
+ } | {
235
+ type: 'files';
236
+ paths: string[];
237
+ manifestDigest: string;
238
+ /** One frozen copy per path, in the same order as `paths`. */
239
+ blobs?: {
240
+ path: string;
241
+ digest: string;
242
+ blobPath: string;
243
+ }[];
244
+ };
245
+ export type SubmissionSourceArtifact = {
246
+ type: 'report';
247
+ bodyRef: string;
248
+ } | {
249
+ type: 'files';
250
+ paths: string[];
251
+ };
252
+ export type SubmissionCheck = {
253
+ command: string;
254
+ status: 'passed' | 'failed';
255
+ output?: string;
256
+ };
257
+ /**
258
+ * What the declaration was worth at submission time (B30). `unchecked` is a
259
+ * first-class outcome: an unobservable workspace must not read as a clean one.
260
+ */
261
+ export interface SubmissionScope {
262
+ declared: string[];
263
+ changed?: string[];
264
+ unexpected?: string[];
265
+ /**
266
+ * Paths outside this declaration that another *active* task was writing at the same
267
+ * time. The first parallel run showed that in one shared tree this is what most
268
+ * "unexpected" changes are: a colleague working on their own package, not an
269
+ * intrusion into this one. The name says what was observed.
270
+ */
271
+ sharedTree?: string[];
272
+ state: 'clean' | 'violation' | 'shared-tree' | 'unchecked';
273
+ /**
274
+ * Where this record's evidence came from (B32): `git` in a work tree, `files` in a folder
275
+ * that is not a repository. The record keeps its shape either way; the mode says what the
276
+ * evidence is worth.
277
+ */
278
+ mode?: 'git' | 'files';
279
+ /** Why the workspace could not be observed, when `state` is `unchecked`. */
280
+ reason?: 'no-repository' | 'too-many-files' | 'no-snapshot' | 'unreadable';
281
+ /**
282
+ * Phase 6: the paths that went into the submission's commit — exactly what the member
283
+ * submitted. `changed` stays the working tree's own story, so an edit outside the declaration
284
+ * is still visible even though the reviewed artifact is a commit.
285
+ */
286
+ committed?: string[];
287
+ }
288
+ /**
289
+ * The Git half of a submission (ADR 0006): the commit the plugin built from the submitted paths,
290
+ * the base it was built on, and the boundary of the reviewed diff.
291
+ */
292
+ export interface SubmissionGit {
293
+ baseRef: string;
294
+ baseTipSha: string;
295
+ /**
296
+ * Set when the recorded claim-time base had already moved by the time the submission was frozen
297
+ * and the movement could not touch the declared paths: the commit is parented on the base as it
298
+ * was at freeze time, and this names the base the attempt actually started from.
299
+ */
300
+ rebasedFrom?: string;
301
+ headSha: string;
302
+ mergeBaseSha?: string;
303
+ commitPaths: string[];
304
+ }
305
+ /** A path whose workspace content no longer matches what the submission froze. */
306
+ export interface SubmissionDrift {
307
+ path: string;
308
+ expected: string;
309
+ /** `null` when the file is gone. */
310
+ actual: string | null;
311
+ }
312
+ export interface Submission {
313
+ id: string;
314
+ taskId: string;
315
+ attemptId: string;
316
+ attemptGeneration: number;
317
+ artifact: SubmissionArtifact;
318
+ /**
319
+ * Phase 6: the commit this submission is reviewed as. It belongs to the submission, not to an
320
+ * artifact shape — a coding task that submits a report still has a diff to review.
321
+ */
322
+ git?: SubmissionGit;
323
+ summary: string;
324
+ checks: SubmissionCheck[];
325
+ scope?: SubmissionScope;
326
+ createdAt: number;
327
+ revision: number;
328
+ }
329
+ export type ReviewFinding = {
330
+ severity: 'low' | 'medium' | 'high' | 'blocker';
331
+ problem: string;
332
+ requiredFix: string;
333
+ artifactLocation?: string;
334
+ };
335
+ export type ReviewVerdict = 'pass' | 'needs_revision' | 'escalate';
336
+ export interface ReviewRound {
337
+ id: string;
338
+ taskId: string;
339
+ submissionId: string;
340
+ reviewerMemberId: string;
341
+ round: number;
342
+ verdict: ReviewVerdict;
343
+ findings: ReviewFinding[];
344
+ evidence: SubmissionCheck[];
345
+ createdAt: number;
346
+ revision: number;
347
+ }
348
+ export interface SubmissionsState {
349
+ schemaVersion: number;
350
+ revision: number;
351
+ teamId: string;
352
+ submissions: Submission[];
353
+ [key: string]: unknown;
354
+ }
355
+ export interface ReviewsState {
356
+ schemaVersion: number;
357
+ revision: number;
358
+ teamId: string;
359
+ rounds: ReviewRound[];
360
+ [key: string]: unknown;
361
+ }
362
+ export interface SubmitTaskRequest {
363
+ taskId: string;
364
+ memberId: string;
365
+ sessionId: string;
366
+ bindingGeneration: number;
367
+ artifact: SubmissionSourceArtifact;
368
+ summary: string;
369
+ checks: readonly SubmissionCheck[];
370
+ }
371
+ export interface AcceptTaskRequest {
372
+ taskId: string;
373
+ actor: string;
374
+ note?: string;
375
+ }
376
+ export interface AcceptTaskResult {
377
+ task: TeamTask;
378
+ archivedPath: string;
379
+ }
380
+ export interface ReviseTaskRequest {
381
+ taskId: string;
382
+ actor: string;
383
+ instructions: string;
384
+ }
385
+ export interface ReviseTaskResult {
386
+ task: TeamTask;
387
+ }
388
+ export interface ReviewTaskRequest {
389
+ taskId: string;
390
+ submissionId: string;
391
+ reviewerMemberId: string;
392
+ verdict: ReviewVerdict;
393
+ findings?: readonly ReviewFinding[];
394
+ evidence?: readonly SubmissionCheck[];
395
+ }
396
+ export interface ReviewTaskResult {
397
+ round: ReviewRound;
398
+ task: TeamTask;
399
+ }
400
+ export interface ReadTaskResult {
401
+ task: TeamTask;
402
+ document: string;
403
+ /**
404
+ * Submissions as they were frozen, plus a drift note per submission: paths whose workspace content
405
+ * no longer matches the frozen bytes. A reviewer that reads the workspace path would otherwise
406
+ * judge text the digest does not describe.
407
+ */
408
+ submissions: (Submission & {
409
+ drift?: SubmissionDrift[];
410
+ })[];
411
+ rounds: ReviewRound[];
412
+ /**
413
+ * Every attempt this task ever had, oldest first. A task sitting in `claimed` with an
414
+ * attempt nobody acknowledged is the state a human most needs to see, and reading the
415
+ * journal for it was the only way before B31.
416
+ */
417
+ attempts: TaskAttempt[];
418
+ }
419
+ export interface CreateTaskResult {
420
+ task: TeamTask;
421
+ documentPath: string;
422
+ }
423
+ export interface ListTasksOptions {
424
+ includeArchived?: boolean;
425
+ }
426
+ type TaskStoreOptions = {
427
+ workspacePath: string;
428
+ teamId: string;
429
+ appendAudit?: (event: unknown) => Promise<void>;
430
+ deliverEscalation?: (request: {
431
+ leadMemberId: string;
432
+ taskId: string;
433
+ submissionId: string;
434
+ round: ReviewRound;
435
+ reviewerMemberId: string;
436
+ problem: string;
437
+ tried: string;
438
+ }) => Promise<void>;
439
+ deliverClosing?: (request: {
440
+ assignee: string;
441
+ taskId: string;
442
+ note?: string;
443
+ }) => Promise<void>;
444
+ deliverDirection?: (request: {
445
+ assignee: string;
446
+ taskId: string;
447
+ instructions: string;
448
+ }) => Promise<void>;
449
+ /**
450
+ * Ask a Session to stop. Absent means the store cannot interrupt, and a
451
+ * revocation then refuses rather than pretending the session was stopped.
452
+ */
453
+ interruptSession?: (sessionId: string, reason: string) => Promise<void>;
454
+ /** Observe that a Session is no longer running. `false` means "not observed in time". */
455
+ awaitQuiescence?: (sessionId: string, timeoutMs: number) => Promise<boolean>;
456
+ quiescenceTimeoutMs?: number;
457
+ /**
458
+ * What changed in the workspace at submission time (B30). `undefined` means "not
459
+ * observable" — no git, or not a repository — and is recorded as `unchecked`
460
+ * rather than as a clean result. The store never shells out itself.
461
+ */
462
+ changedPaths?: (workspacePath: string) => Promise<readonly string[] | undefined>;
463
+ /**
464
+ * B32: which observation this workspace supports. Absent means the composition cannot
465
+ * tell, and a scope record says `unchecked (unreadable)` rather than reading as clean.
466
+ */
467
+ workspaceMode?: (workspacePath: string) => Promise<'git' | 'files' | undefined>;
468
+ /** Migration steps to use; the module's registry by default, injectable so a test can add one. */
469
+ migrations?: Readonly<Record<number, Migration>>;
470
+ /** B32: a cheap fingerprint of the tree, taken at claim and at submit in `files` mode. */
471
+ snapshotTree?: (workspacePath: string) => Promise<Readonly<Record<string, string>> | undefined>;
472
+ /**
473
+ * Phase 6 (ADR 0006): Git plumbing, injected the same way the changed-paths port is. The store
474
+ * never shells out itself. Absent means submissions carry no Git artifact — a workspace without
475
+ * a repository, or a composition that does not offer one.
476
+ */
477
+ git?: {
478
+ base: (workspacePath: string) => Promise<{
479
+ ref: string;
480
+ sha: string;
481
+ } | undefined>;
482
+ tip: (workspacePath: string, ref: string) => Promise<string | undefined>;
483
+ commit: (workspacePath: string, base: {
484
+ ref: string;
485
+ sha: string;
486
+ } | undefined, request: {
487
+ paths: readonly string[];
488
+ message: string;
489
+ author: {
490
+ name: string;
491
+ email: string;
492
+ };
493
+ }) => Promise<{
494
+ headSha: string;
495
+ treeSha: string;
496
+ commitPaths: readonly string[];
497
+ mergeBaseSha?: string;
498
+ } | undefined>;
499
+ pin: (workspacePath: string, ref: string, sha: string) => Promise<boolean>;
500
+ integrate: (workspacePath: string, ref: string, headSha: string, expectedTip: string | undefined, commitPaths?: readonly string[]) => Promise<'integrated' | 'stale' | 'index-failed'>;
501
+ /** Paths changed between two commits, for deciding whether a moved base can be refreshed safely. */
502
+ changedBetween: (workspacePath: string, fromSha: string, toSha: string) => Promise<readonly string[] | undefined>;
503
+ };
504
+ };
505
+ /**
506
+ * The dependencies that still stand between a task and its claim, in the order
507
+ * the contract lists them. A dependency in a terminal non-completed state is
508
+ * included on purpose: it blocks forever and the human must see that.
509
+ */
510
+ export declare function blockedBy(task: Pick<TeamTask, 'dependsOn'>, tasks: readonly TeamTask[]): Array<{
511
+ id: string;
512
+ title: string;
513
+ status: TaskStatus;
514
+ }>;
515
+ export declare function routeText(route: TaskRoute | undefined, team: TeamState): string;
516
+ export declare function assignmentRouteText(route: TaskRoute | undefined, team: TeamState): string;
517
+ export declare function deriveRouteContacts(task: TeamTask, team: TeamState): string[];
518
+ export declare function routeAllowsContact(task: TeamTask, from: string, to: string, team: TeamState): boolean;
519
+ export declare function taskDocumentPath(workspacePath: string, teamId: string, taskId: string): string;
520
+ declare function renderDocument(task: TeamTask, team: TeamState, fields?: {
521
+ why?: string;
522
+ checks?: readonly string[];
523
+ body?: string;
524
+ tasks?: readonly TeamTask[];
525
+ }): string;
526
+ export declare class TaskStore {
527
+ private readonly location;
528
+ private readonly workspacePath;
529
+ private readonly appendAudit;
530
+ private readonly deliverEscalation?;
531
+ private readonly deliverClosing?;
532
+ private readonly deliverDirection?;
533
+ private readonly interruptSession?;
534
+ private readonly awaitQuiescence?;
535
+ private readonly quiescenceTimeoutMs;
536
+ private readonly changedPaths;
537
+ private readonly git;
538
+ private readonly migrations;
539
+ private readonly workspaceMode;
540
+ private readonly snapshotTree;
541
+ /** `null` means "no port": the answer is cached because a store lives with one workspace. */
542
+ private resolvedMode;
543
+ constructor(options: TaskStoreOptions);
544
+ get filePath(): string;
545
+ get directoryPath(): string;
546
+ open(): Promise<TasksState>;
547
+ listTasks(options?: ListTasksOptions | boolean): Promise<TeamTask[]>;
548
+ readTask(taskId: string): Promise<ReadTaskResult>;
549
+ readAttempts(): Promise<AttemptsState>;
550
+ readSubmissions(): Promise<SubmissionsState>;
551
+ readReviews(): Promise<ReviewsState>;
552
+ activeTaskForMember(memberId: string): Promise<TeamTask | undefined>;
553
+ activeTaskForMemberSync(memberId: string): TeamTask | undefined;
554
+ /**
555
+ * The latest attempt of one task, read synchronously for context assembly: the member
556
+ * has to know that a human wrote into its Session mid-flight (B31).
557
+ */
558
+ latestAttemptForTaskSync(taskId: string): TaskAttempt | undefined;
559
+ /**
560
+ * Every task, read synchronously for context assembly. The authorization gate
561
+ * scans all tasks — a reviewer holds a path to a task it is not the assignee of —
562
+ * so the line a member reads about where it may send has to see the same set.
563
+ */
564
+ listTasksSync(): TeamTask[];
565
+ /**
566
+ * Close attempts whose task has already ended (F5 of the first live run).
567
+ *
568
+ * Both acceptance paths are supposed to close the attempt, but "supposed to" is how
569
+ * the member stayed busy forever: a stale active attempt blocks every later task for
570
+ * that member and keeps the intervention observer watching a finished Session. This
571
+ * heals the class rather than the instance — a task in a terminal status cannot have
572
+ * work in flight.
573
+ */
574
+ closeTerminalAttempts(): Promise<{
575
+ closed: {
576
+ attemptId: string;
577
+ taskId: string;
578
+ memberId: string;
579
+ status: 'completed' | 'failed';
580
+ }[];
581
+ }>;
582
+ /**
583
+ * Record that a human wrote into this member's Session mid-attempt (B31). Idempotent:
584
+ * the first observation wins, and the task revision at that moment is stored so that a
585
+ * later formal restatement of the contract resolves the mark without a second write.
586
+ */
587
+ markHumanIntervention(attemptId: string, at: number): Promise<{
588
+ attempt: TaskAttempt;
589
+ changed: boolean;
590
+ }>;
591
+ private contractRevisionLocked;
592
+ submitTask(request: SubmitTaskRequest): Promise<{
593
+ submission: Submission;
594
+ }>;
595
+ acceptTask(request: AcceptTaskRequest): Promise<AcceptTaskResult>;
596
+ reviseTask(request: ReviseTaskRequest): Promise<ReviseTaskResult>;
597
+ reviewTask(request: ReviewTaskRequest): Promise<ReviewTaskResult>;
598
+ /**
599
+ * What the declaration was worth at submission time. Only a writing task that
600
+ * declared paths is checked: with nothing declared there is nothing to violate, and
601
+ * an unobservable workspace is reported as `unchecked` rather than as clean (B30).
602
+ */
603
+ /**
604
+ * Integrate a submission's commit into its base, refusing when the base moved.
605
+ *
606
+ * Nothing is checked out and nothing is merged: the working tree already holds the work, so
607
+ * integration is one compare-and-swap ref update. `STALE_BASE` is a refusal — nothing is written,
608
+ * and the decision has to be made again against the current base (ADR 0006).
609
+ */
610
+ private integrateSubmission;
611
+ /**
612
+ * Record the intent to integrate, and return the task as it now stands.
613
+ *
614
+ * `Locked` on purpose: the callers (both acceptance paths) already hold the team lock, and taking it
615
+ * again deadlocks the process — which is exactly what the git suite reported, as a thirty-second
616
+ * hang rather than a failure.
617
+ */
618
+ private writeIntegrationPendingLocked;
619
+ /** Forget the intent: either the integration did not happen, or the caller is about to finish it. */
620
+ private clearIntegrationPendingLocked;
621
+ /**
622
+ * Where an archived document lives. New records store the path relative to the team directory;
623
+ * records written before that change hold an absolute path and are still honoured.
624
+ */
625
+ private archivedPathOf;
626
+ /**
627
+ * Record that an active attempt is not moving, once. Idempotent by design: a second call with the
628
+ * same reason is a no-op, so the reconciler may call it on every pass without filling the journal.
629
+ * Returns `true` when this call was the one that recorded it.
630
+ */
631
+ markAttemptStalled(request: {
632
+ attemptId: string;
633
+ reason: 'unacknowledged' | 'idle';
634
+ detail: string;
635
+ actor: string;
636
+ }): Promise<boolean>;
637
+ /**
638
+ * Keep a copy of submitted bytes, addressed by their digest. Returns the path relative to the team
639
+ * directory, or `undefined` when the copy could not be written — a submission is never refused for
640
+ * this, but it is then read back from the workspace and the drift check says so.
641
+ */
642
+ private storeBlob;
643
+ /** The frozen bytes of a submission artifact, or `undefined` when no copy was kept. */
644
+ readBlob(blobPath: string): Promise<Buffer | undefined>;
645
+ /**
646
+ * Reconcile an integration that was interrupted: the marker is on the task, and the branch tells
647
+ * the rest of the story. Reports once, and clears the marker only when nothing happened — a branch
648
+ * that is already at the reviewed commit means the acceptance still has to be finished, and that is
649
+ * a decision, not a cleanup.
650
+ */
651
+ reconcilePendingIntegrations(request: {
652
+ tipOf: (ref: string) => Promise<string | undefined>;
653
+ actor: string;
654
+ }): Promise<{
655
+ taskId: string;
656
+ state: 'moved' | 'untouched' | 'elsewhere';
657
+ detail: string;
658
+ }[]>;
659
+ /**
660
+ * Bring every state file this store owns forward to the current schema version.
661
+ *
662
+ * Reading tolerates an older version, but nothing brought it forward: a field added later stayed
663
+ * missing until something happened to write the file again, and no record said the state had been
664
+ * read at an older version. A state from the future is refused with both versions named.
665
+ *
666
+ * The file is copied before it is rewritten, and the migration is recorded in the journal: a
667
+ * migration is the one operation that changes history, and it should be possible to say when it
668
+ * happened and to go back.
669
+ */
670
+ migrate(): Promise<{
671
+ files: {
672
+ file: string;
673
+ from: number;
674
+ to: number;
675
+ backup: string;
676
+ }[];
677
+ }>;
678
+ /** B32: resolved once per store; the answer cannot change while a store lives. */
679
+ private mode;
680
+ private snapshotPathFor;
681
+ /** Take the fingerprint a `files`-mode submission will be compared against. */
682
+ private takeSnapshot;
683
+ /** Delete fingerprints whose attempt is no longer running: garbage, not evidence. */
684
+ pruneSnapshots(): Promise<number>;
685
+ private checkScope;
686
+ private freezeArtifact;
687
+ /**
688
+ * Edit the contract of one task (B27).
689
+ *
690
+ * Only while nobody is working on it: an attempt is bound to the contract its
691
+ * assignee is working from, and a human decision owns every later transition.
692
+ */
693
+ updateTask(request: UpdateTaskRequest): Promise<UpdateTaskResult>;
694
+ claimTask(request: ClaimTaskRequest): Promise<ClaimTaskResult>;
695
+ /**
696
+ * Take work away from its current attempt (B28, ADR 0005).
697
+ *
698
+ * The order matters and is the contract: the capability is invalidated and made
699
+ * durable *before* the session is asked to stop, so a session that keeps running
700
+ * cannot write anything; and `revoked` is only ever reported once quiescence has
701
+ * actually been observed. When it has not, the attempt stays `revoking` and the
702
+ * task goes to the human rather than claiming a stop that did not happen.
703
+ */
704
+ revokeTask(request: RevokeTaskRequest): Promise<RevokeTaskResult>;
705
+ /** Mark the settled outcome: revoked and re-owned, or escalated to the human. */
706
+ private settleRevocation;
707
+ acknowledgeTask(request: AcknowledgeTaskRequest): Promise<{
708
+ task: TeamTask;
709
+ }>;
710
+ createTask(request: CreateTaskRequest): Promise<CreateTaskResult>;
711
+ private get teamId();
712
+ private documentPath;
713
+ private readOrCreateLocked;
714
+ private readTasksIfPresentLocked;
715
+ private readAttemptsIfPresentLocked;
716
+ private readSubmissionsIfPresentLocked;
717
+ private readReviewsIfPresentLocked;
718
+ private readTeamLocked;
719
+ private validateCreate;
720
+ /**
721
+ * The one place the contract rules live, shared by creation (B19) and editing
722
+ * (B27): a second copy of them is how the writer and the reader start to
723
+ * disagree about what a valid contract is.
724
+ */
725
+ private validateContract;
726
+ }
727
+ export declare function createTaskStore(options: TaskStoreOptions): TaskStore;
728
+ //#endregion
729
+ export { renderDocument };