Commit Graph

15 Commits

Author SHA1 Message Date
9fae1c7dd8 Merge Phase 4: corpus and retrieval
Chunks, deterministic ids, payload filter, FTS5 keyword index, incremental mode,
freshness audit, Qdrant container shape, measured memory. Deploying Qdrant to
the runtime host and the Prefect deployment are still outstanding and belong to
the runtime host, not this repository.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-26 21:04:59 +11:00
f9dc574172 Phase 4: corpus and retrieval — chunks, Qdrant, FTS5, freshness audit
Satisfies docs/phase-4-corpus.md. content/ stays the single source of truth:
one pass over it writes both indexes, so the static pages and the retrieval
corpus cannot drift.

- corpus/: stdlib-only pipeline. validate -> chunk -> enrich -> embed -> upsert.
  Chunking is block-based, so a boundary cannot fall inside a fenced block, a
  table or front-matter (verified structurally, not by inspection).
- Deterministic ids: sha256(source_path + chunk_index + content_hash), rendered
  as a UUID. Re-running updates points; re-chunking deletes the old ones by
  source filter, so duplicates and orphans are both impossible.
- Incremental by content hash against a SQLite manifest. A provider, dimension
  or chunker change is refused instead of mixed: it needs a rebuild.
- Payloads per section 4.2 plus provenance, validated before any vector is
  written; a missing field fails the run and names the document and chunk.
- SQLite FTS5 over chunks (the keyword half of hybrid retrieval, decision D10)
  with area/confidence filters and a `terms` column so front-matter tech and
  tags are findable.
- Freshness audit: stale documents, due soon, index drift, contract drift;
  optional Apprise alert; exit code for Prefect.
- Embeddings behind one interface: omniroute (OpenAI-compatible, retries, key
  from the environment only) and local-hashing (offline placeholder). The
  provider decision is still Sam's; the pipeline does not depend on it.
- deploy/qdrant.yml: mem_limit = memswap_limit = 400m, loopback only.
- docs/phase-4-notes.md: operator manual, DoD evidence, measured memory.
- 76 unit tests, no container and no network.

Measured, Qdrant 1.20.0 under a 400 m cap at 1024 dimensions: 500 / 5,000 /
20,000 chunks settle at 128 / 138 / 151 MiB, peaking at 149 / 305 / 292 MiB
during ingest. Qdrant's default settings are OOM-killed between 5,000 and
20,000, so the collection is created with HNSW and payloads on disk; recorded in
reference/stack.md with the ceiling and the escalation order.

Note: the `make corpus*` targets were swept into the Phase 3 deploy commit
24590cc by concurrent work in this tree; the Makefile content is correct, and
this commit contains the pipeline they call.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-26 21:04:52 +11:00
24590cc351 deploy: static binary and a compose project name that does not collide
Two failures from the first real deploy, both repo-side:

1. The container logged "exec /usr/bin/resume-server: no such file or
   directory" and restarted until the health gate failed. The build host has
   cgo enabled, so the binary carried the nix glibc interpreter, and the
   distroless/static image has no dynamic loader. Every SQLite path already
   uses pure-Go modernc.org/sqlite, so build with CGO_ENABLED=0 and ship a
   static binary.

2. Compose warned about orphan containers ai-resume-backend-1 and
   ai-resume-db-1: /home/sam/deployment/ai-resume on .13 already owns the
   `ai-resume` compose project (on the ai-mesh network). Sharing the name makes
   each stack visible as orphans to the other, so a `compose down
   --remove-orphans` in either could stop the other. Run the site as project
   `ai-resume-site`; the image tag stays ai-resume:local and the port binding,
   mem_limit and memswap_limit are untouched.
2026-10-26 20:53:25 +11:00
c95e5361e3 deploy: pin the compose network to an explicit /28
The first deploy failed before the health gate:

    Network ai-resume_default Error Error response from daemon:
    all predefined address pools have been fully subnetted

