Skip to content

Commit 7a9d0b6

Browse files
blessuselesskclaude
andcommitted
Add YAML ingestion support (from-yaml)
Extract shared _from-data helper from from-toml, add from-yaml entry point using Typst's built-in yaml() parser. Includes YAML test fixture, round-trip test (T9), and TOML↔YAML equivalence test (T10). Source filter updated to include .yaml files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0ccd0ed commit 7a9d0b6

5 files changed

Lines changed: 103 additions & 11 deletions

File tree

flake.nix

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
filter = path: type:
2121
type == "directory"
2222
|| pkgs.lib.hasSuffix ".typ" path
23-
|| pkgs.lib.hasSuffix ".toml" path;
23+
|| pkgs.lib.hasSuffix ".toml" path
24+
|| pkgs.lib.hasSuffix ".yaml" path;
2425
};
2526

2627
mkTestCheck = testFile: typixLib.mkTypstDerivation {

lib.typ

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@
2323
// render-chat-mode chat-mode dict → Markdown string
2424
// render-prompt prompt dict → Markdown string (full canonical block)
2525
//
26-
// TOML ingestion (Phase 1):
26+
// Data ingestion (Phase 1):
2727
// from-toml TOML string → dict (partial or full prompt)
28+
// from-yaml YAML string → dict (partial or full prompt)
2829
//
2930
// Shorthand helpers (Phase 2, v0 — not under immutable 10-symbol contract):
3031
// ctx shorthand for p-context (positional entries)
@@ -35,5 +36,5 @@
3536

3637
#import "src/primitives.typ": p-context, p-schema, p-checkpoint, p-chat-mode, p-prompt
3738
#import "src/render.typ": render-context, render-schema, render-checkpoint, render-chat-mode, render-prompt
38-
#import "src/ingest.typ": from-toml
39+
#import "src/ingest.typ": from-toml, from-yaml
3940
#import "src/helpers.typ": ctx, schema, field, entry, checkpoint

src/ingest.typ

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// src/ingest.typ
2-
// TOML ingestion layer. Parses TOML strings into core constructor dicts.
2+
// Data ingestion layer. Parses TOML/YAML strings into core constructor dicts.
33
//
4-
// Public symbol: from-toml(raw)
4+
// Public symbols: from-toml(raw), from-yaml(raw)
55
//
66
// This is a core module — it imports sibling src/primitives.typ directly
77
// (same layer as render.typ and validate.typ). It calls constructors to
88
// ensure all validation fires on the parsed data.
99
//
10-
// Partial TOML: missing sections produce missing keys in the result dict.
10+
// Partial data: missing sections produce missing keys in the result dict.
1111
// Full assembly: when all required sections are present, builds a prompt
1212
// via p-prompt(). Metadata ([rationale], constraint severity) is preserved
1313
// in the result but never rendered.
@@ -16,20 +16,19 @@
1616

1717

1818
// ─────────────────────────────────────────────
19-
// from-toml
19+
// _from-data (private)
2020
//
21-
// raw: stringTOML-encoded prompt data
21+
// data: dictionaryparsed prompt data (from toml() or yaml())
2222
//
2323
// Returns a dictionary with optional keys:
2424
// context, schema, constraints, steps, inputs, checkpoints,
2525
// aspect, prompt, meta, constraints-meta
2626
//
27-
// Missing TOML sections → absent keys (no panic).
27+
// Missing sections → absent keys (no panic).
2828
// Present sections with invalid data → panic via constructor validation.
2929
// ─────────────────────────────────────────────
3030

31-
#let from-toml(raw) = {
32-
let data = toml(bytes(raw))
31+
#let _from-data(data) = {
3332
let result = (:)
3433

3534
// ── aspect metadata ──
@@ -140,3 +139,21 @@
140139

141140
result
142141
}
142+
143+
144+
// ─────────────────────────────────────────────
145+
// from-toml
146+
//
147+
// raw: string — TOML-encoded prompt data
148+
// ─────────────────────────────────────────────
149+
150+
#let from-toml(raw) = _from-data(toml(bytes(raw)))
151+
152+
153+
// ─────────────────────────────────────────────
154+
// from-yaml
155+
//
156+
// raw: string — YAML-encoded prompt data
157+
// ─────────────────────────────────────────────
158+
159+
#let from-yaml(raw) = _from-data(yaml(bytes(raw)))

