diff --git a/docs/web-foundation.md b/docs/web-foundation.md index 81b94ae..309bdb6 100644 --- a/docs/web-foundation.md +++ b/docs/web-foundation.md @@ -2,111 +2,168 @@ ## Status -Version: `0.2` +Version: `0.3` + +The v0.1 gate established canonical cross-platform tokens, the Web Awesome +integration boundary, the component laboratory, representative Pinega +compositions, and executable quality gates. Version 0.2 added the first public +homepage, documentation, research, and getting-started routes. + +Version 0.3 implements the Information Architecture Foundation accepted in +`docs/web-content-audit.md`: + +- Pinega is presented as the umbrella database-systems research and engineering + programme rather than as a synonym for one storage engine; +- Pinega Engine is the first active implementation programme beneath that + master brand; +- public navigation owns Technology, Research, Documentation, About, and + GitHub destinations; +- the component laboratory remains an internal validation surface; +- a versioned content registry owns route and discovery metadata; +- documentation exposes a topic filter and content-purpose labels without + claiming full-text search; +- no new diagram family, blog engine, paper viewer, or Typst HTML pipeline is + introduced by this milestone. + +The working master-brand line is: -The `0.1` gate established canonical cross-platform tokens, the Web Awesome -integration boundary, the component laboratory, five representative Pinega -compositions, and executable quality gates. +```text +Correctness under concurrency. +``` + +It is used as a technical positioning line, not as a claim that every proposed +programme has reached formal verification or production maturity. + +## Public hierarchy + +```text +Pinega / Pinega Labs programme and organisation boundary + ↓ +Technology programmes what may become maintained software + ↓ +Pinega Engine first active implementation programme + +Research questions, evidence, models, and experiments +Documentation start, how-to, explanation, and reference paths +About identity, principles, and commercial boundary +``` + +Current routes: + +```text +/ master-brand homepage +/technology/ programme catalogue and Pinega Engine boundary +/research/ research taxonomy, method, and active studies +/docs/ documentation topics and purpose/maturity labels +/docs/getting-started/ first contributor-oriented start page +/about/ Pinega and Pinega Labs identity +/component-lab/ internal validation surface +/404.html explicit not-found page +``` + +The blog is not placed in navigation before a real content model and route +exist. Empty destination pages are prohibited. -Version `0.2` adds the first public information architecture: +## Content contract + +`web/content/content-index.json` is the only route and public-discovery +registry. The schema records: ```text -/ product homepage -/docs/ documentation landing -/docs/getting-started/ first documentation article -/research/ research programme -/component-lab/ design-system validation surface +id, route, source/output paths, content type, titles, summary, audience, +programme, research area, topics, maturity, dates, authors, sitemap, +searchability, structured-data intent, public/canonical status ``` -The public pages remain research-stage communications. They explicitly -separate available artefacts, validated design contracts, research hypotheses, -and planned engine work. +Page bodies remain native semantic HTML. The build validates agreement between +registry and source pages, then derives routes, sitemap entries, and +`site-manifest.json` from the registry. + +This boundary is intentionally reusable by later blog, paper, feed, JSON-LD, +related-content, and static-search implementations. Those gates must extend the +same metadata model rather than introducing parallel front matter or route +lists without reconciliation. ## Principles 1. Native HTML owns document semantics and durable content. 2. CSS owns presentation, layout, responsive adaptation, and visual state. -3. JavaScript owns domain state, lifecycle, and interaction orchestration. +3. JavaScript owns lifecycle and interaction enhancement. 4. Web Awesome supplies generic interaction primitives, not Pinega semantics. 5. Pinega custom elements wrap only stable domain concepts or intentional - vendor isolation boundaries. + vendor-isolation boundaries. 6. Licensed Pro components progressively enhance complete semantic fallbacks. 7. Canonical Pinega tokens generate CSS, TypeScript, and Typst adapters. 8. Product maturity is expressed with text and structure, never colour alone. -9. Public claims distinguish implemented, validated, proposed, and planned work. - -## Layers - -```text -@layer vendor, - pinega.tokens, - pinega.base, - pinega.layout, - pinega.webawesome, - pinega.components, - pinega.utilities, - pinega.overrides; -``` - -Web Awesome retains its internal layers inside `vendor`. The Pinega adapter -maps canonical tokens after vendor defaults, and component styling remains -above that adapter. +9. Public claims distinguish implemented, validated, experimental, proposed, + and planned work. +10. Public navigation follows user intent and programme ownership, not repository + directories or internal validation surfaces. ## Page and build model The site is a static multi-page build. Source HTML is not generated from a -client-side framework and is useful before Custom Elements register. +client-side framework and remains useful before Custom Elements register. `web/scripts/build.mjs`: -- bundles the shared CSS and JavaScript entrypoint; -- copies each explicit page to a clean directory route; -- injects the private Web Awesome project boundary when configured; -- emits canonical URLs from `PINEGA_SITE_ORIGIN`; -- generates `robots.txt`, `sitemap.xml`, and `site-manifest.json`; -- copies Web Awesome assets and static Pinega assets. - -`web/scripts/serve.mjs` resolves directory indexes, rejects path traversal, -supports `GET` and `HEAD`, and returns the generated not-found page with HTTP -status 404. +- validates the versioned content registry; +- verifies title, description, page identity, canonical policy, heading count, + and public navigation for every registered source page; +- bundles shared CSS and JavaScript; +- injects semantic diagrams and the private Web Awesome project boundary; +- emits clean directory routes from the registry; +- copies canonical content, diagram, and static sources needed by consumers; +- generates `robots.txt`, `sitemap.xml`, and a metadata-rich + `site-manifest.json`. -## Component decision rule +## Navigation decision rule ```text -native element - when HTML already owns the semantics and behavior - -direct wa-* element - for a stable generic interaction primitive +public primary navigation + stable audience destination with real content and route ownership -Pinega composition - when multiple generic primitives form a stable product pattern +footer navigation + durable public destinations and repository access -Pinega custom element - for domain semantics, lifecycle, or vendor API isolation +internal/contributor navigation + component lab, design-system validation, build and maintenance surfaces ``` -`pinega-doc-search` follows this rule: documentation cards are durable HTML; -the Custom Element adds filtering and an aria-live result count without -constructing or replacing the cards. +`/component-lab/` remains out of the sitemap, public primary navigation, and +future site search. It is reachable from contributor documentation. + +## Documentation and search boundary + +The current `pinega-doc-search` component filters eight durable topic cards. +Its visible UI is therefore labelled **Filter documentation topics**. It does +not claim indexed page search. + +The documentation cards expose purpose independently from maturity: -## Pro boundary +- Start; +- How-to; +- Explanation; +- Research; +- Reference. -The licensed project is deployment configuration, not public source. No Pro -assets or project credentials are committed. `pinega-benchmark` always retains -an accessible HTML table. It renders a native SVG chart first and upgrades to -Web Awesome Pro only after `wa-line-chart` is registered. +A later documentation-reorganisation gate will split the first broad +getting-started article into real pages. Site-wide static search follows only +after the docs, blog, and paper corpus are large enough to justify an index. ## Gate exit criteria -- generated token artifacts are deterministic and current; -- no purchased project URL or license material is present in the repository; -- TypeScript passes strict checking; -- all public routes and the component laboratory build from the pinned dependency set; -- local links, canonical metadata, sitemap entries, and 404 handling are validated; -- semantic and keyboard browser tests pass in Chromium, Firefox, and WebKit; -- serious and critical axe violations are absent on every public route; -- mobile and desktop layouts have no horizontal overflow; -- committed screenshots protect the homepage, docs, research, component lab, - and representative dark-mode compositions; -- public pages remain meaningful without the Pro project and without client-side content generation. +- Pinega is framed as the master programme on homepage and metadata; +- `Correctness under concurrency.` appears as the working brand line; +- Pinega Engine is explicitly subordinate to the wider programme; +- Technology and About routes contain substantive content; +- Research exposes the accepted seven-area taxonomy; +- public navigation is consistent on every public page and excludes the + component laboratory; +- content metadata is versioned, validated, copied to output, and represented + in the generated site manifest; +- all local routes and fragments resolve; +- the topic-filter label matches actual behaviour; +- the site remains useful without client-side rendering or Pro assets; +- TypeScript, unit, build-budget, Chromium, Firefox, WebKit, accessibility, + responsive-overflow, and visual-regression gates pass. diff --git a/docs/web-ia-foundation.md b/docs/web-ia-foundation.md new file mode 100644 index 0000000..681e8c6 --- /dev/null +++ b/docs/web-ia-foundation.md @@ -0,0 +1,80 @@ +# Pinega Web IA Foundation + +Status: **CURRENT IMPLEMENTATION** +Baseline: Information Architecture and Content Audit merged by PR #20 +Implementation branch: `agent/pinega-web-ia-foundation` + +## Purpose + +This milestone implements the first public information architecture for Pinega +as an umbrella database-systems research, engineering, and future product +programme. It changes the site's ownership model without introducing a blog, +paper viewer, full-text search, Typst HTML output, or new scientific diagrams. + +## Accepted public model + +```text +Pinega master technology and product programme +Pinega Labs working research/engineering and future company identity +Technology programmes candidate paths to maintained commercial software +Pinega Engine first active implementation programme +Research evidence, models, experiments, and open questions +Documentation reproducible start/how-to/explanation/reference paths +``` + +The working brand line is: + +```text +Correctness under concurrency. +``` + +The line is deliberately concise. It identifies the technical centre of Pinega +without replacing maturity labels or evidence-based claims. + +## Implemented routes + +| Route | Ownership | +|---|---| +| `/` | Master-brand proposition, programme status, featured Pinega Engine, and evidence discipline | +| `/technology/` | Technology catalogue, Pinega Engine architecture, accepted decisions, and promotion criteria | +| `/research/` | Seven-area research catalogue, YDMP method, and existing active studies | +| `/docs/` | Documentation-topic filter with purpose and maturity labels | +| `/docs/getting-started/` | Programme boundary and reproducible workspace commands | +| `/about/` | Pinega/Pinega Labs relationship, principles, current company boundary, and collaboration path | +| `/component-lab/` | Internal validation surface, excluded from public discovery | + +## Content metadata + +`web/content/content-index.json` is the single route/discovery registry. Native +HTML remains the body source. This avoids premature adoption of a CMS while +establishing the metadata required by future publishing systems. + +The registry is expected to become input to: + +- blog indexes and feeds; +- paper and research-artefact catalogues; +- JSON-LD generation; +- related-content navigation; +- static full-text search; +- author and update metadata; +- sitemap and route validation. + +A later system may generate or import registry entries, but it must not create a +second conflicting source of route identity. + +## Explicitly deferred work + +- Documentation article decomposition. +- Blog content and RSS/Atom. +- Original-paper and PDF integration. +- Typst-to-HTML publication experiments. +- Site-wide indexed search. +- Contact or commercial-sales claims before a real company boundary exists. +- New scientific-diagram families or candidate-profile promotion. + +## Next milestone + +After this gate is accepted, proceed to **Documentation Reorganisation and +Review**. That work should split mixed documentation modes, establish real +concept/how-to/reference pages, and evaluate the current filtering UX before +adding the blog or a full-text search dependency. diff --git a/web/README.md b/web/README.md index f1b9273..dfce2f9 100644 --- a/web/README.md +++ b/web/README.md @@ -1,10 +1,65 @@ # Pinega Website and Strata Web System -This directory contains the production-oriented web implementation of Pinega -Strata. It uses semantic HTML, modern CSS, native Custom Elements, and Web -Awesome as the generic interaction kernel. +This directory contains Pinega's production-oriented static Web platform. It +uses semantic HTML, modern CSS, native Custom Elements, and Web Awesome as the +generic interaction kernel. Pinega Strata remains the canonical design system. -## Architecture +## Public information architecture + +The public site presents Pinega as an umbrella database-systems research and +engineering programme. Pinega Engine is the first active implementation +programme beneath that master brand. + +```text +/ master-brand homepage +/technology/ technology programmes and maturity boundaries +/research/ research-area catalogue and active studies +/docs/ documentation topics and content-type map +/docs/getting-started/ first guided contributor path +/about/ Pinega / Pinega Labs identity and principles +/component-lab/ internal design-system validation surface +/404.html explicit not-found page +``` + +`/component-lab/` remains buildable and testable but is excluded from primary +public navigation, the sitemap, and future site search. + +## Content and route contract + +`content/content-index.json` is the versioned route and discovery registry. It +owns page identity, route/output mapping, navigation, title, summary, audience, +programme, topics, maturity, author/update metadata, sitemap policy, +searchability, and future structured-data intent. + +Native HTML under `pages/` remains the durable semantic body content. The build +checks that each page agrees with the registry on title, description, +`data-page`, canonical URL policy, one `h1`, and public navigation. + +```text +content/content-index.json + ↓ validation +registered native HTML pages + ↓ static build +routes + sitemap + site-manifest.json + future publishing indexes +``` + +The registry is designed to support later blog, paper/library, RSS, JSON-LD, +related-content, and static full-text search gates without creating another +manual route catalogue. + +## Working brand line + +The current master-brand line is: + +```text +Correctness under concurrency. +``` + +It describes the programme's technical centre of gravity. It does not claim +that every proposed Pinega technology is already implemented, verified, or +commercially available. + +## Web architecture ```text canonical Pinega tokens @@ -13,46 +68,31 @@ Pinega CSS + Web Awesome theme adapter ↓ semantic Pinega components ↓ -multi-page website and documentation compositions +registered static pages and documentation compositions ``` Web Awesome is intentionally not the canonical design system. Vendor tokens are mapped from `design/tokens/strata.tokens.json`; research roles such as `confirmed`, `inferred`, and `hypothesis` remain Pinega domain semantics. -## Routes - -The build emits a static multi-page site: - -```text -/ product homepage -/docs/ documentation landing -/docs/getting-started/ first documentation article -/research/ research programme and shared semantic diagrams -/component-lab/ design-system validation surface -/404.html explicit not-found page -``` - -Source pages live under `pages/`. The component laboratory remains a separate -source under `component-lab/` so production pages do not become test-fixture -markup. +Native HTML owns content and document semantics. CSS owns presentation, +responsive adaptation, and visual state. JavaScript owns lifecycle and +interaction enhancement. Public pages remain meaningful before Custom Elements +register and without the licensed Web Awesome Pro project. ## Shared semantic diagrams Canonical renderer-independent models live under `design/diagrams/models/`. -The website build creates a temporary Node-only bundle from -`src/diagrams/index.ts`, validates every model, lays it out, and replaces -explicit `PINEGA_DIAGRAM` placeholders with complete accessible inline SVG -figures. +The build creates a temporary Node-only renderer bundle, validates and lays out +each model, and replaces explicit `PINEGA_DIAGRAM` placeholders with complete +accessible inline SVG figures. -The temporary renderer bundle is deleted before the build finishes, so diagram -layout code is not shipped to the browser. Each generated figure retains a -caption, direct SVG title and description, keyboard-reachable viewport, text -transcript, and a link to the canonical JSON model copied under -`dist/diagrams/`. +The temporary renderer is deleted before the build completes. Generated figures +retain captions, direct SVG titles/descriptions, keyboard-reachable viewports, +text transcripts, layout-profile identity, and links to canonical JSON. The +same models feed the Typst/CeTZ publication renderer. -The same JSON is consumed by `ydmp/templates/diagrams/shared-model.typ` for the -Typst/CeTZ publication renderer. +The Web IA milestone adds no new diagram family or promoted layout profile. ## Run locally with Nushell @@ -60,6 +100,7 @@ Typst/CeTZ publication renderer. cd web ^npm ci --ignore-scripts ^npm run build +^npm run check:build ^npm run serve ``` @@ -69,9 +110,6 @@ Then open: start 'http://127.0.0.1:4173' ``` -The development server resolves clean directory routes such as `/docs/` and -returns the generated `404.html` with an HTTP 404 status for unknown paths. - ## Deployment origin Canonical URLs, `robots.txt`, and `sitemap.xml` are generated from @@ -84,13 +122,12 @@ with-env { PINEGA_SITE_ORIGIN: 'https://www.example.com' } { } ``` -The origin must contain only scheme and host, with no path, query, or fragment. - ## Purchased Web Awesome Pro project The public repository contains no project URL, license key, kit code, or Pro asset. To activate the purchased project locally or in deployment, provide the -private module URL at build time: +private module URL at build time. Every public page retains a complete +accessible fallback without Pro. ```nu with-env { @@ -101,24 +138,20 @@ with-env { } ``` -The URL is injected as a `` value. Without -Pro, every public page remains complete, readable, and testable. - ## Implemented compositions - `pinega-site-header`: light-DOM semantic header/navigation enhancement; -- `pinega-hero`: responsive product narrative; +- `pinega-hero`: responsive programme or technology narrative; - `pinega-evidence`: YDMP provenance semantics with explicit text labels; -- `pinega-code-example`: native code plus isolated Web Awesome copy action; +- `pinega-code-example`: native code plus isolated copy enhancement; - `pinega-benchmark`: canonical table, native SVG fallback, optional Pro chart; -- `pinega-doc-search`: progressive client-side filtering over durable docs cards; -- build-time semantic diagrams: linearizability histories, version chains, and - object lifecycles with accessible SVG and textual projections. +- `pinega-doc-search`: progressive filtering over durable documentation-topic + cards; it is not yet site-wide full-text search; +- build-time semantic diagrams: histories, version chains, and lifecycles with + accessible SVG and textual projections. ## Validation -Native checks: - ```nu ^npm run tokens:check ^npm run typecheck @@ -128,8 +161,8 @@ Native checks: ``` The complete browser matrix should use the version-matched Playwright container -on unsupported Linux distributions. Browser tests cover the public routes, -component laboratory, keyboard behavior, theme switching, docs search, -semantic diagrams, not-found handling, horizontal overflow, metadata, local -links, and automated accessibility. Chromium screenshots provide the visual- -regression contract. +on unsupported Linux distributions. Tests cover registered routes, route and +fragment integrity, master-brand and maturity claims, public navigation, +component-lab isolation, metadata generation, documentation filtering, browser +behaviour, keyboard interaction, accessibility, responsive overflow, and +committed visual baselines. diff --git a/web/component-lab/index.html b/web/component-lab/index.html index e32d236..1fb7e84 100644 --- a/web/component-lab/index.html +++ b/web/component-lab/index.html @@ -1,12 +1,12 @@ - + - + - Pinega Web Foundation v0.1 + Pinega Strata component laboratory @@ -16,7 +16,7 @@
- + Pinega @@ -248,7 +248,7 @@

