@happyvertical/smrt-ingestion 0.57.21 → 0.57.22
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.
- package/FEEDBACK.md +104 -0
- package/PROPOSALS.md +19 -0
- package/README.md +126 -0
- package/REVIEW.md +272 -4
- package/agents/execution.md +72 -0
- package/dist/chunks/{execution-9CfjxX8I.js → execution-BzVQax0n.js} +421 -23
- package/dist/chunks/execution-BzVQax0n.js.map +1 -0
- package/dist/chunks/{feedback-CuUzWKzP.js → feedback-D3UTF8fm.js} +119 -21
- package/dist/chunks/feedback-D3UTF8fm.js.map +1 -0
- package/dist/chunks/{extraction-CJ-zrbg0.js → proposal-contracts-77Uo1B1j.js} +39 -39
- package/dist/chunks/proposal-contracts-77Uo1B1j.js.map +1 -0
- package/dist/chunks/{reviewer-instruction-Db3jDN9a.js → reviewer-instruction-CEc3AOuU.js} +2 -2
- package/dist/chunks/{reviewer-instruction-Db3jDN9a.js.map → reviewer-instruction-CEc3AOuU.js.map} +1 -1
- package/dist/dto.d.ts +69 -0
- package/dist/dto.d.ts.map +1 -1
- package/dist/execution-contracts.d.ts +4 -1
- package/dist/execution-contracts.d.ts.map +1 -1
- package/dist/execution-dto.d.ts +16 -0
- package/dist/execution-dto.d.ts.map +1 -1
- package/dist/execution.d.ts +58 -3
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +2 -2
- package/dist/feedback-dto.d.ts +12 -4
- package/dist/feedback-dto.d.ts.map +1 -1
- package/dist/feedback-internal.d.ts +9 -0
- package/dist/feedback-internal.d.ts.map +1 -1
- package/dist/feedback.d.ts +3 -0
- package/dist/feedback.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/manifest.json +321 -3
- package/dist/models.d.ts +15 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +73 -2
- package/dist/models.js.map +1 -1
- package/dist/policy.d.ts +3 -0
- package/dist/policy.d.ts.map +1 -1
- package/dist/proposal-errors.d.ts +4 -0
- package/dist/proposal-errors.d.ts.map +1 -1
- package/dist/proposals.d.ts +1 -0
- package/dist/proposals.d.ts.map +1 -1
- package/dist/proposals.js +11 -6
- package/dist/proposals.js.map +1 -1
- package/dist/redaction.d.ts +9 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/review-dto.d.ts +14 -4
- package/dist/review-dto.d.ts.map +1 -1
- package/dist/review-host.d.ts.map +1 -1
- package/dist/server.d.ts +82 -10
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +366 -47
- package/dist/server.js.map +1 -1
- package/dist/smrt-knowledge.json +124 -18
- package/dist/svelte/components/ActionReview.svelte +39 -9
- package/dist/svelte/components/ActionReview.svelte.d.ts +8 -1
- package/dist/svelte/components/ActionReview.svelte.d.ts.map +1 -1
- package/dist/svelte/components/EvidenceViewer.svelte +60 -4
- package/dist/svelte/components/EvidenceViewer.svelte.d.ts +12 -1
- package/dist/svelte/components/EvidenceViewer.svelte.d.ts.map +1 -1
- package/dist/svelte/components/InboxView.svelte +160 -18
- package/dist/svelte/components/InboxView.svelte.d.ts +28 -1
- package/dist/svelte/components/InboxView.svelte.d.ts.map +1 -1
- package/dist/svelte/components/IntakeWorkspace.svelte +27 -0
- package/dist/svelte/components/IntakeWorkspace.svelte.d.ts +19 -0
- package/dist/svelte/components/IntakeWorkspace.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ReviewView.svelte +264 -23
- package/dist/svelte/components/ReviewView.svelte.d.ts +18 -1
- package/dist/svelte/components/ReviewView.svelte.d.ts.map +1 -1
- package/dist/svelte/components/TriageView.svelte +669 -0
- package/dist/svelte/components/TriageView.svelte.d.ts +16 -0
- package/dist/svelte/components/TriageView.svelte.d.ts.map +1 -0
- package/dist/svelte/i18n.d.ts +39 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +39 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -0
- package/dist/svelte/view-utils.d.ts +65 -0
- package/dist/svelte/view-utils.d.ts.map +1 -1
- package/dist/svelte/view-utils.js +145 -0
- package/package.json +13 -13
- package/dist/chunks/execution-9CfjxX8I.js.map +0 -1
- package/dist/chunks/extraction-CJ-zrbg0.js.map +0 -1
- package/dist/chunks/feedback-CuUzWKzP.js.map +0 -1
package/FEEDBACK.md
CHANGED
|
@@ -30,6 +30,110 @@ an authorized existing event; it cannot supply a correctness label or fabricate
|
|
|
30
30
|
an execution result. An initial denial does not mutate retention. Once authorized,
|
|
31
31
|
a narrowed privacy ceiling survives business rollback and later revocation.
|
|
32
32
|
|
|
33
|
+
Observed `action_decision` events carry the decision's `decisionMode`
|
|
34
|
+
(`review` or `review_own`) and `selfDecision` flag from the durable decision
|
|
35
|
+
record; `selfDecision` is true whenever the decider submitted the item, in
|
|
36
|
+
either mode. A submitter confirming their own proposal (#4085) is observed as a
|
|
37
|
+
decision by that actor, never as reviewer correctness evidence; consumers that
|
|
38
|
+
derive reviewer agreement must exclude `selfDecision: true` events, and nothing
|
|
39
|
+
in this package turns them into automation eligibility.
|
|
40
|
+
|
|
41
|
+
## Reviewer routing choices
|
|
42
|
+
|
|
43
|
+
A `reviewer_routing` record is appended inside the owning transaction when
|
|
44
|
+
feedback is configured and `capture` is authorized, at the moment the choice is
|
|
45
|
+
final: `resolveItem` (REVIEW.md) records `choice: 'none'` keyed
|
|
46
|
+
`routing:<requestId>`, and `submitDecision` records `choice: 'handler'` keyed
|
|
47
|
+
`routing:<decision requestId>` when a reviewer **approves** a reviewer-selected
|
|
48
|
+
action. Handler-choice capture requires `mode: 'review'`: a `review_own`
|
|
49
|
+
submitter approval authorizes normally but appends no reviewer-routing example or
|
|
50
|
+
rule support. A reviewer using the reviewer path may still record an explicit
|
|
51
|
+
routing choice for their own receipt; it does not judge any model suggestion.
|
|
52
|
+
Starting an action records nothing; rejecting or deferring it never produces an
|
|
53
|
+
example. The record needs no model suggestion: it binds the current
|
|
54
|
+
completed/partial attempt (`attemptId`, output digest, analysis revision, frozen
|
|
55
|
+
evidence hashes and the extracted text as `query`), the acting reviewer
|
|
56
|
+
(`reviewer`, also `approvedBy` for a handler choice), the actor who started the
|
|
57
|
+
action (`startedBy`), the start reason, and for a handler choice the
|
|
58
|
+
`handlerId@version`, exact approved arguments and the approved action binding
|
|
59
|
+
(revision and binding hash). Denied or oversized capture, an unavailable source
|
|
60
|
+
pin or malformed evidence skip the projection and leave the authoritative
|
|
61
|
+
decision or disposition intact; database write failures still roll everything
|
|
62
|
+
back. There is no
|
|
63
|
+
supersession: a correction after approval (`previewProposal` on the current
|
|
64
|
+
revision) changes the revision, which retires the old record from retrieval and
|
|
65
|
+
support until the new revision is approved and captured again; a correction
|
|
66
|
+
before approval records nothing until that revision is approved.
|
|
67
|
+
|
|
68
|
+
Settledness is a selection-time condition. Once a generation used an example,
|
|
69
|
+
or a rule suggestion stored its support, the lineage re-checks (reading the
|
|
70
|
+
generation, reviewing or deciding its actions, reading or adopting the rule,
|
|
71
|
+
and replaying `suggestRoutingRule`, which returns the stored suggestion) verify:
|
|
72
|
+
|
|
73
|
+
- every record: its digest, that it is not superseded, retention (no
|
|
74
|
+
redaction or deletion), the unchanged source attempt digest and frozen
|
|
75
|
+
evidence hashes, and live `retrieve` authority in the same tenant and
|
|
76
|
+
confidential scope with every generation recipient allowed by the source's
|
|
77
|
+
provider policy;
|
|
78
|
+
- an `interpretation` record (unchanged from #3676): its action's current
|
|
79
|
+
revision, binding hash, attempt, handler@version and arguments; the action's
|
|
80
|
+
target preconditions at their recorded revisions unless the action
|
|
81
|
+
`succeeded`; the prompt, configuration and model versions; and that the action
|
|
82
|
+
is not reviewer-selected;
|
|
83
|
+
- a `reviewer_routing` record with `choice: 'handler'`: its action's current
|
|
84
|
+
revision, binding hash, attempt, handler@version and arguments, that the
|
|
85
|
+
action is still reviewer-selected, and that its targets are still accessible
|
|
86
|
+
(not their revisions: the record means the handler choice, not target state);
|
|
87
|
+
- a `reviewer_routing` record with `choice: 'none'`: it has no action, so the
|
|
88
|
+
source item is re-checked under the first currently offered receiving handler
|
|
89
|
+
that still authorizes it.
|
|
90
|
+
|
|
91
|
+
Rule support (`suggestRoutingRule`, and on read, adoption and replay of a stored
|
|
92
|
+
suggestion) additionally requires: at least two records from distinct source
|
|
93
|
+
items; `judgment: 'correct'` with matching prompt/configuration/model versions
|
|
94
|
+
for an interpretation record; `choice: 'handler'` for a routing record; each
|
|
95
|
+
record's handler@version equal to the receiving binding's; and each source's
|
|
96
|
+
live routing projection (or full arguments when no projection is configured)
|
|
97
|
+
equal to the receiving action's. Settledness of a routing source is checked only
|
|
98
|
+
when the support is first selected.
|
|
99
|
+
|
|
100
|
+
A later disposition, approval lapse, outcome change or target write of a routing
|
|
101
|
+
source never revokes it.
|
|
102
|
+
|
|
103
|
+
Retrieval scans `interpretation` and `reviewer_routing` records together under
|
|
104
|
+
the same bounds and lexical matcher. A routing example is returned only when its
|
|
105
|
+
digest verifies, its attempt and evidence hashes are unchanged, the source item
|
|
106
|
+
is still readable in the same tenant and confidential scope, every generation
|
|
107
|
+
recipient is allowed by the source's live provider policy, and (for a chosen
|
|
108
|
+
handler) the action is still reviewer-selected and settled — applied
|
|
109
|
+
(`executing`, `succeeded`) or `authorized` with an unexpired approval, on an item
|
|
110
|
+
without a disposition — its binding is still the current revision, and the handler with its candidate
|
|
111
|
+
references is currently offered to the receiver. A `none` choice is
|
|
112
|
+
revalidated under the first currently offered receiving handler the source item
|
|
113
|
+
authorizes. Routing examples carry `kind: 'reviewer_routing'`,
|
|
114
|
+
`judgment: 'correct'`, `routing: { choice, reason }`, `handlerId: 'none'` with an
|
|
115
|
+
empty action for closed items, and no model/prompt/configuration versions, since
|
|
116
|
+
no suggestion was judged. They are included in provider input and selection
|
|
117
|
+
provenance exactly like interpretation examples and are rechecked by the same
|
|
118
|
+
lineage gates.
|
|
119
|
+
|
|
120
|
+
`suggestRoutingRule` accepts `reviewer_routing` support only for `choice: 'handler'`
|
|
121
|
+
records whose action is settled in the same sense when the support is chosen
|
|
122
|
+
(a lapsed, unapplied approval or a disposition excludes it) and whose handler, version and
|
|
123
|
+
full arguments match the receiving binding and each other, from distinct source
|
|
124
|
+
items, under the live routing projection; `none`, rejected, deferred and
|
|
125
|
+
re-corrected records never support a rule. Suggestions and adoptions keep every existing #3676
|
|
126
|
+
rule: no catalog, candidate, permission or automation expansion, and
|
|
127
|
+
`automaticActionEligible: false`. Reviewer-selected actions themselves are refused
|
|
128
|
+
by `authorizeAutomatic`. They are routing choices, never labels for a model
|
|
129
|
+
suggestion: `recordFeedback` refuses an interpretation judgment on one (even
|
|
130
|
+
when the item also carries a generation, and whatever its decision), retrieval
|
|
131
|
+
and rule support ignore interpretation records bound to one, and `ActionReview`
|
|
132
|
+
hides the correctness buttons for them. A disposition says nothing about
|
|
133
|
+
whether a suggestion was right, so interpretation judgments on a resolved item
|
|
134
|
+
still count. Only
|
|
135
|
+
`startReviewerAction` mints that origin; `createAction` refuses it.
|
|
136
|
+
|
|
33
137
|
## Retrieval and provider boundaries
|
|
34
138
|
|
|
35
139
|
`retrieveFeedback({ itemId, query })` derives the current offered catalog,
|
package/PROPOSALS.md
CHANGED
|
@@ -85,6 +85,25 @@ Expired, cancelled, revoked, retried or superseded workers cannot read or publis
|
|
|
85
85
|
current lease and each offered handler/candidate are rechecked immediately before
|
|
86
86
|
each outbound generative/decision boundary and before publication.
|
|
87
87
|
|
|
88
|
+
### Inline interpretation
|
|
89
|
+
|
|
90
|
+
Receipts created with `dispatch: 'inline'` (text/structured parts only) run the
|
|
91
|
+
same stages in-process through `interpretInline(itemId, { requestId, extraction
|
|
92
|
+
})`: an immutable extraction revision claimed under a fenced lease and extracted
|
|
93
|
+
with the built-in UTF-8 extractor, then `prepareGeneration` and a fenced
|
|
94
|
+
interpretation revision generated by `generateProposals`. The analysis lease,
|
|
95
|
+
catalog digest and live catalog/candidate checks, source pin, provenance and
|
|
96
|
+
`StageFailure` categories are identical to the job path; no dispatch intent is
|
|
97
|
+
ever deliverable and nothing is enqueued. A retry with the same `requestId`
|
|
98
|
+
replays the recorded revisions rather than re-preparing against a superseded
|
|
99
|
+
source, also under concurrency. The interpretation append rechecks, under the
|
|
100
|
+
item lock, that its pinned extraction is still the latest revision and refuses
|
|
101
|
+
a superseded pin: first through the general latest-extraction check that
|
|
102
|
+
every interpretation append runs (`GenerationSourceStaleError`), then by the
|
|
103
|
+
inline check that the pinned attempt is still current (`Generation source changed`). Generated suggestions still require explicit preview and a human
|
|
104
|
+
decision, either through the reviewer surface or the submitter-scoped surface
|
|
105
|
+
described in [REVIEW.md](REVIEW.md).
|
|
106
|
+
|
|
88
107
|
## Providers and validation
|
|
89
108
|
|
|
90
109
|
`createSDKProposalGenerator(client, identity, {maxTokens,timeoutMs})` uses an
|
package/README.md
CHANGED
|
@@ -197,11 +197,22 @@ Mount the `/svelte` inbox and review components through an authenticated host us
|
|
|
197
197
|
the browser-safe `IntakeReviewHost` DTO contract. See [REVIEW.md](REVIEW.md) for
|
|
198
198
|
exact revision-bound callbacks, live evidence access, logical split corrections,
|
|
199
199
|
assignment ownership, and the maintained upload-to-domain-result browser fixture.
|
|
200
|
+
`IntakeWorkspace` puts the inbox, the selected item with its original inline,
|
|
201
|
+
the message box and approve-and-advance on one page with keyboard next/previous;
|
|
202
|
+
it uses the same callbacks and adds no authority (REVIEW.md, "Single-page triage
|
|
203
|
+
workspace").
|
|
200
204
|
|
|
201
205
|
Optional explicit feedback and scoped example retrieval are documented in
|
|
202
206
|
[FEEDBACK.md](FEEDBACK.md). Approval and execution outcomes remain separate from
|
|
203
207
|
interpretation correctness; routing policy adoption is explicit and versioned.
|
|
204
208
|
|
|
209
|
+
Reviewers can start any offered handler for an item (`startReviewerAction`) or
|
|
210
|
+
close it as handled without action (`resolveItem`); the published host adapter
|
|
211
|
+
wires both as `startAction`/`resolve` and exposes the offered `catalog`. Hosts
|
|
212
|
+
with their own transport add the two POST operations behind the same session,
|
|
213
|
+
CSRF and reviewer checks as `decide`. The `intake_dispositions` table requires
|
|
214
|
+
a manifest migration before upgrading.
|
|
215
|
+
|
|
205
216
|
## Recipe preview
|
|
206
217
|
|
|
207
218
|
`ingestion.inbox` and `ingestion.review` declare the user-facing inbox and
|
|
@@ -248,6 +259,121 @@ and `maxSuggestions` are each capped at 20; `timeoutMs` is capped at 2147483647.
|
|
|
248
259
|
Invalid limits are rejected when constructing `IngestionService`, before receipt
|
|
249
260
|
or interpretation. See [REVIEW.md](REVIEW.md) for the published server review host.
|
|
250
261
|
|
|
262
|
+
## Trusted internal sources
|
|
263
|
+
|
|
264
|
+
A host with a trusted internal source, such as chat messages recorded in
|
|
265
|
+
smrt-messages, can receive **by reference**, interpret **inline**, and let the
|
|
266
|
+
**submitter** confirm their own proposal (#4085). None of these modes changes
|
|
267
|
+
copied receipts: their identity, validation, queueing and retention are
|
|
268
|
+
untouched, and a replay that switches mode conflicts.
|
|
269
|
+
|
|
270
|
+
**Receive by reference.** A part may be `{ partId, reference: { owner, id,
|
|
271
|
+
version? }, snapshot: { text } | { structured } }` instead of bytes. The
|
|
272
|
+
service derives UTF-8 bytes once (`text/plain` or canonical `application/json`;
|
|
273
|
+
text, reference fields and owner identity containing NUL or unpaired
|
|
274
|
+
surrogates are rejected `invalid` on every engine, and `expireOwned` refuses
|
|
275
|
+
them, so distinct owners can never collapse onto one stored key),
|
|
276
|
+
hashes them into the receipt identity, and stores only that snapshot in the
|
|
277
|
+
durable evidence row; no AssetStore object or asset record is created, and
|
|
278
|
+
`readEvidence` serves the snapshot after the same hash/length check. The
|
|
279
|
+
reference is identity and provenance, never authority. Such parts must be
|
|
280
|
+
text or structured; files still arrive as copied bytes.
|
|
281
|
+
|
|
282
|
+
**Owner-bound retention.** `retention: { kind: 'owner', owner: { kind, id },
|
|
283
|
+
version, expiresAt, replayUntil, acceptAfter }` binds the receipt to its
|
|
284
|
+
owner's lifecycle. The time deadlines remain required and validated exactly as
|
|
285
|
+
before: they are the hard privacy ceiling, and `sweepRetention` and execution's
|
|
286
|
+
policy ceiling still apply. When the owner is deleted the host calls
|
|
287
|
+
`expireOwned({ kind, id })` from its deletion path; every active receipt bound
|
|
288
|
+
to that owner is revoked and removed through the existing `expire` /
|
|
289
|
+
`repairDeletions` protocol, which redacts the snapshot together with every
|
|
290
|
+
other payload, and the deleted delivery is never re-admitted. The owner key is
|
|
291
|
+
recorded in the indexed `owner_key` column (`<kind>:<id>`, so `kind` cannot
|
|
292
|
+
contain `:`). Deletion redaction blanks `owner_key` and `submitter_id` together
|
|
293
|
+
with every payload: only the receipt identity (`delivery_key`) and the captured
|
|
294
|
+
ceiling survive, and the ceiling loses `execution.principalId` unless an
|
|
295
|
+
external action is still `executing` or `outcome_unknown`. So once nothing is
|
|
296
|
+
left to reconcile, a redacted row no longer links an owner to an actor, and a
|
|
297
|
+
repeated `expireOwned` finds nothing. Redaction also overwrites every
|
|
298
|
+
identity- or content-derived digest (`payload_digest`, `scope_digest`, analysis
|
|
299
|
+
`input_digest`, attempt `output_digest`, plan `digest` and proposal, decision
|
|
300
|
+
and execution `binding_hash`) with the fixed marker `redacted`; successful
|
|
301
|
+
result digests remain as the replay tombstone. The principal is retained only
|
|
302
|
+
for an external action that was still in flight when the item was redacted,
|
|
303
|
+
because reconciling it authorizes under that principal; it is dropped when that
|
|
304
|
+
reconciliation reaches a final state, or by the next `repairDeletions` /
|
|
305
|
+
`sweepRetention` pass. For a self-approvable receipt the principal is the
|
|
306
|
+
submitter, so until then it identifies the sender. Later deletion sweeps pass
|
|
307
|
+
this redacted ceiling to the host's `authorize({ operation: 'delete' })`, so
|
|
308
|
+
delete authorization must not depend on `execution.principalId`: a host that
|
|
309
|
+
requires it would refuse, and the sweep would stop with later deletion intents
|
|
310
|
+
still pending. A successful result digest
|
|
311
|
+
is a plain SHA-256 of the canonical result, so it can confirm a guessed result:
|
|
312
|
+
keep handler results to record ids or opaque references, never content.
|
|
313
|
+
|
|
314
|
+
A replay of a deleted delivery is rejected and never becomes fresh work. While
|
|
315
|
+
deletion is still pending it returns `rejected/expired`; once redaction has
|
|
316
|
+
completed the identity digests are gone, so it returns `rejected/conflict`.
|
|
317
|
+
Sources treat both as terminal and must not retry. `delivery_key` stays in plaintext because it is
|
|
318
|
+
the unique receipt identity and the package holds no secret to key it: hosts
|
|
319
|
+
must use an opaque value (for example a random UUID recorded with the message)
|
|
320
|
+
and must not encode owner ids, sender ids or content in `deliveryKey`.
|
|
321
|
+
|
|
322
|
+
Host ordering for owner deletion: first stop issuing receipts for the owner,
|
|
323
|
+
then call `expireOwned`, and call it again once in-flight receipts settle (or
|
|
324
|
+
from a periodic sweep). A receipt still in flight when its owner is expired
|
|
325
|
+
returns `{ kind: 'rejected', category: 'expired' }` rather than `accepted`, but
|
|
326
|
+
one that reserved its row after the first `expireOwned` is only found by the
|
|
327
|
+
later call; until then the time ceiling bounds it. Services are confidential-scope bound, so
|
|
328
|
+
a host whose owner has receipts in several confidential scopes calls
|
|
329
|
+
`expireOwned` from each scope's service; a foreign tenant or scope sees zero.
|
|
330
|
+
|
|
331
|
+
**Inline mode.** `receive({ ..., dispatch: 'inline' })` requires text/structured
|
|
332
|
+
parts only, records the route provenance but **no deliverable dispatch intent**
|
|
333
|
+
for any revision (every intent is retained as `completed`, so no worker or
|
|
334
|
+
`repairDispatches` ever picks it up), and does not enqueue. The host then calls
|
|
335
|
+
`interpretInline(itemId, { requestId, extraction: { configurationRevision,
|
|
336
|
+
limits } })`, which runs in-process exactly what the job path runs: an
|
|
337
|
+
immutable `stage: 'extract'` revision claimed under a fenced lease and
|
|
338
|
+
extracted with the built-in UTF-8 extractor, then `prepareGeneration` and the
|
|
339
|
+
`stage: 'interpret'` revision with the same analysis lease, catalog-live
|
|
340
|
+
checks, provenance and failure categories as `generateProposals`. A retry with
|
|
341
|
+
the same `requestId` replays the immutable revisions, including when two
|
|
342
|
+
identical calls race. The interpretation is appended under the item lock only
|
|
343
|
+
while its pinned extraction is still the latest revision; if another request
|
|
344
|
+
reprocessed the item meanwhile it fails with `GenerationSourceStaleError` (the
|
|
345
|
+
general latest-extraction check every interpretation append runs, #4228) or,
|
|
346
|
+
for a pin whose extraction attempt was replaced, `Generation source changed`. The result reports each
|
|
347
|
+
stage's attempt and safe error and, when generation completed, the authorized
|
|
348
|
+
`getCompletedAnalysis` snapshot. Inline is for send-triggered suggestions that
|
|
349
|
+
must not depend on the scheduler; it is not a bypass of review or execution.
|
|
350
|
+
|
|
351
|
+
**Submitter-scoped approval.** The service records `submitter_id` as the
|
|
352
|
+
authenticated `scope.actorId` of the receiving service, server-side; payload
|
|
353
|
+
and source metadata never supply it. `listOwnProposals({ cursor?, limit? })`
|
|
354
|
+
lists only this actor's own active submissions with their saved review pages,
|
|
355
|
+
reloaded under current item/handler/target authority, without the reviewer
|
|
356
|
+
inbox. A submission the actor can no longer read is skipped before projection
|
|
357
|
+
and never named by the cursor; a readable one whose reviews are denied by
|
|
358
|
+
ingestion authority is returned as `{ item, reviews: null, unavailable: true }`
|
|
359
|
+
with no reason, so one revoked grant never hides the others. `submitOwnDecision(input)` accepts `approve`, `reject` or `defer` (never
|
|
360
|
+
`correct`) under the `review_own` authority: the host's execution `authorize`
|
|
361
|
+
must return `submitter: true` for that actor, the application policy must opt
|
|
362
|
+
in with `submitterReview: true` (narrower layers may only disable it), and the
|
|
363
|
+
item's recorded submitter must equal the current actor. Every other check is
|
|
364
|
+
the ordinary one, and `applyAction` remains the single once-only effect path
|
|
365
|
+
under the approver's live grants. Because the effect runs as the receipt's
|
|
366
|
+
captured `capturedCeiling.execution.principalId`, `review_own` additionally
|
|
367
|
+
requires that principal to be the submitter itself (`submitter_id ===
|
|
368
|
+
scope.actorId === principalId`), checked under the item lock at decision time
|
|
369
|
+
and again at apply; a receipt captured for a bot or service principal can only
|
|
370
|
+
be approved by a reviewer. The decision is recorded with `mode` (`review` or
|
|
371
|
+
`review_own`) and `selfDecision`, which is true whenever the deciding actor is
|
|
372
|
+
the item's submitter, in either mode (mirrored as `decisionMode` /
|
|
373
|
+
`selfDecision` on feedback events), so a self-confirmation is never mistaken
|
|
374
|
+
for independent review and never contributes to automation eligibility. See
|
|
375
|
+
[REVIEW.md](REVIEW.md) and [execution integration](agents/execution.md).
|
|
376
|
+
|
|
251
377
|
## Dispatch route ownership and legacy recovery
|
|
252
378
|
|
|
253
379
|
A receipt retains a versioned, server-owned `jobTarget` snapshot, and every
|
package/REVIEW.md
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# Authenticated review surfaces
|
|
2
2
|
|
|
3
|
-
`@happyvertical/smrt-ingestion/svelte` exports `IntakeInbox`, `IntakeReview
|
|
4
|
-
`EvidenceViewer`. The browser-safe DTO entry exports their `IntakeReviewHost`
|
|
3
|
+
`@happyvertical/smrt-ingestion/svelte` exports `IntakeInbox`, `IntakeReview`,
|
|
4
|
+
`IntakeWorkspace` and `EvidenceViewer`. The browser-safe DTO entry exports their `IntakeReviewHost`
|
|
5
5
|
callback contract. Import styling/theme configuration from smrt-ui in the host.
|
|
6
6
|
Components use smrt-ui primitives and the `ingestion.*` i18n catalog.
|
|
7
|
+
Existing `IntakeInbox` hosts now keep the previous rows visible, with their
|
|
8
|
+
controls disabled, while a refresh or filter change loads; any failed read
|
|
9
|
+
(Load more included) still clears them.
|
|
7
10
|
|
|
8
11
|
The host authenticates every request, resolves tenant/actor/confidential scope
|
|
9
12
|
from its session, enforces CSRF on mutations, and constructs `IngestionService`.
|
|
@@ -103,6 +106,49 @@ Denied refresh clears prior originals, candidates, arguments and results.
|
|
|
103
106
|
Missing confidence is unavailable; page/time links exist only for observed
|
|
104
107
|
locations. Provider errors and unresolved/partial results remain review states.
|
|
105
108
|
|
|
109
|
+
## Submitter-scoped decisions
|
|
110
|
+
|
|
111
|
+
For trusted internal sources the person who submitted an item may confirm or
|
|
112
|
+
decline their own proposal without the reviewer inbox. `listOwnProposals`
|
|
113
|
+
returns only items whose server-recorded `submitter_id` equals the current
|
|
114
|
+
authenticated actor, each with its bounded saved review page reloaded under
|
|
115
|
+
current item, handler and target authority. An item the actor can no longer
|
|
116
|
+
read (the host `authorize` returns false or throws `IntakeAccessDeniedError`)
|
|
117
|
+
is skipped before projection: the page is filled from readable items only, in
|
|
118
|
+
50-row batches, and `nextCursor` names the last emitted item and is present
|
|
119
|
+
only when a further readable item was actually found. Neither the entries, the
|
|
120
|
+
page size nor the cursor reveal a hidden item. If one call has to skip 1000
|
|
121
|
+
hidden submissions it fails with `Own proposal scan limit` rather than return a
|
|
122
|
+
short page or a cursor that stops inside the hidden run; this error is the only
|
|
123
|
+
signal, and it says only that such a run exists. Any other `authorize` error
|
|
124
|
+
propagates instead of shrinking the list. An item that is still readable but
|
|
125
|
+
whose reviews are denied is returned as `{ item, reviews: null, unavailable:
|
|
126
|
+
true }`: that covers ingestion's own denials and any throw from the host
|
|
127
|
+
`assertTarget` (deleted or revoked targets), whose text is never returned.
|
|
128
|
+
Integrity, validation and database errors from ingestion itself propagate. The
|
|
129
|
+
reviewer `listReviews` surface keeps propagating host target errors unchanged.
|
|
130
|
+
`submitOwnDecision` takes the same `ReviewInput` CAS fields and request ID as
|
|
131
|
+
`submitDecision` and accepts `approve`, `reject` and `defer` only; `correct`
|
|
132
|
+
belongs to the reviewer surface. It runs under the `review_own` execution
|
|
133
|
+
operation: the host's `authorize` must return `submitter: true`, the resolved
|
|
134
|
+
policy must have `submitterReview` enabled, and the recorded submitter must be
|
|
135
|
+
the current actor and also the receipt's captured execution principal (the
|
|
136
|
+
identity the effect runs as), checked under the item lock and again at apply.
|
|
137
|
+
A receipt captured for a bot or service principal is never self-approvable. Policy enablement, handler/operation allowlists, captured
|
|
138
|
+
ceiling, confidential access, retention and the first-valid-decision lock are
|
|
139
|
+
unchanged. The decision is a human decision by that actor, recorded with
|
|
140
|
+
`mode: 'review_own'`; reviewer decisions carry `mode: 'review'`. In both modes
|
|
141
|
+
`selfDecision` is true exactly when the deciding actor is the item's
|
|
142
|
+
submitter, so a reviewer approving their own submission is never independent
|
|
143
|
+
review. A self-decision is not a reviewer judgment: it never feeds
|
|
144
|
+
automation eligibility (machine authorization remains the separate evaluated
|
|
145
|
+
`kind: 'machine'` path) and its feedback events carry the same marker. The
|
|
146
|
+
`unavailable` entry discloses nothing about the denied handler or target.
|
|
147
|
+
`applyAction` then runs once under the approver's live grants exactly as
|
|
148
|
+
for reviewer decisions. Hosts render this surface from their own authenticated
|
|
149
|
+
session; presentation state confers no authority, and a source payload cannot
|
|
150
|
+
nominate a submitter.
|
|
151
|
+
|
|
106
152
|
## Assignment and logical splits
|
|
107
153
|
|
|
108
154
|
Assignment is explicitly host-owned, durable and versioned. `assign` receives
|
|
@@ -283,6 +329,215 @@ choice and execution success. No learning, feedback storage or rule promotion is
|
|
|
283
329
|
implemented by these components; #3676 owns those services. Do not pass feedback
|
|
284
330
|
or a feedback callback to readers without the relevant host grant.
|
|
285
331
|
|
|
332
|
+
## Reviewer-chosen actions and "handled, no action"
|
|
333
|
+
|
|
334
|
+
A reviewer is not limited to model suggestions. `ItemReviewView.catalog` carries
|
|
335
|
+
the handlers the item's catalog currently offers (`listHandlers`, discovery media
|
|
336
|
+
types and live preview authority; it is presentation, never a grant). The optional
|
|
337
|
+
`startAction` callback forwards `StartActionInput` (`itemId`, `handlerId@version`,
|
|
338
|
+
explicit `args`, `requestId`, optional `reason`) to `startReviewerAction`, which
|
|
339
|
+
requires the current reviewer grant under that handler, a completed or partial
|
|
340
|
+
attempt on the current analysis revision, and an operation handler the item's
|
|
341
|
+
evidence media types offer. Plan handlers go through `previewPlan`. The server
|
|
342
|
+
creates an action keyed `reviewer:<requestId>` with origin `reviewer-selected`
|
|
343
|
+
(`createAction` refuses that key prefix and origin, so process authority cannot
|
|
344
|
+
mint reviewer provenance; re-previewing such an action with `previewProposal`
|
|
345
|
+
needs the reviewer grant and keeps its handler fixed, so only its arguments can
|
|
346
|
+
be corrected),
|
|
347
|
+
runs the handler's normal `validate`/`preview`, publishes a `waiting_review`
|
|
348
|
+
revision and requires the same explicit approval and apply as a generated
|
|
349
|
+
proposal. Replays with the same request return the same review; changed arguments
|
|
350
|
+
or reason conflict. Such actions are never automation-eligible: `authorizeAutomatic`
|
|
351
|
+
refuses them regardless of policy opt-ins. The UI pre-fills arguments only from a
|
|
352
|
+
same-handler suggestion (for example a `needs_review` one) and a single original's
|
|
353
|
+
evidence references, lists the required fields still missing, and submits exactly
|
|
354
|
+
what the reviewer typed. An item whose current attempt failed outright must be
|
|
355
|
+
reprocessed first; an interpretation that completed with `provider_error`,
|
|
356
|
+
`unknown` or no suggestions can be acted on directly. `ReviewAction.origin`
|
|
357
|
+
exposes the provenance so `ActionReview` can label human-started actions.
|
|
358
|
+
|
|
359
|
+
The optional `resolve` callback forwards `ResolveItemInput`
|
|
360
|
+
(`disposition: 'resolve_no_action'`, `requestId`, optional `reason`) to
|
|
361
|
+
`resolveItem`. It needs reviewer authority under any handler the item's captured
|
|
362
|
+
ceiling offers and refuses while an action is `authorized` with a live approval,
|
|
363
|
+
`executing`, `outcome_unknown` or `succeeded`; waiting, deferred, rejected and
|
|
364
|
+
failed actions, and authorized ones whose approval expired, may remain (neither
|
|
365
|
+
an exhausted failed action nor a lapsed approval can be rejected, so they must
|
|
366
|
+
not keep the queue open). Afterwards only reject remains: those actions can no
|
|
367
|
+
longer be approved, corrected, deferred, re-previewed, retried, applied or
|
|
368
|
+
automated. `submitDecision` refuses every decision but `reject`, no new revision
|
|
369
|
+
is published for the item, `applyAction` rechecks the disposition before any
|
|
370
|
+
effect, and `analyze`, `reviseLogicalSplit` and `createAction` refuse the item
|
|
371
|
+
under its lock before any write, so it is never reanalysed, re-split or given a
|
|
372
|
+
new action. `resolveItem` supersedes any queued or running analysis under the
|
|
373
|
+
same lock and `claimAnalysis` refuses a resolved item, so no provider is called
|
|
374
|
+
after a human closed it; the item's `processingState` keeps its last value (it
|
|
375
|
+
describes analysis, not review), and the inbox state `resolved` is authoritative.
|
|
376
|
+
`publishProposal` (process authority) refuses a resolved item and any
|
|
377
|
+
reviewer-selected action. The UI hides Approve, Defer, Apply, every edit control, suggestion
|
|
378
|
+
preview and split controls on a resolved item.
|
|
379
|
+
Resolved items hide the instruction editor and stop reinterpretation polling, even if an older processing projection is still queued. Recording an instruction cannot append a new revision after resolution.
|
|
380
|
+
|
|
381
|
+
Paged lists project the resolved state and disposition time in the same bounded, scope-filtered query, so state filters and row controls agree with a full item reload.
|
|
382
|
+
|
|
383
|
+
The append-only `intake_dispositions` record is terminal: replaying the same
|
|
384
|
+
request returns the same disposition, any other request conflicts, and
|
|
385
|
+
`startReviewerAction` refuses the item. `IntakeItemDTO.disposition` projects only
|
|
386
|
+
`{ state: 'resolved', at }` to any item reader (`listItems` reads it in the same
|
|
387
|
+
statement as the page); the reviewer and reason are returned only by
|
|
388
|
+
`getItemDisposition`, which requires reviewer authority, and the review hosts put
|
|
389
|
+
them on `ItemReviewView.disposition` (omitted when that read is denied, so the
|
|
390
|
+
view shows only "Handled, no action"). `loadReviewSummary` reports inbox state `resolved` unless an action
|
|
391
|
+
actually succeeded. Privacy deletion and retention expiry redact the reason and
|
|
392
|
+
reviewer (`intake_dispositions.data`) and keep only the kind. The disposition is
|
|
393
|
+
distinct from `reject`, which still means the proposal was wrong. Closing an item
|
|
394
|
+
records `reviewer_routing` feedback immediately; a started action records it only
|
|
395
|
+
when the reviewer approves it (see FEEDBACK.md). Capture is an optional projection
|
|
396
|
+
that never vetoes the decision or disposition.
|
|
397
|
+
|
|
398
|
+
`startReviewerAction` applies the handler's discovery media types and the live
|
|
399
|
+
reviewer grant/policy for that handler. It intentionally does not apply the
|
|
400
|
+
proposal catalog's provider-policy filter or `maxHandlers` slice: those bound what
|
|
401
|
+
a model may be offered, and a human choosing a handler sends nothing to a
|
|
402
|
+
provider. The UI picker shows the offered catalog, so a handler hidden from it is
|
|
403
|
+
only reachable by a reviewer who already holds `review` authority for it. The maintained
|
|
404
|
+
browser fixture serves both operations behind CSRF and the same session scope.
|
|
405
|
+
|
|
406
|
+
## Single-page triage workspace
|
|
407
|
+
|
|
408
|
+
`IntakeWorkspace` (#4215) composes the existing pieces so a reviewer works the
|
|
409
|
+
whole queue on one page: the paged inbox as a list pane, the review view of the
|
|
410
|
+
selected item beside it (the original above its action form), and the
|
|
411
|
+
reviewer-instruction message box of the item pane. Props: `host`, `contextKey`
|
|
412
|
+
(replace on session change, as for the other components), optional initial
|
|
413
|
+
`itemId`, `pollMs`, `rowAside` and `onselect(itemId | undefined)`, which only
|
|
414
|
+
reports the selection (to keep a URL in step) and never navigates. It adds **no
|
|
415
|
+
host callback and no server authority**: every read is the host's `list` and
|
|
416
|
+
`load`, every action still goes through the handler preview and an explicit
|
|
417
|
+
approval and apply under the approver's live grants, and the message steers the
|
|
418
|
+
model only, exactly as in `IntakeReview`.
|
|
419
|
+
|
|
420
|
+
- **Inline original.** In the item pane `EvidenceViewer` shows one part at a
|
|
421
|
+
time (`layout="pager"`), opening on the first part that renders inline (the
|
|
422
|
+
uploaded file rather than its capture record), with previous/next original.
|
|
423
|
+
A PDF with more than one *observed* page (extraction locations or proposed
|
|
424
|
+
page groups; `pages` prop) gets previous/next page, which reloads the frame
|
|
425
|
+
at the same-origin URL plus `#page=n`; only `evidenceUrl`-approved URLs ever
|
|
426
|
+
reach a frame. `IntakeReview` gets the same page navigation over its
|
|
427
|
+
unchanged stacked list of parts. The chosen part and page belong to the item
|
|
428
|
+
view (bindable `chosen`/`page`), so they survive in-place reloads, Prepare
|
|
429
|
+
review, Approve and Apply.
|
|
430
|
+
- **Keyboard.** J/K and Alt+Down/Alt+Up move to the next/previous row (loading
|
|
431
|
+
the next page at the end), ignored while focus is in a text field, select or
|
|
432
|
+
editable region; "Previous item"/"Next item" buttons carry
|
|
433
|
+
`aria-keyshortcuts`. The selected row is `aria-current`, and every selection
|
|
434
|
+
moves focus to the item pane region, named by the row's label. A navigation
|
|
435
|
+
that waited for the next page is dropped if the reviewer chose another item
|
|
436
|
+
meanwhile.
|
|
437
|
+
- **Unsent input.** The pane holds unsent input when it has instruction text,
|
|
438
|
+
an edited suggestion draft or split draft, a chosen start handler or start
|
|
439
|
+
reason, a no-action reason, or an action editor with edited arguments, a
|
|
440
|
+
reason or a feedback comment. It is read from the drafts themselves, so it
|
|
441
|
+
holds while a reload has cleared the view; while the view is denied nothing
|
|
442
|
+
is shown and nothing counts (the drafts stay in memory and return with an
|
|
443
|
+
authorized refresh, as before). A reviewer-initiated move (J/K, Alt+arrows,
|
|
444
|
+
Next/Previous item, a row's Review button) then asks inline, in a group
|
|
445
|
+
labelled by its note: "Stay on this item" takes focus and returns it to the
|
|
446
|
+
field the reviewer last used; "Discard and open the other item" proceeds.
|
|
447
|
+
When an item is closed without action (here or by another session), input a
|
|
448
|
+
closed item cannot take
|
|
449
|
+
(instruction, suggestion and split drafts, a started action, a no-action
|
|
450
|
+
reason) is discarded.
|
|
451
|
+
- **Live state.** A row's status follows the item: "Interpreting…", "Ready to
|
|
452
|
+
review", "Needs review" with missing argument names or the generation
|
|
453
|
+
outcome, "Awaiting approval", "Approved, not yet applied", "Failed" with a
|
|
454
|
+
safe category, "Handled, no action", or the listed inbox state. Rows the
|
|
455
|
+
reviewer has not opened show the listed state only; reasons come from a
|
|
456
|
+
loaded view, never from server exception text. The open item's own polling
|
|
457
|
+
(`ReviewView`, unchanged backoff and three-failure fail-closed) reports each
|
|
458
|
+
displayed view and the workspace patches that row in place
|
|
459
|
+
(`InboxView.patch`, keeping the listed label and preview, and the row's
|
|
460
|
+
assignment unless the patch carries a newer assignment version, so a view
|
|
461
|
+
read before an assignment never rolls the row or the next expected version
|
|
462
|
+
back), so the row follows each newer read of the form and the list is
|
|
463
|
+
**not** re-listed per tick. An item the
|
|
464
|
+
reviewer leaves while its stage is in flight, or before the pane showed the
|
|
465
|
+
result of its last operation (an instruction just recorded), keeps
|
|
466
|
+
following: the workspace re-reads followed items with the same `load` (first
|
|
467
|
+
review page) on the pane's backoff, at most **5** reads per tick, one after
|
|
468
|
+
another, least recently read first (so every followed row is reached), with
|
|
469
|
+
only one tick running at a time. At most **20** items are followed; beyond
|
|
470
|
+
that the longest-followed one (the earliest started, whatever the read
|
|
471
|
+
rotation) stops. An item left while its pane was mid-operation (an Apply not
|
|
472
|
+
yet answered) keeps following while a read still shows an action authorized
|
|
473
|
+
or executing: exactly **3** more reads when it never answers, the fourth
|
|
474
|
+
ending the follow. That debt belongs to the operation, so it survives
|
|
475
|
+
reopening and leaving the item again until a view shows the operation
|
|
476
|
+
answered or the reads run out; leaving mid-operation again starts a full
|
|
477
|
+
debt for the new operation. Each follow has a
|
|
478
|
+
generation: an answer from an earlier follow of the same item (one asked
|
|
479
|
+
before the reviewer reopened or left it again) is dropped, and its failure
|
|
480
|
+
does not count toward the new follow's three. An item stops being followed
|
|
481
|
+
when it settles, its view is denied, three reads in a row throw, its row
|
|
482
|
+
leaves the displayed list (a filter or a refreshed page removed it), the
|
|
483
|
+
list fails closed, or the cap drops it; a denial, the failure stop or the
|
|
484
|
+
cap drops its cached status and restores its entry exactly as its page read
|
|
485
|
+
returned it (never a patched entry). Opening an item drops its cached
|
|
486
|
+
status until the pane shows a view; a denial in the open pane (a failed
|
|
487
|
+
action, the pane's own fail-closed poll, or a first load that failed) does
|
|
488
|
+
the same as a follow denial, clears the announcement, and the item is not
|
|
489
|
+
followed when left. A settled stage that was seen running is announced in a
|
|
490
|
+
polite status region ("badger-po.pdf: Ready to review"), only while its row
|
|
491
|
+
is displayed; a refreshed page or a failed list clears it.
|
|
492
|
+
Other rows update on Refresh, a filter change or when opened; there is no
|
|
493
|
+
streaming. While a refresh or "Load more" is pending, the displayed rows stay
|
|
494
|
+
(controls disabled) and keep taking patches. Every read (a page, a pane
|
|
495
|
+
view, including an unchanged poll, and a follow read) is numbered when its
|
|
496
|
+
request starts, and each row shows the entry of its newest read, whatever
|
|
497
|
+
the source: a page read before a pane or follow read never regresses the
|
|
498
|
+
row, and a page read after them replaces what they showed. The same rule
|
|
499
|
+
governs a follow: a follow read older than the row's newest read changes
|
|
500
|
+
no status, does not stop the follow and announces nothing (a newer read
|
|
501
|
+
settles it), so the announcement never contradicts the row. Cached status reasons follow the same ordering, even when entries have identical
|
|
502
|
+
fingerprints. "Load more" continues the
|
|
503
|
+
filter the rows were listed under, not filter inputs edited since, and does
|
|
504
|
+
not repeat a row already shown. A failed "Load more" fails closed like any
|
|
505
|
+
failed read (the host cannot tell a transient failure from a revoked
|
|
506
|
+
grant): the rows clear and the list shows its error.
|
|
507
|
+
- **Approve and advance.** After an approve, apply or "Handled, no action" whose
|
|
508
|
+
reload left nothing for the reviewer (closed without action, or an action
|
|
509
|
+
succeeded and none is waiting for approval, waiting to be applied, executing,
|
|
510
|
+
unknown or failed; stale, rejected and deferred actions do not hold it), the
|
|
511
|
+
item's row is patched and leaves the list when it no longer matches the
|
|
512
|
+
listed state/assignee filter, and the next row that is not closed or
|
|
513
|
+
completed opens and takes focus, with no navigation and no re-listing. An
|
|
514
|
+
approval that leaves an apply step keeps the item open with Apply in place.
|
|
515
|
+
Unloaded saved-review pages and an active background stage also prevent
|
|
516
|
+
settlement. Advancing skips completed inbox pages until a reviewable row
|
|
517
|
+
appears; a page that makes no progress stops the walk.
|
|
518
|
+
It never advances over unsent input on the same item: the item stays open,
|
|
519
|
+
the input stays, and a note says it is finished but has unsent input; the
|
|
520
|
+
reviewer moves on explicitly, through the confirmation; input typed while
|
|
521
|
+
the next page loads counts too. When no open row is displayed, the advance
|
|
522
|
+
appends pages of finished rows until one has an open row or no page
|
|
523
|
+
remains, at most **5** appends. After each append, the advance (like
|
|
524
|
+
J/K at the end of the list) finds its place again by id: just after the
|
|
525
|
+
item it left, or, if that row has gone, at the first row that followed it
|
|
526
|
+
and is still listed, or at the first new row, so a row that left the list
|
|
527
|
+
meanwhile never shifts it onto the wrong item. Its notes (and "nothing else
|
|
528
|
+
needs review") are written into one persistent polite status region. The queue is never reported empty when
|
|
529
|
+
that is unknown: past the bound, or when an append cannot start (a refresh
|
|
530
|
+
is pending) or is superseded, the item stays open with a note to try Next
|
|
531
|
+
item again, which still works; when an append fails, the list has failed
|
|
532
|
+
closed (no rows, Next disabled), so the item stays open with a note to
|
|
533
|
+
refresh the list to continue. The open item stays listed while the
|
|
534
|
+
reviewer works on it even if its state stops matching the filter; it
|
|
535
|
+
leaves once they move on.
|
|
536
|
+
When nothing is left the pane says so. The per-row "Handled, no action"
|
|
537
|
+
control is hidden in the workspace (`resolveInRow={false}`) so closing always
|
|
538
|
+
goes through the item pane and advances.
|
|
539
|
+
|
|
540
|
+
|
|
286
541
|
## Maintained browser proof
|
|
287
542
|
|
|
288
543
|
`pnpm test:components` runs real Svelte component tests; `pnpm test:e2e` runs the
|
|
@@ -323,8 +578,13 @@ Import `createIntakeReviewHost` and `IntakeReviewHostOptions` from
|
|
|
323
578
|
options)` with a service constructed from the current authenticated session.
|
|
324
579
|
The returned `IntakeReviewHost` implements inbox state projection across saved
|
|
325
580
|
review pages, current generation loading, stable `ui:<index>` intention keys,
|
|
326
|
-
action/plan preview CAS lookup, decisions, apply, corrections, candidates, split
|
|
327
|
-
|
|
581
|
+
action/plan preview CAS lookup, decisions, apply, corrections, candidates, split,
|
|
582
|
+
feedback, the offered `catalog`, `startAction` and `resolve`. Any catalog
|
|
583
|
+
failure (not configured, bound exceeded, denied) omits `catalog`, so the picker
|
|
584
|
+
disappears while existing reviews stay reviewable; item, review and evidence
|
|
585
|
+
denials still propagate as before; the reference host (`reference/review-host.ts`)
|
|
586
|
+
degrades the same way. Repeated
|
|
587
|
+
preparation of an unchanged authorized suggestion retains
|
|
328
588
|
its current review instead of creating a new revision. Changed arguments still
|
|
329
589
|
require explicit re-preview and a fresh human decision.
|
|
330
590
|
`list({ state?, assigneeId?, cursor?, limit? })` returns one page newest receipt
|
|
@@ -518,3 +778,11 @@ choosing the same local file explicitly starts a new upload identity. Opening
|
|
|
518
778
|
then dismissing the chooser preserves the separately retained originals and
|
|
519
779
|
request identity for retry; it does not clear the shared FilePicker's native-form
|
|
520
780
|
behavior. Successful upload and host/context disposal still discard retry state.
|
|
781
|
+
|
|
782
|
+
Workspace ordering also protects an open item during an awaited automatic page walk:
|
|
783
|
+
the selected row remains until navigation succeeds, except that a failed list read
|
|
784
|
+
clears the inbox. Applying an action is disabled
|
|
785
|
+
while that action card holds unsent arguments, reason, or feedback comment. A list
|
|
786
|
+
refresh retires earlier follow snapshots; only follow reads started after that
|
|
787
|
+
page request may override its rows, so a newer refresh cannot be regressed by an
|
|
788
|
+
already settled or older in-flight follow read.
|