.13's docker daemon has 31 bridge networks already, which exhausts its
default-address-pools, so any new implicit compose network fails to create.
Pinning 10.89.0.0/28 (no route, bridge or LAN overlap on .13) makes this
project independent of that host condition without touching another project,
container or the daemon config. mem_limit, memswap_limit and the 127.0.0.1
port binding are unchanged.
2026-10-26 20:51:17 +11:00
2906683f44 deploy: add deploy.sh (release, rollback, status) for Phase 3 section 3.1
The release directory under /home/sam/deployment/resume is the rollback unit;
deploy.sh stages releases/<timestamp>-<sha>/, flips `current` atomically,
builds the image from that directory and gates on /healthz, restoring the
previous release on any failure.

The rehearsal recipe in the header is kept as comments only; the pasted spec
lost the comment prefixes on the multi-line printf, which would have made the
script exit 1 on startup. It now uses \n escapes on one commented line.
2026-10-26 20:49:53 +11:00
c2b4e33897 Merge Phase 2: Mode A static site
DoD satisfied (evidence in AUD-142): A1 with no AI services, one binary,
deterministic templ generate, 6 L1 + 6 L2 + artefact pages, FTS5 search,
Lighthouse 100/100 mobile and desktop, <100ms first byte, stage panel driven
by route or stream, print legible.
2026-10-26 20:43:03 +11:00
84fc585c91 Phase 2: Mode A static site — build, serve, search, stage panel
One Go binary serves a pre-rendered site. No Node, no bundler, no node_modules:
htmx and the SSE extension are vendored files, Markdown and code highlighting
run at build time (goldmark + chroma), and the server only writes files.

Implements docs/phase-2-static-site.md:

  2.1 service skeleton   cmd/build (content -> dist), cmd/server (dist -> HTTP),
                         env-configured, 127.0.0.1:8096, /healthz with separate
                         static / ai / search readiness, --health-check probe,
                         deploy/compose.yml + deploy/Dockerfile at 120m
  2.2 rendering          templ components (Layout, Nav, PillarCard, Stage,
                         EvidenceTable, CodeBlock, Timeline, Footer);
                         `make generate` is a build step and is byte-stable
  2.3 navigation         six-area rail, <details> disclosure under 900px,
                         breadcrumbs, aria-current, L1/L2/L3 with artefact pages
  2.4 stage panel        internal/stage owns the contract; RouteDriver (fragment
                         swap) and SSEDriver (event stream) differ only in the
                         Binding, never in layout. fragment ids live in the
                         contract so route files, links and streamed frames agree
  2.5 search             SQLite FTS5 over titles and body, rebuilt by the build;
                         one handler returns a page without HTMX, a fragment with
                         it. Visitors' FTS operators are never honoured
  2.6 quality floor      print stylesheet, JS-off navigation, focus states,
                         semantic landmarks, dark default; security headers

Definition of Done evidence (see issue AUD-142):
  A1  all pages 200 in a network namespace with no external route
  one binary serves the site; make dist writes 45 pages, 86 fragments, 40 docs
  templ generate is deterministic (md5 identical across runs)
  6 L1 + 6 L2 pages, artefact pages reachable in <= 3 clicks
  FTS5 returns sensible hits for zigbee / signage / routing (tested)
  Lighthouse 100 performance and 100 accessibility, mobile and desktop
  first byte well inside 100ms over loopback (test)
  panel renders from a route driver and re-points at the stream untouched
  print legible: /edge/ renders to a 3-page PDF with the print stylesheet

Also fixes found while finishing the build:
  - reject "." / ".." path segments before ServeMux redirects them into a file
    outside the asset directory (/static/../build.json now 404, was 307->200)
  - make test is the real gate: go test ./... plus the validator self-test