diff --git a/web/content/README.md b/web/content/README.md new file mode 100644 index 0000000..ea9c8d7 --- /dev/null +++ b/web/content/README.md @@ -0,0 +1,46 @@ +# Pinega Web content contract + +`content-index.json` is the versioned registry for public routes, internal web +surfaces, page identity, navigation, maturity, audience, and discovery metadata. +It does not own article body content: native HTML remains the durable semantic +source for each page. + +The build validates that every registered page agrees with the registry on: + +- route, source, and output identity; +- document title and meta description; +- canonical URL policy; +- one visible `h1`; +- `data-page` identity; +- public-navigation destinations; +- sitemap and searchability flags. + +The registry is intentionally useful beyond the current build. Later gates may +consume the same metadata to generate blog indexes, RSS/Atom feeds, paper +catalogues, JSON-LD, related-content links, and a static full-text search index. +Those systems must not introduce a second manually maintained route catalogue. + +## Version boundaries + +- `schema_version` versions the registry contract. +- `content_type` describes the user's task, not a repository directory. +- `maturity_status` describes the current evidence boundary. +- `structured_data_type` is a future publishing hint; it does not cause JSON-LD + to be emitted until the visible page content supports that type. + +## Current public navigation + +```text +Technology | Research | Documentation | About | GitHub +``` + +The blog is deliberately absent until a real blog route and content pipeline +exist. The component laboratory remains an internal validation route and is not +part of public navigation or the sitemap. + +## Working brand line + +The current homepage uses `Correctness under concurrency.` as a working Pinega +brand line. It expresses the programme's central technical concern without +claiming that every proposed technology has already been implemented or +verified. diff --git a/web/content/content-index.json b/web/content/content-index.json new file mode 100644 index 0000000..17994ff --- /dev/null +++ b/web/content/content-index.json @@ -0,0 +1,202 @@ +{ + "schema_version": 1, + "site": { + "name": "Pinega", + "organization": "Pinega Labs", + "tagline": "Correctness under concurrency.", + "summary": "Pinega is a research and engineering programme that turns database-systems research into high-performance software." + }, + "primary_navigation": [ + { "label": "Technology", "route": "/technology/" }, + { "label": "Research", "route": "/research/" }, + { "label": "Documentation", "route": "/docs/" }, + { "label": "About", "route": "/about/" }, + { "label": "GitHub", "href": "https://github.com/likern/research", "external": true } + ], + "entries": [ + { + "id": "home", + "route": "/", + "source_path": "pages/home/index.html", + "output_path": "index.html", + "content_type": "landing", + "canonical_title": "Pinega — correctness under concurrency", + "navigation_title": "Home", + "summary": "Pinega turns database-systems research into high-performance software across storage, transactions, optimisation, verification, and distributed execution.", + "audience": ["prospective-partner", "database-engineer", "researcher"], + "programme": "pinega", + "research_area": null, + "topics": ["database-systems", "high-performance-computing", "correctness", "commercialisation"], + "maturity_status": "research", + "published_at": null, + "updated_at": "2026-08-07", + "authors": ["Pinega Labs"], + "sitemap": true, + "searchable": true, + "structured_data_type": "WebSite", + "public": true, + "canonical": true + }, + { + "id": "technology", + "route": "/technology/", + "source_path": "pages/technology/index.html", + "output_path": "technology/index.html", + "content_type": "technology-catalogue", + "canonical_title": "Technology — Pinega", + "navigation_title": "Technology", + "summary": "Pinega technology programmes connect database research to measured, maintainable software, beginning with Pinega Engine.", + "audience": ["prospective-partner", "database-engineer", "researcher"], + "programme": "pinega", + "research_area": null, + "topics": ["pinega-engine", "query-optimisation", "verification", "distributed-systems"], + "maturity_status": "research", + "published_at": null, + "updated_at": "2026-08-07", + "authors": ["Pinega Labs"], + "sitemap": true, + "searchable": true, + "structured_data_type": "CollectionPage", + "public": true, + "canonical": true + }, + { + "id": "research", + "route": "/research/", + "source_path": "pages/research/index.html", + "output_path": "research/index.html", + "content_type": "research-catalogue", + "canonical_title": "Research — Pinega", + "navigation_title": "Research", + "summary": "The Pinega research programme covers storage, concurrency, transactions, distributed systems, optimisation, performance engineering, and verification.", + "audience": ["database-engineer", "researcher", "contributor"], + "programme": "pinega-research", + "research_area": null, + "topics": ["storage", "concurrency", "transactions", "distributed-systems", "optimisation", "verification"], + "maturity_status": "available", + "published_at": null, + "updated_at": "2026-08-07", + "authors": ["Pinega Labs"], + "sitemap": true, + "searchable": true, + "structured_data_type": "CollectionPage", + "public": true, + "canonical": true + }, + { + "id": "documentation", + "route": "/docs/", + "source_path": "pages/docs/index.html", + "output_path": "docs/index.html", + "content_type": "documentation-landing", + "canonical_title": "Documentation — Pinega", + "navigation_title": "Documentation", + "summary": "Pinega documentation for understanding the programme, reproducing the research and web workspaces, and following technology decisions.", + "audience": ["database-engineer", "contributor", "researcher"], + "programme": "pinega-platform", + "research_area": null, + "topics": ["documentation", "workspace", "architecture", "contributing"], + "maturity_status": "available", + "published_at": null, + "updated_at": "2026-08-07", + "authors": ["Pinega Labs"], + "sitemap": true, + "searchable": true, + "structured_data_type": "CollectionPage", + "public": true, + "canonical": true + }, + { + "id": "getting-started", + "route": "/docs/getting-started/", + "source_path": "pages/docs/getting-started/index.html", + "output_path": "docs/getting-started/index.html", + "content_type": "documentation-start", + "canonical_title": "Getting started — Pinega Documentation", + "navigation_title": "Getting started", + "summary": "Start with the Pinega programme boundary, current repository status, research workspace, web workspace, and validation commands.", + "audience": ["contributor", "database-engineer"], + "programme": "pinega-platform", + "research_area": null, + "topics": ["getting-started", "repository", "nushell", "web", "validation"], + "maturity_status": "available", + "published_at": null, + "updated_at": "2026-08-07", + "authors": ["Pinega Labs"], + "sitemap": true, + "searchable": true, + "structured_data_type": "TechArticle", + "public": true, + "canonical": true + }, + { + "id": "about", + "route": "/about/", + "source_path": "pages/about/index.html", + "output_path": "about/index.html", + "content_type": "about", + "canonical_title": "About Pinega and Pinega Labs", + "navigation_title": "About", + "summary": "Pinega is a master technology programme; Pinega Labs is its working research, engineering, and future company identity.", + "audience": ["prospective-partner", "future-employee", "researcher"], + "programme": "pinega", + "research_area": null, + "topics": ["pinega-labs", "principles", "company", "commercialisation"], + "maturity_status": "research", + "published_at": null, + "updated_at": "2026-08-07", + "authors": ["Pinega Labs"], + "sitemap": true, + "searchable": true, + "structured_data_type": "AboutPage", + "public": true, + "canonical": true + }, + { + "id": "component-lab", + "route": "/component-lab/", + "source_path": "component-lab/index.html", + "output_path": "component-lab/index.html", + "content_type": "internal-validation", + "canonical_title": "Pinega Strata component laboratory", + "navigation_title": "Component laboratory", + "summary": "Internal validation surface for Pinega Strata components, Web Awesome integration, accessibility, and visual regression work.", + "audience": ["web-maintainer"], + "programme": "pinega-platform", + "research_area": null, + "topics": ["design-system", "components", "visual-regression"], + "maturity_status": "internal", + "published_at": null, + "updated_at": "2026-08-07", + "authors": ["Pinega Labs"], + "sitemap": false, + "searchable": false, + "structured_data_type": null, + "public": false, + "canonical": false + }, + { + "id": "not-found", + "route": "/404.html", + "source_path": "pages/404.html", + "output_path": "404.html", + "content_type": "system", + "canonical_title": "Page not found — Pinega", + "navigation_title": "Page not found", + "summary": "The requested Pinega page could not be found.", + "audience": ["all"], + "programme": "pinega-web", + "research_area": null, + "topics": ["not-found"], + "maturity_status": "system", + "published_at": null, + "updated_at": "2026-08-07", + "authors": ["Pinega Labs"], + "sitemap": false, + "searchable": false, + "structured_data_type": null, + "public": false, + "canonical": false + } + ] +} diff --git a/web/content/content.schema.json b/web/content/content.schema.json new file mode 100644 index 0000000..81f3c94 --- /dev/null +++ b/web/content/content.schema.json @@ -0,0 +1,121 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://pinega.example/schemas/web-content-index.schema.json", + "title": "Pinega Web content index", + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "site", "primary_navigation", "entries"], + "properties": { + "schema_version": { "const": 1 }, + "site": { + "type": "object", + "additionalProperties": false, + "required": ["name", "organization", "tagline", "summary"], + "properties": { + "name": { "type": "string", "minLength": 1 }, + "organization": { "type": "string", "minLength": 1 }, + "tagline": { "type": "string", "minLength": 1 }, + "summary": { "type": "string", "minLength": 1 } + } + }, + "primary_navigation": { + "type": "array", + "minItems": 1, + "items": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": ["label", "route"], + "properties": { + "label": { "type": "string", "minLength": 1 }, + "route": { "type": "string", "pattern": "^/" } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": ["label", "href", "external"], + "properties": { + "label": { "type": "string", "minLength": 1 }, + "href": { "type": "string", "format": "uri" }, + "external": { "const": true } + } + } + ] + } + }, + "entries": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/entry" } + } + }, + "$defs": { + "entry": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "route", + "source_path", + "output_path", + "content_type", + "canonical_title", + "navigation_title", + "summary", + "audience", + "programme", + "research_area", + "topics", + "maturity_status", + "published_at", + "updated_at", + "authors", + "sitemap", + "searchable", + "structured_data_type", + "public", + "canonical" + ], + "properties": { + "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" }, + "route": { "type": "string", "pattern": "^/" }, + "source_path": { "type": "string", "minLength": 1 }, + "output_path": { "type": "string", "minLength": 1 }, + "content_type": { "type": "string", "minLength": 1 }, + "canonical_title": { "type": "string", "minLength": 1 }, + "navigation_title": { "type": "string", "minLength": 1 }, + "summary": { "type": "string", "minLength": 1 }, + "audience": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 } + }, + "programme": { "type": "string", "minLength": 1 }, + "research_area": { "type": ["string", "null"] }, + "topics": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 } + }, + "maturity_status": { "type": "string", "minLength": 1 }, + "published_at": { "type": ["string", "null"], "format": "date" }, + "updated_at": { "type": "string", "format": "date" }, + "authors": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "type": "string", "minLength": 1 } + }, + "sitemap": { "type": "boolean" }, + "searchable": { "type": "boolean" }, + "structured_data_type": { "type": ["string", "null"] }, + "public": { "type": "boolean" }, + "canonical": { "type": "boolean" } + } + } + } +} diff --git a/web/pages/404.html b/web/pages/404.html index e084301..9280956 100644 --- a/web/pages/404.html +++ b/web/pages/404.html @@ -18,16 +18,22 @@
Pinega - +
Use dark themeMenu

