feat(tui): reconcile scoped selection and publication lifetimes

Reuse canonical escaped absolute reference labels; retain structured operation selection across loading and row movement. Freeze editor targets and retire confirmation on Client observation invalidation. Mirror retained stale/loading Object, tree and Interface state without ref fabrication or automatic execution.

Validation: 36 TUI unit tests, real filesystem HTTP workflow, 8 formatter and 6 asynchronous runtime tests, and 2 doc-tests pass. Full workspace fmt/check/all-target tests/doc-tests, Nix clippy -D warnings, rustdoc -D warnings, public example and diff check pass. Scoped regressions cover same-name dispatch, stale/ref-less/replaced/deleted scopes, old in-flight contexts and selective typed-entry observation.
This commit is contained in:
2026-10-08 04:03:51 +09:00
parent fc52d72a01
commit 84cb5904df
7 changed files with 933 additions and 57 deletions
+45 -8
View File
@@ -91,10 +91,29 @@ these labels. The output has no extra outer signature string quoting.
Keep selection, cache and calls keyed by structured references and operation
names, never display text. No parser, scope inference, identity reconstruction,
Object/Interface signature renderer or new crate is introduced. Membership,
ancestry, authorization and freshness remain runtime concerns. Screen integration
is follow-up T-23; the current bounded screen/value/diagnostic rendering is
unchanged. At integration, apply existing UI layout bounds to display labels
without feeding shortened labels back into identity or calls.
ancestry, authorization and freshness remain runtime concerns. Normal Entry,
Interface/Operation, editor, confirmation and operation-history screens use these
qualified labels with existing bounded layout. Shortened labels never feed back
into identity or calls.
### Rust API and wire migration
Interface identities in the presentation map, action/selection rows, in-flight
subjects and editors are now `InterfaceReference { scope, name }`, not strings.
This is a breaking Rust API change; consumers must construct an explicit canonical
absolute scope rather than parse a label or infer a scope from a target Entry.
`ArgumentEditor` also retains its Entry `path`; `new` defaults to `/`, and
`for_target(path)` fixes the chosen target before editing. Normal UI selection
sets it automatically. Confirmation never retargets an existing editor.
`App::operation_selection` retains the Entry and structured row through temporary
loading gaps; the numeric cursor is only a layout position. Retaining this subject
does not expose stale operations or authorize execution.
The TUI uses the scoped Protocol/HTTP Client wire: Interface references are
structured, and calls use an Interface target carrying the reference and optional
`scope_ref`/validator preconditions. The optional identity is copied by the Client,
not invented by the UI; no additional required ref is introduced. This wire is not
compatible with the former string-reference request/response shape.
## Keyboard bindings
@@ -128,10 +147,12 @@ a second explicit Enter (or `e`) to execute once. There is no operation auto-ret
interfaces, and freshness. Validators, credentials, Host internals, and backend
paths are deliberately hidden. If current metadata disappears, old text is not
presented as current.
- **Interfaces / Operations** identifies every operation by `(interface scope,
interface name, operation name)` and explicitly displays `scope=…` and `name=…`.
Structured references remain intact in selection, acquisition, and call state;
display labels are never parsed back into references. Descriptor lookahead follows fresh visible Objects, shares the
- **Interfaces / Operations** identifies every operation by structured `(scope,
interface name, operation name)`, displaying `scope::interface#operation` with
explicit absolute scope and safely quoted components. Structured references
remain intact in selection, acquisition, and call state; display labels are
never parsed back into references. Descriptor lookahead follows fresh visible
Objects, shares the
client's in-flight/cache bounds, and never retries retained failures implicitly.
The pane shows descriptor documentation, ordered parameters, required/optional
status, and result types.
@@ -158,6 +179,22 @@ Validator mismatch, interface mismatch, `OperationOutcomeUnknown`, undecodable
post-dispatch responses, and post-dispatch timeout/disconnect are displayed using
the runtime's authoritative transition.
Every Client transition is mirrored into retained UI Object, tree and Interface
state, including scope retirement and cache removal. An open editor/confirmation
is bound to private Object/Interface snapshots (not displayed refs/validators).
Changed, stale, loading, removed or retargeted observations retire the editor;
reobservation never recreates or executes it. Descriptor rows remain identified
by the structured reference when another Interface gains or loses operation rows.
Explicitly select/load an Interface header to refresh a retained stale descriptor,
then select an operation and confirm a new call. Client calls already dispatched
retain their original immutable context and are never moved to a new scope.
Without refs, a same-path replacement with indistinguishable public metadata
cannot always be detected; the UI does not synthesize identity or infer deletion
from missing tree edges. Optional refs remain optional. Typed `Entry` results are
only observed when individually selected; the UI neither bulk-fetches result
paths nor adds guessed parent/indexable edges.
For an unknown outcome, the UI states that execution or side effects may have
occurred, marks the related Entry stale, and tells the user to refresh. Refreshing
does not recreate the call: the user must select the operation, review fresh input,