fix: integrate develop and preserve invocation notification recovery

This commit is contained in:
2026-10-05 22:25:32 +09:00
92 changed files with 14528 additions and 1005 deletions
+3 -1
View File
@@ -35,7 +35,9 @@ It is not a dumping ground for external research, old plans, API inventories, or
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.
29. [`design/objective-wip-projection.md`](design/objective-wip-projection.md) — Host-assigned Objective collection/item routes, operation mapping, Backend authority, bounds, and compatibility suppression.
30. [`design/feature-chat-invocations.md`](design/feature-chat-invocations.md) — declarative `/` invocation identity, grammar, completion, typed draft, attachment staging, execution, and authority boundaries.
30. [`design/ticket-wip-projection.md`](design/ticket-wip-projection.md) — Host-assigned Ticket collection/item routes, role-specific operations, route identity, and typed Backend preservation.
31. [`design/merge-request-wip-projection.md`](design/merge-request-wip-projection.md) — Host-assigned Merge Request routes and preserved review/integration authority gates.
32. [`design/feature-chat-invocations.md`](design/feature-chat-invocations.md) — declarative `/` invocation identity, grammar, completion, typed draft, attachment staging, execution, and authority boundaries.
## What belongs here
+5 -3
View File
@@ -4,13 +4,15 @@
When a Worker explicitly runs in `wip` mode and its Merge Request Feature has at least one enabled operation, the Worker Host projects that exact operation set as native Worldspace objects. The projection is an adapter over the existing scoped Workspace Merge Request tools and Backend routes. It does not create a second Merge Request store, provider integration path, review grant, approval rule, or completion authority.
The Host allocates `/features/merge-request` and the Feature mounts:
The Host assigns the Merge Request provider the root `merge-requests` namespace through `WipMountRegistry` and mounts:
```text
/features/merge-request/merge-requests
/features/merge-request/merge-requests/<merge-request-id>
/merge-requests
/merge-requests/<merge-request-id>
```
There is no Feature Object or `/features/merge-request` compatibility alias. Legacy paths remain historical display data only and fail as current input with rediscovery guidance.
An item identity is a single canonical path segment. Nested paths and percent-encoded target substitution do not resolve. Item operations remove `merge_request_id` from their descriptor and reconstruct it only from the selected object route. Discovery does not mint authority: every call still uses the Worker's scoped `WorkspaceClient`, the enabled Feature flags, ordinary Tool permission rules, and the Backend's Workspace, repository, assignment, provider-ref, review, and integration checks.
## Current Feature inventory
+5 -3
View File
@@ -4,13 +4,15 @@
When a Worker explicitly runs in `wip` mode and the Objective Feature is enabled, the Worker Host projects Objectives as native Worldspace objects. The projection is an adapter over the existing scoped Workspace API. It does not introduce another Objective store, state machine, reference resolver, or audit path.
The Host allocates `/features/objective` to the Feature through `WipMountRegistry`. The Objective Feature then mounts:
The Host assigns the Objective provider the root `objectives` namespace through `WipMountRegistry` and mounts:
```text
/features/objective/objectives
/features/objective/objectives/<objective-reference>
/objectives
/objectives/<objective-reference>
```
There is no Feature Object or `/features/objective` compatibility alias. Legacy paths are retained only as historical text and fail as current inputs with rediscovery guidance.
`<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`.
+5 -3
View File
@@ -4,13 +4,15 @@
When a Worker explicitly runs in `wip` mode and its Ticket Feature is enabled, the Worker Host projects the enabled Ticket surface as native Worldspace objects. The projection is an adapter over the existing scoped Workspace Ticket API and typed Ticket tools. It does not add a Ticket store, resolver, audit path, state machine, readiness rule, queue path, or relation implementation.
The Host allocates `/features/ticket` and the Feature mounts:
The Host assigns the Ticket provider the root `tickets` namespace through `WipMountRegistry` and mounts:
```text
/features/ticket/tickets
/features/ticket/tickets/<ticket-reference>
/tickets
/tickets/<ticket-reference>
```
There is no Feature Object or `/features/ticket` compatibility alias. A legacy path supplied as current input fails with rediscovery guidance; saved history containing that text is left unchanged.
`<ticket-reference>` is either a canonical `T-<digits>` key or an existing canonical internal alphanumeric ID. Only a direct child is accepted. Nested suffixes, Objective keys, unrelated prefixes, and hidden routes do not resolve.
Every call retains the Worker's scoped `WorkspaceClient`, current Ticket Feature access, and ordinary Tool permission policy. Discovery is not mutation authority. A direct call to an unpublished operation fails even when the caller knows its name. `deny` and interactive `ask` permission outcomes fail closed before Backend dispatch.
+186
View File
@@ -0,0 +1,186 @@
# Repository, Workdir and attachment WIP projection
## Objects and operations
The Host owns these root namespaces, using the same `WipMountRegistry`
namespace/resolver/contribution contracts as Tickets, Objectives and Merge Requests:
```text
/
├── repositories list
│ └── <encoded repository key> read
├── workdirs list, create
│ └── <encoded Workdir ID> read, attach, delete (when supported)
└── workdir-attachments list
└── <connection lifetime ID> read, detach
```
Operations are Interface declarations, **not child files**. There is no
`/features/manage-workdir`, no writable Repository catalog and no Repository
content tree here. `/checkouts` content projection is a separate provider (T-699),
as is Workspace configuration (T-695). Management Objects do not grant access to
unattached contents. A future content provider should return its entrance from
an authorized, live attachment; it must not infer content/command capability
from catalog or lifecycle access.
`path` in inventory results is an absolute WIP Object path. It is a reference,
not an OS path, a capability grant, or an instruction to reinterpret another
string argument as a route. Source Repository references use `repository_path`;
attachment references use `workdir_path`. Attach results include
`attachments_path` to discover the current connection and its lifetime ID.
### Identity and route encoding
Repository identity is the formal Backend `repository_key`, never its display
name or a guessed `main`. Workdir identity is `working_directory_id`, never a
Worker-local alias. A segment containing only ASCII letters, digits, `-`, `_`
is unchanged. All other UTF-8 identities are encoded as `~` followed by lowercase
hexadecimal UTF-8 bytes. The `~` marker is itself always encoded. Only the
canonical encoding is accepted; no alternate spellings, recursive URL decoding,
case folding, slash traversal or namespace fallback are accepted. For example,
`repo/name` is `/repositories/~7265706f2f6e616d65`. This is injective and accepts
existing valid keys, including Unicode and slash-containing identities.
Attachment identity is a fresh opaque Backend connection ID for each lifetime,
including detach/reattach to the **same** alias and Workdir. Read exposes alias,
Workdir ID and effective attachment capabilities as attributes. Workdir existence
and Worker connection are separate Objects. Detaching a connection does not
remove the Workdir or delete a logical settings space.
## Feature and authority separation
`feature.workdir_catalog.enabled` independently supplies read-only Repository,
Workdir and attachment reference operations. It defaults enabled so a WIP Worker
with Workspace authority and appropriate permissions can find registered keys
without Ticket or conversation hints. It does not add ordinary Tools or require
a filesystem/Bash location. Standalone Workers without Workspace authority have
no Workspace catalog mounted.
`feature.manage_workdir.enabled` supplies management operation implementations
on the **same Objects** using `contribute_operations` and
`contribute_dynamic_operations`. It remains default disabled. It neither owns
another resolver nor confers permission. The object provider is unique; duplicate
namespace, route, operation name or inconsistent Interface contributions fail at
registration through the common registry.
Native permission identities are:
| Operation | Manifest permission identity | Bound identity in permission input |
| --- | --- | --- |
| repositories.list | RepositoryList | list arguments |
| repository.read | RepositoryRead | repository_key |
| workdirs.list | WorkdirList | list arguments |
| workdir.read | WorkdirRead | working_directory_id |
| workdirs.create | WorkdirCreate | original create arguments |
| workdir.attach | WorkdirAttach | working_directory_id + alias |
| workdir.delete | WorkdirDelete | working_directory_id + reason |
| attachments.list | WorkdirAttachmentList | list arguments |
| attachment.read | WorkdirAttachmentRead | connection_id |
| attachment.detach | WorkdirDetach | current bound alias |
Read permissions gate direct item resolution as well as list item visibility.
The manifest's existing default policy is retained; `ask` is denied fail-closed.
An argument-dependent policy may advertise an operation that could be allowed,
but every call checks its exact reconstructed input before execution. Mutation
arguments cannot override path-bound Workdir or connection identity. Having
RepositoryRead is not WorkdirCreate; inventory/management permissions are not
file access, delegation or command execution grants. Backend caller/Workspace
identity, source proof, attachment capability ceilings and lifecycle policy are
checked by the existing paths on every request.
## Contextual Interfaces and stale observations
Providers opt into common Host contextual Interfaces. Operation publication is
the intersection of registered implementation, current target support and current
subject policy. The collection reference feature can remain available while the
management feature is disabled. External Workdirs without cleanup authority do
not expose delete. Only currently supported lifecycle operations are advertised;
no delete/command is added to logical attachments solely because they appear as
workspaces.
A contextual Interface reference is opaque and path-qualified as
`<base>/@/<hex UTF-8 Object path>`. Clients must use the returned reference, not
construct it. Fetch and call freshly resolve the same Object and filtered
Interface; base references cannot fetch an unfiltered descriptor. Descriptor and
Object validators invalidate old observations after target/connection/permission
changes. Validators are managed by the WIP Client, not manually by the LLM.
Cached Interface possession and known direct paths never bypass current checks.
Historic session text remains append-only and is not rewritten or replayed.
## Backend lifecycle and safe output
Catalogs are request-time projections of existing Backend authority, not another
store or synchronization job. WIP list pages default to 50 and accept 1–100
items. Repository pages sort the existing complete Repository catalog by stable
key. Workdir pages use the signed caller-bound Backend
`workers/self/workdir-catalog` operation, with SQL keyset paging over the existing
Workdir registry in stable ID order. This is not the legacy bounded
`working-directories` browser/Tool snapshot: Workdirs older than its 200-record
cap remain reachable. `next_cursor` advances over scanned rows even when the
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
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
or capability change beyond row 50 therefore invalidates old collection
observations without adding a ledger or revision store. 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
page. Unknown/expired IDs yield no item, never a current alias substitute.
Repository projection uses an explicit field
allowlist: formal key, kind/provider, any existing display metadata and explicit
nullable default selector. Source URI/path, credentials, remotes, Runtime
connection information and internal fingerprints are not model-visible.
Workdir projection includes stable ID, optional display name, public source kind
and Repository key, existing selector/ref/tree, status, cleanliness and occupancy
when available. Cleanup handles and External grant internals are not forwarded.
No missing attributes or implicit selectors are fabricated. Backend errors are
sanitized rather than forwarding raw diagnostic bodies with possible host paths.
Create and attach reuse the ordinary Workdir backend, request identity,
Worker-session router, mutation lock and Internal SubWorker lifecycle hooks.
Creation does **not** attach. Attach installs the Backend-attenuated session
capabilities, never a fabricated `ALL` capability for External Workdirs. Detach
uses the same begin/finish/cancel-detach path and command/operation exclusion as
Tools; the Backend checks expected connection identity under its existing
current-Worker lifecycle lock before closing sessions, then conditionally
updates that exact row in the existing connection ledger. Alias reuse cannot
retarget an old connection operation. Compensation retains the original
connection identity. Existing removal dispositions `removed`, `retained`, and
`attention_required`, plus retryability, remain intact.
Stale identity/validator rejection is distinct from unknown operation outcome.
Transport failure, invalid post-dispatch response or uncertain lifecycle failure
is **not** proof that no effect occurred; it must not be automatically retried.
Inspect current state or request operator guidance. No new retry or attachment
ledger is introduced by WIP.
When native management is mounted, `WorkdirList/Create/Attach/Detach/Delete` are
claimed as represented native capabilities, so `/tools/Workdir*` is not also
published. Ordinary Tools mode keeps its names and inputs. Existing native
Ticket/Objective/Merge Request routes retain their T-698 contracts.
## First-Workspace example
1. `WipDiscover(path="/", depth=1)` finds `repositories`, `workdirs` and
`workdir-attachments` when permitted.
2. Inspect the returned Repository collection Interface and call `list` with a
bounded limit. An empty registered/visible catalog explicitly returns an empty
list; do not invent a key. Follow `next_cursor` when supplied.
3. Discover the returned item `path`, inspect its Interface and call `read`.
`default_selector: null` means no configured default, not `main`.
4. Inspect the Workdir collection and `create` using the discovered formal key,
with optional selector, display name and Runtime under the existing contract.
5. Follow created `item.path`, discover/inspect and `attach` with a chosen
Worker-local alias. This is separate from creation.
6. Follow `attachments_path`, `list`, then discover/read the returned connection
path to confirm alias, Workdir and effective capabilities.
7. Call that connection's `detach` with no identity override. The observed
lifetime ID guards alias reuse; the Workdir remains in its inventory.
Each step uses current WIP observations and no Ticket-derived key, host path,
Runtime endpoint or manual validator. Explicitly refresh/reset discovery after
external changes and before reconstructing a rejected stale call.
+36 -6
View File
@@ -52,21 +52,47 @@ The interface descriptor uses WIP `Json` for compatibility input and includes th
## Native projection extension and collisions
`worker::wip::WipMountRegistry` is the Host-owned mount allocator. A Feature can independently construct a `WipProjection` with an async `WipOperationHandler`, then pass its aggregate registry through `install_wip_mode_with_mounts` before compatibility finalization. A Feature first requests a Host-owned `/features/<feature>` namespace with `allocate_feature_route`; checked direct-child joins prevent a Feature from claiming an ambient global route.
`worker::wip::WipMountRegistry` is the Host-owned namespace, Object, and operation registry. A provider requests a root namespace with `allocate_namespace(owner, namespace)`; the owner is retained as Host metadata and is not inserted into the public path. It then mounts the collection Object directly at `/<namespace>` and may install a bounded direct-child resolver. Features do not appear as intermediate Objects.
- Routes must be canonical absolute WIP paths.
Object resolution and operations are separate contracts. One mounted `WipProjection` owns a static Object route and its initial operations. Another Feature may add operations with `contribute_operations` without replacing that Object. Dynamic families likewise keep one `WipDynamicItemResolver`, while `contribute_dynamic_operations` supplies disjoint handlers for every resolved item without a second resolver. The Host merges only contributions with the same interface format, documentation, and type declarations; duplicate operation names, inconsistent interfaces/validators, missing targets, duplicate namespace owners, and unrelated route claims fail deterministically at registration. The merged interface validator changes with the effective operation set, so stale Known Space cannot authorize a newly contributed operation.
- Routes must be canonical absolute WIP paths, and allocated namespaces are one lowercase root segment.
- Object names must match the final route segment.
- A projected object exposes exactly its declared interface.
- Unrelated duplicate routes and conflicting interface descriptors or validators fail startup.
- Object existence and resolution have one owner; operation contributors cannot remount or substitute the Object.
- Multiple Features may contribute disjoint operations to the same Object interface.
- Unrelated duplicate routes, duplicate operation names, and conflicting interface shapes fail startup.
- A bounded dynamic mount may resolve exactly one item segment below an already mounted native collection; deeper paths never fall through to the resolver.
- A native projection replaces a compatibility projection either by the original same-route semantic capability or by an explicit registry capability claim owned by a mounted native route.
- Conflicting native claims fail startup. When native is selected, the claimed compatibility tool is hidden rather than exposed through a second entry. Normal Tool mode is unaffected because it does not install the WIP registry.
`WipInspect` renders the complete descriptor, including descriptor-local named declarations and recursive record, list, enum, and union shapes with required flags and declaration documentation. The unit samples `mount_collision_and_native_replacement_are_explicit` and `native_projection_preserves_descriptor_and_decodes_typed_arguments_end_to_end` demonstrate independent native registration, deterministic replacement, collision rejection, complete descriptor inspection, and a descriptor-typed native invocation.
`WipInspect` renders the complete effective descriptor, including contributed operations, descriptor-local named declarations, and recursive record, list, enum, and union shapes with required flags and declaration documentation. The tests `host_namespace_object_resolution_and_operation_contributions_are_independent`, `operation_contributions_reject_interface_and_target_conflicts`, and `native_projection_preserves_descriptor_and_decodes_typed_arguments_end_to_end` demonstrate root allocation, unique resolution, contribution merging, collision rejection, current call-time authorization, complete descriptor inspection, and descriptor-typed invocation.
The built-in native route migration is:
| Historical route | Canonical route |
| --- | --- |
| `/features/ticket/tickets[/<reference>]` | `/tickets[/<reference>]` |
| `/features/objective/objectives[/<reference>]` | `/objectives[/<reference>]` |
| `/features/merge-request/merge-requests[/<reference>]` | `/merge-requests[/<reference>]` |
The built-in Repository/Workdir reference provider allocates `/repositories`, `/workdirs`, and `/workdir-attachments` using this contract; `manage-workdir` contributes operations rather than owning another Object. See [Repository, Workdir and attachment WIP projection](workdir-wip-projection.md) for identity encoding, reference/management permissions, attachment lifetime safety, and first-Workspace discovery. `/checkouts` content projection remains a separate provider.
### Registering a subsequent provider
1. Allocate `allocate_namespace("repository", "repositories")`. The returned `WipNamespaceRoute::root()` is `/repositories`; the owner is independent of any Feature registration name.
2. Mount a native collection `WipProjection` at that root with capability `repository:collection`, matching Object name and one declared interface. Static Objects underneath the root use the same capability owner prefix. A mounted collection retains its own identity, interfaces, and validator even when it has static children.
3. For dynamic items, register one `WipDynamicMount` on the collection with capability `repository:item`. A foreign capability owner is rejected. Its `WipDynamicItemResolver` owns reference acceptance, Object identity, and current Object validator. It returns only a bounded direct child; it must not reinterpret a reference as another namespace or grant permission from existence.
4. The reference provider supplies its read operations and handlers independently of management Feature enablement. An Object-only provider may use an empty operation descriptor. Enabled management Features register `WipOperationContribution` for static Objects or `WipDynamicOperationContribution` for a dynamic family; they do not allocate the namespace again or replace its resolver.
5. Contributions use the existing interface reference and identical format, documentation, and type declarations, supplying only their new operations. Interface references are global: extending a descriptor that is also mounted elsewhere must not create different descriptors/validators under the same reference (registration fails). Use separate references for collection and item contracts. Failed contributions leave the registry unchanged.
6. Handlers are responsible for checking current target capability and current subject authorization before dispatch. Registration/discovery is metadata, not a grant. A resolver/handler must retain the original permission identity and domain audit/failure contracts. Revocation after discovery must fail before provider dispatch. Compatibility mounts are restricted to `/tools/<tool-name>` and cannot bypass namespace ownership or republish legacy routes.
Tests `management_enablement_is_independent_of_read_and_operation_permissions` and `contributed_interface_invalidates_observations_and_restore_requires_discovery` cover all eight enablement/read/manage combinations, direct-call rejection, effective descriptor changes, fresh runtime isolation under the same Worker identity, reset, and post-observation revocation.
## Authority and execution
Discovery is not authority. The Worldspace contains only the tools already registered from enabled Features and the Worker's current execution context. Calling a compatibility operation:
Discovery is not authority. Object publication, operation implementation, target capability, and subject permission are independent checks. An Object may remain discoverable through an enabled read provider when a management Feature contributes no operations; conversely, publishing an operation never bypasses the target's current capability or the subject's current permission. Every handler re-evaluates permission at call time, so revocation after discovery is rejected before provider dispatch. Feature enablement selects implementations and does not itself grant read or mutation authority.
The Worldspace contains only the operations registered from enabled Features and the Worker's current execution context. Calling a compatibility operation:
1. resolves the exact mounted route;
2. verifies object and interface membership and validators;
@@ -85,6 +111,8 @@ The stateful `wip-client::Client` is owned by one Worker WIP runtime. Sessions a
Known Space and interface observations are therefore not shared across Workers, endpoints, or security contexts. A restored Worker creates a new runtime/client and must explore again. `WipDiscover(reset = true)` drops all observations before reconnect/authority-change exploration; `refresh = true` explicitly supersedes one cached observation. A normal compaction keeps the live runtime but the three gateway schemas are supplied again on every LLM request, including the instruction to begin discovery at `/`; the compacted transcript is not treated as cache authority.
Saved Session/history entries remain append-only evidence and are never rewritten or replayed as calls. In particular, historical `/features/<feature>/...` text stays displayable but is not a current route alias. Passing such a legacy path to discovery or call returns `NotFound` with guidance to rediscover from `/`; the Host does not translate it, resolve it under another namespace, or expose old and new routes in parallel.
## Errors, cancellation, and retries
The adapter keeps these outcomes distinct:
@@ -96,6 +124,8 @@ The adapter keeps these outcomes distinct:
- **disconnected/timeout after dispatch**: `wip-client` records an unknown outcome;
- **operation outcome unknown**: an executing provider fails without a rollback/non-execution guarantee, a response cannot be completed after dispatch, or the call future is force-closed.
Cooperative cancellation is scoped to the active execution's selected operation and exact resolved handler instance. Static/dynamic dispatchers forward `cancel_operation` only to that operation's provider, once per cancellation request, never to unrelated contributors or every alias backed by a shared handler. The runtime pins the dispatcher used for execution rather than resolving the Object again on cancellation, since dynamic resolvers may return stateful instances. Providers may implement `cancel_operation` for operation-specific control or the shared `cancel` default. Terminal completion or a dropped execution future releases the active binding; unknown/finished execution IDs do not invoke any provider. A cancellation request is not itself terminal confirmation.
An unknown mutating outcome must never be retried automatically. The model must inspect external state or ask for operator guidance. Retrieval failures may be explicitly refreshed.
## Measurement
@@ -0,0 +1,133 @@
# Subject / Memory information audit (T-692)
## Decision and scope
The previous Subjects index exposed `Store revision` alongside identity, state and
update time; Subject detail led with technical metadata and raw surface states.
These numbers require implementation knowledge before they help a user choose a
Subject or understand what is available. The revised hierarchy starts with
identity, connection, committed records and generated-context availability.
This is a presentation change, not a change to storage, freshness comparisons,
permissions, generation, or Worker history. T-688's ordinary Worker launch and
T-689's independent Subject creation remain the actual operation paths. T-693
owns editable user-managed behavior: that content must remain distinct from this
page's context generated from committed Memories. A successful read or a current
surface does not attest what is already in any running Worker's history.
## Field classification and before/after priorities
| Information | Classification and reason | Presentation decision |
| --- | --- | --- |
| Subject role and ID | Normal identification. Similar or identical roles need a stable identifier for selection and comparison. | Keep role as heading and the complete wrapping ID visible in index and detail. |
| Subject active/retired state | Normal operation decision: whether a new Worker connection can use the Subject. | Replace raw `active` with `Available`; keep `Retired`. Detail explains connection eligibility, not an assertion that a Worker is running. |
| Current Worker | Normal connection decision, derived from the existing Backend singleton projection. | Promote `Connected` / `Not connected` and display name. Do not infer ownership from store revision or claim all Workers received current Memory. Worker start remains the ordinary launch form. |
| Committed-Memory presence | Normal reading decision, independent from surface generation or candidates. | Show `Available` / `None` only on the successful first bounded page. On later pages say `N on this page`; errors say `Unavailable`. Never infer counts from revision or scan all pages. |
| List counts | Normal navigation context, not a store-wide statistic. | Label `N shown` and `more available`, preserving existing bounded cursor pagination. |
| Resident availability | Normal context decision. A missing surface is not missing committed Memory. | `Ready to use`, `Ready, no context`, `Not generated`, `Needs refresh`, `Generation failed`, and request `Unavailable` are distinct. These describe the current generated store artifact, not content truth or Worker-history propagation. |
| Resident body and generated time | Normal content/read freshness context. | Keep the shared Markdown renderer, generated date and exact snapshot reference count. Ready-empty explains that a valid snapshot has no body; stale/failed do not display a partial or old snapshot. |
| Subject store revision | Diagnostic only: internal committed-Memory change number, not record count, schema version or quality score. | Remove it from the index's normal columns. Retain the exact value under detail's `Technical details`, with its meaning explained. Backend comparisons are untouched. |
| Subject update time | Secondary metadata; not a context-injection or generation completion timestamp. | Label `Last changed`; on detail put it with Subject diagnostics rather than competing with the availability summary. |
| Snapshot ID and built-from revision | Diagnostic correlation, distinct from per-Memory revisions. | Retain under `Surface sources and diagnostics`, labelled `Generated from Subject revision` and explained. |
| Surface Memory references | Detailed audit/reachability. | Preserve reference IDs, immutable revision values and Memory links in the disclosure. |
| Memory claim, kind, lifecycle state, excerpt and body | Normal content decision. Active, resolved and retracted are not interchangeable. | Retain list scanning and body reading. Keep the Markdown renderer and current/historical distinction. |
| Individual Memory revision and history | Detailed content comparison, also useful for identifying a selected version. | Explicit `Memory revision` labels, separate from Subject counters. Preserve immutable history, selected revision links and revision pagination. |
| Candidate provenance, evidence, origin, source refs and derivations | Detailed audit, required to understand why a record exists. | Retain IDs, ranges, summaries, excerpts, origins and derived-revision links. Counts say `shown` except where the API provides exact per-candidate totals. Preserve body and evidence continuation links. |
| Raw field names / duplicated general revision terminology | Unnecessary in the normal hierarchy; multiple unrelated counters are misleading. | Replace bare revision labels and raw surface enums with purpose-specific labels. Do not delete API fields, stored information, diagnostics or their detailed access. |
No diagnostic datum was removed from Subject detail, surface detail or Memory
provenance/history. The index no longer shows a store revision, but the exact
value remains one click away in Subject detail. No invented regeneration,
editing or recovery action was added; `Needs refresh` does not mean an update job
is running. Creation still creates only a Subject, and selection still uses the
existing ordinary launch contract.
## Candidate-only verification
Candidates are not committed records and are not an input to this page. The
Subject loader performs just three bounded reads: Subject, resident surface and
`/memories?limit=100[&cursor=...]`. It does not read or count staging records.
The existing real-store regression
`subjektiv::tests::candidate_decisions_reject_conflicts_and_roll_back_partial_writes`
stages a candidate and attempts an invalid apply. It asserts the candidate has no
resolution and the committed Memory list remains empty. The component regression
`candidate-only Subjects show no committed Memories, not an inferred candidate count`
uses that read projection (store revision 0, ungenerated surface, empty successful
committed list) and checks `None`, `No committed Memories yet.`, `Not connected`
and `Not generated`, with no resident body. Thus a staged candidate does not turn
into a committed-Memory count or a claim that context has been generated.
The store test proves the persistence boundary; the component test proves the
rendering of its public projection. These are separate boundary tests, **not** a
live browser-to-Backend candidate-creation E2E. The existing `ungenerated` capture
represents the same visible projection, but does not itself stage a candidate.
## Visual review and validation evidence
The restored implementation is in `fbb09e65` and `4cf6c130`; this report/test
supplement does not change rendered markup, styling, copy or data contracts.
- Scenarios: `tools/web-ux/scenarios/memory-subjects-{light,dark}.json`.
- Before: `target/web-ux/t-692-before-{light,dark}` at `cc979874`.
- After: `target/web-ux/t-692-final2-{light,dark}` at clean `4cf6c130`.
- Comparisons: `target/web-ux/t-692-final2-{light,dark}-comparison`.
- Browser interactions: `target/web-ux/t-692-browser-interactions`.
- Persona: synthetic Workspace fixture; permitted creation and intentional
permission-rejection responses. No real account or private store data used.
- Viewports: 1440, 768, 390 and 320px; light and dark themes.
- States: connected/unconnected, active/retired, successful empty lists, ready
long Markdown, ready-empty, ungenerated, stale, failed, unavailable HTTP503,
create form and create permission error; expanded technical disclosures.
- The takeover implementer personally inspected individual before/after images
for index at 768px, representative detail at desktop/mobile and Memory detail,
then ready-empty mobile, ungenerated narrow, stale desktop, failed mobile,
endpoint error at 320px, mobile surface sources, compact Subject diagnostics
and mobile permission error. Oversized contact sheets were rejected by the
image provider; individual viewport PNGs were inspected instead.
- Findings/result: primary state groups replace counters without losing IDs or
document access; long IDs wrap, status is textual rather than color-only,
mobile groups stack, diagnostics remain readable, and error state regions stay
separate. No new visual defect identified. **Pass for the audited scope.**
- Final bundles contain 64 captures each. `completed-with-errors` is expected:
intentional creation HTTP403, endpoint HTTP503/associated aborted requests,
and the deliberately failed surface's alert are retained as evidence. Healthy
capture states have empty error arrays; this is not a blanket claim that all
captures were error-free. Review-context diagnostics are empty.
- The focused production-shell browser test proves main-owned vertical scrolling
with wheel/keyboard/touch, 320px/mobile shell behavior, no page-wide overflow,
local table/code scroll, Markdown safety, creation/launch separation, bounded
pagination and Memory/source/revision reachability.
Independent review at source `4cf6c130` ran root `cargo check`,
`cargo fmt --all -- --check`, diff hygiene, Web check (0 errors/warnings), 12
Memory API tests, 452 full Web unit tests, 134 component tests (8 Memory tests),
production build, the focused production-shell browser test (1), and Web-UX unit
tests (16), all passing.
The takeover implementer then validated this report/test supplement with:
- `cargo test -p yoi-workspace-server --lib candidate_decisions_reject_conflicts_and_roll_back_partial_writes`: 1 passed.
- Root `cargo check`, `cargo fmt --all -- --check`, and `git diff --check HEAD`: passed.
- Targeted Memory component tests: 9 passed, including the added candidate-only projection test.
- Web `deno task check`: 0 errors/warnings; `deno task test`: 452 passed;
`deno task test:component`: 135 passed.
- Scoped `deno fmt --check` on the changed TypeScript test: passed.
- JSON audit of both final2 bundles: 64 captures each, exact clean source
`4cf6c130`, and no errors outside the deliberate permission/error/failed routes.
The supplement changes only a document and a test, so it does not require new
rendered-UI captures. Rereview records and the final approved Ticket handoff link
this report to the new exact source.
Known non-blocking validation diagnostics: component runs emitted incidental
localhost:3000 connection-refused messages while passing; the production build
warned about a large chunk. A broader Web-UX typecheck fails in unchanged
`tools/web-ux/src/processes.ts:44` on a `TextDecoderStream` generic incompatibility;
this report does not claim that broad check passed.
No live-Backend browser interactions, real Worker model-request propagation,
regeneration, or server/runtime process E2E were performed. No Rust production
crate changed in T-692; the supplemental store test exercises an existing
contract without changing that contract. Backend counters, comparison logic,
storage schema and feature behavior remain intact.
@@ -0,0 +1,21 @@
# Review finding severity contract mismatch during T-697
The independent Reviewer completed source/requirement analysis and validation,
then its one `ReviewMergeRequest` submission was rejected with HTTP 422 because
it used severity `medium`. The tool exposed a free-form string, while Backend
validation accepts only `blocker`, `major`, `minor`, or `note`. No verdict was
recorded by that failed call. The child preserved the source and reported the
blocked submission rather than loop-retrying a mutation.
The parent inspected the committed review session and fresh MR authority,
confirmed the same request/source/Ticket snapshot with no verdict, and submitted
a fresh child turn to correct the input contract. The Reviewer independently
selected supported severities and recorded `request_changes` successfully on
the same MR. No alternate token/context, CLI/storage write, source movement or
replacement MR was used to compensate for the rejection.
Suggested improvement: expose finding severity as the typed Backend enum in the
LLM tool schema, and document allowed values in the Reviewer prompt. Rejecting
an unadvertised enum after a costly review should not prevent durable findings
from reaching the MR. This report is tool feedback, not a T-697 product
constraint or a request to alter review judgments.
@@ -0,0 +1,30 @@
# Reviewer verdict のvalidation rejectionとone-call制約
## 確認した事実
T-703のMR `01a10b3b-87ec-7093-b0c4-a00a78095695`、source
`3174142bd1314fe34c5de7b6a66654ff1c2a8af2` の独立ReviewerはRestoreとno-op
attention checkの競合を見つけ、`ReviewMergeRequest`でrequest_changesを提出しようとした。
severityに`medium`を指定したためHTTP 422となった。Backendの許容値は
`blocker` / `major` / `minor` / `note` だった。
親の`ShowMergeRequest`再読でthreadはReviewRequested seq2までであり、verdictが
永続化されていないことを確認した。Reviewerは「ReviewMergeRequestを正確に一度だけ
呼ぶ」という指示に従い、validation rejection後も再提出を行わなかった。親のfollow-upでも
同じ境界を報告したため、再提出を強制せずReviewerを停止した。
## 障壁
正しいBackend拒否を迂回すべきではない一方、mutationが行われていない入力validation失敗が
一回限りの提出枠を消費する扱いだと、レビュー済みの指摘がMR authorityに残らずpendingになる。
セッションでの報告をverdictや承認の代わりにはできない。
## 改善案(このTicketの実装対象ではない)
- Reviewer-facing tool schemaにseverityのBackend enumを明示し、不正値を入力時に防ぐ。
- one-call指示について「一度のdurable verdict」と「validationで拒否された呼出」を区別できるか
設計上判断する。無制限再提出や記録済みverdictの変更を認める提案ではない。
- mutationなし/retry可否をtyped errorとして返せると、親とReviewerの双方が推測なしに扱える。
T-703では元のMR/source selectorを保持し、指摘を修正したcommitを通常pushした後、fresh
exact-source reviewで修正内容とその証拠をMRに記録する。未記録のverdictを承認扱いしない。
@@ -0,0 +1,18 @@
# Reviewer SubWorkerがreview開始前に停止する
## 状況
T-698の公開済みMerge Requestに対して、`builtin:reviewer`を指定し、対象Ticket/Merge Request、writable delegated scope、command grantを明示してReviewer SubWorkerを3回起動した。
いずれも`review_requested` eventは正しいsource refとTicket revisionを記録したが、SubWorkerは直後に`Stopped`となった。session observationには最初のuser taskだけが存在し、assistant output、tool call、typed review verdictは記録されなかった。Merge Request threadにもapprove/request_changesは追加されなかった。
## 障壁
CoderはReviewer approvalを代行できず、親Workerには`ReviewMergeRequest` authorityがないため、実装・公開・検証が完了していてもfresh approvalを取得できない。同じ入力を再送するだけでは診断情報が増えず、review requestだけが複数残る。
## 改善案
- Reviewer起動がmodel run前に停止した場合、停止理由を親へ構造化して通知する。
- `review_requested`を記録した後にReviewer sessionの開始に失敗した場合、request eventと失敗eventを関連付け、未解決reviewとして明示する。
- profile/provider初期化、review worktree準備、capability bindingのどの段階で失敗したかを、credentialやhost pathを露出しないbounded diagnosticとして提供する。
- 同一sourceへの再試行時は、先行Reviewerが一度も実行されていないことを識別できるようにする。
@@ -0,0 +1,33 @@
# SubWorker cwd attachment-root ambiguity during T-697
## Observed barrier
A delegated backend investigation was spawned with `cwd=crates/workspace-server`
and explicit writable rules for both `crates/workspace-server` and
`crates/server-api`, plus repository-root read access. The child reported that
its available attachment exposed only the workspace-server crate; its observed
file reads used `src/server.rs`/`src/lib.rs` and it could not access server-api.
It also had no `ShowTicket` or parent-notification tool, despite the parent
having typed Ticket authority. No implementation changes were made by that child.
The parent inspected the committed child session, released the idle child, and
respawned at the repository root with the same delegated write boundaries and
an explicit summary of the authoritative Ticket requirements. That child could
then implement the cross-crate API boundary. No filesystem capability rejection
was worked around through another write path.
## Suggested improvement
The advertised SubWorker `cwd` contract says that it changes only the tool
working directory, not attachment roots. Show the effective attachment aliases,
logical roots and readable/writable scopes explicitly to the child and parent,
and test cross-crate reads/writes for a non-root cwd. If cwd intentionally
narrows or rebases an attachment, document that instead of presenting it as
only a command default. A denied/inaccessible sibling diagnostic should identify
the effective logical attachment root without disclosing unauthorized paths.
Also expose which typed domain and parent-messaging tools will be available
before spawning. Delegated implementation requires authoritative requirements;
where Ticket reads are unavailable, the parent must deliberately provide the
bounded contract rather than assume the child can recover it by ID. This report
records tool ergonomics only; it is not a new T-697 product requirement.