2026-10-26 20:42:52 +11:00
fd9974d07e Merge branch 'main' into feat/phase-2-static-site 2026-10-26 20:01:38 +11:00
ea2c722816 WIP Phase 2: content loader, stage contract, search index, templ components 2026-10-26 20:01:38 +11:00
72edfec59c Phase 1: precise container/host wording on platform L1/L2
'The spine' records 50+ containers across 3 hosts and a 4-host estate; the
pages asserted containers 'across four hosts'. Reworded so the container
count and the host count are each stated without implying a distribution
the sources do not give. No metric value changed.

DoD: schema valid (make validate clean), metrics still trace to the
approved chip set.
2026-10-26 19:51:22 +11:00
Documenter
00fa15f98f Phase 1: content source of truth — 40 documents across six areas
Satisfies the Phase 1 Definition of Done:

- content/ validates cleanly against the frozen schema. `make validate`
  reports OK: 40 document(s) validated, 0 violations.
- Six areas exist, each with an L1 entry: ai-overview, platform-overview,
  edge-overview, products-overview, history-overview, about-overview.
- All six areas have an L2 entry: ai-systems, platform-estate, edge-fleet,
  products-business, history-engagements, about-narrative.
- 28 L3 artefact stubs under content/<area>/l3/. Every artefacts[].url in
  every document resolves to one of them; the validator proves there are no
  dangling references (and a deliberately broken copy exits 1).
- Every metric value traces to a source line in the evidence notes or the
  approved metric set in reference/content-map.md (per-metric citations are
  recorded in the AUD-131 completion comment).
- Every document carries confidence and last_verified=2026-10-26; the
  validator rejects a missing or non-canonical value.
- docs/content-redaction.md records the redaction pass: identifiers, third
  party personal data, credentials and infrastructure detail excluded, with
  reasons, plus the two numbers that are reported rather than smoothed.

Honesty positions held: 3 businesses active (12 adoptions over time),
Where-Woof deployed and pre-market, failures kept in (edge-failures), no
promotion of in-progress work to delivered.

No site code, no templates, no CSS, no rendering (Phase 2).
2026-10-26 17:30:35 +11:00
0eb151117e Phase 1a: content schema validator (freeze the schema)
Implements AGENTS.md section 7 front-matter schema as a single-file,
standard-library-only Python validator; no third-party YAML dependency
and no framework. Adds the `make validate` gate, a 20-case fixture set
and docs/content-schema.md.

DoD satisfied:
- make validate runs and exits 0 on an empty/clean content/ tree
  (missing dir and empty dir both exit 0; fixture ok-empty covers it).
- Every violation class has a fixture under test/fixtures/fail-*; each
  exits non-zero with one line per violation (file:line: field: rule).
  `make test` runs all 20 cases: 20/20 pass.
- Dangling artefact reference is fatal (fail-dangling-artefact;
  artefacts[].url must resolve to an existing in-repo L3 stub or asset).
- Unknown front-matter field is fatal (fail-unknown-field), including
  unknown keys nested inside artefacts[]/metrics[].
- docs/content-schema.md committed; states the frozen schema, links to
  AGENTS.md section 7, and records that any later schema change is a
  breaking change requiring Sam's approval.
- Scope respected: no content authored, no rendering, no site code.

Files: Makefile, tools/validate_content.py, test/fixtures/**,
docs/content-schema.md, .gitignore (__pycache__).
2026-10-26 17:26:00 +11:00
Sam Rolfe
b267e55f8c Replace Vite/React scaffold with Go build spec
- AGENTS.md: build spec for Paperclip (constraints, conventions, 7 phases)
- docs/phase-1..7: per-phase work, DoD, out-of-scope, risks
- reference/: frozen decisions, stack+ports+budgets, content map, acceptance tests
- agents/: persona and security policy drafts
- Retires the React scaffold; history retained as record of the false start
2026-10-26 16:02:35 +11:00
35a3097042 Add professional README showcasing project, stack, and patterns 2026-08-26 13:31:37 +10:00
b814a4a8d5 Initial commit: ai-resume 2026-08-26 13:19:14 +10:00