From 137817e2b04264f2f1842c199157aab871989979 Mon Sep 17 00:00:00 2001
From: Ori Nachum
Date: Wed, 22 Jul 2026 19:11:40 +0300
Subject: [PATCH 1/2] fix(deck): move the closing footer to the finale, the
deck's last slide
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The "full argument, in text" CTA, the all-presentations link, and the
subordinate Sources disclosure sat on slide 6 (close) — where the deck
used to end. With the whats-next finale as slide 7, the deck's closing
chrome now lives there, so a viewer reaches it at the actual end.
The .slide--close padding-bottom that reserved room for the fixed pager
chrome beneath the sources moves to .slide--next with the footer.
check-presentations pins the new location (article CTA and
all-presentations link inside the finale, Sources rendered exactly once
deck-wide and inside the finale) — the CTA assertion was observed
failing against the pre-move build. Dataset prose updated: deckSources
renders once in the finale's footer, not on the close slide.
Verified: 20/20 node tests, astro build clean, 23/23 presentation
checks, 56 pytest, markdownlint clean, both slides screenshot-reviewed.
Co-Authored-By: Claude Fable 5
Claude-Session: https://claude.ai/code/session_01FqTq59W6wi1YZqxGf228Tt
---
CHANGELOG.md | 6 ++
pyproject.toml | 2 +-
site-astro/scripts/check-presentations.mjs | 22 ++++++
.../data/mind-nervous-system-body-slides.ts | 5 +-
.../mind-nervous-system-body.astro | 68 ++++++++++---------
uv.lock | 2 +-
6 files changed, 69 insertions(+), 36 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ff798f..9d2ebee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
Format follows [Keep a Changelog](https://keepachangelog.com/). This project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.18.1] - 2026-07-22
+
+### Changed
+
+- The deck's closing footer — the "full argument, in text" CTA, the all-presentations link, and the subordinate Sources disclosure — moved from slide 6 (close) to slide 7 (the whats-next finale), so it sits on the deck's actual last slide; the fixed-chrome padding follows it, and check-presentations pins the new location.
+
## [0.18.0] - 2026-07-22
### Added
diff --git a/pyproject.toml b/pyproject.toml
index 3ffef3a..a7f11ba 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "org"
-version = "0.18.0"
+version = "0.18.1"
description = "The AgentCulture org site — source of the web presence published to AgentCulture.org. Replaces the deleted landing-page repo. Not distributed on PyPI: the CLI stays repo-local; the deliverable is the published site."
readme = "README.md"
license = "Apache-2.0"
diff --git a/site-astro/scripts/check-presentations.mjs b/site-astro/scripts/check-presentations.mjs
index a0589d1..8f1c0e8 100644
--- a/site-astro/scripts/check-presentations.mjs
+++ b/site-astro/scripts/check-presentations.mjs
@@ -673,6 +673,28 @@ check("deck finale: whats-next is last with the triad, separation band, no photo
finale.attrs["data-robot"] === undefined,
"finale section must carry no data-robot attribute",
);
+
+ // The deck's closing footer (article CTA, all-presentations link, the
+ // subordinate Sources disclosure) sits on the LAST slide — it moved off
+ // the close slide when the finale became slide 7.
+ assert(
+ linksTo(finale.inner, articleRoute).length === 1,
+ `the finale must carry the article CTA (${articleRoute}) exactly once`,
+ );
+ assert(
+ linksTo(finale.inner, presentationsRoute).length >= 1,
+ `the finale must carry the all-presentations link (${presentationsRoute})`,
+ );
+ const finaleSources = countOccurrences(finale.inner, ">Sources"); // scope-attr safe
+ assert(
+ finaleSources === 1,
+ `the Sources disclosure must render exactly once, on the finale; found ${finaleSources} there`,
+ );
+ const deckSourcesTotal = countOccurrences(deckHtml, ">Sources");
+ assert(
+ deckSourcesTotal === 1,
+ `the deck must render exactly one Sources disclosure, found ${deckSourcesTotal}`,
+ );
});
check("deck imagery: three robot photos with alt text", () => {
diff --git a/site-astro/src/data/mind-nervous-system-body-slides.ts b/site-astro/src/data/mind-nervous-system-body-slides.ts
index 85ce539..7e772b2 100644
--- a/site-astro/src/data/mind-nervous-system-body-slides.ts
+++ b/site-astro/src/data/mind-nervous-system-body-slides.ts
@@ -11,7 +11,8 @@
// evidence-pinned beat text stay put in `mind-nervous-system-body.ts`; this
// module still does not derive from it and carries no per-slide evidence
// ids. Sources stay subordinate: see `deckSources` below (repo homes + one
-// dated note), rendered once on the close slide, never per-slide.
+// dated note), rendered once in the deck's closing footer on the finale
+// slide, never per-slide.
//
// DECK SHAPE — exactly seven slides, one per primary beat (`beat` 1-7, in
// slide order): org#23's six plus the whats-next-finale-slide trajectory
@@ -328,7 +329,7 @@ export interface DeckSourceProject {
}
/**
- * The deck's subordinate sources note, rendered once on the close slide (not
+ * The deck's subordinate sources note, rendered once on the finale slide (not
* per-slide): repo-home links for both projects plus one dated note. This is
* the deck's evidence surface — the article keeps the strict, commit-pinned
* ledger in `presentations.ts` / `mind-nervous-system-body.ts`, untouched.
diff --git a/site-astro/src/pages/presentations/mind-nervous-system-body.astro b/site-astro/src/pages/presentations/mind-nervous-system-body.astro
index fe2f78d..5c5f08b 100644
--- a/site-astro/src/pages/presentations/mind-nervous-system-body.astro
+++ b/site-astro/src/pages/presentations/mind-nervous-system-body.astro
@@ -15,10 +15,13 @@
// 4. surfaces — the reachy-mini-action photo with three labels around it.
// 5. autonomy — a triptych of three DeckSituationVignettes (stuck,
// disconnected, routine); no photo.
-// 6. close — two dual-maturity robot cards, the verbatim thesis, sources.
+// 6. close — two dual-maturity robot cards and the verbatim thesis.
// 7. whats-next — the trajectory finale: See/Remember/Act glyph triad, two
// next-step cards (no photos, no data-robot), the shared
-// separation band, the six-sentence spoken close.
+// separation band, the six-sentence spoken close, and the
+// deck's closing footer (article CTA, all-presentations link,
+// the subordinate Sources disclosure) — it sits on the last
+// slide, so it moved off "close" when the finale landed.
//
// Navigation is progressive enhancement over natural scroll: the dot rail is
// plain anchors, the prev/next chrome and keyboard support exist only under
@@ -390,34 +393,6 @@ const slideViews = slides.map((slide, index) => {
{slide.bottomLine}
-
)}
@@ -478,6 +453,34 @@ const slideViews = slides.map((slide, index) => {
>
{slide.spokenLine}
+
)}
@@ -1041,8 +1044,9 @@ const slideViews = slides.map((slide, index) => {
color: var(--accent-strong);
}
- .slide--close {
- /* room for the fixed chrome beneath the sources on short viewports */
+ .slide--next {
+ /* room for the fixed chrome beneath the sources on short viewports —
+ the closing footer moved here when the finale became the last slide */
padding-bottom: 7rem;
}
diff --git a/uv.lock b/uv.lock
index 1740f40..4327dba 100644
--- a/uv.lock
+++ b/uv.lock
@@ -236,7 +236,7 @@ wheels = [
[[package]]
name = "org"
-version = "0.18.0"
+version = "0.18.1"
source = { editable = "." }
[package.dev-dependencies]
From c9d00922de85a9f6c500021d5f6652ad8dd6b9a4 Mon Sep 17 00:00:00 2001
From: Ori Nachum
Date: Wed, 22 Jul 2026 19:36:32 +0300
Subject: [PATCH 2/2] test(site): tighten and de-brittle the finale footer
checks (Qodo review)
Two fixes to the finale check this PR added, both from Qodo on #28:
- the all-presentations link asserted >= 1, so duplicate footer
rendering would still pass while the article CTA next to it required
exactly one. Now === 1, with the found count in both messages.
- the Sources uniqueness assertions counted the raw substring
">Sources", which a harmless whitespace or formatting
change in the rendered markup would break. Replaced with a semantic
countSourcesSummaries() built on the existing elements() +
textContent() helpers.
Both verified against the built HTML: injecting a second
/presentations/ link into the finale fails the tightened assertion, and
reformatting the summary across newlines still passes 23/23 where the
old substring count would have read 0.
Co-Authored-By: Claude Fable 5
Claude-Session: https://claude.ai/code/session_01FqTq59W6wi1YZqxGf228Tt
---
site-astro/scripts/check-presentations.mjs | 24 ++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/site-astro/scripts/check-presentations.mjs b/site-astro/scripts/check-presentations.mjs
index 8f1c0e8..e58a408 100644
--- a/site-astro/scripts/check-presentations.mjs
+++ b/site-astro/scripts/check-presentations.mjs
@@ -197,6 +197,16 @@ function linksTo(html, href) {
return anchors(html).filter((anchor) => anchor.attrs.href === href);
}
+/** Count semantic Sources disclosures. Element-based rather
+ * than a raw-substring count, so Astro's scope attributes and any harmless
+ * whitespace/formatting change in the rendered markup can't cause a false
+ * failure (PR #28 review). */
+function countSourcesSummaries(html) {
+ return elements(html, "summary").filter(
+ (summary) => textContent(summary.inner) === "Sources",
+ ).length;
+}
+
function countOccurrences(html, needle) {
return (html.match(new RegExp(needle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g")) ?? [])
.length;
@@ -677,20 +687,22 @@ check("deck finale: whats-next is last with the triad, separation band, no photo
// The deck's closing footer (article CTA, all-presentations link, the
// subordinate Sources disclosure) sits on the LAST slide — it moved off
// the close slide when the finale became slide 7.
+ const finaleArticleLinks = linksTo(finale.inner, articleRoute).length;
assert(
- linksTo(finale.inner, articleRoute).length === 1,
- `the finale must carry the article CTA (${articleRoute}) exactly once`,
+ finaleArticleLinks === 1,
+ `the finale must carry the article CTA (${articleRoute}) exactly once; found ${finaleArticleLinks}`,
);
+ const finalePresentationsLinks = linksTo(finale.inner, presentationsRoute).length;
assert(
- linksTo(finale.inner, presentationsRoute).length >= 1,
- `the finale must carry the all-presentations link (${presentationsRoute})`,
+ finalePresentationsLinks === 1,
+ `the finale must carry the all-presentations link (${presentationsRoute}) exactly once; found ${finalePresentationsLinks}`,
);
- const finaleSources = countOccurrences(finale.inner, ">Sources"); // scope-attr safe
+ const finaleSources = countSourcesSummaries(finale.inner);
assert(
finaleSources === 1,
`the Sources disclosure must render exactly once, on the finale; found ${finaleSources} there`,
);
- const deckSourcesTotal = countOccurrences(deckHtml, ">Sources");
+ const deckSourcesTotal = countSourcesSummaries(deckHtml);
assert(
deckSourcesTotal === 1,
`the deck must render exactly one Sources disclosure, found ${deckSourcesTotal}`,