Files

407 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.