404 · no matching stratum

That page is not part of the current model.

-

Return to the homepage or open the documentation map to find a supported route.

+

Return to the homepage, inspect the technology programmes, or open the documentation topics to find a supported route.

HomepageDocumentation
+ diff --git a/web/pages/about/index.html b/web/pages/about/index.html new file mode 100644 index 0000000..d8c4fb8 --- /dev/null +++ b/web/pages/about/index.html @@ -0,0 +1,183 @@ + + + + + + About Pinega and Pinega Labs + + + + + + + + + + + + + + + Skip to main content + +
+ +
+ Pinega + +
+ Use dark theme + Menu +
+
+
+ +
+
+
+

Pinega · Pinega Labs

+

A programme for building defensible database technology.

+

+ Pinega is the master technology and product programme. Pinega Labs is the working research, + engineering, and future company identity behind it. The purpose is to turn difficult database + and systems research into software that can be measured, maintained, licensed, supported, and sold. +

+
+
+ RESEARCH + PLATFORM + TECHNOLOGY + PRODUCT + COMPANY +
+
+ +
+
+

Identity model

+

Pinega names the programme; Pinega Labs names the organisation that may commercialise it.

+

The distinction keeps research, engineering infrastructure, product lines, and company claims from collapsing into one ambiguous label.

+
+
+
+ Master brand +

Pinega

+

The umbrella for database-systems research, shared engineering infrastructure, technology programmes, and future products.

+
+
+ Working identity +

Pinega Labs

+

The research and engineering identity used for the programme today and a plausible future company identity.

+
+
+ Future boundary +

Commercial offerings

+

Products, licensing, support, and services will be named only when implementation, ownership, and customer value are concrete.

+
+
+
+ +
+
+
+

Operating principles

+

Correctness, performance, and commercial intent reinforce one another.

+
+
+ + +

Correctness under concurrency

+

Safety, progress, visibility, durability, and failure assumptions are explicit before performance claims are accepted.

+
+ + +

Evidence before claims

+

Implemented, validated, experimental, proposed, and planned work remain visibly distinct in public and internal materials.

+
+ + +

Performance from architecture

+

Cache behaviour, parallelism, hardware fit, and storage layout are treated as design constraints and measured on representative workloads.

+
+ + +

Reusable intellectual property

+

Research should converge toward maintainable libraries, engines, tools, tests, documentation, and defensible product boundaries.

+
+ + +

Upstream respect

+

PostgreSQL, ParadeDB, Vortex, and other systems retain their own identities, licenses, roadmaps, and contribution boundaries.

+
+ + +

Commercial discipline

+

Novelty, feasibility, maintenance, licensing, customer value, and time-to-MVP are evaluated together rather than after the research is complete.

+
+
+
+
+ +
+
+

Current reality

+

The programme is public; the company and products are not yet claimed as established offerings.

+
+
+ +

Available

+

The research workspace, Strata design system, Web platform, Scientific Diagram Language, source repository, and reproducible CI artefacts exist today.

+
+ +

Under development

+

Pinega Engine and related optimiser, verification, and distributed-systems directions remain research and implementation programmes.

+
+ +

Not yet offered

+

There is no current production engine release, paid support contract, customer deployment claim, or registered-company claim on this site.

+
+
+
+ +
+
+

Contact and participation

+

The repository is the current public collaboration boundary.

+

Issues, pull requests, source code, research artefacts, and validation workflows are visible in the canonical repository. A dedicated commercial contact path will be added when the company boundary exists.

+
+ +
+ +
+
+

Explore Pinega

+

Technology shows what is being built; Research shows why it may work.

+

Both remain connected to implementation evidence and explicit maturity labels.

+
+ +
+
+ + +
+ + diff --git a/web/pages/docs/getting-started/index.html b/web/pages/docs/getting-started/index.html index fe54aae..6ac5082 100644 --- a/web/pages/docs/getting-started/index.html +++ b/web/pages/docs/getting-started/index.html @@ -1,17 +1,17 @@ - + Getting started — Pinega Documentation - + - + @@ -26,10 +26,11 @@ Pinega
@@ -42,18 +43,19 @@
@@ -69,44 +71,48 @@

Start here

-

Getting started with the Pinega workspace

-

- This guide documents what can be built and inspected today. It does not present an installable Pinega database engine. -

+

Getting started with the Pinega programme and workspace

+

This guide documents what can be built and inspected today. It does not present an installable Pinega database engine or a commercial product offering.

- Project status: Pinega is in architecture and research-tooling development. Commands below reproduce documentation and web artefacts, not a production database extension. + Programme status: Pinega is in research, architecture, and engineering-platform development. Commands below reproduce documentation, research, and web artefacts—not a production database extension. +
+

Understand the programme boundary

+

Pinega is the master technology programme. Pinega Labs is its working research, engineering, and future company identity. Pinega Engine is the first active implementation programme beneath that umbrella.

+

Use the About page for the identity and commercial boundary, and the Technology page for programme maturity and architecture.

+
+

What is available now

  • A versioned YDMP research workspace with Typst templates, MODEL and VERIFY artefacts.
  • -
  • The Strata cross-platform design system and semantic diagram layer.
  • -
  • A multi-browser Web Components foundation built on native HTML, CSS, JavaScript, and Web Awesome.
  • -
  • Architecture decisions and validation specimens for the future PostgreSQL storage engine.
  • +
  • The Strata cross-platform design system and Scientific Diagram Language.
  • +
  • A semantic static Web Components platform with content metadata, multi-browser gates, and generated discovery files.
  • +
  • Accepted architecture decisions and validation specimens for Pinega Engine.
-

The runnable engine, SQL interface, persistence layer, and performance results remain future milestones.

+

The runnable engine, SQL interface, persistence implementation, customer deployment, and performance results remain future milestones.

-

Architecture contract

+

Pinega Engine architecture contract

The current design separates four responsibilities:

  1. PostgreSQL integration: Table Access Method callbacks, snapshots, transaction identity, and SQL-visible behaviour.
  2. Mutable version store: out-of-place row versions, explicit visibility metadata, and stable handles.
  3. -
  4. Shared buffer pool: stable frames, protect/pin/revalidate, retirement, and reclamation.
  5. -
  6. Durability: PostgreSQL WAL remains the single write-ahead logging boundary.
  7. +
  8. Shared buffer pool: stable frames, protect/pin/revalidate, retirement, reclamation, and cache-aware placement.
  9. +
  10. Durability: PostgreSQL WAL remains the single write-ahead logging and commit boundary.
-

These are design boundaries. Individual modules may still change as implementation evidence accumulates.

+

These are accepted design boundaries. Individual mechanisms may still change as implementation and benchmark evidence accumulates. The Technology page owns the current public architecture summary.

WAL and durability

Current decision

-

Pinega uses one PostgreSQL WAL pipeline. The MVP may log complete row images, while physical standby support can follow after the core storage path is validated.

+

Pinega Engine uses one PostgreSQL WAL pipeline. The MVP may log complete row images, while physical standby support can follow after the core storage path is validated.

@@ -114,10 +120,7 @@

Current decision

Build the research workspace

From the repository root, first verify the pinned toolchain and then compile registered Typst artefacts.

-
- Nushell · research workspace -
-
+
Nushell · research workspace
^nu research.nu doctor
 ^nu research.nu list documents
 ^nu research.nu build all
@@ -127,38 +130,33 @@

Build the research workspace

Build and inspect the website

-

The web workspace uses a committed lockfile and a static multi-page build.

+

The web workspace uses a committed lockfile, a versioned content registry, and a static multi-page build.

-
- Nushell · website -
-
+
Nushell · website
cd web
 ^npm ci --ignore-scripts
 ^npm run build
 ^npm run serve
-

Open http://127.0.0.1:4173. The component laboratory remains available at /component-lab/.

+

Open http://127.0.0.1:4173. The internal component laboratory remains available at /component-lab/ for contributors and visual review, but it is not part of public navigation or the sitemap.

Run the validation gate

-

On RHEL, use the version-matched Playwright container for the complete Chromium, Firefox, and WebKit matrix. Native Node commands remain suitable for token generation, type checking, and the static build.

+

On RHEL, use the version-matched Playwright container for the complete Chromium, Firefox, and WebKit matrix. Native Node commands remain suitable for token generation, type checking, unit tests, and the static build.

-
- Nushell · non-browser checks -
-
+
Nushell · non-browser checks
^npm run tokens:check
 ^npm run typecheck
 ^npm run test:unit
-^npm run build
+^npm run build +^npm run check:build

Where to continue

-

Use the documentation map to follow a topic, or open the research programme to inspect the assumptions behind versioned storage and concurrent lifetimes.

+

Use the documentation topics for task-oriented paths, the technology catalogue for programme boundaries, or the research catalogue for evidence and open questions.

@@ -166,9 +164,9 @@

Where to continue

diff --git a/web/pages/docs/index.html b/web/pages/docs/index.html index c4ad538..77ec568 100644 --- a/web/pages/docs/index.html +++ b/web/pages/docs/index.html @@ -1,17 +1,17 @@ - + - Pinega Documentation - + Documentation — Pinega + - - + + @@ -23,20 +23,16 @@
- - - Pinega - - + Pinega -
Use dark theme Menu @@ -48,91 +44,114 @@
- Documentation v0.1 - Research-stage project + Documentation foundation + Research-stage programme
-

Documentation that distinguishes facts, decisions, and hypotheses.

+

Documentation organised by user intent and evidence maturity.

- Start with the current project status, then follow the architecture, concurrency, durability, and research trails. Pages describe what exists now and label proposed behaviour explicitly. + Use the start, explanation, how-to, research, and reference paths below. The current corpus is deliberately small: + cards point to real pages or real sections rather than promising documentation that does not yet exist.

- Current scope: the workspace, design system, and architecture artefacts are runnable. The PostgreSQL storage engine is not yet a production release. + Current scope: the research workspace, Web platform, design system, and architecture artefacts are runnable or inspectable. Pinega Engine is not yet an installable production release.
- +
-

Find a topic

-

Documentation map

+

Current corpus

+

Filter documentation topics

8 topics

- +
-
+
Available +

Start

Getting started

-

Understand the current scope, repository layout, and commands that work today.

+

Understand the programme boundary, repository status, and commands that work today.

-
- Design contract -

Architecture overview

-

See the PostgreSQL boundary, mutable store, buffer pool, and WAL decomposition.

+
+ Current framing +

Explanation

+

What Pinega is

+

Distinguish the master programme, Pinega Labs, technology programmes, and future commercial boundaries.

-
- Research -

Versioned storage and MVCC

-

Track the assumptions behind out-of-place versions and visibility evaluation.

+
+ Programme map +

Explanation

+

Technology programmes

+

See active design, research infrastructure, portfolio directions, and promotion rules.

-
- Research -

Concurrent frame lifetimes

-

Follow publication, protection, revalidation, retirement, and reclamation.

+
+ Design contract +

Explanation

+

Pinega Engine architecture

+

Follow the PostgreSQL boundary, mutable version store, buffer pool, and WAL decisions.

-
- Decision -

WAL and durability

-

Document the one-PostgreSQL-WAL decision and its current boundaries.

+
+ Active research +

Research

+

Research programme

+

Browse the research-area taxonomy, evidence method, and current technical studies.

-
+ -
+
Available -

Web workspace

-

Build the multi-page site, run the component laboratory, and execute browser gates.

+

How-to

+

Build and validate the Web workspace

+

Build the static site, start the local server, and run the non-browser and browser gates.

-
- +
+
+
+

Content types

+

Purpose and maturity are separate labels.

+

