From 3a546a0898afe2c9acb0aa58a449d765b6c2e897 Mon Sep 17 00:00:00 2001 From: Martin Hinshelwood Date: Fri, 18 Sep 2026 16:50:48 +0100 Subject: [PATCH 1/2] feat: centralize guide catalogue capabilities +semver: minor --- docs/architecture/current-system.md | 91 +++++++++ docs/architecture/hugo-guide-catalogue.md | 73 +++++++ docs/platform-development.md | 4 +- readme.md | 1 + .../components/guide/render-guide.html | 18 +- .../translations/official-version.html | 2 +- .../components/versions/version-card.html | 12 +- .../_partials/functions/get-all-versions.html | 62 +----- .../get-guide-translations-catalogue.html | 106 ++-------- .../get-guide-translations-for-version.html | 166 +-------------- .../get-guide-translations-list.html | 102 +--------- .../functions/get-history-chain.html | 113 +---------- .../functions/get-latest-version.html | 38 +--- .../editions/discover-guide-editions.html | 16 ++ .../editions/get-edition-version.html | 9 + .../editions/get-guide-version-options.html | 13 ++ .../editions/select-latest-guide-edition.html | 23 +++ .../openguide/guides/discover-guides.html | 14 ++ .../openguide/guides/get-guide-catalogue.html | 13 ++ .../history/get-guide-history-chain.html | 112 +++++++++++ .../openguide/pdfs/discover-edition-pdfs.html | 11 + .../pdfs/select-first-edition-pdf.html | 6 + .../pdfs/select-translation-pdfs.html | 7 + .../discover-edition-translations.html | 30 +++ .../project-legacy-translation.html | 32 +++ .../resolve-translation-fallback.html | 3 + .../select-preferred-guide-translations.html | 82 ++++++++ .../Contracts/README.md | 12 +- tests/Core/HugoCatalogue.Tests.ps1 | 189 ++++++++++++++++++ 29 files changed, 769 insertions(+), 591 deletions(-) create mode 100644 docs/architecture/current-system.md create mode 100644 docs/architecture/hugo-guide-catalogue.md create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/editions/discover-guide-editions.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/editions/get-edition-version.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/editions/get-guide-version-options.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/editions/select-latest-guide-edition.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/guides/discover-guides.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/guides/get-guide-catalogue.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/history/get-guide-history-chain.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/pdfs/discover-edition-pdfs.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/pdfs/select-first-edition-pdf.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/pdfs/select-translation-pdfs.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/translations/discover-edition-translations.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/translations/project-legacy-translation.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/translations/resolve-translation-fallback.html create mode 100644 system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/translations/select-preferred-guide-translations.html create mode 100644 tests/Core/HugoCatalogue.Tests.ps1 diff --git a/docs/architecture/current-system.md b/docs/architecture/current-system.md new file mode 100644 index 00000000..ac5d341f --- /dev/null +++ b/docs/architecture/current-system.md @@ -0,0 +1,91 @@ +# OpenGuidePlatform current system + +This describes the implementation inspected from repository commit `2a178e6618c2b55c4ff55913e4e7965454ea19c1`, including the subsequently approved Hugo catalogue changes in this working tree (18 September 2026). Each section links to the implementation or schema that supports its claims. The [execution plan](open-guide-platform-execution-plan.md) records acceptance status; this page describes behavior in source, not a completed consumer rollout. + +## Components and ownership + +| Component | Current responsibility | +|---|---| +| [`OpenGuidePlatform.Hugo.Guides`](../../system/OpenGuidePlatform.Hugo.Guides/layouts/guide/single.html) | Hugo guide templates and reusable presentation partials. Consumer wrappers may have their own layouts and overrides; see the [architecture proposal](open-guide-platform-proposal.md#5-hugo-architecture-and-extension-compatibility) for the ownership boundary. | +| [`OpenGuidePlatform.PowerShell.Core`](../../system/OpenGuidePlatform.PowerShell.Core/OpenGuidePlatform.PowerShell.Core.psm1) | Policy import, guide assessment, publishing operations and PDF operations. | +| [`OpenGuidePlatform.PowerShell.GuideSiteBuild`](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Invoke-GuideSiteBuild.ps1) | Source discovery and the guide-site Prepare, Build, Validate, Deploy and Verify operations. | +| [`OpenGuidePlatform.PowerShell.GuideSiteAdoption`](../../system/OpenGuidePlatform.PowerShell.GuideSiteAdoption/OpenGuidePlatform.PowerShell.GuideSiteAdoption.psm1) | Installation, update and selected-package restoration. | +| [`OpenGuidePlatform.PowerShell.PlatformBuild`](../../system/OpenGuidePlatform.PowerShell.PlatformBuild/OpenGuidePlatform.PowerShell.PlatformBuild.psm1) | Platform testing, packaging, candidate sample acceptance and release operations. The [distribution package](../../system/OpenGuidePlatform.PowerShell.PlatformBuild/Packaging/Package-OpenGuidePlatform.ps1) keeps this separate from GuideSite. | +| [`OpenGuidePlatform.Agents.Integration`](../../system/OpenGuidePlatform.Agents.Integration/README.md) and [`OpenGuidePlatform.PowerShell.AgentControls`](../../system/OpenGuidePlatform.PowerShell.AgentControls/README.md) | Contributor skills/instructions and a repository governance evaluator. The [AgentControls README](../../system/OpenGuidePlatform.PowerShell.AgentControls/README.md) distinguishes repository code from independently installed enforcement. | + +The root [`build.ps1`](../../build.ps1) selects the platform or guide-site operation. The [installed launcher](../../system/OpenGuidePlatform.PowerShell.GuideSiteAdoption/build.ps1) resolves its installed package and calls [`Invoke-GuideSiteBuild`](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Invoke-GuideSiteBuild.ps1). PlatformBuild packages and tests the candidate GuideSite distribution. The [build instructions](../platform-development.md) define the supported acceptance commands. + +## Source and discovery + +The [settings reader](../../system/OpenGuidePlatform.PowerShell.GuideSiteAdoption/Resolve-OpenGuidePlatform.ps1) reads `site.source` from `.OpenGuidePlatform/settings.yaml`. [`Build-GuideSite.ps1`](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Build-GuideSite.ps1) imports a supplied `-PolicyPath`, or calls `New-GuideSiteDiscovery` and saves its result as `discovered-site.json` under that run's `.processing/` directory. The generated file is passed to the same subsequent policy import and assessment code. Installed sites do not have to maintain a separate guide inventory. [Implementation: build entry point](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Build-GuideSite.ps1). + +### How the source list is populated + +The **PowerShell filesystem walk**, not `hugo list all`, creates the `guides`, `editions`, `translations` and `downloads` entries. [`New-GuideSiteDiscovery`](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/Discovery/New-GuideSiteDiscovery.ps1) obtains the effective default language and content directory, then applies these rules: + +| Entry | Exact current recognition rule | +|---|---| +| Guide | Recursively find `_index.md` under the configured content directory; keep a directory when that file's front matter has `type: guide` and `layout: root` and at least one accepted edition. Guide `id` is its path relative to the content directory. | +| Edition/version | Inspect immediate child directories of the guide. Keep one when it contains `index.md`, its layout is not `translations`, `history`, `root` or `details`, and its front matter has either `type: guide` or a `version` field. Use the `version` field as edition `id` when present; otherwise use the directory name. | +| Language | Collect languages from edition-root `index*.md` filenames and from recognised PDF suffixes. `index.md` means the configured default language; `index..md` supplies ``. | +| PDF/download | Find `*.pdf` recursively inside the edition. A filename ending `..pdf` is assigned to that language; a PDF without a recognised suffix is assigned to the configured default language. Store its path relative to the edition and mark its inferred handling `supplied`. | + +For each collected language, the code reads the corresponding `index.md` or `index..md` body and infers `web` when populated, `pdf-only` when the body is empty but a matching PDF exists, `web` for the empty default-language source, or `fallback` to the default language otherwise. It also records required guide-root, history and translations wrapper files for active guide languages. These are the rules in the [source walk and entry construction](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/Discovery/New-GuideSiteDiscovery.ps1); they describe what the code currently recognises, not a proposed content format. + +### What the Hugo source listing contributes + +Separately, [`Get-GuideSourcePages`](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/Discovery/Get-GuideSourcePages.ps1) runs `hugo list all` with the selected configuration and environment. It filters pages according to the effective draft, future and expiry settings and returns page paths and permalinks. Discovery uses those observations to derive page routes, PDF `publicationRoots`, and evidence for environment exclusions; it derives some additional required routes from aliases and output configuration. It does **not** use that listing to enumerate guide directories, edition directories, translation files or PDF files. [Use of `$pages` and route construction in discovery](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/Discovery/New-GuideSiteDiscovery.ps1). + +An explicit policy is the other supported input. Its [v1 schema](../../system/OpenGuidePlatform.PowerShell.Core/Contracts/site-policy.schema.json) permits reviewed publication intent, protected paths, download handling and wrapper obligations. Discovery returns the same top-level shape but infers values from files; an inferred `web` or `pdf-only` state is not an editorial judgement about translation quality. [Discovery output](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/Discovery/New-GuideSiteDiscovery.ps1), [Core inventory](../../system/OpenGuidePlatform.PowerShell.Core/GuideInventory/Get-GuideInventory.ps1). + +## Site-policy-shaped inventory format + +The [v1 site-policy schema](../../system/OpenGuidePlatform.PowerShell.Core/Contracts/site-policy.schema.json) requires `schemaVersion`, `siteId`, `wrapper`, `guides`, `publication` and `protectedPaths`, and rejects additional properties. It defines repository-relative paths, with edition and download paths relative to their parent entries. Core's path resolver also checks filesystem containment; see [path handling](../../system/OpenGuidePlatform.PowerShell.Core/Internal/Paths.ps1). + +| Field | Meaning | +|---|---| +| `wrapper.sourcePath` | Hugo site directory. Wrapper requirements can include `requiredFiles`, `requiredRoutes`, `requiredI18nKeys`, `integrationPoints`, `requiredPageContent`, `runtimeAnchors`, JSON index expectations and recorded legacy aliases. `wrapper.discovery: source` marks generated discovery evidence. | +| `guides[]` | Any number of guides, each with `id`, `contentRoot`, `relationship`, `protectSource` and one or more `editions`. Optional `artifactPrefixes` identify known public paths for exclusions. | +| `editions[]` | Edition `id`, directory `path`, `sourceLanguage` and `translations`. | +| `translations[]` | Language, `intent`, optional `fallbackLanguage` and `downloads`. Intents are `web`, `pdf-only`, `fallback`, `scaffold` and `excluded`. Intent is a declaration or inference, not proof of translation quality. | +| `downloads[]` | PDF path relative to the edition and `handling`: `supplied`, `generated` or `protected`. Explicit policies can record `publishedPaths` and generated-PDF `generationReceipt`; discovery can retain `publicationRoots` for later artifact observation. Source and public paths are distinct. | +| `publication` | Target environments with excluded languages/guides and permanent exclusions for language, guide or edition. | +| `protectedPaths` | Repository paths that publishing operations must preserve. | + +The [schema](../../system/OpenGuidePlatform.PowerShell.Core/Contracts/site-policy.schema.json) checks structure. [Core assessment](../../system/OpenGuidePlatform.PowerShell.Core/Assessment/Get-GuideAssessment.ps1) checks declared relationships and source readiness; [Build](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Build-GuideSite.ps1) separately observes effective Hugo configuration and validates the artifact. The [trusted-policy decision](decisions/004-trusted-policy-authority.md) explains why a candidate-controlled repository file cannot approve its own changes. + +## Prepare, assessment and build evidence + +The [Prepare branch](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Build-GuideSite.ps1) resolves the Hugo module, generates or loads the site-policy-shaped input and records source/tool evidence. [`Prepare-GuideSite.ps1`](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Prepare-GuideSite.ps1) reads effective Hugo configuration and calls [`Get-GuideAssessment`](../../system/OpenGuidePlatform.PowerShell.Core/Assessment/Get-GuideAssessment.ps1). Core [inventory](../../system/OpenGuidePlatform.PowerShell.Core/GuideInventory/Get-GuideInventory.ps1) observes each declared edition/translation body as `populated`, `empty` or `missing`; [translation readiness](../../system/OpenGuidePlatform.PowerShell.Core/TranslationReadiness/Get-GuideTranslationState.ps1) returns `web`, `pdf-only`, `fallback`, `scaffold`, `excluded` or `unknown`. The inventory follows declared fallback chains to populated web content and does not require a web body for PDF-only intent. None of these source checks establishes translation quality. + +[`Prepare-GuideSite.ps1`](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Prepare-GuideSite.ps1) checks downloads, declared generated-PDF receipts, legacy/latest aliases and source translation catalogues, then writes `prepare/assessment.json` and a Markdown report. The [assessment v1 schema](../../system/OpenGuidePlatform.PowerShell.Core/Contracts/assessment.schema.json) defines `sourceCommit`, `platformVersion`, `policyDigest`, `target`, `stage`, `outcome`, `findings` and observed `inventory`. Findings include a code, severity, scope, subject, message, remediation and evidence. The schema permits `pass`, `fail` and `blocked`; Prepare throws unless the assessment passes. + +The [build-stage script](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Build-GuideSite.ps1) saves prepared inputs and tool evidence. Both Build and Validate recheck the prepared inputs, commit, target and policy digest; Build additionally checks the tool hash before invoking Hugo and writing an artifact identity. Validate checks routes/resources, PDF publication, links, JSON indexes and runtime anchors against the built artifact. [`Invoke-GuideSiteBuild`](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Invoke-GuideSiteBuild.ps1) calls Deploy and Verify when explicitly requested; an ordinary `All` run stops after Validate. + +## PDF evidence and preservation + +[`Get-GuidePdfPlan` and `New-GuidePdf`](../../system/OpenGuidePlatform.PowerShell.Core/PdfPublishing/Get-GuidePdfPlan.ps1) select only a download declared `generated`, read `index.md` or `index..md`, and pass language to Pandoc as metadata. They check tools/fonts, render in staging and check the PDF header before publishing. Replacement requires the existing output's expected SHA256. Supplied and protected downloads cannot be selected for generation. [`Get-GuideAssessment`](../../system/OpenGuidePlatform.PowerShell.Core/Assessment/Get-GuideAssessment.ps1) flags `lang` in Hugo front matter as deprecated. + +[`New-GuidePdf`](../../system/OpenGuidePlatform.PowerShell.Core/PdfPublishing/Get-GuidePdfPlan.ps1) returns a v1 receipt with guide, edition, language, input/output hashes, configuration hash, toolchain, environment digest and optional cache key. [`Save-GuidePdfReceipt` and `Get-GuidePdfReceipts`](../../system/OpenGuidePlatform.PowerShell.Core/PdfPublishing/Get-GuidePdfReceipts.ps1) save and validate declared receipts; Prepare calls the latter without rerunning Pandoc. [`Test-GuidePdfCache`](../../system/OpenGuidePlatform.PowerShell.Core/PdfPublishing/Test-GuidePdfCache.ps1) uses content/recipe/tool/environment evidence rather than timestamps. The returned PDF result says visual review is required; neither receipt validation nor source discovery assesses translation quality. + +At Validate, [`Get-GuideDownloadRequirements`](../../system/OpenGuidePlatform.PowerShell.Core/PublicationPolicy/Get-GuideDownloadRequirements.ps1) uses explicit `publishedPaths` when present. For inferred discovery it considers recorded `publicationRoots` and published JSON `PathPdf` entries, accepts matching artifact paths only when their SHA256 equals the source PDF, and checks excluded PDFs by path, hash and filename across the artifact. [`Get-GuidePublishedDownloads`](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/ArtifactValidation/Get-GuidePublishedDownloads.ps1) reads the JSON entries. + +## Settings, installation and release files + +| File | Owner and current role | +|---|---| +| `.OpenGuidePlatform/settings.yaml` | The [settings reader](../../system/OpenGuidePlatform.PowerShell.GuideSiteAdoption/Resolve-OpenGuidePlatform.ps1) accepts `platform.version`, `platform.ring`, `site.source` and `delivery`; version selection can be exact or a major/minor family. See the [user README](../../readme.md#platform-settings-and-updates) for an example. | +| `.OpenGuidePlatform/installation.json` | The [adoption module](../../system/OpenGuidePlatform.PowerShell.GuideSiteAdoption/OpenGuidePlatform.PowerShell.GuideSiteAdoption.psm1) writes the v1 installation record, including release, native dependency and managed-file hashes. The [installed launcher](../../system/OpenGuidePlatform.PowerShell.GuideSiteAdoption/build.ps1) reads it. | +| `release-manifest.json` | The [packager](../../system/OpenGuidePlatform.PowerShell.PlatformBuild/Packaging/Package-OpenGuidePlatform.ps1) writes schema version 2 with package ZIP names, hashes, component versions, one source commit and PlatformBuild's exact GuideSite dependency. | +| `platform.json` | The same [packager](../../system/OpenGuidePlatform.PowerShell.PlatformBuild/Packaging/Package-OpenGuidePlatform.ps1) writes version, source commit, channel, native Hugo module, workflow and tool requirements into the GuideSite package. | +| `.processing/...` | [`Build-GuideSite.ps1`](../../system/OpenGuidePlatform.PowerShell.GuideSiteBuild/GuideSiteBuild/Build-GuideSite.ps1) writes per-run discovery, prepared inputs, tool evidence and validation results there. | + +The [adoption resolver](../../system/OpenGuidePlatform.PowerShell.GuideSiteAdoption/Resolve-OpenGuidePlatform.ps1) validates selected release/package identity; the [adoption module](../../system/OpenGuidePlatform.PowerShell.GuideSiteAdoption/OpenGuidePlatform.PowerShell.GuideSiteAdoption.psm1) handles installation conflicts and the coordinated native Hugo dependency. A run records its selected package for subsequent stages. The generated release manifest and the [platform-lock v1 schema](../../system/OpenGuidePlatform.PowerShell.Core/Contracts/platform-lock.schema.json) are distinct formats. + +## Presentation and current limits + +Hugo dynamically discovers guides, editions, translations and PDF resources through [capability partials and their contracts](hugo-guide-catalogue.md). The [shared catalogue](../../system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/guides/get-guide-catalogue.html) retains Hugo Page and Resource objects; the [compatibility adapter](../../system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/functions/get-guide-translations-catalogue.html) projects the existing public JSON fields. Neither reads `discovered-site.json` or `assessment.json`. + +The [guide rendering template](../../system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/components/guide/render-guide.html) uses capability functions for PDF lookup and same-path translation fallback. It retains presentation and its existing content-availability rules. The original [execution plan](open-guide-platform-execution-plan.md#16a-e14--refactor-hugo-module-contents-last) defers broader Hugo refactoring until consumer adoption; the subsequently approved catalogue work does not establish that those wider gates have passed. + +The [execution plan](open-guide-platform-execution-plan.md#current-acceptance-status) records the remaining consumer adoption, independently administered enforcement, named-release verification and later Hugo work. The presence of a schema or test fixture is not evidence that one of those gates passed. diff --git a/docs/architecture/hugo-guide-catalogue.md b/docs/architecture/hugo-guide-catalogue.md new file mode 100644 index 00000000..4a429428 --- /dev/null +++ b/docs/architecture/hugo-guide-catalogue.md @@ -0,0 +1,73 @@ +# Hugo guide catalogue contract + +The Hugo catalogue is derived from the current Hugo site, its pages and page resources. It is not loaded from the PowerShell discovery or assessment files. PowerShell's source assessment remains a separate build responsibility; see [current system](current-system.md). + +## Ownership and naming + +Capability partials live directly below `layouts/_partials/openguide/{guides,editions,translations,pdfs,history}/`. The folder names describe guide publishing capabilities. There is no nested `functions/` folder. HTML presentation stays under `components/`; the existing `functions/get-...` entry points remain compatibility adapters. + +The paths in the following table are relative to [`openguide/`](../../system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/openguide/). Inputs are Hugo template values, not JSON. + +| Partial | Input | Result and responsibility | +|---|---|---| +| `guides/discover-guides.html` | Page | Current site's sections of type `guide`, deduplicated by relative permalink and ordered by weight. | +| `guides/get-guide-catalogue.html` | Page | `SiteLanguage`, `Guides[]`; aggregates editions, translations and PDF resources. | +| `editions/discover-guide-editions.html` | Dict: `Page`, `Scope`, optional `Order` | `Version`, `Page` records in descending date order, or original page order with `Order: source`. `children` uses the page's children; `section` uses the site's regular pages in the page's section. Both filter type `guide` and require a version in the URL. | +| `editions/get-edition-version.html` | Page | First permalink segment matching `YYYY.number`, or an empty string. | +| `editions/select-latest-guide-edition.html` | Page | `Version`, `Page`; date-first selection with the existing lexical URL fallback. | +| `editions/get-guide-version-options.html` | Page | Existing `version`, `url`, `title`, `date`, `page`, `isLatest` records. Latest links to the section root. | +| `translations/discover-edition-translations.html` | Edition Page | Union of `AllTranslations` languages and language suffixes found in edition PDFs. Each record retains pages and resources. | +| `translations/select-preferred-guide-translations.html` | Guide section Page | Public translation rows with `Version`. Orders editions numerically by year/month; an older readable translation takes precedence over a newer PDF-only translation. Preserves the legacy per-edition override hook. | +| `translations/project-legacy-translation.html` | Discovered translation record | The existing public translation fields, including availability and status. This operation reads `.Plain`. | +| `translations/resolve-translation-fallback.html` | Page | Same-path page in the first Hugo language site, preserving the existing fallback lookup. | +| `pdfs/discover-edition-pdfs.html` | Dict: `Page`, optional `Pattern` | All matching `Name`, `Language`, `Resource` records. Default pattern: `pdf/*.*.pdf`. Language is the penultimate dot-separated filename component. | +| `pdfs/select-translation-pdfs.html` | Dict: `PDFs`, `Language` | Case-insensitive language filter over discovered records, preserving order. | +| `pdfs/select-first-edition-pdf.html` | Dict: `Page`, `Pattern` | First matching resource, or `false`; preserves the rendering templates' previous `GetMatch` selection. | +| `history/get-guide-history-chain.html` | Page | Existing `current`, `history`, `forkSource`, `forkHistory` structure containing Hugo pages; preserves explicit fork edition and fallback behavior. | + +## Internal structure + +`get-guide-catalogue` returns this **in-memory shape**. The names below illustrate a Kanban guide; they are not configuration or a maintained inventory. All entries are discovered. + +```text +SiteLanguage: "en" +Guides: + - Id: "the-kanban-guide" # Hugo section identifier + Page: + Title: "The Kanban Guide" + LatestVersion: "2025.5" + Versions: + - Version: "2025.5" + Page: + Translations: + - Language: "en" + Page: + EditionPage: + PDFs: + - Name: "pdf/kanban-guide.en.pdf" + Language: "en" + Resource: + LegacyPDF: +``` + +An empty translated page remains a page. A PDF-only language without a page has `Page: false`; no translated page or URL is invented. Multiple matching PDFs remain in `PDFs`. `LegacyPDF` preserves the old single-download choice: last matching edition resource, or first matching translated-page resource when the edition has no match. Resource enumeration follows Hugo's `Resources.Match` order. + +Raw discovery and catalogue aggregation do not read `.Content` or `.Plain`. They can be used from rendering without eagerly rendering every translation. Availability projection and preferred-translation selection do read `.Plain` and should not be invoked recursively from a shortcode that is itself being evaluated for that projection. No global cache is introduced; each call retains its language-site context. + +## Public compatibility + +[`functions/get-guide-translations-catalogue.html`](../../system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/functions/get-guide-translations-catalogue.html) explicitly projects the internal catalogue into the existing JSON-compatible shape. Hugo Page and Resource objects must not be passed directly to `jsonify`. `index.translations.json` continues to use this adapter. + +Public guide records retain `Title`, `Path`, `RelPermalink`, `Section`, `Type`, `Weight`, `Description`, `Versions`, `LatestVersion`. Edition records retain `Version`, `Title`, `Date`, `RelPermalink`, `Path`, `Description`, `Latest`, `Translations`. + +Translation records retain exactly `Date`, `Language`, `LanguageName`, `Path`, `PathPdf`, `ReadOnline`, `ReadOnlineStub`, `ReadPDF`, `RelPermalink`, `Status`, `Title`, `VersionPath`, `Weight`. The preferred-language list additionally includes `Version`. `ReadOnlineStub` means a page exists, not that it has readable content. `ReadOnline` preserves the existing greater-than-ten-space-separated-words rule. Status remains `published`, `online-only`, `pdf-only` or `site-only`. + +The rendering components retain their separate greater-than-500-character content rule and production draft checks. Their PDF patterns and first-match policy remain unchanged. Consolidating these differing availability rules would change behavior and is outside this refactor. + +The six existing entry points for catalogue, edition translations, preferred translations, version options, latest version and history remain callable. The catalogue adapter and preferred selection continue calling the legacy per-edition translation hook. Catalogue projection and version options retain the legacy latest-version hook; history retains the legacy version-options hook. Their default implementations delegate to capability partials without recursion. These deliberate extension points preserve existing consumer overrides; raw catalogue discovery still describes the Hugo objects themselves. Consumer adoption must check any additional site-specific overrides. + +## Verification + +[`HugoCatalogue.Tests.ps1`](../../tests/Core/HugoCatalogue.Tests.ps1) builds multilingual fixtures with several guides, dated editions, empty translations, PDF-only languages, regional language tags, multiple PDFs, fork ancestry, equal-date ordering, consumer overrides and production language exclusion. Existing rendering and SEO tests cover their public output. Acceptance also requires the root platform build and both reference-site targets described in [platform development](../platform-development.md). + +These checks establish local platform behavior. They do not establish consumer adoption or deployment approval. diff --git a/docs/platform-development.md b/docs/platform-development.md index d7b75f90..4a86ff37 100644 --- a/docs/platform-development.md +++ b/docs/platform-development.md @@ -12,7 +12,7 @@ Install the tools listed in the README and Node.js 20 or newer and npm (for real ./build.ps1 -Version 0.0.0-local ``` -Without a version override, the platform calculates GitVersion using the same module operation as Actions. The repository currently uses GitVersion 5 configuration, so Dependencies installs a compatible 5.x tool beneath `.processing/tools/` without changing a global GitVersion installation. `-Version` remains an explicit override. +Without a version override, the platform calculates GitVersion using the same module operation as Actions. The repository uses GitVersion 6 configuration; Dependencies installs a compatible 6.x tool beneath `.processing/tools/` without changing a global GitVersion installation. `-Version` remains an explicit override. The platform build runs preparation, tests, packaging and package verification. It writes to a fresh directory under `.processing/platform/`, then builds and validates the sample in preview and production from the exact package ZIP it produced. It does not publish a release or deploy the sample by default; the opt-in commands below add those operations. Pester is a platform-development dependency, not an everyday guide-site requirement. @@ -34,6 +34,8 @@ The built-in hosting adapter follows the [Azure Static Web Apps CLI deployment c ## Build module ownership +For the current component boundaries, source/discovery behavior, file formats and build evidence, see [Current system](architecture/current-system.md). + `OpenGuidePlatform.PowerShell.PlatformBuild` owns platform engineering. `OpenGuidePlatform.PowerShell.GuideSiteBuild` owns guide-site stages and remains independently importable. They ship as separate GuideSite and PlatformBuild ZIP assets in one coordinated release. Root scripts dispatch to the selected module; existing `.build/` build/test/package entry points forward to PlatformBuild. Both root build entry points accept `-PlatformSource Local|Preview|Production|Path`, `-PlatformRelease ` and `-PlatformPath `. Platform checkouts default to their local module. Installed consumers default to their installation lock. An explicit override does not change that lock. Preview/Production without a tag select the latest eligible release once at startup; Production means a non-prerelease platform package, independently of the site's `-Target`. diff --git a/readme.md b/readme.md index a1c1d121..7342d06f 100644 --- a/readme.md +++ b/readme.md @@ -177,6 +177,7 @@ If you need help, include the command, finding and relevant report in a [GitHub - [First-time site setup](docs/using/first-adoption.md) — policy, existing files and deployment setup. - [SEO metadata](docs/using/seo-metadata.md) — homepage titles, social images, publisher logos, authors and publication licences. - [Platform development](docs/platform-development.md) — build this repository, run the sample locally and understand releases. +- [Current system](docs/architecture/current-system.md) — component ownership, guide discovery, contracts, file formats and build evidence. - [Workflow dependency locking](docs/platform-development.md#workflow-dependency-lockfile) — regenerate, verify and review Actions dependency locks when changing platform workflows. - [Execution plan and current progress](docs/architecture/open-guide-platform-execution-plan.md). diff --git a/system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/components/guide/render-guide.html b/system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/components/guide/render-guide.html index 276bca92..c0f115f7 100644 --- a/system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/components/guide/render-guide.html +++ b/system/OpenGuidePlatform.Hugo.Guides/layouts/_partials/components/guide/render-guide.html @@ -57,8 +57,7 @@

