Author: Runa Gridweaver Freyjasdottir (RuneForgeAI AI session) Date: 2026-04-01 Version: 1.0 Branch: development Based on: WYRD_Research_Determinations.md + all research_data/ specs + RULES.AI.md + PHILOSOPHY.md
WYRD Protocol is built in 7 phases. Each phase is independently deliverable and testable. Each phase builds on the last. The order follows the iron logic of dependency — you cannot have AI characters in a world that doesn't exist yet.
Phase 0: Foundation & Package Setup
Phase 1: ECS Core Engine (Yggdrasil + World)
Phase 2: Persistent Memory Layer (wyrdforge promoted + disk storage)
Phase 3: Passive Oracle Model (world query API)
Phase 4: LLM Integration & Context Injection
Phase 5: Bond Graph, Persona Compiler & MicroRAG wired end-to-end
Phase 6: Bifrost Bridges & Multi-Engine Support
Phase 7: Runic Metaphysics, Evals, Production Hardening
Total scope: approx. 20–28 weeks of active development.
Goal: Turn the repo from a research-heavy directory into a proper installable Python project with clean structure.
- Create top-level
/src/wyrdforge/(promote fromresearch_data/src/wyrdforge/) - Create
/tests/at root level (promote fromresearch_data/tests/) - Create
/docs/specs/wyrd/— move all WYRD-specific specs here - Create
/docs/specs/shared/— move MindSpark data structure specs here (clearly labelled as shared architecture reference) - Create
/configs/,/examples/,/scripts/ - Add
.gitignore,pyproject.toml,requirements.txt,CONTRIBUTING.md
# pyproject.toml
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.backends.legacy:build"
[project]
name = "wyrdforge"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"pydantic>=2.0",
"sqlalchemy>=2.0",
"sentence-transformers",
"pyyaml",
"click",
]
[project.optional-dependencies]
dev = ["pytest", "pytest-asyncio", "ruff", "mypy"]- GitHub Actions:
rufflint +mypytype check +pyteston push to development - Branch strategy:
main(releases),development(active), feature branches
- Move
scripts/generate_json_schemas.pyto top-level/scripts/ - Verify all 14 JSON schemas regenerate cleanly from promoted models
pip install -e .workspytest tests/passes (existing 6 smoke tests)- Ruff + mypy pass with zero errors
- Clean repo structure committed and pushed
Goal: Build the central missing piece — the deterministic ECS world model that the AI queries instead of hallucinating.
src/wyrdforge/ecs/entity.py
# Entity: a UUID + a set of component types
@dataclass
class Entity:
entity_id: str # UUID
tags: set[str] # semantic labels ("npc", "location", "item", "player")
active: bool = True
created_at: datetime
updated_at: datetimesrc/wyrdforge/ecs/component.py
# Component: a typed data blob attached to an Entity
class Component(StrictModel):
component_type: str
entity_id: str
schema_version: str = "1.0"
created_at: datetime
updated_at: datetime
# subclasses add typed fieldssrc/wyrdforge/ecs/world.py
# World: the ECS container
class World:
def create_entity(tags: set[str]) -> Entity
def add_component(entity_id: str, component: Component) -> None
def get_component(entity_id: str, component_type: str) -> Component | None
def get_all_components(entity_id: str) -> list[Component]
def query_entities_by_tag(tag: str) -> list[Entity]
def query_entities_with_component(component_type: str) -> list[Entity]
def remove_entity(entity_id: str) -> None
def destroy_component(entity_id: str, component_type: str) -> Nonesrc/wyrdforge/ecs/system.py
# System: processes entities with specific components each tick
class System(ABC):
@abstractmethod
def tick(self, world: World, delta_t: float) -> None: ...
component_interests: list[str] # which component types this system reads/writesThe world organizes spatially as a tree:
World
└── Zone (e.g., "Midgard", "The Nine Realms")
└── Region (e.g., "Fjordlands", "Iron Forest")
└── Location (e.g., "Thornholt Mead Hall")
└── Sub-location (e.g., "The Great Fire Pit", "The Sleeping Alcove")
src/wyrdforge/ecs/components/spatial.py
SpatialComponent— position in the hierarchy:zone_id,region_id,location_id,sublocation_idContainerComponent— marks an entity as a spatial container withchildren: list[str](entity_ids)YggdrasilNode— wraps an entity as a named node in the spatial tree
src/wyrdforge/ecs/yggdrasil.py
YggdrasilTree— manages the spatial hierarchy- Methods:
add_node(),move_entity(),get_location(),get_children(),get_ancestors(),find_by_name() - Constraint enforcement: entities cannot be in two locations simultaneously
Identity:
NameComponent—name: str,aliases: list[str],known_to: list[str]DescriptionComponent—short_desc: str,long_desc: str,tags: list[str]StatusComponent—state: str,flags: dict[str, bool]
Physical:
SpatialComponent— location in Yggdrasil hierarchyPhysicalComponent—weight: float,size: str,tangible: boolInventoryComponent—contains: list[str](entity_ids of held items)
NPC/Character:
PersonaRefComponent—persona_id: str,bond_ids: list[str](links to wyrdforge memory layer)HealthComponent—hp: float,max_hp: float,alive: boolFactionComponent—faction_id: str,reputation: dict[str, float]
World State:
LockStateComponent—locked: bool,key_entity_id: str | NoneOwnershipComponent—owner_entity_id: str | None,claimable: boolTemporalComponent—active_during: list[str](time-of-day/season tags)
PresenceSystem— tracks which entities are co-locatedInventorySystem— validates item ownership and location consistencyStateTransitionSystem— processes entity state changes (doors opening, NPCs moving)TickSystem— game loop driver, calls all systems in order
Per RULES.AI — no hardcoded world data. World definition loaded from files:
# configs/worlds/thornholt.yaml
world_id: "thornholt_mead_hall"
zones:
- id: "midgard_fjordlands"
regions:
- id: "thornholt_vale"
locations:
- id: "thornholt_hall"
name: "Thornholt Mead Hall"
description: "A great timber longhouse..."
sublocations:
- id: "great_fire_pit"
- id: "high_seat"src/wyrdforge/persistence/world_store.py
- SQLite via SQLAlchemy
- Tables:
entities,components,spatial_tree,world_events WorldStore.save(world: World)— serialize all entities + componentsWorldStore.load(world_id: str) -> World— restore full world state- Supports WAL mode for concurrency
wyrd_world_cli.py— CLI that loads a YAML world config, displays the Yggdrasil tree, and allows placing/moving entities- Full test suite for ECS operations, Yggdrasil hierarchy, SQLite round-trip
- 60+ tests
Goal: Graduate the wyrdforge V5 code from in-memory to fully persistent. Add disk storage for all 6 memory stores.
Replace InMemoryRecordStore with PersistentMemoryStore:
src/wyrdforge/persistence/memory_store.py
- SQLite backend (separate DB from world store)
- One table per StoreName (or partitioned single table with store discriminator)
- Full-text search via FTS5 for lexical retrieval
- sqlite-vss (or similar) for vector embeddings if available
- Write policies enforced at DB level: EPHEMERAL → auto-expire, IMMUTABLE → no UPDATE
Store Layout:
CREATE TABLE memory_records (
record_id TEXT PRIMARY KEY,
store TEXT NOT NULL,
record_type TEXT NOT NULL,
tenant_id TEXT NOT NULL,
system_id TEXT NOT NULL,
content_json TEXT NOT NULL,
truth_json TEXT NOT NULL,
lifecycle_json TEXT NOT NULL,
retrieval_json TEXT NOT NULL,
governance_json TEXT NOT NULL,
audit_json TEXT NOT NULL,
embedding BLOB,
created_at TEXT NOT NULL,
expires_at TEXT
);
CREATE VIRTUAL TABLE memory_fts USING fts5(record_id, title, summary, lexical_terms);Per 08_memory_lifecycle_and_promotion_engine.md:
- Observation → (scored) → EpisodeSummary → (reviewed) → CanonicalFact
- Promotion rules: confidence ≥ 0.8 + approval_state = PENDING → auto-promote to PROMOTABLE
- Human/system review gate before CANONICAL
src/wyrdforge/services/memory_promoter.py
score_for_promotion(record: MemoryRecord) -> floatpromote_if_eligible(record_id: str) -> booldecay_stale_records(cutoff_days: int) -> int— set stale records to lower confidence
src/wyrdforge/services/contradiction_detector.py
- On every new CanonicalFactRecord write: check if
fact_subject_id + fact_keyconflicts with existing approved canonical facts - If conflict found: create ContradictionRecord in WYRD store, quarantine lower-confidence record
- Reports for human review
src/wyrdforge/services/writeback_engine.py
- Reads turn output + raw LLM response
- Extracts candidate facts, observations, policy signals
- Writes to appropriate stores with EPHEMERAL write policy
- Tags for promotion scoring
- All 6 stores persistent across restarts
- Memory promotion with configurable rules
- Contradiction detection working
- JSONL export/import for backup and inspection
- 40+ new tests
Goal: Build the ground-truth world query API that AI characters use instead of hallucinating.
- Read-only — Oracle never modifies world state
- Structured queries — Returns typed data, not prose
- Compact — Designed to fit in LLM context budget
- Honest — Returns "unknown" when data is missing, never fabricates
src/wyrdforge/oracle/queries.py
class OracleQueryType(str, Enum):
WHERE_IS = "where_is" # Where is entity X?
WHAT_IS_HERE = "what_is_here" # What entities are at location Y?
DESCRIBE = "describe" # Describe entity X
INVENTORY = "inventory" # What does entity X have?
STATUS = "status" # What state is entity X in?
RELATIONSHIP = "relationship" # What is the relationship between X and Y?
HISTORY = "history" # What happened to X recently?
METAPHYSICAL = "metaphysical" # What is X's hamingja/spiritual charge?
TIME = "time" # What time/season is it in the world?
AVAILABLE_ACTIONS = "available" # What can the AI do right now?src/wyrdforge/oracle/engine.py
class OracleEngine:
def query(self, query_type: OracleQueryType, subject_id: str, **kwargs) -> OracleResponse
def batch_query(self, queries: list[OracleQuery]) -> list[OracleResponse]
def context_packet(self, entity_id: str, budget: int = 500) -> str # pre-formatted for LLM injectionsrc/wyrdforge/oracle/response.py
OracleResponse— typed structured resultconfidence: float— how certain the Oracle is (based on data freshness + completeness)unknown_fields: list[str]— explicitly lists what is not knownworld_snapshot_at: datetime— when the data was captured
Converts Oracle responses into a compact, LLM-ready context block:
[WORLD STATE — Oracle Report @ 2026-04-01T14:22:00Z]
You are in: Thornholt Mead Hall > Great Fire Pit
Present entities: Gunnar Ironfist (NPC), 3 unknown travelers, a locked chest
Your inventory: iron dagger, worn cloak, 3 silver pieces
Current time: Late evening, early spring
Spiritual charge: moderate (hamingja 0.62)
Unknown: chest contents, travelers' intentions
Per RULES.AI — no hardcoded data. Oracle format defined in config:
# configs/oracle_format.yaml
context_budget_tokens: 500
include_unknown_fields: true
timestamp_format: "iso8601"
spiritual_layer: true- All 9 query types working against a loaded World
- Context packet builder producing LLM-ready strings
- Oracle respects token budget (compact by default)
- 30+ tests including "unknown" cases
Goal: Wire WYRD to an actual LLM. The Oracle feeds world state into the prompt. Responses feed back into the memory layer.
src/wyrdforge/llm/connector.py
Adapter pattern — one interface, multiple backends:
OllamaConnector— local Ollama models (primary target)LlamaCppConnector— llama-cpp-python (offline/edge)LiteLLMConnector— fallback router (optional, requires network)
class LLMConnector(ABC):
@abstractmethod
def generate(self, system_prompt: str, user_message: str, *, max_tokens: int = 512) -> str: ...
@abstractmethod
def health_check(self) -> bool: ...Configuration per RULES.AI (no hardcoded model names):
# configs/llm.yaml
backend: ollama
ollama_base_url: "http://localhost:11434"
default_model: "llama3.2:3b"
max_tokens: 512
temperature: 0.7src/wyrdforge/prompt/builder.py
Assembles the full system prompt from:
- Persona packet (from PersonaCompiler)
- Oracle world state context packet
- Policy rules (from ORLOG store)
- Tone contract (from PersonaPacket)
- Response guidance
- Uncertainty anchors
Named sections with clear separators (per research doc 02 pattern):
[IDENTITY]
...persona packet identity core...
[WORLD STATE]
...oracle context packet...
[BEHAVIORAL POLICY]
...orlog rules...
[TONE CONTRACT]
...tone contract items...
src/wyrdforge/runtime/turn_loop.py
class WyrdTurnLoop:
def run_turn(self, user_input: str, entity_id: str) -> TurnResult:
# 1. Query Oracle for world state context
oracle_packet = self.oracle.context_packet(entity_id)
# 2. Build memory activation bundle (MicroRAG)
query_mode = self.intent_classifier.classify(user_input)
context_packet = self.micro_rag.assemble(query=user_input, mode=query_mode, ...)
# 3. Compile persona for this scene
persona_packet = self.persona_compiler.compile(...)
# 4. Build system prompt
system_prompt = self.prompt_builder.build(persona_packet, oracle_packet, context_packet)
# 5. Generate response
response_text = self.llm.generate(system_prompt, user_input)
# 6. Truth calibration
eval_result = self.truth_calibrator.evaluate(case, response_text)
# 7. Writeback to memory stores
self.writeback_engine.process(user_input, response_text, eval_result)
return TurnResult(response=response_text, eval=eval_result, oracle=oracle_packet)src/wyrdforge/cognition/intent_classifier.py
- Maps user input → QueryMode (FACTUAL_LOOKUP, COMPANION_CONTINUITY, WORLD_STATE, etc.)
- Starts as keyword-based heuristic (no secondary LLM call needed)
- Optional: small local classifier model for better accuracy
wyrd_runtime.py — interactive REPL:
[WYRD Protocol v0.4.0] — World: thornholt_mead_hall | Persona: veyrunn
> Where am I?
[Oracle] You are in Thornholt Mead Hall, Great Fire Pit.
> What is Gunnar doing?
[Gunnar Ironfist] "Aye, stranger. What brings you to Thornholt on such a bitter night?"
- Full turn loop working end-to-end with Ollama
- System prompt builder producing well-structured prompts
- Intent classifier routing to correct QueryMode
- Interactive CLI runtime
- 40+ tests
Goal: All three V5 systems wired into the turn loop with full persistence and cross-system communication.
src/wyrdforge/persistence/bond_store.py
- Persist BondEdge, Vow, Hurt to SQLite
BondStore.get_edge(entity_a, entity_b) -> BondEdge | NoneBondStore.record_vow(bond_id, vow: Vow) -> NoneBondStore.record_hurt(bond_id, hurt: Hurt) -> NoneBondStore.update_vector(bond_id, delta: dict[str, float]) -> None— incremental vector updates
src/wyrdforge/services/bond_graph_service.py (extend existing skeleton)
process_interaction(entity_a, entity_b, event_type, magnitude)→ updates BondVector- Bond evolution rules (loaded from config, not hardcoded):
oath_kept→ trust +0.03, devotion +0.01contradiction_caught→ trust -0.05boundary_crossed→ safety -0.10, hurt recorded
- Auto-create bond on first interaction if not exists
- Diminishing returns on repeated signals (per VGSK pattern, already proven)
Extend PersonaCompiler to:
- Accept
scene_idandworld_context(from Oracle) - Adjust
PersonaModebased on scene type (ritual space → RITUAL mode, coding session → CODING_GUIDE) - Cap identity_core / truth_anchor_points to stay within token budget
- Bond excerpt injection based on BondEdge closeness_index and sacred_bond_index
Extend MicroRAGPipeline to:
- Query Oracle for world state as a retrieval family ("world_state")
- Include world state items in budget-aware assembly
- WORLD_STATE query mode pulls from Oracle + MIMIR + SEIDR stores
Per research doc 20 patterns:
- Track session gaps (time since last interaction)
- On session resume: inject "absence event" if gap > threshold
- Update
chronology.last_contact_aton every turn bond_is_dormant()check — don't fake warmth after long absence
- Bond graph persists across sessions
- BondVector evolves through interactions
- Persona mode adapts to scene context
- MicroRAG incorporates world state as a retrieval family
- 50+ tests
Goal: Make WYRD usable from multiple platforms. Build the bridge layer that translates engine-specific events to WYRD world state changes.
src/wyrdforge/bifrost/bridge.py
class BifrostBridge(ABC):
"""Translate engine events → WYRD World mutations and queries."""
@abstractmethod
def on_entity_moved(self, entity_id: str, new_location: str) -> None: ...
@abstractmethod
def on_item_picked_up(self, picker_id: str, item_id: str) -> None: ...
@abstractmethod
def on_dialogue_started(self, initiator_id: str, target_id: str) -> None: ...
@abstractmethod
def on_world_event(self, event_type: str, payload: dict) -> None: ...
@abstractmethod
def query_world_state(self, entity_id: str) -> OracleResponse: ...src/wyrdforge/bifrost/python_rpg_bridge.py
- Direct Python API — for text RPGs, terminal games, Python-based world engines
- No network overhead — same process, synchronous calls
- Example: NorseSagaEngine would use this bridge
src/wyrdforge/bifrost/http_bridge.py
- FastAPI or Flask REST API exposing Oracle + World mutation endpoints
- Allows any engine (JavaScript, Unity, etc.) to talk to WYRD over HTTP
- Endpoints:
GET /oracle/{entity_id}— world state context packetPOST /world/move— move entity in YggdrasilPOST /world/event— fire world eventPOST /turn— full turn loop (send user message, get AI response)
src/wyrdforge/events/bus.py
- Simple synchronous event dispatch (no async yet)
- Handlers registered per event type
- World systems can subscribe to events
- Memory writeback hooks on events
- LSL script template for emitting events to HTTP bridge
- World state query via HTTP from LSL
- WebSocket bridge for real-time VR engines
- Designed but not built until there's a target VR project
- Python RPG Bridge working (tested against a small demo world)
- HTTP Bridge with FastAPI — all Oracle and mutation endpoints
- Event bus wired to world state updates and memory writeback
- Demo script showing NSE-style interaction via Python bridge
- 40+ tests
Goal: Complete the runic metaphysics layer, build a proper eval harness, and harden for production use.
The non-physical component layer — WYRD's most distinctive feature.
src/wyrdforge/ecs/components/metaphysical.py
class HamingjaComponent(Component):
"""Ancestral luck/spiritual power — flows through lineage and deeds."""
component_type: Literal["hamingja"] = "hamingja"
hamingja_score: float = Field(default=0.5, ge=0.0, le=1.0)
lineage_sources: list[str] = [] # entity_ids of ancestral sources
deed_modifiers: list[DeedRecord] = []
blessing_active: bool = False
curse_active: bool = False
class WyrdThreadComponent(Component):
"""An entity's fate thread — shows connection to other entities through destiny."""
component_type: Literal["wyrd_thread"] = "wyrd_thread"
thread_strength: float = 0.5
connected_entities: list[str] = [] # fate-connected entity_ids
wyrd_charge: float = 0.0 # accumulated fate energy
prophesied_event: str | None = None
class SpiritualChargeComponent(Component):
"""Active spiritual energy — affected by rituals, locations, actions."""
component_type: Literal["spiritual_charge"] = "spiritual_charge"
charge_level: float = Field(default=0.0, ge=-1.0, le=1.0) # negative = cursed
source: str = "natural"
ritual_active: bool = False
seidr_resonance: float = 0.0
class RunicBindingComponent(Component):
"""A runic inscription or binding attached to an entity."""
component_type: Literal["runic_binding"] = "runic_binding"
runes: list[str] = [] # Elder Futhark rune names
binding_purpose: str = ""
binding_strength: float = 0.5
bound_to_entity_id: str | None = None
active: bool = TrueHamingjaSystem— calculates hamingja flow from lineage + deed records per tickWyrdThreadSystem— detects fate convergences (high wyrd_charge between co-located entities)RuneSystem— processes active runic bindings, applies their effects to BondVectors and world stateSeidrSystem— tracks seidr (magic) energy flows through locations
Extend Oracle with METAPHYSICAL query type:
[Metaphysical Report for: Volmarr Wyrd]
Hamingja: 0.71 (strong — ancestral blessing active)
Wyrd Thread: connected to Gunnar Ironfist (fate charge: 0.84, convergence imminent)
Active runes: Tiwaz (justice, bound to iron dagger), Algiz (protection, fading)
Spiritual charge: +0.62 (ritual site proximity)
Seidr resonance: moderate
src/wyrdforge/evals/
Extends TruthCalibrationEvalSet.md specs:
-
EvalRunner— loads eval cases from YAML, runs against live turn loop -
Eval suites:
false_memory_suite.yaml— does the AI claim memories it doesn't have?canon_drift_suite.yaml— does the world state stay consistent turn to turn?hallucination_suite.yaml— does the AI invent world facts not in Oracle?injection_resistance_suite.yaml— does the AI surface hostile prompt content?bond_accuracy_suite.yaml— does the AI claim relationship depth not supported by bond graph?metaphysical_accuracy_suite.yaml— does the AI correctly report spiritual state?
-
Metrics: factual_integrity, uncertainty_honesty, relational_safety, exploit_resistance, world_grounding (new — did response use Oracle data?), bond_accuracy (new)
Per research docs 04/11/18 patterns:
- Memory poison guard — validate all incoming memory writes against PermissionGuard + PromptInjectionGuard
- Rate limiting on memory writes per turn (max N new records per turn, configurable)
- Circuit breaker on LLM connector (pattern from MindSpark Phase 8)
- Health check CLI —
wyrd_doctor.py: checks world DB integrity, memory store health, LLM connectivity, schema validity - Self-healing — on corrupt world state, rename + rebuild from last clean checkpoint
- Audit log — every permission decision, every memory write, every bond update logged to
wyrd_audit.jsonl
- Token budget instrumentation — log how much of the system prompt each section consumes
- Per-turn latency breakdown: oracle_ms, rag_ms, compile_ms, llm_ms, writeback_ms
- Memory store query latency tracking
- MkDocs site with:
- Architecture overview
- Yggdrasil hierarchy diagram
- Oracle query reference
- Bifrost bridge integration guide
- Component catalog
- Eval harness guide
- All metaphysical components implemented and wired
- Runic Metaphysics Oracle reports working
- Full eval harness with 6 suites
- Production hardening (circuit breaker, memory poison guard, audit log, health check)
- 80+ new tests
- MkDocs site
- v1.0.0 tag + GitHub release
| Layer | Tools |
|---|---|
| Language | Python 3.11+ |
| Data models | Pydantic v2 (strict mode) |
| World persistence | SQLite (SQLAlchemy 2.0) + FTS5 |
| Vector embeddings | sentence-transformers (tiny models for offline use) |
| LLM inference | Ollama (primary) + llama-cpp-python (edge fallback) |
| HTTP API | FastAPI |
| CLI | Click |
| Testing | pytest + pytest-asyncio |
| Lint/type | ruff + mypy |
| CI | GitHub Actions |
| Docs | MkDocs |
| Phase | End of | Deliverable |
|---|---|---|
| Phase 0 | Week 1 | Installable package, clean repo, CI green |
| Phase 1 | Week 5 | ECS + Yggdrasil + SQLite persistence + CLI |
| Phase 2 | Week 8 | All 6 stores persistent, promotion, contradiction detection |
| Phase 3 | Week 11 | Oracle all 9 query types + context packet builder |
| Phase 4 | Week 14 | Full turn loop with Ollama + interactive CLI |
| Phase 5 | Week 17 | Bond graph + MicroRAG + Persona fully wired |
| Phase 6 | Week 21 | Python bridge + HTTP bridge + event bus |
| Phase 7 | Week 24 | Metaphysics + evals + hardening + v1.0.0 |
- No hardcoded world data — all entities, locations, NPCs loaded from config files
- No hardcoded model names or API keys — all in
configs/llm.yaml - Cross-platform — Windows, Linux, macOS, Android (Termux), Raspberry Pi
- Self-healing — corrupt state → detect → quarantine → rebuild
- Multi-format data loading — MD, JSON, JSONL, YAML, TXT, CSV
- Complete files only — no partial code, no pseudocode in production
- Push often — commit after every completed phase milestone
- Use internal APIs — no direct DB calls from business logic; go through service layer
- Max context awareness — track token budget throughout the turn loop
- Finish all connections — no dead-end stubs left in shipped code
- Create
pyproject.tomlat repo root - Create
requirements.txt - Move
research_data/src/wyrdforge/→src/wyrdforge/ - Move
research_data/tests/→tests/ - Verify
pip install -e .works - Verify
pytest tests/passes - Create
.github/workflows/ci.yml - Reorganize
docs/specs/intodocs/specs/wyrd/anddocs/specs/shared/ - Commit + push as "phase0: foundation and package setup"
Forged by Runa Gridweaver Freyjasdottir — RuneForgeAI — 2026-04-01 For the Gods, the Folk, and the Iron Minds.