FDAI Console Conversations
How a human operator talks back to FDAI through CLI, Teams, Slack, and web chat. This FDAI Console capability owns the conversational surface, not a separate product: layered architecture, tool catalog, LLM tiers, session persistence, per-tool RBAC, safety invariants, and rollout status.
Push-direction notifications (system → human) live in channels-and-notifications.md; operational views and requests are defined in console-operations.md, and the SPA lives under project-structure.md § console/; evidence provenance, stream recovery, localization, and Architecture-map resilience are owned by console-evidence-and-resilience.md. Login bootstrap derives assigned-principal access from verified App Roles without requiring the optional access-request projection; unassigned access remains closed when that projection is unavailable. In local development, an independent service adapter may use Azure CLI only for model narration; it has no provider-read or execution authority. Ontology presents a reviewed Semantic model and Catalog topology from one exact-release registry projection. Runtime instances appear only in a separate, purpose-scoped Context snapshot backed by a secured receipt.
Settings > Integrations can preview the production incident-open email renderer with synthetic placeholders. That GET-only preview does not send mail or grant approval or execution authority.
For optional Console projections, typed 404, 501, and source-gate 503 responses render as unavailable. Authentication failures, unexpected transport or 500 responses, and decoder failures remain visible errors.
Catalog topology preserves its deterministic exact-release coordinates while using one bounded 900 ms spring-settle on initial entry. Interaction ends the effect, reduced-motion preference skips it, and no persistent simulation runs.
Agent Activity links a correlation to Trace only when the row is backed by durable audit evidence.
Inventory scan, ontology projection, and current-state read correlations remain visible identifiers without an audit-trace link. A manual lookup with no matching audit steps renders a neutral unavailable state instead of an operational failure.
The authenticated active-incident stream can open an idle Command Deck with an incident selector. That selector is a presentation hint only; the server re-resolves the durable incident and its evidence before answering.
When the tab and Deck are idle, the first browser observation of an incident submits one localized read-only investigation turn. A browser-local incident ledger suppresses replay after reload; the incident badge remains an explicit way to investigate again.
When an incident question matches several records equally, the terminal answer includes bounded candidate buttons rather than relying on a plain-text instruction. A button opens the candidate’s exact incident conversation and immediately submits the localized read-only investigation turn.
The click is the operator’s explicit request; an automatic active-incident stream open never submits a managed-resource action.
This doc covers the pull direction - the operator asks, simulates, approves - across every channel the notification doc already ships adapters for. Push and pull share the same channel credentials
and the same audit contract, but they are distinct integration surfaces.
Customer-agnostic: every channel id, LLM deployment name, resource id, and group name below is a placeholder. A fork supplies concrete values via config (generic-scope.instructions.md).
Implementation status
Section titled “Implementation status”Implementation scope
Section titled “Implementation scope”| Area | State | Evidence | Notes |
|---|---|---|---|
| Durable/live current-state activity identity | implemented | read_investigation_latency.py; fdai_operator_service/activity_projection.py; focused persistence and projection tests (6 passed) | Snapshot replay and live frames converge on one hashed-correlation activity id without persisting the operator question, resource identity, or execution authority. |
| Optional Console projection availability | implemented | console/src/routes; focused route tests (64 passed) and npm --prefix console run typecheck | Typed optional-source absence renders as unavailable; authentication, unexpected server, and decoder failures remain errors. |
| Agent Activity durable projection resilience | implemented | fdai_operator_service/postgres_sql.py; fdai_operator_service/activity_projection.py; focused Operator projection tests (25 passed), Console source and localization tests (8 passed), typecheck, and catalog parity | The PostgreSQL query escapes its literal wildcard, and an out-of-contract optional duration becomes null with duration_out_of_range instead of aborting otherwise valid activity. No governed browser artifact was retained, so this row does not claim runtime validation. |
| Audit Trace navigation eligibility | implemented | agent-activity-log-model.ts; agent-live-activity.tsx; rule-trace.tsx; focused Console tests (26 passed) and typecheck | Only audit-backed rows link to Trace. Expected 404, 501, and source-gate 503 responses render as unavailable, while unexpected failures remain errors. The Browser Entra behavior was observed but no governed artifact was retained. |
| Inventory provider-execution boundary | implemented | discovery_receipts.py; inventory-execution-display.ts; focused Azure delivery tests (18 passed), parser tests (11 passed), and Console typecheck | New server receipts derive placeholder-only commands from registered plans. The Console independently rejects shell controls, redirects, environment assignments, executable shell words, live GUIDs, raw ARM ids, credentials, continuation tokens, and provider errors. |
| Channel-neutral terminal response parity | implemented | Operator Console Progressive Conversations; test_rich_contract.py; focused channel contract tests (36 passed) | Teams and Slack preserve one canonical terminal answer, limitations, evidence references, no execution authority, and the final confirmed update through durable replay. Production A3 publishers and governed runtime receipts remain open. |
| Read-only Conversation Search interactions | implemented | conversation-search.tsx; conversation-search.model.ts; conversation-search.test.ts; focused route tests (5 passed) and Console typecheck | Bounded filters, safe text highlights, exact context toggling, empty and unavailable states, and fail-closed decoder errors add no approval or execution authority. |
| Incident conversation RCA presentation | implemented | incident_queries.py; semantic_turn_processor.py; semantic_turn_presentation.py; presentation-artifact.ts; focused Core, Operator, and Console checks (138 passed) | A recorded cause requires a grounded hypothesis with matching citations or an allowlisted deterministic terminal failure grounded on its exact audit row. T0 records bounded impact evidence, and both locales render root cause, impact, and citations without execution authority. Missing evidence stays explicit. |
| Structured incident evidence readability | implemented | structured-reply.tsx; presentation-value.ts; structured-reply.css; focused Console checks (19 passed) and typecheck | The Console preserves exact RFC 3339 values in semantic markup while rendering explicit operator-local time, elapsed span, readable actor and machine-token labels, responsive evidence rows, scoped chat connectivity, and 44 px mobile controls. Five browser critique rounds left only Low residual horizontal scrolling for the bounded mobile source list; no governed browser artifact was retained. |
| Command Deck workspace visual hierarchy | implemented | console/src/styles.css; retrieval-trace.tsx; structured-reply.css; focused Command Deck visual checks and typecheck | Workspace answers and structured evidence share one 760 px reading measure, long values wrap within their cells, long-table headers remain visible on desktop, and the existing labeled-row reflow remains active at narrow widths. Preparing answer separates every observed stage label from its detail and opens a bounded three-source window with source kind, title, and detail. Authenticated desktop and 390 px browser canaries found no horizontal body, transcript, or answer overflow. No governed browser artifact was retained. |
Implementation history
Section titled “Implementation history”| Date | State | Change | Evidence | Remaining |
|---|---|---|---|---|
| 2026-08-13 | implemented | Adopted the implementation ledger without reconstructing earlier provenance and recorded the current-state activity identity contract. | Current source plus test_read_investigation_latency.py and test_activity_projection.py; the focused suites passed. | Record governed cross-service parity evidence for snapshot-first hydration and live convergence. |
| 2026-08-13 | implemented | Standardized optional projection absence across production Console routes without hiding unexpected failures. | Current change; focused route tests passed (64 passed) and Console typecheck passed. | Keep Browser Entra route assurance in the full-stack validation campaign. |
| 2026-08-14 | implemented | Restored the durable Agent Activity projection by escaping the psycopg wildcard, preserving valid rows with an out-of-range optional duration, and keeping the audit timeline available during typed optional-source absence. | current change; focused Operator tests passed (25 passed), focused Console tests passed (8 passed), and Console typecheck and catalog parity passed. | Retain a governed Browser Entra artifact for the existing snapshot-first and live-convergence exit criterion. |
| 2026-08-14 | implemented | Restricted Trace links to durable audit rows and presented an absent audit correlation as unavailable rather than failed. | current change; agent-live-activity.test.ts and rule-trace.test.ts passed 26 focused tests, Console typecheck passed, and the authenticated Browser Entra page showed a non-linked inventory correlation plus a neutral Trace unavailable state. | Retain the observed Browser Entra result as a governed artifact before claiming runtime validation. |
| 2026-08-14 | implemented | Added the server-owned provider-execution receipt producer and hardened the Console parser against executable or sensitive provider text. | current change; focused Azure delivery tests 8 passed, Console parser tests 6 passed, and Console typecheck passed. | Retain the read-only live canary receipts tracked by the Azure discovery owner document. |
| 2026-08-14 | implemented | Rejected environment assignments in both server-rendered and browser-accepted provider commands. | current change; focused Azure delivery tests 10 passed, Console parser tests 7 passed, and Console typecheck passed. | Retain the read-only live canary receipts tracked by the Azure discovery owner document. |
| 2026-08-14 | implemented | Preserved registered placeholders while rejecting redirects, control characters, and executable shell words in both server and browser provider-command validators. | current change; focused Azure delivery tests 18 passed, Console parser tests 11 passed, and Console typecheck passed. | Retain the read-only live canary receipts tracked by the Azure discovery owner document. |
| 2026-08-14 | implemented | Restored localized unavailable copy for the capability directory and removed persistent top and left edge accents from legacy Console design mocks. | current change; mock-visual-boundary.test.ts and catalog-usage.test.ts passed 6 focused tests, and catalog parity passed. | Keep the visual-boundary and catalog-usage checks in the operator-surface CI lane. |
| 2026-08-14 | implemented | Verified the shared terminal response reducer explicitly for Teams and Slack without adding vendor-specific authority. | current change; test_rich_contract.py passed 36 focused cases, with detailed ownership retained in Operator Console Progressive Conversations. | Implement and exercise production A3 publishers before claiming channel runtime validation. |
| 2026-08-14 | implemented | Added a bounded deterministic spring-settle to the exact-release Catalog topology without changing stored coordinates or leaving a persistent simulation. | current change; focused topology tests report 12 passed, Console typecheck passed, and the authenticated standard-port browser rendered a nonblank canvas without overflow. | Retain the separate authenticated Context snapshot evidence tracked by the wire-contract owner. |
| 2026-08-14 | implemented | Added focused Conversation Search route interactions while preserving the Console as a read-only projection surface. | current change; route model and focused tests 5 passed; Console typecheck. | Retain governed cross-surface search evidence under the Conversation Search owner. |
| 2026-08-17 | implemented | Preserved recorded grounded RCA, bounded T0 impact evidence, and matching citations across an incident-bound semantic answer and the strict Console artifact decoder. The previous hardcoded “causal analysis hasn’t been implemented” limitation is retained only for legacy replay and no longer describes current results. | current change; focused Core, Operator, and Console checks passed 138 cases; Ruff, strict mypy, and Console typecheck passed. | Retain a governed Browser artifact for a newly recorded RCA with all three evidence sections. |
| 2026-08-17 | implemented | Projected the active incident’s recorded route_unresolved notification failure as a deterministic T0 cause, route-outcome impact row, and exact audit citation. The read path changes no audit or notification state and rejects successful or unknown outcomes as causes. | current change; focused Core and Operator checks passed 133 cases; Ruff and strict mypy passed. | Restart Core and verify the three evidence blocks in the authenticated Browser. |
| 2026-08-17 | implemented | Hardened structured incident evidence for operator reading without changing the verified artifact or machine records: explicit local time and timezone, preserved raw timestamps, elapsed span, readable actors and tokens, responsive native tables, scoped readiness copy, non-overlapping latest-message navigation, and 32/44 px controls. | current change; presentation-value.test.ts and command-deck-workspace-visual.test.ts passed 19 focused cases; Console typecheck passed; five authenticated browser critique rounds ended with 12 of 12 final desktop/mobile checks passing and only Low residual source-list scrolling. | Retain the broader governed incident presentation artifact already tracked below; no Medium-or-higher implementation detected issue remains in this readability slice. |
| 2026-08-17 | implemented | Refined the live ontology assurance oracle so each fixed question identity declares its valid typed capability family instead of inheriting one broad operation-level capability. The oracle still rejects answered plans that omit the relationship, causal, temporal, filter, or evidence capability the exact question requires. | current change; ontology-query-assurance.test.ts passed 99 cases and Console typecheck passed. | Retain a passing strict artifact before running another full seeded cohort. |
| 2026-08-17 | implemented | Corrected the prompt-specific oracle so evidence-validation questions continue to require Core’s server-owned secured ObjectSet plan. A topology or metric capability can supply evidence to another answer shape, but it doesn’t replace the evidence-validation contract. | current change; ontology-query-assurance.test.ts passed 99 cases and Console typecheck passed. | Retain a passing strict artifact before running another full seeded cohort. |
| 2026-08-18 | implemented | Aligned the production Command Deck with the reviewed mock’s calm transcript hierarchy without hiding verification, evidence, or observed work. One reading measure now owns answers and structured tables, opaque values wrap in place, long desktop tables retain their headers, and the composer keeps a narrower command weight. | current change; command-deck-workspace-visual.test.ts, investigation-timeline.test.ts, and conversation-trajectory-visual.test.ts passed 29 focused cases; Console typecheck passed; ten isolated authenticated questions covered prose, lists, unavailable or clarification states, one-row tables, and 20-row English and Korean tables with zero horizontal overflow at desktop and 390 px widths. | Browser observations were not retained as a governed artifact. Keep the existing bounded mobile source-strip scrolling as a Low residual and retain broader governed conversation evidence separately. |
| 2026-08-18 | implemented | Made Preparing answer show the observed content behind each stage instead of compressing labels and values into one row. Screen context includes the route, headline, and visible fact count; routing keeps its recorded reason; backend progress keeps check and source counts; and source previews show kind, title, and bounded detail in an open three-card window. | current change; retrieval-trace.tsx, styles.css, localized Console catalogs, and the focused investigation-timeline.test.ts; authenticated desktop and 390 px checks showed open details, stage-specific text, at most three source cards in the DOM, and zero horizontal overflow. | Browser observations were not retained as a governed artifact. Later server stage events can enrich the same typed progress seam without browser inference. |
Remaining work
Section titled “Remaining work”- Record a governed cross-service receipt showing that snapshot-first
GET /agents/activityhydration and a newer live frame converge on the same current-state activity id without duplicate Console rows. - Retain a governed Browser Entra artifact showing that a non-audit operational correlation has no Trace link and that a manual absent-audit lookup renders a neutral unavailable state.
- Retain a governed Browser Entra artifact showing root cause, impact evidence, and grounded citations from a newly recorded RCA incident; historical incidents without impact rows must remain explicitly incomplete.
- Keep structured incident evidence readable at desktop and mobile widths while preserving exact machine values; focused tests, typecheck, and five browser critique rounds pass with no Medium-or-higher residual detected issue.
- Keep Command Deck answers, detailed preparation stages, structured evidence, and the composer on one bounded reading hierarchy; focused tests, typecheck, and desktop/mobile browser canaries pass with no horizontal overflow.
1. Framing - what this is (and what it is not)
Section titled “1. Framing - what this is (and what it is not)”The FDAI Console conversation surface does not carry judgment authority. FDAI’s judgment stays where it already is - the deterministic engine (T0), the quality gate (T2 verifier), the safety check, and the shipped Rego policies. The console is the conversational surface through which an operator inspects that judgment, simulates change, and approves what the system has already queued.
Three properties follow directly:
- LLM is a translator, not a judge. Natural language becomes tool calls and tool results become natural language; execution eligibility comes only from the verifier (architecture.instructions.md § Design Principles).
- Tools expose pipeline stages, not primitive data sources. The console exposes
describe_event(),explain_verdict(), andsimulate_change()instead of primitive log, metric, and config queries. The system has already reasoned; the operator asks about the result. - Growth is catalog growth, not model memory growth. Recurring investigation patterns become
new rule candidates through the discovery loop
(architecture.instructions.md § Rule Catalog) -
not opaque session memory. Persistent conversation state stays in auditable, exportable,
CSP-neutral
audit_logandoperator_memoryrecords.
Completed answers also enter the off-path Conversation Assurance loop. JSON and SSE adapters share the typed conversation-turn service and extracted request setup, evidence, progress, verification, and terminal-delivery helpers while preserving their existing wire contracts.
Terminal intake preserves the exact verification reason and evidence-manifest completeness. Outcome summaries, context selection, Azure investigations, durable delivery, and attachment evidence remain owned by their typed providers; adapter modules only coordinate presentation and persistence.
The version 1.2 semantic projection preserves this boundary across the service split: answered requires exact release, principal manifest, plan, execution receipt, and evidence references; unavailable dependencies return a typed limitation.
Operator-owned Kafka adapters publish semantic proposals, consume semantic projections, and relay validated Core stage and Pantheon runtime-state frames into separate bounded /live/stream and /agents/stream SSE hubs. Authenticated GET /agents/activity projects bounded inventory scan, ontology projection, and current-state read history from durable sources before the Console applies newer stream frames; current-state replay and live frames share the same hashed-correlation activity id. These observation routes use the same bearer gate as snapshot reads, stay connected with keepalives when Kafka is absent, and report Awaiting source until an authoritative frame arrives; GET /chat/health reads the semantic bridge’s process-owned worker readiness directly instead of requiring a durable conversation projection row.
Terraform pins the request and projection topics, while Core renders verified query tables and Operator maps durable results to the existing done event.
Injected providers take precedence and the local narrator is exclusive.
The Operator API never marks a review ready, creates a catalog proposal, or grants authority. Reporting an incorrect answer adds evidence for autonomous re-evaluation, and every governed transition still requires exact replay evidence plus the existing catalog lifecycle.
1.1 Vocabulary added to the shared glossary
Section titled “1.1 Vocabulary added to the shared glossary”The following tokens are added to the shared vocabulary in architecture.instructions.md and are used consistently by every referring doc:
- operator-console - the legacy contract token for the conversation capability documented here; the product display name remains FDAI Console.
- narrator - the LLM tier of the operator console (translator role; never a judge). Distinct from the T2 quality-gate role, which is a domain reasoner over a proposed action.
- operator-conversation - one bounded exchange between an operator and the console (multi-turn, RBAC-scoped, audited).
- console-tool - one exposed pipeline stage or catalog view the narrator may call.
Explanatory questions about T2 execution-eligibility checks or insufficient-evidence handling use the canonical glossary before action-context or intent-graph tools. This precedence applies only to concept explanations. A question with an exact action, approval, correlation, or idempotency selector continues to require server-owned action lifecycle evidence.
2. Three-layer architecture
Section titled “2. Three-layer architecture”flowchart TD
subgraph L3["Layer 3 - Channel (thin adapter)"]
CLI["CLI REPL"]
TEAMS_PULL["Teams (pull)"]
SLACK_PULL["Slack (pull)"]
WEB["Web chat (Console SPA)"]
end
subgraph L2["Layer 2 - Conversation Coordinator"]
NARR["Narrator (LLM)\nT1 translation default\nT2 translation escalation"]
INTENT["Intent classify\n(read | simulate | approve | breakglass)"]
RBAC["RBAC gate\n(per-tool role floor)"]
VERIF["Verifier re-check\n(no auto-execute)"]
SESS["Session state\n(audit-log-backed)"]
end
subgraph L1["Layer 1 - Existing deterministic core (unchanged)"]
CL["ControlLoop"]
RULES["RuleIndex / T0Engine"]
QG["QualityGate"]
EXEC["ShadowExecutor / RiskGate"]
INV["Inventory / StateStore"]
end
CLI --> INTENT
TEAMS_PULL --> INTENT
SLACK_PULL --> INTENT
WEB --> INTENT
INTENT --> RBAC --> NARR --> VERIF --> SESS
NARR -.tool call.-> CL
NARR -.tool call.-> RULES
NARR -.tool call.-> QG
NARR -.tool call.-> EXEC
NARR -.tool call.-> INV
-
Layer 3 (Channel) is thin. Every adapter converts one turn between its wire format and a
ConversationTurn; no judgment lives here. A streamed read sends SSE comment heartbeats while the provider task is idle, without progress or evidence. Stream close cancels and awaits that task. Web, Slack, and Teams render the same ordered agent-activity contract: Bragi shows the handoff, and the accountable observer shows canonical command/result evidence. An agent selected by an agent conversation target or incident binding, or addressed withAsk <agent>or@<agent>, remains the response owner. An agent-card Ask opens with a compact projected-state line list; its longer fixed context stays non-rendered for backend history, and the visible report streams in bounded two-word bursts. Web Investigation animates only received branch frames with elapsed time, typed badges, and staggered status rows. A terminal investigation keeps its session header and observed steps visible beside the final answer; only redacted command output and timestamps remain in disclosures. A source branch linked to an observed execution step is represented once on that step rather than repeated as a separate row. Full workspace reserves one 760 px reading measure for the desktop answer and structured evidence, keeps long-table headers visible while rows scroll, and uses the full mobile viewport with labeled-row reflow and no horizontal overflow. Phase markers, a 15 px conversation scale, and one dark command/code surface keep the production hierarchy aligned with the execution mock. The browser never replays work or invents progress. Observed activity distinguishes actual process commands from canonical server queries with a requiredinput_kindcontract. Inventory, subscription-health, and read-investigation activity usesquery: it renders the verifier-accepted typed query, authority, snapshot provenance, and bounded result projection without inventing Azure CLI argv or an exit code. Web labels a validated inventory query asIQL; source and result disclosures each close. A strict bounded execution receipt uses one terminal icon for Azure CLI and ARG commands. New receipts derive placeholder-only argv from a registered plan and show page count, result count, and at most ten allowlisted preview rows while omitting live subscription ids, raw resource ids, credentials, pagination tokens, and provider errors. Rows identify snapshot-refresh work while IQL filters that snapshot. The browser never reconstructs a command from IQL; a missing or invalid receipt produces no provider row. Other server queries retainQUERY, and only a provider receipt that records a process invocation usescommand. Slack, Teams, and durable replay preserve the query/command distinction. A narrator milestone settles the preceding activity group before the next group begins. Web shows the milestone as a compact progress note, opens only the current group, and restores completed groups in causal order. Slack and Teams edit the same cumulative redacted activity projection. When a request carries both a plain agent target and an incident binding, the two structured agent values must match; conflict is rejected before evidence retrieval. For model-backed answers, global read-only safety stays first and the selected immutable charter follows only on an exactconversation_policymatch. A dedicated target session assigns that verified agent’s voice across follow-up turns and renders self-role questions deterministically from content-addressed capability facts; ordinary screen delegation keeps Bragi as narrator. A policy mismatch or explicit handoff returns narration to Bragi, and the charter never becomes evidence, authority, or tool permission. The injected charter is composed for the turn: the immutable baseline plus the operator-locale layer when the answer locale is not English. Agent evidence also carries the layer manifest and digest of the prompt that governed the agent’s own turn, so a spent escalation budget or evidence gap is stated as a constraint rather than left invisible. Bragi becomes the response owner only after that agent holds for review and hands the turn back. Vendor adapters change presentation only. Slack uses plain-text activity blocks for query, command, and output bodies so markup characters cannot change the observed input, and preserves those blocks across posts, stream updates, and edits. Teams keeps the Adaptive Card under 24,000 bytes, counts omitted activities, and always retains the final accountable-agent answer. Renderers distinguish producer-side partial evidence with[UPSTREAM OUTPUT TRUNCATED]from vendor-limit clipping with[CHANNEL OUTPUT TRUNCATED]. Full-workspace web chat opens transcript-first. Conversation history and the current-screen digest are toolbar panels rather than permanent columns. The Deck header shows the active route; the Digest toggle and header own record count, snapshot age, and stale refresh. The composer keeps only attachments, question entry, and send or stop. Sent images render inside the operator turn, and validated image attachments bypass prompt-only semantic tool planning and omitted-subject LLM-usage refinement so the current image reaches vision narration. Terminal verification preserves the interpretation as unverified with a currentconversation-imageref instead of treating it as screen-verified. Explicit measured LLM usage remains a deterministic tool request. Browser transcript caches retain only image descriptors, while authenticated history reads load bytes from the principal-scoped conversation image repository. A restored transcript shows its last recorded time and a new-conversation action. Tables render every bounded row without internal scrolling or expansion controls; cell-level<br>variants become safe line breaks while other raw HTML remains text. On narrow screens, cells reflow while preserving native table semantics. -
Layer 2 (Coordinator) owns intent classification, RBAC gating, tool dispatch, verifier re-check, and session bookkeeping. Core translation uses the
NarratorProtocol. A narrator that also implementsGroundedAnswerNarratorreceives a completed successfulToolResultin a second presentation-only pass. The coordinator retains the original tool-result turn, accepts no new tool call, and falls back to the deterministic preview when rendering fails, exceeds the response bound, or omits anevidence_ref. Its system prompt is assembled deterministically fromAnswerPlan, tool side-effect class, evidence-reference count, and the presence of prior conversation context. The current inbound/tool/result transaction is excluded from that prior context. Web generation uses the Operator API backend seam, so deployments can bind providers.AnswerPlan.formatkeepstable,chart, andmixedas presentation preferences. An explicit request format or saved response preference wins only when the verified result can support that shape without changing meaning. After eligible read evidence resolves, a bounded structured model call may arrange aPresentationPlanfrom server-declared slots. The model receives only shape metadata, allowed slot-component pairs, coverage classes, and the operator request. It never receives row values and cannot emit titles, facts, units, thresholds, status, severity, colors, links, or evidence references. The plan can choose slot order, one allowlisted component, emphasis, and whether supporting detail starts collapsed. It cannot repeat or omit a required slot.AnswerPlan.formatcontinues to own the canonical Markdown text fallback, whilePresentationPlanowns only the Console artifact layout. Presentation planning never rewrites the canonical text format. An explicit format or saved preference skips the artifact and keeps the established table, chart, list, or prose renderer.The server validates the plan, then compiles immutable evidence into a bounded
presentation_artifactv1. The compiler enforces compatible units and threshold directions for charts, keeps partial or truncated coverage visible, and binds every block reference to the terminal verification receipt. A partial source never removes completed slots: the answer renders every available verified fact and marks only the missing portion as unknown or unavailable. A streamed evidence-fast-path turn starts with the complete deterministic plan and streams the canonical answer immediately while the optional mini-model planner runs concurrently. After the answer is visible, the terminal event waits at most five seconds for a valid alternative layout; timeout, cancellation, invalid output, or provider failure keeps the deterministic plan. The non-stream JSON route uses the deterministic plan directly and never delays an evidence answer for presentation planning. Presentation planning can return no artifact only when no relevant verified slot exists. Model, schema, timeout, or compiler failure uses the deterministic answer and default layout, so the operator still receives the maximum evidence-supported answer. Existing Markdown table, fenced chart, bullet, and prose output remains the compatibility contract for other channels and older clients. The semantic turn planner projects only the bounded capabilities for that request into a strict structured-output schema. Every object rejects additional properties and marks its declared fields required. A tool’s optional arguments are represented as nullable fields, and the coordinator removes null placeholders before deterministic selection validation and dispatch. After rendering, a core validator rejects numeric values, percentages, RFC3339 timestamps, and canonical rule, event, incident, correlation, or ActionType identifiers that do not occur in the immutableToolResult. Freshness words such ascurrent,live, orlatestrequire an exact timestamp from that result. Markdown list ordinals, ordinary resource aliases, and numbers embedded in identifiers are excluded from this conservative check to avoid treating formatting as a claim. When intent translation remains ambiguous, an optionalClarificationNarratorsees only the installed tool schemas visible to the principal and may return one bounded question. This path invokes no tool, guesses no argument, and falls back to the deterministic hold for review response when the provider fails or the response is not one question. An optionalContextualNarratorcan translate a single-tool follow-up from bounded prior turns. Prior text is escaped as untrusted data, and every parsed scalar argument must occur in the current utterance or those prior turns after Unicode and separator normalization. Missing or invented arguments discard the translation before tool lookup and execution. Adapters that do not implement this protocol retain the original context-freeNarrator.translatebehavior. For a compound request that misses direct T0 matching, an optionalReadPlanNarratormay propose two or three canonical commands. The coordinator reparses every command with its own grammar and validates the complete plan for installed-tool membership, RBAC, distinct commands, andside_effect_class=readbefore the first call. Invalid plans execute nothing. Valid reads run serially, retain one tool-call/result pair per step, aggregate evidence references, and use the same grounded presentation pass. A failed or unavailable read stops the remaining plan, skips synthesis, and returns a deterministic unverified hold instead of empty-screen or narrator output. Before synthesis, the aggregator compares high-signalstate,status,verdict,mode,health, andoutcomefields only when two tools name the sameresource_id,scope_ref, orid. Different values produce a structured conflict, preserve both evidence sets, change the aggregate toabstain, and skip model rendering. Different identities are not compared. Local and deployed interactive reads use one core-owned mode policy, so the same latency profile selects the same direct, streamed, or detached mode. -
Layer 1 (Core) is exactly the deterministic core that already ships. The console adds no new judgment path, no new persistence store, and no new execution vector. A console tool call resolves to a call the existing pipeline already knows how to make.
2.1 Module map
Section titled “2.1 Module map”The source inventory and boundaries are owned by Operator Console Module Map and Boundaries.
3. Tool catalog
Section titled “3. Tool catalog”Tools are pipeline-stage views. A core tool has a stable name, bounded argument_hint, RBAC
floor, side-effect class, and documented failure surface. Web/provider-specific tools can add
their own typed request contracts. New tools are additive; they never override a rule or policy.
RuntimeToolDiscovery provides search and describe over installed narrator schemas. It intersects schema metadata with the actually installed tool names, applies the same RBAC ladder as the coordinator, and returns only name, verb, description, argument hint, RBAC floor, and side-effect class. A lower-role principal cannot discover a higher-role tool, and descriptors contain no handler or invocation capability. If an explicit request still resolves to a tool above the principal’s role, the deterministic refusal names the tool, required role, and current role and confirms that no tool was called. Discovery improves navigation; it grants no new authority.
The same projection is available through the deterministic channel verbs search_tools and
describe_tool, and typed read RPC methods tools.search and tools.describe. Channel calls use
the resolved Principal; RPC calls derive the role from server-authorized scopes, never from a
caller-supplied role parameter. Both surfaces return descriptors only and cannot invoke the target.
3.1 Day-1 tool set (read-only + explain)
Section titled “3.1 Day-1 tool set (read-only + explain)”| Tool | Purpose | RBAC floor | Delegates to |
|---|---|---|---|
describe_event(payload) | Run one event through EventIngest → TrustRouter → T0Engine in memory (no PR, no audit write); return the resulting routing decision + candidate rule ids. | Reader | EventIngest, TrustRouter, T0Engine |
explain_verdict(event_id) | Read the audit trail for one already-processed event; return the tier, decision, citing rule ids, verifier report, mode. | Reader | StateStore.query_audit() |
explore_catalog(query) | Search the shipped rule catalog / action-type catalog / ontology vocabulary by id, keyword, or resource_type. | Reader | Loaded catalogs (no I/O) |
query_audit(filters) | Structured audit query: by event id, actor, decision, mode, time window. Paginated. | Reader | StateStore.query_audit() |
query_llm_usage(group_by, lookback_days, usage_scope) | Read measured LLM token usage by day, model, workload scope, or mode for a bounded 1-90 day window. The independent Operator Service serves the same token-only projection from llm_invocation through a SELECT-only runtime role; record and conversation ledgers are capped at 500 while aggregate counts remain exact. The tool can narrow to operator-chat records, never estimates money without measured pricing evidence, and returns deterministic prose, table, or chart output. LlmCostPanel declares this tool through conversation_tool; a chat-enabled composition fails startup if the declared capability is absent. | Reader | MeteringReader |
query_inventory(resource_type, filter) | Server-owned Azure inventory count, list, type, location, resource-group, name, status, and relationship queries. The schema-validated inventory-query-language.yaml catalog owns natural-language terms, state and operation semantics, evidence authority, grouping, projection, scope defaults, and freshness requirements; Python performs generic token matching and typed query assembly without question-specific aliases. Resource types come from the separate canonical resource-type catalog. The typed query owns scope, grouping, projection, workload intent, state-history intent, and freshness before evidence retrieval, so rendering never reinterprets the prompt. Unqualified cross-screen inventory reads use the server-owned subscription root; explicit current-view wording keeps the active architecture view. Current-state questions wait on the provider refresh barrier and return unavailable rather than confirming stale evidence. Degraded or unavailable resource questions are routed by catalog authority to query_subscription_health; a concrete resource-family filter remains attached and excludes detected issues from other types. Results expose bounded allowlisted fields, exact scope, snapshot source/freshness, and only records satisfying every predicate. Explicit semantic state groups remain separate and can show a grounded zero-result group. State-filtered answers also disclose that normalized current operational status does not prove the absence of deployment or Activity Log failures. The stream exposes the verifier-accepted canonical query_inventory operation with redacted server scope plus bounded result metadata. Provider failure renders unavailable. AKS results stay cluster-only unless a server-owned workload provider is explicitly bound to an inventory-matching cluster; a valid binding adds bounded Deployment and Pod readiness, while other matched clusters remain an explicit coverage gap. A current snapshot never establishes when a workload entered a state; the answer keeps that transition time unconfirmed until Kubernetes events or another history authority is connected. | Reader | InventoryGraphProvider, KubernetesWorkloadProvider |
query_subscription_scope() | Read the server-configured subscription display name and state from Azure Resource Manager for current-subscription identity questions. The deterministic answer masks the subscription ID, includes observation time, and never accepts caller-supplied scope. | Reader | SubscriptionScopeProvider |
query_subscription_health() | Inspect the server-configured Azure reader scope for explicit subscription checks, general service-outage questions, and catalog-selected degraded or unavailable resource collections. The provider defaults to the resource-group allowlist; an explicit composition-owned subscription mode aligns interactive local health with its subscription inventory. Query Resource Graph inventory and Resource Health, fall back to current Resource Health status for the configured scope when ARG is empty, then run bounded representative metric checks. Preserve requested state groups, including grounded empty groups. Normalize name, provider type, and resource group from a scope-validated Resource Health target when its display name is absent; don’t expose the raw target ID. Return detected issues, cause classification, coverage gaps, freshness, and truncation without allowing caller-supplied scope or mode. | Reader | SubscriptionHealthProvider |
query_detection_readiness() | Read Heimdall’s latest AKS readiness decisions from Muninn StateSnapshots, including six-axis coverage gaps and the authority ceiling. It does not probe Azure or recompute readiness. | Reader | DetectionReadinessReader |
query_t2_recovery() | Read sanitized proposer attempt receipts from the server StateStore. Return the retained attempt count, recovery state, route roles, failure class, observation time, and explicit legacy-detail gaps without exposing provider error text. | Reader | T2RecoveryStateReader |
query_configuration_baseline() | Read one server-configured frozen configuration baseline, its current scoped observation, and the exact integrity-pinned DOCX citation. The caller cannot select scope, version, digest, document, or a mutation operation. Missing structured topology remains unknown. | Reader | ConfigurationDriftService + KnowledgeSource |
capture_browser_evidence(policy_id, policy_version, source_url, stable_selectors) | Submit a credential-free bounded capture under an exact server-owned policy. Returns an immutable artifact receipt; never returns a page or interaction API. | Reader | BrowserEvidenceCaptureService |
| Matched inventory result sets are sorted before the 40-record bound is applied. Lists use resource | |||
| name order by default; an explicit status, type, or location grouping uses that grouping field and | |||
| then resource name. The same order drives rendered rows and durable ordinal follow-ups. | |||
Future VM shutdown questions use the catalog-owned scheduled_shutdown query kind and the | |||
compute.vm-shutdown-schedule resource type. The query pins one aware server reference time and a | |||
closed today_evening window. Provider adapters project only validated | |||
ComputeVmShutdownTask records and expose the target VM name and resource group, enabled state, | |||
| daily local time, and provider timezone without exposing the target ARM id. The deterministic | |||
| projection includes only enabled occurrences from 18:00 through 23:59 that have not passed in the | |||
| schedule timezone. Disabled schedules are not results. A truncated snapshot, missing production | |||
| coverage for the schedule type, malformed schedule, or unsupported timezone returns unavailable | |||
| instead of proving that no VM will shut down. | |||
| A concrete resource-type query with no complete lexical state match can use semantic retrieval only | |||
| to propose state or operation candidates. Model and embedding candidates never execute a provider | |||
| query in that turn. An exact or promoted catalog mapping, or a separately verified operator | |||
| confirmation receipt, is required before the server can produce a complete typed query. | |||
| If semantic planning is unavailable, ambiguous, or omits the required state, the server returns a | |||
| typed interpretation hold with the deterministic query skeleton. It does not execute that | |||
| type-only skeleton or drop the unresolved modifier to widen the result set. | |||
Negative state candidates use the bounded not_in operator over canonical catalog states. Provider | |||
| evidence check resolves excluded values against the same snapshot; negation never becomes an | |||
| unsupported positive-state guess. | |||
| Exact catalog terms remain a T0 latency optimization, not the only entry gate. When production has | |||
| the existing T1 embedding binding, the same credential path retrieves state and operation | |||
descriptions and examples. A retrieved concept remains candidate_only and causes a localized | |||
| clarification without querying inventory. If the embedder is absent or fails, the resolver returns | |||
| no candidate and the deterministic hold remains authoritative. The resolver rejects empty prompts, control characters, and text over 4,096 characters before building catalog vectors or calling the query embedder. | |||
FDAI_INVENTORY_SEMANTIC_ENABLED controls this clarification capability independently from | |||
FDAI_CATALOG_SEARCH_ENABLED; disabling Rule search does not silently disable inventory semantic | |||
| retrieval. | |||
| The clarification is not a dead end. A later operator turn that selects an exact promoted catalog | |||
| expression recompiles deterministically and can perform the provider read. The earlier model or | |||
| embedding arguments are never reused as query authority. | |||
| Intent-graph planning cannot override a complete deterministic inventory query. Planner-supplied | |||
| status concepts are still checked against the canonical catalog; invalid values are rejected, and | |||
| execution uses the deterministic query. A required semantic status that is omitted remains held. | |||
| An unfiltered summary still preserves every provider-observed resource, groups by provider-native type, and separates resource-group containers and topology-derived records from the resource total. | |||
The catalog-owned scope_counts query kind returns provider-native resource and resource-group | |||
| totals from one fresh snapshot without narrowing the query to resource groups. It retains the same | |||
| container, derived-record, truncation, freshness, and verification disclosures as type summaries. | |||
| Architecture publishes at most one selected resource in its bounded screen digest. A current-screen | |||
| service-summary question may use a selected resource-group name only as a selector hint; the server | |||
| inventory re-resolves that group and its members before returning canonical service-type counts. | |||
| Missing, malformed, or non-group selection does not create scope authority. | |||
| Selected-group detail requests use the same boundary. Named Architecture projections retain only | |||
| allowlisted location, resource-group, and provider-type fields after dropping raw properties. | |||
| Observed operational or power state takes precedence; provisioning state is the final displayed | |||
| state fallback. The deterministic list excludes the resource-group container itself and | |||
| topology-derived records that lack a provider type. | |||
| Inventory records preserve the displayed state’s provenance independently. The catalog-owned | |||
state_coverage result treats operational and power evidence as directly observed, while | |||
| provisioning-only and unknown evidence remain operationally unavailable. A selected-screen | |||
| continuation reuses only the bounded group selector and rechecks all records in server inventory. | |||
The catalog-owned inventory_coverage result reports checked provider types separately from | |||
| skipped and failed types. A complete atomic snapshot can prove zero skipped and zero failed types; | |||
| a truncated snapshot leaves skipped coverage unknown. Operational-state limitations remain a | |||
| separate coverage class and are never relabeled as inventory read failures. | |||
| Explicit subscription or platform-health intent has deterministic tool precedence over a semantic | |||
| agent or public-web plan. Resource Health cause classification separates platform impact from | |||
| customer-initiated state before narration. Broad platform-impact reads disable representative | |||
| metrics, query active Service Health events and impacted resources, separate outages from planned | |||
| maintenance and advisories, and enrich missing availability causes from bounded Resource Health | |||
| annotations. An unavailable or truncated Service Health or annotation query remains a partial | |||
| coverage gap and cannot prove zero platform impact. | |||
| Catalog-owned resource-health history intent has the same deterministic precedence. It caps a | |||
| parsed lookback at 24 hours, merges availability statuses and annotations chronologically, reports | |||
| customer-initiated, status-only, and platform-initiated counts, and never substitutes current ARM | |||
| status for missing historical evidence. | |||
| A complete history answer can return its latest verified event resource as the next-turn selector | |||
| hint. Attribution and history follow-ups must re-resolve that resource in server scope and collect | |||
| fresh Activity Log or Resource Health evidence; the hint never becomes evidence authority. | |||
| Catalog matching preserves Korean query terms when common case or conjunction particles, including | |||
와 and 과, are attached, so compound comparisons retain every requested semantic class. |
Reader-floor tools are provably side-effect-free. describe_event
runs EventIngest -> TrustRouter -> T0Engine in memory only: it does
not invoke T1 embedding lookups, T2 models, external adapters, or any
mutation surface, and it writes no PR and no audit entry. Its
side_effect_class is read, and a observation mode test asserts it never
touches the executor, the PR adapter, or the state store. This is what
keeps it safe at the Reader floor. Browser capture follows Browser evidence collection; Bragi never receives a browser handle.
3.2 Week-1 additions (write / approve / runbook)
Section titled “3.2 Week-1 additions (write / approve / runbook)”| Tool | Purpose | RBAC floor | Notes |
|---|---|---|---|
simulate_change(scenario) | End-to-end ControlLoop.process() in observation mode mode; return the executor outcome + generated PR intent without publishing. | Contributor | Observation mode-only; still writes an audit entry so the operator can find it in query_audit. |
approve_hil(approval_id, decision, justification) | Resolve one queued human approval item. Verifier + no_self_approval invariant re-checked. | Approver | Approver group; same principal as PR gate enforcement in security-and-identity.md. |
list_hil() | Return currently queued human approval items visible to the caller’s role. | Approver | Reader-visible would leak intent to non-approvers; kept Approver-scoped. |
run_runbook(name, params, dry_run) | Execute one runbook under docs/runbooks/. dry_run=true requires Contributor; dry_run=false requires Owner. | Contributor / Owner | Concrete runbook adapters (e.g. db_dr_drill_cli) are already shipped; this tool routes by name. |
activate_break_glass(reason, expiry) | Validate TTL/reason and create Owner-page and audit receipts. | Reader | The current implementation does not change the session principal/role or grant elevation. |
Two clarifications on the write set:
simulate_changewriting an audit entry does not violate “observation mode never mutates”. The audit log is append-only; recording that a simulation ran is not a mutation of any managed resource. The observation mode property test asserts no executor / PR / state-store write, and explicitly allows the audit append.list_hil(Approver) vs the read-console human approval view (Reader) are different surfaces. The read-only Console SPA shows Reader the existence and count of queued human approval items (dashboard tile);list_hilreturns the full item detail (target, proposed action, requester), which can reveal sensitive intent, so it stays Approver-scoped. The two are intentionally not the same visibility.
3.3 Month-1 additions (observation depth)
Section titled “3.3 Month-1 additions (observation depth)”| Tool | Purpose | RBAC floor | Depends on |
|---|---|---|---|
query_log(query, window) | Bounded, single-workspace Log Analytics KQL query. | Reader | new AzureMonitorAdapter |
query_metric(namespace, metric, window, aggregation) | Azure Monitor metrics API. | Reader | new AzureMonitorAdapter |
query_deployments(window) | Git + ARM deployment-history join. | Reader | new DeploymentHistoryAdapter |
correlate_incident(incident_id) | Multi-signal correlation over ingest events + audit + inventory + logs + metrics for one incident id. | Reader | Above three + event_ingest |
query_log accepts explicit bounded KQL and three natural-language diagnostic shapes through server-owned templates. Failed-request summaries group AppRequests by operation and result code without claiming that the grouping proves root cause. Error-signature timelines and related-log requests require an exact signature or selected context; missing context returns a clarification without calling the provider or narrator. Representative error samples use a fixed multi-table template, cap the requested window at 24 hours, and redact secret assignments, bearer values, resource identifiers, GUIDs, email addresses, URLs, and IP addresses before rendering any cell. Additional fixed templates rank spans in the slowest observed distributed trace, aggregate dependency latency, and list slow database dependency calls. These results do not by themselves prove root cause, causal contribution, or that a database call explains a CPU increase. Natural-language requests to run bounded read-only error KQL use a server-owned error template and preserve an explicit English or Korean minute/hour window, capped at 24 hours. Prompt text never becomes executable KQL. When the workspace provider is not configured, the same tool returns a typed unavailable result and does not fall through to current-screen, incident, web, or narrator evidence.
Context-free questions about a proposal, approval, execution, outcome verification, retry, or idempotency use a deterministic action-context hold. The operator must supply an exact ActionType, target resource, proposal, approval, or action receipt before the Console can verify lifecycle claims. Current-screen, repository, incident, and narrator evidence never substitute for that governed record, and the hold performs no mutation or model call.
The exact configuration-baseline filename selects the read-only baseline tool before action-context classification. A negative instruction such as “do not call mitigation tools” cannot turn the document read into an action-lifecycle question. The deterministic answer cites the pinned DOCX in every section and reports unavailable relationships as unknown instead of inferring them from prose. Generic baseline wording stays on the validated semantic planning path instead of creating another keyword router.
The Month-1 additions bring the console close to a multi-signal
incident-response experience, but they still surface
already-correlated results; the correlator lives in Layer 1, not
inside the narrator.
3.4 Tool discovery contract
Section titled “3.4 Tool discovery contract”Each tool declares:
name- CLI-friendly snake_case verb (nodescribe-*/explore-*prefix taxonomy; the verb itself is the category).description- one sentence, English, no marketing language.argument_hint- bounded argument shape expected by the canonical verb parser. Each tool reapplies typed and bounded validation before invocation; invalid arguments never become a partial call.rbac_floor- the lowest role that MAY call the tool.side_effect_class-read/simulate/approve/execute/breakglass. The audit entry carries this class so downstream analytics can slice cheaply.failure_modes- typed error surface documented in the tool’s docstring.
RuntimeToolDiscovery and tools.search/tools.describe return descriptors without handlers or
invocation capability. The narrator sees only the same descriptors allowed by the principal role.
3.5 Public web evidence
Section titled “3.5 Public web evidence”Public web search routing, retrieval, alternative discovery, safety boundaries, and regression coverage are defined in operator-console-web-evidence.md.
4-6. Runtime model (Narrator, DI seams, session model)
Section titled “4-6. Runtime model (Narrator, DI seams, session model)”Moved to a focused owner document: operator-console-runtime-model.md. It covers the Narrator LLM tier model (section 4), DI seams (section 5), and the session model and memory (section 6).
6. Session model + memory
Section titled “6. Session model + memory”See operator-console-runtime-model.md#6-session-model—memory.
7. Safety invariants (chat does not weaken them)
Section titled “7. Safety invariants (chat does not weaken them)”The seven autonomous-action safeguards from coding-conventions.instructions.md § Safety apply unchanged. Chat adds three of its own on top.
7.1 The seven existing safeguards
Section titled “7.1 The seven existing safeguards”Every write-class tool call (simulate_change in enforcement mode -
disallowed today - approve_hil, run_runbook --live) MUST carry:
- Stop-condition - inherited from the ActionType without console alteration.
- Rollback path - inherited from the ActionType’s
rollback_contract. - Impact scope limit - inherited from
blast_radius; language cannot widen it. - Dry-run receipt - required before a write-class tool reaches live dispatch.
- Per-resource lock - held by the execution path, never by the browser.
- Idempotency - binds retries to the same action and suppresses duplicate mutation.
- Audit entry - persisted before dispatch and closed with the terminal outcome.
7.2 Three chat-specific invariants
Section titled “7.2 Three chat-specific invariants”- Verifier re-check on every write-class tool call. After the
narrator emits a
tool_callsframe that targets a write-class tool, the coordinator re-runs the T0Engine + policy-as-code check against the tool arguments. On hold for review / deny, the tool call is dropped and the turn falls through to human approval (see §7.4). This is the mechanical guarantee behind “the LLM never grants execution eligibility”. - No self-approval, chat-scoped.
approve_hilrefuses when the caller’s Entraoidmatches the requester recorded on the queued item, even if the caller holds Owner. This is the same invariant as the PR gate (security-and-identity.md); chat adds the invariant name to the audit reason on refusal. - A BreakGlass request must be time-boxed and explicit.
activate_break_glassrequires(reason, expiry <= 4h)and pages every configured Owner via the push-direction Slack/Teams adapter (channels-and-notifications.md). No silent elevation. The request is fail-closed on notification: if the primary pager channel is down, the coordinator tries the configured fallback channel; if no channel confirms delivery, the request is refused (a break-glass with no audit witness is more dangerous than a delayed emergency), and the refusal is itself audited so an Owner can see the attempt. The shipped tool returns pager and audit receipts only; it does not changeConversationSession,Principal, or the RiskGate role axis, so it raises no approval eligibility. Until a session-scoped grant store and dispatch integration exist, no elevation occurs. A future grant must never returnautoor permit self-approval (safeguard 9). The exact eligibility semantics are defined in user-rbac-and-identity.md § 2 and mirrored by the RiskGate role axis (execution-model.md § 2.5).
7.3 BreakGlass request receipt
Section titled “7.3 BreakGlass request receipt”The current ActivateBreakGlassTool result contains activated_at, expires_at, a redacted
reason, pager_receipt, and audit_id. Its max_ttl_seconds default and ceiling are 14400; a
larger adapter setting is rejected. This result is not an authorization grant record, and no
persistent store currently enforces session-end or expiry revocation. No downstream path may use
the receipt as elevation evidence.
7.4 Human approval fall-through when the LLM proposes a write
Section titled “7.4 Human approval fall-through when the LLM proposes a write”The narrator MAY, when the operator says “just fix it”, emit a
tool_call for run_runbook(dry_run=false) or approve_hil. On the
verifier re-check (safeguard 8):
- If verifier passes AND RBAC is satisfied → the tool call proceeds.
- If verifier holds for review or RBAC is under the floor → the coordinator
substitutes an
enqueue_hil(...)call that files a review item in the existing human approval queue and returns “I filed a human approval item, id X” to the operator. - Under no circumstance does the write happen without an audit entry before dispatch.
8. Channel integration (push vs pull)
Section titled “8. Channel integration (push vs pull)”The channel abstraction (channels-and-notifications.md) already handles push (system → human). Pull uses separate adapters and configuration contracts. A deployment can reuse a secret provider or workload identity, but it does not derive inbound conversation enablement from the outbound notification matrix. This separation preserves the different trust posture and impact scope of send-only and receive-plus-send surfaces.
The shared pull-direction contract, gateway, Slack signed ingress, Teams authenticated activity
normalizer, bounded Starlette routes, Slack Web API publisher, and Teams Bot Framework publisher
are implemented. The Slack route verifies timestamped signatures. The Teams route calls an
injected bearer authenticator before parsing activity JSON. Reply publishers use only configured
HTTPS endpoints, injected app/workload credentials, and server-owned conversation resolution.
ProductionChannelRuntime binds the concrete Bot Framework JWT verifier, Teams principal resolver,
Slack secrets/app credentials, fixed-endpoint publishers, and background gateway lifecycle.
Missing required credentials or identity bindings fail startup before traffic. Those bindings stay
in delivery/; they do not change the coordinator.
ChannelAccessService is the sender-access foundation for those principal resolvers. Each channel
selects disabled, allowlist, or pairing. Unknown senders resolve to no principal and never
reach the coordinator. Pairing mode issues a bounded, expiring challenge, stores only its SHA-256
digest, caps pending requests per channel, requires a separately authorized approver, verifies the
code in constant time, and maps the approved sender to an existing FDAI principal. Disabled and
allowlist modes never self-enroll a sender. The PostgreSQL store now enforces the pending cap and
approval transition atomically across replicas. Native challenge delivery replies in the originating
thread and conditionally removes the pending digest when delivery fails. The code is never stored or
placed in response metadata.
CrossChannelIdentityLinkService records an explicit relationship only after both channel senders
are independently paired to the same principal. It rejects same-channel links, self-approval,
unapproved endpoints, and any attempt to relate two distinct principals. The durable link is
idempotent and does not merge principal records, roles, sessions, or audit histories.
| Channel | Push (existing) | Pull (this doc) | Shared config |
|---|---|---|---|
| Teams | A1 human approval and outbound notification adapters | TeamsBotChannel + authenticated bounded activity route + workload-identity reply publisher + principal binding | Deployments can reuse selected identity/secret providers. |
| Slack | SlackWebhookChannel and A1 adapter | SlackBotChannel + signed Events API route + fixed-endpoint Web API reply publisher | Deployments can reuse selected secret providers. |
| send-only | (not planned; asynchronous, ill-suited to interactive) | n/a | |
| Webhook | send-only | (not planned; caller must own an interactive protocol themselves) | n/a |
| Pager (PagerDuty) | send-only | (not planned) | n/a |
| SMS | send-only | (not planned) | n/a |
| Web chat | n/a | Authenticated POST /chat and POST /chat/stream SSE | Console SPA/Operator API config |
| CLI | n/a | stdin/stdout UI calling the shared Operator API /chat | local auth/Operator API config |
8.1 Separate channel configuration
Section titled “8.1 Separate channel configuration”config/notifications-matrix.yaml owns outbound
notification routing only. Conversation channels use separate enablement, secret references,
Teams identity/principal bindings, and queue-capacity settings. Sharing a credential backend does
not merge configuration ownership.
9. Growth model (catalog + operator memory)
Section titled “9. Growth model (catalog + operator memory)”The console gets better over time via three deterministic mechanisms. Model-side learning is not one of them.
9.1 Day 1
Section titled “9.1 Day 1”The Day-1 console can answer:
- “What rules apply to
network.nsginexample-rg?” →query_inventory+explore_catalog. - “Why did event
<id>route to human approval?” →explain_verdict. - “Show me every audit entry for
object-storage.public-access.denyin the last 24h.” →query_audit. - “If I create a storage account with public access enabled, what would
the loop do?” →
describe_event.
No writes, no runbooks, no approvals - just orientation.
9.2 Week 1
Section titled “9.2 Week 1”Adds simulate_change, approve_hil, run_runbook --dry-run, and the
Teams / Slack pull adapters. The console can now:
- Preview a change end-to-end in observation mode.
- Resolve queued human approval items with the same identity gate the PR flow uses.
- Trigger the shipped runbooks (docs/runbooks/) from any channel.
9.3 Month 1
Section titled “9.3 Month 1”Adds the observation-depth tools (§3.3) and the discovery-loop hook:
- The coordinator publishes a
console.recurrent_querysignal to the discovery-loop input stream when the same tool-argument shape appears N times across distinct principals in a rolling window (N configured; default 5 / week). - The rule-candidate generator (rule-governance.md) receives that signal like any other; the resulting rule ships observation-first through the same promotion pipeline.
The result is that a common investigation pattern in chat becomes a first-class rule in the catalog - the console grows the catalog, not itself.
10. Rollout reconciliation
Section titled “10. Rollout reconciliation”The original Day/Week/Month sequence is historical implementation context, not the current availability source.
| Slice | Current status |
|---|---|
| Core/CLI translator | Narrator, grounded answer rendering in AzureOpenAINarratorModel, coordinator, read tools, Python headless harness, and shared-API TypeScript CLI ship. Intent translation and answer rendering use separate prompts; both retain the deterministic tool and RBAC boundary. |
| Write/approval tools | Simulation, human approval, runbook, and proposal routes ship. Break-glass stops at the pager/audit request receipt in §7.3 and grants no elevation. |
| Teams/Slack conversation | ProductionChannelRuntime, authenticated ingress, principal resolution, publishers, and optional durable replies ship; environment-owned enablement and credentials remain required. |
| Web chat and memory | JSON/SSE chat, principal-scoped history/preferences/memory, AnswerPlan, and progressive verification ship. |
| Observation/discovery | POST /read-investigations selects direct, streamed, or detached execution from durable latency evidence before Azure I/O. Direct Command Deck and HTTP reads share an owner-scoped result-replay ledger; closing a streamed response cancels its in-flight read. The surface is registered only with a dedicated reader binding; catalog presence alone proves neither provider health nor promotion. |
| Forecast and Dynamic learning | GET /forecast-learning projects forecast closure and publication health; GET /dynamic-assurance projects durable scalar/graph model summaries and trajectory closure counts. Both routes are Reader-only and expose no detector/model mutation, promotion, approval, or execution control. |
Live Azure completion evidence and capability promotion remain governed by deployment verification and the authoritative registry, never inferred from phase names in this document.
11. Testability
Section titled “11. Testability”- Coordinator - property tests: “verifier re-check runs on every write-class tool call”, “RBAC floor is enforced before the narrator sees the tool schema”, “audit entry precedes every tool dispatch”, “escalation records tier and trigger”.
- Narrator adapter - contract tests using
httpx.MockTransportfor the strict Azure OpenAI intent translator, injection-isolated grounded answer prompt, exact evidence-reference preservation, and resolved deployment binding. - Tools - each tool has a observation mode test showing it never mutates
when its
side_effect_class == read | simulate; awrite/approvetest showing the verifier re-check gate. - Channels - CLI REPL golden transcript, Teams Bot Framework activity/JWT, Slack signed HTTP Events API, and publisher receipts.
- RBAC matrix - table-driven test over every (Role × Tool) cell to prove the floor from §3.1-§3.3 is applied.
- Break-glass - tests prove
activate_break_glassrefusesexpiry > 4h, requires Owner notification and audit receipts, and does not mutate the session principal. Persistent grants and session-end revocation are not shipped contracts. - Determinism - two runs of the same CLI transcript through a fake
Narratorproduce byte-identical audit trails (given fixed timestamps and idempotency keys). - Session recovery - principal-scoped
ConversationHistoryStorereloads prior turns by session id, while stable request idempotency prevents duplicate appends. Audit/ontology retain hashes and references rather than raw transcripts.
12. Failure modes
Section titled “12. Failure modes”- Narrator unavailable - fall through to Chat T0 direct-hit; if the turn does not match a T0 pattern, respond with a canned “reasoning layer is temporarily unavailable; here is the direct query surface” and expose the tools list.
- Grounded answer rendering unavailable or invalid - return the completed deterministic tool preview. The coordinator also uses this fallback when the model returns an empty or oversized answer or drops any required evidence reference. Rendering failure never changes tool data, status, authorization, or execution state.
- Verifier hold for review on write-class tool - substitute
enqueue_hil(...)(see §7.4), return the human approval id, audit reasonverifier_abstained. - Channel adapter disconnects - when durable delivery is configured, the complete response and terminal/ambiguous state remain in the ledger. The direct path still resumes durable conversation history by session id but does not claim exactly-once provider send.
- Break-glass request receipt - the coordinator does not interpret the receipt as elevated capability. A future grant integration must recheck TTL before every privileged tool call.
- Tool implementation raises - the tool’s typed error surface (§3.4)
is wrapped as a
ToolResult(status=error); the narrator sees a structured error, not an exception traceback.
13. Data + wire contracts
Section titled “13. Data + wire contracts”Split into focused owner documents:
- operator-console-wire-contracts.md - audit entry, CLI REPL, approval callback (13.1-13.3), action submit, Python VM workbench, grounded code, and ontology projection (13.6-13.9).
- operator-console-view-snapshot.md - the self-describing screen contract (13.4).
- operator-console-incident-roster.md - incident roster and fix history (13.5).
14. MCP delivery and managed catalog
Section titled “14. MCP delivery and managed catalog”FDAI can consume externally hosted MCP tools through the managed outbound catalog under
services/core-control-plane/src/fdai/delivery/mcp/. Servers install disabled. Enable performs a non-invoking tools/list
discovery and verifies every ActionType-to-tool allowlist entry. Catalog mutations use a durable
revision-CAS snapshot; manifest, health, revision, and the admin audit record commit in one
PostgreSQL transaction. A periodic monitor records health transitions, and only enabled, healthy
servers are routable. Endpoint validation rejects credentials, query strings, fragments, and
non-loopback plaintext HTTP.
This outbound catalog is distinct from publishing FDAI itself as an MCP server. The repository
currently ships no inbound MCP server process, list_tools/call_tool wire endpoint, or external
MCP principal mapping. A fork MUST NOT infer an FDAI-to-client MCP surface from this document.
A future inbound MCP proposal can additively reuse the coordinator and RBAC, reject anonymous
callers, map mTLS or audience-scoped Entra identities to service Principal records, and audit the
resolved role. That remains future scope requiring its own threat model, protocol tests, and
deployment gates.
15. Decision status
Section titled “15. Decision status”- OD-C1 resolved - the strict core narrator prompt lives in
AzureOpenAINarratorModel; the broader prompt catalog usesrule-catalog/prompts/base,packs,scenarios, andtools. - OD-C2 resolved - principal-scoped user memory/preferences and separate governed operator memory now have schemas, provenance, consent, and retention paths.
- OD-C3 residual - persistent BreakGlass grant/elevation is not implemented. A future design must retain no-self-approval and separately approve any distinct-approver requirement.
- OD-C4 current behavior - CLI history is bounded process-memory navigation only. A persistent history file and retention/redaction contract are neither shipped nor blockers for the current CLI.
16. Related docs
Section titled “16. Related docs”- architecture.instructions.md - trust routing, verifier authority.
- action-ontology.md - ActionType schema with the
trigger_kindaxis (operator_request) that the console emits, plus theargument_schemathe coordinator validates against. - execution-model.md - the unified RiskGate the chat verifier re-check (§7.2) invokes, and the 5-axis authority matrix that decides auto / human approval / deny for every write-class tool call.
- channels-and-notifications.md - the push-direction channel matrix this doc’s pull side extends.
- user-rbac-and-identity.md - the RBAC role set the tool matrix (§3) references.
- security-and-identity.md - no-self-approval, execution identity, safety invariants.
- prompt-composition.md - narrator prompt layering, tool-schema exposure, debate orchestrator (Wave 4.5) that Month 1 may consume.
- rule-governance.md - the discovery loop the Month-1 console feeds.
- project-structure.md § console/ - the FDAI Console SPA the Month-1 web-chat channel extends.