Files
Oliver Kieselbach 516cdc71a7 Rules: optional presentation block, {{signal.key}} tokens, one resolution for web and MCP (D-356)
Analyze rules can describe how a finding is shown: a role (outcome, cause, context,
advisory), a one-line summary, the subject it is about and up to six key facts. Like the
explanation, the block is stored raw on the result and resolved where it is rendered.

- Templates: a qualified token {{signal.key}} reads one key of one condition's evidence,
  so counts, durations and correlation fields are reachable. RuleEvidenceKeys (Shared,
  exported to the manifest) lists the keys each condition source records; tests pin it
  against the evaluators.
- Backend: PresentationJson on AnalyzeRules and RuleResults, RuleVersion on results,
  ValidatePresentation on create/update, and results stored without a presentation get
  the current rule's at read time.
- Web + MCP: lib/rulePresentation.ts resolves the block (unrecorded facts left out, summary
  falls back to the explanation). The MCP now runs generated copies of the web modules
  instead of a hand-kept mirror (npm run generate:mcp-logic, freshness tests in both
  suites); the duplicate baseline shrinks by one.
- MCP output: get_session and get_session_summary return the resolved block (issues gain
  ruleId, role, summary, subject, facts); test_analyze_rule previews it; validate_rule
  checks qualified tokens against the key catalog and lints the subject.
- Shared case files: rule-template-interpolation gains the qualified-token cases,
  rule-presentation is new.

No rule carries a presentation yet; renderers behave as before until rules gain one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 22:48:46 +02:00
..

Rule template interpolation — shared cases

A rule result stores the rule's text with its {{token}} placeholders; each renderer resolves them from the result's matchedConditions. cases.json is the one definition of that resolution, and every implementation runs all of it:

  • Web — interpolateRuleTemplate in src/Web/autopilot-monitor-web/lib/interpolateRuleTemplate.ts (test: lib/__tests__/interpolateRuleTemplate.test.ts)
  • MCP server — interpolateRuleTemplate in src/McpServer/autopilot-monitor-mcp/src/interpolate-rule-template.ts (test: src/__tests__/interpolate-rule-template.test.ts)
  • Backend — RuleTemplateInterpolator.Interpolate in src/Backend/AutopilotMonitor.Functions/Services/RuleTemplateInterpolator.cs (test: RuleTemplateInterpolatorTests.cs)

autoFields is the list of same-event fields the rule engine copies into every evidence entry (RuleEngine.EvidenceAutoFields). Each implementation's list is compared against it.

A qualified token {{signal.key}} reads one key of one evidence entry (a count, a duration, a correlation field). It has no fallback order: an unknown signal, an unknown key or a null value leaves it literal, so a renderer can tell "not recorded" from an empty value.

A change to the resolution goes into cases.json first; the three tests then say which implementation is behind.