feat: replace revision counters with domain state contracts
Use content digests, key fingerprints, binding and trust identities, immutable mutation receipts, and observation tokens instead of state-update ordinals across API, storage, Runtime, and Web contracts. Preserve historical replay and secret material through frozen migrations; preflight uncertain removals before cutover, resume provisioning receipts, validate encrypted migration candidates, and align fresh/upgraded schemas. Include reviewed regression fixes, generated artifacts, and validation evidence.
This commit is contained in:
+2
-2
@@ -11,7 +11,7 @@ It is not a dumping ground for external research, old plans, API inventories, or
|
||||
3. [`design/context-history.md`](design/context-history.md) — the highest-risk invariant: inputs that affect the model must be committed to history before they enter context.
|
||||
4. [`design/worker-session-state.md`](design/worker-session-state.md) — Worker identity, replayable session logs, current metadata, and live process hints.
|
||||
5. [`design/session-observation.md`](design/session-observation.md) — common session captures, `SessionEntryRef`, Memory evidence, and host-authorized Worker observation.
|
||||
6. [`design/flow-state-graph.md`](design/flow-state-graph.md) — Workspace Flow sources, immutable revisions, transition attempts, and bounded internal verification.
|
||||
6. [`design/flow-state-graph.md`](design/flow-state-graph.md) — Workspace Flow sources, frozen content-addressed definitions, transition attempts, and bounded internal verification.
|
||||
7. [`design/profiles-manifests-prompts.md`](design/profiles-manifests-prompts.md) — reusable Profiles, resolved Manifests, and prompt resources.
|
||||
8. [`design/tool-permissions-scope.md`](design/tool-permissions-scope.md) — tool policy and filesystem scope.
|
||||
9. [`design/plugin-packages.md`](design/plugin-packages.md) — the retained offline package format and the prohibition on ambient Plugin authority.
|
||||
@@ -30,7 +30,7 @@ It is not a dumping ground for external research, old plans, API inventories, or
|
||||
22. [`development/workspace-schema-migrations.md`](development/workspace-schema-migrations.md) — how to preflight, apply, verify, and roll back control-plane SQLite schema changes.
|
||||
23. [`design/standalone-agent-host.md`](design/standalone-agent-host.md) — in-process standalone Worker host の依存方向、authority、lifecycle、非目標。
|
||||
24. [`design/server-feature-storage.md`](design/server-feature-storage.md) — Server管理のWorkspace/Feature専用SQLite、migration、transaction、backup/restore、削除・shutdown境界。
|
||||
25. [`design/subjektiv-store.md`](design/subjektiv-store.md) — subject-scoped Memoryのownership、authorization、Session attribution、revision、candidate consolidation境界。
|
||||
25. [`design/subjektiv-store.md`](design/subjektiv-store.md) — subject-scoped Memoryのownership、authorization、Session attribution、変更履歴、candidate consolidation境界。
|
||||
26. [`design/subjektiv-surface.md`](design/subjektiv-surface.md) — confirmed Memoryから生成するresident surface、availability、append-only restore injection。
|
||||
27. [`development/subjektiv-product-cutover.md`](development/subjektiv-product-cutover.md) — legacy Workspace Memoryからsubjektivへのoperator cutover、explicit reset、backup/rollback runbook。
|
||||
28. [`design/worker-wip-mode.md`](design/worker-wip-mode.md) — explicit-opt-in Worker WIP mode, Worldspace projection, authority, cache, and retry contracts.
|
||||
|
||||
@@ -7,9 +7,9 @@ T-701 extends the [registry-bound operation reception](workspace-worker-operatio
|
||||
| Operation | Observed identity / expected input | Atomic validation and execution owner | Conflict / uncertain outcome | Shared client behavior / deliberate difference |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Restore | Workspace + Runtime + Worker identity; Runtime-owned `restore_observation_token`; immutable `expected_observation_token` + `request_id` | Existing Runtime per-Worker lifecycle operation lock, durable admission and lifecycle commit. Preparation and execution follow admission, not a frontend GET comparison | Stale new intent is HTTP 409 (`restore_observation_conflict`); operation results remain accepted / rejected / rolled_back / reconciliation_required. A lost reply is unknown, not definite rollback | Both entrances use the same scoped POST/parser/notice classifiers and observation refresh. Never replace a rejected intent's token and automatically retry |
|
||||
| Stop | Identity and reason, **not** a catalog revision CAS | Runtime per-Worker operation lock and existing backend Stop settlement; Workspace session finalization only after accepted | Existing flat accepted / unsupported / rejected result with diagnostics | Same encoded target paths and bounded HTTP-error messages; refresh observations instead of writing a guessed local stopped state. Stop does not gain Restore's generation guarantee |
|
||||
| Pause / Resume / Cancel / Compact | Connected execution identity and `WorkerCommandEnvelope.command_id` | Existing bound execution/controller command admission | Command acknowledgement: Accepted / StaleCommandId / Conflict / InvalidState, with authoritative state | Command IDs order commands; they are not catalog observation generations. Resume is not Restore; connections do not silently move to a replacement execution |
|
||||
| Delete / cleanup | Runtime + selected target IDs; `expected_plan_revision` **and** `expected_plan_digest` | Existing cleanup validation, per-target live/retention revalidation, removal service and catalog commits | Existing cleanup-plan conflicts, per-target status and diagnostics | Preserve plan protection for candidate sets and dependencies. Never substitute a Worker generation. Page plans invalidate at the beginning of every catalog refresh and are unavailable until fresh catalog and plan observations settle |
|
||||
| Stop | Identity and reason, **not** a catalog content CAS | Runtime per-Worker operation lock and existing backend Stop settlement; Workspace session finalization only after accepted | Existing flat accepted / unsupported / rejected result with diagnostics | Same encoded target paths and bounded HTTP-error messages; refresh observations instead of writing a guessed local stopped state. Stop does not gain Restore's observation-token admission guarantee |
|
||||
| Pause / Resume / Cancel / Compact | Connected execution identity and `WorkerCommandEnvelope.command_id` | Existing bound execution/controller command admission | Command acknowledgement: Accepted / StaleCommandId / Conflict / InvalidState, with authoritative state | Command IDs order commands; they are not catalog observation tokens. Resume is not Restore; connections do not silently move to a replacement execution |
|
||||
| Delete / cleanup | Runtime + selected target IDs; `expected_plan_digest` of the candidate set, dependencies and blockers | Existing cleanup validation, per-target live/retention revalidation, removal service and catalog commits | Existing cleanup-plan conflicts, per-target status and diagnostics | Preserve plan protection for candidate sets and dependencies. Never substitute a Worker observation token. Page plans invalidate at the beginning of every catalog refresh and are unavailable until fresh catalog and plan observations settle |
|
||||
| Pin / Unpin | Registry Worker identity and explicit boolean | Registry retention update and ordered projection publication | Existing retention result/errors | Preserve retention semantics and full catalog metadata; refresh shared observations and cleanup plan |
|
||||
| Submit / Notify | Authenticated source + durable submission / notification request ID | Existing controller/session durable receipt admission | Existing accepted/rejected receipts and unknown delivery diagnostics | These are not Restore requests; Restore never manufactures input, Resume or a new Worker |
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ For the HTTP rows below, `R` abbreviates `/api/runtimes/{runtime_id}/workers/{wo
|
||||
| `POST R/cancel` and `POST W/cancel` | `WorkspaceWorker::cancel` | `RuntimeRegistry::cancel_worker` → target execution controller; not registry removal |
|
||||
| `PUT W/pin` / `DELETE W/pin` | `WorkspaceWorker::set_pinned(true/false)` | Workspace store retention update, ordered through `worker_projection`; no execution command |
|
||||
| `GET /api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-plan` | `build_runtime_cleanup_plan` (preview, not a Worker mutation) | Workspace cleanup planner/projections |
|
||||
| `POST /api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-executions` | `execute_runtime_cleanup_with_context` validates plan revision/digest and selected candidates → each selected Worker's `delete_from_plan` | `WorkerRemovalService::execute_cleanup_removal` → existing retention planning, Runtime retention execution, and Workspace catalog commit. Workdir-only actions remain in the cleanup service |
|
||||
| `POST /api/w/{workspace_id}/runtimes/{runtime_id}/cleanup-executions` | `execute_runtime_cleanup_with_context` validates plan digest and selected candidates → each selected Worker's `delete_from_plan` | `WorkerRemovalService::execute_cleanup_removal` → existing retention planning, Runtime retention execution, and Workspace catalog commit. Workdir-only actions remain in the cleanup service |
|
||||
| `POST /api/w/{workspace_id}/workers/remove` (`WorkerRemove`, not a browser removal alias) | `worker_remove_contract` verifies its source proof against the requested target → `WorkspaceWorker::remove` | `WorkerRemovalService::execute_async` checks the `remove` grant and coordinates retention/removal. It rejects self-removal and requires a stopped target for a new removal; existing durable removal operations have their own recovery path |
|
||||
| Embedded `WorkerRemove` Tool | Runtime-owned in-process target-bound proof → `EmbeddedServerWorkerMutationDispatcher` verifies/consumes proof → installed `EmbeddedWorkspaceWorkerRemoveExecutor` → `WorkspaceWorker::resolve_with_services` → the same `WorkspaceWorker::remove` | Same `WorkerRemovalService::execute_async`, grant-lock/recheck, self/live/retention guards, durable plan/recovery and catalog commit as HTTP; no Runtime-to-Server dependency |
|
||||
| Dedicated WS `R/protocol/ws` and `W/protocol/ws` | `worker_protocol_ws` (scoped wrapper for `W`) → `WorkspaceWorker::connect_protocol` → `workspace_worker_protocol_session` → bound sender | One remote WS or embedded execution transport; controller/session owns protocol operation results |
|
||||
@@ -79,7 +79,7 @@ There is no universal Worker command token or universal success result.
|
||||
- `WorkerCommandEnvelope.command_id` orders controller lifecycle commands. `WorkerCommandAcknowledgement` contains command kind, disposition (`Accepted`, `StaleCommandId`, `Conflict`, `InvalidState`) and authoritative state. These IDs belong to controller command admission, not durable Submit identity or Workspace removal planning.
|
||||
- `submission_request_id` is the caller's Submit retry key in an authenticated-source namespace. A durable `SubmissionAccepted` receipt also contains the Worker-allocated `submission_id` and `Started`/`Queued` disposition. `SubmissionRejected` is correlated by request ID. Acceptance is not a successful completed run; `last_finished_submission_request_id` is a teardown fence, not a result-success claim.
|
||||
- `notification_request_id` correlates `NotificationAccepted` / `NotificationRejected`. Notify has its own durable receipt and exact-payload retry behavior; it does not allocate a Submit FIFO entry. Browser Submit/Notify are rewritten to `SubmitTracked`/`NotifyTracked` with the server's Account source. Browser-supplied tracked forms, `SubmitIfIdle`, and `WorkerEvent` are rejected.
|
||||
- Runtime `WorkerLifecycleOperationId` is a durable lifecycle/reconciliation identity, separate from a controller command ID. Ticket `assignment_operation_id`, cleanup plan revision/digest, and retention plan/operation IDs and fingerprints likewise belong to their own domain checks. A multiplexed `subscription_id` only routes frames to an already-bound sender.
|
||||
- Runtime `WorkerLifecycleOperationId` is a durable lifecycle/reconciliation identity, separate from a controller command ID. Ticket `assignment_operation_id`, cleanup plan digest, and retention plan/operation IDs and fingerprints likewise belong to their own domain checks. A multiplexed `subscription_id` only routes frames to an already-bound sender.
|
||||
|
||||
The common sender returns `Result<()>` after authorization and enqueueing on its transport channel. This is **not** a controller acknowledgement, durable receipt, or proof of upstream delivery. Actual outcomes arrive on the event stream. The dedicated WS reports dispatch errors as protocol error events; the multiplexed adapter currently terminates the connection when sending a method fails. It also requires an initial session Snapshot before accepting the WorkerProtocol subscription.
|
||||
|
||||
@@ -125,10 +125,10 @@ The boundary above covers the T-702 minimum operation set. It is not a claim tha
|
||||
Known limitations worth follow-up, rather than undocumented guarantees:
|
||||
|
||||
1. Browser credentials/Account are rechecked for each common operation or WS method send, but not continuously for every outgoing observation event. This is not a promise of immediate revocation-driven stream teardown.
|
||||
2. Cleanup is sequential rather than an all-target atomic transaction. The existing cleanup plan revision/digest and each removal service's live revalidation remain distinct protections.
|
||||
2. Cleanup is sequential rather than an all-target atomic transaction. The existing cleanup plan digest and each removal service's live revalidation remain distinct protections.
|
||||
3. Unary input has its existing narrower wire contract, rather than the WS authenticated receipt envelope. Identity unification does not create a new retry contract or manufacture caller request IDs across transports.
|
||||
|
||||
**T-701's stale-observation Restore guard is not implemented here.** T-701 will connect the caller's observed revision/generation to Runtime's existing operation lock, distinguish a new Restore intent from pending reconciliation, and handle UI conflicts. This boundary leaves those Restore inputs/results and the pending-operation state machine intact rather than creating a duplicate guard.
|
||||
T-701's [stale-observation Restore guard](worker-restore-observations.md) connects the caller's `expected_observation_token` and `request_id` to Runtime's existing operation lock, distinguishes a new Restore intent from pending reconciliation, and handles UI conflicts. This boundary leaves those Restore inputs/results and the pending-operation state machine intact rather than creating a duplicate guard.
|
||||
|
||||
### Implementation references
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ The record normally contains:
|
||||
- Workspace and operation identity;
|
||||
- a fingerprint of stable caller intent;
|
||||
- the created resource or result identity;
|
||||
- the committed revision and timestamp where relevant.
|
||||
- the committed content digest or immutable change ID and timestamp where relevant.
|
||||
|
||||
It does not need `pending`, `executing`, or intermediate stages. An exact retry
|
||||
returns the recorded result. Reusing the same operation identity with a
|
||||
@@ -173,7 +173,7 @@ Prefer factual fields over stage names:
|
||||
|
||||
- `provider_deleted_at` is evidence that provider deletion succeeded;
|
||||
- `child_operation_id` binds delegated work;
|
||||
- `result_revision` identifies the committed result;
|
||||
- `result_digest` identifies the committed result content;
|
||||
- `target_ref_after` records verified merge evidence.
|
||||
|
||||
Avoid fields such as `validating`, `closing_session`, `detaching`,
|
||||
@@ -211,7 +211,7 @@ results.
|
||||
Before every retry or side effect, reread live authority and revalidate its
|
||||
fence. A previously recorded blocker does not prove that the operation remains
|
||||
blocked, and a previously unblocked operation does not retain permission after
|
||||
assignment, ownership, revision, or attachment authority changes.
|
||||
assignment, ownership, content, or attachment authority changes.
|
||||
|
||||
## Identity and fingerprinting
|
||||
|
||||
@@ -243,7 +243,7 @@ When an external provider is involved:
|
||||
|
||||
1. reserve stable intent and identity if retry needs them;
|
||||
2. invoke the provider with the strongest available idempotency, expected-old
|
||||
revision, or stable resource key;
|
||||
content digest, or stable resource key;
|
||||
3. verify the provider result through authoritative response or observation;
|
||||
4. commit only the checkpoint or result evidence that changes retry behavior;
|
||||
5. on retry, reread both the operation and current domain/provider authority
|
||||
@@ -315,7 +315,7 @@ The durable split is deliberate:
|
||||
|
||||
- `backend_jobs` stores immutable bounded input, its fingerprint, the current
|
||||
attempt number, and the final structured result or terminal failure;
|
||||
- `backend_job_attempts` binds one attempt and input revision to one dedicated
|
||||
- `backend_job_attempts` binds one attempt and input digest to one dedicated
|
||||
`(Workspace, Runtime, Worker)` identity, a precommitted tracked Runtime
|
||||
submission request identity, a deadline, and its bounded outcome;
|
||||
- `backend_job_deliveries` records best-effort notification separately from the
|
||||
@@ -342,7 +342,7 @@ The Ticket checker continues to select `builtin:backend-job` itself.
|
||||
|
||||
Job identity/result capability comes from an immutable Host-authored create and
|
||||
restore binding, not the Profile name, tag, instruction, Worker display name or
|
||||
Runtime placement. Backend checks the exact live Job/attempt/input revision and
|
||||
Runtime placement. Backend checks the exact live Job/attempt/input digest and
|
||||
authenticated Worker before accepting results or domain operations. Profile
|
||||
policy alone never confers a subject grant.
|
||||
|
||||
@@ -357,14 +357,14 @@ remains attention-required even after its Worker is removed.
|
||||
|
||||
Success is accepted only by the structured result boundary. Before Runtime
|
||||
input can execute, the attempt is fenced by Workspace, Job ID, attempt ID, input
|
||||
revision, bound Runtime Worker identity, and a fixed-size collision-resistant
|
||||
digest, bound Runtime Worker identity, and a fixed-size collision-resistant
|
||||
tracked submission request identity derived from the attempt. Ordinary Worker
|
||||
input and Console mutation are rejected for
|
||||
Job-owned Workers, so no later run in the same Session can exercise the result
|
||||
capability. Result JSON bytes are bounded before persistence. Worker final prose,
|
||||
Idle, Stopped, or a successful model call are not success evidence. Identical
|
||||
structured submission is idempotent; conflicting replay, stale or late attempts,
|
||||
wrong revisions, and other Workers fail closed.
|
||||
wrong input digests, and other Workers fail closed.
|
||||
|
||||
On restart, an unbound reservation is redispatched with its stable allocation.
|
||||
A dispatched attempt continues only when its bound Worker and precommitted
|
||||
|
||||
@@ -50,7 +50,7 @@ Clients may parse this spelling only for input assistance. A call becomes execut
|
||||
|
||||
TUI and Web composers retain selected invocations as atomic typed chips containing identity and structured arguments. Backspace/Delete and range deletion remove an unsent chip; removal is not an undo for an already executed operation. Draft history, transport retry, queueing, Session history, snapshot/live projections, and rewind restoration preserve the typed segment rather than flattening and reparsing it.
|
||||
|
||||
Completion requests carry a kind, fresh `request_id`, and source-qualified argument context. Host and Runtime echo the request ID unchanged. Clients correlate replies with that nonce plus their current draft revision, cursor, target Worker, authority snapshot, and connection generation; even identical-prefix/context ABA replies are discarded. Old uncorrelated replies cannot establish a selected invocation. Client-file arguments use a declared `InvocationClientAdapter`; they do not ask the Worker to resolve a client-local path.
|
||||
Completion requests carry a kind, fresh `request_id`, and source-qualified argument context. Host and Runtime echo the request ID unchanged. Clients correlate replies with that nonce plus their current draft identity/content and edit invalidation state, cursor, target Worker, authority snapshot, and connection generation; even identical-prefix/context ABA replies are discarded. Old uncorrelated replies cannot establish a selected invocation. Client-file arguments use a declared `InvocationClientAdapter`; they do not ask the Worker to resolve a client-local path.
|
||||
|
||||
## Attachment adapter
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ DCDL source
|
||||
|
||||
The Serde-compatible intermediate is private compiler infrastructure. Public APIs and persisted Flow runtime records use typed Flow domain values.
|
||||
|
||||
`flow_sources` stores one current logical Workspace-authored source per Workspace/slug. Every changed source creates an immutable `flow_source_revisions` row containing the original content, content digest, and compiled definition. Built-ins remain read-only embedded resources with an explicit monotonic resource revision; resolving one compiles and returns that resource snapshot without writing it into Workspace DB. Runtime pins source identity, revision, digest, and compiled definition in Worker state, so editing a Workspace source or updating a built-in resource never changes an existing instance.
|
||||
`flow_sources` stores one current logical Workspace-authored source per Workspace/slug. Every changed source retains the original content, content digest, and compiled definition in immutable source history. Built-ins are read-only embedded resources: resolving one compiles and returns its exact source snapshot without writing it into Workspace DB or maintaining a manual change counter. Runtime pins source identity, content digest, and compiled definition in Worker state, so editing a Workspace source or updating a built-in resource never changes an existing instance.
|
||||
|
||||
The compiler rejects unknown fields, unsupported schema versions, invalid/reserved identifiers, unknown transition targets, authored `$cancelled` state/targets, terminal states with outgoing transitions, non-terminal states without transitions, unreachable states, and reachable closed paths that cannot reach a user-declared terminal state. It injects the synthetic exceptional-cancellation transition and `$cancelled` terminal state after validation.
|
||||
|
||||
@@ -36,12 +36,12 @@ The compiler rejects unknown fields, unsupported schema versions, invalid/reserv
|
||||
|
||||
Flow source authority and Flow execution authority are split at the immutable source snapshot boundary.
|
||||
|
||||
The Workspace Backend stores only current Flow sources and immutable source revisions. Resolving a source-qualified selector returns the Workspace id, Flow id, revision, digest, and compiled definition. Resolution is read-only with respect to Flow execution: it never creates an instance, attempt, or event.
|
||||
The Workspace Backend stores only current Flow sources and immutable source history. Resolving a source-qualified selector returns the Workspace id, Flow id, content digest, and compiled definition. Resolution is read-only with respect to Flow execution: it never creates an instance, attempt, or event.
|
||||
|
||||
One Runtime Worker durably owns:
|
||||
|
||||
- the pinned source snapshot and compiled definition;
|
||||
- its active Flow instance, current state, revision, and lifecycle status;
|
||||
- its active Flow instance, current state, and lifecycle status;
|
||||
- its active transition attempt;
|
||||
- its ordered Flow events.
|
||||
|
||||
@@ -49,6 +49,15 @@ The complete `FlowRuntimeState` is persisted as a typed `flow.runtime.v1` Worker
|
||||
|
||||
Transition mutations clone the current Runtime state, append ordered events, persist the new session extension, and only then replace the in-memory projection. A persisted verifying attempt survives Runtime restart and same-Worker restore; the next transition request recovers it instead of creating a competing attempt.
|
||||
|
||||
The executable checks live in `FlowRuntimeState::begin_or_recover_transition` and `resolve_active_transition`, with private `begin_transition` / `resolve_transition` helpers:
|
||||
|
||||
- Before starting, require an active instance, no other active attempt, and the same compiled content digest as the pinned source. Reject an attempt ID already present in `TransitionRequested` events, including cancelled/failed attempts.
|
||||
- Before consuming a result, require its exact instance and currently reserved attempt ID. Then compare the compiled and captured definition digests against the pinned digest.
|
||||
- Before applying a successful verdict, require the instance to remain active and its current state to equal the captured source state. Validate the complete outgoing-condition result set and accept exactly one met condition.
|
||||
- Completion or cancellation releases the active attempt. A delayed result cannot consume a newer attempt even if the graph returns to the same state. The persisted event log prevents reusing the old attempt ID after restore.
|
||||
|
||||
There is no independent state counter or manually incremented built-in source counter. Event `sequence` remains only the append order of the actual event log, not a substitute for any of these conditions.
|
||||
|
||||
Worker stop retains the Flow with the Worker session. Restoring the same Worker reconstructs the latest state from session extensions and the saved Profile still determines `feature.flow` eligibility. Worker deletion removes the owning Worker/session; Flow state is not implicitly handed to another Worker.
|
||||
|
||||
Workspace Server schema migration v26 removes the legacy `flow_instances`, `flow_transition_attempts`, and `flow_events` tables. Backend/Web visibility, when needed, is a bounded Runtime Worker projection rather than a second instance authority.
|
||||
|
||||
@@ -31,7 +31,7 @@ Item interface `yoi.merge-request/item/v1`:
|
||||
| --- | --- | --- |
|
||||
| `read` | `ShowMergeRequest` | bounded thread `after` and `limit`; Backend resolves current source and target refs |
|
||||
| `check_readiness` | `CheckMergeRequestReadiness` | Backend resolves the current source subject and derives current approval/blockers |
|
||||
| `review` | `ReviewMergeRequest` | decision/body/findings plus the injected Reviewer capability, captured request event, Ticket revision, linked result snapshot, and exact candidate |
|
||||
| `review` | `ReviewMergeRequest` | decision/body/findings plus the injected Reviewer capability, captured request event, Ticket content digest, linked result snapshot, and exact candidate |
|
||||
| `complete` | `CompleteMergeRequest` | operation ID, approval event, target-before/after refs, merge strategy and conflict resolution; Backend requires Orchestrator authority |
|
||||
|
||||
Typical role surfaces are therefore:
|
||||
@@ -49,7 +49,7 @@ The native projection delegates to the same ordinary tools and REST endpoints. I
|
||||
- Workspace and configured-repository isolation;
|
||||
- current Coder assignment and assigned-Workdir source-selector proof for opening/review requests;
|
||||
- provider resolution of the exact current source candidate;
|
||||
- ReviewRequested binding to the Ticket item revision and complete linked-MR/source snapshot;
|
||||
- ReviewRequested binding to the Ticket item `content_digest` and complete linked-MR/source snapshot;
|
||||
- injected Reviewer capability consumption and exact MR/candidate matching;
|
||||
- source movement invalidating source approval while target-only movement preserves unchanged-source review;
|
||||
- unresolved request-change and revoked/stale approval rejection;
|
||||
|
||||
@@ -15,7 +15,7 @@ There is no Feature Object or `/features/objective` compatibility alias. Legacy
|
||||
|
||||
`<objective-reference>` is either a canonical `O-<digits>` key or an existing canonical internal alphanumeric ID. Only one direct child segment is accepted. Nested suffixes, Ticket keys, and unrelated or hidden prefixes do not resolve as Objective objects.
|
||||
|
||||
The Backend remains authoritative for existence, `O-*`/internal-ID resolution, Workspace scope, validation, audit events, revision generation, pagination, body/event bounds, and linked-Ticket validation. A discovered route is not independent authority; every operation still uses the Worker's current `WorkspaceClient`.
|
||||
The Backend remains authoritative for existence, `O-*`/internal-ID resolution, Workspace scope, validation, audit events, pagination, body/event bounds, and linked-Ticket validation. A discovered route is not independent authority; every operation still uses the Worker's current `WorkspaceClient`.
|
||||
|
||||
## Current operation inventory
|
||||
|
||||
@@ -44,9 +44,9 @@ The item operations intentionally do not accept an Objective `id`. The handler i
|
||||
|
||||
`query` retains the ordinary Worker limit of 1–100 and returns stable snippets, canonical `O-*` references, linked `T-*` references, `next_cursor`, and `has_more`. Each result also includes its canonical item path. It does not expand Objective bodies, complete events, or Ticket records.
|
||||
|
||||
`read` retains the ordinary Worker event limit of 1–50 and projects the Backend-bounded body, `body_truncated`, authoritative `revision`, linked-Ticket summaries, bounded events, and event pagination. Internal Objective, Ticket, and event-storage IDs are removed from model-visible results.
|
||||
`read` retains the ordinary Worker event limit of 1–50 and projects the Backend-bounded body, `body_truncated`, linked-Ticket summaries, bounded events, and event pagination. Internal Objective, Ticket, and event-storage IDs are removed from model-visible results.
|
||||
|
||||
`create` returns the authoritative Objective detail plus its canonical item path. Mutations return the authoritative post-operation detail. The revision observed in a successful read or mutation updates the item's object validator. The common Host validator contract rejects stale object/interface calls before dispatch, and the call response carries the refreshed route validator.
|
||||
`create` returns the authoritative Objective detail plus its canonical item path. Mutations return the authoritative post-operation detail. The Object publishes fixed operation signatures, not the Objective's mutable content: editing content does not invalidate an unchanged Object/Interface contract. The adapter therefore keeps no content-freshness cache or synthetic change identifier. Each invocation checks current permission and dispatches to current Backend authority; `edit` evaluates its explicit `old_string`/uniqueness conditions there. Reads always fetch current Backend data rather than replaying cached content. Host validators still protect route and Interface signatures before dispatch.
|
||||
|
||||
Linked Ticket references remain strings passed to the existing Objective Backend operation. Their presence does not publish a Ticket object, grant Ticket read or mutation authority, or require a Ticket-native WIP projection.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ The package validator may reject legacy core-Wasm artifacts and require Componen
|
||||
A future Server Plugin platform may execute Wasmtime Component Model packages only after the architecture is implemented as a coherent authority boundary:
|
||||
|
||||
1. an operator installs an immutable package into Server-owned artifact authority;
|
||||
2. a Workspace owner selects an installed package through an immutable Addon revision;
|
||||
2. a Workspace owner selects an installed package through an immutable Addon configuration snapshot bound to exact package/content digests;
|
||||
3. Backend authors a per-Worker execution plan containing exact identities, digests, configuration, and bounded grants;
|
||||
4. Runtime fetches only Server-authorized digests and verifies package bytes and execution-plan identity;
|
||||
5. Runtime instantiates a fresh bounded Wasmtime Store with no ambient WASI authority; and
|
||||
|
||||
@@ -58,9 +58,9 @@ Relative imports **inside a builtin source** resolve against its builtin virtual
|
||||
|
||||
Builtin dependencies share the Workspace snapshot budgets: at most 256 entries across Workspace entries and distinct observed builtin sources, 256 KiB per source, and 4 MiB of total source bytes. Unused Workspace entries still count; repeated imports of the same builtin count once. The observed import graph must be acyclic and at most 32 edges deep, **including the synthetic evaluation wrapper's import of the entrypoint**. These guards apply before the loader returns sources to Decodal, including imports in lazy object fields; builtins do not bypass them. Launch archives retain their separate transport limits (64 sources, 256 KiB per source, 1 MiB total source bytes).
|
||||
|
||||
The former `entries = [{ selector = "project:name"; source = "profiles/name.dcdl"; }]` registration is no longer accepted. Migrate it explicitly to `profile = import "./profiles/name.dcdl";`, preserving the selector, label, description and default. For registrations in a nested config file, adjust the relative import to the original virtual-tree entry (for example `../profiles/name.dcdl`). Do not rename or recreate the Profile file merely for registration. Previously saved source-based revisions remain readable as config data and are not rewritten, deleted, or silently substituted with a builtin. Their Profile projection/launch reports `profile_source_registration_removed` with the replacement form; saves under the new schema reject the old field. Existing Workers continue using their saved Manifest while the author migrates and saves the config.
|
||||
The former `entries = [{ selector = "project:name"; source = "profiles/name.dcdl"; }]` registration is no longer accepted. Migrate it explicitly to `profile = import "./profiles/name.dcdl";`, preserving the selector, label, description and default. For registrations in a nested config file, adjust the relative import to the original virtual-tree entry (for example `../profiles/name.dcdl`). Do not rename or recreate the Profile file merely for registration. Previously saved source-based saved content snapshots remain readable as config data and are not rewritten, deleted, or silently substituted with a builtin. Their Profile projection/launch reports `profile_source_registration_removed` with the replacement form; saves under the new schema reject the old field. Existing Workers continue using their saved Manifest while the author migrates and saves the config.
|
||||
|
||||
The schema contribution separates its materialized registration schema from an optional, fingerprint-bound `authoring_source`. The latter supplies editor field suggestions inside array elements and partial Profile records, plus a separate constraint-only analysis pass for supplied nested values (including imported recipes); that pass never materializes the shape, makes omitted fields required, or supplies defaults to the saved projection. Editor diagnostics point to the supplied value's virtual file/span. The authoring shape is never substituted for the persisted evaluation schema. Decodal structural diagnostics cover registration and nested Profile field types; the shared Backend save boundary additionally checks full Profile/Manifest semantics (including mode values, forbidden runtime fields, duplicate selectors and default references). UI and WIP saves use that same boundary; invalid candidates leave the active config revision untouched.
|
||||
The schema contribution separates its materialized registration schema from an optional, fingerprint-bound `authoring_source`. The latter supplies editor field suggestions inside array elements and partial Profile records, plus a separate constraint-only analysis pass for supplied nested values (including imported recipes); that pass never materializes the shape, makes omitted fields required, or supplies defaults to the saved projection. Editor diagnostics point to the supplied value's virtual file/span. The authoring shape is never substituted for the persisted evaluation schema. Decodal structural diagnostics cover registration and nested Profile field types; the shared Backend save boundary additionally checks full Profile/Manifest semantics (including mode values, forbidden runtime fields, duplicate selectors and default references). UI and WIP saves use that same boundary; invalid candidates leave the active config content untouched.
|
||||
|
||||
`scope` and `delegation_scope` retain both supported forms: a string such as `"workspace_read"` / `"workspace_write"`, or a table such as `{ intent = "workspace_read"; deny_write = ["private"]; symlink_policy = "resolved"; }`. Decodal 0.4 has no union range, so these fields are opaque to the editor constraint pass; a non-materialized table hint supplies nested completion without rejecting the string form or rewriting it. The shared Backend save validator checks scope forms, intent values and table fields (including their types and unknown keys). Other structurally typed fields such as `worker.mode` and `feature.task.enabled` retain editor type diagnostics. Completion hints never become Profile defaults or saved values.
|
||||
|
||||
@@ -72,9 +72,9 @@ A resolved Manifest is the concrete contract used to create or restore a Worker.
|
||||
|
||||
Source/partial layers may omit fields. Resolved manifests should be explicit enough that Worker creation does not depend on ambient configuration later changing under it.
|
||||
|
||||
`--manifest <path>` exists as an explicit low-level escape hatch. Normal fresh startup selects a `builtin:*` or `project:*` Profile from the Backend-managed Workspace Config revision rather than applying an ambient manifest cascade.
|
||||
`--manifest <path>` exists as an explicit low-level escape hatch. Normal fresh startup selects a `builtin:*` or `project:*` Profile from the Backend-managed Workspace Config content snapshot rather than applying an ambient manifest cascade.
|
||||
|
||||
Project Profiles are evaluated as part of the revisioned Virtual Config, including its imports and value composition. The Backend packages the **evaluated selected value** into a digest-bound JSON entry in the existing Profile archive transport, and binds the launch bundle to the Workspace identity, revision, tree/projection digests and schema/toolchain fingerprints. Runtime verifies the archive and resolves that exact value without re-evaluating Decodal, scanning imports or reading Workspace files. When Workspace evaluation imports builtins, the archive additionally seals the exact observed transitive builtin closure for the Workspace contract: canonical `$builtin/profiles/...` source keys, original Decodal bytes, content types, sizes and source digests, all bound by the outer archive and launch bundle digests. These are provenance dependencies, not runtime entrypoints or runtime import instructions. Missing or corrupt provenance fails verification even though the entrypoint is JSON; no local file or current embedded builtin can rescue it. Builtin Profiles selected directly retain their embedded Decodal archive path. Changing an embedded builtin so that it changes the evaluated Workspace value requires an **explicit config save/re-evaluation before a fresh launch**. The Backend compares current evaluation with the saved projection digest and rejects a mismatch rather than silently adopting the new value. T-711 retains the existing version-2 toolchain contract/fingerprint tuple so existing stored contracts remain readable; builtin provenance is bound by the archive and bundle digests, not a new catalog fingerprint field. Neither a builtin update nor a config save rewrites a sealed archive or silently rebases an existing Worker. Saving config changes only future fresh launches; the Worker persists the resulting resolved Manifest and restore uses that saved snapshot rather than selecting the current recipe again. Files below the Workdir are not implicit Profile override layers.
|
||||
Project Profiles are evaluated as part of the content-addressed Virtual Config, including its imports and value composition. The Backend packages the **evaluated selected value** into a digest-bound JSON entry in the existing Profile archive transport, and binds the launch bundle to the Workspace identity, tree/projection digests and schema/toolchain fingerprints. Runtime verifies the archive and resolves that exact value without re-evaluating Decodal, scanning imports or reading Workspace files. When Workspace evaluation imports builtins, the archive additionally seals the exact observed transitive builtin closure for the Workspace contract: canonical `$builtin/profiles/...` source keys, original Decodal bytes, content types, sizes and source digests, all bound by the outer archive and launch bundle digests. These are provenance dependencies, not runtime entrypoints or runtime import instructions. Missing or corrupt provenance fails verification even though the entrypoint is JSON; no local file or current embedded builtin can rescue it. Builtin Profiles selected directly retain their embedded Decodal archive path. Changing an embedded builtin so that it changes the evaluated Workspace value requires an **explicit config save/re-evaluation before a fresh launch**. The Backend compares current evaluation with the saved projection digest and rejects a mismatch rather than silently adopting the new value. T-711 retains the existing version-2 toolchain contract/fingerprint tuple so existing stored contracts remain readable; builtin provenance is bound by the archive and bundle digests, not a new catalog fingerprint field. Neither a builtin update nor a config save rewrites a sealed archive or silently rebases an existing Worker. Saving config changes only future fresh launches; the Worker persists the resulting resolved Manifest and restore uses that saved snapshot rather than selecting the current recipe again. Files below the Workdir are not implicit Profile override layers.
|
||||
|
||||
## Local stdio MCP server declarations
|
||||
|
||||
@@ -124,7 +124,7 @@ Prompts live under `resources/prompts` so builtins, project overrides, and user
|
||||
|
||||
The prompt layer should explain policy and behavior, but it should not smuggle volatile state into model context. Runtime facts that affect later turns must still go through history.
|
||||
|
||||
Builtin resources should be embedded at compile time. Project Profiles and prompt overlays belong to the Backend-managed revisioned Workspace Config and travel as digest-bound source archives. User Profile registries, provider/model catalog overrides, and explicit low-level Manifests remain filesystem-based where those local resolution paths are used.
|
||||
Builtin resources should be embedded at compile time. Project Profiles and prompt overlays belong to the Backend-managed content-addressed Workspace Config and travel as digest-bound source archives. User Profile registries, provider/model catalog overrides, and explicit low-level Manifests remain filesystem-based where those local resolution paths are used.
|
||||
|
||||
## Why this separation matters
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ A Feature database is an API/ownership isolation boundary inside the trusted Ser
|
||||
|
||||
## Registration and repository example
|
||||
|
||||
Registration is trusted startup code. Merely constructing or registering storage is lazy and creates no directory or database; the first `open` applies migrations.
|
||||
Registration is trusted startup code. Merely constructing or registering storage is lazy and creates no directory or database; the first `open` applies migrations. The simplified example below demonstrates storage ownership and transaction atomicity, not the production subjektiv schema or Memory-change API; those contracts are described in [subjektiv-store.md](subjektiv-store.md).
|
||||
|
||||
```rust
|
||||
use rusqlite::{Transaction, params};
|
||||
@@ -37,7 +37,7 @@ fn create_subject_memory(tx: &Transaction<'_>) -> StorageResult<()> {
|
||||
tx.execute_batch(
|
||||
"CREATE TABLE subjects (
|
||||
subject_id TEXT PRIMARY KEY,
|
||||
revision INTEGER NOT NULL
|
||||
state TEXT NOT NULL CHECK (state IN ('active', 'retired'))
|
||||
);
|
||||
CREATE TABLE memories (
|
||||
subject_id TEXT NOT NULL,
|
||||
@@ -75,7 +75,7 @@ impl SubjektivRepository {
|
||||
body: &str,
|
||||
intervention_id: &str,
|
||||
) -> StorageResult<()> {
|
||||
// Candidate application, memory revision, and intervention recording are
|
||||
// Candidate content and intervention recording are
|
||||
// one atomic transaction in the subjektiv Feature database.
|
||||
self.database.transaction(|tx| {
|
||||
tx.execute(
|
||||
@@ -83,11 +83,6 @@ impl SubjektivRepository {
|
||||
VALUES (?1, ?2, ?3)",
|
||||
params![subject_id, memory_id, body],
|
||||
)?;
|
||||
tx.execute(
|
||||
"UPDATE subjects SET revision = revision + 1
|
||||
WHERE subject_id = ?1",
|
||||
[subject_id],
|
||||
)?;
|
||||
tx.execute(
|
||||
"INSERT INTO interventions (intervention_id, subject_id, detail)
|
||||
VALUES (?1, ?2, 'candidate applied')",
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
Base: `3c424fd17d1a5e8bb5542de2d0ad88f55338e0ea` (current origin/develop; T-704 integrated). T-709's subjektiv consumer is not part of this change.
|
||||
|
||||
The reusable `job` crate owns the transport-neutral immutable request (registry Profile selector, input snapshot/reference/revision, caller Job ID, instruction, serialization key and bounded limits), result envelope/digest and validation. Backend retains its existing SQLite state machine, Runtime binding, domain grants, authorization, unknown-outcome and cleanup implementation; its adapter uses the shared request validation and result contract, not a second runner.
|
||||
The reusable `job` crate owns the transport-neutral immutable request (registry Profile selector, input snapshot/reference and SHA-256 digest, caller Job ID, instruction, serialization key and bounded limits), result envelope/digest and validation. Backend retains its existing SQLite state machine, Runtime binding, domain grants, authorization, unknown-outcome and cleanup implementation; its adapter uses the shared request validation and result contract, not a second runner.
|
||||
|
||||
Standalone owns a separate adapter, not a WorkspaceClient impersonation. `StandaloneHost` owns a Job service and its connection lifecycle. The concrete local placement is `<standalone state directory>/<WorkerId>/jobs.sqlite3` (using the existing Worker directory layout; alongside, not inside, interactive sessions). The `standalone` crate owns the versioned local schema: durable intents, immutable attempts, execution state, accepted result/digest, cancellation and consumer acknowledgement. A single Host connection is protected by the existing Worker lease. SQLite transactions serialize reserve, dispatch, result acceptance and terminal transitions. No subjektiv tables or fake Workspace/Runtime registry are created.
|
||||
|
||||
Local adapter states follow the existing intent/attempt/result distinctions. Exact immutable request replay returns the same Job, never starts another attempt. Explicit retry creates a bounded new attempt only for known failure; unknown outcomes require caller reconciliation and never auto-replay. Unstarted pending attempts remain pending on shutdown/reopen. Interrupted dispatch/running attempts without an accepted result become unknown; accepted results survive restart and remain available until consumer acknowledgement. Cancellation is durable and never auto-resumed. Consumer acknowledgement/delivery failure is separate from model success and never reruns the model.
|
||||
|
||||
Execution uses the existing private Internal Worker substrate with a new Host-facing bounded Job entry point. It gets a distinct ephemeral session, explicitly resolved selected Profile/system prompt/model, no ambient parent tools, filesystem, Workspace or Feature connections, and only a Job/attempt/revision-bound structured result submission capability. Profiles requiring unavailable tools/features are explicitly rejected rather than silently stripped. Result authority is independent of Profile name; final prose, Idle and Worker termination are never success evidence. This first generic adapter grants no domain operations; future consumers must provide explicit domain capabilities rather than gaining them through a profile tag.
|
||||
Execution uses the existing private Internal Worker substrate with a new Host-facing bounded Job entry point. It gets a distinct ephemeral session, explicitly resolved selected Profile/system prompt/model, no ambient parent tools, filesystem, Workspace or Feature connections, and only a Job/attempt/input-digest-bound structured result submission capability. Profiles requiring unavailable tools/features are explicitly rejected rather than silently stripped. Result authority is independent of Profile name; final prose, Idle and Worker termination are never success evidence. This first generic adapter grants no domain operations; future consumers must provide explicit domain capabilities rather than gaining them through a profile tag.
|
||||
|
||||
The Host limits concurrent active resources independently of caller limits, enforces timeout/turn/output limits, routes cancellation into the Internal Worker lifecycle and waits for resource cleanup. Accepted results are committed before resource release; required domain postprocessing must finish before submission (or be independently durable in its consumer). No daemon, pool, subprocess, HTTP/WS listener or Server dependency is introduced.
|
||||
|
||||
@@ -35,7 +35,7 @@ is success authority; merely returning from `wait` is not. The injected-client
|
||||
entry point resolves/validates the same selected Profile and model configuration;
|
||||
it replaces only the model transport for embedders/tests.
|
||||
|
||||
A typical generic consumer supplies its own stable Job ID/revision/reference,
|
||||
A typical generic consumer supplies its own stable Job ID/reference and immutable input,
|
||||
immutable JSON input, instruction and registry Profile:
|
||||
|
||||
```rust,ignore
|
||||
@@ -82,7 +82,7 @@ choosing `builtin:subjektiv-memory-consolidation` alone grants nothing.
|
||||
|
||||
Every run uses the existing private Internal Worker substrate, a distinct identity
|
||||
and ephemeral Session. Only `SubmitJobResult` is installed by a FeatureModule.
|
||||
Its model input is `{result: ...}`; Job/attempt/input-revision bindings come from
|
||||
Its model input is `{result: ...}`; Job/attempt/input-digest bindings come from
|
||||
the Host capability, not model arguments. The sink checks those bindings, the
|
||||
current persisted attempt, deadline, byte limit and result digest. Exact result
|
||||
replay is idempotent; changed/foreign/stale results are rejected. Submission is
|
||||
@@ -103,7 +103,7 @@ default / 64 KiB absolute byte limits, not a larger standalone exception.
|
||||
|
||||
Local schema version **1** is owned only by `standalone::job_store`: `job_intents`
|
||||
contains immutable serialized intent/fingerprint, serialization key, current
|
||||
attempt and acknowledgement; `job_attempts` contains immutable identity/revision,
|
||||
attempt and acknowledgement; `job_attempts` contains immutable identity/input digest,
|
||||
execution state, failure and accepted result/digest. Foreign keys, immutable
|
||||
identity/result triggers, transactions, `WAL` and `synchronous=FULL` protect the
|
||||
boundary. No Server schema or subjektiv/Memory schema is copied or opened.
|
||||
|
||||
@@ -18,6 +18,8 @@ Validation must distinguish scripted model/Host and real test-owned process/lock
|
||||
|
||||
## Implemented boundaries and validation
|
||||
|
||||
The validation counts and former API terminology below record T-709 at the cited source, not new executions. Current Memory changes and Job input-digest bindings follow [the store contract](subjektiv-store.md) and [the Job contract](standalone-jobs.md).
|
||||
|
||||
- `feature-storage` owns the shared SQLite connection policy/Feature manager; `subjektiv` owns the unchanged domain schema/migrations, Host-neutral API dispatch, and T-704 consolidation request/result/surface validation. Server modules remain thin adapters; Worker/standalone do not depend on Workspace Server.
|
||||
- `worker::subjektiv` exposes a nonserializable live Host capability plus a separate attenuated consolidation capability. Backend creation still requires the actual authenticated client and bound snapshot. Local creation is explicit Host injection; unavailable Backend never falls back locally.
|
||||
- The local catalog keeps private scope/language settings and common Feature storage under `subjektiv/`, separately from generic Subject Job ledgers. The common attribution table's existing `runtime_id` lookup key is the local storage namespace for local rows, not a running Runtime identity; public local evidence leaves `runtime_id`, Workspace, and Account fields absent. Worker IDs, Subject binding, and operator-managed scope must all match before Session content is read.
|
||||
|
||||
@@ -10,7 +10,7 @@ planned in T-708/T-709 are outside this Backend change.
|
||||
|
||||
The existing backlog/byte thresholds and missing/stale/failed surface policy
|
||||
remain the launch decision. A Job captures an immutable bounded batch (up to 100
|
||||
candidate IDs) plus the Subject store revision. Selection also reserves the Job's
|
||||
candidate IDs) plus the Subject `memory_fingerprint`. Selection also reserves the Job's
|
||||
16 KiB result budget, accounting for JSON-escaped IDs appearing in both the batch
|
||||
claim and compact verified outcomes. Excess candidates remain staged for a later
|
||||
batch. An empty batch is valid for a
|
||||
@@ -39,7 +39,7 @@ may snapshot remaining candidates once all previous Workers are safely cleaned.
|
||||
|
||||
## Memory and surface completion
|
||||
|
||||
`MemoryApplyCandidate` still uses the existing expected-revision checks, atomic
|
||||
`MemoryApplyCandidate` still uses the existing `expected_change_id` checks, atomic
|
||||
apply/close and immutable decision receipts. Partial application survives model
|
||||
failure, timeout, lost response, retry, Worker deletion and Backend recovery.
|
||||
Job replay is not a second memory apply: resolved candidates retain their
|
||||
@@ -51,9 +51,9 @@ clean-context surface editor and publish-or-recorded-failure, then adds the actu
|
||||
structured surface outcome. It does **not** schedule a Worker-owned post-Run task.
|
||||
The result call serializes and caches the exact generated outcome for ambiguous
|
||||
submission replay. The Backend verifies every batch disposition and the current
|
||||
store revision, generation ownership and persisted ready/failed surface fields;
|
||||
Memory-input fingerprint, generation-run ownership and persisted ready/failed surface fields;
|
||||
it enriches the durable result with actual immutable candidate disposition
|
||||
summaries (`candidate_id` and `action`). Full reasons, affected revisions and
|
||||
summaries (`candidate_id` and `action`). Full reasons, affected change references and
|
||||
source edges remain in the existing immutable receipt store; they are not copied
|
||||
into a bounded Job result. Exact replay uses the same verified compact projection.
|
||||
Thus confirmed-memory consolidation and surface readiness are separate outcomes.
|
||||
@@ -87,7 +87,7 @@ Subject-body/ordinary/Workspace-Memory Workers are never selected by this key.
|
||||
|
||||
The Job runner uses its existing durable dispatch allocation, tracked input,
|
||||
T-701 registry-bound restore/recovery, deadlines, unknown outcomes, result replay,
|
||||
delivery and T-690 cleanup. Job results, candidate receipts, Memory revisions,
|
||||
delivery and T-690 cleanup. Job results, candidate receipts, Memory changes,
|
||||
provenance and surfaces remain in their existing Backend stores after Worker
|
||||
removal. No live dogfood Worker or environment is stopped/updated by development
|
||||
validation.
|
||||
|
||||
@@ -36,7 +36,7 @@ or create a subjektiv database. T-668 opens it lazily for explicit subject
|
||||
operations or an authenticated subject extraction write; leaving the Feature
|
||||
disabled has no subject-store side effect.
|
||||
|
||||
## Versioned records
|
||||
## Immutable changes and schema contracts
|
||||
|
||||
All new persisted JSON records use `schema_version: 1`. Subject staging retains
|
||||
existing extract `StagingRecord` schema version 2 and its six kinds:
|
||||
@@ -45,47 +45,51 @@ and `lesson`. The host adds only `subject_id` and `created_at`; the model does
|
||||
not provide subject scope or provenance.
|
||||
|
||||
A subject record has a store-issued random ID, an explicit role, lifecycle state,
|
||||
and `store_revision`. It starts `active` at generation 0. Retirement is terminal
|
||||
and lifecycle timestamps. It starts `active` with no confirmed Memory. Retirement is terminal
|
||||
in this baseline and is distinct from stopping a Worker. Retirement prevents new
|
||||
writes but retains all reads and history.
|
||||
|
||||
A Memory revision contains:
|
||||
User-managed `behavior_md` is separate from generated Memory context. Updating it compares the exact previously read text through `expected_behavior_md`, not a behavior counter. Conflicts require rereading the actual current text; changing behavior neither changes confirmed Memory nor rewrites Worker history.
|
||||
|
||||
- a stable store-issued Memory ID and positive revision;
|
||||
An immutable Memory change contains:
|
||||
|
||||
- a stable store-issued Memory ID, opaque `change_id: String`, and optional `previous_change_id`;
|
||||
- subject ID, existing six-way kind, state, claim, optional Markdown body,
|
||||
`why_useful`, and optional staleness condition;
|
||||
- exact source candidate IDs and exact `{memory_id, revision}` derivations;
|
||||
- exact source candidate IDs and exact `{memory_id, change_id}` derivations;
|
||||
- a required change reason and host-issued creation/update times.
|
||||
|
||||
Revision 1 must start `active`. For every kind, `active` may remain active or
|
||||
The first change must start `active`. For every kind, `active` may remain active or
|
||||
become `resolved`/`retracted`; `resolved` may remain resolved, reopen to active,
|
||||
or be retracted. `retracted` is terminal. A corrected experience after
|
||||
retraction receives a separate Memory ID and may cite the old fixed revision.
|
||||
retraction receives a separate Memory ID and may cite the old fixed change.
|
||||
`active` means eligible for ordinary recall, not proven true and not authorized.
|
||||
|
||||
Refinement/correction of the same experience writes the next revision under the
|
||||
Refinement/correction of the same experience appends a new immutable change under the
|
||||
same Memory ID. A distinct experience or lesson gets a new ID. Every write uses
|
||||
an expected current revision; stale callers receive a typed `RevisionConflict`.
|
||||
`expected_change_id` equal to the current change ID; stale callers receive a typed `MemoryChangeConflict`.
|
||||
Old rows, evidence edges, and derivations are immutable and retained.
|
||||
|
||||
A surface snapshot stores only its body, exact Memory revision references, the
|
||||
subject's `built_from_store_revision`, and host-issued ID/time. Creation requires
|
||||
the generation to equal the subject's current store generation and permits at
|
||||
most one fixed revision of each Memory. The snapshot may deliberately select a
|
||||
`memory_fingerprint` is SHA-256 over the sorted, length-framed `(memory_id, current_change_id)` set, including all Memory states. Each committed content/state/provenance change gets a fresh change ID, preventing ABA reuse; staging alone does not change this fingerprint.
|
||||
|
||||
A surface snapshot stores only its body, exact Memory change references, the
|
||||
subject's `built_from_memory_fingerprint`, and host-issued ID/time. Creation requires
|
||||
the captured fingerprint to equal the current `memory_fingerprint` and permits at
|
||||
most one fixed change of each Memory. The snapshot may deliberately select a
|
||||
bounded subset; selection, generation, bounds, and injection policy are later
|
||||
work.
|
||||
|
||||
## Relational invariants and retention
|
||||
|
||||
The feature database contains `subjects`, `staging_records`,
|
||||
`staging_resolutions`, `memory_records`, immutable `memory_revisions`, normalized
|
||||
`staging_resolutions`, `memory_records`, immutable `memory_changes`, normalized
|
||||
candidate/derivation edges, and immutable `surface_snapshots` with normalized
|
||||
references. Composite foreign keys always include subject scope. As the physical
|
||||
database is already Workspace-scoped, Workspace identity is stored once in
|
||||
`store_scope` rather than copied into every row.
|
||||
|
||||
`memory_records` is the current-revision pointer and current JSON projection;
|
||||
`memory_revisions` is the immutable historical authority. Candidate JSON is
|
||||
`memory_records` is the `current_change_id` pointer and current JSON projection;
|
||||
`memory_changes` is the immutable historical authority. Candidate JSON is
|
||||
immutable. A resolution stores both the resolution JSON and the exact staged JSON
|
||||
bytes that were resolved. Aggregate seal rows prevent evidence, derivations,
|
||||
resolution targets, or snapshot references from being appended after their
|
||||
@@ -107,11 +111,11 @@ ownership as `transaction`.
|
||||
`SubjektivStore::apply_candidate` and `apply_candidates` atomically:
|
||||
|
||||
1. verify the active subject and every unresolved candidate in repository scope;
|
||||
2. check the expected Memory revision and every exact candidate/derivation ref;
|
||||
3. derive the revision's candidate provenance from that same candidate set;
|
||||
4. insert and seal the immutable Memory revision and evidence edges;
|
||||
5. update the current Memory projection and increment `store_revision` once;
|
||||
6. insert and seal each immutable candidate resolution and exact target revision.
|
||||
2. check the expected Memory change ID and every exact candidate/derivation ref;
|
||||
3. derive the change's candidate provenance from that same candidate set;
|
||||
4. insert and seal the immutable Memory change and evidence edges;
|
||||
5. update the current Memory projection and recompute the fingerprint of current Memory identities;
|
||||
6. insert and seal each immutable candidate resolution and exact target change.
|
||||
|
||||
Generic candidate resolution cannot emit `applied`, and direct Memory writes
|
||||
cannot attach candidate provenance; those relationships must go through this
|
||||
@@ -170,8 +174,8 @@ Memory migration remain separate work.
|
||||
|
||||
An enabled `feature.subjektiv` installs five names that do not overlap the legacy
|
||||
single-Markdown Memory tools: `SubjektivMemoryQuery`, `SubjektivMemoryRead`,
|
||||
`SubjektivMemoryListRevisions`, `SubjektivMemoryRemember`, and
|
||||
`SubjektivMemoryProposeRevision`. Runtime-signed Worker source plus current
|
||||
`SubjektivMemoryListChanges`, `SubjektivMemoryRemember`, and
|
||||
`SubjektivMemoryProposeChange`. Runtime-signed Worker source plus current
|
||||
`subjektiv:<subject-id>` singleton ownership is re-evaluated for every operation;
|
||||
none of the model-visible inputs contains a subject, Runtime, Worker, Session,
|
||||
origin, or raw provenance object.
|
||||
@@ -179,26 +183,23 @@ origin, or raw provenance object.
|
||||
Query searches only current projections. Omitted `states` means `active`; explicit
|
||||
empty `states` or `kinds` is invalid. Output defaults to 20 and is capped at 100,
|
||||
ordered by `updated_at DESC, memory_id ASC`. Its opaque cursor binds the subject,
|
||||
canonical filters, offset, and subject `store_revision`; any confirmed-Memory
|
||||
canonical filters, offset, and subject `memory_fingerprint`; any confirmed-Memory
|
||||
change makes it a typed stale-cursor conflict rather than silently mixing
|
||||
snapshots. Revision conflicts and stale cursors retain their diagnostic code as a
|
||||
snapshots. Change-ID conflicts and stale cursors retain their diagnostic code as a
|
||||
structured `{status: "error", error: {code, message}}` tool result instead of
|
||||
requiring models to parse prose. Revision history is ordered by revision
|
||||
descending. Its first page
|
||||
fixes the maximum revision, so later revisions neither duplicate nor displace old
|
||||
page members.
|
||||
requiring models to parse prose. Change history follows the immutable `previous_change_id` chain, newest first. Its first page fixes `head_change_id`, so later appended changes neither duplicate nor displace old page members. IDs are not counters and are not ordered numerically.
|
||||
|
||||
Read accepts a positive exact revision or resolves the current revision once. A
|
||||
missing historical revision never falls back to current. Markdown pagination is
|
||||
Read accepts an exact nonempty `change_id` or resolves the current change once. A
|
||||
missing historical change never falls back to current. Markdown pagination is
|
||||
line-based (default 200, maximum 1000) with a preliminary 16 KiB UTF-8 body
|
||||
cap and a global 56 KiB budget over the exact pretty-serialized model-visible
|
||||
response. JSON escaping, provenance, and metadata therefore reduce the current
|
||||
body page before the Worker content ceiling is reached. It reports
|
||||
`body_truncated`, `body_next_offset`, and `body_next_byte_offset`; an oversized
|
||||
or escape-heavy single line resumes from the returned UTF-8 byte boundary within
|
||||
that same line. Every continuation offset must also supply the exact revision
|
||||
returned by the first page, so a current-revision change cannot mix body versions.
|
||||
Provenance uses a separate immutable-revision-bound cursor and returns one
|
||||
that same line. Every continuation offset must also supply the exact `change_id`
|
||||
returned by the first page, so a current change cannot mix body contents.
|
||||
Provenance uses a separate immutable-change-bound cursor and returns one
|
||||
candidate/derivation reference per page. New staged candidates contain at most 10
|
||||
evidence records and 10 source references; read pages project at most two of each,
|
||||
with displayed anchor text capped at 64 UTF-8 bytes and nested offsets carried by
|
||||
@@ -206,10 +207,10 @@ the ordinary evidence cursor. Compatible candidates persisted before the admissi
|
||||
cap remain readable across as many evidence pages as needed, while every current
|
||||
staging path rejects new larger anchor sets. Candidate evidence is the bounded
|
||||
host-resolved anchor saved in staging; raw Session bodies are not copied
|
||||
into Memory responses. Resolved, retracted, and historical revisions remain
|
||||
into Memory responses. Resolved, retracted, and historical changes remain
|
||||
addressable by ID.
|
||||
|
||||
Remember and ProposeRevision only stage candidates. Entry references are resolved
|
||||
Remember and ProposeChange only stage candidates. Entry references are resolved
|
||||
against the host's committed Session capture, and preference candidates continue
|
||||
to require exclusively `HumanInput` evidence. When no entry is supplied, a
|
||||
non-preference request returns `pending_commit`; after the run commits, the host
|
||||
@@ -221,7 +222,7 @@ restore, and the fail-closed pre-rewrite hook prevents compaction from dropping
|
||||
unstaged receipt. Hook failure and process restart therefore retain an idempotent
|
||||
retry path, while an operation that never commits is not staged. Receipt identity is derived from Session and tool-call identity,
|
||||
and exact backend retries return the first candidate rather than creating another
|
||||
one. Neither path changes `memory_records`, `memory_revisions`, `store_revision`,
|
||||
one. Neither path changes `memory_records`, `memory_changes`, the confirmed-Memory fingerprint,
|
||||
or a surface snapshot.
|
||||
|
||||
|
||||
@@ -264,7 +265,7 @@ agree and the archived observation grant remains valid; incomplete, expired,
|
||||
corrupt, unavailable, or changed sources are reported explicitly rather than as
|
||||
an empty successful search.
|
||||
|
||||
Revision proposals add optional `revision_proposal` metadata to the existing v2
|
||||
Change proposals add optional `change_proposal` metadata to the existing v2
|
||||
`SubjectStagingRecord` envelope; automatic extraction remains proposal-free and
|
||||
its model schema is unchanged. The metadata is serialized atomically with the
|
||||
candidate and therefore remains present in candidate reads and immutable
|
||||
@@ -274,16 +275,16 @@ resolution copies:
|
||||
{
|
||||
"intent": "revise",
|
||||
"memory_id": "memory-…",
|
||||
"expected_revision": 3,
|
||||
"expected_change_id": "change-…",
|
||||
"change_reason": "The committed evidence corrects the prior condition"
|
||||
}
|
||||
```
|
||||
|
||||
This is the T-670 handoff fixture. Consolidation must re-read this typed metadata,
|
||||
re-check subject ownership, `expected_revision`, and the requested state
|
||||
re-check subject ownership, `expected_change_id`, and the requested state
|
||||
transition in the same transaction that applies the candidate, and leave a stale
|
||||
proposal unresolved/conflicted. It must not parse target information from the
|
||||
claim, retarget to the latest revision, or revive a retracted Memory. Valid
|
||||
claim, retarget to the latest change, or revive a retracted Memory. Valid
|
||||
transitions at staging are revise while active/resolved, active→resolved,
|
||||
active/resolved→retracted, and resolved→active. Retraction remains terminal.
|
||||
|
||||
@@ -295,27 +296,27 @@ batch grant. The common runner resolves the recipe; Profile names do not grant
|
||||
permissions. The Host derives authority from the authenticated current
|
||||
Job/attempt/Worker binding; model-visible inputs cannot select a Workspace,
|
||||
subject, Runtime, Worker, or Session. Its tools list/read only pending candidates
|
||||
in that batch, query/read confirmed revisions, and make one candidate decision.
|
||||
in that batch, query/read confirmed changes, and make one candidate decision.
|
||||
Candidate text is evidence rather than authority. No subject-specific idle Worker
|
||||
is resubmitted; see [Job execution and cutover](subjektiv-consolidation-jobs.md).
|
||||
|
||||
Every decision carries a stable request ID. An exact retry returns the committed
|
||||
receipt, while reuse with different input is a conflict. Applying a candidate
|
||||
atomically writes one new confirmed revision, its exact candidate and derivation
|
||||
edges, the immutable resolution and affected revision references, and advances
|
||||
the subject store revision. A non-applied decision explicitly records
|
||||
atomically writes one new confirmed change, its exact candidate and derivation
|
||||
edges, the immutable resolution and affected change references, and updates
|
||||
the subject Memory fingerprint. A non-applied decision explicitly records
|
||||
`discarded`, `invalid`, `duplicate`, or `already_covered`; an empty model response,
|
||||
transport failure, or aborted consolidation records no disposition and leaves the
|
||||
candidate pending. A successful applied response is the only evidence that
|
||||
confirmed Memory changed.
|
||||
|
||||
A typed revision proposal must be applied to its exact `memory_id` and
|
||||
`expected_revision`, preserving its intent and change reason. The transaction
|
||||
rechecks the current revision and state transition. It never rebases or retargets
|
||||
A typed change proposal must be applied to its exact `memory_id` and
|
||||
`expected_change_id`, preserving its intent and change reason. The transaction
|
||||
rechecks the current change ID and state transition. It never rebases or retargets
|
||||
a stale proposal, revives retracted Memory, or extracts a target from claim prose.
|
||||
Correction/refinement of the same experience appends under the same Memory ID;
|
||||
a corrected experience after terminal retraction receives a new Memory ID and may
|
||||
cite the retracted fixed revision as a derivation. Surface generation is a
|
||||
cite the retracted fixed change as a derivation. Surface generation is a
|
||||
separate post-consolidation lifecycle: its failure cannot roll back a committed
|
||||
candidate decision.
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Subject Memory surface generation
|
||||
|
||||
The subject Memory surface is a bounded start-time context derived from confirmed, revisioned Memory. It is not another Memory authority and is never used as extraction or consolidation input.
|
||||
The subject Memory surface is a bounded start-time context derived from confirmed Memory with immutable change history. It is not another Memory authority and is never used as extraction or consolidation input.
|
||||
|
||||
## Fixed policy
|
||||
|
||||
The Backend captures one generation from current `active` revisions for exactly one subject. Staging records, Session history, historical revisions, and prior surfaces are not queried.
|
||||
The Backend captures one bounded input snapshot from current `active` records for exactly one subject. Staging records, Session history, historical changes, and prior surfaces are not queried.
|
||||
|
||||
Selection is deterministic:
|
||||
|
||||
@@ -16,16 +16,16 @@ Selection is deterministic:
|
||||
6. both category and aggregate material bounds use the provider-independent estimate `ceil(UTF-8 bytes / 4)`;
|
||||
7. before invoking the editor, the Worker measures the actual normalized Agen request fields containing the rendered system prompt, conversation items (including the fixed question and material JSON), and the typed `SubmitMemorySurface` tool definition/schema against the 12,000-token complete-input budget. It reapplies equal per-category fairness reservations under that final estimator, reclaims unused shares in canonical order, fails rather than publishing a false empty surface if no grounded material fits, and guards every subsequent correction request before it reaches the provider. Provider-specific HTTP envelope bytes are outside this normalized request budget.
|
||||
|
||||
The clean-context editor uses the effective `builtin:subjektiv-memory-consolidation` profile model (`codex-oauth/gpt-5.6-luna`, medium reasoning). A generation permits three model turns (the initial attempt plus at most two correction turns). A store-generation conflict causes at most one complete re-read and regeneration. The published Markdown is capped at 1,024 tokens by the same estimate.
|
||||
The clean-context editor uses the effective `builtin:subjektiv-memory-consolidation` profile model (`codex-oauth/gpt-5.6-luna`, medium reasoning). A generation permits three model turns (the initial attempt plus at most two correction turns). A Memory-input fingerprint conflict causes at most one complete re-read and regeneration. The published Markdown is capped at 1,024 tokens by the same estimate.
|
||||
|
||||
Every editor point carries one or more exact `{memory_id, revision}` references. The Host checks the output shape, non-empty grounding, budget, subject scope, membership in the captured materials, and current store generation. These checks prove reference existence and freshness only; they do not claim semantic correctness.
|
||||
Every editor point carries one or more exact `{memory_id, change_id}` references. The Host checks the output shape, non-empty grounding, budget, subject scope, membership in the captured materials, and current `memory_fingerprint`. These checks prove reference existence and freshness only; they do not claim semantic correctness.
|
||||
|
||||
Publication is idempotent for identical output at one store revision. A different concurrent output cannot replace the first published surface. Confirmed Memory writes atomically mark the prior surface stale. Failed surface work never rolls back confirmed Memory or candidate disposition.
|
||||
Publication is idempotent for identical output for one exact Memory-input fingerprint. A different concurrent output cannot replace the first published surface. Confirmed Memory writes atomically mark the prior surface stale. Failed surface work never rolls back confirmed Memory or candidate disposition.
|
||||
|
||||
Resident injection uses five distinct product states:
|
||||
|
||||
- `ready`: inject the non-empty snapshot only when its `built_from_store_revision`
|
||||
equals the subject's exact current store revision;
|
||||
- `ready`: inject the non-empty snapshot only when its `built_from_memory_fingerprint`
|
||||
equals the subject's exact current `memory_fingerprint`;
|
||||
- `ready-empty`: a successful current generation selected no Markdown; inject no
|
||||
prose, but preserve the explicit successful-empty state;
|
||||
- `ungenerated`: no successful current generation exists; inject nothing;
|
||||
|
||||
@@ -12,13 +12,13 @@ A merged MR's review status/timestamps describe **integration approval**, not a
|
||||
|
||||
## Current requirements
|
||||
|
||||
`merge_request::requirement_approval` evaluates Ticket read-side MR evidence; it is not a Ticket completion gate. It validates an effective Reviewer approval of the current item revision and the **exact complete linked MR/source snapshot**, including the hosting MR's own source and corresponding ReviewRequested event. There is no primary-MR fallback, comment-derived approval, or timestamp-only freshness rule. A post-integration review may attest a new revision/snapshot without replacing MergeResult or its integration approval.
|
||||
`merge_request::requirement_approval` evaluates Ticket read-side MR evidence; it is not a Ticket completion gate. It validates an effective Reviewer approval of the current item content digest and the **exact complete linked MR/source snapshot**, including the hosting MR's own source and corresponding ReviewRequested event. There is no primary-MR fallback, comment-derived approval, or timestamp-only freshness rule. A post-integration review may attest new item content/a linked-result snapshot without replacing MergeResult or its integration approval.
|
||||
|
||||
`approved_current_subject` describes valid source/integration approvals. `review_after_rescope` retains its wire name but now describes exact current requirement attestation, not ordering of wall-clock timestamps. `approved_review` query filtering requires that current attestation. `stale_after_rescope` requires substantive item-edit history and a known source-approved snapshot lacking current attestation; unknown observations are only `missing_evidence`.
|
||||
|
||||
`complete_for_integration` means a nonempty linked MR set has valid source approvals and current requirement evidence, including for partially or fully integrated sets. It is not a claim that open repositories have been integrated, that target integration readiness has been checked, or that Ticket completion is authorized. An MRless Ticket has no applicable MR evidence: `missing` is empty, `review_status` is null, and approval/integration flags remain false. It does not match `missing_evidence` or `approved_review` merely because its state is done.
|
||||
|
||||
CompleteTicket belongs to the Ticket domain. It records the authenticated actor's reason/result and optional references using current item revision/state CAS and an operation receipt. It needs no MR or Reviewer approval, and does not create or modify those proofs. State setting, completion, close, and reopen share the same transactional update boundary. Exact replay returns current Ticket authority without reapplying a historical decision or appending an event. Historical MR-based `ticket_completion` records remain readable and distinct from new judgment receipts.
|
||||
CompleteTicket belongs to the Ticket domain. It records the authenticated actor's reason/result and optional references using current item `content_digest`/state CAS and an operation receipt. It needs no MR or Reviewer approval, and does not create or modify those proofs. State setting, completion, close, and reopen share the same transactional update boundary. Exact replay returns current Ticket authority without reapplying a historical decision or appending an event. Historical MR-based `ticket_completion` records remain readable and distinct from new judgment receipts.
|
||||
|
||||
Writable targets authorize deliverables, not mandatory MRs for every target. Every linked MR remains part of the exact requirement snapshot; no arbitrary latest-review or single-repository shortcut is used.
|
||||
|
||||
|
||||
@@ -4,33 +4,33 @@ Worker-originated `TicketCreate` and `TicketEditItem` mutations are checked afte
|
||||
|
||||
## Applied paths
|
||||
|
||||
The trigger is in the Workspace Server's authenticated Worker Ticket REST path. A complete, verified Runtime/Worker source header pair is required before a check is scheduled. Every successful Worker `Create` and `EditItem` operation schedules the persisted item revision; failed saves and thread/state events do not schedule work.
|
||||
The trigger is in the Workspace Server's authenticated Worker Ticket REST path. A complete, verified Runtime/Worker source header pair is required before a check is scheduled. Every successful Worker `Create` and `EditItem` operation schedules the persisted item content digest; failed saves and thread/state events do not schedule work.
|
||||
|
||||
The browser's direct item-edit handler currently does **not** schedule this check because it has no originating Runtime Worker advisory destination. Browser edits must not be presented as checked. Adding that path later requires an explicit notification/audit destination rather than borrowing an unrelated Worker identity.
|
||||
|
||||
## Immutable input and revision
|
||||
## Immutable input and content identity
|
||||
|
||||
The Job input contains only:
|
||||
|
||||
- canonical and user-facing Ticket IDs;
|
||||
- the exact saved item revision;
|
||||
- the exact saved item `content_digest`;
|
||||
- exact saved title and body;
|
||||
- whether the operation created or edited the item;
|
||||
- changed-field flags and the necessary previous title/body for edited fields.
|
||||
|
||||
The revision is the latest `create` or `item_edit` event ID, matching `TicketDetail.item_revision`. Conversation history, user requests, agreements, Worker transcripts, and external facts are not captured. The generic Backend Job input limit is 128 KiB; an oversized exact snapshot is not truncated or misrepresented as checked and produces a server diagnostic after the Ticket remains saved.
|
||||
The digest is SHA-256 of editable Ticket item content, matching `TicketDetail.content_digest`; it is not a `create`/`item_edit` event ID or an edit counter. Conversation history, user requests, agreements, Worker transcripts, and external facts are not captured. The generic Backend Job input limit is 128 KiB; an oversized exact snapshot is not truncated or misrepresented as checked and produces a server diagnostic after the Ticket remains saved.
|
||||
|
||||
## Execution and bounds
|
||||
|
||||
The trigger reserves durable intent synchronously after save, then dispatches without waiting for model completion. Concurrency pressure never rejects that intent: attempts above the four-Job cap remain durably `reserved`, and terminal attempt transitions plus restart recovery drain queued attempts through an atomic `reserved` → `dispatching` slot claim. It uses T-674's Backend-owned Job runner and an ordinary registered embedded Runtime Worker with `builtin:backend-job`. That profile selects the existing lightweight `codex-oauth/gpt-5.6-luna` model through normal model configuration and authentication. The checker has only `SubmitBackendJobResult`; it has no Ticket mutation, Queue, Worker management, conversation discovery, Web, Memory, or Workdir tools.
|
||||
|
||||
Each check is bounded to 4 concurrent Backend Jobs, 45 seconds, one attempt, an 8 KiB result, at most 8 findings, bounded finding fields, and the Backend Job runner's fixed input/instruction/delivery limits. The deterministic Ticket/revision Job ID suppresses duplicate execution. Generic Job and attempt records retain completed results and categorized dispatch, timeout, invalid-output, and other failures.
|
||||
Each check is bounded to 4 concurrent Backend Jobs, 45 seconds, one attempt, an 8 KiB result, at most 8 findings, bounded finding fields, and the Backend Job runner's fixed input/instruction/delivery limits. The deterministic Job ID hashes the complete checker intent, including the saved content, edit context, instruction and source/notification identity; exact retries reuse a Job, while a different edit or source producing the same Ticket content does not collide with it. Generic Job and attempt records retain completed results and categorized dispatch, timeout, invalid-output, and other failures.
|
||||
|
||||
## Result and advisory behavior
|
||||
|
||||
`internal.ticket_item_checker` defines the text-only categories and exact structured output. Server validation denies unknown fields, rejects duplicate or excessive findings, bounds every string, and requires each quote to be an exact substring of the inspected saved title or body. Invalid output terminalizes the attempt with `invalid_result` rather than treating it as a clean check.
|
||||
|
||||
Before advisory delivery, the server reloads the Ticket and compares the current item revision with the immutable checked revision. Findings for the current revision are sent only to the authenticated source Worker over the existing tracked `Notify` path. A notification identifies itself as a post-save checker advisory, includes Ticket/revision, exact quote, reason, and correction/confirmation direction, and explicitly states that it is not a user request, approval gate, workflow blocker, or implementation instruction.
|
||||
Before advisory delivery, the server reloads the Ticket and compares the current item content digest with the immutable checked digest. Findings for the current content are sent only to the authenticated source Worker over the existing tracked `Notify` path. A notification identifies itself as a post-save checker advisory, includes Ticket/content digest, exact quote, reason, and correction/confirmation direction, and explicitly states that it is not a user request, approval gate, workflow blocker, or implementation instruction.
|
||||
|
||||
Clean results and stale results send no Worker input. Their durable delivery records are terminalized with distinct suppression categories so restart recovery cannot later emit or repeatedly reclaim them. Notification failures remain separate from the completed checker result. A stopped or missing destination is never recreated or started.
|
||||
|
||||
|
||||
@@ -64,13 +64,13 @@ No native operation is inferred for a Tool that the Feature did not register. En
|
||||
|
||||
Backend 401/403 failures map to permission denial, 404 to missing objects, and deterministic 400/409/422 validation or workflow failures to invalid arguments when the typed operation fails before any successful mutation. Cancellation and interruption remain distinct. Post-mutation canonical-reference lookups are explicitly marked as unknown-outcome stages, so even a later deterministic HTTP status cannot misreport an already-committed write as a rejection. Transport, server, malformed response, and other post-dispatch failures retain unknown outcome, and mutating operations are not retried automatically.
|
||||
|
||||
Successful reads observe the authoritative item revision. Successful mutations advance a local observation generation even when the first call uses an internal ID whose canonical alias has not yet been learned. Once a read or mutation reveals the canonical key, the canonical/internal alias groups are merged and invalidated together. Relation mutations also invalidate the reported target Ticket, while queue results invalidate every reported queued Ticket, because those objects' incoming relations or workflow states changed. A stale validator is rejected by the common WIP Host before another Backend dispatch; the caller must rediscover/reinspect and explicitly decide whether to call again.
|
||||
Successful reads observe the authoritative item `content_digest`. Successful mutations advance a local observation generation even when the first call uses an internal ID whose canonical alias has not yet been learned. Once a read or mutation reveals the canonical key, the canonical/internal alias groups are merged and invalidated together. Relation mutations also invalidate the reported target Ticket, while queue results invalidate every reported queued Ticket, because those objects' incoming relations or workflow states changed. A stale validator is rejected by the common WIP Host before another Backend dispatch; the caller must rediscover/reinspect and explicitly decide whether to call again.
|
||||
|
||||
## Preserved Ticket contracts
|
||||
|
||||
The projection invokes the existing Tool and scoped Backend routes, so the following remain authoritative and are not reimplemented by WIP:
|
||||
|
||||
- item revision and audited item/thread/state events;
|
||||
- item content digest and audited item/thread/state events;
|
||||
- title/body/targets input validation;
|
||||
- planning-to-ready target validation and write-target requirement;
|
||||
- queue dependency traversal, planning-dependency rejection and cycle detection;
|
||||
|
||||
@@ -121,11 +121,11 @@ current read policy or unsupported legacy source makes a page sparse or empty;
|
||||
`empty` describes that page, and `has_more` remains authoritative for progress.
|
||||
Attachment pages use the Backend's SQL-bounded, alias-ordered limit/offset query
|
||||
and opaque `offset:<n>` cursors. Both Workdir and attachment responses supply an
|
||||
opaque complete-set `revision` derived from existing authoritative records in
|
||||
opaque complete-set `digest` derived from existing authoritative records in
|
||||
the same database snapshot as the page, not by hashing the first page. The Host
|
||||
uses this complete-set revision for the collection Object validator. A lifetime
|
||||
uses this complete-set digest for the collection Object validator. A lifetime
|
||||
or capability change beyond row 50 therefore invalidates old collection
|
||||
observations without adding a ledger or revision store. A cursor is not
|
||||
observations without adding a ledger or edit counter. A cursor is not
|
||||
a snapshot or authority grant: restart listing after concurrent collection
|
||||
changes. Filtering by exact `connection_id` is applied in the caller-scoped
|
||||
Backend ledger before paging, so direct item lookup also works beyond the first
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
## Authority and source mapping
|
||||
|
||||
Workspace configuration is not the Backend host's configuration directory. The
|
||||
canonical authored source is already a SQLite-backed, revisioned virtual tree:
|
||||
canonical authored source is already a SQLite-backed, content-addressed virtual tree:
|
||||
`workspace_config_trees`, `workspace_config_entries`, and append-only
|
||||
`workspace_config_tree_revisions`. `WorkspaceConfigState` combines its
|
||||
`workspace_config_tree_history`. `WorkspaceConfigState` combines its
|
||||
`ConfigTreeSnapshot`, toolchain/schema contract and projection digest. The
|
||||
`main.dcdl` entrypoint and its imports are authored files; evaluated profiles,
|
||||
prompts and other projections are not replacement source files.
|
||||
@@ -23,12 +23,15 @@ configuration subject; it is not a Bash cwd or a host filesystem grant.
|
||||
|
||||
## Existing save and activation contract
|
||||
|
||||
The browser source-tree editor uses `ConfigCommitRequest`: a base revision and
|
||||
tree digest, changes with expected entry digests, and the `main.dcdl`
|
||||
The browser source-tree editor uses `ConfigCommitRequest`: a `base_digest` of the complete source tree, changes with expected entry digests, and the `main.dcdl`
|
||||
entrypoint. Candidate evaluation applies changes, formats/normalizes Decodal,
|
||||
and evaluates the composed schema. Commit rechecks revision **and** digest in
|
||||
an SQLite Immediate transaction before replacing active entries and appending
|
||||
the revision manifest. Worker operations must share that conflict domain with
|
||||
and evaluates the composed schema. Commit rechecks both the tree digest and the
|
||||
base toolchain/projection identity in an SQLite Immediate transaction before
|
||||
replacing active entries. Source content remains immutable and digest-addressed;
|
||||
evaluation provenance is retained by source, toolchain and projection identity,
|
||||
so reevaluating unchanged source does not erase earlier schema/compiler evidence.
|
||||
A candidate prepared before a schema-only refresh cannot reactivate its old
|
||||
contract. Worker operations must share that conflict domain with
|
||||
UI and other Worker edits; a per-Worker mutex alone would not prevent lost
|
||||
updates.
|
||||
|
||||
@@ -36,7 +39,7 @@ The Backend commit orchestration validates the prompt projection and
|
||||
repository-access references, then commits and best-effort publishes the prompt
|
||||
projection to the embedded Runtime. Saving establishes new authoritative
|
||||
Workspace configuration, **not universal live application to existing Workers**.
|
||||
New Worker configuration bundles are revision-bound. In-flight consumers can
|
||||
New Worker configuration bundles are digest-bound. In-flight consumers can
|
||||
retain immutable projections. Skill availability and activation are separate
|
||||
contracts. A transport failure after dispatch is not proof of rollback and is
|
||||
not authorization to retry a mutation automatically.
|
||||
@@ -102,7 +105,7 @@ authored writes converge on source-tree commit. Existing runtime consumption of
|
||||
Prompt projections, Skill catalog/activation and resolved config bundles remains
|
||||
a separate read-only/activation contract needed for Worker startup and normal
|
||||
operation. Those evaluated projections are not authored source-tree bodies,
|
||||
revision history or a second editor. Do not fence all `/config` routes as one
|
||||
content history or a second editor. Do not fence all `/config` routes as one
|
||||
capability: that would break the existing Prompt reader's runtime contract.
|
||||
|
||||
The existing connection ledger remains authoritative. Each granted Worker has
|
||||
@@ -152,10 +155,13 @@ Worker operations use identity-bound routes under
|
||||
| Read | POST `read` | Connection + path validator; canonical body/type/digest |
|
||||
| Save | POST `commit` | Connection + root validator + canonical config changes/CAS |
|
||||
|
||||
Observe returns root revision/digest and path validators from the same snapshot.
|
||||
Observe returns root digest and path validators from the same snapshot.
|
||||
Opaque validators bind Workspace, Worker, grant, connection lifetime, logical
|
||||
path and whole-tree revision/digest. Even replacing a file with identical bytes
|
||||
in a later revision invalidates older observations. Whole-tree CAS is
|
||||
path, whole-tree digest and active/applied evaluation contract. Identical source
|
||||
paths, content types and bytes retain the same digest; saving them again under
|
||||
the same contract does not create an edit counter or invalidate a content
|
||||
observation by itself. A schema/toolchain refresh invalidates observations even
|
||||
when source bytes are unchanged. Whole-tree CAS is
|
||||
intentionally conservative: an unrelated UI edit can require fresh observation.
|
||||
The Client holds validators; the LLM must not manage or fabricate them.
|
||||
|
||||
@@ -190,7 +196,7 @@ operations, not a generic POSIX filesystem or an OS mount.
|
||||
|
||||
Content type is `decodal` (default for creation) or `text`. File operation
|
||||
targets come from the resolved WIP route. The root change batch uses canonical
|
||||
relative source paths; it does **not** accept `expected_digest`, revision,
|
||||
relative source paths; it does **not** accept `expected_digest`, tree digest,
|
||||
validator, Workspace, grant or connection overrides as model arguments. The
|
||||
adapter captures metadata and fills those preconditions automatically, requiring
|
||||
all paths in a batch to match one observed tree state. Unsupported/invalid
|
||||
@@ -233,7 +239,7 @@ post-save-response cases complement that sequence.
|
||||
Web tests exercise the production generic composer component's declaration
|
||||
completion, selection and typed submission, plus removal/literal-text behavior.
|
||||
Logical-source parsers and launch/sidebar/TUI tests ensure that a settings
|
||||
attachment is not treated as a repository revision or process cwd. The two
|
||||
attachment is not treated as a repository commit or process cwd. The two
|
||||
layers are complementary; the Rust fixture uses a recording LLM client and
|
||||
in-process transport, not a live browser-to-provider deployment. No live
|
||||
Backend/Runtime/LLM browser or physical power-loss result is claimed. Bounds
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
# Workspace Web Drive
|
||||
|
||||
The Web Drive is a latest-only shared Workspace resource, independent of Git, repositories, Worker sessions and Workdir occupancy. The Server's DB node tree, revision, receipt and current authorization are authoritative. The UI never uses physical blob keys/paths or stores a second persistent hierarchy.
|
||||
The Web Drive is a latest-only shared Workspace resource, independent of Git, repositories, Worker sessions and Workdir occupancy. The Server's DB node tree, last committed request, receipt and current authorization are authoritative. The UI never uses physical blob keys/paths or stores a second persistent hierarchy.
|
||||
|
||||
## References and authorization
|
||||
|
||||
`/w/<workspace>/drive/<node-id>` is a human-facing stable latest URL. Its node ID and revisions remain canonical positive decimal **strings** (including values above JavaScript's safe-integer range). Rename/move retain the URL; deleting and recreating a name does not revive the deleted ID. Copy latest URL emits this Web route, not the API's safe binary download URL. Ticket/Objective/comment Markdown can link to this route. Possessing any URL/ID grants no access: metadata, content, mutations, download and receipt queries all use the current authenticated API.
|
||||
`/w/<workspace>/drive/<node-id>` is a human-facing stable latest URL. Its node ID remains a canonical positive decimal **string** (including values above JavaScript's safe-integer range). Rename/move retain the URL; deleting and recreating a name does not revive the deleted ID. Copy latest URL emits this Web route, not the API's safe binary download URL. Ticket/Objective/comment Markdown can link to this route. Possessing any URL/ID grants no access: metadata, content, mutations, download and receipt queries all use the current authenticated API.
|
||||
|
||||
Ordinary Drive usage retains the existing member-facing API policy, not owner-only settings policy. Worker grants are owner-controlled on the existing Workers page, using its current authoritative Worker catalog. Read-only/read-write are explicit Backend grants, not consequences of enabling a Profile tool. The UI does not invent a Web read-only ACL: Backend denial is displayed and suppresses mutations. Worker read-only denial and revoke are separately exercised against the real API.
|
||||
|
||||
## Drafts, revision and delayed operations
|
||||
## Drafts, observed changes and delayed operations
|
||||
|
||||
The optional node route reuses one controller across Workspace/entry switches. Drafts and mutation receipts are keyed by Workspace/node identity in this page session. Reads/writes/status/list/search/ancestor/image completion are generation fenced, including A→B→A. No delayed response can select a new target or change its draft/save destination. Leaving the Drive page prompts before destroying a dirty draft or unresolved receipt; they are not claimed to survive a browser reload.
|
||||
|
||||
Text edits use the read revision as `expected_revision`. A conflict preserves the draft and that revision, displays an explicit conflict and never silently rebases, automerges or resubmits. Refresh can show the latest saved text alongside the draft. Discard/load-latest is an explicit destructive user decision. Partial previews cannot be saved. Save acknowledgments retain edits made while saving.
|
||||
Text edits retain the `last_mutation_id` returned with the read and send it as `expected_mutation_id`. A conflict preserves the draft and that original observation, displays an explicit conflict and never silently rebases, automerges or resubmits. Refresh can show the latest saved text alongside the draft. Discard/load-latest is an explicit destructive user decision. Partial previews cannot be saved. Save acknowledgments retain edits made while saving.
|
||||
|
||||
## Transfer and media policy
|
||||
|
||||
- Markdown/plain text: 64 KiB UTF-8 read/edit limit. Markdown uses the existing raw HTML-rejecting safe document renderer. Other active/binary media are download-only.
|
||||
- Raster image preview: PNG/JPEG/GIF/WebP, authenticated revision-bound fetch, 256 KiB bounded bytes/Blob; decode/load failure is explicit. Object URLs are released on switching/destruction. Larger images remain downloadable.
|
||||
- Download: native same-origin authenticated binary endpoint, pinned revision; Server's attachment/nosniff/sandbox/no-store headers remain authoritative.
|
||||
- Raster image preview: PNG/JPEG/GIF/WebP, authenticated committed-request-bound fetch, 256 KiB bounded bytes/Blob; decode/load failure is explicit. Object URLs are released on switching/destruction. Larger images remain downloadable.
|
||||
- Download: native same-origin authenticated binary endpoint, bound to the observed committed request; Server's attachment/nosniff/sandbox/no-store headers remain authoritative.
|
||||
- Markdown inline images never automatically fetch external/cross-Workspace/API resources. They display alternative text and require explicit navigation to an authorized Drive URL. Same-Workspace resource links navigate normally; external HTTP(S) links have no userinfo, open a new tab with `noreferrer`; script/data/file, protocol-relative, arbitrary API/relative paths are not links.
|
||||
- Upload/replacement: at most 16 MiB raw Blob request, not JSON/base64 or DOM bytes. SHA-256 needs one bounded ArrayBuffer; there is no constant-memory browser hashing claim. Transfer/pending is distinct from the DB-published acknowledgment.
|
||||
- A request ID is allocated before a mutation. Transport/response loss or abort after transmission is conservatively unknown, not success/failure. Its current authorized receipt endpoint is queried; no new request or automatic reupload is generated. `uncommitted` means no commit observed yet, not proof that an in-flight publication cannot still finish. Unknown requests block new writes to that entry.
|
||||
|
||||
Name conflict, revision conflict, limit, denied, missing node, storage/I/O failure and outcome unknown are not translated into empty contents/empty Drive/success. Initial folder deletion is empty-only with explicit confirmation; no recursive operation or blob-copy rename is introduced.
|
||||
Name conflict, changed-content conflict, limit, denied, missing node, storage/I/O failure and outcome unknown are not translated into empty contents/empty Drive/success. Initial folder deletion is empty-only with explicit confirmation; no recursive operation or blob-copy rename is introduced.
|
||||
|
||||
## Validation
|
||||
|
||||
@@ -34,4 +34,4 @@ Browser API fixtures and real authority evidence are intentionally separate:
|
||||
- `web/workspace/test/drive-browser`: production static shell + synthetic API fixtures, pinned Chromium interactions and visual evidence.
|
||||
- `crates/workspace-server/src/server/tests/drive_web_roundtrip_tests.rs` and `web/workspace/test/drive-real-api`: hermetic real Server HTTP, SQLite, FeatureStorage and LocalFileSystem, repositoryless bootstrap and production Web adapter. Only the Runtime process boundary is fake; no live dogfood data is used.
|
||||
|
||||
See the test READMEs and T-724 validation record for exact commands/results, artifact paths, source revisions and any deliberate failing fixture state.
|
||||
See the test READMEs and T-724 validation record for exact commands/results, artifact paths, source commits and any deliberate failing fixture state.
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
## Scope and ownership
|
||||
|
||||
Drive is a Workspace-owned **latest-version document tree**, not a mounted or
|
||||
POSIX-compatible filesystem. `workspace-drive` owns storage, hierarchy, revision
|
||||
conflicts, mutation receipts, bounded reads/search, and garbage collection.
|
||||
POSIX-compatible filesystem. `workspace-drive` owns storage, hierarchy, conflicting
|
||||
changes, mutation receipts, bounded reads/search, and garbage collection.
|
||||
`WorkspaceApi::drive` is the trusted Server adapter. The authorized HTTP API and
|
||||
Backend-managed grants are documented below. Worker Tools/WIP, Web UI, and full
|
||||
end-to-end routing belong to T-723–725.
|
||||
@@ -52,10 +52,11 @@ operations. External edits of the store are outside the Drive operation contract
|
||||
Migration 1 registers `workspace-drive` through FeatureStorage. Each scoped DB
|
||||
contains its Workspace binding, nodes, deletion tombstone, and mutation receipts.
|
||||
SQLite `AUTOINCREMENT` node IDs are stable, scoped to a Workspace and never
|
||||
reused after deletion. They are opaque application IDs, not inodes. IDs and
|
||||
revisions serialize as canonical positive **decimal strings**, preserving signed
|
||||
64-bit precision across JavaScript DTO boundaries. Restore preserves
|
||||
`sqlite_sequence` as well as IDs and revisions.
|
||||
reused after deletion. They are opaque application IDs, not inodes. Node IDs serialize as canonical positive **decimal strings**, preserving signed
|
||||
64-bit precision across JavaScript DTO boundaries. `last_mutation_id` is the
|
||||
existing request ID of the last committed mutation, not a counter or timestamp.
|
||||
The immutable root has an empty value. Restore preserves `sqlite_sequence`,
|
||||
node IDs, request IDs and receipts.
|
||||
|
||||
The DB is the only tree/latest-reference authority. Each node has one parent;
|
||||
root is the sole parentless directory, has empty name, and cannot be renamed,
|
||||
@@ -68,17 +69,19 @@ Parents must be directories in this scoped Workspace. Self/descendant moves are
|
||||
rejected. Ordinary folder deletion is **empty-only**, never implicitly recursive.
|
||||
Workspace destruction is the separate fenced purge operation.
|
||||
|
||||
Every update/move/delete requires `expected_revision`. A stale revision or
|
||||
Every update/move/delete requires `expected_mutation_id`. The same IMMEDIATE
|
||||
transaction compares it with the node’s last successful request ID before writing.
|
||||
A different committed request or
|
||||
same-name/request-identity competitor returns `Conflict`; it never falls back to
|
||||
unconditional writes. Root mutations and malformed requests are `Invalid`;
|
||||
missing/deleted IDs are `NotFound`. Revision exhaustion fails rather than wrapping.
|
||||
missing/deleted IDs are `NotFound`. No update counter is allocated or incremented.
|
||||
Actor identity is trusted caller input (the authorization adapter supplies it),
|
||||
and successful mutations record actor and UTC epoch milliseconds.
|
||||
|
||||
`BEGIN IMMEDIATE` is held for validation, blob operations, metadata mutation and
|
||||
receipt insertion. It serializes the whole Workspace hierarchy and all blob
|
||||
read/upload/GC activity across independent processes. A→B/B→A, parent deletion
|
||||
versus child creation, and same-revision updates therefore have one valid winner.
|
||||
versus child creation, and updates based on the same observation therefore have one valid winner.
|
||||
FeatureStorage configures foreign keys, WAL, `synchronous=FULL`, and a 5-second
|
||||
busy timeout. Busy/SQL/I/O failures remain errors; they do not mean an empty Drive.
|
||||
Before opening SQLite or admitting a Feature handle, FeatureStorage creates and
|
||||
@@ -122,7 +125,7 @@ publication is not power-loss durability. The small adapter compensates:
|
||||
1. Create/validate Host directories; `sync_all` their entries and parents.
|
||||
2. Use LocalFileSystem to atomically create the immutable blob.
|
||||
3. `sync_all` the published file, then its containing directory.
|
||||
4. Only after both succeed, update the DB reference/revision and insert the
|
||||
4. Only after both succeed, update the DB reference/last committed request and insert the
|
||||
receipt in the **same SQL transaction**.
|
||||
5. SQLite commit is the sole success/publication point.
|
||||
|
||||
@@ -188,9 +191,9 @@ page; an empty matching page can still have continuation. Name search is exact
|
||||
substring matching. Optional bounded text search inspects only UTF-8 `text/*`
|
||||
files no larger than 64 KiB, never images/large files. No index/embedding exists.
|
||||
|
||||
Each read supplies node ID **and the selected revision**, and is locked to that
|
||||
Each read supplies node ID **and the observed committed mutation**, and is locked to that
|
||||
blob for the entire chunk. The SQLite lock excludes upload/GC while reading.
|
||||
After an update the old revision expires: the next chunk returns `Conflict` (or
|
||||
After an update the previous observation expires: the next chunk returns `Conflict` (or
|
||||
`NotFound` after deletion), not the new generation. No long-lived read lease or
|
||||
full-history retention is promised. Binary ranges are byte-based and exact;
|
||||
text reads require UTF-8 character-aligned offsets/ranges and reject split or
|
||||
@@ -254,7 +257,7 @@ DB-plus-object snapshot is implemented. Use this administrative procedure:
|
||||
scope and Host blob roots. Use `ScopedFeatureStorage::restore` for its snapshot
|
||||
format, or restore the entire stopped deployment consistently. Never restore
|
||||
just `server.db` or create a new empty Drive DB over surviving blobs.
|
||||
6. Restore blobs from the matching generation; restore internal IDs, revisions,
|
||||
6. Restore blobs from the matching generation; restore internal IDs, committed request IDs,
|
||||
receipts and `sqlite_sequence` unchanged. Validate references/lengths and
|
||||
sanitized metadata/text/binary reads before reopening admission. Resume GC
|
||||
only after validation. A deletion tombstone must not be cleared on restore.
|
||||
@@ -301,12 +304,13 @@ wins first is ordered first. A mutation committing before revocation is valid;
|
||||
a revoked cached handle cannot commit later. Upload admission also checks access
|
||||
before consuming the body, but that admission is not a lease: publication always
|
||||
rechecks current access. Download checks authorization separately for every
|
||||
chunk, and stops on revocation, deletion, revision expiry or storage failure.
|
||||
chunk, and stops on revocation, deletion, a changed committed request or storage failure.
|
||||
|
||||
References contain **both** Workspace ID and stable decimal node ID. JSON uses
|
||||
`DriveEntryRef`; flat GET/binary queries require `entry_workspace_id` matching the
|
||||
route Workspace before resolving `id`/`parent_id`. IDs/revisions are canonical
|
||||
positive decimal strings within signed 64-bit range; file sizes/offsets are
|
||||
route Workspace before resolving `id`/`parent_id`. Node IDs are canonical
|
||||
positive decimal strings within signed 64-bit range. Mutation IDs are request
|
||||
strings (1–128 UTF-8 bytes, no control characters); file sizes/offsets are
|
||||
bounded `u32`. List/search cursors carry Workspace identity and the last node ID;
|
||||
they are continuation hints, not capabilities or snapshots. Returned `latest_url`
|
||||
is a relative authenticated URL bound to Workspace and ID, independent of logical
|
||||
@@ -321,7 +325,7 @@ most one bounded binary file in memory before calling T-721; they are not
|
||||
constant-memory object-store multipart uploads and are never expanded to
|
||||
JSON/base64. Runtime source-proof verification separately buffers a bounded body
|
||||
before granting trusted ingress, as in other signed Server operations. Web
|
||||
downloads stream **64 KiB** revision-fixed chunks; they do not materialize the
|
||||
downloads stream **64 KiB** committed-request-bound chunks; they do not materialize the
|
||||
whole file. Generated Rust clients retain their explicit bounded-response policy
|
||||
and return exact `BinaryBody` bytes. Worker JSON-facing adapters can request
|
||||
bounded text or binary chunks through this same contract, but Tools/WIP and
|
||||
@@ -330,12 +334,12 @@ base64 JSON blobs.
|
||||
|
||||
Downloads validate bare MIME types and always use `Content-Disposition:
|
||||
attachment` with percent-encoded UTF-8 names, `nosniff`, a sandbox/default-none
|
||||
CSP, `private, no-store`, and a Workspace/node/revision ETag. Arbitrary HTML/SVG
|
||||
CSP, `private, no-store`, and a SHA-256 ETag over Workspace, node ID and committed request ID. Arbitrary HTML/SVG
|
||||
bytes are storable, not safe app-origin inline previews. Content length is exact;
|
||||
if a later chunk loses authority or expires, the response stream fails rather
|
||||
than returning another generation or a successful truncated file. Even an empty
|
||||
file validates its referenced blob before HTTP success. Conditional 304/range
|
||||
HTTP semantics are not added; revision-selected chunk reads are explicit.
|
||||
HTTP semantics are not added; observation-bound chunk reads are explicit.
|
||||
|
||||
Errors use fixed, path-free typed codes: `denied`, `not_found`, `conflict`,
|
||||
`invalid`, `limit`, `storage_unavailable`, `outcome_unknown`. SQL/commit/task
|
||||
@@ -366,3 +370,22 @@ done
|
||||
The existing BinaryBody contract also supports binary **success responses**,
|
||||
including declared response headers, without changing binary request bytes or
|
||||
JSON/error behavior. It exports inline OpenAPI binary bodies, not JSON byte arrays.
|
||||
|
||||
## Upgrade to receipt-bound mutation checks
|
||||
|
||||
Migration 2 obtains each non-root node’s latest request ID from its existing
|
||||
committed receipt, transforms historical receipt payloads, and drops the old
|
||||
update counter. The current-state receipt must be unique; missing or ambiguous
|
||||
evidence aborts and rolls back the entire migration. Equal timestamps do not
|
||||
change the result. No content, node identity, request fingerprint or blob is
|
||||
regenerated. Deleted nodes’ receipts and AUTOINCREMENT history remain intact.
|
||||
|
||||
The frozen migration module retains old SQL/JSON names and proven predecessor
|
||||
correspondence only to verify existing receipts against their original request
|
||||
fingerprints. This evidence never authorizes a new mutation. Active node CAS and
|
||||
HTTP contracts do not accept the old numeric precondition. Exact pre-upgrade
|
||||
Update/Relocate/Delete replays are accepted only when the complete actor/payload
|
||||
and mapped predecessor reproduce the original fingerprint. If that evidence
|
||||
cannot be established, recover the committed result through `request_status`;
|
||||
changing the actor, payload or precondition and reusing its ID is a conflict,
|
||||
not permission to execute it again.
|
||||
|
||||
@@ -26,7 +26,7 @@ conflict. No schema migration is needed for the existing fingerprint/Workspace
|
||||
receipts.
|
||||
|
||||
A replay is not a historical snapshot. It returns the current Workspace and
|
||||
config revision. If creation supplied a Repository, it returns the current
|
||||
config content snapshot. If creation supplied a Repository, it returns the current
|
||||
record **at the originally requested key**. Removal or renaming away from that
|
||||
key produces `repository: null`; registration at that key later returns that
|
||||
current record. Replay never re-registers or resets an asset. If creation omitted
|
||||
|
||||
@@ -9,7 +9,7 @@ Never use the live dogfood Server or Runtime as the first startup test for a new
|
||||
1. Build the production entrypoints: `cargo build -p worker-runtime --bin yoi-runtime -p yoi-workspace-server --bin yoi-server`.
|
||||
2. Run the focused and dependent tests for the changed contracts, followed by workspace-root `cargo check`, `cargo fmt --all -- --check`, and `git diff --check HEAD`.
|
||||
3. Exercise the provisioning and operational checks in [Workspace ↔ Runtime authentication](server-runtime-auth.md) against isolated Server DB, Runtime data, and ports. The Workspace owner must create the binding, the Runtime operator must install the Workspace issuer bundle, and the challenge proof must become verified.
|
||||
4. Have an external supervisor or operator restart Server and Runtime at the same generation. A Worker hosted by the target Runtime must never terminate its own Runtime.
|
||||
4. Have an external supervisor or operator restart Server and Runtime from the same validated source commit/build artifacts. A Worker hosted by the target Runtime must never terminate its own Runtime.
|
||||
5. Verify post-restart readiness through the Workspace Runtime projection, ping, Worker list/create, protocol subscription, and a Runtime-to-Server source-proof operation before treating the environment as healthy.
|
||||
|
||||
The former isolated startup shell harness depended on removed Server-global trust commands and is intentionally not a fallback smoke path. New automated startup coverage must provision the same Workspace-scoped binding and challenge authority used by production rather than recreating global trust or seeding private authority directly.
|
||||
@@ -25,7 +25,7 @@ A report is useful when it explains:
|
||||
- what design boundary was missing
|
||||
- what evidence was observed
|
||||
|
||||
For a Remote Runtime rollout, also record the source commit, binary generation, Server schema version, Runtime binding revision, Workspace key generation, and typed HTTP/WebSocket outcomes. A successful document response does not outweigh visible UI, console, or API errors.
|
||||
For a Remote Runtime rollout, also record the source commit, binary artifact digest, Server schema version, Runtime `binding_id`, Workspace `key_id` and `workspace_trust_id`, and typed HTTP/WebSocket outcomes. A successful document response does not outweigh visible UI, console, or API errors.
|
||||
|
||||
## Runtime command caveat
|
||||
|
||||
|
||||
@@ -24,12 +24,12 @@ size independently of persistence metadata.
|
||||
retain their existing validator, identity and save boundary in the provider.
|
||||
- Workspace settings grants, access mode and connection lifetime remain in Backend.
|
||||
The settings adapter binds the read to captured entry metadata and generates a
|
||||
canonical commit using the same tree revision/digest. Decodal validation,
|
||||
canonical commit using the same tree digest. Decodal validation,
|
||||
formatting and atomic multi-file persistence remain in the canonical Backend
|
||||
commit. Content type, create/delete/rename/apply_changes and attach are settings
|
||||
operations, not ordinary OS file operations.
|
||||
|
||||
The core does not perform I/O, authorize anything, or own revision/existence
|
||||
The core does not perform I/O, authorize anything, or own content/existence
|
||||
mirrors. Providers invoke pure processing inside their existing consistency
|
||||
boundary; it must not be used to turn a checked edit into an unchecked
|
||||
Read–Write sequence.
|
||||
|
||||
@@ -6,10 +6,10 @@ Server-global な署名鍵や Runtime 側の trusted-Server catalog は使わな
|
||||
## Authority
|
||||
|
||||
- Server DB は Workspace ごとの signing identity と Runtime binding を保持する。
|
||||
- Runtime は `trust-workspace` で受理した `WorkspaceIssuerAuthorizationBundle` を保持する。
|
||||
- bundle は `workspace_id`、Workspace key id/generation、Workspace public key、Backend URL、許可された Runtime identity を固定する。
|
||||
- Runtime は `trust-workspace` で受理した `WorkspacePublicIdentityBundle` を保持する。
|
||||
- bundle は `workspace_id`、Workspace `key_id`、Workspace public key/fingerprint、Backend URL を固定する。
|
||||
- Server → Runtime の各 HTTP / WebSocket request は、対象 Workspace の signing identity で短命な capability token を発行する。
|
||||
- Runtime は request method、`path_and_query`、body digest、permission、Workspace、Runtime、key generation、expiry、JTI を検証する。
|
||||
- Runtime は request method、`path_and_query`、body digest、permission、Workspace、Runtime、key ID、現在の trust ID、expiry、JTI を検証する。
|
||||
- Runtime → Server の source proof は Runtime identity で署名し、対象 Workspace と bundle の Backend URL を audience に固定する。
|
||||
- Server は現在の Workspace Runtime binding、Runtime public key、Backend public URL、request target、body digest、permission、expiry、replay state を検証する。
|
||||
|
||||
@@ -17,6 +17,13 @@ Server-global な署名鍵や Runtime 側の trusted-Server catalog は使わな
|
||||
|
||||
## Provisioning
|
||||
|
||||
The enrollment input includes the exact Runtime-issued `workspace_trust_id` from
|
||||
`trust-workspace add/show`. Export the current Workspace public bundle first,
|
||||
accept it on the Runtime, then register the Runtime public bundle, endpoint and
|
||||
trust ID. Server verification binds that enrollment ID; it must never invent an
|
||||
ID from the key fingerprint or assume an initial counter value. Re-enrollment
|
||||
requires registering the newly issued trust ID, even when the key is unchanged.
|
||||
|
||||
1. Runtime identity を初期化する。
|
||||
|
||||
```sh
|
||||
@@ -24,17 +31,17 @@ Server-global な署名鍵や Runtime 側の trusted-Server catalog は使わな
|
||||
yoi-runtime identity show
|
||||
```
|
||||
|
||||
2. Workspace owner が Settings → Runtimes から Runtime public bundle と endpoint を登録する。
|
||||
3. Server が Workspace issuer bundle と challenge を発行する。
|
||||
4. operator が bundle を Runtime に追加する。
|
||||
2. The Workspace owner exports the current Workspace issuer public bundle from Workspace identity settings.
|
||||
3. The operator accepts that bundle on the Runtime and reads the issued trust ID.
|
||||
|
||||
```sh
|
||||
yoi-runtime trust-workspace add --bundle <workspace-issuer-bundle.json>
|
||||
yoi-runtime trust-workspace show --workspace-id <workspace-id>
|
||||
```
|
||||
|
||||
5. Runtime が challenge proof を生成し、Workspace owner が Server に submit する。
|
||||
6. Server が verified binding を commit した後、通常の Workspace-signed request が利用可能になる。
|
||||
4. The owner registers the Runtime public bundle, endpoint and exact `workspace_trust_id` in Settings > Runtimes.
|
||||
5. The Server sends a signed challenge bound to that trust ID, the Workspace key fingerprint and the binding ID, then verifies the Runtime proof and acknowledgement.
|
||||
6. Normal Workspace-signed requests become available only after the verified binding is committed. Revoking and re-enrolling the same key still requires the newly issued Runtime trust ID.
|
||||
|
||||
同じ Runtime identity は異なる Workspace から独立して信頼できる。trust record、replay protection、binding、失効はすべて Workspace scope で評価する。
|
||||
|
||||
@@ -48,9 +55,9 @@ Server-global な署名鍵や Runtime 側の trusted-Server catalog は使わな
|
||||
- 最大 8 MiB
|
||||
- 最大 4,096 records
|
||||
- exact Workspace / Runtime identity
|
||||
- key id/generation と public key fingerprint
|
||||
- `key_id` と public key fingerprint、opaque `trust_id`
|
||||
- normalized Backend URL
|
||||
- replace 時の expected current generation
|
||||
- 同じ `key_id` に異なる鍵materialを割り当てないこと。実際の置換・失効は新しい `trust_id` を発行する
|
||||
- list は `offset` / `limit` 必須で、1 page 最大 100 records
|
||||
|
||||
## Local token
|
||||
@@ -59,16 +66,16 @@ Server-global な署名鍵や Runtime 側の trusted-Server catalog は使わな
|
||||
|
||||
## Rotation と失効
|
||||
|
||||
Workspace signing key または Runtime key の変更は、現在 binding を置き換える明示的な provisioning 操作として行う。古い generation、古い Runtime key、revoked binding、失効済み token、replayed JTI は即時拒否する。
|
||||
Workspace signing key または Runtime key の変更は、現在 binding を置き換える明示的な provisioning 操作として行う。古い key/trust ID、古い Runtime key、revoked binding、失効済み token、replayed JTI は即時拒否する。
|
||||
|
||||
Server の Runtime cache は現在の persisted binding 全体と照合する。endpoint、Runtime public key/fingerprint、binding revision、Workspace key generation の変更を検知した場合、stale client を利用しない。
|
||||
Server の Runtime cache は現在の persisted binding 全体と照合する。endpoint、Runtime public key/fingerprint、`binding_id`、Workspace `key_id`、`workspace_trust_id` の変更を検知した場合、stale client を利用しない。
|
||||
|
||||
## 運用確認
|
||||
|
||||
Remote Runtime を有効化した後は次を確認する。
|
||||
|
||||
1. `yoi-runtime trust-workspace show --workspace-id <workspace-id>` が期待する bundle を表示する。
|
||||
2. Workspace Settings の Runtime binding が `verified` で、現在の key id/generation と verification evidence を表示する。
|
||||
2. Workspace Settings の Runtime binding が `verified` で、現在の `binding_id`・`workspace_key_id`・`workspace_trust_id` と verification evidence を表示する。
|
||||
3. Runtime ping、Worker list/create、`worker.protocol` subscription が Workspace-signed token で成功する。
|
||||
4. wrong Workspace、wrong Runtime、wrong target/body、expired token、revoked/replaced binding、replayed JTI が拒否される。
|
||||
5. Runtime → Server source proof が configured Backend public URL audience と一致し、spoofed headers だけでは認証されない。
|
||||
|
||||
@@ -11,7 +11,7 @@ Two independent Memory systems exist during the transition:
|
||||
| Product path | Persistence owner | Durable content | Status |
|
||||
| --- | --- | --- | --- |
|
||||
| Legacy Workspace Memory | Workspace Server control-plane database | one Workspace document, staging candidates, and staging resolutions | legacy/deprecated; retained only for compatibility and explicit reset |
|
||||
| Subject Memory | Server-managed `FeatureStorage`, in the Workspace-scoped `subjektiv` Feature database | subjects, immutable candidate and resolution records, confirmed Memory revisions, Session attribution, surface generation records, and immutable surface snapshots | current product path |
|
||||
| Subject Memory | Server-managed `FeatureStorage`, in the Workspace-scoped `subjektiv` Feature database | subjects, immutable candidate and resolution records, confirmed Memory changes, Session attribution, surface generation records, and immutable surface snapshots | current product path |
|
||||
|
||||
The shared `memory` crate and Worker Features do not own either database. `FeatureStorage` owns the subjektiv SQLite lifecycle (path, connection, migration, backup/restore, shutdown, and Workspace deletion), while the subjektiv repository owns its schema and domain transactions. The legacy control-plane tables are not imported or dual-written into subjektiv.
|
||||
|
||||
@@ -25,20 +25,20 @@ Workspace Memory settings remain Workspace-owned configuration. `feature.subjekt
|
||||
|
||||
- **Session attribution and recall:** only a committed primary-Worker Session with Host-recorded subject attribution enters subject Session discovery. Attribution is immutable and idempotent. Recall reads the retained or committed-archive Session projection under current Workspace, subject, observation, and retention authorization; it does not copy Session bodies into subjektiv, infer attribution for older Sessions, restore a Worker, or turn recalled tool output into `HumanInput`.
|
||||
- **`SubjektivMemoryRemember`:** stages a new candidate; it does not directly create confirmed Memory. Explicit committed evidence is resolved against the Session capture. A non-preference request without an entry is staged only after its tool call commits, using the committed call as model-origin evidence. An uncommitted operation creates no candidate.
|
||||
- **`SubjektivMemoryProposeRevision`:** stages a candidate with typed target metadata: exact Memory ID, expected revision, intent, and change reason. It does not mutate the target or silently retarget to a newer revision.
|
||||
- **Candidate consolidation:** a restricted, subject-bound consolidator compares an immutable pending candidate with confirmed Memory and makes one idempotent atomic decision. An applied decision writes the new confirmed revision, exact provenance and affected revision references, and the immutable candidate resolution in one subjektiv transaction. Non-applied dispositions are explicit (`discarded`, `invalid`, `duplicate`, or `already_covered`). A failed or empty model run is not a disposition, and surface generation failure cannot undo an already committed decision.
|
||||
- **Corrections:** refinement or correction of the same experience appends the next revision under the same Memory ID using an expected-revision check. Stale proposals remain unresolved/conflicted and are never rebased automatically. Retraction is terminal; a later corrected experience receives a new Memory ID and may derive from the old fixed revision. Old revisions, candidates, resolutions, provenance, and surface snapshots remain immutable history.
|
||||
- **`SubjektivMemoryProposeChange`:** stages a candidate with typed target metadata: exact Memory ID, `expected_change_id`, intent, and change reason. It does not mutate the target or silently retarget to a newer change.
|
||||
- **Candidate consolidation:** a restricted, subject-bound consolidator compares an immutable pending candidate with confirmed Memory and makes one idempotent atomic decision. An applied decision writes the new confirmed change, exact provenance and affected change references, and the immutable candidate resolution in one subjektiv transaction. Non-applied dispositions are explicit (`discarded`, `invalid`, `duplicate`, or `already_covered`). A failed or empty model run is not a disposition, and surface generation failure cannot undo an already committed decision.
|
||||
- **Corrections:** refinement or correction of the same experience appends the new immutable change under the same Memory ID using an `expected_change_id` check. Stale proposals remain unresolved/conflicted and are never rebased automatically. Retraction is terminal; a later corrected experience receives a new Memory ID and may derive from the old fixed change. Old changes, candidates, resolutions, provenance, and surface snapshots remain immutable history.
|
||||
|
||||
Confirmed Memory is useful subject context, not authorization and not exact implementation authority. Tickets, Objectives, repository state, Git history, and append-only Session records remain the exact sources for implementation state.
|
||||
|
||||
## Resident surface and history
|
||||
|
||||
A subject surface is a bounded rendering of current active confirmed revisions, not another Memory authority. The injection contract is:
|
||||
A subject surface is a bounded rendering of current active confirmed records, not another Memory authority. The injection contract is:
|
||||
|
||||
- **ready:** inject the current non-empty snapshot only when it was built from the subject's exact current store revision;
|
||||
- **ready:** inject the current non-empty snapshot only when it was built from the subject's exact current `memory_fingerprint`;
|
||||
- **ready-empty:** explicitly record that a successful current generation produced no Markdown and inject no Memory prose; do not conflate this with missing or failed generation;
|
||||
- **ungenerated:** inject nothing and report no current surface;
|
||||
- **stale:** inject nothing until regeneration publishes a snapshot for the current store revision;
|
||||
- **stale:** inject nothing until regeneration publishes a snapshot for the current Memory-input fingerprint;
|
||||
- **failed:** inject nothing; retain the failure for diagnosis and retry rather than presenting it as an empty Memory.
|
||||
|
||||
A new Worker receives the current ready or ready-empty result in its initial durable prompt construction. A restored Worker does not rewrite its persisted prompt or prior Session history. It appends one durable restore-boundary system item that supersedes prior resident summaries with the latest ready surface, explicit ready-empty state, or a no-current-surface tombstone. Later turns in the same restored process do not append the boundary again. This preserves append-only history: no stale summary, failed generation, correction, or cutover operation edits previously committed Session entries or immutable subjektiv records.
|
||||
@@ -47,7 +47,7 @@ A new Worker receives the current ready or ready-empty result in its initial dur
|
||||
|
||||
The existing legacy tools (`MemoryQuery`, `MemoryReadDocument`, `MemoryUpdateDocument`, and the legacy `MemoryStaging*` operations) and `/api/w/{workspace_id}/memory...` APIs retain their established Workspace-document meanings while they exist. They are legacy/deprecated surfaces, not aliases for subject Memory. Do not silently route an old request to subjektiv, infer a subject from old input, or change an old input field's meaning.
|
||||
|
||||
New product work uses the explicit subject/session/confirmed-Memory/revision/source and surface operations. Old clients must be stopped or upgraded before reset; a post-reset legacy read may observe the legacy empty/default state, but it must never expose subjektiv records through the old schema.
|
||||
New product work uses the explicit subject/session/confirmed-Memory/change-history/source and surface operations. Old clients must be stopped or upgraded before reset; a post-reset legacy read may observe the legacy empty/default state, but it must never expose subjektiv records through the old schema.
|
||||
|
||||
## Explicit legacy reset contract
|
||||
|
||||
@@ -65,11 +65,11 @@ Legacy `AppendAudit` was acknowledgement-only and created no dedicated durable M
|
||||
|
||||
The reset must retain:
|
||||
|
||||
- Workspace Memory settings and their revision;
|
||||
- Workspace Memory settings content;
|
||||
- Sessions, Session logs, archives, and diagnostics;
|
||||
- Tickets, Objectives, and their audit/history;
|
||||
- Knowledge and Skills;
|
||||
- the entire Workspace-scoped subjektiv Feature database, including subjects, candidate/resolution history, confirmed revisions, attribution, generation runs, failures, and surface snapshots;
|
||||
- the entire Workspace-scoped subjektiv Feature database, including subjects, candidate/resolution history, confirmed changes, attribution, generation runs, failures, and surface snapshots;
|
||||
- all other new-product and unrelated Workspace data.
|
||||
|
||||
The reset is not a subject deletion API, a Workspace deletion API, a settings reset, a retention override, or a data migration.
|
||||
@@ -81,7 +81,7 @@ Perform the cutover in this order:
|
||||
1. **Stop legacy activity.** Disable legacy `feature.memory` Profiles and automatic extraction/consolidation, stop or drain old Workers, and block Browser/API clients from writing `/memory` routes. Wait for in-flight legacy writes to finish. Do not run legacy and new automatic extraction for the same Worker.
|
||||
2. **Back up while writers are stopped.** Capture the control-plane database containing legacy Memory and a `FeatureStorage` backup containing subjektiv. Record the Workspace and backup identities and verify the backup artifacts. Feature backup is internally consistent, but there is no cross-database snapshot instant; stopping both writer sets is what makes the pair a usable cutover checkpoint.
|
||||
3. **Trigger reset explicitly.** An authenticated operator invokes the legacy reset API for the intended Workspace. Do not automate this as a startup migration or infer consent from `feature.subjektiv` enablement.
|
||||
4. **Verify both sides.** Confirm the legacy document, staging, and resolutions are absent (or return their defined empty/default projection); confirm Workspace Memory settings and unrelated control-plane data remain; reopen/read the subjektiv store and verify the expected subject, confirmed revisions, attribution, and current surface state are unchanged. Verify a subject Worker resolves through the `subjektiv:<subject-id>` singleton and has only the intended new tools.
|
||||
4. **Verify both sides.** Confirm the legacy document, staging, and resolutions are absent (or return their defined empty/default projection); confirm Workspace Memory settings and unrelated control-plane data remain; reopen/read the subjektiv store and verify the expected subject, confirmed changes, attribution, and current surface state are unchanged. Verify a subject Worker resolves through the `subjektiv:<subject-id>` singleton and has only the intended new tools.
|
||||
5. **Resume only the new path.** Start subject Workers with `feature.subjektiv`, keep legacy write surfaces disabled, and monitor typed surface/Session diagnostics. Do not treat an ungenerated, stale, or failed surface as ready-empty.
|
||||
|
||||
### Failure and recovery
|
||||
|
||||
@@ -60,7 +60,7 @@ Workspaceを選ぶ前のscopeを扱う。
|
||||
- Subjects
|
||||
- Surface
|
||||
- Committed Memory
|
||||
- Revisions
|
||||
- Changes
|
||||
- Sources
|
||||
5. Workers
|
||||
|
||||
@@ -108,7 +108,7 @@ Workspace pageは、次のいずれかを基本形とする。
|
||||
- 同じfieldをresource間で比較する場合、flat listまたはtableを使う。
|
||||
- 人が読むnameとcanonical keyを最初のcolumnに置く。
|
||||
- columnは判断に必要な値だけに絞る。
|
||||
- provider detail、revision、digestはdetailまたはtechnical disclosureへ移す。
|
||||
- provider detail、変更ID、digestはdetailまたはtechnical disclosureへ移す。
|
||||
|
||||
### Resource detail
|
||||
|
||||
@@ -130,8 +130,8 @@ Workspace pageは、次のいずれかを基本形とする。
|
||||
- Memoryの入口はWorkspace全体のlegacy `Document` / `Staging`ではなく、明示的に選択したsubjectとする。
|
||||
- subject detailではcurrent Workerをkeyed-singleton projectionとして表示し、historical Worker/Session attributionをcurrent authorityとして扱わない。
|
||||
- `Surface`はresident contextのcurrent stateを表示する。`ready`、`ready-empty`、`ungenerated`、`stale`、`failed`を区別し、空表示やrequest failureへまとめない。
|
||||
- `Committed Memory`はcurrent confirmed revisionの一覧とdetailを持つ。revision historyはappend-only viewとして分離し、過去revisionをcurrentへ暗黙fallbackしない。
|
||||
- `Sources`は候補、resolution、exact Memory revision refs、Host-recorded Session attribution、閲覧可能なSession sourceをたどるviewとする。source IDをpermissionとして扱わず、現在のWorkspace/subject authorizationを毎回適用する。
|
||||
- `Committed Memory`はcurrent confirmed Memoryの一覧とdetailを持つ。変更履歴はappend-only viewとして分離し、過去の変更内容をcurrentへ暗黙fallbackしない。
|
||||
- `Sources`は候補、resolution、exact `{memory_id, change_id}` refs、Host-recorded Session attribution、閲覧可能なSession sourceをたどるviewとする。source IDをpermissionとして扱わず、現在のWorkspace/subject authorizationを毎回適用する。
|
||||
- legacy `/memory` の`Document` / `Staging` viewはdeprecated compatibility surfaceとして明示し、新しいsubject viewへ同じinputを別の意味で転送しない。
|
||||
- Markdown本文とdetail presentationはT-661の共有rendererを再利用する。Memory route独自のrenderer、application shell、Header、Sidebarを再実装せず、既存shellのmain contentとnested navigationを保つ。
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Subject / Memory information audit (T-692)
|
||||
|
||||
This document records T-692's UI decisions and validation at the commits cited below. Its counter labels and fixture values describe that historical implementation, not the current API. Current Memory uses opaque `change_id` references, `memory_fingerprint` and `built_from_memory_fingerprint`; see [the current store contract](../../design/subjektiv-store.md). Historical observations and test results below are retained unchanged.
|
||||
|
||||
## Decision and scope
|
||||
|
||||
The previous Subjects index exposed `Store revision` alongside identity, state and
|
||||
|
||||
@@ -259,7 +259,7 @@ Visual review
|
||||
```
|
||||
|
||||
画像をTicketやGitへ大量にcommitしない。artifact pathとsource
|
||||
revisionを結び付け、必要なreviewerが同じ条件を再現できるようにする。認証profile、cookie、token、secret、private
|
||||
commitを結び付け、必要なreviewerが同じ条件を再現できるようにする。認証profile、cookie、token、secret、private
|
||||
response bodyをartifactやhandoffへ含めない。
|
||||
|
||||
## Toolの位置付け
|
||||
|
||||
@@ -13,7 +13,7 @@ removed when T-713 thread sequence 26 specified 0.2.0.
|
||||
|
||||
Canonical contract reference: wip-reference
|
||||
`6086f3c5ef10aa1464ce9c824750d7f667217bfd`. This reference documents the contract;
|
||||
Cargo resolves the release, not that checkout or an unreleased Rust revision.
|
||||
Cargo resolves the release, not that checkout or an unreleased Rust source commit.
|
||||
|
||||
## Executed checks on registry 0.2.0
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ These are instruction/tool contracts, not a claim that a mechanism proves real a
|
||||
|
||||
## Ticket tools and decisions
|
||||
|
||||
`QueryTicket` provides bounded discovery/filtering; `ShowTicket` provides authoritative item revision, paged thread, relations, Objectives, and any current MR context. Read the relevant Ticket before implementation, routing, review, state, or conclusion decisions. Check potential duplicates before creation or material rescope. Use `QueryObjective` and `ShowObjective` for broader context without replacing the Ticket read.
|
||||
`QueryTicket` provides bounded discovery/filtering; `ShowTicket` provides authoritative item content and `content_digest`, paged thread, relations, Objectives, and any current MR context. Read the relevant Ticket before implementation, routing, review, state, or conclusion decisions. Check potential duplicates before creation or material rescope. Use `QueryObjective` and `ShowObjective` for broader context without replacing the Ticket read.
|
||||
|
||||
Useful mutation surfaces include `TicketCreate`, `TicketComment`, relation/plan tools, and authorized `TicketWorkflowState`, `CompleteTicket`, and `TicketClose` decisions. Exposure depends on the effective Profile/Feature configuration. Decision tools use current item revision, expected state, a reason/resolution, stable operation key, and optional supporting references. Backend checks actor authority, CAS, replay, and resource boundaries. It does not enforce a universal state sequence, MR/approval gate, or natural-language satisfaction.
|
||||
Useful mutation surfaces include `TicketCreate`, `TicketComment`, relation/plan tools, and authorized `TicketWorkflowState`, `CompleteTicket`, and `TicketClose` decisions. Exposure depends on the effective Profile/Feature configuration. Decision tools use current `content_digest` as `expected_content_digest`, expected state, a reason/resolution, stable operation key, and optional supporting references. Backend checks actor authority, CAS, replay, and resource boundaries. It does not enforce a universal state sequence, MR/approval gate, or natural-language satisfaction.
|
||||
|
||||
`CompleteTicket` records completion; `TicketClose` records closure. Neither integrates an MR or expands resource grants. Ticket progress/conclusion can occur without commits, MRs, or approvals when consistent with the request. Conversely, code's default publication/review practices must not be erased by confusing an independent Ticket decision with merge authority. Do not claim an unmerged result was integrated. Reread after conflicts and replay a recorded operation only with the same fingerprint.
|
||||
|
||||
@@ -51,7 +51,7 @@ Relations remain metadata, not a scheduling mechanism: record actual project dep
|
||||
|
||||
For code work using the default publication/review recipe, select `builtin:coder` and optionally `builtin:coder-review`. The Flow guides implement, review, fix, and handoff; it is not a Backend Ticket completion requirement and grants no authority. Read current repository state, use coherent commits and normal non-force publication only when authorized, validate the changed contracts, and report changed files, validation, and limitations. No commits/push should be inferred from a request explicitly prohibiting them.
|
||||
|
||||
Use one open repository-scoped MR per Ticket/repository. `OpenMergeRequest` creates selectors; once an MR exists, explicitly address its ID with `ShowMergeRequest` and preserve its selectors. Advance only the existing source selector with a normal non-force push. Do not invent an add-revision operation, replacement MR, or fresh integration branch for every fix. Publish the exact committed source and verify provider resolution before requesting review.
|
||||
Use one open repository-scoped MR per Ticket/repository. `OpenMergeRequest` creates selectors; once an MR exists, explicitly address its ID with `ShowMergeRequest` and preserve its selectors. Advance only the existing source selector with a normal non-force push. Do not invent an operation to append another source candidate, replacement MR, or fresh integration branch for every fix. Publish the exact committed source and verify provider resolution before requesting review.
|
||||
|
||||
The assigned Coder launches the Reviewer as an actual direct-child `builtin:reviewer` SubWorker, with explicit command grant and appropriate Workdir scope for inspection/validation, plus the structured Ticket/MR handoff. Server authority verifies assignment, Runtime-owned child identity, effective Profile, one-shot attempt, and captured subject. Review capability is injected by the trusted layer, not supplied by the model. The Reviewer makes an independent judgment against intent, acceptance criteria, the complete captured result snapshot, and validation, then records `ReviewMergeRequest`; prose and observation are not approval authority.
|
||||
|
||||
|
||||
@@ -43,13 +43,13 @@ Public entry inputs are **Workspace-bound** references:
|
||||
{"workspace_id":"workspace-id","node_id":"9007199254740993"}
|
||||
```
|
||||
|
||||
Node IDs and revisions remain canonical positive decimal **strings**, including
|
||||
Node IDs remain canonical positive decimal **strings**, including
|
||||
values above JavaScript's safe-integer range. Renaming/moving preserves IDs and
|
||||
authenticated latest URLs. Deleting/recreating the same name allocates a new ID.
|
||||
|
||||
| Tool | Input/use |
|
||||
| --- | --- |
|
||||
| `DriveMetadata` | `entry`: one live entry and its observed revision |
|
||||
| `DriveMetadata` | `entry`: one live entry and its last committed request ID |
|
||||
| `DriveList` | optional `parent` (default root), `limit` 1–200, opaque `after` |
|
||||
| `DriveSearch` | `query`, optional `include_text`, `limit` 1–128, opaque `after` |
|
||||
| `DriveRead` | `entry`, optional `max_bytes` 1–65,536; reports `truncated` |
|
||||
@@ -59,7 +59,7 @@ authenticated latest URLs. Deleting/recreating the same name allocates a new ID.
|
||||
| `DriveEdit` | observed `entry`, `old_string`, `new_string`, optional `replace_all` |
|
||||
| `DriveRelocate` | observed `entry`, destination `parent`, new/current `name` |
|
||||
| `DriveDelete` | observed `entry`; folder deletion follows Backend rules |
|
||||
| `DriveViewImage` | `entry`; bounded revision-fixed image bytes, not a URL |
|
||||
| `DriveViewImage` | `entry`; bounded committed-request-bound image bytes, not a URL |
|
||||
| `DriveSaveWorkdir` | explicit `target_workdir` alias, relative `path`, destination `parent`, `name`, optional `content_type` (octet-stream by default) |
|
||||
| `DriveRequestStatus` | `request_id` from an uncertain mutation |
|
||||
|
||||
@@ -76,7 +76,7 @@ all Drive versions. Restored workers observe again before mutating.
|
||||
`DriveEdit` uses the same pure `fs_operation::text` rules as Workdir and Workspace
|
||||
config: nonempty `old_string`, different replacement, exactly one nonoverlapping
|
||||
match unless `replace_all=true`, and bounded resulting text. It fetches a full
|
||||
<=64 KiB preimage and verifies the **previously observed** revision. It cannot edit
|
||||
<=64 KiB preimage and verifies the **previously observed** committed request ID. It cannot edit
|
||||
truncated content. The Backend's atomic CAS still decides the first winner.
|
||||
|
||||
## Native WIP 0.2.0
|
||||
@@ -103,7 +103,7 @@ Direct entry paths returned by discovery are:
|
||||
Use the returned path rather than hand-constructing it. A file exposes metadata,
|
||||
read, write/edit, relocate/delete and view_image; a folder exposes metadata,
|
||||
list, create_folder/create_text, save_workdir and relocate/delete where valid.
|
||||
A native target binds its entry or creation parent: no ID or revision argument is
|
||||
A native target binds its entry or creation parent: no ID or mutation-precondition argument is
|
||||
needed. Relocate's `parent` is a WIP typed entry path. Each entry is self-scoped;
|
||||
Host captures target, scope object, descriptor, validator and handler together.
|
||||
Client retains and supplies metadata automatically. Do not copy validators or
|
||||
@@ -116,8 +116,8 @@ selected path. No Worker index/embedding or eager body injection is added.
|
||||
|
||||
## Conflicts, unknown outcomes and publication
|
||||
|
||||
Mutations use a Host-generated request identity, observed node ID and exact
|
||||
revision. Conflict never causes an adapter to replace the expected revision with
|
||||
Mutations use a Host-generated request identity, observed node ID and last
|
||||
committed request ID. Conflict never causes an adapter to replace that observation with
|
||||
latest and retry. Observe explicitly, reconsider the edit and submit a deliberate
|
||||
new operation. Native Client stale handling likewise does not replay dispatch.
|
||||
|
||||
@@ -144,7 +144,7 @@ Backend's **DB-publication commit**, not merely blob transfer completion.
|
||||
## Images and historical observations
|
||||
|
||||
ViewImage reads <=10 MiB PNG/JPEG/GIF/WebP bytes in <=64 KiB Drive chunks at the
|
||||
metadata revision. Every chunk is authorized and revision-fixed. An intervening
|
||||
metadata’s last committed request ID. Every chunk is authorized and committed-request-bound. An intervening
|
||||
update expires the read; no mixed-generation image is attached. Media is detected
|
||||
from bytes using the existing image helper, not the filename or a download URL.
|
||||
|
||||
|
||||
+1
-3
@@ -274,7 +274,6 @@ permission = "write"
|
||||
# # Backendがresolved Manifestへbindする。手書き/Profile入力では指定しない。
|
||||
# # [feature.memory.workspace_settings]
|
||||
# # workspace_id = "workspace-id"
|
||||
# # settings_revision = 1
|
||||
# # language = "日本語"
|
||||
#
|
||||
# Query結果/抜粋の上限とconsolidation eligibility/thresholdはoperation/Backend
|
||||
@@ -306,10 +305,9 @@ permission = "write"
|
||||
# # ref = "codex-oauth/gpt-5.6-luna"
|
||||
#
|
||||
# # Backendがresolved Manifestへbindする。Profile・Browser・model入力では指定しない。
|
||||
# # language/settings revisionのtrusted snapshotであり、subject identityではない。
|
||||
# # Workspace identity/language設定のtrusted snapshotであり、subject identityではない。
|
||||
# # [feature.subjektiv.workspace_settings]
|
||||
# # workspace_id = "workspace-id"
|
||||
# # settings_revision = 1
|
||||
# # language = "日本語"
|
||||
#
|
||||
# Surface selection/generation/injection、candidate consolidation eligibility、recall
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# State-contract cleanup completion and recovery record
|
||||
|
||||
The interrupted cleanup has been restored, integrated with `develop` at
|
||||
`72ce817de4592e8c9703e0725dd705f46cba63aa`, completed, and validated. This record
|
||||
supersedes the earlier incomplete restart checkpoint. No live Server, Runtime,
|
||||
or user database was stopped or migrated as part of this work.
|
||||
|
||||
## Recovery and integration
|
||||
|
||||
- Workdir attachment: `yoi-origin`; branch: `hare/develop`.
|
||||
- Preserved the Workdir delegation fix (`7321d6c7`), asynchronous cleanup fix
|
||||
(`dc8e586a`), and the T-731/T-732 changes integrated by `72ce817d`.
|
||||
- Applied stash `dd31c0ecd41d2aca1ef29889a5194fb4499cb149` once after merging
|
||||
develop. It remains as recovery evidence; do not apply it again over this work.
|
||||
- Older stash `a386a9e2943d53f331a555a8c255741bc004c419` also remains untouched.
|
||||
- Direct implementation/review SubWorkers were collected after their committed
|
||||
sessions and concrete changes/test evidence were inspected.
|
||||
|
||||
## Completed contracts
|
||||
|
||||
State/update ordinals have been replaced by content digests, key fingerprints,
|
||||
binding/trust/connection IDs, immutable mutation IDs, and observation tokens.
|
||||
Schema/protocol versions, ordered events, command IDs, and local asynchronous
|
||||
invalidation fences remain distinct contracts and were not indiscriminately removed.
|
||||
|
||||
- Config source/history preserves separate source/toolchain/projection evaluations.
|
||||
Cache identities occupy a namespace forbidden to real virtual paths, including
|
||||
JSON-deserialized paths. Native and generated WASM regressions cover the boundary.
|
||||
- Job/content migration retains completed results and cleanup/replay evidence.
|
||||
Proven reserved-only work can fail definitively; uncertain execution is not
|
||||
silently admitted again. Historical Flow definitions are validated, not recompiled.
|
||||
- Drive preserves old receipt fingerprints and validates old Update/Relocate/Delete
|
||||
replays against frozen evidence rather than granting authority from old ordinals.
|
||||
- SSH migration reseals envelopes under actual mutation receipts. Terminal Workdir
|
||||
history whose credentials were deleted remains non-authorizing archival evidence.
|
||||
INSERT/UPDATE guards prevent that archive from acquiring candidate/lease authority.
|
||||
- Runtime binding verification ties together binding/trust identities and actual
|
||||
public-key fingerprints. Worker command-ID replay is separate from admission-time
|
||||
state. Ticket checker intent IDs include the complete immutable request.
|
||||
|
||||
## Migration safety and review corrections
|
||||
|
||||
- Uncertain Worker removals (`executing`/`failed`) block **before any retained
|
||||
migration runs**, including ordinary Server open and migration CLI entrypoints.
|
||||
Tests start at schema84, verify unchanged schema/receipt after rejection, then
|
||||
reconcile with the old contract and upgrade successfully.
|
||||
- SSH dry-run uses the original database's secret context while validating an
|
||||
in-memory backup. Actual encrypted-envelope tests prove dry-run non-mutation,
|
||||
formal migration, new-AAD decryption, and unchanged master-key material.
|
||||
- Interrupted signing identity provisioning resumes the same reserved key/material.
|
||||
Frozen old receipt validation precedes operation-key/fingerprint conversion;
|
||||
activation and migration failures roll back. Completed and create receipts are
|
||||
also covered.
|
||||
- The old reservation CHECK constraint is removed through a transactional rebuild
|
||||
retaining other columns, constraints, indexes, triggers, and incoming/outgoing
|
||||
foreign keys. Unknown dependencies are not broadly rewritten.
|
||||
- Fresh/upgraded metadata comparison covers 36 state-contract tables: columns,
|
||||
defaults, PK/FK structure, index columns, and triggers. It caught and repaired a
|
||||
missing history DEFAULT and retained-chain reservation lookup index. This is not
|
||||
a general SQL-equivalence proof of all CHECK expressions or index predicates;
|
||||
separate behavior/rollback tests cover the relevant authority constraints.
|
||||
- Independent data and Server reviews found the above defects; follow-up reviews
|
||||
confirmed their resolution without additional blockers in the reviewed scope.
|
||||
|
||||
## Final validation
|
||||
|
||||
- Root `cargo check`: PASS.
|
||||
- All 21 changed Rust crates tested together: **3,795 passed**, **3 ignored**,
|
||||
zero failures, including doctests. The Server contributes 809 library tests and
|
||||
11 CLI tests (one library test ignored).
|
||||
- `server-api --features typescript`: **96 passed**.
|
||||
- Additional T-731/T-732 integration coverage: yoi, workdir, tools, fs-operation
|
||||
tests passed separately.
|
||||
- Web standard suite: **589 passed**; component suite: **315 passed / 33 files**.
|
||||
- Web type check: zero errors/warnings; production build passed (chunk-size warning).
|
||||
- Config WASM parity includes 18 passing cases, including literal-cache-key collision
|
||||
and JSON URI-path refusal. Generated JS/types/WASM were compared with the pinned
|
||||
wasm-bindgen 0.2.117 toolchain. OpenAPI/API/protocol generated checks passed.
|
||||
- `cargo fmt --all -- --check` and `git diff --check HEAD`: PASS. Included frozen
|
||||
Rust migration/test files were formatted explicitly as well.
|
||||
|
||||
Local evidence logs (ignored build artifacts, not part of stash/history):
|
||||
`target/revision-final-combined-rust-tests.log`,
|
||||
`target/revision-final-typescript-tests.log`,
|
||||
`target/revision-final-root-check.log`,
|
||||
`target/revision-final-web-{tests,component,check,build}.log`,
|
||||
`target/revision-virtual-path-web-{config-tests,final-check}.log`,
|
||||
`target/revision-web-final-{artifacts,normalized-artifacts}.log`, and
|
||||
`target/revision-schema-equivalence.log`.
|
||||
|
||||
## Deployment and separate findings
|
||||
|
||||
Deployment/restart remains user-operated. Take the normal database/key-material
|
||||
backup before migration. Resolve uncertain old Worker removals with the old Server
|
||||
before attempting cutover. Existing migrated Runtime bindings become configured;
|
||||
re-enrollment/verification requires the actual Runtime-issued trust ID from
|
||||
`trust-workspace add/show`, not an invented value or public-key fingerprint.
|
||||
|
||||
Previously identified Server-only capability revocation and missing enrollment
|
||||
identity in Runtime-to-Server source proofs predate this cleanup. They are not
|
||||
claimed fixed here. The separately recorded Runtime HTTP-only feature dependency
|
||||
issue also remains outside this cleanup.
|
||||
|
||||
Workdir operations intermittently returned HTTP 401 during the work. Failed calls
|
||||
were not treated as successful edits/tests; subsequent reads and final combined
|
||||
validation established the recorded result. No authorization or live-process
|
||||
workaround was used. The default wasm-bindgen CLI had a schema-version mismatch;
|
||||
a matching CLI was installed only under ignored `target/revision-wasm-tools`, with
|
||||
no global CLI change or additional change to the restored Cargo.lock.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Recovery session: SubWorker scope delegation is rejected
|
||||
|
||||
## Observed boundary
|
||||
|
||||
During state-contract cleanup after restoring the worktree, `WorkerList` returned no children. The attached External Workdir `yoi-origin` advertised READ/WRITE/COMMAND, and the parent could read files, write files, and execute build commands through that attachment.
|
||||
|
||||
A new `SubWorkerSpawn` requesting only recursive READ of `.` on `yoi-origin` was rejected before the child started:
|
||||
|
||||
```text
|
||||
Invalid argument: scope parent-owned Workdir tools: Workdir operation denied: Workdir operation was denied
|
||||
```
|
||||
|
||||
An empty scope is not supported (`scope must not be empty`), so a context-only child was also unavailable. No child was created, and no delegated work is running.
|
||||
|
||||
The response does not identify which delegation condition failed. Successful direct parent access does not establish delegation authority, so these observations alone do not establish whether the refusal is a policy restriction or a Provider implementation defect. No running process was stopped or reloaded to investigate this.
|
||||
|
||||
## Additional observed tool failure
|
||||
|
||||
`Edit` of `crates/workspace-server/src/store.rs`, after reading it through the same attachment, returned:
|
||||
|
||||
```text
|
||||
Invalid argument: invalid argument: Workdir operation request is invalid
|
||||
```
|
||||
|
||||
The parent could still modify that file using its granted command capability. This is an input-request failure observation, not evidence that `Edit` has the same root cause as the SubWorker denial.
|
||||
|
||||
## Diagnostic need
|
||||
|
||||
Return bounded, non-sensitive reason categories that distinguish direct access from delegation denial, and distinguish invalid tool requests from scope checks. Avoid reporting generic denial as proof that a child is running or that the parent lost its direct attachment authority.
|
||||
+1
-1
@@ -41,7 +41,7 @@ On explicit selected-Subject Host startup, reserved Jobs resume automatically. A
|
||||
|
||||
## Memory and historical Sessions
|
||||
|
||||
The normal body Profile has recall, revision reads, explicit candidate tools, and bounded historical Session exploration. It cannot directly apply Memory. Explicit Remember/Revise without `entry_refs` returns a pending-commit receipt; the Host stages its exact tool-call evidence only after the logical Run is append-only committed. Pre-request retry may defer an open Run, but commit/rewrite failures are not silently treated as success. A candidate remains unconfirmed until the restricted consolidation Job applies or rejects it using the shared CAS/receipt rules.
|
||||
The normal body Profile has recall, exact Memory change reads, explicit candidate tools, and bounded historical Session exploration. It cannot directly apply Memory. Explicit Remember/Revise without `entry_refs` returns a pending-commit receipt; the Host stages its exact tool-call evidence only after the logical Run is append-only committed. Pre-request retry may defer an open Run, but commit/rewrite failures are not silently treated as success. A candidate remains unconfirmed until the restricted consolidation Job applies or rejects it using the shared CAS/receipt rules.
|
||||
|
||||
Normal extraction uses the existing committed-capture lifecycle, restricted extraction tools, and append-only `subjektiv.extract.v1` pointer. The selected Profile owns extraction thresholds/model policy. An explicit extraction model override chooses that provider, not an injected dialog transport. Local Memory language defaults to English and is stored in operator-managed `subjektiv/language.json`, never loaded from repository policy files.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user