271 lines
9.6 KiB
Python
271 lines
9.6 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import asdict, dataclass
|
|
|
|
HALL_OF_KNOWLEDGE = "Hall of Knowledge"
|
|
LEDGER_OBJECT = "The Ledger"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MindPalaceIssue:
|
|
issue_number: int
|
|
state: str
|
|
title: str
|
|
layer: str
|
|
spatial_role: str
|
|
rationale: str
|
|
|
|
def summary_line(self) -> str:
|
|
return f"#{self.issue_number} {self.title} [{self.state} · {self.layer} · {self.spatial_role}]"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MutableFact:
|
|
key: str
|
|
value: str
|
|
source: str
|
|
|
|
def to_dict(self) -> dict[str, str]:
|
|
return asdict(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class BurnCycleSnapshot:
|
|
repo: str
|
|
branch: str
|
|
active_issue: int
|
|
focus: str
|
|
active_operator: str
|
|
blockers: tuple[str, ...] = ()
|
|
|
|
def to_dict(self) -> dict[str, object]:
|
|
return {
|
|
"repo": self.repo,
|
|
"branch": self.branch,
|
|
"active_issue": self.active_issue,
|
|
"focus": self.focus,
|
|
"active_operator": self.active_operator,
|
|
"blockers": list(self.blockers),
|
|
}
|
|
|
|
|
|
EVENNIA_MIND_PALACE_ISSUES = (
|
|
MindPalaceIssue(
|
|
508,
|
|
"closed",
|
|
"[P0] Tower Game — contextual dialogue (NPCs recycle 15 lines forever)",
|
|
"L4",
|
|
"Dialogue tutor NPCs",
|
|
"Contextual dialogue belongs in procedural behavior surfaces so the right NPC can teach or respond based on current room state.",
|
|
),
|
|
MindPalaceIssue(
|
|
509,
|
|
"closed",
|
|
"[P0] Tower Game — trust must decrease, conflict must exist",
|
|
"L2",
|
|
"Mutable relationship state",
|
|
"Trust, resentment, and alliance changes are world facts that should live on objects and characters, not in flat prompt text.",
|
|
),
|
|
MindPalaceIssue(
|
|
510,
|
|
"closed",
|
|
"[P0] Tower Game — narrative arc (tick 200 = tick 20)",
|
|
"L3",
|
|
"Archive chronicle",
|
|
"A spatial memory needs a chronicle room where prior events can be replayed and searched so the world can develop an actual arc.",
|
|
),
|
|
MindPalaceIssue(
|
|
511,
|
|
"open",
|
|
"[P0] Tower Game — energy must meaningfully constrain",
|
|
"L2",
|
|
"Mutable world meter",
|
|
"Energy is a changing state variable that should be visible in-room and affect what actions remain possible.",
|
|
),
|
|
MindPalaceIssue(
|
|
512,
|
|
"open",
|
|
"[P1] Sonnet workforce — full end-to-end smoke test",
|
|
"L3",
|
|
"Proof shelf",
|
|
"End-to-end smoke traces belong in the archive so world behavior can be proven, revisited, and compared over time.",
|
|
),
|
|
MindPalaceIssue(
|
|
513,
|
|
"open",
|
|
"[P1] Tower Game — world events must affect gameplay",
|
|
"L2",
|
|
"Event-reactive room state",
|
|
"If storms, fire, or decay do not alter the room state, the world is decorative instead of mnemonic.",
|
|
),
|
|
MindPalaceIssue(
|
|
514,
|
|
"open",
|
|
"[P1] Tower Game — items that change the world",
|
|
"L2",
|
|
"Interactive objects",
|
|
"World-changing items are exactly the kind of mutable facts and affordances that a spatial memory substrate should expose.",
|
|
),
|
|
MindPalaceIssue(
|
|
515,
|
|
"open",
|
|
"[P1] Tower Game — NPC-NPC relationships",
|
|
"L2",
|
|
"Social graph in-world",
|
|
"Relationships should persist on characters and become inspectable through spatial proximity rather than hidden transcript-only state.",
|
|
),
|
|
MindPalaceIssue(
|
|
516,
|
|
"closed",
|
|
"[P1] Tower Game — Timmy richer dialogue + internal monologue",
|
|
"L4",
|
|
"Inner-room teaching patterns",
|
|
"Internal monologue and richer dialogue are procedural behaviors that can be attached to rooms, NPCs, and character routines.",
|
|
),
|
|
MindPalaceIssue(
|
|
517,
|
|
"open",
|
|
"[P1] Tower Game — NPCs move between rooms with purpose",
|
|
"L5",
|
|
"Movement-driven retrieval",
|
|
"Purposeful movement is retrieval logic made spatial: who enters which room determines what knowledge is loaded and acted on.",
|
|
),
|
|
MindPalaceIssue(
|
|
534,
|
|
"open",
|
|
"[BEZ-P0] Fix Evennia settings on 104.131.15.18 — remove bad port tuples, DB is ready",
|
|
"L1",
|
|
"Runtime threshold",
|
|
"Before the mind palace can be inhabited, the base Evennia runtime topology has to load cleanly at the threshold.",
|
|
),
|
|
MindPalaceIssue(
|
|
535,
|
|
"open",
|
|
"[BEZ-P0] Install Tailscale on Bezalel VPS (104.131.15.18) for internal networking",
|
|
"L1",
|
|
"Network threshold",
|
|
"Network identity and reachability are static environment facts that determine which rooms and worlds are even reachable.",
|
|
),
|
|
MindPalaceIssue(
|
|
536,
|
|
"open",
|
|
"[BEZ-P1] Create Bezalel Evennia world with themed rooms and characters",
|
|
"L1",
|
|
"First room graph",
|
|
"Themed rooms and characters are the static world scaffold that lets memory become place instead of prose.",
|
|
),
|
|
MindPalaceIssue(
|
|
537,
|
|
"closed",
|
|
"[BRIDGE-P1] Deploy Evennia bridge API on all worlds — sync presence and events",
|
|
"L5",
|
|
"Cross-world routing",
|
|
"Bridge APIs turn movement across worlds into retrieval across houses instead of forcing one global prompt blob.",
|
|
),
|
|
MindPalaceIssue(
|
|
538,
|
|
"closed",
|
|
"[ALLEGRO-P1] Fix SSH access from Mac to Allegro VPS (167.99.126.228)",
|
|
"L1",
|
|
"Operator ingress",
|
|
"Operator access is part of the static world boundary: if the house cannot be reached, its memory cannot be visited.",
|
|
),
|
|
MindPalaceIssue(
|
|
539,
|
|
"closed",
|
|
"[ARCH-P2] Implement Evennia hub-and-spoke federation architecture",
|
|
"L5",
|
|
"Federated retrieval map",
|
|
"Federation turns room-to-room travel into selective retrieval across sovereign worlds instead of a single central cache.",
|
|
),
|
|
)
|
|
|
|
|
|
OPEN_EVENNIA_MIND_PALACE_ISSUES = tuple(issue for issue in EVENNIA_MIND_PALACE_ISSUES if issue.state == "open")
|
|
|
|
|
|
def build_hall_of_knowledge_entry(
|
|
active_issues: tuple[MindPalaceIssue, ...] | list[MindPalaceIssue],
|
|
ledger_fact: MutableFact,
|
|
burn_cycle: BurnCycleSnapshot,
|
|
) -> dict[str, object]:
|
|
issue_lines = [issue.summary_line() for issue in active_issues]
|
|
blocker_lines = list(burn_cycle.blockers) or ["No blockers recorded."]
|
|
return {
|
|
"room": {
|
|
"key": HALL_OF_KNOWLEDGE,
|
|
"purpose": "Load live issue topology, current burn-cycle focus, and the minimum durable facts Timmy needs before acting.",
|
|
},
|
|
"object": {
|
|
"key": LEDGER_OBJECT,
|
|
"purpose": "Expose one mutable fact from Timmy's durable memory so the room proves stateful recall instead of static documentation.",
|
|
"fact": ledger_fact.to_dict(),
|
|
},
|
|
"ambient_context": [
|
|
f"Room entry into {HALL_OF_KNOWLEDGE} preloads active Gitea issue topology for {burn_cycle.repo}.",
|
|
*issue_lines,
|
|
f"Ledger fact {ledger_fact.key}: {ledger_fact.value}",
|
|
f"Timmy burn cycle focus: issue #{burn_cycle.active_issue} on {burn_cycle.branch} — {burn_cycle.focus}",
|
|
f"Operator lane: {burn_cycle.active_operator}",
|
|
],
|
|
"burn_cycle": burn_cycle.to_dict(),
|
|
"commands": {
|
|
"/who lives here": "; ".join(issue_lines) or "No issues loaded.",
|
|
"/status forge": f"{burn_cycle.repo} @ {burn_cycle.branch} (issue #{burn_cycle.active_issue})",
|
|
"/what is broken": "; ".join(blocker_lines),
|
|
},
|
|
}
|
|
|
|
|
|
|
|
def render_room_entry_proof(
|
|
active_issues: tuple[MindPalaceIssue, ...] | list[MindPalaceIssue],
|
|
ledger_fact: MutableFact,
|
|
burn_cycle: BurnCycleSnapshot,
|
|
) -> str:
|
|
entry = build_hall_of_knowledge_entry(active_issues, ledger_fact, burn_cycle)
|
|
lines = [
|
|
f"ENTER {entry['room']['key']}",
|
|
f"Purpose: {entry['room']['purpose']}",
|
|
"Ambient context:",
|
|
]
|
|
lines.extend(f"- {line}" for line in entry["ambient_context"])
|
|
lines.extend(
|
|
[
|
|
f"Object: {entry['object']['key']}",
|
|
f"- {entry['object']['fact']['key']}: {entry['object']['fact']['value']}",
|
|
f"- source: {entry['object']['fact']['source']}",
|
|
"Timmy burn cycle:",
|
|
f"- repo: {burn_cycle.repo}",
|
|
f"- branch: {burn_cycle.branch}",
|
|
f"- active issue: #{burn_cycle.active_issue}",
|
|
f"- focus: {burn_cycle.focus}",
|
|
f"- operator: {burn_cycle.active_operator}",
|
|
"Command surfaces:",
|
|
f"- /who lives here -> {entry['commands']['/who lives here']}",
|
|
f"- /status forge -> {entry['commands']['/status forge']}",
|
|
f"- /what is broken -> {entry['commands']['/what is broken']}",
|
|
]
|
|
)
|
|
return "\n".join(lines)
|
|
|
|
|
|
|
|
def demo_room_entry_proof() -> str:
|
|
return render_room_entry_proof(
|
|
active_issues=OPEN_EVENNIA_MIND_PALACE_ISSUES[:3],
|
|
ledger_fact=MutableFact(
|
|
key="canonical-evennia-body",
|
|
value="timmy_world on localhost:4001 remains the canonical local body while room entry preloads live issue topology.",
|
|
source="reports/production/2026-03-28-evennia-world-proof.md",
|
|
),
|
|
burn_cycle=BurnCycleSnapshot(
|
|
repo="Timmy_Foundation/timmy-home",
|
|
branch="fix/567",
|
|
active_issue=567,
|
|
focus="Evennia as Agent Mind Palace — Spatial Memory Architecture",
|
|
active_operator="BURN-7-1",
|
|
blockers=("Comment on issue #567 with room-entry proof after PR creation",),
|
|
),
|
|
)
|