ditdash U-J5a/b: the converge button (registry, dispatch, decision surface, panel) #10
Loading…
Reference in a new issue
No description provided.
Delete branch "ditdash-u-j5"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
J5a (sensitive): the operation registry, the dispatch client, the decision endpoint, the D6 identity fail-closed without custody (the WS-0 convention). J5b: the panel, the relayed prompt, the verbatim refusal render. Critic fixes: exact-tenant run binding + event run_id validation (colliding-tenants fixture), the stale-prompt gate, the runs-list token sweep. Critic: PASS-WITH-FINDINGS, fixed.
The registry (src/operations.rs): the typed Operation (converge on a tenant) with the frozen run-state vocabulary (dispatched|awaiting-sign-in|planned|gated|applying|succeeded|failed — byte-identical to converge.yml's contract test), the D1/D6 dispatch client (workflow_dispatch via the DITDASH_DISPATCH_TOKEN identity; a failed dispatch leaves no phantom run), the D2 job-log poll (parses the run's converge_state + device_code JSON lines; a failed poll keeps the last observed state, never a fabricated advance), the D3 per-run decision record (bearer decision_token for the run's poll; the write path is the initiating operator's session alone), the D7 second-dispatch guard (one active run per tenant; the forge concurrency group and the dito lock stay the authorities), the terminal-state collector nudge (U-F3, fired once), and the FR-22 action row. In-memory only: a restart drops pending records and the run's bounded wait fails closed to apply-not-confirmed (FR-24). The surface: POST /api/v1/runs (dispatch; JSON for tools, the panel's urlencoded reason form with a 303 back), GET /api/v1/runs (collection), GET /api/v1/runs/{run_id} (state + plan; the relayed prompt scoped to the initiating operator's session alone — a foreign operator sees the run, never the prompt), GET|POST /api/v1/runs/{run_id}/decision (the bare D3 record the workflow polls; Apply/Decline recorded once). All operator@msp only except the bearer-gated decision GET; below the role every route answers the same 404 (zero-leak). The tenant resource gains `data.actions` (operator@msp + lag>0 or drift + no active run + a configured identity → available; the blocking reason otherwise; ABSENT below operator@msp) and the J5a-rendered actions row. The run never acks a gate — no ack path exists, not even behind a flag. The token discipline: DITDASH_DISPATCH_TOKEN reads via config.rs, rides only the forge Authorization header, and is asserted absent from every response body. The manifest ref stays UNDECLARED (a present-but-unseeded ref aborts deploys — WS-0); the comment carries the re-add recipe. The decision endpoint notes its forward-auth dependency (U-K3-router) — until then the run times out fail-closed. Tests: 14 operations.rs unit tests through a loopback forge stub (typed inputs, guard, log parsing, prompt scoping, verbatim refusal text, nudge-once, decision once, poll-failure stance, D6 fix naming), the HTTP rows (role denials, dispatch→poll→guard flow, prompt isolation, bearer gate, registry restart, token-never-renders), the policy-matrix operator@msp row, the canon `decision` exemption (the §26.8 rename + the workflow's fixed path), and the refreshed tenant--operator snapshot. Assisted-by: worker:synthetic/hf:deepseek-ai/DeepSeek-V4.1-FlashCritic findings on the U-J5 converge surface: - resolve_forge_run takes the tenant from the record (never the run-id split) and matches the title boundary exactly (after 'converge <tenant>' comes ' (' or the end): prefix-colliding tenants (e2e / e2e-lab) never bind each other's runs. - refresh drops converge_state lines whose run_id is not the record's — the log is resolved by title, so a sibling run's lines must not advance. - the device-prompt panel renders only while the run still awaits sign-in with the decision pending: no stale prompt after the run advances. - the runs list joins the token-never-renders sweep. Fixtures: the colliding-tenants listing (e2e vs e2e-lab), the mismatched-event drop, and the stale-prompt absence. Assisted-by: worker:synthetic/hf:deepseek-ai/DeepSeek-V4.1-Flash