diff --git a/presentation/specgraph-sib/README.md b/presentation/specgraph-sib/README.md new file mode 100644 index 0000000..8e3de63 --- /dev/null +++ b/presentation/specgraph-sib/README.md @@ -0,0 +1,20 @@ +# SpecGraph / SIB Metrics Presentation Shell + +Static 16:9 HTML deck for a future SpecGraph, SpecSpace, and SIB Metrics +presentation. + +Open `index.html` directly in a browser. The deck intentionally avoids build +tooling, slide transitions, autoplay, and animation so the artifact can double +as a landing-style page and a presentation surface. + +## Editing Notes + +- Keep each slide short: one main claim, one visual, and a small amount of + supporting text. +- Use diagrams for relationships between `SpecGraph`, `SpecSpace`, `Metrics`, + and the AI-driven SDLC loop. +- Preserve the warm editorial style from the SpecGraph landing page: serif + display type, strict rules, monochrome surfaces, and a single blue signal + accent. +- Prefer semantic HTML sections with `data-slide` for future content changes. + diff --git a/presentation/specgraph-sib/index.html b/presentation/specgraph-sib/index.html new file mode 100644 index 0000000..39a4beb --- /dev/null +++ b/presentation/specgraph-sib/index.html @@ -0,0 +1,196 @@ + + + + + +SpecGraph / SpecSpace / SIB Metrics - Presentation Shell + + + + + + +
+
+ + + SpecGraph + +
+ Presentation shell + SpecSpace + SIB Metrics +
+ +
+ +
+
+
+
+

AI-driven SDLC observability

+

SpecGraph, SpecSpace and SIB Metrics

+

A landing-style deck for explaining specification-first development, operator-guided agents, and measurable software delivery loops.

+
+
+
+ Intent + Spec + Runtime + Metric + + + + +
+
+ governed graph + measured lifecycle +
+
+
+
+ +
+
+
+

Problem frame

+

LLM work needs a stable evidence plane.

+
+
+
+ 01 +

Intent drifts

+

Prompts, diffs, reviews, and runtime behavior often live in different systems.

+
+
+ 02 +

Progress is opaque

+

Teams see activity, but not always specification coverage or verification depth.

+
+
+ 03 +

Metrics lack lineage

+

SIB-style signals become useful only when they are tied back to governed artifacts.

+
+
+
+
+ +
+
+
+

SpecGraph role

+

Executable product ontology.

+

SpecGraph keeps product intent, decomposition, readiness, evidence, and downstream handoffs connected as reviewable graph state.

+
+
+
+ Intent + Pre-Spec + Spec Node +
+
+ Proposal + Evidence + Handoff +
+
canonical state + derived surfaces
+
+
+
+ +
+
+
+
+ +
+
+
Graph canvas
+
+ Inspector + Metrics + Agent context +
+
+
+
+

SpecSpace role

+

Operator surface for graph-guided agents.

+

SpecSpace turns graph state into a human-operable interface: inspect, steer, compare, and pass context to agents without making derived metrics canonical truth.

+
+
+
+ +
+
+
+

SIB Metrics bridge

+

Measure the SDLC as a governed loop.

+
+
+
+ Input + Spec quality +

Structure, verifiability, lineage, and readiness before implementation.

+
+
+ Behavior + Process observability +

Agent runs, interventions, review gates, and evidence production.

+
+
+ Outcome + SIB signal +

Derived diagnostic pressure, not automatic policy authority.

+
+
+
+
+ +
+
+
+

Content next

+

Few words, strong diagrams, clear measurement story.

+
+
    +
  1. 01 Define the AI-driven SDLC loop.
  2. +
  3. 02 Map SpecGraph and SpecSpace responsibilities.
  4. +
  5. 03 Introduce SIB metric families and authority boundaries.
  6. +
  7. 04 Show how evidence becomes reviewable graph state.
  8. +
