feat(engine): ext.struct typed-records built-in; close engine implementation (WP-0014 T6)

engine/extensions/struct.py: ext.struct (typed records) — in-text frontmatter
parse + ON_WRITE validation (allowed-fields, content-preserving), ON_READ tags
PageShape.TYPED_RECORD, ON_PROFILE raises structured-payload. Proves the framework:
feature absent when off (opaque prose, honest profile), present + profile-reflected
when on; works through InformationSpace edit. SCOPE updated. 6 tests, 107 total,
~97% coverage, pyflakes clean. Marks T6 + SHARD-WP-0014 done.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-06-16 00:56:22 +02:00
parent 8393a9c55d
commit a165cced33
5 changed files with 158 additions and 3 deletions

View file

@ -0,0 +1,10 @@
"""engine/extensions/ — built-in typed extensions for the wiki engine.
Each is a typed :class:`~shard_wiki.engine.extension.Extension` a shard activates only if needed.
``ext.struct`` (typed records) is the first; more (views, addressing, computational, authz) follow
the same pattern.
"""
from shard_wiki.engine.extensions.struct import StructExt, parse_frontmatter
__all__ = ["StructExt", "parse_frontmatter"]

View file

@ -0,0 +1,81 @@
"""ext.struct — typed records, a first built-in extension (WikiEngineCoreArchitecture X-STRUCT).
Demonstrates the typed-extension framework end-to-end. A page may carry a leading in-text
frontmatter block (`---` `---`, `key: value` lines git-diffable structure, blueprint T12).
With this extension **active**, the engine:
- **ON_WRITE** validates the structured block (optionally against an allowed-field set) a
malformed/disallowed structured page is rejected; the body is otherwise unchanged
(content-preserving, so write conformance holds);
- **ON_READ** tags such pages as `PageShape.TYPED_RECORD`;
- **ON_PROFILE** raises the shard's profile with the `structured-payload` verb (E-5).
With the extension **inactive**, the kernel treats the same page as opaque prose the feature
is genuinely absent (honest profile). This is "activate only what you need" in action.
"""
from __future__ import annotations
import dataclasses
from collections.abc import Iterable, Mapping
from typing import Any
from shard_wiki.engine.extension import Extension, Hook
from shard_wiki.engine.profile import ProfileContribution
from shard_wiki.model import Page, PageShape, Verb
__all__ = ["StructExt", "parse_frontmatter"]
def parse_frontmatter(body: str) -> tuple[dict[str, str], bool]:
"""Parse a leading ``---`` … ``---`` block of ``key: value`` lines.
Returns ``(fields, has_block)``. An unterminated opening ``---`` is *not* a valid block.
"""
lines = body.splitlines()
if not lines or lines[0].strip() != "---":
return {}, False
fields: dict[str, str] = {}
for line in lines[1:]:
if line.strip() == "---":
return fields, True
if ":" in line:
key, _, value = line.partition(":")
fields[key.strip()] = value.strip()
return {}, False # no closing fence → not a frontmatter block
class StructExt(Extension):
id = "ext.struct"
declares_types = ("record",)
provides = ("capability.wiki.page-model",)
def __init__(self, allowed_fields: Iterable[str] | None = None) -> None:
self._allowed: set[str] | None = set(allowed_fields) if allowed_fields is not None else None
def hooks(self) -> Mapping[Hook, Any]:
return {
Hook.ON_WRITE: self._on_write,
Hook.ON_READ: self._on_read,
Hook.ON_PROFILE: self._on_profile,
}
def _on_write(self, body: str, ctx: Any) -> str:
fields, has_block = parse_frontmatter(body)
if has_block and self._allowed is not None:
disallowed = set(fields) - self._allowed
if disallowed:
raise ValueError(f"ext.struct: disallowed fields {sorted(disallowed)}")
return body # structure stays in-text (git-diffable); body unchanged
def _on_read(self, page: Page, ctx: Any) -> Page:
_, has_block = parse_frontmatter(page.body)
return dataclasses.replace(page, shape=PageShape.TYPED_RECORD) if has_block else page
def _on_profile(self, payload: Any, ctx: Any) -> ProfileContribution:
return ProfileContribution(verbs_add=frozenset({Verb.STRUCTURED_PAYLOAD}))
@staticmethod
def fields(body: str) -> dict[str, str]:
"""Parsed structured fields of a page body (empty if it has no frontmatter block)."""
return parse_frontmatter(body)[0]