From 885e8045feaf236ac447652cf247e2d5d40f07c6 Mon Sep 17 00:00:00 2001 From: Hare Date: Thu, 15 Oct 2026 19:41:40 +0900 Subject: [PATCH] docs: map notification routing and verification boundaries --- docs/notification-routing.md | 105 +++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 docs/notification-routing.md diff --git a/docs/notification-routing.md b/docs/notification-routing.md new file mode 100644 index 000000000..a7d652ca6 --- /dev/null +++ b/docs/notification-routing.md @@ -0,0 +1,105 @@ +# Notification routing and evidence + +Business notifications are derived from committed source facts. A callback is only a +low-latency wakeup: startup and bounded periodic recovery consume the same persisted +sources, decisions and requests. A notification neither grants authority nor changes +Ticket case/handling, MR approval, Job execution, or Worker lifecycle. + +## Connection inventory + +| Committed origin | Capture / decision owner | Recipient and negative outcomes | Delivery / recovery / evidence | +| --- | --- | --- | --- | +| Ticket Assign, Answer, item/content edits | Ticket transaction triggers → `notification_routing` | Original delegation; explicit request vs attention. No recipient, released/history, closed/done, unsupported recipient and self-notification are recorded separately. | Assignment reservations, pinned requests, authorized targets and ACKs; common recovery; `assignment_delivery_tests.rs` and `server_assignment_delivery_tests.rs`. | +| Ticket comments/post edits, tags, case, tracking, release | Same capture and routing ledger | They do not become new Orchestrator requests. Applicable implementation-Worker attention is distinct from new delegation. | Same evidence; `notification_routing_tests.rs` covers positive and negative routes. Unknown source kinds defer explicitly. | +| MR review requested/result/revoked/cancelled/merge and observed source changes | MR transaction/source-observation triggers → common routing | Ticket delegation and implementation recipient captured at the event. MR remains the freshness/approval authority. | Stable MR event identity; current MR must be reread before action. Ordinary review comments are not inferred handoffs. `implementation_result_tests.rs` and `server_implementation_result_tests.rs` cover capture, self-suppression, recovery and public diagnostics. | +| Explicit implementation result/handoff/blocker | Authenticated `TicketImplementationResult` transaction | Implementation assignment and its pinned Orchestrator delegation, with exact-replay identity. No substitution of AssignmentAnswer or self-reported handling. | Result and source persist together; same common delivery/receipt path. No MR is required. | +| Backend Job accepted result | Result transaction → `notification_routing::jobs` | Fixed recipient, never replacement by the current Orchestrator. No target, checker no-findings and stale content suppress without ACK. Domain validation/rendering remains in the adapter. | Common request/ACK/failure ledger, fixed bytes/recipient, startup and periodic recovery; `job_notification_tests.rs` and `server_job_notification_tests.rs`. | +| Explicit WorkerNotify/WorkerSendInput | Authenticated caller | Explicit communication, not automatic business routing. | Existing Worker control boundary; not inferred from Ticket activity. | +| Internal SubWorker → parent / Web subscriptions | Parent-owned execution / UI synchronization | Separate ownership and authorization. | Not migrated into business-notification routing. | + +The capture adapters and the central event-kind mapping must be updated together. +The tests intentionally exercise unknown kinds and missing source capture: neither +is represented as successful delivery or intentional suppression. Adding a producer +requires a positive persisted-receipt test and a negative/suppression test, not merely +an assertion that a send function ran. `notification_registration_tests.rs` checks +public MR event variants against the central mapping; comments are explicitly excluded. + +MR source changes are captured when an existing Backend MR operation observes the +provider ref, not by an external Git-push listener. The initial source observation +is saved atomically with MR creation. `mr_open_retains_source_before_first_review_and_observed_result` +uses real Git publication and signed HTTP to cover the opening baseline, a later +source change before review, and result reporting against observation-only evidence. + +Schema 96 migrates Job notifications; schema 97 installs MR/implementation-result +capture. Legacy Job ACKs and suppressions remain distinct. Ambiguous legacy +implementation ownership remains unknown; comments never synthesize a result. +Preparation failures remain retryable and visible after recovery; durable fair +scheduling prevents repeated failures from starving healthy or already-pinned work. + +## Public diagnostic read + +- HTTP: `GET /api/w/{workspace_id}/notification-diagnostics` +- Normal Tool: `NotificationDiagnostics` +- WIP: `notification_diagnostics` on the Ticket collection +- Shared DTOs: `crates/server-api/src/notification.rs` + +Filters include Ticket, MR, Job, source event, assignment, delivery and exact source +identity. Pages contain at most 100 items. Continue with `next_cursor` and unchanged +filters. Cursors are validated against the Workspace and filters; they are not global +authority. A read uses a consistent database transaction and does not route, retry, +start/restore Workers or modify evidence. + +`source_checks` reconciles requested origin references independently of pagination +and recipient filters. A present origin with no captured source is **unconnected**, +not suppressed. An MR-wide check reports whether *any* source was captured; it is not +proof of complete capture. Use the exact `mr-event::` reference to +check a particular event, including a later event after earlier successful capture. +Unsupported origin-reference forms report unknown presence, not absence. + +The projection distinguishes awaiting routing, suppression, deferral, historical +recipients, pending/reserved/persisted requests, rejection, failures, uncertain +acceptance and durable Runtime acceptance. Decision/reason remain separate from +current delivery evidence and the last failure. Historical rows are not pending +work. `retry_candidate` is saved evidence, not authorization to send. + +`acceptance_outcome_unknown` is conservative: a prepared request without a durable +ACK is sufficient, and does not prove a network attempt occurred. Later preflight +failure cannot disprove earlier acceptance. Multiple authorized assignment targets +without an ACK are reported as ambiguous rather than attributed to the original +Worker; an ACK identifies the accepted target. Old Job receipts are marked with +`legacy_evidence`, retaining the distinction between accepted and suppressed legacy +Completed records. No modern ACK is fabricated from suppression. + +The response contains public references and bounded reason codes, not notification +body text, binding credentials, capabilities, or private session content. Runtime +acceptance is not model comprehension, successful handling, or permission to act; +`model_processing_confirmed` remains false without such evidence. + +## Verification boundaries + +Store fixtures use actual SQLite transactions, migrations, reopen, concurrent scans +and immutable records. HTTP tests use the authenticated production router. The +assignment and Job integration fixtures use the production Worker controller and +persistent session, substituting fixed model output and the deliberate response-loss +boundary. They verify request identity/bytes, one retained session notification, +restart recovery and agreement with public diagnostics. They do not establish real +model decision quality or unbounded exactly-once execution after Runtime receipt +retention expires. + +Useful focused checks: + +```sh +cargo test -p yoi-workspace-server --lib notification_diagnostics +cargo test -p yoi-workspace-server --lib job_notification +cargo test -p yoi-workspace-server --lib implementation_result +cargo test -p yoi-workspace-server --lib mr_result_recovery_and_handoff +cargo test -p yoi-workspace-server --lib notification_registration +cargo test -p yoi-workspace-server --lib assignment_real_worker_response_loss +cargo test -p worker --lib feature::builtin::ticket::notification_diagnostics +cargo test -p worker-runtime --lib worker_source::tests:: +cargo test -p server-api +``` + +OpenAPI and the affected browser DTO artifacts are generated from the same public +contract. WIP and normal Tool paths share the typed operation and Profile permission +check; there is no separate WIP notification policy.