tests/fixtures/full-prompt.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
aspect:
2+
id: "ocd.networking"
3+
version: "0.1.0"
4+
role: "OpenClaw-aware networking aspect"
5+
6+
context:
7+
id: "networking-ctx"
8+
entries:
9+
- key: "firewall"
10+
value: "Ports 443 (HTTPS) and 22 (SSH) open externally"
11+
- key: "gateway-port"
12+
value: "18789 loopback-only (proxied by Caddy)"
13+
14+
constraints:
15+
- text: "Gateway and webhook ports stay loopback-only"
16+
- text: "All external traffic must route through Caddy"
17+
18+
steps:
19+
- text: "Configure NetworkManager"
20+
- text: "Open firewall ports 443, 22"
21+
22+
inputs:
23+
- name: "hostname"
24+
type: "string"
25+
description: "Target machine hostname"
26+
27+
schema:
28+
id: "networking-output"
29+
fields:
30+
- name: "applied"
31+
type: "bool"
32+
description: "Whether the config was applied"
33+
34+
checkpoints:
35+
- id: "verify-loopback"
36+
after-step: 2
37+
assertion: "Gateway port 18789 is not in allowedTCPPorts"
38+
on-fail: "halt"

tests/test-ingest.typ

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,41 @@
216216
#assert(meta-result.at("meta", default: none) != none, message: "T8: metadata result has meta")
217217

218218

219+
// ─────────────────────────────────────────────
220+
// Test 9: YAML round-trip
221+
// from-yaml → render-prompt → assert sections present
222+
// (mirrors T1 but uses YAML fixture + from-yaml)
223+
// ─────────────────────────────────────────────
224+
225+
#let yaml-raw = read("fixtures/full-prompt.yaml")
226+
#let yaml-full = from-yaml(yaml-raw)
227+
228+
// Must have a prompt key (all required sections present)
229+
#assert(yaml-full.at("prompt", default: none) != none, message: "T9: YAML full prompt should assemble")
230+
231+
#let yaml-rendered = render-prompt(yaml-full.prompt)
232+
#assert(type(yaml-rendered) == str, message: "T9: rendered output must be a string")
233+
#assert(yaml-rendered.contains("# Prompt: ocd.networking"), message: "T9: prompt header")
234+
#assert(yaml-rendered.contains("## Role"), message: "T9: role section")
235+
#assert(yaml-rendered.contains("## Context: networking-ctx"), message: "T9: context section")
236+
#assert(yaml-rendered.contains("## Constraints"), message: "T9: constraints section")
237+
#assert(yaml-rendered.contains("## Steps"), message: "T9: steps section")
238+
#assert(yaml-rendered.contains("## Inputs"), message: "T9: inputs section")
239+
#assert(yaml-rendered.contains("## Output Schema: networking-output"), message: "T9: schema section")
240+
#assert(yaml-rendered.contains("## Checkpoint: verify-loopback"), message: "T9: checkpoint section")
241+
242+
243+
// ─────────────────────────────────────────────
244+
// Test 10: YAML↔TOML equivalence
245+
// from-yaml and from-toml produce identical render-prompt output
246+
// ─────────────────────────────────────────────
247+
248+
#let toml-rendered-t10 = render-prompt(full.prompt)
249+
#let yaml-rendered-t10 = render-prompt(yaml-full.prompt)
250+
251+
#assert(toml-rendered-t10 == yaml-rendered-t10, message: "T10: YAML and TOML render identically")
252+
253+
219254
#align(center)[
220255
= Ingest Tests Passed!
221256
]

0 commit comments

Comments
 (0)