generated from coulomb/repo-seed
Complete memory graph and document value workplans
This commit is contained in:
@@ -7,9 +7,11 @@ Generated from `markitect_tool.__all__`.
|
||||
- `BACKEND_CAPABILITIES` - object. set() -> new empty set object
|
||||
- `DEFAULT_BACKEND_PATHS` - object. Built-in immutable sequence.
|
||||
- `DEFAULT_LOCAL_INDEX_PATH` - object. str(object='') -> str
|
||||
- `DOCUMENT_VALUE_KINDS` - object. set() -> new empty set object
|
||||
- `EMPTY_PARSE_OPTIONS_HASH` - object. str(object='') -> str
|
||||
- `EXPLODE_MANIFEST_NAME` - object. str(object='') -> str
|
||||
- `LOCAL_INDEX_SCHEMA_VERSION` - object. str(object='') -> str
|
||||
- `MAX_FUNCTION_PIPELINE_DEPTH` - object. int([x]) -> integer
|
||||
- `NORMALIZED_SOURCE_SCHEMA_VERSION` - object. str(object='') -> str
|
||||
- `SOURCE_ADAPTER_ENTRY_POINT_GROUP` - object. str(object='') -> str
|
||||
|
||||
@@ -122,8 +124,12 @@ Generated from `markitect_tool.__all__`.
|
||||
- `DocumentFunctionEvaluationResult(content: 'str', calls: 'list[DocumentFunctionRun]' = <factory>, diagnostics: 'list[Diagnostic]' = <factory>, provenance: 'list[ProcessingProvenance]' = <factory>, trace: 'list[ProcessingTrace]' = <factory>) -> None` - class. Result of expanding document functions in a Markdown document.
|
||||
- `DocumentFunctionParameter(name: 'str', kind: 'str' = 'string', required: 'bool' = True, default: 'Any' = None, variadic: 'bool' = False, description: 'str | None' = None) -> None` - class. One declared document function parameter.
|
||||
- `DocumentFunctionRegistry(descriptors: 'list[DocumentFunctionDescriptor] | None' = None) -> 'None'` - class. Registry and evaluator for document functions.
|
||||
- `DocumentFunctionRun(call: 'DocumentFunctionCall', output: 'Any' = None, diagnostics: 'list[Diagnostic]' = <factory>, provenance: 'list[ProcessingProvenance]' = <factory>, trace: 'list[ProcessingTrace]' = <factory>) -> None` - class. One function call result.
|
||||
- `DocumentFunctionRun(call: 'DocumentFunctionCall', output: 'Any' = None, value: 'DocumentValue | None' = None, diagnostics: 'list[Diagnostic]' = <factory>, provenance: 'list[ProcessingProvenance]' = <factory>, trace: 'list[ProcessingTrace]' = <factory>) -> None` - class. One function call result.
|
||||
- `DocumentValue(kind: 'str', value: 'Any' = None, items: "list['DocumentValue']" = <factory>, fields: "dict[str, 'DocumentValue']" = <factory>, metadata: 'dict[str, Any]' = <factory>, provenance: 'list[dict[str, Any]]' = <factory>) -> None` - class. Typed value produced by a document function.
|
||||
- `coerce_document_value(value: 'Any', *, declared_kind: 'str' = 'dynamic') -> 'DocumentValue'` - function. Coerce a Python value into a typed document value.
|
||||
- `default_document_function_registry() -> 'DocumentFunctionRegistry'` - function. Return built-in deterministic document functions.
|
||||
- `document_value_to_json(value: 'DocumentValue | Any') -> 'dict[str, Any]'` - function. Return the stable JSON-compatible representation of a document value.
|
||||
- `format_document_value(value: 'DocumentValue | Any', *, inline: 'bool') -> 'str'` - function. Map a typed document value to deterministic Markdown text.
|
||||
- `parse_document_function_calls(text: 'str') -> 'list[DocumentFunctionCall]'` - function. Parse inline and fenced document function calls.
|
||||
- `render_document_functions(text: 'str', *, registry: 'DocumentFunctionRegistry | None' = None, context: 'ProcessingContext | None' = None) -> 'DocumentFunctionEvaluationResult'` - function. Expand deterministic document functions in Markdown content.
|
||||
- `validate_document_functions(text: 'str', *, registry: 'DocumentFunctionRegistry | None' = None, allowed: 'list[str] | None' = None, forbidden: 'list[str] | None' = None) -> 'DocumentFunctionEvaluationResult'` - function. Validate function calls without rendering the document.
|
||||
|
||||
@@ -78,6 +78,38 @@ Initial deterministic functions:
|
||||
| `md.codeblock` | Create a fenced code block. |
|
||||
| `data.get` | Read a value from processing context variables. |
|
||||
|
||||
## Typed Values
|
||||
|
||||
Document functions now expose typed result values in addition to their legacy
|
||||
raw `output` field. Each `DocumentFunctionRun` includes a `value` object with a
|
||||
stable `kind`, metadata, and optional provenance.
|
||||
|
||||
Supported value kinds:
|
||||
|
||||
| Kind | Markdown mapping |
|
||||
| --- | --- |
|
||||
| `string` | Inline or block text. |
|
||||
| `number` | Decimal text. |
|
||||
| `boolean` | `true` or `false`. |
|
||||
| `none` | Empty text. |
|
||||
| `markdown` | Markdown content passed through directly. |
|
||||
| `list` | Comma-separated inline text or newline-separated block text. |
|
||||
| `dictionary` | Stable JSON object text. |
|
||||
| `record` | Stable JSON object text. |
|
||||
| `table` | Deterministic Markdown table. |
|
||||
| `reference` | Label/title/value text, with provenance required. |
|
||||
| `content_unit` | Label/title/value text, with provenance required. |
|
||||
| `unknown` | Diagnostic fallback for mismatched output values. |
|
||||
| `dynamic` | Reserved for explicitly dynamic values. |
|
||||
|
||||
Function descriptors declare `output_type`; execution validates the returned
|
||||
value against that declaration. Mismatches produce
|
||||
`function.output_type_mismatch`, while reference-like values without provenance
|
||||
produce `function.provenance_missing`.
|
||||
|
||||
The raw `output` field remains for compatibility. New callers should prefer
|
||||
`value` for typed API use and use the mapper when Markdown output is needed.
|
||||
|
||||
## CLI
|
||||
|
||||
List functions:
|
||||
@@ -132,18 +164,44 @@ capabilities before execution. External policy services may provide decisions
|
||||
through adapters later, but deterministic function execution has no external
|
||||
service dependency.
|
||||
|
||||
## Syntax Boundary
|
||||
|
||||
The supported syntax remains intentionally conservative:
|
||||
|
||||
- inline calls with `{{mkt:...}}`
|
||||
- fenced calls with `mkt-function`
|
||||
- positional and named arguments parsed with shell-like quoting
|
||||
- pipeline chaining with quoted pipe characters preserved
|
||||
- `${name}` context variable lookup
|
||||
- bounded pipeline depth to avoid accidental runaway expressions
|
||||
|
||||
Deferred syntax:
|
||||
|
||||
- nested function expressions
|
||||
- document-local function definitions
|
||||
- conditionals, loops, lambdas, or general scripting
|
||||
- Quarkdown syntax compatibility in the core parser
|
||||
|
||||
## Natural Extensions
|
||||
|
||||
The deterministic layer deliberately stops before becoming a full publishing
|
||||
language. Future extension work is captured in
|
||||
`MKTT-WP-0015: Render And Document Function Extensions`.
|
||||
language. The original broad render/function follow-up has been split into
|
||||
native workplans:
|
||||
|
||||
That workplan should consider:
|
||||
- `MKTT-WP-0015`: typed document-function value contracts.
|
||||
- `MKTT-WP-0020`: render/export adapter contracts.
|
||||
- `MKTT-WP-0021`: render reference and asset manifest contracts.
|
||||
- `MQD-WP-0001`: concrete Quarkdown adapter implementation in
|
||||
`markitect-quarkdown`.
|
||||
- `MKTF-WP-0003`: read-side source attachment metadata compatibility in
|
||||
`markitect-filter`.
|
||||
|
||||
Those workplans should consider:
|
||||
|
||||
- typed document values and value-to-Markdown mapping
|
||||
- richer multiline and nested function syntax
|
||||
- document-local reusable functions
|
||||
- render/export adapters, including optional Quarkdown source export
|
||||
- constrained parser compatibility improvements, while deferring nested
|
||||
function expressions and document-local reusable functions
|
||||
- render/export adapter contracts, including optional Quarkdown source export
|
||||
- render-aware numbering, references, tables, figures, equations, and code
|
||||
blocks
|
||||
- static asset and media manifests with checksums
|
||||
|
||||
@@ -18,7 +18,7 @@ This index maps example files to practical usecases and useful commands.
|
||||
| `examples/references/context.md` | Namespace, region, fence, and section references | `mkt ref resolve examples/references/context.md 'std:clauses.md#payment-terms' --root examples/references` |
|
||||
| `examples/references/standard/clauses.md` | Referenced reusable content | Use with `mkt ref resolve` or `mkt process` |
|
||||
| `examples/migration/legacy-path-include.md` | Migration-style include handling | `mkt include examples/migration/legacy-path-include.md --base-dir examples/migration` |
|
||||
| `examples/functions/basic-functions.md` | Deterministic document functions | `mkt function render examples/functions/basic-functions.md` |
|
||||
| `examples/functions/basic-functions.md`, `examples/functions/typed-values.md` | Deterministic document functions and typed value examples | `mkt function render examples/functions/basic-functions.md` |
|
||||
|
||||
## Templates, Generation, And Workflows
|
||||
|
||||
@@ -46,7 +46,8 @@ This index maps example files to practical usecases and useful commands.
|
||||
| `examples/policy/local-label-policy.yaml` | Local policy gateway | `mkt policy check public-agent read note --policy examples/policy/local-label-policy.yaml --label public` |
|
||||
| `examples/policy/enterprise-policy-map.yaml` | Enterprise IAM mapping fixture | `mkt policy subject examples/policy/netkingdom-claims.yaml --policy-map examples/policy/enterprise-policy-map.yaml` |
|
||||
| `examples/memory/workplan-context.manifest.yaml` | Context package manifest | `mkt context pack examples/memory/workplan-context.manifest.yaml --root . --no-save` |
|
||||
| `examples/memory/memory-profile.local.yaml`, `examples/memory/decision-graph*.yaml` | Memory graph/profile contract fixtures | `mkt memory graph pack examples/memory/decision-graph-selection.yaml --format yaml` |
|
||||
| `examples/memory/memory-profile.local.yaml`, `examples/memory/*graph*.yaml`, `examples/memory/*path*.yaml`, `examples/memory/*neighborhood*.yaml` | Memory graph/profile contract fixtures | `mkt memory graph pack examples/memory/decision-graph-selection.yaml --format yaml` |
|
||||
| `examples/memory/invalid-memory-*.yaml`, `examples/memory/runtime-adapter-boundaries.yaml` | Negative memory fixtures and runtime handoff descriptors | `mkt memory graph validate examples/memory/invalid-memory-graph.yaml --format text` |
|
||||
|
||||
## Literate And Migration
|
||||
|
||||
|
||||
@@ -42,10 +42,10 @@ validation so that cross-repository contracts do not drift silently.
|
||||
|
||||
Valid node kinds include `question`, `claim`, `assumption`, `evidence`,
|
||||
`decision`, `alternative`, `outcome`, `risk`, `follow_up`, `turn`, `plan`,
|
||||
`tool_call`, `observation`, `edit`, `validation`, `task`, `topic`, `document`,
|
||||
`entity`, `artifact`, `concept`, `capability`, `contract`, `policy`,
|
||||
`preference`, `source_fact`, `episode`, `finding`, `constraint`, `profile`,
|
||||
`context_package`, and `memory`.
|
||||
`tool_call`, `observation`, `edit`, `validation`, `interruption`,
|
||||
`activation`, `task`, `topic`, `document`, `entity`, `artifact`, `concept`,
|
||||
`capability`, `contract`, `policy`, `preference`, `source_fact`, `episode`,
|
||||
`finding`, `constraint`, `profile`, `context_package`, and `memory`.
|
||||
|
||||
Valid edge kinds include `supports`, `contradicts`, `depends_on`,
|
||||
`derived_from`, `led_to`, `affects`, `references`, `relates_to`, `supersedes`,
|
||||
@@ -70,6 +70,42 @@ mkt memory graph validate examples/memory/decision-graph.yaml
|
||||
mkt memory graph pack examples/memory/decision-graph-selection.yaml --format yaml
|
||||
```
|
||||
|
||||
## Fixture Catalog
|
||||
|
||||
The memory fixtures cover the three intended memory views:
|
||||
|
||||
| Files | View | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `examples/memory/decision-graph.yaml`, `examples/memory/decision-graph-selection.yaml` | reasoning | Decision and constraint path that pins the contract/runtime boundary. |
|
||||
| `examples/memory/conversation-path.yaml`, `examples/memory/conversation-path-selection.yaml` | conversation | Short-term branch/plan/tool/observation episode with event activation. |
|
||||
| `examples/memory/knowledge-neighborhood.yaml`, `examples/memory/knowledge-neighborhood-selection.yaml` | knowledge | Durable neighborhood around schema versions, docs, compiler capability, and runtime policy. |
|
||||
| `examples/memory/memory-profile.local.yaml` | mixed profile | Local profile declaring reasoning, knowledge, and package stores plus activation limits. |
|
||||
| `examples/memory/invalid-memory-graph.yaml`, `examples/memory/invalid-memory-profile.yaml` | invalid | Negative fixtures for validation diagnostics and handoff contract tests. |
|
||||
|
||||
The valid selections compile to standard `ContextPackage` output. Invalid
|
||||
fixtures are deliberately small so downstream runtimes can assert diagnostic
|
||||
codes without needing Markitect-owned durable storage.
|
||||
|
||||
## Runtime Adapter Handoff
|
||||
|
||||
`examples/memory/runtime-adapter-boundaries.yaml` is a non-executing descriptor
|
||||
catalog for external memory runtimes and stores. It names the contracts that
|
||||
runtime packages should accept or emit while keeping Markitect limited to local
|
||||
validation, planning, and package compilation.
|
||||
|
||||
The descriptor catalog covers:
|
||||
|
||||
| Boundary | Responsibility |
|
||||
| --- | --- |
|
||||
| `memory.runtime.kontextual-engine` | Durable graph snapshots, event append, selection resolution, and refresh execution. |
|
||||
| `memory.runtime.phased-memory` | Future lifecycle policies such as compaction, retention intent, and observability. |
|
||||
| `memory.store.external-graph` | External graph database path and neighborhood queries. |
|
||||
| `memory.store.vector` | Embedding and vector retrieval flows that return graph selections. |
|
||||
| `memory.extract.llm-assisted` | Optional graph extraction proposals from transcripts or source artifacts. |
|
||||
| `memory.policy.enterprise-pdp` | Runtime policy authorization and activation reauthorization. |
|
||||
| `memory.registry.remote` | Future remote profile/package registry interactions. |
|
||||
| `memory.audit.sink` | Durable audit and policy decision event sinks. |
|
||||
|
||||
This keeps Markitect as the compiler/contract boundary. `kontextual-engine`
|
||||
should implement runtime stores and event production against these schemas, and
|
||||
`infospace-bench` should benchmark generated context packages and runtime
|
||||
|
||||
@@ -138,6 +138,16 @@ extension descriptors, `mkt source` commands, API exports, fake adapter
|
||||
fixtures, and sibling-repo migration notes. Concrete EPUB3 extraction remains
|
||||
`markitect-filter` scope.
|
||||
|
||||
`MKTT-WP-0015` has been narrowed to document-function typed value contracts.
|
||||
The former render-and-function extension scope is split across native
|
||||
workplans: `MKTT-WP-0020` owns render/export adapter contracts,
|
||||
`MKTT-WP-0021` owns passive render reference and asset manifests,
|
||||
`MQD-WP-0001` owns concrete Quarkdown adapter implementation in
|
||||
`markitect-quarkdown`, and `MKTF-WP-0003` owns read-side source attachment
|
||||
metadata compatibility in `markitect-filter`. This preserves the
|
||||
`markitect-filter` read-only boundary and avoids pulling renderer execution
|
||||
into `markitect-tool`.
|
||||
|
||||
## State Hub Mirror
|
||||
|
||||
Native State Hub dependency edges should mirror the whole-workstream
|
||||
@@ -182,3 +192,8 @@ dependencies:
|
||||
- `MKTT-WP-0018 -> MKTT-WP-0013`
|
||||
- `MKTT-WP-0018 -> MKTT-WP-0017`
|
||||
- `MKTT-WP-0018 -> MKTT-WP-0019`
|
||||
- `MKTT-WP-0020 -> MKTT-WP-0013`
|
||||
- `MKTT-WP-0020 -> MKTT-WP-0015`
|
||||
- `MKTT-WP-0021 -> MKTT-WP-0010`
|
||||
- `MKTT-WP-0021 -> MKTT-WP-0015`
|
||||
- `MKTT-WP-0021 -> MKTT-WP-0020`
|
||||
|
||||
12
examples/functions/typed-values.md
Normal file
12
examples/functions/typed-values.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Typed Document Function Values
|
||||
|
||||
String value: {{mkt:text.upper "draft"}}
|
||||
|
||||
Markdown value:
|
||||
|
||||
```mkt-function md.heading level=3
|
||||
Typed Section
|
||||
```
|
||||
|
||||
Dynamic context values can map lists, records, dictionaries, and tables through
|
||||
the typed value mapper when supplied by an embedding application.
|
||||
21
examples/memory/conversation-path-selection.yaml
Normal file
21
examples/memory/conversation-path-selection.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
schema_version: markitect.memory.selection.v1
|
||||
graph: conversation-path.yaml
|
||||
profile: memory-profile.local.yaml
|
||||
title: Conversation Episode Package
|
||||
intent: Activate the short-term path that explains why MKTT-WP-0016 is the current implementation target.
|
||||
namespace:
|
||||
project: markitect-tool
|
||||
task: MKTT-WP-0016
|
||||
node_ids:
|
||||
- turn.user-correction
|
||||
- interruption.direction-corrected
|
||||
- plan.finish-memory-graph-profile
|
||||
- observation.remaining-gap
|
||||
event_ids:
|
||||
- event.user-correction
|
||||
budget:
|
||||
max_items: 5
|
||||
max_tokens: 1400
|
||||
metadata:
|
||||
memory_view: conversation
|
||||
purpose: short-term-episode
|
||||
86
examples/memory/conversation-path.yaml
Normal file
86
examples/memory/conversation-path.yaml
Normal file
@@ -0,0 +1,86 @@
|
||||
schema_version: markitect.memory.graph.v1
|
||||
id: markitect-memory-conversation-path
|
||||
title: Markitect Memory Conversation Path
|
||||
intent: Preserve a short-term implementation episode with branch, tool, observation, and activation context.
|
||||
namespace:
|
||||
project: markitect-tool
|
||||
task: MKTT-WP-0016
|
||||
nodes:
|
||||
- id: turn.user-correction
|
||||
kind: turn
|
||||
text: The requester corrected the target from the Pragmatic State Hub workplan to MKTT-WP-0016.
|
||||
metadata:
|
||||
title: User correction
|
||||
speaker: user
|
||||
- id: interruption.direction-corrected
|
||||
kind: interruption
|
||||
text: The implementation path changed before any file edits were made.
|
||||
metadata:
|
||||
title: Direction corrected
|
||||
- id: plan.finish-memory-graph-profile
|
||||
kind: plan
|
||||
text: Finish the memory graph profile workplan by closing fixture breadth and runtime handoff gaps.
|
||||
metadata:
|
||||
title: Finish profile workplan
|
||||
- id: tool.inspect-workplan
|
||||
kind: tool_call
|
||||
text: Inspect the active MKTT-WP-0016 workplan and memory graph implementation files.
|
||||
metadata:
|
||||
title: Inspect workplan and implementation
|
||||
command_family: rg/sed
|
||||
- id: observation.remaining-gap
|
||||
kind: observation
|
||||
text: The core graph compiler and CLI are implemented; the remaining work is fixture breadth and adapter handoff guidance.
|
||||
metadata:
|
||||
title: Remaining gap
|
||||
- id: activation.contract-context
|
||||
kind: activation
|
||||
text: Activate the memory graph contract context package for follow-on implementation and cross-repo handoff.
|
||||
metadata:
|
||||
title: Contract context activation
|
||||
edges:
|
||||
- id: edge.turn-to-interruption
|
||||
kind: led_to
|
||||
source: turn.user-correction
|
||||
target: interruption.direction-corrected
|
||||
- id: edge.interruption-to-plan
|
||||
kind: led_to
|
||||
source: interruption.direction-corrected
|
||||
target: plan.finish-memory-graph-profile
|
||||
- id: edge.plan-to-tool
|
||||
kind: depends_on
|
||||
source: plan.finish-memory-graph-profile
|
||||
target: tool.inspect-workplan
|
||||
- id: edge.tool-to-observation
|
||||
kind: led_to
|
||||
source: tool.inspect-workplan
|
||||
target: observation.remaining-gap
|
||||
- id: edge.observation-to-activation
|
||||
kind: activates
|
||||
source: observation.remaining-gap
|
||||
target: activation.contract-context
|
||||
events:
|
||||
- id: event.user-correction
|
||||
kind: branched
|
||||
timestamp: "2026-05-15T08:15:00Z"
|
||||
actor: user
|
||||
thread: markitect-tool
|
||||
task: MKTT-WP-0016
|
||||
node_updates:
|
||||
- node_id: turn.user-correction
|
||||
operation: create
|
||||
- node_id: interruption.direction-corrected
|
||||
operation: create
|
||||
branch:
|
||||
from: pragmatic-state-hub
|
||||
to: MKTT-WP-0016
|
||||
metadata:
|
||||
reason: user-correction
|
||||
- id: event.contract-context-activated
|
||||
kind: activated
|
||||
timestamp: "2026-05-15T08:20:00Z"
|
||||
actor: codex
|
||||
task: MKTT-WP-0016
|
||||
node_updates:
|
||||
- node_id: activation.contract-context
|
||||
operation: create
|
||||
17
examples/memory/invalid-memory-graph.yaml
Normal file
17
examples/memory/invalid-memory-graph.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
schema_version: markitect.memory.graph.v1
|
||||
id: invalid-memory-graph
|
||||
title: Invalid Memory Graph
|
||||
intent: Exercise graph validation diagnostics for fixture consumers.
|
||||
nodes:
|
||||
- id: node.unknown-kind
|
||||
kind: unknown_kind
|
||||
text: This node kind is not part of the memory graph vocabulary.
|
||||
edges:
|
||||
- id: edge.unknown-target
|
||||
kind: supports
|
||||
source: node.unknown-kind
|
||||
target: node.missing
|
||||
events:
|
||||
- id: event.unknown-kind
|
||||
kind: unrecognized_event
|
||||
timestamp: "2026-05-15T08:35:00Z"
|
||||
11
examples/memory/invalid-memory-profile.yaml
Normal file
11
examples/memory/invalid-memory-profile.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
schema_version: markitect.memory.profile.v1
|
||||
id: invalid-memory-profile
|
||||
title: Invalid Memory Profile
|
||||
intent: Exercise profile validation diagnostics for fixture consumers.
|
||||
memory_kinds:
|
||||
- reasoning
|
||||
- telemetry
|
||||
stores:
|
||||
reasoning: local-event-log
|
||||
metadata:
|
||||
fixture: invalid-profile
|
||||
23
examples/memory/knowledge-neighborhood-selection.yaml
Normal file
23
examples/memory/knowledge-neighborhood-selection.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
schema_version: markitect.memory.selection.v1
|
||||
graph: knowledge-neighborhood.yaml
|
||||
profile: memory-profile.local.yaml
|
||||
title: Knowledge Neighborhood Package
|
||||
intent: Activate the durable contract and runtime-boundary knowledge around memory graph profiles.
|
||||
namespace:
|
||||
project: markitect-tool
|
||||
task: MKTT-WP-0016
|
||||
node_ids:
|
||||
- artifact.memory-graph-doc
|
||||
- concept.graph-profile-contract
|
||||
- capability.context-package-compile
|
||||
- contract.schema-versions
|
||||
- policy.runtime-boundary
|
||||
- source_fact.workplan-out-of-scope
|
||||
event_ids:
|
||||
- event.knowledge-refresh
|
||||
budget:
|
||||
max_items: 7
|
||||
max_tokens: 1800
|
||||
metadata:
|
||||
memory_view: knowledge
|
||||
purpose: durable-neighborhood
|
||||
94
examples/memory/knowledge-neighborhood.yaml
Normal file
94
examples/memory/knowledge-neighborhood.yaml
Normal file
@@ -0,0 +1,94 @@
|
||||
schema_version: markitect.memory.graph.v1
|
||||
id: markitect-memory-knowledge-neighborhood
|
||||
title: Markitect Memory Knowledge Neighborhood
|
||||
intent: Represent the durable knowledge neighborhood around memory graph contracts and runtime boundaries.
|
||||
namespace:
|
||||
project: markitect-tool
|
||||
task: MKTT-WP-0016
|
||||
nodes:
|
||||
- id: entity.markitect-tool
|
||||
kind: entity
|
||||
text: Markitect Tool owns deterministic Markdown-native contracts, validation, and context package compilation.
|
||||
metadata:
|
||||
title: markitect-tool
|
||||
- id: artifact.memory-graph-doc
|
||||
kind: artifact
|
||||
text: docs/memory-graph-contract.md describes the graph, profile, selection, fixture, and runtime handoff contracts.
|
||||
source_spans:
|
||||
- path: docs/memory-graph-contract.md
|
||||
unit_kind: document
|
||||
selector: document
|
||||
engine: selector
|
||||
metadata:
|
||||
title: Memory graph contract document
|
||||
- id: concept.graph-profile-contract
|
||||
kind: concept
|
||||
text: A memory profile declares runtime intent while a memory graph records nodes, edges, and event envelopes.
|
||||
metadata:
|
||||
title: Graph/profile contract
|
||||
- id: capability.context-package-compile
|
||||
kind: capability
|
||||
text: Selected graph nodes and events compile into regular ContextPackage objects with provenance metadata.
|
||||
metadata:
|
||||
title: Graph selection compiler
|
||||
- id: contract.schema-versions
|
||||
kind: contract
|
||||
text: markitect.memory.profile.v1, markitect.memory.graph.v1, and markitect.memory.selection.v1 are the stable contract versions for this slice.
|
||||
metadata:
|
||||
title: Memory schema versions
|
||||
- id: policy.runtime-boundary
|
||||
kind: policy
|
||||
text: Runtime storage, retrieval, compaction, policy enforcement, audit, and service operation stay outside markitect-tool.
|
||||
metadata:
|
||||
title: Runtime boundary policy
|
||||
- id: preference.local-deterministic
|
||||
kind: preference
|
||||
text: Keep first-version memory contract behavior deterministic, local-first, and service-free.
|
||||
metadata:
|
||||
title: Local deterministic preference
|
||||
- id: source_fact.workplan-out-of-scope
|
||||
kind: source_fact
|
||||
text: MKTT-WP-0016 explicitly excludes durable graph stores, runtime daemons, retention enforcement, and remote registries.
|
||||
source_spans:
|
||||
- path: workplans/MKTT-WP-0016-agentic-memory-graphs-and-service-blueprints.md
|
||||
unit_kind: section
|
||||
selector: heading[Out Of Scope]
|
||||
engine: selector
|
||||
metadata:
|
||||
title: Out-of-scope source fact
|
||||
edges:
|
||||
- id: edge.owner-to-doc
|
||||
kind: mentions
|
||||
source: entity.markitect-tool
|
||||
target: artifact.memory-graph-doc
|
||||
- id: edge.doc-to-contract
|
||||
kind: supports
|
||||
source: artifact.memory-graph-doc
|
||||
target: contract.schema-versions
|
||||
- id: edge.contract-to-concept
|
||||
kind: supports
|
||||
source: contract.schema-versions
|
||||
target: concept.graph-profile-contract
|
||||
- id: edge.policy-governs-compiler
|
||||
kind: governs
|
||||
source: policy.runtime-boundary
|
||||
target: capability.context-package-compile
|
||||
- id: edge.preference-supports-policy
|
||||
kind: supports
|
||||
source: preference.local-deterministic
|
||||
target: policy.runtime-boundary
|
||||
- id: edge.fact-supports-policy
|
||||
kind: supports
|
||||
source: source_fact.workplan-out-of-scope
|
||||
target: policy.runtime-boundary
|
||||
events:
|
||||
- id: event.knowledge-refresh
|
||||
kind: refreshed
|
||||
timestamp: "2026-05-15T08:30:00Z"
|
||||
actor: codex
|
||||
task: MKTT-WP-0016
|
||||
node_updates:
|
||||
- node_id: artifact.memory-graph-doc
|
||||
operation: refresh
|
||||
- node_id: contract.schema-versions
|
||||
operation: refresh
|
||||
114
examples/memory/runtime-adapter-boundaries.yaml
Normal file
114
examples/memory/runtime-adapter-boundaries.yaml
Normal file
@@ -0,0 +1,114 @@
|
||||
schema_version: markitect.memory.runtime-adapter-boundaries.v1
|
||||
id: markitect-memory-runtime-adapter-boundaries
|
||||
title: Memory Runtime Adapter Boundaries
|
||||
intent: Document non-executing handoff descriptors for external memory runtimes and stores.
|
||||
boundary:
|
||||
markitect_tool:
|
||||
owns:
|
||||
- validate memory profiles, graph snapshots, events, and selections
|
||||
- compile selected graph context into ContextPackage objects
|
||||
- emit deterministic provenance and diagnostics
|
||||
does_not_own:
|
||||
- durable graph or event persistence
|
||||
- vector search, embedding generation, or graph database queries
|
||||
- runtime retention, deletion, compaction, policy enforcement, or audit sinks
|
||||
descriptors:
|
||||
- id: memory.runtime.kontextual-engine
|
||||
kind: memory-runtime-adapter
|
||||
target_repo: kontextual-engine
|
||||
operations:
|
||||
- persist_graph_snapshot
|
||||
- append_memory_event
|
||||
- resolve_graph_selection
|
||||
- refresh_memory_profile
|
||||
input_contracts:
|
||||
- markitect.memory.profile.v1
|
||||
- markitect.memory.graph.v1
|
||||
- markitect.memory.selection.v1
|
||||
output_contracts:
|
||||
- markitect.memory.graph.v1
|
||||
- markitect.memory.selection.v1
|
||||
notes: Durable graph and event persistence belongs here, not in markitect-tool.
|
||||
- id: memory.runtime.phased-memory
|
||||
kind: memory-runtime-adapter
|
||||
target_repo: phased-memory
|
||||
operations:
|
||||
- compact_memory_window
|
||||
- enforce_retention_intent
|
||||
- produce_profile_observability
|
||||
input_contracts:
|
||||
- markitect.memory.profile.v1
|
||||
- markitect.memory.graph.v1
|
||||
output_contracts:
|
||||
- markitect.memory.graph.v1
|
||||
notes: Future phased-memory runtimes may implement lifecycle policies described by profiles.
|
||||
- id: memory.store.external-graph
|
||||
kind: graph-store-adapter
|
||||
operations:
|
||||
- query_neighborhood
|
||||
- query_path
|
||||
- resolve_edges
|
||||
input_contracts:
|
||||
- markitect.memory.graph.v1
|
||||
output_contracts:
|
||||
- markitect.memory.graph.v1
|
||||
notes: External graph databases are queried by runtime packages and returned as contract snapshots.
|
||||
- id: memory.store.vector
|
||||
kind: vector-store-adapter
|
||||
operations:
|
||||
- embed_text
|
||||
- retrieve_similar_nodes
|
||||
- return_ranked_selection
|
||||
input_contracts:
|
||||
- markitect.memory.profile.v1
|
||||
output_contracts:
|
||||
- markitect.memory.selection.v1
|
||||
notes: Embeddings and vector retrieval are optional runtime concerns.
|
||||
- id: memory.extract.llm-assisted
|
||||
kind: extraction-adapter
|
||||
operations:
|
||||
- propose_nodes
|
||||
- propose_edges
|
||||
- propose_event_summary
|
||||
input_contracts:
|
||||
- conversational transcript
|
||||
- source artifact
|
||||
output_contracts:
|
||||
- markitect.memory.graph.v1
|
||||
notes: LLM-assisted extraction must stay adapter-only and emit reviewable graph contracts.
|
||||
- id: memory.policy.enterprise-pdp
|
||||
kind: policy-adapter
|
||||
operations:
|
||||
- authorize_memory_read
|
||||
- authorize_memory_write
|
||||
- reauthorize_activation
|
||||
input_contracts:
|
||||
- profile policy block
|
||||
- graph node and edge policy metadata
|
||||
output_contracts:
|
||||
- policy decision envelope
|
||||
notes: Enterprise policy decisions are external; Markitect preserves policy metadata only.
|
||||
- id: memory.registry.remote
|
||||
kind: remote-registry-adapter
|
||||
operations:
|
||||
- publish_profile_descriptor
|
||||
- resolve_remote_package
|
||||
- list_available_memory_views
|
||||
input_contracts:
|
||||
- markitect.memory.profile.v1
|
||||
- ContextPackage metadata
|
||||
output_contracts:
|
||||
- markitect.memory.selection.v1
|
||||
notes: Remote registries are future scope and must not be required for local validation.
|
||||
- id: memory.audit.sink
|
||||
kind: audit-adapter
|
||||
operations:
|
||||
- record_memory_event
|
||||
- record_policy_decision
|
||||
- record_activation
|
||||
input_contracts:
|
||||
- markitect.memory.graph.v1 event envelope
|
||||
- policy decision envelope
|
||||
output_contracts:
|
||||
- audit receipt
|
||||
notes: Durable audit/event sinks belong to runtime or governance packages.
|
||||
@@ -21,6 +21,8 @@ from markitect_tool.contract import (
|
||||
validate_contract_file,
|
||||
)
|
||||
from markitect_tool.document_function import (
|
||||
DOCUMENT_VALUE_KINDS,
|
||||
MAX_FUNCTION_PIPELINE_DEPTH,
|
||||
DocumentFunctionCall,
|
||||
DocumentFunctionDescriptor,
|
||||
DocumentFunctionError,
|
||||
@@ -28,7 +30,11 @@ from markitect_tool.document_function import (
|
||||
DocumentFunctionParameter,
|
||||
DocumentFunctionRegistry,
|
||||
DocumentFunctionRun,
|
||||
DocumentValue,
|
||||
coerce_document_value,
|
||||
default_document_function_registry,
|
||||
document_value_to_json,
|
||||
format_document_value,
|
||||
parse_document_function_calls,
|
||||
render_document_functions,
|
||||
validate_document_functions,
|
||||
@@ -354,6 +360,8 @@ __all__ = [
|
||||
"load_contract_file",
|
||||
"validate_contract",
|
||||
"validate_contract_file",
|
||||
"DOCUMENT_VALUE_KINDS",
|
||||
"MAX_FUNCTION_PIPELINE_DEPTH",
|
||||
"DocumentFunctionCall",
|
||||
"DocumentFunctionDescriptor",
|
||||
"DocumentFunctionError",
|
||||
@@ -361,7 +369,11 @@ __all__ = [
|
||||
"DocumentFunctionParameter",
|
||||
"DocumentFunctionRegistry",
|
||||
"DocumentFunctionRun",
|
||||
"DocumentValue",
|
||||
"coerce_document_value",
|
||||
"default_document_function_registry",
|
||||
"document_value_to_json",
|
||||
"format_document_value",
|
||||
"parse_document_function_calls",
|
||||
"render_document_functions",
|
||||
"validate_document_functions",
|
||||
|
||||
@@ -23,14 +23,62 @@ FENCE_CALL_RE = re.compile(
|
||||
r"```(?P<info>[^\n`]*)\n(?P<body>.*?)\n```",
|
||||
re.DOTALL,
|
||||
)
|
||||
MAX_FUNCTION_PIPELINE_DEPTH = 12
|
||||
|
||||
FunctionImplementation = Callable[..., Any]
|
||||
|
||||
DOCUMENT_VALUE_KINDS = {
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"none",
|
||||
"markdown",
|
||||
"list",
|
||||
"dictionary",
|
||||
"record",
|
||||
"table",
|
||||
"reference",
|
||||
"content_unit",
|
||||
"unknown",
|
||||
"dynamic",
|
||||
}
|
||||
|
||||
|
||||
class DocumentFunctionError(ValueError):
|
||||
"""Raised when document function parsing or evaluation fails."""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DocumentValue:
|
||||
"""Typed value produced by a document function."""
|
||||
|
||||
kind: str
|
||||
value: Any = None
|
||||
items: list["DocumentValue"] = field(default_factory=list)
|
||||
fields: dict[str, "DocumentValue"] = field(default_factory=dict)
|
||||
metadata: dict[str, Any] = field(default_factory=dict)
|
||||
provenance: list[dict[str, Any]] = field(default_factory=list)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.kind not in DOCUMENT_VALUE_KINDS:
|
||||
raise DocumentFunctionError(f"Unknown document value kind `{self.kind}`.")
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return _drop_empty(
|
||||
{
|
||||
"kind": self.kind,
|
||||
"value": self.value,
|
||||
"items": [item.to_dict() for item in self.items],
|
||||
"fields": {key: value.to_dict() for key, value in self.fields.items()},
|
||||
"metadata": self.metadata,
|
||||
"provenance": self.provenance,
|
||||
}
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return format_document_value(self, inline=True)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DocumentFunctionParameter:
|
||||
"""One declared document function parameter."""
|
||||
@@ -107,6 +155,7 @@ class DocumentFunctionRun:
|
||||
|
||||
call: DocumentFunctionCall
|
||||
output: Any = None
|
||||
value: DocumentValue | None = None
|
||||
diagnostics: list[Diagnostic] = field(default_factory=list)
|
||||
provenance: list[ProcessingProvenance] = field(default_factory=list)
|
||||
trace: list[ProcessingTrace] = field(default_factory=list)
|
||||
@@ -120,7 +169,8 @@ class DocumentFunctionRun:
|
||||
{
|
||||
"call": self.call.to_dict(),
|
||||
"valid": self.valid,
|
||||
"output": self.output,
|
||||
"output": _serialize_output(self.output),
|
||||
"value": self.value.to_dict() if self.value else None,
|
||||
"diagnostics": [diagnostic.to_dict() for diagnostic in self.diagnostics],
|
||||
"provenance": [event.to_dict() for event in self.provenance],
|
||||
"trace": [event.to_dict() for event in self.trace],
|
||||
@@ -180,6 +230,10 @@ class DocumentFunctionRegistry:
|
||||
raise DocumentFunctionError(f"Duplicate document function `{descriptor.id}`")
|
||||
if descriptor.implementation is None:
|
||||
raise DocumentFunctionError(f"Document function `{descriptor.id}` has no implementation")
|
||||
if _normalize_output_type(descriptor.output_type) not in DOCUMENT_VALUE_KINDS:
|
||||
raise DocumentFunctionError(
|
||||
f"Document function `{descriptor.id}` declares unknown output type `{descriptor.output_type}`"
|
||||
)
|
||||
self._descriptors[descriptor.id] = descriptor
|
||||
|
||||
def get(self, function_id: str) -> DocumentFunctionDescriptor:
|
||||
@@ -208,6 +262,7 @@ class DocumentFunctionRegistry:
|
||||
) -> DocumentFunctionRun:
|
||||
context = context or ProcessingContext()
|
||||
output: Any = None
|
||||
value: DocumentValue | None = None
|
||||
diagnostics: list[Diagnostic] = []
|
||||
provenance: list[ProcessingProvenance] = []
|
||||
trace: list[ProcessingTrace] = []
|
||||
@@ -230,12 +285,15 @@ class DocumentFunctionRegistry:
|
||||
trace.extend(run.trace)
|
||||
if not run.valid:
|
||||
output = current.raw
|
||||
value = run.value
|
||||
break
|
||||
output = run.output
|
||||
value = run.value
|
||||
|
||||
return DocumentFunctionRun(
|
||||
call=call,
|
||||
output=output,
|
||||
value=value,
|
||||
diagnostics=diagnostics,
|
||||
provenance=provenance,
|
||||
trace=trace,
|
||||
@@ -281,6 +339,10 @@ class DocumentFunctionRegistry:
|
||||
else:
|
||||
assert descriptor.implementation is not None
|
||||
output = descriptor.implementation(*args, **kwargs)
|
||||
value = coerce_document_value(output, declared_kind=descriptor.output_type)
|
||||
value_diagnostic = _validate_output_value(descriptor, value, call, context)
|
||||
if value_diagnostic is not None:
|
||||
return DocumentFunctionRun(call=call, output=output, value=value, diagnostics=[value_diagnostic])
|
||||
except Exception as exc:
|
||||
return _call_error(call, "function.evaluation_failed", str(exc), context)
|
||||
|
||||
@@ -301,7 +363,83 @@ class DocumentFunctionRegistry:
|
||||
metadata={"function": descriptor.id, "line": call.line},
|
||||
)
|
||||
]
|
||||
return DocumentFunctionRun(call=call, output=output, provenance=provenance, trace=trace)
|
||||
return DocumentFunctionRun(call=call, output=output, value=value, provenance=provenance, trace=trace)
|
||||
|
||||
|
||||
def coerce_document_value(value: Any, *, declared_kind: str = "dynamic") -> DocumentValue:
|
||||
"""Coerce a Python value into a typed document value."""
|
||||
|
||||
normalized_kind = _normalize_output_type(declared_kind)
|
||||
if isinstance(value, DocumentValue):
|
||||
if normalized_kind in {"dynamic", "any"} or _value_matches_kind(value, normalized_kind):
|
||||
return value
|
||||
return DocumentValue(kind="unknown", value=value.to_dict(), metadata={"declared_kind": normalized_kind})
|
||||
if normalized_kind == "dynamic" or normalized_kind == "any":
|
||||
return _infer_document_value(value)
|
||||
if normalized_kind == "markdown":
|
||||
if isinstance(value, str):
|
||||
return DocumentValue(kind="markdown", value=value)
|
||||
if normalized_kind == "string":
|
||||
if isinstance(value, str):
|
||||
return DocumentValue(kind="string", value=value)
|
||||
if normalized_kind == "number":
|
||||
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
||||
return DocumentValue(kind="number", value=value)
|
||||
if normalized_kind == "boolean":
|
||||
if isinstance(value, bool):
|
||||
return DocumentValue(kind="boolean", value=value)
|
||||
if normalized_kind == "none":
|
||||
if value is None:
|
||||
return DocumentValue(kind="none")
|
||||
if normalized_kind == "list":
|
||||
if isinstance(value, list):
|
||||
return DocumentValue(kind="list", items=[coerce_document_value(item) for item in value])
|
||||
if normalized_kind in {"dictionary", "record"}:
|
||||
if isinstance(value, dict):
|
||||
return DocumentValue(
|
||||
kind=normalized_kind,
|
||||
fields={str(key): coerce_document_value(raw) for key, raw in value.items()},
|
||||
)
|
||||
if normalized_kind == "table":
|
||||
if isinstance(value, list):
|
||||
return DocumentValue(kind="table", items=[coerce_document_value(item, declared_kind="record") for item in value])
|
||||
if normalized_kind in {"reference", "content_unit", "unknown"}:
|
||||
return DocumentValue(kind=normalized_kind, value=value)
|
||||
return DocumentValue(kind="unknown", value=value, metadata={"declared_kind": normalized_kind})
|
||||
|
||||
|
||||
def format_document_value(value: DocumentValue | Any, *, inline: bool) -> str:
|
||||
"""Map a typed document value to deterministic Markdown text."""
|
||||
|
||||
document_value = value if isinstance(value, DocumentValue) else coerce_document_value(value)
|
||||
if document_value.kind in {"markdown", "string"}:
|
||||
return str(document_value.value or "")
|
||||
if document_value.kind == "number":
|
||||
return str(document_value.value)
|
||||
if document_value.kind == "boolean":
|
||||
return "true" if document_value.value else "false"
|
||||
if document_value.kind == "none":
|
||||
return ""
|
||||
if document_value.kind == "list":
|
||||
rendered = [format_document_value(item, inline=inline) for item in document_value.items]
|
||||
return ", ".join(rendered) if inline else "\n".join(rendered)
|
||||
if document_value.kind in {"dictionary", "record"}:
|
||||
return json.dumps(_document_value_to_plain(document_value), sort_keys=True, ensure_ascii=False)
|
||||
if document_value.kind == "table":
|
||||
return _format_table_value(document_value)
|
||||
if document_value.kind in {"reference", "content_unit"}:
|
||||
label = document_value.metadata.get("label") or document_value.metadata.get("title")
|
||||
return str(label or document_value.value or "")
|
||||
if document_value.kind == "dynamic":
|
||||
return format_document_value(coerce_document_value(document_value.value), inline=inline)
|
||||
return "" if document_value.value is None else str(document_value.value)
|
||||
|
||||
|
||||
def document_value_to_json(value: DocumentValue | Any) -> dict[str, Any]:
|
||||
"""Return the stable JSON-compatible representation of a document value."""
|
||||
|
||||
document_value = value if isinstance(value, DocumentValue) else coerce_document_value(value)
|
||||
return document_value.to_dict()
|
||||
|
||||
|
||||
def default_document_function_registry() -> DocumentFunctionRegistry:
|
||||
@@ -314,6 +452,7 @@ def default_document_function_registry() -> DocumentFunctionRegistry:
|
||||
"Uppercase text.",
|
||||
_text_upper,
|
||||
[DocumentFunctionParameter("value")],
|
||||
output_type="string",
|
||||
examples=['{{mkt:text.upper "draft"}}'],
|
||||
),
|
||||
_descriptor(
|
||||
@@ -321,6 +460,7 @@ def default_document_function_registry() -> DocumentFunctionRegistry:
|
||||
"Lowercase text.",
|
||||
_text_lower,
|
||||
[DocumentFunctionParameter("value")],
|
||||
output_type="string",
|
||||
examples=['{{mkt:text.lower "DRAFT"}}'],
|
||||
),
|
||||
_descriptor(
|
||||
@@ -328,6 +468,7 @@ def default_document_function_registry() -> DocumentFunctionRegistry:
|
||||
"Title-case text.",
|
||||
_text_title,
|
||||
[DocumentFunctionParameter("value")],
|
||||
output_type="string",
|
||||
examples=['{{mkt:text.title "release notes"}}'],
|
||||
),
|
||||
_descriptor(
|
||||
@@ -335,6 +476,7 @@ def default_document_function_registry() -> DocumentFunctionRegistry:
|
||||
"Trim surrounding whitespace.",
|
||||
_text_trim,
|
||||
[DocumentFunctionParameter("value")],
|
||||
output_type="string",
|
||||
examples=['{{mkt:text.trim " ok "}}'],
|
||||
),
|
||||
_descriptor(
|
||||
@@ -346,6 +488,7 @@ def default_document_function_registry() -> DocumentFunctionRegistry:
|
||||
DocumentFunctionParameter("old"),
|
||||
DocumentFunctionParameter("new"),
|
||||
],
|
||||
output_type="string",
|
||||
examples=['{{mkt:text.replace "draft" draft final}}'],
|
||||
),
|
||||
_descriptor(
|
||||
@@ -356,6 +499,7 @@ def default_document_function_registry() -> DocumentFunctionRegistry:
|
||||
DocumentFunctionParameter("items", variadic=True),
|
||||
DocumentFunctionParameter("sep", required=False, default=""),
|
||||
],
|
||||
output_type="string",
|
||||
examples=['{{mkt:text.join "A" "B" sep=", "}}'],
|
||||
),
|
||||
_descriptor(
|
||||
@@ -398,6 +542,7 @@ def default_document_function_registry() -> DocumentFunctionRegistry:
|
||||
"Read a value from processing context variables.",
|
||||
_data_get,
|
||||
[DocumentFunctionParameter("key"), DocumentFunctionParameter("default", required=False, default="")],
|
||||
output_type="dynamic",
|
||||
examples=["{{mkt:data.get title}}"],
|
||||
),
|
||||
]
|
||||
@@ -460,7 +605,7 @@ def render_document_functions(
|
||||
trace.extend(run.trace)
|
||||
if not run.valid:
|
||||
return match.group(0)
|
||||
return _format_function_output(run.output, inline=True)
|
||||
return _format_function_output(run.value or run.output, inline=True)
|
||||
|
||||
content = INLINE_CALL_RE.sub(replace_inline, text)
|
||||
|
||||
@@ -483,7 +628,7 @@ def render_document_functions(
|
||||
trace.extend(run.trace)
|
||||
if not run.valid:
|
||||
return match.group(0)
|
||||
return _format_function_output(run.output, inline=False)
|
||||
return _format_function_output(run.value or run.output, inline=False)
|
||||
|
||||
content = FENCE_CALL_RE.sub(replace_fence, content)
|
||||
trace.append(ProcessingTrace(event="document_function.rendered", metadata={"calls": len(runs)}))
|
||||
@@ -554,6 +699,10 @@ def _parse_call_expression(
|
||||
pipeline_parts = _split_pipeline_expression(expression)
|
||||
if not pipeline_parts:
|
||||
raise DocumentFunctionError("Document function call is empty.")
|
||||
if len(pipeline_parts) > MAX_FUNCTION_PIPELINE_DEPTH:
|
||||
raise DocumentFunctionError(
|
||||
f"Document function pipeline exceeds maximum depth {MAX_FUNCTION_PIPELINE_DEPTH}."
|
||||
)
|
||||
first = _parse_single_call(pipeline_parts[0], raw=raw, inline=inline, line=line, body=body)
|
||||
pipeline = [
|
||||
_parse_single_call(part, raw=part, inline=inline, line=line)
|
||||
@@ -712,6 +861,109 @@ def _blocked_capabilities(
|
||||
return sorted(set(blocked))
|
||||
|
||||
|
||||
def _normalize_output_type(output_type: str | None) -> str:
|
||||
aliases = {
|
||||
None: "dynamic",
|
||||
"": "dynamic",
|
||||
"any": "dynamic",
|
||||
"integer": "number",
|
||||
"float": "number",
|
||||
"bool": "boolean",
|
||||
"dict": "dictionary",
|
||||
"map": "dictionary",
|
||||
"markdown_content": "markdown",
|
||||
"content-unit": "content_unit",
|
||||
}
|
||||
normalized = str(output_type or "dynamic").strip().lower().replace("-", "_")
|
||||
return aliases.get(normalized, normalized)
|
||||
|
||||
|
||||
def _infer_document_value(value: Any) -> DocumentValue:
|
||||
if isinstance(value, DocumentValue):
|
||||
return value
|
||||
if value is None:
|
||||
return DocumentValue(kind="none")
|
||||
if isinstance(value, bool):
|
||||
return DocumentValue(kind="boolean", value=value)
|
||||
if isinstance(value, (int, float)):
|
||||
return DocumentValue(kind="number", value=value)
|
||||
if isinstance(value, str):
|
||||
return DocumentValue(kind="string", value=value)
|
||||
if isinstance(value, list):
|
||||
return DocumentValue(kind="list", items=[coerce_document_value(item) for item in value])
|
||||
if isinstance(value, dict):
|
||||
return DocumentValue(
|
||||
kind="dictionary",
|
||||
fields={str(key): coerce_document_value(raw) for key, raw in value.items()},
|
||||
)
|
||||
return DocumentValue(kind="unknown", value=str(value), metadata={"python_type": type(value).__name__})
|
||||
|
||||
|
||||
def _value_matches_kind(value: DocumentValue, expected_kind: str) -> bool:
|
||||
if expected_kind in {"dynamic", "any"}:
|
||||
return True
|
||||
if expected_kind == "markdown":
|
||||
return value.kind in {"markdown", "string"}
|
||||
if expected_kind == "dictionary":
|
||||
return value.kind in {"dictionary", "record"}
|
||||
return value.kind == expected_kind
|
||||
|
||||
|
||||
def _validate_output_value(
|
||||
descriptor: DocumentFunctionDescriptor,
|
||||
value: DocumentValue,
|
||||
call: DocumentFunctionCall,
|
||||
context: ProcessingContext,
|
||||
) -> Diagnostic | None:
|
||||
expected = _normalize_output_type(descriptor.output_type)
|
||||
if value.kind == "unknown":
|
||||
return _output_diagnostic(
|
||||
call,
|
||||
"function.output_type_mismatch",
|
||||
f"Function `{descriptor.id}` returned a value that does not match output type `{expected}`.",
|
||||
context,
|
||||
{"function": descriptor.id, "output_type": expected, "value_kind": value.kind},
|
||||
)
|
||||
if descriptor.execution == "deterministic" and value.kind == "dynamic":
|
||||
return _output_diagnostic(
|
||||
call,
|
||||
"function.dynamic_output",
|
||||
f"Function `{descriptor.id}` returned a dynamic value in a deterministic context.",
|
||||
context,
|
||||
{"function": descriptor.id, "output_type": expected},
|
||||
)
|
||||
if value.kind in {"reference", "content_unit"} and not value.provenance:
|
||||
return _output_diagnostic(
|
||||
call,
|
||||
"function.provenance_missing",
|
||||
f"Function `{descriptor.id}` returned `{value.kind}` without provenance.",
|
||||
context,
|
||||
{"function": descriptor.id, "value_kind": value.kind},
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def _output_diagnostic(
|
||||
call: DocumentFunctionCall,
|
||||
code: str,
|
||||
message: str,
|
||||
context: ProcessingContext,
|
||||
details: dict[str, Any],
|
||||
) -> Diagnostic:
|
||||
return Diagnostic(
|
||||
severity="error",
|
||||
code=code,
|
||||
message=message,
|
||||
source=SourceLocation(
|
||||
path=str(context.source_path) if context.source_path else None,
|
||||
line=call.line,
|
||||
)
|
||||
if context.source_path or call.line
|
||||
else None,
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
def _resolve_value(value: Any, context: ProcessingContext) -> Any:
|
||||
if isinstance(value, str):
|
||||
if value.startswith("${") and value.endswith("}"):
|
||||
@@ -721,13 +973,59 @@ def _resolve_value(value: Any, context: ProcessingContext) -> Any:
|
||||
|
||||
|
||||
def _format_function_output(value: Any, *, inline: bool) -> str:
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
return format_document_value(value, inline=inline)
|
||||
|
||||
|
||||
def _document_value_to_plain(value: DocumentValue) -> Any:
|
||||
if value.kind in {"string", "number", "boolean", "markdown", "reference", "content_unit", "unknown", "dynamic"}:
|
||||
return value.value
|
||||
if value.kind == "none":
|
||||
return None
|
||||
if value.kind in {"list", "table"}:
|
||||
return [_document_value_to_plain(item) for item in value.items]
|
||||
if value.kind in {"dictionary", "record"}:
|
||||
return {key: _document_value_to_plain(raw) for key, raw in value.fields.items()}
|
||||
return value.value
|
||||
|
||||
|
||||
def _format_table_value(value: DocumentValue) -> str:
|
||||
rows = [item for item in value.items if item.kind in {"record", "dictionary"}]
|
||||
if not rows:
|
||||
return ""
|
||||
columns: list[str] = []
|
||||
for row in rows:
|
||||
for key in row.fields:
|
||||
if key not in columns:
|
||||
columns.append(key)
|
||||
if not columns:
|
||||
return ""
|
||||
header = "| " + " | ".join(_escape_table_cell(column) for column in columns) + " |"
|
||||
separator = "| " + " | ".join("---" for _ in columns) + " |"
|
||||
body = []
|
||||
for row in rows:
|
||||
body.append(
|
||||
"| "
|
||||
+ " | ".join(
|
||||
_escape_table_cell(format_document_value(row.fields.get(column, DocumentValue(kind="none")), inline=True))
|
||||
for column in columns
|
||||
)
|
||||
+ " |"
|
||||
)
|
||||
return "\n".join([header, separator, *body])
|
||||
|
||||
|
||||
def _escape_table_cell(value: str) -> str:
|
||||
return value.replace("|", "\\|").replace("\n", " ").strip()
|
||||
|
||||
|
||||
def _serialize_output(value: Any) -> Any:
|
||||
if isinstance(value, DocumentValue):
|
||||
return value.to_dict()
|
||||
if isinstance(value, list):
|
||||
return ", ".join(str(item) for item in value) if inline else "\n".join(str(item) for item in value)
|
||||
return [_serialize_output(item) for item in value]
|
||||
if isinstance(value, dict):
|
||||
return json.dumps(value, sort_keys=True, ensure_ascii=False)
|
||||
return "" if value is None else str(value)
|
||||
return {str(key): _serialize_output(raw) for key, raw in value.items()}
|
||||
return value
|
||||
|
||||
|
||||
def _parse_literal(value: str) -> Any:
|
||||
|
||||
@@ -26,6 +26,7 @@ def builtin_extension_registry() -> ExtensionRegistry:
|
||||
_local_label_policy_descriptor(),
|
||||
_document_function_descriptor(),
|
||||
_memory_graph_contract_descriptor(),
|
||||
_memory_runtime_adapter_descriptor(),
|
||||
_agent_memory_descriptor(),
|
||||
source_adapter_registry_descriptor(),
|
||||
]:
|
||||
@@ -319,6 +320,23 @@ def _document_function_descriptor() -> ExtensionDescriptor:
|
||||
metadata={
|
||||
"execution": "deterministic-only",
|
||||
"external_policy_services_required": False,
|
||||
"typed_values": True,
|
||||
"value_kinds": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"none",
|
||||
"markdown",
|
||||
"list",
|
||||
"dictionary",
|
||||
"record",
|
||||
"table",
|
||||
"reference",
|
||||
"content_unit",
|
||||
"unknown",
|
||||
"dynamic",
|
||||
],
|
||||
"render_export_execution": False,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -412,6 +430,13 @@ def _memory_graph_contract_descriptor() -> ExtensionDescriptor:
|
||||
"examples/memory/memory-profile.local.yaml",
|
||||
"examples/memory/decision-graph.yaml",
|
||||
"examples/memory/decision-graph-selection.yaml",
|
||||
"examples/memory/conversation-path.yaml",
|
||||
"examples/memory/conversation-path-selection.yaml",
|
||||
"examples/memory/knowledge-neighborhood.yaml",
|
||||
"examples/memory/knowledge-neighborhood-selection.yaml",
|
||||
"examples/memory/invalid-memory-graph.yaml",
|
||||
"examples/memory/invalid-memory-profile.yaml",
|
||||
"examples/memory/runtime-adapter-boundaries.yaml",
|
||||
],
|
||||
metadata={
|
||||
"schema_versions": [
|
||||
@@ -422,7 +447,59 @@ def _memory_graph_contract_descriptor() -> ExtensionDescriptor:
|
||||
"runtime_execution_required": False,
|
||||
"runtime_handoff_repositories": [
|
||||
"kontextual-engine",
|
||||
"phased-memory",
|
||||
"infospace-bench",
|
||||
],
|
||||
"runtime_adapter_boundaries": [
|
||||
"memory.runtime.kontextual-engine",
|
||||
"memory.runtime.phased-memory",
|
||||
"memory.store.external-graph",
|
||||
"memory.store.vector",
|
||||
"memory.extract.llm-assisted",
|
||||
"memory.policy.enterprise-pdp",
|
||||
"memory.registry.remote",
|
||||
"memory.audit.sink",
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _memory_runtime_adapter_descriptor() -> ExtensionDescriptor:
|
||||
return ExtensionDescriptor(
|
||||
id="memory.runtime-adapter-boundary",
|
||||
kind="memory-runtime-adapter",
|
||||
summary="Non-executing handoff descriptors for external memory runtimes, stores, extraction, policy, and audit.",
|
||||
capabilities=[
|
||||
ProcessingCapability(id="memory_runtime_adapters", kind="describe"),
|
||||
ProcessingCapability(id="memory_graphs", kind="handoff"),
|
||||
ProcessingCapability(id="memory_events", kind="handoff"),
|
||||
ProcessingCapability(id="context_packages", kind="handoff"),
|
||||
ProcessingCapability(id="policy_decisions", kind="handoff"),
|
||||
],
|
||||
safety={
|
||||
"reads_files": False,
|
||||
"writes_files": False,
|
||||
"network": False,
|
||||
"launches_services": False,
|
||||
"runtime_execution": False,
|
||||
},
|
||||
input_contract="MemoryProfile | MemoryGraph | MemoryEvent | MemoryGraphSelection | ContextPackage metadata",
|
||||
output_contract="External runtime/store/policy/audit adapter descriptor",
|
||||
diagnostics_namespace="memory.runtime_adapter",
|
||||
provenance_prefix="memory.runtime_adapter_boundary",
|
||||
docs=["docs/memory-graph-contract.md"],
|
||||
examples=["examples/memory/runtime-adapter-boundaries.yaml"],
|
||||
metadata={
|
||||
"descriptor_catalog": "examples/memory/runtime-adapter-boundaries.yaml",
|
||||
"markitect_role": "contract-validation-and-context-package-compilation",
|
||||
"external_runtime_roles": [
|
||||
"durable graph and event persistence",
|
||||
"graph and vector retrieval",
|
||||
"LLM-assisted graph extraction",
|
||||
"policy enforcement and reauthorization",
|
||||
"remote registry coordination",
|
||||
"audit and event sinks",
|
||||
],
|
||||
"services_launched_by_markitect_tool": False,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -31,6 +31,7 @@ MEMORY_NODE_KINDS = {
|
||||
"assumption",
|
||||
"alternative",
|
||||
"artifact",
|
||||
"activation",
|
||||
"capability",
|
||||
"claim",
|
||||
"concept",
|
||||
@@ -45,6 +46,7 @@ MEMORY_NODE_KINDS = {
|
||||
"evidence",
|
||||
"finding",
|
||||
"follow_up",
|
||||
"interruption",
|
||||
"memory",
|
||||
"observation",
|
||||
"outcome",
|
||||
|
||||
@@ -21,6 +21,8 @@ def test_builtin_extension_registry_lists_query_processors_and_backend():
|
||||
assert "runtime.assessment" in ids
|
||||
assert "policy.local-label" in ids
|
||||
assert "document.function" in ids
|
||||
assert "memory.graph-contract" in ids
|
||||
assert "memory.runtime-adapter-boundary" in ids
|
||||
assert "memory.context-package" in ids
|
||||
assert "source.adapter-registry" in ids
|
||||
|
||||
@@ -131,6 +133,9 @@ def test_builtin_document_function_descriptor_exposes_deterministic_boundary():
|
||||
assert descriptor.kind == "document-function"
|
||||
assert descriptor.safety["network"] is False
|
||||
assert descriptor.metadata["external_policy_services_required"] is False
|
||||
assert descriptor.metadata["typed_values"] is True
|
||||
assert "table" in descriptor.metadata["value_kinds"]
|
||||
assert descriptor.metadata["render_export_execution"] is False
|
||||
assert {capability.id for capability in descriptor.capabilities} >= {
|
||||
"document_function",
|
||||
"deterministic",
|
||||
@@ -158,3 +163,22 @@ def test_builtin_memory_descriptor_exposes_local_optional_boundary():
|
||||
}
|
||||
assert "mkt context pack" in descriptor.cli["commands"]
|
||||
assert "mkt context activate" in descriptor.cli["commands"]
|
||||
|
||||
|
||||
def test_builtin_memory_graph_descriptor_exposes_runtime_handoff_boundaries():
|
||||
registry = builtin_extension_registry()
|
||||
|
||||
graph = registry.get("memory.graph-contract")
|
||||
adapters = registry.get("memory.runtime-adapter-boundary")
|
||||
|
||||
assert graph.kind == "memory-contract"
|
||||
assert graph.safety["external_memory_services"] is False
|
||||
assert "mkt memory graph pack" in graph.cli["commands"]
|
||||
assert "examples/memory/conversation-path.yaml" in graph.examples
|
||||
assert "examples/memory/knowledge-neighborhood.yaml" in graph.examples
|
||||
assert "memory.runtime.kontextual-engine" in graph.metadata["runtime_adapter_boundaries"]
|
||||
assert "memory.audit.sink" in graph.metadata["runtime_adapter_boundaries"]
|
||||
assert adapters.kind == "memory-runtime-adapter"
|
||||
assert adapters.safety["runtime_execution"] is False
|
||||
assert adapters.metadata["services_launched_by_markitect_tool"] is False
|
||||
assert "examples/memory/runtime-adapter-boundaries.yaml" in adapters.examples
|
||||
|
||||
@@ -19,6 +19,7 @@ def test_collect_cli_command_specs_from_builtin_registry():
|
||||
assert ("backend.local-sqlite", "mkt cache index") in commands
|
||||
assert ("backend.local-sqlite", "mkt search") in commands
|
||||
assert ("document.function", "mkt function render") in commands
|
||||
assert ("memory.graph-contract", "mkt memory graph pack") in commands
|
||||
assert ("memory.context-package", "mkt context pack") in commands
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,12 @@ from markitect_tool.document_function import (
|
||||
DocumentFunctionDescriptor,
|
||||
DocumentFunctionParameter,
|
||||
DocumentFunctionRegistry,
|
||||
DocumentValue,
|
||||
MAX_FUNCTION_PIPELINE_DEPTH,
|
||||
coerce_document_value,
|
||||
default_document_function_registry,
|
||||
document_value_to_json,
|
||||
format_document_value,
|
||||
parse_document_function_calls,
|
||||
render_document_functions,
|
||||
validate_document_functions,
|
||||
@@ -51,6 +56,20 @@ Decision
|
||||
assert "### Decision" in result.content
|
||||
assert len(result.calls) == 2
|
||||
assert result.provenance[0].operation == "document_function.text.upper"
|
||||
assert result.calls[0].value.kind == "string"
|
||||
assert result.calls[1].value.kind == "markdown"
|
||||
|
||||
|
||||
def test_document_values_coerce_and_map_to_markdown():
|
||||
table = coerce_document_value(
|
||||
[{"name": "Ada", "role": "Architect"}, {"name": "Grace", "role": "Reviewer"}],
|
||||
declared_kind="table",
|
||||
)
|
||||
|
||||
assert document_value_to_json(table)["kind"] == "table"
|
||||
assert format_document_value(DocumentValue(kind="boolean", value=True), inline=True) == "true"
|
||||
assert "| name | role |" in format_document_value(table, inline=False)
|
||||
assert "| Ada | Architect |" in format_document_value(table, inline=False)
|
||||
|
||||
|
||||
def test_pipeline_passes_previous_output_to_next_function():
|
||||
@@ -67,6 +86,17 @@ def test_pipeline_separator_inside_quotes_is_literal():
|
||||
assert result.content == "a/b"
|
||||
|
||||
|
||||
def test_pipeline_depth_limit_reports_syntax_error():
|
||||
expression = " | ".join(["text.trim value"] * (MAX_FUNCTION_PIPELINE_DEPTH + 1))
|
||||
|
||||
try:
|
||||
parse_document_function_calls("{{mkt:" + expression + "}}")
|
||||
except Exception as exc:
|
||||
assert "maximum depth" in str(exc)
|
||||
else:
|
||||
raise AssertionError("Expected pipeline depth diagnostic")
|
||||
|
||||
|
||||
def test_context_variables_can_be_used_in_function_arguments():
|
||||
context = ProcessingContext(variables={"title": "Architecture Decision"})
|
||||
|
||||
@@ -75,6 +105,16 @@ def test_context_variables_can_be_used_in_function_arguments():
|
||||
assert result.content == "## Architecture Decision"
|
||||
|
||||
|
||||
def test_dynamic_context_values_render_through_typed_mapper():
|
||||
context = ProcessingContext(variables={"items": ["alpha", "beta"]})
|
||||
|
||||
result = render_document_functions("{{mkt:data.get items}}", context=context)
|
||||
|
||||
assert result.valid
|
||||
assert result.content == "alpha, beta"
|
||||
assert result.calls[0].value.kind == "list"
|
||||
|
||||
|
||||
def test_validate_document_functions_reports_forbidden_calls():
|
||||
result = validate_document_functions("{{mkt:text.upper draft}}", forbidden=["text.upper"])
|
||||
|
||||
@@ -106,6 +146,41 @@ def test_registry_can_expose_custom_function_without_core_rewrite():
|
||||
assert result.content == "[ok]"
|
||||
|
||||
|
||||
def test_descriptor_output_type_mismatch_is_diagnostic():
|
||||
registry = DocumentFunctionRegistry()
|
||||
registry.register(
|
||||
DocumentFunctionDescriptor(
|
||||
id="demo.count",
|
||||
summary="Return a count.",
|
||||
output_type="number",
|
||||
implementation=lambda: "not-a-number",
|
||||
)
|
||||
)
|
||||
|
||||
result = render_document_functions("{{mkt:demo.count}}", registry=registry)
|
||||
|
||||
assert not result.valid
|
||||
assert result.content == "{{mkt:demo.count}}"
|
||||
assert result.diagnostics[0].code == "function.output_type_mismatch"
|
||||
|
||||
|
||||
def test_reference_values_require_provenance():
|
||||
registry = DocumentFunctionRegistry()
|
||||
registry.register(
|
||||
DocumentFunctionDescriptor(
|
||||
id="demo.reference",
|
||||
summary="Return a reference.",
|
||||
output_type="reference",
|
||||
implementation=lambda: DocumentValue(kind="reference", value="std:clause.md#payment"),
|
||||
)
|
||||
)
|
||||
|
||||
result = render_document_functions("{{mkt:demo.reference}}", registry=registry)
|
||||
|
||||
assert not result.valid
|
||||
assert result.diagnostics[0].code == "function.provenance_missing"
|
||||
|
||||
|
||||
def test_unknown_function_is_left_in_place_with_diagnostic():
|
||||
result = render_document_functions("{{mkt:nope.missing value}}")
|
||||
|
||||
@@ -133,6 +208,17 @@ def test_mkt_function_render_outputs_expanded_markdown(tmp_path: Path):
|
||||
assert "**Important**" in result.output
|
||||
|
||||
|
||||
def test_mkt_function_render_json_includes_typed_values(tmp_path: Path):
|
||||
file = tmp_path / "functions.md"
|
||||
file.write_text("{{mkt:text.upper draft}}\n", encoding="utf-8")
|
||||
|
||||
result = CliRunner().invoke(main, ["function", "render", str(file), "--format", "json"])
|
||||
data = json.loads(result.output)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert data["calls"][0]["value"] == {"kind": "string", "value": "DRAFT"}
|
||||
|
||||
|
||||
def test_mkt_function_check_can_restrict_allowed_functions(tmp_path: Path):
|
||||
file = tmp_path / "functions.md"
|
||||
file.write_text("{{mkt:text.upper draft}}\n", encoding="utf-8")
|
||||
@@ -148,3 +234,4 @@ def test_default_registry_serializes_without_implementations():
|
||||
|
||||
assert data["count"] >= 1
|
||||
assert "implementation" not in data["functions"][0]
|
||||
assert {function["id"]: function["output_type"] for function in data["functions"]}["data.get"] == "dynamic"
|
||||
|
||||
@@ -48,6 +48,62 @@ def test_memory_graph_validation_and_context_package_compile(tmp_path: Path):
|
||||
assert package.retrieval_recipes[0].kind == "memory-graph-selection"
|
||||
|
||||
|
||||
def test_memory_graph_example_fixtures_cover_reasoning_conversation_and_knowledge():
|
||||
examples = Path("examples/memory")
|
||||
profile = load_memory_profile_file(examples / "memory-profile.local.yaml")
|
||||
|
||||
for graph_name, selection_name, package_title, expected_node_count in [
|
||||
(
|
||||
"decision-graph.yaml",
|
||||
"decision-graph-selection.yaml",
|
||||
"Memory Contract Boundary Package",
|
||||
3,
|
||||
),
|
||||
(
|
||||
"conversation-path.yaml",
|
||||
"conversation-path-selection.yaml",
|
||||
"Conversation Episode Package",
|
||||
4,
|
||||
),
|
||||
(
|
||||
"knowledge-neighborhood.yaml",
|
||||
"knowledge-neighborhood-selection.yaml",
|
||||
"Knowledge Neighborhood Package",
|
||||
6,
|
||||
),
|
||||
]:
|
||||
graph = load_memory_graph_file(examples / graph_name)
|
||||
selection = load_memory_graph_selection_file(examples / selection_name)
|
||||
validation = validate_memory_graph(graph, path=examples / graph_name)
|
||||
package = compile_memory_graph_selection_to_context_package(graph, selection, profile=profile)
|
||||
|
||||
assert validation.valid, validation.to_dict()
|
||||
assert package.title == package_title
|
||||
assert package.metadata["memory_graph"]["selected_nodes"] == selection.node_ids
|
||||
assert len(selection.node_ids) == expected_node_count
|
||||
assert package.metadata["memory_profile"]["id"] == "local-agent-memory"
|
||||
|
||||
|
||||
def test_memory_graph_invalid_example_fixtures_emit_errors():
|
||||
examples = Path("examples/memory")
|
||||
graph = load_memory_graph_file(examples / "invalid-memory-graph.yaml")
|
||||
profile = load_memory_profile_file(examples / "invalid-memory-profile.yaml")
|
||||
|
||||
graph_result = validate_memory_graph(graph, path=examples / "invalid-memory-graph.yaml")
|
||||
profile_result = validate_memory_profile(profile, path=examples / "invalid-memory-profile.yaml")
|
||||
|
||||
graph_codes = {diagnostic.code for diagnostic in graph_result.diagnostics}
|
||||
profile_codes = {diagnostic.code for diagnostic in profile_result.diagnostics}
|
||||
|
||||
assert not graph_result.valid
|
||||
assert "memory.graph.node.unknown_kind" in graph_codes
|
||||
assert "memory.graph.edge.unknown_target" in graph_codes
|
||||
assert "memory.graph.event.unknown_kind" in graph_codes
|
||||
assert not profile_result.valid
|
||||
assert "memory.profile.unknown_kind" in profile_codes
|
||||
assert "memory.profile.store_missing" in profile_codes
|
||||
|
||||
|
||||
def test_mkt_memory_blueprint_and_graph_cli(tmp_path: Path):
|
||||
_write_profile(tmp_path)
|
||||
_write_graph(tmp_path)
|
||||
|
||||
@@ -1,222 +1,221 @@
|
||||
---
|
||||
id: MKTT-WP-0015
|
||||
type: workplan
|
||||
title: "Render And Document Function Extensions"
|
||||
title: "Document Function Value Contracts"
|
||||
domain: markitect
|
||||
status: todo
|
||||
status: done
|
||||
owner: markitect-tool
|
||||
topic_slug: markitect
|
||||
planning_priority: P2
|
||||
planning_priority: complete
|
||||
planning_order: 130
|
||||
depends_on_workplans:
|
||||
- MKTT-WP-0010
|
||||
- MKTT-WP-0011
|
||||
- MKTT-WP-0012
|
||||
related_workplans:
|
||||
- MKTT-WP-0007
|
||||
- MKTT-WP-0008
|
||||
- MKTT-WP-0009
|
||||
- MKTT-WP-0013
|
||||
- KONT-WP-0017
|
||||
- IB-WP-0017
|
||||
- MKTT-WP-0020
|
||||
- MKTT-WP-0021
|
||||
- MKTF-WP-0003
|
||||
- MQD-WP-0001
|
||||
created: "2026-05-04"
|
||||
updated: "2026-05-04"
|
||||
updated: "2026-05-15"
|
||||
state_hub_workstream_id: "a38f676a-0d0b-493c-9792-2e34480c3681"
|
||||
---
|
||||
|
||||
# MKTT-WP-0015: Render And Document Function Extensions
|
||||
# MKTT-WP-0015: Document Function Value Contracts
|
||||
|
||||
## Purpose
|
||||
|
||||
Capture the natural follow-on work from the Quarkdown comparison and the first
|
||||
Markitect document-function layer.
|
||||
Split the original render-and-function extension plan into a small,
|
||||
Markitect-native core slice: typed document-function values and deterministic
|
||||
value mapping.
|
||||
|
||||
The current function layer is intentionally small: deterministic functions,
|
||||
Markdown-native explicit syntax, local context variables, diagnostics,
|
||||
provenance, and capability metadata. This workplan should extend that model
|
||||
only when the need is concrete, keeping the core framework clean and avoiding a
|
||||
second workflow engine.
|
||||
The current document-function layer is intentionally small: deterministic
|
||||
functions, Markdown-native explicit syntax, local context variables,
|
||||
diagnostics, provenance, and capability metadata. This workplan should make
|
||||
function outputs more explicit without turning Markitect into a publishing
|
||||
language, renderer, or second workflow engine.
|
||||
|
||||
## Background
|
||||
## Split Decision - 2026-05-15
|
||||
|
||||
Quarkdown shows the value of a document language where functions are not just
|
||||
macros. They can return typed values, Markdown content, layout structures,
|
||||
tables, dictionaries, booleans, and renderable nodes. Its compiler expands
|
||||
function-call nodes, maps output values back to renderable nodes, and then
|
||||
continues through traversal, rendering, and post-rendering stages.
|
||||
The previous `MKTT-WP-0015` scope bundled typed values, syntax extensions,
|
||||
document-local reusable functions, render/export adapters, render-aware
|
||||
references/assets, and permission sandboxing. That was architecturally too
|
||||
large for one workplan.
|
||||
|
||||
Markitect should not become a Quarkdown clone. The better fit is:
|
||||
The split is now:
|
||||
|
||||
- keep Markitect as the contract, reference, processor, workflow, cache,
|
||||
provenance, and policy framework
|
||||
- make document functions an authoring surface over those primitives
|
||||
- add render/export behavior as optional extensions
|
||||
- use Quarkdown as an optional external publishing target where that is useful
|
||||
- `MKTT-WP-0015`: typed document-function value contracts in `markitect-tool`.
|
||||
- `MKTT-WP-0020`: render/export adapter contracts in `markitect-tool`.
|
||||
- `MKTT-WP-0021`: render reference and asset manifest contracts in
|
||||
`markitect-tool`.
|
||||
- `MQD-WP-0001`: concrete Quarkdown render adapter work in
|
||||
`markitect-quarkdown`.
|
||||
- `MKTF-WP-0003`: read-side source attachment and asset metadata compatibility
|
||||
in `markitect-filter`.
|
||||
|
||||
## Decision
|
||||
`markitect-filter` remains a source normalization repository. It should not own
|
||||
write/export adapters or renderer execution. Concrete Quarkdown invocation
|
||||
belongs in `markitect-quarkdown`; durable render jobs, publication state, and
|
||||
artifact storage belong outside `markitect-tool`.
|
||||
|
||||
Defer this work until after the current original successor work is stable,
|
||||
unless a concrete document publishing, render provenance, or function-language
|
||||
use case becomes urgent.
|
||||
## Boundary
|
||||
|
||||
When picked up, treat this as an extension workplan. It may evolve framework
|
||||
interfaces, but should not make Quarkdown, flex-auth, network access, live LLM
|
||||
calls, filesystem writes, or external processes required for deterministic
|
||||
Markitect parsing and function validation.
|
||||
`markitect-tool` owns:
|
||||
|
||||
Boundary clarification: `markitect-tool` owns typed value contracts,
|
||||
Markdown-compatible function syntax, render/export adapter interfaces,
|
||||
provenance envelopes, diagnostics, and deterministic fake renderers. It does
|
||||
not own durable rendered artifact storage, publication workflows, enterprise
|
||||
authorization, real renderer operations, or concrete application reports. Those
|
||||
belong in `kontextual-engine`, optional renderer packages, or
|
||||
`infospace-bench` application workflows.
|
||||
- typed document value contracts
|
||||
- value-to-Markdown and value-to-JSON mapping
|
||||
- function descriptor output validation
|
||||
- deterministic diagnostics and provenance for value coercion
|
||||
- compatibility notes for future syntax extensions
|
||||
|
||||
## P15.1 - Typed document values and value mapping
|
||||
`markitect-tool` does not own in this workplan:
|
||||
|
||||
- nested function expression parsing
|
||||
- document-local reusable functions
|
||||
- render/export adapter execution
|
||||
- Quarkdown invocation
|
||||
- filesystem-writing render jobs
|
||||
- publication lifecycle or durable artifact storage
|
||||
- concrete source-format filtering
|
||||
|
||||
## Implementation Summary - 2026-05-15
|
||||
|
||||
Implemented the narrowed value-contract slice:
|
||||
|
||||
- `DocumentValue` and `DOCUMENT_VALUE_KINDS`.
|
||||
- Deterministic coercion and Markdown/JSON mapping helpers:
|
||||
`coerce_document_value`, `format_document_value`, and
|
||||
`document_value_to_json`.
|
||||
- `DocumentFunctionRun.value` while preserving the legacy raw `output` field.
|
||||
- Descriptor `output_type` validation for string, markdown, dynamic, table,
|
||||
record, reference, content unit, and related value kinds.
|
||||
- Diagnostics for output type mismatches and reference/content-unit values that
|
||||
lack provenance.
|
||||
- Conservative pipeline depth limit without adding nested function syntax.
|
||||
- Docs, examples, API exports, extension descriptor metadata, and tests.
|
||||
|
||||
Render/export contracts remain in `MKTT-WP-0020`; render references and asset
|
||||
manifests remain in `MKTT-WP-0021`; concrete Quarkdown execution remains in
|
||||
`MQD-WP-0001`; read-side source attachment metadata remains in `MKTF-WP-0003`.
|
||||
|
||||
## P15.1 - Define typed document values
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0015-T001
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "995945c5-6cec-435c-8943-b8da0a9ff89d"
|
||||
```
|
||||
|
||||
Define a typed value model for document functions:
|
||||
Define a small `DocumentValue` model for function results:
|
||||
|
||||
- string, number, boolean, none
|
||||
- string, number, boolean, and none
|
||||
- Markdown content
|
||||
- list and dictionary values
|
||||
- lists and dictionaries
|
||||
- records and tables
|
||||
- references and content units
|
||||
- tables and records
|
||||
- diagnostics-friendly unknown or dynamic values
|
||||
|
||||
Define how each value maps back to Markdown or structured output. Keep the
|
||||
mapper deterministic and inspectable.
|
||||
The model should serialize cleanly and stay independent of renderer-specific
|
||||
objects.
|
||||
|
||||
Output: value model, mapper API, tests, and documentation.
|
||||
Output: value dataclasses or schema objects, serialization helpers, tests, and
|
||||
documentation.
|
||||
|
||||
## P15.2 - Richer function syntax without losing Markdown compatibility
|
||||
## P15.2 - Add deterministic value mapping
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0015-T002
|
||||
status: todo
|
||||
priority: medium
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "bfce1388-e123-4e91-a5ab-ba67d21c22b8"
|
||||
```
|
||||
|
||||
Evaluate syntax extensions that improve author ergonomics without turning
|
||||
Markitect into a full compiler language:
|
||||
Define how each `DocumentValue` maps to:
|
||||
|
||||
- multiline argument continuation
|
||||
- nested function expressions
|
||||
- clearer escaping rules
|
||||
- block-body argument refinements
|
||||
- source spans beyond line numbers
|
||||
- cycle and depth limits for nested calls
|
||||
- inline Markdown
|
||||
- block Markdown
|
||||
- JSON/YAML API output
|
||||
- diagnostic fallback text for unknown or dynamic values
|
||||
|
||||
Output: syntax compatibility note, parser tests, and diagnostics examples.
|
||||
Mapping must be deterministic, inspectable, and safe to use from
|
||||
`mkt function render`.
|
||||
|
||||
## P15.3 - Document-local reusable functions
|
||||
Output: mapper API, compatibility tests for existing function outputs, and
|
||||
diagnostics examples.
|
||||
|
||||
## P15.3 - Validate function descriptor output contracts
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0015-T003
|
||||
status: todo
|
||||
priority: medium
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "a8a8f017-3622-47f1-814e-0c71bd49a42f"
|
||||
```
|
||||
|
||||
Explore document-local reusable functions as a constrained, contract-aware
|
||||
extension:
|
||||
Extend `DocumentFunctionDescriptor` output metadata so functions can declare
|
||||
their result type using the new value vocabulary.
|
||||
|
||||
- named reusable snippets
|
||||
- parameter lists and default values
|
||||
- body arguments
|
||||
- provenance for expansions
|
||||
- validation against allowed function namespaces
|
||||
Validation should catch:
|
||||
|
||||
Avoid general-purpose Turing-complete scripting in core. If assisted or
|
||||
external behavior is needed, route it through workflow steps and explicit
|
||||
capability gates.
|
||||
- declared output type mismatches
|
||||
- values that cannot be mapped to Markdown in the requested context
|
||||
- unstable or dynamic values in deterministic contexts
|
||||
- missing provenance for reference/content-unit values
|
||||
|
||||
Output: design proposal and one deterministic prototype if justified.
|
||||
Output: descriptor updates, evaluator checks, diagnostics, and tests.
|
||||
|
||||
## P15.4 - Quarkdown and render/export adapters
|
||||
## P15.4 - Pin syntax extension boundaries
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0015-T004
|
||||
status: todo
|
||||
priority: high
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "69e550a0-188b-4bc4-9658-47219b090904"
|
||||
```
|
||||
|
||||
Design optional render/export adapter contracts:
|
||||
Document which syntax improvements are still acceptable without replacing the
|
||||
current conservative parser:
|
||||
|
||||
- emit Quarkdown source from Markitect references, processors, templates, and
|
||||
function calls
|
||||
- support output profiles such as plain, docs, slides, paged, and static site
|
||||
- invoke external renderers only through declared capabilities and fake
|
||||
deterministic test adapters in this repo
|
||||
- keep direct code reuse license-safe
|
||||
- track source to rendered-artifact provenance
|
||||
- clearer escaping rules
|
||||
- fenced block-body argument refinements
|
||||
- line/span reporting improvements
|
||||
- cycle and depth limits for current pipeline evaluation
|
||||
|
||||
Output: adapter interface, Quarkdown export sketch, local policy model, and
|
||||
tests with deterministic fake renderers. Durable render jobs, stored artifacts,
|
||||
publication state, and application-specific reports are out of scope here.
|
||||
Explicitly defer:
|
||||
|
||||
## P15.5 - Render-aware references, numbering, and assets
|
||||
- nested function expressions
|
||||
- document-local function definitions
|
||||
- conditionals, loops, lambdas, or Turing-complete scripting
|
||||
- Quarkdown syntax compatibility as a core parser requirement
|
||||
|
||||
Output: syntax compatibility note and parser diagnostics tests for the retained
|
||||
scope.
|
||||
|
||||
## P15.5 - Update examples and extension descriptor metadata
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0015-T005
|
||||
status: todo
|
||||
priority: high
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "53eb9f94-830b-4fdf-bb47-3f549048c82a"
|
||||
```
|
||||
|
||||
Extend the reference model for rendered documents:
|
||||
Update document-function docs, examples, and extension descriptor metadata to
|
||||
show typed outputs without advertising render/export behavior as core function
|
||||
execution.
|
||||
|
||||
- figures, tables, equations, code blocks, and custom numbered units
|
||||
- generated table of contents and cross-reference links
|
||||
- static asset manifests
|
||||
- media checksums and copy policies
|
||||
- root output asset references
|
||||
|
||||
Output: reference/asset manifest model and docs with examples.
|
||||
|
||||
## P15.6 - Permission sandbox for non-core functions
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0015-T006
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "9ef2c516-2cd0-40ba-b270-abefbfd8fc40"
|
||||
```
|
||||
|
||||
Add explicit local permission gates for functions that need:
|
||||
|
||||
- filesystem reads or writes
|
||||
- network access
|
||||
- external processes
|
||||
- native content inclusion
|
||||
- assisted generation
|
||||
- render/export side effects
|
||||
|
||||
Use Markitect-local policy contracts first. flex-auth, OpenFGA, OPA, Cedar,
|
||||
Keycloak, Entra, or similar systems may be optional adapters, but must not be
|
||||
required for deterministic function parsing, validation, and rendering of pure
|
||||
functions.
|
||||
|
||||
Output: permission vocabulary, denied-operation diagnostics, and policy tests.
|
||||
This is a syntax-layer capability contract, not a durable authorization service
|
||||
or audit system.
|
||||
Output: docs, examples, extension catalog assertions, and generated API/CLI
|
||||
reference updates if the public surface changes.
|
||||
|
||||
## Exit Criteria
|
||||
|
||||
- Core deterministic document functions remain simple and dependency-light.
|
||||
- Richer functions are optional extensions with declared capabilities.
|
||||
- Render/export adapters can be tested without live external services.
|
||||
- Quarkdown interoperability is conceptually supported without direct code
|
||||
dependency.
|
||||
- Typed values, render provenance, references, and assets have clear contracts.
|
||||
- The extension does not duplicate the dataflow workflow engine.
|
||||
- Durable render operations, publication lifecycle, and enterprise policy
|
||||
enforcement remain outside `markitect-tool`.
|
||||
- Core deterministic document functions remain dependency-light.
|
||||
- Function outputs use explicit typed value contracts.
|
||||
- Existing Markdown rendering behavior remains compatible.
|
||||
- Renderer-specific objects and Quarkdown invocation are outside this
|
||||
workplan.
|
||||
- Richer syntax and reusable local functions are deferred until a concrete use
|
||||
case justifies a parser/evaluator redesign.
|
||||
|
||||
@@ -3,7 +3,7 @@ id: MKTT-WP-0016
|
||||
type: workplan
|
||||
title: "Memory Graph Profile Contract And Context Package Compiler"
|
||||
domain: markitect
|
||||
status: active
|
||||
status: done
|
||||
owner: markitect-tool
|
||||
topic_slug: markitect
|
||||
planning_priority: P2
|
||||
@@ -119,9 +119,16 @@ mkt memory graph pack <selection-file>
|
||||
```
|
||||
|
||||
Examples and documentation now live in `docs/memory-graph-contract.md` and
|
||||
`examples/memory/*graph*.yaml`. The remaining refinement is fixture breadth:
|
||||
add invalid fixtures and richer conversation/knowledge examples before closing
|
||||
the workplan fully.
|
||||
`examples/memory/`. The fixture catalog now covers reasoning decision graphs,
|
||||
conversation paths, knowledge neighborhoods, mixed profiles, invalid graph and
|
||||
profile examples, and non-executing runtime adapter handoff descriptors.
|
||||
|
||||
## Closure Update - 2026-05-15
|
||||
|
||||
This workplan is complete. The final pass added deterministic conversation and
|
||||
knowledge fixtures, invalid validation fixtures, a runtime adapter boundary
|
||||
catalog, extension descriptor coverage, and tests that compile the valid graph
|
||||
selections into `ContextPackage` objects.
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
@@ -216,7 +223,7 @@ retention, compaction, latency, or policy decisions.
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0016-T004
|
||||
status: in_progress
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "afcd7ce9-e657-4779-b3b2-0ae3f8e2d66e"
|
||||
```
|
||||
@@ -286,7 +293,7 @@ Output: CLI commands, docs, and tests.
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0016-T007
|
||||
status: in_progress
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "ff78d449-0738-4f1b-a018-2efed3d8e878"
|
||||
```
|
||||
|
||||
174
workplans/MKTT-WP-0020-render-export-adapter-contract.md
Normal file
174
workplans/MKTT-WP-0020-render-export-adapter-contract.md
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
id: MKTT-WP-0020
|
||||
type: workplan
|
||||
title: "Render Export Adapter Contract"
|
||||
domain: markitect
|
||||
status: todo
|
||||
owner: markitect-tool
|
||||
topic_slug: markitect
|
||||
planning_priority: P2
|
||||
planning_order: 150
|
||||
depends_on_workplans:
|
||||
- MKTT-WP-0013
|
||||
- MKTT-WP-0015
|
||||
related_workplans:
|
||||
- MKTT-WP-0018
|
||||
- MKTT-WP-0021
|
||||
- MQD-WP-0001
|
||||
created: "2026-05-15"
|
||||
updated: "2026-05-15"
|
||||
state_hub_workstream_id: "19d1a377-848a-4156-b712-7b9febd836a6"
|
||||
---
|
||||
|
||||
# MKTT-WP-0020: Render Export Adapter Contract
|
||||
|
||||
## Purpose
|
||||
|
||||
Define the `markitect-tool` contract for optional render/export adapters
|
||||
without implementing real renderer operations in the core toolkit.
|
||||
|
||||
This is the output-side sibling of the source adapter contract. Source
|
||||
adapters normalize input formats into canonical Markdown; render/export
|
||||
adapters transform Markitect-compatible Markdown into rendered artifacts or
|
||||
renderer-specific sources. The two directions must stay separate now that
|
||||
`markitect-filter` owns concrete source normalization.
|
||||
|
||||
## Boundary
|
||||
|
||||
`markitect-tool` owns:
|
||||
|
||||
- render/export request and result envelopes
|
||||
- adapter descriptors and extension catalog metadata
|
||||
- declared output profiles such as plain, docs, slides, paged, static site,
|
||||
and PDF
|
||||
- deterministic fake renderers for tests
|
||||
- diagnostics, provenance, source maps, and capability declarations
|
||||
|
||||
`markitect-tool` does not own:
|
||||
|
||||
- Quarkdown CLI invocation
|
||||
- Pandoc, browser, PDF, or static-site generator execution
|
||||
- filesystem-writing publication jobs beyond test fixtures
|
||||
- durable artifact storage
|
||||
- renderer-specific dependency installation
|
||||
- concrete application reports
|
||||
|
||||
Concrete Quarkdown integration belongs in `markitect-quarkdown`.
|
||||
|
||||
## P20.1 - Define render adapter descriptors
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0020-T001
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "5b52b196-a7f5-4e4f-abc6-972febdc2638"
|
||||
```
|
||||
|
||||
Define a `RenderExportAdapterDescriptor` shape that mirrors the source adapter
|
||||
descriptor style while remaining output-oriented.
|
||||
|
||||
Descriptor fields should include:
|
||||
|
||||
- stable adapter id
|
||||
- version and human-readable name
|
||||
- operations such as `export-source`, `render-artifact`, and `inspect-profile`
|
||||
- supported input contracts
|
||||
- output profiles and artifact media types
|
||||
- option schema
|
||||
- optional dependencies
|
||||
- safety and capability metadata
|
||||
- docs/examples links
|
||||
|
||||
Output: descriptor dataclass or schema, extension descriptor mapping, and
|
||||
registry tests.
|
||||
|
||||
## P20.2 - Define render request and result envelopes
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0020-T002
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "3d43b168-7e55-4885-ae17-fcea262f2641"
|
||||
```
|
||||
|
||||
Define service-free request/result types:
|
||||
|
||||
- `RenderExportRequest`
|
||||
- `RenderExportResult`
|
||||
- `RenderArtifact`
|
||||
- `RenderDiagnostic`
|
||||
- `RenderProvenance`
|
||||
|
||||
Results should describe artifacts and source maps without requiring durable
|
||||
artifact storage.
|
||||
|
||||
Output: serializable models, round-trip tests, and docs.
|
||||
|
||||
## P20.3 - Add deterministic fake renderer fixtures
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0020-T003
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "45748ad7-131c-4b75-9720-6958fde93208"
|
||||
```
|
||||
|
||||
Add an in-tree fake renderer that proves the contract without invoking
|
||||
Quarkdown or any external process.
|
||||
|
||||
The fake renderer should support:
|
||||
|
||||
- profile inspection
|
||||
- source export into deterministic text
|
||||
- artifact metadata emission
|
||||
- source-to-render provenance
|
||||
- denied-operation diagnostics for disabled capabilities
|
||||
|
||||
Output: fake adapter, tests, and examples.
|
||||
|
||||
## P20.4 - Define capability and policy gates
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0020-T004
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "50aa8f00-eeba-495d-9d45-089f855fc3bd"
|
||||
```
|
||||
|
||||
Pin a local capability vocabulary for render/export adapters:
|
||||
|
||||
- filesystem read
|
||||
- filesystem write
|
||||
- external process
|
||||
- network
|
||||
- native renderer dependency
|
||||
- assisted generation
|
||||
- publication side effect
|
||||
|
||||
This is a syntax-layer contract and local denial model, not a durable
|
||||
authorization service.
|
||||
|
||||
Output: capability constants, blocked-operation diagnostics, and tests.
|
||||
|
||||
## P20.5 - Document Quarkdown handoff
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0020-T005
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "173a75d0-d82c-4dcb-9ced-eb73e9438db2"
|
||||
```
|
||||
|
||||
Document how `markitect-quarkdown` should implement a concrete Quarkdown
|
||||
adapter against this contract.
|
||||
|
||||
Output: handoff note linking `MQD-WP-0001`, example descriptor metadata, and
|
||||
no direct Quarkdown dependency in `markitect-tool`.
|
||||
|
||||
## Exit Criteria
|
||||
|
||||
- Render/export adapters are discoverable through the extension framework.
|
||||
- Contract tests pass with a fake deterministic renderer.
|
||||
- Core Markitect remains usable without renderer dependencies.
|
||||
- `markitect-filter` remains read-side only.
|
||||
- Quarkdown interop is supported by contract, not by core dependency.
|
||||
147
workplans/MKTT-WP-0021-render-reference-asset-manifest.md
Normal file
147
workplans/MKTT-WP-0021-render-reference-asset-manifest.md
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
id: MKTT-WP-0021
|
||||
type: workplan
|
||||
title: "Render Reference And Asset Manifest Contract"
|
||||
domain: markitect
|
||||
status: todo
|
||||
owner: markitect-tool
|
||||
topic_slug: markitect
|
||||
planning_priority: P2
|
||||
planning_order: 155
|
||||
depends_on_workplans:
|
||||
- MKTT-WP-0010
|
||||
- MKTT-WP-0015
|
||||
- MKTT-WP-0020
|
||||
related_workplans:
|
||||
- MKTT-WP-0018
|
||||
- MKTF-WP-0003
|
||||
- MQD-WP-0001
|
||||
created: "2026-05-15"
|
||||
updated: "2026-05-15"
|
||||
state_hub_workstream_id: "c567c1a8-3029-4f9c-9587-e85fede64599"
|
||||
---
|
||||
|
||||
# MKTT-WP-0021: Render Reference And Asset Manifest Contract
|
||||
|
||||
## Purpose
|
||||
|
||||
Define passive contracts for render-aware references, numbered units, and
|
||||
static asset manifests.
|
||||
|
||||
This workplan should make rendered document structure inspectable before and
|
||||
after an optional renderer runs. It should not implement renderer layout,
|
||||
final numbering, asset copying, or publication lifecycle.
|
||||
|
||||
## Boundary
|
||||
|
||||
`markitect-tool` owns:
|
||||
|
||||
- stable render unit identities
|
||||
- references to figures, tables, equations, code blocks, and custom numbered
|
||||
units
|
||||
- table-of-contents and cross-reference planning metadata
|
||||
- static asset manifest fields
|
||||
- media checksums and copy-policy declarations
|
||||
- source-to-render provenance envelopes
|
||||
|
||||
Renderer packages own:
|
||||
|
||||
- final numbering and layout
|
||||
- link rewriting
|
||||
- asset copying
|
||||
- output directory conventions
|
||||
- artifact validation
|
||||
|
||||
`markitect-filter` owns only read-side source asset and attachment metadata
|
||||
needed by normalized Markdown inputs.
|
||||
|
||||
## P21.1 - Define render unit references
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0021-T001
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "3d33d387-633e-4ffb-962e-1a5061d3db01"
|
||||
```
|
||||
|
||||
Define stable identities for renderable units:
|
||||
|
||||
- figures
|
||||
- tables
|
||||
- equations
|
||||
- code blocks
|
||||
- sections
|
||||
- custom numbered units
|
||||
|
||||
Output: render reference model, serialization tests, and examples.
|
||||
|
||||
## P21.2 - Define cross-reference and TOC planning metadata
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0021-T002
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "4a96e27b-9165-450c-899c-f7af484d9438"
|
||||
```
|
||||
|
||||
Represent requested cross-reference links and table-of-contents entries before
|
||||
renderer-specific numbering is known.
|
||||
|
||||
Output: manifest model and tests that keep final numbering outside core.
|
||||
|
||||
## P21.3 - Define static asset manifests
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0021-T003
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "ba917e45-1912-4bdb-bf3f-5946c20957b2"
|
||||
```
|
||||
|
||||
Define an asset manifest with:
|
||||
|
||||
- source URI/path
|
||||
- media type and extension
|
||||
- digest/checksum
|
||||
- logical role
|
||||
- copy policy declaration
|
||||
- renderer output reference placeholder
|
||||
- provenance back to source spans or source adapter attachments
|
||||
|
||||
Output: model, examples, and compatibility note for `MKTF-WP-0003`.
|
||||
|
||||
## P21.4 - Define source-to-render provenance maps
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0021-T004
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "dd9f1128-af1e-44a8-961b-3aba6104ec9a"
|
||||
```
|
||||
|
||||
Define a provenance envelope that maps Markitect source spans and generated
|
||||
function outputs to renderer-source units and artifact references.
|
||||
|
||||
Output: source map model, fake-renderer fixture integration, and tests.
|
||||
|
||||
## P21.5 - Add docs and examples
|
||||
|
||||
```task
|
||||
id: MKTT-WP-0021-T005
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "1d472c44-d970-4403-9f0b-18e6192da737"
|
||||
```
|
||||
|
||||
Add examples showing render references and asset manifests without requiring a
|
||||
real renderer.
|
||||
|
||||
Output: docs, examples, and extension catalog metadata if needed.
|
||||
|
||||
## Exit Criteria
|
||||
|
||||
- Render-aware references can be represented before renderer execution.
|
||||
- Asset manifests are deterministic and provenance-preserving.
|
||||
- Core Markitect does not perform asset copying or final layout numbering.
|
||||
- `markitect-filter` attachment metadata can feed the manifest without making
|
||||
`markitect-filter` a renderer.
|
||||
Reference in New Issue
Block a user