This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
name: validate
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Validate skill package
|
||||||
|
run: python3 scripts/validate_skill.py
|
||||||
|
- name: Check whitespace
|
||||||
|
run: git diff --check
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Source provenance
|
||||||
|
|
||||||
|
_Last reviewed: 2026-08-14T12:30:45Z_
|
||||||
|
|
||||||
|
This is an independent VSSA-managed skill. It does not automatically mirror or overwrite another project's skill repository.
|
||||||
|
|
||||||
|
## Baseline documentation skill
|
||||||
|
|
||||||
|
The original runtime skill at `/opt/data/skills/productivity/documentation-docusaurus` was byte-identical at `SKILL.md` to the accessible central repository:
|
||||||
|
|
||||||
|
- Repository: `home-v1-skills-code-agent/documentation-docusaurus`
|
||||||
|
- Branch: `test`
|
||||||
|
- Commit: `f810f8c92005e5d55b08700b67fd34a37be435bc`
|
||||||
|
- Baseline `SKILL.md` SHA-256: `f5e4f1e45193629bdba5ad0a34aba5f48493de181b2444ff993e876ce893fe61`
|
||||||
|
|
||||||
|
All baseline reference files were preserved.
|
||||||
|
|
||||||
|
## Merged Gitea/Docusaurus capability
|
||||||
|
|
||||||
|
The local runtime skill `gitea-docusaurus-projects` was absorbed into this package:
|
||||||
|
|
||||||
|
- Source path: `/opt/data/skills/software-development/gitea-docusaurus-projects`
|
||||||
|
- Source `SKILL.md` SHA-256: `a694558fa3a7d336a093e88d801b7ef9092d633ed89610185ac6a0279166c753`
|
||||||
|
|
||||||
|
Its operational body is preserved as `references/gitea-docusaurus-delivery.md`; its references and Gitea Actions template are included directly in this repository. The top-level skill routes users to those capabilities while retaining `gitea-repository-operations` as the authority for general Gitea safety and repository operations.
|
||||||
|
|
||||||
|
## Requested Corp comparison
|
||||||
|
|
||||||
|
The requested path `corp-v1-skills-code-agent/documentation-docusaurus` returned HTTP `404` through the authenticated Gitea API and `Cannot find repository` through SSH. It was not created, modified or otherwise touched.
|
||||||
|
|
||||||
|
The accessible project adoption `corp-v1-aerosim-skills-code-agent/documentation-docusaurus--aerosim` was inspected read-only at commit `3c8e08cdfa24105434bb29a23dffdb8d8656319f`. Its skill content differs from the central baseline only by the adopted skill name; its unique files record independent-adoption provenance. This package adopted that useful principle: central/project sources are compared deliberately, and changes are never pushed back automatically.
|
||||||
|
|
||||||
|
If `corp-v1-skills-code-agent/documentation-docusaurus` later becomes available, compare it read-only against this package and port only generally useful, verified deltas through a normal reviewed commit here.
|
||||||
@@ -1,3 +1,28 @@
|
|||||||
# documentation-docusaurus
|
# documentation-docusaurus
|
||||||
|
|
||||||
VSSA-managed Docusaurus documentation skill with Gitea delivery capabilities
|
VSSA-managed Hermes skill for the complete Docusaurus documentation lifecycle:
|
||||||
|
|
||||||
|
- Diátaxis information architecture and writing quality;
|
||||||
|
- Docusaurus v3 configuration, MDX, React Flow and Draw.io;
|
||||||
|
- evidence registries, generated dossiers and source-to-derived cross-linking;
|
||||||
|
- Gitea organization/repository setup and safe Git delivery;
|
||||||
|
- self-hosted Gitea Actions and LEGO Cloud Pages publication;
|
||||||
|
- build, browser, exact-SHA CI, authenticated readback and deployment verification.
|
||||||
|
|
||||||
|
## Authoritative location
|
||||||
|
|
||||||
|
- Gitea: <https://gitea.lego-cloud.eu/vssa-v1-skills-code-agent/documentation-docusaurus>
|
||||||
|
- Branch: `main`
|
||||||
|
- Maintainer worktree: `/opt/data/documentation-docusaurus-skill`
|
||||||
|
- Runtime installation: `/opt/data/skills/productivity/documentation-docusaurus` → maintainer worktree
|
||||||
|
|
||||||
|
## Validate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/validate_skill.py
|
||||||
|
git diff --check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
Fetch and fast-forward before editing. Commit reusable changes to this repository, push them, verify exact ref equality, read back the changed artifact through the authenticated Gitea API, require the exact-SHA Actions task to succeed, and verify runtime loading. Other project skill repositories are read-only comparison sources unless their owners explicitly request changes.
|
||||||
|
|||||||
@@ -0,0 +1,282 @@
|
|||||||
|
---
|
||||||
|
name: documentation-docusaurus
|
||||||
|
description: >
|
||||||
|
Opinionated Docusaurus v3 guidance using Diátaxis. Use when creating or restructuring documentation,
|
||||||
|
writing MDX, configuring navigation or deployment, building evidence registries and local YAML data,
|
||||||
|
creating React Flow or Draw.io visualizations, provisioning Gitea repositories, or publishing through
|
||||||
|
self-hosted Gitea Actions and LEGO Cloud Pages. Triggers on docs/, blog/, docusaurus.config, sidebars,
|
||||||
|
MDX, documentation data, Gitea documentation repositories, CI/CD, and documentation delivery.
|
||||||
|
Requires audience and outcome clarity, separates documentation types, preserves canonical evidence,
|
||||||
|
and applies accessibility, build, browser, exact-SHA CI, remote-readback, and deployment verification.
|
||||||
|
license: MIT
|
||||||
|
metadata:
|
||||||
|
author: workspace-skills-code-agent
|
||||||
|
version: "2.0"
|
||||||
|
spec: agentskills.io/specification
|
||||||
|
framework: Diátaxis
|
||||||
|
hermes:
|
||||||
|
tags: [docusaurus, documentation, diataxis, drawio, mdx, react-flow, gitea, ci-cd, pages]
|
||||||
|
related_skills: [drawio-main, gitea-repository-operations]
|
||||||
|
compatibility: Designed for Docusaurus v3. Works with Cline, Claude Code, GitHub Copilot, OpenAI Codex, and other compatible agent environments
|
||||||
|
---
|
||||||
|
|
||||||
|
# Docusaurus Documentation Skill
|
||||||
|
|
||||||
|
*Opinionated guidance for building documentation people actually read.*
|
||||||
|
|
||||||
|
## Authoritative repository
|
||||||
|
|
||||||
|
This VSSA-managed skill is maintained in Gitea at
|
||||||
|
`https://gitea.lego-cloud.eu/vssa-v1-skills-code-agent/documentation-docusaurus`.
|
||||||
|
Treat repository `main` as the source of truth. The maintainer worktree is
|
||||||
|
`/opt/data/documentation-docusaurus-skill`, and the runtime installation is a symlink to that
|
||||||
|
worktree. For every reusable improvement: fetch and fast-forward, edit the repository package,
|
||||||
|
run `python3 scripts/validate_skill.py`, review the complete diff, commit and push, verify exact ref
|
||||||
|
equality and authenticated remote readback, require the exact-SHA Gitea Actions task to succeed, and
|
||||||
|
then verify `skill_view(name='documentation-docusaurus')` loads the changed package. Never maintain a
|
||||||
|
divergent runtime-only copy.
|
||||||
|
|
||||||
|
This repository is an independent VSSA adoption. Upstream or project-specific skill repositories are
|
||||||
|
read-only learning sources: compare deliberately and port only generally useful, verified practices.
|
||||||
|
Never push to or rewrite another project's skill repository while maintaining this one. See
|
||||||
|
[`PROVENANCE.md`](PROVENANCE.md).
|
||||||
|
|
||||||
|
## Capability router
|
||||||
|
|
||||||
|
Use this single skill for the full documentation lifecycle:
|
||||||
|
|
||||||
|
| Work | Primary guidance |
|
||||||
|
|---|---|
|
||||||
|
| Audience, page purpose, prose and information architecture | This file plus Diátaxis and writing references |
|
||||||
|
| Docusaurus configuration, MDX, React Flow and Draw.io | Configuration and visualization references |
|
||||||
|
| Existing Markdown/evidence corpus migration | Corpus, registry, cross-linking and multi-section references |
|
||||||
|
| Gitea organization/repository provisioning and Git delivery | [`references/gitea-docusaurus-delivery.md`](references/gitea-docusaurus-delivery.md) plus `gitea-repository-operations` |
|
||||||
|
| Gitea Actions and LEGO Cloud Pages publication | Delivery, behavioral acceptance and deployment references |
|
||||||
|
|
||||||
|
Load `gitea-repository-operations` alongside this skill for repository/API/SSH mutations. This skill
|
||||||
|
defines documentation-specific structure, migration, CI/CD and acceptance requirements; the repository
|
||||||
|
operations skill remains authoritative for general Gitea safety, authentication and remote verification.
|
||||||
|
|
||||||
|
## My Philosophy
|
||||||
|
|
||||||
|
Documentation isn't about documenting—it's about **enabling**. Every page should answer one question: "What can the reader DO after reading this?"
|
||||||
|
|
||||||
|
I follow the **Diátaxis framework**. Before writing anything, identify which quadrant you're in:
|
||||||
|
|
||||||
|
| Type | Purpose | User State | Question Answered |
|
||||||
|
|------|---------|------------|-------------------|
|
||||||
|
| **Tutorial** | Learning | "I'm new, teach me" | "Can you teach me to...?" |
|
||||||
|
| **How-to** | Doing | "I need to accomplish X" | "How do I...?" |
|
||||||
|
| **Reference** | Information | "I need to look up Y" | "What is the API for...?" |
|
||||||
|
| **Explanation** | Understanding | "I want to understand why" | "Why does...?" |
|
||||||
|
|
||||||
|
**Don't mix them.** A tutorial that becomes reference midway loses both audiences.
|
||||||
|
|
||||||
|
## Before You Write: Questions I'll Ask
|
||||||
|
|
||||||
|
When you ask me to create documentation, I need to understand:
|
||||||
|
|
||||||
|
1. **Who is reading this?** (New user? Developer? API consumer? Decision maker?)
|
||||||
|
2. **What should they be able to DO after?** (Not "know"—DO)
|
||||||
|
3. **Which Diátaxis quadrant?** (Tutorial/How-to/Reference/Explanation)
|
||||||
|
4. **What do they already know?** (Prerequisites matter)
|
||||||
|
|
||||||
|
If you haven't thought through these, I'll ask. Good docs require clear thinking first.
|
||||||
|
|
||||||
|
## Structure: My Strong Opinions
|
||||||
|
|
||||||
|
### Sidebar Organization
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── getting-started/ # Tutorials: learning journeys
|
||||||
|
│ ├── _category_.json # collapsed: false
|
||||||
|
│ └── ...
|
||||||
|
├── guides/ # How-tos: task completion
|
||||||
|
├── concepts/ # Explanation: understanding
|
||||||
|
├── reference/ # Reference: lookup
|
||||||
|
│ ├── api/
|
||||||
|
│ └── configuration/
|
||||||
|
└── resources/ # Links, community, changelog
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why this order?** It matches the reader's journey: Learn → Do → Understand → Look up.
|
||||||
|
|
||||||
|
### Frontmatter: Non-Negotiables
|
||||||
|
|
||||||
|
Every doc needs these. No exceptions:
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
title: "Action-Oriented Title" # What they'll DO, not what it IS
|
||||||
|
description: "One sentence outcome" # Appears in search, make it count
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
Skip `sidebar_position` unless order matters semantically. Let alphabetical work.
|
||||||
|
|
||||||
|
### Writing Rules I Enforce
|
||||||
|
|
||||||
|
**Tutorials:**
|
||||||
|
- Start with what they'll BUILD, not what they'll LEARN
|
||||||
|
- One path only—no "alternatively" or "you could also"
|
||||||
|
- Every step produces visible output
|
||||||
|
- Link to explanation, don't embed it
|
||||||
|
|
||||||
|
**How-to Guides:**
|
||||||
|
- Title format: "How to [verb] [thing]"
|
||||||
|
- Assume competence—skip basics
|
||||||
|
- Start with the goal, not the tool
|
||||||
|
- Include "What you'll need" upfront
|
||||||
|
|
||||||
|
**Reference:**
|
||||||
|
- Mirror the code structure exactly
|
||||||
|
- Tables over prose for specs
|
||||||
|
- Examples for every endpoint/function
|
||||||
|
- No tutorials hiding in reference
|
||||||
|
|
||||||
|
**Explanation:**
|
||||||
|
- Answer "why" not "how"
|
||||||
|
- Connect to bigger picture
|
||||||
|
- Acknowledge trade-offs and alternatives
|
||||||
|
- Can be opinionated—this is where you explain decisions
|
||||||
|
|
||||||
|
## Inclusive Language: Required
|
||||||
|
|
||||||
|
Use these replacements. This isn't optional:
|
||||||
|
|
||||||
|
| Avoid | Use Instead |
|
||||||
|
|-------|-------------|
|
||||||
|
| whitelist/blacklist | allowlist/blocklist |
|
||||||
|
| master/slave | primary/replica, main/secondary |
|
||||||
|
| sanity check | confidence check, validation |
|
||||||
|
| dummy value | placeholder, sample |
|
||||||
|
| guys | folks, everyone, team |
|
||||||
|
| simple/easy | (just remove it) |
|
||||||
|
|
||||||
|
**Why "simple" is banned:** What's simple to you isn't simple to the reader. Saying "simply run X" makes struggling readers feel dumb.
|
||||||
|
|
||||||
|
**Pronouns:** Use "you" for the reader. Use "they" for hypothetical users. Avoid "we" unless it's genuinely collaborative.
|
||||||
|
|
||||||
|
## Dynamic Documentation Patterns
|
||||||
|
|
||||||
|
### Local YAML data-backed documentation
|
||||||
|
|
||||||
|
When Epics, Features, Tasks, requirements, or architectural decisions must feed several documentation views, keep the authoritative records in a local `.yml`-only data tree and load them during the Docusaurus build. Requirements must use separate functional and non-functional folders, and architectural decisions must use the exact `data/architecture-decisions/` folder. Do not duplicate records inside MDX. Follow [references/local-yaml-data-database.md](references/local-yaml-data-database.md) for record schemas, stable IDs, canonical relationships, generated indexes, validation, React hooks/components, MDX composition, and maintenance commands.
|
||||||
|
|
||||||
|
### React Flow roadmaps and dependency maps
|
||||||
|
|
||||||
|
Use `@xyflow/react` when validated documentation data needs an interactive roadmap, Gantt-like timeline, Feature dependency graph, Requirement-to-Feature traceability map, or architectural decision-to-Feature map. React Flow is the renderer, not the source database or an automatic layout engine. Follow [references/react-flow-visualizations.md](references/react-flow-visualizations.md) for Docusaurus BrowserOnly integration, deterministic Gantt positioning, Dagre/ELK layout choices, source-YAML adapters, read-only interaction, accessibility, sharp styling, semantic table fallbacks, testing, and browser verification.
|
||||||
|
|
||||||
|
### Draw.io source diagrams
|
||||||
|
|
||||||
|
When a page needs a Draw.io diagram, keep the editable `.drawio` XML in the documentation repository and render that source directly with `docusaurus-plugin-drawio`. Load `drawio-main` to author or change the diagram, then follow [references/drawio-source-diagrams.md](references/drawio-source-diagrams.md) to install the plugin with pnpm, register it, consume the source from MDX with `@theme/Drawio` and `!!raw-loader!`, select multi-page diagrams, and verify the production build and browser rendering. An exported PNG/SVG alone is not an onboarded source diagram.
|
||||||
|
|
||||||
|
### Interactive Elements (Use Sparingly)
|
||||||
|
|
||||||
|
Tabs for platform differences:
|
||||||
|
```mdx
|
||||||
|
<Tabs groupId="os">
|
||||||
|
<TabItem value="mac" label="macOS" default>
|
||||||
|
```
|
||||||
|
|
||||||
|
Details for optional deep-dives:
|
||||||
|
```mdx
|
||||||
|
<details>
|
||||||
|
<summary>Why does this matter?</summary>
|
||||||
|
...explanation that most readers can skip...
|
||||||
|
</details>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Don't use tabs for:** Code language alternatives (pick one and show it well), or "beginner vs advanced" (separate pages instead).
|
||||||
|
|
||||||
|
### Admonitions: The Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
:::tip → "This will make your life easier"
|
||||||
|
:::note → "Relevant context you might miss"
|
||||||
|
:::warning → "This could cause problems"
|
||||||
|
:::danger → "This WILL break things if ignored"
|
||||||
|
```
|
||||||
|
|
||||||
|
**One admonition per section max.** If everything is highlighted, nothing is.
|
||||||
|
|
||||||
|
## What I Won't Do
|
||||||
|
|
||||||
|
- Create docs without understanding the audience
|
||||||
|
- Mix documentation types in one page
|
||||||
|
- Add "simple" or "easy" to instructions
|
||||||
|
- Generate walls of code without context
|
||||||
|
- Skip frontmatter description fields
|
||||||
|
- Create sidebars deeper than 3 levels
|
||||||
|
|
||||||
|
## Reference Files
|
||||||
|
|
||||||
|
Load the one that matches the task. Don't read all four.
|
||||||
|
|
||||||
|
| You're about to | Read |
|
||||||
|
|---|---|
|
||||||
|
| Write a page, or decide which quadrant it belongs in | [references/diataxis-patterns.md](references/diataxis-patterns.md) — copy-ready template per quadrant, the decision tree, and how to link between them |
|
||||||
|
| Write or edit prose, headings, lists, or code examples | [references/writing-guide.md](references/writing-guide.md) — voice, words to cut, inclusive language, pre-publish checklist |
|
||||||
|
| Touch `docusaurus.config.ts` | [references/config-reference.md](references/config-reference.md) — the options that matter, sensible defaults, config anti-patterns |
|
||||||
|
| Build or maintain a local YAML database that feeds React components and MDX | [references/local-yaml-data-database.md](references/local-yaml-data-database.md) — dedicated entity folders, `.yml` schemas, indexes, relationships, build-time loading, typed hooks, and validation |
|
||||||
|
| Build a React Flow roadmap, Gantt-like view, or dependency/traceability map | [references/react-flow-visualizations.md](references/react-flow-visualizations.md) — `@xyflow/react`, Docusaurus client boundaries, YAML adapters, layouts, accessibility, fallbacks, and verification |
|
||||||
|
| Onboard or render an editable `.drawio` source | [references/drawio-source-diagrams.md](references/drawio-source-diagrams.md) — pnpm install, plugin registration, MDX raw-source import, viewer options, and verification |
|
||||||
|
| Repair a docs section that builds but violates a required hierarchy or content contract | [references/architecture-ia-remediation.md](references/architecture-ia-remediation.md) — RED-first structure validation, approval-safe scaffolding, link pitfalls, browser checks, and remote PR/CI readback |
|
||||||
|
| Ship the site, or debug a failing build | [references/deployment.md](references/deployment.md) — Vercel, Netlify, GitHub Pages, Cloudflare, and common failures |
|
||||||
|
|
||||||
|
### Gitea, evidence-portal and publication references
|
||||||
|
|
||||||
|
| You're about to | Read |
|
||||||
|
|---|---|
|
||||||
|
| Provision or publish a Docusaurus repository on Gitea/LEGO Cloud Pages | [references/gitea-docusaurus-delivery.md](references/gitea-docusaurus-delivery.md) — merged repository, pnpm, Actions, Pages and verification workflow |
|
||||||
|
| Preserve an established Markdown/evidence corpus during Docusaurus adoption | [references/migrating-existing-markdown-corpora.md](references/migrating-existing-markdown-corpora.md) |
|
||||||
|
| Build a dedicated homepage and fully local search | [references/dedicated-homepage-local-search.md](references/dedicated-homepage-local-search.md) |
|
||||||
|
| Generate a conservative cross-client system/provider registry | [references/generated-evidence-registries.md](references/generated-evidence-registries.md) |
|
||||||
|
| Organize a large evidence portal around user tasks | [references/research-led-evidence-registry-ia.md](references/research-led-evidence-registry-ia.md) |
|
||||||
|
| Split Clients, Systems, Contractors or other major collections into dedicated sidebars | [references/multi-section-evidence-portals.md](references/multi-section-evidence-portals.md) |
|
||||||
|
| Link source observations to generated system/contractor dossiers | [references/cross-linking-source-and-derived-dossiers.md](references/cross-linking-source-and-derived-dossiers.md) |
|
||||||
|
| Prove interactive behavior rather than only a green build | [references/behavioral-acceptance-for-doc-portals.md](references/behavioral-acceptance-for-doc-portals.md) |
|
||||||
|
| Adapt a selected reference design without copying it | [references/reference-inspired-documentation-portals.md](references/reference-inspired-documentation-portals.md) |
|
||||||
|
| Use C4 Mermaid or the maintained modern theme | [references/c4-docusaurus.md](references/c4-docusaurus.md) and [references/modern-docusaurus-theme.md](references/modern-docusaurus-theme.md) |
|
||||||
|
| Start a source-backed research/address-book repository | [references/research-address-book-repositories.md](references/research-address-book-repositories.md) |
|
||||||
|
|
||||||
|
The reusable Gitea Actions starter is
|
||||||
|
[`templates/gitea-actions-build.yml`](templates/gitea-actions-build.yml). Treat it as a starting point,
|
||||||
|
then adapt it to the repository's actual package manager, validators, route base, Pages destination and
|
||||||
|
self-hosted runner constraints.
|
||||||
|
|
||||||
|
## Package-manager validation pitfall
|
||||||
|
|
||||||
|
If `pnpm` is available only through `corepack`, `corepack pnpm validate` can still fail when the `validate` script invokes nested `pnpm` commands, because child scripts resolve `pnpm` from `PATH`. Before treating this as a repository failure, either:
|
||||||
|
|
||||||
|
- add a trusted `pnpm` shim that executes `corepack pnpm "$@"` to `PATH`; or
|
||||||
|
- run each validation script directly through `corepack pnpm`.
|
||||||
|
|
||||||
|
Re-run the complete validator and production build after fixing command resolution. Do not report a successful build from the outer command alone.
|
||||||
|
|
||||||
|
## Gitea delivery gate
|
||||||
|
|
||||||
|
A local build is not a delivered documentation change. For Gitea-backed sites, completion requires:
|
||||||
|
|
||||||
|
1. discovery and synchronization before mutation;
|
||||||
|
2. project validators, production build and `git diff --check`;
|
||||||
|
3. reviewed commit and push through explicit non-interactive SSH identity;
|
||||||
|
4. local/remote ref equality and authenticated readback of a distinctive source marker;
|
||||||
|
5. the latest terminal Gitea Actions task matched to the exact pushed SHA, with every matching task in
|
||||||
|
`success` state;
|
||||||
|
6. route-level and behavior-level verification of changed documentation; and
|
||||||
|
7. deployed content verification through the authorized Pages path or service proxy. An expected
|
||||||
|
Keycloak/OAuth `302` proves the authentication boundary, not the changed page body.
|
||||||
|
|
||||||
|
Never treat a push, green build, visible control, HTTP status alone, or guessed static route as feature
|
||||||
|
acceptance. Map every requirement to direct evidence and preserve canonical source artifacts rather than
|
||||||
|
editing generated output.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Tell me:
|
||||||
|
1. What documentation you're building
|
||||||
|
2. Who it's for
|
||||||
|
3. What they should be able to do after
|
||||||
|
|
||||||
|
I'll ask follow-up questions, then we'll build something people actually want to read.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Architecture information-architecture remediation
|
||||||
|
|
||||||
|
Use this workflow when a Docusaurus architecture section builds successfully but does not satisfy its required navigation or content contract.
|
||||||
|
|
||||||
|
## Separate the three verdicts
|
||||||
|
|
||||||
|
Audit and report these independently:
|
||||||
|
|
||||||
|
1. **Structural compliance** — dedicated docs plugin/sidebar, exact ordering and nesting, stable document IDs, routes, and registry links.
|
||||||
|
2. **Content completeness** — required C4 levels, registries, responsibilities, relationships, traceability, and explicit placeholders for gated decisions.
|
||||||
|
3. **Build/render health** — structure validator, typecheck, strict broken-link production build, diagram-source validation, browser rendering, and console errors.
|
||||||
|
|
||||||
|
A green build proves only build health. An autogenerated sidebar or matching headings inside one page does not prove structural compliance.
|
||||||
|
|
||||||
|
## Safe remediation sequence
|
||||||
|
|
||||||
|
1. Fetch the remote base branch and create a clean worktree/branch from its exact SHA.
|
||||||
|
2. Encode the required hierarchy in a deterministic validator and run it once to prove RED.
|
||||||
|
3. Replace autogenerated navigation with an explicit sidebar when semantic order matters.
|
||||||
|
4. Add the smallest compliant pages and registries. If product/solution approval is absent, create visibly gated scaffolding rather than inventing requirements, decisions, tasks, interfaces, or versions.
|
||||||
|
5. Keep editable `.drawio` sources beside the consuming architecture pages. Render them through `docusaurus-plugin-drawio` and MDX raw imports; do not substitute download links or static exports.
|
||||||
|
6. Run the structure validator, Draw.io/XML validation, typecheck, strict-link production build, and `git diff --check`.
|
||||||
|
7. Serve the production build and verify the actual sidebar nesting, representative routes, rendered diagram DOM, and browser console.
|
||||||
|
8. Commit and push, open a reviewable PR, read back its head/base SHAs and changed-file list, and wait for the exact commit's CI status to reach a terminal state.
|
||||||
|
9. Post one concise evidence message in the governing project channel with PR, commit, structure, validation, and remaining human gate.
|
||||||
|
|
||||||
|
## Docusaurus link pitfall
|
||||||
|
|
||||||
|
Docusaurus may resolve a source-relative link such as `./containers` from the generated page route (`/architecture/overview/`) rather than from the source-file directory, producing `/architecture/overview/containers/`. For links between sibling architecture documents, prefer explicit site-root routes such as `/architecture/containers/` and `/architecture/features/<id>/overview/`, then let the production build's broken-link check verify them.
|
||||||
|
|
||||||
|
Do not weaken `onBrokenLinks` or broken-Markdown-link handling to make remediation pass.
|
||||||
|
|
||||||
|
## Verification evidence to retain
|
||||||
|
|
||||||
|
- base branch and SHA;
|
||||||
|
- feature branch and commit SHA;
|
||||||
|
- validator output;
|
||||||
|
- strict production-build result;
|
||||||
|
- Draw.io validation result for every edited source;
|
||||||
|
- browser evidence for sidebar hierarchy and representative rendered diagram;
|
||||||
|
- zero relevant console/JavaScript errors;
|
||||||
|
- PR URL, mergeability, exact remote head SHA, and terminal CI result.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Behavioral acceptance for Docusaurus documentation portals
|
||||||
|
|
||||||
|
Use this gate when a request includes interactive behavior such as theme switching, section navigation, or local search. A successful build and Actions run prove compilation/publication only; they do not prove the requested behavior works.
|
||||||
|
|
||||||
|
## Requirement-to-proof matrix
|
||||||
|
|
||||||
|
Write the matrix before implementation and require one direct proof per request:
|
||||||
|
|
||||||
|
| Requirement | Source/config proof | Built-artifact proof | Browser proof |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Light/dark mode | `disableSwitch: false`; separate light/dark tokens | both selectors present in compiled CSS | click toggle; verify `data-theme`, persisted theme, and computed colors all change |
|
||||||
|
| Dedicated section sidebars | separate docs-plugin instances, sidebar files, and `docSidebar` navbar items | each section route renders its own sidebar labels | open every top-menu section and inspect `Docs sidebar`; ensure unrelated section labels are absent |
|
||||||
|
| Local search | local-search dependency/theme configured; no hosted provider | generated search index contains representative terms from every docs instance | type a query, wait for loading to end, confirm a visible result, and open it |
|
||||||
|
|
||||||
|
Do not report completion until every requested row has a browser proof or an explicitly stated blocker.
|
||||||
|
|
||||||
|
## Multi-section sidebars
|
||||||
|
|
||||||
|
A differently named category inside one shared sidebar is not a dedicated sidebar. For top-level menu areas that must own navigation:
|
||||||
|
|
||||||
|
1. Keep one default docs instance for overview/global theme compatibility.
|
||||||
|
2. Add one `@docusaurus/plugin-content-docs` instance per section with a unique `id`, content `path`, `routeBasePath`, and `sidebarPath`.
|
||||||
|
3. Give each sidebar an independently named root, such as `projectsSidebar`.
|
||||||
|
4. Configure each navbar entry as `type: 'docSidebar'`, with both `docsPluginId` and `sidebarId`.
|
||||||
|
5. Give each section index `slug: /` relative to its plugin route.
|
||||||
|
6. Build and inspect every top-level route, not only the source configuration.
|
||||||
|
|
||||||
|
## Static local-search verification
|
||||||
|
|
||||||
|
Use `@easyops-cn/docusaurus-search-local` as a theme and include every docs route base in `docsRouteBasePath`. Search must remain static/browser-local, with no Algolia or hosted crawler credentials.
|
||||||
|
|
||||||
|
Prefer `hashed: 'filename'` on static hosting that redirects requests carrying query strings. With `hashed: true`, the theme fetches `search-index.json?_=HASH`; a host that redirects query-string asset requests can leave the search field indefinitely loading even though `search-index.json` exists. Filename hashing emits and fetches `search-index-HASH.json` directly.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
1. Assert exactly one `search-index*.json` exists in `build/`.
|
||||||
|
2. Inspect it for representative terms from overview and every docs plugin.
|
||||||
|
3. Serve the production build under the real configured `baseUrl`.
|
||||||
|
4. Type a representative query in the navbar.
|
||||||
|
5. Confirm loading ends and a visible result appears.
|
||||||
|
6. Open the result and verify its route.
|
||||||
|
7. After deployment, fetch the exact generated hashed index from the Pages backend and repeat term checks.
|
||||||
|
|
||||||
|
## Theme debugging
|
||||||
|
|
||||||
|
If the toggle exists but the appearance does not change, inspect hard-coded colors before changing Docusaurus configuration. Typical root causes are:
|
||||||
|
|
||||||
|
- `html, body` fixed to the dark background;
|
||||||
|
- navbar/sidebar/footer colors fixed with `!important`;
|
||||||
|
- custom homepage CSS modules defining only dark values;
|
||||||
|
- light and dark Prism themes configured identically.
|
||||||
|
|
||||||
|
Move component colors behind semantic variables. Define a complete light palette in `:root, [data-theme='light']` and dark overrides in `[data-theme='dark']`. For CSS modules, use `:global([data-theme='light']) .page` to override page-level tokens. Browser verification must compare computed colors, not only the `data-theme` attribute.
|
||||||
|
|
||||||
|
## Reporting discipline
|
||||||
|
|
||||||
|
Do not substitute these for behavioral acceptance:
|
||||||
|
|
||||||
|
- local TypeScript success;
|
||||||
|
- production-build success;
|
||||||
|
- successful CI;
|
||||||
|
- a deployed HTML marker;
|
||||||
|
- presence of the toggle/search input/sidebar container.
|
||||||
|
|
||||||
|
Those are supporting checks. Report the pipeline only after matching it to the intended commit SHA, and report the feature only after exercising it.
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# C4 Architecture in Docusaurus — Mermaid Syntax Reference
|
||||||
|
|
||||||
|
Mermaid (built into Docusaurus via `@docusaurus/theme-mermaid`) supports C4 diagrams natively.
|
||||||
|
|
||||||
|
## L1 — C4Context
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
C4Context
|
||||||
|
title System Context — My Application
|
||||||
|
|
||||||
|
Person(user, "User", "Description of the human actor.")
|
||||||
|
|
||||||
|
System(mySystem, "My System", "What it does in one sentence.")
|
||||||
|
|
||||||
|
System_Ext(extA, "External System A", "Description")
|
||||||
|
System_Ext(extB, "External System B", "Description")
|
||||||
|
|
||||||
|
Rel(user, mySystem, "Uses")
|
||||||
|
Rel(mySystem, extA, "Fetches data", "REST API")
|
||||||
|
Rel(mySystem, extB, "Delivers output", "Webhook")
|
||||||
|
```
|
||||||
|
|
||||||
|
## L2 — C4Container
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
C4Container
|
||||||
|
title Container Diagram — My Application
|
||||||
|
|
||||||
|
Person(user, "User", "Description")
|
||||||
|
|
||||||
|
System_Boundary(mySystem, "My Application") {
|
||||||
|
Container(api, "API Service", "Python / FastAPI", "Handles requests.")
|
||||||
|
Container(worker, "Background Worker", "Python / Celery", "Async processing.")
|
||||||
|
ContainerDb(db, "Database", "PostgreSQL", "Stores data.")
|
||||||
|
Container(notifier, "Notifier", "Python", "Sends notifications.")
|
||||||
|
}
|
||||||
|
|
||||||
|
System_Ext(extA, "External System", "Description")
|
||||||
|
|
||||||
|
Rel(user, api, "Calls", "HTTPS")
|
||||||
|
Rel(api, worker, "Enqueues tasks", "Redis")
|
||||||
|
Rel(worker, db, "Reads/writes", "SQL")
|
||||||
|
Rel(notifier, extA, "Posts", "Webhook")
|
||||||
|
```
|
||||||
|
|
||||||
|
## L3 — C4Component
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
C4Component
|
||||||
|
title Components — API Service
|
||||||
|
|
||||||
|
Container_Ext(db, "Database", "PostgreSQL", "Stores data")
|
||||||
|
Container_Ext(worker, "Worker", "Celery", "Async tasks")
|
||||||
|
|
||||||
|
Container_Boundary(api, "API Service") {
|
||||||
|
Component(router, "Request Router", "FastAPI", "Routes HTTP requests.")
|
||||||
|
Component(authMiddleware, "Auth Middleware", "Python", "Validates tokens.")
|
||||||
|
Component(handler, "Request Handler", "Python", "Business logic.")
|
||||||
|
Component(repo, "Repository", "SQLAlchemy", "DB access layer.")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rel(router, authMiddleware, "Passes request")
|
||||||
|
Rel(authMiddleware, handler, "Authenticated request")
|
||||||
|
Rel(handler, repo, "Queries data")
|
||||||
|
Rel(repo, db, "SQL", "TCP")
|
||||||
|
Rel(handler, worker, "Enqueues task", "Redis")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sidebar Structure for C4
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// In sidebars.ts — recommended structure per app
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'C4 Architecture',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
'applications/app-name/c4/context', // L1
|
||||||
|
'applications/app-name/c4/containers', // L2
|
||||||
|
'applications/app-name/c4/components', // L3
|
||||||
|
],
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
- Each C4 level is a separate `.md` file under `docs/applications/<app>/c4/`
|
||||||
|
- Add an `:::info C4 Model — Level N` admonition at the top of each page to explain what the level shows
|
||||||
|
- For interactive diagrams (richer layout), use `draw.io` via `docusaurus-plugin-drawio` instead of Mermaid
|
||||||
|
- Mermaid C4 is best for quick text-as-code diagrams; drawio is better for polished presentation diagrams
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
# Configuration Reference
|
||||||
|
|
||||||
|
Essential `docusaurus.config.ts` options. Not exhaustive—see [Docusaurus docs](https://docusaurus.io/docs/api/docusaurus-config) for everything.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Minimum Viable Config
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const config = {
|
||||||
|
title: 'Project Name',
|
||||||
|
url: 'https://docs.example.com',
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
|
presets: [
|
||||||
|
['classic', {
|
||||||
|
docs: { routeBasePath: '/' }, // Docs at root
|
||||||
|
blog: false, // Disable if not using
|
||||||
|
theme: { customCss: './src/css/custom.css' },
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
My opinion: Start minimal. Add complexity only when you need it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Options That Actually Matter
|
||||||
|
|
||||||
|
### Site Identity
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
title: 'Your Project', // Browser tab, search results
|
||||||
|
tagline: 'One sentence pitch', // Shows in default homepage
|
||||||
|
favicon: 'img/favicon.ico',
|
||||||
|
```
|
||||||
|
|
||||||
|
### URLs
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
url: 'https://docs.example.com', // Production URL (no trailing slash)
|
||||||
|
baseUrl: '/', // Usually '/' unless in subdirectory
|
||||||
|
trailingSlash: false, // Pick one and stick with it
|
||||||
|
```
|
||||||
|
|
||||||
|
**Opinion:** `trailingSlash: false`. It's cleaner and most platforms handle it correctly.
|
||||||
|
|
||||||
|
### Build Behavior
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
onBrokenLinks: 'throw', // Fail build on broken links
|
||||||
|
onBrokenMarkdownLinks: 'throw', // Same for markdown links
|
||||||
|
```
|
||||||
|
|
||||||
|
**Opinion:** Always `throw` in production. `warn` during development is OK.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Preset: Classic (Use This)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
presets: [
|
||||||
|
['classic', {
|
||||||
|
docs: {
|
||||||
|
sidebarPath: './sidebars.ts',
|
||||||
|
routeBasePath: '/', // Docs as homepage
|
||||||
|
editUrl: 'https://github.com/org/repo/edit/main/',
|
||||||
|
showLastUpdateTime: true, // Shows git commit time
|
||||||
|
},
|
||||||
|
blog: {
|
||||||
|
showReadingTime: true,
|
||||||
|
blogSidebarCount: 'ALL',
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
customCss: './src/css/custom.css',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docs-Only Mode
|
||||||
|
|
||||||
|
Set `routeBasePath: '/'` and either `blog: false` or keep blog at `/blog`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Theme Config: What Readers See
|
||||||
|
|
||||||
|
### Navbar
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
themeConfig: {
|
||||||
|
navbar: {
|
||||||
|
title: 'Project',
|
||||||
|
logo: { alt: 'Logo', src: 'img/logo.svg' },
|
||||||
|
items: [
|
||||||
|
{ type: 'docSidebar', sidebarId: 'docs', label: 'Docs' },
|
||||||
|
{ to: '/blog', label: 'Blog' },
|
||||||
|
{ href: 'https://github.com/...', label: 'GitHub', position: 'right' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Opinion:** Keep navbar items under 5. More than that, use dropdowns.
|
||||||
|
|
||||||
|
### Color Mode
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
colorMode: {
|
||||||
|
defaultMode: 'light', // Or 'dark'
|
||||||
|
respectPrefersColorScheme: true, // Honor system preference
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
**Opinion:** Always `respectPrefersColorScheme: true`. Don't force your preference.
|
||||||
|
|
||||||
|
### Sidebar Behavior
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
docs: {
|
||||||
|
sidebar: {
|
||||||
|
hideable: true, // Let users collapse it
|
||||||
|
autoCollapseCategories: true, // Only one category expanded
|
||||||
|
},
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
### Search (Algolia)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
algolia: {
|
||||||
|
appId: 'YOUR_APP_ID',
|
||||||
|
apiKey: 'YOUR_SEARCH_API_KEY', // Public search-only key
|
||||||
|
indexName: 'YOUR_INDEX',
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply at [Algolia DocSearch](https://docsearch.algolia.com/apply). It's free for open source.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plugins Worth Adding
|
||||||
|
|
||||||
|
### Client-Side Redirects
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
plugins: [
|
||||||
|
['@docusaurus/plugin-client-redirects', {
|
||||||
|
redirects: [
|
||||||
|
{ from: '/old-page', to: '/new-page' },
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
Use for moved pages. Don't leave 404s.
|
||||||
|
|
||||||
|
### Ideal Image
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
['@docusaurus/plugin-ideal-image', {
|
||||||
|
quality: 85,
|
||||||
|
max: 1030,
|
||||||
|
min: 640,
|
||||||
|
}],
|
||||||
|
```
|
||||||
|
|
||||||
|
Auto-generates responsive images. Worth it for image-heavy docs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Things I Never Configure
|
||||||
|
|
||||||
|
These defaults are fine:
|
||||||
|
|
||||||
|
- `i18n` (unless you actually have translations ready)
|
||||||
|
- `onDuplicateRoutes` (the default `warn` is fine)
|
||||||
|
- `staticDirectories` (default `static` works)
|
||||||
|
- `titleDelimiter` (the `|` is universal)
|
||||||
|
|
||||||
|
Don't add config for things you're not using. It's clutter.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Config Anti-Patterns
|
||||||
|
|
||||||
|
**Don't:** Copy entire example configs and modify
|
||||||
|
**Do:** Start minimal, add what you need
|
||||||
|
|
||||||
|
**Don't:** Set `onBrokenLinks: 'ignore'` to "fix" build errors
|
||||||
|
**Do:** Actually fix the broken links
|
||||||
|
|
||||||
|
**Don't:** Add `customFields` unless you're using them in code
|
||||||
|
**Do:** Keep config lean
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
For values that change between environments:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const config = {
|
||||||
|
url: process.env.SITE_URL || 'http://localhost:3000',
|
||||||
|
customFields: {
|
||||||
|
apiUrl: process.env.API_URL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Access in components:
|
||||||
|
```javascript
|
||||||
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
const { siteConfig } = useDocusaurusContext();
|
||||||
|
const apiUrl = siteConfig.customFields.apiUrl;
|
||||||
|
```
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Cross-linking source dossiers to generated derived dossiers
|
||||||
|
|
||||||
|
Use this pattern when canonical entity pages contain observations that are consolidated into generated records such as systems, suppliers, or projects.
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Add a generated column to each source observation table that displays the derived record's internal ID and links to its documentation route, without editing the canonical source corpus or duplicating identity logic.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```md
|
||||||
|
| Service or system | System dossier | Relationship | Evidence | Confidence |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| PPVIS | [`SYS-2D859CB9C5`](/org/repo/systems/ppvis-2d859cb9c5/) | operates | … | high |
|
||||||
|
```
|
||||||
|
|
||||||
|
## Generation pattern
|
||||||
|
|
||||||
|
1. Parse canonical source tables and retain a stable observation locator, normally `(source_file, source_line)`.
|
||||||
|
2. Run the canonical identity/consolidation algorithm once.
|
||||||
|
3. When assigning each derived record its ID and slug, annotate every contributing observation with both values.
|
||||||
|
4. Return an observation-link map from the derived-registry generator:
|
||||||
|
|
||||||
|
```python
|
||||||
|
{
|
||||||
|
(observation["source_file"], observation["source_line"]): {
|
||||||
|
"id": observation["derived_id"],
|
||||||
|
"slug": observation["derived_slug"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Generate MDX-safe source dossier copies. Add the new table header and inject one link into each data row using that map.
|
||||||
|
6. Leave canonical source Markdown unchanged; generated display enrichment belongs in the publishing layer.
|
||||||
|
|
||||||
|
## Coverage invariant
|
||||||
|
|
||||||
|
Generation must fail when any parsed observation is not linked:
|
||||||
|
|
||||||
|
```python
|
||||||
|
expected = sum(1 for source, _line in observation_links if source == source_file)
|
||||||
|
if linked != expected:
|
||||||
|
raise SystemExit(
|
||||||
|
f"derived-link coverage mismatch in {source_file}: "
|
||||||
|
f"linked={linked} expected={expected}"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
After generation, independently verify:
|
||||||
|
|
||||||
|
- displayed link count equals the registry observation count;
|
||||||
|
- every `(ID, slug)` target exists in the generated registry;
|
||||||
|
- the number of unique targets equals the expected derived-record count;
|
||||||
|
- representative legacy and structured table formats render correctly;
|
||||||
|
- the full Docusaurus production build and route validator pass.
|
||||||
|
|
||||||
|
## Important pitfalls
|
||||||
|
|
||||||
|
- Do not recompute identity independently in the source-page generator. Reuse the canonical registry result or links will drift when aliases change.
|
||||||
|
- Do not match only by displayed label; duplicate labels and client-scoped generic resources make that unsafe.
|
||||||
|
- Do not use the internal ID as a guessed route. Display the ID, but link with the generated canonical slug.
|
||||||
|
- Preserve numeric source line locators until enrichment is complete. Transformations such as frontmatter injection or `<br>` normalization can otherwise shift line numbers.
|
||||||
|
- Ensure generated internal links are not collected into public-source References indexes; references should remain provenance links, not site-navigation links.
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
# Dedicated Docusaurus Homepage + Native Local Search
|
||||||
|
|
||||||
|
Use this pattern when a docs-only Docusaurus site needs a product/showcase homepage and search that runs entirely from static build artifacts.
|
||||||
|
|
||||||
|
## Route separation
|
||||||
|
|
||||||
|
A docs plugin with `routeBasePath: '/'` can coexist with `src/pages/index.tsx`, but no doc may also claim `/`.
|
||||||
|
|
||||||
|
```md
|
||||||
|
---
|
||||||
|
id: intro
|
||||||
|
title: Documentation overview
|
||||||
|
slug: /overview
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
After moving the intro, re-check Markdown links. A link such as `./methodology` from `/overview/` resolves under `/overview/`; use `../methodology`, a Docusaurus doc link, or an explicit base-path URL.
|
||||||
|
|
||||||
|
Keep footer/navbar links explicit:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
{label: 'Home', to: '/'},
|
||||||
|
{label: 'Overview', to: '/overview'},
|
||||||
|
```
|
||||||
|
|
||||||
|
## Local search without hosted services
|
||||||
|
|
||||||
|
Install at the root of a root-only pnpm workspace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm add -w @easyops-cn/docusaurus-search-local
|
||||||
|
```
|
||||||
|
|
||||||
|
Configure it as a **theme**, not a plugin:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
themes: [
|
||||||
|
[
|
||||||
|
require.resolve('@easyops-cn/docusaurus-search-local'),
|
||||||
|
{
|
||||||
|
hashed: true,
|
||||||
|
indexDocs: true,
|
||||||
|
indexBlog: false,
|
||||||
|
indexPages: true,
|
||||||
|
docsRouteBasePath: ['/', 'clients'],
|
||||||
|
language: ['en'],
|
||||||
|
highlightSearchTermsOnTargetPage: true,
|
||||||
|
explicitSearchResultPath: true,
|
||||||
|
searchResultLimits: 12,
|
||||||
|
searchResultContextMaxLength: 120,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
`docsRouteBasePath` must cover every docs plugin whose content should be searchable. Do not add Algolia credentials, crawler configuration, Ask AI, or another network service when the requirement is browser-local search.
|
||||||
|
|
||||||
|
### One SearchBar only
|
||||||
|
|
||||||
|
The theme supplies the navbar `@theme/SearchBar`. Avoid importing and rendering another `SearchBar` on the homepage: duplicate instances can contend for the singleton autocomplete/index lifecycle and leave the UI with input text but no fetched index or result overlay.
|
||||||
|
|
||||||
|
Use a landing-page trigger instead:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
const openSearch = () => {
|
||||||
|
const input = document.querySelector<HTMLInputElement>('.navbar__search-input');
|
||||||
|
if (input) {
|
||||||
|
input.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.dispatchEvent(
|
||||||
|
new KeyboardEvent('keydown', {key: 'k', ctrlKey: true, bubbles: true}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
<button type="button" onClick={openSearch}>
|
||||||
|
Search documentation <kbd>Ctrl/⌘ + K</kbd>
|
||||||
|
</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep the navbar field itself as the sole search implementation.
|
||||||
|
|
||||||
|
## Generated homepage metrics
|
||||||
|
|
||||||
|
Do not hard-code changing registry statistics into JSX. Extend the canonical content generator to emit an ignored TypeScript module:
|
||||||
|
|
||||||
|
```python
|
||||||
|
summary = ROOT / "src" / "generated" / "registrySummary.ts"
|
||||||
|
summary.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
summary.write_text(
|
||||||
|
"export const registrySummary = "
|
||||||
|
+ json.dumps({"total": total, "researched": researched}, separators=(",", ":"))
|
||||||
|
+ " as const;\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `/src/generated` to `.gitignore`, run generation before both `tsc` and `docusaurus build`, and import from `@site/src/generated/registrySummary`.
|
||||||
|
|
||||||
|
## Government-portal visual adaptation
|
||||||
|
|
||||||
|
When adapting an official public-sector site, copy the visual grammar rather than cloning its page:
|
||||||
|
|
||||||
|
- derive a small token set from the real site (navy, action blue, pale neutral background, white surfaces, restrained borders);
|
||||||
|
- use a locally installed font package to avoid runtime font dependencies;
|
||||||
|
- reuse only appropriate official marks with clear provenance;
|
||||||
|
- reproduce hierarchy: narrow institutional strip, white utility navigation, large editorial hero, evidence/status blocks, and dark institutional footer;
|
||||||
|
- preserve the user's established geometry preference (for Lego, globally force `border-radius: 0` even if the source site uses pills);
|
||||||
|
- provide dark-mode equivalents without replacing the light-first official character.
|
||||||
|
|
||||||
|
For the VSSA pattern, the useful observed tokens were approximately navy `#091a5a`, action blue `#0b66e4`, pale background `#f3f6f9`, and Public Sans. The official coat-of-arms SVG was suitable as a local navbar asset.
|
||||||
|
|
||||||
|
## Public page unchanged: distinguish an untriggered workflow from a failed workflow
|
||||||
|
|
||||||
|
When a user cannot see locally completed changes, do not begin with runner debugging. Establish the publication chain in order:
|
||||||
|
|
||||||
|
1. `git status --short --branch` — are the changes still uncommitted?
|
||||||
|
2. `git rev-list --left-right --count HEAD...origin/main` — were they pushed?
|
||||||
|
3. Read the newest Actions run and compare its `head_sha` to the intended commit.
|
||||||
|
4. If the newest run still points to the previous SHA, Actions did not fail; the new workflow was never triggered. Commit and push.
|
||||||
|
5. Wait for the run attached to the intended SHA to reach `completed success`.
|
||||||
|
6. Fetch the public page with `Cache-Control: no-cache` and a query marker such as `?verify=<short-sha>`; assert a distinctive new heading or asset, not only HTTP 200.
|
||||||
|
7. Verify the public `search-index.json` contains representative searchable terms, then run a real browser query and open a result.
|
||||||
|
|
||||||
|
This sequence prevents two common false diagnoses: treating a successful local build as a deployment, and treating an old successful Actions run as evidence that a new change was published.
|
||||||
|
|
||||||
|
## Verification gate
|
||||||
|
|
||||||
|
Run all of these before declaring completion:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm validate
|
||||||
|
pnpm build
|
||||||
|
test -s build/search-index.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Then programmatically inspect the index for representative terms from:
|
||||||
|
|
||||||
|
1. the main docs plugin;
|
||||||
|
2. every secondary docs plugin;
|
||||||
|
3. the custom homepage when `indexPages: true`.
|
||||||
|
|
||||||
|
Finally, serve the production build and verify in a browser:
|
||||||
|
|
||||||
|
- desktop and mobile homepage layout;
|
||||||
|
- navbar search and keyboard shortcut;
|
||||||
|
- a query produces visible results;
|
||||||
|
- opening a result reaches the expected dossier route;
|
||||||
|
- square geometry and responsive navigation remain intact.
|
||||||
|
|
||||||
|
Publication is complete only after commit/push, a successful Gitea Actions run, and HTTP/content verification of the public homepage, overview, representative result route, and search index.
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
# Deployment
|
||||||
|
|
||||||
|
Getting your docs live. Pick the platform that fits your workflow.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Decision
|
||||||
|
|
||||||
|
| Platform | Best For | Effort |
|
||||||
|
|----------|----------|--------|
|
||||||
|
| **Vercel** | Teams already using Vercel | Lowest |
|
||||||
|
| **Netlify** | Need redirects, forms, functions | Low |
|
||||||
|
| **GitHub Pages** | Open source, simple hosting | Low |
|
||||||
|
| **Cloudflare Pages** | Global performance, free tier | Low |
|
||||||
|
|
||||||
|
**My recommendation:** Vercel or Netlify for most projects. GitHub Pages if you want everything in one repo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Vercel
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
1. Push to GitHub
|
||||||
|
2. Import at vercel.com
|
||||||
|
3. Done—it auto-detects Docusaurus
|
||||||
|
|
||||||
|
### Config (vercel.json, usually not needed)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"buildCommand": "npm run build",
|
||||||
|
"outputDirectory": "build"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Vercel handles this automatically. Only add if you need overrides.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Netlify
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
1. Push to GitHub
|
||||||
|
2. Connect at app.netlify.com
|
||||||
|
3. Build command: `npm run build`
|
||||||
|
4. Publish directory: `build`
|
||||||
|
|
||||||
|
### Config (netlify.toml)
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[build]
|
||||||
|
command = "npm run build"
|
||||||
|
publish = "build"
|
||||||
|
|
||||||
|
[[redirects]]
|
||||||
|
from = "/old-path"
|
||||||
|
to = "/new-path"
|
||||||
|
status = 301
|
||||||
|
```
|
||||||
|
|
||||||
|
**Opinion:** Netlify's redirect handling is cleaner than most.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GitHub Pages
|
||||||
|
|
||||||
|
### Config
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// docusaurus.config.ts
|
||||||
|
const config = {
|
||||||
|
url: 'https://username.github.io',
|
||||||
|
baseUrl: '/repo-name/', // or '/' for username.github.io
|
||||||
|
organizationName: 'username',
|
||||||
|
projectName: 'repo-name',
|
||||||
|
trailingSlash: false,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### GitHub Actions (recommended)
|
||||||
|
|
||||||
|
`.github/workflows/deploy.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run build
|
||||||
|
- uses: actions/upload-pages-artifact@v3
|
||||||
|
with:
|
||||||
|
path: build
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/deploy-pages@v4
|
||||||
|
id: deployment
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable Pages in repo settings → Pages → Source: GitHub Actions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cloudflare Pages
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
1. Connect repo at pages.cloudflare.com
|
||||||
|
2. Framework: None (not auto-detected well)
|
||||||
|
3. Build command: `npm run build`
|
||||||
|
4. Output directory: `build`
|
||||||
|
|
||||||
|
### Why Cloudflare
|
||||||
|
|
||||||
|
- Fastest global CDN
|
||||||
|
- Generous free tier
|
||||||
|
- Web analytics included
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pre-Deployment Checklist
|
||||||
|
|
||||||
|
Before pushing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Build locally first
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 2. Test the build
|
||||||
|
npm run serve
|
||||||
|
|
||||||
|
# 3. Check for broken links (build will fail if onBrokenLinks: 'throw')
|
||||||
|
# 4. Verify all images load
|
||||||
|
# 5. Test search works (if Algolia configured)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Custom Domain
|
||||||
|
|
||||||
|
All platforms support custom domains. General steps:
|
||||||
|
|
||||||
|
1. Add domain in platform settings
|
||||||
|
2. Update DNS:
|
||||||
|
- `A` record to platform IP, or
|
||||||
|
- `CNAME` to platform URL
|
||||||
|
3. Wait for SSL certificate (automatic, usually minutes)
|
||||||
|
4. Update `url` in docusaurus.config.ts
|
||||||
|
|
||||||
|
**DNS propagation:** Can take up to 48 hours. Usually much faster.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment-Specific Builds
|
||||||
|
|
||||||
|
For staging vs production:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
url: isProd
|
||||||
|
? 'https://docs.example.com'
|
||||||
|
: 'https://staging-docs.example.com',
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use platform environment variables.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
### Build Fails Locally But Worked Before
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -rf node_modules .docusaurus
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Works Locally, Fails in CI
|
||||||
|
|
||||||
|
- Check Node.js version matches
|
||||||
|
- Check for case-sensitive filename issues (Linux is case-sensitive, macOS isn't)
|
||||||
|
- Check for absolute paths that only exist on your machine
|
||||||
|
|
||||||
|
### "Page Not Found" After Deploy
|
||||||
|
|
||||||
|
- Check `baseUrl` matches your hosting path
|
||||||
|
- Check `trailingSlash` is consistent
|
||||||
|
- Clear CDN cache if platform supports it
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
# Diátaxis Patterns for Docusaurus
|
||||||
|
|
||||||
|
Templates and patterns for each documentation quadrant.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tutorial Template
|
||||||
|
|
||||||
|
**Purpose:** Take someone from zero to "I built something!"
|
||||||
|
|
||||||
|
**Filename:** `getting-started/your-first-[thing].md`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
title: "Build Your First [Thing]"
|
||||||
|
description: "Create a working [thing] in 10 minutes"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Build Your First [Thing]
|
||||||
|
|
||||||
|
By the end of this tutorial, you'll have a working [thing] that [does X].
|
||||||
|
|
||||||
|
## What You'll Build
|
||||||
|
|
||||||
|
[Screenshot or diagram of the end result]
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [Specific version] of [tool] installed
|
||||||
|
- Basic familiarity with [concept] (see [link] if new)
|
||||||
|
|
||||||
|
## Step 1: [Action verb] the [thing]
|
||||||
|
|
||||||
|
[One action, one visible result]
|
||||||
|
|
||||||
|
You should see:
|
||||||
|
```
|
||||||
|
[Expected output]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: [Next action]
|
||||||
|
|
||||||
|
[Continue pattern...]
|
||||||
|
|
||||||
|
## What You've Built
|
||||||
|
|
||||||
|
You now have a [thing] that [capability].
|
||||||
|
|
||||||
|
**Next steps:**
|
||||||
|
- [Link to how-to guide for customization]
|
||||||
|
- [Link to explanation of how it works]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tutorial Anti-patterns:**
|
||||||
|
- "First, let's understand how X works..." → Link instead
|
||||||
|
- "You can also do Y..." → One path only
|
||||||
|
- "This is simple..." → Never
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How-To Guide Template
|
||||||
|
|
||||||
|
**Purpose:** Help someone accomplish a specific task.
|
||||||
|
|
||||||
|
**Filename:** `guides/how-to-[verb]-[thing].md`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
title: "How to [Verb] [Thing]"
|
||||||
|
description: "[Outcome] in [context]"
|
||||||
|
---
|
||||||
|
|
||||||
|
# How to [Verb] [Thing]
|
||||||
|
|
||||||
|
[One sentence: what this accomplishes]
|
||||||
|
|
||||||
|
## What You'll Need
|
||||||
|
|
||||||
|
- [Prerequisite 1]
|
||||||
|
- [Prerequisite 2]
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
### 1. [Action]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
command here
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. [Action]
|
||||||
|
|
||||||
|
[Instructions...]
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
[How to confirm it worked]
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**[Symptom]:** [Quick fix or link to explanation]
|
||||||
|
```
|
||||||
|
|
||||||
|
**How-to Anti-patterns:**
|
||||||
|
- Teaching concepts (that's a tutorial)
|
||||||
|
- Listing all options (that's reference)
|
||||||
|
- Explaining why (that's explanation)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reference Template
|
||||||
|
|
||||||
|
**Purpose:** Describe what something IS, not how to use it.
|
||||||
|
|
||||||
|
**Filename:** `reference/[category]/[item].md`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
title: "[Component/API/Config] Reference"
|
||||||
|
description: "Complete reference for [thing]"
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Thing] Reference
|
||||||
|
|
||||||
|
[One sentence: what this is]
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Default | Description |
|
||||||
|
|----------|------|---------|-------------|
|
||||||
|
| `name` | `string` | required | Brief description |
|
||||||
|
| `enabled` | `boolean` | `true` | Brief description |
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Minimal
|
||||||
|
{ name: "example" }
|
||||||
|
|
||||||
|
// Full
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
enabled: true,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Link to how-to guide using this]
|
||||||
|
- [Link to explanation of design]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Reference Anti-patterns:**
|
||||||
|
- "To use this, first..." → That's a how-to
|
||||||
|
- "This is useful when..." → That's explanation
|
||||||
|
- Incomplete tables (every prop documented)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Explanation Template
|
||||||
|
|
||||||
|
**Purpose:** Help someone understand WHY.
|
||||||
|
|
||||||
|
**Filename:** `concepts/[topic].md`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
title: "Understanding [Concept]"
|
||||||
|
description: "Why [thing] works the way it does"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Understanding [Concept]
|
||||||
|
|
||||||
|
[Hook: why this matters to the reader]
|
||||||
|
|
||||||
|
## The Problem
|
||||||
|
|
||||||
|
[What challenge does this solve?]
|
||||||
|
|
||||||
|
## How [Product] Approaches This
|
||||||
|
|
||||||
|
[Your design decision and reasoning]
|
||||||
|
|
||||||
|
## Trade-offs
|
||||||
|
|
||||||
|
| Approach | Pros | Cons |
|
||||||
|
|----------|------|------|
|
||||||
|
| [Option A] | ... | ... |
|
||||||
|
| [Option B] | ... | ... |
|
||||||
|
|
||||||
|
**We chose [X] because [reasoning].**
|
||||||
|
|
||||||
|
## When to Choose Differently
|
||||||
|
|
||||||
|
[Acknowledge alternatives have merit in certain contexts]
|
||||||
|
|
||||||
|
## Further Reading
|
||||||
|
|
||||||
|
- [External resource]
|
||||||
|
- [Related concept in these docs]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Explanation Anti-patterns:**
|
||||||
|
- Step-by-step instructions (how-to)
|
||||||
|
- Property tables (reference)
|
||||||
|
- "Let's build..." (tutorial)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deciding Which Type
|
||||||
|
|
||||||
|
Ask yourself:
|
||||||
|
|
||||||
|
```
|
||||||
|
Is the reader trying to LEARN?
|
||||||
|
├── Yes → Tutorial
|
||||||
|
└── No
|
||||||
|
└── Are they trying to DO something specific?
|
||||||
|
├── Yes → How-to Guide
|
||||||
|
└── No
|
||||||
|
└── Are they trying to LOOK UP information?
|
||||||
|
├── Yes → Reference
|
||||||
|
└── No → Explanation
|
||||||
|
```
|
||||||
|
|
||||||
|
**If you're unsure:** Write it as a how-to first. They're the most commonly needed and easiest to split later.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Connecting the Quadrants
|
||||||
|
|
||||||
|
Good docs link between types:
|
||||||
|
|
||||||
|
- **Tutorial** → "To learn more about why this works, see [Explanation]"
|
||||||
|
- **Tutorial** → "For the full API, see [Reference]"
|
||||||
|
- **How-to** → "Prerequisites: complete [Tutorial] first"
|
||||||
|
- **How-to** → "For all options, see [Reference]"
|
||||||
|
- **Reference** → "For a walkthrough, see [How-to]"
|
||||||
|
- **Explanation** → "To try this yourself, see [Tutorial]"
|
||||||
|
|
||||||
|
Never dead-end a reader. Always point them forward.
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
# Docusaurus Config Template — Modern Dark Theme
|
||||||
|
|
||||||
|
Full `docusaurus.config.ts` for a modern dark-first docs site with:
|
||||||
|
- draw.io plugin (`docusaurus-plugin-drawio`)
|
||||||
|
- Mermaid C4 diagrams (`@docusaurus/theme-mermaid`)
|
||||||
|
- Dark mode default
|
||||||
|
- Prism oneDark theme
|
||||||
|
- Announcement bar
|
||||||
|
- Sidebar TOC config
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import {themes as prismThemes} from 'prism-react-renderer';
|
||||||
|
import type {Config} from '@docusaurus/types';
|
||||||
|
import type * as Preset from '@docusaurus/preset-classic';
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
title: 'My Project',
|
||||||
|
tagline: 'Documentation tagline',
|
||||||
|
favicon: 'img/favicon.ico',
|
||||||
|
|
||||||
|
url: 'https://docs.example.com',
|
||||||
|
baseUrl: '/',
|
||||||
|
|
||||||
|
organizationName: 'org-name',
|
||||||
|
projectName: 'documentation',
|
||||||
|
|
||||||
|
onBrokenLinks: 'throw',
|
||||||
|
markdown: {
|
||||||
|
hooks: {
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
|
},
|
||||||
|
mermaid: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
i18n: { defaultLocale: 'en', locales: ['en'] },
|
||||||
|
|
||||||
|
themes: ['@docusaurus/theme-mermaid'],
|
||||||
|
|
||||||
|
plugins: ['docusaurus-plugin-drawio'],
|
||||||
|
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'classic',
|
||||||
|
{
|
||||||
|
docs: {
|
||||||
|
sidebarPath: './sidebars.ts',
|
||||||
|
editUrl: 'https://gitea.lego-cloud.eu/ORG/REPO/src/branch/main/',
|
||||||
|
showLastUpdateTime: true,
|
||||||
|
showLastUpdateAuthor: true,
|
||||||
|
},
|
||||||
|
blog: {
|
||||||
|
showReadingTime: true,
|
||||||
|
blogTitle: 'Changelog',
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
customCss: './src/css/custom.css',
|
||||||
|
},
|
||||||
|
} satisfies Preset.Options,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
themeConfig: {
|
||||||
|
colorMode: {
|
||||||
|
defaultMode: 'dark',
|
||||||
|
disableSwitch: false,
|
||||||
|
respectPrefersColorScheme: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
announcementBar: {
|
||||||
|
id: 'wip',
|
||||||
|
content: '🚧 Under active development.',
|
||||||
|
backgroundColor: '#1a1a2e',
|
||||||
|
textColor: '#818cf8',
|
||||||
|
isCloseable: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
navbar: {
|
||||||
|
title: 'My Project',
|
||||||
|
hideOnScroll: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'docSidebar',
|
||||||
|
sidebarId: 'docsSidebar',
|
||||||
|
position: 'left',
|
||||||
|
label: 'Docs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: 'https://gitea.lego-cloud.eu/ORG',
|
||||||
|
position: 'right',
|
||||||
|
label: 'Gitea ↗',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
footer: {
|
||||||
|
style: 'dark',
|
||||||
|
copyright: `© ${new Date().getFullYear()} My Project · Built by Jarvis`,
|
||||||
|
},
|
||||||
|
|
||||||
|
prism: {
|
||||||
|
theme: prismThemes.oneDark,
|
||||||
|
darkTheme: prismThemes.oneDark,
|
||||||
|
additionalLanguages: ['bash', 'python', 'sql', 'yaml', 'json', 'typescript', 'docker'],
|
||||||
|
},
|
||||||
|
|
||||||
|
mermaid: {
|
||||||
|
theme: { light: 'neutral', dark: 'dark' },
|
||||||
|
},
|
||||||
|
|
||||||
|
tableOfContents: {
|
||||||
|
minHeadingLevel: 2,
|
||||||
|
maxHeadingLevel: 4,
|
||||||
|
},
|
||||||
|
} satisfies Preset.ThemeConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required packages
|
||||||
|
```bash
|
||||||
|
npx pnpm add @docusaurus/theme-mermaid docusaurus-plugin-drawio raw-loader
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
- `onBrokenMarkdownLinks` moved to `markdown.hooks.onBrokenMarkdownLinks` in Docusaurus v3 — using `siteConfig.onBrokenMarkdownLinks` triggers a deprecation warning
|
||||||
|
- Remove blog from navbar if you have no blog posts — broken link on build
|
||||||
|
- `to: 'https://...'` does NOT work for external links in navbar; use `href:` instead
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
# Draw.io Source Diagrams in Docusaurus
|
||||||
|
|
||||||
|
Use this workflow when a Docusaurus v3 site must render an editable Draw.io source file directly. The integration is based on [`xiguaxigua/docusaurus-plugin-drawio`](https://github.com/xiguaxigua/docusaurus-plugin-drawio) and its upstream README.
|
||||||
|
|
||||||
|
## Source-of-truth rule
|
||||||
|
|
||||||
|
- Commit the editable `.drawio` XML in the documentation repository.
|
||||||
|
- Render that source directly in MDX; do not make a PNG/SVG export the only authoritative artifact.
|
||||||
|
- Keep the source adjacent to, or in a clearly named diagram directory near, the page that consumes it.
|
||||||
|
- Use the global `drawio-main` skill to author and validate diagram content.
|
||||||
|
- Use this `documentation-docusaurus` skill to onboard and render the source in Docusaurus.
|
||||||
|
|
||||||
|
Recommended layout:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/
|
||||||
|
└── architecture/
|
||||||
|
├── context.mdx
|
||||||
|
└── diagrams/
|
||||||
|
└── system-context.drawio
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
This platform uses pnpm. Add the plugin and the raw loader required by the upstream import syntax:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm add docusaurus-plugin-drawio
|
||||||
|
pnpm add -D raw-loader
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit `package.json` and `pnpm-lock.yaml`. Do not substitute npm or create a second lockfile.
|
||||||
|
|
||||||
|
## Register the plugin
|
||||||
|
|
||||||
|
Add the plugin to `docusaurus.config.ts` or `docusaurus.config.js`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export default {
|
||||||
|
// ...
|
||||||
|
plugins: [
|
||||||
|
['drawio', {}],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Preserve existing plugins. Do not replace the whole array merely to add Draw.io.
|
||||||
|
|
||||||
|
The plugin uses this viewer script by default:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://cdn.jsdelivr.net/npm/docusaurus-plugin-drawio/viewer.min.js
|
||||||
|
```
|
||||||
|
|
||||||
|
For production environments that require controlled or offline assets, host `viewer.min.js` from an approved trusted location and configure it explicitly:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
plugins: [
|
||||||
|
['drawio', {lib: '/drawio/viewer.min.js'}],
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify the configured path is published and reachable. Do not point production documentation at an unapproved HTTP origin.
|
||||||
|
|
||||||
|
## Consume a `.drawio` source file from MDX
|
||||||
|
|
||||||
|
The page must use `.mdx`, because it imports and renders a React component:
|
||||||
|
|
||||||
|
```mdx
|
||||||
|
---
|
||||||
|
title: "Architecture context"
|
||||||
|
description: "System context and external relationships."
|
||||||
|
---
|
||||||
|
|
||||||
|
import Drawio from '@theme/Drawio';
|
||||||
|
import systemContext from '!!raw-loader!./diagrams/system-context.drawio';
|
||||||
|
|
||||||
|
<Drawio
|
||||||
|
content={systemContext}
|
||||||
|
title="System context"
|
||||||
|
toolbar="zoom layers lightbox"
|
||||||
|
responsive
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `!!raw-loader!` prefix loads the Draw.io XML as text. The `Drawio` theme component passes that XML to the viewer.
|
||||||
|
|
||||||
|
For a multi-page source file, select a page by zero-based/numeric page index as supported upstream:
|
||||||
|
|
||||||
|
```mdx
|
||||||
|
<Drawio content={architecture} page={1} />
|
||||||
|
```
|
||||||
|
|
||||||
|
Or select a stable page ID when the source uses one:
|
||||||
|
|
||||||
|
```mdx
|
||||||
|
<Drawio content={architecture} pageId="containers" />
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefer one source file per durable diagram unless a multi-page source has a clear ownership reason. If using multiple pages, document page names/IDs and verify every consumed page after edits.
|
||||||
|
|
||||||
|
## Supported display controls
|
||||||
|
|
||||||
|
The upstream plugin exposes Draw.io viewer properties including `page`, `pageId`, `toolbar`, `zoom`, `maxHeight`, `title`, `responsive`, `layers`, `lightbox`, and navigation/resize controls. Use only properties verified by the site build and browser rendering. Do not copy undocumented combinations without testing.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
Run the repository's prescribed checks, at minimum:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
|
Then serve or preview the built site and verify in a browser:
|
||||||
|
|
||||||
|
1. the page loads without console errors;
|
||||||
|
2. the diagram is visible and readable;
|
||||||
|
3. zoom/lightbox controls work when enabled;
|
||||||
|
4. the selected page/pageId is correct;
|
||||||
|
5. diagram labels and relationships match the `.drawio` source;
|
||||||
|
6. the browser can load `viewer.min.js` from the configured location;
|
||||||
|
7. no mixed-content, CSP, missing-module, or raw-loader error appears.
|
||||||
|
|
||||||
|
Also verify that every MDX import resolves to an existing committed `.drawio` file. A successful XML edit without a successful Docusaurus build and browser render is incomplete.
|
||||||
|
|
||||||
|
## Common failures
|
||||||
|
|
||||||
|
- **`Module not found: raw-loader`** — add `raw-loader` as a development dependency and refresh the pnpm lockfile.
|
||||||
|
- **MDX parse/import failure** — use `.mdx`, keep imports after frontmatter, and check the relative source path.
|
||||||
|
- **Blank viewer** — inspect browser console/network output, validate the Draw.io XML, and verify `viewer.min.js` is reachable.
|
||||||
|
- **Wrong page shown** — verify `page` or `pageId` against the current source; page ordering can drift.
|
||||||
|
- **Works in development but not production** — check production base URL, CSP, static asset path, and viewer CDN/network policy.
|
||||||
|
- **Stale exported image** — remove the export from the authority path or regenerate it; the `.drawio` source remains authoritative.
|
||||||
|
|
||||||
|
## Evidence to record
|
||||||
|
|
||||||
|
Record exact paths for:
|
||||||
|
|
||||||
|
- `.drawio` source;
|
||||||
|
- consuming `.mdx` page;
|
||||||
|
- Docusaurus configuration;
|
||||||
|
- `package.json` and `pnpm-lock.yaml` changes;
|
||||||
|
- build result;
|
||||||
|
- browser/console verification;
|
||||||
|
- branch and commit SHA.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Generated evidence registries in Docusaurus
|
||||||
|
|
||||||
|
Use this pattern when a canonical Markdown corpus must also expose a cross-client registry such as systems, products, suppliers, or services.
|
||||||
|
|
||||||
|
## Preserve the canonical layer
|
||||||
|
|
||||||
|
- Keep client/source Markdown and its validator authoritative.
|
||||||
|
- Generate ignored MDX copies and registry pages at build time.
|
||||||
|
- Never edit generated pages to improve evidence; improve canonical table rows and regenerate.
|
||||||
|
- Emit a machine-readable registry JSON alongside human-readable pages.
|
||||||
|
|
||||||
|
## Conservative identity model
|
||||||
|
|
||||||
|
1. Parse only the canonical, schema-defined table under the expected heading.
|
||||||
|
2. Preserve every source observation with client ID, client route, relationship, meaning/context, contractor, evidence, and confidence.
|
||||||
|
3. Normalize case, whitespace, dashes, and Markdown decoration for matching only; preserve observed labels for display.
|
||||||
|
4. Maintain explicit reviewed alias rules in durable source data. Merge only unmistakable product/shared-service identities.
|
||||||
|
5. Keep composite labels separate unless evidence proves their components and relationships.
|
||||||
|
6. Scope generic labels such as “official website”, “institution portal”, or “virtual exhibition” to the client; identical wording across clients does not prove one shared system.
|
||||||
|
7. Use stable hash-derived registry IDs/slugs so later insertions do not renumber established records.
|
||||||
|
|
||||||
|
## Docusaurus integration
|
||||||
|
|
||||||
|
- Use a separate `@docusaurus/plugin-content-docs` instance with its own `id`, generated path, route base, and sidebar file.
|
||||||
|
- Include every docs-plugin route base in local-search configuration.
|
||||||
|
- A flat sidebar can be produced by generating all docs into one directory and using `{type: 'autogenerated', dirName: '.'}`; range directories create folder categories.
|
||||||
|
- Keep existing client slugs explicit when changing generated-directory layout.
|
||||||
|
- Validate source rows, unique records, generated routes, observation totals, representative pages, JSON output, and search-index terms after the real production build.
|
||||||
|
|
||||||
|
## Deployment verification behind authentication
|
||||||
|
|
||||||
|
A successful Actions run is not sufficient. First prove `HEAD == origin/main` and that the run's `head_sha` matches. Then verify deployment.
|
||||||
|
|
||||||
|
For an authentication-protected Pages ingress:
|
||||||
|
|
||||||
|
- unauthenticated public `302` to the configured OAuth start path is expected, not a publish failure;
|
||||||
|
- do not weaken or bypass authentication for users;
|
||||||
|
- verify content through an authorized browser session or the internal static-site service/backend when operational access permits it;
|
||||||
|
- check the homepage, one canonical dossier, registry index, one generated registry dossier, machine-readable JSON, and search index;
|
||||||
|
- distinguish ingress/authentication changes from static publication failures before rolling back application code.
|
||||||
@@ -0,0 +1,625 @@
|
|||||||
|
# Gitea and Docusaurus Delivery
|
||||||
|
|
||||||
|
|
||||||
|
Set up Gitea organizations, repositories, and Docusaurus documentation sites with pnpm and Gitea Actions CI/CD pipelines.
|
||||||
|
|
||||||
|
## Trigger Conditions
|
||||||
|
|
||||||
|
- User asks to create a Gitea organization or repo
|
||||||
|
- User wants a documentation site (Docusaurus)
|
||||||
|
- User needs CI/CD for docs on Gitea
|
||||||
|
- Any project scaffolding on gitea.lego-cloud.eu
|
||||||
|
- User asks about Gitea Pages / static hosting for docs
|
||||||
|
- User wants to convert an established Markdown, evidence, registry, or research repository into Docusaurus without losing canonical paths or provenance
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Gitea API token from Bitwarden Secrets Manager as `HL_V1_GITEA_ACCESS_TOKEN`, injected into the Hermes gateway environment at startup
|
||||||
|
- Never use `GITHUB_TOKEN` for `gitea.lego-cloud.eu`; if the Gitea variable is absent, restart the gateway rather than copying it into `/opt/data/.env`
|
||||||
|
- SSH key configured for `ssh://git@gitea.lego-cloud.eu:30009/`
|
||||||
|
- Node.js 18+ available
|
||||||
|
- pnpm available (via `npx pnpm` if not globally installed)
|
||||||
|
|
||||||
|
## 1. Gitea API Operations
|
||||||
|
|
||||||
|
### Create Organization
|
||||||
|
|
||||||
|
```bash
|
||||||
|
: "${HL_V1_GITEA_ACCESS_TOKEN:?restart the Hermes gateway to load the Bitwarden Gitea token}"
|
||||||
|
curl -s -X POST "https://gitea.lego-cloud.eu/api/v1/orgs" \
|
||||||
|
-H "Authorization: token $HL_V1_GITEA_ACCESS_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"username": "org-name",
|
||||||
|
"full_name": "Display Name",
|
||||||
|
"description": "Description",
|
||||||
|
"visibility": "public"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Repository in Organization
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST "https://gitea.lego-cloud.eu/api/v1/orgs/ORG_NAME/repos" \
|
||||||
|
-H "Authorization: token $HL_V1_GITEA_ACCESS_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"name": "repo-name",
|
||||||
|
"description": "Description",
|
||||||
|
"private": false,
|
||||||
|
"auto_init": true,
|
||||||
|
"default_branch": "main"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Clone via SSH
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone ssh://git@gitea.lego-cloud.eu:30009/ORG/REPO.git
|
||||||
|
cd REPO
|
||||||
|
git config user.email "jarvis.at.skic@gmail.com"
|
||||||
|
git config user.name "Jarvis"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Docusaurus Setup with pnpm
|
||||||
|
|
||||||
|
### Pitfalls
|
||||||
|
|
||||||
|
1. **pnpm not globally available**: Use `npx pnpm` as wrapper — corepack may fail with EACCES on permission-restricted systems.
|
||||||
|
2. **`create docusaurus` fails on non-empty dirs**: The `.git` folder counts. Create in a temp dir first, then copy files into the git repo.
|
||||||
|
3. **Interactive prompt blocks**: Always use `--typescript` flag to skip the language selection prompt.
|
||||||
|
4. **`pnpm install` fails first time**: Run `npx pnpm approve-builds @swc/core core-js` to approve post-install scripts, then retry the build.
|
||||||
|
5. **Broken link on build — `/blog`**: Docusaurus throws on build if the navbar links to `/blog` but no blog posts exist. Remove the blog nav item until posts exist.
|
||||||
|
6. **External links in navbar**: Use `href:` not `to:` for external URLs (GitHub, Gitea). `to:` triggers internal routing and breaks.
|
||||||
|
7. **`onBrokenMarkdownLinks` deprecation**: In Docusaurus v3, move this option to `markdown.hooks.onBrokenMarkdownLinks` — the top-level `siteConfig` version shows a deprecation warning.
|
||||||
|
8. **draw.io files must be co-located**: `.drawio` files imported via `!!raw-loader!` must be in the same `docs/` subtree as the `.mdx` importing them. Placing them in `/static/` and importing by path does NOT work.
|
||||||
|
9. **C4 Mermaid diagrams need the theme package**: `C4Context` / `C4Container` / `C4Component` require `@docusaurus/theme-mermaid` to be installed AND listed in `themes:` in config AND `markdown.mermaid: true`.
|
||||||
|
10. **Root-only pnpm workspace still needs `packages`**: If `pnpm-workspace.yaml` exists, include `packages: ['.']` (YAML list form is preferred). Otherwise pnpm 9 fails in CI with `packages field missing or empty` before Docusaurus builds.
|
||||||
|
11. **Runner-owned build means runner-owned verification**: When the user requires Gitea Actions to install/build, push the minimal change and inspect the actual Actions run rather than installing dependencies locally. Retrieve the failed job log, fix the observed step, and re-trigger until the runner and public URL both verify.
|
||||||
|
12. **Do not rewrite a canonical corpus to fit Docusaurus**: For established research/evidence repositories, keep canonical paths and validators intact. Generate ignored MDX-safe copies, add explicit slugs for stable numeric routes, publish byte-identical raw artifacts with checksums, and validate route/artifact counts after the real build. Follow `references/migrating-existing-markdown-corpora.md`.
|
||||||
|
13. **A custom homepage conflicts with a docs page at `/`**: Before adding `src/pages/index.tsx`, move the docs intro from `slug: /` to a stable route such as `slug: /overview`. Update relative Markdown links from the new route and keep the docs sidebar pointed at the same document ID.
|
||||||
|
14. **Local search should have one theme-owned search instance**: Use `@easyops-cn/docusaurus-search-local` as a Docusaurus theme and let the navbar render `@theme/SearchBar`. Do not render a second `SearchBar` on the landing page; multiple instances can interfere with lazy index loading and result overlays. A landing-page search control should focus the navbar input or dispatch the configured keyboard shortcut instead.
|
||||||
|
15. **Adding packages at a root-only pnpm workspace requires workspace intent**: use `pnpm add -w PACKAGE` when `pnpm-workspace.yaml` declares `packages: ['.']`; otherwise pnpm rejects the add as an accidental workspace-root mutation.
|
||||||
|
16. **An unchanged public page may mean Actions never triggered—not that Actions failed**: first compare local `git status`, `HEAD`, `origin/main`, and the newest Actions run `head_sha`. If the implementation is still uncommitted or unpushed and the latest run points to the previous commit, commit/push before debugging the runner. After success, verify a distinctive new content marker with a cache-busting query rather than relying on a possibly cached browser tab.
|
||||||
|
17. **Authentication can change the public verification result without breaking publication**: if Pages starts returning `302` to an OAuth start path, check another hosted site and the ingress/auth rollout before blaming the new Docusaurus build. Treat the configured auth redirect as expected, then verify content through an authorized browser session or the internal Pages backend. Do not change authentication to make a health check pass.
|
||||||
|
18. **Verify a protected Pages deployment through the Kubernetes service proxy when appropriate**: from an authorized MicroK8s control-plane node, `microk8s kubectl get --raw '/api/v1/namespaces/NAMESPACE/services/http:SERVICE:PORT/proxy/ORG/REPO/ROUTE/?verify=COMMIT'` exercises the deployed service without depending on host-side cluster DNS or embedding a raw ClusterIP. Derive `ROUTE` from the built output or generated registry slug—not from an entity ID, system ID, or guessed short path—and preserve the route's trailing slash when the static-site build emits directory indexes. A Kubernetes `NotFound` on a guessed route is a route-discovery failure, not evidence that publication failed. Check the response body for distinctive markers from changed pages, not merely byte count or HTTP success. This is an operator-side deployment check, not a way to bypass authorization for users.
|
||||||
|
19. **Exact text deduplication is unsafe for cross-client registries**: generic labels such as “official website” or “institution portal” can describe different resources. Scope generic labels to the client, preserve every source observation, and merge only reviewed aliases for unmistakably identical named systems. Follow `references/generated-evidence-registries.md`.
|
||||||
|
20. **Large evidence portals should be organized around user tasks, not docs-plugin internals**: make entity browsing, derived-record exploration, and evidence search the primary discovery paths; keep methodology and provenance globally accessible but secondary. Link global navigation to registry/index routes rather than arbitrary first dossiers, distinguish entity/system/observation counts, and generate changing overview content from canonical data. Follow `references/research-led-evidence-registry-ia.md`.
|
||||||
|
21. **Changing only `navbar.title` leaves inconsistent page titles**: update the top-level Docusaurus `title` as well as the visible navbar label, then verify `document.title` on the homepage and a docs route.
|
||||||
|
22. **Multiple top-level user tasks should not share one giant sidebar**: use a dedicated docs-plugin instance and sidebar for each major area. Keep one instance as the default plugin (omit `id`) because global search/404 rendering may call default docs hooks; disabling the default docs plugin without replacing it can pass compilation but fail static rendering. Follow `references/multi-section-evidence-portals.md`.
|
||||||
|
23. **Evidence tables need both readable presentation and source indexing**: for dense cross-entity relationships, generate reusable React evidence cards while preserving canonical observations and claim-level links. Add a de-duplicated References index to every generated entity and derived-record page, and document hash-derived IDs as internal rather than public identifiers. Follow `references/multi-section-evidence-portals.md`.
|
||||||
|
24. **A dedicated section can still have an unusably flat sidebar**: for large entity sections, make the first level exactly **Overview** plus an expandable **Registry** category; place every ordered entity dossier under Registry. Do not leave hundreds of dossiers beside Overview at the root. When constructing sidebar document IDs from generated filenames, verify Docusaurus's numeric-prefix behavior: it may strip a leading position prefix, but preserve the filename ID when the remaining basename itself begins with a digit. Prefer explicit frontmatter `id` values for new generators; otherwise derive IDs deliberately and prove the first numeric-leading and ordinary records in a production build. Follow `references/multi-section-evidence-portals.md`.
|
||||||
|
25. **Source observations should navigate to their generated derived dossiers**: when client/entity tables feed a generated system or supplier registry, enrich only the generated source-page copies with a dedicated dossier-link column. Display the internal derived ID as the anchor and link with the registry's canonical slug. Reuse the canonical identity result through a stable observation map such as `(source_file, source_line)`; do not recompute identity or match by label in the page generator. Fail generation unless every observation is linked, then verify link count, target validity, and unique-target coverage. Follow `references/cross-linking-source-and-derived-dossiers.md`.
|
||||||
|
26. **Reference shortlists must preserve novelty across turns**: track every previously suggested site. When the user asks for additional or “new” options, retain only explicitly selected references and replace all other entries; do not repeat rejected or already-presented choices.
|
||||||
|
27. **Inspect the exact selected reference URL before implementation**: similarly named documentation and wiki domains can have materially different entry pages. Extract hierarchy and visual principles from the user-selected URL, then create an original adaptation rather than relying on memory or cloning branded content.
|
||||||
|
28. **A reference site does not override explicit shape constraints**: if the chosen design uses rounded cards but the user requires sharp rectangles, preserve the layout and information hierarchy while setting all radius tokens to zero and, when the rule is global, applying `*, *::before, *::after { border-radius: 0 !important; }`. Verify the compiled production CSS contains the rule. See `references/reference-inspired-documentation-portals.md`.
|
||||||
|
|
||||||
|
29. **A green pipeline is not feature acceptance**: before implementation, map each interactive requirement to a direct browser-level proof. A build, successful Actions run, deployed marker, visible toggle, sidebar container, or search input proves infrastructure/presence—not behavior. For theme switching, verify `data-theme`, persistence, and computed colors; for dedicated sidebars, open every top-menu section and verify only its sidebar items; for local search, type a query, wait for loading to finish, confirm a visible result, and open it. Follow `references/behavioral-acceptance-for-doc-portals.md`.
|
||||||
|
30. **Local-search query hashing can conflict with static hosting redirects**: `hashed: true` fetches `search-index.json?_=HASH`. If the static host redirects asset requests with query strings, search can remain indefinitely loading despite a valid index. Use `hashed: 'filename'`, verify the emitted `search-index-HASH.json`, and exercise a real browser query under the configured `baseUrl`. Follow `references/behavioral-acceptance-for-doc-portals.md`.
|
||||||
|
31. **A named category is not a dedicated sidebar**: when top-menu sections require their own sidebars, use separate docs-plugin instances with unique IDs, paths, route bases, sidebar files, and navbar `docSidebar` items carrying both `docsPluginId` and `sidebarId`. Keep a default docs instance for overview/global compatibility, then inspect every rendered section sidebar. Follow `references/behavioral-acceptance-for-doc-portals.md`.
|
||||||
|
32. **Renaming a Pages-backed Gitea repository is also a deployment-path migration**: the publication workflow derives its destination from `${{ gitea.repository }}`, while Docusaurus `baseUrl`, `projectName`, edit links, search routes, and hard-coded portal links may still carry the old slug. Inventory and update those values, preserve repository history with the Gitea rename API, update the Git remote, and push a commit after the rename so Actions republishes to `/gondor-v1-gitea-pages/NEW_ORG/NEW_REPO`. Verify the exact post-rename `head_sha` has a successful Actions run and read back distinctive content from the renamed repository. If the browser reaches the configured OAuth boundary, treat that as route/auth evidence—not content evidence—and use an authorized session or internal backend for marker verification. Decide explicitly whether the old Pages directory should redirect, remain temporarily, or be removed; a repository rename does not clean the old static directory automatically.
|
||||||
|
|
||||||
|
### Dedicated homepage and native local search
|
||||||
|
|
||||||
|
For documentation sites that need a showcase-style product landing page rather than docs content at `/`:
|
||||||
|
|
||||||
|
1. Move the docs intro to `/overview` (or another explicit route).
|
||||||
|
2. Build the homepage in `src/pages/index.tsx` with `<Layout>`, semantic sections, and CSS Modules.
|
||||||
|
3. Keep changing registry counts generated from canonical data rather than hard-coding them in JSX.
|
||||||
|
4. Add `@easyops-cn/docusaurus-search-local` under `themes`, set `indexPages: true`, and include every docs plugin route in `docsRouteBasePath` (for example `['/', 'clients']`). Do not configure Ask AI or any external search provider when the requirement is native/offline search.
|
||||||
|
5. Verify the production build emits `build/search-index.json`, confirm representative terms from each docs plugin are present, then exercise a real browser query and open a result.
|
||||||
|
6. Treat push, successful Gitea Actions, and public Pages checks as part of completion; a local build alone is not a published result.
|
||||||
|
|
||||||
|
See `references/dedicated-homepage-local-search.md` for a known-good route, search, generated-summary, styling, and verification pattern.
|
||||||
|
|
||||||
|
### Setup Steps
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Step 1: Create in a temp directory (avoids "directory not empty" error)
|
||||||
|
npx pnpm create docusaurus@latest /tmp/docs-temp classic --package-manager pnpm --typescript
|
||||||
|
|
||||||
|
# Step 2: Install deps in temp dir
|
||||||
|
cd /tmp/docs-temp && npx pnpm install
|
||||||
|
|
||||||
|
# Step 3: Approve build scripts (required for @swc/core and core-js)
|
||||||
|
npx pnpm approve-builds @swc/core core-js
|
||||||
|
|
||||||
|
# Step 4: Copy into git repo (preserving .git)
|
||||||
|
cp -r /tmp/docs-temp/* /path/to/git-repo/
|
||||||
|
cp /tmp/docs-temp/.gitignore /path/to/git-repo/
|
||||||
|
|
||||||
|
# Step 5: Verify build
|
||||||
|
cd /path/to/git-repo && npx pnpm build
|
||||||
|
|
||||||
|
# Step 6: Cleanup
|
||||||
|
rm -rf /tmp/docs-temp
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-Application Documentation Structure
|
||||||
|
|
||||||
|
For a docs site covering multiple autonomous applications:
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── intro/ # Landing page, app portfolio
|
||||||
|
├── applications/
|
||||||
|
│ └── app-name/
|
||||||
|
│ ├── index.md # Overview + constraints
|
||||||
|
│ ├── overview/
|
||||||
|
│ │ └── architecture.md # System design, tech stack
|
||||||
|
│ ├── module-1/
|
||||||
|
│ │ └── index.md # Module docs
|
||||||
|
│ └── module-n/
|
||||||
|
│ └── index.md
|
||||||
|
├── architecture/ # Platform-wide infra
|
||||||
|
│ └── overview.md
|
||||||
|
└── guides/
|
||||||
|
└── getting-started.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Gitea Actions CI/CD
|
||||||
|
|
||||||
|
Workflow file goes in `.gitea/workflows/build.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Build Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
- name: Cache pnpm
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: $(pnpm store path --silent)
|
||||||
|
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
- run: pnpm build
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
with:
|
||||||
|
name: docusaurus-build
|
||||||
|
path: build/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: Gitea Actions uses the same syntax as GitHub Actions. Most `actions/*` work unchanged. Requires a Gitea runner registered for the org.
|
||||||
|
|
||||||
|
## 4. Key Gitea API Differences from GitHub
|
||||||
|
|
||||||
|
| Feature | GitHub | Gitea |
|
||||||
|
|---|---|---|
|
||||||
|
| Base URL | `api.github.com` | `gitea.lego-cloud.eu/api/v1` |
|
||||||
|
| Auth header | `Authorization: token X` | Same |
|
||||||
|
| Create org | `POST /orgs` | Same |
|
||||||
|
| Create org repo | `POST /orgs/O/repos` | Same |
|
||||||
|
| SSH clone | `git@github.com:O/R.git` | `ssh://git@gitea.lego-cloud.eu:30009/O/R.git` |
|
||||||
|
| Actions dir | `.github/workflows/` | `.gitea/workflows/` |
|
||||||
|
| Runner | GitHub-hosted | Self-hosted (must register) |
|
||||||
|
|
||||||
|
## 5. LEGO Cloud Pages Publishing
|
||||||
|
|
||||||
|
Although Gitea core has no built-in Pages feature, LEGO Cloud provides a confirmed external Pages platform backed by the TrueNAS-hosted Actions runner and Gondor Nginx/NFS.
|
||||||
|
|
||||||
|
### Docusaurus path configuration
|
||||||
|
|
||||||
|
For repository `ORG/REPO`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
url: 'https://pages.apps.lego-cloud.eu',
|
||||||
|
baseUrl: '/ORG/REPO/',
|
||||||
|
organizationName: 'ORG',
|
||||||
|
projectName: 'REPO',
|
||||||
|
```
|
||||||
|
|
||||||
|
The live URL is `https://pages.apps.lego-cloud.eu/ORG/REPO/`. Verify both the page and at least one generated `/ORG/REPO/assets/...` URL return HTTP 200.
|
||||||
|
|
||||||
|
### Confirmed runner publication workflow
|
||||||
|
|
||||||
|
The runner exposes the persistent Pages dataset inside each job container at `/gondor-v1-gitea-pages`. Publish Docusaurus `build/` contents to the repository-derived directory:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Publish to Pages
|
||||||
|
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
ORG_REPO='${{ gitea.repository }}'
|
||||||
|
case "$ORG_REPO" in */*) ;; *) exit 1 ;; esac
|
||||||
|
case "$ORG_REPO" in *..*|/*|*/|*//* ) exit 1 ;; esac
|
||||||
|
test -f build/index.html
|
||||||
|
pages_root="/gondor-v1-gitea-pages"
|
||||||
|
mountpoint -q "$pages_root" || {
|
||||||
|
echo "$pages_root is not a persistent host mount inside the Gitea Actions job container" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
destination="$pages_root/$ORG_REPO"
|
||||||
|
rm -rf -- "$destination"
|
||||||
|
mkdir -p "$destination"
|
||||||
|
cp -a build/. "$destination/"
|
||||||
|
```
|
||||||
|
|
||||||
|
Use dependency-free `cp`, because the standard runner image may not include `rsync`. The `mountpoint` guard prevents a false-success write into an ephemeral job filesystem.
|
||||||
|
|
||||||
|
If `pnpm-workspace.yaml` exists for a root-only Docusaurus project, it must contain:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
packages:
|
||||||
|
- '.'
|
||||||
|
```
|
||||||
|
|
||||||
|
Without that field, pnpm 9 fails with `packages field missing or empty`.
|
||||||
|
|
||||||
|
The runner-side bind and authorization are documented in the `gitea-repository-operations` skill under `references/truenas-actions-pages.md`.
|
||||||
|
|
||||||
|
## 6. Gitea Core Pages Status
|
||||||
|
|
||||||
|
**Gitea does NOT have a built-in Pages feature** (as of v1.26.2). The GitHub issue (#23521) was a discussion that closed — actual implementation lives in a **separate standalone project** (`gitea/pages-server`) that must be deployed alongside Gitea. It is NOT baked into Gitea like GitHub Pages is baked into GitHub.
|
||||||
|
|
||||||
|
When the user asks "does Gitea have Pages?", the accurate answer is:
|
||||||
|
- No native equivalent in Gitea core
|
||||||
|
- `gitea/pages-server` exists but requires a separate deployment
|
||||||
|
- Recommended self-hosted alternative: **MinIO for static file hosting** (S3-compatible, bucket with public policy + `mc cp` in CI) or a thin Nginx/Caddy container
|
||||||
|
- MinIO caveat: no clean-URL routing natively → add a reverse proxy or use `trailingSlash: true` in Docusaurus config
|
||||||
|
|
||||||
|
## 6. Tailwind CSS + shadcn/ui Integration
|
||||||
|
|
||||||
|
**Recommended stack** — endorsed by Docusaurus maintainer team (used on docusaurus.io itself).
|
||||||
|
|
||||||
|
### Install
|
||||||
|
```bash
|
||||||
|
npx pnpm add -D tailwindcss postcss autoprefixer
|
||||||
|
npx pnpm add server-only clsx tailwind-merge @radix-ui/react-slot @docusaurus/theme-common
|
||||||
|
```
|
||||||
|
|
||||||
|
### tailwind.config.js
|
||||||
|
```js
|
||||||
|
module.exports = {
|
||||||
|
content: ['./src/**/*.{js,jsx,ts,tsx,mdx}', './docs/**/*.{md,mdx}'],
|
||||||
|
darkMode: ['class'], // ThemeSynchronizer bridges data-theme → .dark
|
||||||
|
theme: { extend: { borderRadius: { lg:'0px', md:'0px', sm:'0px' } } },
|
||||||
|
corePlugins: { preflight: false }, // MUST — prevents fighting Infima CSS
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### PostCSS plugin in docusaurus.config.ts
|
||||||
|
```ts
|
||||||
|
plugins: [
|
||||||
|
async function tailwindPlugin() {
|
||||||
|
return {
|
||||||
|
name: 'docusaurus-tailwindcss',
|
||||||
|
configurePostCSS(postcssOptions) {
|
||||||
|
postcssOptions.plugins.push(require('tailwindcss'));
|
||||||
|
postcssOptions.plugins.push(require('autoprefixer'));
|
||||||
|
return postcssOptions;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
### ThemeSynchronizer — bridges Docusaurus ↔ shadcn dark mode
|
||||||
|
Create `src/theme/Layout/index.tsx` (swizzle wrap):
|
||||||
|
```tsx
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import Layout from '@theme-original/Layout';
|
||||||
|
import { useColorMode } from '@docusaurus/theme-common';
|
||||||
|
|
||||||
|
function ThemeSynchronizer(): null {
|
||||||
|
const { colorMode } = useColorMode();
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.classList.toggle('dark', colorMode === 'dark');
|
||||||
|
}, [colorMode]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LayoutWrapper(props) {
|
||||||
|
return <Layout {...props}><ThemeSynchronizer />{props.children}</Layout>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### CSS variables for custom.css (add to @layer base)
|
||||||
|
```css
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root { --radius: 0rem; /* sharp corners */ }
|
||||||
|
.dark { --radius: 0rem; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pitfalls
|
||||||
|
- `@docusaurus/theme-common` is a transitive dep under pnpm — add it explicitly: `npx pnpm add @docusaurus/theme-common`
|
||||||
|
- shadcn v2+ requires `server-only` package: `npx pnpm add server-only`
|
||||||
|
- CSS variable namespaces don't conflict: `--background` / `--ifm-*` / `--tw-*` are all separate
|
||||||
|
- Infima can win specificity battles — use `!` prefix (e.g. `!text-red-500`) for overrides
|
||||||
|
|
||||||
|
### Sharp corners (user preference for Lego)
|
||||||
|
Set `--radius: 0rem` in CSS variables AND `border-radius: 0 !important` on all Infima selectors:
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--ifm-global-radius: 0;
|
||||||
|
--ifm-code-border-radius: 0;
|
||||||
|
--ifm-pre-border-radius: 0;
|
||||||
|
--ifm-alert-border-radius: 0;
|
||||||
|
--ifm-button-border-radius: 0;
|
||||||
|
--ifm-badge-border-radius: 0;
|
||||||
|
}
|
||||||
|
.button, code, .prism-code, div[class*='codeBlockContainer'],
|
||||||
|
.admonition, .pagination-nav__link { border-radius: 0 !important; }
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Forced Dark Navbar (both light and dark modes)
|
||||||
|
|
||||||
|
To lock the navbar to dark regardless of color mode:
|
||||||
|
```css
|
||||||
|
.navbar {
|
||||||
|
background-color: #0a0a0a !important;
|
||||||
|
border-bottom: 1px solid #1f1f1f !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
.navbar__title { color: #ededed !important; }
|
||||||
|
.navbar__link { color: #a1a1a1 !important; }
|
||||||
|
.navbar__link:hover, .navbar__link--active { color: #ededed !important; background: transparent !important; }
|
||||||
|
.navbar-sidebar { background-color: #0a0a0a !important; }
|
||||||
|
```
|
||||||
|
The `!important` is required — Infima sets navbar background with high specificity.
|
||||||
|
|
||||||
|
## 8. Professional Platform Navigation Structure
|
||||||
|
|
||||||
|
For a multi-application platform docs site, use this top-level structure (not just a flat "Applications" list):
|
||||||
|
|
||||||
|
```
|
||||||
|
Introduction ← What this platform is, app portfolio table
|
||||||
|
Platform ← Infrastructure, CI/CD, networking, auth
|
||||||
|
Products ← Each application (with C4 + Modules sub-structure)
|
||||||
|
Engineering ← Monorepo structure, tech stack, conventions
|
||||||
|
Runbooks ← Operational procedures, incident response
|
||||||
|
```
|
||||||
|
|
||||||
|
In `sidebars.ts`:
|
||||||
|
```ts
|
||||||
|
const sidebars = {
|
||||||
|
docsSidebar: [
|
||||||
|
{ type: 'doc', id: 'intro/index', label: 'Introduction' },
|
||||||
|
{ type: 'category', label: 'Platform', items: ['platform/overview', 'platform/cicd'] },
|
||||||
|
{ type: 'category', label: 'Products', items: [
|
||||||
|
{ type: 'category', label: 'App Name', link: { type: 'doc', id: 'products/app/index' }, items: [
|
||||||
|
{ type: 'category', label: 'Architecture (C4)', items: ['products/app/c4/context','products/app/c4/containers','products/app/c4/components'] },
|
||||||
|
{ type: 'category', label: 'Modules', items: [...] },
|
||||||
|
]},
|
||||||
|
]},
|
||||||
|
{ type: 'category', label: 'Engineering', items: ['engineering/guidelines'] },
|
||||||
|
{ type: 'category', label: 'Runbooks', collapsed: true, items: ['runbooks/index'] },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. Modern Docusaurus Styling
|
||||||
|
|
||||||
|
For a dark-first, modern technical docs theme (Vercel/Linear aesthetic):
|
||||||
|
|
||||||
|
### Font
|
||||||
|
```css
|
||||||
|
@import url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-sans/style.css');
|
||||||
|
@import url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-mono/style.css');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--ifm-font-family-base: 'Geist', 'Inter', system-ui, sans-serif;
|
||||||
|
--ifm-font-family-monospace: 'Geist Mono', 'Fira Code', monospace;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dark mode first in config
|
||||||
|
```ts
|
||||||
|
themeConfig: {
|
||||||
|
colorMode: {
|
||||||
|
defaultMode: 'dark',
|
||||||
|
disableSwitch: false,
|
||||||
|
respectPrefersColorScheme: true,
|
||||||
|
},
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key --ifm- tokens for dark theme
|
||||||
|
```css
|
||||||
|
[data-theme='dark'] {
|
||||||
|
--ifm-background-color: #0a0a0a;
|
||||||
|
--ifm-background-surface-color: #111111;
|
||||||
|
--ifm-navbar-background-color: rgba(10,10,10,0.85);
|
||||||
|
--ifm-color-primary: #818cf8; /* indigo-400 */
|
||||||
|
--ifm-color-content: #ededed;
|
||||||
|
--ifm-color-content-secondary: #a1a1a1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See `references/modern-docusaurus-theme.md` for the full CSS file.
|
||||||
|
|
||||||
|
## 7. draw.io Plugin Integration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx pnpm add docusaurus-plugin-drawio raw-loader
|
||||||
|
```
|
||||||
|
|
||||||
|
Config (`docusaurus.config.ts`):
|
||||||
|
```ts
|
||||||
|
plugins: ['docusaurus-plugin-drawio'],
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage in `.mdx` files — must use **raw-loader** to import the `.drawio` file:
|
||||||
|
```mdx
|
||||||
|
import Drawio from '@theme/Drawio';
|
||||||
|
import myDiagram from '!!raw-loader!./diagrams/my-diagram.drawio';
|
||||||
|
|
||||||
|
<Drawio content={myDiagram} />
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pitfall**: `.drawio` files must be co-located with the `.mdx` file or in a relative path — they cannot be imported from `/static/`. Place them in e.g. `docs/architecture/diagrams/`.
|
||||||
|
|
||||||
|
## 8. C4 Architecture with Mermaid
|
||||||
|
|
||||||
|
Install Mermaid theme:
|
||||||
|
```bash
|
||||||
|
npx pnpm add @docusaurus/theme-mermaid
|
||||||
|
```
|
||||||
|
|
||||||
|
Config:
|
||||||
|
```ts
|
||||||
|
themes: ['@docusaurus/theme-mermaid'],
|
||||||
|
markdown: { mermaid: true },
|
||||||
|
themeConfig: {
|
||||||
|
mermaid: { theme: { light: 'neutral', dark: 'dark' } },
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
Mermaid supports C4 diagrams natively: `C4Context`, `C4Container`, `C4Component`. Use these for the C4 architecture levels. See `references/c4-docusaurus.md` for example syntax per level.
|
||||||
|
|
||||||
|
## 9. Multi-Application Docs Structure with C4
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── intro/index.md
|
||||||
|
├── applications/
|
||||||
|
│ └── app-name/
|
||||||
|
│ ├── index.md
|
||||||
|
│ ├── c4/
|
||||||
|
│ │ ├── context.md # L1 — system in the world
|
||||||
|
│ │ ├── containers.md # L2 — deployable units
|
||||||
|
│ │ └── components.md # L3 — internal structure
|
||||||
|
│ └── modules/ # Detailed module docs
|
||||||
|
├── architecture/
|
||||||
|
│ ├── overview.md
|
||||||
|
│ └── diagrams/ # .drawio files live here
|
||||||
|
└── guides/
|
||||||
|
└── getting-started.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Repository Collaborators and Admin-Level Access
|
||||||
|
|
||||||
|
Gitea repository permissions are `read`, `write`, and `admin`. When a user asks to make someone an "owner of this repository," use repository-level `admin` permission unless they explicitly ask for ownership of the entire organization.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Confirm both the user and repository exist first
|
||||||
|
curl -fsS -H "Authorization: token $HL_V1_GITEA_ACCESS_TOKEN" \
|
||||||
|
"https://gitea.lego-cloud.eu/api/v1/users/USERNAME"
|
||||||
|
|
||||||
|
curl -fsS -H "Authorization: token $HL_V1_GITEA_ACCESS_TOKEN" \
|
||||||
|
"https://gitea.lego-cloud.eu/api/v1/repos/ORG/REPO"
|
||||||
|
|
||||||
|
# Add or update the collaborator with repository-admin permission
|
||||||
|
curl -fsS -X PUT \
|
||||||
|
-H "Authorization: token $HL_V1_GITEA_ACCESS_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"https://gitea.lego-cloud.eu/api/v1/repos/ORG/REPO/collaborators/USERNAME" \
|
||||||
|
-d '{"permission":"admin"}'
|
||||||
|
|
||||||
|
# Verify the effective permission; do not rely only on the PUT returning 204
|
||||||
|
curl -fsS -H "Authorization: token $HL_V1_GITEA_ACCESS_TOKEN" \
|
||||||
|
"https://gitea.lego-cloud.eu/api/v1/repos/ORG/REPO/collaborators/USERNAME/permission"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected add/update response is HTTP `204`; the verification response must report `permission: "admin"` and the requested username.
|
||||||
|
|
||||||
|
## Verify a Private Repository Push
|
||||||
|
|
||||||
|
After pushing, verify both Git refs and the remote artifact:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git fetch origin
|
||||||
|
test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"
|
||||||
|
```
|
||||||
|
|
||||||
|
A private repository's unauthenticated `/raw/branch/...` web URL may return `404`. That does not prove the file is absent. Verify through the authenticated Gitea API instead:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsS \
|
||||||
|
-H "Authorization: token $HL_V1_GITEA_ACCESS_TOKEN" \
|
||||||
|
"https://gitea.lego-cloud.eu/api/v1/repos/ORG/REPO/raw/path/to/file.md?ref=main"
|
||||||
|
```
|
||||||
|
|
||||||
|
Read back a distinctive heading or marker from the response, not only the HTTP status. Before pushing into an actively maintained repository, run `git fetch origin` and inspect divergence. If the local branch is merely behind and remote changes do not overlap local edits, a fast-forward merge can preserve the working tree:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git merge --ff-only origin/main
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise commit/stash safely, then rebase or merge normally; never overwrite concurrent remote work.
|
||||||
|
|
||||||
|
## Automated Git Push from Scripts / Cron
|
||||||
|
|
||||||
|
When pushing to Gitea from a non-interactive context (cron job, shell script), use the SSH key directly via `GIT_SSH_COMMAND`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GIT_SSH_COMMAND="ssh -i /opt/data/home/.ssh/id_ed25519 -o StrictHostKeyChecking=no -o BatchMode=yes" \
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
Always include `-o BatchMode=yes` — without it, SSH may hang waiting for interactive input when host key verification fails.
|
||||||
|
|
||||||
|
**No-op pattern** (safe to run every sync, only commits when something changed):
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
echo "No changes."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
CHANGED=$(git diff --cached --name-only | wc -l)
|
||||||
|
git commit -m "sync $(date -u '+%Y-%m-%d %H:%M UTC') — $CHANGED file(s)"
|
||||||
|
GIT_SSH_COMMAND="ssh -i /opt/data/home/.ssh/id_ed25519 -o StrictHostKeyChecking=no -o BatchMode=yes" git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pitfalls (Continued)
|
||||||
|
|
||||||
|
10. **"This account is prohibited from signing in"**: The Gitea user tied to the API token has been marked *Prohibit Login* by an admin. Both API calls (`/api/v1/user`) and SSH pushes fail with this message. Fix: Gitea admin panel → Site Administration → User Accounts → find the user → uncheck "Prohibit Login" → Save. URL: `https://gitea.lego-cloud.eu/-/admin/users`. The API token itself remains valid once the account is re-enabled — no need to regenerate it.
|
||||||
|
|
||||||
|
11. **SSH key "Key check failed"**: If SSH push gives `error: Key check failed`, the SSH public key is either not registered on the Gitea account or the account is prohibited (see pitfall #10). Check: Gitea → User Settings → SSH/GPG Keys. The key at `/opt/data/home/.ssh/id_ed25519.pub` must appear there.
|
||||||
|
|
||||||
|
## Research and Address-Book Repositories
|
||||||
|
|
||||||
|
When the requested Gitea project maps a broad ecosystem, global infrastructure, organizations, or other changing domain, do not default to a Docusaurus application scaffold. Establish a source-backed research repository first:
|
||||||
|
|
||||||
|
1. Start with authoritative coordinating bodies, registries, and maintained public directories rather than claiming exhaustive participant coverage.
|
||||||
|
2. Separate human-readable `address-book/`, canonical `data/`, methodology/findings in `docs/`, unverified work in `research/`, and provenance policy in `sources/`.
|
||||||
|
3. Use a controlled functional taxonomy; avoid imposing a false hierarchy on decentralized ecosystems.
|
||||||
|
4. Include a dependency-free validator plus `make validate` and `make summary` targets.
|
||||||
|
5. Document source verification dates, evidence status, coverage limitations, and public-contact safety.
|
||||||
|
6. Run `git diff --cached --check` before committing, then verify the pushed ref and read back a distinctive remote artifact through the authenticated Gitea API.
|
||||||
|
|
||||||
|
See `references/research-address-book-repositories.md` for the full repository pattern, round-1 CSV schema, research method, and validation checklist.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `curl -s -H "Authorization: token $HL_V1_GITEA_ACCESS_TOKEN" https://gitea.lego-cloud.eu/api/v1/user` — confirms account is active (returns user JSON, not error message)
|
||||||
|
- `curl -s -H "Authorization: token $HL_V1_GITEA_ACCESS_TOKEN" https://gitea.lego-cloud.eu/api/v1/orgs/ORG` — org exists
|
||||||
|
- `npx pnpm build` — docs compile without errors
|
||||||
|
- Push triggers workflow (check Actions tab in Gitea UI)
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- `references/docusaurus-config-template.md` — full docusaurus.config.ts example
|
||||||
|
- `templates/gitea-actions-build.yml` — CI workflow template
|
||||||
|
- `references/modern-docusaurus-theme.md` — dark-first CSS theme (Geist font, indigo accent, forced dark navbar, sharp corners)
|
||||||
|
- `references/c4-docusaurus.md` — C4Context/C4Container/C4Component Mermaid syntax + sidebar structure
|
||||||
|
- `references/go-monorepo-conventions.md` — Go workspace monorepo layout, standard libraries, code conventions for SKIC apps
|
||||||
|
- `references/migrating-existing-markdown-corpora.md` — preserve canonical corpora while generating MDX-safe docs, stable routes, raw artifacts, and end-to-end Pages validation
|
||||||
|
- `references/dedicated-homepage-local-search.md` — separate a showcase landing page from docs routes, generate live homepage metrics, configure fully local search, avoid duplicate SearchBar instances, and verify public deployment
|
||||||
|
- `references/generated-evidence-registries.md` — generate conservative cross-client registries with stable identity, separate docs plugins, flat sidebars, route/count validation, and auth-aware Pages verification
|
||||||
|
- `references/research-led-evidence-registry-ia.md` — organize large entity/evidence portals around user tasks, generate scope and interpretation content from canonical data, and verify the resulting IA and deployment
|
||||||
|
- `references/reference-inspired-documentation-portals.md` — adapt a selected Docusaurus reference into an original portal, preserve explicit visual constraints, manage non-repeating shortlists, and verify dedicated homepage routes plus compiled sharp-corner rules
|
||||||
|
- `references/behavioral-acceptance-for-doc-portals.md` — requirement-to-proof gates for real theme switching, independent section sidebars, browser-local search, static-host query redirect handling, and deployment verification
|
||||||
|
- `references/multi-section-evidence-portals.md` — split major areas into independent sidebars, preserve a default docs instance, generate reusable evidence cards, document internal IDs, add per-record References indexes, and verify exact coverage
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# Go Monorepo Conventions — SKIC Playground
|
||||||
|
|
||||||
|
## Directory Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
skic-v1-playground/
|
||||||
|
├── documentation/ # Docusaurus site
|
||||||
|
├── stock-market-pro/ # Go service
|
||||||
|
│ ├── cmd/server/main.go
|
||||||
|
│ ├── internal/ # App-private packages
|
||||||
|
│ │ ├── ingestor/
|
||||||
|
│ │ ├── analysis/
|
||||||
|
│ │ ├── signals/
|
||||||
|
│ │ └── notifier/
|
||||||
|
│ ├── pkg/ # Public packages (if any)
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ ├── Makefile
|
||||||
|
│ └── go.mod
|
||||||
|
├── shared/ # Cross-app Go libraries
|
||||||
|
│ ├── discord/
|
||||||
|
│ ├── config/
|
||||||
|
│ └── telemetry/
|
||||||
|
├── infra/
|
||||||
|
│ └── docker-compose.yml
|
||||||
|
├── go.work # Go workspace — ties all modules together
|
||||||
|
└── Makefile # Root targets: build all, test all
|
||||||
|
```
|
||||||
|
|
||||||
|
## go.work File
|
||||||
|
|
||||||
|
```
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
use (
|
||||||
|
./stock-market-pro
|
||||||
|
./shared/discord
|
||||||
|
./shared/config
|
||||||
|
./shared/telemetry
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Local modules reference each other without `replace` directives.
|
||||||
|
|
||||||
|
## Standard Go Libraries (SKIC stack)
|
||||||
|
|
||||||
|
| Package | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `net/http` | HTTP server/client |
|
||||||
|
| `encoding/json` | JSON |
|
||||||
|
| `database/sql` + `modernc.org/sqlite` | SQLite (dev) |
|
||||||
|
| `github.com/lib/pq` | PostgreSQL/TimescaleDB (prod) |
|
||||||
|
| `github.com/rs/zerolog` | Structured logging |
|
||||||
|
| `github.com/spf13/viper` | Config from env vars |
|
||||||
|
| `github.com/robfig/cron/v3` | Scheduled jobs |
|
||||||
|
| `golang.org/x/sync` | Concurrency utilities |
|
||||||
|
|
||||||
|
## Code Conventions
|
||||||
|
|
||||||
|
- **Error wrapping**: `fmt.Errorf("context: %w", err)` — never swallow errors
|
||||||
|
- **Logging**: zerolog structured JSON, no `fmt.Println` in prod
|
||||||
|
- **Tests**: table-driven, `testify/assert`, race detector on CI (`-race`)
|
||||||
|
- **Config**: env vars via Viper, validated at startup with explicit error
|
||||||
|
|
||||||
|
## Python as Sidecar
|
||||||
|
|
||||||
|
Python (pandas-ta, ta-lib, scikit-learn) runs as a **sidecar HTTP/gRPC service** when the Go ecosystem is thin for data/ML. The Go service calls localhost endpoints.
|
||||||
|
|
||||||
|
## Makefile Targets (per app)
|
||||||
|
|
||||||
|
```makefile
|
||||||
|
build: go build -o bin/server ./cmd/server
|
||||||
|
test: go test ./... -race -cover
|
||||||
|
lint: golangci-lint run
|
||||||
|
docker: docker build -t $(APP_NAME):$(VERSION) .
|
||||||
|
run: source .env && go run ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding a New App
|
||||||
|
|
||||||
|
1. `mkdir skic-v1-playground/<app-name>/`
|
||||||
|
2. `go mod init gitea.lego-cloud.eu/skic-v1-playground/<app-name>`
|
||||||
|
3. Add `use ./<app-name>` to `go.work`
|
||||||
|
4. Copy `.gitea/workflows/build.yml` from template
|
||||||
|
5. Create multi-stage Dockerfile: `golang:1.22-alpine` → `alpine:3.19`
|
||||||
|
6. Add docs under `documentation/docs/products/<app-name>/` with C4 structure
|
||||||
|
7. Add to `documentation/src/pages/index.tsx` applications array
|
||||||
@@ -0,0 +1,406 @@
|
|||||||
|
# Local YAML Data Database
|
||||||
|
|
||||||
|
Use this pattern when structured documentation data must live in the documentation repository, remain reviewable through Git, and drive React components and MDX pages without duplicating entity content inside MDX.
|
||||||
|
|
||||||
|
## Contract
|
||||||
|
|
||||||
|
The source database is a local `data/` tree. It is not a runtime service and it is not a collection of generated MDX pages.
|
||||||
|
|
||||||
|
- Every database source file uses the `.yml` extension.
|
||||||
|
- Keep one record per file.
|
||||||
|
- Keep one `index.yml` in every leaf collection.
|
||||||
|
- Requirements live only under dedicated requirement folders.
|
||||||
|
- Architectural decisions live under the exact folder `data/architecture-decisions/`.
|
||||||
|
- Stable IDs, not titles or paths, define relationships.
|
||||||
|
- Load and validate data during the Docusaurus build-time phase.
|
||||||
|
- React components consume typed build data; they do not read the filesystem in the browser.
|
||||||
|
- MDX composes components and explanatory prose; it does not duplicate structured record content.
|
||||||
|
|
||||||
|
A JSON module created by Docusaurus inside `.docusaurus/` is an allowed generated build artifact. It is not a maintained database source and must not be committed as authoritative data.
|
||||||
|
|
||||||
|
## Required structure
|
||||||
|
|
||||||
|
```text
|
||||||
|
data/
|
||||||
|
├── database.yml
|
||||||
|
├── epics/
|
||||||
|
│ ├── index.yml
|
||||||
|
│ └── epic-001.yml
|
||||||
|
├── features/
|
||||||
|
│ ├── index.yml
|
||||||
|
│ └── feature-001.yml
|
||||||
|
├── tasks/
|
||||||
|
│ ├── index.yml
|
||||||
|
│ └── task-001.yml
|
||||||
|
├── requirements/
|
||||||
|
│ ├── functional/
|
||||||
|
│ │ ├── index.yml
|
||||||
|
│ │ └── fr-001.yml
|
||||||
|
│ └── non-functional/
|
||||||
|
│ ├── index.yml
|
||||||
|
│ └── nfr-001.yml
|
||||||
|
├── architecture-decisions/
|
||||||
|
│ ├── index.yml
|
||||||
|
│ └── adr-001.yml
|
||||||
|
└── indexes/
|
||||||
|
├── epic-features.yml
|
||||||
|
├── feature-tasks.yml
|
||||||
|
├── feature-requirements.yml
|
||||||
|
└── feature-architecture-decisions.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not combine functional requirements, non-functional requirements, architectural decisions, or delivery entities into one catch-all collection. Do not create a generic decision folder. The explicit folders are part of the data contract.
|
||||||
|
|
||||||
|
`database.yml` declares the database schema version and enabled collections:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
schema_version: 1
|
||||||
|
collections:
|
||||||
|
- epics
|
||||||
|
- features
|
||||||
|
- tasks
|
||||||
|
- requirements/functional
|
||||||
|
- requirements/non-functional
|
||||||
|
- architecture-decisions
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stable identifiers and filenames
|
||||||
|
|
||||||
|
Use lowercase stable IDs with a type prefix and numeric sequence:
|
||||||
|
|
||||||
|
| Entity | ID example | Filename |
|
||||||
|
|---|---|---|
|
||||||
|
| Epic | `epic-001` | `epic-001.yml` |
|
||||||
|
| Feature | `feature-001` | `feature-001.yml` |
|
||||||
|
| Functional requirement | `fr-001` | `fr-001.yml` |
|
||||||
|
| Non-functional requirement | `nfr-001` | `nfr-001.yml` |
|
||||||
|
| Task | `task-001` | `task-001.yml` |
|
||||||
|
| Architectural decision | `adr-001` | `adr-001.yml` |
|
||||||
|
|
||||||
|
The filename stem must equal the record's `id`. Never reuse an ID, even after archiving a record. Renaming a title must not change its ID.
|
||||||
|
|
||||||
|
## Entity records
|
||||||
|
|
||||||
|
### Epic
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
id: epic-001
|
||||||
|
title: Project lifecycle registry
|
||||||
|
status: in-progress
|
||||||
|
summary: >
|
||||||
|
Provide a traceable registry of project lifecycle information.
|
||||||
|
owners:
|
||||||
|
- root-at-skic
|
||||||
|
tags:
|
||||||
|
- governance
|
||||||
|
- lifecycle
|
||||||
|
```
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
A Feature belongs to one Epic. The Feature owns that relationship through `epic_id`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
id: feature-001
|
||||||
|
title: Display project lifecycle state
|
||||||
|
status: in-progress
|
||||||
|
epic_id: epic-001
|
||||||
|
depends_on_feature_ids:
|
||||||
|
- feature-000
|
||||||
|
roadmap:
|
||||||
|
start: 2026-08-01
|
||||||
|
end: 2026-08-21
|
||||||
|
lane: portal
|
||||||
|
progress: 60
|
||||||
|
summary: >
|
||||||
|
Display the current lifecycle state and supporting evidence.
|
||||||
|
owners:
|
||||||
|
- root-at-skic
|
||||||
|
```
|
||||||
|
|
||||||
|
`depends_on_feature_ids` is the canonical Feature dependency field. `roadmap` is optional unless the documentation exposes a date-based roadmap. When present, validate ISO dates, require `end` not to precede `start`, require a stable lane, and constrain `progress` to 0–100. React Flow adapters may derive dependency edges and Gantt-like positions from these fields; TSX and MDX must not maintain separate schedules or dependency lists.
|
||||||
|
|
||||||
|
### Task
|
||||||
|
|
||||||
|
A Task belongs to one Feature. The Task owns that relationship through `feature_id`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
id: task-001
|
||||||
|
title: Implement the lifecycle state component
|
||||||
|
status: in-backlog
|
||||||
|
feature_id: feature-001
|
||||||
|
summary: >
|
||||||
|
Render lifecycle status from the local data source.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Functional requirement
|
||||||
|
|
||||||
|
Functional requirements remain in `data/requirements/functional/`. A requirement owns its many-to-many Feature mapping through `feature_ids`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
id: fr-001
|
||||||
|
type: functional
|
||||||
|
title: Display lifecycle state
|
||||||
|
status: approved
|
||||||
|
statement: >
|
||||||
|
The portal must display the current lifecycle state for every project.
|
||||||
|
feature_ids:
|
||||||
|
- feature-001
|
||||||
|
- feature-004
|
||||||
|
```
|
||||||
|
|
||||||
|
### Non-functional requirement
|
||||||
|
|
||||||
|
Non-functional requirements remain in `data/requirements/non-functional/` and use the same relationship field.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
id: nfr-001
|
||||||
|
type: non-functional
|
||||||
|
category: performance
|
||||||
|
title: Resolve documentation data during the static build
|
||||||
|
status: approved
|
||||||
|
statement: >
|
||||||
|
Documentation pages must not require a runtime database service.
|
||||||
|
feature_ids:
|
||||||
|
- feature-001
|
||||||
|
- feature-002
|
||||||
|
```
|
||||||
|
|
||||||
|
### Architectural decision
|
||||||
|
|
||||||
|
Architectural decisions remain in `data/architecture-decisions/`. A decision owns its many-to-many Feature mapping through `feature_ids`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
id: adr-001
|
||||||
|
title: Use local YAML records for structured documentation
|
||||||
|
status: accepted
|
||||||
|
date: 2026-08-14
|
||||||
|
context: >
|
||||||
|
Structured entities must be reused across several documentation views.
|
||||||
|
decision: >
|
||||||
|
Store authoritative records in local YAML files and resolve them during
|
||||||
|
the static documentation build.
|
||||||
|
consequences:
|
||||||
|
- Documentation changes remain Git-reviewable.
|
||||||
|
- The deployed site does not require a database service.
|
||||||
|
feature_ids:
|
||||||
|
- feature-001
|
||||||
|
- feature-002
|
||||||
|
```
|
||||||
|
|
||||||
|
## Relationship ownership
|
||||||
|
|
||||||
|
Store each relationship in one canonical direction only:
|
||||||
|
|
||||||
|
| Relationship | Cardinality | Canonical owner |
|
||||||
|
|---|---:|---|
|
||||||
|
| Epic to Feature | 1:n | Feature stores `epic_id` |
|
||||||
|
| Feature to Task | 1:n | Task stores `feature_id` |
|
||||||
|
| Requirement to Feature | n:m | Requirement stores `feature_ids` |
|
||||||
|
| Architectural decision to Feature | n:m | Architectural decision stores `feature_ids` |
|
||||||
|
|
||||||
|
Do not also store `feature_ids` on Epics, `task_ids` on Features, or reverse requirement and decision IDs on Features. Generate reverse lookups in `data/indexes/`. This prevents two editable fields from disagreeing about the same relationship.
|
||||||
|
|
||||||
|
## Collection indexes
|
||||||
|
|
||||||
|
Each leaf collection has an `index.yml` containing stable IDs and relative record paths. Sort entries by ID.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
schema_version: 1
|
||||||
|
items:
|
||||||
|
- id: feature-001
|
||||||
|
file: feature-001.yml
|
||||||
|
- id: feature-002
|
||||||
|
file: feature-002.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Collection indexes are generated and committed. They make reviews explicit and allow tools to discover records without relying on filesystem ordering.
|
||||||
|
|
||||||
|
## Relationship indexes
|
||||||
|
|
||||||
|
Relationship indexes are derived from canonical record fields. For example, `data/indexes/feature-requirements.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
schema_version: 1
|
||||||
|
features:
|
||||||
|
feature-001:
|
||||||
|
functional:
|
||||||
|
- fr-001
|
||||||
|
non_functional:
|
||||||
|
- nfr-001
|
||||||
|
feature-002:
|
||||||
|
functional: []
|
||||||
|
non_functional:
|
||||||
|
- nfr-001
|
||||||
|
```
|
||||||
|
|
||||||
|
And `data/indexes/feature-architecture-decisions.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
schema_version: 1
|
||||||
|
features:
|
||||||
|
feature-001:
|
||||||
|
architecture_decisions:
|
||||||
|
- adr-001
|
||||||
|
feature-002:
|
||||||
|
architecture_decisions:
|
||||||
|
- adr-001
|
||||||
|
```
|
||||||
|
|
||||||
|
Never edit derived relationship indexes manually. Regenerate them from source records.
|
||||||
|
|
||||||
|
## Maintenance commands
|
||||||
|
|
||||||
|
Every adopting documentation repository must expose these commands:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm data:index
|
||||||
|
pnpm data:validate
|
||||||
|
pnpm data:check
|
||||||
|
```
|
||||||
|
|
||||||
|
- `pnpm data:index` deterministically rebuilds collection and relationship indexes.
|
||||||
|
- `pnpm data:validate` validates schemas, IDs, files, and relationship integrity.
|
||||||
|
- `pnpm data:check` regenerates indexes in memory or a temporary directory and fails when committed indexes are stale.
|
||||||
|
|
||||||
|
Index generation must not add timestamps or machine-specific paths. Running it twice against unchanged records must produce byte-identical output.
|
||||||
|
|
||||||
|
## Validation requirements
|
||||||
|
|
||||||
|
Validation must fail for:
|
||||||
|
|
||||||
|
- any source database file whose extension is not `.yml`;
|
||||||
|
- a missing collection `index.yml`;
|
||||||
|
- a duplicate record ID;
|
||||||
|
- a filename stem that differs from the record ID;
|
||||||
|
- an unknown entity type, status, or requirement category;
|
||||||
|
- a required field with an empty value;
|
||||||
|
- a dangling `epic_id`, `feature_id`, or entry in `feature_ids`;
|
||||||
|
- a dangling entry in `depends_on_feature_ids`;
|
||||||
|
- an invalid roadmap date, reversed date range, missing lane, or progress outside 0–100;
|
||||||
|
- an index entry whose record is missing;
|
||||||
|
- a record missing from its collection index;
|
||||||
|
- a stale relationship index;
|
||||||
|
- a record placed in the wrong collection;
|
||||||
|
- an architectural decision outside `data/architecture-decisions/`;
|
||||||
|
- a functional or non-functional requirement outside its dedicated folder.
|
||||||
|
|
||||||
|
Run validation before the Docusaurus build and in Gitea Actions. A successful site build is not sufficient if data validation was skipped.
|
||||||
|
|
||||||
|
## Docusaurus build-time loading
|
||||||
|
|
||||||
|
Browsers cannot safely discover repository files at runtime. Implement a local Docusaurus plugin that:
|
||||||
|
|
||||||
|
1. reads `data/database.yml` and all collection indexes;
|
||||||
|
2. parses the referenced `.yml` records;
|
||||||
|
3. validates records and relationships;
|
||||||
|
4. constructs typed entity maps and reverse indexes;
|
||||||
|
5. publishes one immutable client data module with `actions.createData`;
|
||||||
|
6. optionally registers generic routes for entity details and traceability views.
|
||||||
|
|
||||||
|
Keep parsing, validation, and relationship resolution on the Node build side. Do not bundle filesystem APIs or independent YAML parsers into each React component.
|
||||||
|
|
||||||
|
A minimal plugin shape is:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export default function localYamlDataPlugin() {
|
||||||
|
return {
|
||||||
|
name: 'local-yaml-data',
|
||||||
|
|
||||||
|
async loadContent() {
|
||||||
|
return loadAndValidateDatabase('data');
|
||||||
|
},
|
||||||
|
|
||||||
|
async contentLoaded({content, actions}) {
|
||||||
|
await actions.createData(
|
||||||
|
'documentation-database.json',
|
||||||
|
JSON.stringify(content),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Treat the generated module as derived data. Rebuild it from the YAML source on every production build.
|
||||||
|
|
||||||
|
## Typed React access
|
||||||
|
|
||||||
|
Provide one data context and stable hooks rather than letting components know storage paths:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const epic = useEpic('epic-001');
|
||||||
|
const features = useFeaturesForEpic('epic-001');
|
||||||
|
const tasks = useTasksForFeature('feature-001');
|
||||||
|
const requirements = useRequirementsForFeature('feature-001');
|
||||||
|
const decisions = useArchitectureDecisionsForFeature('feature-001');
|
||||||
|
const traceability = useFeatureTraceability('feature-001');
|
||||||
|
```
|
||||||
|
|
||||||
|
Recommended components:
|
||||||
|
|
||||||
|
- `EpicTree`
|
||||||
|
- `EpicDetails`
|
||||||
|
- `FeatureDetails`
|
||||||
|
- `FeatureTasks`
|
||||||
|
- `RequirementDetails`
|
||||||
|
- `RequirementsForFeature`
|
||||||
|
- `ArchitectureDecisionDetails`
|
||||||
|
- `ArchitectureDecisionsForFeature`
|
||||||
|
- `RequirementFeatureMatrix`
|
||||||
|
- `DecisionFeatureMatrix`
|
||||||
|
- `TraceabilityMatrix`
|
||||||
|
|
||||||
|
Components receive stable IDs or filter properties. They must render a clear missing-record state rather than silently returning empty output.
|
||||||
|
|
||||||
|
## MDX composition
|
||||||
|
|
||||||
|
MDX pages provide page purpose, explanation, and component placement. They must not copy record fields that already exist in the database.
|
||||||
|
|
||||||
|
```mdx
|
||||||
|
---
|
||||||
|
title: "Explore delivery scope"
|
||||||
|
description: "Inspect Epics, Features, Tasks, requirements, and decisions."
|
||||||
|
---
|
||||||
|
|
||||||
|
import EpicTree from '@site/src/components/documentation-data/EpicTree';
|
||||||
|
import TraceabilityMatrix from '@site/src/components/documentation-data/TraceabilityMatrix';
|
||||||
|
|
||||||
|
<EpicTree />
|
||||||
|
|
||||||
|
<TraceabilityMatrix />
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a generic `FeatureDetails` component or generated route for individual Features instead of generating and maintaining one content-heavy MDX file per Feature.
|
||||||
|
|
||||||
|
## Change workflow
|
||||||
|
|
||||||
|
For every data change:
|
||||||
|
|
||||||
|
1. Add or edit the authoritative record in its dedicated folder.
|
||||||
|
2. Preserve existing stable IDs.
|
||||||
|
3. Run `pnpm data:index`.
|
||||||
|
4. Review source and derived index changes together.
|
||||||
|
5. Run `pnpm data:validate`.
|
||||||
|
6. Run `pnpm data:check`.
|
||||||
|
7. Run the Docusaurus typecheck and production build.
|
||||||
|
8. Exercise the affected React view in a browser.
|
||||||
|
9. Commit records and indexes together.
|
||||||
|
10. Verify the Gitea Actions run and deployed documentation route.
|
||||||
|
|
||||||
|
For schema evolution, increment `schema_version`, migrate all records and indexes atomically, and keep the loader's error message explicit about unsupported versions.
|
||||||
|
|
||||||
|
## Acceptance checklist
|
||||||
|
|
||||||
|
- [ ] Database source uses only `.yml` files.
|
||||||
|
- [ ] Requirements are separated into functional and non-functional folders.
|
||||||
|
- [ ] Architectural decisions use exactly `data/architecture-decisions/`.
|
||||||
|
- [ ] Every entity has one stable-ID record file.
|
||||||
|
- [ ] Every leaf collection has a deterministic `index.yml`.
|
||||||
|
- [ ] Canonical relationship fields implement Epic → Feature → Task and n:m mappings to requirements and architectural decisions.
|
||||||
|
- [ ] Feature dependencies and optional roadmap scheduling remain in source YAML and pass referential and date-range validation.
|
||||||
|
- [ ] Reverse indexes are generated, committed, and stale-index checked.
|
||||||
|
- [ ] Referential validation rejects duplicate and dangling IDs.
|
||||||
|
- [ ] Docusaurus loads data at build time.
|
||||||
|
- [ ] React components consume typed hooks rather than parsing source files.
|
||||||
|
- [ ] MDX contains composition and explanatory content rather than duplicate records.
|
||||||
|
- [ ] Data checks, typecheck, production build, browser view, Actions, and deployed route are verified.
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# Migrating an Existing Markdown Corpus to Docusaurus
|
||||||
|
|
||||||
|
Use this pattern when an established evidence, registry, research, or documentation repository must become a Docusaurus site without turning generated presentation files into the source of truth.
|
||||||
|
|
||||||
|
## Preserve the canonical corpus
|
||||||
|
|
||||||
|
Keep canonical files in their existing paths when validators, scheduled jobs, links, or provenance depend on them. Do not bulk-move or inject front matter into hundreds of source documents merely to satisfy Docusaurus.
|
||||||
|
|
||||||
|
Recommended split:
|
||||||
|
|
||||||
|
```text
|
||||||
|
clients/ or records/ # canonical Markdown
|
||||||
|
INDEX.md # canonical registry
|
||||||
|
source/ # supplied/source artifacts
|
||||||
|
scripts/validate_registry.py # canonical content validation
|
||||||
|
site-docs/ # authored website guidance
|
||||||
|
.generated-docs/ # ignored, generated MDX-safe copies
|
||||||
|
static/raw/ # ignored, staged byte-identical artifacts
|
||||||
|
scripts/generate_site_content.py
|
||||||
|
scripts/publish_raw_artifacts.py
|
||||||
|
scripts/validate_site_build.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Configure a second `@docusaurus/plugin-content-docs` instance for `.generated-docs/`. Build scripts should regenerate it from canonical sources every time.
|
||||||
|
|
||||||
|
## Strict generated-copy transform
|
||||||
|
|
||||||
|
The generator should:
|
||||||
|
|
||||||
|
1. Parse the canonical index strictly and require the expected IDs, paths, statuses, and order.
|
||||||
|
2. Verify every indexed source exists.
|
||||||
|
3. Recreate the generated tree from scratch.
|
||||||
|
4. Apply only an explicit compatibility allowlist.
|
||||||
|
5. Fail if a new or unexpected transform is needed.
|
||||||
|
6. Generate navigation/index content from the same canonical registry.
|
||||||
|
|
||||||
|
A common MDX hazard is raw `<br>` in Markdown tables. Convert it to `<br />` only in generated copies and assert the exact files/replacement counts. Literal placeholders such as `<ID>` in templates should remain outside the compiled docs tree or be escaped in a generated display copy.
|
||||||
|
|
||||||
|
## Preserve stable numeric routes
|
||||||
|
|
||||||
|
Docusaurus treats numeric filename prefixes as sidebar ordering metadata and can remove them from inferred routes. For canonical file `001-client-name.md`, prepend generated front matter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
slug: /001-client-name
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
This preserves `/clients/001-client-name/` while leaving the canonical file untouched.
|
||||||
|
|
||||||
|
For hundreds of documents, generate category directories (for example `001-050`, `051-100`) with `_category_.json`. Keep the explicit `slug` so grouping changes navigation without changing public routes.
|
||||||
|
|
||||||
|
## Raw artifact publication
|
||||||
|
|
||||||
|
For provenance-sensitive repositories, stage byte-identical canonical files under `static/raw/` before `docusaurus build` and generate a checksum manifest. Validate hashes between canonical files and `build/raw/` after the build.
|
||||||
|
|
||||||
|
If `trailingSlash: true` causes Docusaurus broken-link checking to append `/` to extensionless static paths, publish/link a manifest with an extension such as `SHA256SUMS.txt`, or use the final absolute Pages URL. Do not relax `onBrokenLinks: 'throw'` merely to hide this.
|
||||||
|
|
||||||
|
## Build pipeline
|
||||||
|
|
||||||
|
A robust package script is:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"validate": "python3 scripts/generate_site_content.py && python3 scripts/validate_registry.py && tsc --noEmit",
|
||||||
|
"build": "python3 scripts/generate_site_content.py && python3 scripts/validate_registry.py && python3 scripts/publish_raw_artifacts.py && docusaurus build && python3 scripts/validate_site_build.py"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit `pnpm-lock.yaml`, pin one Docusaurus release across packages, and keep generated directories ignored.
|
||||||
|
|
||||||
|
## Site-build validation
|
||||||
|
|
||||||
|
After the real production build, assert:
|
||||||
|
|
||||||
|
- expected number of rendered document routes;
|
||||||
|
- representative first, middle, compatibility-transformed, longest-path, and last routes;
|
||||||
|
- expected production `baseUrl` in emitted HTML/assets;
|
||||||
|
- generated directory/index record count;
|
||||||
|
- byte equality or SHA-256 equality for every published raw artifact;
|
||||||
|
- homepage, directory, representative deep links, and checksum manifest return HTTP 200 under the exact subpath.
|
||||||
|
|
||||||
|
Also inspect the site visually at the production subpath. For large tables, enforce horizontal scrolling without removing columns. For a square UI, use both Infima radius variables and a final global `border-radius: 0 !important` override.
|
||||||
|
|
||||||
|
## Actions and continuity
|
||||||
|
|
||||||
|
The Gitea workflow must run source validation, project validation, the real build, and build-output validation before publishing. Observe the actual Actions conclusion and then verify the public site; workflow YAML presence is not completion.
|
||||||
|
|
||||||
|
Before conversion, pause scheduled writers. After publication:
|
||||||
|
|
||||||
|
- update their workdir and remote URL;
|
||||||
|
- tell them to edit canonical files only, never generated site output;
|
||||||
|
- resume only after local worktrees and remote synchronization are verified.
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# Modern Docusaurus Dark Theme — CSS Reference
|
||||||
|
|
||||||
|
Dark-first, Vercel/Linear-inspired theme built for SKIC Playground docs.
|
||||||
|
Full source lives at `/opt/data/documentation/src/css/custom.css`.
|
||||||
|
|
||||||
|
## Design Decisions
|
||||||
|
|
||||||
|
- **Font**: Geist (by Vercel) via CDN + Geist Mono for code
|
||||||
|
- **Accent**: Indigo `#6366f1` (light) / `#818cf8` (dark)
|
||||||
|
- **Background**: `#0a0a0a` (near-black, not pure black)
|
||||||
|
- **Surface**: `#111111` for cards, sidebars
|
||||||
|
- **Text**: `#ededed` primary, `#a1a1a1` secondary
|
||||||
|
|
||||||
|
## Key Overrides
|
||||||
|
|
||||||
|
### Dark mode base palette
|
||||||
|
```css
|
||||||
|
[data-theme='dark'] {
|
||||||
|
--ifm-color-primary: #818cf8;
|
||||||
|
--ifm-background-color: #0a0a0a;
|
||||||
|
--ifm-background-surface-color: #111111;
|
||||||
|
--ifm-navbar-background-color: rgba(10, 10, 10, 0.85);
|
||||||
|
--ifm-color-content: #ededed;
|
||||||
|
--ifm-color-content-secondary: #a1a1a1;
|
||||||
|
--ifm-color-emphasis-100: #1a1a1a;
|
||||||
|
--ifm-color-emphasis-200: #222222;
|
||||||
|
--ifm-color-emphasis-300: #333333;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Typography
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--ifm-font-family-base: 'Geist', 'Inter', system-ui, sans-serif;
|
||||||
|
--ifm-font-family-monospace: 'Geist Mono', 'Fira Code', monospace;
|
||||||
|
--ifm-font-size-base: 15px;
|
||||||
|
--ifm-line-height-base: 1.7;
|
||||||
|
--ifm-heading-letter-spacing: -0.02em;
|
||||||
|
--ifm-h1-font-size: 2.25rem;
|
||||||
|
}
|
||||||
|
h2 { border-bottom: 1px solid var(--ifm-color-emphasis-200); }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Glassmorphic Navbar
|
||||||
|
```css
|
||||||
|
.navbar {
|
||||||
|
border-bottom: 1px solid var(--ifm-color-emphasis-200);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
-webkit-backdrop-filter: blur(12px);
|
||||||
|
}
|
||||||
|
[data-theme='dark'] .navbar { background: rgba(10, 10, 10, 0.85); }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Table Styling
|
||||||
|
```css
|
||||||
|
table { border: 1px solid var(--ifm-color-emphasis-200); border-radius: 8px; overflow: hidden; }
|
||||||
|
thead { background: var(--ifm-color-emphasis-100); }
|
||||||
|
th { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; }
|
||||||
|
tbody tr:hover { background: var(--ifm-color-emphasis-100); }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hero Section (homepage)
|
||||||
|
```css
|
||||||
|
.heroTitle {
|
||||||
|
background: linear-gradient(135deg, #ffffff 0%, #a5b4fc 60%, #818cf8 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
font-size: clamp(2.5rem, 5vw, 3.75rem);
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.04em;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### App Cards (dark glass style)
|
||||||
|
```css
|
||||||
|
.appCard {
|
||||||
|
background: #111111;
|
||||||
|
border: 1px solid #222222;
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
.appCard:hover {
|
||||||
|
border-color: #6366f1;
|
||||||
|
background: #161625;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 24px rgba(99,102,241,0.12);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prism Code Theme
|
||||||
|
Use `oneDark` for both light and dark (visually consistent in docs):
|
||||||
|
```ts
|
||||||
|
prism: {
|
||||||
|
theme: prismThemes.oneDark,
|
||||||
|
darkTheme: prismThemes.oneDark,
|
||||||
|
additionalLanguages: ['bash', 'python', 'sql', 'yaml', 'json', 'typescript', 'docker'],
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
## Announcement Bar
|
||||||
|
```ts
|
||||||
|
announcementBar: {
|
||||||
|
id: 'wip',
|
||||||
|
content: '🚧 Under active development',
|
||||||
|
backgroundColor: '#1a1a2e',
|
||||||
|
textColor: '#818cf8',
|
||||||
|
isCloseable: true,
|
||||||
|
},
|
||||||
|
```
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# Multi-section evidence portals in Docusaurus
|
||||||
|
|
||||||
|
Use this pattern when top-level areas such as Overview, Clients, Systems, Methodology, and Data are distinct user tasks and a shared sidebar makes navigation noisy.
|
||||||
|
|
||||||
|
## Dedicated docs instances
|
||||||
|
|
||||||
|
Give each top-level area its own `@docusaurus/plugin-content-docs` instance, content path, route base, and sidebar file. One instance must remain the default plugin (omit `id`) because themes such as local search may call the default docs hooks while rendering global pages, search, or 404 routes. Assign explicit IDs only to the additional instances.
|
||||||
|
|
||||||
|
For each sidebar, use an independently named autogenerated root:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const sidebars = {
|
||||||
|
sectionSidebar: [{type: 'autogenerated', dirName: '.'}],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
When moving generated indexes into their section directories:
|
||||||
|
|
||||||
|
- give each index `slug: /` within its plugin;
|
||||||
|
- update navbar, footer, homepage, and cross-document links;
|
||||||
|
- include every route base in local-search configuration;
|
||||||
|
- remove obsolete shared docs only after the new routes exist;
|
||||||
|
- validate each top-level route by a distinctive content marker after production build.
|
||||||
|
|
||||||
|
## Large registry sidebar hierarchy
|
||||||
|
|
||||||
|
A dedicated sidebar is not sufficient if hundreds of dossiers remain at its root. For large entity sections, use exactly two first-level entries:
|
||||||
|
|
||||||
|
1. **Overview** — the section's registry/index document.
|
||||||
|
2. **Registry** — an expandable category containing every individual dossier in stable domain order.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const sidebars = {
|
||||||
|
entitiesSidebar: [
|
||||||
|
{type: 'doc', id: 'entity-directory', label: 'Overview'},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Registry',
|
||||||
|
collapsed: false,
|
||||||
|
collapsible: true,
|
||||||
|
items: registryItems,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
For generated content, build `registryItems` deterministically from canonical IDs or generated filenames. Be careful when deriving Docusaurus document IDs from filenames with numeric prefixes:
|
||||||
|
|
||||||
|
- a filename such as `001-client-name.md` commonly resolves to document ID `client-name`, because Docusaurus treats `001-` as a position prefix;
|
||||||
|
- if removing the generated position prefix leaves a basename beginning with a digit, Docusaurus may retain the complete filename ID—for example, `0002-112-mobile-application.md` may resolve to `0002-112-mobile-application`, not `112-mobile-application`;
|
||||||
|
- the safest new-generator design is an explicit stable `id` in frontmatter;
|
||||||
|
- for existing generators, derive IDs with the observed rule and verify both an ordinary dossier and the first numeric-leading dossier in the production build.
|
||||||
|
|
||||||
|
After building, inspect rendered section indexes—not only configuration source—and assert that `Overview`, `Registry`, and the first several ordered dossier labels are present. Also retain exact route-count validation for all dossiers.
|
||||||
|
|
||||||
|
## Reusable evidence cards
|
||||||
|
|
||||||
|
Wide relationship/evidence tables become difficult to scan. Generate MDX pages that import a reusable React card component and pass structured props for entity, relationship, context, contractor, confidence, and source links. Keep the component generic and keep evidence extraction in the generator.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
- preserve claim-level direct links;
|
||||||
|
- render confidence and role explicitly;
|
||||||
|
- retain a link to the canonical entity dossier;
|
||||||
|
- use responsive semantic markup (`article`, `header`, `dl`, lists);
|
||||||
|
- preserve the site's shape language, including square edges when required;
|
||||||
|
- validate that all generated records contain cards and that the old table signature is absent.
|
||||||
|
|
||||||
|
## Generated identifier provenance
|
||||||
|
|
||||||
|
If registry identifiers are hash-derived, label them as internal generated IDs everywhere users encounter them: registry introduction, record page, methodology, and provenance/data documentation. State the exact derivation (for example, the first ten hexadecimal characters of a SHA-1 digest of the canonical identity key), its determinism boundary, and that it is not a public-source identifier.
|
||||||
|
|
||||||
|
## Per-record reference indexes
|
||||||
|
|
||||||
|
A record should preserve precise claim-level citations and also expose a de-duplicated `## References` index for scanning and reuse.
|
||||||
|
|
||||||
|
- Client pages: extract all direct Markdown URLs from the canonical dossier while generating the MDX copy; preserve first-seen label/order and de-duplicate by URL.
|
||||||
|
- Derived system pages: combine direct URLs from every source observation with canonical system-analysis evidence URLs; de-duplicate by URL.
|
||||||
|
- Do not replace claim-level links with the index.
|
||||||
|
- If there is no URL, say so explicitly rather than inventing one.
|
||||||
|
- Validate exact coverage counts: every generated entity dossier and every generated derived-record dossier must have a References heading.
|
||||||
|
|
||||||
|
## Verification sequence
|
||||||
|
|
||||||
|
1. Run source generation and schema validation.
|
||||||
|
2. Run TypeScript checks.
|
||||||
|
3. Run the real production build and route validator.
|
||||||
|
4. Assert exact dossier/reference/card counts and absence of obsolete table signatures.
|
||||||
|
5. Serve the build locally and request every top-level index plus representative entity and derived-record routes, checking distinctive markers.
|
||||||
|
6. Commit/push, prove local/remote SHA equality, inspect the Actions run matching that SHA, and verify deployed content when the protected backend is reachable.
|
||||||
|
7. Treat expected public authentication redirects separately from content publication.
|
||||||
@@ -0,0 +1,409 @@
|
|||||||
|
# React Flow Documentation Visualizations
|
||||||
|
|
||||||
|
Use [React Flow](https://reactflow.dev/) when structured documentation data needs an interactive spatial view: roadmaps, Gantt-like timelines, dependency graphs, or traceability maps between Features, requirements, and architectural decisions. React Flow is a renderer and interaction layer, not the authoritative data store and not a built-in layout engine.
|
||||||
|
|
||||||
|
This capability assumes the local YAML data pattern in [local-yaml-data-database.md](local-yaml-data-database.md). Build nodes and edges from validated source YAML records. Never maintain a second copy of roadmap or relationship data inside TSX or MDX.
|
||||||
|
|
||||||
|
## Appropriate uses
|
||||||
|
|
||||||
|
Use React Flow for:
|
||||||
|
|
||||||
|
- a roadmap organized by Epic, lane, status, release, or date;
|
||||||
|
- a Gantt-like roadmap where time determines horizontal position and lane determines vertical position;
|
||||||
|
- Feature-to-Feature dependencies;
|
||||||
|
- Requirement-to-Feature n:m traceability;
|
||||||
|
- architectural decision-to-Feature n:m traceability;
|
||||||
|
- a combined, filterable delivery or architecture dependency map.
|
||||||
|
|
||||||
|
Do not use React Flow for ordinary prose, small static tables, or a linear list with no useful spatial relationship. Keep a semantic table fallback for every visualization so the information remains searchable, printable, and accessible without client-side JavaScript.
|
||||||
|
|
||||||
|
## Official baseline
|
||||||
|
|
||||||
|
Use the current package name documented by React Flow:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm add @xyflow/react
|
||||||
|
```
|
||||||
|
|
||||||
|
Import the required base stylesheet once in the visualization component or shared theme entry:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import '@xyflow/react/dist/style.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
Relevant official guidance:
|
||||||
|
|
||||||
|
- [Quick Start](https://reactflow.dev/learn)
|
||||||
|
- [Layouting overview](https://reactflow.dev/learn/layouting/layouting)
|
||||||
|
- [Accessibility](https://reactflow.dev/learn/advanced-use/accessibility)
|
||||||
|
- [Server-side rendering](https://reactflow.dev/learn/advanced-use/ssr-ssg-configuration)
|
||||||
|
- [Testing](https://reactflow.dev/learn/advanced-use/testing)
|
||||||
|
|
||||||
|
React Flow does not provide one automatic graph layout. Its official layouting guide describes external options including Dagre, D3, and ELK. Choose and document the layout deliberately.
|
||||||
|
|
||||||
|
## Component boundary
|
||||||
|
|
||||||
|
Keep data loading, graph adaptation, layout, and rendering separate:
|
||||||
|
|
||||||
|
```text
|
||||||
|
data/**/*.yml
|
||||||
|
↓ build-time validation
|
||||||
|
DocumentationDatabase
|
||||||
|
↓ pure adapter
|
||||||
|
GraphModel { nodes, edges, fallbackRows, legend }
|
||||||
|
↓ pure layout
|
||||||
|
PositionedGraphModel
|
||||||
|
↓ React component
|
||||||
|
RoadmapFlow | DependencyMap
|
||||||
|
↓ MDX composition
|
||||||
|
Documentation page
|
||||||
|
```
|
||||||
|
|
||||||
|
Recommended source structure:
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/components/documentation-data/
|
||||||
|
├── react-flow/
|
||||||
|
│ ├── DocumentationFlow.tsx
|
||||||
|
│ ├── RoadmapFlow.tsx
|
||||||
|
│ ├── DependencyMap.tsx
|
||||||
|
│ ├── nodes/
|
||||||
|
│ │ ├── FeatureNode.tsx
|
||||||
|
│ │ ├── RequirementNode.tsx
|
||||||
|
│ │ └── ArchitectureDecisionNode.tsx
|
||||||
|
│ ├── adapters/
|
||||||
|
│ │ ├── roadmapGraph.ts
|
||||||
|
│ │ ├── featureDependencyGraph.ts
|
||||||
|
│ │ ├── requirementFeatureGraph.ts
|
||||||
|
│ │ └── decisionFeatureGraph.ts
|
||||||
|
│ ├── layout/
|
||||||
|
│ │ ├── ganttLayout.ts
|
||||||
|
│ │ └── dependencyLayout.ts
|
||||||
|
│ └── DocumentationFlow.module.css
|
||||||
|
└── fallbacks/
|
||||||
|
├── RoadmapTable.tsx
|
||||||
|
└── TraceabilityTable.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
Adapters and layout functions must be pure TypeScript functions. Test them without a browser. React components receive already validated records or a typed graph model; they do not parse files.
|
||||||
|
|
||||||
|
## Docusaurus client boundary
|
||||||
|
|
||||||
|
React Flow 12 supports server rendering when node dimensions and handle positions are supplied. That is an advanced path. For normal interactive Docusaurus documentation, use Docusaurus `BrowserOnly` to avoid hydration differences and browser-global failures:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import BrowserOnly from '@docusaurus/BrowserOnly';
|
||||||
|
import type {Props} from './DependencyMap';
|
||||||
|
|
||||||
|
export default function DependencyMapClient(props: Props) {
|
||||||
|
return (
|
||||||
|
<BrowserOnly fallback={<div>Loading dependency map…</div>}>
|
||||||
|
{() => {
|
||||||
|
const DependencyMap = require('./DependencyMap').default;
|
||||||
|
return <DependencyMap {...props} />;
|
||||||
|
}}
|
||||||
|
</BrowserOnly>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `@docusaurus/BrowserOnly`, not an unguarded `window` check scattered through components. If server-rendered graph HTML is an explicit requirement, follow the official React Flow 12 SSR guidance and provide deterministic node `width` and `height`, handle positions, and initial viewport data.
|
||||||
|
|
||||||
|
## Required canvas sizing
|
||||||
|
|
||||||
|
React Flow requires a parent with explicit dimensions. Never rely on content height:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: min(72vh, 760px);
|
||||||
|
min-height: 420px;
|
||||||
|
border: 1px solid var(--ifm-color-emphasis-300);
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.react-flow__node),
|
||||||
|
:global(.react-flow__controls-button),
|
||||||
|
:global(.react-flow__minimap) {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use sharp edges throughout. Do not introduce pill nodes, rounded cards, rounded controls, or rounded filter inputs.
|
||||||
|
|
||||||
|
## Shared read-only renderer
|
||||||
|
|
||||||
|
Documentation views are read-only unless an editing workflow is separately approved. Disable accidental graph mutation:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import {
|
||||||
|
Background,
|
||||||
|
Controls,
|
||||||
|
MiniMap,
|
||||||
|
ReactFlow,
|
||||||
|
ReactFlowProvider,
|
||||||
|
type Edge,
|
||||||
|
type Node,
|
||||||
|
} from '@xyflow/react';
|
||||||
|
import '@xyflow/react/dist/style.css';
|
||||||
|
|
||||||
|
export function DocumentationFlow({nodes, edges}: {
|
||||||
|
nodes: Node[];
|
||||||
|
edges: Edge[];
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={styles.canvas} aria-label="Documentation dependency map">
|
||||||
|
<ReactFlowProvider>
|
||||||
|
<ReactFlow
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
fitView
|
||||||
|
nodesDraggable={false}
|
||||||
|
nodesConnectable={false}
|
||||||
|
deleteKeyCode={null}
|
||||||
|
nodesFocusable
|
||||||
|
edgesFocusable
|
||||||
|
disableKeyboardA11y={false}
|
||||||
|
minZoom={0.35}
|
||||||
|
maxZoom={1.8}
|
||||||
|
proOptions={{hideAttribution: false}}
|
||||||
|
>
|
||||||
|
<Controls showInteractive={false} />
|
||||||
|
<MiniMap pannable zoomable />
|
||||||
|
<Background />
|
||||||
|
</ReactFlow>
|
||||||
|
</ReactFlowProvider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep attribution behavior compliant with the installed package's license and React Flow terms. Do not hide attribution merely for visual preference.
|
||||||
|
|
||||||
|
## Roadmap and Gantt-like layout
|
||||||
|
|
||||||
|
React Flow is not a dedicated Gantt package. Implement a Gantt-like roadmap as a deterministic node layout over validated scheduling fields.
|
||||||
|
|
||||||
|
Recommended Feature source fields:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
id: feature-001
|
||||||
|
title: Display project lifecycle state
|
||||||
|
epic_id: epic-001
|
||||||
|
status: in-progress
|
||||||
|
depends_on_feature_ids:
|
||||||
|
- feature-000
|
||||||
|
roadmap:
|
||||||
|
start: 2026-08-01
|
||||||
|
end: 2026-08-21
|
||||||
|
lane: portal
|
||||||
|
progress: 60
|
||||||
|
```
|
||||||
|
|
||||||
|
Layout rules:
|
||||||
|
|
||||||
|
1. Parse dates in one declared timezone and reject invalid ranges.
|
||||||
|
2. Compute `x` from `start` relative to the roadmap's minimum date.
|
||||||
|
3. Compute node width from the duration between `start` and `end`.
|
||||||
|
4. Compute `y` from the stable lane order in data or configuration.
|
||||||
|
5. Draw Feature dependency edges from `depends_on_feature_ids`.
|
||||||
|
6. Render release or milestone markers as non-connectable custom nodes.
|
||||||
|
7. Preserve a consistent time scale while zooming; include a visible date-axis component outside the graph viewport.
|
||||||
|
8. Provide filters for Epic, status, lane, release, and owner without mutating source data.
|
||||||
|
9. Provide a table fallback ordered by lane, start date, end date, and stable Feature ID.
|
||||||
|
|
||||||
|
Do not let dragging persist a new schedule. A documentation reader moving a node is not a data edit. If manual positioning is required for a non-time-based roadmap, store approved coordinates in source YAML and validate them.
|
||||||
|
|
||||||
|
For a roadmap without dates, use a phase or status-column layout. Keep phases in source data and calculate positions deterministically. Do not infer ordering from file enumeration.
|
||||||
|
|
||||||
|
## Dependency layouts
|
||||||
|
|
||||||
|
### Feature dependencies
|
||||||
|
|
||||||
|
Build one node per Feature and one directed edge per `depends_on_feature_ids` entry. Reject missing Feature IDs. Detect cycles and either fail validation for a declared DAG or render cycles with an explicit warning when cyclic relationships are valid domain data.
|
||||||
|
|
||||||
|
For ordinary directed acyclic Feature graphs, use Dagre:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm add @dagrejs/dagre
|
||||||
|
```
|
||||||
|
|
||||||
|
Dagre is appropriate for a compact, deterministic directed layout. Set node dimensions before layout and map Dagre's center-based coordinates to React Flow positions.
|
||||||
|
|
||||||
|
### Complex or grouped dependencies
|
||||||
|
|
||||||
|
For larger maps with nested Epics, compound nodes, multiple ports, or stronger edge-routing requirements, use ELK:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm add elkjs
|
||||||
|
```
|
||||||
|
|
||||||
|
ELK layout is asynchronous. Resolve it before rendering the final graph and show a stable loading state. Do not run a new layout on every React render.
|
||||||
|
|
||||||
|
### Requirement-to-Feature traceability
|
||||||
|
|
||||||
|
Create typed nodes and edges from canonical requirement `feature_ids`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Functional Requirement ── satisfies/constrains ──> Feature
|
||||||
|
Non-functional Requirement ── constrains ──> Feature
|
||||||
|
```
|
||||||
|
|
||||||
|
Use distinct node types, edge styles, labels, and a visible legend. Filters must distinguish functional and non-functional requirements. Do not encode meaning only with color.
|
||||||
|
|
||||||
|
### Architectural decision-to-Feature traceability
|
||||||
|
|
||||||
|
Create typed nodes and edges from canonical architectural decision `feature_ids`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Architectural Decision ── governs/affects ──> Feature
|
||||||
|
```
|
||||||
|
|
||||||
|
Display decision status and link each node to its canonical documentation route. A superseded decision remains traceable but must be visually and textually identified as superseded.
|
||||||
|
|
||||||
|
### Combined maps
|
||||||
|
|
||||||
|
A combined map may include Features, requirements, and architectural decisions, but it must start with a constrained default scope. Large all-record graphs become unreadable and expensive. Require one or more of:
|
||||||
|
|
||||||
|
- selected Epic;
|
||||||
|
- selected Feature;
|
||||||
|
- selected requirement type;
|
||||||
|
- selected decision status;
|
||||||
|
- bounded dependency depth;
|
||||||
|
- search result or explicit ID list.
|
||||||
|
|
||||||
|
Apply filtering before layout so hidden nodes do not consume space.
|
||||||
|
|
||||||
|
## Adapter contract
|
||||||
|
|
||||||
|
A graph adapter should return all rendering and fallback information from one source:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export type DocumentationGraph = {
|
||||||
|
nodes: Node<DocumentationNodeData>[];
|
||||||
|
edges: Edge<DocumentationEdgeData>[];
|
||||||
|
fallbackRows: TraceabilityRow[];
|
||||||
|
legend: LegendItem[];
|
||||||
|
warnings: GraphWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function requirementFeatureGraph(
|
||||||
|
database: DocumentationDatabase,
|
||||||
|
filter: RequirementFeatureFilter,
|
||||||
|
): DocumentationGraph;
|
||||||
|
```
|
||||||
|
|
||||||
|
Requirements for every adapter:
|
||||||
|
|
||||||
|
- stable node IDs equal stable database IDs;
|
||||||
|
- stable edge IDs derived from source type, source ID, relation, and target ID;
|
||||||
|
- no random coordinates or IDs;
|
||||||
|
- no filesystem access;
|
||||||
|
- no mutation of database records;
|
||||||
|
- explicit behavior for missing, filtered, superseded, or cyclic records;
|
||||||
|
- table fallback rows derived in the same function as nodes and edges.
|
||||||
|
|
||||||
|
## MDX composition
|
||||||
|
|
||||||
|
MDX chooses the view and scope. It does not define nodes or edges:
|
||||||
|
|
||||||
|
```mdx
|
||||||
|
---
|
||||||
|
title: "Explore Feature dependencies"
|
||||||
|
description: "Inspect Feature dependencies and related requirements."
|
||||||
|
---
|
||||||
|
|
||||||
|
import DependencyMapClient from '@site/src/components/documentation-data/react-flow/DependencyMapClient';
|
||||||
|
import TraceabilityTable from '@site/src/components/documentation-data/fallbacks/TraceabilityTable';
|
||||||
|
|
||||||
|
<DependencyMapClient
|
||||||
|
mode="feature-requirements"
|
||||||
|
epicId="epic-001"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TraceabilityTable
|
||||||
|
mode="feature-requirements"
|
||||||
|
epicId="epic-001"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
The map and table must consume the same adapter output or the same normalized selector so they cannot disagree.
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
React Flow provides keyboard and screen-reader support. Preserve it:
|
||||||
|
|
||||||
|
- keep `nodesFocusable` and `edgesFocusable` enabled;
|
||||||
|
- keep `disableKeyboardA11y={false}`;
|
||||||
|
- provide meaningful `ariaLabel` values for custom nodes and edges;
|
||||||
|
- ensure custom node links and controls have visible focus states;
|
||||||
|
- provide text labels in addition to colors and line styles;
|
||||||
|
- provide a legend that explains node types, statuses, and edge meanings;
|
||||||
|
- provide an adjacent semantic table fallback with the same filtered records;
|
||||||
|
- preserve keyboard access to filters, fit-view controls, and linked detail pages;
|
||||||
|
- test at 200% zoom and in both light and dark themes.
|
||||||
|
|
||||||
|
Read-only means mutation is disabled, not navigation or focus.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- Memoize node types outside React components.
|
||||||
|
- Build graph data with pure selectors and memoize by database revision and filter state.
|
||||||
|
- Filter before layout.
|
||||||
|
- Avoid recreating nodes and edges on every render.
|
||||||
|
- Do not render the full database by default.
|
||||||
|
- Use bounded dependency depth for large graphs.
|
||||||
|
- Lazy-load the BrowserOnly visualization component.
|
||||||
|
- Run asynchronous ELK layout only when the relevant graph input changes.
|
||||||
|
- Measure browser interaction with representative production-size data, not only two-node examples.
|
||||||
|
|
||||||
|
## Testing and verification
|
||||||
|
|
||||||
|
Test pure transformation and layout behavior first:
|
||||||
|
|
||||||
|
- scheduling dates produce stable Gantt `x`, width, and lane `y` values;
|
||||||
|
- Feature dependencies produce the expected directed edges;
|
||||||
|
- Requirement and architectural decision n:m mappings produce complete edges;
|
||||||
|
- filters remove nodes before layout;
|
||||||
|
- dangling IDs fail validation before graph construction;
|
||||||
|
- stable inputs produce byte-equivalent graph IDs and coordinates;
|
||||||
|
- fallback table rows represent the same relationships as graph edges;
|
||||||
|
- cycles follow the declared policy.
|
||||||
|
|
||||||
|
Then test React behavior:
|
||||||
|
|
||||||
|
- the BrowserOnly fallback renders during static generation;
|
||||||
|
- the canvas mounts with explicit width and height;
|
||||||
|
- nodes and edges are keyboard focusable;
|
||||||
|
- graph mutation is disabled;
|
||||||
|
- filters, detail links, fit view, pan, and zoom work;
|
||||||
|
- missing and empty datasets render useful states;
|
||||||
|
- table fallback remains usable without the graph.
|
||||||
|
|
||||||
|
Required delivery checks:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm data:validate
|
||||||
|
pnpm data:check
|
||||||
|
pnpm test
|
||||||
|
pnpm typecheck
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
|
After the production build, exercise each affected page in a real browser. Verify light and dark themes, desktop and narrow widths, keyboard navigation, labels, filters, links, pan/zoom, empty states, and the table fallback. Then verify the exact Gitea Actions SHA and deployed route.
|
||||||
|
|
||||||
|
## Acceptance checklist
|
||||||
|
|
||||||
|
- [ ] `@xyflow/react` and its base stylesheet are installed and imported.
|
||||||
|
- [ ] React Flow receives validated build-time data from source YAML through a pure adapter.
|
||||||
|
- [ ] Roadmap scheduling and Feature dependencies are stored in YAML, not TSX or MDX.
|
||||||
|
- [ ] Gantt-like positions are deterministic from dates and lanes.
|
||||||
|
- [ ] Dagre or ELK is chosen explicitly for dependency layout; React Flow is not described as providing automatic layout.
|
||||||
|
- [ ] Feature, requirement, and architectural decision relationships use stable IDs and typed nodes/edges.
|
||||||
|
- [ ] Docusaurus uses `BrowserOnly`, or the advanced React Flow 12 SSR dimensions are fully configured.
|
||||||
|
- [ ] The canvas has explicit width and height.
|
||||||
|
- [ ] Documentation views disable dragging, connecting, and deletion.
|
||||||
|
- [ ] Keyboard and screen-reader support remains enabled.
|
||||||
|
- [ ] Sharp styling applies `border-radius: 0` to nodes, controls, minimaps, filters, and surrounding panels.
|
||||||
|
- [ ] Every visualization has a semantic table fallback derived from the same adapter.
|
||||||
|
- [ ] Unit, component, type, production build, browser, Actions, and deployed-route checks pass.
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Reference-inspired documentation portals
|
||||||
|
|
||||||
|
Use this reference when a user selects an existing Docusaurus site as the visual and entry-page model for another documentation portal.
|
||||||
|
|
||||||
|
## Selection workflow
|
||||||
|
|
||||||
|
1. Inspect the exact URL the user selected; similarly named domains can present different products and layouts (for example, a documentation portal versus a community wiki).
|
||||||
|
2. Extract structural principles rather than cloning branded content:
|
||||||
|
- navigation hierarchy;
|
||||||
|
- hero composition;
|
||||||
|
- discovery groups;
|
||||||
|
- card/link density;
|
||||||
|
- typography posture;
|
||||||
|
- palette and surface relationships;
|
||||||
|
- responsive collapse behavior.
|
||||||
|
3. Map the target repository's real information architecture into those patterns. Do not invent fake metrics, features, or portfolio content.
|
||||||
|
4. Preserve explicit user constraints even when the reference violates them. A selected reference is a direction, not permission to override established design rules.
|
||||||
|
5. When presenting alternative references, track prior suggestions and exclusions. If the user requests “new options,” do not repeat any previously proposed option.
|
||||||
|
|
||||||
|
## Dedicated homepage pattern
|
||||||
|
|
||||||
|
When the docs plugin already owns `/`:
|
||||||
|
|
||||||
|
1. Move its introduction to a stable route such as `/overview/`.
|
||||||
|
2. Add `src/pages/index.tsx` and a CSS Module for the dedicated landing page.
|
||||||
|
3. Keep registry and detail routes unchanged.
|
||||||
|
4. Build with broken-link checking enabled and verify both `build/index.html` and `build/overview/index.html`.
|
||||||
|
|
||||||
|
## Sharp-rectangle adaptation
|
||||||
|
|
||||||
|
For a user who explicitly rejects rounded shapes, enforce the rule at both token and rendered-component levels:
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--ifm-global-radius: 0;
|
||||||
|
--ifm-code-border-radius: 0;
|
||||||
|
--ifm-pre-border-radius: 0;
|
||||||
|
--ifm-alert-border-radius: 0;
|
||||||
|
--ifm-button-border-radius: 0;
|
||||||
|
--ifm-badge-border-radius: 0;
|
||||||
|
--ifm-card-border-radius: 0;
|
||||||
|
--ifm-pagination-nav-border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *::before, *::after {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The universal override is intentionally forceful. Use it only when the no-rounding requirement is global. Verify the minified production CSS contains the override, not just the source file.
|
||||||
|
|
||||||
|
## IOTA Docs-inspired posture
|
||||||
|
|
||||||
|
General principles observed from `https://docs.iota.org/` that transfer well to an original governance or ecosystem portal:
|
||||||
|
|
||||||
|
- dark technical canvas with one restrained teal accent;
|
||||||
|
- large editorial hero with a concise purpose statement;
|
||||||
|
- clear “Discover” groups that route users by task or domain;
|
||||||
|
- compact monospace eyebrow labels;
|
||||||
|
- bordered grid composition with low-elevation surfaces;
|
||||||
|
- content-rich cards whose lower rows act as direct navigation;
|
||||||
|
- a secondary section that explains the operating model or platform value;
|
||||||
|
- responsive collapse from multi-column grids to one column.
|
||||||
|
|
||||||
|
Do not reproduce IOTA names, claims, illustrations, or exact branded layout. Translate the hierarchy and visual posture to the target content.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Typecheck and production build pass.
|
||||||
|
- Root and relocated overview routes both exist.
|
||||||
|
- Distinctive landing-page markers appear in built HTML.
|
||||||
|
- Global shape rule appears in compiled CSS.
|
||||||
|
- Responsive grids collapse without horizontal overflow.
|
||||||
|
- Real CI completes successfully.
|
||||||
|
- Protected Pages deployments are verified through an authorized browser or the internal service proxy, with content markers rather than status alone.
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# Research Address-Book Repositories
|
||||||
|
|
||||||
|
Use this pattern when a Gitea repository is intended to map a broad ecosystem, organization network, infrastructure landscape, or other changing domain.
|
||||||
|
|
||||||
|
## Design principle
|
||||||
|
|
||||||
|
Start with an **address book of authoritative directories and coordinating bodies**, not an attempted exhaustive list of every participant. Large ecosystems change too quickly for a hand-maintained flat inventory. Anchor later ingestion to official registries, public directories, and primary sources.
|
||||||
|
|
||||||
|
Separate four concerns:
|
||||||
|
|
||||||
|
1. Human navigation and concise summaries.
|
||||||
|
2. Canonical machine-readable records.
|
||||||
|
3. Method, taxonomy, and analytical findings.
|
||||||
|
4. Unverified leads and future research.
|
||||||
|
|
||||||
|
## Recommended structure
|
||||||
|
|
||||||
|
```text
|
||||||
|
.
|
||||||
|
├── README.md
|
||||||
|
├── address-book/
|
||||||
|
│ ├── README.md
|
||||||
|
│ └── <category>.md
|
||||||
|
├── data/
|
||||||
|
│ ├── README.md
|
||||||
|
│ └── entities.csv
|
||||||
|
├── docs/
|
||||||
|
│ ├── 01-scope-and-method.md
|
||||||
|
│ ├── 02-first-round-investigation.md
|
||||||
|
│ └── 03-taxonomy.md
|
||||||
|
├── research/
|
||||||
|
│ └── next-round.md
|
||||||
|
├── scripts/
|
||||||
|
│ └── validate.py
|
||||||
|
├── sources/
|
||||||
|
│ └── README.md
|
||||||
|
├── CONTRIBUTING.md
|
||||||
|
└── Makefile
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep the top level small and obvious. Number only documents that have a reading sequence. Prefer category pages over one giant address-book document.
|
||||||
|
|
||||||
|
## Round-1 data model
|
||||||
|
|
||||||
|
CSV is a strong first format because it is Git-readable, spreadsheet-compatible, and dependency-free. Useful columns:
|
||||||
|
|
||||||
|
```text
|
||||||
|
id,name,category,scope,region,homepage,directory_url,role,source_url,status,verified_at,notes
|
||||||
|
```
|
||||||
|
|
||||||
|
Conventions:
|
||||||
|
|
||||||
|
- `id`: stable lowercase kebab-case; never recycle IDs.
|
||||||
|
- `category`: controlled taxonomy, based on function rather than assumed hierarchy.
|
||||||
|
- `scope`: distinguish global, regional, and regional-community scope.
|
||||||
|
- `region`: service or community coverage, not merely headquarters.
|
||||||
|
- `directory_url`: best public discovery, contact, membership, registry, or data entry point.
|
||||||
|
- `source_url`: primary evidence for the role claim.
|
||||||
|
- `status`: `verified`, `partial`, `lead`, or `stale`.
|
||||||
|
- `verified_at`: UTC date in `YYYY-MM-DD`.
|
||||||
|
- `notes`: caveats about automated access, completeness, ownership, or interpretation.
|
||||||
|
|
||||||
|
Move to versioned JSON/YAML records only when nested relationships, source merges, or repeated attributes make CSV inadequate. Do not add a database just for presentation.
|
||||||
|
|
||||||
|
## Research method
|
||||||
|
|
||||||
|
1. State the research question and explicit boundary.
|
||||||
|
2. Build a functional taxonomy; do not force decentralized ecosystems into a false parent/child hierarchy.
|
||||||
|
3. Identify globally or regionally authoritative directories and coordinating bodies.
|
||||||
|
4. Prefer official registries, APIs, standards bodies, and operator-maintained sources.
|
||||||
|
5. Verify URL reachability, but distinguish transport success from factual proof.
|
||||||
|
6. Put canonical entries in structured data; keep hypotheses in `research/`.
|
||||||
|
7. Write a first-round synthesis explaining findings, evidence limits, and missing coverage.
|
||||||
|
8. Define the next research rounds before adding automation.
|
||||||
|
|
||||||
|
## Provenance and contact safety
|
||||||
|
|
||||||
|
- Prefer public institutional and role-based contact points.
|
||||||
|
- Do not publish private personal data, credentials, or member-only incident contacts.
|
||||||
|
- A registry record does not necessarily establish current operator, owner, or beneficial controller.
|
||||||
|
- Community-maintained databases and measurement collectors expose partial viewpoints; document coverage caveats.
|
||||||
|
- A `403` from an automated client may be an access-policy caveat, not evidence that the official URL or entity is invalid. Record the caveat and use browser/manual verification when appropriate.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
Provide a standard-library-only validator where practical. Check:
|
||||||
|
|
||||||
|
- exact required columns;
|
||||||
|
- non-empty required fields;
|
||||||
|
- unique and well-formed IDs;
|
||||||
|
- controlled taxonomy/status values;
|
||||||
|
- HTTPS URLs;
|
||||||
|
- parseable, non-future verification dates;
|
||||||
|
- presence of required orientation documents.
|
||||||
|
|
||||||
|
Expose predictable commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make validate
|
||||||
|
make summary
|
||||||
|
```
|
||||||
|
|
||||||
|
Before push:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git diff --cached --check
|
||||||
|
make validate
|
||||||
|
```
|
||||||
|
|
||||||
|
After push, verify both the Git ref and at least one distinctive remote artifact using the authenticated Gitea API. A successful push message is useful evidence, but authenticated readback confirms the requested content rather than only the ref update.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Research-led information architecture for evidence registries
|
||||||
|
|
||||||
|
Use this pattern when a Docusaurus site publishes a large evidence corpus organized around entities (for example clients) and derived records (for example systems).
|
||||||
|
|
||||||
|
## Guidance synthesis
|
||||||
|
|
||||||
|
Authoritative sources:
|
||||||
|
|
||||||
|
- GOV.UK content planning: begin with user needs and choose formats around tasks rather than mirroring the publisher's organization.
|
||||||
|
- GOV.UK writing guidance: use clear titles, summaries, headings, language, and descriptive links so users can find and understand content.
|
||||||
|
- W3C WAI design guidance: preserve clear hierarchy, meaningful controls, keyboard-visible focus, and multiple understandable ways to locate information.
|
||||||
|
- USWDS card guidance: cards should be coherent entry points within a collection, each covering one subject and leading to detail.
|
||||||
|
- USWDS table guidance: tables suit long, structured, comparable registries when headers and interpretation are explicit.
|
||||||
|
|
||||||
|
## Recommended portal model
|
||||||
|
|
||||||
|
1. Identify the dominant user tasks before editing navigation. For an entity/evidence registry these are usually:
|
||||||
|
- find an entity;
|
||||||
|
- follow a system/topic across entities;
|
||||||
|
- search names, aliases, contractors, and evidence text;
|
||||||
|
- understand methodology;
|
||||||
|
- verify or reuse source data.
|
||||||
|
2. Make the first three tasks the homepage's equal primary discovery paths. Keep methodology and provenance globally available but visually secondary.
|
||||||
|
3. Use stable global navigation labels based on destinations, such as `Overview`, `Clients`, `Systems`, `Methodology`, and `Data`. Avoid mixing a generated dossier sidebar destination with a duplicate directory destination in the same global navigation.
|
||||||
|
4. Keep large dossier sidebars available inside their docs plugin, but link global navigation to the registry/index page rather than opening an arbitrary first dossier.
|
||||||
|
5. Present scope before detail and label each metric precisely: authoritative entities, consolidated records, and preserved evidence observations are different measures.
|
||||||
|
6. Generate every changing count and overview table from canonical source data. Do not duplicate live totals in hand-maintained Markdown or JSX.
|
||||||
|
7. Explain table ordering, status vocabulary, count semantics, and safe interpretation immediately before long registry tables.
|
||||||
|
8. State explicitly that an association may mean ownership, use, procurement, operation, hosting, maintenance, support, or another relationship. Counts summarize public evidence, not a complete inventory.
|
||||||
|
9. Preserve direct provenance at detail level: every derived record must link observations back to canonical dossiers and evidence.
|
||||||
|
10. Record the reviewed guidance and resulting IA decisions in a repository research note so future redesigns can distinguish intentional structure from accidental layout.
|
||||||
|
|
||||||
|
## Docusaurus implementation notes
|
||||||
|
|
||||||
|
- Ensure both the top-level `title` and visible navbar title use the requested product name; changing only `themeConfig.navbar.title` leaves page titles inconsistent.
|
||||||
|
- For task-oriented global navigation, direct `to:` links to registry/index routes are often clearer than `docSidebar` items that enter a very large sidebar at its first document.
|
||||||
|
- Keep the navbar persistent when users frequently move among large registries.
|
||||||
|
- A homepage search card/button should focus the theme-owned navbar search input or dispatch its shortcut; never mount a second search component.
|
||||||
|
- Give button-based cards the same geometry, typography, hover state, and `:focus-visible` treatment as link cards.
|
||||||
|
- Use semantic `nav`, `section`, `aside`, headings, real links, and real buttons.
|
||||||
|
- Preserve square geometry when required by the site's design language.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
1. Regenerate source-derived content before validation.
|
||||||
|
2. Run repository validation, TypeScript checking, production build, and `git diff --check`.
|
||||||
|
3. Verify representative entity, derived-record, methodology, provenance, and search-index routes.
|
||||||
|
4. Inspect the homepage visually for hierarchy, alignment, clipping, overlap, and consistency.
|
||||||
|
5. Exercise the search trigger in a browser and inspect console/JavaScript errors.
|
||||||
|
6. Commit and push only after local checks pass; observe the exact Gitea Actions run through success.
|
||||||
|
7. Verify distinctive changed text through the deployed backend or an authorized browser. If public Pages is intentionally OAuth-protected, treat the expected redirect separately from backend deployment health.
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
- Do not present entity count, unique-record count, and observation count as interchangeable.
|
||||||
|
- Do not make four or five homepage cards compete equally when only three are primary discovery tasks.
|
||||||
|
- Do not hard-code generated overview/status counts in Markdown.
|
||||||
|
- Do not infer that a successful Actions run alone proves the served content changed; verify a distinctive marker.
|
||||||
|
- Do not diagnose an OAuth redirect as a Docusaurus deployment failure.
|
||||||
@@ -0,0 +1,276 @@
|
|||||||
|
# Writing Guide
|
||||||
|
|
||||||
|
Voice, tone, and language standards for documentation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Voice Principles
|
||||||
|
|
||||||
|
### Be Direct
|
||||||
|
|
||||||
|
Not: "It should be noted that the configuration file needs to be updated."
|
||||||
|
Yes: "Update the configuration file."
|
||||||
|
|
||||||
|
Not: "Users may find it helpful to restart the server."
|
||||||
|
Yes: "Restart the server."
|
||||||
|
|
||||||
|
### Address the Reader
|
||||||
|
|
||||||
|
Not: "Developers can use the API to..."
|
||||||
|
Yes: "Use the API to..."
|
||||||
|
|
||||||
|
Not: "One might consider..."
|
||||||
|
Yes: "Consider..."
|
||||||
|
|
||||||
|
### Present Tense, Active Voice
|
||||||
|
|
||||||
|
Not: "The file will be created when..."
|
||||||
|
Yes: "The file is created when..."
|
||||||
|
|
||||||
|
Not: "The request is processed by the server."
|
||||||
|
Yes: "The server processes the request."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Words That Weaken
|
||||||
|
|
||||||
|
Remove these. They add nothing:
|
||||||
|
|
||||||
|
| Remove | Why |
|
||||||
|
|--------|-----|
|
||||||
|
| simply, just, easily | Implies difficulty is your fault |
|
||||||
|
| obviously, clearly | If it were obvious, you wouldn't write it |
|
||||||
|
| please | Unnecessary in instructions |
|
||||||
|
| in order to | "To" works |
|
||||||
|
| it is important to note that | Just state it |
|
||||||
|
| basically | Delete |
|
||||||
|
| actually | Delete |
|
||||||
|
|
||||||
|
**Before:** "Simply run the following command to easily set up..."
|
||||||
|
**After:** "Run the command to set up..."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Inclusive Language
|
||||||
|
|
||||||
|
### Required Replacements
|
||||||
|
|
||||||
|
| Don't Use | Use Instead |
|
||||||
|
|-----------|-------------|
|
||||||
|
| whitelist / blacklist | allowlist / blocklist |
|
||||||
|
| master / slave | primary / replica, main / secondary |
|
||||||
|
| master branch | main branch |
|
||||||
|
| sanity check | confidence check, validation, smoke test |
|
||||||
|
| dummy (value/data) | placeholder, sample, test |
|
||||||
|
| cripple | disable, impair, limit |
|
||||||
|
| blind to | unaware of, ignoring |
|
||||||
|
| crazy, insane | unexpected, surprising, intense |
|
||||||
|
| man-hours | person-hours, engineering hours |
|
||||||
|
| manpower | workforce, staffing |
|
||||||
|
| guys | folks, everyone, team, y'all |
|
||||||
|
| he/she, his/her | they, their |
|
||||||
|
| normal users | typical users, most users |
|
||||||
|
|
||||||
|
### Writing About People
|
||||||
|
|
||||||
|
**Disability:**
|
||||||
|
- Not: "suffers from," "victim of," "wheelchair-bound"
|
||||||
|
- Yes: "has," "uses a wheelchair," "with [condition]"
|
||||||
|
|
||||||
|
**Age:**
|
||||||
|
- Not: "the elderly," "seniors"
|
||||||
|
- Yes: "older adults"
|
||||||
|
|
||||||
|
**Experience level:**
|
||||||
|
- Not: "for dummies," "idiot's guide"
|
||||||
|
- Yes: "introduction," "getting started"
|
||||||
|
|
||||||
|
### Cultural Sensitivity
|
||||||
|
|
||||||
|
- Avoid US-centric examples (Thanksgiving, Super Bowl, Fahrenheit)
|
||||||
|
- Use ISO date format: 2024-01-15, not 01/15/2024
|
||||||
|
- Use 24-hour time or specify timezone
|
||||||
|
- Don't assume everyone celebrates the same holidays
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Second Person "You"
|
||||||
|
|
||||||
|
Always address the reader directly:
|
||||||
|
|
||||||
|
Not: "The user should configure their settings."
|
||||||
|
Yes: "Configure your settings."
|
||||||
|
|
||||||
|
Not: "Developers will need to install..."
|
||||||
|
Yes: "Install..."
|
||||||
|
|
||||||
|
**When to use "we":**
|
||||||
|
- Only when the reader is genuinely working WITH you
|
||||||
|
- "We'll build this together" (in a collaborative tutorial)
|
||||||
|
- Never use "we" to mean "our company"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code in Prose
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
Use backticks and be specific:
|
||||||
|
|
||||||
|
Not: "Run the build command."
|
||||||
|
Yes: "Run `npm run build`."
|
||||||
|
|
||||||
|
### Filenames and Paths
|
||||||
|
|
||||||
|
Always in backticks: `docusaurus.config.ts`, `src/pages/`
|
||||||
|
|
||||||
|
### Configuration Values
|
||||||
|
|
||||||
|
In backticks: Set `trailingSlash` to `false`.
|
||||||
|
|
||||||
|
### Placeholders
|
||||||
|
|
||||||
|
Use angle brackets and ALL_CAPS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/<YOUR_USERNAME>/<REPO_NAME>
|
||||||
|
```
|
||||||
|
|
||||||
|
Not `{username}` or `$USERNAME` in documentation prose.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Headings
|
||||||
|
|
||||||
|
### Capitalization
|
||||||
|
|
||||||
|
Use sentence case, not title case:
|
||||||
|
|
||||||
|
Not: "How To Configure Your Build Settings"
|
||||||
|
Yes: "How to configure your build settings"
|
||||||
|
|
||||||
|
### Parallel Structure
|
||||||
|
|
||||||
|
If one heading starts with a verb, they all should:
|
||||||
|
|
||||||
|
**Good:**
|
||||||
|
- Install dependencies
|
||||||
|
- Configure the project
|
||||||
|
- Deploy to production
|
||||||
|
|
||||||
|
**Bad:**
|
||||||
|
- Installation
|
||||||
|
- Configure the project
|
||||||
|
- Deploying to production
|
||||||
|
|
||||||
|
### Depth
|
||||||
|
|
||||||
|
Never go past H3 in a single page. If you need H4, the page is too complex—split it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lists
|
||||||
|
|
||||||
|
### When to Use Bullets vs. Numbers
|
||||||
|
|
||||||
|
**Numbers:** When order matters (steps, priority)
|
||||||
|
**Bullets:** When order doesn't matter (features, options)
|
||||||
|
|
||||||
|
### List Formatting
|
||||||
|
|
||||||
|
Each item should be grammatically parallel:
|
||||||
|
|
||||||
|
**Good:**
|
||||||
|
- Configure the database
|
||||||
|
- Set up authentication
|
||||||
|
- Deploy the application
|
||||||
|
|
||||||
|
**Bad:**
|
||||||
|
- Database configuration
|
||||||
|
- Set up authentication
|
||||||
|
- The application should be deployed
|
||||||
|
|
||||||
|
### Sentence Fragments OK
|
||||||
|
|
||||||
|
In lists, sentence fragments are fine:
|
||||||
|
|
||||||
|
- Fast builds
|
||||||
|
- Hot reloading
|
||||||
|
- TypeScript support
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Show, Don't Tell
|
||||||
|
|
||||||
|
Not: "The configuration accepts various options for customization."
|
||||||
|
|
||||||
|
Yes:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
theme: 'dark',
|
||||||
|
language: 'en',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Minimal, Then Complete
|
||||||
|
|
||||||
|
Show the simplest working example first:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Minimal
|
||||||
|
{ name: 'my-project' }
|
||||||
|
```
|
||||||
|
|
||||||
|
Then show comprehensive:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// With all options
|
||||||
|
{
|
||||||
|
name: 'my-project',
|
||||||
|
version: '1.0.0',
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comments in Code
|
||||||
|
|
||||||
|
Use comments to explain WHY, not WHAT:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Good: Explains reasoning
|
||||||
|
timeout: 30000, // Matches API's default request timeout
|
||||||
|
|
||||||
|
// Bad: States the obvious
|
||||||
|
timeout: 30000, // Set timeout to 30000
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Messages
|
||||||
|
|
||||||
|
When documenting errors:
|
||||||
|
|
||||||
|
1. Show the exact error message the user sees
|
||||||
|
2. Explain what caused it
|
||||||
|
3. Give the fix
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: Cannot find module 'react'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cause:** Dependencies aren't installed.
|
||||||
|
**Fix:** Run `npm install` in your project directory.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checklist Before Publishing
|
||||||
|
|
||||||
|
- [ ] Title describes what the reader will DO
|
||||||
|
- [ ] Description is under 160 characters
|
||||||
|
- [ ] No "simple," "easy," "just," or "obviously"
|
||||||
|
- [ ] All code examples tested and working
|
||||||
|
- [ ] Links to related pages (no dead ends)
|
||||||
|
- [ ] Heading structure: H1 → H2 → H3 only
|
||||||
|
- [ ] Inclusive language throughout
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SKILL = ROOT / "SKILL.md"
|
||||||
|
text = SKILL.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert text.startswith("---\n"), "SKILL.md must start with YAML frontmatter"
|
||||||
|
parts = text.split("\n---\n", 1)
|
||||||
|
assert len(parts) == 2 and parts[1].strip(), "frontmatter must close and body must be non-empty"
|
||||||
|
frontmatter, body = parts
|
||||||
|
assert re.search(r"^name:\s*documentation-docusaurus\s*$", frontmatter, re.M), "wrong skill name"
|
||||||
|
assert "description:" in frontmatter, "missing description"
|
||||||
|
assert len(text) <= 100_000, "SKILL.md exceeds Hermes size limit"
|
||||||
|
assert "## Authoritative repository" in body
|
||||||
|
assert "## Gitea delivery gate" in body
|
||||||
|
assert "references/gitea-docusaurus-delivery.md" in text
|
||||||
|
|
||||||
|
required = [
|
||||||
|
ROOT / "README.md",
|
||||||
|
ROOT / "PROVENANCE.md",
|
||||||
|
ROOT / "templates/gitea-actions-build.yml",
|
||||||
|
ROOT / "references/gitea-docusaurus-delivery.md",
|
||||||
|
]
|
||||||
|
assert all(path.is_file() for path in required), "required package artifact missing"
|
||||||
|
|
||||||
|
markdown_files = [SKILL, ROOT / "README.md", ROOT / "PROVENANCE.md", *sorted((ROOT / "references").glob("*.md"))]
|
||||||
|
link_pattern = re.compile(r"\[[^]]*\]\(([^)]+)\)")
|
||||||
|
missing = []
|
||||||
|
for source in markdown_files:
|
||||||
|
source_text = source.read_text(encoding="utf-8")
|
||||||
|
for target in link_pattern.findall(source_text):
|
||||||
|
target = target.split("#", 1)[0]
|
||||||
|
if not target or "://" in target or target.startswith(("mailto:", "#", "/")):
|
||||||
|
continue
|
||||||
|
resolved = (source.parent / target).resolve()
|
||||||
|
if ROOT not in resolved.parents and resolved != ROOT:
|
||||||
|
missing.append(f"{source.relative_to(ROOT)} -> outside package: {target}")
|
||||||
|
elif not resolved.exists():
|
||||||
|
missing.append(f"{source.relative_to(ROOT)} -> missing: {target}")
|
||||||
|
assert not missing, "broken local links:\n" + "\n".join(missing)
|
||||||
|
|
||||||
|
reference_names = {path.name for path in (ROOT / "references").glob("*.md")}
|
||||||
|
coverage_text = "\n".join(path.read_text(encoding="utf-8") for path in markdown_files)
|
||||||
|
unreferenced = sorted(name for name in reference_names if name not in coverage_text.replace(str(ROOT), ""))
|
||||||
|
assert not unreferenced, f"unreferenced support files: {unreferenced}"
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"OK skill=documentation-docusaurus chars={len(text)} "
|
||||||
|
f"references={len(reference_names)} markdown_files={len(markdown_files)}"
|
||||||
|
)
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
name: Build and Deploy Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Cache pnpm dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm build
|
||||||
|
|
||||||
|
- name: Upload build artifact
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docusaurus-build
|
||||||
|
path: build/
|
||||||
|
retention-days: 7
|
||||||
Reference in New Issue
Block a user