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:
parent
8393a9c55d
commit
a165cced33
5 changed files with 158 additions and 3 deletions
10
src/shard_wiki/engine/extensions/__init__.py
Normal file
10
src/shard_wiki/engine/extensions/__init__.py
Normal 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"]
|
||||
81
src/shard_wiki/engine/extensions/struct.py
Normal file
81
src/shard_wiki/engine/extensions/struct.py
Normal 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]
|
||||
Loading…
Add table
Add a link
Reference in a new issue