Publication Validation
The gate that keeps the published portal evidence-first and render-safe. Every rule below is an automated acceptance test run at pipeline stage ⑥ (pipeline); a single failure blocks publish. Rules reference the Data Contract and Model.
1 · Rule set
A · No invented content (the cardinal rule)
- A1 Every factual value (metric, artefact, relationship, maturity, sample) traces to source (graph/estate/
repo). Values without provenance are rejected or downgraded to
awaiting. - A2 No feed contains an entity, edge, or number absent from
portfolio-graph.json/health.jsonunless it is explicitly editorial copy (hero/story/flagship-experience prose) — and prose asserts no unsourced metric/fact. - A3
evidence-plateswithstatus:"verified"must carrysourceRepo+sourcePath. Missing ⇒ fail (or auto-downgrade toawaiting).
B · Missing data handled correctly
- B1 Absent data appears as an empty collection with a
status, never as a missing key. - B2 Every
awaiting/planned/missingitem carries a reason and is listed in the feed’smissing[]. - B3 No
planned/in-progress/awaitingitem is serialized with fields that imply completion.
C · Routes resolve
- C1 Every
routein every feed exists inroutes.json. - C2 Every
EntityRef.routeand link target resolves; zero dead links. - C3
routes.jsoncovers every entity referenced anywhere inpublication/**.
D · Evidence has source
- D1 Every
Factinstory.jsonhas anEvidenceRef. - D2 Every
CaseStudy.artefacts[]resolves to anevidence-plate. - D3 Every
intelligence-product.evidence[]resolves (or the product isawaiting).
E · Status & maturity labels valid
- E1 Every
status∈ the closed enum; everymaturity∈ the closed enum; everyrealization∈ its enum. - E2 A feed’s
statusis consistent with its data (alivefeed cannot contain onlyawaitingitems). - E3 Maturity/realization labels equal the source frontmatter/digest — no upgrades in transit.
F · Every destination has data
- F1 Each of the 9 destinations has a feed present in
manifest.json. - F2 Each destination feed is either non-empty or explicitly
awaiting/futurewith a reason. - F3 No destination renders empty by omission (only by declared
awaiting).
G · Schema conformance
- G1 Every feed validates against its
publication/schemas/<feed>.schema.json. - G2 The envelope (contract/version/status/source/data/missing) is present and typed on every feed.
- G3
manifest.hashfor each feed equals the sha256 of the feed bytes.
H · Lovable rendering safety
- H1 No
nullwhere the schema requires a value; optional fields are absent or typed, neverundefined. - H2 Text fields are plain strings (no unescaped HTML); diagram bodies are referenced, not inlined raw.
- H3 Enumerated
role/altitude/docType/artefactTypevalues are within their closed sets (Lovable can switch on them safely). - H4 Arrays Lovable iterates (
items,chapters,studies,exhibits,sections,products,plates) are always present (possibly empty), never absent.
I · Claude ownership boundary
- I1 No feed contains presentation directives (CSS, component names, layout hints) — truth only.
- I2 No published feed version is mutated in place (hash of a given version never changes).
- I3 Determinism: regenerating from the same
estateCommit+editorialCommit+graphHashyields identicaldatabytes (ignoring thegeneratedAtstamp).
J · Canonical models (single source)
- J1
spine-single-source— exactly one stage list exists (incanonical/spine.json, 6 stages);arrival,story,architecture-gallerycarryspineRef:"platform-spine"and no inline stage list. - J2
navigation-single-source— exactly onenavigation.json; everyroute/feedit names resolves; no other feed defines site structure. - J3
search-single-source— exactly onesearch-index.json; everySearchDoc.routeresolves;embeddingRefis unset unless semantic search exists. - J4
system-venture-distinct— no entity is typed bothsystemandventure; the canonical spine stage 5 isventure(business), notsystem.
K · Feed & publication classes
- K1 Every feed declares
feedClass+publicationClass+shape, all within their closed enums. - K2
feedClass:"live"requires attland lives underlive/, notpublication/; zero live feeds exist today (any live feed inpublication/fails). - K3 Approval routing matches class:
reference/generated→ auto-publishable;editorial/claim-bearing→ human-approved;structural→ human+ADR (the publish log records which gate ran). - K4
shape:"index"feeds reference detail feeds that all appear (with hashes) in the manifest;shape:"single"carries no shard/page fields.
L · Editorial boundary
- L1 Editorial narrative originates in
content/editorial/**(never extracted from Lovable/presentation). - L2 Any factual claim inside editorial-sourced fields carries an
EvidenceRef; unsourced facts in prose fail (no-inventedapplies to editorial prose too). - L3 No feed sources authored copy from a presentation artefact (
site/**, Lovable components).
2 · Acceptance-test matrix
| Test | Checks rule(s) | Fails when |
|---|---|---|
schema-valid |
G1–G2 | any feed violates its schema |
manifest-integrity |
A(manifest), G3 | a feed hash mismatches or a feed is unlisted |
routes-resolve |
C1–C3 | any route/ref/link is unresolved |
evidence-sourced |
A1,A3,D1–D3 | a verified claim/fact/artefact lacks a resolvable source |
no-invented |
A1–A2 | a metric/entity/edge not in graph/health appears as fact |
enums-valid |
E1,H3 | a status/maturity/role/altitude is out of set |
maturity-honest |
E3,B3 | a label differs from source, or a planned item looks done |
destination-coverage |
F1–F3 | a destination lacks a feed or renders empty-by-omission |
missing-explicit |
B1–B2 | absent data is a missing key rather than empty+status |
render-safe |
H1–H4 | a required value is null, or an iterated array is absent |
boundary-clean |
I1, L3 | a feed carries presentation directives or sources copy from presentation |
determinism |
I3 | two builds of the same inputs differ |
spine-single-source |
J1 | more than one stage list exists, or a feed inlines the spine |
navigation-single-source |
J2 | missing/duplicate nav, or an unresolved nav route/feed |
search-single-source |
J3 | missing/duplicate search index, or an unresolved doc route |
system-venture-distinct |
J4 | an entity is both system and venture, or spine stage 5 ≠ venture |
classes-valid |
K1–K4 | a bad/missing feedClass/publicationClass/shape, or a live feed under publication/ |
editorial-boundary |
L1–L2 | authored prose lacks an editorial source, or asserts an unsourced fact |
stats-cross-check |
A1 | arrival.scale/story.meta.stats ≠ graph/health values |
Publish requires all green. The report lists, per feed: pass/fail, counts of awaiting/planned items, and
any unresolved refs.
3 · Manual review checklist (gate ⑦, human)
Beyond the automated gate, the founder confirms:
- [ ] The diff (what changed since the last version) is understood.
- [ ] New/changed maturity labels match reality (no optimistic drift).
- [ ] New flagship-experience / flagship claims are ones you’d defend to a customer.
- [ ]
awaiting/futureitems are correctly labelled (nothing planned reads as live). - [ ] Editorial copy invents no facts beyond the sourced data.
- [ ] Any structural change (new feed, schema shape, tier promotion) has an ADR.
4 · Known validation gaps to design against
- Cross-feed referential integrity (an
EntityRefin one feed pointing at an id that another feed dropped) — theroutes.json+ graph id set is the canonical id registry; validate all refs against it. - Editorial-vs-fact separation is partly human-judged (D-rules cover machine facts; prose is checked at gate
⑦). Keep editorial copy in clearly-marked fields so
no-inventedcan scope machine facts precisely.