generated from coulomb/repo-seed
221 lines
5.4 KiB
Markdown
221 lines
5.4 KiB
Markdown
# 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.
|