A page can be a how-to and still describe experimental work; it can be an explanation and document an accepted decision.

+
+
+
Start
A guided first path through current scope and working commands.
+
How-to
Task-oriented steps for building, validating, or contributing.
+
Explanation
Architecture, concepts, trade-offs, and programme boundaries.
+
Reference
Exact commands, schemas, manifests, APIs, and repository contracts.
+
+
+

Recommended path

Read in the order that preserves the evidence boundary.

    -
  1. 01
    Current status

    Learn which artefacts are runnable and which claims remain proposed.

  2. -
  3. 02
    Architecture contract

    Understand the ownership and durability boundaries before implementation details.

  4. -
  5. 03
    Concurrency model

    Study object histories, lifetime protection, and reclamation assumptions.

  6. +
  7. 01
    Programme boundary

    Understand Pinega, Pinega Labs, technology programmes, and what is not yet offered.

  8. +
  9. 02
    Current status

    Learn which artefacts are runnable and which claims remain proposed.

  10. +
  11. 03
    Architecture and research

    Follow ownership, correctness, and durability boundaries before implementation detail.

  12. 04
    Implementation evidence

    Only then interpret benchmarks, traces, tests, and future prototype results.

-

Documentation maturity

-

Labels are part of the contract.

+

Evidence maturity

+

Labels are part of the public contract.

Available
A runnable or inspectable artefact exists in the repository.
@@ -145,16 +164,8 @@

Labels are part of the contract.

diff --git a/web/pages/home/index.html b/web/pages/home/index.html index 9be6d63..e41c4f9 100644 --- a/web/pages/home/index.html +++ b/web/pages/home/index.html @@ -3,15 +3,15 @@ - Pinega — a research-stage PostgreSQL storage engine - + Pinega — correctness under concurrency + - - + + @@ -30,10 +30,11 @@ @@ -49,43 +50,44 @@
- Research stage - PostgreSQL 19 target + Research and engineering programme + Database systems · Pinega Labs
-

A storage engine designed around explicit versions and concurrent lifetimes.

+

Correctness under concurrency.

- Pinega is an architecture and implementation programme for high-contention OLTP inside PostgreSQL. - The current work validates storage, buffer-pool, WAL, and correctness contracts; it is not a production release. + Pinega turns database-systems research into high-performance software across storage, + transactions, optimisation, verification, and distributed execution. Pinega Engine is + the first active implementation programme; it is not yet a production release.

@@ -93,110 +95,140 @@

A storage engine designed around explicit versions and concurrent lifetimes.
-

Current project status

+

Current programme status

Available now -

Research workspace

-

Versioned notes, formal models, diagrams, and reproducible web and Typst tooling.

+

Research and web platform

+

Versioned research artefacts, Strata design tokens, semantic web pages, and reproducible validation gates.

- Validated -

Architecture specimens

-

Storage and concurrency contracts are exercised as explicit models and documentation artefacts.

+ Active programme +

Pinega Engine

+

An extension-oriented PostgreSQL OLTP architecture under design and implementation validation.

- Next milestone -

Runnable engine prototype

-

The PostgreSQL extension and mutable-store prototype remain implementation work, not a shipped feature.

+ Portfolio direction +

Optimisation, verification, and distributed systems

+

Research directions with explicit product potential, not shipped products or performance claims.

-

Design thesis

-

Make the hot path explicit enough to reason about.

+

Programme thesis

+

Performance matters only when the result remains correct and explainable.

- Pinega treats visibility, publication, pinning, reclamation, and durability as separate contracts instead of hiding them inside one mutable heap abstraction. + Pinega connects formal reasoning, systems experiments, production engineering, and commercial intent. + The objective is reusable intellectual property and measurable customer value, not isolated demonstrations.

-

Version-first storage

-

Out-of-place row versions make history and visibility data explicit, enabling specialised access paths and later SIMD evaluation.

+

Correctness under concurrency

+

Histories, visibility, publication, lifetime protection, durability, and failure boundaries are explicit design objects.

-

Protected lifetimes

-

Candidate handles are protected, pinned, and revalidated before use; reclamation is modelled as a protocol rather than an afterthought.

+

Performance with evidence

+

Cache behaviour, NUMA placement, SIMD, storage media, and query planning are measured rather than advertised in advance.

-

One durability boundary

-

The current architecture keeps one PostgreSQL WAL pipeline and avoids inventing an independent durability subsystem.

+

Research to product

+

Papers and hypotheses move through models, verification, implementation, benchmarks, and documented product boundaries.

-
+
-

Proposed architecture

-

Four strata with explicit ownership boundaries.

-

- The diagram is a design contract. It describes the intended decomposition; it does not claim that every layer is implemented. -

+

Technology programmes

+

One programme, several paths to defensible database software.

+

Programme labels describe active research and engineering boundaries. They do not imply commercial availability.

-
-
    -
  1. - 01 -
    PostgreSQL integrationTable AM callbacks, snapshots, transactions, SQL-facing behaviour
    - target -
  2. -
  3. - 02 -
    Mutable version storeOut-of-place tuples, stable handles, visibility metadata
    - design -
  4. -
  5. - 03 -
    Shared buffer poolStable frames, protect/pin/revalidate, cache-aware placement
    - research -
  6. -
  7. - 04 -
    PostgreSQL WALOne write-ahead log and one transaction durability boundary
    - decision -
  8. -
-
- Proposed Pinega OLTP path from PostgreSQL callbacks to version storage, frame lifetime protection, and PostgreSQL WAL. -
-
+
+
+ Active design +

Pinega Engine

+

Version-first OLTP storage, a Pinega-owned shared buffer pool, PostgreSQL integration, and one PostgreSQL WAL boundary.

+ Inspect the engine programme +
+
+ Research +

Query optimisation and AI

+

Cardinality reasoning, query rewrites, MCTS/RL join ordering, and low-latency inference for future optimiser products.

+ Inspect the optimisation direction +
+
+ Research platform +

Correctness and verification

+

Executable models, deterministic testing, semantic diagrams, and tools for validating concurrent and transactional systems.

+ Inspect the verification direction +
+
-
+
-

Correctness before claims

-

Evidence remains labelled by maturity and origin.

+

Featured active programme

+

Pinega Engine: an explicit OLTP path inside PostgreSQL.

+

+ The current architecture separates PostgreSQL integration, mutable versions, shared-frame lifetimes, and durability. + It is an accepted design direction under implementation validation, not an installable release. +

+
+
    +
  1. + 01 +
    PostgreSQL integrationTable AM callbacks, snapshots, transactions, and SQL-visible behaviour
    + target +
  2. +
  3. + 02 +
    Mutable version storeOut-of-place tuples, stable handles, and explicit visibility metadata
    + design +
  4. +
  5. + 03 +
    Shared buffer poolStable frames, protect/pin/revalidate, cache-aware placement, and reclamation
    + research +
  6. +
  7. + 04 +
    PostgreSQL WALOne write-ahead log and one transaction durability boundary
    + decision +
  8. +
+
+ Proposed Pinega Engine OLTP path. Follow the Technology page for programme scope, decisions, and maturity. +
+
+
+ +
+
+

Research-to-product discipline

+

Claims advance only when their evidence advances.

+
- -

Confirmed design decision

-

Pinega uses PostgreSQL WAL as the durability pipeline for the current architecture.

+ +

Source and formalise

+

Start from papers, standards, source code, tests, and measured system behaviour. Keep external evidence separate from inference.

-

Research hypothesis

-

A cache-aware, partitioned buffer pool may reduce coherence traffic under high contention.

+

Prototype and falsify

+

Turn architectural ideas into executable models, focused prototypes, deterministic schedules, and benchmarks with explicit failure criteria.

- -

External evidence

-

Published concurrent algorithms inform the design, but must still be adapted and verified against PostgreSQL process semantics.

+ +

Implement and productise

+

Promote only validated capability into maintained software, documented support boundaries, licensing, and customer-facing claims.

@@ -204,19 +236,20 @@

External evidence

Maturity ledger

-

What exists, what is being validated, and what is still planned.

+

What exists, what is being validated, and what remains proposed.

- + - - - - + + + + +
AreaCurrent stateEvidence
AreaCurrent stateEvidence boundary
Research workspaceAvailableTypst templates, semantic diagrams, MODEL and VERIFY artefacts
Web design systemAvailableCanonical tokens, Web Components, browser and accessibility gates
Storage architectureUnder validationDocumented contracts and implementation specimens
PostgreSQL enginePlannedNo production binary or performance claim yet
YDMP research workspaceAvailableVersioned notes, MODEL/VERIFY artefacts, build commands, and CI
Pinega Strata and Web platformAvailableCanonical tokens, Web Components, static pages, and browser/accessibility gates
Scientific Diagram LanguageAvailableCanonical models, Web/Typst renderers, layout profiles, and review artifacts
Pinega Engine architectureUnder validationAccepted decisions and executable specimens; no production engine binary
Future product programmesResearchNo commercial availability or measured product claims yet
@@ -224,13 +257,13 @@

What exists, what is being validated, and what is still p
-

Start with the evidence

-

Read the architecture and reproduce the workspace.

-

The documentation begins with the current status, repository layout, and commands that work today.

+

Start with the boundary

+

Understand the programmes, then inspect the evidence.

+

Technology describes what Pinega is building; Research explains why; Documentation shows what can be reproduced today.

@@ -241,12 +274,13 @@

Read the architecture and reproduce the workspace.

Pinega -

Pinega is a research-stage database systems programme by Pinega Labs.

+

Correctness under concurrency. Database systems research and engineering by Pinega Labs.

diff --git a/web/pages/research/index.html b/web/pages/research/index.html index f91aaf4..197fc76 100644 --- a/web/pages/research/index.html +++ b/web/pages/research/index.html @@ -3,15 +3,15 @@ - Pinega Research - + Research — Pinega + - - + + @@ -26,10 +26,11 @@ Pinega
@@ -42,10 +43,11 @@
-

Pinega Labs

+

Pinega Research

Research is part of the product boundary, not a marketing appendix.

- Pinega keeps formal definitions, learner interpretations, external evidence, architecture decisions, and unresolved questions as distinct artefacts. The goal is to make implementation claims traceable to the reasoning that supports them. + Pinega studies database systems where correctness, concurrency, performance, and failure semantics interact. + Claims remain traceable to sources, models, experiments, implementation evidence, and unresolved questions.

@@ -53,23 +55,40 @@

Research is part of the product boundary, not a marketin

-
+
+
+

Research-area catalogue

+

Seven connected areas feed Pinega technology programmes.

+

The catalogue establishes stable ownership for current and future work without pretending that every area already has a public paper, experiment, or product.

+
+
+
Active

Storage and execution

MVCC layouts, Table AM integration, buffer management, indexing, vectorised visibility, and later analytical storage.

+
Active

Concurrency and memory reclamation

Atomic publication, ABA prevention, stable handles, helping, pins, HP, EBR/QSBR/RCU, and process-safe reclamation.

+
Active

Transactions and correctness

MVCC, serializability, SSI, SSN/ESSN, dependency graphs, anomalies, and PostgreSQL compatibility.

+
Programme

Distributed systems

Replication, consensus, clocks, distributed transactions, failure models, MPP execution, and one-copy correctness.

+
Active

Query optimisation and AI

Cardinality, decorrelation, rewrites, materialised views, recursive queries, MCTS/RL join ordering, and edge inference.

+
Active

Performance engineering and hardware

Cache coherence, NUMA, false sharing, per-CPU sharding, batching, SIMD/AVX-512, NVM, SSD, and ZNS.

+
Platform

Verification and deterministic testing

Linearizability, memory models, executable specifications, stress schedules, trace capture, and visual evidence.

+
+
+ +

YDMP Research Workspace

A workflow that preserves uncertainty and provenance.

-

The workspace separates captured sessions, canonical models, verification ledgers, and implementation evidence.

+

External sources, learner interpretations, canonical models, verification ledgers, experiments, and implementation claims remain separate artefacts.

-

Capture

Record questions, answers, corrections, and source scope without rewriting history.

-

Canonical model

Consolidate definitions, invariants, examples, and dependencies into a stable explanatory model.

-

Verification ledger

Track confirmed, inferred, external, contradicted, and unresolved claims explicitly.

+

Capture

Record questions, answers, corrections, source scope, and reading frontiers without rewriting history.

+

Canonical model

Consolidate definitions, invariants, examples, dependencies, and accepted project decisions.

+

Verification ledger

Track confirmed, inferred, external, contradicted, experimental, and unresolved claims explicitly.

-

Research direction 01

+

Active study 01

Versioned OLTP storage

@@ -90,7 +109,7 @@

Open hypothesis

Shared semantic model

Snapshot selection over a newest-to-oldest version chain

-

The same version records and visibility selection drive this SVG and the Typst/CeTZ publication specimen.

+

The current figure remains a validated cross-renderer specimen. No new diagram family is introduced by the Web IA milestone.

@@ -98,7 +117,7 @@

Snapshot selection over a newest-to-oldest version chain

-

Research direction 02

+

Active study 02

Concurrent object lifetimes and reclamation

@@ -119,7 +138,7 @@

Publication, withdrawal, quiescence, and generation-safe reuse

-

Research direction 03

+

Active study 03

Concurrent histories as explicit data

Calls, responses, real-time precedence, candidate linearization points, and sequential witnesses are stored independently from either renderer.

@@ -127,31 +146,28 @@

Concurrent histories as explicit data

-
+
-

Current programme

-

Topics that feed the Pinega architecture.

+

Research outputs

+

The catalogue will grow by adding real artefacts, not empty route placeholders.

-
-

PostgreSQL MVCC

Visibility, snapshots, tuple headers, indexes, and the extension boundary.

-

Lock-free buffer management

Publication, pins, ABA mitigation, helping, and safe memory reclamation.

-

Cache-aware concurrency

