@looply/sdk 0.0.0-stage → 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.
@@ -0,0 +1,701 @@
1
+ /**
2
+ * WHAT AN EXTERNAL CLIENT IS PROMISED, AND NOTHING ELSE.
3
+ *
4
+ * ─────────────────────────────────────────────────────────────────────────────
5
+ * 🔴 WHITELISTS, NOT PASS-THROUGHS.
6
+ *
7
+ * Every type here names its fields. None is `Row & {…}`, none spreads a
8
+ * database record, and none is an alias for a domain type that might grow. That
9
+ * is the whole point: a field added to a Prisma model or to `@looply/core` must
10
+ * not become part of a public contract because somebody widened a type
11
+ * elsewhere. It becomes public when it is written down here, deliberately.
12
+ *
13
+ * `contract.spec.ts` asserts the field SETS, so a silent addition fails a test
14
+ * rather than shipping.
15
+ *
16
+ * ─────────────────────────────────────────────────────────────────────────────
17
+ * 🔴 PROVIDER-NEUTRAL BY CONSTRUCTION
18
+ *
19
+ * No field here is named for a provider, and none carries a session id, a tool
20
+ * name, a permission mode or an MCP concept. An external client integrating
21
+ * Looply is buying "governed autonomous work on a repository"; which model
22
+ * performs it is Looply's business and may change under them.
23
+ *
24
+ * ⚠️ `provider` and `model` may appear one day as DIAGNOSTICS. They are absent
25
+ * now rather than optional, because an optional field is one a client starts
26
+ * depending on.
27
+ */
28
+ export interface ProjectView {
29
+ readonly id: string;
30
+ readonly name: string;
31
+ /**
32
+ * ⚠️ NO LOCAL PATH. Where the repository sits on somebody's disk is a fact
33
+ * about their machine, contains their account name, and is of no use to a
34
+ * client that cannot reach that disk anyway.
35
+ */
36
+ readonly createdAt: string;
37
+ }
38
+ /**
39
+ * ⚠️ THERE IS NO `met` STATE, AND AN SDK THAT OFFERED ONE WOULD BE LYING.
40
+ *
41
+ * A goal is `draft` until somebody chooses to pursue it, `active` while it is
42
+ * pursued, and `abandoned` if they give up on it. "Met" is not recorded: whether
43
+ * an intention was satisfied is a judgement a person makes by looking at the
44
+ * work, not a column, and inventing one here would promise a client a fact the
45
+ * server does not have.
46
+ *
47
+ * 🔴 `draft` IS THE STATE A NEW GOAL IS BORN IN. A client that renders only
48
+ * `active` will show nothing after a successful `createGoal`.
49
+ */
50
+ export type GoalStateView = 'draft' | 'active' | 'abandoned';
51
+ /** One requirement or constraint of a stored source a goal serves. */
52
+ export interface RequirementLinkView {
53
+ readonly sourceId: string;
54
+ /** The statement's ordinal within the source. */
55
+ readonly ordinal: number;
56
+ }
57
+ export interface GoalView {
58
+ readonly id: string;
59
+ readonly projectId: string;
60
+ /** The person's own wording, preserved verbatim. Never a summary. */
61
+ readonly text: string;
62
+ readonly state: GoalStateView;
63
+ /**
64
+ * The optimistic-concurrency token AND the identity of the intent.
65
+ *
66
+ * Amending the wording bumps it, so a client holding version 3 is holding a
67
+ * different question from one holding version 4 — which is why every
68
+ * version-scoped answer below carries it too.
69
+ */
70
+ readonly version: number;
71
+ /** The caller's own key for the creation attempt. See `createGoal`. */
72
+ readonly clientRef: string;
73
+ readonly createdAt: string;
74
+ /**
75
+ * "B after A": ids of goals of the same project this one waits for. Always
76
+ * present, empty when none. The machine will not start this goal while one
77
+ * of them is outstanding; a cycle is refused at write time, named.
78
+ */
79
+ readonly dependsOn: readonly string[];
80
+ /**
81
+ * Repository regions a run bound to this goal may write to, canonical
82
+ * (`packages/orders`, never `./Packages/Orders/`). With `run.surfaces` on,
83
+ * the machine takes a lease for exactly these before any work, and a second
84
+ * run on one of them waits. Always present, empty when none.
85
+ */
86
+ readonly surfaces: readonly string[];
87
+ /** Which requirements and constraints of the project's sources this goal serves. Always present, empty when none. */
88
+ readonly requirements: readonly RequirementLinkView[];
89
+ /**
90
+ * DERIVED, never stored: the keys `<sourceId>#<ordinal>` of the linked
91
+ * requirements a person attested `implemented-and-evidenced` at the tree of
92
+ * the latest measurement. Present on `goal()`, absent on `goals()`. No
93
+ * client can write it and no rule produces it.
94
+ */
95
+ readonly satisfiedCriteria?: readonly string[];
96
+ }
97
+ export type ItemStateView = 'ready' | 'in-progress' | 'done' | 'closed' | 'blocked';
98
+ export interface ItemView {
99
+ readonly id: string;
100
+ readonly projectId: string;
101
+ readonly title: string;
102
+ readonly state: ItemStateView;
103
+ /** The goal that currently owns this work, or null when the queue does. */
104
+ readonly goalId: string | null;
105
+ /** Where in the repository the work is, as project-relative paths. */
106
+ readonly evidence: readonly EvidenceRefView[];
107
+ }
108
+ export interface EvidenceRefView {
109
+ /** Project-relative. Never absolute — see `ProjectView`. */
110
+ readonly path: string;
111
+ readonly line?: number;
112
+ }
113
+ /**
114
+ * WHAT LOOPLY IS DOING ON A PROJECT RIGHT NOW.
115
+ *
116
+ * 🔴 THIS REPLACED A `RunView` THAT WAS PARTLY INVENTED. That interface declared
117
+ * an `outcome` of `'working' | 'finished' | 'failed' | 'held'`, plus an `id` and
118
+ * a `goalVersion` — none of which this route has ever sent. It was written from
119
+ * what a client would find convenient rather than from what the server returns,
120
+ * and it type-checked perfectly, because a cast cannot be wrong about a shape it
121
+ * is asserting. `sdk-flagship.e2e.spec.ts` now enumerates every field below
122
+ * against a live response, with a positive control asserting `outcome` is NOT
123
+ * among them.
124
+ *
125
+ * ⚠️ SO THERE IS NO SINGLE "OUTCOME" FIELD, AND A CLIENT MUST COMPOSE ONE. The
126
+ * server reports `live`, and `stopReason` once a run has ended. Deriving a
127
+ * tidier verdict here would mean this SDK asserting a judgement the server never
128
+ * made — which is the failure the whole package is built to avoid.
129
+ *
130
+ * ⚠️ IT IS A WHITELIST OF A LARGER PROJECTION. The internal `Status` also
131
+ * carries per-round gate steps, item titles, open questions and alerts. Those
132
+ * are the app's working surface and are omitted deliberately: they change with
133
+ * the product's needs, and a client pinned to them would be pinned to the
134
+ * app's release cycle.
135
+ */
136
+ export interface RunStatusView {
137
+ readonly projectId: string | null;
138
+ /** Null when nothing has ever run here. */
139
+ readonly runId: string | null;
140
+ readonly lane: string | null;
141
+ readonly branch: string | null;
142
+ /**
143
+ * 🔴 A PAUSED RUN IS STILL `live`. Its process is alive and its budget is
144
+ * still running down, so this cannot be used alone to decide whether to offer
145
+ * Pause or Resume.
146
+ */
147
+ readonly live: boolean;
148
+ readonly startedAt: string | null;
149
+ readonly endedAt: string | null;
150
+ /** Why the run stopped, in the machine's own words. Null while it runs. */
151
+ readonly stopReason: string | null;
152
+ /** The last event folded into this projection. */
153
+ readonly lastSeq: number;
154
+ readonly lastEventAt: string | null;
155
+ }
156
+ export type ApprovalStateView = 'pending' | 'approved' | 'denied' | 'expired' | 'revoked';
157
+ export interface ApprovalView {
158
+ readonly id: string;
159
+ readonly projectId: string;
160
+ /** The caller's key for the creation attempt. Request identity, not action identity. */
161
+ readonly clientRef: string;
162
+ readonly state: ApprovalStateView;
163
+ /**
164
+ * THE EXACT ACTION, IN THE SERVER'S OWN FLAT SHAPE.
165
+ *
166
+ * 🔴 AN EARLIER VERSION OF THIS INTERFACE NESTED THESE UNDER `action` as
167
+ * `{ type, risk, summary }`. The server has never sent that. It sends
168
+ * `actionType`, `riskClass`, `display` and `action` side by side, and the
169
+ * flagship caught the difference by reading a real response — a mocked
170
+ * `fetch` would have handed back the nested shape this file asked for.
171
+ */
172
+ readonly actionType: string;
173
+ /** The digest the approval is bound to. What was approved is what is executed. */
174
+ readonly actionFingerprint: string;
175
+ readonly riskClass: string;
176
+ /** The action rendered for a person to read. This is what a UI should show. */
177
+ readonly display: string;
178
+ /**
179
+ * The canonical parsed action the fingerprint covers.
180
+ *
181
+ * ⚠️ ITS FIELDS VARY BY `actionType` AND ARE NOT PROMISED ONE BY ONE. Branch
182
+ * on `actionType` first. Typed loosely on purpose: pretending to a fixed shape
183
+ * here would be this SDK asserting a contract the server does not offer.
184
+ *
185
+ * ⚠️ NEVER A CREDENTIAL. The broker holds what it needs; anything here reaches
186
+ * every viewer of the approval screen.
187
+ */
188
+ readonly action: Readonly<Record<string, unknown>>;
189
+ readonly requestedBy: string;
190
+ readonly requestedAt: string;
191
+ readonly expiresAt: string;
192
+ readonly decidedAt: string | null;
193
+ readonly decidedBy: string | null;
194
+ readonly decisionNote: string | null;
195
+ readonly revokedAt: string | null;
196
+ readonly revokedBy: string | null;
197
+ /** Optimistic-concurrency token. Deciding against a stale one is a conflict. */
198
+ readonly version: number;
199
+ /**
200
+ * ADVICE, NEVER AUTHORITY: the latest recommendation attached while pending.
201
+ * `by` is `integration:<id>` or `human:<id>`. Writing it never moves `version`.
202
+ */
203
+ readonly recommendation: {
204
+ readonly decision: 'approve' | 'deny';
205
+ readonly note: string | null;
206
+ readonly by: string;
207
+ readonly at: string;
208
+ } | null;
209
+ }
210
+ export type ExecutionStateView = 'claimed' | 'executing' | 'result-pending' | 'succeeded' | 'failed'
211
+ /**
212
+ * 🔴 THE STATE THAT EXISTS BECAUSE THE OUTSIDE WORLD CAN BE UNCLEAR.
213
+ *
214
+ * Looply attempted something consequential and cannot establish whether it
215
+ * happened. It will NOT retry: a client rendering this must not offer one,
216
+ * and must offer a person the resolution instead.
217
+ */
218
+ | 'ambiguous';
219
+ export interface GovernedExecutionView {
220
+ readonly id: string;
221
+ readonly projectId: string;
222
+ readonly approvalId: string;
223
+ readonly state: ExecutionStateView;
224
+ readonly startedAt: string;
225
+ readonly settledAt: string | null;
226
+ /** Safe, redacted, and never raw command output. */
227
+ readonly detail: string | null;
228
+ }
229
+ /**
230
+ * ONE ACT OF PERMISSION TO PURSUE A GOAL AUTONOMOUSLY, AND WHAT BECAME OF IT.
231
+ *
232
+ * 🔴 THIS REPLACED AN INVENTED `SafetyView`. That interface had a `holding`
233
+ * field carrying reasons like `no_progress` and `oscillation` — which are
234
+ * Phase 9's LOOP-SAFETY reasons, enforced on the machine in
235
+ * `loop-safety-store.ts` and, as this SDK's own documentation said two
236
+ * paragraphs earlier, never sent to the server. It described a route that does
237
+ * not exist using vocabulary that never travels. Both halves were wrong and
238
+ * neither was catchable by a type.
239
+ *
240
+ * What the server actually knows is that a person authorised pursuit, of which
241
+ * goal at which version, how many Starts it was worth, and — once it is over —
242
+ * why it ended.
243
+ */
244
+ export type GoalAutonomyStateView = string;
245
+ export interface GoalAutonomyView {
246
+ readonly controllerId: string;
247
+ readonly goalId: string;
248
+ /**
249
+ * 🔴 VERSION-SCOPED, AND THAT IS A SAFETY PROPERTY. Permission to pursue
250
+ * wording 4 is not permission to pursue wording 5 — otherwise editing a goal
251
+ * would silently re-authorise a machine against text nobody looked at.
252
+ */
253
+ readonly goalVersion: number;
254
+ /**
255
+ * Which act of permission this was within the version. A version can have
256
+ * several, and only the newest can still be cancelled — so two rows for one
257
+ * version are indistinguishable without this.
258
+ */
259
+ readonly generation: number;
260
+ readonly state: GoalAutonomyStateView;
261
+ /** Why it ended, in the words a person reads. Null while it is still running. */
262
+ readonly reason: string | null;
263
+ /** How many Starts this grant was worth. The budget, not a suggestion. */
264
+ readonly maxStarts: number;
265
+ readonly expiresAt: string;
266
+ readonly version: number;
267
+ readonly createdBy: string;
268
+ readonly createdAt: string;
269
+ }
270
+ /**
271
+ * The pursuits of a goal, beside the goal as it stands.
272
+ *
273
+ * ⚠️ THE TWO ARRIVE TOGETHER ON PURPOSE. A caller deciding whether to spend
274
+ * another Start needs both, and reading them separately means acting on two
275
+ * answers from two moments with an edit able to land in between. The server
276
+ * returns them in one response for exactly that reason, and this SDK does not
277
+ * split them back apart.
278
+ */
279
+ export interface GoalAutonomyReport {
280
+ readonly autonomy: readonly GoalAutonomyView[];
281
+ /** Null when the goal does not exist, or is not visible to this caller. */
282
+ readonly goal: {
283
+ readonly version: number;
284
+ readonly state: GoalStateView;
285
+ } | null;
286
+ }
287
+ /** One gate step as the round's gate recorded it. No output: only the verdict and timing. */
288
+ export interface EvidenceGateStepView {
289
+ readonly step: string;
290
+ readonly ok: boolean;
291
+ readonly ms: number | null;
292
+ readonly skipped: boolean;
293
+ readonly timedOut: boolean;
294
+ }
295
+ export interface EvidenceRoundView {
296
+ readonly seq: number;
297
+ readonly itemId: string | null;
298
+ readonly itemTitle: string | null;
299
+ readonly attempt: number;
300
+ /** committed | discarded | abandoned, or null while the round is open. */
301
+ readonly outcome: string | null;
302
+ readonly sha: string | null;
303
+ readonly gate: {
304
+ readonly verdict: string;
305
+ readonly steps: readonly EvidenceGateStepView[];
306
+ } | null;
307
+ /** The prover's verdict and confidence — never its prose, which is a model's sentence about the code. */
308
+ readonly proof: {
309
+ readonly verdict: string;
310
+ readonly confidence: number | null;
311
+ } | null;
312
+ /** A person's review of the committed sha: approve | undo, or null. */
313
+ readonly review: string | null;
314
+ }
315
+ export interface EvidenceRunView {
316
+ readonly runId: string;
317
+ readonly branch: string;
318
+ readonly startedAt: string;
319
+ readonly endedAt: string | null;
320
+ readonly stopReason: string | null;
321
+ readonly rounds: readonly EvidenceRoundView[];
322
+ }
323
+ export interface EvidencePursuitView {
324
+ readonly controllerId: string;
325
+ readonly generation: number;
326
+ readonly state: GoalAutonomyStateView;
327
+ readonly reason: string | null;
328
+ readonly maxStarts: number;
329
+ readonly expiresAt: string;
330
+ readonly updatedAt: string;
331
+ }
332
+ /** A push carried out under an approval. The sha is the one read back from the remote when known. */
333
+ export interface EvidencePushView {
334
+ readonly approvalId: string;
335
+ /**
336
+ * The server's own approval state — requested | approved | denied | revoked |
337
+ * expired | claimed — NOT `ApprovalStateView`, which is a mapped vocabulary.
338
+ * A push happens only after `claimed`.
339
+ */
340
+ readonly approvalState: string;
341
+ /** The governed execution's server state, or null when nothing was attempted. */
342
+ readonly executionState: string | null;
343
+ readonly ref: string | null;
344
+ readonly sha: string | null;
345
+ readonly settledAt: string | null;
346
+ }
347
+ export interface GoalVersionEvidenceView {
348
+ readonly goalVersion: number;
349
+ readonly pursuits: readonly EvidencePursuitView[];
350
+ readonly runs: readonly EvidenceRunView[];
351
+ readonly pushes: readonly EvidencePushView[];
352
+ /** Shas committed under this version. A convenience over `runs`. */
353
+ readonly landed: readonly string[];
354
+ }
355
+ /**
356
+ * WHAT THE SERVER SAW OF A GOAL BEING DELIVERED — NOT THE MACHINE'S PROOF.
357
+ *
358
+ * Newest version first; a version appears only if something happened under it.
359
+ * `Round` line counts are absent rather than null: nothing has ever filled
360
+ * them, and a null would read as "nothing changed".
361
+ */
362
+ export interface GoalEvidenceView {
363
+ readonly goal: {
364
+ readonly id: string;
365
+ readonly version: number;
366
+ readonly state: GoalStateView;
367
+ };
368
+ readonly versions: readonly GoalVersionEvidenceView[];
369
+ }
370
+ /**
371
+ * ONE NORMALISED EVENT, ACROSS EVERY INTERNAL LEDGER.
372
+ *
373
+ * 🔴 THE INTERNAL STORES STAY SEPARATE. Run events, approval events and
374
+ * execution events answer different questions, have different authorities and
375
+ * are written by different components — merging them internally would be a
376
+ * regression. This is a PROJECTION for clients, and the projection is what has
377
+ * a single cursor.
378
+ */
379
+ export interface PlatformEventView {
380
+ readonly id: string;
381
+ /**
382
+ * Opaque, monotonic within a project, and the ONLY thing a client should
383
+ * persist to resume.
384
+ *
385
+ * ⚠️ NOT A TIMESTAMP AND NOT AN INTEGER TO DO ARITHMETIC ON. It is compared
386
+ * for equality and passed back verbatim; anything else assumes a structure
387
+ * the server has not promised.
388
+ */
389
+ readonly cursor: string;
390
+ readonly projectId: string;
391
+ readonly type: string;
392
+ readonly occurredAt: string;
393
+ readonly resourceType: 'goal' | 'item' | 'run' | 'approval' | 'execution' | 'project';
394
+ readonly resourceId: string;
395
+ /** Safe, whitelisted, and never raw output or a credential. */
396
+ readonly payload: Readonly<Record<string, unknown>>;
397
+ }
398
+ /**
399
+ * ⚠️ THERE IS NO `EvidenceView`, AND THE ONE THAT WAS HERE HAS BEEN REMOVED.
400
+ *
401
+ * It declared `{ id, projectId, runId, kind, at, summary }` for a `/evidence`
402
+ * route that does not exist. An exported type with no method producing it is the
403
+ * same defect as a method with no route behind it: a promise the package cannot
404
+ * keep, and one a caller will write code against.
405
+ *
406
+ * Gate results, verifier verdicts and failure signatures ride the RUN EVENT
407
+ * LEDGER. Read `events()` and filter by `type`.
408
+ */
409
+ /**
410
+ * WHAT BECAME OF ONE INSTRUCTION.
411
+ *
412
+ * 🔴 THERE ARE EXACTLY TWO STATES AND THERE IS DELIBERATELY NO MIDDLE ONE.
413
+ * "The machine has it" is not a state a client may branch on: a command is
414
+ * either still deliverable or it is finished, and inventing "in progress" would
415
+ * invite a client to render a spinner it can never clear — the daemon does not
416
+ * report picking something up, only acking it.
417
+ *
418
+ * ⚠️ `done` ON A START MEANS THE START WAS ACCEPTED, NOT THAT THE WORK
419
+ * SUCCEEDED. Composing this with `status()` is how a client learns the rest.
420
+ */
421
+ export interface CommandStatusView {
422
+ readonly id: string;
423
+ /** Which instruction this was — `start-run`, `pause`, `stop`, and so on. */
424
+ readonly kind: string;
425
+ readonly state: 'pending' | 'terminal';
426
+ /**
427
+ * ⚠️ ABSENT, NOT NULL, WHILE PENDING — and the difference is not cosmetic.
428
+ *
429
+ * The server OMITS these keys rather than sending null, so a client writing
430
+ * `if (s.outcome === null)` would be comparing against `undefined` and taking
431
+ * the wrong branch. Written down as optional because that is what arrives on
432
+ * the wire; an earlier version of this interface declared them nullable and
433
+ * named the timestamp `settledAt`, which the server has never sent.
434
+ *
435
+ * `expired` is written by no ack — it is inferred from the delivery window.
436
+ */
437
+ readonly outcome?: 'done' | 'refused' | 'expired';
438
+ /** Why it was refused, so a client can say more than "no". */
439
+ readonly reason?: string;
440
+ readonly issuedAt: string;
441
+ /** When the daemon acked it. Absent while pending. */
442
+ readonly ackedAt?: string;
443
+ /**
444
+ * When it stops being deliverable: the `deliverBy` the caller gave, or the
445
+ * default one-hour window. Null for a kind that never expires.
446
+ */
447
+ readonly deliverBy: string | null;
448
+ /**
449
+ * Pending and past its deadline. Expiry is written when the machine next
450
+ * polls, so a command for a sleeping machine reads `pending` until it wakes;
451
+ * this is what says that `pending` is no longer a promise.
452
+ */
453
+ readonly overdue: boolean;
454
+ }
455
+ /**
456
+ * Whether the machine that holds a project is awake. Online is a live
457
+ * heartbeat; `lastOnline` is when it was last heard from.
458
+ */
459
+ export interface PresenceView {
460
+ readonly machineId: string;
461
+ readonly online: boolean;
462
+ readonly lastOnline: string | null;
463
+ readonly cliVersion: string | null;
464
+ }
465
+ /**
466
+ * Every list answer, so no route can quietly return everything.
467
+ *
468
+ * `nextCursor === null` means the end; it does NOT mean "ask again later".
469
+ */
470
+ export interface PageView<T> {
471
+ readonly items: readonly T[];
472
+ readonly nextCursor: string | null;
473
+ }
474
+ /** One ledger line an answer rests on, so a reader can check it. */
475
+ export interface KeeperCitationView {
476
+ readonly runId: string;
477
+ readonly seq: number;
478
+ readonly kind: string;
479
+ readonly at: string;
480
+ }
481
+ /**
482
+ * What the keeper said, and what it read to say it (DESIGN §9 read path).
483
+ *
484
+ * ⚠️ `kind` IS THE ASK IT UNDERSTOOD, NOT A TOPIC LABEL. `unknown` means the
485
+ * question was not recognised and `text` is the keeper saying so — a client that
486
+ * renders `unknown` as an answer presents a refusal as a reply.
487
+ *
488
+ * Every sentence in `text` is composed from `citations` and nothing else; the
489
+ * read path runs no model. A client that wants to show its working has the seqs
490
+ * to resolve through `events()`.
491
+ */
492
+ export interface KeeperAnswerView {
493
+ readonly question: string;
494
+ readonly kind: 'working-on' | 'needs-me' | 'time-left' | 'failures' | 'landed' | 'safety' | 'unknown';
495
+ readonly text: string;
496
+ readonly citations: readonly KeeperCitationView[];
497
+ /** True when more lines were read than are cited — the list is bounded. */
498
+ readonly citationsTruncated?: boolean;
499
+ }
500
+ /**
501
+ * A document a person handed over — a plan, a spec — kept exactly as handed
502
+ * over and content-addressed. A `pdf` arrives as the pages of its text layer,
503
+ * read on the machine that holds the file; a scanned PDF is refused there
504
+ * rather than accepted and silently emptied.
505
+ */
506
+ export type SourceKindView = 'markdown' | 'text' | 'pdf';
507
+ /** What a statement IS structurally. Never what it means. */
508
+ export type SourceStatementKindView = 'heading' | 'paragraph' | 'list-item' | 'table' | 'code' | 'unsupported';
509
+ export interface SourceStatementView {
510
+ /** 1-based position in document order. */
511
+ readonly ordinal: number;
512
+ readonly kind: SourceStatementKindView;
513
+ /** The statement's lines, verbatim, joined with a newline. */
514
+ readonly text: string;
515
+ readonly anchor: {
516
+ /** For a pdf: the 1-based page the lines are on. Absent for text kinds. */
517
+ readonly page?: number;
518
+ /** 1-based, inclusive, counted in the document as stored. */
519
+ readonly lineStart: number;
520
+ readonly lineEnd: number;
521
+ /** The enclosing heading chain, outermost first. Empty for plain text. */
522
+ readonly headingPath: readonly string[];
523
+ };
524
+ }
525
+ /** A source as a list answers with it: identity and provenance, no content. */
526
+ export interface SourceView {
527
+ readonly id: string;
528
+ readonly projectId: string;
529
+ /** The name the caller gave it — typically the file name. Not unique. */
530
+ readonly name: string;
531
+ readonly kind: SourceKindView;
532
+ /** Hex sha256 of the UTF-8 bytes of the content. The identity of the source. */
533
+ readonly sha256: string;
534
+ /** For a pdf: the sha256 of the file the text was read from, as the reader reported. Provenance, not authority. */
535
+ readonly originSha256?: string;
536
+ readonly byteLength: number;
537
+ /** The caller's own key for the creation attempt. See `createSource`. */
538
+ readonly clientRef: string;
539
+ readonly createdAt: string;
540
+ }
541
+ /** The stored record as the wire carries it: the summary plus the verbatim content. */
542
+ export interface SourceRecordView extends SourceView {
543
+ readonly content: string;
544
+ }
545
+ /** What a statement IS to a plan. Never what the repository has done about it. */
546
+ export type StatementClassView = 'requirement' | 'constraint' | 'context' | 'duplicate' | 'exclusion' | 'question';
547
+ export interface SourceClassificationView {
548
+ /** The statement's ordinal within its source. */
549
+ readonly ordinal: number;
550
+ readonly class: StatementClassView;
551
+ /** For a requirement: demands (`positive`) or forbids (`negative`). */
552
+ readonly polarity?: 'positive' | 'negative';
553
+ /** For a duplicate: the ordinal it repeats. */
554
+ readonly duplicateOf?: number;
555
+ /** The rule that decided, so a reader can argue with it. */
556
+ readonly rule: string;
557
+ readonly classifierVersion: number;
558
+ }
559
+ /**
560
+ * Where a requirement stands against the repository, as MEASURED on the
561
+ * machine at a tree. `absent` names nothing; `candidate` is a place to look
562
+ * (anchors), not evidence; `conflicting` contradicts another statement of the
563
+ * same source; `blocked` could not be measured (no index, no terms, only
564
+ * unparsed files). `implemented-and-evidenced` and `partial` are what a person
565
+ * says with evidence — no rule produces them, and the reconcile route refuses
566
+ * them.
567
+ */
568
+ export type DispositionKindView = 'absent' | 'candidate' | 'conflicting' | 'blocked' | 'implemented-and-evidenced' | 'partial';
569
+ /** One place in the repository that names a statement's terms. */
570
+ export interface RepositoryAnchorView {
571
+ /** Canonical repository path, as the project index records it. */
572
+ readonly path: string;
573
+ /** The line of the first symbol there that names a term; absent when only the path did. */
574
+ readonly line?: number;
575
+ /** Which of the statement's terms this place names. */
576
+ readonly terms: readonly string[];
577
+ }
578
+ export interface SourceDispositionView {
579
+ /** The statement's ordinal within its source. */
580
+ readonly ordinal: number;
581
+ readonly disposition: DispositionKindView;
582
+ /** The statement's terms — what was looked for. */
583
+ readonly terms: readonly string[];
584
+ /** Best first, capped; `candidatesTotal` says how many there were before the cap. */
585
+ readonly anchors: readonly RepositoryAnchorView[];
586
+ readonly candidatesTotal: number;
587
+ /** For `conflicting`: the ordinal that contradicts this one. */
588
+ readonly conflictsWith?: number;
589
+ /** The rule that decided. */
590
+ readonly rule: string;
591
+ readonly reconcilerVersion: number;
592
+ }
593
+ /** One place a person points at as evidence. */
594
+ export interface EvidenceAnchorView {
595
+ readonly path: string;
596
+ readonly line?: number;
597
+ /** The words at that place, as the person read them. At most 300 characters. */
598
+ readonly quote?: string;
599
+ }
600
+ /**
601
+ * What a PERSON said about a requirement, with evidence, at a tree — the only
602
+ * way `implemented-and-evidenced` or `partial` reaches a source. Never written
603
+ * by a machine token or by a rule.
604
+ */
605
+ export interface SourceAttestationView {
606
+ readonly ordinal: number;
607
+ readonly disposition: 'implemented-and-evidenced' | 'partial';
608
+ /** The committed tree the evidence was read at. */
609
+ readonly treeSha: string;
610
+ /** At least one; at most 8. */
611
+ readonly anchors: readonly EvidenceAnchorView[];
612
+ readonly note?: string;
613
+ /** `human:<deviceId>`, from the token that made the request. */
614
+ readonly attestedBy: string;
615
+ readonly createdAt: string;
616
+ }
617
+ /** One source, verbatim, with its anchored statements, the latest classification of each, the most recent reconciliation, and what people attested. */
618
+ export interface SourceDetailView extends SourceRecordView {
619
+ readonly statements: readonly SourceStatementView[];
620
+ readonly classifications: readonly SourceClassificationView[];
621
+ /** The classifier version the classifications came from; null when none were ever made. */
622
+ readonly classifierVersion: number | null;
623
+ /** The most recent hand-over of where each requirement stands; empty when none was ever made. */
624
+ readonly dispositions: readonly SourceDispositionView[];
625
+ /** The tree and reconciler version those dispositions were measured at; null when there are none. */
626
+ readonly reconciledAt: {
627
+ readonly treeSha: string;
628
+ readonly reconcilerVersion: number;
629
+ readonly createdAt: string;
630
+ } | null;
631
+ /** The latest attestation per statement; empty when nobody said anything. */
632
+ readonly attestations: readonly SourceAttestationView[];
633
+ }
634
+ /**
635
+ * One word per requirement: `unserved` (no goal names it), `served` (a goal
636
+ * names it), `partial` / `evidenced` (a person's word at the tree of the
637
+ * latest measurement), `evidenced-at-an-earlier-tree`. Nothing here comes
638
+ * from a rule or from a finished queue.
639
+ */
640
+ export type CoverageKindView = 'unserved' | 'served' | 'partial' | 'evidenced' | 'evidenced-at-an-earlier-tree';
641
+ export interface GoalRefView {
642
+ readonly id: string;
643
+ readonly state: string;
644
+ readonly version: number;
645
+ }
646
+ export interface RequirementCoverageView {
647
+ readonly ordinal: number;
648
+ readonly class: 'requirement' | 'constraint';
649
+ /**
650
+ * Whether this one can block a COMPLETE, or is only a recommendation.
651
+ *
652
+ * ⚠️ NAMED HERE OR THE APP NEVER SEES IT. This view is an ingress
653
+ * allow-list: a field the server sends and this interface does not declare is
654
+ * dropped without a word and without a type error, so the phone would render
655
+ * the same "everything is a MUST" the CLI did.
656
+ */
657
+ readonly mandatory: boolean;
658
+ /** Whether the document said "must NOT" rather than "must". Named for the same reason. */
659
+ readonly negative: boolean;
660
+ /** The statement's first line. */
661
+ readonly text: string;
662
+ readonly anchor: SourceStatementView['anchor'];
663
+ readonly goals: readonly GoalRefView[];
664
+ readonly disposition?: SourceDispositionView;
665
+ readonly attestation?: SourceAttestationView;
666
+ readonly coverage: CoverageKindView;
667
+ }
668
+ /** What is covered and by whom, for one source. */
669
+ /**
670
+ * A person's authorization to direct one session, as the server holds it (F-14).
671
+ *
672
+ * ⚠️ READ-ONLY TO EVERY CLIENT. The fields that decide authority — who issued
673
+ * it, the handshake nonce, the expiry, the state — are stamped by the server and
674
+ * appear here only so a machine can ASK whether it may act. Nothing a client
675
+ * sends can set them.
676
+ */
677
+ export interface SessionControlGrantView {
678
+ readonly id: string;
679
+ /** `human:<deviceId>`. A grant whose issuer is a machine authorizes nothing. */
680
+ readonly issuedBy: string;
681
+ readonly projectId: string;
682
+ readonly sessionId: string;
683
+ readonly repositoryId: string;
684
+ readonly workspace: string;
685
+ readonly scope: readonly string[];
686
+ readonly handshakeNonce: string;
687
+ readonly issuedAt: string;
688
+ readonly expiresAt: string;
689
+ readonly state: 'authorized' | 'revoked' | 'expired';
690
+ readonly generation: number;
691
+ /** The optimistic-concurrency token the revoke route demands; see core's note. */
692
+ readonly version: number;
693
+ }
694
+ export interface SourceCoverageView {
695
+ readonly sourceId: string;
696
+ readonly name: string;
697
+ readonly reconciledAt: SourceDetailView['reconciledAt'];
698
+ readonly requirements: readonly RequirementCoverageView[];
699
+ readonly tally: Readonly<Record<CoverageKindView, number>>;
700
+ }
701
+ //# sourceMappingURL=views.d.ts.map