# LOOM v1.0 Parser + Validator (plain text version)

:

loom_reader.txt

inside your .md folder.

# When you move it into your code repo, rename it to loom_reader.py.

import re

import sys

from dataclasses import dataclass, asdict

from typing import List

LOOM_TAG_PATTERN = re.compile(r'^\[LOOM:\s*(.+?)\s*\]\s*$', re.MULTILINE)

@dataclass

class LoomArtifact:

name: str

domain: str

object: str

intent: str

conditions: str

effects: str

verdict: str

errors: List[str]

FIELD_LABELS = ["Domain:", "Object:", "Intent:", "Conditions:", "Effects:"]

def split_artifacts(text: str) -> List[tuple]:

"""Return list of (name, body_text) for each [LOOM: ...] block."""

artifacts = []

matches = list(LOOM_TAG_PATTERN.finditer(text))

for i, m in enumerate(matches):

name = m.group(1).strip()

start = m.end()

end = matches[i + 1].start() if i + 1 < len(matches) else len(text)

body = text[start:end].strip()

artifacts.append((name, body))

return artifacts

def extract_fields(body: str) -> dict:

"""

Extract the five fields from the artifact body.

Returns a dict mapping label (without colon) -> value (string or None).

"""

lines = body.splitlines()

fields = {label[:-1]: None for label in FIELD_LABELS}

current_label = None

buffer = []

def flush():

nonlocal buffer, current_label

if current_label is not None:

value = "\n".join(line.rstrip() for line in buffer).strip()

fields[current_label[:-1]] = value if value else None

buffer = []

for line in lines:

stripped = line.strip()

if any(stripped.startswith(label) for label in FIELD_LABELS):

flush()

for label in FIELD_LABELS:

if stripped.startswith(label):

current_label = label

content = stripped[len(label):].strip()

buffer = [content] if content else []

break

else:

if current_label is not None:

buffer.append(line)

flush()

return fields

def validate_artifact(name: str, body: str) -> LoomArtifact:

fields = extract_fields(body)

errors = []

for label in ["Domain", "Object", "Intent", "Conditions", "Effects"]:

if not fields.get(label):

errors.append(f"Missing {label} section")

verdict = "ACCEPTED" if not errors else "REJECTED"

return LoomArtifact(

name=name,

domain=fields.get("Domain") or "",

object=fields.get("Object") or "",

intent=fields.get("Intent") or "",

conditions=fields.get("Conditions") or "",

effects=fields.get("Effects") or "",

verdict=verdict,

errors=errors,

)

def parse_text(text: str) -> List[LoomArtifact]:

artifacts = []

for name, body in split_artifacts(text):

artifacts.append(validate_artifact(name, body))

return artifacts

def main():

if len(sys.argv) != 2:

print("Usage: python loom_reader.py <file.txt>")

sys.exit(1)

path = sys.argv[1]

with open(path, "r", encoding="utf-8") as f:

text = f.read()

artifacts = parse_text(text)

if not artifacts:

print("No [LOOM: ...] artifacts found.")

sys.exit(0)

for art in artifacts:

print("-----")

for k, v in asdict(art).items():

if k != "errors":

print(f"{k.capitalize()}: {v}")

if art.errors:

print("Errors:")

for e in art.errors:

print(f" - {e}")

print("-----")

if name == "__main__":

main()