+
+
+
+ + +
+ + + + + diff --git a/presentation/specgraph-sib/slides.js b/presentation/specgraph-sib/slides.js new file mode 100644 index 0000000..04b5ec9 --- /dev/null +++ b/presentation/specgraph-sib/slides.js @@ -0,0 +1,73 @@ +(function () { + const slides = Array.from(document.querySelectorAll("[data-slide]")); + const prevButton = document.getElementById("prevSlide"); + const nextButton = document.getElementById("nextSlide"); + const slideCount = document.getElementById("slideCount"); + const progressBar = document.getElementById("progressBar"); + let activeIndex = 0; + + function formatIndex(index) { + return String(index + 1).padStart(2, "0"); + } + + function updateSlide(nextIndex) { + activeIndex = Math.max(0, Math.min(nextIndex, slides.length - 1)); + + slides.forEach((slide, index) => { + slide.classList.toggle("is-active", index === activeIndex); + slide.setAttribute("aria-hidden", String(index !== activeIndex)); + }); + + prevButton.disabled = activeIndex === 0; + nextButton.disabled = activeIndex === slides.length - 1; + slideCount.value = `${formatIndex(activeIndex)} / ${formatIndex(slides.length - 1)}`; + slideCount.textContent = `${formatIndex(activeIndex)} / ${formatIndex(slides.length - 1)}`; + progressBar.style.width = `${((activeIndex + 1) / slides.length) * 100}%`; + + const activeSlide = slides[activeIndex]; + if (activeSlide && window.location.hash !== `#${activeSlide.id}`) { + history.replaceState(null, "", `#${activeSlide.id}`); + } + } + + function slideFromHash() { + const hash = window.location.hash.replace("#", ""); + const index = slides.findIndex((slide) => slide.id === hash); + return index >= 0 ? index : 0; + } + + prevButton.addEventListener("click", () => updateSlide(activeIndex - 1)); + nextButton.addEventListener("click", () => updateSlide(activeIndex + 1)); + + document.addEventListener("keydown", (event) => { + if (event.defaultPrevented) return; + + switch (event.key) { + case "ArrowLeft": + case "PageUp": + event.preventDefault(); + updateSlide(activeIndex - 1); + break; + case "ArrowRight": + case "PageDown": + case " ": + event.preventDefault(); + updateSlide(activeIndex + 1); + break; + case "Home": + event.preventDefault(); + updateSlide(0); + break; + case "End": + event.preventDefault(); + updateSlide(slides.length - 1); + break; + default: + break; + } + }); + + window.addEventListener("hashchange", () => updateSlide(slideFromHash())); + updateSlide(slideFromHash()); +})(); + diff --git a/presentation/specgraph-sib/styles.css b/presentation/specgraph-sib/styles.css new file mode 100644 index 0000000..aa3140c --- /dev/null +++ b/presentation/specgraph-sib/styles.css @@ -0,0 +1,657 @@ +:root { + --ink: #0b0b0c; + --ink-2: #17181a; + --paper: #f3f1ec; + --paper-2: #e8e5dd; + --rule: #1b1c1e; + --rule-soft: rgba(11, 11, 12, .14); + --muted: #55524b; + --muted-2: #7a766d; + --signal: oklch(74% .14 240); + --signal-ink: oklch(46% .13 240); + --serif: "Instrument Serif", "Times New Roman", serif; + --sans: "Inter", "Helvetica Neue", Helvetica, Arial, sans-serif; + --mono: "JetBrains Mono", ui-monospace, Menlo, monospace; +} + +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; + min-height: 100%; + background: var(--paper); + color: var(--ink); + font-family: var(--sans); +} + +body { + min-height: 100vh; + overflow: hidden; + letter-spacing: 0; +} + +button, +a { + color: inherit; + font: inherit; +} + +a { + text-decoration: none; +} + +button { + border: 0; + background: none; + cursor: pointer; +} + +svg { + display: block; +} + +::selection { + background: var(--ink); + color: var(--paper); +} + +.deck-shell { + min-height: 100vh; + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + padding: 18px 22px; + gap: 16px; +} + +.deck-topbar, +.deck-footer { + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: center; + gap: 22px; + min-height: 42px; +} + +.brand { + display: inline-flex; + align-items: center; + gap: 10px; + font-family: var(--serif); + font-size: 23px; + line-height: 1; +} + +.brand-mark, +.brand-mark svg { + width: 32px; + height: 32px; +} + +.topbar-meta, +.deck-footer { + font-family: var(--mono); + font-size: 11px; + letter-spacing: .08em; + text-transform: uppercase; + color: var(--muted); +} + +.topbar-meta { + display: flex; + justify-content: center; + gap: 28px; + min-width: 0; +} + +.deck-nav { + display: inline-flex; + align-items: center; + gap: 10px; +} + +.icon-button { + width: 38px; + height: 38px; + display: inline-grid; + place-items: center; + border: 1px solid var(--rule); + color: var(--ink); +} + +.icon-button:disabled { + color: var(--muted-2); + border-color: var(--rule-soft); + cursor: default; +} + +.icon-button svg { + width: 18px; + height: 18px; +} + +.icon-button path { + fill: none; + stroke: currentColor; + stroke-width: 1.8; + stroke-linecap: round; + stroke-linejoin: round; +} + +.slide-count { + min-width: 72px; + font-family: var(--mono); + font-size: 11px; + letter-spacing: .08em; + color: var(--muted); + text-align: center; +} + +.stage { + width: min(100%, calc((100vh - 132px) * 16 / 9)); + aspect-ratio: 16 / 9; + justify-self: center; + align-self: center; + position: relative; + overflow: hidden; + border: 1px solid var(--rule); + background: + linear-gradient(to right, rgba(11, 11, 12, .045) 1px, transparent 1px), + linear-gradient(to bottom, rgba(11, 11, 12, .045) 1px, transparent 1px), + var(--paper); + background-size: 74px 74px; +} + +.slide { + position: absolute; + inset: 0; + display: none; + padding: clamp(34px, 4.3vw, 64px); +} + +.slide.is-active { + display: block; +} + +.slide-grid { + height: 100%; + display: grid; + gap: 0; +} + +.two-column { + grid-template-columns: 1fr 1fr; +} + +.split-rule { + grid-template-columns: .9fr 1.1fr; +} + +.diagram-layout { + grid-template-columns: .78fr 1.22fr; +} + +.closing-layout { + grid-template-columns: 1.06fr .94fr; +} + +.copy-block { + display: flex; + flex-direction: column; + justify-content: center; + padding-right: clamp(22px, 3vw, 54px); +} + +.eyebrow { + display: flex; + align-items: center; + gap: 10px; + margin: 0 0 28px; + color: var(--muted); + font-family: var(--mono); + font-size: 11px; + letter-spacing: .14em; + text-transform: uppercase; +} + +.eyebrow::before { + content: ""; + width: 28px; + height: 1px; + background: currentColor; +} + +h1, +h2, +h3, +p { + margin: 0; +} + +h1, +h2 { + font-family: var(--serif); + font-weight: 400; + letter-spacing: 0; +} + +h1 { + max-width: 10ch; + font-size: clamp(60px, 7.3vw, 106px); + line-height: .98; +} + +h2 { + max-width: 11ch; + font-size: clamp(54px, 6.3vw, 92px); + line-height: .98; +} + +h3 { + font-family: var(--serif); + font-size: clamp(28px, 2.9vw, 40px); + font-weight: 400; + line-height: 1.05; + letter-spacing: 0; +} + +.muted-serif { + color: var(--muted-2); + font-style: italic; +} + +.lede, +.body-copy { + max-width: 49ch; + margin-top: 28px; + color: var(--muted); + font-size: clamp(15px, 1.35vw, 19px); + line-height: 1.55; +} + +.hero-panel, +.surface-panel, +.flow-diagram { + border-left: 1px solid var(--rule); +} + +.hero-panel { + min-height: 100%; + background: var(--ink); + color: var(--paper); + display: grid; + grid-template-rows: 1fr auto; + position: relative; + overflow: hidden; +} + +.hero-panel::before { + content: ""; + position: absolute; + inset: 0; + background: + linear-gradient(to right, rgba(243, 241, 236, .1) 1px, transparent 1px), + linear-gradient(to bottom, rgba(243, 241, 236, .1) 1px, transparent 1px); + background-size: 64px 64px; +} + +.graph-field { + position: relative; + z-index: 1; + min-height: 100%; +} + +.node { + position: absolute; + min-width: 112px; + min-height: 44px; + display: grid; + place-items: center; + border: 1px solid rgba(243, 241, 236, .72); + background: rgba(11, 11, 12, .62); + font-family: var(--mono); + font-size: 11px; + letter-spacing: .1em; + text-transform: uppercase; +} + +.node-intent { + left: 12%; + top: 17%; +} + +.node-spec { + right: 17%; + top: 29%; + border-color: var(--signal); + color: var(--signal); +} + +.node-runtime { + left: 21%; + bottom: 24%; +} + +.node-metric { + right: 12%; + bottom: 15%; +} + +.edge { + position: absolute; + height: 1px; + transform-origin: left center; + background: rgba(243, 241, 236, .46); +} + +.edge-a { + left: 33%; + top: 24%; + width: 28%; + transform: rotate(14deg); +} + +.edge-b { + left: 36%; + top: 61%; + width: 39%; + transform: rotate(-19deg); +} + +.edge-c { + left: 36%; + top: 31%; + width: 34%; + transform: rotate(83deg); +} + +.edge-d { + right: 24%; + top: 45%; + width: 24%; + transform: rotate(74deg); +} + +.panel-caption { + position: relative; + z-index: 1; + display: flex; + justify-content: space-between; + padding: 20px 22px; + border-top: 1px solid rgba(243, 241, 236, .28); + font-family: var(--mono); + font-size: 11px; + letter-spacing: .08em; + text-transform: uppercase; + color: rgba(243, 241, 236, .72); +} + +.statement-stack { + border-left: 1px solid var(--rule); + display: grid; + grid-template-rows: repeat(3, 1fr); +} + +.statement { + display: grid; + grid-template-columns: auto 1fr; + grid-template-rows: auto 1fr; + column-gap: 24px; + padding: 30px 0 30px 38px; + border-bottom: 1px solid var(--rule-soft); +} + +.statement:last-child { + border-bottom: 0; +} + +.statement-index, +.metric-label, +.roadmap span { + font-family: var(--mono); + font-size: 11px; + letter-spacing: .14em; + color: var(--muted-2); +} + +.statement p { + grid-column: 2; + max-width: 40ch; + margin-top: 12px; + color: var(--muted); + font-size: clamp(14px, 1.15vw, 17px); + line-height: 1.48; +} + +.flow-diagram { + align-self: stretch; + display: flex; + flex-direction: column; + justify-content: center; + gap: 58px; + padding-left: clamp(36px, 5vw, 74px); +} + +.flow-row { + display: grid; + grid-template-columns: repeat(3, minmax(96px, 1fr)); + gap: 18px; + position: relative; +} + +.flow-row::before { + content: ""; + position: absolute; + left: 8%; + right: 8%; + top: 50%; + height: 1px; + background: var(--rule); +} + +.flow-row.offset { + margin-left: 8%; +} + +.flow-row span { + position: relative; + min-height: 92px; + display: grid; + place-items: center; + border: 1px solid var(--rule); + background: var(--paper); + font-family: var(--mono); + font-size: 11px; + letter-spacing: .08em; + text-transform: uppercase; +} + +.flow-row span:nth-child(2) { + border-color: var(--signal-ink); +} + +.diagram-note { + border-top: 1px solid var(--rule-soft); + padding-top: 18px; + font-family: var(--mono); + font-size: 11px; + letter-spacing: .12em; + text-transform: uppercase; + color: var(--muted); +} + +.surface-panel { + padding-right: clamp(34px, 5vw, 70px); + border-left: 0; + border-right: 1px solid var(--rule); + display: flex; + flex-direction: column; + justify-content: center; +} + +.window-bar { + display: flex; + gap: 8px; + padding: 13px 16px; + border: 1px solid var(--rule); + border-bottom: 0; + background: var(--ink); +} + +.window-bar span { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--paper); +} + +.surface-grid { + display: grid; + grid-template-columns: 1fr 150px; + min-height: 360px; + border: 1px solid var(--rule); +} + +.surface-main { + display: grid; + place-items: center; + background: + radial-gradient(circle at 28% 35%, rgba(52, 162, 211, .35) 0 4px, transparent 5px), + radial-gradient(circle at 63% 26%, rgba(11, 11, 12, .42) 0 4px, transparent 5px), + radial-gradient(circle at 44% 69%, rgba(11, 11, 12, .42) 0 4px, transparent 5px), + linear-gradient(to right, rgba(11, 11, 12, .07) 1px, transparent 1px), + linear-gradient(to bottom, rgba(11, 11, 12, .07) 1px, transparent 1px); + background-size: auto, auto, auto, 42px 42px, 42px 42px; + font-family: var(--mono); + font-size: 11px; + letter-spacing: .12em; + text-transform: uppercase; + color: var(--muted); +} + +.surface-side { + display: grid; + grid-template-rows: repeat(3, 1fr); + border-left: 1px solid var(--rule); +} + +.surface-side span { + display: grid; + place-items: center; + border-bottom: 1px solid var(--rule-soft); + font-family: var(--mono); + font-size: 10px; + letter-spacing: .08em; + text-transform: uppercase; + color: var(--muted); + text-align: center; + padding: 12px; +} + +.surface-side span:last-child { + border-bottom: 0; +} + +.metric-band { + border-left: 1px solid var(--rule); + display: grid; + grid-template-columns: repeat(3, 1fr); +} + +.metric-band section { + display: flex; + flex-direction: column; + justify-content: flex-end; + min-width: 0; + padding: 32px 25px; + border-right: 1px solid var(--rule-soft); +} + +.metric-band section:last-child { + border-right: 0; +} + +.metric-band strong { + margin-top: 96px; + font-family: var(--serif); + font-size: clamp(30px, 3vw, 42px); + font-weight: 400; + line-height: 1.04; + letter-spacing: 0; +} + +.metric-band p { + margin-top: 16px; + color: var(--muted); + font-size: clamp(13px, 1.05vw, 16px); + line-height: 1.45; +} + +.roadmap { + align-self: center; + margin: 0; + padding: 0; + list-style: none; + border-left: 1px solid var(--rule); +} + +.roadmap li { + display: grid; + grid-template-columns: 54px 1fr; + gap: 20px; + padding: 24px 0 24px 34px; + border-bottom: 1px solid var(--rule-soft); + color: var(--ink); + font-size: clamp(19px, 1.8vw, 25px); + line-height: 1.2; +} + +.roadmap li:last-child { + border-bottom: 0; +} + +.deck-footer { + grid-template-columns: minmax(180px, 1fr) auto; +} + +.progress-track { + height: 1px; + background: var(--rule-soft); +} + +.progress-track span { + display: block; + width: 16.6667%; + height: 100%; + background: var(--ink); +} + +@media (max-width: 900px) { + body { + overflow: auto; + } + + .deck-shell { + min-height: 100svh; + padding: 12px; + } + + .deck-topbar { + grid-template-columns: auto auto; + } + + .topbar-meta { + display: none; + } + + .deck-nav { + justify-self: end; + } + + .stage { + width: 100%; + } +} + diff --git a/tests/test_presentation_shell.py b/tests/test_presentation_shell.py new file mode 100644 index 0000000..2927608 --- /dev/null +++ b/tests/test_presentation_shell.py @@ -0,0 +1,30 @@ +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] +PRESENTATION_DIR = REPO_ROOT / "presentation" / "specgraph-sib" + + +def test_presentation_shell_is_static_slide_deck(): + html = (PRESENTATION_DIR / "index.html").read_text(encoding="utf-8") + css = (PRESENTATION_DIR / "styles.css").read_text(encoding="utf-8") + js = (PRESENTATION_DIR / "slides.js").read_text(encoding="utf-8") + + assert html.count("data-slide") == 6 + assert 'id="prevSlide"' in html + assert 'id="nextSlide"' in html + assert "aspect-ratio: 16 / 9" in css + assert "@keyframes" not in css + assert "transition" not in css + assert "addEventListener(\"keydown\"" in js + + +def test_presentation_shell_uses_specgraph_landing_visual_tokens(): + css = (PRESENTATION_DIR / "styles.css").read_text(encoding="utf-8") + + assert "--paper: #f3f1ec" in css + assert "--ink: #0b0b0c" in css + assert "--signal: oklch(74% .14 240)" in css + assert "Instrument Serif" in css + assert "JetBrains Mono" in css +