# Runtime Context, Forms, Rules, And Assessments Date: 2026-05-04 ## Purpose The runtime layer turns contract extension points into executable behavior while keeping the deterministic contract framework intact. Static checks still handle document type, sections, assertions, and metric bands. Runtime checks add external context, field prefill, UI-neutral form state, dynamic rules, and a provider-neutral assessment protocol. The layer is deliberately local-first. Core Markitect reads YAML or JSON context files and runs deterministic rules. Network calls, application lookups, and LLM providers belong behind adapters. ## Context Files Runtime context can be a raw YAML/JSON mapping: ```yaml recipient: name: Ada Lovelace sender: name: Markitect Team ``` or an envelope with metadata and schema: ```yaml metadata: case_id: case-42 schema: type: object required: [recipient, sender] context: recipient: name: Ada Lovelace sender: name: Markitect Team ``` The value under `context` is bound as `context` in field sources and dynamic rules. `schema` validates the full context object. `schemas` can validate named objects individually. Malformed context and schema failures produce normal diagnostics: - `runtime.context.malformed` - `runtime.context.schema_invalid` - `runtime.context.schema_target_missing` - `runtime.context.schema` ## Field Runtime Field specs continue to live in the contract: ```yaml fields: recipient_name: type: string required: true source: context.recipient.name delivery_channel: type: string default: email enum: [email, print] ``` Runtime resolution order is: 1. Manual document value from `path`, usually frontmatter. 2. Context value from `source` or `sources`. 3. Contract `default`. 4. Missing. Manual document values win over context. If both exist and differ, Markitect emits `runtime.field.conflict` as a warning by default. A field can set `conflict: error` to make that stricter. Multiple context sources with distinct values produce `runtime.field.ambiguous`. `mkt contract check` uses runtime evaluation only when `--context` is supplied: ```text mkt contract check document.md --contract contract.md --context context.yaml ``` `mkt contract form-state` always emits the UI-neutral runtime view: ```text mkt contract form-state document.md --contract contract.md --context context.yaml ``` ## Form State Form state is not a UI framework. It is a stable contract that future UIs, agents, generators, and workflow steps can render: - field id - value - origin: `manual`, `prefilled`, `defaulted`, `calculated`, or `missing` - required/optional - visible/hidden - enabled/disabled - allowed values - diagnostics - metadata Hidden fields are not required unless a future adapter explicitly asks for hidden validation. This matches practical form behavior and avoids punishing authors for data that the current context made irrelevant. ## Dynamic Rules Rules are deterministic YAML. They use a deliberately small condition language: ```yaml rules: - id: postal-address-for-print if: path: fields.delivery_channel.value equals: print then: required: [postal_address] visible: postal_address: true else: hidden: [postal_address] ``` Supported condition operators: - `exists` - `equals` / `eq` - `not_equals` - `in` - `contains` - `matches` - `gt`, `gte`, `lt`, `lte` - `all`, `any`, `not` Supported actions: - `required` / `optional` - `visible` / `hidden` - `enabled` / `disabled` - `allowed_values` - `set` - `assert` - `sections` Calculated values can reference runtime paths: ```yaml then: set: contact_label: "${fields.sender_name.value} <${context.sender.email}>" ``` Context assertions use the same condition vocabulary: ```yaml assert: path: context.sender.email matches: "@example\\.com$" message: Sender email must come from example.com. severity: warning ``` Dynamic section rules are intentionally narrow. They can require, recommend, discourage, or forbid section specs already declared in the contract. ## Assessment Protocol Rubrics remain provider-neutral contract declarations: ```yaml rubrics: - id: tone-fit scope: section.body criteria: The body should match the recipient relationship. threshold: 0.75 ``` Core Markitect turns rubrics into `AssessmentRequest` objects and normalizes adapter results into `AssessmentResult` and diagnostics. It does not call an LLM provider directly. The cache key includes contract id, rule id, scope, text, criteria, context, structured inputs, threshold, provider, model, and metadata. Adapters can be injected from workflows, applications, or tests. A transparent in-memory cache exists for tests and short runs; persistent storage remains a backend concern. ## Workflow Integration Workflow `contract_check` steps accept `context`: ```yaml steps: - id: check-letter kind: contract_check document: letter.md contract: letter.contract.md context: letter.context.yaml ``` Workflow `form_state` steps expose the runtime state as a step result: ```yaml steps: - id: form kind: form_state document: letter.md contract: letter.contract.md context: letter.context.yaml ``` This keeps workflow orchestration separate from the runtime engine. The runtime engine answers "what does this contract imply in this context"; the workflow engine decides when to run it and where to send the output.