# Ai Resume — Build Spec > Build spec for Paperclip. Follow the phases in section 11 in order. Do not skip a > phase's Definition of Done. When something is unclear, ask Sam — do not guess. **Project:** `Resume Ai` (Paperclip project `aeea3686-b9fb-4e20-aaaa-4d970f7aeb64`) **Repo:** `sam/ai-resume` — this working copy. One repo, one product. **Planning folder (read-only reference, do not edit):** `/home/sam/paseo/projects/resume_ai_system/` --- ## 1. Purpose and outcome A public portfolio site for Sam Rolfe, aimed at three roles: **AI / LLM Engineer**, **Platform / DevOps Engineer**, **Edge / IoT Engineer**. It must present 25 years of delivery, a homelab, an edge fleet and an AI estate in a form a hiring manager can judge in 90 seconds and an engineer can verify in 30 minutes. **The single success statement:** > A recruiter opens the link, understands within 90 seconds that Sam builds AI systems, the platforms under them and the hardware at the edge — and can drill from any claim to the code, diagram or device that proves it. Separately, they can ask a question in plain English and get a short, honest, cited answer. If those two things work, the project succeeded. Everything else is polish. --- ## 2. The two-mode rule — non-negotiable The site has **two delivery modes over one body of content**. Both are mandatory. **Mode A — prebuilt static content.** Conventional navigation into fixed, pre-rendered pages. No LLM call, no embedding lookup. This is the fallback and the first impression. It must work with every AI service stopped. **Mode B — AI conversational retrieval.** A prompt box; answers generated from the corpus, streamed; the AI can push artefacts (code, diagrams, images) into the presentation panel. **One source of truth, two renderers.** `content/` (Markdown + front-matter + assets) is authoritative. A build step emits both (a) the static pages and (b) the chunked, metadata-tagged corpus. Content is never authored twice, and the two modes can never drift. **Acceptance test A1 *(hard gate)*:** stop Qdrant, OmniRoute and the query service. Every page must still render correctly. Any change that breaks this is rejected. --- ## 3. Scope **In scope** - The static site: navigation, six top-level areas, drill-down pages, print/accessible. - The content pipeline: `content/` → static render + corpus. - The AI overlay: query graph, retrieval, streaming, citations, guardrails, cost caps. - Public deployment behind the existing Caddy edge. - Honest labelling of in-progress and abandoned work. **Out of scope** - Changing, stopping or "cleaning up" any other system on any host. See §4. - Family/household systems, the Hub, Paperclip itself, the model gateway's internals. - A CMS, admin editor, user accounts, comments, analytics dashboards. - Native apps, voice *output* beyond the optional last phase. --- ## 4. Hard constraints 1. **Stack is frozen.** Go + `templ` + HTMX + SQLite; one hand-written TypeScript island; Qdrant; Prefect; LangGraph; OmniRoute. React, Vite and `node_modules` do not enter this repo. Reopening a frozen decision requires Sam's written approval, not a judgement call. 2. **Do not touch other systems.** Nothing outside this repo and the deploy path. No stopping containers, no editing other projects' compose files, no changes to Caddy beyond the single block specified in Phase 3, no `nixos-rebuild`. If a task appears to require it, stop and ask. 3. **Memory ceiling.** New footprint ≤ **1.0 GiB** and ≤ **4 containers**. Every new service sets `mem_limit` **and** `memswap_limit` to the same value, so it cannot consume the host's swap. 4. **Secrets** come from `~/.config/environment.d/` only. Never in the repo, never in logs, never echoed. A secret-scanning pre-commit hook is mandatory. 5. **All model calls go through OmniRoute.** No direct provider SDK or HTTP call anywhere in this codebase. 6. **No LLM in the path of a consequential action.** Deployments are executed by a script, not decided by a model. 7. **The public AI has no tools.** No shell, no filesystem, no network fetch, no write path, no memory between visitors. This is architectural, not a prompt instruction. --- ## 5. Where it runs | Host | Role | Notes | |---|---|---| | `.27` | Build / dev | Source, builds, agent work | | `.13` | Runtime | Go app, Qdrant, Prefect worker. **2.3 GiB free, 8 GiB swap in use — respect the ceiling** | | `.35` | Public ingress | Caddy terminates 443 for `*.lab.audasmedia.com.au` | | Item | Value | |---|---| | App port | **8096** (verify free; 8095 is taken by `offsite.lab`) | | Qdrant | 6333 / 6334, bound to `127.0.0.1` | | Public hostname | `resume.lab.audasmedia.com.au` (wildcard DNS already resolves) | | Release path | `/home/sam/deployment/resume/releases/` + `current` symlink | | Edge proxy | Caddy on `.35` — **Sam applies this change, not the agent** | Bind every new service to `127.0.0.1`. Caddy is the only public listener. --- ## 6. Repository rules - `main` is always deployable. Work on branches; merge when the phase's DoD passes. - The repo **is** the content source of truth. `content/` lives here. - No generated artefacts committed (build output, `node_modules`, `.venv`, binaries). - Every phase ends with a commit that states which DoD items it satisfies. - The repo stays **private until Phase 3 passes**, then visibility is Sam's decision. --- ## 7. Content conventions Every content file carries front-matter. Both renderers read it. ```yaml id: edge-snapcast area: ai | platform | edge | products | history | about level: 1 | 2 | 3 title: "Distributed Audio" claim: "Eight synchronised endpoints with music ducking for voice." confidence: delivered | running | in-progress | abandoned | historic period: "2023–" tech: [Snapcast, Mopidy, Spotify, Raspberry Pi] metrics: [{label: "endpoints", value: 8}] artefacts: [{type: repo|image|video|doc, label: "...", url: "..."}] public: true last_verified: 2026-10-20 tags: [audio, snapcast, voice] ``` **`confidence` is displayed on the page.** `running` and `delivered` are claims; `in-progress` and `abandoned` are stated as such. Never promote a value to make a page read better — that is a defect, not a style choice. **Framing rule:** headings describe capabilities in corporate language. Product names are supporting evidence, never the heading. `ESP32` → *distributed edge computing under power and latency constraints*. --- ## 8. Quality bar - Renders correctly with JavaScript disabled, except the AI console. - Lighthouse ≥ 95 performance and ≥ 95 accessibility, mobile and desktop. - Every page printable. - Markdown and code highlighting rendered server-side (`goldmark`, `chroma`) — not in the browser. - No page requires an AI call to be useful. - Server-rendered HTML validated; no unescaped user input reaches a template. --- ## 9. What is explicitly out of scope for the agent - Deciding what is true about Sam's career. If the evidence is not in `reference/` or `content/`, ask. - Naming clients that Sam has not approved. - Publishing personal details: family, address, phone. - Changing the stack, the ports, the host allocation or the public hostname. - Deleting anything outside this repo. --- ## 10. When something is unclear Ask Sam. Do not guess, do not invent a plausible answer, do not silently choose an alternative. A blocked task that gets answered in a minute is cheaper than a completed task built on an assumption. Escalate in this order: (1) the phase document, (2) `reference/`, (3) Sam. --- ## 11. Phases Each phase is one Paperclip issue. Each has a Definition of Done that must be demonstrated with an artifact — command output, screenshot, measurement or URL — not asserted. | # | Phase | Doc | Depends on | |---|---|---|---| | 1 | **Content source of truth** — schema, validator, `content/` populated for all six areas | `docs/phase-1-content-source.md` | — | | 2 | **Mode A static site** — Go + templ + HTMX, nav, stage panel, drill-downs | `docs/phase-2-static-site.md` | 1 | | 3 | **Public deployment** — `deploy.sh`, releases, Caddy block, live URL | `docs/phase-3-deploy.md` | 2 | | 4 | **Corpus and retrieval** — chunk, tag, embed, upsert Qdrant; freshness audit | `docs/phase-4-corpus.md` | 1, 2 | | 5 | **Mode B console** — SSE, query graph, citations, panel takeover | `docs/phase-5-ai-overlay.md` | 3, 4 | | 6 | **Guardrails, cost and kill switch** — any-guardrail, persona, caps, red-team | `docs/phase-6-guardrails.md` | 5 | | 7 | **Polish** — logos, diagrams, code exhibits; optional voice | `docs/phase-7-polish.md` | 3 | **Phase order is deliberate.** Content first because both renderers depend on it. Deploy at Phase 3, not last, because a live URL that is half-built is worth more than a perfect site that never ships. AI after deployment so it can never block launch. Reference material: `reference/decisions.md` (why the stack is what it is), `reference/stack.md` (topology, ports, budgets), `reference/content-map.md` (what goes where), `reference/acceptance-tests.md` (the full test list).