NUMA, false sharing, ownership migration, sharding, batching, and SIMD validation.

-

Formal correctness

Linearizability, sequential consistency, progress conditions, and executable models.

-

Durability and recovery

One PostgreSQL WAL, row-image trade-offs, replication, and crash boundaries.

-

Deterministic testing

Stress schedules, model checking, trace capture, and regression artefacts.

+
+

Source material

Papers, standards, official documentation, source code, tests, release notes, datasets, and benchmark specifications.

+

Pinega artefacts

Preparation packets, captured sessions, canonical models, verification ledgers, diagrams, prototypes, tests, and measured results.

+

Unresolved work

Gaps, competing designs, falsifiable hypotheses, risk registers, and proposed implementation milestones.

-

Inspect the artefacts

Follow the documentation or read the repository directly.

Research claims remain useful only when their source, status, and implementation boundary are visible.

- +

Inspect the boundary

Technology consumes research; documentation exposes reproducible work.

The next publishing gates will add real project, paper, experiment, and blog indexes on top of this taxonomy.

+
diff --git a/web/pages/technology/index.html b/web/pages/technology/index.html new file mode 100644 index 0000000..13f0c34 --- /dev/null +++ b/web/pages/technology/index.html @@ -0,0 +1,190 @@ + + + + + + Technology — Pinega + + + + + + + + + + + + + + + Skip to main content + +
+ +
+ Pinega + +
+ Use dark theme + Menu +
+
+
+ +
+
+
+

Technology programmes

+

Research becomes technology only after its boundaries survive implementation.

+

+ Pinega groups related database-systems work into programmes with explicit maturity, evidence, and product intent. + Pinega Engine is the first active implementation programme; the other directions remain research or portfolio candidates. +

+
+
+ RESEARCH + MODEL + IMPLEMENT + MEASURE + PRODUCT +
+
+ +
+
+

Current portfolio map

+

Programmes are named by ownership and evidence, not by marketing ambition.

+

No item below is presented as a shipped product unless a runnable, supported release and measured claim exist.

+
+ +
+
+ Active design +

Pinega Engine

+

An extension-oriented PostgreSQL OLTP engine with out-of-place versions, Pinega-owned persistence and buffer management, and one PostgreSQL WAL boundary.

+ Architecture and decisions +
+
+ Research +

Query optimisation and AI

+

Cardinality reasoning, decorrelation, dependency-driven rewrites, materialised views, recursive queries, and MCTS/RL join-order selection.

+ Research areas +
+
+ Research platform +

Correctness and verification tooling

+

Executable histories, deterministic testing, lifecycle models, semantic diagrams, and future tools for concurrent and transactional correctness.

+ Research method +
+
+ Portfolio direction +

Distributed systems

+

Replication, consensus, clocks, distributed transactions, failure models, and MPP execution remain research directions without a committed product boundary.

+ Research areas +
+
+
+ +
+
+
+

Pinega Engine

+

An OLTP architecture with explicit storage, lifetime, and durability contracts.

+

This is an accepted programme direction for PostgreSQL 19. Individual mechanisms remain subject to implementation and measurement.

+
+ +
+
    +
  1. + 01 +
    PostgreSQL integrationExtension-only Table AM boundary, callbacks, snapshots, transaction identity, and SQL-visible behaviour
    + target +
  2. +
  3. + 02 +
    Mutable version storeOut-of-place versions, explicit visibility metadata, stable identities, and postponed analytical storage
    + design +
  4. +
  5. + 03 +
    Pinega buffer poolShared-memory frames, protect/pin/revalidate, cache-aware placement, retirement, and reclamation
    + research +
  6. +
  7. + 04 +
    PostgreSQL WALOne authoritative durability and transaction-commit pipeline for the current architecture
    + decision +
  8. +
+
The OLAP/Vortex direction is deliberately excluded from the OLTP MVP and remains a later programme boundary.
+
+
+
+ +
+
+

Accepted boundaries

+

Current decisions are explicit, but implementation maturity remains visible.

+
+
+ +

One PostgreSQL WAL

+

The current engine architecture uses PostgreSQL WAL rather than creating a second independent commit and durability subsystem.

+
+ +

Extension-only first

+

The active direction targets PostgreSQL 19 through supported extension boundaries and a narrow C ABI around Rust/pgrx code.

+
+ +

Performance remains a hypothesis

+

Version layout, cache behaviour, contention, recovery, and operational cost must be measured against PostgreSQL heap and relevant engines.

+
+
+
+ +
+
+

Promotion rule

+

A programme becomes a product only after six boundaries are credible.

+
+
    +
  1. 01
    Novelty

    The capability is meaningfully differentiated from upstream and existing products.

  2. +
  3. 02
    Correctness

    Safety, progress, durability, and failure assumptions are stated and tested.

  4. +
  5. 03
    Performance

    Representative benchmarks demonstrate value without hiding operational costs.

  6. +
  7. 04
    Product boundary

    Users, workloads, compatibility, deployment, support, and non-goals are explicit.

  8. +
  9. 05
    Maintainability

    CI, reproducible builds, observability, documentation, and upgrade paths exist.

  10. +
  11. 06
    Commercial fit

    Licensing, IP, customer value, and portfolio ownership support a sustainable offering.

  12. +
+
+ +
+
+

Follow the evidence

+

Research explains the claims; documentation explains what works today.

+

The technology catalogue intentionally separates active design, research infrastructure, and portfolio directions.

