feat: caller-selected Backend Jobs and bounded subjektiv consolidation (T-704)
This commit is contained in:
@@ -330,9 +330,30 @@ Exact dispatch retry reuses the deterministic attempt and Worker allocation key.
|
||||
Only explicit re-evaluation of a definitively failed attempt increments the
|
||||
bounded attempt number; unknown outcomes are not replayable. The runner
|
||||
uses the existing embedded Runtime spawn, Worker registry, Session input,
|
||||
observation, usage, and lifecycle paths with no Workdir attachment. It uses the
|
||||
dedicated `builtin:backend-job` profile, whose tools cannot mutate Workspace
|
||||
state, recursively create Workers or Jobs, or inherit caller Workdir authority.
|
||||
observation, usage, and lifecycle paths with no Workdir attachment. Each client
|
||||
explicitly selects an existing registry Profile. The runner resolves the complete
|
||||
Builtin/Workspace project catalog through the normal virtual config bundle and
|
||||
prompt projection path; browser launch candidates remain a narrower UI policy.
|
||||
Raw Profile source and filesystem/path selectors are not accepted. A selected
|
||||
recipe's instruction and features are not replaced with `backend-job` defaults.
|
||||
Profiles requiring ungranted Workspace mutation, ordinary Memory, child/peer,
|
||||
Flow, Workdir or MCP authority fail explicitly; no caller scope is inherited.
|
||||
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
|
||||
authenticated Worker before accepting results or domain operations. Profile
|
||||
policy alone never confers a subject grant.
|
||||
|
||||
A common optional resource key serializes Jobs using their existing durable
|
||||
intent/attempt records (schema 81), not a second domain workflow table. Exact
|
||||
request replay is distinct from a resource collision: the latter returns the
|
||||
original active immutable request with `resource_reused`, and never claims that
|
||||
new input was processed. Pending/unknown Jobs and **all** unfinished attempt
|
||||
cleanups retain the fence. Retry requires definitive failure, remaining attempt
|
||||
budget, completed prior cleanups and no competing resource owner. Unknown intent
|
||||
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
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
# Backend subjektiv consolidation Jobs (T-704)
|
||||
|
||||
subjektiv requests the common Backend Job runner with an explicit
|
||||
`builtin:subjektiv-memory-consolidation` Profile. The runner has no purpose-based
|
||||
Profile switch. Subject-body Workers and the independent Workspace Memory feature
|
||||
are not Jobs and are not migrated or reset here. The shared/standalone adapters
|
||||
planned in T-708/T-709 are outside this Backend change.
|
||||
|
||||
## Input, grants and concurrency
|
||||
|
||||
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. An empty batch is valid for a
|
||||
surface-only rebuild. The Backend explicitly binds the Subject and candidate
|
||||
batch grant in durable Job intent before Worker execution. No subject-body
|
||||
singleton ownership or Session attribution is transferred to the Job.
|
||||
|
||||
Authenticated `(Workspace, Runtime, Worker)` registry binding and the exact
|
||||
current dispatched attempt authorize each operation. Names, Profile tags and
|
||||
model-provided `subject_id` are not authority. Candidate listing exposes only
|
||||
unresolved IDs in the captured batch; reads/decisions outside it are denied.
|
||||
Surface prepare requires all batch candidates to have durable dispositions.
|
||||
Surface generation records preserve immutable Job/attempt provenance (subjektiv
|
||||
schema migration 6); publication, failure and completion reject another attempt's
|
||||
generation, including pre-Job/legacy generations.
|
||||
|
||||
The common resource key `subjektiv-consolidation:<subject>` prevents parallel
|
||||
Jobs for a Subject. Concurrent requests return the original active intent and
|
||||
report `skipped_existing_job`; they do not expand its grants. New candidates and
|
||||
items beyond the bounded batch stay in staging. A subsequent consolidation
|
||||
request snapshots remaining work; it is not counted as processed by the first
|
||||
Job. There is no domain-specific status ledger, pool or persistent idle Worker.
|
||||
Definitive failures use the common bounded retry API with the original intent;
|
||||
unknown execution stays fenced for attention. A later explicitly requested Job
|
||||
may snapshot remaining candidates once all previous Workers are safely cleaned.
|
||||
|
||||
## Memory and surface completion
|
||||
|
||||
`MemoryApplyCandidate` still uses the existing expected-revision 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
|
||||
existing dispositions, and exact candidate decisions converge on their receipts.
|
||||
|
||||
`SubmitBackendJobResult` for a bound consolidation Job receives only the Subject
|
||||
and captured candidate IDs. Its Host capability awaits the existing bounded
|
||||
clean-context surface editor and publish-or-recorded-failure, then adds the actual
|
||||
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;
|
||||
it enriches the durable result with actual immutable candidate dispositions.
|
||||
Thus confirmed-memory consolidation and surface readiness are separate outcomes.
|
||||
A recorded surface failure does not undo Memory. A stale/unfinished/fabricated
|
||||
surface cannot be reported completed. Final prose or Idle is never success.
|
||||
|
||||
Only after durable structured result acceptance does the existing terminal Job
|
||||
cleanup run. Its tracked Run teardown fence lets the result tool finish before
|
||||
stop/remove. Timeout/unknown paths cancel unfinished Worker execution through
|
||||
that same cleanup; later operations/results are rejected. The next request can
|
||||
regenerate failed/stale surfaces without touching confirmed memory.
|
||||
|
||||
## Legacy cutover and recovery
|
||||
|
||||
The subject-specific direct spawn and Idle resubmission paths are removed.
|
||||
On a consolidation request the Backend checks the **persisted singleton owner**
|
||||
for the old `subjektiv-consolidation:<subject>` key. It revalidates Workspace,
|
||||
key and old dedicated recipe provenance, not the display name. A running/paused
|
||||
legacy foreground operation is left alone and returns `skipped_legacy_inflight`;
|
||||
no Job is launched alongside it. Legacy owned operations can finish during this
|
||||
bounded cutover.
|
||||
|
||||
After foreground Idle (or Stopped) is confirmed, Backend stop is confirmed before
|
||||
canonical WorkerRemovalService removal. Any legacy surface background task is
|
||||
cancelled at stop; the subsequent Job rebuilds from confirmed Memory and does not
|
||||
infer successful surface completion from Idle. Busy/unobservable/mismatched
|
||||
owners fail closed. Empty/ready Subjects also retire their idle legacy owner.
|
||||
Removal failures remain diagnosable through existing durable removal recovery;
|
||||
a subsequent request rechecks ownership instead of deleting storage directly.
|
||||
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,
|
||||
provenance and surfaces remain in their existing Backend stores after Worker
|
||||
removal. No live dogfood Worker or environment is stopped/updated by development
|
||||
validation.
|
||||
|
||||
## Validation boundary
|
||||
|
||||
Server recording-backend fixtures prove reservation/binding/auth/result/cleanup
|
||||
contracts, and existing store tests prove candidate CAS/atomic receipts and
|
||||
surface freshness. Worker scripted-client tests prove the clean-context editor
|
||||
completion-before-result boundary and result replay. They are not real-provider
|
||||
LLM or live dogfood process evidence. See the T-704 validation report for exact
|
||||
commands/results and inherited intermittent failures; later passes do not erase
|
||||
those failures.
|
||||
@@ -289,11 +289,15 @@ active/resolved→retracted, and resolved→active. Retraction remains terminal.
|
||||
|
||||
## Candidate consolidation and corrections (T-670)
|
||||
|
||||
The Backend starts a restricted `builtin:subjektiv-memory-consolidation` Worker
|
||||
for exactly one authenticated subject. The Host binds the subject; model-visible
|
||||
inputs cannot select a Workspace, subject, Runtime, Worker, or Session. Its tools
|
||||
list/read pending immutable candidates, query/read confirmed revisions, and make
|
||||
one candidate decision. Candidate text is evidence rather than authority.
|
||||
subjektiv requests a bounded Backend-owned Job with the explicit
|
||||
`builtin:subjektiv-memory-consolidation` Profile and an immutable Subject/candidate
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# T-704 implementation and validation ledger
|
||||
|
||||
Base: `6f9621ca` (clean detached assigned checkout). Parent created
|
||||
`work/t-704-backend-subjektiv-jobs` without replacing a branch or discarding changes.
|
||||
Parent owns Backend/Profile/Subject authorization, cutover, documentation and
|
||||
integration. Two direct implementation SubWorkers supplied the common durable
|
||||
Job/store and Worker/Runtime contracts; parent inspected their committed sessions,
|
||||
diffs and test evidence, integrated them and corrected the boundaries below.
|
||||
Independent MR review is separate from implementation delegation.
|
||||
|
||||
## Implemented contracts
|
||||
|
||||
- Caller-selected existing Builtin/project registry Profiles, normal virtual
|
||||
config/prompt bundle resolution and explicit execution-requirement rejection.
|
||||
Chosen instructions/features/model are not replaced with backend-job defaults.
|
||||
- Immutable Host-authored Job/attempt create/restore binding and result capability,
|
||||
independent of Profile names/instructions/tags. No inherited Workdir, parent or
|
||||
subject-body execution authority.
|
||||
- Common transactional resource serialization (Workspace schema 81), immutable
|
||||
Subject/candidate batch grants, exact replay vs resource collision, unknown
|
||||
fences and all-old-attempt cleanup fences before retry/followup.
|
||||
- subjektiv requests its Profile; immutable bounded batches, empty surface-only
|
||||
Jobs, candidate authorization, existing CAS/atomic receipts and dispositions.
|
||||
- Synchronous bounded clean-context surface generation before result submission.
|
||||
Preparation reports an already-current snapshot to avoid needless conflicting
|
||||
regeneration. Job failure acknowledgements are `failed_confirmed` only after
|
||||
checking the actual failed marker/reason/revision and generation provenance.
|
||||
Subject feature migration 6 adds immutable generation Job/attempt provenance,
|
||||
without changing Subject/Memory/candidate content.
|
||||
- Durable results contain actual candidate dispositions and a separate verified
|
||||
ready/failed surface outcome. Existing terminal cleanup retains results/memory.
|
||||
- Removed subject direct spawn and Idle resubmission. Persisted legacy ownership
|
||||
plus actual foreground WorkerState and confirmed stop/removal fence the cutover.
|
||||
Busy legacy workers are not interrupted or duplicated; subject bodies and the
|
||||
independent old Workspace Memory feature are not selected for removal.
|
||||
|
||||
## Final validation (bounded `CARGO_BUILD_JOBS=2`)
|
||||
|
||||
| Command/boundary | Result |
|
||||
| --- | --- |
|
||||
| `cargo test -p yoi-workspace-server --lib backend_job` | 33 passed (before final surface acknowledgement addition; final whole-crate run below covers it) |
|
||||
| `cargo test -p yoi-workspace-server --lib subjektiv_jobs_tests` | 5 passed, including project recipe, batch/auth/receipts, actual failed acknowledgement, current surface and legacy cutover |
|
||||
| `cargo test -p worker --lib memory_surface_lifecycle` | 16 passed, including publication/failure before result, cancellation, definitive correction, ambiguous exact retry and already-current surface |
|
||||
| Root `cargo check` | Passed; normal TUI/Server/standalone compile closure |
|
||||
| `cargo test -p worker -p worker-runtime -p manifest -p runtime-api -p server-api -- --test-threads=4` | Passed: manifest 185; runtime-api 9; server-api 70; worker 815 lib + 110 integration; runtime 335 lib + 34 binary tests |
|
||||
| `cargo test -p yoi-workspace-server -- --test-threads=4` (final) | 637 lib + 11 binary passed, no skips |
|
||||
| `cargo test -p server-api --features typescript` | 77 passed, including generated/OpenAPI/TypeScript contract freshness; existing ts-rs serde-attribute warnings |
|
||||
| `cargo fmt --all -- --check` / `git diff --check HEAD` | Passed |
|
||||
|
||||
These are recording-backend/store and scripted-client fixtures, plus real compiler
|
||||
and test binaries. They are **not** real-provider LLM execution, a live Backend /
|
||||
Runtime deployment, live dogfood consolidation or an environment update. No live
|
||||
Worker/environment was stopped or updated.
|
||||
|
||||
## Failed checks and fixes (not erased by later passes)
|
||||
|
||||
- During integration, new request fields, resource replay metadata and surface
|
||||
validation arguments initially failed compilation; all were corrected before
|
||||
final validation.
|
||||
- Initial Backend tests exposed the narrower browser builtin candidate allowlist;
|
||||
Host launch now resolves the complete existing registry without widening browser
|
||||
choices or adding a purpose-specific Profile allowlist.
|
||||
- Initial surface-only test exposed rejection of empty candidate grants; empty
|
||||
batches now retain the subject lock and grant no candidate access (owned store
|
||||
tests 9 + contract tests 9 passed).
|
||||
- Legacy fixture initially compared catalog compatibility state rather than
|
||||
actual WorkerState. Cutover now requires the actual Idle snapshot, and the test
|
||||
synchronizes on that authoritative observation before asserting behavior.
|
||||
- First full Server run: **635 passed / 2 failed**, both stale migration-history
|
||||
fixtures expecting schema 80. Their explicit retained chain/history is updated
|
||||
to 81; migration filter passed 14 tests and subsequent whole-crate runs passed.
|
||||
- Exploratory `cargo check -p worker-runtime --no-default-features` and
|
||||
`--no-default-features --features fs-store` both **failed**, with 16 errors from
|
||||
existing unconditional `workspace_request` / `reqwest` imports while HTTP is
|
||||
disabled (plus unused-import warnings). The source/gates are present in the
|
||||
original baseline and are recorded in the
|
||||
[T-701 ledger](2026-10-06-t701-validation.md#validation-results). No isolated
|
||||
baseline build was performed here. These remain failed exploratory unsupported
|
||||
configurations, not feature passes or a T-704 feature-gating fix. The default
|
||||
Runtime fs-store/ws-server configuration is fully tested.
|
||||
- The inherited intermittent Ticket checker failure described in the
|
||||
[T-702 ledger](2026-10-06-t702-stale-checker-validation.md) and T-701 report was
|
||||
not observed in the final T-704 full Server runs, but is **not** claimed fixed.
|
||||
|
||||
Raw parent logs are under `target/t704-validation/` (ignored local artifacts),
|
||||
including `server-full-initial-migration-fixtures.log`, `server-full-final.log`,
|
||||
`changed-crates.log`, `root-check.log`, `worker-surface-final.log`,
|
||||
`server-subjektiv-final.log`, `server-api-typescript.log`, `runtime-no-default.log`
|
||||
and `runtime-fs-only.log`. The durable report records their concrete results;
|
||||
local log paths are not a substitute for the published immutable MR source.
|
||||
Reference in New Issue
Block a user