From 56d3c10d9e192a3fcc6e8dca11eace8c9957093b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesu=CC=81s=20Pe=CC=81rez?= Date: Sun, 12 Jul 2026 00:02:01 +0100 Subject: [PATCH] site: put the ADR projection in the build chain, and a contract outside the data tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two failures, one shape: something living in a tree whose consumers assume it holds only something else. adr-pages: gen-adr-pages.nu existed but no recipe called it, so the ADR projection silently stopped tracking the spine — ADR-059..069 were written months ago and never reached the site. A generator outside the dependency chain is a generator that does not run. Wired into sync-full ahead of `content` (it emits the markdown that content indexes), and it is idempotent: this commit carries no ADR diff because regenerating reproduces the tree byte for byte. graph: gen-content-graph nickel-exports every .ncl under site/content, and expedientes/_schema/expediente.ncl was a CONTRACT, not an instance — export demanded `id` and the recipe aborted. `just graph` had been dead since expedientes landed. The generator was right and the file was in the wrong tree: site/content/ holds instances, so a schema there is a category error. Moved to site/schemas/expediente.ncl; the one sidecar that imports it still type-checks against the contract. graph now builds (mini:154 related:222 ontology:15). sync-full ordering: graph / adr-map / about-json ran AFTER content, but the leg that copies the four nu artifacts into the serve tree lived INSIDE content — so a full sync left site/r rendering the previous run's About and ADR map. Added sync-artifacts as the final link. Both trees now agree on all four. Gates ----- adr-check spine vs site: fails if the spine holds an ADR the site never published. Verified by deleting adr-069 — the gate caught it (69 vs 68, exit 1) and sync-full rebuilt it unattended. This is the third gate (with templates-check and posts-check) and, like the others, it runs before the damage, not after. None of them run on deploy yet: `just authoring publish` still ships without asking. That is what keeps these gates a habit rather than a law — and is the subject of the governed-delivery ADR still to be written. Refs: ADR-066 (the check decides, never the reporter). Versión corta, si la prefieres: site: wire the ADR projection into the build chain; move a contract out of the data tree gen-adr-pages existed but no recipe called it — eleven ADRs never reached the site. Now in sync-full, with an adr-check gate that fails when the spine holds an ADR the site doesn't. `just graph` had been dead since expedientes landed: a schema sat inside site/content/, which gen-content-graph nickel-exports as if everything there were an instance. Moved to site/schemas/. Also fixed sync-full ordering so the serve tree stops rendering the previous run's About and ADR map. --- site/.gitignore | 1 + site/justfile | 53 ++++++++++++++++++- .../es/casos/deriva-hardcoded-anti-pap.ncl | 2 +- .../_schema => schemas}/expediente.ncl | 0 4 files changed, 53 insertions(+), 3 deletions(-) rename site/site/{content/expedientes/_schema => schemas}/expediente.ncl (100%) diff --git a/site/.gitignore b/site/.gitignore index f87f0e6..5edfe54 100644 --- a/site/.gitignore +++ b/site/.gitignore @@ -26,3 +26,4 @@ data/ logs_*.out .wrks/ works-pv/ +*.swp diff --git a/site/justfile b/site/justfile index 1a2b6c4..8a54b8a 100644 --- a/site/justfile +++ b/site/justfile @@ -105,6 +105,36 @@ diagram: nu "{{ project_root }}/scripts/build/gen-diagram.nu" --root "{{ project_root }}/../.." --out "{{ project_root }}/site/public/images/ontoref-diagram.svg" nu "{{ project_root }}/scripts/build/gen-graph-page.nu" --root "{{ project_root }}/../.." --out "{{ project_root }}/site/public/images/ontoref-graph.html" +# The ADR set moves with the spine, not with this repo, so the projection has to be in +# the build chain or it silently stops tracking it: ADR-059..069 sat unpublished for +# months because this generator existed but no recipe called it. A generator outside the +# dependency chain is a generator that does not run. Regenerating is safe and idempotent — +# it rewrites the whole adr/ kind from .ontoref/adrs, md + .ncl sidecar per ADR. +# Project .ontoref/adrs → adr content-kind markdown (site/content/adr/en/{accepted,proposed}). +[no-cd] +adr-pages: + ONTOREF_NICKEL_IMPORT_PATH="{{ project_root }}/../../code/ontology" \ + nu "{{ project_root }}/scripts/build/gen-adr-pages.nu" \ + --root "{{ project_root }}/../.." \ + --mode content \ + --content-out "{{ project_root }}/site/content/adr/en" + +# Fails if the spine holds an ADR the site never published — the exact drift that hid +# eleven ADRs. Non-mutating: compares .ontoref/adrs against the adr content tree. +# Gate: assert every accepted/proposed ADR in the spine has a page. +[no-cd] +adr-check: + #!/usr/bin/env bash + set -e + spine=$(ls "{{ project_root }}/../../.ontoref/adrs"/adr-[0-9]*.ncl | wc -l | tr -d ' ') + pages=$(ls "{{ project_root }}"/site/content/adr/en/*/adr-[0-9]*.md | wc -l | tr -d ' ') + if [ "$spine" -eq "$pages" ]; then + echo "adr-check: $pages/$spine ADRs projected — spine and site agree" + else + echo "adr-check: DRIFT — spine has $spine ADRs, site publishes $pages. Run: just adr-pages" + exit 1 + fi + # Makes /catalog (browse) + /catalog/{slug} serve as SSR content (runtime kind, no # rebuild — registered in site/content/content-kinds.toml). # Project .ontoref/catalog → catalog markdown, then build the content index. @@ -272,7 +302,26 @@ posts-check: [no-cd] sync: templates htmx-assets rotator-assets content +# Order is load-bearing, not cosmetic: +# adr-pages / catalog-content BEFORE content — they emit the markdown content indexes. +# graph / adr-map / about-json AFTER content — they read the freshly built indexes. +# sync-artifacts LAST — the nu generators write to site/public/r, and only `content` +# copies them back to the serve tree; without this final leg a sync-full leaves +# site/r rendering the previous run's About and ADR map. # Requires ONTOREF_NICKEL_IMPORT_PATH in .env. -# Full resync including content_graph + ADR map + About view-model. +# Full resync: projections → content indexes → derived artifacts → serve tree. [no-cd] -sync-full: templates htmx-assets rotator-assets content catalog-content graph adr-map about-json +sync-full: templates htmx-assets rotator-assets adr-pages catalog-content content graph adr-map about-json sync-artifacts + +# The nu generators own these four files in site/public/r (the deploy tree); the serve +# tree needs the same copies. `content` does this too, so a standalone `just content` +# stays correct — this recipe exists for the chains that regenerate them AFTER content. +# Copy the four nu artifacts public/r → site/r so the dev server serves what was just built. +[no-cd] +sync-artifacts: + #!/usr/bin/env bash + set -e + for f in about.json adr-map.json taglines.json content_graph.json; do + src="{{ project_root }}/site/public/r/$f" + if [ -f "$src" ]; then cp -f "$src" "{{ project_root }}/site/r/$f"; fi + done diff --git a/site/site/content/expedientes/es/casos/deriva-hardcoded-anti-pap.ncl b/site/site/content/expedientes/es/casos/deriva-hardcoded-anti-pap.ncl index 05dcdf8..72fcdb5 100644 --- a/site/site/content/expedientes/es/casos/deriva-hardcoded-anti-pap.ncl +++ b/site/site/content/expedientes/es/casos/deriva-hardcoded-anti-pap.ncl @@ -1,7 +1,7 @@ # Typed source for expediente "deriva-hardcoded-anti-pap". # The sibling .md is the rendered prose; this record is the machine-parseable # case. Regenerate/render via the `generate-expediente` mode. -let s = import "../../_schema/expediente.ncl" in +let s = import "../../../../schemas/expediente.ncl" in s.make_expediente { id = "deriva-hardcoded-anti-pap", diff --git a/site/site/content/expedientes/_schema/expediente.ncl b/site/site/schemas/expediente.ncl similarity index 100% rename from site/site/content/expedientes/_schema/expediente.ncl rename to site/site/schemas/expediente.ncl