+
+ +
+
+ + +
+ + diff --git a/web/scripts/build.mjs b/web/scripts/build.mjs old mode 100644 new mode 100755 index 42fb656..92a9ff3 --- a/web/scripts/build.mjs +++ b/web/scripts/build.mjs @@ -10,25 +10,15 @@ const siteOrigin = normalizeSiteOrigin(process.env.PINEGA_SITE_ORIGIN ?? 'https: const diagramRoot = resolve(root, '../design/diagrams'); const diagramModelRoot = resolve(diagramRoot, 'models'); const diagramBuildRoot = resolve(dist, '.diagram-build'); - -const pages = [ - { source: 'pages/home/index.html', output: 'index.html', route: '/', sitemap: true }, - { source: 'pages/docs/index.html', output: 'docs/index.html', route: '/docs/', sitemap: true }, - { - source: 'pages/docs/getting-started/index.html', - output: 'docs/getting-started/index.html', - route: '/docs/getting-started/', - sitemap: true, - }, - { source: 'pages/research/index.html', output: 'research/index.html', route: '/research/', sitemap: true }, - { - source: 'component-lab/index.html', - output: 'component-lab/index.html', - route: '/component-lab/', - sitemap: false, - }, - { source: 'pages/404.html', output: '404.html', route: '/404.html', sitemap: false }, -]; +const contentRoot = resolve(root, 'content'); +const contentIndex = validateContentIndex( + JSON.parse(await readFile(resolve(contentRoot, 'content-index.json'), 'utf8')), +); +const pages = contentIndex.entries.map(entry => ({ + ...entry, + source: entry.source_path, + output: entry.output_path, +})); await import('../../design/scripts/build-tokens.mjs'); await rm(dist, { recursive: true, force: true }); @@ -57,6 +47,7 @@ await build({ for (const page of pages) { const source = await readFile(resolve(root, page.source), 'utf8'); + validatePageSource(source, page, contentIndex.primary_navigation); const html = replaceDiagramPlaceholders( source .replaceAll('{{SITE_ORIGIN}}', escapeHtml(siteOrigin)) @@ -82,6 +73,7 @@ try { } await cp(diagramRoot, resolve(dist, 'diagrams'), { recursive: true }); +await cp(contentRoot, resolve(dist, 'content'), { recursive: true }); await rm(diagramBuildRoot, { recursive: true, force: true }); const webAwesomeAssets = resolve(root, 'node_modules/@awesome.me/webawesome/dist/assets'); @@ -102,7 +94,30 @@ await writeFile( resolve(dist, 'site-manifest.json'), `${JSON.stringify({ origin: siteOrigin, - routes: pages.map(({ route, output, sitemap }) => ({ route, output, sitemap })), + site: contentIndex.site, + primaryNavigation: contentIndex.primary_navigation, + routes: pages.map(page => ({ + id: page.id, + route: page.route, + output: page.output, + contentType: page.content_type, + title: page.canonical_title, + navigationTitle: page.navigation_title, + summary: page.summary, + audience: page.audience, + programme: page.programme, + researchArea: page.research_area, + topics: page.topics, + maturityStatus: page.maturity_status, + publishedAt: page.published_at, + updatedAt: page.updated_at, + authors: page.authors, + sitemap: page.sitemap, + searchable: page.searchable, + structuredDataType: page.structured_data_type, + public: page.public, + canonical: page.canonical, + })), diagrams: diagrams.ids.map(id => ({ id, model: `/diagrams/models/${id}.json`, @@ -111,7 +126,9 @@ await writeFile( 'utf8', ); -console.log(`Built Pinega website at ${dist} with ${diagrams.ids.length} semantic diagrams`); +console.log( + `Built Pinega website at ${dist} with ${pages.length} registered pages and ${diagrams.ids.length} semantic diagrams`, +); async function buildSemanticDiagrams() { const rendererPath = resolve(diagramBuildRoot, 'renderer.mjs'); @@ -151,6 +168,173 @@ async function buildSemanticDiagrams() { return { figures, ids }; } +function validateContentIndex(value) { + if (!value || typeof value !== 'object' || Array.isArray(value)) { + throw new TypeError('content-index.json must contain an object'); + } + if (value.schema_version !== 1) { + throw new TypeError(`Unsupported content index schema: ${JSON.stringify(value.schema_version)}`); + } + if (!value.site || typeof value.site !== 'object') { + throw new TypeError('Content index must define site metadata'); + } + for (const field of ['name', 'organization', 'tagline', 'summary']) { + if (typeof value.site[field] !== 'string' || value.site[field].trim() === '') { + throw new TypeError(`Content index site.${field} must be a non-empty string`); + } + } + if (!Array.isArray(value.primary_navigation) || value.primary_navigation.length === 0) { + throw new TypeError('Content index must define primary_navigation'); + } + if (!Array.isArray(value.entries) || value.entries.length === 0) { + throw new TypeError('Content index must define entries'); + } + + const identities = new Set(); + const routes = new Set(); + const sources = new Set(); + const outputs = new Set(); + const requiredStrings = [ + 'id', + 'route', + 'source_path', + 'output_path', + 'content_type', + 'canonical_title', + 'navigation_title', + 'summary', + 'programme', + 'maturity_status', + 'updated_at', + ]; + + for (const entry of value.entries) { + if (!entry || typeof entry !== 'object' || Array.isArray(entry)) { + throw new TypeError('Every content entry must be an object'); + } + for (const field of requiredStrings) { + if (typeof entry[field] !== 'string' || entry[field].trim() === '') { + throw new TypeError(`${entry.id ?? ''}.${field} must be a non-empty string`); + } + } + if (!/^[a-z][a-z0-9-]*$/u.test(entry.id)) { + throw new TypeError(`Invalid content id: ${JSON.stringify(entry.id)}`); + } + if (!entry.route.startsWith('/')) { + throw new TypeError(`${entry.id}: route must start with /`); + } + if (entry.route !== '/' && !entry.route.endsWith('/') && !entry.route.endsWith('.html')) { + throw new TypeError(`${entry.id}: route must end with / or .html`); + } + if (!Array.isArray(entry.audience) || entry.audience.length === 0) { + throw new TypeError(`${entry.id}: audience must be a non-empty array`); + } + if (!Array.isArray(entry.topics) || entry.topics.length === 0) { + throw new TypeError(`${entry.id}: topics must be a non-empty array`); + } + if (!Array.isArray(entry.authors) || entry.authors.length === 0) { + throw new TypeError(`${entry.id}: authors must be a non-empty array`); + } + for (const flag of ['sitemap', 'searchable', 'public', 'canonical']) { + if (typeof entry[flag] !== 'boolean') { + throw new TypeError(`${entry.id}.${flag} must be boolean`); + } + } + if (!/^\d{4}-\d{2}-\d{2}$/u.test(entry.updated_at)) { + throw new TypeError(`${entry.id}: updated_at must use YYYY-MM-DD`); + } + if (entry.published_at !== null && !/^\d{4}-\d{2}-\d{2}$/u.test(entry.published_at)) { + throw new TypeError(`${entry.id}: published_at must be null or YYYY-MM-DD`); + } + if (entry.research_area !== null && typeof entry.research_area !== 'string') { + throw new TypeError(`${entry.id}: research_area must be a string or null`); + } + if (entry.structured_data_type !== null && typeof entry.structured_data_type !== 'string') { + throw new TypeError(`${entry.id}: structured_data_type must be a string or null`); + } + if (entry.sitemap && (!entry.public || !entry.canonical)) { + throw new TypeError(`${entry.id}: sitemap entries must be public and canonical`); + } + if (entry.searchable && !entry.public) { + throw new TypeError(`${entry.id}: searchable entries must be public`); + } + + addUnique(identities, entry.id, 'content id'); + addUnique(routes, entry.route, 'route'); + addUnique(sources, entry.source_path, 'source path'); + addUnique(outputs, entry.output_path, 'output path'); + } + + if (!routes.has('/')) throw new TypeError('Content index must contain the homepage route'); + for (const item of value.primary_navigation) { + if (!item || typeof item !== 'object' || typeof item.label !== 'string') { + throw new TypeError('Every primary-navigation item must define a label'); + } + if ('route' in item) { + const target = value.entries.find(entry => entry.route === item.route); + if (!target?.public) { + throw new TypeError(`Primary navigation route is not public: ${JSON.stringify(item.route)}`); + } + } else if ('href' in item) { + const url = new URL(item.href); + if (url.protocol !== 'https:' && url.protocol !== 'http:') { + throw new TypeError(`Unsupported primary-navigation URL: ${item.href}`); + } + if (item.external !== true) { + throw new TypeError(`External navigation item must set external=true: ${item.label}`); + } + } else { + throw new TypeError(`Primary navigation item has no route or href: ${item.label}`); + } + } + + return value; +} + +function validatePageSource(html, page, primaryNavigation) { + const title = html.match(/([^<]+)<\/title>/u)?.[1]; + if (title !== page.canonical_title) { + throw new TypeError(`${page.source}: expected title ${JSON.stringify(page.canonical_title)}, got ${JSON.stringify(title)}`); + } + const description = html.match(/<meta name="description" content="([^"]+)">/u)?.[1]; + if (description !== page.summary) { + throw new TypeError(`${page.source}: meta description does not match the content registry`); + } + const pageId = html.match(/<html\b[^>]*\bdata-page="([^"]+)"/u)?.[1]; + if (pageId !== page.id) { + throw new TypeError(`${page.source}: expected data-page=${JSON.stringify(page.id)}`); + } + if ((html.match(/<h1\b/gu) ?? []).length !== 1) { + throw new TypeError(`${page.source}: every registered page must contain exactly one h1`); + } + const canonical = `<link rel="canonical" href="{{SITE_ORIGIN}}${page.route}">`; + if (page.canonical && !html.includes(canonical)) { + throw new TypeError(`${page.source}: missing canonical template ${canonical}`); + } + if (!page.canonical && /<link rel="canonical"/u.test(html)) { + throw new TypeError(`${page.source}: non-canonical content must not emit a canonical link`); + } + + if (page.public) { + const navigation = html.match(/<nav\b[^>]*data-primary-navigation[^>]*>[\s\S]*?<\/nav>/u)?.[0]; + if (!navigation) throw new TypeError(`${page.source}: missing primary navigation`); + for (const item of primaryNavigation) { + const destination = 'route' in item ? item.route : item.href; + if (!navigation.includes(`href="${destination}"`)) { + throw new TypeError(`${page.source}: primary navigation is missing ${destination}`); + } + } + if (navigation.includes('/component-lab/')) { + throw new TypeError(`${page.source}: component lab must not appear in public primary navigation`); + } + } +} + +function addUnique(values, value, label) { + if (values.has(value)) throw new TypeError(`Duplicate ${label}: ${JSON.stringify(value)}`); + values.add(value); +} + function replaceDiagramPlaceholders(html, figures, sourcePath) { const output = html.replace(/<!--\s*PINEGA_DIAGRAM:([a-z][a-z0-9-]*)\s*-->/gu, (_match, id) => { const figure = figures.get(id); diff --git a/web/scripts/check-build.mjs b/web/scripts/check-build.mjs old mode 100644 new mode 100755 index 22e2d5c..b939ac1 --- a/web/scripts/check-build.mjs +++ b/web/scripts/check-build.mjs @@ -9,26 +9,29 @@ const diagramIds = [ 'linearizability-overlap', 'version-chain-snapshot', ]; +const contentIndex = JSON.parse( + await readFile(resolve(root, 'content/content-index.json'), 'utf8'), +); const required = [ - 'index.html', - 'docs/index.html', - 'docs/getting-started/index.html', - 'research/index.html', - 'component-lab/index.html', - '404.html', + ...contentIndex.entries.map(entry => entry.output_path), 'robots.txt', 'sitemap.xml', 'site-manifest.json', 'favicon.svg', 'assets/main.js', 'assets/main.css', + 'content/README.md', + 'content/content-index.json', + 'content/content.schema.json', 'diagrams/README.md', 'diagrams/schema/diagram.schema.json', 'diagrams/layouts/profiles.json', ...diagramIds.map(id => `diagrams/models/${id}.json`), ]; -for (const path of required) assert.ok(await isFile(resolve(root, path)), `Missing build output: ${path}`); +for (const path of required) { + assert.ok(await isFile(resolve(root, path)), `Missing build output: ${path}`); +} const files = await walk(root); const totals = new Map(); @@ -43,14 +46,81 @@ const css = totals.get('.css') ?? 0; assert.ok(javascript <= 450 * 1024, `JavaScript budget exceeded: ${javascript} bytes`); assert.ok(css <= 190 * 1024, `CSS budget exceeded: ${css} bytes`); -for (const path of required.filter(path => path.endsWith('.html'))) { - const html = await readFile(resolve(root, path), 'utf8'); - assert.doesNotMatch(html, /\{\{SITE_ORIGIN\}\}|PINEGA_PROJECT_META|PINEGA_DIAGRAM:/u, `${path} contains an unresolved build marker`); - assert.match(html, /\/assets\/main\.css/u, `${path} does not load the shared stylesheet`); - assert.match(html, /\/assets\/main\.js/u, `${path} does not load the shared module`); - assert.match(html, /<main\b/u, `${path} does not contain main content`); +for (const entry of contentIndex.entries) { + const html = await readFile(resolve(root, entry.output_path), 'utf8'); + assert.doesNotMatch( + html, + /\{\{SITE_ORIGIN\}\}|PINEGA_PROJECT_META|PINEGA_DIAGRAM:/u, + `${entry.output_path} contains an unresolved build marker`, + ); + assert.match(html, /\/assets\/main\.css/u, `${entry.output_path} does not load the shared stylesheet`); + assert.match(html, /\/assets\/main\.js/u, `${entry.output_path} does not load the shared module`); + assert.match(html, /<main\b/u, `${entry.output_path} does not contain main content`); + assert.equal((html.match(/<h1\b/gu) ?? []).length, 1, `${entry.output_path} must contain one h1`); + assert.match(html, new RegExp(`<title>${escapeRegex(entry.canonical_title)}<\\/title>`, 'u')); + assert.match( + html, + new RegExp(`<meta name="description" content="${escapeRegex(entry.summary)}">`, 'u'), + ); + + if (entry.canonical) { + assert.match( + html, + new RegExp(`<link rel="canonical" href="https:\/\/pinega\\.example${escapeRegex(entry.route)}">`, 'u'), + `${entry.output_path} has no generated canonical URL`, + ); + } else { + assert.doesNotMatch(html, /<link rel="canonical"/u, `${entry.output_path} must remain non-canonical`); + } } +const manifest = JSON.parse(await readFile(resolve(root, 'site-manifest.json'), 'utf8')); +assert.equal(manifest.site.tagline, 'Correctness under concurrency.'); +assert.deepEqual(manifest.primaryNavigation, contentIndex.primary_navigation); +assert.deepEqual( + manifest.routes.map(entry => entry.id), + contentIndex.entries.map(entry => entry.id), +); +assert.deepEqual( + manifest.routes.map(entry => entry.route), + contentIndex.entries.map(entry => entry.route), +); +assert.deepEqual( + manifest.routes.filter(entry => entry.sitemap).map(entry => entry.route), + ['/', '/technology/', '/research/', '/docs/', '/docs/getting-started/', '/about/'], +); +assert.deepEqual( + manifest.routes.filter(entry => entry.searchable).map(entry => entry.id), + ['home', 'technology', 'research', 'documentation', 'getting-started', 'about'], +); +assert.deepEqual(manifest.diagrams.map(entry => entry.id).toSorted(), diagramIds.toSorted()); + +const publicNavigation = manifest.primaryNavigation.map(item => item.route ?? item.href); +assert.deepEqual(publicNavigation, [ + '/technology/', + '/research/', + '/docs/', + '/about/', + 'https://github.com/likern/research', +]); +assert.ok(!publicNavigation.includes('/component-lab/')); + +const sitemap = await readFile(resolve(root, 'sitemap.xml'), 'utf8'); +for (const route of manifest.routes.filter(entry => entry.sitemap).map(entry => entry.route)) { + assert.match(sitemap, new RegExp(`<loc>https:\/\/pinega\\.example${escapeRegex(route)}<\\/loc>`, 'u')); +} +assert.doesNotMatch(sitemap, /component-lab/u); + +const home = await readFile(resolve(root, 'index.html'), 'utf8'); +assert.match(home, /<h1>Correctness under concurrency\.<\/h1>/u); +assert.match(home, /Pinega Engine is\s+the first active implementation programme/u); +assert.doesNotMatch(home, /href="\/component-lab\/"/u); + +const technology = await readFile(resolve(root, 'technology/index.html'), 'utf8'); +assert.match(technology, /id="pinega-engine"/u); +assert.match(technology, /id="engine-architecture"/u); +assert.match(technology, /One PostgreSQL WAL/u); + const research = await readFile(resolve(root, 'research/index.html'), 'utf8'); assert.equal((research.match(/class="pinega-semantic-diagram"/gu) ?? []).length, 3, 'research page must contain three semantic diagrams'); assert.equal((research.match(/role="img" aria-labelledby=/gu) ?? []).length, 3, 'each semantic SVG must have an accessible image role and name'); @@ -58,10 +128,13 @@ assert.equal((research.match(/<title id="pinega-diagram-/gu) ?? []).length, 3, ' assert.equal((research.match(/<desc id="pinega-diagram-/gu) ?? []).length, 3, 'each semantic SVG must have a direct description'); assert.equal((research.match(/data-layout-profile="production-v0\.2"/gu) ?? []).length, 6, 'each semantic figure and SVG must identify the accepted layout profile'); -const manifest = JSON.parse(await readFile(resolve(root, 'site-manifest.json'), 'utf8')); -assert.deepEqual(manifest.diagrams.map(entry => entry.id).toSorted(), diagramIds.toSorted()); +const componentLab = await readFile(resolve(root, 'component-lab/index.html'), 'utf8'); +assert.match(componentLab, /data-page="component-lab"/u); +assert.doesNotMatch(componentLab, /<link rel="canonical"/u); -console.log(`Validated ${files.length} build files; JavaScript ${javascript} B, CSS ${css} B, diagrams ${diagramIds.length}.`); +console.log( + `Validated ${files.length} build files; JavaScript ${javascript} B, CSS ${css} B, pages ${contentIndex.entries.length}, diagrams ${diagramIds.length}.`, +); async function walk(directory) { const entries = await readdir(directory, { withFileTypes: true }); @@ -82,3 +155,7 @@ async function isFile(path) { throw error; } } + +function escapeRegex(value) { + return String(value).replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'); +} diff --git a/web/tests/browser/foundation.spec.ts b/web/tests/browser/foundation.spec.ts index 91435f4..0ee763f 100644 --- a/web/tests/browser/foundation.spec.ts +++ b/web/tests/browser/foundation.spec.ts @@ -5,7 +5,7 @@ const require = createRequire(import.meta.url); const axePath = require.resolve('axe-core/axe.min.js'); async function ready(page: Page) { - await page.goto('/component-lab/', { waitUntil: 'domcontentloaded' }); + await page.goto('/component-lab/', { waitUntil: 'commit' }); await expect(page.locator('html')).toHaveAttribute('data-pinega-ready', 'true'); } diff --git a/web/tests/browser/site.spec.ts b/web/tests/browser/site.spec.ts index 2e7a633..6b274fc 100644 --- a/web/tests/browser/site.spec.ts +++ b/web/tests/browser/site.spec.ts @@ -3,11 +3,18 @@ import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); const axePath = require.resolve('axe-core/axe.min.js'); -const publicRoutes = ['/', '/docs/', '/docs/getting-started/', '/research/']; +const publicRoutes = [ + '/', + '/technology/', + '/research/', + '/docs/', + '/docs/getting-started/', + '/about/', +]; const allRoutes = [...publicRoutes, '/component-lab/']; async function ready(page: Page, route: string) { - const response = await page.goto(route, { waitUntil: 'domcontentloaded' }); + const response = await page.goto(route, { waitUntil: 'commit' }); expect(response?.status(), `${route} should return a successful response`).toBeLessThan(400); await expect(page.locator('html')).toHaveAttribute('data-pinega-ready', 'true'); } @@ -17,70 +24,150 @@ for (const route of allRoutes) { await ready(page, route); await expect(page.getByRole('main')).toBeVisible(); await expect(page.locator('pinega-site-header')).toHaveCount(1); - const overflow = await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth); + const overflow = await page.evaluate( + () => document.documentElement.scrollWidth - document.documentElement.clientWidth, + ); expect(overflow).toBeLessThanOrEqual(1); }); } -test('homepage states the research boundary and architecture maturity', async ({ page }) => { +test('public navigation exposes the programme hierarchy and hides the component lab', async ({ page }) => { + for (const route of publicRoutes) { + await ready(page, route); + const navigation = page.locator('nav[data-primary-navigation]'); + await expect(navigation.locator('a[href="/technology/"]')).toHaveText('Technology'); + await expect(navigation.locator('a[href="/research/"]')).toHaveText('Research'); + await expect(navigation.locator('a[href="/docs/"]')).toHaveText('Documentation'); + await expect(navigation.locator('a[href="/about/"]')).toHaveText('About'); + await expect(navigation.locator('a[href="https://github.com/likern/research"]')).toHaveText('GitHub'); + await expect(navigation.locator('a[href="/component-lab/"]')).toHaveCount(0); + } +}); + +test('homepage states the Pinega master-brand and evidence boundary', async ({ page }) => { await ready(page, '/'); - await expect(page.getByRole('heading', { level: 1 })).toContainText('storage engine'); - await expect(page.getByText('it is not a production release', { exact: false })).toBeVisible(); + await expect(page.getByRole('heading', { level: 1 })).toHaveText('Correctness under concurrency.'); + await expect(page.getByText('Pinega Engine is the first active implementation programme', { exact: false })).toBeVisible(); + await expect(page.getByText('it is not yet a production release', { exact: false })).toBeVisible(); await expect(page.locator('.pinega-home-status .pinega-status-card')).toHaveCount(3); - await expect(page.locator('.pinega-architecture-layers > li')).toHaveCount(4); - await expect(page.getByRole('table')).toContainText('No production binary or performance claim yet'); + await expect(page.locator('#programmes .pinega-research-programme > article')).toHaveCount(3); + await expect(page.locator('#pinega-engine .pinega-architecture-layers > li')).toHaveCount(4); + await expect(page.getByRole('table')).toContainText('no production engine binary'); +}); + +test('technology page separates active, research, and portfolio programmes', async ({ page }) => { + await ready(page, '/technology/'); + await expect(page.getByRole('heading', { level: 1 })).toContainText('Research becomes technology'); + await expect(page.locator('#pinega-engine')).toBeAttached(); + await expect(page.locator('#optimisation')).toBeAttached(); + await expect(page.locator('#verification')).toBeAttached(); + await expect(page.locator('#distributed-systems')).toBeAttached(); + await expect(page.locator('#engine-architecture .pinega-architecture-layers > li')).toHaveCount(4); + await expect(page.getByText('One PostgreSQL WAL', { exact: true })).toBeVisible(); + await expect(page.getByText('No item below is presented as a shipped product', { exact: false })).toBeVisible(); }); -test('documentation search filters durable cards without replacing their markup', async ({ page }) => { +test('documentation topic filter enhances durable cards without claiming full-text search', async ({ page }) => { await ready(page, '/docs/'); const cards = page.locator('[data-doc-card]'); await expect(cards).toHaveCount(8); + await expect(page.getByRole('heading', { name: 'Filter documentation topics' })).toBeVisible(); + const input = page.locator('[data-doc-search-input]'); await input.evaluate((element: HTMLElement & { value?: string }) => { - element.value = 'durability'; + element.value = 'engine architecture'; element.dispatchEvent(new Event('input', { bubbles: true, composed: true })); }); - const visibleTitles = await cards.evaluateAll(elements => elements.filter(element => !(element as HTMLElement).hidden).map(element => element.querySelector('h3')?.textContent?.trim())); - expect(visibleTitles).toEqual(['WAL and durability']); + const visibleTitles = await cards.evaluateAll(elements => + elements + .filter(element => !(element as HTMLElement).hidden) + .map(element => element.querySelector('h3')?.textContent?.trim()), + ); + expect(visibleTitles).toEqual(['Pinega Engine architecture']); await expect(page.locator('[data-doc-search-status]')).toHaveText('1 of 8 topics'); }); -test('getting-started page exposes real workspace commands and a table of contents', async ({ page }) => { +test('getting-started page exposes programme boundaries and working commands', async ({ page }) => { await ready(page, '/docs/getting-started/'); - await expect(page.getByRole('heading', { level: 1 })).toContainText('Getting started'); + await expect(page.getByRole('heading', { level: 1 })).toContainText('programme and workspace'); await expect(page.locator('pinega-code-example')).toHaveCount(3); await expect(page.getByRole('navigation', { name: 'Documentation sections' })).toBeAttached(); await expect(page.locator('aside[aria-label="On this page"]')).toBeAttached(); await expect(page.getByText('does not present an installable Pinega database engine', { exact: false })).toBeVisible(); + await expect(page.getByText('Pinega Engine is the first active implementation programme', { exact: false })).toBeVisible(); }); -test('research page exposes provenance and concurrent-lifetime stages', async ({ page }) => { +test('research landing exposes the area catalogue, method, and existing diagrams', async ({ page }) => { await ready(page, '/research/'); await expect(page.getByRole('heading', { level: 1 })).toContainText('Research is part'); + await expect(page.locator('#research-areas .pinega-research-programme > article')).toHaveCount(7); await expect(page.locator('.pinega-research-ledger span')).toHaveCount(5); await expect(page.locator('.pinega-research-timeline > [role="listitem"]')).toHaveCount(5); - await expect(page.locator('#versioned-storage')).toBeAttached(); - await expect(page.locator('#concurrent-lifetimes')).toBeAttached(); + await expect(page.locator('figure[data-diagram-id]')).toHaveCount(3); + await expect(page.locator('#method')).toBeAttached(); +}); + +test('about page distinguishes Pinega, Pinega Labs, and future offerings', async ({ page }) => { + await ready(page, '/about/'); + await expect(page.getByRole('heading', { level: 1 })).toContainText('defensible database technology'); + await expect(page.getByText('Pinega is the master technology and product programme', { exact: false })).toBeVisible(); + await expect(page.getByText('Pinega Labs is the working research', { exact: false })).toBeVisible(); + await expect(page.getByText('There is no current production engine release', { exact: false })).toBeVisible(); + await expect(page.locator('.pinega-principle-grid wa-card')).toHaveCount(6); }); -test('generated discovery files expose public routes only', async ({ request }) => { +test('generated discovery files expose registry metadata and public routes only', async ({ request }) => { const manifest = await request.get('/site-manifest.json'); expect(manifest.ok()).toBeTruthy(); - const payload = await manifest.json() as { routes: Array<{ route: string; sitemap: boolean }> }; - expect(payload.routes.map(entry => entry.route)).toEqual(['/', '/docs/', '/docs/getting-started/', '/research/', '/component-lab/', '/404.html']); + const payload = await manifest.json() as { + site: { tagline: string }; + primaryNavigation: Array<{ label: string; route?: string; href?: string }>; + routes: Array<{ id: string; route: string; sitemap: boolean; searchable: boolean; public: boolean }>; + }; + + expect(payload.site.tagline).toBe('Correctness under concurrency.'); + expect(payload.primaryNavigation.map(item => item.route ?? item.href)).toEqual([ + '/technology/', + '/research/', + '/docs/', + '/about/', + 'https://github.com/likern/research', + ]); + expect(payload.routes.map(entry => entry.route)).toEqual([ + '/', + '/technology/', + '/research/', + '/docs/', + '/docs/getting-started/', + '/about/', + '/component-lab/', + '/404.html', + ]); + expect(payload.routes.filter(entry => entry.sitemap).map(entry => entry.route)).toEqual(publicRoutes); + expect(payload.routes.filter(entry => entry.searchable).map(entry => entry.route)).toEqual(publicRoutes); + + const componentLab = payload.routes.find(entry => entry.id === 'component-lab'); + expect(componentLab).toEqual(expect.objectContaining({ public: false, sitemap: false, searchable: false })); const sitemap = await request.get('/sitemap.xml'); expect(sitemap.ok()).toBeTruthy(); const sitemapText = await sitemap.text(); for (const route of publicRoutes) expect(sitemapText).toContain(route); expect(sitemapText).not.toContain('/component-lab/'); + expect(sitemapText).not.toContain('/404.html'); + + const registry = await request.get('/content/content-index.json'); + expect(registry.ok()).toBeTruthy(); + const registryPayload = await registry.json() as { schema_version: number; entries: unknown[] }; + expect(registryPayload.schema_version).toBe(1); + expect(registryPayload.entries).toHaveLength(8); const robots = await request.get('/robots.txt'); expect(await robots.text()).toContain('Sitemap: https://pinega.example/sitemap.xml'); }); test('unknown routes return the accessible not-found page with HTTP 404', async ({ page }) => { - const response = await page.goto('/missing-stratum', { waitUntil: 'domcontentloaded' }); + const response = await page.goto('/missing-stratum', { waitUntil: 'commit' }); expect(response?.status()).toBe(404); await expect(page.locator('html')).toHaveAttribute('data-pinega-ready', 'true'); await expect(page.getByRole('heading', { level: 1 })).toContainText('not part of the current model'); @@ -92,10 +179,19 @@ test('public pages have no serious or critical axe violations', async ({ page }) await ready(page, route); await page.addScriptTag({ path: axePath }); const results = await page.evaluate(async () => { - const axe = (window as unknown as Window & { axe: { run: (context: Document, options: unknown) => Promise<{ violations: Array<{ impact: string | null; id: string }> }> } }).axe; + const axe = (window as unknown as Window & { + axe: { + run: ( + context: Document, + options: unknown, + ) => Promise<{ violations: Array<{ impact: string | null; id: string }> }>; + }; + }).axe; return axe.run(document, { resultTypes: ['violations'] }); }); - const blocking = results.violations.filter(violation => violation.impact === 'serious' || violation.impact === 'critical'); + const blocking = results.violations.filter( + violation => violation.impact === 'serious' || violation.impact === 'critical', + ); expect(blocking, `${route}: ${blocking.map(violation => violation.id).join(', ')}`).toEqual([]); } }); diff --git a/web/tests/unit/site-structure.test.mjs b/web/tests/unit/site-structure.test.mjs index e59b515..5b02a06 100644 --- a/web/tests/unit/site-structure.test.mjs +++ b/web/tests/unit/site-structure.test.mjs @@ -5,69 +5,217 @@ import { fileURLToPath } from 'node:url'; import test from 'node:test'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '../..'); -const sources = [ - ['/', 'pages/home/index.html'], - ['/docs/', 'pages/docs/index.html'], - ['/docs/getting-started/', 'pages/docs/getting-started/index.html'], - ['/research/', 'pages/research/index.html'], - ['/component-lab/', 'component-lab/index.html'], -]; -const routeSet = new Set(sources.map(([route]) => route)); const read = path => readFile(resolve(root, path), 'utf8'); +const contentIndex = JSON.parse(await read('content/content-index.json')); +const contentSchema = JSON.parse(await read('content/content.schema.json')); +const entries = contentIndex.entries; +const routeMap = new Map(entries.map(entry => [entry.route, entry])); +const sourceByRoute = new Map( + await Promise.all(entries.map(async entry => [entry.route, await read(entry.source_path)])), +); +const publicEntries = entries.filter(entry => entry.public); +const publicNavigation = contentIndex.primary_navigation.map(item => item.route ?? item.href); + +const expectedPublicRoutes = [ + '/', + '/technology/', + '/research/', + '/docs/', + '/docs/getting-started/', + '/about/', +]; + +const expectedPrimaryNavigation = [ + '/technology/', + '/research/', + '/docs/', + '/about/', + 'https://github.com/likern/research', +]; + +test('content registry is the unique route and discovery contract', () => { + assert.equal(contentIndex.schema_version, 1); + assert.equal(contentSchema.properties.schema_version.const, 1); + assert.equal(contentIndex.site.tagline, 'Correctness under concurrency.'); + assert.deepEqual(publicNavigation, expectedPrimaryNavigation); + + assert.equal(new Set(entries.map(entry => entry.id)).size, entries.length); + assert.equal(new Set(entries.map(entry => entry.route)).size, entries.length); + assert.equal(new Set(entries.map(entry => entry.source_path)).size, entries.length); + assert.equal(new Set(entries.map(entry => entry.output_path)).size, entries.length); -test('public pages preserve semantic HTML and deployment metadata', async () => { - for (const [route, path] of sources.slice(0, 4)) { - const html = await read(path); - assert.match(html, /<!doctype html>/iu, `${route} must declare HTML`); - assert.match(html, /<title>[^<]+<\/title>/u, `${route} must have a title`); - assert.match(html, /<meta name="description" content="[^"]+">/u, `${route} must have a description`); - assert.match(html, /<link rel="canonical" href="\{\{SITE_ORIGIN\}\}[^"]*">/u, `${route} must have a canonical template`); - assert.match(html, /<header>/u, `${route} must have a header`); - assert.match(html, /<main id="main-content"/u, `${route} must have main content`); - assert.match(html, /<footer class="pinega-site-footer/u, `${route} must have a footer`); - assert.match(html, /<!-- PINEGA_PROJECT_META -->/u, `${route} must expose the private Pro injection boundary`); - assert.doesNotMatch(html, /innerHTML\s*=/u, `${route} must remain durable source HTML`); + assert.deepEqual( + entries.filter(entry => entry.sitemap).map(entry => entry.route), + expectedPublicRoutes, + ); + assert.deepEqual( + entries.filter(entry => entry.searchable).map(entry => entry.route), + expectedPublicRoutes, + ); + + const componentLab = entries.find(entry => entry.id === 'component-lab'); + assert.ok(componentLab); + assert.equal(componentLab.public, false); + assert.equal(componentLab.canonical, false); + assert.equal(componentLab.searchable, false); + assert.equal(componentLab.sitemap, false); + assert.ok(!publicNavigation.includes('/component-lab/')); +}); + +test('registered pages preserve semantic HTML and registry metadata', async () => { + for (const entry of entries) { + const html = sourceByRoute.get(entry.route); + assert.match(html, /<!doctype html>/iu, `${entry.route} must declare HTML`); + assert.match( + html, + new RegExp(`<html\\b[^>]*\\bdata-page="${escapeRegex(entry.id)}"`, 'u'), + `${entry.route} must expose its registry identity`, + ); + assert.match( + html, + new RegExp(`<title>${escapeRegex(entry.canonical_title)}<\\/title>`, 'u'), + `${entry.route} title must match the registry`, + ); + assert.match( + html, + new RegExp(`<meta name="description" content="${escapeRegex(entry.summary)}">`, 'u'), + `${entry.route} description must match the registry`, + ); + assert.equal((html.match(/<h1\b/gu) ?? []).length, 1, `${entry.route} must have one h1`); + assert.match(html, /<main id="main-content"/u, `${entry.route} must have main content`); + assert.match(html, /<!-- PINEGA_PROJECT_META -->/u, `${entry.route} must expose the private Pro boundary`); + assert.doesNotMatch(html, /innerHTML\s*=/u, `${entry.route} must remain durable source HTML`); + + if (entry.canonical) { + assert.match( + html, + new RegExp(`<link rel="canonical" href="\\{\\{SITE_ORIGIN\\}\\}${escapeRegex(entry.route)}">`, 'u'), + `${entry.route} must have a canonical template`, + ); + } else { + assert.doesNotMatch(html, /<link rel="canonical"/u, `${entry.route} must remain non-canonical`); + } } }); -test('all internal anchor routes resolve to a generated page', async () => { - for (const [route, path] of sources) { - const html = await read(path); +test('all internal routes and fragments resolve to registered durable content', () => { + for (const entry of entries) { + const html = sourceByRoute.get(entry.route); const links = [...html.matchAll(/<a\b[^>]*\bhref="([^"]+)"/gu)].map(match => match[1]); + for (const href of links) { - if (/^(?:https?:|mailto:|tel:|#)/u.test(href)) continue; - const pathname = href.split('#', 1)[0] || route; - assert.ok(routeSet.has(pathname), `${path}: unresolved local route ${href}`); + if (/^(?:https?:|mailto:|tel:)/u.test(href)) continue; + const target = new URL(href, `https://pinega.example${entry.route}`); + const pathname = target.pathname; + const targetEntry = routeMap.get(pathname); + assert.ok(targetEntry, `${entry.source_path}: unresolved local route ${href}`); + + if (target.hash) { + const targetHtml = sourceByRoute.get(pathname); + const id = decodeURIComponent(target.hash.slice(1)); + assert.match( + targetHtml, + new RegExp(`\\bid="${escapeRegex(id)}"`, 'u'), + `${entry.source_path}: unresolved fragment ${href}`, + ); + } } } }); -test('homepage and docs make the research-stage boundary explicit', async () => { - const home = await read('pages/home/index.html'); - const gettingStarted = await read('pages/docs/getting-started/index.html'); - assert.match(home, /Research stage/u); - assert.match(home, /not a production release/u); - assert.match(home, /No production binary or performance claim yet/u); - assert.match(gettingStarted, /does not present an installable Pinega database engine/u); +test('public navigation expresses the Pinega master-brand hierarchy', () => { + for (const entry of publicEntries) { + const html = sourceByRoute.get(entry.route); + const navigation = html.match(/<nav\b[^>]*data-primary-navigation[^>]*>[\s\S]*?<\/nav>/u)?.[0]; + assert.ok(navigation, `${entry.route} must have primary navigation`); + + for (const destination of expectedPrimaryNavigation) { + assert.match( + navigation, + new RegExp(`href="${escapeRegex(destination)}"`, 'u'), + `${entry.route} primary navigation misses ${destination}`, + ); + } + assert.doesNotMatch(navigation, /\/component-lab\//u); + assert.doesNotMatch(navigation, /href="\/#architecture"/u); + } }); -test('documentation search enhances durable cards instead of generating them', async () => { - const docs = await read('pages/docs/index.html'); +test('homepage presents Pinega as a programme and Pinega Engine as its first implementation', () => { + const home = sourceByRoute.get('/'); + assert.match(home, /<h1>Correctness under concurrency\.<\/h1>/u); + assert.match(home, /database-systems research into high-performance software/u); + assert.match(home, /Pinega Engine is\s+the first active implementation programme/u); + assert.match(home, /not yet a production release/u); + assert.match(home, /Research and web platform/u); + assert.match(home, /Optimisation, verification, and distributed systems/u); + assert.doesNotMatch(home, /Pinega — a research-stage PostgreSQL storage engine/u); +}); + +test('technology and about pages own programme and company boundaries', () => { + const technology = sourceByRoute.get('/technology/'); + const about = sourceByRoute.get('/about/'); + + assert.match(technology, /id="pinega-engine"/u); + assert.match(technology, /id="optimisation"/u); + assert.match(technology, /id="verification"/u); + assert.match(technology, /id="engine-architecture"/u); + assert.match(technology, /One PostgreSQL WAL/u); + assert.match(technology, /No item below is presented as a shipped product/u); + + assert.match(about, /Pinega is the master technology(?: and product)? programme/u); + assert.match(about, /Pinega Labs is the working research/u); + assert.match(about, /There is no current production engine release/u); + assert.match(about, /Correctness under concurrency/u); +}); + +test('research landing exposes the accepted area taxonomy', () => { + const research = sourceByRoute.get('/research/'); + const areaSection = research.match(/<section class="pinega-section" id="research-areas"[\s\S]*?<\/section>/u)?.[0]; + assert.ok(areaSection); + for (const title of [ + 'Storage and execution', + 'Concurrency and memory reclamation', + 'Transactions and correctness', + 'Distributed systems', + 'Query optimisation and AI', + 'Performance engineering and hardware', + 'Verification and deterministic testing', + ]) { + assert.match(areaSection, new RegExp(escapeRegex(title), 'u')); + } + assert.equal((areaSection.match(/<article>/gu) ?? []).length, 7); + assert.equal((research.match(/PINEGA_DIAGRAM:/gu) ?? []).length, 3); +}); + +test('documentation filter labels its actual topic-filter behaviour', async () => { + const docs = sourceByRoute.get('/docs/'); const search = await read('src/components/doc-search/doc-search.ts'); + assert.equal((docs.match(/data-doc-card/gu) ?? []).length, 8); - assert.match(docs, /<pinega-doc-search/u); + assert.match(docs, /<h2 id="doc-filter-title">Filter documentation topics<\/h2>/u); + assert.match(docs, /label="Filter documentation topics"/u); + assert.doesNotMatch(docs, /label="Search documentation"/u); + assert.match(docs, /data-content-type="start"/u); + assert.match(docs, /data-content-type="how-to"/u); + assert.match(docs, /data-content-type="explanation"/u); + assert.match(docs, /data-content-type="reference"/u); assert.match(search, /card\.hidden = !matches/u); assert.doesNotMatch(search, /innerHTML/u); }); -test('the build defines every public route and keeps the component lab out of the sitemap', async () => { +test('the build derives routes and discovery metadata from the content registry', async () => { const build = await read('scripts/build.mjs'); - for (const [route] of sources) assert.match(build, new RegExp(`route: ['\"]${escapeRegex(route)}['\"]`, 'u')); - assert.match(build, /route: '\/component-lab\/',\n\s+sitemap: false/u); - assert.match(build, /PINEGA_SITE_ORIGIN/u); - assert.match(build, /site-manifest\.json/u); + assert.match(build, /const contentRoot = resolve\(root, 'content'\)/u); + assert.match(build, /readFile\(resolve\(contentRoot, 'content-index\.json'\)/u); + assert.match(build, /const pages = contentIndex\.entries\.map/u); + assert.match(build, /validateContentIndex/u); + assert.match(build, /validatePageSource/u); + assert.match(build, /primaryNavigation: contentIndex\.primary_navigation/u); + assert.match(build, /searchable: page\.searchable/u); + assert.doesNotMatch(build, /const pages = \[/u); }); function escapeRegex(value) { - return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'); + return String(value).replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'); } diff --git a/web/tests/visual/site.visual.spec.ts b/web/tests/visual/site.visual.spec.ts index 34e9f72..9850837 100644 --- a/web/tests/visual/site.visual.spec.ts +++ b/web/tests/visual/site.visual.spec.ts @@ -6,16 +6,23 @@ async function open(page: Page, route: string) { await expect(page.locator('html')).toHaveAttribute('data-pinega-ready', 'true'); } -test('homepage product narrative and architecture', async ({ page }) => { +test('homepage master-brand narrative and programme catalogue', async ({ page }) => { await open(page, '/'); - await expect(page.locator('pinega-hero')).toHaveScreenshot('home-hero.png'); - await expect(page.locator('#architecture')).toHaveScreenshot('home-architecture.png'); + await expect(page.locator('pinega-hero')).toHaveScreenshot('home-master-brand-hero.png'); + await expect(page.locator('#programmes')).toHaveScreenshot('home-programmes.png'); + await expect(page.locator('#pinega-engine')).toHaveScreenshot('home-engine.png'); }); -test('documentation landing and search map', async ({ page }) => { +test('technology catalogue and Pinega Engine boundary', async ({ page }) => { + await open(page, '/technology/'); + await expect(page.locator('.pinega-research-hero')).toHaveScreenshot('technology-hero.png'); + await expect(page.locator('#engine-architecture')).toHaveScreenshot('technology-engine-architecture.png'); +}); + +test('documentation landing and topic filter', async ({ page }) => { await open(page, '/docs/'); await expect(page.locator('.pinega-doc-hero')).toHaveScreenshot('docs-hero.png'); - await expect(page.locator('pinega-doc-search')).toHaveScreenshot('docs-map.png'); + await expect(page.locator('pinega-doc-search')).toHaveScreenshot('docs-topics.png'); }); test('getting-started article shell', async ({ page }) => { @@ -23,12 +30,19 @@ test('getting-started article shell', async ({ page }) => { await expect(page.locator('.pinega-doc-shell')).toHaveScreenshot('getting-started.png'); }); -test('research programme', async ({ page }) => { +test('research catalogue and active studies', async ({ page }) => { await open(page, '/research/'); await expect(page.locator('.pinega-research-hero')).toHaveScreenshot('research-hero.png'); + await expect(page.locator('#research-areas')).toHaveScreenshot('research-areas.png'); await expect(page.locator('#concurrent-lifetimes')).toHaveScreenshot('research-lifetimes.png'); }); +test('about and company-boundary narrative', async ({ page }) => { + await open(page, '/about/'); + await expect(page.locator('.pinega-research-hero')).toHaveScreenshot('about-hero.png'); + await expect(page.locator('.pinega-section-contrast')).toHaveScreenshot('about-principles.png'); +}); + test('homepage dark mode remains coherent', async ({ page }) => { await open(page, '/'); await page.locator('[data-theme-toggle]').click(); diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/about-hero-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/about-hero-chromium-desktop-linux.png new file mode 100644 index 0000000..d8a8949 Binary files /dev/null and b/web/tests/visual/site.visual.spec.ts-snapshots/about-hero-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/about-principles-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/about-principles-chromium-desktop-linux.png new file mode 100644 index 0000000..d2ccce2 Binary files /dev/null and b/web/tests/visual/site.visual.spec.ts-snapshots/about-principles-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/docs-hero-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/docs-hero-chromium-desktop-linux.png index 9250fb9..2335467 100644 Binary files a/web/tests/visual/site.visual.spec.ts-snapshots/docs-hero-chromium-desktop-linux.png and b/web/tests/visual/site.visual.spec.ts-snapshots/docs-hero-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/docs-map-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/docs-map-chromium-desktop-linux.png deleted file mode 100644 index df781e1..0000000 Binary files a/web/tests/visual/site.visual.spec.ts-snapshots/docs-map-chromium-desktop-linux.png and /dev/null differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/docs-topics-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/docs-topics-chromium-desktop-linux.png new file mode 100644 index 0000000..13ef308 Binary files /dev/null and b/web/tests/visual/site.visual.spec.ts-snapshots/docs-topics-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/getting-started-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/getting-started-chromium-desktop-linux.png index 2a1555a..5ce3708 100644 Binary files a/web/tests/visual/site.visual.spec.ts-snapshots/getting-started-chromium-desktop-linux.png and b/web/tests/visual/site.visual.spec.ts-snapshots/getting-started-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/home-architecture-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/home-architecture-chromium-desktop-linux.png deleted file mode 100644 index 7e910d2..0000000 Binary files a/web/tests/visual/site.visual.spec.ts-snapshots/home-architecture-chromium-desktop-linux.png and /dev/null differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/home-engine-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/home-engine-chromium-desktop-linux.png new file mode 100644 index 0000000..33cb386 Binary files /dev/null and b/web/tests/visual/site.visual.spec.ts-snapshots/home-engine-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/home-hero-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/home-hero-chromium-desktop-linux.png deleted file mode 100644 index 6ce237c..0000000 Binary files a/web/tests/visual/site.visual.spec.ts-snapshots/home-hero-chromium-desktop-linux.png and /dev/null differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/home-master-brand-hero-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/home-master-brand-hero-chromium-desktop-linux.png new file mode 100644 index 0000000..dda7187 Binary files /dev/null and b/web/tests/visual/site.visual.spec.ts-snapshots/home-master-brand-hero-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/home-programmes-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/home-programmes-chromium-desktop-linux.png new file mode 100644 index 0000000..cc49bcc Binary files /dev/null and b/web/tests/visual/site.visual.spec.ts-snapshots/home-programmes-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/home-status-dark-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/home-status-dark-chromium-desktop-linux.png index ee3ae31..3611fae 100644 Binary files a/web/tests/visual/site.visual.spec.ts-snapshots/home-status-dark-chromium-desktop-linux.png and b/web/tests/visual/site.visual.spec.ts-snapshots/home-status-dark-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/research-areas-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/research-areas-chromium-desktop-linux.png new file mode 100644 index 0000000..9338571 Binary files /dev/null and b/web/tests/visual/site.visual.spec.ts-snapshots/research-areas-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/research-hero-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/research-hero-chromium-desktop-linux.png index a26e28b..39b2608 100644 Binary files a/web/tests/visual/site.visual.spec.ts-snapshots/research-hero-chromium-desktop-linux.png and b/web/tests/visual/site.visual.spec.ts-snapshots/research-hero-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/research-lifetimes-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/research-lifetimes-chromium-desktop-linux.png index 57d3f5f..f2af6bd 100644 Binary files a/web/tests/visual/site.visual.spec.ts-snapshots/research-lifetimes-chromium-desktop-linux.png and b/web/tests/visual/site.visual.spec.ts-snapshots/research-lifetimes-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/technology-engine-architecture-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/technology-engine-architecture-chromium-desktop-linux.png new file mode 100644 index 0000000..af34ba3 Binary files /dev/null and b/web/tests/visual/site.visual.spec.ts-snapshots/technology-engine-architecture-chromium-desktop-linux.png differ diff --git a/web/tests/visual/site.visual.spec.ts-snapshots/technology-hero-chromium-desktop-linux.png b/web/tests/visual/site.visual.spec.ts-snapshots/technology-hero-chromium-desktop-linux.png new file mode 100644 index 0000000..ba36b78 Binary files /dev/null and b/web/tests/visual/site.visual.spec.ts-snapshots/technology-hero-chromium-desktop-linux.png differ