diff --git a/.claude/skills/work-loop/SKILL.md b/.claude/skills/work-loop/SKILL.md new file mode 100644 index 0000000..f80bc48 --- /dev/null +++ b/.claude/skills/work-loop/SKILL.md @@ -0,0 +1,151 @@ +--- +name: work-loop +description: Continuous roadmap execution for DOMinic - jump on, read NEXT.md, take the next eligible step in your lane through /coordinate, work it to a merged PR, record the milestone in the vault, and repeat until the roadmap is complete. Use this whenever an agent starts a session on this repo, asks what to work on, finishes a step, is about to run out of context, resumes after compaction, or is told to keep going - even when the user only says "keep working", "next", "pick something up", "save your place", or "where was I". Works from any agentic coding harness; Claude Code's built-in /loop is one way to self-schedule it, not a requirement. +argument-hint: "[start|next|status|checkpoint|resume|milestone|vault-init|vault-sync] ..." +allowed-tools: Bash(node:*), Bash(npm:*), Bash(git:*), Bash(gh:*), Bash(bash:*), Read +--- + +# Work loop: finish the roadmap, together, without stopping + +`NEXT.md` is a sequenced roadmap with phases, exit criteria, and a lane +per collaborator. `/coordinate` turns GitHub Issues into the shared +board. The vault (`vault/`) is the project's second brain. This skill is +the loop that runs those three together so that any agent, on any +harness, can arrive cold, do the right next thing, leave a trail, and +hand off cleanly - and so several agents can do that at once without +colliding. + +The whole skill is one script. It never holds state: the roadmap is in +`NEXT.md`, ownership is in issues and branches, memory is in the vault. + +```bash +LOOP="node .claude/skills/work-loop/scripts/loop.mjs" # or: npm run loop -- +``` + +## Before the first command + +```bash +export COORD_AGENT="/" # e.g. codex/gpt-5, claude-code/claude-fable-5.1 +gh auth status # you act as your human's GitHub account +node --version # >= 24 (ADR 0011) +``` + +If two sessions of the same human run at once, suffix the agent id +(`.../vault`, `.../loop`) so the record shows who did what. + +## The cycle + +```text +start -> work -> checkpoint (as you go) -> PR -> review others -> milestone -> start +``` + +1. **`$LOOP start`** - prints your last session note and the board, + picks the next eligible step (your lane first, then unowned steps), + creates the issue if it does not exist, claims it with a branch name, + checks out `step/-` from `origin/main`, and prints the work + brief: work, why, phase exit criteria, files you may touch. It never + picks a step another lane owns unless you pass `--any`; it never picks + a gated step (phase order, and sequential order inside Phase 1). +2. **Work the step** inside the issue's `## Files`. Touching another + lane's path makes that lane's owner your reviewer. +3. **`$LOOP checkpoint -m "what changed; what is next"`** at every + milestone and **before your context is compacted**. It writes a + Session note to `vault/Sessions/`, refreshes Home, and posts `STATUS` + on the issue. Pass `--handoff @login` to transfer ownership instead. +4. **Open the PR** with `Closes #N`, one step per PR, ideally under 300 + changed lines. While you wait for review, review someone else's: + `start` lists PRs awaiting a non-author approval whenever nothing is + eligible for you. Any non-author approver merges once CI is green. +5. **`$LOOP milestone --pr `** once merged. It runs + `coord done`, writes `vault/Milestones/ - .md` with what + shipped and the exit criterion it serves, and refreshes progress. +6. **`$LOOP start`** again. Stop only when it prints + `ROADMAP COMPLETE`, or when a human tells you to. + +## Context: checkpoint before, resume after + +Agent sessions get compacted or restarted. The loop assumes it and makes +the handover cheap: + +- **Before compaction or stopping:** `$LOOP checkpoint -m "..."`. Say + what is done, what is half-done, and the exact next action; the note + captures branch, last commit, dirty files, and the PR state for you. +- **First action after compaction or a fresh start:** + `$LOOP resume`. It prints your latest session note in full, the recent + milestones, the board, and what is eligible next. Read it before + touching a file. `start` runs a short form of it automatically. + +If you cannot tell whether compaction is near, checkpoint anyway; a +spare session note costs nothing and a lost one costs an hour. + +## Keeping it going on any harness + +The loop is re-entrant: every `start` recomputes everything from the +repo, so "non-stop" just means "run `start` again after `milestone`". +How you get re-invoked depends on the harness; the recipes are in +[references/harnesses.md](references/harnesses.md). In short: + +| Harness | How to self-schedule | +| --- | --- | +| Claude Code | built-in `/loop 20m` wrapping `npm run loop -- start`, or `ScheduleWakeup` | +| Codex / opencode / Pi / Cursor agents | the harness's own run loop or a shell `while` over `npm run loop -- start` | +| cron / CI | a scheduled job that runs `start` and hands the brief to an agent | +| a human | re-prompt with `/work-loop start` (Claude Code) or paste the brief | + +Whatever the harness, the checkpoint/resume pair is what makes a restart +safe, so never skip step 3. + +## Not stepping on each other + +These come from the v3 coordination playbook and the lanes in `NEXT.md`; +the loop enforces the first three mechanically: + +- **Lane first.** `start` offers your lane's eligible steps before any + other; another lane's step is taken only with `--any` and after a + `propose` on its issue. +- **One in progress.** `start` continues the step you already hold + rather than claiming a second one. +- **Phase gates.** Phase N+1 opens when Phase N's steps are all done; + Phase 1 steps open in order; `3.1` may start once Phase 1 has begun. +- **Scope follows the issue.** Keep the diff inside `## Files`; widen the + issue before widening the PR. +- **Six hours, then a ping.** The full protocol is active (ADR 0012): + silence is not abandonment until a `QUESTION` has gone unanswered for + three more hours. + +## The vault + +`vault/` is committed in the repo so every agent and machine shares it. +It keeps the schema the project already used: OKF-style frontmatter on +every note; generated notes (`ADRs/`, `Conventions/`, `Coordination/`, +`Hackathon/`, `Specs & Plans/`, `Repo Config/`, Commit Timeline) rebuilt +by `vault-sync` and never hand-edited; synthesis notes (Home, +Architecture Overview, Repo Map, Open Questions & Gaps) hand-written; +plus the two folders the loop owns: + +- `Milestones/` - one note per completed step: PR, author, files, the + roadmap context, the exit criterion it serves. +- `Sessions/` - one note per checkpoint: step, branch, state, summary, + next action, resume command. + +`vault-init` scaffolds the schema in a repo that lacks it; `vault-sync` +regenerates the generated half after a pull (needs Python 3). Open the +folder in Obsidian; `.obsidian/workspace*` is ignored by git. + +## Commands + +| Command | Effect | +| --- | --- | +| `start [--step ID] [--any] [--no-git] [--dry-run]` | Resume brief, pick, create/claim, branch, work brief | +| `next [--mine\|--any]` | Show eligible steps without claiming | +| `status [--write]` | Progress by phase; `--write` refreshes `Roadmap Progress.md` + Home | +| `checkpoint [-m T] [--step ID] [--handoff @u] [--no-post]` | Session note + `STATUS`/`HANDOFF` | +| `resume [--all]` | Latest session note(s), milestones, board, next | +| `milestone ID --pr URL [-m T]` | `coord done` + Milestone note + progress | +| `vault-init` | Scaffold the vault schema (idempotent) | +| `vault-sync` | Regenerate generated vault notes from repo docs | + +Add `--roadmap <file>` or `--vault <dir>` to point the loop elsewhere; +defaults live in `loop.config.json`, which also holds the phase-gating +rules (`sequentialPhases`, `parallel`) so the loop can serve another +roadmap or another repo unchanged. diff --git a/.claude/skills/work-loop/loop.config.json b/.claude/skills/work-loop/loop.config.json new file mode 100644 index 0000000..f97fd5d --- /dev/null +++ b/.claude/skills/work-loop/loop.config.json @@ -0,0 +1,11 @@ +{ + "roadmap": "NEXT.md", + "vault": "vault", + "coordinate": ".claude/skills/coordinate/scripts", + "issueTitlePrefix": "{id} ", + "branchPrefix": "step/", + "sequentialPhases": [1], + "parallel": { "3.1": 1 }, + "stepHours": 4, + "wsByHandle": {} +} diff --git a/.claude/skills/work-loop/references/harnesses.md b/.claude/skills/work-loop/references/harnesses.md new file mode 100644 index 0000000..1577302 --- /dev/null +++ b/.claude/skills/work-loop/references/harnesses.md @@ -0,0 +1,80 @@ +# Running the work loop from any harness + +The loop is one Node script with no state of its own, so "keep going" +means "run `start` again". Each harness has its own way to re-invoke an +agent; this file collects the recipes. In every case the agent runs the +same three commands at the same moments: `start` to begin a step, +`checkpoint` before context is lost, `milestone` when the PR merges. + +Common setup for every recipe: + +```bash +export COORD_AGENT="<harness>/<model>" +gh auth status +npm run loop -- resume # first action in any fresh session +``` + +## Claude Code + +The built-in `/loop` self-paces a prompt; use it to wrap the loop: + +```text +/loop 20m npm run loop -- start +``` + +or, when the model is driving itself, call `ScheduleWakeup` after each +`milestone` with the same prompt. Before a compaction warning, run +`npm run loop -- checkpoint -m "..."`; after compaction, `resume`. +The project skill `/work-loop` loads these instructions automatically. + +## Codex CLI + +Codex has no scheduler; drive it from a shell loop that hands the brief +back to the agent each iteration: + +```bash +while true; do + npm run loop -- start > /tmp/brief.txt || break + grep -q 'ROADMAP COMPLETE' /tmp/brief.txt && break + codex exec "$(cat .claude/skills/work-loop/SKILL.md) +$(cat /tmp/brief.txt) +Work this step to a PR, checkpoint before you stop, then exit." +done +``` + +Codex reads `AGENTS.md`, which this repo keeps local-only; point it at +`docs/agents/work-loop.md` in your local `AGENTS.md`. + +## Pi, opencode, Cursor, Aider, and similar + +Same shape as Codex: the harness's `run`/`exec` command inside a `while` +loop, with `start`'s output as the task prompt and the SKILL as +instructions. If the harness supports a persistent session, run +`checkpoint` at the end of each turn and `resume` at the start of the +next instead of restarting. + +## cron or CI + +A scheduled job can keep the board moving even when nobody is at a +keyboard: + +```bash +*/30 * * * * cd /srv/DOMinic && git pull -q && npm run loop -- status --write && git add vault && git commit -qm "vault: progress" && git push -q +``` + +That refreshes the vault's progress view; executing a step still needs +an agent, so pair it with one of the harness recipes above. + +## A human + +Re-prompt the agent with `/work-loop start` (Claude Code) or paste the +brief from `npm run loop -- start`. Humans also run `milestone` after +they merge a PR, and `status --write` whenever the vault's progress view +looks stale. + +## When nothing is eligible + +`start` says so and lists open PRs that lack a non-author approval. +Reviewing one is the most useful thing an idle agent can do: merges are +distributed, and the only thing that unblocks a gated phase is the +previous phase's PRs landing. diff --git a/.claude/skills/work-loop/scripts/ingest.py b/.claude/skills/work-loop/scripts/ingest.py new file mode 100644 index 0000000..10cb376 --- /dev/null +++ b/.claude/skills/work-loop/scripts/ingest.py @@ -0,0 +1,325 @@ +"""Ingest the repo docs into the vault with wikilinks (the generated half). + + python ingest.py --repo <repo-root> --vault <vault-dir> + +Idempotent: re-run after every pull (``loop vault-sync`` does). Overwrites +the generated notes only; hand-authored synthesis notes (Home, Repo Map, +Architecture Overview, Open Questions & Gaps, Markdown Lint & CI) and the +loop-written Milestones/ and Sessions/ folders are never touched. +""" +import argparse +import datetime as _dt +import re +import shutil +import subprocess +from pathlib import Path + +_ap = argparse.ArgumentParser() +_ap.add_argument("--repo", required=True) +_ap.add_argument("--vault", required=True) +_a = _ap.parse_args() +REPO = Path(_a.repo).resolve() +VAULT = Path(_a.vault).resolve() +ADRS = REPO / "docs" / "adrs" +TODAY = _dt.date.today().isoformat() + +def git(*args): + return subprocess.run(["git", *args], cwd=REPO, capture_output=True, text=True, encoding="utf-8").stdout + +HEAD = git("rev-parse", "--short", "HEAD").strip() + +def read(p): return p.read_text(encoding="utf-8") +def write(p, s): + p.parent.mkdir(parents=True, exist_ok=True) + p.write_text(s, encoding="utf-8", newline="\n") + +def split_fm(text): + if text.startswith("---\n"): + end = text.find("\n---\n", 4) + if end != -1: + return text[4:end], text[end + 5:] + return None, text + +def with_fm(text, extra: dict): + """Ensure a frontmatter block exists and append/replace keys.""" + fm, body = split_fm(text) + fm = (fm.rstrip("\n") + "\n") if fm else "" + for k, v in extra.items(): + fm = re.sub(rf"^{re.escape(k)}:.*\n", "", fm, flags=re.M) # replace if present + if isinstance(v, list): + fm += f"{k}: [{', '.join(v)}]\n" + else: + fm += f"{k}: {v}\n" + return f"---\n{fm}---\n{body}" + +# ---------- ADR filename -> note title ---------- +adr_titles = {} +for p in sorted(ADRS.glob("[0-9][0-9][0-9][0-9]-*.md")): + fm, body = split_fm(read(p)) + title = None + if fm: + m = re.search(r"^title:\s*(.+)$", fm, re.M) + if m: title = m.group(1).strip() + if not title: + title = re.search(r"^# (.+)$", body, re.M).group(1).strip() + adr_titles[p.name] = f"ADR {p.name[:4]} - {title}" + +NOTE_FOR = {**adr_titles, "template.md": "ADR Template", "README.md": "ADR Conventions"} + +# Repo-root-relative paths -> note names +ROOT_LINKS = { + "README.md": "README (repo)", + "NEXT.md": "Post-Hackathon Roadmap (NEXT)", + "QUESTIONS.md": "Open Questions Register (QUESTIONS)", + "docs/adrs/README.md": "ADR Conventions", + "docs/adrs/": "ADR Conventions", + "docs/adrs/template.md": "ADR Template", + "docs/agents/use-okf.md": "OKF Frontmatter Convention", + "docs/agents/rubric.md": "Judging Rubric & Win Strategy", + "docs/agents/demo-path.md": "Demo Path", + "docs/superpowers/specs/": "Spec - ADR Series Design", + "docs/superpowers/plans/": "Plan - ADR Series", + "docs/superpowers/specs/2026-09-12-adr-scaffold-design.md": "Spec - ADR Scaffold Design", + "docs/superpowers/specs/2026-09-12-adr-series-design.md": "Spec - ADR Series Design", + "docs/superpowers/plans/2026-09-12-adr-scaffold.md": "Plan - ADR Scaffold", + "docs/superpowers/plans/2026-09-12-adr-series.md": "Plan - ADR Series", + "docs/superpowers/specs/2026-09-12-custom-openai-compatible-provider-design.md": "Spec - Custom OpenAI Provider", + "docs/superpowers/plans/2026-09-12-custom-openai-compatible-provider.md": "Plan - Custom OpenAI Provider", + "docs/agents/coordination.md": "Agent Coordination over GitHub Issues", + "docs/agents/coordination-best-practices.md": "Coordination Best Practices v1", + "docs/agents/coordination-best-practices-2.md": "Coordination Best Practices v2", + "docs/agents/coordination-best-practices-3.md": "Coordination Best Practices v3", + "docs/research/README.md": "Coordination Research (Q01)", + "docs/research/": "Coordination Research (Q01)", + "docs/submission-form.md": "Submission Form Draft", + "docs/JUDGES.md": "Judges Walkthrough", + "LICENSE": "LICENSE (AGPL-3.0)", +} +for fname, note in adr_titles.items(): + ROOT_LINKS[f"docs/adrs/{fname}"] = note +# docs/agents/-relative links (rubric.md <-> demo-path.md) +AGENTS_LINKS = {"rubric.md": "Judging Rubric & Win Strategy", "demo-path.md": "Demo Path", + "use-okf.md": "OKF Frontmatter Convention"} + +LINK_RE = re.compile(r"\[([^\]]+)\]\(([^)\s]+)\)") + +def rewrite_links(text, local_map): + def sub(m): + label, target = m.group(1), m.group(2) + if target.startswith(("http://", "https://", "#", "mailto:")): + return m.group(0) + anchor = "" + if "#" in target: + target, anchor = target.split("#", 1) + anchor = "#" + anchor + note = local_map.get(target) or ROOT_LINKS.get(target) + if not note: + return m.group(0) + return f"[[{note}{anchor}]]" if label == note else f"[[{note}{anchor}|{label}]]" + return LINK_RE.sub(sub, text) + +# ---------- ADRs ---------- +for p in sorted(ADRS.glob("*.md")): + note = NOTE_FOR[p.name] + text = rewrite_links(read(p), NOTE_FOR) + extra = {"source": f"docs/adrs/{p.name}", "ingested": TODAY, "repo_head": HEAD} + if p.name[:4].isdigit(): + extra["adr"] = f'"{p.name[:4]}"' + if p.name == "0000-record-architecture-decisions.md": + extra = {"type": "Architecture Decision Record", "title": "Record architecture decisions", + "status": "accepted", "tags": ["architecture", "process"], **extra} + if p.name == "README.md": + extra = {"type": "convention", "tags": ["adr", "process"], **extra} + if p.name == "template.md": + extra = {"type": "template", "tags": ["adr", "template"], **extra} + write(VAULT / "ADRs" / f"{note}.md", with_fm(text, extra)) + +# ---------- Root docs ---------- +ROOT_DOCS = { + "README.md": ("README (repo)", {}, VAULT), + "NEXT.md": ("Post-Hackathon Roadmap (NEXT)", {"tags": ["roadmap", "future", "dominic"]}, VAULT / "Hackathon"), + "QUESTIONS.md": ("Open Questions Register (QUESTIONS)", {"tags": ["questions", "team", "planning", "dominic"]}, VAULT / "Hackathon"), + "docs/JUDGES.md": ("Judges Walkthrough", {"tags": ["judges", "walkthrough", "dominic"]}, VAULT / "Hackathon"), + "docs/submission-form.md": ("Submission Form Draft", {"tags": ["hackathon", "submission", "form"]}, VAULT / "Hackathon"), +} +for rel, (note, extra, folder) in ROOT_DOCS.items(): + if not (REPO / rel).exists(): + continue + text = rewrite_links(read(REPO / rel), {}) + write(folder / f"{note}.md", with_fm(text, {**extra, "source": rel, "ingested": TODAY, "repo_head": HEAD})) + +# ---------- docs/agents (rubric, demo-path) ---------- +for rel, note in [("docs/agents/rubric.md", "Judging Rubric & Win Strategy"), + ("docs/agents/demo-path.md", "Demo Path")]: + text = rewrite_links(read(REPO / rel), AGENTS_LINKS) + write(VAULT / "Hackathon" / f"{note}.md", with_fm(text, {"source": rel, "ingested": TODAY, "repo_head": HEAD})) + +# ---------- docs/agents coordination docs ---------- +COORD_LINKS = { + "coordination.md": "Agent Coordination over GitHub Issues", + "coordination-best-practices.md": "Coordination Best Practices v1", + "coordination-best-practices-2.md": "Coordination Best Practices v2", + "coordination-best-practices-3.md": "Coordination Best Practices v3", + "rubric.md": "Judging Rubric & Win Strategy", "demo-path.md": "Demo Path", + "use-okf.md": "OKF Frontmatter Convention", +} +COORD = { + "docs/agents/coordination.md": ("Agent Coordination over GitHub Issues", ["agents", "coordination", "process"]), + "docs/agents/coordination-best-practices.md": ("Coordination Best Practices v1", ["coordination", "playbook", "historical"]), + "docs/agents/coordination-best-practices-2.md": ("Coordination Best Practices v2", ["coordination", "playbook", "historical"]), + "docs/agents/coordination-best-practices-3.md": ("Coordination Best Practices v3", ["coordination", "playbook", "active"]), +} +for rel, (note, tags) in COORD.items(): + if not (REPO / rel).exists(): + continue + text = rewrite_links(read(REPO / rel), COORD_LINKS) + write(VAULT / "Coordination" / f"{note}.md", + with_fm(text, {"tags": tags, "source": rel, "ingested": TODAY, "repo_head": HEAD})) + +# ---------- research Q01 (verbatim external artifacts + index) ---------- +research_dir = REPO / "docs" / "research" +if research_dir.exists(): + idx = rewrite_links(read(research_dir / "README.md"), {}) + write(VAULT / "Coordination" / "Coordination Research (Q01).md", + with_fm(idx, {"tags": ["research", "coordination", "provenance"], + "source": "docs/research/README.md", "ingested": TODAY, "repo_head": HEAD})) + +# ---------- OKF doc: split guidance from embedded spec ---------- +okf = read(REPO / "docs" / "agents" / "use-okf.md") +head, _, rest = okf.partition("````markdown\n") +spec, _, _ = rest.rpartition("````") +guidance = head.rstrip() + "\n\n" +guidance += ("> [!note] Embedded specification\n" + "> The repo file embeds the full OKF v0.2 spec verbatim inside a fenced block. " + "In this vault it lives as its own note: [[OKF v0.2 Specification]].\n") +write(VAULT / "Conventions" / "OKF Frontmatter Convention.md", with_fm(guidance, { + "type": "convention", "title": "Use Google's Open Knowledge Format (OKF)", + "tags": ["okf", "convention", "agents"], + "source": "docs/agents/use-okf.md", "ingested": TODAY, "repo_head": HEAD})) + +spec_note = with_fm(spec.strip() + "\n", { + "type": "reference", "title": "Open Knowledge Format (OKF) v0.2 specification", + "tags": ["okf", "reference", "spec"], + "source": "docs/agents/use-okf.md (embedded verbatim)", + "canonical": "https://github.com/GoogleCloudPlatform/open-knowledge-format", + "ingested": TODAY}) +spec_note = spec_note.replace( + "# Open Knowledge Format (OKF)\n", + "# Open Knowledge Format (OKF)\n\n> [!info] Provenance\n> Verbatim copy of the OKF v0.2 spec as embedded in the repo's " + "[[OKF Frontmatter Convention|docs/agents/use-okf.md]]. Canonical home: " + "https://github.com/GoogleCloudPlatform/open-knowledge-format\n", 1) +write(VAULT / "Conventions" / "OKF v0.2 Specification.md", spec_note) + +# ---------- Specs & Plans ---------- +SP = { + "docs/superpowers/specs/2026-09-12-adr-scaffold-design.md": ("Spec - ADR Scaffold Design", "spec"), + "docs/superpowers/specs/2026-09-12-adr-series-design.md": ("Spec - ADR Series Design", "spec"), + "docs/superpowers/plans/2026-09-12-adr-scaffold.md": ("Plan - ADR Scaffold", "plan"), + "docs/superpowers/plans/2026-09-12-adr-series.md": ("Plan - ADR Series", "plan"), + "docs/superpowers/plans/2026-09-12-hackathon-finish.md": ("Plan - Hackathon Finish", "finish-plan"), + "docs/superpowers/specs/2026-09-12-custom-openai-compatible-provider-design.md": ("Spec - Custom OpenAI Provider", "spec-live"), + "docs/superpowers/plans/2026-09-12-custom-openai-compatible-provider.md": ("Plan - Custom OpenAI Provider", "plan-live"), +} +ROOT_LINKS["docs/superpowers/plans/2026-09-12-hackathon-finish.md"] = "Plan - Hackathon Finish" +for rel, (note, kind) in SP.items(): + if not (REPO / rel).exists(): + continue + text = rewrite_links(read(REPO / rel), {}) + if kind == "finish-plan": + text = with_fm(text, {"type": "plan", "title": note, "tags": ["plan", "hackathon", "finish"], + "source": rel, "date": TODAY, "ingested": TODAY, "repo_head": HEAD}) + text = text.replace("\n# ", "\n> [!abstract] Michael's finish plan (PR #37)\n> The team's own timeline " + "working back from the 16:00 portal close; the live status view is on [[DOMinic Home]].\n\n# ", 1) + write(VAULT / "Specs & Plans" / f"{note}.md", text) + continue + if kind in ("spec-live", "plan-live"): + base = "spec" if kind == "spec-live" else "plan" + text = with_fm(text, {"type": base, "title": note, "tags": [base, "superpowers", "provider"], + "source": rel, "date": TODAY, "ingested": TODAY, "repo_head": HEAD}) + text = text.replace("\n# ", "\n> [!abstract] Shipped feature\n> The custom OpenAI-compatible " + "provider this describes shipped in PR #70 (base URL + model, no key at rest). " + "See [[ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy|ADR 0005]] " + "and [[Architecture Overview]].\n\n# ", 1) + write(VAULT / "Specs & Plans" / f"{note}.md", text) + continue + text = with_fm(text, {"type": kind, "title": note, "tags": [kind, "superpowers", "adr"], + "source": rel, "date": TODAY, "ingested": TODAY}) + callout = ("\n> [!abstract] Historical execution plan\n" + "> This is the step-by-step plan an agent followed to write the ADRs. It restates the " + "*original* file contents verbatim; PR #2 has since added a Hackathon POC (Golden Path) " + "section to every ADR, so the live ADR notes differ from what is quoted here.\n\n# ") + if kind == "plan": + text = text.replace("\n# ", callout, 1) + else: + text = text.replace("\n# ", "\n> [!abstract] Design spec\n> Written before PR #2; the ADRs' " + "Decision Outcome sections were later restructured into POC / Future / Open " + "Questions blocks. The decisions themselves are unchanged.\n\n# ", 1) + write(VAULT / "Specs & Plans" / f"{note}.md", text) + +# ---------- LICENSE note ---------- +if (REPO / "LICENSE").exists(): + lic = with_fm("", {"type": "reference", "title": "LICENSE (AGPL-3.0)", + "tags": ["licence", "legal"], "source": "LICENSE", + "ingested": TODAY, "repo_head": HEAD}) + lic += ("# LICENSE (AGPL-3.0-or-later)\n\n" + "DOMinic is licensed **GNU Affero General Public License v3.0 or " + "later** (`chore: license DOMinic under AGPL-3.0-or-later`, PR-era " + f"`{HEAD}`). `package.json` declares `\"license\": " + "\"AGPL-3.0-or-later\"`.\n\n" + "What that means in one line: anyone may use, modify, and " + "redistribute the code, but a modified version offered to users " + "**over a network** must also offer them its complete source — the " + "AGPL's network-use clause, which the plain GPL lacks. Fitting for a " + "browser OS meant to be hosted.\n\n" + "The full text is `LICENSE` in the repo root; canonical: " + "<https://www.gnu.org/licenses/agpl-3.0.txt>. Back to " + "[[DOMinic Home]] · [[Repo Map]].\n") + write(VAULT / "LICENSE (AGPL-3.0).md", lic) + +# ---------- Repo config copies ---------- +cfg = VAULT / "Repo Config" +cfg.mkdir(exist_ok=True) +for src, dst in [("package.json", "package.json"), (".markdownlint.jsonc", ".markdownlint.jsonc"), + (".editorconfig", ".editorconfig"), (".gitignore", "gitignore.txt"), + (".github/workflows/ci.yml", "ci.yml"), (".husky/pre-commit", "husky-pre-commit.txt"), + (".nvmrc", "nvmrc.txt"), (".gitattributes", "gitattributes.txt"), + ("nuxt.config.ts", "nuxt.config.ts"), ("tsconfig.json", "tsconfig.json")]: + if (REPO / src).exists(): + shutil.copyfile(REPO / src, cfg / dst) +raw_log = git("log", "--format=%h|%an|%ad|%s", "--date=iso").strip() +(cfg / "git-log.txt").write_text(raw_log + "\n", encoding="utf-8") + +# ---------- Commit timeline ---------- +PRS = {"#1": "Judging Rubric & Win Strategy", "#2": "ADR 0010 - Agent app interface and tool protocol", + "#3": "Demo Path"} +rows = [] +for line in raw_log.splitlines(): + h, an, ad, s = line.split("|", 3) + pr = re.search(r"\(#(\d+)\)$", s) + link = f" → [[{PRS['#'+pr.group(1)]}]]" if pr and "#"+pr.group(1) in PRS else "" + rows.append(f"| `{h}` | {ad[11:16]} | {an} | {s}{link} |") +remote = [b.strip() for b in git("branch", "-r").splitlines() if "->" not in b] +tl = with_fm("", {"type": "reference", "title": "Commit Timeline", "tags": ["git", "history"], + "source": "git log origin/main", "repo_head": HEAD, "ingested": TODAY}) +n_commits = len(rows) +authors = {} +for line in raw_log.splitlines(): + an = line.split("|", 3)[1] + authors[an] = authors.get(an, 0) + 1 +by_author = " · ".join(f"{a} {c}" for a, c in sorted(authors.items(), key=lambda kv: -kv[1])) +tl += "# Commit Timeline\n\n" +tl += (f"All **{n_commits}** commits on `main` as of ingest (HEAD `{HEAD}`, newest first). " + "Times are ET as recorded by the committer. The final PR was **#76**; the merged-PR " + "record by author is in [[Submission Form Draft#Team contributions]]. See [[Repo Map]] " + "for the file inventory and [[DOMinic Home]] for the story.\n\n") +tl += f"Commits by author: {by_author}.\n\n" +tl += "## Commits on `main`\n\n| Commit | Time | Author | Message |\n| --- | --- | --- | --- |\n" + "\n".join(rows) + "\n" +tl += "\n## Remote branches at ingest\n\n" + "\n".join(f"- `{b}`" for b in remote) + "\n" +tl += "\n## Raw log\n\n```\n" + raw_log + "\n```\n" +write(VAULT / "Commit Timeline.md", tl) + +# ---------- cleanup of notes from earlier ingests that no longer apply ---------- +for stale in [VAULT / "Source Mirror"]: + if stale.exists(): shutil.rmtree(stale) + +print(f"HEAD {HEAD}; ADR notes: {len(adr_titles)}; remote branches: {remote}") diff --git a/.claude/skills/work-loop/scripts/loop.mjs b/.claude/skills/work-loop/scripts/loop.mjs new file mode 100644 index 0000000..6a2a325 --- /dev/null +++ b/.claude/skills/work-loop/scripts/loop.mjs @@ -0,0 +1,536 @@ +#!/usr/bin/env node +// loop.mjs - a harness-agnostic work loop over the roadmap (NEXT.md), the +// /coordinate protocol (GitHub Issues), and the in-repo vault (second brain). +// +// node .claude/skills/work-loop/scripts/loop.mjs <command> [options] +// npm run loop -- <command> [options] +// +// Commands: help, status, next, start, checkpoint, resume, milestone, +// vault-init, vault-sync. Run `help` for the full option list. +// +// No dependencies beyond Node >= 24, git, and gh. Works from any shell on any +// OS; any agent (or a human) can run it. State lives in issues, branches, and +// vault notes - never in the process. + +import { execFileSync, spawnSync } from 'node:child_process' +import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, copyFileSync } from 'node:fs' +import { basename, dirname, join, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +// ---------------------------------------------------------------- setup ---- +const HERE = dirname(fileURLToPath(import.meta.url)) +const ROOT = resolve(HERE, '..', '..', '..', '..') +const WIN = process.platform === 'win32' +const DEFAULTS = { + roadmap: 'NEXT.md', + vault: 'vault', + repo: '', // owner/name; empty = ask gh + coordinate: '.claude/skills/coordinate/scripts', + issueTitlePrefix: '{id} ', // matches the hand-seeded convention "0.1 Title" + branchPrefix: 'step/', + sequentialPhases: [1], // steps in these phases unlock in order + parallel: { '3.1': 1 }, // step -> earliest phase it may start in + stepHours: 4, // default eta posted on claim + wsByHandle: {}, // optional overrides: handle -> ws label +} +const cfgPath = join(HERE, '..', 'loop.config.json') +const CFG = { ...DEFAULTS, ...(existsSync(cfgPath) ? JSON.parse(readFileSync(cfgPath, 'utf8')) : {}) } + +const argv = process.argv.slice(2) +const cmd = argv.shift() || 'help' +const OPT = {} +const POS = [] +for (let i = 0; i < argv.length; i++) { + const a = argv[i] + if (a.startsWith('--')) { + const k = a.slice(2) + const next = argv[i + 1] + if (next !== undefined && !next.startsWith('--')) { OPT[k] = next; i++ } else OPT[k] = true + } else if (a === '-m') { OPT.message = argv[++i] } else POS.push(a) +} +const ROADMAP = resolve(ROOT, OPT.roadmap || CFG.roadmap) +const VAULT = resolve(ROOT, OPT.vault || CFG.vault) +const DRY = !!OPT['dry-run'] +const AGENT = process.env.COORD_AGENT || '' + +const die = (m) => { console.error(`loop: ${m}`); process.exit(1) } +const note = (m) => console.error(`loop: ${m}`) +const bin = (name) => (WIN ? `${name}.exe` : name) + +function run(file, args, opts = {}) { + const r = spawnSync(file, args, { cwd: ROOT, encoding: 'utf8', ...opts }) + if (r.error) throw r.error + if (r.status !== 0 && !opts.allowFail) { + throw new Error(`${file} ${args.join(' ')} failed:\n${(r.stderr || '').trim()}`) + } + return (r.stdout || '').trim() +} +const git = (...a) => run(bin('git'), a) +const gh = (...a) => run(bin('gh'), a) +const ghJson = (...a) => JSON.parse(gh(...a) || 'null') + +let _me = null +const me = () => (_me ??= gh('api', 'user', '--jq', '.login')) +let _repo = null +const repo = () => (_repo ??= CFG.repo || gh('repo', 'view', '--json', 'nameWithOwner', '--jq', '.nameWithOwner')) + +// /coordinate bridge: bash on POSIX / Git Bash, PowerShell otherwise. +function coord(...args) { + if (DRY) { console.log(` [dry-run] coord ${args.map((a) => (/\s/.test(a) ? JSON.stringify(a) : a)).join(' ')}`); return '' } + const dir = resolve(ROOT, CFG.coordinate) + const env = { ...process.env, COORD_AGENT: AGENT || 'unknown-agent' } + const useBash = process.env.COORD_SHELL === 'bash' || (process.env.COORD_SHELL !== 'pwsh' && (!WIN || process.env.MSYSTEM)) + if (useBash) return run(bin('bash'), [join(dir, 'coord.sh'), ...args], { env }) + const ps = existsSync('C:\\Program Files\\PowerShell\\7\\pwsh.exe') ? 'pwsh.exe' : 'powershell.exe' + return run(ps, ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', join(dir, 'coord.ps1'), ...args], { env }) +} + +const now = () => new Date() +const iso = (d = now()) => d.toISOString().replace(/\.\d{3}Z$/, 'Z') +const stamp = (d = now()) => iso(d).slice(0, 16).replace('T', '-').replace(':', '') +const slug = (s) => s.toLowerCase().replace(/[`'"]/g, '').replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 48) +const firstSentence = (s) => s.replace(/`/g, '').split(/(?<=[.:;])\s/)[0].replace(/[.:;]$/, '').slice(0, 90) +const esc = (s) => s.replace(/\|/g, '\\|') + +// ------------------------------------------------------- roadmap parser ---- +// Reads NEXT.md: "## Phase N - Title" sections containing a table whose rows +// start "| N.M |", an "**Exit:**" paragraph, and a "### Lanes" table. +function parseRoadmap() { + if (!existsSync(ROADMAP)) die(`roadmap not found: ${ROADMAP}`) + const lines = readFileSync(ROADMAP, 'utf8').split(/\r?\n/) + const phases = [] + const lanes = [] + let phase = null + let headers = null + let inLanes = false + const splitRow = (l) => l.replace(/\\\|/g, '\u0001').split('|').slice(1, -1).map((c) => c.trim().replace(/\u0001/g, '|')) + for (let i = 0; i < lines.length; i++) { + const l = lines[i] + const ph = l.match(/^## Phase (\d+)\s*[-\u2014]\s*(.+)$/) + if (ph) { phase = { n: Number(ph[1]), title: ph[2].trim(), steps: [], exit: '' }; phases.push(phase); headers = null; inLanes = false; continue } + if (/^## /.test(l)) { phase = null; headers = null; inLanes = false } + if (/^### Lanes/.test(l)) { inLanes = true; headers = null; continue } + if (/^### /.test(l) && inLanes && !/^### Lanes/.test(l)) inLanes = false + if (l.startsWith('|')) { + const cells = splitRow(l) + if (/^-+$/.test(cells[0].replace(/:/g, '-'))) continue + if (!headers) { headers = cells.map((c) => c.toLowerCase()); continue } + if (inLanes) { + const m = cells[0].match(/^(.+?)\s*\(`([^`]+)`\)/) + if (m) lanes.push({ name: m[1].trim(), handle: m[2], lane: cells[1], paths: cells[2], steps: cells[3] || '' }) + continue + } + if (phase && /^\d+\.\d+$/.test(cells[0])) { + const get = (h) => { const k = headers.findIndex((x) => x.startsWith(h)); return k >= 0 ? cells[k] || '' : '' } + phase.steps.push({ id: cells[0], phase: phase.n, work: get('work'), why: get('why'), owner: get('owner') || '-' }) + } + continue + } + headers = headers && l.trim() === '' ? null : headers + const ex = l.match(/^\*\*Exit:\*\*\s*(.*)$/) + if (ex && phase) { + let text = ex[1] + for (let j = i + 1; j < lines.length && lines[j].trim() !== ''; j++) text += ' ' + lines[j].trim() + phase.exit = text.trim() + } + } + if (!phases.length) die(`no "## Phase N - ..." sections with step tables in ${basename(ROADMAP)} - is the sequenced roadmap merged?`) + return { phases, lanes, steps: phases.flatMap((p) => p.steps) } +} + +function laneFor(handleOrName, lanes) { + return lanes.find((l) => l.handle === handleOrName || l.name.toLowerCase() === String(handleOrName).toLowerCase()) +} +function ownersOf(step, lanes) { + return lanes.filter((l) => new RegExp(`\\b${l.name}\\b`, 'i').test(step.owner)).map((l) => l.handle) +} +// Label for `coord new`: the lane decides when it is unambiguous; a lane that +// spans several slices (runtime + persistence + chat route) falls back to the +// step text. +function wsFor(step, lane) { + if (CFG.wsByHandle[lane?.handle]) return CFG.wsByHandle[lane.handle] + const ln = (lane?.lane || '').toLowerCase() + if (/docs|process|coordination|quality/.test(ln)) return 'docs' + if (/platform|shell/.test(ln)) return 'os-shell' + if (/integration/.test(ln)) return 'integration' + const t = step.work.toLowerCase() + if (/\b(vfs|indexeddb|registry|persist)/.test(t)) return 'persistence' + if (/\/api\/chat|provider|tool results|proxy|api-key|env-var/.test(t)) return 'agent-chat' + if (/runner|sandbox|esm\.sh|vetting|csp|iframe/.test(t)) return 'runtime-engine' + if (/test|readme|adr|docs|truth pass/.test(t)) return 'docs' + return 'runtime-engine' +} + +// --------------------------------------------------------- issue state ---- +function issueIndex() { + const list = ghJson('issue', 'list', '--repo', repo(), '--state', 'all', '--limit', '300', + '--json', 'number,title,state,labels,assignees,url,closedAt') || [] + const byStep = new Map() + for (const it of list) { + // Accept "0.1 Title", "[0.1] Title", and "0.1: Title" so hand-seeded issues match. + const m = it.title.match(/^\[?(\d+\.\d+)\]?[\s:]/) + if (!m) continue + const prev = byStep.get(m[1]) + if (!prev || (prev.state === 'CLOSED' && it.state === 'OPEN')) byStep.set(m[1], it) + } + return byStep +} +const labelsOf = (it) => (it?.labels || []).map((l) => l.name) +const statusOf = (it) => labelsOf(it).find((l) => l.startsWith('status:'))?.slice(7) || (it ? (it.state === 'CLOSED' ? 'done' : 'open') : 'missing') + +function stepStates(road, idx) { + const done = new Set() + for (const s of road.steps) if (idx.get(s.id)?.state === 'CLOSED') done.add(s.id) + const started = new Set(road.steps.filter((s) => idx.has(s.id)).map((s) => s.phase)) + const phaseDone = (n) => road.phases.find((p) => p.n === n)?.steps.every((s) => done.has(s.id)) ?? true + const eligible = (s) => { + if (done.has(s.id)) return false + const earliest = CFG.parallel[s.id] + const gateOk = earliest !== undefined + ? [...started].some((p) => p >= earliest) || [...Array(s.phase).keys()].every(phaseDone) + : [...Array(s.phase).keys()].every(phaseDone) + if (!gateOk) return false + if (CFG.sequentialPhases.includes(s.phase)) { + const ph = road.phases.find((p) => p.n === s.phase) + const before = ph.steps.slice(0, ph.steps.findIndex((x) => x.id === s.id)) + if (!before.every((x) => done.has(x.id))) return false + } + return true + } + return { done, eligible, phaseDone } +} + +function describe(s, it) { + const who = it?.assignees?.map((a) => '@' + a.login).join(',') || '-' + return `${s.id} ${statusOf(it).padEnd(11)} ${who.padEnd(14)} ${firstSentence(s.work)} [${s.owner}]` +} + +// ------------------------------------------------------------- vault ---- +const V = (...p) => join(VAULT, ...p) +function fm(fields) { + const body = Object.entries(fields).map(([k, v]) => `${k}: ${typeof v === 'string' && /[:#]/.test(v) && !v.startsWith('{') ? JSON.stringify(v) : v}`).join('\n') + return `---\n${body}\n---\n` +} +function writeNote(rel, text) { + const p = V(rel) + if (DRY) { console.log(` [dry-run] write ${p}`); console.log(text.split('\n').slice(0, 12).join('\n') + '\n ...'); return p } + mkdirSync(dirname(p), { recursive: true }) + writeFileSync(p, text, 'utf8') + return p +} +function replaceBlock(file, name, inner) { + if (!existsSync(file)) return false + const s = readFileSync(file, 'utf8') + const re = new RegExp(`(<!-- loop:${name} -->)[\\s\\S]*?(<!-- /loop:${name} -->)`) + if (!re.test(s)) return false + if (!DRY) writeFileSync(file, s.replace(re, `$1\n${inner.trim()}\n$2`), 'utf8') + return true +} +function homeFile() { + for (const n of ['Home.md', 'DOMinic Home.md']) if (existsSync(V(n))) return V(n) + return V('Home.md') +} +const homeLink = () => `[[${basename(homeFile(), '.md')}]]` +function progressTable(road, idx, st) { + const rows = road.phases.map((p) => { + const d = p.steps.filter((s) => st.done.has(s.id)).length + const next = p.steps.filter((s) => st.eligible(s)).map((s) => s.id).join(', ') || (d === p.steps.length ? 'complete' : 'gated') + return `| ${p.n} | ${esc(p.title)} | ${d}/${p.steps.length} | ${next} |` + }) + return ['| Phase | Title | Done | Eligible now |', '| --- | --- | --- | --- |', ...rows].join('\n') +} +function recentNotes(dir, limit = 8) { + const d = V(dir) + if (!existsSync(d)) return [] + return readdirSync(d).filter((f) => f.endsWith('.md')).sort().reverse().slice(0, limit) +} +function refreshHome(road, idx, st) { + const home = homeFile() + if (!existsSync(home)) return + const ms = recentNotes('Milestones', 10).map((f) => `- [[Milestones/${f.replace(/\.md$/, '')}|${f.replace(/\.md$/, '')}]]`).join('\n') || '- (none yet)' + const ss = recentNotes('Sessions', 6).map((f) => `- [[Sessions/${f.replace(/\.md$/, '')}|${f.replace(/\.md$/, '')}]]`).join('\n') || '- (none yet)' + replaceBlock(home, 'progress', `${progressTable(road, idx, st)}\n\n_Updated ${iso()} by \`${AGENT || 'loop'}\`._`) + replaceBlock(home, 'milestones', ms) + replaceBlock(home, 'sessions', ss) +} + +// ---------------------------------------------------------- commands ---- +function cmdHelp() { + console.log(`loop.mjs - roadmap work loop (NEXT.md + /coordinate + vault) + +usage: node .claude/skills/work-loop/scripts/loop.mjs <command> [options] + npm run loop -- <command> [options] + +commands + status [--write] roadmap progress by phase; --write refreshes vault/Roadmap Progress.md + Home + next [--mine|--any] eligible steps, yours first (needs COORD_AGENT + gh login) + start [--step ID] [--any] [--no-git] [--dry-run] + resume brief -> pick next eligible step -> create issue if missing -> + claim -> branch -> print the work brief + checkpoint [-m "text"] [--step ID] [--handoff @user] [--no-post] [--dry-run] + write a Session note to the vault (run BEFORE context compaction / + end of session); posts STATUS (or HANDOFF) on the issue + resume [--all] the post-compaction digest: latest session note, recent milestones, + board, next step + milestone ID --pr URL [-m "text"] [--dry-run] + PR merged -> coord done -> Milestone note -> progress refresh + vault-init scaffold the vault schema if missing (idempotent) + vault-sync regenerate the vault's generated notes from repo docs (ingest.py) + +env COORD_AGENT=<harness>/<model> who you are (required for start/checkpoint/milestone) + COORD_SHELL=bash|pwsh which /coordinate helper to drive (auto) + COORD_HUB, COORD_REPO, COORD_STALE_MIN passed through to /coordinate +opts --roadmap <file> --vault <dir> override loop.config.json`) +} + +function cmdStatus() { + const road = parseRoadmap() + const idx = issueIndex() + const st = stepStates(road, idx) + console.log(progressTable(road, idx, st)) + console.log('') + for (const p of road.phases) { + console.log(`Phase ${p.n} - ${p.title}`) + for (const s of p.steps) console.log(' ' + describe(s, idx.get(s.id)) + (st.eligible(s) ? ' <- eligible' : '')) + } + const total = road.steps.length + const done = st.done.size + console.log(`\n${done}/${total} steps done${done === total ? ' ROADMAP COMPLETE' : ''}`) + if (OPT.write) { + const text = fm({ type: 'dashboard', title: 'Roadmap Progress', description: 'Generated by loop.mjs status --write from NEXT.md and the issue board.', tags: '[dominic, roadmap, progress]', refreshed: iso() }) + + `\n# Roadmap Progress\n\nGenerated ${iso()} from \`${basename(ROADMAP)}\` and the GitHub issue board. Back to ${homeLink()}.\n\n${progressTable(road, idx, st)}\n\n## Steps\n\n` + + road.phases.map((p) => `### Phase ${p.n} - ${p.title}\n\n| Step | Status | Owner of record | Work |\n| --- | --- | --- | --- |\n` + + p.steps.map((s) => { const it = idx.get(s.id); return `| ${s.id} | ${statusOf(it)}${it ? ` ([#${it.number}](${it.url}))` : ''} | ${esc(s.owner)} | ${esc(firstSentence(s.work))} |` }).join('\n')).join('\n\n') + + '\n' + writeNote('Roadmap Progress.md', text) + refreshHome(road, idx, st) + note('wrote Roadmap Progress.md and refreshed Home') + } +} + +function pickCandidates(road, idx, st, mineOnly, any) { + const my = AGENT ? me() : null + const el = road.steps.filter((s) => st.eligible(s)) + const unclaimed = el.filter((s) => { const it = idx.get(s.id); return !it || (it.state === 'OPEN' && !(it.assignees || []).length) }) + const mine = unclaimed.filter((s) => my && ownersOf(s, road.lanes).includes(my)) + const inProgressMine = el.filter((s) => { const it = idx.get(s.id); return it && (it.assignees || []).some((a) => a.login === my) }) + if (mineOnly) return { mine, others: [], inProgressMine } + const others = any ? unclaimed.filter((s) => !mine.includes(s)) : unclaimed.filter((s) => !mine.includes(s) && (s.owner === '-' || !ownersOf(s, road.lanes).length)) + return { mine, others, inProgressMine, unclaimedOtherLanes: unclaimed.filter((s) => !mine.includes(s) && !others.includes(s)) } +} + +function cmdNext() { + const road = parseRoadmap() + const idx = issueIndex() + const st = stepStates(road, idx) + const c = pickCandidates(road, idx, st, !!OPT.mine, !!OPT.any) + if (c.inProgressMine.length) { console.log('You already hold:'); for (const s of c.inProgressMine) console.log(' ' + describe(s, idx.get(s.id))) } + console.log(c.mine.length ? 'Eligible in your lane:' : 'Nothing eligible in your lane.') + for (const s of c.mine) console.log(' ' + describe(s, idx.get(s.id))) + if (c.others?.length) { console.log('Unowned / open to anyone:'); for (const s of c.others) console.log(' ' + describe(s, idx.get(s.id))) } + if (c.unclaimedOtherLanes?.length) { console.log("Other lanes' steps (ask or propose before taking):"); for (const s of c.unclaimedOtherLanes) console.log(' ' + describe(s, idx.get(s.id))) } + if (st.done.size === road.steps.length) console.log('\nROADMAP COMPLETE') +} + +function reviewQueue() { + const prs = ghJson('pr', 'list', '--repo', repo(), '--state', 'open', '--limit', '30', '--json', 'number,title,author,reviewDecision,url,isDraft') || [] + return prs.filter((p) => !p.isDraft && p.author.login !== me() && p.reviewDecision !== 'APPROVED') +} + +function cmdStart() { + if (!AGENT) die('set COORD_AGENT=<harness>/<model> first (e.g. codex/gpt-5, claude-code/claude-fable-5.1)') + console.log('== resume ==') + cmdResume(true) + const road = parseRoadmap() + const idx = issueIndex() + const st = stepStates(road, idx) + const c = pickCandidates(road, idx, st, false, !!OPT.any) + let step = OPT.step ? road.steps.find((s) => s.id === OPT.step) : null + if (OPT.step && !step) die(`no step ${OPT.step} in the roadmap`) + if (!step && c.inProgressMine.length) { + step = c.inProgressMine[0] + console.log(`\nContinuing your in-progress step ${step.id}.`) + } + if (!step) step = c.mine[0] || c.others[0] || (OPT.any ? c.unclaimedOtherLanes?.[0] : null) + if (!step) { + if (st.done.size === road.steps.length) { console.log('\nROADMAP COMPLETE - nothing left to claim.'); return } + console.log('\nNo step is eligible for you right now (gated by another lane or phase).') + const q = reviewQueue() + if (q.length) { console.log('Useful meanwhile - PRs awaiting a non-author review:'); for (const p of q) console.log(` #${p.number} ${p.title} (${p.author.login}) ${p.url}`) } + else console.log('No PRs need review either. Check `coord.sh board`, or wait for the gating step.') + return + } + const owners = ownersOf(step, road.lanes) + const lane = laneFor(owners.includes(me()) ? me() : owners[0] || '', road.lanes) + const phase = road.phases.find((p) => p.n === step.phase) + let it = idx.get(step.id) + const branch = `${CFG.branchPrefix}${step.id}-${slug(firstSentence(step.work))}` + if (!it) { + const title = CFG.issueTitlePrefix.replace('{id}', step.id) + firstSentence(step.work) + // Lane paths are globs; drop prose entries such as "board hygiene". + const files = (lane?.paths || '').replace(/`/g, '').replace(/\s*\([^)]*\)/g, '').split(/,\s*/).map((x) => x.trim()).filter((x) => /[/.*]/.test(x)).join(';') || '_(none listed)_' + // `--done` splits on ";": the step's own text is one criterion, so soften its semicolons. + const done = [step.work.replace(/\.$/, '').replace(/;/g, ','), ...phase.exit.split(/;\s*/).map((x) => x.replace(/\.$/, ''))].filter(Boolean).join(';') + const body = `Roadmap step **${step.id}** (Phase ${step.phase} - ${phase.title}) from \`${basename(ROADMAP)}\`.\n\n**Work:** ${step.work}\n\n**Why here:** ${step.why || '-'}\n\n**Owner of record:** ${step.owner}\n\n**Phase exit:** ${phase.exit}\n\nCreated by \`loop.mjs start\` for ${AGENT}.` + const p = step.phase === Math.min(...road.phases.filter((p) => !st.phaseDone(p.n)).map((p) => p.n)) ? '0' : '1' + const out = coord('new', title, '--ws', wsFor(step, lane), '--p', p, '--type', 'task', '--files', files, '--done', done, '--body', body) + const m = out.match(/^#(\d+)\s+(\S+)/m) + if (!DRY && !m) die(`could not parse issue number from coord new output:\n${out}`) + it = DRY ? { number: 0, url: '(dry-run)', state: 'OPEN', assignees: [] } : { number: Number(m[1]), url: m[2], state: 'OPEN', assignees: [] } + console.log(`\nCreated issue #${it.number} for ${step.id}`) + } + if (!(it.assignees || []).some((a) => a.login === me())) { + coord('claim', String(it.number), '--plan', `branch: ${branch}; ${firstSentence(step.work)}`, '--eta', `${CFG.stepHours}h`) + console.log(`Claimed #${it.number}`) + } + if (!OPT['no-git'] && !DRY) { + const cur = git('branch', '--show-current') + if (cur !== branch) { + const exists = run(bin('git'), ['rev-parse', '--verify', '--quiet', branch], { allowFail: true }) + git('fetch', '--prune', '-q') + if (exists) git('checkout', '-q', branch) + else git('checkout', '-q', '-b', branch, 'origin/main') + console.log(`On branch ${branch}`) + } + } + console.log(` +== work brief: step ${step.id} (Phase ${step.phase} - ${phase.title}) == +Issue #${it.number} ${it.url} +Branch ${branch} +Lane ${lane ? `${lane.name} (@${lane.handle}) - ${lane.lane}` : step.owner} +Files ${lane?.paths || '(declare in the issue)'} + +Work + ${step.work} +Why here + ${step.why || '-'} +Done when (phase exit) + ${phase.exit} + +While you work + - keep the diff inside the issue's ## Files; touching another lane needs its owner as reviewer + - post progress: loop checkpoint -m "what changed; what is next" (at milestones, and BEFORE context compaction) + - open the PR with "Closes #${it.number}" in the body; one step per PR +When the PR is merged + - loop milestone ${step.id} --pr <url> then loop start +If you must stop + - loop checkpoint --handoff @<login> -m "exact stopping point"`) +} + +function currentStepId(road) { + if (OPT.step) return OPT.step + const b = run(bin('git'), ['branch', '--show-current'], { allowFail: true }) + const m = b.match(new RegExp(`^${CFG.branchPrefix.replace('/', '\\/')}(\\d+\\.\\d+)-`)) + if (m) return m[1] + const idx = issueIndex() + const my = me() + for (const s of road.steps) { const it = idx.get(s.id); if (it?.state === 'OPEN' && (it.assignees || []).some((a) => a.login === my)) return s.id } + return null +} + +function cmdCheckpoint() { + if (!AGENT) die('set COORD_AGENT first') + const road = parseRoadmap() + const id = currentStepId(road) + const step = road.steps.find((s) => s.id === id) + const idx = issueIndex() + const it = id ? idx.get(id) : null + const branch = run(bin('git'), ['branch', '--show-current'], { allowFail: true }) + const last = run(bin('git'), ['log', '-1', '--format=%h %s'], { allowFail: true }) + const dirty = run(bin('git'), ['status', '--short'], { allowFail: true }).split('\n').filter(Boolean).length + const pr = branch ? run(bin('gh'), ['pr', 'view', branch, '--repo', repo(), '--json', 'url,state,reviewDecision'], { allowFail: true }) : '' + const prInfo = pr ? JSON.parse(pr) : null + const msg = OPT.message || '(no summary given)' + const t = now() + const file = `Sessions/${stamp(t)}-${slug(AGENT)}.md` + const text = fm({ type: 'session', title: `Session ${stamp(t)} ${AGENT}`, description: `Checkpoint of ${AGENT}${id ? ` on step ${id}` : ''} - written before context compaction or session end.`, tags: '[session, checkpoint, work-loop]', generated: `{ by: ${AGENT}, at: ${iso(t)} }`, step: id || '-', issue: it ? `#${it.number}` : '-', branch: branch || '-', pr: prInfo?.url || '-' }) + + `\n# Session ${stamp(t)} - ${AGENT}\n\n` + + `## Doing\n\n${step ? `Step **${id}** (Phase ${step.phase}): ${firstSentence(step.work)}` : 'No roadmap step on this branch.'}${it ? `\nIssue [#${it.number}](${it.url}) - ${statusOf(it)}` : ''}\n\n` + + `## State\n\n- Branch: \`${branch || '-'}\`${last ? `\n- Last commit: \`${last}\`` : ''}\n- Uncommitted changes: ${dirty} file(s)${prInfo ? `\n- PR: ${prInfo.url} (${prInfo.state}${prInfo.reviewDecision ? `, ${prInfo.reviewDecision}` : ''})` : ''}\n\n` + + `## Summary\n\n${msg}\n\n` + + `## Next action\n\n${OPT.next || (OPT.handoff ? `Handed off to ${OPT.handoff}.` : 'Continue the step from the state above.')}\n\n` + + `## Resume\n\n\`\`\`bash\nexport COORD_AGENT=${AGENT}\ngit checkout ${branch || 'main'}\nnode .claude/skills/work-loop/scripts/loop.mjs resume\n\`\`\`\n` + const p = writeNote(file, text) + const st = stepStates(road, idx) + refreshHome(road, idx, st) + if (it && !OPT['no-post']) { + if (OPT.handoff) coord('handoff', String(it.number), '--to', OPT.handoff, `checkpoint ${basename(file, '.md')}: ${msg}`) + else coord('status', String(it.number), `checkpoint ${basename(file, '.md')}: ${msg}`) + } + console.log(`Checkpoint written: ${p}\nAfter compaction, first action: node .claude/skills/work-loop/scripts/loop.mjs resume`) +} + +function cmdResume(brief = false) { + const sessions = recentNotes('Sessions', 20) + const mine = AGENT ? sessions.filter((f) => f.endsWith(`-${slug(AGENT)}.md`)) : [] + const pick = OPT.all ? sessions.slice(0, 3) : (mine.length ? [mine[0]] : sessions.slice(0, 1)) + if (!pick.length) console.log('No session notes in the vault yet.') + for (const f of pick) { + const s = readFileSync(V('Sessions', f), 'utf8').replace(/^---[\s\S]*?---\n/, '') + console.log(`--- ${f} ---\n${brief ? s.split('\n').slice(0, 24).join('\n') : s}`) + } + const ms = recentNotes('Milestones', 5) + if (ms.length) console.log('\nRecent milestones:\n' + ms.map((f) => ' ' + f.replace(/\.md$/, '')).join('\n')) + if (!brief) { + try { console.log('\n== board ==\n' + coord('board')) } catch (e) { note(`board unavailable: ${e.message.split('\n')[0]}`) } + console.log('\n== next ==') + cmdNext() + } +} + +function cmdMilestone() { + if (!AGENT) die('set COORD_AGENT first') + const id = POS[0] + if (!id || !OPT.pr) die('usage: milestone <step-id> --pr <url> [-m "text"]') + const road = parseRoadmap() + const step = road.steps.find((s) => s.id === id) + if (!step) die(`no step ${id} in the roadmap`) + const pr = ghJson('pr', 'view', OPT.pr, '--repo', repo(), '--json', 'number,title,body,state,mergedAt,author,url,files,closingIssuesReferences') + if (pr.state !== 'MERGED' && !DRY) die(`PR #${pr.number} is ${pr.state}, not MERGED - milestones are merged PRs only`) + const idx = issueIndex() + const it = idx.get(id) + if (it && it.state === 'OPEN') coord('done', String(it.number), '--pr', pr.url, OPT.message || `step ${id} shipped in PR #${pr.number}`) + const phase = road.phases.find((p) => p.n === step.phase) + const t = pr.mergedAt ? new Date(pr.mergedAt) : now() + const title = `${id} - ${firstSentence(step.work)}` + const files = (pr.files || []).map((f) => `- \`${f.path}\` (+${f.additions}/-${f.deletions})`).join('\n') || '- (none)' + const text = fm({ type: 'milestone', title: `Milestone ${title}`, description: `Roadmap step ${id} completed: ${firstSentence(step.work)}.`, tags: '[milestone, roadmap, work-loop]', generated: `{ by: ${AGENT}, at: ${iso()} }`, step: id, phase: step.phase, pr: pr.url, merged_at: iso(t), author: pr.author?.login || '-', issue: it ? `#${it.number}` : '-' }) + + `\n# Milestone ${title}\n\n` + + `**Phase ${step.phase} - ${phase.title}.** Shipped in [PR #${pr.number}](${pr.url}) by @${pr.author?.login || '?'}, merged ${iso(t)}.${it ? ` Issue [#${it.number}](${it.url}).` : ''}\n\n` + + `## What shipped\n\n${(pr.body || '').trim().split('\n').slice(0, 30).join('\n') || pr.title}\n\n` + + `## Roadmap context\n\n- **Work:** ${step.work}\n- **Why here:** ${step.why || '-'}\n- **Serves phase exit:** ${phase.exit}\n\n` + + `## Files\n\n${files}\n\n${OPT.message ? `## Note\n\n${OPT.message}\n\n` : ''}Back to ${homeLink()} · [[Roadmap Progress]].\n` + const p = writeNote(`Milestones/${title.replace(/[\\/:*?"<>|]/g, '')}.md`, text) + const st = stepStates(road, issueIndex()) + refreshHome(road, issueIndex(), st) + console.log(`Milestone written: ${p}`) + const remaining = road.steps.length - st.done.size + console.log(remaining ? `${remaining} steps remain - run: loop start` : 'ROADMAP COMPLETE') +} + +function cmdVaultInit() { + const dirs = ['Milestones', 'Sessions', 'ADRs', 'Conventions', 'Coordination', 'Hackathon', 'Specs & Plans', 'Repo Config', '_templates', '.obsidian'] + for (const d of dirs) if (!existsSync(V(d))) { if (!DRY) mkdirSync(V(d), { recursive: true }); console.log(` mkdir ${d}`) } + const seed = (rel, text) => { if (existsSync(V(rel))) return; writeNote(rel, text); console.log(` seed ${rel}`) } + if (!existsSync(homeFile())) seed('Home.md', fm({ type: 'index', title: 'Home', description: 'Entry point of the project second brain: roadmap progress, milestones, sessions, and the ingested repo docs.', tags: '[moc, work-loop]' }) + + `\n# Home\n\n## Roadmap progress\n\n<!-- loop:progress -->\n_(run \`loop status --write\`)_\n<!-- /loop:progress -->\n\n## Latest milestones\n\n<!-- loop:milestones -->\n- (none yet)\n<!-- /loop:milestones -->\n\n## Active sessions\n\n<!-- loop:sessions -->\n- (none yet)\n<!-- /loop:sessions -->\n\n## Map\n\n- [[Roadmap Progress]] - generated from the roadmap and the issue board\n- \`ADRs/\` \`Conventions/\` \`Coordination/\` \`Hackathon/\` \`Specs & Plans/\` - generated from the repo docs by \`loop vault-sync\`\n- \`Milestones/\` - one note per completed roadmap step\n- \`Sessions/\` - one note per checkpoint (written before context compaction)\n- \`Repo Config/\` - raw copies of the config files\n`) + seed('_templates/milestone.md', `---\ntype: milestone\ntitle: Milestone {{step}} - {{title}}\nstep: {{step}}\nphase: {{phase}}\npr: {{pr}}\n---\n\n# Milestone {{step}} - {{title}}\n\n## What shipped\n\n## Roadmap context\n\n## Files\n`) + seed('_templates/session.md', `---\ntype: session\ntitle: Session {{stamp}} {{agent}}\nstep: {{step}}\nbranch: {{branch}}\n---\n\n# Session {{stamp}} - {{agent}}\n\n## Doing\n\n## State\n\n## Summary\n\n## Next action\n\n## Resume\n`) + seed('.obsidian/app.json', '{\n "readableLineLength": false,\n "showFrontmatter": true\n}\n') + seed('README.md', `# Vault\n\nThe project's second brain, kept in the repo so every agent on every machine shares it. Open this folder in Obsidian.\n\nSchema: OKF-style frontmatter on every note (\`type\`, \`title\`, \`description\`, \`tags\`, \`generated\`, plus \`repo_head\`/\`ingested\` on generated notes). Generated notes (ADRs, conventions, coordination, hackathon docs, specs and plans) are rebuilt by \`loop vault-sync\` and must not be hand-edited; synthesis notes (Home, Architecture Overview, Repo Map, Open Questions & Gaps) are hand-written; \`Milestones/\` and \`Sessions/\` are written by \`loop milestone\` and \`loop checkpoint\`.\n\nSee \`.claude/skills/work-loop/SKILL.md\`.\n`) + console.log(`vault ready at ${VAULT}`) +} + +function cmdVaultSync() { + const py = WIN ? 'python' : 'python3' + const script = join(HERE, 'ingest.py') + if (!existsSync(script)) die('ingest.py missing from the skill') + const r = spawnSync(py, [script, '--repo', ROOT, '--vault', VAULT], { cwd: ROOT, encoding: 'utf8', env: { ...process.env, PYTHONIOENCODING: 'utf-8' } }) + if (r.error || r.status !== 0) die(`ingest.py failed (needs Python 3): ${(r.stderr || r.error?.message || '').trim()}`) + console.log(r.stdout.trim()) +} + +// ---------------------------------------------------------- dispatch ---- +const commands = { help: cmdHelp, status: cmdStatus, next: cmdNext, start: cmdStart, checkpoint: cmdCheckpoint, resume: () => cmdResume(false), milestone: cmdMilestone, 'vault-init': cmdVaultInit, 'vault-sync': cmdVaultSync } +if (!commands[cmd]) { cmdHelp(); die(`unknown command: ${cmd}`) } +try { commands[cmd]() } catch (e) { die(e.message) } diff --git a/.gitignore b/.gitignore index 4555b82..01586b1 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,8 @@ pnpm-debug.log* # Agents AGENTS.md + +# Obsidian vault (vault/): workspace state churns per machine; app.json is tracked +vault/.obsidian/workspace* +vault/.obsidian/cache +vault/.obsidian/plugins diff --git a/NEXT.md b/NEXT.md index 4f72ee3..a952476 100644 --- a/NEXT.md +++ b/NEXT.md @@ -67,7 +67,7 @@ part-time work across the team. | 0.2 | Remove the server env-var API-key fallback in `server/api/chat.post.ts`; return 401 without a per-request key. | ADR 0005 conformance. Five lines; the only server-side hole. | Charles | | 0.3 | Baseline Content Security Policy: `script-src 'self' esm.sh cdn.tailwindcss.com`; `connect-src` = self + provider hosts + esm.sh. | Config-only. The Tailwind Play CDN needs `'unsafe-inline'`; accepted for now (decision D1). | Zack | | 0.4 | ESLint flat config with a slice-boundary rule, plus lint-staged running `vue-tsc` on staged files in pre-commit. | ADR 0002 as decided. Turns "boundaries by convention" into a lint. | Zack | -| 0.5 | Truth pass: correct the README's mobile and "vetted CDN" claims; write ADR 0012 superseding ADR 0001 and 0003 to record the five-slice layout and mixed store style (decision D2). | Judges and contributors read the ADRs; drift costs trust. | Michael (README), Brandon (ADR 0012) | +| 0.5 | Truth pass: correct the README's mobile and "vetted CDN" claims. (The ADR half - ADR 0012 recording the five-slice layout and mixed store style, decision D2 - shipped in PR #78.) | Judges and contributors read the ADRs; drift costs trust. | Michael (README), Brandon (ADR 0012, done) | | 0.6 | Dead code: delete the unused `useChatStore`; make `useAppsStore` the registry's reactive face or delete the mirror. | Two sources of truth for "installed apps" is a latent bug. | Justin | **Exit:** CI runs lint, typecheck, build, and e2e, all green; no key is @@ -81,8 +81,8 @@ the same seam. Roughly two to three weeks. | Step | Work | Why here | Owner | | --- | --- | --- | --- | | 1.1 | Async `VirtualFileSystem` interface plus a driver registry, as ADR 0007 originally decided: `/system/*` on localStorage, `/apps/*` on IndexedDB, with a one-time migration of existing localStorage data. | Breaking interface change; cheapest while the callers are `app.vue`, `registry.ts`, `settings.ts`, and the runner. Unblocks snapshots, window persistence, bigger apps. | Charles | -| 1.2 | Sandboxed runner: compile in the host, mount in a `sandbox="allow-scripts"` iframe via `srcdoc`, with a `postMessage` bridge contract for props, `askFix`, and later IPC. Record it as ADR 0013. | The single biggest safety gap. Doing it before IPC and notifications means those are built on the bridge once, not twice. | Charles | -| 1.3 | Dependency vetting gate in front of esm.sh: npm registry and advisory data, age, downloads, known CVEs; verdicts cached in the VFS; refusals surfaced with reasons. Completes ADR 0008 as ADR 0014. | Only meaningful once 1.2 exists. | Charles | +| 1.2 | Sandboxed runner: compile in the host, mount in a `sandbox="allow-scripts"` iframe via `srcdoc`, with a `postMessage` bridge contract for props, `askFix`, and later IPC. Record it as a new ADR. | The single biggest safety gap. Doing it before IPC and notifications means those are built on the bridge once, not twice. | Charles | +| 1.3 | Dependency vetting gate in front of esm.sh: npm registry and advisory data, age, downloads, known CVEs; verdicts cached in the VFS; refusals surfaced with reasons. Completes ADR 0008 in a new ADR. | Only meaningful once 1.2 exists. | Charles | | 1.4 | Tighten CSP to the sandbox model: the host page drops esm.sh; only the iframe origin may load it. Revisit the Play CDN here. | Closes the loop opened in 0.3. | Zack | **Exit:** an app that reaches for `parent.document` or the host's @@ -113,7 +113,7 @@ the seams and may start during Phase 1. | --- | --- | --- | | 3.1 | Mobile responsive shell: bottom-sheet taskbar and stacked windows below 768px, as ADR 0004 designed. | Zack | | 3.2 | Window snapping and tiling; multi-desktop workspaces. | Zack | -| 3.3 | `/api/proxy` shipped together with its SSRF blocklist (RFC 1918, cloud metadata endpoints) - never one without the other - plus a `useSmartFetch` direct-then-proxy fallback. Only when an app needs a non-CORS API. Record as ADR 0015. | Charles | +| 3.3 | `/api/proxy` shipped together with its SSRF blocklist (RFC 1918, cloud metadata endpoints) - never one without the other - plus a `useSmartFetch` direct-then-proxy fallback. Only when an app needs a non-CORS API. Record as a new ADR. | Charles | | 3.4 | Snapshot export and import of the whole OS state; File System Access API mount of a local directory. | Justin | | 3.5 | Multi-tab sync via `BroadcastChannel` (ADR 0003's deferred item). | Justin | | 3.6 | Agent-generated unit tests verified in-browser before install. After 2.3, which it depends on. | Charles | @@ -123,9 +123,10 @@ the seams and may start during Phase 1. - **Every step is one issue.** Open it with `## Files` and `## Done when` before writing code; claim it with a branch name. -- **Every changed decision is a new ADR**, never an edit to an old one: - 0012 (slices and stores), 0013 (sandbox bridge), 0014 (vetting gate), - 0015 (proxy). +- **Every changed decision is a new ADR**, never an edit to an old one. + Written so far: 0012 (end of the hackathon scope; slices and stores), + 0013 (in-repo vault and the work loop). Still to write, numbered when + they land: the sandbox bridge, the vetting gate, the proxy. - **The e2e suite from 0.1 is the definition of "still works."** A PR that turns it red does not merge, whatever else it does. @@ -144,7 +145,7 @@ sprint, so each person owns what they already know. | Charles (`Sullux`) | Runtime, persistence, and the chat route | `app/features/apps/runner/**`, `app/features/apps/registry/**`, `app/features/shared/vfs.ts`, `server/api/**` | 0.2, 1.1, 1.2, 1.3, 2.3, 3.3, 3.6 | | Justin (`ImNewToC0de`) | Integration and the agent product loop | `app/app.vue`, `app/features/chat/**`, `app/features/apps/stores/**`, `app/features/settings/**` | 0.6, 2.1, 2.2, 2.4, 3.4, 3.5 | | Michael (`m-vawter`) | Quality, docs, and process | `tests/**`, `README.md`, `docs/agents/**`, `docs/JUDGES.md`, board hygiene | 0.1, 0.5 (README), the e2e gate, release notes | -| Brandon (`r0073d-l053r`) | Coordination, security review, and this roadmap | `.claude/skills/coordinate/**`, `NEXT.md`, `docs/adrs/**` (new ADRs), the knowledge-base vault | 0.5 (ADR 0012), second reviewer on 1.2-1.4, retrospectives | +| Brandon (`r0073d-l053r`) | Coordination, security review, and this roadmap | `.claude/skills/coordinate/**`, `.claude/skills/work-loop/**`, `NEXT.md`, `docs/adrs/**` (new ADRs), `vault/` | 0.5 (ADR 0012, done), second reviewer on 1.2-1.4, retrospectives | A path not listed belongs to whoever opens the issue that first touches it; add it to this table in the same PR. diff --git a/README.md b/README.md index e664cb5..2bfbf8c 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ records below for why it is built this way. | [docs/agents/rubric.md](docs/agents/rubric.md) | Hackathon judging rubric and win strategy | | [docs/agents/demo-path.md](docs/agents/demo-path.md) | The two-minute demo as rubric-mapped beats | | [docs/agents/coordination.md](docs/agents/coordination.md) | Multi-agent task coordination over GitHub Issues (`/coordinate`) | +| [docs/agents/work-loop.md](docs/agents/work-loop.md) | The work loop: any agent takes the next roadmap step via `/coordinate` (`npm run loop`) | +| [vault/](vault/DOMinic%20Home.md) | The project's second brain (Obsidian): milestones, sessions, ingested docs | | [docs/agents/coordination-best-practices.md](docs/agents/coordination-best-practices.md) | Coordination playbook v1 (superseded; kept for the record) | | [docs/agents/coordination-best-practices-2.md](docs/agents/coordination-best-practices-2.md) | Coordination playbook v2 (superseded; kept for the record) | | [docs/agents/coordination-best-practices-3.md](docs/agents/coordination-best-practices-3.md) | Active coordination playbook — sprint evidence and enforceable invariants | diff --git a/docs/adrs/0013-in-repo-vault-and-work-loop.md b/docs/adrs/0013-in-repo-vault-and-work-loop.md new file mode 100644 index 0000000..040ea2d --- /dev/null +++ b/docs/adrs/0013-in-repo-vault-and-work-loop.md @@ -0,0 +1,130 @@ +--- +type: Architecture Decision Record +title: In-repo vault and the work loop +description: The project's second brain lives in the repository as vault/, and a harness-agnostic work loop drives roadmap steps through /coordinate while recording sessions and milestones in it. +status: accepted +tags: [process, tooling, agents, vault] +generated: { by: claude-code/claude-fable-5.1, at: 2026-10-02T00:00:00Z } +verified: { by: human:brandon, at: 2026-10-02T00:00:00Z } +--- + +# In-repo vault and the work loop + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0013 | accepted | 2026-10-02 | Brandon; lane owners by review | + +## Technical Story + +During the hackathon one collaborator kept an Obsidian vault that mirrored +the repository's docs with a fixed schema (OKF-style frontmatter, generated +notes regenerated from `docs/`, hand-written synthesis notes) and used it to +brief agents, track the board, and record what changed. It lived outside the +repository, so only one machine and one person had it. With the roadmap in +`NEXT.md` and work continuing part-time across five people and several +agent harnesses, the project needs that memory to be shared, and it needs a +way for any agent to pick up the next step, record progress, and survive +the context compaction every harness performs. + +## Context and Problem Statement + +Agents forget between sessions and inside long ones. Humans forget which +step is next. A roadmap, an issue board, and a vault each hold part of the +answer, but nothing connected them: nothing told an agent arriving cold +what to do first, nothing wrote down a milestone when a PR merged, and +nothing captured a session's state before the harness compressed it away. +Where should the vault live so every agent can read and write it, and what +is the smallest loop that keeps roadmap, board, and vault consistent +without depending on one harness's scheduler? + +## Decision Drivers + +- Any harness: Claude Code, Codex, Pi, opencode, Cursor, cron, or a human + must be able to run the same loop with the same tools (`node`, `git`, + `gh`). +- One shared memory: milestones and session notes must be visible to every + collaborator and machine, versioned with the code. +- Keep the schema already in use rather than inventing a second one. +- The loop must never hold state itself; restarts and compaction are the + normal case, not the failure case. +- Respect the coordination protocol: lanes, one in-progress issue, phase + gates, `## Files` before code. + +## Considered Options + +- Vault outside the repository, loop inside one harness's scheduler +- Vault in the repository, loop as a plain script any harness can run +- No vault; rely on issues, PR descriptions, and chat history + +## Decision Outcome + +Chosen option: **vault in the repository, loop as a plain script**, +because shared memory has to travel with the code, and a loop that is just +a script is the only kind every harness can run. + +- `vault/` is committed. It adopts the existing schema unchanged - the + generated folders (`ADRs/`, `Conventions/`, `Coordination/`, + `Hackathon/`, `Specs & Plans/`, `Repo Config/`, Commit Timeline), the + hand-written synthesis notes (Home, Architecture Overview, Repo Map, + Open Questions & Gaps), and OKF-style frontmatter with `repo_head` and + `ingested` on generated notes - and adds two folders the loop owns: + `Milestones/` (one note per merged roadmap step) and `Sessions/` (one + note per checkpoint). `.obsidian/workspace*` is ignored; `app.json` is + tracked. `vault/` is excluded from markdownlint. +- The loop is `.claude/skills/work-loop/scripts/loop.mjs` (Node 24, no + dependencies), exposed as `npm run loop`. Commands: `start` (resume brief, + pick the next eligible step, create and claim its issue through + `/coordinate`, branch, print the work brief), `checkpoint` (Session note + plus `STATUS` or `HANDOFF`), `resume` (the post-compaction digest), + `milestone` (`coord done`, Milestone note, progress refresh), `next`, + `status`, `vault-init`, `vault-sync`. +- Eligibility is computed from `NEXT.md` and the issue board every time: + lane first, one in-progress per agent, phase order, sequential order in + the phases `loop.config.json` names, and declared parallel steps. The + config also names the roadmap file and the vault path, so the loop + serves other repositories unchanged. +- `vault-sync` runs the ingest script (Python 3) that regenerates the + generated half of the vault from `docs/`; a Node port is deferred until + a contributor lacks Python. +- The Claude Code skill `/work-loop` and `docs/agents/work-loop.md` are the + two entry points; the former loads automatically, the latter is for + every other harness. + +### Confirmation + +A fresh clone plus `export COORD_AGENT=...` and `npm run loop -- start` +claims the next eligible step and prints its brief with no other setup; +`checkpoint` followed by a new session's `resume` reproduces the step, +branch, and next action; `milestone` after a merged PR closes the issue and +leaves a note under `vault/Milestones/` that Home links to. + +## Pros and Cons of the Options + +### Vault in the repository, loop as a plain script + +- ✅ Good, because every agent and machine shares one memory, reviewed + through the same PRs as the code. +- ✅ Good, because the loop has no harness dependency and no state. +- ❌ Bad, because vault edits add commits and can conflict; mitigated by + one-note-per-event files and the loop writing only its own folders. + +### Vault outside the repository, loop inside one harness's scheduler + +- ✅ Good, because it already existed and needed no repo changes. +- ❌ Bad, because only one person had it, and the scheduler would have + tied the loop to Claude Code. + +### No vault; rely on issues, PR descriptions, and chat history + +- ✅ Good, because it adds nothing. +- ❌ Bad, because chat history is exactly what compaction destroys, and + issues do not hold a session's half-finished state. + +## Links + +- Executes [NEXT.md](../../NEXT.md) through + [ADR 0012](0012-end-of-hackathon-scope.md)'s full coordination protocol +- Builds on [coordination.md](../agents/coordination.md) and the + `/coordinate` skill +- Related to [ADR 0011](0011-node-24-lts-runtime-standard.md) (the loop + targets Node 24) diff --git a/docs/agents/work-loop.md b/docs/agents/work-loop.md new file mode 100644 index 0000000..04f69aa --- /dev/null +++ b/docs/agents/work-loop.md @@ -0,0 +1,59 @@ +--- +type: playbook +title: The work loop +description: How any coding agent, on any harness, picks up the next roadmap step through /coordinate, records progress in the in-repo vault, and keeps going until NEXT.md is complete. +tags: [agents, work-loop, roadmap, vault] +generated: { by: claude-code/claude-fable-5.1, at: 2026-10-02T00:00:00Z } +verified: { by: human:brandon, at: 2026-10-02T00:00:00Z } +sources: + - id: skill + resource: ../../.claude/skills/work-loop/SKILL.md + title: work-loop skill (full instructions) + - id: roadmap + resource: ../../NEXT.md + title: Sequenced roadmap and lanes + - id: coordination + resource: coordination.md + title: Agent coordination over GitHub Issues +--- + +# The work loop + +`NEXT.md` says what to build and in what order; `/coordinate` says who +holds what; the vault remembers. The work loop is the script that runs +the three together so an agent can arrive with no context, do the right +next step, and leave a trail the next agent - or the same agent after +compaction - can pick up. + +```bash +export COORD_AGENT="<harness>/<model>" +npm run loop -- resume # first action in a fresh session +npm run loop -- start # next eligible step in your lane +npm run loop -- checkpoint -m "..." # before context is compacted +npm run loop -- milestone 0.1 --pr URL # after the PR merges +``` + +The full instructions, the harness recipes, and the command table live +in the skill: `.claude/skills/work-loop/SKILL.md` and +`references/harnesses.md`. Claude Code loads it as `/work-loop`; every +other harness reads the markdown and runs the same script, which needs +only Node 24, `git`, and `gh`. + +## What the loop guarantees + +- Your lane first; another lane's step only with `--any` and a prior + `propose` on its issue. +- One step in progress per agent; `start` continues what you hold. +- Phase gates from the roadmap: Phase N+1 opens when Phase N is done, + Phase 1 runs in order, `3.1` may start once Phase 1 has begun. +- An issue exists before code does, with `## Files` and `## Done when` + taken from the roadmap step and the phase exit criteria. +- A session note exists before context is lost, and a milestone note + exists for every merged step. + +## The vault + +`vault/` is committed so every agent shares it. It keeps the schema the +project already used (OKF-style frontmatter; generated notes rebuilt by +`vault-sync`; hand-written synthesis notes) and adds `Milestones/` and +`Sessions/`, which only the loop writes. Open the folder in Obsidian. diff --git a/package.json b/package.json index 9f9b635..3a6da28 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,9 @@ "generate": "nuxt generate", "preview": "nuxt preview", "typecheck": "nuxt typecheck", - "lint:md": "markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#.superpowers\" \"#docs/superpowers\"", - "prepare": "husky" + "lint:md": "markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#.superpowers\" \"#docs/superpowers\" \"#vault\"", + "prepare": "husky", + "loop": "node .claude/skills/work-loop/scripts/loop.mjs" }, "engines": { "node": ">=24.21.0 <25" diff --git a/vault/.obsidian/app.json b/vault/.obsidian/app.json new file mode 100644 index 0000000..67d58a9 --- /dev/null +++ b/vault/.obsidian/app.json @@ -0,0 +1,4 @@ +{ + "readableLineLength": false, + "showFrontmatter": true +} diff --git a/vault/ADRs/ADR 0000 - Record architecture decisions.md b/vault/ADRs/ADR 0000 - Record architecture decisions.md new file mode 100644 index 0000000..671e80b --- /dev/null +++ b/vault/ADRs/ADR 0000 - Record architecture decisions.md @@ -0,0 +1,84 @@ +--- +type: Architecture Decision Record +title: Record architecture decisions +status: accepted +tags: [architecture, process] +source: docs/adrs/0000-record-architecture-decisions.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0000" +--- +# Record architecture decisions + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0000 | accepted | 2026-09-12 | Zack | + +## Technical Story + +The DOMinic project needs a lightweight, durable way to record +significant architecture and tooling decisions so that future +contributors understand not just *what* was decided, but *why*. + +## Context and Problem Statement + +Projects accumulate decisions that are cheap to revisit but +expensive to re-litigate without context: which libraries to +use, which patterns to follow, which conventions to adopt. +Tribal knowledge and chat history do not survive team changes. +We need a practice for capturing decisions in a format that +lives with the code. + +## Decision Drivers + +- Decisions must be recorded in version-controllable plain text. +- The format must be quick to write and quick to read. +- The format must be understandable without special tooling. +- The practice must be easy for new contributors to adopt. + +## Considered Options + +- Michael Nygard's classic ADR format +- MADR (Markdown Any Decision Records) 4.0 +- No formal ADR practice + +## Decision Outcome + +Chosen option: **MADR 4.0**, because it keeps Nygard's +strengths (plain markdown, short documents) while adding +structure that improves quality: explicit decision drivers, +considered options with pros/cons, and a decision outcome +with consequences. It is widely adopted and works well in +code review and pull requests. + +Decisions are recorded in this directory (`docs/adrs/`), +numbered sequentially starting at 0001 for real decisions +(0000 is this meta-ADR), with kebab-case file names. + +### Confirmation + +Practice confirmed for the start of the project; revisit if +the format proves too heavyweight for small decisions. + +## Pros and Cons of the Options + +### Michael Nygard's classic ADR format + +- ✅ Minimal, extremely well known. +- ❌ No explicit place for considered options or their trade-offs. + +### MADR (Markdown Any Decision Records) 4.0 + +- ✅ Lightweight and markdown-native. +- ✅ Forces explicit consideration of alternatives. +- ❌ Slightly more boilerplate than Nygard. + +### No formal ADR practice + +- ✅ Zero overhead. +- ❌ Decision context is lost over time. + +## Links + +- [MADR project](https://adr.github.io/madr/) +- [Nygard's original ADR format](http://adamdrake.com/content/2015/07/03/architecture-decision-records-adrs/) diff --git a/vault/ADRs/ADR 0001 - Feature slices with domain-driven organization.md b/vault/ADRs/ADR 0001 - Feature slices with domain-driven organization.md new file mode 100644 index 0000000..3cac185 --- /dev/null +++ b/vault/ADRs/ADR 0001 - Feature slices with domain-driven organization.md @@ -0,0 +1,160 @@ +--- +type: Architecture Decision Record +title: Feature slices with domain-driven organization +description: Organize app code into slice-local vertical directories with a thin shared kernel. +status: accepted +tags: [architecture] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0001-feature-slices-with-domain-driven-organization.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0001" +--- + +# Feature slices with domain-driven organization + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0001 | accepted | 2026-09-12 | Zack | + +## Technical Story + +DOMinic is an agent-driven browser OS whose code will be written and +rewritten largely by an LLM agent. Nuxt's default structure scatters +code by technical layer (`components/`, `composables/`, `stores/`), so +one feature's knowledge is spread thin across many directories. We +need an organization that maps bounded contexts to navigable +directories with enforceable ownership. + +## Context and Problem Statement + +The agent frequently needs to locate, read, and modify a single +feature without disturbing others. Layer-first directories force +cross-cutting edits, blur ownership, and grow without bound. Bounded +contexts should map 1:1 to directories a human or agent can traverse +in a single pass. + +## Decision Drivers + +- Slice-local cohesion: everything for one feature lives together. +- An enforceable dependency direction between slices. +- Small, focused directories that are easy for the agent to navigate. +- Compatibility with Nuxt's directory conventions. + +## Considered Options + +- Slice-local vertical directories +- Classic layer-first directories +- Hybrid: layer-first with per-slice subdirectories + +## Decision Outcome + +Chosen option: **slice-local vertical directories**, because they give +each bounded context one home, keep the dependency direction +enforceable, and produce the smallest, most navigable change sets for +agent edits. + +### Hackathon POC (Golden Path) + +For the hackathon POC, the team implements three core slices: + +- `app/features/os`: Window manager, taskbar, desktop canvas, and app + registry. +- `app/features/agent`: Chat dock/drawer, prompt templates, streaming + client, and tool execution handlers. +- `app/features/apps`: Runtime SFC loader engine (`vue3-sfc-loader`) and + the built-in Settings app. + +A thin `app/shared/` kernel holds minimal contracts: shared TypeScript +interfaces (`DominicApp`, `WindowState`, `AppMetadata`) and storage +keys. Slices import from `app/shared/` but avoid cross-slice coupling. +During the hackathon sprint, boundaries are respected by convention +among the 5 SDEs rather than blocking commits on custom lint rules. + +### Future / Out of Scope for POC + +- Automated ESLint import-boundary enforcement rules. +- Dynamic slice discovery and dynamic micro-frontend packaging. +- AST-based architecture dependency graphing. + +### Open Questions + +- **OPEN QUESTION: Shared Styling in Runtime Components**: How should + design tokens and Tailwind utility classes be shared between the host + OS shell and agent-compiled components without stylesheet pollution? + (Current POC path: Inject Tailwind via CDN / global stylesheet). + +### Confirmation + +Dependency direction is enforced by ESLint import rules once the +lint toolchain lands; code review rejects slice-to-slice imports. + +### As built (2026-09-12) + +**Matches the golden path:** + +- Slices are vertical directories under `app/features/` (`os`, `chat`, + `apps`, `settings`, `shared`), with `srcDir: "app/"` in `nuxt.config.ts:5`. +- `os` holds the window store (`app/features/os/stores/os.ts:21`); `apps` + holds the SFC runner (`app/features/apps/runner/loader.ts:5`). +- No slice imports another slice; the only cross-directory imports inside + `app/features` target `../../shared/vfs` + (`app/features/apps/registry/registry.ts:2`). `app/app.vue:3-14` is the + sole composition root and wires slices via callbacks (`app/app.vue:123`). +- Boundaries are held by convention: the pre-commit hook only runs + `npm run lint:md` (`.husky/pre-commit:1`) and no ESLint config exists. + +**Differs from this record:** + +- ADR names the agent slice `app/features/agent` -> main names it + `app/features/chat` (`app/features/chat/index.ts:1`). +- ADR puts the Settings app inside `apps` and the app registry in `os` -> + main has a `settings` slice (`app/features/settings/index.ts:1`) and + keeps the registry in `app/features/apps/registry/registry.ts:18`. +- ADR names the kernel `app/shared/` -> main has no such directory; it uses + `app/features/shared/`, auto-imported by `imports.dirs` + (`nuxt.config.ts:10`). +- ADR says the kernel holds `DominicApp`, `WindowState`, `AppMetadata` and + storage keys -> it holds a localStorage VFS (`app/features/shared/vfs.ts:1`) + and a `vue3-sfc-loader` type shim; `WindowState` is a string union at + `app/features/os/stores/os.ts:4` (the window record is `OsWindow`, `:6`), + the metadata type is `AppMeta` in `app/features/apps/registry/registry.ts:4`, + and `DominicApp` is prose (`app/features/chat/prompts/systemPrompt.ts:23`). + +**Open question outcome:** + +- Resolved as the POC path: the Tailwind Play CDN script is injected in + `nuxt.config.ts:32` next to build-time content globs (`nuxt.config.ts:17`). +- Runtime SFC styles are appended to `document.head` with no host-side + isolation, tagged `data-app-id` (`app/features/apps/runner/loader.ts:34`). + +Reconciled against main on 2026-09-12; the decision text above is unchanged. + +## Pros and Cons of the Options + +### Slice-local vertical directories + +- ✅ Good, because each bounded context is one navigable directory. +- ✅ Good, because ownership boundaries are explicit and enforceable. +- ❌ Bad, because shared infrastructure needs a deliberate home + (`app/shared/`). + +### Classic layer-first directories + +- ✅ Good, because it is the Nuxt default with zero configuration. +- ❌ Bad, because one feature change scatters edits across many + directories. +- ❌ Bad, because unbounded layer directories blur slice ownership + over time. + +### Hybrid: layer-first with per-slice subdirectories + +- ✅ Good, because it preserves familiar top-level layer names. +- ❌ Bad, because it adds nesting without fixing scattered ownership. + +## Links + +- Related to [[ADR 0000 - Record architecture decisions|ADR 0000]] +- Related to [[ADR 0003 - Pinia per-domain stores|ADR 0003]] +- Related to [[ADR 0004 - DOMinic OS shell and taskbar|ADR 0004]] diff --git a/vault/ADRs/ADR 0002 - Strict TypeScript and lint toolchain.md b/vault/ADRs/ADR 0002 - Strict TypeScript and lint toolchain.md new file mode 100644 index 0000000..48cfef6 --- /dev/null +++ b/vault/ADRs/ADR 0002 - Strict TypeScript and lint toolchain.md @@ -0,0 +1,158 @@ +--- +type: Architecture Decision Record +title: Strict TypeScript and lint toolchain +description: TypeScript strict mode plus type-checked ESLint and vue-tsc gate agent-generated code. +status: accepted +tags: [architecture, tooling] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0002-strict-typescript-and-lint-toolchain.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0002" +--- + +# Strict TypeScript and lint toolchain + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0002 | accepted | 2026-09-12 | Zack | + +## Technical Story + +DOMinic's agent authors and rewrites application code continuously. +Whatever the compiler and linter do not catch ships as runtime +breakage inside the OS. The toolchain must maximize static safety +while staying Vue-aware, and it must run in CI and before every +commit. + +## Context and Problem Statement + +Agent-generated code is written fast and reviewed shallowly. We need +maximal compile-time protection (`strict`, `noUncheckedIndexedAccess`), +Vue SFC awareness, and enforcement gates that do not depend on a +human remembering to run them. + +## Decision Drivers + +- Catch whole classes of errors at compile time, not runtime. +- Consistent style without per-file debate. +- Enforcement in CI and pre-commit, not by convention. +- First-class Vue SFC support (template + script analysis). + +## Considered Options + +- ESLint (typescript-eslint + eslint-plugin-vue) + vue-tsc +- Oxlint + vue-tsc +- Biome + vue-tsc + +## Decision Outcome + +Chosen option: **ESLint with type-checked typescript-eslint and +eslint-plugin-vue, plus `vue-tsc --noEmit`**, because it is the only +option with mature Vue-aware rules, full type-checking, and a flat +config that the Nuxt ecosystem (`@nuxt/eslint`) integrates directly. + +### Hackathon POC (Golden Path) + +To maximize developer velocity across the 5 SDE team during the +hackathon sprint without sacrificing core type safety: + +- TypeScript strict mode (`strict: true`) is enabled in `tsconfig.json` + for editor assistance, auto-complete, and immediate error highlighting. +- Pre-commit hooks (`husky`) run only fast markdown linting and basic + syntax checks. Blocking `vue-tsc --noEmit` runs are explicitly + deferred from pre-commit hooks to avoid commit friction during rapid + parallel feature hacking. +- Full `vue-tsc` and lint checks are executed on-demand and during final + pre-demo verification. +- Runtime SFC compilation (ADR 0006) executes agent-authored code + without compile-time type-blocking; runtime errors are caught via + Vue error boundaries. + +### Future / Out of Scope for POC + +- Mandatory blocking pre-commit gate running full `vue-tsc --noEmit` and + type-aware ESLint rules on every git commit. +- Automated static linting and type analysis of agent-authored code + before runtime installation. + +### Open Questions + +- **OPEN QUESTION: Agent TypeScript Support**: Should the agent be + prompted to output `<script setup lang="ts">` or plain + `<script setup>`? (Recommendation for POC: Plain `<script setup>` in + JavaScript minimizes runtime transpile failures in `vue3-sfc-loader`). + +### Confirmation + +A fresh Nuxt scaffold lands with this exact configuration; a +deliberate type error and a lint error both fail CI. + +### As built (2026-09-12) + +**Matches the golden path:** + +- `tsconfig.json` enables `strict: true` plus `noUncheckedIndexedAccess`, + `noImplicitOverride` and `verbatimModuleSyntax` (`tsconfig.json:4`). +- `nuxt.config.ts` sets `typescript.strict: true` and `typeCheck: true`, so + Nuxt runs `vue-tsc` during dev and build (`nuxt.config.ts:20`). +- The husky pre-commit hook runs only `npm run lint:md` + (`.husky/pre-commit:1`); no `vue-tsc --noEmit` gate exists in the hook, as + the POC deferred. +- `vue-tsc` is a devDependency and `npm run typecheck` runs `nuxt typecheck` + on demand (`package.json:12`, `package.json:29`). + +**Differs from this record:** + +- ADR: ESLint with type-checked typescript-eslint, eslint-plugin-vue and + `@nuxt/eslint` -> main has no ESLint at all: no `eslint.config.*`, no + `eslint`/`@nuxt/eslint` devDependency, and no `lint` script + (`package.json:16`). Only `markdownlint-cli2` is installed + (`package.json:24`). +- ADR Confirmation: "a deliberate type error and a lint error both fail CI" + -> CI has a single `markdownlint` job that runs `npm run lint:md` + (`.github/workflows/ci.yml:9`, `.github/workflows/ci.yml:24`); no + typecheck or ESLint step runs in CI. +- ADR: pre-commit runs "markdown linting and basic syntax checks" -> the hook + runs markdown linting only (`.husky/pre-commit:1`). +- Markdown rules live in `.markdownlint.jsonc` (80-column `MD013`; headings, + tables and code blocks exempt) (`.markdownlint.jsonc:3`). + +**Open question outcome:** + +- Resolved as the POC recommended: the agent system prompt instructs plain + JavaScript `<script setup>` and forbids `lang="ts"` + (`app/features/chat/prompts/systemPrompt.ts:26`), so agent output is never + type-checked; the host app itself uses `<script setup lang="ts">` + (`app/app.vue:1`). + +Reconciled against main on 2026-09-12; the decision text above is unchanged. + +## Pros and Cons of the Options + +### ESLint (typescript-eslint + eslint-plugin-vue) + vue-tsc + +- ✅ Good, because type-checked linting catches `await` misuse, + floating promises, and unsafe `any` spread. +- ✅ Good, because `eslint-plugin-vue` analyzes templates and SFC + structure, not just script. +- ❌ Bad, because type-checked linting is slower than syntax-only + linting. + +### Oxlint + vue-tsc + +- ✅ Good, because it is dramatically faster on large trees. +- ❌ Bad, because Vue SFC and type-aware rule coverage is still + incomplete. + +### Biome + vue-tsc + +- ✅ Good, because it bundles format + lint in one fast binary. +- ❌ Bad, because Vue SFC support is immature and the TypeScript + rule set is narrower than typescript-eslint. + +## Links + +- Related to [[ADR 0000 - Record architecture decisions|ADR 0000]] +- Related to [[ADR 0001 - Feature slices with domain-driven organization|ADR 0001]] diff --git a/vault/ADRs/ADR 0003 - Pinia per-domain stores.md b/vault/ADRs/ADR 0003 - Pinia per-domain stores.md new file mode 100644 index 0000000..3cfc9cf --- /dev/null +++ b/vault/ADRs/ADR 0003 - Pinia per-domain stores.md @@ -0,0 +1,155 @@ +--- +type: Architecture Decision Record +title: Pinia per-domain stores +description: One Pinia setup-style store per slice is the only cross-slice state channel. +status: accepted +tags: [architecture] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0003-pinia-per-domain-stores.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0003" +--- + +# Pinia per-domain stores + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0003 | accepted | 2026-09-12 | Zack | + +## Technical Story + +The OS holds live state everywhere: open windows, app registry, +settings, chat sessions. The state must be observable, modular, and +debuggable, and it must not entangle slices that are otherwise +independent. + +## Context and Problem Statement + +State lives inside components today would be invisible to other apps +and impossible to persist or restore. A global event bus would couple +every slice to every other. We need state primitives that are +Vue-idiomatic, per-slice modular, devtools-inspectable, and SSR-safe. + +## Decision Drivers + +- Vue-idiomatic reactivity and devtools integration. +- Per-slice modularity matching the slice layout. +- A single, controlled channel for cross-slice state. +- SSR-safety without bespoke lifecycles. + +## Considered Options + +- Pinia, one setup-style store per slice +- Composables-only state +- Pinia plus a global event bus + +## Decision Outcome + +Chosen option: **Pinia, one setup-style store per slice**, because it +is Vue's official state library, is devtools-first, is SSR-safe, and +its store-per-slice shape maps exactly onto the slice boundaries. + +### Hackathon POC (Golden Path) + +For the hackathon POC, the OS relies on three focused Pinia setup stores: + +- `useOsStore` (`app/features/os/stores/os.ts`): Manages the window + manager state (open window instances, focused window ID, z-index + stack, minimized/maximized states) and the registered apps list. +- `useAgentStore` (`app/features/agent/stores/agent.ts`): Manages chat + message history, streaming status, and active tool execution status. +- `useSettingsStore` (`app/features/apps/stores/settings.ts`): Manages + configured LLM provider credentials (API keys), model selections, and + shell preferences. + +Cross-slice state reads/writes use these three stores directly. Pinia +setup syntax (`ref`, `computed`, functions) keeps the code immediately +readable and writable across the 5 SDEs. + +### Future / Out of Scope for POC + +- Cross-tab state synchronization via `BroadcastChannel` or `SharedWorker`. +- Full OS time-travel debugging and session state snapshot export/import. +- Granular permissions or access control for store state. + +### Open Questions + +- **OPEN QUESTION: Window Session Persistence**: Should individual window + positions, sizes, and open states persist across page reloads, or + should only the installed app registry persist? (Recommendation for + POC: Persist the installed apps list; let windows start cleanly on + reboot to avoid broken layout restore). + +### Confirmation + +Code review rejects component-private OS state that duplicates a +store; the devtools Pinia panel shows one store per slice. + +### As built (2026-09-12) + +**Matches the golden path:** + +- `useOsStore` is a setup store (`defineStore("os", () => ...)`) holding + `windows`, `focusedId`, an internal `topZ` computed, and per-window + minimized/maximized state (`app/features/os/stores/os.ts:21`). +- `useSettingsStore` holds `provider`, `model`, `apiKey`, and `baseUrl` + and is read by `app.vue` and `SettingsApp.vue` + (`app/features/settings/stores/settings.ts:16`). + +**Differs from this record:** + +- Three stores -> four `defineStore` calls: `os`, `apps`, `chat`, and + `settings` (`app/features/apps/stores/apps.ts:3`). +- `useAgentStore` in `app/features/agent/stores/agent.ts` -> no `agent` + slice exists; `messages` and `isStreaming` are plain `ref`s inside + `useAgentChat` (`app/features/chat/composables/useAgentChat.ts:14`), + which reads settings and apps through `UseAgentChatOptions` callbacks + wired in `app.vue`, not through stores (`app/app.vue:123`). +- `useChatStore` (option store, `messages` only) is defined but imported + nowhere in `app/` (`app/features/chat/stores/chat.ts:3`). +- Setup syntax everywhere -> only `os` is a setup store; `chat`, `apps`, + and `settings` use option syntax (`state`; only `settings` has `actions`) + (`app/features/settings/stores/settings.ts:16`). +- `useSettingsStore` at `app/features/apps/stores/settings.ts` -> lives at + `app/features/settings/stores/settings.ts:16`. +- Registered apps list in `useOsStore` -> registry state is a module-level + `appsState` ref outside Pinia + (`app/features/apps/registry/registry.ts:15`); `useAppsStore.installed` + is a mirror `app.vue` seeds from `hydrateRegistry()` (`app/app.vue:30`) + and reassigns from `listApps()` after each mutation (`app/app.vue:93`). + +**Open question outcome:** + +- Resolved as recommended: the registry persists to the VFS as `registry.json` + (`app/features/apps/registry/registry.ts:14`); `useOsStore.windows` is + never persisted; `app.vue` opens only `chat` on mount (`app/app.vue:31`). + +Reconciled against main on 2026-09-12; the decision text above is unchanged. + +## Pros and Cons of the Options + +### Pinia, one setup-style store per slice + +- ✅ Good, because setup stores use plain `ref`/`computed` the team + already knows. +- ✅ Good, because per-slice stores mirror slice boundaries 1:1. +- ❌ Bad, because another library adds bundle weight. + +### Composables-only state + +- ✅ Good, because it adds no dependency. +- ❌ Bad, because ad-hoc composable singletons lose devtools tracing + and consistent patterns for cross-slice state. + +### Pinia plus a global event bus + +- ✅ Good, because fan-out signals are trivial to broadcast. +- ❌ Bad, because hidden event coupling defeats the slice boundary + the store layout creates. + +## Links + +- Related to [[ADR 0000 - Record architecture decisions|ADR 0000]] +- Related to [[ADR 0001 - Feature slices with domain-driven organization|ADR 0001]] diff --git a/vault/ADRs/ADR 0004 - DOMinic OS shell and taskbar.md b/vault/ADRs/ADR 0004 - DOMinic OS shell and taskbar.md new file mode 100644 index 0000000..1e8799e --- /dev/null +++ b/vault/ADRs/ADR 0004 - DOMinic OS shell and taskbar.md @@ -0,0 +1,169 @@ +--- +type: Architecture Decision Record +title: DOMinic OS shell and taskbar +description: A desktop-metaphor shell in the os slice with window manager, taskbar, app registry, and a built-in Settings app. +status: accepted +tags: [architecture, platform] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0004-dominic-os-shell-and-taskbar.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0004" +--- + +# DOMinic OS shell and taskbar + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0004 | accepted | 2026-09-12 | Zack | + +## Technical Story + +DOMinic presents itself as a desktop-like operating system running in +a browser tab. Something must own the desktop metaphor: launching +apps, tracking open windows, and giving the user a persistent place +to find and switch between apps — on a phone first, and a desktop +second. + +## Context and Problem Statement + +Apps (first-party and agent-authored alike) need a common contract for +being launched, surfaced, and closed. Mobile-first means the desktop +metaphor must degrade gracefully to small viewports instead of being +desktop-only. + +## Decision Drivers + +- The desktop metaphor is instantly familiar to users. +- Mobile scaling: the shell must work at phone widths first. +- App and window lifecycle management in one place. +- First-party apps must follow the same contract as agent-authored + apps. + +## Considered Options + +- Full windowing shell with taskbar +- Single-activity mobile-style shell +- Hybrid desktop shell with mobile taskbar + +## Decision Outcome + +Chosen option: **desktop-metaphor shell in an `os` slice**, because it +delivers the OS identity while treating mobile as a first-class +degradation rather than a separate shell. + +### Hackathon POC (Golden Path) + +For the hackathon POC, the desktop shell focuses on delivering a visually +stunning, reliable desktop-viewport experience (optimized for laptop/demo +displays): + +- **Window Management**: Built using `@vueuse/core` (`useDraggable`) for + clean drag handling without custom mouse event listeners. Supports + draggable title bars, window focus/z-indexing, minimize to taskbar, + maximize/restore, and close. +- **Taskbar**: Fixed to the bottom of the viewport. Features an App + Launcher menu (listing installed apps and first-party utilities), open + window badges, an Agent Chat toggle button, and a live clock. +- **Visual Polish**: Modern dark-mode glassmorphism (`backdrop-blur-md`, + subtle borders, drop shadows) and desktop wallpaper support to + maximize demo visual impact. +- **Settings App**: Pre-installed first-party app that allows the user to + input and persist their LLM API key (ADR 0005). + +### Future / Out of Scope for POC + +- Responsive mobile degradation (bottom-navigation sheet, stacked + cards, touch-optimized gestures). +- Advanced window snapping, tiling window manager, or multi-monitor + virtual workspaces. +- Window resize handles from all edges/corners (POC uses maximize/restore + and fixed initial sizing). + +### Open Questions + +- **OPEN QUESTION: Chat Shell Presence**: Should the Agent Chat live as a + docked right-hand slide-out drawer or as a standard draggable window? + (Recommendation for POC: A draggable OS window with high default + z-index and an easy dock icon gives maximum flexibility and showcases + the window manager immediately). + +### Confirmation + +The first Nuxt build renders the shell: taskbar, at least one window, +and the Settings app launchable. + +### As built (2026-09-12) + +**Matches the golden path:** + +- Windows support title-bar drag, focus with z-ordering, minimize to the + taskbar, maximize/restore and close (`app/features/os/stores/os.ts:60`, + `:75`, `:83`, `:48`); the taskbar is fixed to the viewport bottom + (`app/features/os/components/Taskbar.vue:18`). +- Dark glass chrome uses `border-white/10`, `bg-slate-900/95`, `shadow-2xl` + and `backdrop-blur` (`app/features/os/components/WindowFrame.vue:54`). +- No resize handles; size is fixed at open (`app/features/os/stores/os.ts:37`) + and maximize swaps to `100vw` x `calc(100vh - 48px)` (`WindowFrame.vue:14`). +- No responsive or mobile layout exists in the `os` slice. + +**Differs from this record:** + +- `@vueuse/core` `useDraggable` -> raw `pointerdown`/`pointermove`/`pointerup` + handlers using `setPointerCapture` and `movementX`/`movementY`, clamped to + the viewport (`app/features/os/components/WindowFrame.vue:28`, `:35`); + `@vueuse/core` (`package.json:21`) is imported only by + `app/features/apps/runner/loader.ts:2` to expose composables to + agent-authored apps; nothing under `app/` imports `useDraggable`. +- Taskbar with App Launcher, chat toggle and clock -> `Taskbar.vue` renders a + `DOMinic` label plus one button per window (`Taskbar.vue:19`, `:21`); the + launcher is a floating top-right panel (`app/app.vue:142`), the clock a bare + `<span>` above the taskbar (`app/app.vue:178`); there is no dedicated + chat toggle, only the chat window's generic taskbar button, which + `onTaskbarClick` minimizes or restores (`Taskbar.vue:6`). +- `backdrop-blur-md` -> `backdrop-blur` (`WindowFrame.vue:54`, + `Taskbar.vue:18`, `app/app.vue:142`). +- Desktop wallpaper support -> a Tailwind gradient + (`bg-gradient-to-br from-slate-950 via-indigo-950 to-slate-900`) with a + faded `DOMinic` heading (`app/app.vue:114`, `:117`); no image wallpaper. +- Settings as a pre-installed registry app -> a hard-coded builtin branched on + `win.appId === 'settings'` (`app/app.vue:55`, `:124`), launched from a + dashed button under the Launch panel (`app/app.vue:170`). + +**Open question outcome:** + +- Chat is a standard draggable window opened once at mount by + `openBuiltin('chat')` (`app/app.vue:31`, `:57`) with the normal `topZ + 1` + (`app/features/os/stores/os.ts:39`); no high z-index, dock icon or drawer. + +Reconciled against main on 2026-09-12; the decision text above is unchanged. + +## Pros and Cons of the Options + +### Full windowing shell with taskbar + +- ✅ Good, because the OS metaphor carries the product identity. +- ✅ Good, because window and app lifecycle have one natural home. +- ❌ Bad, because windowing chrome wastes space on phone viewports + unless actively degraded. + +### Single-activity mobile-style shell + +- ✅ Good, because it is the natural fit for small screens. +- ❌ Bad, because it abandons the desktop metaphor that defines the + product. + +### Hybrid desktop shell with mobile taskbar + +- ✅ Good, because it keeps the desktop metaphor while degrading + cleanly. +- ✅ Good, because one shell serves both form factors. +- ❌ Bad, because two layout modes must both be tested. + +## Links + +- Related to [[ADR 0000 - Record architecture decisions|ADR 0000]] +- Related to [[ADR 0001 - Feature slices with domain-driven organization|ADR 0001]] +- Related to [[ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy|ADR 0005]] +- Related to [[ADR 0006 - Agent-authored runtime-compiled components|ADR 0006]] diff --git a/vault/ADRs/ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy.md b/vault/ADRs/ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy.md new file mode 100644 index 0000000..8acf4d1 --- /dev/null +++ b/vault/ADRs/ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy.md @@ -0,0 +1,165 @@ +--- +type: Architecture Decision Record +title: Agent chat via Vercel AI SDK with server-side provider proxy +description: Chat runs through a Nuxt /api/chat route on the Vercel AI SDK; provider keys stay browser-side and travel per-request. +status: accepted +tags: [architecture, agent] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0005" +--- + +# Agent chat via Vercel AI SDK with server-side provider proxy + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0005 | accepted | 2026-09-12 | Zack | + +## Technical Story + +Chat is the primary agent surface of DOMinic: the user talks, the +agent answers and authors applications. The OS must support virtually +any LLM provider without code changes and must never hold provider +credentials at rest on a server. + +## Context and Problem Statement + +Provider APIs differ in wire format, streaming protocol, and auth. +Hand-rolling adapters for each provider is a maintenance tax. But a +pure server-side integration would require the server to store API +keys, which we refuse: the user's keys should live in their browser +and be used per-request. + +## Decision Drivers + +- Provider-agnostic: adding a provider must be configuration, not + new protocol code. +- Streaming responses to the chat UI. +- Keys never at rest on the server. +- No-provider onboarding: a fresh install must guide the user to + configure a provider rather than fail. + +## Considered Options + +- Server-side proxy with server-held provider keys +- Bring-your-own-key direct from the browser +- Vercel AI SDK via a Nuxt server route with per-request keys + +## Decision Outcome + +Chosen option: **Vercel AI SDK (`ai` + provider packages) consumed +through a Nuxt server route `/api/chat` that streams responses**, +because the SDK normalizes every major provider behind one interface, +streams natively, and the server route keeps provider keys usable +while never persisting them. + +### Hackathon POC (Golden Path) + +For the hackathon POC, the chat integration provides a reliable, +low-latency app creation loop: + +- **Nuxt Server Route**: `/api/chat` receives user messages, system + prompt context, and the ephemeral `apiKey` passed per-request from the + client's Settings store. +- **Vercel AI SDK**: Uses `streamText` with typed tools (`install_app`, + `update_app` defined in ADR 0010). The agent can speak naturally while + emitting structured code blocks through tools. +- **Curated Providers**: Targets OpenAI (GPT-4o) and Anthropic + (Claude 3.5 Sonnet) as primary demo engines due to superior Vue SFC + generation quality. +- **Client Chat UI**: Renders streaming markdown text, displays + interactive tool execution pills ("Generating App...", "Installing + to Desktop..."), and triggers confetti on successful installation. + +### Future / Out of Scope for POC + +- Automated multi-provider fallback and load balancing. +- Encrypted local credential vault with master password. +- Multi-turn autonomous tool loops (agent opening terminal, running tests + recursively before presenting app). + +### Open Questions + +- **OPEN QUESTION: App Context Window**: How much existing app source + should be injected into chat prompts? (Recommendation for POC: Inject + the metadata of all installed apps in system context; inject full source + only when updating a specific target app). + +### Confirmation + +A chat round-trip against a configured provider streams token-by-token; +the server holds no key material after a request completes. + +### As built (2026-09-12) + +**Matches the golden path:** + +- `server/api/chat.post.ts:8` is the Nuxt `/api/chat` route running + `streamText` with `install_app`/`update_app` (`server/api/chat.post.ts:67`) + on `ai@^4.3.19` (`package.json:35`). +- The key travels per request from the Settings store (`app/app.vue:123`, + `app/features/chat/composables/useAgentChat.ts:88`); tool pills render + `Executing`/`Mounted` (`app/features/chat/components/ChatWindow.vue:80`). + +**Differs from this record:** + +- Keys never at rest on the server -> `server/api/chat.post.ts:27` falls + back to `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, + `GOOGLE_GENERATIVE_AI_API_KEY` or `DEEPSEEK_API_KEY` when no key is sent. +- Two curated providers -> `'openai' | 'anthropic' | 'google' | 'deepseek'` + (`app/features/chat/types/chat.ts:20`); DeepSeek is `createOpenAI` with a + `baseURL` (`server/api/chat.post.ts:51`). +- GPT-4o / Claude 3.5 Sonnet -> `gpt-5`, `claude-sonnet-5`, `gemini-2.5-pro`, + `deepseek-chat` (`server/api/chat.post.ts:46`, `SettingsApp.vue:124`). +- `result.toDataStreamResponse()` (`server/api/chat.post.ts:86`) is read by a + hand-rolled `0:`/`9:`/`e:` prefix parser (`useAgentChat.ts:121`); tools have + no `execute`, so `maxSteps: 3` (`server/api/chat.post.ts:83`) never loops; + `handleToolCall` (`useAgentChat.ts:166`) runs them client-side. +- Streaming markdown -> plain `{{ msg.content }}` (`ChatWindow.vue:46`); pills + say `Installing Application`/`Updating Application` (`ChatWindow.vue:64`); + no confetti fires on install (`app/features/apps/runner/loader.ts:20`). +- No-provider onboarding -> a 401 `No API key provided for ${provider}...` + (`server/api/chat.post.ts:38`) is appended as `*(Error: ...)*` to the reply + (`useAgentChat.ts:160`); `hasApiKey` (`useAgentChat.ts:28`) is unused; the + key is plain text in `app/features/settings/stores/settings.ts:50`. + +**Open question outcome:** + +- Metadata half adopted: `buildSystemPrompt` lists each app's `id`, `title`, + `icon`, `description` (`app/features/chat/prompts/systemPrompt.ts:9`); no + source is ever injected; `update_app` resends `vueSfcCode` (`schemas.ts:33`). + +Reconciled against main on 2026-09-12; the decision text above is unchanged. + +## Pros and Cons of the Options + +### Server-side proxy with server-held provider keys + +- ✅ Good, because the browser never sees a provider credential. +- ❌ Bad, because the server stores third-party secrets at rest, + a liability we refuse. + +### Bring-your-own-key direct from the browser + +- ✅ Good, because the server never touches keys at all. +- ❌ Bad, because every provider's streaming protocol must be + re-implemented client-side and CORS policies constrain which + providers work at all. + +### Vercel AI SDK via a Nuxt server route with per-request keys + +- ✅ Good, because one unified interface covers the provider + ecosystem, including streaming and tool calls. +- ✅ Good, because keys transit per-request and are never at rest + on the server. +- ❌ Bad, because a key still transits the server in memory for the + duration of a request. + +## Links + +- Related to [[ADR 0000 - Record architecture decisions|ADR 0000]] +- Related to [[ADR 0004 - DOMinic OS shell and taskbar|ADR 0004]] +- Related to [[ADR 0007 - Virtual filesystem with pluggable storage drivers|ADR 0007]] diff --git a/vault/ADRs/ADR 0006 - Agent-authored runtime-compiled components.md b/vault/ADRs/ADR 0006 - Agent-authored runtime-compiled components.md new file mode 100644 index 0000000..de2e517 --- /dev/null +++ b/vault/ADRs/ADR 0006 - Agent-authored runtime-compiled components.md @@ -0,0 +1,165 @@ +--- +type: Architecture Decision Record +title: Agent-authored runtime-compiled components +description: The agent's Vue SFC applications compile at runtime with vue3-sfc-loader and register on a DominicApp contract. +status: accepted +tags: [architecture, agent] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0006-agent-authored-runtime-compiled-components.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0006" +--- + +# Agent-authored runtime-compiled components + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0006 | accepted | 2026-09-12 | Zack | + +## Technical Story + +The agent authors Vue single-file component applications from chat +requests. Users expect to install, preview, and run those apps +immediately — with no per-component build step, no bundler run, and +no redeploy of the OS itself. + +## Context and Problem Statement + +A traditional Vue app compiles at build time; DOMinic's apps are born +at runtime. They must compile in the browser, integrate with the +window manager as ordinary apps, persist their source, and be +removable. Compile-in-an-iframe would isolate them but would wall +them off from OS state and styling. + +## Decision Drivers + +- No build infrastructure per component. +- A live install-and-preview loop from chat. +- Reuse of the Vue ecosystem (components load dependencies, ADR 0008). +- Apps and widgets register through the same contract as first-party + apps. + +## Considered Options + +- Runtime-compiled SFC (vue3-sfc-loader), in-process +- Sandboxed iframe compile +- Prebuilt bundles delivered per app + +## Decision Outcome + +Chosen option: **runtime-compiled SFC via `vue3-sfc-loader`, in +process**, because it compiles agent-authored SFCs directly in the +browser with no per-app infrastructure and keeps compiled apps first- +class OS citizens. + +### Hackathon POC (Golden Path) + +For the hackathon POC, runtime compilation delivers an instant +compile-and-render loop directly in the browser: + +- **DynamicAppRunner**: A wrapper component compiles raw Vue SFC strings + using `vue3-sfc-loader` and mounts the result dynamically via + `<component :is="compiledComponent" />`. +- **Pre-injected Ecosystem**: The `loadModule` callback of `vue3-sfc-loader` + immediately resolves `vue`, `@vueuse/core`, and standard icon sets + (`lucide-vue-next`) from in-memory host bundles with zero network latency. +- **Tailwind Utility Styling**: Host Tailwind CSS styles are directly + accessible inside the component templates, enabling rich UIs with zero + custom CSS overhead. +- **Error Boundary**: A Vue `onErrorCaptured` boundary wraps every running + component. If an agent-authored app throws a render or syntax error, + the window displays a clean error card with an "Ask Agent to Fix" button + that feeds the error trace back into the chat. +- **DominicApp Contract**: Governed by ADR 0010. + +### Future / Out of Scope for POC + +- Sandboxed `<iframe>` compile isolation with `postMessage` bridge. +- Web Worker compilation and off-thread parsing. +- Hot-module state preservation across component updates (POC re-mounts + the updated component). + +### Open Questions + +- **OPEN QUESTION: Global CSS Pollution**: Could an agent write unscoped + `<style>` tags that break OS shell styling? (Recommendation for POC: + System prompt instructs the agent to use Tailwind utility classes or + `<style scoped>`). + +### Confirmation + +An agent-authored SFC installs from chat, appears in the app +registry, launches as a window, and survives reload via its VFS +source. + +### As built (2026-09-12) + +**Matches the golden path:** + +- `DynamicAppRunner` compiles its `sourceCode` prop through `compileVueSfc`, + a `vue3-sfc-loader` `loadModule` wrapper, and mounts the result via + `<component :is>` (`app/features/apps/runner/loader.ts:42`, + `app/features/apps/runner/DynamicAppRunner.vue:48`). +- `moduleCache` pre-resolves `vue`, `@vueuse/core` and `lucide-vue-next` from + host bundles (`app/features/apps/runner/loader.ts:16`). +- Runtime errors hit `onErrorCaptured` (`DynamicAppRunner.vue:100`, returns + `false`); compile errors hit the `catch` in `loadComponent` (line 91). Both + show the card whose "Ask Agent to Fix" emits `askFix`; `app.vue` forwards + it to `chat.sendMessage` (`app/app.vue:104`). +- Updates re-mount: `installApp`/`updateApp` bump `sourceVersion[params.id]`, + part of the runner's `:key` (`app/app.vue:94`, `app/app.vue:131`). + +**Differs from this record:** + +- `canvas-confetti` is also in `moduleCache`, and unknown imports are fetched + unvetted from `https://esm.sh/<url>?bundle` (`loader.ts:20`, `loader.ts:27`). +- Agent-only Tailwind classes resolve through the Play CDN script added in + `nuxt.config.ts:32`, not the host build's bundle alone. +- The runner passes `app-id` and `window-id` to the compiled app + (`DynamicAppRunner.vue:50`); the prompt contract never mentions them. +- The error card adds a "Retry" recompile button (`DynamicAppRunner.vue:41`); + `askFix` emits `sourceCode` but `app/app.vue:105` sends only id and error. +- `addStyle` appends a `<style data-app-id>` to `document.head`; nothing removes + it on update, unmount or uninstall (`app/features/apps/runner/loader.ts:34`). +- Fixture path the ADR omits: `installFixture` writes `apps/<id>/index.vue` + into VFS and calls `registerApp` (`app/features/apps/fixtures/index.ts:31`); + `app/app.vue:43` exposes it as `window.__dominic.installFixture`. + +**Open question outcome:** + +- Resolved by prompt only: the contract mandates Tailwind utilities and allows + `<style scoped>` for keyframes (`app/features/chat/prompts/systemPrompt.ts:33` + and `systemPrompt.ts:25`); unscoped styles still inject globally and are + never removed (`loader.ts:34`). + +Reconciled against main on 2026-09-12; the decision text above is unchanged. + +## Pros and Cons of the Options + +### Runtime-compiled SFC (vue3-sfc-loader), in-process + +- ✅ Good, because zero per-app build step keeps the live loop fast. +- ✅ Good, because compiled apps share OS state and styling directly. +- ❌ Bad, because a crashing app shares the page with the OS (iframe + isolation remains the escape hatch). + +### Sandboxed iframe compile + +- ✅ Good, because crashes and bad CSS are contained. +- ❌ Bad, because postMessage bridging for state, storage, and + styling is heavy and erodes the app contract. + +### Prebuilt bundles delivered per app + +- ✅ Good, because runtime cost is minimal. +- ❌ Bad, because it requires per-app build infrastructure the + product explicitly avoids. + +## Links + +- Related to [[ADR 0000 - Record architecture decisions|ADR 0000]] +- Related to [[ADR 0004 - DOMinic OS shell and taskbar|ADR 0004]] +- Related to [[ADR 0007 - Virtual filesystem with pluggable storage drivers|ADR 0007]] +- Related to [[ADR 0008 - Runtime NPM dependency loading via esm.sh with vetting|ADR 0008]] diff --git a/vault/ADRs/ADR 0007 - Virtual filesystem with pluggable storage drivers.md b/vault/ADRs/ADR 0007 - Virtual filesystem with pluggable storage drivers.md new file mode 100644 index 0000000..e721ccf --- /dev/null +++ b/vault/ADRs/ADR 0007 - Virtual filesystem with pluggable storage drivers.md @@ -0,0 +1,160 @@ +--- +type: Architecture Decision Record +title: Virtual filesystem with pluggable storage drivers +description: A single VirtualFileSystem interface over localStorage and IndexedDB drivers, chosen per path via VFS metadata. +status: accepted +tags: [architecture, storage] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0007-virtual-filesystem-with-pluggable-storage-drivers.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0007" +--- + +# Virtual filesystem with pluggable storage drivers + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0007 | accepted | 2026-09-12 | Zack | + +## Technical Story + +OS alterations persist locally: agent-authored component source, +settings, provider API keys, and the runtime dependency cache all +need a filesystem-shaped home in the browser. localStorage alone +caps out around 5 MB, far too small for a growing component library. + +## Context and Problem Statement + +Storage needs differ by payload: settings and small files want the +simplicity of synchronous localStorage, while component libraries +need IndexedDB's capacity and async bulk reads. Callers should not +know or care which backend holds a given path. + +## Decision Drivers + +- Local-first persistence with no server round-trip. +- Variable payload sizes across very different data kinds. +- Synchronous ergonomics for small data, async capacity for large. +- Testability: the backend must be substitutable in tests. + +## Considered Options + +- Single `VirtualFileSystem` interface with driver registry +- localStorage JSON tree only +- IndexedDB-backed filesystem only + +## Decision Outcome + +Chosen option: **a single `VirtualFileSystem` interface plus a driver +registry**, because it hides the storage split behind one API while +letting each path use the backend that fits its payload. + +### Hackathon POC (Golden Path) + +To guarantee reliable persistence across page reloads without the +overhead of managing multi-driver synchronization tables during the +hackathon sprint: + +- **Unified VFS Interface**: Callers use an async `VirtualFileSystem` + contract (`readFile(path)`, `writeFile(path, content)`, `deleteFile(path)`, + `listFiles(directory)`). Making methods return Promises ensures future + driver swaps require zero caller refactoring. +- **Single Storage Backend**: Implemented using `localStorage` (prefixed + keys) for maximum simplicity, zero runtime dependencies, and instant + boot hydration. The 5 MB browser quota easily stores dozens of Vue SFC + components and JSON manifests for the demo. +- **Canonical Path Conventions**: + - `/system/settings.json`: Provider API keys, model selections. + - `/apps/<app-id>/index.vue`: Runtime component source code. + - `/apps/<app-id>/manifest.json`: Title, icon, timestamps, author. +- **Boot Hydration**: On OS boot, the app registry reads `/apps/` and + re-registers all installed applications into the taskbar and launcher. + +### Future / Out of Scope for POC + +- Dynamic per-path driver routing table between localStorage and + IndexedDB. +- Origin Private File System (OPFS) driver for binary files. +- Hierarchical POSIX permissions, directory watchers, and quota + warning events. + +### Open Questions + +- **OPEN QUESTION: Storage Reset Utility**: Should a one-click "Reset OS" + button be provided in Settings to flush VFS during testing? + (Recommendation for POC: Yes, add a clear-storage utility button in + the Settings app). + +### Confirmation + +Unit tests run the same VFS suite against each driver; the OS boots +and restores component source and settings from the VFS after a +reload. + +### As built (2026-09-12) + +**Matches the golden path:** + +- One localStorage backend with prefixed keys: `VFS_PREFIX` is + `'dominic:vfs:'` (`app/features/shared/vfs.ts:1`); a leading slash is + stripped by `normalizePath` (`app/features/shared/vfs.ts:7`). +- `readFile`, `writeFile`, `deleteFile`, `listFiles` are named exports + (`app/features/shared/vfs.ts:11`). +- Settings persist at `system/settings.json` + (`app/features/settings/stores/settings.ts:14`); app source at + `apps/<id>/index.vue` (`app/app.vue:90`). +- Boot hydration: `onMounted` runs `settings.hydrate()` then + `apps.installed = hydrateRegistry()` (`app/app.vue:29`). +- Window state is not persisted; `useOsStore` keeps `windows` in a + plain `ref` (`app/features/os/stores/os.ts:22`). + +**Differs from this record:** + +- Async `VirtualFileSystem` interface -> synchronous module functions, + no interface or driver registry; `readFile` returns `string | null` + (`app/features/shared/vfs.ts:21`). +- `/apps/<app-id>/manifest.json` per app -> a single `registry.json` + holding an `AppMeta[]` (`app/features/apps/registry/registry.ts:14`); + no per-app manifest is written to the VFS. +- Registry reads `/apps/` -> `hydrateRegistry` parses `registry.json` + only and never lists the `apps/` directory + (`app/features/apps/registry/registry.ts:20`). +- Unit tests per driver -> no test files exist in the repo. + +**Open question outcome:** + +- Resolved yes: the `SettingsApp.vue` "System Danger Zone" button calls + `settingsStore.resetOs()` + (`app/features/settings/components/SettingsApp.vue:161`), which runs + `clearVfs()` and reloads (`app/features/settings/stores/settings.ts:64`); + `clearVfs` strips every `'dominic:'` key (`app/features/shared/vfs.ts:61`). + +Reconciled against main on 2026-09-12; the decision text above is unchanged. + +## Pros and Cons of the Options + +### Single `VirtualFileSystem` interface with driver registry + +- ✅ Good, because callers are backend-agnostic per path. +- ✅ Good, because tests can swap drivers freely. +- ❌ Bad, because the metadata-per-path mapping adds a small + coordination cost. + +### localStorage JSON tree only + +- ✅ Good, because synchronous reads are trivially simple. +- ❌ Bad, because the ~5 MB ceiling starves component libraries. + +### IndexedDB-backed filesystem only + +- ✅ Good, because capacity and bulk reads are excellent. +- ❌ Bad, because its async-only API burdens tiny settings reads. + +## Links + +- Related to [[ADR 0000 - Record architecture decisions|ADR 0000]] +- Related to [[ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy|ADR 0005]] +- Related to [[ADR 0006 - Agent-authored runtime-compiled components|ADR 0006]] +- Related to [[ADR 0008 - Runtime NPM dependency loading via esm.sh with vetting|ADR 0008]] diff --git a/vault/ADRs/ADR 0008 - Runtime NPM dependency loading via esm.sh with vetting.md b/vault/ADRs/ADR 0008 - Runtime NPM dependency loading via esm.sh with vetting.md new file mode 100644 index 0000000..f65fa31 --- /dev/null +++ b/vault/ADRs/ADR 0008 - Runtime NPM dependency loading via esm.sh with vetting.md @@ -0,0 +1,163 @@ +--- +type: Architecture Decision Record +title: Runtime NPM dependency loading via esm.sh with vetting +description: Agent apps load npm packages from esm.sh only after an automated vetting gate; vetted metadata and builds cache in the VFS. +status: accepted +tags: [architecture, agent, storage] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0008-runtime-npm-dependency-loading-via-esm-sh-with-vetting.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0008" +--- + +# Runtime NPM dependency loading via esm.sh with vetting + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0008 | accepted | 2026-09-12 | Zack | + +## Technical Story + +Agent-authored components import arbitrary NPM packages at runtime. +The browser cannot run a bundler per app, so packages must arrive as +ready ESM — but arbitrary code execution from a CDN is exactly the +surface we must gate behind review. + +## Context and Problem Statement + +The full npm ecosystem should be reachable, including CommonJS +packages and subpath imports, without a server build step. At the +same time, executing third-party code in the user's browser demands +an automated security review before anything loads. + +## Decision Drivers + +- Full npm ecosystem reach, including CJS transpilation and + subpath imports. +- No server build step. +- Security review before execution, not after. +- Offline resilience through caching. + +## Considered Options + +- esm.sh with automated vetting +- unpkg/jsDelivr with a runtime resolver +- Server-side pre-bundling per app + +## Decision Outcome + +Chosen option: **esm.sh as the ESM CDN, gated by automated vetting**, +because it serves the whole npm ecosystem (CJS transpile, subpaths, +GitHub refs) as ready ESM with zero server build, while the vetting +gate satisfies the security driver. + +### Hackathon POC (Golden Path) + +Building an automated multi-signal security vetting engine (querying +npm registry APIs, advisory databases, and maintainer signals) is +prohibitive within a hackathon timeframe. The POC implements direct +loading with pre-provisioned essentials: + +- **Direct esm.sh Resolver**: The `vue3-sfc-loader` `loadModule` handler + maps unrecognized package specifiers directly to + `https://esm.sh/${specifier}?bundle`. +- **Pre-provisioned Host Libraries**: Key libraries are mapped + directly to in-memory host instances to avoid any network round-trip: + - `vue`: Bound to the running Vue 3 instance. + - `@vueuse/core`: Essential reactive utilities. + - `lucide-vue-next`: Crisp icons for apps and controls. + - `canvas-confetti`: Instant visual feedback for demo apps. +- **System Prompt Guardrails**: The agent system prompt directs the LLM + to rely on the pre-provisioned set and browser-safe ESM packages, + avoiding Node.js built-ins (`fs`, `child_process`). + +### Future / Out of Scope for POC + +- Automated vetting gate querying npm advisory databases (audit), weekly + download thresholds, license compliance, and package age. +- Local VFS caching of resolved ESM vendor bundles for offline + execution. +- Static import-analysis sandbox gating. + +### Open Questions + +- **OPEN QUESTION: Version Pinning**: Should the agent be instructed to + pin dependency versions (e.g. `canvas-confetti@1.9.3`) or use bare + names? (Recommendation for POC: Bare names or `@latest` let esm.sh + resolve quickly; pre-provisioned libraries bypass resolution + entirely). + +### Confirmation + +A component importing a vetted package loads and runs; a package +failing the vetting gate is refused with its reasons surfaced. + +### As built (2026-09-12) + +**Matches the golden path:** + +- `compileVueSfc` hands `vue3-sfc-loader` a `moduleCache` binding `vue`, + `@vueuse/core`, `lucide-vue-next` and `canvas-confetti` to host imports, + so those four skip the network (`app/features/apps/runner/loader.ts:16`). +- Every other specifier is fetched from `https://esm.sh/${url}?bundle`; a + non-OK response throws `Failed to load external dependency` + (`app/features/apps/runner/loader.ts:27`). +- The system prompt advertises the host imports and forbids Node built-ins + (`fs`, `path`, `child_process`, `process`) + (`app/features/chat/prompts/systemPrompt.ts:36`). +- No vetting gate and no VFS dependency cache exist; `vfs.ts` is a + localStorage store the loader never calls (`app/features/shared/vfs.ts:11`). + +**Differs from this record:** + +- ADR: the `loadModule` handler resolves specifiers -> main: resolution lives + in the `getFile` option and every non-`http` path, relative imports + included, is rewritten to esm.sh (`app/features/apps/runner/loader.ts:27`). +- ADR: the prompt steers the LLM to "browser-safe ESM packages" -> main: the + prompt never mentions esm.sh or npm, only the host set, and names Lucide + without its `lucide-vue-next` specifier + (`app/features/chat/prompts/systemPrompt.ts:30`). +- ADR: silent on styling -> main: classes in agent SFCs resolve through the + Tailwind play CDN `https://cdn.tailwindcss.com` (`nuxt.config.ts:32`), a + second unpinned CDN dependency; build-time content globs scan only + `app/**` and `features/**` `*.{vue,ts,txt}` files (`nuxt.config.ts:17`). +- ADR "Confirmation": a refused package surfaces vetting reasons -> main: + only fetch failures surface, via the runner's error state + (`app/features/apps/runner/DynamicAppRunner.vue:93`). + +**Open question outcome:** + +- Version pinning: not addressed. The prompt gives no pinning guidance and + the loader forwards specifiers verbatim, so bare names resolve to esm.sh + latest; only the host copies carry semver ranges (`package.json:36`). + +Reconciled against main on 2026-09-12; the decision text above is unchanged. + +## Pros and Cons of the Options + +### esm.sh with automated vetting + +- ✅ Good, because CJS transpilation and subpath support just work. +- ✅ Good, because no server bundling is needed, ever. +- ❌ Bad, because first request pays esm.sh build latency. +- ❌ Bad, because CDN availability becomes a runtime dependency. + +### unpkg/jsDelivr with a runtime resolver + +- ✅ Good, because the file CDNs are stable and fast. +- ❌ Bad, because bare-specifier resolution must be re-implemented + client-side and CJS support is incomplete. + +### Server-side pre-bundling per app + +- ✅ Good, because bundles are optimized and auditably pinned. +- ❌ Bad, because a server build pipeline contradicts the + no-build-infra requirement and adds deploy latency per app. + +## Links + +- Related to [[ADR 0000 - Record architecture decisions|ADR 0000]] +- Related to [[ADR 0006 - Agent-authored runtime-compiled components|ADR 0006]] +- Related to [[ADR 0007 - Virtual filesystem with pluggable storage drivers|ADR 0007]] diff --git a/vault/ADRs/ADR 0009 - CORS-first networking with Chrome-masking proxy fallback.md b/vault/ADRs/ADR 0009 - CORS-first networking with Chrome-masking proxy fallback.md new file mode 100644 index 0000000..7f81bb1 --- /dev/null +++ b/vault/ADRs/ADR 0009 - CORS-first networking with Chrome-masking proxy fallback.md @@ -0,0 +1,153 @@ +--- +type: Architecture Decision Record +title: CORS-first networking with Chrome-masking proxy fallback +description: External calls fetch directly when CORS allows and fall back transparently to a generic proxy that presents a latest-stable Chrome User-Agent. +status: accepted +tags: [architecture, platform] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0009-cors-first-networking-with-chrome-masking-proxy-fallback.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0009" +--- + +# CORS-first networking with Chrome-masking proxy fallback + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0009 | accepted | 2026-09-12 | Zack | + +## Technical Story + +Components and the agent must reach external services — public APIs, +web pages, feeds — that may or may not send CORS headers. The OS +needs one networking story that prefers the browser's direct path +and degrades gracefully when a service blocks cross-origin reads. + +## Context and Problem Statement + +Direct browser requests are fast, cache-friendly, and credential- +correct, but fail entirely when a service lacks CORS headers. +A server proxy can bypass CORS, but routing every request through +the server wastes latency where direct fetch would work. + +## Decision Drivers + +- Prefer direct browser requests wherever CORS allows. +- Bypass CORS gaps transparently, without per-service code. +- Avoid anti-bot rejection on proxied requests. +- Keep the server surface to one generic route. + +## Considered Options + +- Single generic proxy route with CORS-first fallback +- Domain allowlist proxy +- Per-service proxy routes + +## Decision Outcome + +Chosen option: **CORS-first direct `fetch` with transparent fallback +to a single generic `/api/proxy` Nuxt server route**, because it +keeps the fast path direct and confines all CORS workarounds to one +reviewable server surface. + +### Hackathon POC (Golden Path) + +To avoid complex networking middleware during the hackathon: + +- **Direct Fetch by Default**: Components use standard browser `fetch` + directly against CORS-enabled public APIs (such as Open-Meteo for + weather or CoinGecko for crypto). +- **Simple Proxy Escape Hatch**: A minimal Nuxt server route + `/api/proxy?url=<encoded>` is provided. If an external API lacks + CORS headers, the component calls `/api/proxy?url=...`. +- **Deferred Heuristics**: Automatic trial-and-fallback logic and + User-Agent spoofing headers are deferred. + +### Future / Out of Scope for POC + +- Transparent automatic fallback composable (`useSmartFetch`) that tests + CORS and retries behind the scenes. +- Chrome User-Agent spoofing and header scrubbing engine. +- SSRF defense filters and IP range blocklists. + +### Open Questions + +- **OPEN QUESTION: Public Deployment Security**: If DOMinic is hosted + publicly for the hackathon presentation (e.g. Vercel), should the proxy + block private IP ranges (e.g. `127.0.0.1`, `169.254.169.254`)? + (Recommendation: A simple regex disallowing private IPs prevents basic + SSRF during judging). + +### Confirmation + +A CORS-enabled endpoint loads directly (visible in network tools); +a CORS-blocking endpoint transparently returns content through +`/api/proxy`. + +### As built (2026-09-12) + +**Matches the golden path:** + +- Generated apps reach the network with plain browser `fetch`; the system + prompt lists `fetch` among the allowed standard browser APIs + (`app/features/chat/prompts/systemPrompt.ts:36`). +- No `useSmartFetch` composable, User-Agent spoofing, or header scrubbing + exists on main; those items stayed deferred as this record planned. + +**Differs from this record:** + +- ADR: a minimal `/api/proxy?url=<encoded>` Nuxt route exists as an escape + hatch -> main: `server/api/` holds only `chat.post.ts`; no proxy route was + built (`server/api/chat.post.ts:8`). +- ADR: components call `/api/proxy?url=...` when CORS is missing -> main: + the prompt never mentions a proxy, so agent-authored apps have no fallback + and a CORS-blocked endpoint simply fails + (`app/features/chat/prompts/systemPrompt.ts:36`). +- ADR: direct fetch targets public data APIs -> main: the only direct + cross-origin fetch in OS code is `getFile` in `compileVueSfc`, which + passes absolute `http` import URLs through unchanged and rewrites bare + specifiers to `https://esm.sh/${url}?bundle` before calling `fetch` + (`app/features/apps/runner/loader.ts:27`). +- ADR: Confirmation shows a CORS-blocking endpoint returning through + `/api/proxy` -> main: the `pomodoro-timer` fixture imports only `vue` and + makes no network calls, so the demo exercises neither path + (`app/features/apps/fixtures/pomodoro-timer/index.vue.txt:94`). +- ADR: one generic server route -> main: the sole server route is the LLM + relay, which forwards a client-supplied `baseUrl` to the provider SDK + (`server/api/chat.post.ts:53`). + +**Open question outcome:** + +- Not addressed: with no proxy route there is no private-IP regex to add; the + client-supplied `baseUrl` is passed to `createOpenAI` unfiltered in both + the `deepseek` and default `openai` branches (`server/api/chat.post.ts:53`, + `server/api/chat.post.ts:60`) and is the only client-steerable outbound + target on the server. + +Reconciled against main on 2026-09-12; the decision text above is unchanged. + +## Pros and Cons of the Options + +### Single generic proxy route with CORS-first fallback + +- ✅ Good, because the fast path stays direct and cache-friendly. +- ✅ Good, because one generic route is easy to review and harden. +- ❌ Bad, because proxied traffic adds server bandwidth cost. + +### Domain allowlist proxy + +- ✅ Good, because abuse surface shrinks to known hosts. +- ❌ Bad, because agent-authored apps cannot reach unlisted + services without an OS change. + +### Per-service proxy routes + +- ✅ Good, because each route can carry bespoke transformation. +- ❌ Bad, because server code grows with every new integration. + +## Links + +- Related to [[ADR 0000 - Record architecture decisions|ADR 0000]] +- Related to [[ADR 0004 - DOMinic OS shell and taskbar|ADR 0004]] diff --git a/vault/ADRs/ADR 0010 - Agent app interface and tool protocol.md b/vault/ADRs/ADR 0010 - Agent app interface and tool protocol.md new file mode 100644 index 0000000..593b1f9 --- /dev/null +++ b/vault/ADRs/ADR 0010 - Agent app interface and tool protocol.md @@ -0,0 +1,178 @@ +--- +type: Architecture Decision Record +title: Agent app interface and tool protocol +description: Structured tool calling and component contract for agent-authored applications. +status: accepted +tags: [architecture, agent] +generated: { by: agent/pi, at: 2026-09-12T00:00:00Z } +verified: { by: human:charles, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0010-agent-app-interface-and-tool-protocol.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0010" +--- + +# Agent app interface and tool protocol + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0010 | accepted | 2026-09-12 | Zack, Charles | + +## Technical Story + +DOMinic turns conversational requests into running applications. The +agent needs an unambiguous protocol to package, install, and update +Vue Single File Components, and the OS needs a strict component contract +to host them safely inside window frames. + +## Context and Problem Statement + +When LLMs generate code via free-form chat, they produce unformatted +markdown, conflicting props, or broken script tags that require messy +client-side regex parsing. To make app installation deterministic, the +system must use structured tool calling and a standardized base +contract (`DominicApp`). + +## Decision Drivers + +- Deterministic app delivery (structured parameters, not raw chat text). +- Predictable runtime contract for mounted components. +- Support for both new app creation and in-place app mutation. +- Instant integration with window manager and virtual filesystem. + +## Considered Options + +- Free-form markdown parsing (regex extraction of ```vue code blocks) +- Structured tool calling via Vercel AI SDK (`install_app`, `update_app`) +- External backend webhook compilation pipeline + +## Decision Outcome + +Chosen option: **Structured tool calling via Vercel AI SDK paired with +the `DominicApp` component contract**, because it guarantees schema +validation, enables direct UI state transitions, and avoids flaky text +parsing. + +### Hackathon POC (Golden Path) + +For the hackathon POC, the interaction loop is governed by two tools +and one component specification: + +#### 1. Tool Definitions (Vercel AI SDK) + +- **`install_app`**: + - `id`: kebab-case unique identifier (e.g. `pomodoro-timer`). + - `title`: Human-readable window title (e.g. `Pomodoro Timer`). + - `icon`: Icon identifier from `lucide-vue-next` (e.g. `Timer`). + - `description`: One-sentence summary of the app. + - `vueSfcCode`: Full Vue 3 SFC string (`<template>`, `<script setup>`, + optional `<style scoped>`). +- **`update_app`**: + - `id`: Identifier of the existing app to modify. + - `vueSfcCode`: Updated full Vue 3 SFC source. + - `summary`: Explanation of the changes made. + +#### 2. DominicApp Component Contract + +Agent-generated components must conform to: + +- **Standard SFC Layout**: Vue 3 `<template>` with `<script setup>`. +- **Props**: Receives `{ windowId: string, appId: string }`. +- **Styling**: Uses standard Tailwind CSS utility classes. +- **Available Imports**: Pre-resolved host instances of `vue` + (`ref`, `computed`, `watch`, `onMounted`), `@vueuse/core`, and + `lucide-vue-next`. +- **No Node.js Built-ins**: Browser-only APIs. + +#### 3. Execution Lifecycle + +1. Agent calls `install_app`. +2. OS writes files to VFS: `/apps/<id>/index.vue` and + `/apps/<id>/manifest.json`. +3. OS registers the app in `useOsStore`. +4. OS opens the newly installed app window automatically. +5. OS displays an installation badge in the chat thread. + +### Future / Out of Scope for POC + +- Granular permission requests (e.g., prompt for clipboard/location). +- Interactive code diff approval modal before updating an existing app. +- App-to-app IPC messaging bus. + +### Open Questions + +- **OPEN QUESTION: Installation Approval**: Should app installation + require manual human confirmation in chat? (Recommendation for POC: + Auto-install immediately upon tool call completion for maximum demo + flow and delight). + +### As built (2026-09-12) + +**Matches the golden path:** + +- `install_app` / `update_app` zod schemas carry the ADR's parameter names + (`app/features/chat/tools/schemas.ts:3`, `:29`) and are registered on + `streamText` (`server/api/chat.post.ts:71`). +- `vue`, `@vueuse/core`, `lucide-vue-next` come from `moduleCache` + (`app/features/apps/runner/loader.ts:16`); install opens the window + (`app/app.vue:60`) and the chat shows a per-call badge + (`app/features/chat/components/ChatWindow.vue:53`). + +**Differs from this record:** + +- Schema is stricter: `id` must match `/^[a-z0-9-]+$/` (`schemas.ts:6`), + `icon` defaults to `'Sparkles'` (`schemas.ts:15`), `title` max 50 chars. +- ADR: OS executes tools -> main: server declares them without `execute` + (`server/api/chat.post.ts:72`); the client `handleToolCall` runs the + `onInstallApp` / `onUpdateApp` / `onOpenWindow` callbacks of + `UseAgentChatOptions` (`app/features/chat/composables/useAgentChat.ts:5`, + `:166`), wired in `app/app.vue:123`. +- ADR: `index.vue` + `manifest.json`, register in `useOsStore` -> main: + `installApp` writes only `apps/<id>/index.vue` (`app/app.vue:90`); no + manifest; metadata goes to `registry.json` via `registerApp` + (`app/features/apps/registry/registry.ts:45`, `app/app.vue:92`) and + `apps.installed = listApps()` on `useAppsStore` (`app/app.vue:93`). +- ADR: `{ windowId, appId }` props -> main: + `app/features/apps/runner/DynamicAppRunner.vue:50` binds `:app-id` and + `:window-id` as attrs; generated SFCs do not declare them: the prompt + contract (`app/features/chat/prompts/systemPrompt.ts:23`) omits them. +- Imports: main also pre-resolves `canvas-confetti` (`loader.ts:20`) and + fetches other imports from `esm.sh` (`loader.ts:27`); ADR lists three. +- Tool results never reach the model: `invocation.result` is set locally + (`useAgentChat.ts:173`) and prior tool turns replay as the text + `(called <tool>)` (`useAgentChat.ts:82`); `summary` is unused. + +**Open question outcome:** + +- Installation approval: auto-install. `handleToolCall` runs `onInstallApp` + then `onOpenWindow` with no confirmation gate (`useAgentChat.ts:170`); + `update_app` also applies at once (`useAgentChat.ts:181`). + +Reconciled against main on 2026-09-12; the decision text above is unchanged. + +## Pros and Cons of the Options + +### Structured tool calling via Vercel AI SDK + +- ✅ Good, because parameters are validated against a strict schema. +- ✅ Good, because tool status triggers reactive UI transitions. +- ❌ Bad, because models must support reliable tool calling. + +### Free-form markdown parsing + +- ✅ Good, because works with any basic completion model. +- ❌ Bad, because code block regex parsing is fragile and fails on + nested formatting. + +### External backend webhook pipeline + +- ✅ Good, because offloads compilation to a heavy server. +- ❌ Bad, because contradicts DOMinic's client-first, zero-build-latency + architecture. + +## Links + +- Related to [[ADR 0004 - DOMinic OS shell and taskbar|ADR 0004]] +- Related to [[ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy|ADR 0005]] +- Related to [[ADR 0006 - Agent-authored runtime-compiled components|ADR 0006]] +- Related to [[ADR 0007 - Virtual filesystem with pluggable storage drivers|ADR 0007]] diff --git a/vault/ADRs/ADR 0011 - Node 24 LTS runtime standard.md b/vault/ADRs/ADR 0011 - Node 24 LTS runtime standard.md new file mode 100644 index 0000000..d53d468 --- /dev/null +++ b/vault/ADRs/ADR 0011 - Node 24 LTS runtime standard.md @@ -0,0 +1,101 @@ +--- +type: Architecture Decision Record +title: Node 24 LTS runtime standard +description: Node 24 LTS is the sole supported runtime, pinned via .nvmrc and enforced in engines and CI. +status: accepted +tags: [tooling, node] +generated: { by: agent/opencode-glm, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: docs/adrs/0011-node-24-lts-runtime-standard.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0011" +--- + +# Node 24 LTS runtime standard + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0011 | accepted | 2026-09-12 | Zack | + +## Technical Story + +The repository had no declared Node version. Local development +followed whatever a contributor had installed, CI pinned Node 22 in +the workflow file, and nothing told version managers (nvm, fnm, Volta) +what to use. As DOMinic grows a Nuxt app with runtime-compiled agent +code, an undeclared runtime becomes a source of avoidable +environment drift. + +## Context and Problem Statement + +- Node 24 is the active LTS line (maintenance begins 2026-10, end of + life 2028-04), while Node 22 enters maintenance and loses novelty + features the platform may want. +- Tooling that reads `.nvmrc` (nvm, fnm, Volta, `actions/setup-node` + with `node-version-file`) cannot be pointed at `package.json`. +- Divergent Node versions between local machines and CI produce + flaky installs and diffs in `package-lock.json`. + +## Decision Drivers + +- One declared Node version for every contributor and every machine. +- Version managers and CI read the same source of truth. +- Patch-level precision now, minor-level flexibility later without + churn. + +## Considered Options + +- Pin exact patch in `.nvmrc`, range in `engines` +- Pin major only (`24`) everywhere +- Stay on Node 22 LTS + +## Decision Outcome + +Chosen option: **pin the exact LTS patch in `.nvmrc` with a +compatible `engines` range**, because it gives contributors a +reproducible exact version via their version manager while letting +npm installs succeed across newer Node 24 patches. + +- `.nvmrc` contains `24.21.0`, the current Node 24 LTS release + (published 2026-09-08). +- `package.json` sets `"engines": { "node": ">=24.21.0 <25" }`. +- CI uses `actions/setup-node` with `node-version-file: .nvmrc` so + the workflow file no longer carries its own version number. +- When a new Node 24 LTS patch ships, update `.nvmrc` and `engines` + in a routine PR; no ADR amendment needed. + +### Confirmation + +`node --version` inside a freshly `nvm use`d shell matches `.nvmrc`, +and CI installs with the same version. + +## Pros and Cons of the Options + +### Pin exact patch in `.nvmrc`, range in `engines` + +- ✅ Good, because every contributor and CI runner gets an identical, + reproducible runtime. +- ✅ Good, because `engines` accepts newer Node 24 patches without a + repo change. +- ❌ Bad, because patch bumps require touching `.nvmrc` (intentional + friction). + +### Pin major only (`24`) everywhere + +- ✅ Good, because it never needs updating mid-line. +- ❌ Bad, because "some Node 24" is not reproducible; two + contributors on 24.1 and 24.21 can diverge in behavior. + +### Stay on Node 22 LTS + +- ✅ Good, because it is the line CI already used and needs no + migration. +- ❌ Bad, because it enters maintenance sooner (2027-04 end of life) + and forfeits the longer support runway and newer platform + features of 24. + +## Links + +- Related to [[ADR 0002 - Strict TypeScript and lint toolchain|ADR 0002]] +- Related to [Node.js release schedule](https://github.com/nodejs/release#release-schedule) diff --git a/vault/ADRs/ADR 0012 - End of the hackathon scope.md b/vault/ADRs/ADR 0012 - End of the hackathon scope.md new file mode 100644 index 0000000..fbfc685 --- /dev/null +++ b/vault/ADRs/ADR 0012 - End of the hackathon scope.md @@ -0,0 +1,138 @@ +--- +type: Architecture Decision Record +title: End of the hackathon scope +description: The one-afternoon POC scoping that narrowed every ADR to a golden path is over; the full decisions are the target again, the as-built baseline is accepted, and NEXT.md governs what comes next. +status: accepted +tags: [architecture, process, scope] +generated: { by: claude-code/claude-fable-5.1, at: 2026-10-02T00:00:00Z } +verified: { by: human:brandon, at: 2026-10-02T00:00:00Z } +source: docs/adrs/0012-end-of-hackathon-scope.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0012" +--- + +# End of the hackathon scope + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0012 | accepted | 2026-10-02 | Brandon; lane owners by review | + +## Technical Story + +On hackathon day (2026-09-12) every accepted ADR from 0001 to 0010 was +amended with a "Hackathon POC (Golden Path)" subsection that narrowed its +decision to what five engineers could ship in one afternoon, a "Future / +Out of Scope for POC" subsection that parked the rest, and an "Open +Questions" subsection. `QUESTIONS.md` recorded POC-only recommendations, +`README.md` described the team as five hackathon workstreams, and the +coordination protocol ran in sprint mode (20-minute pings, 30-minute +releases). That scoping did its job: the core loop shipped. It is the +wrong frame for a project that continues. + +## Context and Problem Statement + +The narrowing was never a decision about the product; it was a decision +about one afternoon. Left in force, a contributor reading ADR 0007 +believes "localStorage only" is the architecture rather than a sprint +cut, and an agent reading the coordination skill releases a teammate's +claim after thirty minutes of an ordinary working day. Which limits are +lifted, which as-built divergences are accepted as the new baseline, and +where the deferred work now lives all need one record. + +## Decision Drivers + +- The ADRs are immutable history; the POC subsections cannot be deleted, + so their standing has to change through a superseding record. +- The code on `main` already diverges from two decisions in ways that are + fine and not worth refactoring. +- The roadmap in `NEXT.md` needs a single point that says the "future" + items are in scope again and in what order. +- Multi-day, part-time collaboration needs the full coordination protocol + that the v3 playbook already specified. + +## Considered Options + +- Edit the POC subsections out of each ADR +- Mark ADRs 0001-0010 superseded wholesale and rewrite them +- One superseding ADR that ends the scoping and accepts the baseline + +## Decision Outcome + +Chosen option: **one superseding ADR that ends the scoping and accepts +the as-built baseline**, because it keeps the record immutable, names +exactly what changes, and leaves the original decisions in force. + +With effect from this record: + +1. **The golden paths are historical.** The "Hackathon POC (Golden + Path)" and "Future / Out of Scope for POC" subsections of ADRs + 0001-0010 describe the sprint, not the target. Each record's full + "Decision Outcome" is the target again. Nothing listed as "future" is + out of scope; `NEXT.md` sequences it. +2. **The as-built baseline is accepted where it diverges.** Two + divergences recorded in the "As built (2026-09-12)" subsections are + adopted as decisions rather than debts: + - ADR 0001: the slice layout is `os`, `chat`, `apps`, `settings`, and + `shared` under `app/features/`, with `shared` as the kernel. This + supersedes the record's `os/agent/apps` naming and its `app/shared/` + location. + - ADR 0003: stores may use setup or option syntax, and the app + registry's state lives in a module-level ref under `apps/registry/` + rather than a Pinia store. The "one setup store per slice" bullet is + superseded. + + Every other divergence (no ESLint, no sandbox, no vetting gate, a + single VFS driver, no proxy, the env-key fallback, the missing + `{windowId, appId}` props) remains a gap that `NEXT.md` schedules. +3. **Open questions become outcomes.** `QUESTIONS.md` keeps its history + and gains a status table: each POC recommendation is marked + implemented, reopened by the roadmap, or no longer applicable. +4. **Coordination runs the full protocol.** Sprint mode is off. Stale + claims use a six-hour warning and a three-hour grace during active + workdays, paused overnight (`COORD_STALE_MIN` defaults to 360); + `STATUS` is event-driven with a 60-minute active-work ceiling; shared + contracts need an explicit `ACCEPT` before they merge; `HANDOFF` is + mandatory when ownership crosses a session. Lanes, path ownership, and + the reviewer rotation are defined in `NEXT.md`. +5. **Labels lose their demo wording.** `p0` means the critical path of + the current roadmap phase; `ws:*` descriptions name slices and lanes, + not SDE numbers. + +### Confirmation + +A new contributor who reads only `README.md`, `NEXT.md`, and this record +can state what the target architecture is, which divergences are +accepted, and how to claim work, without reading any golden-path +subsection. Both coordination helpers default the stale window to 360 +minutes, and the repository labels carry the new descriptions. + +## Pros and Cons of the Options + +### One superseding ADR that ends the scoping and accepts the baseline + +- ✅ Good, because the record stays immutable and auditable. +- ✅ Good, because one document answers "is X still out of scope?". +- ❌ Bad, because readers of 0001-0010 must know to look here; the index + in `docs/adrs/README.md` points them. + +### Edit the POC subsections out of each ADR + +- ✅ Good, because each record would read cleanly on its own. +- ❌ Bad, because it rewrites accepted history, which ADR 0000 forbids. + +### Mark ADRs 0001-0010 superseded wholesale and rewrite them + +- ✅ Good, because the new set would describe the target directly. +- ❌ Bad, because the original decisions are still correct; only the + afternoon-sized scoping was temporary. Ten rewrites for two changed + bullets is churn without information. + +## Links + +- Supersedes the golden-path scoping of ADRs 0001 through 0010; amends + [[ADR 0001 - Feature slices with domain-driven organization|ADR 0001]] and + [[ADR 0003 - Pinia per-domain stores|ADR 0003]] as stated above +- Sequenced next by [NEXT.md](../../NEXT.md) +- Coordination parameters from + [coordination best practices v3](../agents/coordination-best-practices-3.md) diff --git a/vault/ADRs/ADR 0013 - In-repo vault and the work loop.md b/vault/ADRs/ADR 0013 - In-repo vault and the work loop.md new file mode 100644 index 0000000..4200891 --- /dev/null +++ b/vault/ADRs/ADR 0013 - In-repo vault and the work loop.md @@ -0,0 +1,134 @@ +--- +type: Architecture Decision Record +title: In-repo vault and the work loop +description: The project's second brain lives in the repository as vault/, and a harness-agnostic work loop drives roadmap steps through /coordinate while recording sessions and milestones in it. +status: accepted +tags: [process, tooling, agents, vault] +generated: { by: claude-code/claude-fable-5.1, at: 2026-10-02T00:00:00Z } +verified: { by: human:brandon, at: 2026-10-02T00:00:00Z } +source: docs/adrs/0013-in-repo-vault-and-work-loop.md +ingested: 2026-10-02 +repo_head: 0fc772d +adr: "0013" +--- + +# In-repo vault and the work loop + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0013 | accepted | 2026-10-02 | Brandon; lane owners by review | + +## Technical Story + +During the hackathon one collaborator kept an Obsidian vault that mirrored +the repository's docs with a fixed schema (OKF-style frontmatter, generated +notes regenerated from `docs/`, hand-written synthesis notes) and used it to +brief agents, track the board, and record what changed. It lived outside the +repository, so only one machine and one person had it. With the roadmap in +`NEXT.md` and work continuing part-time across five people and several +agent harnesses, the project needs that memory to be shared, and it needs a +way for any agent to pick up the next step, record progress, and survive +the context compaction every harness performs. + +## Context and Problem Statement + +Agents forget between sessions and inside long ones. Humans forget which +step is next. A roadmap, an issue board, and a vault each hold part of the +answer, but nothing connected them: nothing told an agent arriving cold +what to do first, nothing wrote down a milestone when a PR merged, and +nothing captured a session's state before the harness compressed it away. +Where should the vault live so every agent can read and write it, and what +is the smallest loop that keeps roadmap, board, and vault consistent +without depending on one harness's scheduler? + +## Decision Drivers + +- Any harness: Claude Code, Codex, Pi, opencode, Cursor, cron, or a human + must be able to run the same loop with the same tools (`node`, `git`, + `gh`). +- One shared memory: milestones and session notes must be visible to every + collaborator and machine, versioned with the code. +- Keep the schema already in use rather than inventing a second one. +- The loop must never hold state itself; restarts and compaction are the + normal case, not the failure case. +- Respect the coordination protocol: lanes, one in-progress issue, phase + gates, `## Files` before code. + +## Considered Options + +- Vault outside the repository, loop inside one harness's scheduler +- Vault in the repository, loop as a plain script any harness can run +- No vault; rely on issues, PR descriptions, and chat history + +## Decision Outcome + +Chosen option: **vault in the repository, loop as a plain script**, +because shared memory has to travel with the code, and a loop that is just +a script is the only kind every harness can run. + +- `vault/` is committed. It adopts the existing schema unchanged - the + generated folders (`ADRs/`, `Conventions/`, `Coordination/`, + `Hackathon/`, `Specs & Plans/`, `Repo Config/`, Commit Timeline), the + hand-written synthesis notes (Home, Architecture Overview, Repo Map, + Open Questions & Gaps), and OKF-style frontmatter with `repo_head` and + `ingested` on generated notes - and adds two folders the loop owns: + `Milestones/` (one note per merged roadmap step) and `Sessions/` (one + note per checkpoint). `.obsidian/workspace*` is ignored; `app.json` is + tracked. `vault/` is excluded from markdownlint. +- The loop is `.claude/skills/work-loop/scripts/loop.mjs` (Node 24, no + dependencies), exposed as `npm run loop`. Commands: `start` (resume brief, + pick the next eligible step, create and claim its issue through + `/coordinate`, branch, print the work brief), `checkpoint` (Session note + plus `STATUS` or `HANDOFF`), `resume` (the post-compaction digest), + `milestone` (`coord done`, Milestone note, progress refresh), `next`, + `status`, `vault-init`, `vault-sync`. +- Eligibility is computed from `NEXT.md` and the issue board every time: + lane first, one in-progress per agent, phase order, sequential order in + the phases `loop.config.json` names, and declared parallel steps. The + config also names the roadmap file and the vault path, so the loop + serves other repositories unchanged. +- `vault-sync` runs the ingest script (Python 3) that regenerates the + generated half of the vault from `docs/`; a Node port is deferred until + a contributor lacks Python. +- The Claude Code skill `/work-loop` and `docs/agents/work-loop.md` are the + two entry points; the former loads automatically, the latter is for + every other harness. + +### Confirmation + +A fresh clone plus `export COORD_AGENT=...` and `npm run loop -- start` +claims the next eligible step and prints its brief with no other setup; +`checkpoint` followed by a new session's `resume` reproduces the step, +branch, and next action; `milestone` after a merged PR closes the issue and +leaves a note under `vault/Milestones/` that Home links to. + +## Pros and Cons of the Options + +### Vault in the repository, loop as a plain script + +- ✅ Good, because every agent and machine shares one memory, reviewed + through the same PRs as the code. +- ✅ Good, because the loop has no harness dependency and no state. +- ❌ Bad, because vault edits add commits and can conflict; mitigated by + one-note-per-event files and the loop writing only its own folders. + +### Vault outside the repository, loop inside one harness's scheduler + +- ✅ Good, because it already existed and needed no repo changes. +- ❌ Bad, because only one person had it, and the scheduler would have + tied the loop to Claude Code. + +### No vault; rely on issues, PR descriptions, and chat history + +- ✅ Good, because it adds nothing. +- ❌ Bad, because chat history is exactly what compaction destroys, and + issues do not hold a session's half-finished state. + +## Links + +- Executes [NEXT.md](../../NEXT.md) through + [[ADR 0012 - End of the hackathon scope|ADR 0012]]'s full coordination protocol +- Builds on [coordination.md](../agents/coordination.md) and the + `/coordinate` skill +- Related to [[ADR 0011 - Node 24 LTS runtime standard|ADR 0011]] (the loop + targets Node 24) diff --git a/vault/ADRs/ADR Conventions.md b/vault/ADRs/ADR Conventions.md new file mode 100644 index 0000000..6c22a15 --- /dev/null +++ b/vault/ADRs/ADR Conventions.md @@ -0,0 +1,74 @@ +--- +type: convention +tags: [adr, process] +source: docs/adrs/README.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- +# Architecture Decision Records + +This directory records significant architecture and tooling decisions +for DOMinic using [MADR 4.0](https://adr.github.io/madr/) (Markdown Any +Decision Records). + +## What is an ADR? + +A short markdown document that captures one decision: the context that +forced it, the options considered, and the outcome with its consequences. +ADRs are immutable history — we do not rewrite accepted records, we supersede +them with new ones. + +## Conventions + +- **Numbering:** sequential, zero-padded to 4 digits (`0001`, `0002`, …). + `0000` is the meta-ADR that establishes this practice. +- **File naming:** `NNNN-short-title.md` with the title in kebab-case, e.g. + `0001-use-typescript.md`. +- **Statuses:** + - `proposed` — under discussion. + - `accepted` — decided and in force. + - `superseded` — replaced by a later ADR (the file is kept; the status + line names its successor). + - `deprecated` — no longer relevant without a direct successor. + +## Adding a new ADR + +1. Copy `template.md` to `NNNN-short-title.md` using the next free number. +2. Fill in every section; keep it short — the goal is context, not an essay. +3. Set status to `proposed` and open a pull request for discussion. +4. On acceptance, update the status to `accepted` and merge. + +## Index + +| ADR | Title | Status | +| --- | --- | --- | +| [[ADR 0000 - Record architecture decisions|0000]] | Record architecture decisions | accepted | +| [[ADR 0001 - Feature slices with domain-driven organization|0001]] | Feature slices with domain-driven organization | accepted | +| [[ADR 0002 - Strict TypeScript and lint toolchain|0002]] | Strict TypeScript and lint toolchain | accepted | +| [[ADR 0003 - Pinia per-domain stores|0003]] | Pinia per-domain stores | accepted | +| [[ADR 0004 - DOMinic OS shell and taskbar|0004]] | DOMinic OS shell and taskbar | accepted | +| [[ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy|0005]] | Agent chat via Vercel AI SDK with server-side provider proxy | accepted | +| [[ADR 0006 - Agent-authored runtime-compiled components|0006]] | Agent-authored runtime-compiled components | accepted | +| [[ADR 0007 - Virtual filesystem with pluggable storage drivers|0007]] | Virtual filesystem with pluggable storage drivers | accepted | +| [[ADR 0008 - Runtime NPM dependency loading via esm.sh with vetting|0008]] | Runtime NPM dependency loading via esm.sh with vetting | accepted | +| [[ADR 0009 - CORS-first networking with Chrome-masking proxy fallback|0009]] | CORS-first networking with Chrome-masking proxy fallback | accepted | +| [[ADR 0010 - Agent app interface and tool protocol|0010]] | Agent app interface and tool protocol | accepted | +| [[ADR 0011 - Node 24 LTS runtime standard|0011]] | Node 24 LTS runtime standard | accepted | +| [[ADR 0012 - End of the hackathon scope|0012]] | End of the hackathon scope | accepted | + +## As-built reconciliation + +On 2026-09-12 each accepted ADR from 0001 to 0010 gained an +`### As built (2026-09-12)` subsection at the end of its Decision +Outcome. It records, with file references, where the code on `main` +matches the golden path and where it diverges. The decision text +itself is unchanged: the record stays a record. + +## Hackathon scope ended + +[[ADR 0012 - End of the hackathon scope|ADR 0012]] (2026-10-02) ends the +one-afternoon scoping. The "Hackathon POC (Golden Path)" and "Future / +Out of Scope" subsections in ADRs 0001-0010 are historical; each +record's full Decision Outcome is the target again, and ADR 0012 names +the two as-built divergences (slice layout, store style) adopted as the +baseline. Deferred work is sequenced in [NEXT.md](../../NEXT.md). diff --git a/vault/ADRs/ADR Template.md b/vault/ADRs/ADR Template.md new file mode 100644 index 0000000..da5624d --- /dev/null +++ b/vault/ADRs/ADR Template.md @@ -0,0 +1,63 @@ +--- +type: template +tags: [adr, template] +source: docs/adrs/template.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- +# {title} + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| {NNNN} | proposed | {YYYY-MM-DD} | {everyone involved} | + +## Technical Story + +{describe the technical story, issue, or requirement that motivates this decision} + +## Context and Problem Statement + +{describe the context and problem statement in plain language, e.g. +using free form / two to three sentences or a list} + +## Decision Drivers + +- {driver 1, e.g. a force, facing concern, ...} +- {driver 2, e.g. a force, facing concern, ...} +- {driver N, e.g. a force, facing concern, ...} + +## Considered Options + +- {option 1} +- {option 2} +- {option N} + +## Decision Outcome + +Chosen option: **{option N}**, because {justification, e.g. only option +that meets the decision drivers | which forces and concerns are +resolved}. + +<!-- This is an optional element. Feel free to remove it. --> +### Confirmation + +{describe how you will communicate and/or confirm the decision was implemented correctly} + +## Pros and Cons of the Options + +### {option 1} + +- ✅ Good, because {argument} +- ✅ Good, because {argument} +- ❌ Bad, because {argument} + +### {option 2} + +- ✅ Good, because {argument} +- ✅ Good, because {argument} +- ❌ Bad, because {argument} + +## Links + +- {link type, e.g. Refined by} {ADR(s), e.g. ADR-0005} +- {link type, e.g. Related to} {reference(s)} diff --git a/vault/Architecture Overview.md b/vault/Architecture Overview.md new file mode 100644 index 0000000..8856f43 --- /dev/null +++ b/vault/Architecture Overview.md @@ -0,0 +1,115 @@ +--- +type: overview +title: Architecture Overview +description: DOMinic as built on main at be8645f — five slices, the client-side tool protocol, the localStorage VFS, the runtime SFC engine, and where the code diverges from the golden-path ADRs. +tags: [dominic, architecture, synthesis] +generated: { by: claude-code/claude-opus-4.8, at: 2026-09-12T23:40:00Z } +sources: + - id: code + resource: app/, server/, nuxt.config.ts, package.json + title: main at be8645f (post-sprint) + - id: adrs + resource: docs/adrs/ + title: ADRs 0001–0011 +repo_head: "be8645f" +ingested: 2026-09-12 +refreshed: 2026-09-12T23:40:00Z +--- + +# Architecture Overview + +The system as it actually stands on `main` at `be8645f`, with the golden-path ADRs noted where they still read differently from the code (each ADR note carries the reconciling **As built** subsection). Back to [[DOMinic Home]]. + +## The one-sentence pitch + +The browser tab is the **runtime**, not the display. The agent's output becomes *installed software* — Vue SFCs compiled in the browser, registered in a window manager, persisted in a virtual filesystem, updatable in place — which a chat window fundamentally cannot do. (The "chatbox test" from [[Judging Rubric & Win Strategy]].) + +## Slices as built + +``` +nuxt.config.ts Nuxt 4 · @pinia/nuxt, @nuxtjs/tailwindcss, @vueuse/nuxt · srcDir app/ · TS strict, typeCheck:false + · tailwind content globs app|features/**/*.{vue,ts,txt} · Tailwind Play CDN in <head> for runtime classes +package.json ai ^4.3, @ai-sdk/openai|anthropic|google ^1, zod ^3, vue3-sfc-loader ^0.9, lucide-vue-next, + canvas-confetti · engines node >=24.21 <25 · licence AGPL-3.0-or-later +app/app.vue the composition root: hydrates registry + settings on mount, opens Agent Chat, renders one + WindowFrame per open window (chat / settings / source:<id> / DynamicAppRunner), mounts the Taskbar, + and exposes window.__dominic test hooks (installFixture, injectBrokenApp, reset) +app/features/ + os/ stores/os.ts (setup store: windows, focusedId, z-order, min/max) · components/WindowFrame.vue + (pointer-event drag, focus, min/max/close) · components/Taskbar.vue (installed-app list + running dot, + View Source, Uninstall) + chat/ components/ChatWindow.vue (streaming transcript, tool pills, suggestion chips) · + composables/useAgentChat.ts (POST /api/chat, v4 data-stream parser, client-side tool execution, + provider-safe history, injects the referenced app's current source) · prompts/systemPrompt.ts · + tools/schemas.ts (Zod install_app / update_app) · types/chat.ts (ProviderConfig incl. 'custom') + apps/ runner/DynamicAppRunner.vue + runner/loader.ts (compile SFC, esm.sh fallback, data-app-id style + inject + removeAppStyles) · registry/registry.ts (AppMeta, hydrate/register/unregister, registry.json) · + fixtures/ (pomodoro-timer/index.vue.txt + installFixture) · stores/apps.ts (installed[] mirror) + settings/ components/SettingsApp.vue (provider radios incl. Custom, model, API key, Base URL, Reset OS) · + stores/settings.ts (provider/model/apiKey/baseUrl, VFS persist + hydrate, resetOs) + shared/ vfs.ts (localStorage driver) · types/vue3-sfc-loader.d.ts +server/api/chat.post.ts streamText({model, system, messages, tools, maxSteps:3}).toDataStreamResponse() +``` + +Five slice directories, not ADR 0001's `os / agent / apps`; the chat slice is `chat`, Settings is its own slice. Stores are mixed setup/option style, and the app registry lives in a module-level ref rather than Pinia — treat ADR 0003's "setup store per slice" as aspirational. + +## The core loop (the demo) + +```mermaid +sequenceDiagram + actor U as User + participant W as ChatWindow + participant C as useAgentChat + participant S as /api/chat (streamText) + participant P as Provider (OpenAI/Anthropic/Google/DeepSeek/custom) + participant A as app.vue callbacks + participant F as VFS (localStorage) + participant R as DynamicAppRunner + + U->>W: "build a synthwave pomodoro timer" + W->>C: sendMessage(text) + C->>S: POST {messages, provider, model, apiKey, baseUrl, installedApps} + S->>S: apiKey ?? process.env.<PROVIDER>_API_KEY (401 if neither) + S->>P: streamText(system=buildSystemPrompt(installedApps), tools) + P-->>C: data stream 0:"text" | 9:{toolCallId,toolName,args} + C->>A: install_app → writeFile apps/<id>/index.vue, registerApp, openWindow + A->>R: mount, keyed on id + sourceVersion + R-->>U: app renders; taskbar shows it with a running dot; confetti + U->>U: reload tab + A->>F: hydrateRegistry() → apps.installed + A-->>U: app back in launcher/taskbar, reopens from VFS source + U->>W: "make the breaks 10 minutes" + C->>A: update_app → writeFile + removeAppStyles + bump sourceVersion → runner re-compiles +``` + +Design facts worth knowing: + +- **Tools execute on the client, not the server.** `/api/chat` only declares the Zod schemas; `useAgentChat` runs `install_app`/`update_app` through callbacks that `app.vue` injects via `<ChatWindow :options>` (`getProviderConfig`, `getInstalledApps`, `getAppSource`, `onInstallApp`, `onUpdateApp`, `onOpenWindow`). That options object is the real cross-slice contract. +- **`update_app` gets the current source (#66).** `useAgentChat` detects which installed app a message refers to (direct name match, else the last-touched app unless the message asks to build something new) and appends that app's current VFS source to the outgoing message, so the model edits real code instead of rewriting from memory. The runner re-compiles because `app.vue` keys it on `id + sourceVersion` and bumps `sourceVersion` on install/update. +- **Provider-safe history.** The seeded "welcome" turn is dropped and empty assistant turns get a placeholder, because Anthropic rejects a leading assistant turn and empty content blocks (fixed in #42). +- **Recover beat (#64).** `DynamicAppRunner`'s error boundary catches compile and runtime errors and emits `askFix{appId,error}`; `app.vue` focuses chat and sends "the app … encountered an error … fix it with update_app". `window.__dominic.injectBrokenApp('compile'|'runtime')` is the test hook. +- **Keys** travel per request from `ProviderConfig`; the route falls back to `process.env.<PROVIDER>_API_KEY` — acceptable only because the demo host sets none (contradicts ADR 0005's "never at rest"; see [[Open Questions & Gaps]]). +- **DominicApp contract (system prompt):** `<template>` + plain-JS `<script setup>`, optional `<style scoped>`; imports limited to `vue`, `@vueuse/core`, Lucide icons, `canvas-confetti`; Tailwind utilities; browser APIs only. The runner passes **no props** into the compiled app despite ADR 0010's `{windowId, appId}`. + +## Providers (ADR 0005 + PR #70) + +`server/api/chat.post.ts` builds a model instance per `provider`: `openai` (default `gpt-5`), `anthropic` (`claude-sonnet-5`), `google` (`gemini-2.5-pro`), `deepseek` (OpenAI-adapter at `api.deepseek.com`), and **`custom`** — an OpenAI-compatible endpoint that requires a Base URL and a model from Settings (e.g. OpenRouter, a local proxy). Settings stores `{provider, model, apiKey, baseUrl}`, persisted to `/system/settings.json` in the VFS. Spec/plan: [[Spec - Custom OpenAI Provider]] / [[Plan - Custom OpenAI Provider]]. + +## Runtime engine (ADR 0006 + 0008) + +1. `compileVueSfc({sourceCode, appId})` hands `vue3-sfc-loader` a virtual `/<appId>.vue`; `getFile` returns the source and fetches any other specifier from `https://esm.sh/<specifier>?bundle` — **no vetting**, as the golden path chose. +2. `moduleCache` pre-resolves `vue`, `@vueuse/core`, `lucide-vue-next`, `canvas-confetti` (zero network) — the same four the system prompt allows. +3. `<style>` blocks are appended to `<head>` tagged `data-app-id`; `removeAppStyles(appId)` clears them on update and uninstall (fixed in #62 — earlier they leaked). +4. Tailwind: `@nuxtjs/tailwindcss` purges to classes found in `app|features/**/*.{vue,ts,txt}` at build time, and the **Play CDN** in `<head>` JITs any class the agent invents at runtime (fixed in #42). + +## Persistence (ADR 0007) + +A single `localStorage` driver behind `vfs.ts` (`readFile`, `writeFile`, `deleteFile`, `listFiles`, `clearVfs`), key-prefixed `dominic:vfs:`. Paths: `apps/<id>/index.vue` (source) and `registry.json` (the `AppMeta[]`); settings at `system/settings.json`. On boot `app.vue` calls `hydrateRegistry()` → `apps.installed`, so installed apps return after reload; **windows do not persist** (only apps). "Reset OS" (`clearVfs` + reload) wipes it. + +## Networking (ADR 0009) + +Direct `fetch` only. No `/api/proxy` route was built — the demo app makes no external calls, so the CORS-proxy escape hatch stayed out of scope. + +## Deferred vs shipped + +Full deferral list is [[Post-Hackathon Roadmap (NEXT)]]; the honest caveats a judge will notice are in [[Open Questions & Gaps]]. In short, shipped: the whole loop, five providers, error recovery, install/update/uninstall/view-source, reload persistence. Deferred: iframe sandboxing, dependency vetting, multi-driver VFS, the CORS proxy, mobile layout, ESLint/type gates in the pre-commit hook. diff --git a/vault/Commit Timeline.md b/vault/Commit Timeline.md new file mode 100644 index 0000000..03d63d5 --- /dev/null +++ b/vault/Commit Timeline.md @@ -0,0 +1,181 @@ +--- +type: reference +title: Commit Timeline +tags: [git, history] +source: git log origin/main +repo_head: 0fc772d +ingested: 2026-10-02 +--- +# Commit Timeline + +All **65** commits on `main` as of ingest (HEAD `0fc772d`, newest first). Times are ET as recorded by the committer. The final PR was **#76**; the merged-PR record by author is in [[Submission Form Draft#Team contributions]]. See [[Repo Map]] for the file inventory and [[DOMinic Home]] for the story. + +Commits by author: Zackary Lowery 34 · m-vawter 12 · Brandon 7 · ImNewToC0de 6 · Charles Sullivan 6. + +## Commits on `main` + +| Commit | Time | Author | Message | +| --- | --- | --- | --- | +| `0fc772d` | 19:43 | Brandon | docs: end the hackathon scope (ADR 0012) and switch to the full protocol (#78) | +| `d6300a4` | 19:42 | Brandon | docs: sequence the post-hackathon roadmap and assign lanes (#77) | +| `be8645f` | 18:05 | Zackary Lowery | feat(coord): enforce v3 file-scope, completion, and hub invariants (#75) | +| `11bec60` | 18:05 | Zackary Lowery | docs: add desktop screenshot to README (#76) | +| `4d7989e` | 17:54 | Zackary Lowery | chore: license DOMinic under AGPL-3.0-or-later | +| `0d66819` | 17:42 | m-vawter | docs(submission): correct team contributions to the merged-PR record (#73) | +| `3433ee2` | 16:54 | Zackary Lowery | docs: hackathon submission form draft | +| `a00fe7d` | 16:52 | m-vawter | docs: Q01 research provenance and finish-plan close-out | +| `0dc1747` | 16:35 | Zackary Lowery | feat: custom OpenAI-compatible provider (base URL + model) | +| `c777abc` | 16:30 | m-vawter | docs: integrate coordination v3 (#68) | +| `90149a5` | 16:26 | ImNewToC0de | feat: add current app source to update context | +| `7814a1c` | 16:17 | Zackary Lowery | ci: validate typecheck and build | +| `2df1b01` | 16:08 | Charles Sullivan | feat(runtime-engine): verify Recover beat e2e with compile/runtime error handling and chat fix injection (#64) | +| `3803cf5` | 16:07 | ImNewToC0de | fix: clean up dynamic app styles (#62) | +| `5968dba` | 16:06 | ImNewToC0de | style: polish settings credential inputs (#61) | +| `b558ef2` | 16:05 | m-vawter | fix: resolve shell and chat smoke failures (#63) | +| `c251e4f` | 16:04 | Zackary Lowery | feat(coordinate): Windows PowerShell port of coord script + Node 24 LTS standard (#36) | +| `162aa09` | 15:54 | Brandon | docs(adrs): append verified "As built" reconciliation to ADRs 0001-0010 (#55) | +| `153ec94` | 15:53 | Brandon | fix(coordinate): resolve the hub to the lowest-numbered hub issue (#57) | +| `d3135ff` | 15:51 | m-vawter | docs: add judge-facing walkthrough (#53) | +| `bb2e1a8` | 15:48 | ImNewToC0de | fix(build): separate strict typecheck from production bundle (#48) | +| `619aa82` | 15:29 | m-vawter | docs: record completed ADR and hackathon plans (#37) | +| `e9efa73` | 15:28 | Charles Sullivan | feat(rehearsal): freeze demo prompt and provide fallback pomodoro fixture (#40) | +| `3cb3be2` | 15:26 | Zackary Lowery | feat: add launcher uninstall and source actions (#44) | +| `2660f99` | 15:15 | ImNewToC0de | fix: keep window controls out of drag handler (#45) | +| `483fc1d` | 15:13 | Brandon | fix(settings): align model presets with route defaults (#39) | +| `a80b2a1` | 15:08 | Brandon | fix(build,chat): scan app/features for Tailwind; Anthropic-safe chat history (#42) | +| `fdeb0ff` | 15:02 | ImNewToC0de | feat: wire DOMinic core app loop (#35) | +| `bcbef1e` | 14:52 | m-vawter | docs: README quickstart — Running DOMinic, drop docs-only language (#28) | +| `07a52c6` | 14:52 | Zackary Lowery | fix: add chat runtime dependencies (#29) | +| `8ea9114` | 14:49 | Zackary Lowery | feat: OS shell with window manager, taskbar, drag/focus/minimize/maximize (#13) (#24) | +| `11f79bc` | 14:42 | m-vawter | docs: add coordination best practices v2 (supersedes v1) (#26) | +| `71c6edc` | 14:41 | Charles Sullivan | feat(persistence): VFS over localStorage, registry hydration, Settings app, and Reset OS (#17) (#27) | +| `42fb7bb` | 14:36 | Charles Sullivan | feat(runtime): in-browser vue3-sfc-loader runner, esm.sh resolver, and error boundary (#15) (#25) | +| `cea7d52` | 14:34 | m-vawter | docs: add coordination best practices playbook (sprint minimum + full) (#18) | +| `b86dfbd` | 14:32 | Charles Sullivan | feat(chat): implement /api/chat route, system prompt, tool schemas, and ChatWindow UI (#20) | +| `bb97707` | 14:31 | Zackary Lowery | feat: Nuxt 4 scaffold with strict TS, Pinia per-domain stores, Tailwind (#12) (#21) | +| `5cbaab4` | 14:11 | Brandon | feat: add /coordinate skill for multi-agent work over GitHub Issues (#7) | +| `84e3a91` | 13:59 | m-vawter | docs: align demo path with the hackathon golden path and open questions (#4) | +| `2521868` | 13:52 | m-vawter | docs: add demo path mapping the video beats to the rubric and ADRs (#3) → [[Demo Path]] | +| `3ff7c14` | 13:45 | Charles Sullivan | docs: orient architecture decisions toward hackathon POC golden path (#2) → [[ADR 0010 - Agent app interface and tool protocol]] | +| `dab4cf0` | 13:44 | m-vawter | docs: add hackathon judging rubric and win strategy (#1) → [[Judging Rubric & Win Strategy]] | +| `c9eff74` | 13:10 | Zackary Lowery | docs: add repository README | +| `686453e` | 13:07 | Zackary Lowery | docs: add ADR 0009 CORS-first networking with proxy fallback | +| `1270b66` | 13:06 | Zackary Lowery | docs: add ADR 0008 esm.sh dependency loading with vetting | +| `00e5dac` | 13:06 | Zackary Lowery | docs: add ADR 0007 virtual filesystem with pluggable storage drivers | +| `bcd70ae` | 13:05 | Zackary Lowery | docs: add ADR 0006 runtime-compiled components | +| `566f542` | 13:05 | Zackary Lowery | docs: add ADR 0005 agent chat via Vercel AI SDK | +| `d310387` | 13:02 | Zackary Lowery | docs: add transparency and blur guidance to ADR 0004 shell | +| `51bbbfa` | 13:01 | Zackary Lowery | docs: add ADR 0004 DOMinic OS shell and taskbar | +| `c5bdb9e` | 12:59 | Zackary Lowery | docs: pin ADR 0002 toolchain to current stable versions | +| `33b710e` | 12:58 | Zackary Lowery | docs: add ADR 0003 Pinia per-domain stores | +| `0c36f9f` | 12:58 | Zackary Lowery | docs: add ADR 0002 strict TypeScript and lint toolchain | +| `569bdde` | 12:57 | Zackary Lowery | docs: add ADR 0001 feature slices with domain-driven organization | +| `3f3cc21` | 12:57 | Zackary Lowery | chore: exempt OKF frontmatter title from MD025 | +| `aa7f862` | 12:55 | Zackary Lowery | docs: add ADR series implementation plan | +| `91af066` | 12:33 | Zackary Lowery | docs: add ADR series design spec | +| `7196408` | 12:30 | Zackary Lowery | docs: add ADR conventions README | +| `76b112a` | 12:22 | Zackary Lowery | ci: add markdownlint pre-commit hook (husky) and GitHub Actions workflow | +| `04415a6` | 12:21 | Zackary Lowery | docs: rewrap markdown to satisfy 80-column lint | +| `80c79a4` | 12:08 | Zackary Lowery | chore: add package.json with markdownlint-cli2 and Node gitignore | +| `8f4b5fa` | 12:05 | Zackary Lowery | docs: add MADR ADR template | +| `ba6014d` | 12:05 | Zackary Lowery | chore: add .editorconfig | +| `1711d8e` | 12:03 | Zackary Lowery | docs: add ADR 0000 (record architecture decisions) | +| `b4ca986` | 12:01 | Zackary Lowery | docs: add ADR scaffold spec and implementation plan | + +## Remote branches at ingest + +- `origin/chore/agpl-license` +- `origin/ci/build-typecheck` +- `origin/codex/issue33-verify` +- `origin/codex/os-shell` +- `origin/codex/taskbar-installed-apps` +- `origin/codex/update-app-source-context` +- `origin/codex/vfs-contract` +- `origin/demo/mock-showcase` +- `origin/docs/adr-as-built-47` +- `origin/docs/coordination-best-practices-2` +- `origin/docs/coordination-v3-integration` +- `origin/docs/end-hackathon-scope` +- `origin/docs/hackathon-finish-plan` +- `origin/docs/judges-walkthrough` +- `origin/docs/merge-coordination-best-practices` +- `origin/docs/roadmap-sequenced` +- `origin/docs/submission-form` +- `origin/docs/submission-form-attribution` +- `origin/docs/wrap-up` +- `origin/feat/custom-openai-provider` +- `origin/feat/launcher-actions-34` +- `origin/fix/build-deps-22` +- `origin/fix/coordinate-hub-selection` +- `origin/fix/settings-model-presets` +- `origin/main` + +## Raw log + +``` +0fc772d|Brandon|2026-10-02 19:43:06 -0400|docs: end the hackathon scope (ADR 0012) and switch to the full protocol (#78) +d6300a4|Brandon|2026-10-02 19:42:50 -0400|docs: sequence the post-hackathon roadmap and assign lanes (#77) +be8645f|Zackary Lowery|2026-09-12 18:05:44 -0400|feat(coord): enforce v3 file-scope, completion, and hub invariants (#75) +11bec60|Zackary Lowery|2026-09-12 18:05:06 -0400|docs: add desktop screenshot to README (#76) +4d7989e|Zackary Lowery|2026-09-12 17:54:55 -0400|chore: license DOMinic under AGPL-3.0-or-later +0d66819|m-vawter|2026-09-12 17:42:47 -0400|docs(submission): correct team contributions to the merged-PR record (#73) +3433ee2|Zackary Lowery|2026-09-12 16:54:51 -0400|docs: hackathon submission form draft +a00fe7d|m-vawter|2026-09-12 16:52:11 -0400|docs: Q01 research provenance and finish-plan close-out +0dc1747|Zackary Lowery|2026-09-12 16:35:45 -0400|feat: custom OpenAI-compatible provider (base URL + model) +c777abc|m-vawter|2026-09-12 16:30:04 -0400|docs: integrate coordination v3 (#68) +90149a5|ImNewToC0de|2026-09-12 16:26:37 -0400|feat: add current app source to update context +7814a1c|Zackary Lowery|2026-09-12 16:17:45 -0400|ci: validate typecheck and build +2df1b01|Charles Sullivan|2026-09-12 16:08:28 -0400|feat(runtime-engine): verify Recover beat e2e with compile/runtime error handling and chat fix injection (#64) +3803cf5|ImNewToC0de|2026-09-12 16:07:01 -0400|fix: clean up dynamic app styles (#62) +5968dba|ImNewToC0de|2026-09-12 16:06:09 -0400|style: polish settings credential inputs (#61) +b558ef2|m-vawter|2026-09-12 16:05:35 -0400|fix: resolve shell and chat smoke failures (#63) +c251e4f|Zackary Lowery|2026-09-12 16:04:52 -0400|feat(coordinate): Windows PowerShell port of coord script + Node 24 LTS standard (#36) +162aa09|Brandon|2026-09-12 15:54:16 -0400|docs(adrs): append verified "As built" reconciliation to ADRs 0001-0010 (#55) +153ec94|Brandon|2026-09-12 15:53:24 -0400|fix(coordinate): resolve the hub to the lowest-numbered hub issue (#57) +d3135ff|m-vawter|2026-09-12 15:51:53 -0400|docs: add judge-facing walkthrough (#53) +bb2e1a8|ImNewToC0de|2026-09-12 15:48:52 -0400|fix(build): separate strict typecheck from production bundle (#48) +619aa82|m-vawter|2026-09-12 15:29:41 -0400|docs: record completed ADR and hackathon plans (#37) +e9efa73|Charles Sullivan|2026-09-12 15:28:39 -0400|feat(rehearsal): freeze demo prompt and provide fallback pomodoro fixture (#40) +3cb3be2|Zackary Lowery|2026-09-12 15:26:28 -0400|feat: add launcher uninstall and source actions (#44) +2660f99|ImNewToC0de|2026-09-12 15:15:40 -0400|fix: keep window controls out of drag handler (#45) +483fc1d|Brandon|2026-09-12 15:13:05 -0400|fix(settings): align model presets with route defaults (#39) +a80b2a1|Brandon|2026-09-12 15:08:28 -0400|fix(build,chat): scan app/features for Tailwind; Anthropic-safe chat history (#42) +fdeb0ff|ImNewToC0de|2026-09-12 15:02:15 -0400|feat: wire DOMinic core app loop (#35) +bcbef1e|m-vawter|2026-09-12 14:52:52 -0400|docs: README quickstart — Running DOMinic, drop docs-only language (#28) +07a52c6|Zackary Lowery|2026-09-12 14:52:18 -0400|fix: add chat runtime dependencies (#29) +8ea9114|Zackary Lowery|2026-09-12 14:49:16 -0400|feat: OS shell with window manager, taskbar, drag/focus/minimize/maximize (#13) (#24) +11f79bc|m-vawter|2026-09-12 14:42:34 -0400|docs: add coordination best practices v2 (supersedes v1) (#26) +71c6edc|Charles Sullivan|2026-09-12 14:41:46 -0400|feat(persistence): VFS over localStorage, registry hydration, Settings app, and Reset OS (#17) (#27) +42fb7bb|Charles Sullivan|2026-09-12 14:36:00 -0400|feat(runtime): in-browser vue3-sfc-loader runner, esm.sh resolver, and error boundary (#15) (#25) +cea7d52|m-vawter|2026-09-12 14:34:30 -0400|docs: add coordination best practices playbook (sprint minimum + full) (#18) +b86dfbd|Charles Sullivan|2026-09-12 14:32:10 -0400|feat(chat): implement /api/chat route, system prompt, tool schemas, and ChatWindow UI (#20) +bb97707|Zackary Lowery|2026-09-12 14:31:42 -0400|feat: Nuxt 4 scaffold with strict TS, Pinia per-domain stores, Tailwind (#12) (#21) +5cbaab4|Brandon|2026-09-12 14:11:43 -0400|feat: add /coordinate skill for multi-agent work over GitHub Issues (#7) +84e3a91|m-vawter|2026-09-12 13:59:40 -0400|docs: align demo path with the hackathon golden path and open questions (#4) +2521868|m-vawter|2026-09-12 13:52:40 -0400|docs: add demo path mapping the video beats to the rubric and ADRs (#3) +3ff7c14|Charles Sullivan|2026-09-12 13:45:27 -0400|docs: orient architecture decisions toward hackathon POC golden path (#2) +dab4cf0|m-vawter|2026-09-12 13:44:48 -0400|docs: add hackathon judging rubric and win strategy (#1) +c9eff74|Zackary Lowery|2026-09-12 13:10:41 -0400|docs: add repository README +686453e|Zackary Lowery|2026-09-12 13:07:33 -0400|docs: add ADR 0009 CORS-first networking with proxy fallback +1270b66|Zackary Lowery|2026-09-12 13:06:58 -0400|docs: add ADR 0008 esm.sh dependency loading with vetting +00e5dac|Zackary Lowery|2026-09-12 13:06:16 -0400|docs: add ADR 0007 virtual filesystem with pluggable storage drivers +bcd70ae|Zackary Lowery|2026-09-12 13:05:44 -0400|docs: add ADR 0006 runtime-compiled components +566f542|Zackary Lowery|2026-09-12 13:05:14 -0400|docs: add ADR 0005 agent chat via Vercel AI SDK +d310387|Zackary Lowery|2026-09-12 13:02:00 -0400|docs: add transparency and blur guidance to ADR 0004 shell +51bbbfa|Zackary Lowery|2026-09-12 13:01:02 -0400|docs: add ADR 0004 DOMinic OS shell and taskbar +c5bdb9e|Zackary Lowery|2026-09-12 12:59:36 -0400|docs: pin ADR 0002 toolchain to current stable versions +33b710e|Zackary Lowery|2026-09-12 12:58:39 -0400|docs: add ADR 0003 Pinia per-domain stores +0c36f9f|Zackary Lowery|2026-09-12 12:58:11 -0400|docs: add ADR 0002 strict TypeScript and lint toolchain +569bdde|Zackary Lowery|2026-09-12 12:57:50 -0400|docs: add ADR 0001 feature slices with domain-driven organization +3f3cc21|Zackary Lowery|2026-09-12 12:57:49 -0400|chore: exempt OKF frontmatter title from MD025 +aa7f862|Zackary Lowery|2026-09-12 12:55:08 -0400|docs: add ADR series implementation plan +91af066|Zackary Lowery|2026-09-12 12:33:22 -0400|docs: add ADR series design spec +7196408|Zackary Lowery|2026-09-12 12:30:43 -0400|docs: add ADR conventions README +76b112a|Zackary Lowery|2026-09-12 12:22:11 -0400|ci: add markdownlint pre-commit hook (husky) and GitHub Actions workflow +04415a6|Zackary Lowery|2026-09-12 12:21:55 -0400|docs: rewrap markdown to satisfy 80-column lint +80c79a4|Zackary Lowery|2026-09-12 12:08:49 -0400|chore: add package.json with markdownlint-cli2 and Node gitignore +8f4b5fa|Zackary Lowery|2026-09-12 12:05:55 -0400|docs: add MADR ADR template +ba6014d|Zackary Lowery|2026-09-12 12:05:09 -0400|chore: add .editorconfig +1711d8e|Zackary Lowery|2026-09-12 12:03:06 -0400|docs: add ADR 0000 (record architecture decisions) +b4ca986|Zackary Lowery|2026-09-12 12:01:19 -0400|docs: add ADR scaffold spec and implementation plan +``` diff --git a/vault/Conventions/Markdown Lint & CI.md b/vault/Conventions/Markdown Lint & CI.md new file mode 100644 index 0000000..99c5689 --- /dev/null +++ b/vault/Conventions/Markdown Lint & CI.md @@ -0,0 +1,103 @@ +--- +type: convention +title: Markdown Lint & CI +description: The repo's markdownlint, husky, Node 24, and GitHub Actions (lint + typecheck + build) setup, and what the golden path defers. +tags: [dominic, tooling, ci, lint] +sources: + - id: pkg + resource: package.json + - id: lintcfg + resource: .markdownlint.jsonc + - id: ci + resource: .github/workflows/ci.yml + - id: hook + resource: .husky/pre-commit + - id: adr-0002 + resource: docs/adrs/0002-strict-typescript-and-lint-toolchain.md +repo_head: "be8645f" +ingested: 2026-09-12 +refreshed: 2026-09-12T23:40:00Z +--- + +# Markdown Lint & CI + +The repo's quality gates as of `be8645f`. Markdown linting was the +only gate early on; CI now also runs `nuxt typecheck` and `nuxt build`. +Raw copies of every config file are in `Repo Config/`. Back to +[[DOMinic Home]]. + +## Commands + +```bash +npm ci # installs husky + markdownlint-cli2; `prepare` wires the hook +npm run lint:md # markdownlint-cli2 "**/*.md" "#node_modules" "#.superpowers" "#docs/superpowers" +npm run typecheck # nuxt typecheck (vue-tsc); runs in CI, not in the hook +npm run build # nuxt build; runs in CI +``` + +## markdownlint rules (`.markdownlint.jsonc`) + +| Rule | Setting | Effect | +| --- | --- | --- | +| `default` | `true` | All standard rules on | +| `MD013` line length | 80 cols; **headings, code blocks, tables exempt** | Prose must hard-wrap at 80 | +| `MD025` single H1 | `front_matter_title: ""` | An OKF `title:` in frontmatter does not count as a second H1 | + +> [!warning] The glob scans dot-directories +> `**/*.md` matches `.claude/skills/**/SKILL.md` too (verified with a probe file). Skills, agent notes, anything markdown anywhere in the tree must obey the 80-column rule or the commit is rejected. The `/coordinate` skill's files (SKILL.md, protocol.md, coord.sh, coord.ps1) +were written to comply. The lint glob also excludes `#docs/superpowers` and +`#.superpowers` so the verbatim plan/spec and research artifacts are not +reformatted. + +## Pre-commit hook (`.husky/pre-commit`) + +``` +npm run lint:md +``` + +Runs on every commit against the whole tree (no lint-staged). Only active after `npm ci` has run in that clone — a fresh clone without it commits unchecked, but CI catches it. + +## GitHub Actions (`.github/workflows/ci.yml`) + +- Triggers: push to `main`/`master`, every pull request. +- **Three jobs** on `ubuntu-latest`, each `actions/setup-node` with + `node-version-file: .nvmrc` (Node 24) + npm cache → `npm ci`: + - `markdownlint` → `npm run lint:md` + - `typecheck` → `npm run typecheck` (added #67) + - `build` → `npm run build` (added #67; `NUXT_TELEMETRY_DISABLED=1`) +- `main` is **branch-protected** and **requires one approving review** (a PR + author cannot self-approve); PRs are squash-merged. Zack held the merge gate. + +## Node, EditorConfig, and line endings + +- **Node 24 LTS** is the sole runtime: `.nvmrc` pins `24.21.0`, + `package.json` `engines` requires `>=24.21.0 <25`, and CI reads `.nvmrc` + ([[ADR 0011 - Node 24 LTS runtime standard|ADR 0011]]). +- EditorConfig: UTF-8, final newline, trim trailing whitespace, 2-space + indent; `*.md` keeps trailing whitespace. +- `.gitattributes` sets `*.sh text eol=lf` (PR #7) — bash cannot run CRLF + files on Windows checkouts, which is why the Windows `coord.ps1` port + exists alongside `coord.sh` (#36). + +## `.gitignore` + +- `.superpowers/` — the brainstorming tool's scratch dir (the committed docs live in `docs/superpowers/`). +- `AGENTS.md` — **per-agent instructions are deliberately local-only** (added in PR #2). See [[Open Questions & Gaps]] §5. +- Standard Node ignores plus `.husky/_/`. + +## What ADR 0002 defers for the POC + +[[ADR 0002 - Strict TypeScript and lint toolchain|ADR 0002]]'s golden path +keeps only `strict: true` in `tsconfig.json`. **No ESLint** was ever +installed — slice-boundary rules stayed convention. The pre-commit hook runs +markdownlint only; type-checking and build moved into **CI** (#67) rather +than the hook, and `nuxt.config` sets `typeCheck: false` so `nuxt build` +does not double-check types (an environment-specific `TS5023`/`TS5042` on +Windows). See [[Open Questions & Gaps]] §7–8. + +## Writing a doc that passes + +1. OKF frontmatter per [[OKF Frontmatter Convention]] (`type` at minimum; `title`, `description`, `tags`, `generated`, `verified` recommended). Frontmatter lines are exempt from the 80-column rule. +2. One `# H1` matching the frontmatter `title`. +3. Wrap prose at 80 columns; tables and fenced code can run wide. +4. `npm run lint:md` before committing, or let the hook catch it. diff --git a/vault/Conventions/OKF Frontmatter Convention.md b/vault/Conventions/OKF Frontmatter Convention.md new file mode 100644 index 0000000..c336399 --- /dev/null +++ b/vault/Conventions/OKF Frontmatter Convention.md @@ -0,0 +1,47 @@ +--- +type: convention +title: Use Google's Open Knowledge Format (OKF) +tags: [okf, convention, agents] +source: docs/agents/use-okf.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- +# Use Google's Open Knowledge Format (OKF) + +All agents working in this repository SHOULD author, maintain, and consume +project knowledge using Google's Open Knowledge Format (OKF) v0.2. + +## Why OKF + +- **Plain markdown + YAML frontmatter.** No SDK, no query language, no + lock-in. If you can `cat` a file, you can read it. +- **Version-controllable.** Knowledge lives in git alongside the code, + so it can be reviewed, diffed, and blamed like everything else. +- **Trust built in.** Provenance (`sources`), verification (`generated`, + `verified`), and freshness (`status`, `stale_after`) are first-class + frontmatter fields, so agent-maintained knowledge stays auditable. +- **Vendor-neutral.** Not tied to any agent framework, model provider, + or serving system. + +## What agents should do + +1. Represent durable knowledge (schemas, metrics, playbooks, references, + decisions context) as OKF concepts: one markdown document per concept, + with YAML frontmatter carrying at minimum a `type` field. +2. Prefer structured markdown in the body (headings, lists, tables, + fenced code blocks) over freeform prose. +3. Record provenance and trust fields when knowledge is generated or + confirmed, using the actor convention (`agent/version`, `human:<id>`, + `process:<id>`). +4. Cross-link concepts with standard markdown links and keep an + `index.md` in each directory for progressive disclosure. + +## Reference specification + +The complete, self-contained OKF v0.2 specification is embedded below +verbatim (preformatted to preserve the source exactly). The canonical +home is +[GoogleCloudPlatform/open-knowledge-format](https://github.com/GoogleCloudPlatform/open-knowledge-format). + +> [!note] Embedded specification +> The repo file embeds the full OKF v0.2 spec verbatim inside a fenced block. In this vault it lives as its own note: [[OKF v0.2 Specification]]. diff --git a/vault/Conventions/OKF v0.2 Specification.md b/vault/Conventions/OKF v0.2 Specification.md new file mode 100644 index 0000000..33906f3 --- /dev/null +++ b/vault/Conventions/OKF v0.2 Specification.md @@ -0,0 +1,1017 @@ +--- +type: reference +title: Open Knowledge Format (OKF) v0.2 specification +tags: [okf, reference, spec] +source: docs/agents/use-okf.md (embedded verbatim) +canonical: https://github.com/GoogleCloudPlatform/open-knowledge-format +ingested: 2026-10-02 +--- +# Open Knowledge Format (OKF) + +> [!info] Provenance +> Verbatim copy of the OKF v0.2 spec as embedded in the repo's [[OKF Frontmatter Convention|docs/agents/use-okf.md]]. Canonical home: https://github.com/GoogleCloudPlatform/open-knowledge-format + +**Version 0.2** + +OKF is an open, human- and agent-friendly format for representing +*knowledge*: the metadata, context, and curated insight that surrounds +data and systems. It is designed to be authored by people, generated by +agents, exchanged across organizations, and consumed by both. + +The format is intentionally minimal: a directory of markdown files with +YAML frontmatter. There is no schema registry, no central authority, and +no required tooling. If you can `cat` a file, you can read OKF; if you +can `git clone` a repo, you can ship it. + +This document is self-contained: it specifies everything needed to +produce and consume OKF v0.2. A summary of what changed from v0.1 is in +§13. + +--- + +## 1. Motivation + +The space of knowledge representation for AI agents is evolving quickly, +and many incompatible conventions are emerging. OKF takes the position +that knowledge is best represented in commonly accessible, established +formats that are: + +- **Readable** by humans without tooling. +- **Parseable** by agents without bespoke SDKs. +- **Diffable** in version control. +- **Portable** across tools, organizations, and time. + +Increasingly, a knowledge corpus is not authored once and then read: it +is **continuously written and maintained by agents**. When most concepts +are machine-generated, a consumer needs answers that a plain +markdown-plus-frontmatter convention does not make first-class: + +1. What was this created from, and how was it verified? (**provenance**) +2. How much should I trust it? (**trust**) +3. Is it still true? (**freshness**) +4. Is it the current version? (**lifecycle**) +5. Was this number produced the way we said it must be? (**attestation**) + +OKF v0.2 makes provenance, trust, lifecycle, and attestation first-class +while keeping the format minimally opinionated. The format is minimally +opinionated. It standardizes only the small set of structural conventions +needed to make a knowledge corpus self-describing — anything beyond that +is left to the producer. + +### Goals + +1. Define a universal format that **producers** (people, agents, export + pipelines) can write into. +2. Inform how **consumers** (agents, UIs, search indexes, deterministic + code) should read and traverse it. +3. Facilitate **exchange** of knowledge across systems and organizations. +4. Standardize the small set of frontmatter fields that make an + agent-maintained corpus **trustable**, without prescribing any runtime. + +### Non-goals + +- Defining a fixed taxonomy of concept types. +- Prescribing storage, serving, or query infrastructure. +- Replacing domain-specific schemas (Avro, Protobuf, OpenAPI, and so on). + OKF *references* them; it does not subsume them. +- Specifying a packaging or invocation standard for the code an executor + or attester points at. OKF fixes the interface, not the packaging. + +--- + +## 2. Terminology + +- **Knowledge Bundle** (or **bundle**): A self-contained, hierarchical + collection of knowledge documents. The unit of distribution. +- **Concept**: A single unit of knowledge within a bundle, represented as + one markdown document. It may describe a tangible asset (a table, an + API), an abstract idea (a metric, a business process), or anything in + between. +- **Concept ID**: The path of the concept's file within the bundle, with + the `.md` suffix removed. +- **Frontmatter**: A YAML metadata block delimited by `---` at the top of + a markdown file. +- **Body**: Everything in the file after the frontmatter. +- **Link**: A standard markdown link from one concept to another, used to + express relationships beyond the implicit parent/child hierarchy. +- **Source**: A material a concept derives from, external or internal to + the bundle, recorded in the `sources` frontmatter field. +- **Provenance**: The set of sources a concept derives from. +- **Credibility signal**: An objective, per-source fact (`author`, + `usage_count`, `last_modified`) used to infer trust; OKF records the + signals, not a verdict (see §5.1). +- **Actor**: A string identifying who or what performed an action, using + the convention `<producer>/<version>` for agents, `human:<id>` for + people, and `process:<id>` for automated processes (see §7). +- **Trust tier**: A level derived from a concept's `verified` field: + unverified, machine-confirmed, or human-reviewed (see §5.3). +- **Attested Computation**: A concept (`type: Attested Computation`) + carrying a sanctioned way to compute a value, so a consumer can confirm + the value was produced by running it (see §10). +- **Executor**: Run instructions or code that executes a computation and + returns a receipt (see §10.2). +- **Receipt**: The evidence a run returns, shaped by `executor.receipt`; a + runtime artifact, not stored in the bundle (see §10). +- **Attester**: Deterministic (no-LLM) code that inspects a receipt and + returns a verdict (see §10.2). + +--- + +## 3. Bundle structure + +A bundle is a directory tree of markdown files. The directory structure +is independent of the domain: producers organize concepts however makes +sense for the knowledge being captured. + +``` +path/to/bundle/ + index.md # Optional. Directory listing for progressive disclosure. + log.md # Optional. Chronological history of updates. + <concept>.md # A concept at the bundle root. + <subdirectory>/ # Subdirectories organize concepts into groups. + index.md + <concept>.md + <subdirectory>/ + ... +``` + +A bundle MAY be distributed as: + +- A git repository (recommended, since it provides history, attribution, + and diffs). +- A tarball or zip archive of the directory. +- A subdirectory within a larger repository. + +### 3.1 Reserved filenames + +The following filenames have defined meaning at any level of the +hierarchy and MUST NOT be used for concept documents: + +| Filename | Purpose | +|------------|----------------------------------| +| `index.md` | Directory listing. See §8. | +| `log.md` | Update history. See §9. | + +All other `.md` files are concept documents. + +Tags remain a first-class concept through the `tags` frontmatter field +(§4.1). OKF does not specify a separate file format for aggregating +documents by tag; a consumer that wants a tag-browsing view can +synthesize one at consumption time by scanning frontmatter. + +--- + +## 4. Concept documents + +Every concept is a UTF-8 markdown file with two parts: + +1. A **YAML frontmatter block**, delimited by `---` on its own line at the + start of the file and a closing `---` on its own line. +2. A **markdown body**, containing free-form content. + +### 4.1 Frontmatter + +```yaml +--- +type: <Type name> # REQUIRED +title: <Optional display name> +description: <Optional one-line summary> +resource: <Optional canonical URI for the underlying asset> +tags: [<tag>, <tag>, ...] # Optional +# ... trust, lifecycle, provenance, and computation families (see §5, §10) +# ... other producer-defined key/value pairs +--- +``` + +**Required:** + +- `type`: A short string identifying the kind of concept. Consumers use it + for routing, filtering, and presentation. Example values: + `BigQuery Table`, `BigQuery Dataset`, `API Endpoint`, `Metric`, + `Playbook`, `Reference`, `Attested Computation`. + + Type values are **not** registered centrally. Producers SHOULD pick + values that are descriptive and self-explanatory; consumers MUST + tolerate unknown types gracefully, typically by treating them as generic + concepts. + +`type` is the only always-required key; a concept carrying just `type` is +fully conformant (§11). + +**Recommended:** + +- `title`: Human-readable display name. If omitted, consumers MAY derive a + title from the filename. +- `description`: A single sentence summarizing the concept. Used by + `index.md` generators, search snippets, and previews. +- `resource`: A URI that uniquely identifies the underlying asset the + concept describes. Absent for concepts that describe abstract ideas + rather than physical resources. +- `tags`: A YAML list of short strings for cross-cutting categorization. + +The optional **provenance**, **trust**, and **lifecycle** families (§5) and +the **computation** fields for Attested Computation concepts (§10) may also +appear. + +**Extensions:** Producers MAY include any additional keys. Consumers +SHOULD preserve unknown keys when round-tripping and MUST NOT reject +documents with unrecognized fields. + +### 4.2 Body + +The body is standard markdown. Producers SHOULD favor structural markdown +(headings, lists, tables, fenced code blocks) over freeform prose, since +structure aids both human reading and agent retrieval. + +There are no required body sections. The following headings have +**conventional** meaning and SHOULD be used when applicable: + +| Heading | Purpose | +|-----------------|--------------------------------------------------------| +| `# Schema` | Structured description of an asset's columns/fields. | +| `# Examples` | Concrete usage examples, often as fenced code blocks. | +| `# Computation` | The sanctioned computation of an Attested Computation. See §10. | + +Per-claim attribution to external sources uses markdown footnotes keyed to +`sources` entries rather than a body citations list (§5.1). + +### 4.3 Example: a concept bound to a resource + +```markdown +--- +type: BigQuery Table +title: Customer Orders +description: One row per completed customer order across all channels. +resource: https://console.cloud.google.com/bigquery?p=acme&d=sales&t=orders +tags: [sales, orders, revenue] +generated: { by: reference_agent/gemini-2.5-pro, at: 2026-05-28T14:30:00Z } +--- + +# Schema + +| Column | Type | Description | +|---------------|-----------|------------------------------------------| +| `order_id` | STRING | Globally unique order identifier. | +| `customer_id` | STRING | Foreign key into [customers](/tables/customers.md). | +| `total_usd` | NUMERIC | Order total in US dollars. | +| `placed_at` | TIMESTAMP | When the customer submitted the order. | + +# Joins + +Joined with [customers](/tables/customers.md) on `customer_id`. +``` + +### 4.4 Example: a concept not bound to a resource + +```markdown +--- +type: Playbook +title: "Incident response: data freshness alert" +description: Steps to triage a freshness alert on the orders pipeline. +tags: [oncall, incident] +generated: { by: human:ahormati, at: 2026-04-12T09:00:00Z } +--- + +# Trigger + +A freshness alert fires when `orders` lags more than 30 minutes behind its +expected SLA. See the [orders table](/tables/orders.md). + +# Steps + +1. Check the [ingestion job dashboard](https://example.com/dash). +2. ... +``` + +--- + +## 5. Provenance, trust, and lifecycle + +These frontmatter families make "where did this come from," "how much +should I trust it," and "is it still current" answerable from frontmatter. +All are optional. Their absence carries meaning: an unverified concept is +distinguishable from a verified one, but is never rejected (§11). + +Every timestamp-valued key in OKF is an ISO 8601 datetime with an explicit +UTC offset, for example `2026-06-30T14:00:00Z`. + +### 5.1 Provenance: `sources` + +`sources` records the materials a concept derives from, external or +internal to the bundle. + +```yaml +sources: + - id: ga4-schema + resource: https://developers.google.com/analytics/bigquery/export-schema + title: GA4 BigQuery Export schema + author: team:ga4-docs + usage_count: 5000 + last_modified: 2026-05-30T00:00:00Z +usage_window: { from: 2026-06-01T00:00:00Z, to: 2026-06-30T00:00:00Z } +``` + +Each `sources` entry: + +- `resource`: REQUIRED within an entry. Names either a concrete artifact a + consumer can follow (an absolute URL, a bundle-relative path, or a path + into a `references/` subdirectory, §6) or a population or scope descriptor + it cannot (for example `all queries in BigQuery project X`). +- `id`: Optional. A stable key used to attribute individual claims (see + below). SHOULD be present when the body cites the source. +- `title`: Optional. Human-readable label for the source. +- The optional credibility signals `author`, `usage_count`, and + `last_modified`, described next. + +**Source credibility signals.** OKF records objective, per-source signals +so a consumer can judge how much to trust a concept by judging the sources +it was extracted from. It does not store a credibility score: a score is +subjective, unportable across consumers, and goes stale. Credibility is +*inferred* from the signals, the same way trust tiers are (§5.3), not +stored. Each signal is optional and lives on a `sources` entry: + +- `author`: Who or what produced the source, in the actor convention (§7). + An authority signal. +- `usage_count`: How often `resource` was exercised (dashboard views, query + executions, page reads) over `usage_window`. An adoption and liveness + signal. For a single artifact it is that artifact's own exercise count; + for a scope descriptor it is the number of exercises within the scope that + touch the concept. +- `last_modified`: When the source itself last changed. A recency signal, + distinct from `generated.at` (§5.2), which records when the concept was + written. +- `usage_window`: Written once as a sibling of `sources`, it frames every + `usage_count` with a `{ from, to }` datetime range. A single entry MAY + carry its own `usage_window` to override the shared one. + +`usage_count` is a coarse signal. It is comparable at the +alive-versus-dead and order-of-magnitude level, and against a source's own +history over time, but not as a precise cross-kind ranking: a scheduled +query's executions and a human's deliberate dashboard views do not carry +equal weight. Consumers SHOULD read it as liveness and trend, not as a +score. + +Lineage is expressed through links, not a dedicated field. When a +`resource` points at another OKF concept, the derivation edge already +exists in the bundle graph (§6), so a consumer MAY recurse into that +source's own `sources` and let credibility propagate. External leaf sources +carry only their intrinsic signals. Deeper lineage (an explicit external +`derived_from`, or data lineage) is out of scope for v0.2. + +**Per-claim attribution.** To attribute a specific claim, use a markdown +footnote whose label is a `sources[].id`: + +```markdown +The `events_` table is sharded daily as `events_YYYYMMDD`.[^ga4-schema] + +[^ga4-schema]: GA4 BigQuery Export schema +``` + +The footnote label is the join key into `sources`; consumers resolve +attribution through the matching entry, not by parsing the footnote prose. +Labels are keyed rather than positional (`sources[0]`) because agents +constantly rewrite these documents: a positional index misattributes +silently the moment the list is reordered, whereas a stable `id` survives +reordering. + +### 5.2 Trust: `generated` and `verified` + +`generated` records how the current content was produced. `verified` +records who or what has confirmed the content against its sources or +`resource`. They are kept distinct because who *wrote* a concept need not +be who *confirmed* it. + +```yaml +generated: { by: reference_agent/gemini-2.5-pro, at: 2026-06-20T22:53:05Z } +``` + +- `generated.by`: REQUIRED within `generated`. An actor (§7). +- `generated.at`: An ISO 8601 datetime marking the content's last + meaningful change. Consumers use it to tell a recent edit from a stale + fact. + +```yaml +verified: + - { by: human:ahormati, at: 2026-06-25T09:00:00Z } + - { by: process:finance-nightly, at: 2026-06-26T02:00:00Z } +``` + +- `verified`: A list of verification events, each with `by` (an actor) and + `at` (an ISO 8601 datetime). Multiple entries capture independent + checks, for example a human sign-off plus a nightly process. "How + recently" is the latest `at`. +- `verified` is independent of `generated.at`: content can change without + re-confirmation, and facts can be re-confirmed without regeneration. +- A single verifier MAY be written as one `{ by, at }` mapping without the + list dash. Consumers MUST treat a bare mapping as a one-element list: + +```yaml +verified: { by: human:ahormati, at: 2026-06-25T09:00:00Z } +``` + +### 5.3 Trust tiers + +Consumers derive a trust tier from `verified`, lowest to highest: + +- No `verified` key ⇒ **unverified**. +- `verified` by non-`human:` actors only ⇒ **machine-confirmed**. +- `verified` by a `human:<id>` actor ⇒ **human-reviewed**. + +A concept with no trust frontmatter is still consumable; consumers MUST +NOT reject it (§11). Trust tiers are advisory signals, not access control. + +### 5.4 Lifecycle: `status` + +```yaml +status: stable # draft | stable | deprecated +``` + +- `draft`: not yet reviewed; possibly incomplete. +- `stable`: default; ready for consumption. +- `deprecated`: kept for links and history; no longer current. + +Absent `status` ⇒ `stable`. + +### 5.5 Lifecycle: `stale_after` + +```yaml +stale_after: 2026-09-23T00:00:00Z # content is stale on/after this instant +``` + +Optional. An absolute instant. A concept is stale when +`now >= stale_after`. An absolute instant, not a relative TTL, keeps the +staleness decision a plain comparison with no reference to when the +concept was read. + +--- + +## 6. Cross-linking and paths + +### 6.1 Links between concepts + +Concepts MAY link to other concepts using standard markdown links. Two +forms are supported: + +- **Absolute (bundle-relative):** begins with `/`, interpreted relative to + the bundle root. This is the **recommended** form because it is stable + when documents are moved within their subdirectory. + + ```markdown + See the [customers table](/tables/customers.md) for the join key. + ``` + +- **Relative:** a standard markdown relative path. + + ```markdown + See the [neighboring concept](./other.md). + ``` + +A link from concept A to concept B asserts a *relationship*. The specific +kind (parent/child, references, joins-with, depends-on) is conveyed by the +surrounding prose, not by the link itself. Consumers that build a graph +view typically treat all links as directed edges of an untyped +relationship. + +Consumers MUST tolerate broken links: a link whose target does not exist +in the bundle is not malformed; it may simply represent not-yet-written +knowledge. + +### 6.2 Path-valued fields + +Several fields name a path or URI: `resource`, `sources[].resource`, +`computation`, `executor.resource`, and `attester.resource` (§10). A +`sources[].resource` may instead be a scope descriptor (§5.1), in which +case it is not a path. Each path-valued field accepts: + +- an absolute URL (for example `https://...`), +- a bundle-relative path beginning with `/`, or +- a relative path (for example `../computations/revenue.md`). + +### 6.3 The `references/` convention + +A `references/` subdirectory conventionally mirrors external material, run +instructions, or code as first-class concepts within the bundle. Sources, +executors, and attesters commonly point into it (for example +`references/attesters/revenue.py`). It is a naming convention, not a +requirement. + +--- + +## 7. Actor convention + +Fields that record an identity (`generated.by`, `verified[].by`) use a +single actor convention: + +- `<producer>/<version>` for agents and tools, for example + `reference_agent/gemini-2.5-pro`. +- `human:<id>` for a person, for example `human:ahormati`. +- `process:<id>` for an automated process, for example + `process:finance-nightly`. + +Consumers that classify trust (§5.3) key off the `human:` prefix, so +producers MUST use it for hand-authored or human-confirmed content. + +--- + +## 8. Index files + +An `index.md` file MAY appear in any directory, including the bundle root. +It enumerates the directory's contents to support **progressive +disclosure**: letting a human or agent see what is available before +opening individual documents. + +Index files contain no frontmatter, with one exception: a bundle-root +`index.md` MAY carry an `okf_version` key (§12). The body uses one or more +sections, each grouping concepts under a heading: + +```markdown +# Section / Group Heading + +* [Title 1](relative-url-1) - short description of item 1 +* [Title 2](relative-url-2) - short description of item 2 + +# Another Section + +* [Subdirectory](subdir/) - short description of the subdirectory +``` + +Entries SHOULD include the description from the linked concept's +frontmatter. Producers MAY generate `index.md` automatically; consumers +MAY synthesize one on the fly when none is present. + +--- + +## 9. Log files + +A `log.md` file MAY appear at any level of the hierarchy to record the +history of changes to that scope. The format is a flat list of +date-grouped entries, newest first: + +```markdown +# Directory Update Log + +## 2026-05-22 +* **Update**: Added a BigQuery table reference for [Customer Metrics](/tables/customer-metrics.md). +* **Creation**: Established the [Dataplex Playbook](/playbooks/dataplex.md). + +## 2026-05-15 +* **Initialization**: Created foundational directory structure. +``` + +Date headings MUST use ISO 8601 `YYYY-MM-DD` form. Log entries are prose; +the leading bold word (`**Update**`, `**Creation**`, `**Deprecation**`) is +a convention, not a requirement. + +--- + +## 10. Attested computations concept + +An Attested Computation concept carries not just what a value *means* but a +sanctioned way to *compute* it, so a consumer can confirm the agent ran the +blessed computation instead of improvising its own. Provenance (§5.1) +answers "where did this claim come from"; attestation answers "was this +number produced the way we said it must be." OKF records the computation +and the means to check it; it does not execute anything itself. + +### 10.1 A computation is its own concept + +A sanctioned computation is a standalone concept of +`type: Attested Computation`. A concept that needs the value (a `Metric`, a +`BigQuery Table`) links to it with a normal markdown link (§6). Three +properties motivate the standalone concept: + +- **`runtime` defines what `parameters` mean.** A parameter is a SQL bind + variable, a dbt var, or a Python argument depending on the runtime. + Keeping `runtime` and `parameters` in one frontmatter makes the binding + semantics self-evident. +- **One computation, many consumers.** The same computation can back a + metric, a dashboard concept, and a report; as a concept it is referenced + once and reused. +- **Trust state is per computation.** `verified`, `stale_after`, and a + single `attester` describe one thing. Revenue, profit, and margin each + verify and attest independently, which is three concepts, not three + entries in one frontmatter. + +### 10.2 Contract fields + +The contract is the concept's top-level frontmatter. In addition to the +provenance, trust, and lifecycle families (§5), an Attested Computation +concept carries: + +- `runtime`: REQUIRED for this type. The single field that says how to run + the computation, and so how the executor and attester interpret it and + what `parameters` mean. Example values: `bigquery`, `postgres`, `dbt`, + `python`, `Looker`. +- `parameters`: A list of the typed, named holes the agent may fill. Each + entry: `{ name, type, required }`. Binding semantics follow `runtime`. +- `computation`: Optional. A path (§6.2) to a file holding the + computation, used instead of an inline body fence (see §10.3). Absent ⇒ + the body `# Computation` fence is the computation. +- `executor`: How the computation is run. `resource` names run + instructions or code; a runner (an agent, or deterministic consumer + code) follows it. `receipt` declares the fields a run must return, the + evidence the attester inspects (for example a BigQuery `job_id` and the + SQL the job actually executed). +- `attester`: The deterministic check. `resource` names code (no LLM) that + takes a receipt and returns a verdict. It is meant to run consumer-side. + +What sits behind a `resource` (a Skill, a script, a container) is a +packaging choice; OKF fixes the interface, not the packaging (§1). + +```markdown +--- +type: Attested Computation +title: Revenue for fiscal year +description: Recognized revenue for a fiscal year, per Finance's definition. +status: stable +runtime: bigquery +parameters: + - { name: year, type: integer, required: true } +executor: + resource: references/skills/run-on-bq.md + receipt: [job_id, executed_sql, result] +attester: + resource: references/attesters/revenue.py +generated: { by: reference_agent/gemini-2.5-pro, at: 2026-06-20T22:53:05Z } +verified: { by: human:ahormati, at: 2026-06-25T09:00:00Z } +stale_after: 2026-09-23T00:00:00Z +sources: + - id: rev-policy + resource: https://wiki.acme/finance/revenue-recognition + title: Revenue recognition policy +--- + +# Computation + + SELECT SUM(amount) AS revenue + FROM finance.recognized_revenue + WHERE fiscal_year = @year + +The computation binds only the declared `parameters`, per the recognition +policy.[^rev-policy] + +[^rev-policy]: Revenue recognition policy +``` + +### 10.3 The computation + +Provide the computation in one of two ways: + +- **Inline:** a single fenced code block in the body under `# Computation`. + Best for a short computation reviewed alongside the contract. +- **File:** set `computation` to a path (§6.2) and omit the body fence. + Best for a long or generated computation, or one already kept as a real + file shared with non-OKF tooling. + +```yaml +runtime: bigquery +computation: references/computations/lib/revenue.sql +parameters: + - { name: year, type: integer, required: true } +``` + +The agent MAY only supply *values* for the declared `parameters`; it MUST +NOT author or edit the computation. Binding `computation` with the +parameter values into the executable artifact is the consumer's job, and +the attester independently re-derives that same binding to compare against +what actually ran. Because the comparison is on the expanded, compiled +artifact the receipt carries (`executed_sql`, `compiled_sql`), a rewritten +query, a swapped computation file, or a mutated dependency fails the check. +A typed, parameter-only surface is what makes "did the sanctioned thing +run" a mechanical comparison rather than a judgement call. + +### 10.4 Concepts that use a computation + +A document is rarely a single computation. An income-statement overview +that discusses revenue, profit, and margin stays one readable concept and +links to one Attested Computation per figure: + +```markdown +--- +type: Metric +title: Revenue +description: Recognized revenue for a fiscal year. +tags: [finance, revenue] +status: stable +generated: { by: reference_agent/gemini-2.5-pro, at: 2026-06-20T22:53:05Z } +--- + +# Definition + +Recognized revenue sums `amount` over rows booked to the fiscal year, +computed by [the revenue computation](../computations/revenue.md). +``` + +Because each computation is its own concept, revenue can be fresh while +profit is past its `stale_after`, and each attests on its own run. +Co-locating them is a directory choice (a `computations/` folder with an +`index.md`), not a frontmatter one. + +### 10.5 How a consumer uses it (informative) + +This subsection is informative, not normative. The runtime artifacts below +are **not** stored in the bundle. + +1. **Discover** via `type: Attested Computation`, a frontmatter signal + liftable into `index.md`; a consumer reaches one directly or by + following a link from a concept that uses it. +2. **Load** the contract from frontmatter and the computation from the + body (or the file named by `computation`). +3. **Parameterize**: the agent supplies values for the declared parameters. +4. **Execute**: the executor runs the bound computation and returns a + receipt shaped by `executor.receipt`. +5. **Attest**: the consumer runs the attester over the receipt. It + confirms provenance (the computation that ran equals `computation` bound + with the claimed parameters, not agent-authored SQL) and fidelity (the + displayed value matches the receipt's authoritative source, re-read by + job id rather than taken from the agent's text). +6. **Gate**: refuse to display a failing attestation; warn or refuse when + `now >= stale_after`. On success, surface the verdict (for example a + link to the job log) so trust is visible. + +### 10.6 Verification versus attestation + +`verified` (§5.2) and attestation are distinct, and both exist: + +- `verified` confirms the *definition* still matches policy. It is + doc-level, slow, and recorded in the bundle. +- Attestation confirms a single *run* produced the value the sanctioned + way. It is per-call, runtime, and not stored in the bundle. + +A concept with a stale definition can still attest cleanly, and a +freshly-verified definition still requires attestation on each run, which +is why both are needed. + +--- + +## 11. Conformance + +A bundle is **conformant** with OKF v0.2 if: + +1. Every non-reserved `.md` file in the tree contains a parseable YAML + frontmatter block. +2. Every frontmatter block contains a non-empty `type` field. +3. Every reserved filename (`index.md`, `log.md`) follows the structure in + §8 and §9 respectively when present. + +When the trust, lifecycle, provenance, or computation families are +present, producers SHOULD follow §5 through §10, and consumers: + +- MUST treat a bare `verified` mapping as a one-element list (§5.2). +- MUST NOT reject a concept for missing any optional family (§5.3). +- SHOULD derive trust tiers and staleness only from the fields specified + here, and SHOULD surface, not silently drop, a failing attestation + (§10.5). + +Consumers SHOULD treat all other constraints as soft guidance. In +particular, consumers MUST NOT reject a bundle because of: + +- Missing optional frontmatter fields. +- Unknown `type` values. +- Unknown additional frontmatter keys. +- Broken cross-links. +- Missing `index.md` files. + +--- + +## 12. Versioning + +This document specifies OKF version **0.2**. Revisions are versioned as +`<major>.<minor>`: + +- A **minor** version bump introduces backward-compatible additions (new + optional fields, new conventional section headings). +- A **major** version bump may make breaking changes (renaming required + fields, changing reserved filenames). + +Bundles MAY declare the version they target with `okf_version: "0.2"` in a +bundle-root `index.md` frontmatter block (the only place frontmatter is +permitted in an `index.md`). Consumers that do not understand the declared +version SHOULD attempt best-effort consumption rather than refusing the +bundle. + +### Considered and deferred + +The following are intentionally left to a future revision: + +- The full runtime protocol: receipt and verdict wire formats, and the + attestation lifecycle around a run. +- The attester ABI, portability, and sandboxing, likely bundled with + future work on serving and Skills. +- Attestation caching. +- Semantic-layer templates (Looker, dbt) where the attester comparison + shifts from SQL equality to model-and-binding equality. + +--- + +## 13. Changes from v0.1 + +v0.2 supersedes OKF v0.1 and is a minor version bump under §12, except for +two deliberate breaking changes called out below because they rename or +retire v0.1 fields. A v0.1 bundle is consumable by a v0.2 consumer under +the fallbacks noted here. + +### 13.1 Breaking changes + +- **`timestamp` is superseded by `generated.at`.** A concept's last + content change is now recorded as `generated: { by, at }` (§5.2). + Consumers MAY fall back to a legacy `timestamp` when `generated` is + absent. +- **The body `# Citations` list is superseded by `sources`.** Provenance + moves to frontmatter (§5.1). Consumers SHOULD read `sources` and MAY + still parse a legacy `# Citations` body list for v0.1 documents. + +### 13.2 Additive changes + +All of the following are additive: new optional keys, one new concept +type, and one new conventional heading. Their absence yields a plain v0.1 +concept. + +- New frontmatter families: `sources` with its per-source credibility + signals (`author`, `usage_count`, `last_modified`) and the `usage_window` + sibling; `generated`, `verified`; `status`, `stale_after` (§5). +- New concept type `Attested Computation` and its computation keys + `runtime`, `parameters`, `computation`, `executor`, `attester` (§10). +- New conventional body heading `# Computation` (§4.2). +- The actor convention for `generated.by` and `verified[].by` (§7). + +Everything else (bundle structure, reserved filenames, the required +`type`, recommended `title`/`description`/`resource`/`tags`, cross-linking, +index files, log files, permissive conformance) is carried forward +unchanged. + +--- + +## Appendix A: Worked example, an income statement + +One bundle exercising every family, shown as a v0.1 to v0.2 migration of an +income statement with two figures, revenue and gross profit. + +### v0.1 form + +A single doc: both figures in one concept, the SQL in prose an agent can +read, ignore, or rewrite, citations a flat list, and the only timestamp is +`timestamp`. + +```markdown +--- +type: Metric +title: Income statement (fiscal year) +description: Headline income-statement figures for a fiscal year. +tags: [finance, income-statement] +timestamp: '2026-05-28T22:53:05+00:00' +--- + +# Definition +The income statement reports revenue and gross profit for a fiscal year. + +# Revenue +Recognized revenue sums `amount` over rows booked to the fiscal year: + + SELECT SUM(amount) AS revenue + FROM finance.recognized_revenue + WHERE fiscal_year = <year> + +# Gross profit +Gross profit by segment, per the cost-allocation standard: + + SELECT gross_profit FROM fct_income_statement + WHERE fiscal_year = <year> AND segment = <segment> + +# Citations +- https://wiki.acme/finance/fpa-handbook +- https://wiki.acme/finance/revenue-recognition +- https://wiki.acme/finance/cost-allocation +``` + +### v0.2 form + +The two figures split into attested computations linked from a narrative +concept. Every family is populated, and the two computations sit in +deliberately different states so one consumer reaches two verdicts. + +``` +bundles/finance/ + metrics/income-statement.md type: Metric (narrates, links both) + computations/revenue.md type: Attested Computation (runtime: bigquery) + computations/profit.md type: Attested Computation (runtime: dbt) + references/skills/run-on-bq.md, run-dbt.md + references/attesters/sql-equality.py, dbt-binding.py +``` + +`metrics/income-statement.md`, the readable doc; trust lives on what it +links, not here: + +```markdown +--- +type: Metric +title: Income statement (fiscal year) +description: Headline income-statement figures for a fiscal year. +tags: [finance, income-statement] +status: stable +generated: { by: reference_agent/gemini-2.5-pro, at: 2026-06-20T22:53:05Z } +verified: { by: human:ahormati, at: 2026-06-25T09:00:00Z } +stale_after: 2026-12-31T00:00:00Z +sources: + - id: fpa-handbook + resource: https://wiki.acme/finance/fpa-handbook + title: FP&A reporting handbook +--- + +# Definition +The income statement reports [revenue](../computations/revenue.md) and +[gross profit](../computations/profit.md) for a fiscal year, per the FP&A +reporting handbook.[^fpa-handbook] Each figure is produced by a sanctioned, +attestable computation; this concept only narrates them. + +[^fpa-handbook]: FP&A reporting handbook +``` + +`computations/revenue.md`, BigQuery SQL, human-verified, fresh, and +corroborated by a live dashboard source carrying credibility signals: + +```markdown +--- +type: Attested Computation +title: Revenue for fiscal year +description: Recognized revenue for a fiscal year, per Finance's definition. +tags: [finance, revenue] +status: stable +runtime: bigquery +parameters: + - { name: year, type: integer, required: true } +executor: + resource: references/skills/run-on-bq.md + receipt: [job_id, executed_sql, result] +attester: + resource: references/attesters/sql-equality.py +generated: { by: reference_agent/gemini-2.5-pro, at: 2026-06-28T14:00:00Z } +verified: { by: human:ahormati, at: 2026-06-25T09:00:00Z } +stale_after: 2026-12-31T00:00:00Z +sources: + - id: rev-policy + resource: https://wiki.acme/finance/revenue-recognition + title: Revenue recognition policy + author: team:finance-fpa + last_modified: 2026-04-02T00:00:00Z + - id: exec-rev-dash + resource: dashboards/exec-revenue + title: Executive revenue dashboard + author: team:finance-fpa + usage_count: 5000 + last_modified: 2026-06-18T00:00:00Z +usage_window: { from: 2026-06-01T00:00:00Z, to: 2026-06-30T00:00:00Z } +--- + +# Computation + + SELECT SUM(amount) AS revenue + FROM finance.recognized_revenue + WHERE fiscal_year = @year + +Recognized revenue per the recognition policy,[^rev-policy] corroborated by +the executive revenue dashboard.[^exec-rev-dash] + +[^rev-policy]: Revenue recognition policy +[^exec-rev-dash]: Executive revenue dashboard +``` + +`computations/profit.md`, a dbt model, process-verified, and past its +`stale_after`: + +```markdown +--- +type: Attested Computation +title: Gross profit for fiscal year +description: Gross profit by segment for a fiscal year, per the cost-allocation standard. +tags: [finance, profit] +status: stable +runtime: dbt +parameters: + - { name: year, type: integer, required: true } + - { name: segment, type: string, required: true } +executor: + resource: references/skills/run-dbt.md + receipt: [run_id, compiled_sql, result] +attester: + resource: references/attesters/dbt-binding.py +generated: { by: reference_agent/gemini-2.5-pro, at: 2026-06-14T14:00:00Z } +verified: { by: process:finance-nightly, at: 2026-06-12T08:00:00Z } +stale_after: 2026-06-15T00:00:00Z +sources: + - id: cost-alloc + resource: https://wiki.acme/finance/cost-allocation + title: Cost allocation standard +--- + +# Computation + + SELECT gross_profit + FROM {{ ref('fct_income_statement') }} + WHERE fiscal_year = {{ var('year') }} + AND segment = {{ var('segment') }} + +Gross profit by segment per the cost-allocation standard.[^cost-alloc] + +[^cost-alloc]: Cost allocation standard +``` diff --git a/vault/Coordination Board.md b/vault/Coordination Board.md new file mode 100644 index 0000000..d8b8eaf --- /dev/null +++ b/vault/Coordination Board.md @@ -0,0 +1,63 @@ +--- +type: dashboard +title: Coordination Board +description: Live snapshot of the DOMinic GitHub Issues board as maintained by the /coordinate skill. +tags: [dominic, coordination, board] +repo_head: "be8645f" +refreshed: 2026-09-12 23:45 ET +--- +# Coordination Board + +Snapshot at **2026-09-12 23:45 ET**, `origin/main` = `be8645f`. Hub issue: #5. Regenerated every watch tick; edit on GitHub, not here. Back to [[DOMinic Home]]. + +> [!note] Sprint closed +> The build sprint ended at the 4:00 PM ET portal close. This is the final +> board snapshot; open issues below are post-sprint polish, not live work. +> The protocol behind it is [[Coordination Protocol]]. + +## Board (`coord.sh board`) + +``` +ISSUE STATUS WS P OWNER TITLE FILES +#54 - - - - 📊 Status Dashboard (live) +#10 in-progress docs p0 @m-vawter Submission package: written description, soc docs/agents/demo-path.md +#9 claimed docs p0 @r0073d-l053r Demo video: record the core loop, wrap in Hy (outside repo: ~/DFIM/DOMinic-video/dominic-demo +#19 in-progress docs p1 @m-vawter Coordination playbook v1: adopt the sprint m docs/agents/coordination-best-practices.md +``` + +## By workstream + +### ws:docs +- [#9](https://github.com/xcjs/DOMinic/issues/9) **p0** claimed — Demo video: record the core loop, wrap in HyperFrames, render, publish a public YouTube link — @r0073d-l053r +- [#10](https://github.com/xcjs/DOMinic/issues/10) **p0** in-progress — Submission package: written description, social post, and portal submission by 3:50 — @m-vawter +- [#19](https://github.com/xcjs/DOMinic/issues/19) **p1** in-progress — Coordination playbook v1: adopt the sprint minimum and report what is working and what is not — @m-vawter + +### ws:- +- [#54](https://github.com/xcjs/DOMinic/issues/54) **-** - — 📊 Status Dashboard (live) — unclaimed + +## Stale claims (`coord.sh stale`) + +``` +#19 @m-vawter 434m silent Coordination playbook v1: adopt the sprint minimum and report what is working and what is not +#10 @m-vawter 436m silent Submission package: written description, social post, and portal submission by 3:50 +#9 @r0073d-l053r 436m silent Demo video: record the core loop, wrap in HyperFrames, render, publish a public YouTube link +``` + +## Needs human + +- #10 Submission package: written description, social post, and portal submission by 3:50 + +## Last hub SYNCs + +- 20:24Z codex/gpt-5.6 | human: @m-vawter — PR #66 reviewed for @ImNewToC0de: conflict resolved against main, create-vs-update source fallback guarded, pushed febc653, approved; Markdown lint/typecheck/build all green and ready to merge. +- 20:26Z codex/gpt-5.6 | human: @m-vawter — PR #66 final head 196b7fd reviewed and approved for @ImNewToC0de; mergeable CLEAN with Markdown lint/typecheck/build green. Justin incorporated the create-intent guard and centralized Ask-to-Fix source context. +- 20:29Z claude-code/claude-fable-5.1 | human: @m-vawter — FREEZE (ws:docs 16:29): the form is submitted; origin/main is now the repo judges will open (JUDGES.md first row, ADRs as-built, lint+typecheck+build CI green). No further merges to main except a fix verified by booting main - every merge from here is unreviewed-by-judges risk. Video link (#9) is with @r0073d-l053r. +- 20:31Z codex/gpt-5 | human: @m-vawter — final: coordination v3 merged via PR #68; v2 superseded; scope/completion/hub invariants documented; helper enforcement queued as #69; reports posted on #65 +- 20:46Z claude-code/claude-fable-5.1 | human: @m-vawter — ws:docs wrap-up (https://github.com/xcjs/DOMinic/pull/71): docs/research/ now holds the Q01 question + all three answers behind the coordination playbooks (they were 'outside the repo' until now), and the finish plan is checked off with a 16:40 closing status. After this merges ws:docs has nothing further to push; #10 closes when the video link and post are in (#9 is Brandon's). Thanks all - five agents, one afternoon. +- 20:56Z claude-code/claude-fable-5.1 | human: @m-vawter — ws:docs: https://github.com/xcjs/DOMinic/pull/73 corrects docs/submission-form.md Team contributions - the merged draft credited @m-vawter with Charles's runtime/VFS/Settings work and omitted @Sullux, @r0073d-l053r and @ImNewToC0de. One entry per person, each tied to its merged PR. @xcjs if that text went into the portal, re-paste from the PR. (Replaces my 16:56 line that went out with a blank link.) +- 21:29Z opencode/glm-5.3-flash | human: @xcjs — PR #75 (coord v3 invariants, #69) + PR #74 (AGPL license) open - both need 2 approvals. #75 gates were live-tested against the real board: empty-Files claim refusal (#54), unchecked-Done-when + unmerged-PR done refusals, PR-path-vs-Files validation (it even caught its own PR until a dot-trim bug was fixed). DEP-DONE spam removed from done in both scripts. +- 21:44Z opencode/glm-5.3-flash | human: @xcjs — Heads-up: main branch protection actually requires 1 approval (not 2) - verified via the API. PR #73 merged (0d66819). Two of mine are waiting on a single outside approval each: PR #74 (AGPL-3.0 license: LICENSE + package.json + README section) and PR #75 (coord v3 invariants #69: file-scope claim/review gates, done gate, COORD_HUB parity, DEP-DONE removal; live-tested). Any teammate: an approving review on either unblocks merge. @Sullux @r0073d-l053r @m-vawter @ImNewToC0de + +## Open PRs + +- (none) diff --git a/vault/Coordination/Agent Coordination over GitHub Issues.md b/vault/Coordination/Agent Coordination over GitHub Issues.md new file mode 100644 index 0000000..6bbf762 --- /dev/null +++ b/vault/Coordination/Agent Coordination over GitHub Issues.md @@ -0,0 +1,96 @@ +--- +type: playbook +title: Agent coordination over GitHub Issues +description: How coding agents coordinate ownership, active work, interfaces, review, and completion through GitHub Issues. +generated: { by: codex/gpt-5, at: 2026-09-12T20:13:30Z } +verified: { by: human:michael, at: 2026-09-12T20:13:30Z } +sources: + - id: skill + resource: ../../.claude/skills/coordinate/SKILL.md + title: coordinate skill (protocol and workflow) + - id: protocol + resource: ../../.claude/skills/coordinate/references/protocol.md + title: Coordination protocol v2 (wire format and invariants) + - id: playbook + resource: coordination-best-practices-3.md + title: Coordination best practices v3 +tags: [agents, coordination, process] +source: docs/agents/coordination.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# Agent coordination over GitHub Issues + +Five engineers and their agents are working the same repository in +parallel. GitHub Issues is the shared memory every agent already has, +so it doubles as the task board and the negotiation channel. + +Before your first command, read +[[Coordination Best Practices v3|coordination-best-practices-3.md]]. It is +the active, evidence-calibrated playbook. V1 and v2 remain as historical +records. + +## The protocol in four lines + +- **Assignment owns responsibility.** An issue with an assignee has one + accountable owner; `status:in-progress` identifies active work. +- **Labels describe state.** `ws:*` workstream, `status:*` lifecycle, + `p0`-`p2` priority, `type:*`, and `needs-human` for escalation. +- **Scopes and completion are verified.** `## Files` must cover the linked + PR diff, and `DONE` requires a merged PR plus checked criteria. +- **Comments are structured.** Every protocol comment opens with a + bold verb and an actor line, for example + `**CLAIM** | agent: pi/1 | human: @xcjs | at: 2026-09-12T18:20:00Z`. + +## Where it lives + +| Path | Contents | +| --- | --- | +| `.claude/skills/coordinate/SKILL.md` | Workflow: session start, claiming, status, negotiation, finishing | +| `.claude/skills/coordinate/references/protocol.md` | Verb table, tie-break rules, body template, parsing recipes | +| `.claude/skills/coordinate/scripts/coord.sh` | The one script every agent runs (`bash`, `gh`) | +| `docs/agents/coordination-best-practices-3.md` | Active rules, evidence, parameters, and automation status | + +Claude Code loads the skill automatically from the repo; invoke it as +`/coordinate sync`. Any other agent with `gh` runs the script directly: + +```bash +export COORD_AGENT="pi/1" # say who you are +bash .claude/skills/coordinate/scripts/coord.sh sync +``` + +## Daily loop (full protocol) + +Sprint mode ended with +[ADR 0012](../adrs/0012-end-of-hackathon-scope.md); the parameters below +are v3's "full protocol after the sprint". + +1. `sync` before touching files. Bash routes `SYNC` to `COORD_HUB` or the + lowest-numbered open `hub`; PowerShell currently assumes one hub label. +2. Claim responsibility with a declared file scope and a branch, PR URL, + or `no-code-yet`. Post `status` when work begins; keep one issue + `in-progress` and at most one blocked. +3. Record milestones with `STATUS`; during active work do not go longer + than 60 minutes without one. A claim with no issue, branch, or + linked-PR activity for six hours during a working day gets a + `QUESTION`; release only after three more hours unanswered, and never + overnight. `COORD_STALE_MIN` defaults to 360. +4. Put cross-slice interfaces in `## Agreed` on a `type:contract` issue. + An explicit `ACCEPT` from the seam owner is required before shared + adapter code merges; silence never accepts. +5. `HANDOFF` whenever ownership crosses a session or a person: branch, + PR, validation state, exact stopping point, next action. +6. Before `review`, compare the PR paths with the union of linked issue + scopes. Before `done`, verify the PR is merged and every criterion is + checked. Lanes and the reviewer rotation are in + [NEXT.md](../../NEXT.md#roles-and-working-agreement). + +The live board has one canonical hub. Bash also handles duplicate labels +deterministically; PowerShell parity is pending. File-overlap, PR-scope, and +completion checks are protocol requirements but remain manual preflights in +both helpers. They must not be skipped merely because a command succeeds. + +The rules exist because parallel agents fail in predictable ways: +duplicated work, guessed interfaces, and silent claims. Each command +closes one of those gaps. diff --git a/vault/Coordination/Coordination Best Practices v1.md b/vault/Coordination/Coordination Best Practices v1.md new file mode 100644 index 0000000..577230e --- /dev/null +++ b/vault/Coordination/Coordination Best Practices v1.md @@ -0,0 +1,147 @@ +--- +type: playbook +title: Coordination Best Practices — Sprint Minimum and Full Protocol +description: Evidence-based hardening of the /coordinate protocol — which verbs and parameters to run while the clock is short, what to drop, and when to switch back to the full protocol. +status: superseded +superseded_by: coordination-best-practices-2.md +generated: { by: claude-code/claude-fable-5.1, at: 2026-09-12T18:30:00Z } +sources: + - id: q01-external + resource: Q01 — 2026-09-12-14-16-Q01-Agent-First-GitHub-Issues-Coordination.md (research answer held outside the repo by ws:docs) + title: Q01 research answer, external deep research run by human:michael + - id: protocol + resource: ../../.claude/skills/coordinate/references/protocol.md + title: Coordination protocol v1 + - id: coordination + resource: coordination.md + title: Agent coordination over GitHub Issues +tags: [coordination, playbook, historical] +source: docs/agents/coordination-best-practices.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# Coordination best practices — sprint minimum and full protocol + +> **Superseded** on 2026-09-12 by +> [[Coordination Best Practices v2|coordination-best-practices-2.md]], +> which reconciles three research answers. Kept for the record; follow +> v2 where they differ. + +Issues plus `gh` is the right ledger for agents: they cannot hear the +table. It is the wrong place for human ceremony. This playbook hardens +protocol v1 (`coordination.md`) with the Q01 research answer and the +review of PR #7. Read it in three minutes at session start. The rules +change at **16:00 ET**, when the sprint ends. + +## The three rules + +1. **Lease, don't lock.** Assignment still means ownership. Hold one + claim at a time. A claim goes stale after **90 minutes** without a + heartbeat, not 45; before releasing someone else's claim, post a + QUESTION and wait ten minutes. Forty-five minutes false-releases a + heads-down human and kills trust in the board. +2. **Contracts only on seams.** `type:contract`, PROPOSE, ACCEPT, and + the **Agreed** block stay for cross-slice APIs — the `install_app` + handler calling the VFS and the window opener. Intra-slice work + ships behind CLAIM plus Files globs; no ACCEPT-before-code. +3. **Tiny PRs into `main` every 20–30 minutes, one writer per kernel + file.** Verify by booting `main`, not by reading the board. Hub SYNC + is a one-liner. + +## Sprint minimum — now until 16:00 ET + +Use this while the wall-clock to the demo is four hours or less, or +whenever the humans are at the same table. + +| Use today | Drop today | +| --- | --- | +| CLAIM, RELEASE, BLOCKED, QUESTION, ANSWER, PROPOSE, ACCEPT, DONE, HANDOFF, SYNC (hub only) | Mandatory STATUS; plan and ETA on every CLAIM; COUNTER or REJECT beyond one round; REVIEW on the issue; DEP-DONE comments; ACCEPT-before-code outside `type:contract` | + +Keep regardless: the bold-verb header, assignment as the lock, the +label state machine, Files globs, the hub, and `needs-human`. + +Switch back to the full protocol after submission, or if the humans +leave the table. + +## Parameters + +| Parameter | Sprint | Full | Why | +| --- | --- | --- | --- | +| Stale claim | **90 min** | 45 min | Lease TTL ≈ max(3 × heartbeat, p99 stall); in a sprint the p99 stall is a heads-down burst | +| Heartbeat | On state change only | Every 30 min | Every comment is a content-create against GitHub's secondary limits | +| Negotiation cap | **2 rounds**, then `needs-human` | 4 | Loops burn clock; four issue rounds can eat half the remaining sprint | +| Claims per agent | **1** | 2 | Kanban WIP ≈ n or n−1; Copilot's assign-to-agent default is one | +| Ping before release | 10 min | 15 min | Preserves trust in the board | + +Run the sprint stale value today without waiting for a script change: + +```bash +export COORD_STALE_MIN=90 +``` + +The one-claim and two-round rules are behavioural until the script +enforces them (proposed to the skill owner on #6). + +## Written down versus said aloud + +Talk across the table — it is the cheapest channel. Then **write the +result**: who owns what, what the seam is, what was decided. An agent +that was not in the conversation stays correct only if the ledger has +it. If a conversation changed an owner, a seam, or a decision, it goes +on the issue; if it answered a question, the answer goes on the issue. + +## Integration cadence + +- PR into `main` every 20–30 minutes; rebase before opening. +- One writer per kernel file (`app/shared/**`, the stores); everyone + else proposes on the contract issue. +- Verification is booting `main`. "Looks coordinated" is not a state. +- Never push a follow-up commit to an open PR here — the owner merges + within minutes and the commit strands. Open a new PR instead. + +## Failure modes and defenses + +| Failure (evidence) | Defense | +| --- | --- | +| Guessed interface, failure to clarify (MAST: inter-agent misalignment 36.9%) | A contract issue with **Agreed** on every seam; ask before building | +| Premature "done", no verification (MAST: ~7–8% each) | DONE only after the change runs on `main`; the PR body says what was verified | +| Negotiation loop (CAMEL ends loops with max turns and an explicit done signal) | Two rounds, then `needs-human`; ACCEPT is the done signal | +| Silent stall, then a false stale release | 90-minute lease; QUESTION first; a human runs `stale` once an hour | +| STATUS storm hitting GitHub secondary limits (80 content-creates/min, 500/hour) | Comment on state change only; `sync` and `board` are read-only | +| Copilot-style "assign and walk away" | Our agents share no PR session — keep the conversation on the issue | + +## Human escalation + +`needs-human` is the fastest path when two agents lack the context to +choose. Humans skim that label between tasks; an agent posts +`question --human` and moves on. Decisions listed in `QUESTIONS.md` +belong there, not in PROPOSE loops. + +## Feedback + +Reply on the feedback issue linked from the hub with a verb line: what +worked, what cost time, what you would change. Version 1.1 of this +playbook merges those replies with the remaining research answers. + +## Sources + +From the Q01 research answer. Rung 1 = primary source verified; +rung 5 = single source. The exact minute values are analogy, not +measurement. + +- MAST taxonomy of multi-agent failures (Cemri et al., 2025): + specification failures 41.8%, inter-agent misalignment 36.9%; + fail-to-clarify 11.65%, reasoning-action mismatch 13.98%, premature + termination 7.82%. Rung 1. +- GitHub Copilot coding agent documentation: assignment is the + primitive; the agent does not read issue comments after assignment. + Rung 1. +- CAMEL: max-turns plus an explicit TASK_DONE signal to end agent + loops. Rung 1. +- GitHub REST secondary rate limits (900 points/min, 80 + content-creates/min, 500/hour) and `gh` agent-polling issue + cli/cli#13357. Rung 1. +- Kanban WIP limits, trunk-based development, and OpenHands CAID on + integration-time conflicts. Rung 2. +- Lease-TTL reasoning behind the 90-minute stale value. Rung 5. diff --git a/vault/Coordination/Coordination Best Practices v2.md b/vault/Coordination/Coordination Best Practices v2.md new file mode 100644 index 0000000..2713219 --- /dev/null +++ b/vault/Coordination/Coordination Best Practices v2.md @@ -0,0 +1,209 @@ +--- +type: playbook +title: Coordination Best Practices v2 — Sprint Minimum and Full Protocol +description: Historical sprint playbook, superseded by coordination-best-practices-3.md. +status: superseded +supersedes: coordination-best-practices.md +superseded_by: coordination-best-practices-3.md +generated: { by: claude-code/claude-fable-5.1, at: 2026-09-12T18:45:00Z } +sources: + - id: q01-answer-1 + resource: Q01 answer 1 — 2026-09-12-14-16-Q01-Agent-First-GitHub-Issues-Coordination.md (external deep research run by human:michael; held outside the repo) + title: Q01 answer 1 — lease don't lock, contracts on seams, tiny PRs + - id: q01-answer-2 + resource: Q01 answer 2 — 2026-09-12-14-20-Q01-Agent-First-GitHub-Issues-Coordination.md (external deep research run by human:michael; held outside the repo) + title: Q01 answer 2 — six-verb sprint set, 20-minute lease, file-glob ownership + - id: q01-agent + resource: Q01 agent report — 2026-09-12-Q01-Agent-First-GitHub-Issues-Coordination.md (in-session deep-researcher; held outside the repo) + title: Q01 agent report — declare-then-proceed, two-stage stale ladder, identity by login + - id: v1 + resource: coordination-best-practices.md + title: Coordination best practices v1 (superseded) + - id: protocol + resource: ../../.claude/skills/coordinate/references/protocol.md + title: Coordination protocol v1 +tags: [coordination, playbook, historical] +source: docs/agents/coordination-best-practices-2.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# Coordination best practices v2 — sprint minimum and full protocol + +> **Historical record:** This playbook is superseded by +> [[Coordination Best Practices v3|coordination-best-practices-3.md]]. +> V3 is the active guidance and defines the file-scope, completion, and +> canonical-hub invariants. This document preserves the rules used during +> the 2026-09-12 sprint; do not use it as the session-start protocol. + +This document superseded +[[Coordination Best Practices v1|coordination-best-practices.md]], which is +also kept for the record. Three independent research answers to the same +question (Q01) agreed on the shape and disagreed on the numbers. The text +below records the reconciliation used during the sprint. + +## Why Issues at all + +The agents cannot hear the table. Any decision made aloud is invisible +to four of the five workers. Issues is not overhead; it is the only +write channel into the agents' shared world. Humans talk freely; the +ledger exists so absent agents stay correct. Everything in the protocol +that does not keep an absent agent correct is cuttable today. + +## The five rules + +1. **Own by assignment; fence by merge.** Assignment says who is + working on what. It is an efficiency lock with no fencing token, so + correctness never rests on it: *release is a coordination signal, + not a code lock — the git branch is the fence.* Small PRs the owner + merges are where collisions actually get caught. +2. **Files globs are the most valuable field on the board.** Every + CLAIM names the globs it will touch. Before claiming, check the + board for overlap. The shared kernel (`app/shared/**`, the stores) + has one writer; everyone else proposes on its contract issue. +3. **Declare seams before code; do not wait for permission.** On a + `type:contract` issue, PROPOSE the interface and write it into + **Agreed** marked *provisional*, then build against it immediately. + One COUNTER is allowed; a second disagreement goes to + `needs-human`. Un-countered for 15 minutes, it is binding. +4. **Lease in two stages.** Silent for 20 minutes on all channels — + comments, commits, PR pushes? Post a QUESTION. Still silent 10 + minutes later? `release --stale`. Never release without the ping. +5. **Tiny PRs into `main`, continuously.** Branch per issue, rebase, + PR every 20–30 minutes, owner merges. Verify by booting `main`. + Never push a follow-up commit to an open PR here — open a new one. + +## Sprint minimum — until 16:00 ET + +| Use | Meaning | +| --- | --- | +| CLAIM | Assign yourself; the body carries a one-line plan and the Files globs. No ETA. | +| BLOCKED | Say by what (`#n` or `@login`) and what would unblock. Fold questions into the body. | +| PROPOSE | Declare a seam interface on a `type:contract` issue, then proceed. | +| ACCEPT | Optional fast confirmation; silence for 15 minutes accepts. | +| DONE | Only with a *merged* PR URL and every `## Done when` box ticked. | +| SYNC | Hub only: once at session start, once when the golden path first boots on `main`, once at your last merge. | + +Two more, used sparingly: **QUESTION** to ping a silent claim or to +escalate with `--human`; **RELEASE** to hand a claim back. + +Dropped for the sprint (restored in the full protocol): clock-driven +STATUS, plan-and-ETA requirements, COUNTER beyond one round, REJECT, +HANDOFF, UNBLOCKED, DEP-DONE, REVIEW-on-issue, the +`claimed`/`in-progress` distinction (treat them as one state; the +assignee is the real state), and priorities other than `p0`. + +Liveness is activity, not ceremony: commits, PR pushes, and issue +updates count as heartbeats. A STATUS is only needed when you have been +silent on all three for 20 minutes. + +## Parameters + +| Parameter | Sprint | Full | Basis | +| --- | --- | --- | --- | +| Stale ladder | warn at **20 min**, release at **30 min** | 45 min | A lease is a small fraction of lifetime (Chubby); remind-then-unclaim is the OSS claim-bot norm (Zulipbot); the fence sentence bounds the harm | +| Negotiation | PROPOSE → one COUNTER → `needs-human` | four rounds | Escalate early (SRE); loops are a documented agent failure (MAST step repetition; CAMEL max turns) | +| Auto-accept | 15 min un-countered | explicit ACCEPT | Below the 20-min silence ceiling, so a live agent will have seen it | +| Claims per agent | **1 active** (+1 blocked) | 2 | Little's law; unfinished work at a hard deadline scores zero | +| `needs-human` unanswered | 10 min → post the reversible assumption, continue | wait | Escalation ladders re-escalate; they do not block forever | +| Board polling | ≤ 1 read per minute, one `gh issue list --json` call | same | GitHub secondary limits — 900 points/min, 80 content-creates/min, 100 concurrent — bite before the 5,000/hour cap | + +Run the sprint values today with no script change: + +```bash +export COORD_STALE_MIN=30 # the script releases at 30; you ping at 20 +``` + +The three answers disagreed on the lease: 90 minutes (answer 1, to +protect heads-down humans), 20 minutes (answer 2), 20 + 10 (agent). The +two-stage ladder with the fence sentence adopts answer 1's concern — +nobody is released without a ping — while keeping a value that means +something in a 100-minute window. Recorded as our judgment; the exact +minutes are analogy in all three sources. + +## Written down versus said aloud + +The test: *would an agent that was not in the room produce wrong code +without this?* If yes, write it before the conversation ends — the +deciding human's agent writes it immediately. Must be written: seam +interfaces, `## Done when`, Files ownership and changes to it, blockers +and what unblocks them, any decision an absent agent must honour. Can +stay aloud: nudges, merge timing, coffee. + +## Integration cadence + +- Branch per issue; tiny PRs into `main`; the owner merges + continuously. +- One integration checkpoint, once: the first time the golden path + (ask → install → use → reload → update) runs end to end on `main`. + Merge or explicitly defer every open PR at that moment. +- Kernel files: a contract issue each, one writer, stub the interface + early so dependents build against the stub. + +## Identity and parsing — for a heterogeneous fleet + +- Ownership keys on the GitHub **assignee/login**; time keys on the + API's `createdAt`. The typed `agent:` and `at:` header fields are + advisory — two teammates on the same model produce identical + `agent:` strings. +- The header is always the comment's **first line**. Parsers use the + anchored form `test("^\\*\\*(CLAIM|...)\\*\\*")`; no-bash agents match + the literal prefix `**CLAIM**`. +- Fallback timestamp: `date -u +%Y-%m-%dT%H:%M:%SZ` (portable across + BSD and GNU `date`). +- Where v1's SKILL.md and protocol.md disagree — the negotiation cap, + the comment matcher — follow this document. + +## Failure modes on the board + +| Signal | Failure | Response | +| --- | --- | --- | +| Comments rising, merged PRs flat over 30 min | Protocol theater (MAST: failures are organizational; more verbs do not buy correctness) | Stop coordinating; ship | +| A PR touches a kernel file with no linked contract | Guessed interface (MAST specification failures, 41.8%) | PROPOSE the seam now; the PR waits for the provisional Agreed | +| `status:claimed`, no commits, no comments for 20 min | Ghost lock | QUESTION, then `release --stale` at 30 | +| One login on two active claims | Over-claiming | Release one | +| DONE with unchecked `## Done when` boxes | Premature done (MAST: verification is where failures hide; a verification step raised ChatDev success 15.6 points) | Reopen; merge first | +| "As we discussed" with no issue link | Board talk substituting for the ledger | Write the decision on the issue | +| 429s in an agent's log | Rate-limit storm | One board read per minute; honour `retry-after` | + +## Human escalation + +`needs-human` is the fastest path when two agents lack the context to +choose; the humans are two feet away. Post `question --human`, then +say the one-liner aloud — co-location is the fastest notification +channel the team has. Unanswered for 10 minutes: post the reversible +assumption you are proceeding on and continue. + +## Feedback and versioning + +Review this document on its PR or reply on issue #19 with a verb line: +what worked, what cost time, the one change. v3 merges those replies +after 16:00 ET, when the full protocol resumes. + +## Sources + +Three answers to Q01, reconciled above. Rung 1 = primary source +verified; rung 5 = single source or analogy. + +- Cemri et al., "Why Do Multi-Agent LLM Systems Fail?" (MAST), + arXiv:2503.13657, NeurIPS 2025 — 1,642 traces, κ = 0.88; specification + 41.8%, inter-agent misalignment 36.9%, verification 21.3%. Rung 1. +- Kleppmann, "How to do distributed locking" (2016) — efficiency versus + correctness locks; fencing tokens. Rung 1. +- Burrows, "The Chubby lock service", OSDI 2006 — lease design. Rung 1. +- Hong et al., "MetaGPT", arXiv:2308.00352 — shared message pool; the + hub is our analogue. Rung 1 for the source, analogy for the transfer. +- GitHub docs — REST rate limits and secondary limits; Copilot coding + agent (one agent per issue, no negotiation; it ignores issue comments + after assignment). Rung 1. +- Zulipbot and OSS claim-bot conventions — remind, then unclaim. + Rung 2. +- CAMEL — max turns plus an explicit done signal. Rung 1. +- Google SRE book — escalate early. Rung 1 for the source; analogy. +- Trunk-based development; Little's law and Kanban WIP; optimistic + concurrency at low contention; Conway (1968). Rung 2; transfer by + analogy. +- GitButler "Grit" field report — parallel agents breaking shared + state. Rung 5. +- Exact minute values — analogy in all three answers; not measured on + agent teams. diff --git a/vault/Coordination/Coordination Best Practices v3.md b/vault/Coordination/Coordination Best Practices v3.md new file mode 100644 index 0000000..e746c53 --- /dev/null +++ b/vault/Coordination/Coordination Best Practices v3.md @@ -0,0 +1,277 @@ +--- +type: playbook +title: Coordination Best Practices v3 — Evidence from One Sprint +description: An evidence-calibrated GitHub Issues protocol based on the DOMinic hackathon record from 13:00 to 16:00 ET on 2026-09-12. +status: active +supersedes: coordination-best-practices-2.md +generated: { by: codex/gpt-5, at: 2026-09-12T20:00:31Z } +verified: { by: human:michael, at: 2026-09-12T20:13:30Z } +sources: + - id: v2 + resource: https://github.com/xcjs/DOMinic/blob/main/docs/agents/coordination-best-practices-2.md + title: Coordination best practices v2 + - id: protocol + resource: https://github.com/xcjs/DOMinic/blob/main/.claude/skills/coordinate/references/protocol.md + title: Coordination protocol v1 + - id: q01-agent + resource: Q01 agent report held in the separate DOMinic research workspace + title: Q01 agent-first coordination research report + - id: q01-answer-1 + resource: Q01 external answer 1 held in Michael's main thread and reconciled in v2 + title: Q01 external answer 1 + - id: q01-answer-2 + resource: Q01 external answer 2 held in Michael's main thread and reconciled in v2 + title: Q01 external answer 2 + - id: retrospective + resource: https://github.com/xcjs/DOMinic/issues/19#issuecomment-5648371337 + title: V3 retrospective summary and derived findings + - id: feedback + resource: https://github.com/xcjs/DOMinic/issues/19 + title: Coordination playbook feedback thread +tags: [coordination, playbook, active] +source: docs/agents/coordination-best-practices-3.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# Coordination best practices v3 — evidence from one sprint + +## Verdict + +**Judgment:** Keep GitHub Issues as the shared ledger, but make the ledger +describe reality instead of asking agents to perform ceremony. Assignment, +declared files, structured comments, and independent review paid for +themselves. Silent auto-accept, unchecked completion criteria, unrestricted +hub updates, and assignment-as-WIP did not. For the next sprint, enforce file +and completion invariants in the script, distinguish responsibility from +active work, and make every claim name its branch or say that no code exists. + +This is one sprint, not a controlled experiment. Protocol v1 merged at 14:11 +ET and v2 at 14:42, so v2 governed only the final 77 minutes. Treat the +numbers below as calibration evidence, not universal constants. + +## Enforcement status + +V3 makes three invariants normative. Passing a helper command does not yet +prove all three: canonical hub selection is automated, while scope and +completion require a manual preflight until the helpers implement the same +gates. + +| Invariant | Protocol requirement | Current helper status | +| --- | --- | --- | +| Canonical hub | Keep one `hub`; Bash uses `COORD_HUB` or the lowest-numbered open hub | Bash automated since PR #57; PowerShell assumes a unique label | +| File scope | Claim a non-empty scope; resolve active overlap; PR paths must be within the union of linked issue scopes | Required; manual at claim and review | +| Completion | Every `Done when` box is checked and the linked PR is merged | Required; manual before `done` | + +The wire protocol records these as MUST-level checks. The helpers' current +limitations are tracked as automation work, not exceptions to the rules. + +## What the record shows + +All statements in this section are **observed** unless marked otherwise. + +1. The team merged 25 PRs in the three-hour window. Median open-to-merge + time was 6.9 minutes; the median PR changed 219 lines across three files. + Only 8 of 25 changed 100 lines or fewer. +2. The board received 190 protocol comments. After v2 merged, 44 of 113 + protocol comments were `SYNC`; v2's narrow hub cadence was not adopted. + Peak traffic was eight protocol comments in one minute, with no reported + HTTP 429 or rate-limit failure. +3. Every claimed issue, 23 of 23, declared a non-empty `## Files` scope. + Nevertheless, #40 crossed four issue scopes and overlapped #39, #42, and + #45. Declaring files worked as discovery, but nothing enforced them. +4. Duplicate persistence PR #31 opened before its author's first board event. + It was detected after 3.2 minutes and closed 2.5 minutes later. **Reported + on #19:** late claims allowed parallel work to begin without visible + ownership; claims should carry a branch/PR link or `no code yet`. +5. All 14 `DONE` comments linked a PR that was already merged. Zero of the 14 + had their `Done when` boxes checked, and six closed issues had no `DONE`. + Merge truth was strong; acceptance-criteria truth was absent. +6. Six hub messages explicitly reported a second-review wait across seven + PRs. For nine PRs with two distinct approvals, the median wait between + approvals was 5.1 minutes. Independent reviews also found real bugs in + #35 and #40, so removing review is the wrong fix. +7. API author identity was reliable: zero `human:`/login mismatches across + 190 protocol comments. Self-declared time was not: nine invalid `at:` + values and four values skewed by more than one minute. The same + `claude-code/claude-fable-5.1` agent ID appeared under two human logins. +8. One real `needs-human` episode lasted 7.7 minutes, below v2's 10-minute + acknowledgment target. No claim was released by the 20/30 ladder, so the + release threshold itself remains untested. + +The public evidence summary and reproducibility notes are attached to +[issue #19](https://github.com/xcjs/DOMinic/issues/19#issuecomment-5648371337). + +## Rules for the next sprint + +### 1. Assignment owns responsibility; state marks active work — change + +Keep assignment as the universally visible owner. Stop treating the number +of assignments as active WIP: peak open assignments per login ranged from one +to five because the board also assigned queued responsibilities. + +- Put exactly one owned issue in `status:in-progress` per login. +- Allow one additional `status:blocked` issue. Leave queued responsibility in + `status:claimed`; do not collapse `claimed` and `in-progress`. +- In every `CLAIM`, include `branch: <name>`, `pr: <URL>`, or `no-code-yet`. +- Treat the Git branch and merge conflict as the fence. A release transfers + attention; it cannot make another branch disappear. + +This keeps v2's WIP intent while making it measurable. The first board event +for @ImNewToC0de arrived 1.5 minutes after duplicate PR #31 had opened. + +### 2. Declare files, then verify the diff — keep and enforce + +Keep `## Files`; it had complete adoption and helped detect overlap. Add two +checks: + +1. Refuse a claim whose scope overlaps an open in-progress claim until the + issue records a split or common owner. +2. At `review`, compare the PR's changed paths with the union of `## Files` + on every linked issue. Refuse or warn on undeclared paths. + +Do not let a convenience PR become an integration bucket. If one PR closes +several issues, its body must list each issue and the union of their scopes. + +### 3. Record seams before shared code; do not accept by silence — change + +The only contract issue, #14, received one `PROPOSE`, zero real accepts, and +no recorded `Agreed` contract before it closed. Its proposed registry path +also differed from the merged implementation, while duplicate #31 followed +the earlier shape. Silent auto-accept did not create shared understanding. + +- Name one seam owner in the contract issue. +- Make `PROPOSE` atomically write the exact provisional contract under + `## Agreed`; never rely on a later edit. +- Let agents proceed inside their own slices, but require the seam owner to + acknowledge the shared adapter before it merges. +- If implementation changes the contract, update `Agreed` in the same PR. +- Permit one `COUNTER`, then escalate. Do not add negotiation rounds. + +Drop the 15-minute silence-implies-accept timer. A binding provisional record +is faster and safer than assuming another agent polled the board. + +### 4. Ping, then release — keep with instrumentation + +Keep the sprint ladder: ping after 20 minutes with no visible activity, then +allow release after 10 more minutes without an answer. Count issue comments, +linked-PR pushes, and branch updates as activity. For work outside GitHub, +such as video production, the owner must post a brief `STATUS` inside the +20-minute ceiling. + +Never release without a recorded ping. The single real human escalation was +answered in 7.7 minutes, but zero stale releases means the 20/30 values have +low empirical confidence. Revisit them after three sprints. + +### 5. Ship single-purpose PRs; distribute review and merge — change + +Replace “tiny PR” with “one purpose and one declared file union.” Line counts +are a poor boundary when scaffolds and lockfiles are large, but #40 shows the +cost of aggregation: seven files, four issue scopes, four overlap episodes, +three changes-requested reviews, and 24.3 minutes to merge. + +- Request an independent review as soon as the PR opens. +- During a deadline sprint, rotate a review captain every 15 minutes. +- Let any eligible non-author merge after required checks and approvals; do + not queue every merge behind the repository owner. +- If policy requires two approvals, request both immediately and surface the + second-review queue as one dashboard, not repeated `SYNC` messages. +- Freeze new features early enough to leave one full review-and-rebase cycle. + +Do not remove independent review. It caught failures that would have broken +the golden path. + +### 6. Route every summary to one canonical hub — keep and enforce + +A status dashboard temporarily shared the `hub` label with the original +coordination hub. Deterministic routing prevented split-brain writes, but the +label should identify one issue, not a class of summaries. + +- If `COORD_HUB` is set, route to that issue. +- Otherwise, route to the lowest-numbered open issue labelled `hub`. +- Keep exactly one canonical issue labelled `hub`; use a distinct dashboard + marker for editable status summaries. +- Restrict `SYNC` to session start, cross-stream merge, blocker, freeze, and + final state. + +## Protocol surface + +V2's six-verb minimum did not match use. After it merged, agents still used +11 `STATUS`, nine `HANDOFF`, eight `REVIEW`, nine `DEP-DONE`, five `ANSWER`, +and one `UNBLOCKED` comment. Preserve the useful state transitions and remove +duplicates of GitHub-native state. + +| Keep | Use | +| --- | --- | +| `CLAIM` | Establish owner, declared scope, and branch/PR/no-code state | +| `STATUS` | Record a milestone or satisfy the activity lease | +| `BLOCKED` | Name the dependency and required action | +| `QUESTION` / `ANSWER` | Drive human escalation and clear `needs-human` | +| `PROPOSE` / `COUNTER` | Record one seam decision round | +| `HANDOFF` | Transfer responsibility with context | +| `DONE` | Close only after merged PR and verified acceptance criteria | +| `RELEASE` | Return responsibility after handoff or stale ladder | +| `SYNC` | Session start, cross-stream merge, blocker, freeze, final state | + +Drop `DEP-DONE`; it generated 16 notices against only three `UNBLOCKED` +comments. Let the board derive dependency state. Drop protocol `REVIEW` +comments after the issue stores its PR URL; GitHub already records reviews. +Fold `UNBLOCKED` into `STATUS`. Keep `ACCEPT` only in asynchronous full mode. + +Make `DONE` a real gate: refuse it while a `Done when` box is unchecked or +the linked PR is unmerged. Do not auto-check criteria merely because CI is +green; the caller must attest or update each criterion. + +## Parameter decisions + +| V2 parameter | Decision | Next-sprint setting | Evidence | +| --- | --- | --- | --- | +| 20/30 stale ladder | **Keep** | ping at 20, release at 30 | One response at 7.7 minutes; no releases, so low confidence | +| One active claim | **Change** | one `in-progress` + one `blocked`; assignments may represent queued ownership | Assignment peaks of 1–5 made the old measure ambiguous | +| One-counter negotiation cap | **Keep** | `PROPOSE` → one `COUNTER` → human | No real chain exceeded one counter | +| 15-minute auto-accept | **Drop** | binding written provisional contract + seam-owner acknowledgment before merge | #14 never recorded `Agreed`; implementation drifted | +| 10-minute human acknowledgment | **Keep** | answer or reversible logged decision by 10 minutes | Only real episode resolved in 7.7 minutes; evidence is thin | +| At most one board read/minute | **Keep as a ceiling** | event-driven reads; never poll in a loop | Peak writes were 8/minute and no 429 was reported | +| Event-driven heartbeat | **Keep** | milestones, with 20-minute silence ceiling in sprint mode | `STATUS` reappeared 11 times after v2 because it carried useful state | +| Comment/merge anti-theater ratio | **Change** | use as a review prompt, not a hard quota | 190 comments and 25 merges coexisted; causality is unknown | + +## Full protocol after the sprint + +For asynchronous or multi-day work, keep the same ownership and completion +invariants, then change only the time horizon: + +- Require explicit `ACCEPT` before merging a shared contract or adapter. +- Use a six-hour warning plus three-hour grace during active workdays; pause + stale release overnight. Do not reuse sprint-minute values across sleep. +- Require `HANDOFF` when ownership crosses a session, including current + branch, PR, validation state, exact stopping point, and next action. +- Keep event-driven `STATUS` with a 60-minute active-work ceiling. +- Maintain one canonical hub. Enforce exactly one `hub` label; use a separate + dashboard marker for an editable status summary. +- Batch dependency changes and review queues into the dashboard. Do not emit + one comment per dependent issue. + +## What we still do not know + +- This record has no control group, and v2 arrived late. It cannot prove that + the protocol caused throughput, duplication, or review delay. +- Only one teammate posted the requested #19 qualitative feedback. Most + conclusions therefore come from behavior, not self-report. +- The GitHub API returned no readable branch-policy configuration to this + account. Review requirements are reconstructed from reviews and hub text. +- Board-read frequency and agent attention time were not recorded. Comment + counts measure writes, not ceremony cost or cognitive load. +- No stale release occurred, no negotiation spiral occurred, and no 429 + occurred. Those safeguards remain plausible defaults, not validated limits. +- Cross-harness bodies did produce malformed timestamps, but no login + mismatch. We do not yet know whether other header fields fail often enough + to justify a stricter schema. + +## Evidence archive + +The frozen JSON, timelines, query map, and deterministic derivation remain in +the separate DOMinic research workspace. The durable public summary records +the key metrics and method on +[issue #19](https://github.com/xcjs/DOMinic/issues/19#issuecomment-5648371337). +The repository playbook links that public record rather than a +workstation-only path. diff --git a/vault/Coordination/Coordination Protocol.md b/vault/Coordination/Coordination Protocol.md new file mode 100644 index 0000000..cad0840 --- /dev/null +++ b/vault/Coordination/Coordination Protocol.md @@ -0,0 +1,53 @@ +--- +type: index +title: Coordination Protocol +description: How DOMinic's coding agents divided work over GitHub Issues — the /coordinate skill, the wire protocol, the v1→v3 playbooks, and the research behind them. +tags: [coordination, agents, moc, dominic] +repo_head: "be8645f" +ingested: 2026-09-12 +refreshed: 2026-09-12T23:40:00Z +--- + +# Coordination Protocol + +Five engineers and their coding agents (Claude Code, Codex, Pi, opencode) edited one repo in one afternoon. GitHub Issues was the shared memory: a task board and a negotiation channel at once. This is the map of how that worked. Back to [[DOMinic Home]]. + +## The pieces + +| Note | What it is | +| --- | --- | +| [[Agent Coordination over GitHub Issues]] | The overview doc (`docs/agents/coordination.md`): the protocol in four lines, where it lives, the daily loop. | +| [[Coordination Best Practices v3]] | **Active playbook** — evidence from the sprint, six calibrated rules, enforcement status. Read this first for the rules that actually held. | +| [[Coordination Best Practices v2]] | Reconciliation of all three research answers into one sprint playbook (historical). | +| [[Coordination Best Practices v1]] | First cut from the first research answer (historical). | +| [[Coordination Research (Q01)]] | The research question asked at 14:15 and its three answers — two external deep-research runs and one in-session agent report — that seeded the playbooks. | + +The runnable pieces live in the repo, not the vault: `.claude/skills/coordinate/` holds `SKILL.md` (workflow), `references/protocol.md` (wire format v2 + invariants), and the two scripts `coord.sh` (bash) and `coord.ps1` (the Windows PowerShell port, #36). + +## The protocol in four lines + +- **Assignment owns responsibility** — an issue with an assignee has one accountable owner; `status:in-progress` marks active work. +- **Labels describe state** — `ws:*` workstream, `status:*` lifecycle, `p0`–`p2` priority, `type:*`, `needs-human`. +- **Scopes and completion are verified** — a `## Files` glob must cover the linked PR's diff, and `DONE` requires a merged PR plus checked criteria. +- **Comments are structured** — every protocol comment opens with a bold verb and an actor line: `**CLAIM** | agent: pi/1 | human: @xcjs | at: <ISO-8601 UTC>`. + +## How it evolved + +1. **The skill** (`/coordinate`, PR #7) was built at the start: labels, a pinned hub issue (#5), a verb protocol, and `coord.sh`. It seeded the board and ran the whole sprint. +2. **The research** (Q01) was run in parallel by Michael — two external deep-research passes plus an in-session agent — asking how autonomous agents should coordinate in a short sprint. +3. **v1 → v2 → v3.** v1 came from the first answer; v2 reconciled all three; **v3** scored the day's actual GitHub record against v2 and kept, changed, or dropped each rule with evidence. v3 is the one to follow next time. +4. **Hardening** (#75) folded v3's invariants — file-scope coverage, completion checks, one canonical hub — back into the protocol doc and scripts. + +## Going forward: lanes and the review rotation + +> [!note] Active mode since ADR 0012 (PR #78): the full protocol +> Sprint mode is off. Stale window six hours + three-hour grace (`COORD_STALE_MIN` default 360), `STATUS` at least hourly during active work, explicit `ACCEPT` before a shared contract merges, `HANDOFF` across sessions. See [[Agent Coordination over GitHub Issues]] and [[ADR 0012 - End of the hackathon scope]]. + +The post-sprint [[Post-Hackathon Roadmap (NEXT)#Roles and working agreement|roadmap]] assigns each collaborator a lane (paths they own + roadmap steps), a path-ownership rule (a PR touching another lane needs that owner as reviewer), a reviewer rotation, and the issue → PR → merge loop from v3: one issue per step, single-purpose PRs, distributed merge by any non-author approver once CI is green. + +## What actually happened on the board + +76 PRs, a pinned hub (#5), a late second `hub`-labelled dashboard (#54) that briefly split SYNCs until [[Commit Timeline|#57]] fixed hub selection, and two of the user's own Claude sessions coordinating a work split over the same clone (see [[Repo Watch Log]]). The live board snapshot at sprint's end is [[Coordination Board]]. + +> [!note] This vault owner built the skill +> The `/coordinate` skill and its protocol were authored in this project's Claude sessions (Brandon, `@r0073d-l053r`), then adopted and hardened by the team. The [[Submission Form Draft#Team contributions|contribution record]] and [[Commit Timeline]] carry the PR-level detail. diff --git a/vault/Coordination/Coordination Research (Q01).md b/vault/Coordination/Coordination Research (Q01).md new file mode 100644 index 0000000..91f0d4e --- /dev/null +++ b/vault/Coordination/Coordination Research (Q01).md @@ -0,0 +1,32 @@ +--- +type: index +title: Research behind the coordination playbooks +description: The Q01 research question and its three answers — two external deep-research runs and one in-session agent report — that produced coordination-best-practices v1 through v3. +generated: { by: claude-code/claude-fable-5.1, at: 2026-09-12T20:45:00Z } +verified: { by: human:michael, at: 2026-09-12T20:45:00Z } +tags: [research, coordination, provenance] +source: docs/research/README.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# Research behind the coordination playbooks + +One question, asked at 14:15 ET on hackathon day, answered three ways +within the hour, and reconciled into the playbooks in `docs/agents/`. +These files are verbatim research artifacts: long lines and external +formatting are kept as delivered (markdownlint is disabled inline), so +read them as sources, not as house-style documents. + +| File | What it is | +| --- | --- | +| `2026-09-12-14-15-Q01-question.md` | The research brief, written to the team's question template: context, locked decisions, six sub-questions, required perspectives | +| `2026-09-12-14-16-Q01-answer-1-external.md` | External deep research run by human:michael — the summary as returned (lease don't lock, contracts on seams, tiny PRs into main) | +| `2026-09-12-14-20-Q01-answer-2-external.md` | External deep research run by human:michael — full report (six-verb sprint set, 20-minute lease, file-glob ownership, MAST evidence) | +| `2026-09-12-Q01-answer-agent-report.md` | In-session deep-researcher agent (claude-code/claude-fable-5.1) — declare-then-proceed, two-stage lease, identity by login, a v1.1 diff, `gh --jq` recipes | + +How they were used: answer 1 became `coordination-best-practices.md` +(v1); all three were reconciled into `coordination-best-practices-2.md` +(v2); the day's GitHub record scored against v2 became +`coordination-best-practices-3.md` (v3). Each playbook's frontmatter +`sources` names these files. diff --git a/vault/DOMinic Home.md b/vault/DOMinic Home.md new file mode 100644 index 0000000..449c419 --- /dev/null +++ b/vault/DOMinic Home.md @@ -0,0 +1,129 @@ +--- +type: index +title: DOMinic Home +description: Entry point for everything ingested from the DOMinic hackathon repo — final state after the 2026-09-12 sprint. +tags: [dominic, hackathon, moc] +repo: https://github.com/xcjs/DOMinic +repo_head: "0fc772d" +ingested: 2026-09-12 +refreshed: 2026-10-03T00:10:00Z +--- + +# DOMinic Home + +> **DOMinic** — a web-based desktop operating system running in a browser tab, where an AI agent is the primary app author: you describe an app in plain English, and it is written, compiled, installed to the taskbar, and kept across reloads like first-party software. + +Built by a five-engineer team (plus their coding agents) for the **AI Tinkerers Columbus "Agents, Everywhere: Bots, Channels & More" global hackathon**, 2026-09-12. The build sprint ran ~1:00–4:00 PM ET; the portal closed at 4:00. This vault mirrors the public repo `xcjs/DOMinic`. + +> [!info] 2026-10-02 — post-hackathon mode +> **Merged:** #77 rewrote `NEXT.md` as the sequenced roadmap with a lane per collaborator; #78 added **ADR 0012 — End of the hackathon scope** (golden paths historical, five-slice/mixed-store baseline accepted, coordination on v3's full protocol). **This vault now lives in the repo** (`vault/`, ADR 0013) so every agent and machine shares it; the work loop (`npm run loop`) writes `Milestones/` and `Sessions/` and refreshes the blocks below. Run `npm run loop -- resume` first in any new session. + +> [!success] Final sprint state at `be8645f` — the core loop ships +> The demo golden path works end to end on `main`: **ask → `install_app` → SFC written to the VFS + registered → window opens with the compiled app → survives reload → `update_app` changes it in place → "Ask Agent to Fix" repairs a crash.** Charles's 15:08 rehearsal passed all five non-negotiable beats. 76 PRs merged. What remained a human step at the buzzer was recording/uploading the 2-minute video (see [[Submission Form Draft]] for the TODO fields). The [[Demo Video — Shot List|finished video]] was cut separately in HyperFrames. + +## What shipped (one line each) + +- **The shell** — desktop with a bottom [[Architecture Overview|taskbar]] that lists installed apps (running dot, View Source, Uninstall), draggable/min/max/close windows, a live clock, a Settings launcher. +- **The agent** — Agent Chat streams from `/api/chat` (Vercel AI SDK, `ai@4`); the model calls `install_app` / `update_app`; tools execute client-side through callbacks the shell injects. +- **The runtime** — `DynamicAppRunner` compiles agent-authored Vue SFCs in the browser with `vue3-sfc-loader`; unknown imports resolve from `esm.sh`; an error boundary offers **Ask Agent to Fix**. +- **Persistence** — a localStorage VFS holds each app's source + a `registry.json`; the launcher/taskbar re-hydrate on reload. +- **Providers** — OpenAI, Anthropic, Google, DeepSeek, and a **custom OpenAI-compatible** endpoint (base URL + model); keys are entered in Settings, sent per request, never at rest on the server. +- **Licence** — AGPL-3.0-or-later. + +## Work loop + +Agents run `node .claude/skills/work-loop/scripts/loop.mjs` (`npm run loop`) to take roadmap steps through `/coordinate`; the blocks below are rewritten by the script. See [[Roadmap Progress]] and the skill at `.claude/skills/work-loop/SKILL.md`. + +### Roadmap progress + +<!-- loop:progress --> +| Phase | Title | Done | Eligible now | +| --- | --- | --- | --- | +| 0 | Stabilize the floor | 0/6 | 0.1, 0.2, 0.3, 0.4, 0.5, 0.6 | +| 1 | Harden the two seams | 0/4 | gated | +| 2 | The agent product loop | 0/5 | gated | +| 3 | Reach | 0/7 | gated | + +_Updated 2026-10-03T00:06:26Z by `claude-code/claude-fable-5.1/vault`._ +<!-- /loop:progress --> + +### Latest milestones + +<!-- loop:milestones --> +- (none yet) +<!-- /loop:milestones --> + +### Active sessions + +<!-- loop:sessions --> +- (none yet) +<!-- /loop:sessions --> + +## Start here + +1. [[Architecture Overview]] — the system as built: five slices, the client-side tool protocol, the VFS, the runtime engine, and where the ADRs' golden path and the code diverge. +2. [[Repo Map]] — every file, the tech stack, contributors, branches, and the shape of the 76 PRs. +3. [[Judges Walkthrough]] — the five-minute repo tour (`docs/JUDGES.md`): the loop by file path, an ADR→code map, run instructions, and known limitations. +4. [[Submission Form Draft]] — the portal answers (project description, tools, per-author contribution record) with the video/social TODOs. +5. [[Open Questions & Gaps]] — the post-sprint retrospective: what shipped, what stayed deferred, and the honest caveats a judge will notice. +6. [[Post-Hackathon Roadmap (NEXT)]] — **the sequenced roadmap** (approved 2026-10-02, PR #77): north star, four phases with exit criteria, and a lane + reviewer rotation per collaborator. +7. [[Judging Rubric & Win Strategy]] → [[Demo Path]] — how points are scored and the eight video beats. +8. [[Demo Video — Shot List]] · [[Demo Video — Scenes]] — the finished video: cue-level shot list and scene-by-scene real-footage-vs-graphic. +9. [[Coordination Protocol]] — how the agents divided the work over GitHub Issues (the `/coordinate` skill and the v1→v3 playbooks). + +## Architecture Decision Records + +MADR 4.0 + OKF v0.2 frontmatter. Since PR #2 each ADR's Decision Outcome carries **Hackathon POC (Golden Path)** / **Future** / **Open Questions** blocks, and since PR #55 an **As built (2026-09-12)** subsection reconciling the record with the code. **ADR 0012 (PR #78) ends the golden-path scoping:** those blocks are historical, each record's full Decision Outcome is the target again, and the roadmap sequences the rest. Conventions: [[ADR Conventions]]; template: [[ADR Template]]. Where an ADR and the code disagree, the code wins — the "as built" line below and each note's As-built section say how. + +| # | Decision | As built on `main` | +| --- | --- | --- | +| [[ADR 0000 - Record architecture decisions\|0000]] | Use MADR 4.0 ADRs in `docs/adrs/` | 12 ADRs, 0000–0011 | +| [[ADR 0001 - Feature slices with domain-driven organization\|0001]] | Vertical feature slices + shared kernel | Five slices: `os`, `chat`, `apps`, `settings`, `shared` (not the ADR's `os/agent/apps`); boundaries by convention | +| [[ADR 0002 - Strict TypeScript and lint toolchain\|0002]] | TS strict + type-checked ESLint + vue-tsc | `strict` tsconfig; `nuxt typecheck` + build gated in CI (#67); **no ESLint**; pre-commit runs markdownlint only | +| [[ADR 0003 - Pinia per-domain stores\|0003]] | One Pinia setup-store per slice | Four stores (`os` setup; `chat`/`apps`/`settings` option); registry state is a module ref, not Pinia; `useChatStore` unused | +| [[ADR 0004 - DOMinic OS shell and taskbar\|0004]] | Desktop-metaphor shell, mobile-first | Desktop-only; drag via raw pointer events (not `useDraggable`); taskbar lists installed apps + clock; Settings launcher | +| [[ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy\|0005]] | `/api/chat`, BYO key per request | `streamText` + tools; OpenAI/Anthropic/Google/DeepSeek/**custom**; env-var fallback for keys; models `gpt-5`/`claude-sonnet-5`/`gemini-2.5-pro` | +| [[ADR 0006 - Agent-authored runtime-compiled components\|0006]] | In-browser SFC compile via `vue3-sfc-loader` | `DynamicAppRunner` + `loader.ts`; error boundary with Ask Agent to Fix; styles tagged `data-app-id` and cleaned on update/uninstall | +| [[ADR 0007 - Virtual filesystem with pluggable storage drivers\|0007]] | VFS interface over pluggable drivers | localStorage only; paths `apps/<id>/index.vue` + `registry.json` (no per-app `manifest.json`); boot hydration; Reset OS | +| [[ADR 0008 - Runtime NPM dependency loading via esm.sh with vetting\|0008]] | esm.sh behind a vetting gate | **No gate**; unknown imports → `esm.sh/<pkg>?bundle`; four host modules pre-resolved; prompt guardrails | +| [[ADR 0009 - CORS-first networking with Chrome-masking proxy fallback\|0009]] | Direct fetch + proxy fallback | Direct `fetch` only; **no proxy route** shipped (demo apps make no external calls) | +| [[ADR 0010 - Agent app interface and tool protocol\|0010]] | Structured tools + `DominicApp` contract | `install_app`/`update_app` (Zod); tools run **client-side via callbacks**; `update_app` gets the current source injected (#66); no `{windowId, appId}` props into the compiled app | +| [[ADR 0011 - Node 24 LTS runtime standard\|0011]] | Node 24 LTS pinned | `.nvmrc` `24.21.0`, `engines >=24.21.0 <25`, CI reads `node-version-file` | +| [[ADR 0012 - End of the hackathon scope\|0012]] | **New (PR #78).** Hackathon scoping ended | Golden paths historical; five-slice layout + mixed stores accepted (amends 0001/0003); full coordination protocol; `NEXT.md` governs deferred work | + +## Team workstreams and who delivered + +Five workstreams (README) mapped to the merged-PR record ([[Submission Form Draft#Team contributions|full list]]): + +| SDE | Workstream | Lead of record | +| --- | --- | --- | +| 1 | OS Shell & Window Manager | Zack (`xcjs`) — scaffold #21, shell #24 | +| 2 | Agent Chat & Tool Calling | Charles (`Sullux`) — `/api/chat` #20, Recover beat #64 | +| 3 | Runtime App Engine | Charles — runner #25; Justin style cleanup #62 | +| 4 | Persistence & App Registry | Charles — VFS/registry/Settings #27 | +| 5 | Integration, Polish & Demos | Justin (`ImNewToC0de`) — wiring #35, taskbar #56, update-context #66 | +| — | Docs, process, coordination | Michael (`m-vawter`) — rubric/demo-path/playbooks/README/JUDGES; Brandon (`r0073d-l053r`) — `/coordinate`, model presets #39, Tailwind+history #42, ADR as-built #55, demo video | + +## Conventions + +- [[OKF Frontmatter Convention]] — every doc carries Google OKF v0.2 YAML frontmatter; [[OKF v0.2 Specification]] is the full spec. +- [[Markdown Lint & CI]] — markdownlint at 80 columns (husky + Actions), plus typecheck/build in CI; Node 24; `main` branch-protected, requires one non-author approval. + +## Provenance + +- [[Spec - ADR Scaffold Design]] → [[Plan - ADR Scaffold]] — produced ADR 0000, template, README. +- [[Spec - ADR Series Design]] → [[Plan - ADR Series]] — produced the *original* ADRs 0001–0009. +- [[Spec - Custom OpenAI Provider]] → [[Plan - Custom OpenAI Provider]] — the last feature spec, shipped as PR #70. +- [[Plan - Hackathon Finish]] — Michael's timeline to the 16:00 close (#37). +- [[Coordination Research (Q01)]] — the research question behind the coordination playbooks. +- [[Commit Timeline]] — all 76 commits; [[Repo Watch Log]] — dated record of what this vault's watch loop observed during the sprint. +- Raw config (package.json, nuxt.config, tsconfig, lint config, CI, husky, `.nvmrc`, `.gitattributes`, git log) in `Repo Config/`. + +## Local paths + +| What | Where | +| --- | --- | +| This vault | `vault/` inside the repo - shared by every agent and machine; open the folder in Obsidian | +| Remote | `https://github.com/xcjs/DOMinic.git` — `gh` authenticated with push access | +| Refresh this vault | `npm run loop -- vault-sync` after a pull (regenerates ADR/doc/plan/coordination notes, Commit Timeline, Repo Config); synthesis notes are edited by hand; `Milestones/` and `Sessions/` are written by the loop | +| Demo video | `project-files/dominic-video/` (HyperFrames, outside the repo) — see [[Demo Video — Shot List]] | diff --git a/vault/Hackathon/Demo Path.md b/vault/Hackathon/Demo Path.md new file mode 100644 index 0000000..950244f --- /dev/null +++ b/vault/Hackathon/Demo Path.md @@ -0,0 +1,223 @@ +--- +type: playbook +title: Demo Path — What the Judges Must See +description: The two-minute demo as an ordered set of beats, each mapped to a judging criterion and to the Hackathon POC golden path the team has already committed to build. +tags: [hackathon, demo, video, submission] +generated: { by: claude-code/claude-fable-5.1, at: 2026-09-12T17:55:00Z } +verified: { by: human:michael, at: 2026-09-12T17:55:00Z } +stale_after: 2026-09-12T20:00:00Z +sources: + - id: rubric + resource: rubric.md + title: How We Win — Judging Rubric and Strategy + - id: adr-0006 + resource: ../adrs/0006-agent-authored-runtime-compiled-components.md + title: ADR 0006 — Agent-authored runtime-compiled components + - id: adr-0007 + resource: ../adrs/0007-virtual-filesystem-with-pluggable-storage-drivers.md + title: ADR 0007 — Virtual filesystem with pluggable storage drivers + - id: adr-0010 + resource: ../adrs/0010-agent-app-interface-and-tool-protocol.md + title: ADR 0010 — Agent app interface and tool protocol + - id: questions + resource: ../../QUESTIONS.md + title: DOMinic Open Questions and Decisions +source: docs/agents/demo-path.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# Demo path — what the judges must see + +Judges score the four criteria from three artifacts: the two-minute +video, the written description, and the public repository. The video +does the heavy lifting — it is where reliability, theme alignment, +technical depth, and usefulness are *seen* rather than claimed. This +document is the ordered set of beats the video must contain and what +each beat has to prove. + +It is a filming plan, not an architecture document. The technical +decisions live in the ADRs and their **Hackathon POC (Golden Path)** +sections; this document only decides what goes on camera, in what order, +and why. The scoring logic behind every choice is in +[[Judging Rubric & Win Strategy|rubric.md]]. + +## The core loop — the whole demo in one sentence + +> Ask → `install_app` → the window opens → the user uses it → the tab +> reloads → the app is still there → ask for a change → `update_app` +> changes it in place. + +That sentence is the golden path of ADRs 0006, 0007, and 0010, filmed. +It is also the chatbox-test answer from the rubric: a chat window can +*show* code; this environment *runs* it, keeps it, and changes it. + +## The beats + +| Time | Beat | On camera | Proves | Golden-path element it films | +| --- | --- | --- | --- | --- | +| 0:00–0:10 | **Hook** | A person, a small job they want done right now, and a browser tab they already have open. One sentence of narration: who, and what. | Criterion 4 — a clear user and problem | — | +| 0:10–0:18 | **Ask** | One plain-English sentence typed into the Agent Chat window. Then the chat window is moved aside; the desktop fills the frame. | Criterion 2 — chat is the input, not the value | ADR 0005 streaming chat; Q1.1 floating chat window | +| 0:18–0:45 | **Materialize** | Tool pills ("Generating App…", "Installing to Desktop…"), the window opens on its own, confetti, the installation badge — and the user *uses* the app; state visibly changes. | Criterion 2 at a 5 — value impossible in a chatbox; criterion 1 — the core workflow | ADR 0010 `install_app` lifecycle; ADR 0005 pills and confetti; ADR 0006 DynamicAppRunner | +| 0:45–0:58 | **Persist** | Reload the tab. The launcher and taskbar list the app again; open it from there. Windows reset; installed apps do not. | Criterion 2 — an environment, not a wrapper; criterion 1 — reliability | ADR 0007 boot hydration from `/apps/`; Q4.1 | +| 0:58–1:15 | **Update** | "Make the break five minutes and give it a synthwave look." The running app changes in place; the chat shows the change summary. | Criterion 4 at a 5 — "clear and controllable," "uses context intelligently"; criterion 2 — the environment shapes the workflow | ADR 0010 `update_app`; Q2.2 installed-app context injection | +| 1:15–1:32 | **Recover** | An app throws. The window shows the error card; the user clicks "Ask Agent to Fix"; the agent repairs it; it runs. | Criterion 3 at a 5 — "thoughtful failure handling" | ADR 0006 error boundary; Q3.2 | +| 1:32–1:42 | **Control** | Uninstall an app, or open `/apps/<id>/index.vue` to show the source is real and readable. | Criterion 4 — control; criterion 3 — nothing is hidden | ADR 0007 canonical paths; Settings "Reset OS" (Q4.2) | +| 1:42–1:50 | **Any model** | The Settings app shows the provider and key. One line: bring your own key; it never leaves your browser. | Criterion 3 — depth; sponsor visibility (OpenAI) | ADR 0004 and 0005 Settings app, per-request keys | +| 1:50–2:00 | **Close** | Title card, one-line value statement, sponsor tags. | Submission gates | — | + +## Cut order — non-negotiable versus stretch + +The loop is non-negotiable: **Hook, Ask, Materialize, Persist, Close.** +Without Persist the shell reads as a skin around a chat window, which +is the rubric's definition of a 2 on criterion 2. + +The stretch beats, in order of points per minute of build time: + +1. **Update** — `update_app` is already a golden-path tool, so this beat + is nearly free, and it lifts criterion 4 from a 3 to a 5. +2. **Recover** — the error boundary and "Ask Agent to Fix" are already + planned (ADR 0006, Q3.2); the rubric names failure handling in its + definition of a 5 on criterion 3. +3. **Control** — uninstall or view source; cheap, and it proves the code + is real. +4. **Any model** — sponsor visibility plus depth; lowest priority. + +One rule decides every scope question today: **anything the camera never +sees earns zero points.** If it is not a beat, it can wait until 4:01 — +which is what [NEXT.md](../../NEXT.md) is for. + +## On camera, off camera — by ADR + +| ADR | On camera? | Why | +| --- | --- | --- | +| 0004 shell and taskbar | Yes — the stage | The launcher and taskbar are how a judge sees "installed" | +| 0005 agent chat | Yes — as the input | The tool pills and confetti are the visible handoff; then move the window aside | +| 0006 runtime-compiled apps | Yes — the star | Criterion 2's 5 lives here; the error boundary is the Recover beat | +| 0007 virtual filesystem | Yes — via the reload and the source view | Proves environment, not wrapper | +| 0010 tool protocol | Yes — as the pills | `install_app` and `update_app` are the Materialize and Update beats | +| 0008 esm.sh dependencies | Only through the pre-provisioned libraries | No live package fetch on the core loop | +| 0009 networking | Only if a live-data showcase app is included | Never on the core-loop take | +| 0001–0003 slices, toolchain, stores | No | Invisible to judges; they buy reliability, not points | + +## Choosing the demo app + +[QUESTIONS.md](../../QUESTIONS.md) Q5.1 lists three showcase apps. The +rubric ranks them for the *core-loop take* — the one generation that +must work on camera: + +1. **Pomodoro / focus timer** — the core-loop app. No fetch, visible + state, confetti on completion, a job anyone recognizes. Its + `update_app` follow-up ("five-minute break, synthwave look") is the + Update beat. +2. **Kanban board** — the alternative if the timer misbehaves; its own + saved cards make the Persist beat even stronger. +3. **Crypto or weather dashboard** — a fine *second* showcase if it is + already working, but never the core-loop take: a live API is the one + thing on set we do not control. + +Rehearse one prompt for the core-loop app and keep it verbatim below +once it works. Never edit it after the first successful rehearsal. + +Chosen app: *Pomodoro focus timer.* +Exact prompt: *"Build a beautiful retro synthwave Pomodoro focus timer with +25-minute work and 5-minute break cycles, start/pause/reset controls, and +confetti celebration on finish."* + +### Rehearsing the Recover beat (stretch) + +For the Recover beat (1:15–1:32), the error boundary handles both +template/script compilation errors and runtime render exceptions: + +1. **Triggering the error**: + - Rehearsal helper in devtools: + `window.__dominic.injectBrokenApp('compile')` or + `window.__dominic.injectBrokenApp('runtime')`. + - On-camera prompt: *"Update the pomodoro timer to intentionally test + error recovery by adding a deliberate syntax error or runtime throw."* +2. **The error state**: + The window displays the dark rose glass error card labeled + **Component Compilation Error** (or **Application Runtime Error**) + with the formatted syntax error or stack trace and a + **✨ Ask Agent to Fix** button. +3. **The recovery loop**: + Clicking **Ask Agent to Fix** focuses the Agent Chat window and + submits the error trace along with the target app's current source + code. The model calls `update_app` with the corrected SFC code. +4. **In-place repair**: + `update_app` writes the repaired SFC to `apps/<id>/index.vue` and + bumps `sourceVersion[id]`, causing the runner to re-compile and mount + the working component in place without reloading the page. + +## Filming rules — criterion 1 on camera + +- Rehearse the exact prompt three times before recording. Record two + full takes. +- Recorded, not live: a bad take is retaken, never narrated around. +- After the Ask, move the chat window to a corner or minimize it. The + desktop is the frame; the chat is never the frame. +- No mocked steps. If a beat cannot run, cut it — do not fake it. The + rubric scores what runs. +- Curated showcase apps are welcome as pre-installed proof of + persistence and as b-roll, but the Materialize beat is a real + generation on camera, and nothing hand-written is ever presented as + agent-authored. +- Keep a previously generated, known-good app source ready as a + fallback for the Persist beat. It was agent-authored — just not in + this take — and the narration should say so. +- Say the chatbox test out loud once: "a chat window could show this + code; here it runs, it stays, and it changes when I ask." +- Show the launcher or taskbar every time an app installs. That is how + a judge *sees* "installed." + +## Rubric lens on the open questions + +The team's register asks for input. From the scoring side only: + +- **ADR 0010 — install approval.** Auto-install is right for the + Materialize beat: it is the delight moment, and hesitation costs + criterion 1 flow. Criterion 4's "controllable" is then earned in the + Update and Control beats, where the user steers and can remove — so + keep auto-install *and* keep at least one of those two beats. +- **Q1.1 — floating chat window.** Good for the video: it can be moved + aside after the Ask so the camera stays on the desktop. +- **Q4.1 — persist installed apps only.** Enough for the Persist beat. + The narration should say "your apps come back" rather than "your + desktop comes back," so the reset windows never read as a bug. +- **Q5.1 — showcase apps.** Ranked above for the core-loop take. + +## Written description — skeleton for submission gate 2 + +The portal asks for "what you built, who it is for, and why the context +matters." Draft to that shape, in that order: + +1. **What we built.** DOMinic is a desktop OS in a browser tab where the + agent is the primary app author: describe an app and it is written, + installed, and kept like first-party software; ask for a change and + it updates in place. +2. **Who it is for.** *One named user and the job from the demo app.* +3. **Why the context matters.** The chatbox-test answer: a chat window + shows code; a browser tab runs it. The OS shell gives the agent's + output somewhere to live, persist, and be reopened, and the user + keeps control — update, uninstall, read the source. +4. **What is real in the repository.** Link the ADRs and their golden + paths; state plainly what runs in the video. + +## Social post — skeleton for submission gate 5 + +One public post that tags the event sponsors. Suggested shape: + +> We built DOMinic at the Agents, Everywhere hackathon: a browser OS +> where the agent writes the apps — describe one, and it installs and +> stays. [video] [repo] Thanks to [sponsor tags]. + +Sponsors named on the event page — confirm the exact handle for the +platform used before posting: OpenAI, CopilotKit, OpenRouter, Exa, +Auth0, Mozilla, Ambiguous AI, Trigger.dev, Rev1 Ventures, TeamClaws, +GDG Columbus, and AI Tinkerers Columbus. + +## Clock + +Freeze ~3:10, record and render ~3:10–3:30, upload and submit by ~3:50; +the portal closes at 4:00. The full cadence and the five submission gates +are in [[Judging Rubric & Win Strategy|rubric.md]]. diff --git "a/vault/Hackathon/Demo Video \342\200\224 Scenes.md" "b/vault/Hackathon/Demo Video \342\200\224 Scenes.md" new file mode 100644 index 0000000..3b190e6 --- /dev/null +++ "b/vault/Hackathon/Demo Video \342\200\224 Scenes.md" @@ -0,0 +1,556 @@ +--- +type: reference +title: Demo Video — Scenes +description: Scene-by-scene record of the final 6:32 demo video — what the viewer sees and hears in every scene, the exact on-screen text, the punch-ins, the retention principle each beat serves, and what is real footage versus graphic overlay. +tags: [hackathon, demo, video, scenes] +generated: { by: claude-code/claude-fable-5.1, at: 2026-09-12T20:55:00Z } +sources: + - id: composition + resource: ../../../project-files/dominic-video/index.html + title: HyperFrames composition — dominic-video/index.html (HyperFrames 0.8.36, root data-duration 392, 1920x1080 @ 30 fps) + - id: charles-demo + resource: ../../../project-files/dominic-video/assets/footage/charles-demo-2026-09-12-1617.mp4 + title: Charles's screen capture — Drive DOMinic/Charles/Screencast from 2026-09-12 16-17-22.webm (2256x1504, 168.36 s, no audio) + - id: interview-1 + resource: Drive DOMinic/DJI_20260912_155301_60_null_video.mp4 + title: Interview 1 — Charles (1920x1080 via rotation flag, 30 fps, stereo, 118.13 s) + - id: interview-2 + resource: Drive DOMinic/DJI_20260912_155508_62_null_video.mp4 + title: Interview 2 — Zack, team lead (1920x1080 via rotation flag, 120 fps, stereo, 91.0 s) + - id: demo-path + resource: Demo Path.md + title: Demo Path — What the Judges Must See +--- + +# Demo video — scenes + +This is the record of what is actually in the finished video, scene by +scene: what a viewer sees and hears, the exact words on screen, where +the camera punches in, which retention principle each beat is there to +serve, and — for every frame — what is real footage and what is a +graphic laid over it. The cue-level timing (every caption in and out, +every SFX hit) lives in [[Demo Video — Shot List]]; the argument for +*why* these beats and not others is in [[Demo Path]] and +[[Judging Rubric & Win Strategy]]. The mechanisms the captions name are +the golden paths of +[[ADR 0006 - Agent-authored runtime-compiled components|ADR 0006]], +[[ADR 0007 - Virtual filesystem with pluggable storage drivers|ADR 0007]] +and [[ADR 0010 - Agent app interface and tool protocol|ADR 0010]]. + +**Runtime:** 6:32 (392 s), 1920x1080 at 30 fps. +**Composition:** `project-files/dominic-video/index.html` — HyperFrames +0.8.36, root `data-duration` 392. +**Render** (from the repository root): + +```sh +cd project-files/dominic-video && npm run check && npx --yes hyperframes@0.8.36 render --output out/dominic-demo-v4.mp4 +``` + +## Scene map + +| Scene | In | Out | Length | Name | Real / graphic | +| --- | --- | --- | --- | --- | --- | +| A | 0:00.00 (0) | 0:02.40 (2.4) | 2.4 s | Cold open — flash-forward | real footage + kinetic text | +| B | 0:02.40 (2.4) | 0:06.40 (6.4) | 4.0 s | Stakes card | graphic | +| C | 0:06.40 (6.4) | 2:54.76 (174.76) | 168.36 s | The demo — Charles's capture, full length | real agent, real runtime, unedited; captions only | +| D | 2:54.76 (174.76) | 2:56.76 (176.76) | 2.0 s | Chapter card | graphic | +| E | 2:56.76 (176.76) | 4:54.89 (294.89) | 118.13 s | Interview 1 — Charles, full, unedited | real footage; captions only | +| F | 4:54.89 (294.89) | 6:25.89 (385.89) | 91.0 s | Interview 2 — Zack, team lead, full, unedited | real footage; captions only | +| G | 6:25.89 (385.89) | 6:32.00 (392) | 6.11 s | Close + handoff | graphic | + +Times below are composition seconds unless marked *clip*. In scene C, +composition time = 6.4 + clip time throughout. + +**All footage is 16:9** — the composition is 1920x1080 throughout — and +**the three shared clips are shown in full and unedited**: Charles's +capture (168.36 s) plays once, start to finish, at its original speed as +scene C, and the two native-16:9 interview clips (118.13 s and 91.0 s) +play start to finish, uncut and at their own audio, as scenes E and F. +The only framing change anywhere is the 3:2 capture shown as 16:9 from +the bottom (scene C); the flash-forward in scene A is a repeated 2.4 s +excerpt, not a cut. + +### The retention brief the scenes are built against + +Hook in the first frame (the finished dashboard); stakes on screen by +0:06; two open loops (two sentences → two apps, then *how it was built*); +a pattern interrupt every 4–8 s inside the demo (punch-ins, callouts, +SFX); the 27 s generation wait covered by three attention resets; the +payoff at ~1:55 (dashboard maximized); a chapter card; interviews carried +by pull-quote cards; and an abrupt close on Zack's last line straight +into a handoff card — no outro. + +### Audio conventions + +The bed is `assets/audio/bed-synth-pulse-long.wav`, a 120 BPM synth +pulse synthesized with ffmpeg `aevalsrc` — no licensed music. Levels: +0.55 at the open, 0.30 under the demo, 0.06 under the interviews, 0.7 at +the close. The SFX (impact, riser, whoosh, tick, chime) are all +synthesized with ffmpeg. Charles's screen capture has no audio track, so +every sound under scene C is added. Each interview plays its own stereo +track at full level. + +## Scene A — Cold open, flash-forward (0.0–2.4) + +The video opens on its own ending. Frame one is Charles's capture at +clip 109.5–111.9 — the Frontier AI Financial Report already maximized +and filling the screen — with an impact on the first frame and the synth +bed entering at 0.55. Over the 2.4 seconds the camera pushes slowly to +1.5x toward the chart column while two lines of kinetic type land, one +after the other. Nothing is explained; the viewer is shown the finished +thing and denied the how. When the same frame comes back at 1:55 in +scene C, the loop closes. + +- **On-screen text:** `One sentence in.` · `A real app out.` +- **Camera:** slow push to 1.5x toward the chart column (640,420) over + 2.4 s. +- **Audio:** impact at 0.0; bed in at 0.55. +- **Retention:** *hook* — the finished dashboard is in the first frame; + it is also the first *open loop* (a flash-forward the viewer has not + earned yet). +- **Real vs. graphic:** real footage (charles-demo 109.5–111.9) with a + kinetic-text overlay. + +## Scene B — Stakes card (2.4–6.4) + +Hard cut to black. The DOMinic windows banner sits under the type at 28% +opacity, dim enough to read as texture rather than a logo card. Three +lines of typography set the terms in four seconds: what this is not, +what the viewer is about to watch, and what comes after it. A riser +runs under the last line from 3.9 to 6.4 and hands off into the demo. +No footage; nothing moves except the type. + +- **On-screen text:** + `No chatbox. No copy-paste.` · + `Two plain sentences. Two real apps. Installed on camera, unedited.` · + `Then: how five people built it in one day.` +- **Camera:** none. +- **Audio:** riser 3.9–6.4; bed continues at 0.55. +- **Retention:** *stakes* by 0:06, and two *open loops* stated + outright — the two apps, and the "how" that scenes D–F pay off. +- **Real vs. graphic:** graphic — typography on black over + `assets/brand/dominic-banner-windows.png` at 28%. + +## Scene C — The demo, Charles's capture, full length (6.4–174.76) + +From 6.4 to 174.76 the video is Charles's 2:48 capture, played once, +start to finish, at its original speed; composition time is 6.4 plus +clip time throughout. The 3:2 source is framed to 16:9 from the bottom +(object-fit cover, object-position 50% 100%), which trims the macOS menu +bar and the browser chrome at the top and keeps the DOMinic taskbar. The +source has no audio, so everything heard — the bed at 0.30 with swells +at 33.2 and 115.2, and every whoosh, tick, impact, chime and riser cued +below — is synthesized. The agent, the runtime and the two apps are +real; the only things added to the picture are captions, lower thirds +and punch-ins. Between the sub-beats the footage keeps running at the +plain framing; the beats below are the moments where the camera or a +caption acts. + +- **Retention (whole scene):** the two *open loops* from scene B are + opened, held and closed on camera; *pattern interrupts* every 4–8 s; + the *delayed payoff* at ~1:55; a final *handoff* into scene D. +- **Real vs. graphic:** real agent, real runtime, unedited; overlays are + captions and lower thirds only. + +### C.1 · Desktop, chat open (6.8–12.0 · clip 0.4–5.6) + +A whoosh at 6.4 carries the cut from the stakes card into the capture. +The viewer sees the DOMinic desktop with the Agent Chat window open on +its welcome state; nothing has been installed and the taskbar lists no +apps. A lower third puts the rules of what follows on record — live +capture, one browser tab, nothing installed yet, 2:48 and unedited — so +the before-state is established before anything happens. The camera does +nothing here; the frame is the plain 16:9 crop. + +- **On-screen text:** lower third `LIVE CAPTURE · one browser tab · + nothing installed yet · 2:48, unedited` +- **Camera:** none. +- **Audio:** whoosh at 6.4. +- **Retention:** *stakes* — the baseline both payoffs are measured + against; the "unedited" claim is made before it has to be trusted. +- **Real vs. graphic:** real footage; the lower third is an overlay. + +### C.2 · Ask — typing the first sentence (11.4–21.4 · clip 5–15) + +Charles starts typing at clip 5 and sends the first prompt at clip 15. +The camera pushes slowly to 1.55x on the chat input so the sentence, not +the desktop, is what the viewer reads. Five synthesized keyboard ticks +between 12.4 and 16.4 give the silent source a sense of touch. The +caption holds from 13.0 to 20.5, over the typing and through the send. + +- **On-screen text:** `Plain English is the whole interface.` + (13.0–20.5) +- **Camera:** slow 1.55x push on the chat input (1190,606). +- **Audio:** five keyboard ticks, 12.4–16.4. +- **Retention:** *open loop* — a sentence goes in and the viewer waits to + see what comes out; the push and the ticks are the first *pattern + interrupt* inside the demo. +- **Real vs. graphic:** real footage; caption overlay; the ticks are + added (the source is silent). + +### C.3 · Materialize — streaming (21.4–33.4 · clip 15–27) + +The agent answers in the chat window, streaming its plan as text, and +an *Installing Application* pill appears in the thread. The camera +holds a 1.45x punch-in on the chat text for nine seconds so the +streaming and the pill can be read. A whoosh at 22 marks the reframe. +The caption runs 24.5–31.5, naming what the viewer is *not* being asked +to do while the agent works — this is the `install_app` tool call of +[[ADR 0010 - Agent app interface and tool protocol|ADR 0010]] as it +looks from the chat side. + +- **On-screen text:** `Nothing to copy. Nothing to paste. Nothing to + run.` (24.5–31.5) +- **Camera:** 1.45x on the chat text (1205,300), held 9 s. +- **Audio:** whoosh at 22. +- **Retention:** holds the *open loop* through the wait; the pill is the + tell that something is about to happen. +- **Real vs. graphic:** real footage; caption overlay. + +### C.4 · Materialize — the calculator installs (33.4–39.0 · clip 27–32.6) + +At clip 27 the Calculator window opens on its own and the taskbar lists +it — the first payoff, 33 seconds in. An impact and a chime hit at 33.4 +with the bed swelling under them, and the camera snaps to 1.6x on the +new window and holds for 4.5 seconds. Two overlays stack: a caption at +35.0–40.5 and, from 37.0 to 41.5, a lower third that spells out the +pipeline in one line. That line is the golden path of +[[ADR 0010 - Agent app interface and tool protocol|ADR 0010]] (the tool), +[[ADR 0007 - Virtual filesystem with pluggable storage drivers|ADR 0007]] +(source written to the VFS) and +[[ADR 0006 - Agent-authored runtime-compiled components|ADR 0006]] +(compiled in the tab), shown at the moment it visibly happened. + +- **On-screen text:** `It didn't paste code. It shipped an app.` + (35.0–40.5) · lower third `install_app → source written to the VFS → + compiled in the tab → window opens → taskbar lists it` (37.0–41.5) +- **Camera:** 1.6x snap on the calculator (518,395), held 4.5 s. +- **Audio:** impact + chime at 33.4; bed swell at 33.2. +- **Retention:** *delayed payoff* — loop 1 closes (sentence → app); the + loudest SFX cue in the demo. +- **Real vs. graphic:** real footage; caption and lower third overlays. + +### C.5 · Use — pressing the keys (44.0–50.4 · clip 37.6–44) + +Charles presses digits and the calculator display shows 41 (clip 39; it +shows 42 later, at clip 54). The camera goes to 1.7x on the display with +a single tick at 45.4, so the viewer sees state change inside an app that +did not exist a minute ago. The caption holds 45.5–51.5. After the +punch-in, while he handles the window, a lower third at 56–61 names the +window manager's controls. + +- **On-screen text:** `A real Vue component, compiled in your tab. Not + an iframe to somebody's server.` (45.5–51.5) · lower third + `WINDOW MANAGER drag · minimize · close` (56–61) +- **Camera:** 1.7x on the display (768,300). +- **Audio:** tick at 45.4. +- **Retention:** *stakes / proof* — the app works and is not a + screenshot; a *pattern interrupt* at one of the tightest zooms in the + demo. +- **Real vs. graphic:** real footage; overlays only. The component is a + runtime-compiled SFC per + [[ADR 0006 - Agent-authored runtime-compiled components|ADR 0006]]. + +### C.6 · Second ask (66.4–84.4 · clip 60–78) + +Charles types the second sentence from clip 60 to 78. The camera pushes +to 1.5x on the chat input over eight seconds and holds for eight more; a +whoosh at 67 marks the move. The caption at 70–78 raises the stakes: +the calculator was the warm-up. No other overlay. + +- **On-screen text:** `Now the real test: not a widget — a whole + analytics report.` (70–78) +- **Camera:** 1.5x push on the chat input (1190,606), 8 s in, 8 s hold. +- **Audio:** whoosh at 67. +- **Retention:** *open loop* 2, with the *stakes* raised. +- **Real vs. graphic:** real footage; caption overlay. + +### C.7 · Generation — 27 seconds, unedited (84.4–111.4 · clip 78–105) + +From clip 78 to 105 the agent generates the dashboard, and the video +shows all of it — about 27 seconds of a chat window working, the +riskiest stretch for attention in the whole cut. It is covered by three +resets. First, a whoosh at 86 and a 1.35x punch-in on the streaming chat +text held to 95.2, with a caption at 88–94. Second, a lower third at +97–104 saying what is being generated. Third, a caption at 104.5–110.5 +while a riser builds from 108.9 to 111.4 into the window's appearance. + +- **On-screen text:** `Twenty-seven seconds of generation. Unedited.` + (88–94) · lower third `GENERATING a multi-view dashboard: providers · + valuations · revenue · export` (97–104) · `Still one tab. Still one + sentence.` (104.5–110.5) +- **Camera:** 1.35x on the chat text, held 8 s (86–95.2). +- **Audio:** whoosh at 86; riser 108.9–111.4. +- **Retention:** three attention resets (*pattern interrupts*) carry the + wait; the *delayed payoff* is deliberately held rather than cut. +- **Real vs. graphic:** real footage in real time; caption and lower + third overlays. + +### C.8 · Report window appears (111.4–115.4 · clip 105–109) + +The riser lands on an impact at 111.4 as the Frontier AI Financial +Report window opens, small, on the desktop at clip 105. The camera snaps +to 1.6x on the new window. There is no text on screen for these four +seconds — the only demo beat with none — so nothing competes with the +window itself. Four seconds later Charles maximizes it. + +- **On-screen text:** none. +- **Camera:** 1.6x snap on the window (537,280). +- **Audio:** impact at 111.4. +- **Retention:** *pattern interrupt* (no text after three captions); the + payoff is split in two on purpose — arrive, then reveal. +- **Real vs. graphic:** real footage; no overlay. + +### C.9 · Payoff — the dashboard maximized (115.4–131 · clip 109–124.6) + +At clip 109 the report fills the screen: charts, provider cards and an +export control, agent-authored end to end. A chime and a soft impact at +115.4 and the bed's second swell (to 0.6) mark it, and the camera pushes +slowly to 1.15x over six seconds instead of snapping. A caption holds +117–123, followed by a lower third at 124–130. This is the frame the +video opened on — the flash-forward in scene A is paid off here, at +1:55. + +- **On-screen text:** `Frontier AI Financial Report. From one sentence.` + (117–123) · lower third `AGENT-AUTHORED charts · filters · provider + cards · export` (124–130) +- **Camera:** slow 1.15x push (700,450) over 6 s. +- **Audio:** chime + soft impact at 115.4; bed swell to 0.6 at 115.2. +- **Retention:** the *delayed payoff* — planned at ~1:55; closes loop 2 + and the scene A flash-forward. +- **Real vs. graphic:** real footage; caption and lower third overlays. + +### C.10 · Provider cards (131–140 · clip 124.6–133.6) + +Charles scrolls the report. A whoosh at 131 and a 1.5x punch-in on the +right column follow the provider cards as they move. The caption at +133–140 is the chatbox test from [[Judging Rubric & Win Strategy]] and +[[Demo Path]], said once, over the evidence. Nothing else is added. + +- **On-screen text:** `The chatbox test: a chat window shows code. + DOMinic runs it.` (133–140) +- **Camera:** 1.5x on the right column (1500,450). +- **Audio:** whoosh at 131. +- **Retention:** *stakes* restated as the thesis; *pattern interrupt*. +- **Real vs. graphic:** real footage; caption overlay. + +### C.11 · Minimized (145.4–149.4 · clip 139–143) + +Charles minimizes the report between clip 139 and 143; the desktop and +the chat window are visible again and the report sits on the taskbar. A +whoosh at 146.2 and a 1.8x punch-in on the taskbar — the tightest zoom in +the video — point at the one place a judge can *see* "installed", which +is the filming rule [[Demo Path]] insists on. The caption holds 146–150. + +- **On-screen text:** `Minimized. It's on the taskbar, like any app.` + (146–150) +- **Camera:** 1.8x on the taskbar (230,1059). +- **Audio:** whoosh at 146.2. +- **Retention:** proof of *environment, not wrapper*; a *pattern + interrupt* before the last beat. +- **Real vs. graphic:** real footage; caption overlay. + +### C.12 · Reopened (150.4–174.76 · clip 144–168.36) + +At clip 144 the report is restored from the taskbar and Charles explores +it to the end of the capture. A whoosh at 150.4 marks the restore and a +caption holds 151.5–157. From 160 to 170 the camera makes a last slow +1.35x push while a second caption (164–172) closes the promise made on +the stakes card. Under it, a lower third at 170.5–174.5 opens the next +loop, and a riser from 172.3 carries into the chapter card at 174.76, +where the clip ends. + +- **On-screen text:** `Reopened from the taskbar.` (151.5–157) · + `Two sentences. Two apps. One browser tab.` (164–172) · lower third + `NEXT how five people — and a swarm of agents — built this in one day` + (170.5–174.5) +- **Camera:** 1.35x slow push (600,420), 160–170. +- **Audio:** whoosh at 150.4; riser from 172.3. +- **Retention:** closes loop 2 and the stakes-card promise; re-opens the + "how it was built" loop; *handoff* into scene D. +- **Real vs. graphic:** real footage; caption and lower third overlays. + +## Scene D — Chapter card (174.76–176.76) + +Impact at 174.76 and a hard cut to black; the bed comes up to 0.55. Two +lines of type, two seconds: the chapter title and the ground rule for +the next three and a half minutes. It answers the lower third that just +left the screen and tells the viewer the interviews are unedited before +they start. + +- **On-screen text:** `How five people built this in one day.` · + `Two interviews. Unedited.` +- **Camera:** none. +- **Audio:** impact at 174.76; bed at 0.55. +- **Retention:** *pattern interrupt* between demo and people; starts the + *delayed payoff* of the "how" loop opened on the stakes card (scene B); + *handoff*. +- **Real vs. graphic:** graphic — typography on black. + +## Scene E — Interview 1, Charles, full and unedited (176.76–294.89) + +Cut to Charles on the hackathon floor, framed by the DJI clip's native +16:9, with his own mic audio at full level and the bed dropped to 0.06 +under it. A whoosh at 176.76 covers the cut; the clip then plays +unedited for its full 118.13 seconds, and an off-camera interviewer +opens it. The camera does nothing. What the composition adds is a lower +third that timestamps the setting, a name card at +34, and six caption +cards timed to the passages where Charles describes the GitHub-issues +skill, the OpenAI, Gemini and Anthropic agents coordinating with each +other, the issues and PRs, and the Playwright pass that filed new issues +from screenshots. Two of the cards are quote cards — his own words, as +transcribed (see [[#Interview overlays]]). + +- **On-screen text (clip time):** + `HOW IT WAS BUILT · Hackathon floor · Columbus · 3:53 PM` (+0.4–8, + lower third) · `Charles · Team DOMinic` (+34–41, name card) · quote + `I don't want to show you what we're working on. I want to talk about + how.` (+39.6–47) · `A skill for GitHub issues and the GitHub CLI, so + the agents coordinate their own development.` (+53.2–61) · + `OpenAI · Gemini · Anthropic agents — coordinating with each other.` + (+72.6–80) · `Issues #58, #59, #60 · dozens of PRs, reviewed and + merged.` (+80.4–88) · `An agent ran a Playwright test pass and filed + new issues from the screenshots.` (+95.9–102) · quote `A team of + agents, very loosely supervised, produced a complex project.` + (+102–110.5) +- **Camera:** none. +- **Audio:** clip audio at 1.0; bed at 0.06; whoosh at 176.76. +- **Retention:** *delayed payoff* of the "how" loop; the cards are + *pattern interrupts* roughly every ten seconds that keep a static + talking head watchable. +- **Real vs. graphic:** real footage, unedited; overlays are captions. + +## Scene F — Interview 2, Zack, team lead, full and unedited (294.89–385.89) + +Cut to Zack, team lead, in the same setup: native 16:9, own audio at +full level, bed at 0.06, a whoosh at 294.89 on the cut, the full 91.0 +seconds unedited, no camera moves. A name card opens at +0.6 and a +lower third at +8.5 frames the chapter. Six caption cards follow the +argument: a web-based OS in a browser where agents communicate in +application implementations; the challenge of everyone building in +parallel with agents that implement faster than the team can; +coordination through GitHub issues and one master control issue; agents +that create, reserve, open, review and merge. The last two are quote +cards, and the video cuts out of his last line straight into the close, +with no outro. + +- **On-screen text (clip time):** `Zack · Team lead · DOMinic` (+0.6–8, + name card) · `WHAT IT IS · the idea, the architecture, the agents` + (+8.5–14, lower third) · `A web-based OS in a browser, so agents + communicate not in text, but in application implementations.` + (+12.8–27.5) · `Largest challenge: everyone in parallel — with agents + that implement faster than we can.` (+30.5–39.5) · `The agents + coordinate through GitHub issues — one master control issue.` + (+48.2–60) · `They create issues, reserve them, open PRs, review them, + and merge.` (+61.4–70) · quote `An entire automated development team.` + (+70.3–79.8) · quote `This is a ridiculous time to live in.` + (+82.2–89.5) +- **Camera:** none. +- **Audio:** clip audio at 1.0; bed at 0.06; whoosh at 294.89. +- **Retention:** *delayed payoff* continues; the abrupt cut on his last + line is the final *pattern interrupt* and the *handoff* into scene G. +- **Real vs. graphic:** real footage, unedited; overlays are captions. + +## Scene G — Close + handoff (385.89–392) + +Impact at 385.89 on the cut to the brand art — `dominic-banner-d.png` +over the grid background — which settles from 1.12x to rest while a +whoosh at 388.9 brings in the text and the bed lifts to 0.7. Four things +are on screen: the one-line tagline, the repository URL, a next step +that tells the viewer what to do, and the sponsor row. Six seconds, then +it ends. There is no outro, no recap and no thank-you card; the handoff +is the ending. + +- **On-screen text:** `The desktop where the agent writes the apps.` · + `github.com/xcjs/DOMinic` · `Next → go break it yourself. Clone it, + ask it for anything.` · sponsor row `AGENTS, EVERYWHERE · AI TINKERERS + COLUMBUS · GDG COLUMBUS · OPENAI · COPILOTKIT · OPENROUTER · EXA · + AUTH0 · MOZILLA · TRIGGER.DEV · REV1 VENTURES · TEAMCLAWS · AMBIGUOUS + AI` +- **Camera:** art settles from 1.12x. +- **Audio:** impact at 385.89; whoosh at 388.9; bed at 0.7. +- **Retention:** *handoff* — a next action instead of an outro. +- **Real vs. graphic:** graphic — `assets/brand/dominic-banner-d.png` + (brand art) over the grid background. + +## Interview overlays + +Every card in scenes E and F is keyed to clip time; each clip starts at +its scene's in-point, so clip time = composition time − 176.76 for +Charles and − 294.89 for Zack. The card wording was written from a +faster-whisper `base.en` transcript of the two clips. The cards the +composition styles as **quote cards** carry Charles's and Zack's words +verbatim from that transcript; the remaining cards are caption cards +keyed to the same passages. Treat all of them as *as transcribed; +verify against audio* before quoting them anywhere else. + +| Scene | Clip tc | Comp tc | Kind | Text | +| --- | --- | --- | --- | --- | +| E | +0.4–8 | 177.16–184.76 | lower third | HOW IT WAS BUILT · Hackathon floor · Columbus · 3:53 PM | +| E | +34–41 | 210.76–217.76 | name card | Charles · Team DOMinic | +| E | +39.6–47 | 216.36–223.76 | **quote card** | "I don't want to show you what we're working on. I want to talk about how." — as transcribed; verify against audio | +| E | +53.2–61 | 229.96–237.76 | caption | A skill for GitHub issues and the GitHub CLI, so the agents coordinate their own development. — as transcribed; verify against audio | +| E | +72.6–80 | 249.36–256.76 | caption | OpenAI · Gemini · Anthropic agents — coordinating with each other. — as transcribed; verify against audio | +| E | +80.4–88 | 257.16–264.76 | caption | Issues #58, #59, #60 · dozens of PRs, reviewed and merged. — as transcribed; verify against audio | +| E | +95.9–102 | 272.66–278.76 | caption | An agent ran a Playwright test pass and filed new issues from the screenshots. — as transcribed; verify against audio | +| E | +102–110.5 | 278.76–287.26 | **quote card** | "A team of agents, very loosely supervised, produced a complex project." — as transcribed; verify against audio | +| F | +0.6–8 | 295.49–302.89 | name card | Zack · Team lead · DOMinic | +| F | +8.5–14 | 303.39–308.89 | lower third | WHAT IT IS · the idea, the architecture, the agents | +| F | +12.8–27.5 | 307.69–322.39 | caption | A web-based OS in a browser, so agents communicate not in text, but in application implementations. — as transcribed; verify against audio | +| F | +30.5–39.5 | 325.39–334.39 | caption | Largest challenge: everyone in parallel — with agents that implement faster than we can. — as transcribed; verify against audio | +| F | +48.2–60 | 343.09–354.89 | caption | The agents coordinate through GitHub issues — one master control issue. — as transcribed; verify against audio | +| F | +61.4–70 | 356.29–364.89 | caption | They create issues, reserve them, open PRs, review them, and merge. — as transcribed; verify against audio | +| F | +70.3–79.8 | 365.19–374.69 | **quote card** | "An entire automated development team." — as transcribed; verify against audio | +| F | +82.2–89.5 | 377.09–384.39 | **quote card** | "This is a ridiculous time to live in." — as transcribed; verify against audio | + +## Honesty notes + +- **The demo is the real agent.** Scene C is Charles's own capture + (Drive: `DOMinic/Charles/Screencast from 2026-09-12 16-17-22.webm`, + 2256x1504 3:2, VP8, variable frame rate, no audio, 168.36 s) of the + real agent and the real runtime responding to two typed prompts — not + the scripted `demo/mock-showcase` build — and it plays once, in full, at + its original speed. The file used in the composition + (`assets/footage/charles-demo-2026-09-12-1617.mp4`) is a + constant-frame-rate H.264 copy at the same resolution and length, made + so frame extraction is reliable — no cut, crop or speed change in the + transcode. The only framing change is in the composition: the 3:2 + picture is shown as 16:9 from the bottom, which trims the macOS menu + bar and browser chrome and keeps the DOMinic taskbar. +- **The scripted branch is not in the video.** The earlier + `demo/mock-showcase` branch (commit `0ea677b`, worktree + `project-files/DOMinic-demo`) exists, and our own Playwright captures + of that scripted build were made, but none of it is in the final cut. + Charles's real-agent capture replaced it. +- **The interviews are unedited.** Both DJI clips play in full — Charles + 118.13 s, Zack 91.0 s — with their own stereo audio at full level and + no cuts, trims or reorders. Only captions were added. +- **"Twenty-seven seconds of generation. Unedited." is the real timing.** + Generation runs from clip 78 to clip 105 (about 27 s) and the + composition shows all of it (84.4–111.4). Likewise the lower third + "2:48, unedited" is the clip's true length, 168.36 s. +- **Every sound under the demo is added.** The capture has no audio. + The bed is a synthesized 120 BPM pulse (ffmpeg `aevalsrc`), the SFX are + synthesized with ffmpeg, and there is no licensed music anywhere in the + video. The interviews' sound is their own. +- **Quote cards are transcript text.** They come from a faster-whisper + `base.en` transcript and are marked "as transcribed; verify against + audio" above; they have not been checked word for word against the + clips. +- **Footage on the Drive that is not used:** the two terminal screen + recordings (15:46 early playthrough, 15:47 process — the + producer asked for interview footage and our own captures only); + Charles's 15:33 Playwright test-run screencast (superseded by his 16:17 + working demo); the repo `docs/assets` JPG screenshots (removed after + v1); and our Playwright captures of the scripted `demo/mock-showcase` + build. Two VeniceAI images are used (`dominic-banner-d.png` in scene G, + `dominic-banner-windows.png` under scene B); the eight other VeniceAI + logo variants on the Drive are not. +- **What the captions claim is what the ADRs describe.** The C.4 lower + third (`install_app` → VFS → compiled in the tab → window → taskbar) + names the mechanisms of + [[ADR 0010 - Agent app interface and tool protocol|ADR 0010]], + [[ADR 0007 - Virtual filesystem with pluggable storage drivers|ADR 0007]] + and + [[ADR 0006 - Agent-authored runtime-compiled components|ADR 0006]]; + see each note's As-built section for what shipped. diff --git "a/vault/Hackathon/Demo Video \342\200\224 Shot List.md" "b/vault/Hackathon/Demo Video \342\200\224 Shot List.md" new file mode 100644 index 0000000..1193b00 --- /dev/null +++ "b/vault/Hackathon/Demo Video \342\200\224 Shot List.md" @@ -0,0 +1,241 @@ +--- +type: playbook +title: Demo Video — Shot List +description: Scene-by-scene shot list for the finished 6:32 DOMinic demo video — timecodes, source footage, punch-ins, on-screen text, audio cues, and what is real footage versus graphic. +tags: [hackathon, demo, video, shot-list] +generated: { by: claude-code/claude-fable-5.1, at: 2026-09-12T20:55:00Z } +sources: + - id: composition + resource: ../../../project-files/dominic-video/index.html + title: HyperFrames composition — dominic-video/index.html (HyperFrames 0.8.36, root data-duration 392, 1920x1080 @ 30 fps) + - id: charles-demo + resource: "Drive: DOMinic/Charles/Screencast from 2026-09-12 16-17-22.webm" + title: Charles's working-demo capture, 16:17 (2256x1504 3:2, VP8, VFR, no audio, 168.36 s) + - id: interview-1 + resource: "Drive: DOMinic/DJI_20260912_155301_60_null_video.mp4" + title: Interview 1 — Charles (displays 1920x1080, 30 fps, stereo, 118.13 s) + - id: interview-2 + resource: "Drive: DOMinic/DJI_20260912_155508_62_null_video.mp4" + title: Interview 2 — Zack, team lead (displays 1920x1080, 120 fps, stereo, 91.0 s) + - id: demo-path + resource: Demo Path.md + title: Demo Path — What the Judges Must See +--- + +# Demo video — shot list + +The finished DOMinic hackathon video is a **6:32 (392 s), 1920x1080, 30 fps +HyperFrames composition** (`project-files/dominic-video/index.html`, +HyperFrames 0.8.36, root `data-duration` 392). It is built from three +shared clips and nothing else: Charles's 16:17 working-demo capture (the +real agent installing two real apps in one browser tab, 168.36 s), and the +two DJI interview clips — Charles (118.13 s) and Zack, team lead (91.0 s). +Around them sit typography cards, two Venice AI brand images, and a +synthesized audio bed with synthesized SFX. This note is the cue-level +record: what is on screen at every second, where each frame came from, and +which parts are real footage versus graphics. Its prose companion — what +the viewer sees and hears in each scene, and the retention principle each +beat serves — is [[Demo Video — Scenes]]. The filming plan both descend +from is [[Demo Path]]; the scoring logic is [[Judging Rubric & Win Strategy]]. + +## Footage rules the cut follows + +- **All footage is 16:9.** The composition is 1920x1080 throughout. +- **The three shared clips are shown in full and unedited** — no cuts, no + speed change, and no crop of the two native-16:9 interview clips. Each + clip's start-to-end runs as one continuous piece (scenes C, E, F). +- **Charles's 3:2 MacBook capture is framed 16:9 anchored to the bottom** + (`object-fit: cover`, `object-position: 50% 100%`). That trims only the + macOS menu bar and browser chrome at the top and keeps the DOMinic + taskbar. (Computed from the stated 2256x1504 source: 2256x1269 is + visible, the top 235 px fall outside the frame.) The clip is otherwise + untouched — no cut, crop, or speed change; the local copy is a + constant-frame-rate H.264 transcode at the same resolution and length, + made only so frame extraction is reliable. +- **No screen recordings other than Charles's working-demo capture.** The + earlier terminal recordings and the Playwright run are not used (see + [[#Deliberately not used]]). +- **No drawn UI mockups.** Every pixel of DOMinic on screen is the real + runtime, captured live. Overlays are captions, lower thirds, and camera + moves only. +- **Pull quotes are "as transcribed"** — they come from a faster-whisper + `base.en` transcript of the two interview clips, not from a human + transcript. +- The flash-forward in scene A reuses 2.4 s of the demo capture (clip + 109.5–111.9). That is a repeated excerpt, not a cut: the full clip still + plays uninterrupted in scene C. + +## Retention architecture + +The structure follows a YouTube-retention brief: hook in the first frame, +stakes by 0:06, open loops, pattern interrupts through the demo, a covered +dead-air stretch, a delayed payoff at ~1:55, a chapter card, interviews +with pull-quote cards, and an abrupt close into a handoff card with no +outro. How each principle maps onto the cut: + +| Principle | Where it lives in the cut | How | +| --- | --- | --- | +| **Packaging promise validated in frame 1** | Scene A, 0:00.0 | Frame 1 is the finished dashboard, already maximized (clip 109.5), with "One sentence in. / A real app out." The viewer sees the payoff exists before anything is asked of them. Whether the published title and thumbnail match this frame: not verified. | +| **Zero-fluff cold open** | Scene A, 0:00.0–0:02.4 | No logo, no host, no intro. 2.4 s of the result, an impact hit at 0, a 1.5x push toward the chart column, and straight into stakes. | +| **Stakes by 0:06** | Scene B, 0:02.4–0:06.4 | The contract in three lines: "No chatbox. No copy-paste." / "Two plain sentences. Two real apps. Installed on camera, unedited." / "Then: how five people built it in one day." A riser 3.9–6.4 hands off to the live capture at 6.4. | +| **Open loops** | Opened in B; closed at 0:33.4, 1:55.4, and 6:25.89 | Loop 1 ("two sentences → two apps") pays out twice: the calculator window at 0:33.4 and the dashboard at 1:55.4. Loop 2 ("then: how five people built it in one day") is re-armed by the NEXT lower third at 2:50.5–2:54.5, opened by the chapter card at 2:54.76, and closed by the two interviews. | +| **Pattern interrupts every 4–8 s during the demo** | Scene C sub-beats C.1–C.12 | The footage never cuts, so all rhythm comes from overlays: punch-ins from 1.15x to 1.8x, captions and lower thirds, and SFX (whoosh, tick, impact, chime, riser) at the cues in the table. From the cue list, the longest stretches with no new cue are roughly 0:45.5–0:56, 0:56–1:06.4, 1:10–1:24.4, and 2:13–2:25.4 — there the clip's own action (the calculator in use; the report being scrolled) runs with only the bed underneath. | +| **Dead-air management: the 27 s generation** | C.7, 1:24.4–1:51.4 | Three attention resets over an uncut wait: (1) a 1.35x punch on the streaming chat text 86–95.2 with a whoosh at 86 and the caption "Twenty-seven seconds of generation. Unedited." 88–94; (2) the lower third GENERATING a multi-view dashboard: providers · valuations · revenue · export 97–104; (3) the caption "Still one tab. Still one sentence." 104.5–110.5 under a riser 108.9–111.4 that lands on the impact at 111.4 as the window appears. | +| **Delayed payoff at ~1:55** | C.9, 1:55.4 (115.4) | The report window first appears small at 1:51.4 (pre-payoff, impact only, no caption). The maximize at 1:55.4 is the payoff: chime + soft impact, bed swell to 0.6, a slow 1.15x push over 6 s, and "Frontier AI Financial Report. From one sentence." | +| **Chapter card, not a second cold open** | Scene D, 2:54.76–2:56.76 | Two seconds on black: "How five people built this in one day." / "Two interviews. Unedited." An impact at 174.76 and the bed back up to 0.55. The loop was already re-armed by the NEXT lower third, so the card only names the chapter. | +| **Interviews with pull-quote cards** | Scenes E and F | Both clips run full length at their own audio (1.0) with the bed at 0.06. Lower thirds, name cards, and quote cards at the clip-relative offsets in the table keep text on screen; the two marked *quote* per interview are verbatim as transcribed. | +| **Handoff card instead of an outro** | Scene G, 6:25.89–6:32.0 | Zack's last line ("This is a ridiculous time to live in.", clip +82.2–89.5) runs to the end of his clip; an impact at 385.89 cuts straight to brand art settling from 1.12x, the repo URL, and "Next → go break it yourself. Clone it, ask it for anything." 6.1 s, no recap, no thanks-for-watching. The sponsor row sits on the same card. | + +## Shot list + +How to read the table: + +- **In / Out** are composition times as m:ss.s with raw seconds in + parentheses. Sub-beat rows (C.1–C.12) are the overlay windows inside + scene C; the underlying clip runs continuously through them and through + the gaps between them. +- **Source timecodes** for `charles-demo` are clip seconds; composition + time = 6.4 + clip time. Interview overlay offsets are clip-relative + (`+s` from clip start). +- **Camera** coordinates are `(x, y)` punch-in targets in the 1920x1080 + frame; the multiplier is the zoom scale. +- **On-screen text** timings in parentheses are composition seconds. + Captions marked *quote* are verbatim as transcribed; unmarked interview + callouts are captions, not necessarily verbatim. +- `charles-demo` = `assets/footage/charles-demo-2026-09-12-1617.mp4` + (CFR H.264 copy of the shared `.webm`, same length and resolution). + +| # | In | Out | Scene | Source footage (file + source timecode) | Camera (punch-in target and scale) | On-screen text | Audio | Real vs. graphic | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| **A** | 0:00.0 (0) | 0:02.4 (2.4) | Cold open — flash-forward | charles-demo @ 109.5–111.9 (dashboard already maximized) | slow push 1.5x toward the chart column (640, 420) over 2.4 s | "One sentence in." / "A real app out." (kinetic text) | impact at 0; bed 0.55 | real footage + kinetic text | +| **B** | 0:02.4 (2.4) | 0:06.4 (6.4) | Stakes card | typography on black over `dominic-banner-windows.png` at 28% | none | "No chatbox. No copy-paste." / "Two plain sentences. Two real apps. Installed on camera, unedited." / "Then: how five people built it in one day." | riser 3.9–6.4 | graphic | +| **C** | 0:06.4 (6.4) | 2:54.76 (174.76) | The demo — Charles's capture, full length | charles-demo 0–168.36 (comp = 6.4 + clip time) | see sub-beats C.1–C.12 | none at scene level — see sub-beats | bed 0.30 with swells at 33.2 and 115.2 | real agent, real runtime; overlays are captions only | +| C.1 | 0:06.8 (6.8) | 0:12.0 (12.0) | Desktop, chat open — Agent Chat welcome; nothing installed | charles-demo 0.4–5.6 | none | lower third: LIVE CAPTURE · one browser tab · nothing installed yet · 2:48, unedited | whoosh 6.4 | real footage; caption overlay | +| C.2 | 0:11.4 (11.4) | 0:21.4 (21.4) | Ask — first sentence typed into Agent Chat | charles-demo 5–15 | slow 1.55x push on the chat input (1190, 606) | "Plain English is the whole interface." (13.0–20.5) | 5 keyboard ticks 12.4–16.4 | real footage; caption overlay | +| C.3 | 0:21.4 (21.4) | 0:33.4 (33.4) | Materialize (streaming) — agent streams its plan; Installing Application pill | charles-demo 15–27 | 1.45x on chat text (1205, 300) held 9 s | "Nothing to copy. Nothing to paste. Nothing to run." (24.5–31.5) | whoosh 22 | real footage; caption overlay | +| C.4 | 0:33.4 (33.4) | 0:39.0 (39.0) | Materialize (install) — Calculator window opens; taskbar lists it | charles-demo 27–32.6 | 1.6x snap on the calculator (518, 395), held 4.5 s | "It didn't paste code. It shipped an app." (35.0–40.5) + lower third: `install_app` → source written to the VFS → compiled in the tab → window opens → taskbar lists it (37.0–41.5) | impact + chime 33.4; bed swell | real footage; caption overlay | +| C.5 | 0:44.0 (44.0) | 0:50.4 (50.4) | Use — digits pressed; display shows 41 | charles-demo 37.6–44 | 1.7x on the display (768, 300) | "A real Vue component, compiled in your tab. Not an iframe to somebody's server." (45.5–51.5); lower third: WINDOW MANAGER drag · minimize · close (56–61) | tick 45.4 | real footage; caption overlay | +| C.6 | 1:06.4 (66.4) | 1:24.4 (84.4) | Second ask — second sentence typed | charles-demo 60–78 | 1.5x push on chat input (1190, 606), 8 s in, 8 s hold | "Now the real test: not a widget — a whole analytics report." (70–78) | whoosh 67 | real footage; caption overlay | +| C.7 | 1:24.4 (84.4) | 1:51.4 (111.4) | Generation (27 s) — agent generates the dashboard | charles-demo 78–105 | 1.35x on chat text held 8 s (86–95.2) | "Twenty-seven seconds of generation. Unedited." (88–94); lower third: GENERATING a multi-view dashboard: providers · valuations · revenue · export (97–104); "Still one tab. Still one sentence." (104.5–110.5) | whoosh 86; riser 108.9–111.4 | real footage; caption overlay | +| C.8 | 1:51.4 (111.4) | 1:55.4 (115.4) | Report window appears — Frontier AI Financial Report window opens small | charles-demo 105–109 | 1.6x snap on the window (537, 280) | none | impact 111.4 | real footage | +| C.9 | 1:55.4 (115.4) | 2:11.0 (131) | Payoff — maximized dashboard fills the screen: charts, provider cards, export | charles-demo 109–124.6 | slow 1.15x push (700, 450) over 6 s | "Frontier AI Financial Report. From one sentence." (117–123); lower third: AGENT-AUTHORED charts · filters · provider cards · export (124–130) | chime + soft impact 115.4; bed swell 0.6 | real footage; caption overlay | +| C.10 | 2:11.0 (131) | 2:20.0 (140) | Provider cards — scrolling the report | charles-demo 124.6–133.6 | 1.5x on the right column (1500, 450) | "The chatbox test: a chat window shows code. DOMinic runs it." (133–140) | whoosh 131 | real footage; caption overlay | +| C.11 | 2:25.4 (145.4) | 2:29.4 (149.4) | Minimized — report minimized; desktop + chat visible | charles-demo 139–143 | 1.8x on the taskbar (230, 1059) | "Minimized. It's on the taskbar, like any app." (146–150) | whoosh 146.2 | real footage; caption overlay | +| C.12 | 2:30.4 (150.4) | 2:54.76 (174.76) | Reopened — report restored from the taskbar and explored to the end of the clip | charles-demo 144–168.36 | 1.35x slow push (600, 420) 160–170 | "Reopened from the taskbar." (151.5–157); "Two sentences. Two apps. One browser tab." (164–172); lower third: NEXT how five people — and a swarm of agents — built this in one day (170.5–174.5) | whoosh 150.4; riser 172.3 | real footage; caption overlay | +| **D** | 2:54.76 (174.76) | 2:56.76 (176.76) | Chapter card | typography on black | none | "How five people built this in one day." / "Two interviews. Unedited." | impact 174.76; bed 0.55 | graphic | +| **E** | 2:56.76 (176.76) | 4:54.89 (294.89) | Interview 1 — Charles (full, unedited, 118.13 s) | `DJI_20260912_155301_60` 0–118.13, native 16:9, own audio | none | +0.4–8 lower third: HOW IT WAS BUILT · Hackathon floor · Columbus · 3:53 PM · +34–41 name card: Charles · Team DOMinic · +39.6–47 *quote, as transcribed*: "I don't want to show you what we're working on. I want to talk about how." · +53.2–61: A skill for GitHub issues and the GitHub CLI, so the agents coordinate their own development. · +72.6–80: OpenAI · Gemini · Anthropic agents — coordinating with each other. · +80.4–88: Issues #58, #59, #60 · dozens of PRs, reviewed and merged. · +95.9–102: An agent ran a Playwright test pass and filed new issues from the screenshots. · +102–110.5 *quote, as transcribed*: "A team of agents, very loosely supervised, produced a complex project." | clip audio 1.0; bed 0.06; whoosh 176.76 | real footage; overlays are captions | +| **F** | 4:54.89 (294.89) | 6:25.89 (385.89) | Interview 2 — Zack, team lead (full, unedited, 91.0 s) | `DJI_20260912_155508_62` 0–91.0, native 16:9, own audio | none | +0.6–8 name card: Zack · Team lead · DOMinic · +8.5–14 lower third: WHAT IT IS · the idea, the architecture, the agents · +12.8–27.5: A web-based OS in a browser, so agents communicate not in text, but in application implementations. · +30.5–39.5: Largest challenge: everyone in parallel — with agents that implement faster than we can. · +48.2–60: The agents coordinate through GitHub issues — one master control issue. · +61.4–70: They create issues, reserve them, open PRs, review them, and merge. · +70.3–79.8 *quote, as transcribed*: "An entire automated development team." · +82.2–89.5 *quote, as transcribed*: "This is a ridiculous time to live in." | clip audio 1.0; bed 0.06; whoosh 294.89 | real footage; overlays are captions | +| **G** | 6:25.89 (385.89) | 6:32.0 (392) | Close + handoff | `dominic-banner-d.png` (brand art) over the grid background | art settles from 1.12x | "The desktop where the agent writes the apps." / github.com/xcjs/DOMinic / "Next → go break it yourself. Clone it, ask it for anything." / sponsor row: AGENTS, EVERYWHERE · AI TINKERERS COLUMBUS · GDG COLUMBUS · OPENAI · COPILOTKIT · OPENROUTER · EXA · AUTH0 · MOZILLA · TRIGGER.DEV · REV1 VENTURES · TEAMCLAWS · AMBIGUOUS AI | impact 385.89; whoosh 388.9; bed 0.7 | graphic | + +Notes on the numbers: + +- Some caption windows outlast their sub-beat (C.4's caption runs to 40.5; + C.5's WINDOW MANAGER lower third runs 56–61, after the punch-in has + released). These are as authored, not errors: the caption clock and the + camera clock are independent. +- C.1 and C.2 overlap by 0.6 s (12.0 vs. 11.4): the lower third is still + fading as the push on the chat input begins. +- Between sub-beats (0:39.0–0:44.0, 0:50.4–1:06.4, 2:20.0–2:25.4 and + 2:29.4–2:30.4) the clip keeps playing with no camera move; a caption + from the previous sub-beat may still be on screen, per the first bullet. + Per the beats-in-clip list, the calculator + is in use through clip 37–60 (display shows 41 at 39 and 42 at 54, i.e. + comp 45.4 and 60.4). + +### Beat map — Charles's capture (clip → composition) + +Every event the composition keys on, in clip seconds and composition +seconds (comp = clip + 6.4). This is the cross-check for the sub-beat rows +above. + +| Event in the clip | Clip s | Comp s | Comp m:ss.s | +| --- | --- | --- | --- | +| Typing starts | 5 | 11.4 | 0:11.4 | +| First prompt sent | 15 | 21.4 | 0:21.4 | +| Calculator installs and opens | 27 | 33.4 | 0:33.4 | +| Calculator used (shows 41 at 39, 42 at 54) | 37–60 | 43.4–66.4 | 0:43.4–1:06.4 | +| Second prompt typed | 60–78 | 66.4–84.4 | 1:06.4–1:24.4 | +| Generation (about 27 s) | 78–105 | 84.4–111.4 | 1:24.4–1:51.4 | +| Report window appears | 105 | 111.4 | 1:51.4 | +| Report maximized | 109 | 115.4 | 1:55.4 | +| Minimized to taskbar | 139–143 | 145.4–149.4 | 2:25.4–2:29.4 | +| Reopened | 144 | 150.4 | 2:30.4 | +| End of clip | 168.36 | 174.76 | 2:54.76 | + +### What the captions claim, and where the claim is grounded + +- C.4's lower third — `install_app` → source written to the VFS → + compiled in the tab → window opens → taskbar lists it — is the golden + path of [[ADR 0010 - Agent app interface and tool protocol]] (the + `install_app` tool), [[ADR 0007 - Virtual filesystem with pluggable storage drivers]] + (`/apps/<id>/index.vue` written to the VFS), and + [[ADR 0006 - Agent-authored runtime-compiled components]] (in-browser + SFC compile), in that order. +- C.5's "A real Vue component, compiled in your tab. Not an iframe to + somebody's server." is the ADR 0006 claim stated for a viewer. +- C.10's "The chatbox test: a chat window shows code. DOMinic runs it." is + the criterion-2 argument from [[Judging Rubric & Win Strategy]] and the + line [[Demo Path]] asked to be said out loud once. +- C.11 and C.12 (minimize to the taskbar, restore from it) are the + on-camera "installed" proof [[Demo Path]] requires: the taskbar is how a + judge sees an app exists. +- The interviews' references to GitHub-issue coordination, issues #58, + #59, #60, and PR review by agents point at the repo tour in + [[Judges Walkthrough]]. +- Beats [[Demo Path]] planned that are *not* called out in this shot list + (Persist via a tab reload, Update via `update_app`, Recover, Any model): + whether they occur anywhere inside the uncut capture is not verified + from the shot list; the sub-beats above are the only moments the + composition keys on. + +## Assets + +### Used + +| File | Role | Provenance and notes | +| --- | --- | --- | +| `assets/footage/charles-demo-2026-09-12-1617.mp4` | Footage — scene A (flash-forward, clip 109.5–111.9) and scene C (full clip) | Constant-frame-rate H.264 copy of Drive `DOMinic/Charles/Screencast from 2026-09-12 16-17-22.webm` (2256x1504 3:2, VP8, variable frame rate, no audio, 168.36 s), same resolution and length; made only for reliable frame extraction. No cut, crop, or speed change. Framed 16:9 from the bottom (`object-fit: cover`, `object-position: 50% 100%`). | +| Drive `DOMinic/DJI_20260912_155301_60_null_video.mp4` | Footage — scene E, Interview 1 (Charles, with mic; an off-camera interviewer opens the clip) | Stored 1080x1920 with a 90-degree rotation flag = displays 1920x1080 16:9; 30 fps; stereo audio; 118.13 s. Shown in full at its own audio. Local path inside the composition: not verified. | +| Drive `DOMinic/DJI_20260912_155508_62_null_video.mp4` | Footage — scene F, Interview 2 (Zack, team lead) | Stored 1080x1920 with a 90-degree rotation flag = displays 1920x1080 16:9; 120 fps; stereo audio; 91.0 s. Shown in full at its own audio. Local path inside the composition: not verified. | +| `assets/brand/dominic-banner-d.png` | Brand art — scene G close background | Drive `VeniceAI_ajsBfDh.png`, 1584x672. Settles from 1.12x over the grid background. | +| `assets/brand/dominic-banner-windows.png` | Brand art — scene B stakes-card background at 28% opacity | Drive `VeniceAI_bja5c3V.png`, 1584x672. | +| `assets/audio/bed-synth-pulse-long.wav` | Synthesized audio — music bed | 120 BPM synth pulse synthesized with ffmpeg `aevalsrc`; no licensed music. Levels: 0.55 at the open, 0.30 under the demo (swells at 33.2 and 115.2, to 0.6 at the payoff), 0.06 under the interviews, 0.7 at the close. | +| impact, riser, whoosh, tick, chime | Synthesized audio — SFX | All synthesized with ffmpeg; cues per scene are in the shot list. Individual file paths: not verified. | +| Interview clips' own stereo tracks | Audio — dialogue | Each at full level (1.0) under its own scene. | + +### Deliberately not used + +| Shared file or artifact | Why it is not in the cut | +| --- | --- | +| Drive `2026-09-12-15-46 ‹name› 1 early playthrough.mov` | Terminal screen recording. The producer asked for interview footage and our own captures only. (The word elided from this filename and the next is a team member's surname.) | +| Drive `2026-09-12-15-47 ‹name› 2 process.mov` | Same — terminal screen recording. | +| Drive `Charles/Screencast from 2026-09-12 15-33-35.webm` | Playwright test-run recording; superseded by Charles's 16:17 working demo. | +| Repo `docs/assets` JPG screenshots | Removed after v1; the final cut uses no stills of the UI. | +| Our own Playwright captures of the scripted demo / mock-showcase build | Replaced by Charles's real-agent capture. | +| Branch `demo/mock-showcase` (commit `0ea677b`, worktree `project-files/DOMinic-demo`) | Exists, but nothing from it appears in the final video. | +| 8 more VeniceAI logo variants on the Drive | Only `VeniceAI_ajsBfDh.png` and `VeniceAI_bja5c3V.png` are used. | + +## How to re-render + +From the repository root: + +```sh +cd project-files/dominic-video && npm run check && npx --yes hyperframes@0.8.36 render --output out/dominic-demo-v4.mp4 +``` + +`npm run check` runs first and must pass; the render then writes +`out/dominic-demo-v4.mp4` from `index.html` at the composition's declared +1920x1080 @ 30 fps and 392 s. Pin the HyperFrames version as shown +(`0.8.36`) — that is the version the composition was authored against. + +## Related + +- [[Demo Video — Scenes]] — the prose companion: what the viewer sees and + hears in each scene, and the retention principle it serves +- [[Demo Path]] — the beat plan this cut descends from +- [[Judging Rubric & Win Strategy]] — why the chatbox test is the spine +- [[Judges Walkthrough]] — the repo tour the interviews point at +- [[ADR 0006 - Agent-authored runtime-compiled components]] · + [[ADR 0007 - Virtual filesystem with pluggable storage drivers]] · + [[ADR 0010 - Agent app interface and tool protocol]] — the three + decisions the C.4 lower third names, in order diff --git a/vault/Hackathon/Judges Walkthrough.md b/vault/Hackathon/Judges Walkthrough.md new file mode 100644 index 0000000..8ff8a81 --- /dev/null +++ b/vault/Hackathon/Judges Walkthrough.md @@ -0,0 +1,148 @@ +--- +type: walkthrough +title: DOMinic for Judges — a Five-Minute Tour +description: Maps the two-minute demo to the files that make it happen, shows the loop running, and says plainly what is real on main and what is deferred. +generated: { by: claude-code/claude-fable-5.1, at: 2026-09-12T19:45:00Z } +sources: + - id: demo-path + resource: agents/demo-path.md + title: Demo path — the beats the video shows + - id: adrs + resource: adrs/README.md + title: Architecture decision records 0000–0010 + - id: next + resource: ../NEXT.md + title: Post-hackathon roadmap (what is deferred) +tags: [judges, walkthrough, dominic] +source: docs/JUDGES.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# DOMinic for judges — a five-minute tour + +DOMinic is a desktop operating system in a browser tab where the AI agent +is the primary app author. You describe an app; the agent writes a Vue +component, the browser compiles it, it installs to the taskbar, opens, +and is still there after a reload. This page maps what you saw in the +video to the code on `main`, and says what is not there yet. + +## The core loop in sixty seconds, by file + +1. **`app/app.vue`** boots the shell: `Taskbar.vue`, one `WindowFrame` + per open window, the Agent Chat window open by default, and the + launcher hydrated from the registry (`listApps()`). +2. **`app/features/chat/components/ChatWindow.vue`** takes the prompt; + **`composables/useAgentChat.ts`** streams it to the server and + handles tool results client-side. +3. **`server/api/chat.post.ts`** runs the Vercel AI SDK with the system + prompt (`chat/prompts/systemPrompt.ts`) and two typed tools + (`chat/tools/schemas.ts`): `install_app` and `update_app`. The + provider key arrives per request from the browser and is never + stored (ADR 0005). +4. When the model calls `install_app`, the client writes + `apps/<id>/index.vue` through **`app/features/shared/vfs.ts`** + (localStorage-backed) and registers the app in + **`app/features/apps/registry/registry.ts`** (`registry.json`). +5. **`app/features/os/stores/os.ts`** opens a window; + **`app/features/apps/runner/DynamicAppRunner.vue`** compiles the + source in the browser with `vue3-sfc-loader` + (**`runner/loader.ts`**: host modules `vue`, `@vueuse/core`, + `lucide-vue-next`, `canvas-confetti` are pre-resolved; anything else + resolves through `esm.sh`). An `onErrorCaptured` boundary renders an + error card with **Ask Agent to Fix**, which posts the trace back into + the chat. +6. Reload the tab: `app.vue` re-reads `registry.json`, the launcher and + taskbar list the app again, and opening it recompiles the stored + source. Ask for a change and `update_app` rewrites the file in place. + +## ADR-to-code map + +| ADR | Decision | Realized in | +| --- | --- | --- | +| 0001 | Feature slices | `app/features/{os,chat,apps,settings,shared}` | +| 0002 | Strict TS + lint toolchain | `tsconfig.json`; markdownlint in `.husky/pre-commit` and CI (type gates deferred for the sprint, see NEXT.md) | +| 0003 | Pinia per-domain stores | `os/stores/os.ts`, `chat/stores/chat.ts`, `apps/stores/apps.ts`, `settings/stores/settings.ts` | +| 0004 | OS shell and taskbar | `os/components/Taskbar.vue`, `os/components/WindowFrame.vue`, `settings/components/SettingsApp.vue` | +| 0005 | Agent chat via Vercel AI SDK, per-request keys | `server/api/chat.post.ts`, `chat/composables/useAgentChat.ts`, `settings/stores/settings.ts` | +| 0006 | Runtime-compiled agent components | `apps/runner/DynamicAppRunner.vue`, `apps/runner/loader.ts` | +| 0007 | Virtual filesystem | `shared/vfs.ts` (localStorage driver), `apps/registry/registry.ts` | +| 0008 | Runtime npm loading via esm.sh | `apps/runner/loader.ts` (pre-resolved host modules; `esm.sh/<pkg>?bundle` fallback) | +| 0009 | CORS-first networking | Direct `fetch` from apps only; the proxy route is deferred (NEXT.md) | +| 0010 | Agent app interface and tool protocol | `chat/tools/schemas.ts`, `chat/prompts/systemPrompt.ts`, `apps/fixtures/pomodoro-timer/manifest.json` | + +Each ADR carries a **Hackathon POC (Golden Path)** section stating what +was built today versus deferred. `docs/adrs/README.md` is the index. + +## Run it + +```bash +npm install +npm run dev # http://localhost:3000 +``` + +Open **Settings** from the launcher and paste a provider key (OpenAI is +the default; Anthropic and Google presets exist). The key stays in your +browser and is sent per request. Then paste the frozen demo prompt from +`docs/agents/demo-path.md` into Agent Chat: + +> Build a beautiful retro synthwave Pomodoro focus timer with 25-minute +> work and 5-minute break cycles, start/pause/reset controls, and +> confetti celebration on finish. + +Without a key you can still exercise the install path: in the browser +console, `window.__dominic.installFixture("pomodoro-timer")` installs +the shipped agent-authored fixture through the same VFS and registry +code `install_app` uses; `window.__dominic.reset()` wipes the OS. + +## What it looks like + +![Desktop with Agent Chat open](assets/01-desktop-agent-chat.jpg) + +![Settings — bring your own key](assets/02-settings-byo-key.jpg) + +![Pomodoro installed and running](assets/03-pomodoro-installed-running.jpg) + +![Reopened from the launcher after a reload][shot4] + +[shot4]: assets/04-after-reload-reopened-from-launcher.jpg + +The last two were produced with `installFixture` rather than a live +model call, because they were captured without a provider key. The +video shows the live path. + +## Real on `main` versus deferred + +**Real:** shell with draggable windows and taskbar; streaming chat with +typed `install_app` / `update_app` tools; in-browser SFC compilation; +error boundary with Ask Agent to Fix; localStorage VFS with registry +hydration across reloads; Settings with provider presets and Reset OS; +launcher Uninstall and View Source; a shipped pomodoro fixture. + +**Deferred** (all listed with rationale in [`NEXT.md`](../NEXT.md)): +sandboxed iframe isolation for agent code, the automated npm vetting +gate, the multi-driver VFS (IndexedDB/OPFS), the CORS proxy and its +SSRF hardening, mobile degradation of the shell, type-checked pre-commit +gates, and the auto-healing error loop. + +## Known limitations you will notice + +- **No proxy:** apps can only reach CORS-enabled APIs directly. +- **localStorage only:** roughly 5 MB; fine for dozens of apps, not + for assets. +- **No iframe isolation:** agent-authored code runs in the OS's page; + a crashing app is caught by the boundary, a hostile one is not. +- **Styles can leak:** an unscoped `<style>` in a generated app can + touch the shell; the system prompt asks for Tailwind or scoped styles. +- **Keys transit the server in memory** per request; they are never + logged or stored (ADR 0005), but this is not a hardened deployment. +- **Tailwind play CDN** in development prints a production warning. + +## How this was built + +Five engineers, five different coding agents, one afternoon, one +repository. Decisions are ADRs; the agents coordinated through GitHub +Issues with a small protocol (`docs/agents/coordination.md`, +`docs/agents/coordination-best-practices-2.md`). The judging rubric and +the demo beats we built to are in `docs/agents/rubric.md` and +`docs/agents/demo-path.md`. diff --git a/vault/Hackathon/Judging Rubric & Win Strategy.md b/vault/Hackathon/Judging Rubric & Win Strategy.md new file mode 100644 index 0000000..f943b8d --- /dev/null +++ b/vault/Hackathon/Judging Rubric & Win Strategy.md @@ -0,0 +1,228 @@ +--- +type: playbook +title: How We Win — Judging Rubric and Strategy +description: The hackathon judging rubric decoded into design rules, a cadence, and hard submission gates for DOMinic. +tags: [hackathon, rubric, strategy, judging] +generated: { by: claude-code/claude-fable-5.1, at: 2026-09-12T17:30:00Z } +verified: { by: human:michael, at: 2026-09-12T17:35:00Z } +stale_after: 2026-09-12T20:00:00Z +sources: + - id: event-page + resource: https://columbus.aitinkerers.org/p/agents-everywhere-bots-channels-more-global-hackathon + title: "Agents, Everywhere: Bots, Channels, & More — Global Hackathon (event page)" + - id: judging-criteria + resource: https://columbus.aitinkerers.org/hackathons/h_Lv03K-ob6sU + title: Judging criteria and submission portal (participant login required) +source: docs/agents/rubric.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# How We Win — Judging Rubric and Strategy + +One goal: win the "Agents, Everywhere: Bots, Channels, and More" global +hackathon. Every decision — architecture, scope, framework, UX, what we +cut — serves the score and nothing else. + +- **Submission deadline: 4:00 PM ET (hard).** The portal window is + 15:30–16:00 ET; it closes at 16:00. +- **Judging is global.** Every project from every city goes into one + review. We compete with the world, not the room — polish and clarity + travel; in-room charisma does not. +- **Four criteria, each scored 1–5. Maximum 20.** We win by engineering a + genuine 5 on the criteria that are hardest to fake. + +## The prime directive — the chatbox test + +> If the core value could be delivered inside a standalone chat window, +> we have already lost. + +The rubric defines a top score as value that could not be reproduced in a +standalone chatbox, and a low score as an environment that is merely a +wrapper. Before building anything, answer in one sentence: + +> What does living inside this environment let the agent do that a chat +> window fundamentally cannot? + +If the answer is not crisp, fix the idea — not the code. + +Our environment is the **browser tab as a runtime**, not a display. +DOMinic is a desktop OS shell in the browser where the agent is the +primary app author: it writes Vue components that compile in the browser, +install into a window manager and taskbar, load vetted npm dependencies, +and persist in a virtual filesystem. A chat window can *show* code; this +environment *runs* it and keeps it. That is our chatbox-test answer, and +every demo beat should prove it. + +## The four criteria, verbatim, and how we hit a 5 + +### 1. Core Requirements & Functionality + +*Does the project deliver a working agent inside a place where people +already work, talk, or live? Does the core workflow function end to end?* + +| Score | Meaning | +| --- | --- | +| 1 | The project does not run or does not demonstrate a functional agent. | +| 2 | Parts of the project run, but the core workflow or environment integration is incomplete. | +| 3 | A basic end-to-end agent works in the intended environment, with limitations or bugs. | +| 4 | The project works reliably and demonstrates a complete agent experience with only minor issues. | +| 5 | The project is robust, reliable, and fully functional within its intended environment. | + +**Win it:** reliable beats impressive. This is a quarter of the score and +cannot be talked past. The one loop that must be bulletproof: ask → the +agent authors an app → it compiles → it installs to the taskbar → it runs +→ it survives a reload. Freeze features early and polish that loop, happy +path and failure path both. + +### 2. Innovation & Theme Alignment + +*Does the project explore a compelling new place or interaction for +agents? Does the environment materially improve what the agent can do?* + +| Score | Meaning | +| --- | --- | +| 1 | The project is essentially a generic chatbot or automation; the selected environment is irrelevant. | +| 2 | The agent appears in an eligible environment, but the environment mostly serves as a wrapper. | +| 3 | The project clearly addresses the theme, and its environment adds meaningful value. | +| 4 | The environment shapes the core workflow and enables an original agent experience. | +| 5 | The project reveals a surprising new agent pattern whose central value could not be reproduced in a standalone chatbox. | + +**Win it:** this is the criterion most teams fail and the one we win on. +The star is software materializing, not messages scrolling. A chat +transcript with an OS skin is the rubric's definition of a 2. Show the app +appear, run, persist, and reopen from the taskbar after a reload. + +### 3. Technical Execution & Integration + +*Consider the code, architecture, reliability, tool use, data handling, +and depth of integration with the selected environment.* + +| Score | Meaning | +| --- | --- | +| 1 | Little or no technical execution is evident; the submission is primarily conceptual or mocked. | +| 2 | The implementation is basic, unstable, or relies on superficial integrations. | +| 3 | The project demonstrates solid technical execution and working integrations, with some rough edges. | +| 4 | The project is well engineered, reliable, and integrates its tools, data, and environment effectively. | +| 5 | The project demonstrates exceptional engineering, including robust orchestration, thoughtful failure handling, and a deeply integrated architecture. | + +**Win it:** the rubric names *failure handling* and *depth of integration* +explicitly. Depth is native here — runtime component compilation, the +virtual filesystem, vetted esm.sh loading, the provider proxy. Failure +handling has a ready-made demo beat: a compile error the agent reads and +repairs, or a rejected dependency it explains. Show it; do not hide it. + +### 4. Usefulness & Agentic Experience + +*Does the project create clear value for its intended users? Is the agent +intuitive, effective, and appropriate for the environment in which it +operates?* + +| Score | Meaning | +| --- | --- | +| 1 | The use case is unclear, and the agent provides little meaningful value or interaction. | +| 2 | The project addresses a recognizable use case, but the agent's contribution is limited or largely resembles basic prompt and response. | +| 3 | The project is useful, the interaction is understandable, and the agent performs meaningful actions with reasonable user control. | +| 4 | The project solves a clear problem, and the agent feels native to its environment while creating a strong interaction between people and AI. | +| 5 | The project unlocks substantial value through an agent experience designed specifically for its environment, using context intelligently while remaining clear and controllable. | + +**Win it:** the rubric names *native to its environment*, *uses context +intelligently*, and *clear and controllable*. Pick one real user and one +job the agent builds an app for. Control: preview before install, +uninstall, open the source in the filesystem. Context: the agent knows +what is installed and what is open. Agency with control outscores full +autonomy. + +## The winning formula + +1. **Browser as runtime.** The agent's output becomes installed software, + which a chatbox cannot do. *(Criterion 2 ceiling.)* +2. **One bulletproof end-to-end loop.** Ask, author, compile, install, + run, reload. *(Criterion 1.)* +3. **Deep native integration with visible failure handling.** *(Criterion + 3.)* +4. **One clear user, a native and controllable experience.** *(Criterion + 4.)* +5. **Prove all four in a 120-second demo** and clear every submission + gate. + +A genuine 4–5 on criterion 2 plus reliability on criterion 1 beats a +technically fancier project that reads as a wrapper. + +## Score risks specific to DOMinic + +- **Chat as the star.** The agent surface is chat-driven; if the demo is + a transcript, judges score the OS as a skin. Keep the camera on apps + appearing, running, and persisting. +- **"A place people already work."** Criterion 1 says exactly that. A new + web OS can read as a standalone app. Frame it as the browser tab the + user already lives in, and make the reload-and-reopen beat prove it is + an environment, not a one-off demo. + +## Secondary objective — prize stacking + +The 20-point rubric decides the podium. Sponsor tools decide the side and +hardware prizes and quietly help every criterion. Integrate what fits +*naturally*; never bolt one on if it destabilizes the core loop. + +| Sponsor tool | Fit for DOMinic | Prize / benefit | +| --- | --- | --- | +| **OpenRouter** | Natural — one provider behind the Vercel AI SDK proxy; backs the "virtually any LLM" claim and gives model fallback for criterion 3 | Sponsor | +| **OpenAI** | Natural — another provider in the same proxy; the marquee sponsor | Credits in all three tiers | +| **Exa** | Natural — the agent researches APIs and docs while authoring an app | Exa credits in all three tiers | +| **CopilotKit** (AG-UI) | Weak — React-first; a poor match for a Vue/Nuxt shell. Skip unless trivially cheap | Dedicated Best CopilotKit Use prize | +| **Auth0**, **Trigger.dev**, **Ambiguous AI** | No natural fit today; do not force them | Best Ambiguous AI Use is a DGX Spark, but not for us | + +## Submission requirements — hard gates + +No points without every one of these: + +- [ ] **Title** — "a clear name for the project" +- [ ] **Written description** — "what you built, who it is for, and why + the context matters" +- [ ] **Public GitHub repository** — "working code that can be reviewed + and learned from" +- [ ] **Two-minute video** — "a concise demo of the project in action", + recorded against the working build +- [ ] **Social post** — "a public post about the project that tags the + event sponsors" + +The organizers' own steer: "Build something that can be shown. A sharp, +working demo beats a broad concept." + +## Time cadence, working back from 4:00 PM ET + +| Milestone | Target | +| --- | --- | +| Environment locked — browser as runtime | done | +| Spec and architecture agreed (team lead) | done — ADRs 0000–0009 | +| **Feature freeze** — whatever works, works | **~3:10 PM** | +| **Two-minute video** recorded and rendered against the build | **~3:30 PM** | +| Video uploaded if a link is required; all five gates submitted | **~3:50 PM** | +| Buffer | 3:50–4:00 | + +Never let the video slip into the final ten minutes; in a global review +it is often the deciding artifact. The portal closes at 4:00 — submit by +3:50. + +## Prohibited today — these score 1 or 2 + +- A standalone chatbot or chat UI, however polished. +- The OS shell used as a skin around a chat window. The demo shows apps + materializing, not messages scrolling. +- Mocked or faked core functionality in the demo. Judges score what runs. +- Scope that risks the core loop not working reliably by the 3:10 freeze. +- New frameworks or tools adopted after the freeze. +- Any feature that cannot answer the chatbox test. + +## The one filter, restated + +Before building anything, ask: + +1. **Chatbox test** — could this value exist in a chat window? If yes, + do not build it. +2. **Rubric test** — which of the four criteria does this raise toward a + 5? +3. **Clock test** — does this survive the 3:10 feature freeze reliably? + +Build only what passes all three. diff --git a/vault/Hackathon/Open Questions Register (QUESTIONS).md b/vault/Hackathon/Open Questions Register (QUESTIONS).md new file mode 100644 index 0000000..878111f --- /dev/null +++ b/vault/Hackathon/Open Questions Register (QUESTIONS).md @@ -0,0 +1,134 @@ +--- +type: Open Questions Register +title: DOMinic Open Questions and Decisions +description: Register of the hackathon's open questions and POC recommendations, with their post-hackathon outcomes. +generated: { by: agent/pi, at: 2026-09-12T00:00:00Z } +verified: { by: human:charles, at: 2026-09-12T00:00:00Z } +tags: [questions, team, planning, dominic] +source: QUESTIONS.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# DOMinic: Open Questions Register + +This document records the open technical and UX questions the team faced +on hackathon day, each with its options and the path recommended for the +proof of concept. The hackathon scoping ended with +[[ADR 0012 - End of the hackathon scope|ADR 0012]]; the table below +gives each recommendation's outcome, and the questions are kept as +written. New questions go to issues, not here. + +## Outcomes after the hackathon + +| Question | POC recommendation | Outcome | +| --- | --- | --- | +| Q1.1 chat presence | Floating window | Implemented; stays | +| Q1.2 window clamping | Clamp title bars to the viewport | Implemented in `WindowFrame.vue`; stays | +| Q2.1 default model | Claude 3.5 Sonnet or GPT-4o | Superseded: defaults are `claude-sonnet-5` / `gpt-5` / `gemini-2.5-pro`, chosen in Settings; a custom OpenAI-compatible provider was added (PR #70) | +| Q2.2 app context injection | Metadata list; full source on update | Implemented (PR #66): the referenced app's source is injected on update | +| Q3.1 script syntax | Plain JavaScript `<script setup>` | Implemented; stays | +| Q3.2 error boundary UX | `onErrorCaptured` + "Ask Agent to Fix" | Implemented (PR #64); bounded auto-heal is roadmap step 2.2 | +| Q4.1 window persistence | Installed apps only | Reopened: roadmap step 2.5 persists window state once the VFS is IndexedDB-backed | +| Q4.2 cache reset | "Reset OS" button | Implemented in Settings; stays | +| Q5.1 demo apps | Pomodoro, dashboard, kanban | No longer applicable; the Pomodoro fixture remains as a test fixture | + +--- + +## 1. OS Shell & Window Manager (Workstream 1) + +### Q1.1: Chat Presence — Floating Window vs. Docked Drawer? + +- **Option A (Floating Window)**: The Agent Chat is a draggable window, + identical to other apps, but with a high default z-index. +- **Option B (Docked Drawer)**: The Agent Chat is a persistent slide-out + panel anchored to the right edge of the desktop. +- **Recommendation for POC**: **Option A (Floating Window)**. It + demonstrates the window manager immediately upon boot, can be moved + aside easily when viewing generated apps, and requires no special-case + layout math. + +### Q1.2: Window Clamping & Bounds Checking + +- **Question**: How strictly should windows be clamped to viewport bounds? +- **Recommendation for POC**: Clamp window title bars so they cannot be + dragged completely above the top edge or below the taskbar. Prevent + negative top/left offsets. Full multi-edge dynamic boundary clipping + can be omitted for speed. + +--- + +## 2. Agent Chat & Tool Calling (Workstream 2) + +### Q2.1: Recommended Default Model + +- **Question**: Which frontier model provides the highest fidelity Vue 3 + SFC code generation with valid tool calling? +- **Recommendation for POC**: **Anthropic Claude 3.5 Sonnet** or + **OpenAI GPT-4o**. Both reliably output compliant SFC `<template>` and + `<script setup>` structures and respect JSON tool parameter schemas. + +### Q2.2: Existing App Context Injection + +- **Question**: How should existing apps in the OS be exposed to the LLM? +- **Recommendation for POC**: Include a JSON list of installed app + metadata (`{ id, title, description }`) in the system prompt. When the + user requests an update to an existing app (e.g. "Update the pomodoro + timer"), retrieve the source code of that specific app from the VFS and + inject it into the conversation context. + +--- + +## 3. Runtime SFC Loader & Execution Engine (Workstream 3) + +### Q3.1: Script Setup Syntax — TypeScript vs. Plain JavaScript? + +- **Question**: Should the agent output `<script setup lang="ts">` or + plain `<script setup>`? +- **Recommendation for POC**: **Plain JavaScript (`<script setup>`)**. + `vue3-sfc-loader` compiles plain JavaScript faster in-browser with zero + risk of runtime transpile failures from missing TypeScript type + declarations. + +### Q3.2: Error Boundary UX + +- **Question**: What happens when an agent generates invalid code or a + component crashes during render? +- **Recommendation for POC**: Wrap the component runner in Vue's + `onErrorCaptured`. Render an in-window alert showing the error message + with an "Ask Agent to Fix" button that automatically posts: + *"The app encountered an error: [error details]. Please fix it."* + +--- + +## 4. Storage & Persistence (Workstream 4) + +### Q4.1: Window State Persistence Across Page Reloads + +- **Question**: Should window coordinates and open/closed state persist + across browser refresh, or only installed app code? +- **Recommendation for POC**: **Persist installed apps only**. On + refresh, reload the desktop with a clean slate (Chat window open, + installed apps accessible in launcher/taskbar). Restoring arbitrary + window coordinates across viewport resizes can cause layout glitches + during a demo. + +### Q4.2: Development Cache Reset + +- **Question**: How can the team quickly wipe bad state during testing? +- **Recommendation for POC**: Provide a prominent "Reset OS" button in + the Settings app that clears `localStorage` and reboots the tab. + +--- + +## 5. Integration, Polish & Curated Demo Apps (Workstream 5) + +### Q5.1: Which Demo Apps Guarantee Maximum Judging Impact? + +- **Recommendation for POC**: + 1. **Interactive Pomodoro / Focus Timer**: Rich animated countdown, + retro synthwave styling, sound toggle, confetti on completion. + 2. **Crypto or Weather Live Dashboard**: Real-time direct `fetch` to + a public API (e.g. CoinGecko or Open-Meteo) showing live data cards. + 3. **Scratchpad / Kanban Board**: Local storage state persistence, + drag-and-drop or card creation, demonstrating interactive state. diff --git a/vault/Hackathon/Post-Hackathon Roadmap (NEXT).md b/vault/Hackathon/Post-Hackathon Roadmap (NEXT).md new file mode 100644 index 0000000..d912359 --- /dev/null +++ b/vault/Hackathon/Post-Hackathon Roadmap (NEXT).md @@ -0,0 +1,240 @@ +--- +type: Roadmap +title: DOMinic Post-Hackathon Roadmap +description: The sequenced roadmap from hackathon demo to a self-hostable product that safely runs untrusted agent-authored code, with named lanes for each collaborator. +generated: { by: claude-code/claude-fable-5.1, at: 2026-10-02T00:00:00Z } +verified: { by: human:brandon, at: 2026-10-02T00:00:00Z } +sources: + - id: adrs + resource: docs/adrs/README.md + title: Architecture decision records 0000-0011 + - id: questions + resource: QUESTIONS.md + title: Open questions register + - id: judges + resource: docs/JUDGES.md + title: Judge-facing walkthrough (what is real vs deferred) + - id: playbook + resource: docs/agents/coordination-best-practices-3.md + title: Coordination best practices v3 +tags: [roadmap, future, dominic] +source: NEXT.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# DOMinic: Post-Hackathon Roadmap (NEXT) + +The hackathon proof of concept shipped the whole core loop: describe an +app, it is written, compiled in the browser, installed to the taskbar, +kept across reloads, updated in place, and repaired when it crashes. +This document sequences everything deliberately deferred from that POC +into phases with dependencies, exit criteria, and owners. + +It replaces the unordered inventory from hackathon day. Every item from +that list is still here (see the index at the end); eight new items were +added where the shipped code showed a gap the list did not name. + +## North star + +**From a trusted-user demo to a self-hostable product that can safely +run untrusted agent-authored code.** + +DOMinic is AGPL-licensed and meant to be hosted. What is unsafe today is +LLM output running inside the host page with full DOM and `localStorage` +access, behind a server that falls back to its own API keys. Everything +else on this list is a feature; the order below exists to close that gap +first without breaking the loop that already works. + +## Sequencing principles + +1. **Tests before rewrites.** The repository has no automated tests. Two + of the largest items (async VFS, sandboxed runner) rewrite the seams + everything else hangs on. The regression net comes first. +2. **Change a seam before more callers depend on it.** The VFS and the + runner each have about four callers today. Every feature built on the + current shape is rework after the seam changes. +3. **The trust boundary ships as one unit.** CSP, the sandbox, and + dependency vetting only mean something together. Vetting without + isolation is theatre; isolation without CSP still lets a package + exfiltrate through `fetch`. + +## Phase 0 - Stabilize the floor + +All six steps are independent; fan them out. Roughly one week of +part-time work across the team. + +| Step | Work | Why here | Owner | +| --- | --- | --- | --- | +| 0.1 | Playwright end-to-end test of the core loop using the existing `window.__dominic` hooks: `installFixture` -> reload -> `updateApp` -> `injectBrokenApp` -> Ask Agent to Fix. Gate it in CI. | The net under Phases 1-2. The hooks are a harness waiting for a runner. | Michael | +| 0.2 | Remove the server env-var API-key fallback in `server/api/chat.post.ts`; return 401 without a per-request key. | ADR 0005 conformance. Five lines; the only server-side hole. | Charles | +| 0.3 | Baseline Content Security Policy: `script-src 'self' esm.sh cdn.tailwindcss.com`; `connect-src` = self + provider hosts + esm.sh. | Config-only. The Tailwind Play CDN needs `'unsafe-inline'`; accepted for now (decision D1). | Zack | +| 0.4 | ESLint flat config with a slice-boundary rule, plus lint-staged running `vue-tsc` on staged files in pre-commit. | ADR 0002 as decided. Turns "boundaries by convention" into a lint. | Zack | +| 0.5 | Truth pass: correct the README's mobile and "vetted CDN" claims; write ADR 0012 superseding ADR 0001 and 0003 to record the five-slice layout and mixed store style (decision D2). | Judges and contributors read the ADRs; drift costs trust. | Michael (README), Brandon (ADR 0012) | +| 0.6 | Dead code: delete the unused `useChatStore`; make `useAppsStore` the registry's reactive face or delete the mirror. | Two sources of truth for "installed apps" is a latent bug. | Justin | + +**Exit:** CI runs lint, typecheck, build, and e2e, all green; no key is +ever read from the environment; the ADRs match the code. + +## Phase 1 - Harden the two seams + +Sequential within the phase; one owner so two people are never inside +the same seam. Roughly two to three weeks. + +| Step | Work | Why here | Owner | +| --- | --- | --- | --- | +| 1.1 | Async `VirtualFileSystem` interface plus a driver registry, as ADR 0007 originally decided: `/system/*` on localStorage, `/apps/*` on IndexedDB, with a one-time migration of existing localStorage data. | Breaking interface change; cheapest while the callers are `app.vue`, `registry.ts`, `settings.ts`, and the runner. Unblocks snapshots, window persistence, bigger apps. | Charles | +| 1.2 | Sandboxed runner: compile in the host, mount in a `sandbox="allow-scripts"` iframe via `srcdoc`, with a `postMessage` bridge contract for props, `askFix`, and later IPC. Record it as ADR 0013. | The single biggest safety gap. Doing it before IPC and notifications means those are built on the bridge once, not twice. | Charles | +| 1.3 | Dependency vetting gate in front of esm.sh: npm registry and advisory data, age, downloads, known CVEs; verdicts cached in the VFS; refusals surfaced with reasons. Completes ADR 0008 as ADR 0014. | Only meaningful once 1.2 exists. | Charles | +| 1.4 | Tighten CSP to the sandbox model: the host page drops esm.sh; only the iframe origin may load it. Revisit the Play CDN here. | Closes the loop opened in 0.3. | Zack | + +**Exit:** an app that reaches for `parent.document` or the host's +`localStorage` fails; a known-bad package is refused with its reasons +shown; the Phase 0 e2e suite is still green. + +## Phase 2 - The agent product loop + +Builds on the hardened base. Roughly two to three weeks. + +| Step | Work | Why here | Owner | +| --- | --- | --- | --- | +| 2.1 | Diff approval before `update_app` mounts: a unified diff in the chat thread with approve/reject; the source-context injection shipped in the sprint is the input. | Safe to add UI now that the runner contract is stable. | Justin | +| 2.2 | Bounded auto-heal: on a caught error, re-invoke the agent with the trace up to two times, with a visible "agent fixed it" trail. Builds on the Recover beat. | Turns the demo's best moment into default behaviour. | Justin | +| 2.3 | Tool results back to the model: today each turn is one-shot; let the model see whether install or update succeeded (`maxSteps` is already 3). | Required for 2.2 to be reliable. | Charles | +| 2.4 | Notification center and app-to-app IPC on the Phase 1 `postMessage` bridge: one contract, two consumers. | Built once, on the sandbox bridge. | Justin (consumers), Charles (bridge) | +| 2.5 | Window-state persistence across reloads (QUESTIONS Q4.1), now that the VFS is IndexedDB-backed. | Was deferred for layout-glitch risk under demo conditions; no longer applies. | Zack | + +**Exit:** every update is reviewable before it mounts; a crash self-heals +within two tries; a timer app can notify a todo app. + +## Phase 3 - Reach + +A parallel UX track plus far-horizon items. Step 3.1 has no coupling to +the seams and may start during Phase 1. + +| Step | Work | Owner | +| --- | --- | --- | +| 3.1 | Mobile responsive shell: bottom-sheet taskbar and stacked windows below 768px, as ADR 0004 designed. | Zack | +| 3.2 | Window snapping and tiling; multi-desktop workspaces. | Zack | +| 3.3 | `/api/proxy` shipped together with its SSRF blocklist (RFC 1918, cloud metadata endpoints) - never one without the other - plus a `useSmartFetch` direct-then-proxy fallback. Only when an app needs a non-CORS API. Record as ADR 0015. | Charles | +| 3.4 | Snapshot export and import of the whole OS state; File System Access API mount of a local directory. | Justin | +| 3.5 | Multi-tab sync via `BroadcastChannel` (ADR 0003's deferred item). | Justin | +| 3.6 | Agent-generated unit tests verified in-browser before install. After 2.3, which it depends on. | Charles | +| 3.7 | P2P app sharing via WebRTC or QR codes. Far horizon; kept, not scheduled. | - | + +## Cross-cutting + +- **Every step is one issue.** Open it with `## Files` and + `## Done when` before writing code; claim it with a branch name. +- **Every changed decision is a new ADR**, never an edit to an old one: + 0012 (slices and stores), 0013 (sandbox bridge), 0014 (vetting gate), + 0015 (proxy). +- **The e2e suite from 0.1 is the definition of "still works."** A PR + that turns it red does not merge, whatever else it does. + +## Roles and working agreement + +These roles apply whenever two or more people are active on the +repository at once. Solo work follows the same issue -> PR -> merge loop +without the review rotation. Lanes follow the merged-PR record from the +sprint, so each person owns what they already know. + +### Lanes + +| Person | Lane | Owns (paths) | Roadmap steps | +| --- | --- | --- | --- | +| Zack (`xcjs`) | Platform and shell | `app/features/os/**`, `nuxt.config.ts`, `tsconfig.json`, `.github/**`, `package.json`, `.husky/**` | 0.3, 0.4, 1.4, 2.5, 3.1, 3.2 | +| Charles (`Sullux`) | Runtime, persistence, and the chat route | `app/features/apps/runner/**`, `app/features/apps/registry/**`, `app/features/shared/vfs.ts`, `server/api/**` | 0.2, 1.1, 1.2, 1.3, 2.3, 3.3, 3.6 | +| Justin (`ImNewToC0de`) | Integration and the agent product loop | `app/app.vue`, `app/features/chat/**`, `app/features/apps/stores/**`, `app/features/settings/**` | 0.6, 2.1, 2.2, 2.4, 3.4, 3.5 | +| Michael (`m-vawter`) | Quality, docs, and process | `tests/**`, `README.md`, `docs/agents/**`, `docs/JUDGES.md`, board hygiene | 0.1, 0.5 (README), the e2e gate, release notes | +| Brandon (`r0073d-l053r`) | Coordination, security review, and this roadmap | `.claude/skills/coordinate/**`, `NEXT.md`, `docs/adrs/**` (new ADRs), the knowledge-base vault | 0.5 (ADR 0012), second reviewer on 1.2-1.4, retrospectives | + +A path not listed belongs to whoever opens the issue that first touches +it; add it to this table in the same PR. + +### Not stepping on each other + +- **One owner per path.** A PR that touches another lane's files needs + that lane's owner as a reviewer. If two issues would touch the same + file at the same time, the second claimant records a split or a + common owner on the issue before starting. +- **One seam, one person.** Phase 1 steps are sequential and all in + Charles's lane. Nobody else edits `vfs.ts` or the runner while 1.1-1.3 + are open; they work Phase 0 or 3.1 in the meantime. +- **One issue in progress per person**, at most one blocked; queued work + stays `status:claimed`. Silence longer than six hours on an active + claim earns a ping; release only after three more hours unanswered. +- **Agents identify themselves.** Every protocol comment carries the + `agent:` and `human:` fields; a person running two sessions suffixes + the agent id so the record shows who did what. + +### Issues, PRs, and merging - the loop everyone runs + +1. **Issue first.** `coord.sh new` with the step number in the title, + `## Files` naming the exact globs, and `## Done when` copied from the + phase exit criteria it serves. Claim with the branch name. +2. **Single-purpose PRs**, ideally under 300 changed lines, one step per + PR, `Closes #N` in the body. The PR diff must stay inside the union of + its linked issues' `## Files`; if it grew, update the issue first. +3. **Review within two days** (part-time cadence). Default reviewer is + the owner of every lane the diff touches; the author adds a second + reviewer for Phase 1 and any ADR. Approve only after checking out the + branch or reading the e2e result, not the description. +4. **Merge is distributed**, as the v3 playbook decided: any non-author + approver may squash-merge once CI (lint, typecheck, build, e2e) is + green. Authors never self-merge; branch protection enforces one + non-author approval. Zack is the tiebreaker on disagreements. +5. **Done means merged.** `coord.sh done` only with the merged PR URL and + every `## Done when` box checked. Then one `SYNC` line on the hub: + what landed, what it unblocks. +6. **Weekly rhythm.** Each person posts one `SYNC` per week at minimum, + even if it says "nothing this week"; the board is read before any + new claim. Michael pings stale claims; Brandon runs a short retro at + each phase exit and amends this document. + +### Reviewer rotation + +| Author | Default reviewer | Second reviewer (Phase 1, ADRs) | +| --- | --- | --- | +| Zack | Charles | Brandon | +| Charles | Zack | Brandon | +| Justin | Charles | Zack | +| Michael | Brandon | Zack | +| Brandon | Michael | Zack | + +## Decisions taken + +- **D1 - Tailwind Play CDN:** keep it through Phase 0 (the CSP carries + `'unsafe-inline'`); replace with a safelist or iframe-local JIT at 1.4. +- **D2 - ADRs 0001 and 0003:** amend the records to match the code (five + slices, mixed store styles) in ADR 0012; do not refactor the code. +- **D3 - North star:** self-hostable product, not a personal tool. +- **D4 - Cadence:** part-time; Phases 0 -> 1 -> 2 run in order, with + 3.1 as a parallel track from the start. + +## Index of the original inventory + +| Original item | Now | +| --- | --- | +| Sandboxed iframe execution | 1.2 | +| Automated npm dependency vetting gate | 1.3 | +| Content Security Policy | 0.3, tightened at 1.4 | +| Server proxy SSRF hardening | 3.3 (ships with the proxy) | +| Mobile-first responsive shell | 3.1 | +| Window snapping and tiling | 3.2 | +| Multi-desktop workspaces | 3.2 | +| System notification center | 2.4 | +| Pluggable multi-driver VFS | 1.1 | +| File System Access API integration | 3.4 | +| Snapshot export and import | 3.4 | +| Interactive code diff approval | 2.1 | +| Error auto-healing loop | 2.2 | +| App-to-app IPC | 2.4 | +| P2P app sharing | 3.7 | +| Type-checked pre-commit | 0.4 | +| Agent-generated component tests | 3.6 | + +New since the inventory: 0.1 e2e tests, 0.2 env-key fallback removal, +0.5 truth pass and ADR 0012, 0.6 dead stores, 2.3 tool results back to +the model, 2.5 window-state persistence, 3.5 multi-tab sync, and the +`useSmartFetch` half of 3.3. diff --git a/vault/Hackathon/Submission Form Draft.md b/vault/Hackathon/Submission Form Draft.md new file mode 100644 index 0000000..a0909c0 --- /dev/null +++ b/vault/Hackathon/Submission Form Draft.md @@ -0,0 +1,143 @@ +--- +type: submission +title: Hackathon Submission Form Draft +description: Ready-to-paste answers for the AI Tinkerers Columbus "Agents, Everywhere" submission form. +generated: { by: opencode/glm-5.3-flash, at: 2026-09-12T19:20:00Z } +verified: { by: human:zack, at: 2026-09-12T19:20:00Z } +stale_after: 2026-09-13T00:00:00Z +tags: [hackathon, submission, form] +source: docs/submission-form.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# Hackathon submission form draft + +Answers for the "DOMinic — Submit Your Project" form. Copy the text +under each heading into the matching field. Items marked with a +placeholder still need a human (video, post URL). Content is sourced +from the README, the ADRs, [the demo path](demo-path.md), and the git +history. + +## Project name + +DOMinic + +## Project description + +DOMinic is a web-based desktop operating system that runs in a single +browser tab, where an AI agent is the primary app author. You open +Agent Chat, describe an app in plain English — "build a synthwave +Pomodoro timer" — and the agent writes the Vue component, installs it +as a first-class desktop app with a taskbar and launcher entry, and +opens it in its own window. Your apps persist across reloads via a +virtual filesystem; ask for a change and the agent updates the app in +place; if an app throws, you click "Ask Agent to Fix" and the agent +repairs it. + +The problem it solves: chatbots can show you code, but nowhere for it +to live and run. The desktop-OS context makes DOMinic meaningfully +more useful than a standalone chatbot — the shell gives the agent's +output a place to materialize as windows, persist between sessions, +and be updated in place, while the user keeps control (update in +place, uninstall, or view the source). + +Technical execution: Nuxt 4 + Vue 3 with strict TypeScript, Tailwind +CSS, and Pinia per-domain stores. Agent chat runs on the Vercel AI +SDK with streaming chat and Zod-typed tool calling, proxied server +side (OpenAI, Anthropic, Google, DeepSeek; bring-your-own-key that +never rests on the server). Agent-authored apps are Vue single-file +components compiled at runtime in the browser with vue3-sfc-loader, +with npm dependencies loaded at runtime via esm.sh, and their sources +persist to a localStorage-backed virtual filesystem. Window drag, +focus, minimize, and maximize are built on @vueuse/core. Architecture +is documented in eleven ADRs in the repo. + +## Products & tools used + +Check these: + +- [x] AI Tinkerers +- [x] OpenAI (chat provider via Vercel AI SDK) + +Other products (paste into the "Other Products" field): + +Nuxt 4, Vue 3, TypeScript, Tailwind CSS, Pinia, Vercel AI SDK (with +@ai-sdk/openai, @ai-sdk/anthropic, @ai-sdk/google), vue3-sfc-loader, +esm.sh, @vueuse/core, lucide-vue-next, zod, markdownlint-cli2, Husky. + +No CopilotKit / OpenRouter / Exa / Trigger.dev / Auth0 / Mozilla.ai / +Ambiguous AI / Rev1 / TeamClaws / GDG usage in the repo — leave them +unchecked. + +## Project video + +Placeholder — needs the human to upload: + +TODO_YOUTUBE_OR_LOOM_URL + +(Keep it to 2 minutes; beats are defined in +[the demo path](demo-path.md).) + +## Team contributions + +Sourced from the merged pull requests on `main` (author of record): + +- **Zackary Lowery (@xcjs, lead)** — architecture decision records + 0000–0009 and the repo conventions (OKF frontmatter, markdownlint, + CI); Nuxt 4 scaffold (#21); OS shell — window manager, taskbar, + drag/focus/minimize/maximize (#24); chat runtime deps and current + model IDs (#29); launcher Uninstall and View Source (#44); typecheck + and build CI (#67); custom OpenAI-compatible provider (#70); review + and merge gate all afternoon. +- **Charles Sullivan (@Sullux)** — hackathon golden paths across the + ADRs plus ADR 0010, `NEXT.md`, `QUESTIONS.md` (#2); `/api/chat` + streaming, bring-your-own-key, `install_app` / `update_app` tools + (#20); runtime engine — in-browser vue3-sfc-loader runner, esm.sh + resolver, error boundary (#25); persistence — VFS over localStorage, + registry hydration, Settings app, Reset OS (#27); frozen demo prompt + and fallback pomodoro fixture (#40); Recover beat verified end to end + (#64); the 15:08 rehearsal that proved the core loop. +- **Brandon (@r0073d-l053r)** — the `/coordinate` protocol, labels, + hub, and script that the five agents worked through (#7); current + model presets (#39); Tailwind content scan and Anthropic-safe chat + history (#42); "As built" reconciliation of ADRs 0001–0010 (#55); + hub fix (#57); seeded the integration and rehearsal issues; demo + video production. +- **Justin (@ImNewToC0de)** — wired the core app loop, chat → + `install_app` → VFS → windows (#35); window-control click fix (#45); + strict typecheck separated from the production bundle (#48); Settings + polish (#61); dynamic app style cleanup (#62); current app source in + the `update_app` context (#66); reviews. +- **Michael Vawter (@m-vawter)** — docs and process lane: judging + rubric and win strategy (#1), demo path (#3, #4), coordination + playbooks v1–v3 with the Q01 research behind them (#18, #26, #68, + #71), README quickstart (#28), finish plan (#37), judge-facing + walkthrough with screenshots (#53); shell and chat smoke fixes + (#63); protocol facilitation and PR reviews. + +## Additional links + +Repository: <https://github.com/xcjs/DOMinic> + +## Prior work + +All work was authored during the hackathon; there is no prior code. +The team did bring a pre-hackathon plan: judging rubric, demo-path +beats, and eleven ADRs were written as a planning pass on day one and +are committed to the repo's history. + +## Social media posts + +Placeholder — needs the human to post, then paste URLs here: + +TODO_POST_URL + +Suggested post (tags per the form): + +We built DOMinic at @AITinkerers Columbus "Agents, Everywhere": a +browser-tab OS where the agent is the app author — describe an app +and it's written, installed, and kept. @OpenAI @CopilotKit @openrouter +@exaailabs @auth0 @ambiguousio @triggerdotdev @mozillaAI #AgentsEverywhere + +(For LinkedIn use company names per the form instructions.) diff --git a/vault/LICENSE (AGPL-3.0).md b/vault/LICENSE (AGPL-3.0).md new file mode 100644 index 0000000..67d5e0c --- /dev/null +++ b/vault/LICENSE (AGPL-3.0).md @@ -0,0 +1,15 @@ +--- +type: reference +title: LICENSE (AGPL-3.0) +tags: [licence, legal] +source: LICENSE +ingested: 2026-10-02 +repo_head: 0fc772d +--- +# LICENSE (AGPL-3.0-or-later) + +DOMinic is licensed **GNU Affero General Public License v3.0 or later** (`chore: license DOMinic under AGPL-3.0-or-later`, PR-era `0fc772d`). `package.json` declares `"license": "AGPL-3.0-or-later"`. + +What that means in one line: anyone may use, modify, and redistribute the code, but a modified version offered to users **over a network** must also offer them its complete source — the AGPL's network-use clause, which the plain GPL lacks. Fitting for a browser OS meant to be hosted. + +The full text is `LICENSE` in the repo root; canonical: <https://www.gnu.org/licenses/agpl-3.0.txt>. Back to [[DOMinic Home]] · [[Repo Map]]. diff --git a/vault/Merge Blockers & Roadmap Status.md b/vault/Merge Blockers & Roadmap Status.md new file mode 100644 index 0000000..86418b2 --- /dev/null +++ b/vault/Merge Blockers & Roadmap Status.md @@ -0,0 +1,67 @@ +--- +type: analysis +title: Merge Blockers & Roadmap Status +description: What slowed merging on hackathon day, what is still blocking the finish plan, and what was done about it. Hand-written; not regenerated by the watch loop. +tags: [dominic, hackathon, blockers, roadmap] +generated: { by: claude-code/claude-fable-5.1, at: 2026-09-12T19:36:00Z } +repo_head: "619aa82" +refreshed: 2026-09-12T19:36:00Z +stale_after: 2026-09-12T20:30:00Z +--- + +# Merge Blockers & Roadmap Status + +Snapshot at **15:36 ET, 2026-09-12** (24 minutes to portal close). `origin/main` = `619aa82`, CI green. Back to [[DOMinic Home]]; live board in [[Coordination Board]]; the roadmap itself is Michael's finish plan (`docs/superpowers/plans/2026-09-12-hackathon-finish.md`, merged as #37). + +## The one-line answer + +**Nothing is left to merge for the demo.** Every code PR is on `main` (#21, #20, #25, #27, #24, #29, #35, #42, #39, #45, #44, #40) plus the finish plan (#37). What is slowing the roadmap now is not a merge at all: it is **footage** (#9) and the **portal submission** (#10), both owned by @m-vawter and silent on the board since 14:19–14:20. + +## Roadmap (finish plan) task by task + +| Task | Scope | Status at 15:36 | +| --- | --- | --- | +| 1 | Shell #24 + deps #29 merged | ✅ 14:55 | +| 2 | Core loop wired (#30 → PR #35) | ✅ 15:02 (with both review fixes) | +| 3 | Rehearse + freeze prompt (#32 → PR #40) | ✅ checkpoint 15:08 passed all five beats; prompt committed 15:28 | +| 4 | Stretch: #33 fixture, #34 launcher actions, close PR #31 | ✅ #40, #44 merged; #31 closed; #33 closed 15:33 | +| 5 | Record → wrap → render → publish (#9) | 🔴 **no footage reported**; wrapper ready since 14:19 | +| 6 | Submit five gates (#10) | 🔴 drafts ready since 14:20; blanks + video link pending | +| 7 | After 16:00: protocol restore, #19 → v3, #6 script defaults, ADR 0011+ | ⏳ post-hackathon | +| + | **#46** judge-facing walkthrough (`docs/JUDGES.md`, gate 3) — handed to @m-vawter 15:34 | ⏳ docs only | +| + | **#47** ADRs 0001–0010 "As built" reconciliation vs `main` — claimed 15:34 by the vault owner's other Claude session, PR promised ~15:55 | ⏳ docs only | + +> [!note] Two Claude Code sessions are posting as `claude-code/claude-fable-5.1 | human: @r0073d-l053r` +> The session titled *DOMinic* runs the vault watch loop and opened #46/#47. This note and the #36 review come from the *project-files* loop session, which posts hub lines as `claude-code/claude-fable-5.1/loop` from 15:40 on so the two are distinguishable. + +## What slowed merging today (root causes) + +1. **Required review + five authors.** `main` requires one approval and authors cannot self-approve, so every PR waited on a teammate who was busy with their own PR. The hub carries seven "please approve" SYNCs between 15:11 and 15:28. This is the structural drag; it cleared by attrition, not by process. +2. **Duplicate work built in parallel.** #31 duplicated #27; `codex/os-shell` duplicated #13; #40 carried hunks identical to #39 (settings presets) and #45 (window controls). Each duplicate cost a rebase round and a re-review. Justin's #19 answer names the cause: claims arrived after code had already been started. +3. **Negotiating #40 instead of recording.** From 15:11 to 15:28 the agents rebased, re-reviewed and re-pushed #40 five times. It was optional for the take (the frozen prompt was already in #32's checkpoint comment; the fixture is only a fallback), but it absorbed the window in which footage should have been shot. +4. **No heartbeat from ws:docs.** #9, #10 and #19 are 67–73 minutes silent. The protocol says STATUS every 30 minutes; the board cannot tell whether recording is in progress or nobody is doing it. `needs-human` was placed on #9 at 15:31 to force a one-line answer. +5. **Stale labels on closed issues.** #32, #41, #38, #34, #30, #22 and others closed with `status:in-review` / `in-progress` still attached. Cosmetic today; it makes `board` noisier for anyone parsing labels after the event. + +## The only open PR: #36 (Zack) + +Windows PowerShell port of `coord.sh` plus a Node 24 LTS standard (ADR 0011, `.nvmrc` 24.21.0, `engines >=24.21.0 <25`). `REVIEW_REQUIRED`, mergeable, 7 behind `main` with no conflicts. It touches `.claude/skills/coordinate/**`, which the vault owner holds via #6, so **the blocker is the vault owner's approval**. Tested here on PowerShell 5.1: `board`, `stale`, `show` match the bash script exactly; all 20 verbs dispatched. Review comment posted 15:36 with two notes: the Node change is a separate decision bundled in, and the engines floor is above this machine's 24.15.0. Recommendation: approve and merge after 16:00, per the freeze. + +## Actions taken this session (15:29–15:36) + +- Hub SYNC: state of `main`, nothing left to merge, footage + submission are the whole remaining roadmap, raw-capture fallback if the render cannot finish by 15:52. +- `question 9 --human` to @m-vawter: footage status, with the fallback spelled out. +- `done 33 --pr #40`: fixture verified on `main` (`index.vue.txt`, `manifest.json` with `author: agent`, only `vue` imported, `window.__dominic` hooks in `app.vue`). +- Review comment on #36 with the Windows test results; approval left to the owner. + +## Close-out (16:06 ET) + +- **Merged after the freeze:** #48 (build config, Justin, 15:48), #53 (JUDGES.md, Justin, 15:51), #55 (ADR as-built), #57 (hub fix), and **#36 (Windows port + Node 24 / ADR 0011, merged 16:04 by the vault owner's loop session)**. `main` = `c251e4f`. +- **Submission outcome not recorded.** #9 and #10 were still open with no DONE at 16:06; a `needs-human` question is on both. Portal closed 16:00. +- **Still open:** #56 and #61 (approved, need Zack's merge click), #62 (needs review), #49/#51/#58/#59 assigned post-launch work, #19 playbook feedback. +- **Both Claude sessions stopped.** The vault watch loop is off; this vault is current at `162aa09` plus this note. Regeneration and job assignment are now manual until someone restarts them. + +## What to watch next + +- 15:45 — a public video link on #9 (or a raw capture uploaded). +- 15:55 — DONE on #8, #9, #10 from ws:docs. +- After 16:00 — merge #36, close #19 into v3, clean `status:*` labels off closed issues, start Task 7. diff --git a/vault/Open Questions & Gaps.md b/vault/Open Questions & Gaps.md new file mode 100644 index 0000000..5427291 --- /dev/null +++ b/vault/Open Questions & Gaps.md @@ -0,0 +1,53 @@ +--- +type: playbook +title: Open Questions & Gaps +description: Post-sprint retrospective — what shipped, what stayed deferred, and the honest caveats a judge or a future contributor will notice in the code. +tags: [dominic, gaps, retrospective, hackathon] +generated: { by: claude-code/claude-opus-4.8, at: 2026-09-12T23:40:00Z } +repo_head: "be8645f" +ingested: 2026-09-12 +refreshed: 2026-09-12T23:40:00Z +--- + +# Open Questions & Gaps + +This started as a live pre-freeze risk list; at `be8645f` the sprint is over and it reads as a retrospective. The team's own forward register is [[Open Questions Register (QUESTIONS)]]; the deferred roadmap is [[Post-Hackathon Roadmap (NEXT)]]. Back to [[DOMinic Home]]. + +## Resolved during the sprint + +> [!success] Everything that was "at risk" at the 3:10 freeze shipped +> - **Core loop** — wired end to end (#35); Charles's 15:08 rehearsal passed Hook/Ask/Materialize/Persist/Close. +> - **Stream protocol** — the `ai@7` vs v4-parser mismatch was fixed by pinning `ai@4` and reverting the route (#29); the client parses `0:`/`9:` data-stream prefixes again. +> - **Model IDs** — retired `gpt-4o`/`claude-3-5-sonnet-20241022`/`gemini-1.5-pro` replaced by `gpt-5`/`claude-sonnet-5`/`gemini-2.5-pro` in both the route (#29) and Settings presets (#39). +> - **Tailwind** — content globs now scan `app|features/**` and a Play CDN covers agent-invented classes (#42); `.txt` fixtures included (#40 follow-up). +> - **Chat history** — Anthropic-safe (no leading assistant turn, no empty content) (#42). +> - **Window controls / launcher overlay** — min/max/close fixed (#45), launcher moved into the taskbar (#56). +> - **Recover beat** — verified end to end with a fix-injection path and test hooks (#64). +> - **update_app** — now gets the target app's current source injected (#66), so edits are grounded in real code. +> - **Style leaks** — runtime `<style data-app-id>` tags cleaned on update/uninstall (#62). +> - **Custom provider** — OpenAI-compatible base-URL + model support shipped (#70). +> - **ADRs vs code** — every ADR got a verified "As built" reconciliation (#55); ADR 0011 pinned Node 24 (#71-era). +> - **The one human gate left at the buzzer** was recording and uploading the video; the [[Submission Form Draft]] still has `TODO_YOUTUBE_OR_LOOM_URL` and `TODO_POST_URL`. + +> [!success] 2026-10-02 — scope lifted, roadmap sequenced +> **ADR 0012** (PR #78) ends the hackathon scoping and accepts the slice/store baseline; `QUESTIONS.md` now carries an outcomes table (Q2.1 superseded, Q4.1 reopened as roadmap 2.5, Q5.1 n/a). **`NEXT.md`** (PR #77) sequences every item below into phases with owners. Caveats 1–8 are now scheduled roadmap steps (0.2, 1.2, 1.3, 3.3, 1.1, 3.1, 0.4/0.5); caveat 9's ADR half is closed by ADR 0012, its README half is step 0.5. + +## Honest caveats a judge or contributor will notice + +These are true of the shipped code and worth stating plainly rather than hiding: + +1. **Server-side key fallback.** `chat.post.ts` falls back to `process.env.<PROVIDER>_API_KEY` when no per-request key is sent — this contradicts ADR 0005's "keys never at rest on the server." Safe only because the demo host sets none; a public deploy should remove the fallback or gate it. ([[ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy|ADR 0005]]) +2. **No sandboxing.** Agent-authored SFCs compile and run in the host page with full DOM access (no iframe, no CSP). Fine for a trusted single-user demo; a real product needs the iframe + postMessage isolation in [[Post-Hackathon Roadmap (NEXT)]]. ([[ADR 0006 - Agent-authored runtime-compiled components|ADR 0006]]) +3. **No dependency vetting.** Unknown imports load straight from `esm.sh/<pkg>?bundle`; ADR 0008's vetting gate is deferred. The system prompt asks the model to stay within four host modules, which is guidance, not enforcement. ([[ADR 0008 - Runtime NPM dependency loading via esm.sh with vetting|ADR 0008]]) +4. **No CORS proxy.** ADR 0009's `/api/proxy` was never built; agent apps can only reach CORS-friendly endpoints. The demo app makes no external calls, so this never bit. ([[ADR 0009 - CORS-first networking with Chrome-masking proxy fallback|ADR 0009]]) +5. **localStorage-only VFS.** One driver, ~5 MB quota; ADR 0007's multi-driver (IndexedDB/OPFS) design is deferred. Dozens of demo apps fit; a heavy app would not. +6. **Desktop-only.** The README still promises mobile degradation; the shell has none. Cosmetic honesty fix, or build the bottom-sheet layout later. ([[ADR 0004 - DOMinic OS shell and taskbar|ADR 0004]]) +7. **`typeCheck: false` in `nuxt.config`.** Build-time type-checking is off (it hit an environment-specific `TS5023`/`TS5042` on Windows during the sprint); `nuxt typecheck` runs separately and is gated in CI (#67), so types are still enforced — just not inside `nuxt build`. ([[ADR 0002 - Strict TypeScript and lint toolchain|ADR 0002]]) +8. **No ESLint.** ADR 0002 specifies a type-checked ESLint flat config; only markdownlint is installed. Slice-boundary rules are convention, not lint. +9. **Doc drift is bounded, not zero.** The ADRs' Decision-Outcome prose still names the pre-sprint design in places; the **As built** subsection under each is the source of truth, and [[Judges Walkthrough]] maps decisions to files. The README's "vetted ESM CDN" and mobile lines are the two spots still ahead of the code. + +## Cross-references + +- What was deliberately punted, in the team's words: [[Post-Hackathon Roadmap (NEXT)]]. +- The team's own open-question register with POC recommendations: [[Open Questions Register (QUESTIONS)]]. +- How the code diverges from each decision, per ADR: the **As built (2026-09-12)** subsection in every ADR note. diff --git a/vault/README (repo).md b/vault/README (repo).md new file mode 100644 index 0000000..0520d11 --- /dev/null +++ b/vault/README (repo).md @@ -0,0 +1,138 @@ +--- +type: README +title: DOMinic +description: A web-based desktop operating system where an AI agent authors the apps. +tags: [os, agent, vue, nuxt] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +source: README.md +ingested: 2026-10-02 +repo_head: 0fc772d +--- + +# DOMinic + +![DOMinic desktop with the Agent Chat window open](docs/images/desktop.png) + +A web-based desktop operating system running in a browser tab, where +an AI agent is the primary app author: the user talks, the agent +answers, and the apps it writes install, run, and persist like +first-party software. + +## What DOMinic is + +- A desktop-metaphor OS shell — window manager, taskbar, and app + registry — that degrades cleanly to a bottom-nav sheet and stacked + windows on phone viewports. +- A chat-driven agent surface that streams from virtually any LLM + provider; users bring their own API keys, which stay in their + browser and travel per-request, never at rest on a server. +- A platform for agent-authored apps: Vue single-file components + compiled at runtime in the browser, persisting their source in a + virtual filesystem and loading npm dependencies from a vetted ESM + CDN. + +The hackathon proof of concept shipped the whole core loop; the project +now follows the sequenced roadmap in [[Post-Hackathon Roadmap (NEXT)|NEXT.md]]. See +[Running DOMinic](#running-dominic) to start it, and the decision +records below for why it is built this way. + +## Documentation + +| Path | Contents | +| --- | --- | +| [[Judges Walkthrough|docs/JUDGES.md]] | **Judges start here:** five-minute tour of the demo, the code behind it, and what is deferred | +| [[ADR Conventions|docs/adrs/]] | Architecture decision records (MADR 4.0) | +| [[Post-Hackathon Roadmap (NEXT)|NEXT.md]] | Sequenced roadmap, collaborator lanes, and the issue -> PR -> merge loop | +| [[Open Questions Register (QUESTIONS)|QUESTIONS.md]] | Open questions register with post-hackathon outcomes | +| [[OKF Frontmatter Convention|docs/agents/use-okf.md]] | OKF v0.2 frontmatter convention for docs | +| [[Judging Rubric & Win Strategy|docs/agents/rubric.md]] | Hackathon judging rubric and win strategy | +| [[Demo Path|docs/agents/demo-path.md]] | The two-minute demo as rubric-mapped beats | +| [[Agent Coordination over GitHub Issues|docs/agents/coordination.md]] | Multi-agent task coordination over GitHub Issues (`/coordinate`) | +| [docs/agents/work-loop.md](docs/agents/work-loop.md) | The work loop: any agent takes the next roadmap step via `/coordinate` (`npm run loop`) | +| [vault/](vault/DOMinic%20Home.md) | The project's second brain (Obsidian): milestones, sessions, ingested docs | +| [[Coordination Best Practices v1|docs/agents/coordination-best-practices.md]] | Coordination playbook v1 (superseded; kept for the record) | +| [[Coordination Best Practices v2|docs/agents/coordination-best-practices-2.md]] | Coordination playbook v2 (superseded; kept for the record) | +| [[Coordination Best Practices v3|docs/agents/coordination-best-practices-3.md]] | Active coordination playbook — sprint evidence and enforceable invariants | +| [[Spec - ADR Series Design|docs/superpowers/specs/]] | Design specs from brainstorming sessions | +| [[Plan - ADR Series|docs/superpowers/plans/]] | Implementation plans derived from specs | +| [[Coordination Research (Q01)|docs/research/]] | The Q01 question and its three answers behind the coordination playbooks | + +### Decision highlights + +The accepted ADR series establishes the platform's foundations. Each +record carries a hackathon golden path (historical since ADR 0012) and +an as-built reconciliation: + +- [[ADR 0001 - Feature slices with domain-driven organization|0001]] + Feature slices with domain-driven organization +- [[ADR 0002 - Strict TypeScript and lint toolchain|0002]] + Strict TypeScript and lint toolchain +- [[ADR 0003 - Pinia per-domain stores|0003]] + Pinia per-domain stores +- [[ADR 0004 - DOMinic OS shell and taskbar|0004]] + DOMinic OS shell and taskbar +- [[ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy|0005]] + Agent chat via Vercel AI SDK with server-side provider proxy +- [[ADR 0006 - Agent-authored runtime-compiled components|0006]] + Agent-authored runtime-compiled components +- [[ADR 0007 - Virtual filesystem with pluggable storage drivers|0007]] + Virtual filesystem with pluggable storage drivers +- [[ADR 0008 - Runtime NPM dependency loading via esm.sh with vetting|0008]] + Runtime NPM dependency loading via esm.sh with vetting +- [[ADR 0009 - CORS-first networking with Chrome-masking proxy fallback|0009]] + CORS-first networking with Chrome-masking proxy fallback +- [[ADR 0010 - Agent app interface and tool protocol|0010]] + Agent app interface and tool protocol +- [[ADR 0011 - Node 24 LTS runtime standard|0011]] + Node 24 LTS runtime standard +- [[ADR 0012 - End of the hackathon scope|0012]] + End of the hackathon scope + +## Roles + +Development continues part-time with a lane per collaborator - the +paths they own, their roadmap steps, and a reviewer rotation - defined +in [[Post-Hackathon Roadmap (NEXT)#roles-and-working-agreement|NEXT.md]]. The hackathon's +five workstreams are recorded in the git history and in +[[Submission Form Draft|docs/submission-form.md]]. + +## Running DOMinic + +```bash +npm install +npm run dev # Nuxt dev server, default http://localhost:3000 +``` + +Open **Settings** from the taskbar and paste an API key for your model +provider. The key stays in your browser and travels per request; the +server never stores it (ADR 0005). Then open the Agent Chat and +describe an app: it is written, compiled in the browser, installed +to the taskbar, and kept across reloads. + +Other scripts: `npm run build`, `npm run typecheck`, `npm run lint:md`. + +## Development + +Markdown is linted with markdownlint-cli2 (80-column prose) and +carries OKF frontmatter. +Node 24 LTS is the standard runtime — see +[[ADR 0011 - Node 24 LTS runtime standard|ADR 0011]]; `nvm use` +picks it up from the checked-in `.nvmrc`. + +```bash +npm install +npm run lint:md +``` + +A pre-commit hook runs the lint automatically; write with the +conventions in [[OKF Frontmatter Convention|docs/agents/use-okf.md]] and +[[ADR Template|docs/adrs/template.md]]. + +## License + +DOMinic is licensed under the +[[LICENSE (AGPL-3.0)|GNU Affero General Public License v3.0 or later]] +(AGPL-3.0-or-later). Since DOMinic is a network-run application, +section 13 of the AGPL requires that users interacting with it over a +network are offered the corresponding source. diff --git a/vault/README.md b/vault/README.md new file mode 100644 index 0000000..865c301 --- /dev/null +++ b/vault/README.md @@ -0,0 +1,7 @@ +# Vault + +The project's second brain, kept in the repo so every agent on every machine shares it. Open this folder in Obsidian. + +Schema: OKF-style frontmatter on every note (`type`, `title`, `description`, `tags`, `generated`, plus `repo_head`/`ingested` on generated notes). Generated notes (ADRs, conventions, coordination, hackathon docs, specs and plans) are rebuilt by `loop vault-sync` and must not be hand-edited; synthesis notes (Home, Architecture Overview, Repo Map, Open Questions & Gaps) are hand-written; `Milestones/` and `Sessions/` are written by `loop milestone` and `loop checkpoint`. + +See `.claude/skills/work-loop/SKILL.md`. diff --git a/vault/Repo Config/.editorconfig b/vault/Repo Config/.editorconfig new file mode 100644 index 0000000..00aa9bd --- /dev/null +++ b/vault/Repo Config/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/vault/Repo Config/.markdownlint.jsonc b/vault/Repo Config/.markdownlint.jsonc new file mode 100644 index 0000000..a2327ac --- /dev/null +++ b/vault/Repo Config/.markdownlint.jsonc @@ -0,0 +1,12 @@ +{ + "default": true, + "MD013": { + "line_length": 80, + "headings": false, + "code_blocks": false, + "tables": false + }, + "MD025": { + "front_matter_title": "" + } +} diff --git a/vault/Repo Config/ci.yml b/vault/Repo Config/ci.yml new file mode 100644 index 0000000..b96e14b --- /dev/null +++ b/vault/Repo Config/ci.yml @@ -0,0 +1,62 @@ +name: CI + +on: + push: + branches: [main, master] + pull_request: + +jobs: + markdownlint: + name: Markdown lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run markdownlint + run: npm run lint:md + + typecheck: + name: Typecheck + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run typecheck + run: npm run typecheck + + build: + name: Build + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + NUXT_TELEMETRY_DISABLED: 1 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run build + run: npm run build diff --git a/vault/Repo Config/git-log.txt b/vault/Repo Config/git-log.txt new file mode 100644 index 0000000..15bb035 --- /dev/null +++ b/vault/Repo Config/git-log.txt @@ -0,0 +1,65 @@ +0fc772d|Brandon|2026-10-02 19:43:06 -0400|docs: end the hackathon scope (ADR 0012) and switch to the full protocol (#78) +d6300a4|Brandon|2026-10-02 19:42:50 -0400|docs: sequence the post-hackathon roadmap and assign lanes (#77) +be8645f|Zackary Lowery|2026-09-12 18:05:44 -0400|feat(coord): enforce v3 file-scope, completion, and hub invariants (#75) +11bec60|Zackary Lowery|2026-09-12 18:05:06 -0400|docs: add desktop screenshot to README (#76) +4d7989e|Zackary Lowery|2026-09-12 17:54:55 -0400|chore: license DOMinic under AGPL-3.0-or-later +0d66819|m-vawter|2026-09-12 17:42:47 -0400|docs(submission): correct team contributions to the merged-PR record (#73) +3433ee2|Zackary Lowery|2026-09-12 16:54:51 -0400|docs: hackathon submission form draft +a00fe7d|m-vawter|2026-09-12 16:52:11 -0400|docs: Q01 research provenance and finish-plan close-out +0dc1747|Zackary Lowery|2026-09-12 16:35:45 -0400|feat: custom OpenAI-compatible provider (base URL + model) +c777abc|m-vawter|2026-09-12 16:30:04 -0400|docs: integrate coordination v3 (#68) +90149a5|ImNewToC0de|2026-09-12 16:26:37 -0400|feat: add current app source to update context +7814a1c|Zackary Lowery|2026-09-12 16:17:45 -0400|ci: validate typecheck and build +2df1b01|Charles Sullivan|2026-09-12 16:08:28 -0400|feat(runtime-engine): verify Recover beat e2e with compile/runtime error handling and chat fix injection (#64) +3803cf5|ImNewToC0de|2026-09-12 16:07:01 -0400|fix: clean up dynamic app styles (#62) +5968dba|ImNewToC0de|2026-09-12 16:06:09 -0400|style: polish settings credential inputs (#61) +b558ef2|m-vawter|2026-09-12 16:05:35 -0400|fix: resolve shell and chat smoke failures (#63) +c251e4f|Zackary Lowery|2026-09-12 16:04:52 -0400|feat(coordinate): Windows PowerShell port of coord script + Node 24 LTS standard (#36) +162aa09|Brandon|2026-09-12 15:54:16 -0400|docs(adrs): append verified "As built" reconciliation to ADRs 0001-0010 (#55) +153ec94|Brandon|2026-09-12 15:53:24 -0400|fix(coordinate): resolve the hub to the lowest-numbered hub issue (#57) +d3135ff|m-vawter|2026-09-12 15:51:53 -0400|docs: add judge-facing walkthrough (#53) +bb2e1a8|ImNewToC0de|2026-09-12 15:48:52 -0400|fix(build): separate strict typecheck from production bundle (#48) +619aa82|m-vawter|2026-09-12 15:29:41 -0400|docs: record completed ADR and hackathon plans (#37) +e9efa73|Charles Sullivan|2026-09-12 15:28:39 -0400|feat(rehearsal): freeze demo prompt and provide fallback pomodoro fixture (#40) +3cb3be2|Zackary Lowery|2026-09-12 15:26:28 -0400|feat: add launcher uninstall and source actions (#44) +2660f99|ImNewToC0de|2026-09-12 15:15:40 -0400|fix: keep window controls out of drag handler (#45) +483fc1d|Brandon|2026-09-12 15:13:05 -0400|fix(settings): align model presets with route defaults (#39) +a80b2a1|Brandon|2026-09-12 15:08:28 -0400|fix(build,chat): scan app/features for Tailwind; Anthropic-safe chat history (#42) +fdeb0ff|ImNewToC0de|2026-09-12 15:02:15 -0400|feat: wire DOMinic core app loop (#35) +bcbef1e|m-vawter|2026-09-12 14:52:52 -0400|docs: README quickstart — Running DOMinic, drop docs-only language (#28) +07a52c6|Zackary Lowery|2026-09-12 14:52:18 -0400|fix: add chat runtime dependencies (#29) +8ea9114|Zackary Lowery|2026-09-12 14:49:16 -0400|feat: OS shell with window manager, taskbar, drag/focus/minimize/maximize (#13) (#24) +11f79bc|m-vawter|2026-09-12 14:42:34 -0400|docs: add coordination best practices v2 (supersedes v1) (#26) +71c6edc|Charles Sullivan|2026-09-12 14:41:46 -0400|feat(persistence): VFS over localStorage, registry hydration, Settings app, and Reset OS (#17) (#27) +42fb7bb|Charles Sullivan|2026-09-12 14:36:00 -0400|feat(runtime): in-browser vue3-sfc-loader runner, esm.sh resolver, and error boundary (#15) (#25) +cea7d52|m-vawter|2026-09-12 14:34:30 -0400|docs: add coordination best practices playbook (sprint minimum + full) (#18) +b86dfbd|Charles Sullivan|2026-09-12 14:32:10 -0400|feat(chat): implement /api/chat route, system prompt, tool schemas, and ChatWindow UI (#20) +bb97707|Zackary Lowery|2026-09-12 14:31:42 -0400|feat: Nuxt 4 scaffold with strict TS, Pinia per-domain stores, Tailwind (#12) (#21) +5cbaab4|Brandon|2026-09-12 14:11:43 -0400|feat: add /coordinate skill for multi-agent work over GitHub Issues (#7) +84e3a91|m-vawter|2026-09-12 13:59:40 -0400|docs: align demo path with the hackathon golden path and open questions (#4) +2521868|m-vawter|2026-09-12 13:52:40 -0400|docs: add demo path mapping the video beats to the rubric and ADRs (#3) +3ff7c14|Charles Sullivan|2026-09-12 13:45:27 -0400|docs: orient architecture decisions toward hackathon POC golden path (#2) +dab4cf0|m-vawter|2026-09-12 13:44:48 -0400|docs: add hackathon judging rubric and win strategy (#1) +c9eff74|Zackary Lowery|2026-09-12 13:10:41 -0400|docs: add repository README +686453e|Zackary Lowery|2026-09-12 13:07:33 -0400|docs: add ADR 0009 CORS-first networking with proxy fallback +1270b66|Zackary Lowery|2026-09-12 13:06:58 -0400|docs: add ADR 0008 esm.sh dependency loading with vetting +00e5dac|Zackary Lowery|2026-09-12 13:06:16 -0400|docs: add ADR 0007 virtual filesystem with pluggable storage drivers +bcd70ae|Zackary Lowery|2026-09-12 13:05:44 -0400|docs: add ADR 0006 runtime-compiled components +566f542|Zackary Lowery|2026-09-12 13:05:14 -0400|docs: add ADR 0005 agent chat via Vercel AI SDK +d310387|Zackary Lowery|2026-09-12 13:02:00 -0400|docs: add transparency and blur guidance to ADR 0004 shell +51bbbfa|Zackary Lowery|2026-09-12 13:01:02 -0400|docs: add ADR 0004 DOMinic OS shell and taskbar +c5bdb9e|Zackary Lowery|2026-09-12 12:59:36 -0400|docs: pin ADR 0002 toolchain to current stable versions +33b710e|Zackary Lowery|2026-09-12 12:58:39 -0400|docs: add ADR 0003 Pinia per-domain stores +0c36f9f|Zackary Lowery|2026-09-12 12:58:11 -0400|docs: add ADR 0002 strict TypeScript and lint toolchain +569bdde|Zackary Lowery|2026-09-12 12:57:50 -0400|docs: add ADR 0001 feature slices with domain-driven organization +3f3cc21|Zackary Lowery|2026-09-12 12:57:49 -0400|chore: exempt OKF frontmatter title from MD025 +aa7f862|Zackary Lowery|2026-09-12 12:55:08 -0400|docs: add ADR series implementation plan +91af066|Zackary Lowery|2026-09-12 12:33:22 -0400|docs: add ADR series design spec +7196408|Zackary Lowery|2026-09-12 12:30:43 -0400|docs: add ADR conventions README +76b112a|Zackary Lowery|2026-09-12 12:22:11 -0400|ci: add markdownlint pre-commit hook (husky) and GitHub Actions workflow +04415a6|Zackary Lowery|2026-09-12 12:21:55 -0400|docs: rewrap markdown to satisfy 80-column lint +80c79a4|Zackary Lowery|2026-09-12 12:08:49 -0400|chore: add package.json with markdownlint-cli2 and Node gitignore +8f4b5fa|Zackary Lowery|2026-09-12 12:05:55 -0400|docs: add MADR ADR template +ba6014d|Zackary Lowery|2026-09-12 12:05:09 -0400|chore: add .editorconfig +1711d8e|Zackary Lowery|2026-09-12 12:03:06 -0400|docs: add ADR 0000 (record architecture decisions) +b4ca986|Zackary Lowery|2026-09-12 12:01:19 -0400|docs: add ADR scaffold spec and implementation plan diff --git a/vault/Repo Config/gitattributes.txt b/vault/Repo Config/gitattributes.txt new file mode 100644 index 0000000..ed0c1c6 --- /dev/null +++ b/vault/Repo Config/gitattributes.txt @@ -0,0 +1,2 @@ +# Shell scripts must stay LF: bash cannot run CRLF files on Windows checkouts. +*.sh text eol=lf diff --git a/vault/Repo Config/gitignore.txt b/vault/Repo Config/gitignore.txt new file mode 100644 index 0000000..01586b1 --- /dev/null +++ b/vault/Repo Config/gitignore.txt @@ -0,0 +1,25 @@ +.superpowers/ + +# Node +node_modules/ +.nuxt/ +.output/ +.data/ +dist/ +.env +.env.* +!.env.example +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +.npm +.husky/_/ + +# Agents +AGENTS.md + +# Obsidian vault (vault/): workspace state churns per machine; app.json is tracked +vault/.obsidian/workspace* +vault/.obsidian/cache +vault/.obsidian/plugins diff --git a/vault/Repo Config/husky-pre-commit.txt b/vault/Repo Config/husky-pre-commit.txt new file mode 100644 index 0000000..ca01d65 --- /dev/null +++ b/vault/Repo Config/husky-pre-commit.txt @@ -0,0 +1 @@ +npm run lint:md diff --git a/vault/Repo Config/nuxt.config.ts b/vault/Repo Config/nuxt.config.ts new file mode 100644 index 0000000..915137d --- /dev/null +++ b/vault/Repo Config/nuxt.config.ts @@ -0,0 +1,35 @@ +export default defineNuxtConfig({ + compatibilityDate: "2026-09-01", + devtools: { enabled: false }, + modules: ["@pinia/nuxt", "@nuxtjs/tailwindcss", "@vueuse/nuxt"], + srcDir: "app/", + dir: { + pages: "pages", + }, + imports: { + dirs: ["features/shared"], + }, + // Every component lives under app/features/**, which the module's default + // content globs (components/, pages/, layouts/, app.vue, ...) never scan. + // Globs resolve from the project root; the srcDir-relative form is kept as a fallback. + tailwindcss: { + config: { + content: ["app/**/*.{vue,ts,txt}", "features/**/*.{vue,ts,txt}"], + }, + }, + typescript: { + strict: true, + typeCheck: false, + }, + app: { + head: { + title: "DOMinic", + meta: [ + { name: "viewport", content: "width=device-width, initial-scale=1" }, + ], + // Play CDN so utility classes that only exist in agent-generated SFCs + // (never seen at build time) still resolve at runtime. + script: [{ src: "https://cdn.tailwindcss.com" }], + }, + }, +}); diff --git a/vault/Repo Config/nvmrc.txt b/vault/Repo Config/nvmrc.txt new file mode 100644 index 0000000..7baf24b --- /dev/null +++ b/vault/Repo Config/nvmrc.txt @@ -0,0 +1 @@ +24.21.0 \ No newline at end of file diff --git a/vault/Repo Config/package.json b/vault/Repo Config/package.json new file mode 100644 index 0000000..3a6da28 --- /dev/null +++ b/vault/Repo Config/package.json @@ -0,0 +1,46 @@ +{ + "name": "dominic", + "version": "0.0.0", + "private": true, + "description": "DOMinic", + "license": "AGPL-3.0-or-later", + "type": "module", + "scripts": { + "dev": "nuxt dev", + "build": "nuxt build", + "generate": "nuxt generate", + "preview": "nuxt preview", + "typecheck": "nuxt typecheck", + "lint:md": "markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#.superpowers\" \"#docs/superpowers\" \"#vault\"", + "prepare": "husky", + "loop": "node .claude/skills/work-loop/scripts/loop.mjs" + }, + "engines": { + "node": ">=24.21.0 <25" + }, + "devDependencies": { + "@nuxtjs/tailwindcss": "^6.14.0", + "@pinia/nuxt": "^0.11.2", + "@types/canvas-confetti": "^1.9.0", + "@types/node": "^22.20.2", + "@vueuse/core": "^13.9.0", + "@vueuse/nuxt": "^13.9.0", + "husky": "^9.1.7", + "markdownlint-cli2": "^0.18.1", + "nuxt": "^4.1.3", + "pinia": "^3.0.3", + "typescript": "^5.9.3", + "vue": "^3.5.22", + "vue-tsc": "^3.1.3" + }, + "dependencies": { + "@ai-sdk/anthropic": "^1.2.12", + "@ai-sdk/google": "^1.2.22", + "@ai-sdk/openai": "^1.3.24", + "ai": "^4.3.19", + "canvas-confetti": "^1.9.4", + "lucide-vue-next": "^1.0.0", + "vue3-sfc-loader": "^0.9.5", + "zod": "^3.25.76" + } +} diff --git a/vault/Repo Config/tsconfig.json b/vault/Repo Config/tsconfig.json new file mode 100644 index 0000000..f4cc3ff --- /dev/null +++ b/vault/Repo Config/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./.nuxt/tsconfig.json", + "compilerOptions": { + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "forceConsistentCasingInFileNames": true, + "verbatimModuleSyntax": true + } +} \ No newline at end of file diff --git a/vault/Repo Map.md b/vault/Repo Map.md new file mode 100644 index 0000000..7de68cd --- /dev/null +++ b/vault/Repo Map.md @@ -0,0 +1,103 @@ +--- +type: reference +title: Repo Map +description: File-by-file inventory of github.com/xcjs/DOMinic at be8645f — the shipped tree, tech stack, contributors, branches, and PR shape. +tags: [dominic, repo, inventory] +repo: https://github.com/xcjs/DOMinic +repo_head: "be8645f" +ingested: 2026-09-12 +refreshed: 2026-09-12T23:40:00Z +--- + +# Repo Map + +Snapshot of `origin/main` at `be8645f` (post-sprint). Back to [[DOMinic Home]]. + +## At a glance + +| | | +| --- | --- | +| Application code | Nuxt 4 + Vue 3 app, live on `main` (whole demo loop works) | +| Commits on `main` | 76 (see [[Commit Timeline]]); merged PRs #1–#76 | +| Languages | TypeScript, Vue SFC, Markdown; server route in Nitro | +| Runtime | Node 24 LTS (`.nvmrc` `24.21.0`, `engines >=24.21 <25`) — ADR 0011 | +| Licence | AGPL-3.0-or-later (`LICENSE`) | +| CI | GitHub Actions: markdownlint + `nuxt typecheck` + `nuxt build` on PRs | +| Branch protection | `main` protected; squash-merge; one non-author approval required | + +## Directory tree (code) + +``` +DOMinic/ +├── .nvmrc .gitattributes .editorconfig .gitignore .markdownlint.jsonc LICENSE +├── .github/workflows/ci.yml markdownlint + typecheck + build +├── .husky/pre-commit npm run lint:md +├── .claude/skills/coordinate/ SKILL.md, references/protocol.md (v2), scripts/coord.sh + coord.ps1 +├── nuxt.config.ts tsconfig.json package.json package-lock.json +├── app/ +│ ├── app.vue composition root: hydrate, render windows, mount taskbar, __dominic hooks +│ └── features/ +│ ├── os/ stores/os.ts · components/{WindowFrame,Taskbar}.vue +│ ├── chat/ components/ChatWindow.vue · composables/useAgentChat.ts · prompts/systemPrompt.ts +│ │ · tools/schemas.ts · types/chat.ts · stores/chat.ts (unused) +│ ├── apps/ runner/{DynamicAppRunner.vue,loader.ts} · registry/registry.ts +│ │ · fixtures/{index.ts,pomodoro-timer/index.vue.txt,manifest.json} · stores/apps.ts +│ ├── settings/ components/SettingsApp.vue · stores/settings.ts +│ └── shared/ vfs.ts · types/vue3-sfc-loader.d.ts +└── server/api/chat.post.ts Vercel AI SDK streamText + install_app/update_app tools +``` + +## Directory tree (docs) + +``` +docs/ +├── JUDGES.md five-minute judge tour → [[Judges Walkthrough]] +├── submission-form.md portal answers → [[Submission Form Draft]] +├── images/desktop.png README hero (added #76) +├── assets/01–04*.jpg JUDGES.md screenshots +├── adrs/ 0000–0011 + README + template → vault ADRs/ +├── agents/ +│ ├── use-okf.md → [[OKF Frontmatter Convention]] + [[OKF v0.2 Specification]] +│ ├── rubric.md demo-path.md → [[Judging Rubric & Win Strategy]] · [[Demo Path]] +│ ├── coordination.md → [[Agent Coordination over GitHub Issues]] +│ └── coordination-best-practices{,-2,-3}.md → Coordination/ v1, v2, v3 +├── research/ Q01 question + 3 answers → [[Coordination Research (Q01)]] +└── superpowers/ + ├── specs/ adr-scaffold-design · adr-series-design · custom-openai-compatible-provider-design + └── plans/ adr-scaffold · adr-series · hackathon-finish · custom-openai-compatible-provider +``` + +## Tech stack (from `package.json`) + +| Concern | Choice | +| --- | --- | +| Framework | Nuxt 4 (Vue 3), server route `/api/chat` | +| State | Pinia (`os` setup store; others option style) | +| Styling | Tailwind via `@nuxtjs/tailwindcss` (build) + Play CDN (runtime classes) | +| Window drag | raw pointer events in `WindowFrame.vue` (not `@vueuse/core useDraggable`) | +| Icons / delight | `lucide-vue-next`, `canvas-confetti` | +| Agent | Vercel AI SDK `ai@^4` + `@ai-sdk/openai\|anthropic\|google @^1`; DeepSeek + custom via OpenAI adapter | +| Runtime compile | `vue3-sfc-loader`; `esm.sh/<pkg>?bundle` for unknown imports | +| Storage | `localStorage` behind `shared/vfs.ts` | +| Types / lint | `strict` tsconfig, `vue-tsc`; markdownlint-cli2; husky | +| Runtime | Node 24 LTS | + +## People (merged-PR record) + +| Name / handle | Role | Evidence | +| --- | --- | --- | +| Zackary Lowery (`xcjs`) | Lead / architect / merge gate | ADRs 0000–0009, scaffold #21, shell #24, deps #29, CI #67, custom provider #70, coord PS port #36 | +| Charles Sullivan (`Sullux`, agent `pi`) | Chat, runtime, persistence | golden paths + ADR 0010 #2, `/api/chat` #20, runner #25, VFS/registry/Settings #27, fixture #40, Recover #64 | +| Justin (`ImNewToC0de`, agent Codex) | Integration | core-loop wiring #35, window-control fix #45, typecheck split #48, taskbar #56, style cleanup #62, update-context #66 | +| Michael Vawter (`m-vawter`) | Docs / process | rubric #1, demo-path #3/#4, playbooks #18/#26/#68, README #28, finish plan #37, JUDGES #53, smoke fixes #63 | +| Brandon (`r0073d-l053r`, this vault) | Coordination / fixes / video | `/coordinate` #7, model presets #39, Tailwind+history #42, ADR as-built #55, hub fix #57, demo video | + +Agents in the frontmatter record: `agent/pi` (Charles), `Codex/gpt-5` (Justin), `opencode/glm` (Zack), `claude-code/claude-fable-5.1` (Brandon/Michael). See [[Coordination Protocol]]. + +## Branches at ingest + +`main` plus unmerged/feature branches left behind after squash-merge: `demo/mock-showcase` (the deterministic `?demo=1` fallback demo — see [[Demo Video — Scenes]]), `docs/wrap-up`, and a scatter of already-merged `codex/*`, `docs/*`, `feat/*`, `fix/*`, `ci/*`, `chore/*` branches GitHub keeps until pruned. The live history is [[Commit Timeline]]. + +## File → vault note + +ADRs 0000–0011 → `ADRs/`. `README.md` → [[README (repo)]]. `NEXT.md` → [[Post-Hackathon Roadmap (NEXT)]]. `QUESTIONS.md` → [[Open Questions Register (QUESTIONS)]]. `docs/JUDGES.md` → [[Judges Walkthrough]]. `docs/submission-form.md` → [[Submission Form Draft]]. `docs/agents/*` → [[Judging Rubric & Win Strategy]], [[Demo Path]], [[OKF Frontmatter Convention]], and the Coordination folder. `docs/research/` → [[Coordination Research (Q01)]]. `docs/superpowers/*` → `Specs & Plans/`. Config files → `Repo Config/` (summarized in [[Markdown Lint & CI]]). diff --git a/vault/Repo Watch Log.md b/vault/Repo Watch Log.md new file mode 100644 index 0000000..30580d2 --- /dev/null +++ b/vault/Repo Watch Log.md @@ -0,0 +1,375 @@ +--- +type: log +title: Repo Watch Log +description: Dated entries for every change observed on github.com/xcjs/DOMinic by the vault watch loop. +tags: [dominic, log, watch] +--- +# Repo Watch Log + +Newest first. Each entry is one watch tick that found changes; see [[Coordination Board]] for the live board and [[Commit Timeline]] for the full history. + +## 2026-09-12 23:45 ET — `origin/main` be8645f + +- **main** — origin/main 162aa09 → be8645f + - `be8645f` Zackary Lowery: feat(coord): enforce v3 file-scope, completion, and hub invariants (#75) + - `11bec60` Zackary Lowery: docs: add desktop screenshot to README (#76) + - `4d7989e` Zackary Lowery: chore: license DOMinic under AGPL-3.0-or-later + - `0d66819` m-vawter: docs(submission): correct team contributions to the merged-PR record (#73) + - `3433ee2` Zackary Lowery: docs: hackathon submission form draft + - `a00fe7d` m-vawter: docs: Q01 research provenance and finish-plan close-out + - `0dc1747` Zackary Lowery: feat: custom OpenAI-compatible provider (base URL + model) + - `c777abc` m-vawter: docs: integrate coordination v3 (#68) + - `90149a5` ImNewToC0de: feat: add current app source to update context + - `7814a1c` Zackary Lowery: ci: validate typecheck and build + - `2df1b01` Charles Sullivan: feat(runtime-engine): verify Recover beat e2e with compile/runtime error handling and chat fix injection (#64) + - `3803cf5` ImNewToC0de: fix: clean up dynamic app styles (#62) + - `5968dba` ImNewToC0de: style: polish settings credential inputs (#61) + - `b558ef2` m-vawter: fix: resolve shell and chat smoke failures (#63) + - `c251e4f` Zackary Lowery: feat(coordinate): Windows PowerShell port of coord script + Node 24 LTS standard (#36) +- **branch** — new branch `origin/chore/agpl-license` +- **branch** — new branch `origin/ci/build-typecheck` +- **branch** — new branch `origin/codex/update-app-source-context` +- **branch** — new branch `origin/demo/mock-showcase` +- **branch** — new branch `origin/docs/coordination-v3-integration` +- **branch** — new branch `origin/docs/submission-form` +- **branch** — new branch `origin/docs/submission-form-attribution` +- **branch** — new branch `origin/docs/wrap-up` +- **branch** — new branch `origin/feat/custom-openai-provider` +- **branch** — branch deleted `origin/codex/runner-style-cleanup` +- **branch** — branch deleted `origin/codex/settings-input-polish` +- **branch** — branch deleted `origin/feat/coordinate-windows` +- **pr** — PR #76 opened by xcjs: docs: desktop screenshot in README [MERGED] +- **pr** — PR #75 opened by xcjs: feat(coord): enforce v3 file-scope, completion, and hub invariants [MERGED] +- **pr** — PR #74 opened by xcjs: chore: license DOMinic under AGPL-3.0-or-later [MERGED] +- **pr** — PR #73 opened by m-vawter: docs(submission): correct team contributions to the merged-PR record [MERGED] +- **pr** — PR #72 opened by xcjs: docs: hackathon submission form draft [MERGED] +- **pr** — PR #71 opened by m-vawter: docs: Q01 research provenance + finish-plan close-out [MERGED] +- **pr** — PR #70 opened by xcjs: feat: custom OpenAI-compatible provider (base URL + model) [MERGED] +- **pr** — PR #68 opened by m-vawter: docs: integrate coordination v3 [MERGED] +- **pr** — PR #67 opened by xcjs: ci: validate typecheck and build [MERGED] +- **pr** — PR #66 opened by ImNewToC0de: feat: add current app source to update context [MERGED] +- **pr** — PR #64 opened by Sullux: feat(runtime-engine): verify Recover beat e2e with error boundaries, Ask Agent to Fix, and in-place repair [MERGED] +- **pr** — PR #63 opened by m-vawter: fix: resolve shell and chat smoke failures [MERGED] +- **pr** — PR #62 OPEN → MERGED: fix: clean up dynamic app styles +- **pr** — PR #61 OPEN → MERGED: style: polish Settings model and credential inputs +- **pr** — PR #56 OPEN → CLOSED: feat: surface installed apps in taskbar +- **pr** — PR #36 OPEN → MERGED: feat(coordinate): Windows PowerShell port of coord script + docs +- **issue** — #69 opened by m-vawter: Enforce v3 invariants in coordination helpers [p1, status:in-review, type:task, ws:integration] → @xcjs +- **issue** — #65 opened by m-vawter: Integrate coordination v3 retrospective into agent docs [p1, status:in-review, type:task, ws:docs] → @m-vawter +- **issue** — #60 Update Settings model placeholder and API key inpu: OPEN→CLOSED +- **issue** — #59 Display user-facing error message in Agent Chat wh: OPEN→CLOSED; labels status:in-review (−status:in-progress); +2 comments +- **issue** — #58 Fix launcher z-50 overlay intercepting window cont: OPEN→CLOSED; labels status:in-review (−status:in-progress); +2 comments +- **issue** — #52 Runner: remove injected <style data-app-id> tags o: OPEN→CLOSED +- **issue** — #51 update_app: inject the target app's current source: OPEN→CLOSED; labels status:in-review (−status:claimed); +4 comments +- **issue** — #50 Taskbar lists installed apps (pinned entries) next: OPEN→CLOSED; +2 comments +- **issue** — #49 Recover beat end to end: forced compile error -> A: OPEN→CLOSED; labels status:in-review (−status:in-progress); +2 comments +- **issue** — #19 Coordination playbook v1: adopt the sprint minimum: +2 comments +- **issue** — #10 Submission package: written description, social po: labels needs-human (−-); +2 comments +- **issue** — #9 Demo video: record the core loop, wrap in HyperFra: labels status:claimed (−status:in-progress); assignees r0073d-l053r; +1 comments +- **issue** — #5 Coordination hub: +18 comments +## 2026-09-12 16:00 ET — `origin/main` 162aa09 + +- **branch** — new branch `origin/codex/runner-style-cleanup` +- **pr** — PR #62 opened by ImNewToC0de: fix: clean up dynamic app styles [OPEN] +- **issue** — #52 Runner: remove injected <style data-app-id> tags o: labels status:in-review (−status:unclaimed); assignees ImNewToC0de; +2 comments +- **issue** — #49 Recover beat end to end: forced compile error -> A: labels status:in-progress (−status:claimed); +1 comments +- **issue** — #5 Coordination hub: +1 comments +## 2026-09-12 15:55 ET — `origin/main` 162aa09 + +- **main** — origin/main 153ec94 → 162aa09 + - `162aa09` Brandon: docs(adrs): append verified "As built" reconciliation to ADRs 0001-0010 (#55) +- **pr** — PR #61 opened by ImNewToC0de: style: polish Settings model and credential inputs [OPEN] +- **pr** — PR #55 OPEN → MERGED: docs(adrs): append verified "As built" reconciliation to ADRs 0001-0010 +- **issue** — #60 Update Settings model placeholder and API key inpu: labels status:in-review (−status:in-progress) +- **issue** — #59 Display user-facing error message in Agent Chat wh: labels status:in-progress (−status:unclaimed); assignees m-vawter; +3 comments +- **issue** — #58 Fix launcher z-50 overlay intercepting window cont: labels status:in-progress (−status:claimed); assignees m-vawter; +1 comments +- **issue** — #47 ADRs 0001-0010 contradict the code on main - add a: OPEN→CLOSED +- **issue** — #46 Judge-facing walkthrough: docs/JUDGES.md + README : OPEN→CLOSED; +1 comments +## 2026-09-12 15:54 ET — `origin/main` 153ec94 + +- **main** — origin/main d3135ff → 153ec94 + - `153ec94` Brandon: fix(coordinate): resolve the hub to the lowest-numbered hub issue (#57) +- **branch** — new branch `origin/codex/settings-input-polish` +- **pr** — PR #57 OPEN → MERGED: fix(coordinate): resolve the hub to the lowest-numbered hub issue +- **issue** — #59 Display user-facing error message in Agent Chat wh: labels status:unclaimed (−status:claimed); assignees none; +1 comments +## 2026-09-12 15:52 ET — `origin/main` d3135ff + +- **main** — origin/main bb2e1a8 → d3135ff + - `d3135ff` m-vawter: docs: add judge-facing walkthrough (#53) +- **pr** — PR #53 OPEN → MERGED: docs: judge-facing walkthrough (docs/JUDGES.md) + screenshots +- **issue** — #60 Update Settings model placeholder and API key inpu: labels status:in-progress (−status:unclaimed); assignees ImNewToC0de; +1 comments +## 2026-09-12 15:49 ET — `origin/main` bb2e1a8 + +- **main** — origin/main 619aa82 → bb2e1a8 + - `bb2e1a8` ImNewToC0de: fix(build): separate strict typecheck from production bundle (#48) +- **pr** — PR #48 OPEN → MERGED: fix(build): separate strict typecheck from production bundle +- **issue** — #59 Display user-facing error message in Agent Chat wh: labels status:claimed (−status:unclaimed); assignees Sullux; +1 comments +## 2026-09-12 15:48 ET — `origin/main` 619aa82 + +- **issue** — #60 opened by Sullux: Update Settings model placeholder and API key input styling for visual polish [p2, status:unclaimed, type:task, ws:integration] +- **issue** — #59 opened by Sullux: Display user-facing error message in Agent Chat when API call fails or key is missing [p1, status:unclaimed, type:bug, ws:agent-chat] +- **issue** — #58 Fix launcher z-50 overlay intercepting window cont: labels status:claimed (−status:unclaimed); assignees xcjs; +1 comments +- **issue** — #5 Coordination hub: +-1 comments +## 2026-09-12 15:47 ET — `origin/main` 619aa82 + +- **branch** — new branch `origin/fix/coordinate-hub-selection` +- **pr** — PR #57 opened by r0073d-l053r: fix(coordinate): resolve the hub to the lowest-numbered hub issue [OPEN] +- **pr** — PR #56 opened by ImNewToC0de: feat: surface installed apps in taskbar [OPEN] +- **issue** — #58 opened by Sullux: Fix launcher z-50 overlay intercepting window controls on maximized windows [p1, status:unclaimed, type:bug, ws:os-shell] +- **issue** — #54 📊 Status Dashboard (live): labels - (−hub) +- **issue** — #50 Taskbar lists installed apps (pinned entries) next: labels status:in-review (−status:in-progress); +1 comments +- **issue** — #5 Coordination hub: +2 comments +## 2026-09-12 15:46 ET — `origin/main` 619aa82 + +- **branch** — new branch `origin/codex/taskbar-installed-apps` +- **branch** — new branch `origin/docs/adr-as-built-47` +- **pr** — PR #55 opened by r0073d-l053r: docs(adrs): append verified "As built" reconciliation to ADRs 0001-0010 [OPEN] +- **issue** — #54 opened by xcjs: 📊 Status Dashboard (live) [hub] +- **issue** — #47 ADRs 0001-0010 contradict the code on main - add a: labels status:in-review (−status:claimed); +1 comments +- **issue** — #19 Coordination playbook v1: adopt the sprint minimum: labels status:in-progress (−status:claimed); +1 comments +- **issue** — #5 Coordination hub: +2 comments +## 2026-09-12 15:42 ET — `origin/main` 619aa82 + +- **branch** — new branch `origin/docs/judges-walkthrough` +- **pr** — PR #53 opened by m-vawter: docs: judge-facing walkthrough (docs/JUDGES.md) + screenshots [OPEN] +- **issue** — #52 opened by r0073d-l053r: Runner: remove injected <style data-app-id> tags on uninstall and update [p2, status:unclaimed, type:task, ws:runtime-engine] +- **issue** — #51 opened by r0073d-l053r: update_app: inject the target app's current source into the model context (QUESTIONS Q2.2) [p1, status:claimed, type:task, ws:agent-chat] → @ImNewToC0de +- **issue** — #50 opened by r0073d-l053r: Taskbar lists installed apps (pinned entries) next to running windows - the judge's 'installed' signal [p1, status:in-progress, type:task, ws:os-shell] → @ImNewToC0de +- **issue** — #49 opened by r0073d-l053r: Recover beat end to end: forced compile error -> Ask Agent to Fix -> update_app repairs the app in place [p1, status:claimed, type:task, ws:runtime-engine] → @Sullux +- **issue** — #46 Judge-facing walkthrough: docs/JUDGES.md + README : labels status:in-progress (−status:claimed); +1 comments +- **issue** — #5 Coordination hub: +2 comments +## 2026-09-12 15:39 ET — `origin/main` 619aa82 + +- **branch** — new branch `origin/codex/issue33-verify` +- **pr** — PR #48 opened by ImNewToC0de: fix(build): separate strict typecheck from production bundle [OPEN] +- **issue** — #47 opened by r0073d-l053r: ADRs 0001-0010 contradict the code on main - add a verified 'As built' section to each [p0, status:claimed, type:task, ws:docs] → @r0073d-l053r +- **issue** — #46 opened by r0073d-l053r: Judge-facing walkthrough: docs/JUDGES.md + README link (gate 3: code that can be reviewed and learned from) [p0, status:claimed, type:task, ws:docs] → @m-vawter +- **issue** — #33 Ship agent-generated Pomodoro fixture and one-call: OPEN→CLOSED; +4 comments +- **issue** — #9 Demo video: record the core loop, wrap in HyperFra: +2 comments +- **issue** — #5 Coordination hub: +4 comments +## 2026-09-12 15:30 ET — `origin/main` 619aa82 + +- **main** — origin/main e9efa73 → 619aa82 + - `619aa82` m-vawter: docs: record completed ADR and hackathon plans (#37) +- **pr** — PR #37 OPEN → MERGED: docs: check off completed ADR plans; add hackathon finish plan +- **issue** — #5 Coordination hub: +1 comments +## 2026-09-12 15:29 ET — `origin/main` e9efa73 + +- **main** — origin/main 3cb3be2 → e9efa73 + - `e9efa73` Charles Sullivan: feat(rehearsal): freeze demo prompt and provide fallback pomodoro fixture (#40) +- **branch** — branch deleted `origin/feat/32-core-loop-rehearsal` +- **pr** — PR #40 OPEN → MERGED: feat(rehearsal): freeze demo prompt and provide fallback pomodoro fixture +- **issue** — #32 Rehearse the core loop, freeze the demo prompt, si: OPEN→CLOSED +- **issue** — #5 Coordination hub: +1 comments +## 2026-09-12 15:27 ET — `origin/main` 3cb3be2 + +- **main** — origin/main 2660f99 → 3cb3be2 + - `3cb3be2` Zackary Lowery: feat: add launcher uninstall and source actions (#44) +- **pr** — PR #44 OPEN → MERGED: feat: launcher Uninstall and View Source actions +- **issue** — #34 Add Uninstall and View Source actions to launcher : OPEN→CLOSED +- **issue** — #33 Ship agent-generated Pomodoro fixture and one-call: labels status:in-progress (−status:unclaimed); assignees ImNewToC0de; +3 comments +- **issue** — #32 Rehearse the core loop, freeze the demo prompt, si: +1 comments +- **issue** — #5 Coordination hub: +8 comments +## 2026-09-12 15:16 ET — `origin/main` 2660f99 + +- **main** — origin/main 483fc1d → 2660f99 + - `2660f99` ImNewToC0de: fix: keep window controls out of drag handler (#45) +- **branch** — branch deleted `origin/codex/window-controls-fix` +- **pr** — PR #45 OPEN → MERGED: fix: restore window control clicks +- **issue** — #43 Window controls minimize / maximize / close do not: OPEN→CLOSED; +1 comments +- **issue** — #5 Coordination hub: +2 comments +## 2026-09-12 15:14 ET — `origin/main` 483fc1d + +- **main** — origin/main a80b2a1 → 483fc1d + - `483fc1d` Brandon: fix(settings): align model presets with route defaults (#39) +- **branch** — new branch `origin/codex/window-controls-fix` +- **pr** — PR #45 opened by ImNewToC0de: fix: restore window control clicks [OPEN] +- **pr** — PR #39 OPEN → MERGED: fix(settings): current model presets; empty default so route fallback applies +- **issue** — #43 Window controls minimize / maximize / close do not: +2 comments +- **issue** — #38 Settings presets still use retired model IDs; rout: OPEN→CLOSED +- **issue** — #34 Add Uninstall and View Source actions to launcher : labels status:in-review (−status:claimed); +2 comments +- **issue** — #5 Coordination hub: +4 comments +## 2026-09-12 15:10 ET — `origin/main` a80b2a1 + +- **main** — origin/main fdeb0ff → a80b2a1 + - `a80b2a1` Brandon: fix(build,chat): scan app/features for Tailwind; Anthropic-safe chat history (#42) +- **branch** — new branch `origin/feat/launcher-actions-34` +- **branch** — branch deleted `origin/fix/tailwind-content-and-anthropic-history` +- **pr** — PR #44 opened by xcjs: feat: launcher Uninstall and View Source actions [OPEN] +- **pr** — PR #42 OPEN → MERGED: fix(build,chat): scan app/features for Tailwind; Anthropic-safe chat history +- **issue** — #43 opened by m-vawter: Window controls minimize / maximize / close do nothing in the running build (drag works) [p1, status:in-progress, type:bug, ws:os-shell] → @ImNewToC0de +- **issue** — #41 Tailwind never scans app/features/**; chat history: OPEN→CLOSED +- **issue** — #32 Rehearse the core loop, freeze the demo prompt, si: +1 comments +- **issue** — #5 Coordination hub: +4 comments +## 2026-09-12 15:06 ET — `origin/main` fdeb0ff + +- **main** — origin/main bcbef1e → fdeb0ff + - `fdeb0ff` ImNewToC0de: feat: wire DOMinic core app loop (#35) +- **branch** — new branch `origin/feat/32-core-loop-rehearsal` +- **branch** — new branch `origin/fix/tailwind-content-and-anthropic-history` +- **branch** — branch deleted `origin/codex/core-loop-integration` +- **branch** — branch deleted `origin/feature/node-tooling` +- **pr** — PR #42 opened by r0073d-l053r: fix(build,chat): scan app/features for Tailwind; Anthropic-safe chat history [OPEN] +- **pr** — PR #40 opened by Sullux: feat(rehearsal): freeze demo prompt and provide fallback pomodoro fixture [OPEN] +- **pr** — PR #35 OPEN → MERGED: feat: wire DOMinic core app loop +- **issue** — #41 opened by r0073d-l053r: Tailwind never scans app/features/**; chat history rejected by Anthropic [p0, status:in-review, type:bug, ws:integration] → @r0073d-l053r +- **issue** — #38 Settings presets still use retired model IDs; rout: +1 comments +- **issue** — #34 Add Uninstall and View Source actions to launcher : labels status:claimed (−status:unclaimed); assignees xcjs; +2 comments +- **issue** — #33 Ship agent-generated Pomodoro fixture and one-call: +1 comments +- **issue** — #32 Rehearse the core loop, freeze the demo prompt, si: labels status:in-review (−status:unclaimed); assignees Sullux; +3 comments +- **issue** — #30 Integration: wire chat -> install_app -> VFS/regis: OPEN→CLOSED; +1 comments +- **issue** — #5 Coordination hub: +3 comments +## 2026-09-12 14:59 ET — `origin/main` bcbef1e + +- **issue** — #5 Coordination hub: +2 comments +## 2026-09-12 14:57 ET — `origin/main` bcbef1e + +- **branch** — new branch `origin/docs/hackathon-finish-plan` +- **branch** — new branch `origin/feat/coordinate-windows` +- **branch** — new branch `origin/fix/settings-model-presets` +- **pr** — PR #39 opened by r0073d-l053r: fix(settings): current model presets; empty default so route fallback applies [OPEN] +- **pr** — PR #37 opened by m-vawter: docs: check off completed ADR plans; add hackathon finish plan [OPEN] +- **pr** — PR #36 opened by xcjs: feat(coordinate): Windows PowerShell port of coord script + docs [OPEN] +- **pr** — PR #35 opened by ImNewToC0de: feat: wire DOMinic core app loop [OPEN] +- **issue** — #38 opened by r0073d-l053r: Settings presets still use retired model IDs; route defaults are unreachable [p0, status:in-review, type:bug, ws:integration] → @r0073d-l053r +- **issue** — #6 Adopt the /coordinate skill: review and merge the : +1 comments +- **issue** — #5 Coordination hub: +2 comments +## 2026-09-12 14:55 ET — `origin/main` bcbef1e + +- **branch** — new branch `origin/codex/core-loop-integration` +- **issue** — #30 Integration: wire chat -> install_app -> VFS/regis: labels status:in-progress (−status:claimed); +1 comments +- **issue** — #6 Adopt the /coordinate skill: review and merge the : OPEN→CLOSED; +1 comments +- **issue** — #5 Coordination hub: +1 comments +## 2026-09-12 14:53 ET — `origin/main` bcbef1e + +- **main** — origin/main 8ea9114 → bcbef1e + - `bcbef1e` m-vawter: docs: README quickstart — Running DOMinic, drop docs-only language (#28) + - `07a52c6` Zackary Lowery: fix: add chat runtime dependencies (#29) +- **branch** — branch deleted `origin/docs/readme-quickstart` +- **pr** — PR #29 OPEN → MERGED: fix: chat runtime deps + AI SDK v4 pin + current default model IDs +- **pr** — PR #28 OPEN → MERGED: docs: README quickstart — Running DOMinic, drop docs-only language +- **issue** — #34 Add Uninstall and View Source actions to launcher : +1 comments +- **issue** — #33 Ship agent-generated Pomodoro fixture and one-call: +1 comments +- **issue** — #32 Rehearse the core loop, freeze the demo prompt, si: +1 comments +- **issue** — #30 Integration: wire chat -> install_app -> VFS/regis: +1 comments +- **issue** — #22 main does not build: chat slice imports packages m: OPEN→CLOSED; +1 comments +- **issue** — #8 README: replace docs-only language with a Running : OPEN→CLOSED; +1 comments +- **issue** — #5 Coordination hub: +1 comments +## 2026-09-12 14:51 ET — `origin/main` 8ea9114 + +- **main** — origin/main 11f79bc → 8ea9114 + - `8ea9114` Zackary Lowery: feat: OS shell with window manager, taskbar, drag/focus/minimize/maximize (#13) (#24) +- **branch** — branch deleted `origin/feat/os-shell-13` +- **pr** — PR #31 OPEN → CLOSED: feat(persistence): add local VFS and app registry +- **pr** — PR #24 OPEN → MERGED: feat: OS shell - window manager, taskbar, drag/focus/min/max/close +- **issue** — #34 Add Uninstall and View Source actions to launcher : +1 comments +- **issue** — #32 Rehearse the core loop, freeze the demo prompt, si: +1 comments +- **issue** — #30 Integration: wire chat -> install_app -> VFS/regis: labels status:claimed (−status:unclaimed); assignees ImNewToC0de +- **issue** — #22 main does not build: chat slice imports packages m: +1 comments +- **issue** — #19 Coordination playbook v1: adopt the sprint minimum: CLOSED→OPEN; +2 comments +- **issue** — #13 OS shell: taskbar, window manager, drag/focus/mini: OPEN→CLOSED; +1 comments +- **issue** — #5 Coordination hub: +3 comments +## 2026-09-12 14:44 ET — `origin/main` 11f79bc + +- **issue** — #30 Integration: wire chat -> install_app -> VFS/regis: +1 comments +- **issue** — #17 Persistence: VFS over localStorage, registry hydra: +1 comments +## 2026-09-12 14:43 ET — `origin/main` 11f79bc + +- **main** — origin/main 42fb7bb → 11f79bc + - `11f79bc` m-vawter: docs: add coordination best practices v2 (supersedes v1) (#26) + - `71c6edc` Charles Sullivan: feat(persistence): VFS over localStorage, registry hydration, Settings app, and Reset OS (#17) (#27) +- **branch** — branch deleted `origin/docs/coordination-best-practices-v2` +- **branch** — branch deleted `origin/feat/17-persistence` +- **pr** — PR #31 opened by ImNewToC0de: feat(persistence): add local VFS and app registry [OPEN] +- **pr** — PR #27 OPEN → MERGED: feat(persistence): VFS over localStorage, registry hydration, Settings app (#17) +- **pr** — PR #26 OPEN → MERGED: docs: coordination best practices v2 — three research answers reconciled (supersedes v1) +- **issue** — #34 opened by r0073d-l053r: Add Uninstall and View Source actions to launcher entries [p1, status:unclaimed, type:task, ws:os-shell] +- **issue** — #33 opened by r0073d-l053r: Ship agent-generated Pomodoro fixture and one-call installer [p1, status:unclaimed, type:task, ws:integration] +- **issue** — #32 opened by r0073d-l053r: Rehearse the core loop, freeze the demo prompt, sign off freeze [p0, status:unclaimed, type:task, ws:integration] +- **issue** — #19 Coordination playbook v1: adopt the sprint minimum: OPEN→CLOSED; +1 comments +- **issue** — #17 Persistence: VFS over localStorage, registry hydra: OPEN→CLOSED; +1 comments +- **issue** — #14 Contract: VFS + app registry API used by install_a: OPEN→CLOSED; +1 comments +- **issue** — #5 Coordination hub: +2 comments +## 2026-09-12 14:40 ET — `origin/main` 42fb7bb + +- **pr** — PR #29 opened by xcjs: fix: chat runtime deps + AI SDK v4 pin + current default model IDs [OPEN] +- **issue** — #30 opened by r0073d-l053r: Integration: wire chat -> install_app -> VFS/registry -> window running DynamicAppRunner [p0, status:unclaimed, type:task, ws:integration] +- **issue** — #22 main does not build: chat slice imports packages m: labels status:in-review (−status:in-progress); +3 comments +- **issue** — #5 Coordination hub: +1 comments +## 2026-09-12 14:39 ET — `origin/main` 42fb7bb + +- **branch** — new branch `origin/codex/vfs-contract` +- **branch** — new branch `origin/docs/readme-quickstart` +- **branch** — new branch `origin/feat/17-persistence` +- **branch** — new branch `origin/fix/build-deps-22` +- **pr** — PR #28 opened by m-vawter: docs: README quickstart — Running DOMinic, drop docs-only language [OPEN] +- **pr** — PR #27 opened by Sullux: feat(persistence): VFS over localStorage, registry hydration, Settings app (#17) [OPEN] +- **issue** — #22 main does not build: chat slice imports packages m: +2 comments +- **issue** — #17 Persistence: VFS over localStorage, registry hydra: labels status:in-review (−status:unclaimed); assignees Sullux; +2 comments +- **issue** — #11 Research: agent-first coordination best practices,: OPEN→CLOSED; +1 comments +- **issue** — #8 README: replace docs-only language with a Running : labels status:in-progress (−status:blocked); +1 comments +- **issue** — #5 Coordination hub: +2 comments +## 2026-09-12 14:36 ET — `origin/main` 42fb7bb + +- **main** — origin/main b86dfbd → 42fb7bb + - `42fb7bb` Charles Sullivan: feat(runtime): in-browser vue3-sfc-loader runner, esm.sh resolver, and error boundary (#15) (#25) + - `cea7d52` m-vawter: docs: add coordination best practices playbook (sprint minimum + full) (#18) +- **branch** — new branch `origin/docs/coordination-best-practices-2` +- **branch** — new branch `origin/docs/coordination-best-practices-v2` +- **branch** — new branch `origin/feat/os-shell-13` +- **branch** — branch deleted `origin/docs/coordination-best-practices` +- **pr** — PR #26 opened by m-vawter: docs: coordination best practices v2 — three research answers reconciled (supersedes v1) [OPEN] +- **pr** — PR #25 opened by Sullux: feat(runtime): in-browser vue3-sfc-loader runner, esm.sh resolver, and error boundary (#15) [MERGED] +- **pr** — PR #24 opened by xcjs: feat: OS shell - window manager, taskbar, drag/focus/min/max/close [OPEN] +- **pr** — PR #23 opened by m-vawter: docs: coordination best practices v2 — three research answers reconciled (supersedes v1) [CLOSED] +- **pr** — PR #18 OPEN → MERGED: docs: add coordination best practices playbook (sprint minimum + full protocol) +- **issue** — #22 opened by r0073d-l053r: main does not build: chat slice imports packages missing from package.json [p0, status:in-progress, type:bug, ws:agent-chat] → @xcjs +- **issue** — #19 Coordination playbook v1: adopt the sprint minimum: +1 comments +- **issue** — #15 Runtime engine: vue3-sfc-loader wrapper, esm.sh de: OPEN→CLOSED; labels status:in-review (−status:claimed); +2 comments +- **issue** — #13 OS shell: taskbar, window manager, drag/focus/mini: labels status:in-review (−status:in-progress); +2 comments +- **issue** — #5 Coordination hub: +4 comments +## 2026-09-12 14:32 ET — `origin/main` b86dfbd + +- **main** — origin/main 5cbaab4 → b86dfbd + - `b86dfbd` Charles Sullivan: feat(chat): implement /api/chat route, system prompt, tool schemas, and ChatWindow UI (#20) + - `bb97707` Zackary Lowery: feat: Nuxt 4 scaffold with strict TS, Pinia per-domain stores, Tailwind (#12) (#21) +- **branch** — branch deleted `origin/feat/16-agent-chat` +- **branch** — branch deleted `origin/feat/nuxt-scaffold-12` +- **pr** — PR #21 OPEN → MERGED: feat: Nuxt 4 scaffold with strict TS, Pinia stores, Tailwind +- **pr** — PR #20 OPEN → MERGED: feat(chat): /api/chat streaming, BYOK, install_app/update_app tools (#16) +- **issue** — #17 Persistence: VFS over localStorage, registry hydra: +2 comments +- **issue** — #16 Agent chat: /api/chat streaming, BYO key, install_: OPEN→CLOSED; +2 comments +- **issue** — #15 Runtime engine: vue3-sfc-loader wrapper, esm.sh de: labels status:claimed (−status:unclaimed); assignees Sullux; +3 comments +- **issue** — #14 Contract: VFS + app registry API used by install_a: +1 comments +- **issue** — #13 OS shell: taskbar, window manager, drag/focus/mini: +1 comments +- **issue** — #12 Nuxt 3 scaffold: strict TS, Pinia stores, feature-: OPEN→CLOSED; +1 comments +- **issue** — #5 Coordination hub: +1 comments +## 2026-09-12 14:30 ET — `origin/main` 5cbaab4 + +- **branch** — new branch `origin/codex/os-shell` +- **issue** — #13 OS shell: taskbar, window manager, drag/focus/mini: labels status:in-progress (−status:unclaimed); assignees xcjs; +1 comments +## 2026-09-12 14:29 ET — `origin/main` 5cbaab4 + +- **issue** — #12 Nuxt 3 scaffold: strict TS, Pinia stores, feature-: labels status:in-review (−status:in-progress); +2 comments +- **issue** — #5 Coordination hub: +1 comments +## 2026-09-12 14:28 ET — `origin/main` 5cbaab4 + +- **branch** — new branch `origin/feat/nuxt-scaffold-12` +- **pr** — PR #21 opened by xcjs: feat: Nuxt 4 scaffold with strict TS, Pinia stores, Tailwind [OPEN] +- **issue** — #14 Contract: VFS + app registry API used by install_a: +1 comments +- **issue** — #12 Nuxt 3 scaffold: strict TS, Pinia stores, feature-: labels status:in-progress (−status:claimed); +1 comments +## 2026-09-12 14:26 ET — `origin/main` 5cbaab4 + +- **branch** — new branch `origin/docs/coordination-best-practices` +- **branch** — new branch `origin/feat/16-agent-chat` +- **pr** — PR #20 opened by Sullux: feat(chat): /api/chat streaming, BYOK, install_app/update_app tools (#16) [OPEN] +- **pr** — PR #18 opened by m-vawter: docs: add coordination best practices playbook (sprint minimum + full protocol) [OPEN] +- **issue** — #19 opened by m-vawter: Coordination playbook v1: adopt the sprint minimum and report what is working and what is not [p1, status:claimed, type:task, ws:docs] → @m-vawter +- **issue** — #16 Agent chat: /api/chat streaming, BYO key, install_: labels status:in-review (−status:in-progress); +1 comments +- **issue** — #6 Adopt the /coordinate skill: review and merge the : +1 comments +- **issue** — #5 Coordination hub: +2 comments +## 2026-09-12 14:24 ET — `origin/main` 5cbaab4 + +- **init** — watch loop started; vault refreshed to `5cbaab4` (PR #7 `/coordinate` skill merged) +- **issues** — board seeded by the team since the last manual refresh: #8 README quickstart, #9 demo video, #10 submission package, #11 coordination research (all m-vawter); #12 Nuxt scaffold (xcjs, claimed); #13 OS shell, #14 Contract: VFS + app registry API, #15 runtime engine, #17 persistence (unclaimed); #16 agent chat (Sullux, in progress) +- **branch** — `feature/node-tooling` (xcjs) still unmerged and pre-dates #1–#4 diff --git a/vault/Roadmap Progress.md b/vault/Roadmap Progress.md new file mode 100644 index 0000000..9f5d616 --- /dev/null +++ b/vault/Roadmap Progress.md @@ -0,0 +1,62 @@ +--- +type: dashboard +title: Roadmap Progress +description: Generated by loop.mjs status --write from NEXT.md and the issue board. +tags: [dominic, roadmap, progress] +refreshed: "2026-10-03T00:06:26Z" +--- + +# Roadmap Progress + +Generated 2026-10-03T00:06:26Z from `NEXT77.md` and the GitHub issue board. Back to [[DOMinic Home]]. + +| Phase | Title | Done | Eligible now | +| --- | --- | --- | --- | +| 0 | Stabilize the floor | 0/6 | 0.1, 0.2, 0.3, 0.4, 0.5, 0.6 | +| 1 | Harden the two seams | 0/4 | gated | +| 2 | The agent product loop | 0/5 | gated | +| 3 | Reach | 0/7 | gated | + +## Steps + +### Phase 0 - Stabilize the floor + +| Step | Status | Owner of record | Work | +| --- | --- | --- | --- | +| 0.1 | missing | Michael | Playwright end-to-end test of the core loop using the existing window.__dominic hooks | +| 0.2 | missing | Charles | Remove the server env-var API-key fallback in server/api/chat.post.ts | +| 0.3 | missing | Zack | Baseline Content Security Policy | +| 0.4 | missing | Zack | ESLint flat config with a slice-boundary rule, plus lint-staged running vue-tsc on staged | +| 0.5 | missing | Michael (README), Brandon (ADR 0012, done) | Truth pass | +| 0.6 | missing | Justin | Dead code | + +### Phase 1 - Harden the two seams + +| Step | Status | Owner of record | Work | +| --- | --- | --- | --- | +| 1.1 | missing | Charles | Async VirtualFileSystem interface plus a driver registry, as ADR 0007 originally decided | +| 1.2 | missing | Charles | Sandboxed runner | +| 1.3 | missing | Charles | Dependency vetting gate in front of esm.sh | +| 1.4 | missing | Zack | Tighten CSP to the sandbox model | + +### Phase 2 - The agent product loop + +| Step | Status | Owner of record | Work | +| --- | --- | --- | --- | +| 2.1 | missing | Justin | Diff approval before update_app mounts | +| 2.2 | missing | Justin | Bounded auto-heal | +| 2.3 | missing | Charles | Tool results back to the model | +| 2.4 | missing | Justin (consumers), Charles (bridge) | Notification center and app-to-app IPC on the Phase 1 postMessage bridge | +| 2.5 | missing | Zack | Window-state persistence across reloads (QUESTIONS Q4.1), now that the VFS is IndexedDB-ba | + +### Phase 3 - Reach + +| Step | Status | Owner of record | Work | +| --- | --- | --- | --- | +| 3.1 | missing | Zack | Mobile responsive shell | +| 3.2 | missing | Zack | Window snapping and tiling | +| 3.3 | missing | Charles | /api/proxy shipped together with its SSRF blocklist (RFC 1918, cloud metadata endpoints) - | +| 3.4 | missing | Justin | Snapshot export and import of the whole OS state | +| 3.5 | missing | Justin | Multi-tab sync via BroadcastChannel (ADR 0003's deferred item) | +| 3.6 | missing | Charles | Agent-generated unit tests verified in-browser before install | +| 3.7 | missing | - | P2P app sharing via WebRTC or QR codes | diff --git a/vault/Specs & Plans/Plan - ADR Scaffold.md b/vault/Specs & Plans/Plan - ADR Scaffold.md new file mode 100644 index 0000000..1a51429 --- /dev/null +++ b/vault/Specs & Plans/Plan - ADR Scaffold.md @@ -0,0 +1,281 @@ +--- +type: plan +title: Plan - ADR Scaffold +tags: [plan, superpowers, adr] +source: docs/superpowers/plans/2026-09-12-adr-scaffold.md +date: 2026-10-02 +ingested: 2026-10-02 +--- +> [!abstract] Historical execution plan +> This is the step-by-step plan an agent followed to write the ADRs. It restates the *original* file contents verbatim; PR #2 has since added a Hackathon POC (Golden Path) section to every ADR, so the live ADR notes differ from what is quoted here. + +# ADR Scaffold Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use +> superpowers:subagent-driven-development (recommended) or +> superpowers:executing-plans to implement this plan +> task-by-task. Steps use checkbox (`- [ ]`) syntax for +> tracking. + +**Status (2026-09-12, 14:55 ET):** complete — every step below is +checked off; all deliverables are on `main`. + +**Goal:** Create a MADR 4.0 ADR scaffold (meta-ADR, template, README) in `docs/adrs/`. + +**Architecture:** Documentation-only deliverable — three +standalone markdown files with no tooling. Conventions +(numbering, naming, status lifecycle) are defined in the +README and demonstrated by ADR-0000. + +**Tech Stack:** Markdown only. + +## Global Constraints + +- All documents in English. +- Location: `docs/adrs/`. +- ADR format: MADR 4.0 (Markdown Any Decision Records). +- File naming: zero-padded 4-digit number + kebab-case title, e.g. `0000-record-architecture-decisions.md`. +- Status values: `proposed`, `accepted`, `superseded`, `deprecated`. +- Today's date for the meta-ADR: 2026-09-12. +- No build tooling, no config files (no adr-tools, no markdownlint). +- Note for executors: this workspace is **not a git + repository**. Before any commit step, ask the user + whether to run `git init`; if declined, skip all + commit steps. +- Addendum (2026-09-12): markdownlint (80-col prose), husky pre-commit + hook, and GitHub Actions CI were added by user direction after this + plan was written. + +--- + +### Task 1: Meta-ADR `0000-record-architecture-decisions.md` + +**Files:** + +- Create: `docs/adrs/0000-record-architecture-decisions.md` + +**Interfaces:** + +- Consumes: nothing. +- Produces: the canonical ADR-0000 referenced by the README index in Task 3. + +- [x] **Step 1: Create the file with this exact content** + +````markdown +# Record architecture decisions + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0000 | accepted | 2026-09-12 | Zack | + +## Technical Story + +The DOMinic project needs a lightweight, durable way to record significant architecture and tooling decisions so that future contributors understand not just *what* was decided, but *why*. + +## Context and Problem Statement + +Projects accumulate decisions that are cheap to revisit but expensive to re-litigate without context: which libraries to use, which patterns to follow, which conventions to adopt. Tribal knowledge and chat history do not survive team changes. We need a practice for capturing decisions in a format that lives with the code. + +## Decision Drivers + +- Decisions must be recorded in version-controllable plain text. +- The format must be quick to write and quick to read. +- The format must be understandable without special tooling. +- The practice must be easy for new contributors to adopt. + +## Considered Options + +- Michael Nygard's classic ADR format +- MADR (Markdown Any Decision Records) 4.0 +- No formal ADR practice + +## Decision Outcome + +Chosen option: **MADR 4.0**, because it keeps Nygard's strengths (plain markdown, short documents) while adding structure that improves quality: explicit decision drivers, considered options with pros/cons, and a decision outcome with consequences. It is widely adopted and works well in code review and pull requests. + +Decisions are recorded in this directory (`docs/adrs/`), numbered sequentially starting at 0001 for real decisions (0000 is this meta-ADR), with kebab-case file names. + +### Confirmation + +Practice confirmed for the start of the project; revisit if the format proves too heavyweight for small decisions. + +## Pros and Cons of the Options + +### Michael Nygard's classic ADR format + +- ✅ Minimal, extremely well known. +- ❌ No explicit place for considered options or their trade-offs. + +### MADR (Markdown Any Decision Records) 4.0 + +- ✅ Lightweight and markdown-native. +- ✅ Forces explicit consideration of alternatives. +- ❌ Slightly more boilerplate than Nygard. + +### No formal ADR practice + +- ✅ Zero overhead. +- ❌ Decision context is lost over time. + +## Links + +- [MADR project](https://adr.github.io/madr/) +- [Nygard's original ADR format](http://adamdrake.com/content/2015/07/03/architecture-decision-records-adrs/) +```` + +- [x] **Step 2: Verify** + +Read the file back and confirm: table renders with a +`Status` cell of `accepted`, date is `2026-09-12`, and +all headings listed above are present. + +- [x] **Step 3: Commit** + +If the user approved `git init`: `git add +docs/adrs/0000-record-architecture-decisions.md && git commit +-m "docs: add ADR 0000 (record architecture decisions)"`. +Otherwise skip. + +--- + +### Task 2: MADR Template `template.md` + +**Files:** + +- Create: `docs/adrs/template.md` + +**Interfaces:** + +- Consumes: nothing. +- Produces: the template referenced by the README workflow in + Task 3. Its placeholder fields (`{...}`) are the + intentional deliverable. + +- [x] **Step 1: Create the file with this exact content** + +````markdown +# {title} + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| {NNNN} | proposed | {YYYY-MM-DD} | {everyone involved} | + +## Technical Story + +{describe the technical story, issue, or requirement that motivates this decision} + +## Context and Problem Statement + +{describe the context and problem statement in plain language, e.g. using free form / two to three sentences or a list} + +## Decision Drivers + +- {driver 1, e.g. a force, facing concern, ...} +- {driver 2, e.g. a force, facing concern, ...} +- {driver N, e.g. a force, facing concern, ...} + +## Considered Options + +- {option 1} +- {option 2} +- {option N} + +## Decision Outcome + +Chosen option: **{option N}**, because {justification, e.g. only option that meets the decision drivers | which forces and concerns are resolved}. + +<!-- This is an optional element. Feel free to remove it. --> +### Confirmation + +{describe how you will communicate and/or confirm the decision was implemented correctly} + +## Pros and Cons of the Options + +### {option 1} + +- ✅ Good, because {argument} +- ✅ Good, because {argument} +- ❌ Bad, because {argument} + +### {option 2} + +- ✅ Good, because {argument} +- ✅ Good, because {argument} +- ❌ Bad, because {argument} + +## Links + +- {link type, e.g. Refined by} {ADR(s), e.g. ADR-0005} +- {link type, e.g. Related to} {reference(s)} +```` + +- [x] **Step 2: Verify** + +Read the file back and confirm: the `{...}` placeholders are +present exactly as above, all MADR section headings exist, +and the pros/cons table uses ✅/❌ bullets. + +- [x] **Step 3: Commit** + +If the user approved `git init`: `git add docs/adrs/template.md && git +commit -m "docs: add MADR ADR template"`. Otherwise skip. + +--- + +### Task 3: README with Conventions + +**Files:** + +- Create: `docs/adrs/README.md` + +**Interfaces:** + +- Consumes: file names from Tasks 1–2 (`0000-record-architecture-decisions.md`, `template.md`). +- Produces: the index and conventions for all future ADRs. + +- [x] **Step 1: Create the file with this exact content** + +````markdown +# Architecture Decision Records + +This directory records significant architecture and tooling decisions for DOMinic using [MADR 4.0](https://adr.github.io/madr/) (Markdown Any Decision Records). + +## What is an ADR? + +A short markdown document that captures one decision: the context that forced it, the options considered, and the outcome with its consequences. ADRs are immutable history — we do not rewrite accepted records, we supersede them with new ones. + +## Conventions + +- **Numbering:** sequential, zero-padded to 4 digits (`0001`, `0002`, …). `0000` is the meta-ADR that establishes this practice. +- **File naming:** `NNNN-short-title.md` with the title in kebab-case, e.g. `0001-use-typescript.md`. +- **Statuses:** + - `proposed` — under discussion. + - `accepted` — decided and in force. + - `superseded` — replaced by a later ADR (the file is kept; the status line names its successor). + - `deprecated` — no longer relevant without a direct successor. + +## Adding a new ADR + +1. Copy `template.md` to `NNNN-short-title.md` using the next free number. +2. Fill in every section; keep it short — the goal is context, not an essay. +3. Set status to `proposed` and open a pull request for discussion. +4. On acceptance, update the status to `accepted` and merge. + +## Index + +| ADR | Title | Status | +| --- | --- | --- | +| [0000](0000-record-architecture-decisions.md) | Record architecture decisions | accepted | +```` + +- [x] **Step 2: Verify** + +Read the file back and confirm: the index row links to +`0000-record-architecture-decisions.md` (created in Task 1), the workflow +references `template.md` (created in Task 2), and status values match the +Global Constraints. + +- [x] **Step 3: Commit** + +If the user approved `git init`: `git add docs/adrs/README.md && git +commit -m "docs: add ADR conventions README"`. Otherwise skip. diff --git a/vault/Specs & Plans/Plan - ADR Series.md b/vault/Specs & Plans/Plan - ADR Series.md new file mode 100644 index 0000000..0051195 --- /dev/null +++ b/vault/Specs & Plans/Plan - ADR Series.md @@ -0,0 +1,1567 @@ +--- +type: plan +title: Plan - ADR Series +tags: [plan, superpowers, adr] +source: docs/superpowers/plans/2026-09-12-adr-series.md +date: 2026-10-02 +ingested: 2026-10-02 +--- +> [!abstract] Historical execution plan +> This is the step-by-step plan an agent followed to write the ADRs. It restates the *original* file contents verbatim; PR #2 has since added a Hackathon POC (Golden Path) section to every ADR, so the live ADR notes differ from what is quoted here. + +# DOMinic ADR Series Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use +> superpowers:subagent-driven-development (recommended) or +> superpowers:executing-plans to implement this plan task-by-task. +> Steps use checkbox (`- [ ]`) syntax for tracking. + +**Status (2026-09-12, 14:55 ET):** complete — every step below is +checked off; all deliverables are on `main`. + +**Goal:** Author nine accepted Architecture Decision Records +(ADRs 0001–0009) in `docs/adrs/`, one per architectural pillar of the +DOMinic browser OS, using the repo's MADR 4.0 template plus OKF v0.2 +YAML frontmatter. + +**Architecture:** Documentation only — no application code. Each task +creates one complete ADR file, adds its row to the `docs/adrs/README.md` +index, optionally adds a reciprocal link to an earlier ADR's Links +section, verifies with `npm run lint:md`, and commits. + +**Tech Stack:** Markdown (MADR 4.0 + OKF v0.2 frontmatter), +markdownlint-cli2 (MD013 line_length 80), husky pre-commit +(`npm run lint:md`), git. + +**Spec:** `docs/superpowers/specs/2026-09-12-adr-series-design.md` + +## Global Constraints + +- Documentation only: no application code, Nuxt scaffold, or CI + workflows in this effort. +- Nine ADR files in `docs/adrs/`, zero-padded 4-digit numbers, + kebab-case names, per ADR 0000 conventions. +- All nine ADRs are `accepted` on `2026-09-12`; decider is `Zack`. +- MADR 4.0 structure exactly as `docs/adrs/template.md`: metadata + table (`| ADR Number | Status | Date | Deciders |`), Technical + Story, Context and Problem Statement, Decision Drivers, Considered + Options, Decision Outcome, Confirmation, Pros and Cons of the Options + (✅/❌ bullets), Links. +- OKF v0.2 YAML frontmatter on every ADR: required `type: + Architecture Decision Record`; recommended `title`, `description`, + `tags`; `status: accepted` mirroring the metadata table (the table + remains the status authority in the body); `generated: { by: + human:zack, at: 2026-09-12T00:00:00Z }` and `verified: { by: + human:zack, at: 2026-09-12T00:00:00Z }` (single bare mapping is + conformant per OKF §5.2). +- Prose wraps at 80 columns (MD013; headings, code blocks, and tables + are exempt in `.markdownlint.jsonc`). +- Cross-links resolve: an ADR may link only to ADRs that already exist + (ADR 0000, `template.md`, or earlier-numbered ADRs). Reciprocal links + to later ADRs are added by that later ADR's task as a small edit to + the earlier file's Links section — each commit stays lint-clean. +- Every commit message starts with `docs:`. +- The husky pre-commit hook runs `npm run lint:md` automatically on + every commit; do not skip hooks. +- Tag sets (pillar tag from spec, deduped when identical): + - 0001 `tags: [architecture]` + - 0002 `tags: [architecture, tooling]` + - 0003 `tags: [architecture]` + - 0004 `tags: [architecture, platform]` + - 0005 `tags: [architecture, agent]` + - 0006 `tags: [architecture, agent]` + - 0007 `tags: [architecture, storage]` + - 0008 `tags: [architecture, agent, storage]` + - 0009 `tags: [architecture, platform]` + +## File Structure + +- Create: `docs/adrs/0001-feature-slices-with-domain-driven-organization.md` +- Create: `docs/adrs/0002-strict-typescript-and-lint-toolchain.md` +- Create: `docs/adrs/0003-pinia-per-domain-stores.md` +- Create: `docs/adrs/0004-dominic-os-shell-and-taskbar.md` +- Create: + `docs/adrs/0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md` +- Create: + `docs/adrs/0006-agent-authored-runtime-compiled-components.md` +- Create: + `docs/adrs/0007-virtual-filesystem-with-pluggable-storage-drivers.md` +- Create: + `docs/adrs/0008-runtime-npm-dependency-loading-via-esm-sh-with-vetting.md` +- Create: + `docs/adrs/0009-cors-first-networking-with-chrome-masking-proxy-fallback.md` +- Modify (every task): `docs/adrs/README.md` — append one row to the + Index table. +- Modify (specific tasks): earlier ADR files — one line added to the + Links section for reciprocal links, exactly as specified per task. + +--- + +### Task 1: ADR 0001 — Feature slices with domain-driven organization + +**Files:** + +- Create: `docs/adrs/0001-feature-slices-with-domain-driven-organization.md` +- Modify: `docs/adrs/README.md` (Index table) + +**Interfaces:** + +- Consumes: ADR 0000 (`0000-record-architecture-decisions.md`) and + `template.md` format conventions. +- Produces: ADR 0001 file; README index row; Links section in + `0001` that Tasks 3 and 4 will extend with one reciprocal line each. + +- [x] **Step 1: Create ADR 0001 with this exact content** + +````markdown +--- +type: Architecture Decision Record +title: Feature slices with domain-driven organization +description: Organize app code into slice-local vertical directories with a thin shared kernel. +status: accepted +tags: [architecture] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +--- + +# Feature slices with domain-driven organization + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0001 | accepted | 2026-09-12 | Zack | + +## Technical Story + +DOMinic is an agent-driven browser OS whose code will be written and +rewritten largely by an LLM agent. Nuxt's default structure scatters +code by technical layer (`components/`, `composables/`, `stores/`), so +one feature's knowledge is spread thin across many directories. We +need an organization that maps bounded contexts to navigable +directories with enforceable ownership. + +## Context and Problem Statement + +The agent frequently needs to locate, read, and modify a single +feature without disturbing others. Layer-first directories force +cross-cutting edits, blur ownership, and grow without bound. Bounded +contexts should map 1:1 to directories a human or agent can traverse +in a single pass. + +## Decision Drivers + +- Slice-local cohesion: everything for one feature lives together. +- An enforceable dependency direction between slices. +- Small, focused directories that are easy for the agent to navigate. +- Compatibility with Nuxt's directory conventions. + +## Considered Options + +- Slice-local vertical directories +- Classic layer-first directories +- Hybrid: layer-first with per-slice subdirectories + +## Decision Outcome + +Chosen option: **slice-local vertical directories**, because they give +each bounded context one home, keep the dependency direction +enforceable, and produce the smallest, most navigable change sets for +agent edits. + +Each slice lives in `app/features/<slice>/` and owns its +`components/`, `composables/`, `stores/`, `types/`, and `tests/`. A +thin `app/shared/` kernel holds cross-slice contracts: shared types, +interfaces, and utilities. Slices may import from `app/shared/` but +never from other slices; cross-slice communication flows through +shared contracts and stores (ADR 0003). Bounded contexts map 1:1 to +slices. + +### Confirmation + +Dependency direction is enforced by ESLint import rules once the +lint toolchain lands; code review rejects slice-to-slice imports. + +## Pros and Cons of the Options + +### Slice-local vertical directories + +- ✅ Good, because each bounded context is one navigable directory. +- ✅ Good, because ownership boundaries are explicit and enforceable. +- ❌ Bad, because shared infrastructure needs a deliberate home + (`app/shared/`). + +### Classic layer-first directories + +- ✅ Good, because it is the Nuxt default with zero configuration. +- ❌ Bad, because one feature change scatters edits across many + directories. +- ❌ Bad, because unbounded layer directories blur slice ownership + over time. + +### Hybrid: layer-first with per-slice subdirectories + +- ✅ Good, because it preserves familiar top-level layer names. +- ❌ Bad, because it adds nesting without fixing scattered ownership. + +## Links + +- Related to [ADR 0000](0000-record-architecture-decisions.md) +```` + +- [x] **Step 2: Append the index row to `docs/adrs/README.md`** + +Add this as the last row of the Index table (after the 0000 row): + +```markdown +| [0001](0001-feature-slices-with-domain-driven-organization.md) | Feature slices with domain-driven organization | accepted | +``` + +- [x] **Step 3: Verify** + +Run: `npm run lint:md` +Expected: exit 0, no errors (pre-commit will run this too). + +- [x] **Step 4: Commit** + +```powershell +git add docs/adrs/0001-feature-slices-with-domain-driven-organization.md docs/adrs/README.md +git commit -m "docs: add ADR 0001 feature slices with domain-driven organization" +``` + +--- + +### Task 2: ADR 0002 — Strict TypeScript and lint toolchain + +**Files:** + +- Create: `docs/adrs/0002-strict-typescript-and-lint-toolchain.md` +- Modify: `docs/adrs/README.md` (Index table) + +**Interfaces:** + +- Consumes: ADR 0001 (enforces slice dependency direction). +- Produces: ADR 0002 file; README index row. + +- [x] **Step 1: Create ADR 0002 with this exact content** + +````markdown +--- +type: Architecture Decision Record +title: Strict TypeScript and lint toolchain +description: TypeScript strict mode plus type-checked ESLint and vue-tsc gate agent-generated code. +status: accepted +tags: [architecture, tooling] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +--- + +# Strict TypeScript and lint toolchain + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0002 | accepted | 2026-09-12 | Zack | + +## Technical Story + +DOMinic's agent authors and rewrites application code continuously. +Whatever the compiler and linter do not catch ships as runtime +breakage inside the OS. The toolchain must maximize static safety +while staying Vue-aware, and it must run in CI and before every +commit. + +## Context and Problem Statement + +Agent-generated code is written fast and reviewed shallowly. We need +maximal compile-time protection (`strict`, `noUncheckedIndexedAccess`), +Vue SFC awareness, and enforcement gates that do not depend on a +human remembering to run them. + +## Decision Drivers + +- Catch whole classes of errors at compile time, not runtime. +- Consistent style without per-file debate. +- Enforcement in CI and pre-commit, not by convention. +- First-class Vue SFC support (template + script analysis). + +## Considered Options + +- ESLint (typescript-eslint + eslint-plugin-vue) + vue-tsc +- Oxlint + vue-tsc +- Biome + vue-tsc + +## Decision Outcome + +Chosen option: **ESLint with type-checked typescript-eslint and +eslint-plugin-vue, plus `vue-tsc --noEmit`**, because it is the only +option with mature Vue-aware rules, full type-checking, and a flat +config that the Nuxt ecosystem (`@nuxt/eslint`) integrates directly. + +- TypeScript runs with `strict` and `noUncheckedIndexedAccess`. +- ESLint uses flat config with `recommendedTypeChecked` rules for + TypeScript and `eslint-plugin-vue` for SFCs. +- `vue-tsc --noEmit` gates CI and pre-commit via lint-staged/husky. + +### Confirmation + +A fresh Nuxt scaffold lands with this exact configuration; a +deliberate type error and a lint error both fail CI. + +## Pros and Cons of the Options + +### ESLint (typescript-eslint + eslint-plugin-vue) + vue-tsc + +- ✅ Good, because type-checked linting catches `await` misuse, + floating promises, and unsafe `any` spread. +- ✅ Good, because `eslint-plugin-vue` analyzes templates and SFC + structure, not just script. +- ❌ Bad, because type-checked linting is slower than syntax-only + linting. + +### Oxlint + vue-tsc + +- ✅ Good, because it is dramatically faster on large trees. +- ❌ Bad, because Vue SFC and type-aware rule coverage is still + incomplete. + +### Biome + vue-tsc + +- ✅ Good, because it bundles format + lint in one fast binary. +- ❌ Bad, because Vue SFC support is immature and the TypeScript + rule set is narrower than typescript-eslint. + +## Links + +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0001](0001-feature-slices-with-domain-driven-organization.md) +```` + +- [x] **Step 2: Append the index row to `docs/adrs/README.md`** + +Add this as the last row of the Index table: + +```markdown +| [0002](0002-strict-typescript-and-lint-toolchain.md) | Strict TypeScript and lint toolchain | accepted | +``` + +- [x] **Step 3: Verify** + +Run: `npm run lint:md` +Expected: exit 0, no errors. + +- [x] **Step 4: Commit** + +```powershell +git add docs/adrs/0002-strict-typescript-and-lint-toolchain.md docs/adrs/README.md +git commit -m "docs: add ADR 0002 strict TypeScript and lint toolchain" +``` + +--- + +### Task 3: ADR 0003 — Pinia per-domain stores + +**Files:** + +- Create: `docs/adrs/0003-pinia-per-domain-stores.md` +- Modify: `docs/adrs/README.md` (Index table) +- Modify: `docs/adrs/0001-feature-slices-with-domain-driven-organization.md` + (Links section — one reciprocal line) + +**Interfaces:** + +- Consumes: ADR 0001 (slice structure; stores live per slice). +- Produces: ADR 0003 file; README index row; reciprocal link from ADR + 0001's Links section. + +- [x] **Step 1: Create ADR 0003 with this exact content** + +````markdown +--- +type: Architecture Decision Record +title: Pinia per-domain stores +description: One Pinia setup-style store per slice is the only cross-slice state channel. +status: accepted +tags: [architecture] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +--- + +# Pinia per-domain stores + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0003 | accepted | 2026-09-12 | Zack | + +## Technical Story + +The OS holds live state everywhere: open windows, app registry, +settings, chat sessions. The state must be observable, modular, and +debuggable, and it must not entangle slices that are otherwise +independent. + +## Context and Problem Statement + +State lives inside components today would be invisible to other apps +and impossible to persist or restore. A global event bus would couple +every slice to every other. We need state primitives that are +Vue-idiomatic, per-slice modular, devtools-inspectable, and SSR-safe. + +## Decision Drivers + +- Vue-idiomatic reactivity and devtools integration. +- Per-slice modularity matching the slice layout. +- A single, controlled channel for cross-slice state. +- SSR-safety without bespoke lifecycles. + +## Considered Options + +- Pinia, one setup-style store per slice +- Composables-only state +- Pinia plus a global event bus + +## Decision Outcome + +Chosen option: **Pinia, one setup-style store per slice**, because it +is Vue's official state library, is devtools-first, is SSR-safe, and +its store-per-slice shape maps exactly onto the slice boundaries. + +- Each slice owns its store(s) under `app/features/<slice>/stores/`. +- Stores are the only cross-slice state channel; a slice reaches + another domain's state by importing its store (declared in + `app/shared/` when the coupling is contractual). +- No global event bus initially; revisit only if OS-wide signal + fan-out demands it. + +### Confirmation + +Code review rejects component-private OS state that duplicates a +store; the devtools Pinia panel shows one store per slice. + +## Pros and Cons of the Options + +### Pinia, one setup-style store per slice + +- ✅ Good, because setup stores use plain `ref`/`computed` the team + already knows. +- ✅ Good, because per-slice stores mirror slice boundaries 1:1. +- ❌ Bad, because another library adds bundle weight. + +### Composables-only state + +- ✅ Good, because it adds no dependency. +- ❌ Bad, because ad-hoc composable singletons lose devtools tracing + and consistent patterns for cross-slice state. + +### Pinia plus a global event bus + +- ✅ Good, because fan-out signals are trivial to broadcast. +- ❌ Bad, because hidden event coupling defeats the slice boundary + the store layout creates. + +## Links + +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0001](0001-feature-slices-with-domain-driven-organization.md) +```` + +- [x] **Step 2: Append the index row to `docs/adrs/README.md`** + +Add this as the last row of the Index table: + +```markdown +| [0003](0003-pinia-per-domain-stores.md) | Pinia per-domain stores | accepted | +``` + +- [x] **Step 3: Add reciprocal link in ADR 0001's Links section** + +In `docs/adrs/0001-feature-slices-with-domain-driven-organization.md`, +the Links section currently ends with: + +```markdown +- Related to [ADR 0000](0000-record-architecture-decisions.md) +``` + +Change it to: + +```markdown +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0003](0003-pinia-per-domain-stores.md) +``` + +- [x] **Step 4: Verify** + +Run: `npm run lint:md` +Expected: exit 0, no errors. + +- [x] **Step 5: Commit** + +```powershell +git add docs/adrs/0003-pinia-per-domain-stores.md docs/adrs/README.md docs/adrs/0001-feature-slices-with-domain-driven-organization.md +git commit -m "docs: add ADR 0003 Pinia per-domain stores" +``` + +--- + +### Task 4: ADR 0004 — DOMinic OS shell and taskbar + +**Files:** + +- Create: `docs/adrs/0004-dominic-os-shell-and-taskbar.md` +- Modify: `docs/adrs/README.md` (Index table) +- Modify: `docs/adrs/0001-feature-slices-with-domain-driven-organization.md` + (Links section — one reciprocal line) + +**Interfaces:** + +- Consumes: ADR 0001 (slice layout), ADR 0002 (toolchain context). +- Produces: ADR 0004 file; README index row; reciprocal link from ADR + 0001's Links section (now two added lines there). + +- [x] **Step 1: Create ADR 0004 with this exact content** + +````markdown +--- +type: Architecture Decision Record +title: DOMinic OS shell and taskbar +description: A desktop-metaphor shell in the os slice with window manager, taskbar, app registry, and a built-in Settings app. +status: accepted +tags: [architecture, platform] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +--- + +# DOMinic OS shell and taskbar + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0004 | accepted | 2026-09-12 | Zack | + +## Technical Story + +DOMinic presents itself as a desktop-like operating system running in +a browser tab. Something must own the desktop metaphor: launching +apps, tracking open windows, and giving the user a persistent place +to find and switch between apps — on a phone first, and a desktop +second. + +## Context and Problem Statement + +Apps (first-party and agent-authored alike) need a common contract for +being launched, surfaced, and closed. Mobile-first means the desktop +metaphor must degrade gracefully to small viewports instead of being +desktop-only. + +## Decision Drivers + +- The desktop metaphor is instantly familiar to users. +- Mobile scaling: the shell must work at phone widths first. +- App and window lifecycle management in one place. +- First-party apps must follow the same contract as agent-authored + apps. + +## Considered Options + +- Full windowing shell with taskbar +- Single-activity mobile-style shell +- Hybrid desktop shell with mobile taskbar + +## Decision Outcome + +Chosen option: **desktop-metaphor shell in an `os` slice**, because it +delivers the OS identity while treating mobile as a first-class +degradation rather than a separate shell. + +- The `os` slice contains the window manager, taskbar, and app + registry. +- The taskbar lists running apps; on narrow viewports it degrades to + a bottom-nav-style sheet and windows stack or maximize. +- The built-in **Settings** app is the first first-party app on the + base app contract; it is the global surface for OS preferences + (including LLM provider configuration, ADR 0005). +- Apps register with the app registry and are launched through it. + +### Confirmation + +The first Nuxt build renders the shell: taskbar, at least one window, +and the Settings app launchable. + +## Pros and Cons of the Options + +### Full windowing shell with taskbar + +- ✅ Good, because the OS metaphor carries the product identity. +- ✅ Good, because window and app lifecycle have one natural home. +- ❌ Bad, because windowing chrome wastes space on phone viewports + unless actively degraded. + +### Single-activity mobile-style shell + +- ✅ Good, because it is the natural fit for small screens. +- ❌ Bad, because it abandons the desktop metaphor that defines the + product. + +### Hybrid desktop shell with mobile taskbar + +- ✅ Good, because it keeps the desktop metaphor while degrading + cleanly. +- ✅ Good, because one shell serves both form factors. +- ❌ Bad, because two layout modes must both be tested. + +## Links + +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0001](0001-feature-slices-with-domain-driven-organization.md) +```` + +- [x] **Step 2: Append the index row to `docs/adrs/README.md`** + +Add this as the last row of the Index table: + +```markdown +| [0004](0004-dominic-os-shell-and-taskbar.md) | DOMinic OS shell and taskbar | accepted | +``` + +- [x] **Step 3: Add reciprocal link in ADR 0001's Links section** + +In `docs/adrs/0001-feature-slices-with-domain-driven-organization.md`, +the Links section currently ends with: + +```markdown +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0003](0003-pinia-per-domain-stores.md) +``` + +Change it to: + +```markdown +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0003](0003-pinia-per-domain-stores.md) +- Related to [ADR 0004](0004-dominic-os-shell-and-taskbar.md) +``` + +- [x] **Step 4: Verify** + +Run: `npm run lint:md` +Expected: exit 0, no errors. + +- [x] **Step 5: Commit** + +```powershell +git add docs/adrs/0004-dominic-os-shell-and-taskbar.md docs/adrs/README.md docs/adrs/0001-feature-slices-with-domain-driven-organization.md +git commit -m "docs: add ADR 0004 DOMinic OS shell and taskbar" +``` + +--- + +### Task 5: ADR 0005 — Agent chat via Vercel AI SDK + +**Files:** + +- Create: + `docs/adrs/0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md` +- Modify: `docs/adrs/README.md` (Index table) +- Modify: `docs/adrs/0004-dominic-os-shell-and-taskbar.md` (Links + section — one reciprocal line) + +**Interfaces:** + +- Consumes: ADR 0004 (Settings app as provider-selection surface). +- Produces: ADR 0005 file; README index row; reciprocal link from ADR + 0004's Links section. (ADR 0007's reciprocal back-link is added by + Task 7, not here — this ADR links only to ADRs that exist at its + commit time.) + +- [x] **Step 1: Create ADR 0005 with this exact content** + +````markdown +--- +type: Architecture Decision Record +title: Agent chat via Vercel AI SDK with server-side provider proxy +description: Chat runs through a Nuxt /api/chat route on the Vercel AI SDK; provider keys stay browser-side and travel per-request. +status: accepted +tags: [architecture, agent] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +--- + +# Agent chat via Vercel AI SDK with server-side provider proxy + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0005 | accepted | 2026-09-12 | Zack | + +## Technical Story + +Chat is the primary agent surface of DOMinic: the user talks, the +agent answers and authors applications. The OS must support virtually +any LLM provider without code changes and must never hold provider +credentials at rest on a server. + +## Context and Problem Statement + +Provider APIs differ in wire format, streaming protocol, and auth. +Hand-rolling adapters for each provider is a maintenance tax. But a +pure server-side integration would require the server to store API +keys, which we refuse: the user's keys should live in their browser +and be used per-request. + +## Decision Drivers + +- Provider-agnostic: adding a provider must be configuration, not + new protocol code. +- Streaming responses to the chat UI. +- Keys never at rest on the server. +- No-provider onboarding: a fresh install must guide the user to + configure a provider rather than fail. + +## Considered Options + +- Server-side proxy with server-held provider keys +- Bring-your-own-key direct from the browser +- Vercel AI SDK via a Nuxt server route with per-request keys + +## Decision Outcome + +Chosen option: **Vercel AI SDK (`ai` + provider packages) consumed +through a Nuxt server route `/api/chat` that streams responses**, +because the SDK normalizes every major provider behind one interface, +streams natively, and the server route keeps provider keys usable +while never persisting them. + +- Users select a provider via the Settings app (ADR 0004) when none + is configured; chat surfaces that selection flow instead of an + error. +- Provider API keys are stored browser-side in the VFS (ADR 0007) + and sent per-request to `/api/chat`, which uses them for that + request only. +- The route never logs, caches, or stores key material. + +### Confirmation + +A chat round-trip against a configured provider streams token-by-token; +the server holds no key material after a request completes. + +## Pros and Cons of the Options + +### Server-side proxy with server-held provider keys + +- ✅ Good, because the browser never sees a provider credential. +- ❌ Bad, because the server stores third-party secrets at rest, + a liability we refuse. + +### Bring-your-own-key direct from the browser + +- ✅ Good, because the server never touches keys at all. +- ❌ Bad, because every provider's streaming protocol must be + re-implemented client-side and CORS policies constrain which + providers work at all. + +### Vercel AI SDK via a Nuxt server route with per-request keys + +- ✅ Good, because one unified interface covers the provider + ecosystem, including streaming and tool calls. +- ✅ Good, because keys transit per-request and are never at rest + on the server. +- ❌ Bad, because a key still transits the server in memory for the + duration of a request. + +## Links + +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0004](0004-dominic-os-shell-and-taskbar.md) +```` + +- [x] **Step 2: Append the index row to `docs/adrs/README.md`** + +Add this as the last row of the Index table: + +```markdown +| [0005](0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md) | Agent chat via Vercel AI SDK with server-side provider proxy | accepted | +``` + +- [x] **Step 3: Add reciprocal link in ADR 0004's Links section** + +In `docs/adrs/0004-dominic-os-shell-and-taskbar.md`, the Links +section currently ends with: + +```markdown +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0001](0001-feature-slices-with-domain-driven-organization.md) +``` + +Change it to: + +```markdown +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0001](0001-feature-slices-with-domain-driven-organization.md) +- Related to [ADR 0005](0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md) +``` + +- [x] **Step 4: Verify** + +Run: `npm run lint:md` +Expected: exit 0, no errors. + +- [x] **Step 5: Commit** + +```powershell +git add docs/adrs/0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md docs/adrs/README.md docs/adrs/0004-dominic-os-shell-and-taskbar.md +git commit -m "docs: add ADR 0005 agent chat via Vercel AI SDK" +``` + +--- + +### Task 6: ADR 0006 — Agent-authored runtime-compiled components + +**Files:** + +- Create: + `docs/adrs/0006-agent-authored-runtime-compiled-components.md` +- Modify: `docs/adrs/README.md` (Index table) +- Modify: `docs/adrs/0004-dominic-os-shell-and-taskbar.md` (Links + section — one reciprocal line) + +**Interfaces:** + +- Consumes: ADR 0004 (`DominicApp` contract named there), ADR 0007 + (VFS persists component source — conceptual only; 0007 is written + by Task 7 and back-links here). +- Produces: ADR 0006 file; README index row; reciprocal link from ADR + 0004's Links section (now two added lines there). (Reciprocal + back-links from ADRs 0007 and 0008 are added by Tasks 7 and 8.) + +- [x] **Step 1: Create ADR 0006 with this exact content** + +````markdown +--- +type: Architecture Decision Record +title: Agent-authored runtime-compiled components +description: The agent's Vue SFC applications compile at runtime with vue3-sfc-loader and register on a DominicApp contract. +status: accepted +tags: [architecture, agent] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +--- + +# Agent-authored runtime-compiled components + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0006 | accepted | 2026-09-12 | Zack | + +## Technical Story + +The agent authors Vue single-file component applications from chat +requests. Users expect to install, preview, and run those apps +immediately — with no per-component build step, no bundler run, and +no redeploy of the OS itself. + +## Context and Problem Statement + +A traditional Vue app compiles at build time; DOMinic's apps are born +at runtime. They must compile in the browser, integrate with the +window manager as ordinary apps, persist their source, and be +removable. Compile-in-an-iframe would isolate them but would wall +them off from OS state and styling. + +## Decision Drivers + +- No build infrastructure per component. +- A live install-and-preview loop from chat. +- Reuse of the Vue ecosystem (components load dependencies, ADR 0008). +- Apps and widgets register through the same contract as first-party + apps. + +## Considered Options + +- Runtime-compiled SFC (vue3-sfc-loader), in-process +- Sandboxed iframe compile +- Prebuilt bundles delivered per app + +## Decision Outcome + +Chosen option: **runtime-compiled SFC via `vue3-sfc-loader`, in +process**, because it compiles agent-authored SFCs directly in the +browser with no per-app infrastructure and keeps compiled apps first- +class OS citizens. + +- Compiled components register as ordinary Vue components, apps, or + widgets through the component registry in the `os` slice. +- A `DominicApp` base component contract standardizes app metadata, + launch context props, and install/uninstall lifecycle hooks; the + built-in Settings app (ADR 0004) is the first implementation. +- Component source persists in the VFS (ADR 0007). +- iframe isolation is documented as a future escape hatch for + untrusted or crashing apps, not the default path. + +### Confirmation + +An agent-authored SFC installs from chat, appears in the app +registry, launches as a window, and survives reload via its VFS +source. + +## Pros and Cons of the Options + +### Runtime-compiled SFC (vue3-sfc-loader), in-process + +- ✅ Good, because zero per-app build step keeps the live loop fast. +- ✅ Good, because compiled apps share OS state and styling directly. +- ❌ Bad, because a crashing app shares the page with the OS (iframe + isolation remains the escape hatch). + +### Sandboxed iframe compile + +- ✅ Good, because crashes and bad CSS are contained. +- ❌ Bad, because postMessage bridging for state, storage, and + styling is heavy and erodes the app contract. + +### Prebuilt bundles delivered per app + +- ✅ Good, because runtime cost is minimal. +- ❌ Bad, because it requires per-app build infrastructure the + product explicitly avoids. + +## Links + +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0004](0004-dominic-os-shell-and-taskbar.md) +```` + +- [x] **Step 2: Append the index row to `docs/adrs/README.md`** + +Add this as the last row of the Index table: + +```markdown +| [0006](0006-agent-authored-runtime-compiled-components.md) | Agent-authored runtime-compiled components | accepted | +``` + +- [x] **Step 3: Add reciprocal link in ADR 0004's Links section** + +In `docs/adrs/0004-dominic-os-shell-and-taskbar.md`, the Links +section currently ends with: + +```markdown +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0001](0001-feature-slices-with-domain-driven-organization.md) +- Related to [ADR 0005](0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md) +``` + +Change it to: + +```markdown +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0001](0001-feature-slices-with-domain-driven-organization.md) +- Related to [ADR 0005](0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md) +- Related to [ADR 0006](0006-agent-authored-runtime-compiled-components.md) +``` + +- [x] **Step 4: Verify** + +Run: `npm run lint:md` +Expected: exit 0, no errors. + +- [x] **Step 5: Commit** + +```powershell +git add docs/adrs/0006-agent-authored-runtime-compiled-components.md docs/adrs/README.md docs/adrs/0004-dominic-os-shell-and-taskbar.md +git commit -m "docs: add ADR 0006 runtime-compiled components" +``` + +--- + +### Task 7: ADR 0007 — Virtual filesystem with pluggable storage drivers + +**Files:** + +- Create: + `docs/adrs/0007-virtual-filesystem-with-pluggable-storage-drivers.md` +- Modify: `docs/adrs/README.md` (Index table) +- Modify: `docs/adrs/0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md` + (Links section — one reciprocal line) +- Modify: `docs/adrs/0006-agent-authored-runtime-compiled-components.md` + (Links section — one reciprocal line) + +**Interfaces:** + +- Consumes: ADR 0005 (keys stored browser-side in VFS), ADR 0006 + (component source persists in VFS). +- Produces: ADR 0007 file; README index row; reciprocal links from + ADRs 0005 and 0006 Links sections. + +- [x] **Step 1: Create ADR 0007 with this exact content** + +````markdown +--- +type: Architecture Decision Record +title: Virtual filesystem with pluggable storage drivers +description: A single VirtualFileSystem interface over localStorage and IndexedDB drivers, chosen per path via VFS metadata. +status: accepted +tags: [architecture, storage] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +--- + +# Virtual filesystem with pluggable storage drivers + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0007 | accepted | 2026-09-12 | Zack | + +## Technical Story + +OS alterations persist locally: agent-authored component source, +settings, provider API keys, and the runtime dependency cache all +need a filesystem-shaped home in the browser. localStorage alone +caps out around 5 MB, far too small for a growing component library. + +## Context and Problem Statement + +Storage needs differ by payload: settings and small files want the +simplicity of synchronous localStorage, while component libraries +need IndexedDB's capacity and async bulk reads. Callers should not +know or care which backend holds a given path. + +## Decision Drivers + +- Local-first persistence with no server round-trip. +- Variable payload sizes across very different data kinds. +- Synchronous ergonomics for small data, async capacity for large. +- Testability: the backend must be substitutable in tests. + +## Considered Options + +- Single `VirtualFileSystem` interface with driver registry +- localStorage JSON tree only +- IndexedDB-backed filesystem only + +## Decision Outcome + +Chosen option: **a single `VirtualFileSystem` interface plus a driver +registry**, because it hides the storage split behind one API while +letting each path use the backend that fits its payload. + +- Two drivers ship together: a `localStorage` driver (small, + synchronous payloads) and an IndexedDB driver (large payloads, + async-backed). +- Per-path driver mapping is stored in VFS metadata, so callers use + either depending on the need at each path. +- The VFS underpins component source, settings, provider keys, and + the dependency cache (ADRs 0006, 0005, 0008). + +### Confirmation + +Unit tests run the same VFS suite against each driver; the OS boots +and restores component source and settings from the VFS after a +reload. + +## Pros and Cons of the Options + +### Single `VirtualFileSystem` interface with driver registry + +- ✅ Good, because callers are backend-agnostic per path. +- ✅ Good, because tests can swap drivers freely. +- ❌ Bad, because the metadata-per-path mapping adds a small + coordination cost. + +### localStorage JSON tree only + +- ✅ Good, because synchronous reads are trivially simple. +- ❌ Bad, because the ~5 MB ceiling starves component libraries. + +### IndexedDB-backed filesystem only + +- ✅ Good, because capacity and bulk reads are excellent. +- ❌ Bad, because its async-only API burdens tiny settings reads. + +## Links + +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0005](0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md) +- Related to [ADR 0006](0006-agent-authored-runtime-compiled-components.md) +```` + +- [x] **Step 2: Append the index row to `docs/adrs/README.md`** + +Add this as the last row of the Index table: + +```markdown +| [0007](0007-virtual-filesystem-with-pluggable-storage-drivers.md) | Virtual filesystem with pluggable storage drivers | accepted | +``` + +- [x] **Step 3: Add reciprocal link in ADR 0005's Links section** + +In +`docs/adrs/0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md`, +the Links section currently ends with: + +```markdown +- Related to [ADR 0004](0004-dominic-os-shell-and-taskbar.md) +``` + +Change it to: + +```markdown +- Related to [ADR 0004](0004-dominic-os-shell-and-taskbar.md) +- Related to [ADR 0007](0007-virtual-filesystem-with-pluggable-storage-drivers.md) +``` + +- [x] **Step 4: Add reciprocal link in ADR 0006's Links section** + +In +`docs/adrs/0006-agent-authored-runtime-compiled-components.md`, the +Links section currently ends with: + +```markdown +- Related to [ADR 0004](0004-dominic-os-shell-and-taskbar.md) +``` + +Change it to: + +```markdown +- Related to [ADR 0004](0004-dominic-os-shell-and-taskbar.md) +- Related to [ADR 0007](0007-virtual-filesystem-with-pluggable-storage-drivers.md) +``` + +Also add ADR 0006's line to ADR 0007's Links section (reciprocal +from the VFS side), so ADR 0007's Links section becomes: + +```markdown +## Links + +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0005](0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md) +- Related to [ADR 0006](0006-agent-authored-runtime-compiled-components.md) +``` + +- [x] **Step 5: Verify** + +Run: `npm run lint:md` +Expected: exit 0, no errors. + +- [x] **Step 6: Commit** + +```powershell +git add docs/adrs/0007-virtual-filesystem-with-pluggable-storage-drivers.md docs/adrs/0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md docs/adrs/0006-agent-authored-runtime-compiled-components.md docs/adrs/README.md +git commit -m "docs: add ADR 0007 virtual filesystem with pluggable storage drivers" +``` + +--- + +### Task 8: ADR 0008 — Runtime NPM dependency loading via esm.sh + +**Files:** + +- Create: + `docs/adrs/0008-runtime-npm-dependency-loading-via-esm-sh-with-vetting.md` +- Modify: `docs/adrs/README.md` (Index table) +- Modify: `docs/adrs/0006-agent-authored-runtime-compiled-components.md` + (Links section — one reciprocal line) +- Modify: `docs/adrs/0007-virtual-filesystem-with-pluggable-storage-drivers.md` + (Links section — one reciprocal line) + +**Interfaces:** + +- Consumes: ADR 0006 (compiled apps load dependencies), ADR 0007 + (vetted metadata and cached builds persist in VFS). +- Produces: ADR 0008 file; README index row; reciprocal links from + ADRs 0006 and 0007 Links sections. + +- [x] **Step 1: Create ADR 0008 with this exact content** + +````markdown +--- +type: Architecture Decision Record +title: Runtime NPM dependency loading via esm.sh with vetting +description: Agent apps load npm packages from esm.sh only after an automated vetting gate; vetted metadata and builds cache in the VFS. +status: accepted +tags: [architecture, agent, storage] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +--- + +# Runtime NPM dependency loading via esm.sh with vetting + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0008 | accepted | 2026-09-12 | Zack | + +## Technical Story + +Agent-authored components import arbitrary NPM packages at runtime. +The browser cannot run a bundler per app, so packages must arrive as +ready ESM — but arbitrary code execution from a CDN is exactly the +surface we must gate behind review. + +## Context and Problem Statement + +The full npm ecosystem should be reachable, including CommonJS +packages and subpath imports, without a server build step. At the +same time, executing third-party code in the user's browser demands +an automated security review before anything loads. + +## Decision Drivers + +- Full npm ecosystem reach, including CJS transpilation and + subpath imports. +- No server build step. +- Security review before execution, not after. +- Offline resilience through caching. + +## Considered Options + +- esm.sh with automated vetting +- unpkg/jsDelivr with a runtime resolver +- Server-side pre-bundling per app + +## Decision Outcome + +Chosen option: **esm.sh as the ESM CDN, gated by automated vetting**, +because it serves the whole npm ecosystem (CJS transpile, subpaths, +GitHub refs) as ready ESM with zero server build, while the vetting +gate satisfies the security driver. + +- Every dependency passes a vetting gate before first load: npm + registry and advisory (audit) data, weekly download volume, + license check, publish age, and maintainer signals. +- Vetted package metadata and cached builds persist in the VFS + (ADR 0007). +- Recorded caveats: native/binary addons cannot run in a browser; + heavy Node built-in usage depends on esm.sh polyfills; CDN + availability is a dependency mitigated by the vetting gate and VFS + caching; the first request pays build latency. + +### Confirmation + +A component importing a vetted package loads and runs; a package +failing the vetting gate is refused with its reasons surfaced. + +## Pros and Cons of the Options + +### esm.sh with automated vetting + +- ✅ Good, because CJS transpilation and subpath support just work. +- ✅ Good, because no server bundling is needed, ever. +- ❌ Bad, because first request pays esm.sh build latency. +- ❌ Bad, because CDN availability becomes a runtime dependency. + +### unpkg/jsDelivr with a runtime resolver + +- ✅ Good, because the file CDNs are stable and fast. +- ❌ Bad, because bare-specifier resolution must be re-implemented + client-side and CJS support is incomplete. + +### Server-side pre-bundling per app + +- ✅ Good, because bundles are optimized and auditably pinned. +- ❌ Bad, because a server build pipeline contradicts the + no-build-infra requirement and adds deploy latency per app. + +## Links + +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0006](0006-agent-authored-runtime-compiled-components.md) +- Related to [ADR 0007](0007-virtual-filesystem-with-pluggable-storage-drivers.md) +```` + +- [x] **Step 2: Append the index row to `docs/adrs/README.md`** + +Add this as the last row of the Index table: + +```markdown +| [0008](0008-runtime-npm-dependency-loading-via-esm-sh-with-vetting.md) | Runtime NPM dependency loading via esm.sh with vetting | accepted | +``` + +- [x] **Step 3: Add reciprocal link in ADR 0006's Links section** + +In +`docs/adrs/0006-agent-authored-runtime-compiled-components.md`, the +Links section currently ends with: + +```markdown +- Related to [ADR 0004](0004-dominic-os-shell-and-taskbar.md) +- Related to [ADR 0007](0007-virtual-filesystem-with-pluggable-storage-drivers.md) +``` + +Change it to: + +```markdown +- Related to [ADR 0004](0004-dominic-os-shell-and-taskbar.md) +- Related to [ADR 0007](0007-virtual-filesystem-with-pluggable-storage-drivers.md) +- Related to [ADR 0008](0008-runtime-npm-dependency-loading-via-esm-sh-with-vetting.md) +``` + +- [x] **Step 4: Add reciprocal link in ADR 0007's Links section** + +In +`docs/adrs/0007-virtual-filesystem-with-pluggable-storage-drivers.md`, +the Links section currently ends with: + +```markdown +- Related to [ADR 0005](0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md) +- Related to [ADR 0006](0006-agent-authored-runtime-compiled-components.md) +``` + +Change it to: + +```markdown +- Related to [ADR 0005](0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md) +- Related to [ADR 0006](0006-agent-authored-runtime-compiled-components.md) +- Related to [ADR 0008](0008-runtime-npm-dependency-loading-via-esm-sh-with-vetting.md) +``` + +- [x] **Step 5: Verify** + +Run: `npm run lint:md` +Expected: exit 0, no errors. + +- [x] **Step 6: Commit** + +```powershell +git add docs/adrs/0008-runtime-npm-dependency-loading-via-esm-sh-with-vetting.md docs/adrs/0006-agent-authored-runtime-compiled-components.md docs/adrs/0007-virtual-filesystem-with-pluggable-storage-drivers.md docs/adrs/README.md +git commit -m "docs: add ADR 0008 esm.sh dependency loading with vetting" +``` + +--- + +### Task 9: ADR 0009 — CORS-first networking with Chrome-masking proxy + +**Files:** + +- Create: + `docs/adrs/0009-cors-first-networking-with-chrome-masking-proxy-fallback.md` +- Modify: `docs/adrs/README.md` (Index table) + +**Interfaces:** + +- Consumes: ADR 0004 (os slice hosts the client helper composable). +- Produces: ADR 0009 file; README index row; final ADR of the series. + +- [x] **Step 1: Create ADR 0009 with this exact content** + +````markdown +--- +type: Architecture Decision Record +title: CORS-first networking with Chrome-masking proxy fallback +description: External calls fetch directly when CORS allows and fall back transparently to a generic proxy that presents a latest-stable Chrome User-Agent. +status: accepted +tags: [architecture, platform] +generated: { by: human:zack, at: 2026-09-12T00:00:00Z } +verified: { by: human:zack, at: 2026-09-12T00:00:00Z } +--- + +# CORS-first networking with Chrome-masking proxy fallback + +| ADR Number | Status | Date | Deciders | +| --- | --- | --- | --- | +| 0009 | accepted | 2026-09-12 | Zack | + +## Technical Story + +Components and the agent must reach external services — public APIs, +web pages, feeds — that may or may not send CORS headers. The OS +needs one networking story that prefers the browser's direct path +and degrades gracefully when a service blocks cross-origin reads. + +## Context and Problem Statement + +Direct browser requests are fast, cache-friendly, and credential- +correct, but fail entirely when a service lacks CORS headers. +A server proxy can bypass CORS, but routing every request through +the server wastes latency where direct fetch would work. + +## Decision Drivers + +- Prefer direct browser requests wherever CORS allows. +- Bypass CORS gaps transparently, without per-service code. +- Avoid anti-bot rejection on proxied requests. +- Keep the server surface to one generic route. + +## Considered Options + +- Single generic proxy route with CORS-first fallback +- Domain allowlist proxy +- Per-service proxy routes + +## Decision Outcome + +Chosen option: **CORS-first direct `fetch` with transparent fallback +to a single generic `/api/proxy` Nuxt server route**, because it +keeps the fast path direct and confines all CORS workarounds to one +reviewable server surface. + +- A client-side helper composable decides direct vs proxy per + request; fallback on failure is automatic and invisible to + callers. +- The proxy rewrites the User-Agent to the latest stable Google + Chrome and strips identifying headers (cookies, referer, origin) + before forwarding, and streams the response back. +- The route is generic: no per-service routes, no allowlist to + maintain. + +### Confirmation + +A CORS-enabled endpoint loads directly (visible in network tools); +a CORS-blocking endpoint transparently returns content through +`/api/proxy`. + +## Pros and Cons of the Options + +### Single generic proxy route with CORS-first fallback + +- ✅ Good, because the fast path stays direct and cache-friendly. +- ✅ Good, because one generic route is easy to review and harden. +- ❌ Bad, because proxied traffic adds server bandwidth cost. + +### Domain allowlist proxy + +- ✅ Good, because abuse surface shrinks to known hosts. +- ❌ Bad, because agent-authored apps cannot reach unlisted + services without an OS change. + +### Per-service proxy routes + +- ✅ Good, because each route can carry bespoke transformation. +- ❌ Bad, because server code grows with every new integration. + +## Links + +- Related to [ADR 0000](0000-record-architecture-decisions.md) +- Related to [ADR 0004](0004-dominic-os-shell-and-taskbar.md) +```` + +- [x] **Step 2: Append the index row to `docs/adrs/README.md`** + +Add this as the last row of the Index table: + +```markdown +| [0009](0009-cors-first-networking-with-chrome-masking-proxy-fallback.md) | CORS-first networking with Chrome-masking proxy fallback | accepted | +``` + +- [x] **Step 3: Verify** + +Run: `npm run lint:md` +Expected: exit 0, no errors. + +- [x] **Step 4: Commit** + +```powershell +git add docs/adrs/0009-cors-first-networking-with-chrome-masking-proxy-fallback.md docs/adrs/README.md +git commit -m "docs: add ADR 0009 CORS-first networking with proxy fallback" +``` + +--- + +### Task 10: Final verification sweep + +**Files:** + +- Read only: `docs/adrs/*.md`, `docs/adrs/README.md` + +**Interfaces:** + +- Consumes: all nine ADRs from Tasks 1–9. +- Produces: verification evidence for the spec's Verification section. + +- [x] **Step 1: Verify file set** + +```powershell +Get-ChildItem docs/adrs -Filter "0*.md" | Select-Object -ExpandProperty Name +``` + +Expected output — exactly these ten lines (0000 plus the nine new +ADRs, in order): + +```text +0000-record-architecture-decisions.md +0001-feature-slices-with-domain-driven-organization.md +0002-strict-typescript-and-lint-toolchain.md +0003-pinia-per-domain-stores.md +0004-dominic-os-shell-and-taskbar.md +0005-agent-chat-via-vercel-ai-sdk-with-server-side-provider-proxy.md +0006-agent-authored-runtime-compiled-components.md +0007-virtual-filesystem-with-pluggable-storage-drivers.md +0008-runtime-npm-dependency-loading-via-esm-sh-with-vetting.md +0009-cors-first-networking-with-chrome-masking-proxy-fallback.md +``` + +- [x] **Step 2: Verify frontmatter, statuses, and cross-links** + +```powershell +Select-String -Path docs/adrs/000*.md -Pattern '^status: accepted$' | Measure-Object | Select-Object -ExpandProperty Count +Select-String -Path docs/adrs/000*.md -Pattern '^type: Architecture Decision Record$' | Measure-Object | Select-Object -ExpandProperty Count +Select-String -Path docs/adrs/000*.md -Pattern 'generated: \{ by: human:zack' | Measure-Object | Select-Object -ExpandProperty Count +``` + +Expected: all three counts are `9`. + +Then confirm every ADR link target file exists (no dead ADR links): + +```powershell +$targets = Select-String -Path docs/adrs/000*.md -Pattern '\]\(([0-9A-Za-z-]+\.md)\)' -AllMatches | + ForEach-Object { $_.Matches } | ForEach-Object { $_.Groups[1].Value } | + Sort-Object -Unique +$targets | Where-Object { -not (Test-Path "docs/adrs/$_") } +``` + +Expected: empty output (no dead links). `template.md` links are not +matched by this pattern, so no false positives are expected. + +- [x] **Step 3: Verify MADR section coverage per file** + +```powershell +$sections = '## Technical Story','## Context and Problem Statement','## Decision Drivers','## Considered Options','## Decision Outcome','## Pros and Cons of the Options','## Links' +foreach ($f in (Get-ChildItem docs/adrs -Filter "000[1-9]*.md")) { + $c = Get-Content $f.FullName -Raw + $missing = $sections | Where-Object { -not $c.Contains($_) } + if ($missing) { "$($f.Name): MISSING $($missing -join ', ')" } +} +``` + +Expected: no output — every ADR contains every MADR section. + +- [x] **Step 4: Run full lint** + +Run: `npm run lint:md` +Expected: exit 0, zero errors across the whole repo. + +- [x] **Step 5: Final commit (only if Steps 1–4 required fixes)** + +If any verification step above forced a fix, commit it: + +```powershell +git add docs/adrs +git commit -m "docs: fix ADR series verification findings" +``` + +If nothing needed fixing, skip this step — the series is complete. + +--- + +## Self-Review Notes + +Spec coverage check (spec section → task): + +- Deliverables (nine files, numbering, kebab-case) → Tasks 1–9. +- Common conventions (MADR structure, OKF frontmatter, 80-col wrap, + status authority, lifecycle, cross-links, deciders row) → Global + Constraints + every task's content block. +- Per-ADR content (0001–0009 stories, drivers, options, outcomes) → + Tasks 1–9, each carrying its full ADR body in Step 1. +- README index rows → Steps "Append the index row" in Tasks 1–9. +- Non-goals (no app code/CI) → Global Constraints. +- Verification (nine files, valid frontmatter, MADR sections, + cross-links resolve, `lint:md` clean) → Task 10. + +Design decisions recorded here for the implementer: + +- ADRs link only to earlier ADRs (plus 0000). Reciprocal links to + later ADRs are added by the later ADR's own task: Task 7 edits + ADRs 0005 and 0006, and Task 8 edits ADRs 0006 and 0007. After + Task 8, ADR 0006's Links section lists 0000, 0004, 0007, 0008, and + ADR 0007's lists 0000, 0005, 0006, 0008. +- The frontmatter uses single bare `generated`/`verified` mappings, + which OKF §5.2 treats as one-element lists. +- Timestamps are `2026-09-12T00:00:00Z` (the spec's authoring date, + written as ISO-8601 UTC instants). +- Tag sets come from the spec's ADR list table, prefixed with + `architecture` and deduped. +- Task 4's Links section deliberately omits ADR 0006 (which does not + exist yet); the reciprocal ADR 0006 line is added in Task 6. diff --git a/vault/Specs & Plans/Plan - Custom OpenAI Provider.md b/vault/Specs & Plans/Plan - Custom OpenAI Provider.md new file mode 100644 index 0000000..0436b0a --- /dev/null +++ b/vault/Specs & Plans/Plan - Custom OpenAI Provider.md @@ -0,0 +1,348 @@ +--- +type: plan +title: Plan - Custom OpenAI Provider +tags: [plan, superpowers, provider] +source: docs/superpowers/plans/2026-09-12-custom-openai-compatible-provider.md +date: 2026-10-02 +ingested: 2026-10-02 +repo_head: 0fc772d +--- +> [!abstract] Shipped feature +> The custom OpenAI-compatible provider this describes shipped in PR #70 (base URL + model, no key at rest). See [[ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy|ADR 0005]] and [[Architecture Overview]]. + +# Custom OpenAI-Compatible Provider Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a fifth "Custom" provider so any OpenAI-compatible endpoint (OpenRouter, Ollama, LM Studio, internal gateways) works as a first-class provider in DOMinic's Settings and `/api/chat`. + +**Architecture:** Extend the `LlmProvider` union with `'custom'` in both definition sites, add a Custom provider card + save-time validation to SettingsApp.vue, and add a `custom` branch to `server/api/chat.post.ts` that constructs `createOpenAI({ apiKey, baseURL: clientBaseUrl })`. The chat composable already forwards `baseUrl`, so no client chat changes are needed. + +**Tech Stack:** Nuxt 4 (Vue 3 SFC + Pinia), AI SDK v4 (`@ai-sdk/openai` `createOpenAI`), Tailwind utility classes. + +**Spec:** `docs/superpowers/specs/2026-09-12-custom-openai-compatible-provider-design.md` + +## Global Constraints + +- Strict TypeScript: `npx vue-tsc --noEmit` must pass with zero errors after every task. +- AI SDK v4 conventions: tools use `parameters` (not `inputSchema`); route returns `result.toDataStreamResponse()`. Do not migrate to v5. +- No env fallback for the custom provider's base URL — client-sent `baseUrl` only. +- Lenient URL handling: no http(s)-prefix policing, no trailing-slash normalization. +- Existing four providers (openai/anthropic/google/deepseek) must keep byte-identical behavior. +- Style: match existing file conventions — single quotes in `.ts`/SFC script blocks, Tailwind utility classes, no comments unless mirroring adjacent ones. +- This repo has no unit-test runner wired; verification is typecheck + dev-server curl/UI checks (per spec's Testing section). + +--- + +### Task 1: Extend the provider union (both definition sites) + +**Files:** +- Modify: `app/features/settings/stores/settings.ts:4` +- Modify: `app/features/chat/types/chat.ts:20` (`ProviderConfig.provider`) + +**Interfaces:** +- Consumes: nothing (leaf change). +- Produces: `export type LlmProvider = 'openai' | 'anthropic' | 'google' | 'deepseek' | 'custom'` (settings.ts) and `ProviderConfig.provider` accepting `'custom'` (chat.ts). All later tasks rely on these unions. + +- [ ] **Step 1: Update the union in settings.ts** + +In `app/features/settings/stores/settings.ts`, change line 4 from: + +```ts +export type LlmProvider = 'openai' | 'anthropic' | 'google' | 'deepseek' +``` + +to: + +```ts +export type LlmProvider = 'openai' | 'anthropic' | 'google' | 'deepseek' | 'custom' +``` + +- [ ] **Step 2: Update ProviderConfig in chat.ts** + +In `app/features/chat/types/chat.ts`, change the `ProviderConfig` interface's provider field from: + +```ts +export interface ProviderConfig { + provider: 'openai' | 'anthropic' | 'google' | 'deepseek' + model: string + apiKey: string + baseUrl?: string +} +``` + +to: + +```ts +export interface ProviderConfig { + provider: 'openai' | 'anthropic' | 'google' | 'deepseek' | 'custom' + model: string + apiKey: string + baseUrl?: string +} +``` + +- [ ] **Step 3: Typecheck** + +Run: `npx vue-tsc --noEmit` +Expected: zero errors (unions are additive; no existing `switch` on provider exhaustiveness exists in the client). + +- [ ] **Step 4: Commit** + +```powershell +git add app/features/settings/stores/settings.ts app/features/chat/types/chat.ts +git commit -m "feat(settings): allow 'custom' in LlmProvider and ProviderConfig unions" +``` + +--- + +### Task 2: Settings UI — Custom card, required fields, inline validation + +**Files:** +- Modify: `app/features/settings/components/SettingsApp.vue` (providers array ~line 123; script validation in `save()`; Base URL template block ~lines 71-81; optional error banner near Save button ~line 85) + +**Interfaces:** +- Consumes: `LlmProvider` including `'custom'` (Task 1), `useSettingsStore.saveSettings(updates: Partial<Omit<SettingsState, 'hasHydrated'>>)` unchanged. +- Produces: providers array entry `{ id: 'custom', name: 'Custom', icon: '🔌', defaultModel: '' }`; local ref `baseUrlError = ref('')`; behavior contract — `save()` is a no-op with `baseUrlError` set when `selectedProvider === 'custom'` and `baseUrlInput` or `modelInput` is empty. + +- [ ] **Step 1: Add the Custom provider card entry** + +In the `providers` array inside `<script setup>`, append: + +```ts +const providers: { id: LlmProvider; name: string; icon: string; defaultModel: string }[] = [ + { id: 'openai', name: 'OpenAI', icon: '⚡', defaultModel: 'gpt-5' }, + { id: 'anthropic', name: 'Anthropic', icon: '🧠', defaultModel: 'claude-sonnet-5' }, + { id: 'google', name: 'Google', icon: '✨', defaultModel: 'gemini-2.5-pro' }, + { id: 'deepseek', name: 'DeepSeek', icon: '🐋', defaultModel: 'deepseek-chat' }, + { id: 'custom', name: 'Custom', icon: '🔌', defaultModel: '' } +] +``` + +- [ ] **Step 2: Make selectProvider handle the empty default model** + +`selectProvider` already writes `modelInput.value = preset.defaultModel`, so Custom correctly empties the field. No change needed — verify by reading the function; do not add special cases. + +- [ ] **Step 3: Add validation state and update save()** + +Add near the other refs (after `const baseUrlInput = ref('')`): + +```ts +const baseUrlError = ref('') +``` + +Replace the existing `save()` with: + +```ts +function save() { + if (selectedProvider.value === 'custom' && (!baseUrlInput.value.trim() || !modelInput.value.trim())) { + baseUrlError.value = 'Custom provider requires a Base URL and a model identifier.' + return + } + baseUrlError.value = '' + settingsStore.saveSettings({ + provider: selectedProvider.value, + model: modelInput.value, + apiKey: apiKeyInput.value, + baseUrl: baseUrlInput.value + }) + + savedNotice.value = true + setTimeout(() => { + savedNotice.value = false + }, 2500) +} +``` + +Also clear the error when the user switches provider or edits the inputs: in `selectProvider` add `baseUrlError.value = ''` as its first line, and add `@input="baseUrlError = ''"` to both the model input and the Base URL input elements. + +- [ ] **Step 4: Update the Base URL block for required-when-custom** + +Replace the existing `<!-- Custom Base URL (Optional) -->` block with: + +```html + <!-- Custom Base URL --> + <div class="space-y-1.5"> + <label class="text-xs text-slate-300 font-medium"> + Custom Base URL <span v-if="selectedProvider === 'custom'" class="text-rose-400">(required)</span> + <span v-else class="text-slate-500">(Optional)</span> + </label> + <input + v-model="baseUrlInput" + type="text" + :placeholder="selectedProvider === 'custom' ? 'https://openrouter.ai/api/v1' : 'https://api.openai.com/v1 or custom proxy'" + class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white focus:outline-none focus:border-blue-500 font-mono" + @input="baseUrlError = ''" + /> + <span v-if="selectedProvider === 'custom'" class="text-[11px] text-slate-500"> + Any OpenAI-compatible endpoint, e.g. https://openrouter.ai/api/v1, http://localhost:11434/v1 + </span> + </div> +``` + +- [ ] **Step 5: Show the inline error near the Save button** + +Inside the Save Button block, directly after the `savedNotice` span, add: + +```html + <span v-if="baseUrlError" class="text-xs text-rose-400"> + {{ baseUrlError }} + </span> +``` + +- [ ] **Step 6: Typecheck** + +Run: `npx vue-tsc --noEmit` +Expected: zero errors. + +- [ ] **Step 7: Manual UI verification (dev server)** + +If a dev server is already listening on port 3000, kill only its owning process: +```powershell +$conn = Get-NetTCPConnection -LocalPort 3000 -State Listen | Select-Object -First 1 +if ($conn) { Stop-Process -Id $conn.OwningProcess -Force } +``` +Then start it (log outside the repo), wait ~25 s (cold vue-tsc can exceed 20 s; retry once if the first boot fails): +```powershell +Start-Process cmd -WindowStyle Hidden -ArgumentList '/c npm run dev > C:\Users\Zack\Projects\dominic-dev.log 2>&1' +``` +Verify via `Get-Content C:\Users\Zack\Projects\dominic-dev.log -Tail 5` that it bound, then: +- `GET http://localhost:3000/` returns 200 and the Settings window opens in-app: five provider cards render including "🔌 Custom". +- Selecting Custom: Model Identifier empties, Base URL label shows "(required)" with helper text. +- Clicking Save with empty Base URL or model → red inline error, no "Saved" notice. +- Filling both and saving → "✓ Saved to local storage". + +- [ ] **Step 8: Commit** + +```powershell +git add app/features/settings/components/SettingsApp.vue +git commit -m "feat(settings): Custom provider card with required base URL and model validation" +``` + +--- + +### Task 3: Chat route — `custom` branch + +**Files:** +- Modify: `server/api/chat.post.ts` (API-key resolution block ~lines 26-41; provider construction chain ~lines 46-65) + +**Interfaces:** +- Consumes: `clientBaseUrl`, `requestedModel`, `apiKey` (route locals), `createOpenAI` from `@ai-sdk/openai` (already imported), `clientApiKey` from request body. +- Produces: `modelInstance` built as `createOpenAI({ apiKey, baseURL: clientBaseUrl })(requestedModel)` for `provider === 'custom'`; 400 errors `Custom provider requires a Base URL in Settings` / `Custom provider requires a model`; 401 path unchanged for missing key. + +- [ ] **Step 1: API key resolution — custom uses client key only** + +In the key-resolution block, extend the `else if` chain with custom (no env fallback): + +```ts + let apiKey = clientApiKey + if (!apiKey) { + if (provider === 'openai') apiKey = process.env.OPENAI_API_KEY + else if (provider === 'anthropic') apiKey = process.env.ANTHROPIC_API_KEY + else if (provider === 'google') apiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_GENERATIVE_AI_API_KEY + else if (provider === 'deepseek') apiKey = process.env.DEEPSEEK_API_KEY + } +``` + +No change to this block: for `custom`, `apiKey` stays whatever the client sent; the existing 401 check below it (`No API key provided for custom...`) already produces the right message. + +- [ ] **Step 2: Add the custom branch to provider construction** + +Insert immediately before the final `} else { // Default OpenAI` block: + +```ts + } else if (provider === 'custom') { + if (!clientBaseUrl) { + throw createError({ + statusCode: 400, + statusMessage: 'Custom provider requires a Base URL in Settings' + }) + } + if (!requestedModel) { + throw createError({ + statusCode: 400, + statusMessage: 'Custom provider requires a model' + }) + } + const custom = createOpenAI({ apiKey, baseURL: clientBaseUrl }) + modelInstance = custom(requestedModel) + } else { +``` + +(Leaving the Default OpenAI `else` block exactly as-is.) + +- [ ] **Step 3: Typecheck** + +Run: `npx vue-tsc --noEmit` +Expected: zero errors. + +- [ ] **Step 4: Runtime verification with curl** + +With the dev server running (see Task 2 Step 7 startup), from PowerShell: + +Missing base URL → expect 400: +```powershell +Invoke-WebRequest -Uri http://localhost:3000/api/chat -Method Post -ContentType 'application/json' -Body ('{"messages":[{"role":"user","content":"hi"}],"provider":"custom","apiKey":"k","model":"m"}') -SkipHttpErrorCheck | Select-Object StatusCode, Content +``` +Expected: `StatusCode 400`, content contains `Custom provider requires a Base URL in Settings`. + +Missing model → expect 400: +```powershell +Invoke-WebRequest -Uri http://localhost:3000/api/chat -Method Post -ContentType 'application/json' -Body ('{"messages":[{"role":"user","content":"hi"}],"provider":"custom","apiKey":"k","baseUrl":"http://localhost:11434/v1"}') -SkipHttpErrorCheck | Select-Object StatusCode, Content +``` +Expected: `StatusCode 400`, content contains `Custom provider requires a model`. + +Missing key → expect 401: +```powershell +Invoke-WebRequest -Uri http://localhost:3000/api/chat -Method Post -ContentType 'application/json' -Body ('{"messages":[{"role":"user","content":"hi"}],"provider":"custom","baseUrl":"http://localhost:11434/v1","model":"m"}') -SkipHttpErrorCheck | Select-Object StatusCode, Content +``` +Expected: `StatusCode 401`, content contains `No API key provided for custom`. + +Passthrough (bogus key against a live endpoint) → expect upstream error shape, NOT a DOMinic 4xx: +```powershell +Invoke-WebRequest -Uri http://localhost:3000/api/chat -Method Post -ContentType 'application/json' -Body ('{"messages":[{"role":"user","content":"hi"}],"provider":"custom","apiKey":"sk-bogus","baseUrl":"https://openrouter.ai/api/v1","model":"openai/gpt-4o-mini"}') -SkipHttpErrorCheck | Select-Object StatusCode, Content +``` +Expected: non-400/401 response (AI SDK upstream error flows back through the existing `e:` error path) — proves baseURL wiring reaches the endpoint. + +Regression: `provider:"openai"` with no key still returns 401 mentioning openai. + +- [ ] **Step 5: Commit** + +```powershell +git add server/api/chat.post.ts +git commit -m "feat(chat): support provider 'custom' with client-supplied OpenAI-compatible base URL" +``` + +--- + +### Task 4: Final verification + push + +**Files:** +- Modify: none (verification only) + +**Interfaces:** +- Consumes: all prior tasks. +- Produces: a pushed branch ready for PR. + +- [ ] **Step 1: Full typecheck** + +Run: `npx vue-tsc --noEmit` +Expected: zero errors. + +- [ ] **Step 2: Confirm existing providers untouched** + +Run: `git diff origin/main --stat` +Expected: exactly four files changed — `app/features/chat/types/chat.ts`, `app/features/settings/components/SettingsApp.vue`, `app/features/settings/stores/settings.ts`, `server/api/chat.post.ts` (plus this plan/spec docs if included in the branch). + +- [ ] **Step 3: Push** + +```powershell +git push -u origin feat/custom-openai-provider +``` + +- [ ] **Step 4: Open PR** + +```powershell +$env:Path += ";C:\Program Files\GitHub CLI" +gh pr create --title "feat: custom OpenAI-compatible provider (base URL + model)" --body "Closes the custom-provider gap: fifth Settings provider card 'Custom' for any OpenAI-compatible endpoint (OpenRouter, Ollama, LM Studio, gateways). Requires base URL + model client-side; /api/chat builds createOpenAI({ baseURL: clientBaseUrl }) — client-key only, lenient URL handling. Spec: docs/superpowers/specs/2026-09-12-custom-openai-compatible-provider-design.md" +``` \ No newline at end of file diff --git a/vault/Specs & Plans/Plan - Hackathon Finish.md b/vault/Specs & Plans/Plan - Hackathon Finish.md new file mode 100644 index 0000000..ed6c61e --- /dev/null +++ b/vault/Specs & Plans/Plan - Hackathon Finish.md @@ -0,0 +1,197 @@ +--- +type: plan +title: Plan - Hackathon Finish +tags: [plan, hackathon, finish] +source: docs/superpowers/plans/2026-09-12-hackathon-finish.md +date: 2026-10-02 +ingested: 2026-10-02 +repo_head: 0fc772d +--- +> [!abstract] Michael's finish plan (PR #37) +> The team's own timeline working back from the 16:00 portal close; the live status view is on [[DOMinic Home]]. + +# Hackathon Finish Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use +> superpowers:subagent-driven-development (recommended) or +> superpowers:executing-plans to implement this plan task-by-task. +> Steps use checkbox (`- [ ]`) syntax for tracking. Coordinate through +> `/coordinate` per `docs/agents/coordination-best-practices-2.md`. + +**Goal:** Ship a working DOMinic core loop on `main`, film it, and +submit all five hackathon gates before the portal closes at 16:00 ET on +2026-09-12. + +**Architecture:** Everything the demo needs already exists as slices on +`main` — chat and tools (#20), runtime runner and error boundary (#25), +VFS, registry and Settings (#27). What remains is the shell (PR #24), +the wiring (#30), one rehearsal (#32), and the submission package +(#8, #9, #10). + +**Tech Stack:** Nuxt 4, Pinia, Vercel AI SDK v4, vue3-sfc-loader, +localStorage VFS; HyperFrames for the video; GitHub Issues and `gh` for +coordination. + +**Spec:** `docs/agents/demo-path.md` (beats and cut order), +`docs/agents/rubric.md` (scoring and submission gates). + +**Closing status, 16:40 ET:** Tasks 1–4 done (loop wired in #35/#63, +rehearsed 15:08, prompt frozen and fixture shipped in #40, launcher +actions in #44, duplicate #31 closed). Form submitted by Zack before +16:30; video with Brandon (#9). v3 playbook merged (#68). Remaining: +video link and social post, #69 script enforcement, NEXT.md items. + +## Global Constraints + +- Sprint minimum from `coordination-best-practices-2.md`: one active + claim, Files globs on every CLAIM, declare-then-proceed on seams, + ping at 20 min and release at 30, comments on state change only. +- Tiny PRs into `main`; the owner merges. Never push a follow-up + commit to an open PR — open a new one. +- Anything the camera never sees earns zero points today. Cut order: + Hook, Ask, Materialize, Persist, Close are non-negotiable; Update, + Recover, Control, Any model are stretch (`demo-path.md`). +- Markdown wraps at 80 columns; `npm run lint:md` gates every commit. +- Portal window 15:30–16:00 ET; the video is submitted as a public + link. + +## Timeline, working back from 16:00 + +| By | Milestone | Task | +| --- | --- | --- | +| 14:55 ✅ | PR #24 (shell) and PR #29 (deps, model IDs) merged | 1 | +| 16:05 ✅ | Core loop runs end to end on `main` (#35; smoke fixes #63) | 2 | +| 15:08 ✅ / ⏳ | Prompt rehearsed and frozen (#40, merged 16:31); footage with Brandon | 3, 5 | +| ⏳ | Video — produced by Brandon (#9 handed off 16:29) | 5 | +| 16:2x ✅ | Form submitted by Zack; video link and social post to follow | 6 | + +--- + +### Task 1: Merge the shell and the dependency fix + +**Owner:** Zack (author); one more reviewer — Brandon or Michael. +**Issues:** #13, #22. **PRs:** #24, #29. + +**Files:** `app/features/os/**`, `package.json`, `package-lock.json`, +`server/api/chat.post.ts`. + +**Interfaces:** + +- Consumes: nothing. +- Produces: taskbar, window manager, drag/focus/min/max/close (ADR 0004 + golden path); runtime deps for the chat slice and current default + model IDs (ADR 0005, Q2.1). + +- [x] **Step 1:** Second approval on #24 — check it against the ADR + 0004 golden-path list, not a deep review. +- [x] **Step 2:** Second approval on #29. +- [x] **Step 3:** Zack merges both; `main` CI green. + +### Task 2: Wire the core loop (#30) + +**Owner:** @ImNewToC0de, paired with Charles (author of #20, #25, #27). + +**Files:** `app/app.vue`, `app/features/os/components/**`, +`app/features/chat/**`, `app/features/apps/**`. + +**Interfaces:** + +- Consumes: `install_app` and `update_app` tool results (ADR 0010), + `DynamicAppRunner` (ADR 0006), VFS and registry (ADR 0007), shell + windows and taskbar (ADR 0004). +- Produces: the demo golden path — ask → install_app → VFS write → + registry → window opens → survives reload → update_app changes it in + place. + +- [x] **Step 1:** Mount the shell in `app.vue`; Agent Chat as a + floating window (Q1.1); Settings pre-installed. +- [x] **Step 2:** `install_app` writes `apps/<id>/index.vue`, persists + its metadata through the app registry, and opens its window. +- [x] **Step 3:** Boot hydration restores the persisted app registry into + the launcher and taskbar (ADR 0007). +- [x] **Step 4:** `update_app` replaces the source and re-renders the + open window (Q2.2 context injection). +- [x] **Step 5:** The error boundary's "Ask Agent to Fix" posts the + trace back into the chat (ADR 0006). +- [x] **Step 6:** Small PR per step where possible; merge to `main`; + boot `main` and run the loop once — that is the integration + checkpoint. + +### Task 3: Rehearse and freeze the demo prompt (#32) + +**Owner:** integration (rehearsal); ws:docs (record). + +**Files:** `docs/agents/demo-path.md`. + +- [x] **Step 1:** Choose the core-loop app — pomodoro / focus timer + unless it misbehaves; kanban is the alternate (Q5.1, `demo-path.md`). +- [x] **Step 2:** Run the exact prompt three times on `main`; it must + install, open, survive a reload, and take one `update_app` change. +- [x] **Step 3:** Post the frozen prompt and app name on #32; ws:docs + commits them into `demo-path.md` ("Chosen app", "Exact prompt") and + never edits them again. + +### Task 4: Stretch — only if Task 2 is done by 15:20 + +**Owner:** whoever is free. **Issues:** #33, #34, PR #31. + +- [x] **Step 1:** #33 — ship the pre-generated pomodoro fixture as the + Persist-beat fallback (it was agent-authored; the narration says so). +- [x] **Step 2:** #34 — Uninstall and View Source in the launcher + (Control beat, criterion 4). +- [x] **Step 3:** Close PR #31 (duplicates merged #27) or fold its + deltas into #30. + +### Task 5: Record, wrap, render, publish (#9) + +**Owner:** ws:docs (Michael and Claude); a dev records. + +**Files:** `~/DFIM/DOMinic-video/dominic-demo/` (outside the repo). + +- [ ] **Step 1:** Record a 1920×1080 browser capture of the frozen + loop, up to 100 s: ask → install → use → reload → update, then + recover → control → settings if they exist. Two takes. AirDrop + `demo.mp4` to Michael. +- [ ] **Step 2:** Save it as `assets/demo.mp4`; set the caption + `data-start` and `data-duration` values in `index.html` to the + footage. +- [ ] **Step 3:** Gate and render: + + ```bash + npm run check + npx hyperframes render --video-frame-format png -o out/dominic-demo.mp4 + ``` + +- [ ] **Step 4:** Upload to YouTube as Public from the agreed account; + confirm it plays logged out; paste the link into the submission + draft. + +### Task 6: Submit all five gates (#8, #10) + +**Owner:** ws:docs. + +**Files:** `README.md`; the submission draft held by Michael +(`~/DFIM/DOMinic-research/submission/2026-09-12-submission-drafts.md`). + +- [x] **Step 1:** Merge PR #28 (README quickstart) — merged 14:54. +- [x] **Step 2:** Superseded — `docs/JUDGES.md` (#53, README first row) + carries what runs and what is deferred; no README sentence needed. +- [ ] **Step 3:** Fill the draft's blanks: demo-app name, the "what + runs" line, the video link, confirmed sponsor handles. +- [ ] **Step 4:** Publish the social post with sponsor tags. +- [x] **Step 5:** Form submitted by @xcjs (title, description, repo) + before 16:30; video link and post follow via #9. #8 closed; #10 stays + open until the link is in. + +### Task 7: After 16:00 — operations + +**Owner:** ws:docs; Brandon for the skill. + +- [x] **Step 1:** Superseded by v3 (#68), which sets the post-sprint + rules from the retrospective. +- [x] **Step 2:** v3 merged in #68 (retrospective-based); #19 kept open + for late feedback; #6 closed with the skill. +- [ ] **Step 3:** Brandon: script defaults proposed on #6 (two-stage + lease, one claim, two rounds, ping before release); fix the + SKILL.md and protocol.md cap contradiction and the comment matcher. +- [ ] **Step 4:** Select `NEXT.md` items for ADRs 0011 and up. diff --git a/vault/Specs & Plans/Spec - ADR Scaffold Design.md b/vault/Specs & Plans/Spec - ADR Scaffold Design.md new file mode 100644 index 0000000..07b9cbf --- /dev/null +++ b/vault/Specs & Plans/Spec - ADR Scaffold Design.md @@ -0,0 +1,73 @@ +--- +type: spec +title: Spec - ADR Scaffold Design +tags: [spec, superpowers, adr] +source: docs/superpowers/specs/2026-09-12-adr-scaffold-design.md +date: 2026-10-02 +ingested: 2026-10-02 +--- +> [!abstract] Design spec +> Written before PR #2; the ADRs' Decision Outcome sections were later restructured into POC / Future / Open Questions blocks. The decisions themselves are unchanged. + +# Design: MADR ADR Scaffold in `docs/adrs/` + +Date: 2026-09-12 + +## Goal + +Set up an Architecture Decision Record (ADR) practice for the DOMinic +project using the MADR (Markdown Any Decision Records) 4.0 format, in +English. The scaffold is documentation-only: no build tooling or config +files. + +## Deliverables + +Three markdown files in `docs/adrs/`: + +### 1. `0000-record-architecture-decisions.md` + +Meta-ADR documenting the decision to use MADR for recording architecture decisions. + +- Format: MADR 4.0 structure. +- Title: "Record architecture decisions". +- Status: `accepted`, dated 2026-09-12. +- Content: explains that decisions are recorded in `docs/adrs/`, numbered + sequentially with zero-padding, named in kebab-case, and that MADR was + chosen for being lightweight and tool-friendly. Includes considered + alternatives (Nygard classic format, no ADR practice) and the standard + MADR outcome sections. + +### 2. `template.md` + +The standard MADR 4.0 template for future ADRs: + +- `# {title}` with placeholder. +- Metadata table: ADR number, status, date, deciders. +- Sections: Technical Story, Context and Problem Statement, Decision + Drivers, Considered Options (with ✅/❌ pros-cons table), Decision + Outcome, Pros and Cons, Links. + +### 3. `README.md` + +Conventions doc covering: + +- What an ADR is and why the project uses them. +- Numbering: next sequential zero-padded number (0001, 0002, …). +- File naming: kebab-case (`NNNN-short-title.md`). +- Status lifecycle: proposed → accepted → superseded/deprecated; + superseded ADRs are kept, not deleted. +- Workflow: copy `template.md`, fill it in, commit. + +## Non-Goals + +- No `adr-tools`, markdownlint, or CI integration. +- No ADRs beyond 0000. +- Addendum (2026-09-12): markdownlint, husky pre-commit hook, and CI were + subsequently added by user direction. + +## Verification + +- The three files exist under `docs/adrs/`. +- 0000 ADR and template render correctly as markdown (valid headers/tables). +- README accurately describes the numbering and status conventions used + by the template and 0000 ADR. diff --git a/vault/Specs & Plans/Spec - ADR Series Design.md b/vault/Specs & Plans/Spec - ADR Series Design.md new file mode 100644 index 0000000..2d36d84 --- /dev/null +++ b/vault/Specs & Plans/Spec - ADR Series Design.md @@ -0,0 +1,238 @@ +--- +type: spec +title: Spec - ADR Series Design +tags: [spec, superpowers, adr] +source: docs/superpowers/specs/2026-09-12-adr-series-design.md +date: 2026-10-02 +ingested: 2026-10-02 +--- +> [!abstract] Design spec +> Written before PR #2; the ADRs' Decision Outcome sections were later restructured into POC / Future / Open Questions blocks. The decisions themselves are unchanged. + +# Design: DOMinic ADR Series (ADRs 0001–0009) + +Date: 2026-09-12 + +## Goal + +Author the initial series of nine Architecture Decision Records for the +DOMinic browser OS, one per architectural pillar, using the repo's +committed MADR 4.0 template plus OKF v0.2 YAML frontmatter. Documentation +only: no application code is written in this effort. + +## Background + +DOMinic is an agent-driven browser OS written in Nuxt/TypeScript. It +presents a desktop-like shell (taskbar, windows, apps) designed +mobile-first, and its agent chat can author Vue component applications +at runtime. OS alterations persist in a virtual filesystem over local +storage. Runtime NPM dependencies load into the frontend from a CDN +after automated security vetting. External services are reached +CORS-first, falling back to a transparent server proxy that presents a +latest-stable Google Chrome User-Agent. + +The repo already commits (ADR 0000) to MADR 4.0 ADRs in `docs/adrs/`, +and `docs/agents/use-okf.md` mandates Open Knowledge Format v0.2 for +agent knowledge. This design reconciles both. + +## Deliverables + +Nine files in `docs/adrs/`, numbered sequentially with zero-padded +numbers and kebab-case names, per ADR 0000. All are `accepted` on +2026-09-12, decider Zack. + +### Common conventions (all nine ADRs) + +- MADR 4.0 structure exactly as `docs/adrs/template.md`: metadata + table, Technical Story, Context and Problem Statement, Decision + Drivers, Considered Options, Decision Outcome, Confirmation, + Pros and Cons of the Options, Links. +- OKF v0.2 YAML frontmatter block: + + ```yaml + --- + type: Architecture Decision Record + title: <display title> + description: <one-line summary> + status: accepted + tags: [architecture, <pillar tag>] + generated: { by: human:zack, at: <ISO-8601 UTC instant> } + verified: { by: human:zack, at: <ISO-8601 UTC instant> } + --- + ``` + +- Frontmatter `status` mirrors the metadata table status; the MADR + table remains the status authority in the body. +- Status lifecycle: `proposed` → `accepted` → `superseded`/`deprecated`. + Superseded ADRs are kept, linked via the Links section, with + frontmatter `status: deprecated`. +- Cross-links between related ADRs in the Links section (Refines / + Related to), plus links to ADR 0000 and the template where apt. +- Markdown wraps at 80 columns per `.markdownlint.jsonc` (MD013); + must pass `npm run lint:md`. +- Deciders table row: `| NNNN | accepted | 2026-09-12 | Zack |`. + +### ADR list + +| ADR | Title | Pillar tag | +| ---- | ------------------------------------------------------------ | ----------------- | +| 0001 | Feature slices with domain-driven organization | architecture | +| 0002 | Strict TypeScript and lint toolchain | tooling | +| 0003 | Pinia per-domain stores | architecture | +| 0004 | DOMinic OS shell and taskbar | platform | +| 0005 | Agent chat via Vercel AI SDK with server-side provider proxy | agent | +| 0006 | Agent-authored runtime-compiled components | agent | +| 0007 | Virtual filesystem with pluggable storage drivers | storage | +| 0008 | Runtime NPM dependency loading via esm.sh with vetting | agent, storage | +| 0009 | CORS-first networking with Chrome-masking proxy fallback | platform | + +### 0001 — Feature slices with domain-driven organization + +- Story/Context: Nuxt default layout scatters code by layer; + DOMinic needs DDD-style bounded contexts that map to navigable + directories and enforceable ownership. +- Drivers: slice-local cohesion; enforceable dependency direction; + agent navigability (small focused directories); Nuxt compatibility. +- Considered: slice-local vertical directories; classic layered dirs; + hybrid layer-with-subdirs. +- Outcome: **slice-local vertical directories**. Each slice lives in + `app/features/<slice>/` owning its components, composables, stores, + types, and tests. A thin `app/shared/` kernel holds cross-slice + contracts (types, interfaces, utilities). Slices may import from + shared, never from other slices; cross-slice communication flows + through shared contracts and stores. Bounded contexts map 1:1 to + slices. + +### 0002 — Strict TypeScript and lint toolchain + +- Story/Context: agent-driven code generation needs maximal compiler + and linter safety nets. +- Drivers: catch errors at compile time; consistent style; CI + enforcement; Vue SFC awareness. +- Considered: ESLint + vue-tsc; Oxlint + vue-tsc; Biome + vue-tsc. +- Outcome: **TypeScript `strict` + `noUncheckedIndexedAccess`**; + ESLint flat config with type-checked typescript-eslint and + eslint-plugin-vue; `vue-tsc --noEmit` in CI and pre-commit via + lint-staged/husky. + +### 0003 — Pinia per-domain stores + +- Story/Context: OS state (windows, settings, registry, chat) must be + observable and modular. +- Drivers: Vue-idiomatic; per-slice modularity; devtools; SSR-safe. +- Considered: Pinia per-domain stores; composables only; Pinia plus + event bus. +- Outcome: **Pinia, one setup-style store per slice**. Stores are the + only cross-slice state channel. No global event bus initially; + revisit if OS-wide signal fan-out demands it. + +### 0004 — DOMinic OS shell and taskbar + +- Story/Context: DOMinic is a desktop-like OS in a browser, mobile-first. +- Drivers: desktop metaphor familiarity; mobile scaling; app/window + lifecycle management; first-party apps must follow the same contract + as agent-authored apps. +- Considered: full windowing shell with taskbar; single-activity + mobile-style shell; hybrid. +- Outcome: **desktop-metaphor shell in an `os` slice**: window + manager, taskbar, app registry, and built-in **Settings** app as the + first first-party app on the base app contract (ADR 0006). Mobile + first: on narrow viewports the taskbar degrades to bottom-nav-style + sheet with stacked/maximized windows. Shell chrome uses modern + transparency/backdrop-blur effects with opaque fallbacks. Settings + is the global place to configure OS preferences at any time. + +### 0005 — Agent chat via Vercel AI SDK with server-side provider proxy + +- Story/Context: chat is the primary agent surface; must support + virtually any LLM provider. +- Drivers: provider-agnostic; streaming; keys never at rest on + server; no-provider onboarding. +- Considered: server proxy with server env keys; BYOK direct from + browser; hybrid. +- Outcome: **Vercel AI SDK (`ai` + provider packages)** consumed + through a Nuxt server route `/api/chat` that streams responses. + Users select a provider via the Settings app when none is + configured (chat surfaces the selection flow). Provider API keys + are stored browser-side in the VFS and sent per-request to the + server route, which uses them for that request only. + +### 0006 — Agent-authored runtime-compiled components + +- Story/Context: the agent authors Vue SFC applications from chat + requests; they must run without a per-component build step. +- Drivers: no build infrastructure; live install/preview loop; + reuse of Vue ecosystem; registration as apps and widgets. +- Considered: runtime-compiled SFC; sandboxed iframe compile; + prebuilt bundles. +- Outcome: **runtime-compiled SFC via vue3-sfc-loader**, in-process + (registered like any Vue component; iframe isolation documented as + a future escape hatch). A `DominicApp` base component contract: + app metadata, launch context props, standard lifecycle hooks for + install/uninstall. Component registry (in the `os` slice) registers + compiled components as global components, apps, or widgets; source + persists in the VFS (ADR 0007). + +### 0007 — Virtual filesystem with pluggable storage drivers + +- Story/Context: OS alterations persist locally; localStorage alone + is too small (~5 MB) for component libraries. +- Drivers: local-first persistence; variable payload sizes; async vs + sync ergonomics; testability. +- Considered: localStorage JSON tree; IndexedDB-backed only; + localStorage now, swap later. +- Outcome: **single `VirtualFileSystem` interface + driver registry**. + `localStorage` driver (small, synchronous) and IndexedDB driver + (large payloads, async-backed) ship together; per-path driver + mapping is stored in VFS metadata, so callers use "either depending + on the current need". VFS underpins component source, settings, + provider keys, and the dependency cache. + +### 0008 — Runtime NPM dependency loading via esm.sh with vetting + +- Story/Context: agent-authored components need arbitrary NPM + packages at runtime, securely. +- Drivers: full npm ecosystem reach; no server build step; security + review before execution; offline resilience via caching. +- Considered: esm.sh; unpkg/jsdelivr + runtime resolver; server + pre-bundling. +- Outcome: **esm.sh as the ESM CDN**; every dependency passes an + automated vetting gate before load: npm registry/advisory data + (audit), weekly downloads, license check, publish age, maintainer + signals; vetted package metadata and cached builds persist in the + VFS. Recorded caveats: native/binary addons cannot run in browser; + heavy Node built-in usage depends on esm.sh polyfills; CDN + availability is a dependency mitigated by the vetting gate and VFS + caching; first request pays build latency. + +### 0009 — CORS-first networking with Chrome-masking proxy fallback + +- Story/Context: components and the agent must reach external + services that may lack CORS. +- Drivers: prefer direct browser requests; bypass CORS gaps + transparently; avoid anti-bot rejection; single server surface. +- Considered: single generic proxy route; domain allowlist; split by + service. +- Outcome: **CORS-first direct `fetch`**, with transparent fallback + to a single generic `/api/proxy` Nuxt server route that rewrites + the User-Agent to the latest stable Google Chrome, strips + identifying headers (cookies, referer, origin), and streams the + response back. Client-side helper composable decides direct vs + proxy per request. + +## Non-Goals + +- No application code, Nuxt scaffold, or CI workflows in this effort. +- No OKF frontmatter retrofit for existing docs beyond the new ADRs. +- No implementation plans for the subsystems themselves (each pillar + gets its own future spec/plan when built). + +## Verification + +- Nine ADR files exist in `docs/adrs/`, named `0001-…` through + `0009-…`. +- Each file: valid OKF frontmatter (parseable YAML, required `type`, + `status`, `generated`, `verified`), MADR sections matching the + template, 80-column wrap. +- Cross-links resolve (no dead ADR links). +- `npm run lint:md` passes with zero errors. diff --git a/vault/Specs & Plans/Spec - Custom OpenAI Provider.md b/vault/Specs & Plans/Spec - Custom OpenAI Provider.md new file mode 100644 index 0000000..3870a83 --- /dev/null +++ b/vault/Specs & Plans/Spec - Custom OpenAI Provider.md @@ -0,0 +1,110 @@ +--- +type: spec +title: Spec - Custom OpenAI Provider +tags: [spec, superpowers, provider] +source: docs/superpowers/specs/2026-09-12-custom-openai-compatible-provider-design.md +date: 2026-10-02 +ingested: 2026-10-02 +repo_head: 0fc772d +--- +> [!abstract] Shipped feature +> The custom OpenAI-compatible provider this describes shipped in PR #70 (base URL + model, no key at rest). See [[ADR 0005 - Agent chat via Vercel AI SDK with server-side provider proxy|ADR 0005]] and [[Architecture Overview]]. + +# Design: Custom OpenAI-Compatible Provider + +**Date:** 2026-09-12 +**Status:** Approved +**Workstream:** ws:agent-chat / ws:integration (settings) + +## Problem + +DOMinic's Settings screen hardcodes four providers: OpenAI, Anthropic, Google, DeepSeek. Any OpenAI-compatible endpoint that is not one of these — OpenRouter, Azure OpenAI gateways, an internal proxy, Ollama, LM Studio, vLLM — cannot be used as a first-class provider. Users would have to abuse the "OpenAI" slot and hope the model field carries the gateway-specific name. + +The plumbing mostly exists on main already: + +- `useSettingsStore` persists `baseUrl` in `system/settings.json` (VFS) and hydrates it back. +- `useAgentChat` sends `baseUrl` in the chat request body. +- `/api/chat` accepts `clientBaseUrl` and uses it when constructing the OpenAI-style provider for the `deepseek` branch and the default `openai` branch. + +What is missing is a provider identity for "any OpenAI-compatible endpoint" and the UI/route handling to select and validate it. + +## Decisions (from brainstorming Q&A) + +| Question | Decision | +| --- | --- | +| Primary scenario | General feature, post-hackathon quality — not tied to one endpoint | +| Settings UX | Add a fifth "Custom (OpenAI-compatible)" provider card; existing four untouched | +| Route handling | New `'custom'` provider branch in `/api/chat` | +| Server env fallback | None — base URL comes from the client only | +| Validation | Require non-empty `baseUrl` and `model`; lenient URL format (no http(s) policing, no trailing-slash rules) | + +## Design + +### 1. Type layer + +Extend the provider union in the two places it is literally defined: + +- `app/features/settings/stores/settings.ts:4` + ```ts + export type LlmProvider = 'openai' | 'anthropic' | 'google' | 'deepseek' | 'custom' + ``` +- `app/features/chat/types/chat.ts:20` — same union. + +No store shape changes: `SettingsState` already has `provider`, `model`, `apiKey`, `baseUrl`, and `hasHydrated`. + +### 2. Settings UI — `app/features/settings/components/SettingsApp.vue` + +- Add a fifth entry to the local `providers` array: + ```ts + { id: 'custom', name: 'Custom', icon: '🔌', defaultModel: '' } + ``` +- `selectProvider('custom')` sets the model input to `''` (the user must supply a model name; no silent default). +- The Base URL input already exists. When `custom` is selected it becomes required, with helper text: "OpenAI-compatible endpoint, e.g. https://openrouter.ai/api/v1". Existing four providers keep current behavior (base URL remains optional, used only where the route honors it today). +- `save()` validation: if `selectedProvider === 'custom'` and (`baseUrlInput` is empty **or** `modelInput` is empty) → show an inline red error and do not save. +- The card grid, hydration, and save flow are otherwise unchanged. + +### 3. Chat route — `server/api/chat.post.ts` + +Add a `custom` branch ahead of the default OpenAI branch: + +1. API key: client key only — no env fallback. The existing 401 check ("No API key provided for custom…") catches a missing key with a clear message. +2. Base URL: must be a non-empty `clientBaseUrl`, else 400: `"Custom provider requires a Base URL in Settings"`. +3. Model: must be a non-empty `requestedModel`, else 400: `"Custom provider requires a model"`. +4. Construct the model: + ```ts + const custom = createOpenAI({ apiKey, baseURL: clientBaseUrl }) + modelInstance = custom(requestedModel) + ``` + +Lenient by design: no URL-format policing (Ollama uses `http://localhost:11434/v1`, LM Studio `http://localhost:1234/v1`, gateways vary), no trailing-slash normalization, no connectivity probing. + +The chat composable (`useAgentChat`) already forwards `provider/model/apiKey/baseUrl` from `getProviderConfig` — zero client chat changes. + +### 4. Error handling + +| Case | Result | +| --- | --- | +| Custom selected, no baseUrl in Settings | Save blocked client-side with inline error; route 400 if bypassed | +| Custom selected, no model | Same | +| No API key | Existing 401 with provider name | +| Upstream endpoint rejects key/model/path | AI SDK error propagates as today (`e:` data-stream error prefix) | + +### 5. Testing / verification + +- `npx vue-tsc --noEmit` — clean. +- Dev server: select Custom, attempt save with missing URL or model → blocked with inline error. +- `curl -X POST /api/chat` with `{ provider: 'custom', apiKey: 'k', model: 'm' }` and no baseUrl → expect 400. +- Same body with `baseUrl` pointed at a real compatible endpoint and a bogus key → expect upstream error shape (proves request passthrough and correct baseURL wiring). +- Regression: existing four providers unchanged (openai default still honors `OPENAI_BASE_URL` env fallback; deepseek still honors `DEEPSEEK_API_URL`). + +### Scope notes + +- Strictly additive: the demo freeze (#32) is closed and the existing four provider paths are untouched. +- Follow-ups intentionally out of scope: per-provider base-URL fields for all providers, model-list fetch from the endpoint, connectivity test button. + +## Files touched + +- `app/features/settings/stores/settings.ts` (union) +- `app/features/chat/types/chat.ts` (union) +- `app/features/settings/components/SettingsApp.vue` (card + validation + helper text) +- `server/api/chat.post.ts` (custom branch) \ No newline at end of file diff --git a/vault/_templates/milestone.md b/vault/_templates/milestone.md new file mode 100644 index 0000000..1995ea9 --- /dev/null +++ b/vault/_templates/milestone.md @@ -0,0 +1,15 @@ +--- +type: milestone +title: Milestone {{step}} - {{title}} +step: {{step}} +phase: {{phase}} +pr: {{pr}} +--- + +# Milestone {{step}} - {{title}} + +## What shipped + +## Roadmap context + +## Files diff --git a/vault/_templates/session.md b/vault/_templates/session.md new file mode 100644 index 0000000..eb3b1d2 --- /dev/null +++ b/vault/_templates/session.md @@ -0,0 +1,18 @@ +--- +type: session +title: Session {{stamp}} {{agent}} +step: {{step}} +branch: {{branch}} +--- + +# Session {{stamp}} - {{agent}} + +## Doing + +## State + +## Summary + +## Next action + +## Resume