{{ i18n "guide_ta {{ .Page.TableOfContents }} {{ else }} {{- /* Get the default guide content as fallback */ -}} - {{ $defaultSite := index hugo.Sites 0 }} - {{ $thisPageDefault := $defaultSite.GetPage .Path }} + {{ $thisPageDefault := partial "openguide/translations/resolve-translation-fallback.html" . }} {{- if not $thisPageDefault }} {{- fmt.Errorf "thisPageDefault page not found" }} @@ -105,7 +104,7 @@

{{ .Title }}

{{- if eq .Site.Language.Lang "en" }} {{- /* Show both EN and EN-US buttons for English */ -}} {{- $pdfPatternEN := "pdf/*.en.pdf" }} - {{- $pdfResourceEN := .Resources.GetMatch $pdfPatternEN }} + {{- $pdfResourceEN := partial "openguide/pdfs/select-first-edition-pdf.html" (dict "Page" . "Pattern" $pdfPatternEN) }} {{- if $pdfResourceEN }} {{ .Title }} {{- end }} {{- $pdfPatternENUS := "pdf/*.en-us.pdf" }} - {{- $pdfResourceENUS := .Resources.GetMatch $pdfPatternENUS }} + {{- $pdfResourceENUS := partial "openguide/pdfs/select-first-edition-pdf.html" (dict "Page" . "Pattern" $pdfPatternENUS) }} {{- if $pdfResourceENUS }} {{ .Title }} {{- else }} {{- /* Single PDF button for other languages */ -}} {{- $pdfPattern := printf "pdf/*.%s.pdf" .Site.Language.Lang }} - {{- $pdfResource := .Resources.GetMatch $pdfPattern }} + {{- $pdfResource := partial "openguide/pdfs/select-first-edition-pdf.html" (dict "Page" . "Pattern" $pdfPattern) }} {{- if $pdfResource }} {{ .Title }} {{- /* PDF intro text with Google Analytics tracking */ -}} {{- if eq .Site.Language.Lang "en" }} {{- $pdfPatternEN := "pdf/*.en.pdf" }} - {{- $pdfResourceEN := .Resources.GetMatch $pdfPatternEN }} + {{- $pdfResourceEN := partial "openguide/pdfs/select-first-edition-pdf.html" (dict "Page" . "Pattern" $pdfPatternEN) }} {{- $pdfPatternENUS := "pdf/*.en-us.pdf" }} - {{- $pdfResourceENUS := .Resources.GetMatch $pdfPatternENUS }} + {{- $pdfResourceENUS := partial "openguide/pdfs/select-first-edition-pdf.html" (dict "Page" . "Pattern" $pdfPatternENUS) }} {{- if or $pdfResourceEN $pdfResourceENUS }}