From 1c1a3cfc8b60ee77702ed39b1a4b2f2cf4a2915b Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 Jun 2026 16:53:57 +0200 Subject: [PATCH 01/15] =?UTF-8?q?docs(admin):=20complete=20the=20port=20ta?= =?UTF-8?q?ble=20=E2=80=94=20LM=20Studio,=20Piper=20TTS,=20memory=20wiki?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/admin-guide.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/admin-guide.md b/docs/admin-guide.md index ce8271d..cc20d34 100644 --- a/docs/admin-guide.md +++ b/docs/admin-guide.md @@ -134,7 +134,10 @@ famstack uses the 420xx range to avoid conflicts with anything you might already | 42040 | code (Forgejo) | | 42050 | chatai (Open WebUI) | | 42060 | ai (oMLX) | +| 42061 | ai (LM Studio, optional GGUF backend) | | 42062 | ai (Whisper) | +| 42063 | ai (Piper TTS) | +| 42070 | memory (family wiki) | If a port is taken, stop the offender or switch to `domain` mode (see [stack-reference.md](stack-reference.md)). From 8744fce7ac24f274386c8cea0fff3ac6c489d7de Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 Jun 2026 22:28:29 +0200 Subject: [PATCH 02/15] feat: repeatable --member/--topic selection for stack memory wiki Selection flags combine and repeat so one invocation covers every page a filing burst touched. Display names normalize to bucket slugs. --- stacklets/memory/bot/cli/wiki.py | 145 ++++++++++++++++--------- stacklets/memory/bot/cli_entrypoint.py | 10 +- 2 files changed, 102 insertions(+), 53 deletions(-) diff --git a/stacklets/memory/bot/cli/wiki.py b/stacklets/memory/bot/cli/wiki.py index bbf0c5e..1d04a85 100644 --- a/stacklets/memory/bot/cli/wiki.py +++ b/stacklets/memory/bot/cli/wiki.py @@ -10,6 +10,13 @@ stack memory wiki --topics every topic page, no home/members stack memory wiki --dry-run preview to stdout, no writes +`--member` and `--topic` repeat and combine with `--home`: any +selection flag switches from the full sweep to "generate exactly this +set" — one invocation covers every page a filing burst touched (the +curator's incremental path). Member values take a slug or a display +name ("Homer Simpson" hits homer's bucket); unknown slugs warn and +skip, and the run fails only when nothing in the selection matched. + Pages are published to the memory repo on Forgejo, where the wiki container picks them up within seconds. The wiki is Quartz, which renders `index.md` as the landing page for the site (vault-root @@ -68,6 +75,11 @@ _TOKEN_NAME = "stack-memory-wiki-cli" _TOKEN_SCOPES = ["write:repository", "read:repository"] +# Subject prefix of every commit this command pushes. The curator's +# poll loop filters on it so the wiki's own publishes never trigger +# another rebuild — change it and that loop comes back. +COMMIT_PREFIX = "docs(memory): refresh" + # Bracketed regenerate markers. Match the correspondent-page convention # verbatim so the same human contract holds across every generated page: # edits outside the brackets are preserved, inside is ours to rewrite. @@ -122,10 +134,10 @@ def _extract_citations(text: str) -> list[int]: async def run(llm: LLM, argv: list[str]) -> int: dry_run = "--dry-run" in argv - home_only = "--home" in argv + home_sel = "--home" in argv topics_only = "--topics" in argv - single_member = _arg_value(argv, "--member") - single_topic = _arg_value(argv, "--topic") + members_sel = _arg_values(argv, "--member") + topics_sel = _arg_values(argv, "--topic") vault_dir = os.environ.get("MEMORY_VAULT_DIR", "") if not vault_dir: @@ -163,50 +175,85 @@ async def run(llm: LLM, argv: list[str]) -> int: vault, shared_bucket=shared_bucket, member_slugs=roster, ) - # ── Single-surface flags ────────────────────────────────────────── - # `--topic camping` finds the (bucket, slug) match anywhere in the - # vault; ambiguity (both `family/camping/` and `arthur/camping/`) - # generates both pages -- they're genuinely separate topics. - if single_topic: - matched = [t for t in topics if t[1] == single_topic] - if not matched: - _err(f"no topic folder found with slug '{single_topic}'") - return 1 - rc = 0 - for bucket_prefix, topic_slug in matched: - sub_rc = await _generate_topic( + # ── Selection mode ──────────────────────────────────────────────── + # Any selection flag (--home, --member, --topic, --topics) switches + # from the full sweep to "generate exactly this set". Flags combine + # and --member/--topic repeat, so the curator's incremental rebuild + # covers every page a filing burst touched in one invocation. + # Member values are normalized through `_slugify_person`, the same + # mapping the index uses, so display names and slugs both land on + # the bucket. Unknown slugs warn and skip — an auto-run fed a name + # with no bucket must not sink the batch — and the run fails only + # when NOTHING in the selection matched. + if home_sel or topics_only or members_sel or topics_sel: + generated = 0 + + if home_sel: + rc = await _generate_home( + llm, index, roster=roster, + shared_bucket=shared_bucket, lang=lang, write=not dry_run, + ) + if rc == 0: + generated += 1 + + member_slugs: list[str] = [] + for raw in members_sel: + slug = _slugify_person(raw) + if slug and slug not in member_slugs: + member_slugs.append(slug) + for slug in member_slugs: + if slug not in roster: + _err(f"no member bucket for '{slug}' — skipping") + continue + rc = await _generate_member( + llm, slug, index, vault, + shared_bucket=shared_bucket, lang=lang, write=not dry_run, + ) + if rc == 0: + generated += 1 + + # `--topic camping` matches anywhere in the vault; ambiguity + # (both `family/camping/` and `arthur/camping/`) generates both + # pages -- they're genuinely separate topics. + if topics_only: + topic_set = topics + else: + wanted = set(topics_sel) + known = {slug for _, slug in topics} + for t_slug in sorted(wanted - known): + _err(f"no topic folder found with slug '{t_slug}' — skipping") + topic_set = [t for t in topics if t[1] in wanted] + for bucket_prefix, topic_slug in topic_set: + rc = await _generate_topic( llm, bucket_prefix, topic_slug, index, shared_bucket=shared_bucket, lang=lang, write=not dry_run, ) - rc = sub_rc if sub_rc else rc - return rc + if rc == 0: + generated += 1 - if single_member: - return await _generate_member( - llm, single_member, index, vault, - shared_bucket=shared_bucket, lang=lang, write=not dry_run, - ) + if generated == 0: + _err("nothing generated — no page in the selection matched") + return 1 + return 0 # ── Default loop ────────────────────────────────────────────────── - # `--topics` skips home + members; otherwise we cover home, every - # member, and every topic in turn. - if not topics_only: - rc = await _generate_home( - llm, index, roster=roster, + # Bare invocation: home, every member, every topic in turn. + rc = await _generate_home( + llm, index, roster=roster, + shared_bucket=shared_bucket, lang=lang, write=not dry_run, + ) + if rc != 0: + return rc + + # A member with no content is skipped (not an error) so one + # empty bucket doesn't sink the whole run. `_generate_member` + # returns 1 for skipped-empty, which we deliberately swallow + # here -- the overall run still succeeds. + for slug in roster: + await _generate_member( + llm, slug, index, vault, shared_bucket=shared_bucket, lang=lang, write=not dry_run, ) - if rc != 0 or home_only: - return rc - - # A member with no content is skipped (not an error) so one - # empty bucket doesn't sink the whole run. `_generate_member` - # returns 1 for skipped-empty, which we deliberately swallow - # here -- the overall run still succeeds. - for slug in roster: - await _generate_member( - llm, slug, index, vault, - shared_bucket=shared_bucket, lang=lang, write=not dry_run, - ) for bucket_prefix, topic_slug in topics: await _generate_topic( @@ -248,7 +295,7 @@ async def _generate_home( return 0 return await _publish( page, target_path="index.md", shared_bucket=shared_bucket, - commit_msg="docs(memory): refresh the family wiki home page", + commit_msg=f"{COMMIT_PREFIX} the family wiki home page", # Only used if the seed's root index.md is somehow missing; # otherwise the seed already carries this frontmatter. default_preamble="---\ntitle: Family Memory\n---", @@ -283,7 +330,7 @@ async def _generate_member( return 0 return await _publish( page, target_path=f"{slug}/about.md", shared_bucket=shared_bucket, - commit_msg=f"docs(memory): refresh {slug}'s wiki page", + commit_msg=f"{COMMIT_PREFIX} {slug}'s wiki page", # First-creation frontmatter seeds the person entity registry on # the page itself: `canonical` is the longest synonym (usually # the formal first name when the family also uses a nickname), @@ -346,7 +393,7 @@ async def _generate_topic( target_path=f"{page_dir}/about.md", shared_bucket=shared_bucket, commit_msg=( - f"docs(memory): refresh {bucket_prefix}/{topic_slug} topic page" + f"{COMMIT_PREFIX} {bucket_prefix}/{topic_slug} topic page" ), default_preamble=_topic_preamble(topic_slug, display, scope), ) @@ -795,13 +842,13 @@ async def _publish(page: str, *, target_path: str, shared_bucket: str, # ── Arg helpers ────────────────────────────────────────────────────────── -def _arg_value(argv: list[str], flag: str) -> str | None: - """Value following `flag` in argv, or None if the flag is absent.""" - if flag in argv: - i = argv.index(flag) - if i + 1 < len(argv): - return argv[i + 1] - return None +def _arg_values(argv: list[str], flag: str) -> list[str]: + """Every value following an occurrence of `flag`, in argv order.""" + out: list[str] = [] + for i, arg in enumerate(argv): + if arg == flag and i + 1 < len(argv): + out.append(argv[i + 1]) + return out # ── Prompts ────────────────────────────────────────────────────────────── diff --git a/stacklets/memory/bot/cli_entrypoint.py b/stacklets/memory/bot/cli_entrypoint.py index 15475b2..0767e9d 100644 --- a/stacklets/memory/bot/cli_entrypoint.py +++ b/stacklets/memory/bot/cli_entrypoint.py @@ -10,11 +10,13 @@ re-use the bot-runner as their tools runtime. Commands: - wiki [--home | --member ] [--dry-run] + wiki [--home] [--member ]... [--topic ]... [--dry-run] Regenerate the family wiki's entry pages. Apply by default; - `--dry-run` previews to stdout. Bare invocation regenerates the - home page plus every member's page. See `cli/wiki.py` for the - full prompt-and-splice contract. + `--dry-run` previews to stdout. Bare invocation regenerates + home + every member + every topic; selection flags repeat and + combine to cover exactly the pages a filing burst touched (the + curator's incremental path). See `cli/wiki.py` for the full + prompt-and-splice contract. """ from __future__ import annotations From ca8eda80dc50e56899364f2edb477f48b038cc4b Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 Jun 2026 22:28:29 +0200 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20memory=20curator=20sidecar=20?= =?UTF-8?q?=E2=80=94=20incremental=20+=20nightly=20wiki=20rebuilds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Debounced persons+home rebuild after filings settle, nightly full sweep as self-healing catch-all. Own slim image, memory-owned env. --- lib/stack/stack.py | 5 + stacklets/memory/bot/curator.py | 365 +++++++++++++++++++++++++ stacklets/memory/curator.Dockerfile | 22 ++ stacklets/memory/docker-compose.yml | 53 ++++ stacklets/memory/stacklet.toml | 27 ++ tests/stacklets/test_memory_curator.py | 172 ++++++++++++ 6 files changed, 644 insertions(+) create mode 100644 stacklets/memory/bot/curator.py create mode 100644 stacklets/memory/curator.Dockerfile create mode 100644 tests/stacklets/test_memory_curator.py diff --git a/lib/stack/stack.py b/lib/stack/stack.py index 15fc447..8ee2c4a 100644 --- a/lib/stack/stack.py +++ b/lib/stack/stack.py @@ -178,6 +178,11 @@ def _build_template_vars(self) -> dict: # Default "family" fits famstack; deskstack or non-family # deployments override to "office", "household", a surname, etc. "shared_bucket": self._cfg("core", "shared_bucket", "family"), + # Wiki freshness (the memory stacklet's curator sidecar). + # Bool/int from stack.toml render as env-friendly strings. + "wiki_auto_rebuild": str(self._cfg("memory", "wiki_auto_rebuild", True)).lower(), + "wiki_rebuild_quiet_secs": str(self._cfg("memory", "wiki_rebuild_quiet_secs", 180)), + "wiki_nightly": self._cfg("memory", "wiki_nightly", "03:30"), # AI service URLs — host-side for CLI, docker-side for containers "ai_openai_url": ai_openai_url, "ai_openai_url_docker": ai_openai_url.replace("://localhost", "://host.docker.internal"), diff --git a/stacklets/memory/bot/curator.py b/stacklets/memory/bot/curator.py new file mode 100644 index 0000000..90720b0 --- /dev/null +++ b/stacklets/memory/bot/curator.py @@ -0,0 +1,365 @@ +"""Curator — keeps the family wiki fresh without anyone running a command. + +Runs as the `stack-memory-curator` sidecar (own slim image, see +`curator.Dockerfile`) and watches the vault working copy that the wiki +container keeps in sync with Forgejo (a `git pull` loop, 20s +interval). Two jobs: + +1. **Incremental, after filings settle.** New commits land — an + archivist filing, a learn commit, a hand edit pushed from Obsidian. + The curator debounces the burst, reads which persons the changed + files involve (`persons:` frontmatter plus the bucket owner), and + regenerates exactly those member pages plus home. A typical burst + costs 2-3 LLM calls, so freshness where it's felt — "I filed + Homer's letter, Homer's page knows" — is nearly free. +2. **Nightly full sweep.** Home, every member, every topic, at + WIKI_NIGHTLY local time. This is the self-healing catch-all: topic + pages and cross-references go stale invisibly during the day and + are corrected while the GPU is idle. It also means the incremental + person mapping only has to be *helpful*, never load-bearing — + worst case for a missed page is "stale until tonight". + +Rebuilds run the CLI entrypoint as a subprocess (same code path as +`stack memory wiki`), so a wedged LLM call dies with the child, not +inside this loop. The wiki's own publish commits are filtered by +their subject prefix (`wiki.COMMIT_PREFIX`) — the rebuild must not +trigger itself. The last-rebuilt SHA persists across restarts, so an +interrupted rebuild is simply redone. + +Failure shape: a failed rebuild keeps the SHA where it was and +retries after another quiet window — the trigger is never lost, and +`stack memory wiki` always works as the manual override. If the wiki +container is down nothing pulls the vault, so the curator simply +sees no new commits until it returns. + +Config surface (rendered from stack.toml by the runtime): + WIKI_AUTO_REBUILD "true"/"false" — [memory] wiki_auto_rebuild + WIKI_REBUILD_QUIET_SECS quiet window — [memory] wiki_rebuild_quiet_secs + WIKI_NIGHTLY "HH:MM" local, "" disables — [memory] wiki_nightly +""" + +from __future__ import annotations + +import asyncio +import os +import subprocess +import sys +import time +from pathlib import Path + +# Sibling imports. The wiki module is loaded straight off its directory +# (not as `cli.wiki`) — the docs stacklet ships a `cli` package too, +# and the bare package name collides when both bot dirs share a +# sys.path (which the test suite's single process does). +sys.path.insert(0, str(Path(__file__).parent / "cli")) # wiki module +sys.path.insert(0, str(Path(__file__).resolve().parents[2])) # memory.lib +sys.path.insert(0, "/app") # stack.* framework + +from loguru import logger # noqa: E402 +from memory.lib import _parse_frontmatter # noqa: E402 + +from wiki import COMMIT_PREFIX # noqa: E402 + +POLL_SECS = int(os.environ.get("CURATOR_POLL_SECS", "30")) +QUIET_SECS = int(os.environ.get("WIKI_REBUILD_QUIET_SECS", "180")) +NIGHTLY = os.environ.get("WIKI_NIGHTLY", "03:30").strip() + +ENTRYPOINT = "/stacklets/memory/bot/cli_entrypoint.py" + +# Hard ceiling on one rebuild pass. A vault-wide sweep on a slow local +# model stays well under this; anything longer means a wedged LLM call +# and the retry path is cheaper than waiting forever. +REBUILD_TIMEOUT_SECS = 3600 + +# Vault-root entries that never belong to a member or the shared +# bucket. Mirrors the wiki's `_NON_MEMBER_DIRS` plus git itself. +_SKIP_TOP = {".git", ".obsidian", "wiki", "private", "templates", "_shared"} + +# Generated page filenames. Changes to these are the wiki's own output +# (or a human editing around the splice markers) — never a rebuild +# trigger, or the curator would chase its own tail. +_GENERATED_NAMES = {"about.md", "index.md"} + + +# ── Pure decision logic (tested in tests/stacklets) ────────────────────── + +def only_own_commits(subjects: list[str]) -> bool: + """True when every commit subject is a wiki publish of our own. + + An empty list is NOT ours: HEAD moved but the range shows no + commits, which means a history rewrite — the caller treats that + as a rebuild trigger, never as something to skip. + """ + return bool(subjects) and all( + s.startswith(COMMIT_PREFIX) for s in subjects + ) + + +class Debounce: + """Quiet-window tracker: `observe(head, now)` returns True once the + same head has been seen unchanged for `quiet_secs`.""" + + def __init__(self, quiet_secs: float): + self.quiet_secs = quiet_secs + self._head: str | None = None + self._since = 0.0 + + def observe(self, head: str, now: float) -> bool: + if head != self._head: + self._head, self._since = head, now + return False + return (now - self._since) >= self.quiet_secs + + def reset(self) -> None: + self._head = None + + def retry_later(self, now: float) -> None: + """Failed rebuild: keep the head, restart the quiet window.""" + self._since = now + + +def member_selection( + paths: list[str], + fm_reader, + *, + shared_bucket: str, +) -> list[str]: + """Map changed vault paths to an incremental `wiki` selection argv. + + Persons-only by design: member pages are where freshness is felt, + everything else (topics, cross-refs) heals on the nightly sweep. + Collects the bucket owner for personal captures plus every name in + `persons:` frontmatter (`fm_reader(path) -> dict`, fed by + `git show` so the mapping matches the commit). Returns e.g. + `["--home", "--member", "Homer Simpson"]`, or `[]` when nothing + relevant changed (only generated pages or skipped dirs) — home is + included whenever anything relevant changed at all. + """ + members: list[str] = [] + relevant = False + + for path in paths: + parts = [p for p in path.split("/") if p] + if not parts or parts[0] in _SKIP_TOP: + continue + if parts[-1] in _GENERATED_NAMES: + continue + relevant = True + if parts[0] != shared_bucket and len(parts) > 1 and parts[0] not in members: + members.append(parts[0]) + if path.endswith(".md"): + fm = fm_reader(path) or {} + persons = fm.get("persons") or fm.get("person") or [] + if isinstance(persons, str): + persons = [persons] + for person in persons: + if isinstance(person, str) and person.strip() and person not in members: + members.append(person) + + if not relevant: + return [] + argv = ["--home"] + for member in members: + argv += ["--member", member] + return argv + + +def nightly_due(nightly: str, last_run_date: str, now_local: time.struct_time) -> bool: + """True when the nightly sweep should run: a valid HH:MM is + configured, we're past it, and today's sweep hasn't happened.""" + if not nightly or ":" not in nightly: + return False + try: + hour, minute = (int(x) for x in nightly.split(":", 1)) + except ValueError: + return False + today = time.strftime("%Y-%m-%d", now_local) + if last_run_date == today: + return False + return (now_local.tm_hour, now_local.tm_min) >= (hour, minute) + + +# ── Git plumbing ───────────────────────────────────────────────────────── + +class Vault: + """Read-only git view of the vault working copy. + + The wiki container owns the `git pull`; we only ever read. The + bind-mounted repo belongs to the host user, so git's dubious- + ownership check is satisfied via a private GIT_CONFIG_GLOBAL + rather than touching any shared config. + """ + + def __init__(self, path: Path): + self.path = path + self._env = {**os.environ, "GIT_CONFIG_GLOBAL": "/tmp/curator-gitconfig"} + Path("/tmp/curator-gitconfig").write_text( + "[safe]\n\tdirectory = *\n", encoding="utf-8", + ) + + def _run(self, *args: str) -> str | None: + result = subprocess.run( + ["git", "-C", str(self.path), *args], + capture_output=True, text=True, env=self._env, + ) + if result.returncode != 0: + logger.debug("[curator] git {} failed: {}", args[0], result.stderr.strip()) + return None + return result.stdout + + async def head(self) -> str | None: + out = await asyncio.to_thread(self._run, "rev-parse", "HEAD") + return out.strip() if out else None + + async def subjects(self, since: str, until: str) -> list[str]: + """Commit subjects in `since..until`. A broken range (history + rewrite) returns [] — which `only_own_commits` treats as not + ours, so the rebuild still happens.""" + out = await asyncio.to_thread( + self._run, "log", "--format=%s", f"{since}..{until}", + ) + return out.splitlines() if out else [] + + async def changed_paths(self, since: str, until: str) -> list[str]: + out = await asyncio.to_thread( + self._run, "diff", "--name-only", f"{since}..{until}", + ) + return [p for p in (out or "").splitlines() if p.strip()] + + def frontmatter_at(self, rev: str, path: str) -> dict: + """Frontmatter of `path` at `rev` ({} for deleted/binary files).""" + out = self._run("show", f"{rev}:{path}") + return _parse_frontmatter(out) if out else {} + + +# ── Rebuild ────────────────────────────────────────────────────────────── + +async def rebuild(selection: list[str]) -> bool: + """One wiki generation pass via the CLI entrypoint — the same code + path `stack memory wiki` execs, in a subprocess so a wedged LLM + call dies with the child instead of inside this loop.""" + label = " ".join(selection) if selection else "(full sweep)" + logger.info("[curator] rebuilding wiki: {}", label) + try: + proc = await asyncio.create_subprocess_exec( + sys.executable, ENTRYPOINT, "wiki", *selection, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.STDOUT, + ) + out_bytes, _ = await asyncio.wait_for( + proc.communicate(), timeout=REBUILD_TIMEOUT_SECS, + ) + except TimeoutError: + proc.kill() + logger.warning("[curator] rebuild timed out after {}s", REBUILD_TIMEOUT_SECS) + return False + except Exception as e: + logger.warning("[curator] rebuild failed to start: {}", e) + return False + + output = out_bytes.decode(errors="replace").strip() + if proc.returncode != 0: + tail = "\n".join(output.splitlines()[-5:]) + logger.warning("[curator] wiki generation rc={}: {}", proc.returncode, tail) + return False + published = sum(1 for ln in output.splitlines() if ln.startswith("published ")) + logger.info("[curator] wiki refreshed — {} page(s) published", published) + return True + + +# ── Main loop ──────────────────────────────────────────────────────────── + +async def main() -> None: + if os.environ.get("WIKI_AUTO_REBUILD", "true").lower() != "true": + logger.info("[curator] wiki_auto_rebuild is off — idling") + await asyncio.Event().wait() + + vault_dir = Path(os.environ.get("MEMORY_VAULT_DIR", "/data/memory/vault")) + shared_bucket = os.environ.get("SHARED_BUCKET", "family") + state_dir = Path(os.environ.get("CURATOR_STATE_DIR", "/data/memory/curator")) + sha_file = state_dir / "last-rebuilt-sha" + nightly_file = state_dir / "last-nightly-date" + + while not (vault_dir / ".git").exists(): + logger.info("[curator] waiting for vault at {}", vault_dir) + await asyncio.sleep(POLL_SECS) + + vault = Vault(vault_dir) + debounce = Debounce(QUIET_SECS) + + def _write(path: Path, value: str) -> str: + state_dir.mkdir(parents=True, exist_ok=True) + path.write_text(value, encoding="utf-8") + return value + + def _read(path: Path) -> str: + return path.read_text(encoding="utf-8").strip() if path.exists() else "" + + # First boot starts from the current HEAD: existing content is the + # operator's to backfill (`stack memory wiki`), not something to + # surprise a fresh install with. From then on the SHA survives + # restarts, so a rebuild interrupted mid-flight is simply redone. + last = _read(sha_file) + if not last: + head = await vault.head() + if head: + last = _write(sha_file, head) + + logger.info( + "[curator] watching {} (poll {}s, quiet {}s, nightly {}) from {}", + vault_dir, POLL_SECS, QUIET_SECS, NIGHTLY or "off", last[:10] or "(empty repo)", + ) + + while True: + await asyncio.sleep(POLL_SECS) + head = await vault.head() + if not head: + continue + if not last: + last = _write(sha_file, head) + continue + + # ── Nightly full sweep ──────────────────────────────────────── + # Runs regardless of pending changes — it covers everything an + # incremental pass would, so it also clears the debounce. The + # date is recorded even on failure: one attempt per night, the + # incremental path and the manual CLI cover the gap. + if nightly_due(NIGHTLY, _read(nightly_file), time.localtime()): + _write(nightly_file, time.strftime("%Y-%m-%d", time.localtime())) + if await rebuild([]): + last = _write(sha_file, head) + debounce.reset() + continue + + # ── Incremental: persons + home, debounced ──────────────────── + if head == last: + debounce.reset() + continue + + if only_own_commits(await vault.subjects(last, head)): + last = _write(sha_file, head) + debounce.reset() + continue + + if not debounce.observe(head, time.monotonic()): + continue + + paths = await vault.changed_paths(last, head) + selection = member_selection( + paths, lambda p: vault.frontmatter_at(head, p), + shared_bucket=shared_bucket, + ) + if not selection: + last = _write(sha_file, head) + debounce.reset() + continue + + if await rebuild(selection): + last = _write(sha_file, head) + debounce.reset() + else: + debounce.retry_later(time.monotonic()) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/stacklets/memory/curator.Dockerfile b/stacklets/memory/curator.Dockerfile new file mode 100644 index 0000000..c0ee597 --- /dev/null +++ b/stacklets/memory/curator.Dockerfile @@ -0,0 +1,22 @@ +# curator.Dockerfile — slim runtime for the wiki curator sidecar. +# +# Deliberately NOT the bot-runner image: the curator needs python, +# git, loguru, and the OpenAI SDK (the framework LLM client behind +# the wiki generation it subprocesses) — 2 of the bot-runner's 10 +# dependencies. No Matrix, no libolm, no PDF stack. Code arrives by +# bind mount (see docker-compose.yml), same as the bot-runner, so +# code changes don't need an image rebuild; tzdata makes the +# WIKI_NIGHTLY local time honest inside the container. + +FROM python:3.12-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + git tzdata \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install --no-cache-dir "loguru>=0.7,<1.0" "openai>=1.50,<3.0" + +RUN adduser --disabled-password --uid 1000 curator +USER curator + +CMD ["python", "-u", "/stacklets/memory/bot/curator.py"] diff --git a/stacklets/memory/docker-compose.yml b/stacklets/memory/docker-compose.yml index 782fbb7..c9537a7 100644 --- a/stacklets/memory/docker-compose.yml +++ b/stacklets/memory/docker-compose.yml @@ -50,6 +50,59 @@ services: - "${PORT_BIND_IP:-127.0.0.1}:42070:8080" restart: unless-stopped + # Curator — keeps the wiki fresh. Debounced incremental rebuilds + # (member pages + home) after filings settle, plus a nightly full + # sweep as the self-healing catch-all. Own slim image (python + git + # + the LLM client deps) — see curator.Dockerfile; code arrives by + # bind mount like the bot-runner's, the env is memory's own. See + # `bot/curator.py` for the loop. + stack-memory-curator: + container_name: stack-memory-curator + build: + context: . + dockerfile: curator.Dockerfile + image: stack-memory-curator:local + # Locally-built image — nothing for Watchtower to pull. + labels: + - "com.centurylinklabs.watchtower.enable=false" + networks: + - stack + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + # In-container paths: the vault working copy (read-only use; the + # wiki container owns the pull) and the curator's state files, + # both under the /data/memory mount below. + MEMORY_VAULT_DIR: /data/memory/vault + CURATOR_STATE_DIR: /data/memory/curator + # Forgejo, container-network address — memory's own CODE_URL in + # .env is the host-side URL the install hooks use. + CODE_URL: http://stack-code:3000 + MATRIX_ADMIN_USER: ${MATRIX_ADMIN_USER} + MATRIX_ADMIN_PASSWORD: ${MATRIX_ADMIN_PASSWORD} + # LLM endpoint (docker-side URL) + model config for the wiki + # generation the curator subprocesses. + OPENAI_URL: ${OPENAI_URL} + OPENAI_KEY: ${OPENAI_KEY} + AI_DEFAULT_MODEL: ${AI_DEFAULT_MODEL} + AI_MODELS_JSON: ${AI_MODELS_JSON} + LANGUAGE: ${LANGUAGE} + SHARED_BUCKET: ${SHARED_BUCKET} + # Behavior — rendered from stack.toml [memory]. + WIKI_AUTO_REBUILD: ${WIKI_AUTO_REBUILD:-true} + WIKI_REBUILD_QUIET_SECS: ${WIKI_REBUILD_QUIET_SECS:-180} + WIKI_NIGHTLY: ${WIKI_NIGHTLY:-03:30} + # Local time for WIKI_NIGHTLY. + TZ: ${TIMEZONE:-UTC} + volumes: + # Code mounts, same shape as the bot-runner's: curator.py + the + # wiki module from /stacklets, the framework from /app/stack. + - ../../stacklets:/stacklets:ro + - ../../lib/stack:/app/stack:ro + # Vault working copy (read) + curator state dir (write). + - ${MEMORY_DATA_DIR}:/data/memory + restart: unless-stopped + networks: stack: external: true diff --git a/stacklets/memory/stacklet.toml b/stacklets/memory/stacklet.toml index 006d657..e26f7b0 100644 --- a/stacklets/memory/stacklet.toml +++ b/stacklets/memory/stacklet.toml @@ -58,6 +58,33 @@ SHARED_BUCKET = "{shared_bucket}" # also returns this path, so there is exactly one vault on disk. MEMORY_VAULT_DIR = "{data_dir}/memory/vault" +# Bind-mount source for the curator sidecar: the memory data dir as a +# whole, so one mount covers the vault (read) and the curator's state +# files (write) at the same /data/memory paths the containers expect. +MEMORY_DATA_DIR = "{data_dir}/memory" + +# Curator runtime — the slim sidecar runs the same wiki generation the +# CLI does, so it needs the LLM endpoint (docker-side), model config, +# Forgejo admin credentials (CODE_URL is overridden to the container- +# network address in docker-compose.yml), language, and timezone. +OPENAI_URL = "{ai_openai_url_docker}" +OPENAI_KEY = "{ai_openai_key}" +AI_DEFAULT_MODEL = "{ai_default_model}" +AI_MODELS_JSON = "{ai_models_json}" +LANGUAGE = "{language}" +TIMEZONE = "{timezone}" +MATRIX_ADMIN_USER = "{admin_username}" +MATRIX_ADMIN_PASSWORD = "{admin_password}" + +# Wiki freshness knobs (stack.toml [memory]). Incremental rebuilds run +# after the vault has been quiet for the debounce window, so a stack +# of documents filed in one go produces one rebuild, not one per +# document. The nightly full sweep ("HH:MM" local, empty disables) is +# the self-healing catch-all for topic pages and cross-references. +WIKI_AUTO_REBUILD = "{wiki_auto_rebuild}" +WIKI_REBUILD_QUIET_SECS = "{wiki_rebuild_quiet_secs}" +WIKI_NIGHTLY = "{wiki_nightly}" + # Public hostname the wiki renders into absolute URLs (sitemap, # social cards, etc). Empty domain (port mode) falls back to the # auto-detected LAN IP via `{ip}` so the site stays reachable. diff --git a/tests/stacklets/test_memory_curator.py b/tests/stacklets/test_memory_curator.py new file mode 100644 index 0000000..c71a06c --- /dev/null +++ b/tests/stacklets/test_memory_curator.py @@ -0,0 +1,172 @@ +"""Curator — the pure decision logic behind the wiki freshness loop. + +The sidecar's job is mostly plumbing (git reads, one subprocess call); +what's worth pinning is the logic that keeps it from misfiring: the +own-commit filter that stops a rebuild from triggering itself, the +debounce that turns a 25-document ingest into one rebuild instead of +25, the persons-only selection that keeps the incremental pass at 2-3 +LLM calls, and the nightly once-per-day gate. All pure and tested +here; the end-to-end path rides the integration rig like the rest of +the wiki. +""" + +from __future__ import annotations + +import sys +import time +from pathlib import Path + +_REPO_ROOT = Path(__file__).resolve().parent.parent.parent +sys.path.insert(0, str(_REPO_ROOT / "stacklets" / "memory" / "bot")) +sys.path.insert(0, str(_REPO_ROOT / "stacklets" / "memory" / "bot" / "cli")) + +from curator import ( # noqa: E402 + Debounce, + member_selection, + nightly_due, + only_own_commits, +) +from wiki import COMMIT_PREFIX # noqa: E402 + + +def _local(hhmm: str) -> time.struct_time: + """A struct_time for today at HH:MM local — enough for nightly_due.""" + return time.strptime(f"2026-06-11 {hhmm}", "%Y-%m-%d %H:%M") + + +# ── only_own_commits ───────────────────────────────────────────────────── + + +class TestOnlyOwnCommits: + def test_all_wiki_publishes_are_ours(self): + subjects = [ + f"{COMMIT_PREFIX} the family wiki home page", + f"{COMMIT_PREFIX} homer's wiki page", + f"{COMMIT_PREFIX} family/camping topic page", + ] + assert only_own_commits(subjects) is True + + def test_a_filing_in_the_batch_means_rebuild(self): + subjects = [ + f"{COMMIT_PREFIX} homer's wiki page", + "learn: Car Insurance Renewal 2026", + ] + assert only_own_commits(subjects) is False + + def test_empty_range_is_not_ours(self): + # HEAD moved but the log range came back empty — a history + # rewrite. Must read as "rebuild", never as "skip". + assert only_own_commits([]) is False + + def test_prefix_must_anchor_the_subject(self): + assert only_own_commits([f"revert: {COMMIT_PREFIX} home page"]) is False + + +# ── Debounce ───────────────────────────────────────────────────────────── + + +class TestDebounce: + def test_new_head_never_fires_immediately(self): + d = Debounce(quiet_secs=180) + assert d.observe("aaa", now=1000.0) is False + + def test_fires_after_quiet_window(self): + d = Debounce(quiet_secs=180) + assert d.observe("aaa", now=1000.0) is False + assert d.observe("aaa", now=1100.0) is False + assert d.observe("aaa", now=1180.0) is True + + def test_burst_restarts_the_window(self): + # Each new commit during an ingest moves HEAD — the window + # restarts and the rebuild waits for the burst to settle. + d = Debounce(quiet_secs=180) + assert d.observe("aaa", now=1000.0) is False + assert d.observe("bbb", now=1170.0) is False + assert d.observe("bbb", now=1340.0) is False + assert d.observe("bbb", now=1350.0) is True + + def test_reset_forgets_the_head(self): + d = Debounce(quiet_secs=180) + d.observe("aaa", now=1000.0) + d.reset() + assert d.observe("aaa", now=2000.0) is False + assert d.observe("aaa", now=2180.0) is True + + def test_retry_later_defers_a_failed_rebuild(self): + d = Debounce(quiet_secs=180) + d.observe("aaa", now=1000.0) + assert d.observe("aaa", now=1180.0) is True + d.retry_later(now=1200.0) + assert d.observe("aaa", now=1300.0) is False + assert d.observe("aaa", now=1380.0) is True + + +# ── member_selection ───────────────────────────────────────────────────── + + +def _fm(mapping): + """fm_reader stub: path -> frontmatter dict.""" + return lambda path: mapping.get(path, {}) + + +class TestMemberSelection: + def test_document_with_persons_selects_their_pages(self): + paths = ["family/documents/2026/06/2026-06-10-car-insurance-p7.md"] + reader = _fm({paths[0]: {"persons": ["Homer Simpson", "Marge Simpson"]}}) + assert member_selection(paths, reader, shared_bucket="family") == [ + "--home", + "--member", "Homer Simpson", + "--member", "Marge Simpson", + ] + + def test_personal_capture_selects_the_bucket_owner(self): + paths = ["homer/notes/2026/06/duff-recipe-ab12.md"] + assert member_selection(paths, _fm({}), shared_bucket="family") == [ + "--home", "--member", "homer", + ] + + def test_generated_pages_never_trigger(self): + # The wiki's own output (or a hand edit around the splice + # markers) must not feed back into a rebuild. + paths = ["index.md", "homer/about.md", "family/camping/about.md"] + assert member_selection(paths, _fm({}), shared_bucket="family") == [] + + def test_skipped_dirs_never_trigger(self): + paths = ["wiki/config.json", ".obsidian/workspace.json", "private/x.md"] + assert member_selection(paths, _fm({}), shared_bucket="family") == [] + + def test_relevant_change_without_persons_still_refreshes_home(self): + # A shared-bucket document with no persons frontmatter: home + # aggregates it, member pages are untouched. + paths = ["family/documents/2026/06/2026-06-10-electricity-p9.md"] + assert member_selection(paths, _fm({}), shared_bucket="family") == ["--home"] + + def test_no_duplicate_members(self): + paths = [ + "homer/notes/2026/06/a-1111.md", + "homer/notes/2026/06/b-2222.md", + ] + reader = _fm({p: {"persons": ["Homer Simpson"]} for p in paths}) + sel = member_selection(paths, reader, shared_bucket="family") + assert sel.count("--member") == 2 # homer (bucket) + Homer Simpson (fm) + assert sel[0] == "--home" + + +# ── nightly_due ────────────────────────────────────────────────────────── + + +class TestNightlyDue: + def test_due_after_configured_time(self): + assert nightly_due("03:30", "", _local("03:31")) is True + + def test_not_due_before_configured_time(self): + assert nightly_due("03:30", "", _local("02:59")) is False + + def test_runs_once_per_day(self): + assert nightly_due("03:30", "2026-06-11", _local("04:00")) is False + assert nightly_due("03:30", "2026-06-10", _local("04:00")) is True + + def test_empty_or_garbage_disables(self): + assert nightly_due("", "", _local("12:00")) is False + assert nightly_due("never", "", _local("12:00")) is False + assert nightly_due("3x:99y", "", _local("12:00")) is False From 87cc579164bba972449e520b5effa436b4a68024 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 Jun 2026 22:28:30 +0200 Subject: [PATCH 04/15] docs: wiki auto-rebuild in admin guide, curator follow-ups in backlog --- docs/admin-guide.md | 7 ++++++- docs/cleanup-backlog.md | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/admin-guide.md b/docs/admin-guide.md index cc20d34..6c0357c 100644 --- a/docs/admin-guide.md +++ b/docs/admin-guide.md @@ -428,7 +428,7 @@ The persistent layer of the household brain: a curated knowledge vault, rendered Setup seeds the vault with three things: the classification ontology (the topics and document types the archivist files against), household facts, and a hand-curated correspondents layer with aliases, so "Springfield Insurance" and "Springfield Ins. Co." resolve to the same page instead of becoming duplicates. -The wiki rebuilds itself when the vault changes: file a document in chat, refresh the page. Edits happen in Forgejo (every wiki page links to its source), so the commit log doubles as the household's learning history. +The wiki maintains itself. A curator sidecar (`stack-memory-curator`) watches the vault: when new filings settle it regenerates the pages of the family members involved plus the home page (a couple of LLM calls, a few minutes after the burst), and once a night it rebuilds everything — topic pages, cross-references, the lot — while the GPU has nothing better to do. `./stack memory wiki` stays available as the manual trigger, and `[memory]` in `stack.toml` holds the knobs (see [Configuration](#configuration)). Edits happen in Forgejo (every wiki page links to its source), so the commit log doubles as the household's learning history. | | | |---|---| @@ -468,6 +468,11 @@ schedule = "0 0 3 * * *" # Watchtower nightly image updates [ai] default = "mlx-community/Qwen3.5-9B-MLX-4bit" # change to match your RAM language = "en" # "de" for German voice/transcription + +[memory] +wiki_auto_rebuild = true # curator refreshes member pages after filings +wiki_rebuild_quiet_secs = 180 # how long the vault must stay quiet first +wiki_nightly = "03:30" # nightly full rebuild, local time ("" disables) ``` Key things to know: diff --git a/docs/cleanup-backlog.md b/docs/cleanup-backlog.md index eda1700..c2d8c5e 100644 --- a/docs/cleanup-backlog.md +++ b/docs/cleanup-backlog.md @@ -3,6 +3,33 @@ Items land here with a reason. Surface them when adjacent code is touched (see `docs/agent/dev.md`, Pre-1.0 conventions). +## Wiki freshness follow-ups (curator shipped 2026-06-11) + +The curator sidecar ships the first two freshness tiers: debounced +incremental rebuilds (persons + home) and the nightly full sweep. +Design notes that survive it, for whoever touches this next: + +- **Realtime is NOT a requirement.** The mirror is realtime; the wiki + is a derived view. The nightly sweep makes the incremental person + mapping merely *helpful*, never load-bearing — worst case for a + mapping miss is "stale until tonight". Don't grow the incremental + heuristics; grow the deriver instead. +- **famstacker `wiki` command** (Server Room chat trigger) is the + missing third tier: "CLI commands are the primitives, the bot runs + or offers them". Needs the famstack API to allow the command and an + ack-then-report shape for the multi-minute run. +- `{"cmd": "notify"}` for the famstack API (containers → Server Room + via `stack messages send`) lives in the git stash + ("wiki auto-rebuild: curator sidecar + API notify") — platform + piece, ship it with whichever consumer arrives first; the curator's + completion notice is a natural one. +- Rejected runtime homes, don't re-litigate: host daemons (no launchd + surface), quartz container (node image; "the wiki never writes"), + bot-runner service concept (one consumer), bot-runner image reuse + (the curator uses 2 of its 10 deps; slim image won). + +## CI release gate (post-0.3) + ## CI release gate (post-0.3) The pre-tag gate in `docs/agent/dev.md` is manual; v0.3.0-beta.1 shipped From f5b5bbb020887cb095ebd19483970ccd4783d33c Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 Jun 2026 22:55:22 +0200 Subject: [PATCH 05/15] fix: seed nightly date on curator first boot Without it, any boot after the configured time fired an immediate full sweep. Found live on the Simpsons rig. --- stacklets/memory/bot/curator.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stacklets/memory/bot/curator.py b/stacklets/memory/bot/curator.py index 90720b0..0508796 100644 --- a/stacklets/memory/bot/curator.py +++ b/stacklets/memory/bot/curator.py @@ -304,6 +304,12 @@ def _read(path: Path) -> str: head = await vault.head() if head: last = _write(sha_file, head) + # Same contract for the nightly: with no recorded date, any boot + # after the configured time would fire an immediate full sweep + # (found live on the test rig). First boot counts as done-today; + # the first real sweep runs tomorrow night. + if not _read(nightly_file): + _write(nightly_file, time.strftime("%Y-%m-%d", time.localtime())) logger.info( "[curator] watching {} (poll {}s, quiet {}s, nightly {}) from {}", From ec00f1f99ecaa80abfda2c73271c1dcc2df9116c Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 Jun 2026 22:55:23 +0200 Subject: [PATCH 06/15] =?UTF-8?q?docs:=20ops=20lessons=20=E2=80=94=20up=20?= =?UTF-8?q?vs=20restart,=20curator=20troubleshooting,=20wiki=20port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/agent/ops.md | 5 +++++ docs/cleanup-backlog.md | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/docs/agent/ops.md b/docs/agent/ops.md index 46de4b3..8585548 100644 --- a/docs/agent/ops.md +++ b/docs/agent/ops.md @@ -45,6 +45,8 @@ If a precondition is missing, `./stack` prints exactly what to do. Don't improvi **Output contract:** every command returns JSON when piped or when `--json` is passed. Force human output with `--pretty`. Exit code 0 == success. +**Applying config/compose changes to a running stacklet:** `stack up ` is the apply command — it re-renders `.env` and `compose up` creates new services and recreates changed ones, leaving the rest running. `restart` (= `down` + `up`) also works but takes the whole stacklet down first. Plain `docker compose restart` does neither: it won't re-render env or create new services. + ## Invariants - `stack up` is **always safe to re-run.** Failures leave the system in a valid intermediate state; re-running picks up where it failed. @@ -82,6 +84,7 @@ Refuse without explicit, scoped human approval: | 42050 | chatai | Open WebUI | | 42060 | ai | oMLX | | 42062 | ai | Whisper | +| 42070 | memory | Family wiki (Quartz) | Port collisions: do not silently rebind. Surface them. The user's fix is "stop the offender" or switch to domain mode. @@ -99,6 +102,8 @@ Port collisions: do not silently rebind. Surface them. The user's fix is "stop t | LLM OOM / very slow | RAM tier | Edit `[ai] default` to smaller model, then `./stack setup ai`. | | Disk full | `./stack host` | Likely the photo library. Move `data_dir` to external SSD. | | Element warns "browser not supported" | n/a | Click Continue. The check is outdated; Element works in every modern browser. | +| Wiki pages stale after filings | `docker logs stack-memory-curator` | Curator debounces (~3 min quiet) before rebuilding; topic pages wait for the nightly sweep. Manual override: `./stack memory wiki`. | +| Curator logs "waiting for vault" | `./stack status` → memory, code | Vault not cloned yet (memory hooks) or `stack-memory-wiki` down — the wiki container owns the `git pull` the curator watches. | For symptoms not on this table: `./stack logs ` + `./stack errors`, paste output to the user. **Do not invent fixes.** diff --git a/docs/cleanup-backlog.md b/docs/cleanup-backlog.md index c2d8c5e..67d661b 100644 --- a/docs/cleanup-backlog.md +++ b/docs/cleanup-backlog.md @@ -14,6 +14,14 @@ Design notes that survive it, for whoever touches this next: mapping merely *helpful*, never load-bearing — worst case for a mapping miss is "stale until tonight". Don't grow the incremental heuristics; grow the deriver instead. +- **Page update strategy — design when it's time, but the tension is + known (2026-06-11):** full regeneration resamples page quality (a + good page can regress on the next sweep); evolving the existing + page accumulates errors that self-cite (the "Bartley [5]" finding). + Most promising middle: a fact-checking pass — "page + sources, fix + what the sources don't support, touch nothing else" — anchored to + ground truth while preserving good prose. Likely CLI shape then: + `wiki` = update/check, `wiki rebuild` = fresh full generation. - **famstacker `wiki` command** (Server Room chat trigger) is the missing third tier: "CLI commands are the primitives, the bot runs or offers them". Needs the famstack API to allow the command and an From 464eb44d9d56ebce4cc634ed348b19e20d692e7d Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 Jun 2026 23:17:04 +0200 Subject: [PATCH 07/15] refactor: curator owns the vault pull, wiki becomes read-only view Same ls-remote-then-ff-pull semantics, moved from the quartz entrypoint to the curator's 30s tick. Pull runs even with rebuilds off. --- docs/agent/ops.md | 3 +- stacklets/memory/Dockerfile | 4 +-- stacklets/memory/bot/curator.py | 52 +++++++++++++++++++++------ stacklets/memory/docker-compose.yml | 39 ++++++++++---------- stacklets/memory/quartz/entrypoint.sh | 43 ++++------------------ 5 files changed, 70 insertions(+), 71 deletions(-) diff --git a/docs/agent/ops.md b/docs/agent/ops.md index 8585548..a4b2c30 100644 --- a/docs/agent/ops.md +++ b/docs/agent/ops.md @@ -103,7 +103,8 @@ Port collisions: do not silently rebind. Surface them. The user's fix is "stop t | Disk full | `./stack host` | Likely the photo library. Move `data_dir` to external SSD. | | Element warns "browser not supported" | n/a | Click Continue. The check is outdated; Element works in every modern browser. | | Wiki pages stale after filings | `docker logs stack-memory-curator` | Curator debounces (~3 min quiet) before rebuilding; topic pages wait for the nightly sweep. Manual override: `./stack memory wiki`. | -| Curator logs "waiting for vault" | `./stack status` → memory, code | Vault not cloned yet (memory hooks) or `stack-memory-wiki` down — the wiki container owns the `git pull` the curator watches. | +| Wiki shows old content entirely | `docker logs stack-memory-curator` | The curator owns the vault `git pull` (the wiki container is a read-only view). Curator down/stuck → vault and wiki go stale together. | +| Curator logs "waiting for vault" | `./stack status` → memory, code | Vault not cloned yet — the memory install hooks own the initial clone. | For symptoms not on this table: `./stack logs ` + `./stack errors`, paste output to the user. **Do not invent fixes.** diff --git a/stacklets/memory/Dockerfile b/stacklets/memory/Dockerfile index 381dbe6..876f88b 100644 --- a/stacklets/memory/Dockerfile +++ b/stacklets/memory/Dockerfile @@ -40,8 +40,8 @@ RUN git clone --depth 1 --branch "${QUARTZ_TAG}" https://github.com/jackyzha0/qu COPY quartz/quartz.config.ts ./quartz.config.ts COPY quartz/quartz.layout.ts ./quartz.layout.ts -# The entrypoint keeps the vault in sync (background git pull loop) and -# then serves it with Quartz. See quartz/entrypoint.sh. +# The entrypoint serves the vault with Quartz — a pure view. The +# curator sidecar owns the git pull. See quartz/entrypoint.sh. COPY quartz/entrypoint.sh /usr/local/bin/wiki-entrypoint.sh RUN chmod +x /usr/local/bin/wiki-entrypoint.sh diff --git a/stacklets/memory/bot/curator.py b/stacklets/memory/bot/curator.py index 0508796..5e39683 100644 --- a/stacklets/memory/bot/curator.py +++ b/stacklets/memory/bot/curator.py @@ -1,9 +1,9 @@ """Curator — keeps the family wiki fresh without anyone running a command. Runs as the `stack-memory-curator` sidecar (own slim image, see -`curator.Dockerfile`) and watches the vault working copy that the wiki -container keeps in sync with Forgejo (a `git pull` loop, 20s -interval). Two jobs: +`curator.Dockerfile`). The curator is the vault keeper: it owns the +`git pull` that keeps the working copy in sync with Forgejo (the wiki +container is a pure view that just watches the files). Jobs: 1. **Incremental, after filings settle.** New commits land — an archivist filing, a learn commit, a hand edit pushed from Obsidian. @@ -28,9 +28,11 @@ Failure shape: a failed rebuild keeps the SHA where it was and retries after another quiet window — the trigger is never lost, and -`stack memory wiki` always works as the manual override. If the wiki -container is down nothing pulls the vault, so the curator simply -sees no new commits until it returns. +`stack memory wiki` always works as the manual override. If the +curator is down, the vault (and therefore the wiki) goes stale +together — one component, one failure story. Disabling +`wiki_auto_rebuild` stops the rebuilds, NOT the pull: the wiki must +not rot just because the automation is off. Config surface (rendered from stack.toml by the runtime): WIKI_AUTO_REBUILD "true"/"false" — [memory] wiki_auto_rebuild @@ -211,6 +213,29 @@ async def head(self) -> str | None: out = await asyncio.to_thread(self._run, "rev-parse", "HEAD") return out.strip() if out else None + async def sync(self) -> None: + """Fast-forward the working copy from Forgejo. + + Same cheap shape the wiki entrypoint used before the curator + took ownership: an idle tick is one `ls-remote` ref query, the + fetch + ff happens only when the remote actually moved. Never + fatal — Forgejo briefly unreachable means the tick is skipped + and everything keeps serving what is on disk. + """ + def _sync() -> None: + # Same tick shape as the old wiki entrypoint loop: skip on + # any read failure, compare refs, pull only on real change. + local = self._run("rev-parse", "HEAD") + if not local: + return + remote = self._run("ls-remote", "origin", "HEAD") + remote_head = remote.split()[0] if remote and remote.split() else "" + if not remote_head or local.strip() == remote_head: + return + self._run("pull", "--quiet", "--ff-only") + + await asyncio.to_thread(_sync) + async def subjects(self, since: str, until: str) -> list[str]: """Commit subjects in `since..until`. A broken range (history rewrite) returns [] — which `only_own_commits` treats as not @@ -270,9 +295,7 @@ async def rebuild(selection: list[str]) -> bool: # ── Main loop ──────────────────────────────────────────────────────────── async def main() -> None: - if os.environ.get("WIKI_AUTO_REBUILD", "true").lower() != "true": - logger.info("[curator] wiki_auto_rebuild is off — idling") - await asyncio.Event().wait() + rebuild_enabled = os.environ.get("WIKI_AUTO_REBUILD", "true").lower() == "true" vault_dir = Path(os.environ.get("MEMORY_VAULT_DIR", "/data/memory/vault")) shared_bucket = os.environ.get("SHARED_BUCKET", "family") @@ -312,12 +335,19 @@ def _read(path: Path) -> str: _write(nightly_file, time.strftime("%Y-%m-%d", time.localtime())) logger.info( - "[curator] watching {} (poll {}s, quiet {}s, nightly {}) from {}", - vault_dir, POLL_SECS, QUIET_SECS, NIGHTLY or "off", last[:10] or "(empty repo)", + "[curator] keeping {} in sync (poll {}s, quiet {}s, nightly {}, rebuild {}) from {}", + vault_dir, POLL_SECS, QUIET_SECS, NIGHTLY or "off", + "on" if rebuild_enabled else "OFF", last[:10] or "(empty repo)", ) while True: await asyncio.sleep(POLL_SECS) + # The curator owns the pull — the wiki container only watches + # files. Runs even with rebuilds disabled: the wiki must not + # rot just because the automation is off. + await vault.sync() + if not rebuild_enabled: + continue head = await vault.head() if not head: continue diff --git a/stacklets/memory/docker-compose.yml b/stacklets/memory/docker-compose.yml index c9537a7..76ddd88 100644 --- a/stacklets/memory/docker-compose.yml +++ b/stacklets/memory/docker-compose.yml @@ -1,17 +1,17 @@ # stacklets/memory/docker-compose.yml — the family wiki # -# One service. The "wiki" is a Quartz v4 site that renders the +# Two services. The "wiki" is a Quartz v4 site that renders the # family memory vault as a browsable wiki: graph view, backlinks, # full-text search, mobile-friendly. The container runs `quartz # build --serve`, which watches the bind-mounted vault and rebuilds -# whenever the hooks pull new commits in from Forgejo. +# whenever files change. The "curator" is the vault keeper: it owns +# the `git pull` that keeps the working copy in sync with Forgejo, +# and regenerates wiki pages when filings land (see bot/curator.py). # -# The vault is mounted read-write so the wiki can keep its working -# copy in sync with Forgejo (a background `git pull` loop in the -# entrypoint). Edits still happen in Forgejo, which stays the canonical -# source of truth; the wiki only ever fast-forwards. The image is -# built locally from the sibling Dockerfile and Watchtower is told to -# ignore it (there's no upstream image to track). +# Forgejo stays the canonical source of truth; the wiki mounts the +# vault read-only and never touches git. Both images are built +# locally and Watchtower is told to ignore them (no upstream images +# to track). name: stack-memory @@ -28,11 +28,10 @@ services: - "com.centurylinklabs.watchtower.enable=false" networks: - stack - # Mounted read-write so the entrypoint can fast-forward the working - # copy from Forgejo (git pull). Edits still round-trip through - # Forgejo so they land as commits; the wiki never writes its own. + # Read-only: the wiki is a pure view. The curator below owns the + # pull; edits round-trip through Forgejo and land as commits. volumes: - - ${MEMORY_VAULT_DIR}:/vault + - ${MEMORY_VAULT_DIR}:/vault:ro environment: # Used by quartz.config.ts to build "edit on Forgejo" links and # absolute URLs. Resolved by the runtime from stack.toml. @@ -40,8 +39,6 @@ services: WIKI_HOST: ${WIKI_HOST} WIKI_IP: ${WIKI_IP} WIKI_PORT: "42070" - # How often the background loop pulls family/memory from Forgejo. - VAULT_SYNC_INTERVAL: "20" ports: # Container's Quartz preview server listens on 8080; we publish # on the stacklet's declared port (42070). PORT_BIND_IP is set @@ -50,12 +47,12 @@ services: - "${PORT_BIND_IP:-127.0.0.1}:42070:8080" restart: unless-stopped - # Curator — keeps the wiki fresh. Debounced incremental rebuilds - # (member pages + home) after filings settle, plus a nightly full - # sweep as the self-healing catch-all. Own slim image (python + git - # + the LLM client deps) — see curator.Dockerfile; code arrives by - # bind mount like the bot-runner's, the env is memory's own. See - # `bot/curator.py` for the loop. + # Curator — the vault keeper. Owns the `git pull` from Forgejo, + # runs debounced incremental rebuilds (member pages + home) after + # filings settle, plus a nightly full sweep as the self-healing + # catch-all. Own slim image (python + git + the LLM client deps) — + # see curator.Dockerfile; code arrives by bind mount like the + # bot-runner's, the env is memory's own. See `bot/curator.py`. stack-memory-curator: container_name: stack-memory-curator build: @@ -99,7 +96,7 @@ services: # wiki module from /stacklets, the framework from /app/stack. - ../../stacklets:/stacklets:ro - ../../lib/stack:/app/stack:ro - # Vault working copy (read) + curator state dir (write). + # Vault working copy (the curator pulls it) + state dir. - ${MEMORY_DATA_DIR}:/data/memory restart: unless-stopped diff --git a/stacklets/memory/quartz/entrypoint.sh b/stacklets/memory/quartz/entrypoint.sh index 55b8c29..1518e8c 100644 --- a/stacklets/memory/quartz/entrypoint.sh +++ b/stacklets/memory/quartz/entrypoint.sh @@ -1,43 +1,14 @@ #!/bin/sh -# entrypoint.sh — keep the vault in sync from Forgejo, then serve it. +# entrypoint.sh — serve the vault as the family wiki. # -# Quartz `--serve` watches /vault and rebuilds on change, but the working -# copy only advances when we pull. A background loop pulls family/memory -# on a short interval, so a new commit — an archivist mirror, a seed -# push, or a hand-edit in the Forgejo web UI — shows up within -# VAULT_SYNC_INTERVAL seconds and Quartz rebuilds it for free. A Forgejo -# push webhook could replace this loop later; polling keeps it -# dependency-free and is cheap (the rebuild is sub-second). +# Pure view: Quartz `--serve` watches /vault and rebuilds on change. +# The working copy is kept in sync by the curator sidecar (the vault +# keeper — it pulls from Forgejo on its poll tick; see +# `../bot/curator.py`), so this container never touches git and the +# vault is mounted read-only. If the curator is down, the wiki keeps +# serving what is on disk — stale, never broken. set -e -INTERVAL="${VAULT_SYNC_INTERVAL:-20}" - -# The working copy is cloned on the host and bind-mounted here, so git -# sees a different owner than the container user. Trust it explicitly, -# otherwise git refuses to operate on "dubious ownership". -git config --global --add safe.directory /vault - -# Background sync, kept as cheap as possible. Each tick compares the -# local HEAD against the remote with `git ls-remote` — a single -# lightweight ref query, no object transfer, no working-tree touch — and -# only pulls when the remote has actually moved. So an idle tick (the -# common case) costs one tiny request and never triggers a rebuild; the -# fetch + fast-forward happens only when there is genuinely a new commit. -# -# Never fatal: if the clone isn't ready yet (first boot) or Forgejo is -# briefly unreachable, the tick is skipped and retried next time while -# the site keeps serving what is already on disk. -( - while true; do - sleep "$INTERVAL" - local_head=$(git -C /vault rev-parse HEAD 2>/dev/null) || continue - remote_head=$(git -C /vault ls-remote origin HEAD 2>/dev/null | cut -f1) - if [ -n "$remote_head" ] && [ "$local_head" != "$remote_head" ]; then - git -C /vault pull --quiet --ff-only 2>/dev/null || true - fi - done -) & - # Quartz in the foreground; its file watcher turns each pulled change # into a rebuild. Port 8080 is the in-container port Caddy and the # compose port mapping target. From 717d3126ec30cc900b8c920fd60d0630dc2ea095 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 Jun 2026 23:43:44 +0200 Subject: [PATCH 08/15] feat: pin wiki generation to temperature 0 Same evidence now yields the same page byte-for-byte (verified on the rig); pages only change when their evidence does. --- lib/stack/ai/client.py | 13 ++++++++++--- stacklets/memory/bot/cli/wiki.py | 14 +++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/stack/ai/client.py b/lib/stack/ai/client.py index f56015a..24de4b6 100644 --- a/lib/stack/ai/client.py +++ b/lib/stack/ai/client.py @@ -208,13 +208,18 @@ def _full_role(self, role: str) -> str: async def complete(self, role: str, prompt: str, *, images: "list | None" = None, json_mode: bool = False, - model_override: str | None = None) -> str: + model_override: str | None = None, + temperature: float | None = None) -> str: """Run a single chat completion and return the response text. ``role`` resolves to a concrete model via `resolve_model`. Pass ``images`` (objects with ``.data``/``.mime``) for a multimodal - call, and ``json_mode=True`` to ask for a JSON object back. SDK - errors are translated to the typed LLM errors above. + call, and ``json_mode=True`` to ask for a JSON object back. + ``temperature`` pins sampling when output stability matters + (the wiki generator regenerates pages and wants the same + evidence to yield the same page); None keeps the server + default. SDK errors are translated to the typed LLM errors + above. """ model = model_override or resolve_model(self._full_role(role)) content = prompt if not images else _content_parts(prompt, images) @@ -222,6 +227,8 @@ async def complete(self, role: str, prompt: str, *, kwargs: dict = {} if json_mode: kwargs["response_format"] = {"type": "json_object"} + if temperature is not None: + kwargs["temperature"] = temperature try: resp = await self._client.chat.completions.create( diff --git a/stacklets/memory/bot/cli/wiki.py b/stacklets/memory/bot/cli/wiki.py index 1d04a85..dbdb12d 100644 --- a/stacklets/memory/bot/cli/wiki.py +++ b/stacklets/memory/bot/cli/wiki.py @@ -80,6 +80,14 @@ # another rebuild — change it and that loop comes back. COMMIT_PREFIX = "docs(memory): refresh" +# Greedy decoding for page generation. Pages are regenerated, so +# sampling temperature turns every rebuild into a dice roll: the same +# evidence produced visibly different pages run to run (observed live +# 2026-06-11 — a rich facts list vanished on a one-note evidence +# change). Pinned to 0 so a page is a near-deterministic function of +# its evidence and only changes when the evidence does. +_TEMPERATURE = 0.0 + # Bracketed regenerate markers. Match the correspondent-page convention # verbatim so the same human contract holds across every generated page: # edits outside the brackets are preserved, inside is ours to rewrite. @@ -278,7 +286,7 @@ async def _generate_home( """ prompt = _build_home_prompt(index, roster=roster, lang=lang) try: - page = (await llm.complete("overview", prompt)).strip() + page = (await llm.complete("overview", prompt, temperature=_TEMPERATURE)).strip() except LLMUnavailableError as e: _err(f"LLM unavailable: {e}") return 1 @@ -316,7 +324,7 @@ async def _generate_member( facts = _load_facts(vault, slug) prompt = _build_member_prompt(display, slug, entries, facts, lang=lang) try: - page = (await llm.complete("overview", prompt)).strip() + page = (await llm.complete("overview", prompt, temperature=_TEMPERATURE)).strip() except LLMUnavailableError as e: _err(f"LLM unavailable: {e}") return 1 @@ -374,7 +382,7 @@ async def _generate_topic( display, topic_slug, scope, entries, cross_refs, lang=lang, ) try: - page = (await llm.complete("overview", prompt)).strip() + page = (await llm.complete("overview", prompt, temperature=_TEMPERATURE)).strip() except LLMUnavailableError as e: _err(f"LLM unavailable: {e}") return 1 From c711d11f4120cf1ef9c7544b61f3ace41b01cd53 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 Jun 2026 23:57:38 +0200 Subject: [PATCH 09/15] feat: WIKI_TEMPERATURE env override for generation experiments --- stacklets/memory/bot/cli/wiki.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stacklets/memory/bot/cli/wiki.py b/stacklets/memory/bot/cli/wiki.py index dbdb12d..bde9d37 100644 --- a/stacklets/memory/bot/cli/wiki.py +++ b/stacklets/memory/bot/cli/wiki.py @@ -80,13 +80,13 @@ # another rebuild — change it and that loop comes back. COMMIT_PREFIX = "docs(memory): refresh" -# Greedy decoding for page generation. Pages are regenerated, so -# sampling temperature turns every rebuild into a dice roll: the same -# evidence produced visibly different pages run to run (observed live -# 2026-06-11 — a rich facts list vanished on a one-note evidence -# change). Pinned to 0 so a page is a near-deterministic function of -# its evidence and only changes when the evidence does. -_TEMPERATURE = 0.0 +# Decoding temperature for page generation. Sampling turns every +# rebuild into a dice roll: the same evidence produced visibly +# different pages run to run (observed live 2026-06-11 — a rich facts +# list vanished on a one-note evidence change). Default 0 (greedy) so +# a page is a near-deterministic function of its evidence; the env +# override exists for experiments and unusual models. +_TEMPERATURE = float(os.environ.get("WIKI_TEMPERATURE", "0.0")) # Bracketed regenerate markers. Match the correspondent-page convention # verbatim so the same human contract holds across every generated page: From 90213d0d0eec7a5dbfd9ecbeda8031d181a2c862 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 12 Jun 2026 11:33:51 +0200 Subject: [PATCH 10/15] feat: profile-card wiki prompts Overview not archive: recency-weighted About, themed interests, capped activity, no ledger content, omit empty sections. --- stacklets/memory/bot/cli/wiki.py | 46 ++++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/stacklets/memory/bot/cli/wiki.py b/stacklets/memory/bot/cli/wiki.py index bde9d37..548b70a 100644 --- a/stacklets/memory/bot/cli/wiki.py +++ b/stacklets/memory/bot/cli/wiki.py @@ -918,10 +918,10 @@ def _build_home_prompt(entries: list[dict], *, roster: list[str], lang: str) -> Two or three lines about the dwelling itself: ownership/rental, year acquired or moved in, anything structurally noted (mortgage, deed). Skip if nothing on record. ## Real Estate -Other properties owned (rentals, second homes, plots). One bullet per property. Write "(no information on file)" if nothing on record. +Other properties owned (rentals, second homes, plots). One bullet per property. ## Vehicles -One bullet per vehicle: year, make/model, owner. Write "(no information on file)" if nothing. +One bullet per vehicle: year, make/model, owner. ## Insurance One bullet per policy: name / provider — what it covers — owner. @@ -938,7 +938,7 @@ def _build_home_prompt(entries: list[dict], *, roster: list[str], lang: str) -> - The H1 is the family surname prefixed with "The" (e.g. "# The Simpsons"). Pick the surname most frequently associated with household members; if multiple appear (blended family), use the dominant one. - The blockquote line below the H1 is the current primary address. Omit the blockquote entirely if no address is on file. - Respond in: {lang}. -- Use ONLY what the source summaries support. If a section has no source material, write "(no information on file)" — never invent. +- Use ONLY what the source summaries support. A section with no source material is omitted entirely — never invent, never write placeholder lines. - Cite the document that established each fact as [N] inline. Multiple sources: [1, 3]. - Keep entries dense and skimmable. One bullet per member; one bullet per item elsewhere. - Do NOT add a "References" section — it is appended programmatically after your response. Don't list source paths or URLs in your output. @@ -949,11 +949,14 @@ def _build_member_prompt( display: str, slug: str, entries: list[dict], facts: list[tuple[str, str]], *, lang: str, ) -> str: - """Prompt for one member's page: their slice of summaries plus facts. + """Prompt for one member's profile card: summaries plus facts. - Curated facts ride in as ground truth (the family typed them); the - document summaries are the cited evidence. Section layout is fixed - for the same reason the home page's is. + The page is a curated overview for assistants and browsing family, + not an archive (docs/design/brain/wiki-page-anatomy.md): recency- + weighted About, themed interests, capped activity, no ledger + content. Curated facts ride in as ground truth (the family typed + them); the document summaries are the cited evidence. Section + layout is fixed for the same reason the home page's is. The evidence slice is shared by design: Lisa's birth certificate is on Lisa's page AND describes her parents. Small models conflate @@ -972,7 +975,7 @@ def _build_member_prompt( else: facts_block = f"No hand-curated facts on file for {display}." - return f"""You are composing the personal page for one member of a family's private, self-hosted memory wiki. The page is the landing page when someone opens this person's folder in the wiki. The vault is private — identifying details are fine to include. + return f"""You are composing the profile page for one member of a family's private, self-hosted memory wiki. It is an OVERVIEW, not an archive: the card a family member (or the family's assistant) reads to get a feel for who this person is right now — their role, their current life, their interests and quirks. Detail lives in the cited documents, one click away. This page is about: {display} (vault slug: {slug}). @@ -982,35 +985,32 @@ def _build_member_prompt( {evidence} -Produce a markdown page with this EXACT structure and section order: +Produce a markdown page with this EXACT structure and section order (omit a section entirely when nothing supports it): # {display} > ## About -One short paragraph: {display}'s own role, occupation or school, and defining details — only what the documents and facts state about {display} personally. [N] +One short paragraph: who {display} is TODAY — role in the family, occupation or school, current life context. When sources span years, prefer the most recent picture; old contracts and certificates are background, not news. [N] -## Facts & Preferences -Bullet the hand-curated facts above (rules, habits, preferences, goals), one per line. Write "(none on file)" if there are none. +## Interests & Preferences +Themed bullets that give a feel for the person: hobbies, habits, favorite places, pets, preferences, quirks. Merge the hand-curated facts above with what the documents reveal (a league membership, a regular haunt). Format: **:** . [N] ## Recent Activity -The most recent notes, bookmarks, and documents involving them, newest first. One bullet each: . [N] - -## Documents -Key filed documents that name them (insurance, medical, school, financial). One bullet each. [N] +The most recent events involving them, newest first, AT MOST 8 bullets: . [N] -## People & Organizations -Organizations and correspondents associated with their documents. One bullet each. [N] +## Key Documents +The load-bearing documents naming them (identity, contracts, insurance, medical, school, financial). One bullet each. [N] Receipts and everyday ephemera stay out — they remain reachable through Recent Activity citations. Rules: +- The whole page stays under roughly 300 words. It is a profile card, not a ledger: never copy line items, account numbers, or ID numbers onto the page — the citations carry that detail. - The H1 is the person's full name as it appears in the documents; fall back to "{display}". - The documents above also involve OTHER family members (see each entry's "involves:" list). A birth certificate is mostly about the parents; school papers name a parent. State a fact about {display} ONLY when the source ties it to {display} by name. Never give {display} another person's role, profession, or relationship (mother/father, spouse, caretaker, employer). - When a summary says "the mother", "her husband", or similar without naming {display}, that fact belongs to someone else — leave it off this page. - Unsure who a fact is about? Leave it out. A short page is correct; a wrong page is not. - Respond in: {lang}. -- Use ONLY what the sources and facts above support. Empty section → "(none on file)". Never invent. +- Use ONLY what the sources and facts above support. Never invent. Omit empty sections — no placeholder lines. - Cite the source document as [N] inline. Multiple sources: [1, 3]. -- Keep entries dense and skimmable. - Do NOT add a "References" section — it is appended programmatically after your response. Don't list source paths or URLs in your output. """ @@ -1079,7 +1079,7 @@ def _build_topic_prompt( f"tracking on {display.lower()}." ) - return f"""You are composing the landing page for a topic folder in a family's private, self-hosted memory wiki. The page is the first thing someone sees when they open the topic's folder. The vault is private — identifying details (full names, places, prices) are fine to include when documents reveal them. + return f"""You are composing the landing page for a topic folder in a family's private, self-hosted memory wiki. It is an OVERVIEW someone (or the family's assistant) reads to understand what this topic is about and what has happened in it lately — not an archive. Detail lives in the cited captures, one click away. The vault is private — identifying details (full names, places, prices) are fine to include when documents reveal them. This page is about the topic: {display} (vault slug: {slug}, scope: {scope}). @@ -1098,14 +1098,14 @@ def _build_topic_prompt( One short paragraph: what this topic covers in the family's memory, what kinds of captures land here, what makes it worth revisiting. [N] ## Recent Activity -The most recent captures filed under this topic, newest first. One bullet each: ``. [N] +The most recent captures filed under this topic, newest first, AT MOST 8 bullets: ``. [N] ## Cross-references {"Captures filed in other buckets that mention this topic. One bullet each: ` [from ]`. [N]" if cross_refs else "(omit this section)"} Rules: - Respond in: {lang}. -- Use ONLY what the sources above support. Empty section → "(none on file)". Never invent. +- Use ONLY what the sources above support. Never invent. Omit empty sections — no placeholder lines. - Cite the source capture as [N] inline. Multiple sources: [1, 3]. - Keep entries dense and skimmable. - Do NOT add a "References" section — it is appended programmatically after your response. Don't list source paths or URLs in your output. From edf24c7e65dffd733caf471b0a7026cc3e8587fc Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 12 Jun 2026 12:10:28 +0200 Subject: [PATCH 11/15] feat: anchor member page regen to previous version Mechanical citation renumbering + explicit new-evidence block; the model keeps wording stable and only works in what changed. --- stacklets/memory/bot/cli/wiki.py | 119 +++++++++++++++++++++++++++- tests/stacklets/test_memory_wiki.py | 52 ++++++++++++ 2 files changed, 169 insertions(+), 2 deletions(-) diff --git a/stacklets/memory/bot/cli/wiki.py b/stacklets/memory/bot/cli/wiki.py index 548b70a..f0f4a6e 100644 --- a/stacklets/memory/bot/cli/wiki.py +++ b/stacklets/memory/bot/cli/wiki.py @@ -322,7 +322,37 @@ async def _generate_member( display = slug.capitalize() facts = _load_facts(vault, slug) - prompt = _build_member_prompt(display, slug, entries, facts, lang=lang) + + # Anchored regen: renumber the existing page's citations to the + # current evidence order, and name what's new since that page was + # generated. Both jobs are mechanical here so the model carries + # neither (it fails silently at both — see _previous_generated). + previous, prev_refs = _previous_generated(vault / slug / "about.md") + new_evidence: list[str] = [] + if previous: + link_to_n = { + _relative_link(e["rel"], slug): n + for n, e in enumerate(entries, start=1) + if e.get("rel") + } + remap = { + old: link_to_n[link] + for link, old in prev_refs.items() + if link in link_to_n + } + previous = _renumber_citations(previous, remap) + for n, e in enumerate(entries, start=1): + link = _relative_link(e.get("rel") or "", slug) + if link not in prev_refs: + meta = " · ".join( + x for x in (e.get("date"), e.get("title")) if x + ) + new_evidence.append(f"[{n}] {meta}") + + prompt = _build_member_prompt( + display, slug, entries, facts, lang=lang, + previous=previous, new_evidence=new_evidence, + ) try: page = (await llm.complete("overview", prompt, temperature=_TEMPERATURE)).strip() except LLMUnavailableError as e: @@ -756,6 +786,55 @@ def _relative_link(rel: str, page_dir: str) -> str: # ── Bracketed-region splice ──────────────────────────────────────────────── +def _previous_generated(page_path: Path) -> tuple[str, dict[str, int]]: + """The current generated body of a page, for prompt anchoring. + + Returns ``(body, refs)``: the content between the regenerate + markers with the rendered References section stripped, and the + reference map that section carried (link path → citation number). + The map lets the caller renumber the baseline's citations to the + CURRENT evidence order before prompting — new evidence shifts the + numbering, and a model asked to renumber silently doesn't + (measured 2026-06-12: byte-identical body over a shifted + references block — every citation pointing at the wrong source). + ``("", {})`` when the page doesn't exist yet. + """ + try: + text = page_path.read_text(encoding="utf-8") + except OSError: + return "", {} + start = text.find(_BEGIN) + end = text.find(_END) + if start == -1 or end == -1 or end <= start: + return "", {} + body = text[start + len(_BEGIN):end].strip() + refs_map = { + m.group(2): int(m.group(1)) + for m in re.finditer( + r"^- \[(\d+)\] \[[^\]]*\]\(([^)]+)\)", body, re.M, + ) + } + refs = body.find("## References") + if refs != -1: + body = body[:refs].rstrip() + return body, refs_map + + +def _renumber_citations(text: str, remap: dict[int, int]) -> str: + """Rewrite every ``[N]``/``[N, M]`` citation through ``remap``. + + Numbers without a mapping pass through unchanged (evidence that + vanished from the index; the prompt tells the model to drop those + lines). Single-pass over citation groups, so chained renumbering + (1→2 while 2→3) cannot double-apply. + """ + def _sub(m: re.Match) -> str: + nums = [int(x) for x in re.split(r",\s*", m.group(1))] + return "[" + ", ".join(str(remap.get(n, n)) for n in nums) + "]" + + return re.sub(r"\[(\d+(?:,\s*\d+)*)\]", _sub, text) + + def _splice_generated(existing: str, generated: str, *, default_preamble: str = "") -> str: """Place `generated` inside the regenerate region of `existing`. @@ -948,6 +1027,7 @@ def _build_home_prompt(entries: list[dict], *, roster: list[str], lang: str) -> def _build_member_prompt( display: str, slug: str, entries: list[dict], facts: list[tuple[str, str]], *, lang: str, + previous: str = "", new_evidence: list[str] | None = None, ) -> str: """Prompt for one member's profile card: summaries plus facts. @@ -958,6 +1038,12 @@ def _build_member_prompt( them); the document summaries are the cited evidence. Section layout is fixed for the same reason the home page's is. + ``previous`` is the page's current generated body (sans rendered + references): with it the prompt anchors the regen to the existing + wording so a new document produces a reviewable diff instead of a + full resample — temp 0 alone does NOT give that (evidence+1 changes + the input, and everything redraws; measured 2026-06-12). + The evidence slice is shared by design: Lisa's birth certificate is on Lisa's page AND describes her parents. Small models conflate those roles onto the page's subject ("Lisa ... her husband Homer"), @@ -975,6 +1061,35 @@ def _build_member_prompt( else: facts_block = f"No hand-curated facts on file for {display}." + if previous: + if new_evidence: + fresh = "\n".join(new_evidence) + fresh_block = ( + "NEW since the baseline (not yet on the page) — work it " + "into the right sections, citing its [N]:\n" + fresh + ) + else: + fresh_block = ( + "Nothing is new since the baseline — reproduce it, " + "applying corrections only where it contradicts the " + "evidence above." + ) + anchor_block = f""" +The page already exists. Its current version is below — treat it as the baseline, not as something to rewrite: +- Keep its wording, section names, themes, and bullet order EXACTLY wherever the evidence still supports them. +- The baseline's [N] citations already match the evidence numbering above. Do not change them. +- Touch existing lines only where new evidence forces it (e.g. the Recent Activity cap pushing out the oldest entry). +- Drop a line only when its evidence no longer appears above. +- Never rephrase, restyle, or reorganize anything else. + +{fresh_block} + +CURRENT PAGE (baseline): +{previous} +""" + else: + anchor_block = "" + return f"""You are composing the profile page for one member of a family's private, self-hosted memory wiki. It is an OVERVIEW, not an archive: the card a family member (or the family's assistant) reads to get a feel for who this person is right now — their role, their current life, their interests and quirks. Detail lives in the cited documents, one click away. This page is about: {display} (vault slug: {slug}). @@ -984,7 +1099,7 @@ def _build_member_prompt( Source summaries involving {display} (cite as [N] inline where the fact came from a specific document): {evidence} - +{anchor_block} Produce a markdown page with this EXACT structure and section order (omit a section entirely when nothing supports it): # {display} diff --git a/tests/stacklets/test_memory_wiki.py b/tests/stacklets/test_memory_wiki.py index cd7a2af..ef6901c 100644 --- a/tests/stacklets/test_memory_wiki.py +++ b/tests/stacklets/test_memory_wiki.py @@ -319,3 +319,55 @@ def test_display_with_special_chars(self): pre = _topic_preamble("van-life", "Van Life", "shared") assert "title: Van Life" in pre assert "slug: van-life" in pre + + +# ── Anchored regen helpers ────────────────────────────────────────────── + +from wiki import _previous_generated, _renumber_citations # noqa: E402 + + +class TestPreviousGenerated: + def _page(self, tmp_path: Path, body: str) -> Path: + p = tmp_path / "about.md" + p.write_text( + "---\ntitle: Homer\n---\n\n" + "\n\n" + f"{body}\n\n" + "\n", + encoding="utf-8", + ) + return p + + def test_missing_page_is_empty(self, tmp_path: Path) -> None: + assert _previous_generated(tmp_path / "nope.md") == ("", {}) + + def test_page_without_markers_is_empty(self, tmp_path: Path) -> None: + p = tmp_path / "about.md" + p.write_text("# Hand-written page\n", encoding="utf-8") + assert _previous_generated(p) == ("", {}) + + def test_strips_references_and_returns_map(self, tmp_path: Path) -> None: + p = self._page( + tmp_path, + "# Homer\n\nFact [1]. Other [2].\n\n" + "## References\n\n" + "- [1] [Birth Cert](../family/documents/bc.md) - 1956-05-15\n" + "- [2] [Note](notes/n.md) - 2026-06-12", + ) + body, refs = _previous_generated(p) + assert "## References" not in body + assert body.startswith("# Homer") + assert refs == {"../family/documents/bc.md": 1, "notes/n.md": 2} + + +class TestRenumberCitations: + def test_single_and_grouped(self) -> None: + out = _renumber_citations("A [1]. B [1, 3]. C [2].", {1: 2, 2: 3, 3: 1}) + assert out == "A [2]. B [2, 1]. C [3]." + + def test_chained_remap_applies_once(self) -> None: + # 1→2 while 2→3: [1] must become [2], not slide on to [3]. + assert _renumber_citations("[1] [2]", {1: 2, 2: 3}) == "[2] [3]" + + def test_unmapped_numbers_pass_through(self) -> None: + assert _renumber_citations("Kept [7].", {1: 2}) == "Kept [7]." From 0a08764fd9f37900e0dd0a39ccbe229444686fdd Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 12 Jun 2026 16:32:48 +0200 Subject: [PATCH 12/15] feat(memory): seed vault .gitignore for Obsidian compatibility --- stacklets/memory/seeds/.gitignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 stacklets/memory/seeds/.gitignore diff --git a/stacklets/memory/seeds/.gitignore b/stacklets/memory/seeds/.gitignore new file mode 100644 index 0000000..df1abbb --- /dev/null +++ b/stacklets/memory/seeds/.gitignore @@ -0,0 +1,6 @@ +# Obsidian per-user state — the vault is meant to be opened in Obsidian, +# but UI state must not hit git: workspace.json rewrites itself on every +# click, and any non-curator commit triggers a wiki rebuild. +.obsidian/ +.trash/ +.DS_Store From de3f9b726188f88b46810325e54f40990d42a5c2 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 12 Jun 2026 19:10:44 +0200 Subject: [PATCH 13/15] docs: add family wiki to What you get, with chat answer screenshot --- README.md | 8 ++++++++ docs/assets/memory-wiki-answer.webp | Bin 0 -> 36654 bytes 2 files changed, 8 insertions(+) create mode 100644 docs/assets/memory-wiki-answer.webp diff --git a/README.md b/README.md index 1a736ce..d09f687 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ The reference implementation runs on a Mac Studio M1 in our living room at Lake - **Private family chat** with native iOS, Android, macOS, Windows and web apps (Matrix + Element X). - **Photo library and mobile backup** that replaces iCloud Photos for the whole family (Immich). - **Document archive with OCR** that you photograph from your phone and the local AI files for you (Paperless-ngx). +- **Your family wiki**: an Obsidian-compatible second brain, generated from the documents, notes, and voice memos you file (memory). - **Local AI engine** on Apple Metal GPU: voice transcription, text-to-speech, document classification (oMLX + Whisper + Piper). - **A bot runtime in chat** that automates the small stuff: filing receipts, transcribing voice memos, status reports. - **One CLI to operate it all**: `./stack up ` and it is running. @@ -55,6 +56,13 @@ The reference implementation runs on a Mac Studio M1 in our living room at Lake

The archivist files and finds documents from your phone, in chat

+

+ Homer asks in the family chat whether driving under the influence of Duff beer is covered by the car insurance. The archivist answers no, citing the policy documents. +

+ +

Ask the wiki a question in chat, get an answer with sources

+ + Everything runs on your Mac. Nothing leaves your network unless you tell it to. ## Quick Start diff --git a/docs/assets/memory-wiki-answer.webp b/docs/assets/memory-wiki-answer.webp new file mode 100644 index 0000000000000000000000000000000000000000..51c6e808146b070b7c935dfb73edd0f0dafd7975 GIT binary patch literal 36654 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 literal 0 HcmV?d00001 From 6a57862b96cfbfb0615ef698d51e44e6226b4d2c Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 12 Jun 2026 19:38:48 +0200 Subject: [PATCH 14/15] docs: side-by-side demo video and wiki answer in a table --- README.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d09f687..b294b94 100644 --- a/README.md +++ b/README.md @@ -49,18 +49,20 @@ The reference implementation runs on a Mac Studio M1 in our living room at Lake - **A bot runtime in chat** that automates the small stuff: filing receipts, transcribing voice memos, status reports. - **One CLI to operate it all**: `./stack up ` and it is running. -

- -

- -

The archivist files and finds documents from your phone, in chat

- - -

- Homer asks in the family chat whether driving under the influence of Duff beer is covered by the car insurance. The archivist answers no, citing the policy documents. -

- -

Ask the wiki a question in chat, get an answer with sources

+ + + + + + + + + +
+ + + Homer asks in the family chat whether driving under the influence of Duff beer is covered by the car insurance. The archivist answers no, citing the policy documents. +
The archivist files and finds documents from your phone, in chatAsk the wiki a question in chat, get an answer with sources
Everything runs on your Mac. Nothing leaves your network unless you tell it to. From 6d7b90f29125e048e77f1e8bd5675215387dddb1 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 12 Jun 2026 19:55:44 +0200 Subject: [PATCH 15/15] docs: crop wiki answer screenshot to the conversation --- README.md | 2 +- docs/assets/memory-wiki-answer.png | Bin 0 -> 81428 bytes docs/assets/memory-wiki-answer.webp | Bin 36654 -> 0 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/assets/memory-wiki-answer.png delete mode 100644 docs/assets/memory-wiki-answer.webp diff --git a/README.md b/README.md index b294b94..c49e284 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ The reference implementation runs on a Mac Studio M1 in our living room at Lake - Homer asks in the family chat whether driving under the influence of Duff beer is covered by the car insurance. The archivist answers no, citing the policy documents. + Homer asks in the family chat whether driving under the influence of Duff beer is covered by the car insurance. The archivist answers no, citing the policy documents. diff --git a/docs/assets/memory-wiki-answer.png b/docs/assets/memory-wiki-answer.png new file mode 100644 index 0000000000000000000000000000000000000000..4a59e71a7a9f2d275b7836583b032569c7f6481d GIT binary patch literal 81428 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
    sy0sdg24cAs3Rfm`rNs!C14DzrQ&mNn%x}il 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 literal 0 HcmV?d00001 diff --git a/docs/assets/memory-wiki-answer.webp b/docs/assets/memory-wiki-answer.webp deleted file mode 100644 index 51c6e808146b070b7c935dfb73edd0f0dafd7975..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36654 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