[[profile-dieterbaier-arc42]]Profile Dieter Baier arc42 Architecture

Write once. Publish everywhere.
Dieter Baier
v2.0
07/2026

This architecture documentation describes profile-dieterbaier as a software project: a personal profile, CV, thoughts, articles, and publishing pipeline based on Docs-as-Code and the principle "write once, publish everywhere".

1. Introduction and Goals

profile-dieterbaier exists to maintain one authoritative personal profile source and publish it to multiple channels: personal website, CV, GitHub README, GitLab README, article exports, and architecture documentation.

1.1. Motivation

Maintaining the same professional profile across several platforms creates duplication, inconsistency, and avoidable manual work. The project applies Docs-as-Code to personal professional content: source text is versioned, reviewed, validated, generated, and deployed like software.

As a running example of the Docs-as-Code Toolkit, the project keeps structured source content, metadata, and relations above the publishing layer. Generated website, README, CV, article, and architecture outputs are derived artifacts rendered by the build pipeline.

1.2. Business Goals

ID Goal Success indicator

G-001

Single source of truth for profile, CV, articles, shorts, and architecture documentation.

Public artifacts are generated from repository sources.

G-002

Multi-target publishing without copy-paste maintenance.

README, website, CV, and article Markdown are built by Gradle tasks.

G-003

Treat the profile as a software product.

Architecture documentation, decisions, risks, quality scenarios, metamodels, validators, and generators are part of the repository.

1.3. Top Quality Goals

ID Priority Quality goal Meaning

QG-001

1

Reproducibility

Same source and environment produce the same generated artifacts.

QG-002

2

Maintainability

Content, presentation, metadata, and build logic have explicit responsibilities.

QG-003

3

Automation

Normal publishing and validation run without manual transformation steps.

QG-004

4

Correctness

Generated public artifacts reflect the intended source content and metadata.

QG-005

5

Sustainability

The site and pipeline avoid unnecessary runtime logic, tracking, and waste.

1.4. Stakeholders

ID Stakeholder Concern

ST-001

Dieter Baier

Maintain professional profile information efficiently and correctly.

ST-002

Potential employers and clients

Read current CV, project history, articles, and contact information.

ST-003

Technical community

Access thoughts, articles, README content, and toolkit examples.

ST-004

CI/CD runtime

Build and deploy only the artifacts affected by a change.

2. Profile Product Goals

2.1. Content Product Purpose

The project publishes the author’s professional identity as structured source content. The core value is not the build pipeline itself, but the reusable, versioned profile knowledge that the pipeline turns into channel-specific outputs.

2.2. Authoring Principle

The profile content follows the same discipline as architecture documentation:

  • stable artifact identity where content is independently meaningful;

  • explicit metadata for validation and generation;

  • source artifacts as the authority;

  • generated indexes and matrices as derived output;

  • AI-assisted work governed by contracts and skills.

2.3. Private-To-Public Article Lifecycle

The profile product has a private-to-public article lifecycle. New articles and private notes should start in a private source boundary, become reviewable through a preview status, and appear on dieterbaier.eu only after an explicit publication decision.

The implementation is tracked as GitHub issue #26. The epic links backlog work to the architecture roadmap and must not be implemented before the related ADRs are reviewed:

3. Architecture Constraints

ID Constraint Consequence

CON-001

AsciiDoc is the primary authoring format.

Profile and architecture content can use includes, attributes, and multi-format rendering.

CON-002

Gradle orchestrates local and CI builds.

New generators and validators are exposed as Gradle tasks.

CON-003

GitHub Actions and the docs-toolbox container are the deployment runtime.

CI must remain compatible with containerized execution.

CON-004

Private contact data is injected from environment variables.

Secrets must not be committed to the repository.

CON-005

Profile and architecture metadata must remain source-controlled and reviewable.

Generated output is reproducible and excluded from source editing.

4. System Scope and Context

4.1. Business Context

Diagram

4.2. Technical Context

Diagram

5. Solution Strategy

Strategy Supports Description

Single source profile knowledge

QG-002, QG-004

Profile artifacts are authored once and reused across website, CV, README, and article exports.

Metadata-backed content

QG-002, QG-004

Standalone content artifacts receive front matter; include-heavy reusable fragments are covered by sidecar metadata.

Toolkit-compatible architecture knowledge

QG-002, QG-003

arc42 chapters, ADRs, quality scenarios, risks, schemas, validators, and generated fragments follow architecture-knowledge-toolkit conventions.

Gradle task orchestration

QG-001, QG-003

Build, validation, and generation are exposed as repeatable Gradle tasks.

Containerized CI/CD

QG-001, QG-005

GitHub Actions run the same toolchain in the docs-toolbox container and deploy changed outputs through target-specific deploy jobs.

Scoped deployment credentials

QG-005

Deployment credentials are separated by target. GitHub profile README deployment uses a fine-grained token for dieterbaier/dieterbaier; GitLab README deployment uses a GitLab token; private webspace deployment uses SFTP credentials.

5.1. Build Pipeline Strategy

The GitHub Actions workflow separates change detection, build execution, artifact upload, and target-specific deployment.

Diagram

Only changed target categories trigger the matching Gradle tasks:

Change category Build output

README

build/readme/README.md

Articles

Markdown exports under build/articles

Architecture

build/architecture

Site

build/site

Generated artifacts are uploaded once and consumed by separate deploy jobs. Each deploy job owns one deployment target so failed deployments can be re-run without repeating successful targets. The deploy jobs use target-specific credentials instead of one shared deployment token:

Target Credential Reason

GitHub profile README

PROFILE_REPO_TOKEN repository secret

Cross-repository push to dieterbaier/dieterbaier requires write access to the target repository.

GitLab profile README

GITLAB_TOKEN repository secret

GitLab push requires a GitLab credential independent of GitHub Actions.

Personal website and architecture documentation

SFTP_PASSWORD production environment secret plus SFTP_* variables

Private webspace deployment is external to GitHub and uses SFTP.

Personal contact data

SITE_* production environment secrets

Private data is injected only during CI generation and is not stored in source files.

PROFILE_REPO_TOKEN should be a fine-grained GitHub personal access token scoped to dieterbaier/dieterbaier with Contents: Read and write. A failure in the checkout github profile repo step with Bad credentials indicates that this token is invalid, expired, revoked, or missing the required target repository permission.

5.1.1. Consequences

Advantages:

  • Reduced pipeline runtime

  • Lower resource consumption

  • Deterministic builds

  • Clear separation of concerns

  • Smaller credential blast radius per deployment target

Trade-offs:

  • Increased pipeline complexity

  • Additional maintenance for path filters

  • Token expiry and rotation must be operated explicitly

6. Building Block View

6.1. C4 Level 1: System Context

Diagram

The system publishes profile content from source repositories into static artifacts. The public repository owns the reusable publishing application, architecture documentation, validators, generators, and theme. The private repository is proposed as a separate source component for private articles, notes, and protocols.

6.2. Repository Collaboration

Diagram

This component diagram is the proposed decision basis from ADR-009. It is included here to show the official building block view of the public/private repository collaboration without duplicating the model.

6.3. C4 Level 2: Containers

Diagram
Container Responsibility Storage

Public Profile Repository

Public profile source, public articles, previewable article source, reusable build logic, theme, profile metadata contracts, and architecture documentation.

dieterbaier/profile

Article Comments Repository

Public GitHub issues created through a structured form. A repository workflow validates the submitted article ID against the deployed public-article allowlist and assigns a stable per-article label.

dieterbaier/profile-artikelkommentare

Private Profile Repository

Private article, note, and protocol source. Proposed as a separate source component, not as a duplicated product.

dieterbaier/profile-private

Architecture Knowledge

arc42 chapters, ADRs, quality scenarios, risks, canvases, Q&A, and metadata.

src-content/docs

Theme Assets

CSS, fonts, icons, JavaScript, and shared docinfo fragments used by generated static outputs. For feature #44, the local article-comments script progressively loads matching GitHub issues only after reader consent.

src-content/theme, src-content/profile/includes

Validators

Check architecture and profile metadata before generation and deployment.

scripts/

Generators

Create derived indexes, traceability fragments, article summaries, article-specific GitHub comment blocks for feature #44, and proposed publication target inputs.

scripts/ and generated folders

Gradle Tasks

Orchestrate build, validation, generation, conversion, and asset copying.

build.gradle

6.4. C4 Level 3: Build-Time Components

Diagram
Component Responsibility Current implementation

Gradle Build Orchestrator

Defines build tasks, task dependencies, source sets, output directories, and asset copying.

build.gradle

Architecture Metamodel Validator

Validates architecture artifact metadata, IDs, relations, and structural conventions.

COMP-002

Architecture Artifact Generator

Generates ADR, quality scenario, risk, chapter include, open-question, and traceability fragments.

COMP-004

Profile Metamodel Validator

Validates profile sidecar/frontmatter metadata, IDs, statuses, channels, and relations.

COMP-003

Profile Artifact Generator

Generates the profile artifact index from profile metadata.

COMP-005

Article Summary Generator

Generates LinkedIn/Substack-oriented HTML summary packs without publishing them into public site targets.

COMP-006

Publication Target Generator

Proposed component for deriving public, preview, and private article source sets and navigation from metadata.

COMP-001

6.5. COMP-001: Publication Target Generator

6.5.1. Responsibility

The Publication Target Generator is a build-time component that reads article sidecar metadata and creates target-specific AsciiDoc inputs for public, preview, and private site builds.

It owns:

  • status-based article selection;

  • generated article matrix/navigation fragments;

  • publication-target source lists for Gradle/Asciidoctor;

  • target-specific HTML metadata decisions such as canonical URL eligibility and noindex mode.

  • verification inputs that prove build/site does not contain preview or private articles.

It does not own:

  • article prose;

  • reviewer authentication;

  • private repository access control;

  • deployment credentials.

6.5.2. Inputs And Outputs

Kind Description

Input

.profile.yaml article metadata and corresponding .adoc source files

Input

publication target, for example public, preview, or private

Output

metadata-derived article matrix for the target

Output

source include/build list for the target

Output

target attributes for canonical and robots metadata handling

6.5.3. Target Rules

Target Article eligibility HTML metadata

Public

status: published and channels includes website

canonical links allowed, search indexing allowed

Preview

status: preview plus public context

no canonical links, noindex

Private

private repository content plus preview and published context

no canonical links, noindex

6.5.4. Implementation Guard

The public canonical-URL slice is authorized by accepted ADR-008 and may be implemented independently because it processes only already selected public output. Publication selection, preview/private targets, and promotion automation remain subject to ADR-009 and the still-missing first promotion check.

6.5.5. Integration Sketch

Diagram

6.6. COMP-002: Architecture Metamodel Validator

6.6.1. Responsibility

The Architecture Metamodel Validator checks architecture source artifacts before generated architecture fragments or rendered architecture pages are produced.

It owns:

  • required architecture metadata fields;

  • unique artifact IDs;

  • filename and ID consistency warnings;

  • relation key, relation type, relation status, and target validation;

  • detection of unusual bidirectional relations;

  • validation reports for architecture documentation changes.

It does not own:

  • rendered HTML output;

  • profile artifact metadata;

  • human acceptance of proposed architecture decisions.

6.6.2. Inputs And Outputs

Kind Description

Input

architecture AsciiDoc files with YAML front matter

Input

metamodel/artifact.schema.yaml

Input

metamodel/relations.schema.yaml

Output

validation report with errors and warnings

Output

failed Gradle task when architecture metadata is invalid

6.6.3. Implementation

Field Value

Script

scripts/validate-metamodel.rb

Gradle task

validateArchitectureMetamodel

Primary scope

src-content/docs/arc42, roadmap, vision/mission, and canvas docs

6.7. COMP-003: Profile Metamodel Validator

6.7.1. Responsibility

The Profile Metamodel Validator checks profile content metadata before profile indexes, website artifacts, CV output, README output, or article exports are generated.

It owns:

  • profile artifact required fields;

  • supported profile artifact types;

  • supported lifecycle statuses, including proposed preview and private;

  • supported publication channels;

  • profile artifact ID uniqueness;

  • relation key, relation type, relation status, and target validation.

It does not own:

  • article prose quality;

  • publication target filtering;

  • deployment authorization.

6.7.2. Inputs And Outputs

Kind Description

Input

*.profile.yaml sidecar metadata

Input

profile AsciiDoc files with YAML front matter

Input

metamodel/profile-artifact.schema.yaml

Output

validation report with errors and warnings

Output

failed Gradle task when profile metadata is invalid

6.7.3. Implementation

Field Value

Script

scripts/validate-profile-metamodel.rb

Gradle task

validateProfileMetamodel

Primary scope

src-content/profile

6.8. COMP-004: Architecture Artifact Generator

6.8.1. Responsibility

The Architecture Artifact Generator derives navigation and traceability fragments from validated architecture metadata.

It owns:

  • ADR, quality scenario, and risk indexes;

  • chapter include fragments for generated chapter assembly;

  • open-question index generation;

  • per-artifact traceability fragments;

  • the architecture-wide traceability matrix.

It does not own:

  • source architecture decisions;

  • acceptance of generated relations;

  • profile artifact indexes.

6.8.2. Inputs And Outputs

Kind Description

Input

validated architecture artifacts

Input

src-content/docs/doc-005-questions-and-answers.adoc

Output

ignored generated/ AsciiDoc fragments

Output

traceability matrix and per-artifact traceability fragments

6.8.3. Implementation

Field Value

Script

scripts/validate-metamodel.rb --generate

Gradle task

generateArchitectureArtifacts

Generated storage

**/generated/

6.9. COMP-005: Profile Artifact Generator

6.9.1. Responsibility

The Profile Artifact Generator derives a profile artifact index from validated profile metadata.

It owns:

  • collecting metadata-backed profile artifacts;

  • rendering a generated artifact index;

  • rendering per-article navigation includes (series and related articles);

  • rendering the article listings: a recent-articles fragment and the standalone overview pages for all articles, each tag, and each skill;

  • keeping generated profile documentation aligned with source metadata.

It does not own:

  • article lifecycle promotion;

  • architecture traceability generation.

6.9.2. Inputs And Outputs

Kind Description

Input

validated profile artifacts under src-content/profile

Output

generated profile artifact index

Output

per-article navigation includes next to each article

Output

article listing fragment under the articles generated/lists directory

Output

standalone article listing pages under the articles lists directory, written where they are published so the site build renders them as ordinary sources

6.9.3. Implementation

Field Value

Script

scripts/validate-profile-metamodel.rb --generate

Gradle task

generateProfileArtifacts

Generated file

src-content/profile/generated/profile-artifact-index.adoc

Generated listings

recent fragment plus all/tag/skill overview pages, rendered into the site by the buildArticleListPages Gradle task

6.9.4. Affected Features

Feature Description Status

Issue #34

Per-article navigation includes (previous/next series links and related articles) added to this generator.

implemented

Issue #37

Article index replaced by generated listings (recent, all, per tag, per skill) plus skill metadata, adding the article listing outputs to this generator.

implemented

6.10. COMP-006: Article Summary Generator

6.10.1. Responsibility

The Article Summary Generator creates paste-ready publication summary packs from source articles.

It owns:

  • reading article source and article metadata;

  • deriving summary content from the source article;

  • applying LinkedIn and Substack HTML templates;

  • writing generated summary files to build/summaries/;

  • keeping summary outputs out of public website and article export targets.

It does not own:

  • publishing summaries to external platforms;

  • inventing publication URLs;

  • changing article metadata or article status.

6.10.2. Inputs And Outputs

Kind Description

Input

article AsciiDoc files under src-content/profile/site/articles

Input

article *.profile.yaml sidecar metadata

Input

HTML templates under templates/article-summary-pack

Output

LinkedIn and Substack HTML files under build/summaries/

6.10.3. Implementation

Field Value

Script

scripts/generate-article-summaries.rb

Gradle task

generateArticleSummaries

Guard

verifyNoArticleSummariesInGeneratedOutputs

7. Runtime View

7.1. Build And Publish Scenario

Diagram

7.2. Failure Scenario

If metadata validation fails, Gradle stops before rendering or deployment. This keeps generated artifacts from drifting away from source truth.

7.3. DOC-06001: Private To Public Article Lifecycle Runtime

7.3.1. Status

In progress. The backlog epic is tracked as GitHub issue #26. The lifecycle is accepted in ADR-008. Public canonical metadata can proceed independently; private repository integration and promotion automation remain proposed in ADR-009, and no first promotion check has yet been performed.

7.3.2. Scenario 1: Draft Private Article

Diagram

The private site is a reading and review surface for the author. It is not a substitute for source privacy; private content remains in the private repository.

7.3.3. Scenario 2: Promote Article To Preview

Diagram

After promotion, preview content is public-source content. It is not official publication content, but it must no longer contain information that depends on private repository confidentiality.

7.3.4. Scenario 3: Publish Reviewed Article

Diagram

Publishing is an explicit metadata transition. The public target must not include preview or private articles.

7.3.5. Scenario 4: Reject Or Continue Private Draft

Diagram

If an article is not ready to become public-source preview material, it stays private. No generated preview or public artifact is expected.

8. Deployment View

Target Deployment mechanism Output path

Personal website

SFTP from GitHub Actions

build/site

Preview website

SFTP from GitHub Actions or local deploy script to a Basic Auth protected and non-indexed preview target

build/site-preview

Private article notebook

SFTP from private source build to a Basic Auth protected target

build/site-private

Architecture documentation

SFTP from GitHub Actions

build/architecture

GitHub profile README

Git commit from GitHub Actions

build/readme/README.md

GitLab profile README

Git push from GitHub Actions

build/readme/README.md

Personal CV PDF

Local build artifact, not deployed by default

build/cv/cv.pdf

The deploy workflow uses one GitHub Actions job per deployed target. A failed target job can be re-run independently without deploying targets that already succeeded.

8.1. Publication Target Deployment

Diagram

The publication targets are defined by accepted ADR-008. The diagram is included from the ADR so the decision basis and the official deployment view stay aligned.

8.2. Deployment Credentials

Target GitHub Actions configuration Stored in Required permission

GitHub profile README

PROFILE_REPO_TOKEN

Repository secret in dieterbaier/profile

Fine-grained GitHub token for dieterbaier/dieterbaier with Contents: Read and write

GitLab profile README

GITLAB_TOKEN

Repository secret in dieterbaier/profile

GitLab repository write access

Personal website

SFTP_PASSWORD, SFTP_HOST, SFTP_PORT, SFTP_USER, SFTP_REMOTE_BASE

SFTP_PASSWORD as production environment secret; SFTP_* values as production environment variables

SFTP write access to the configured site directory

Preview website

PREVIEW_SFTP_PASSWORD, PREVIEW_SFTP_HOST, PREVIEW_SFTP_PORT, PREVIEW_SFTP_USER, PREVIEW_SFTP_REMOTE_BASE, and .htpasswd material

Preview environment secrets and variables

SFTP write access to the configured preview site directory and Basic Auth configuration

Private article notebook

Private repository deployment secrets and .htpasswd material outside the public document root

Private repository or local deployment environment

SFTP write access to the configured private site directory

Architecture documentation

SFTP_PASSWORD, SFTP_HOST, SFTP_PORT, SFTP_USER, SFTP_REMOTE_BASE

SFTP_PASSWORD as production environment secret; SFTP_* values as production environment variables

SFTP write access to the configured architecture directory

Private contact data

SITE_EMAIL, SITE_ADDRESS_NAME, SITE_STREET, SITE_PLZ, SITE_CITY, SITE_TEL

production environment secrets in dieterbaier/profile

Value injection only; no repository or deployment permission

The GitHub profile README is deployed to a different repository than the workflow source repository. The default GITHUB_TOKEN is intentionally not used for this cross-repository push; it is scoped to the workflow repository. PROFILE_REPO_TOKEN therefore has to be rotated when it expires or when GitHub reports Bad credentials during the checkout github profile repo step.

Preview and private deployments are intentionally separated from the public website deployment and require Basic Auth. The public target emits canonical links for published pages from the deployment-specific SITE_BASE_URL; preview and private targets must emit noindex metadata and omit canonical links.

9. Crosscutting Concepts

9.1. Security

Private contact data and deployment credentials are injected from environment variables or GitHub Secrets. .env is local-only.

Deployment credentials are scoped by target system. PROFILE_REPO_TOKEN grants write access only to the GitHub profile README target repository. GITLAB_TOKEN grants write access to the GitLab profile README target repository. SFTP credentials are used only for private webspace deployment. This avoids one shared credential for all deployment targets and makes token rotation diagnosable from the failing workflow step.

9.2. Testing And Validation

Metadata validation runs before documentation rendering. The architecture validator follows the architecture-knowledge-toolkit metamodel. The profile validator checks profile artifact metadata, relations, and generated indexes.

9.3. Metadata Contract Version

Every profile artifact declares the version of the metadata contract it was written against, and the validator accepts only versions it knows. The accepted versions are declared once, as the metadata_version enum in metamodel/profile-artifact.schema.yaml; the validator reads them from there instead of holding a second list, and any build can read the same file without running the validator.

The set is read from the validator’s own checkout rather than from the tree it was pointed at. Content validated from elsewhere — the private repository reaching the shared tooling through a sibling checkout, as ADR-009 accepts — is judged by the contract of the tooling doing the validating, which is what makes a checkout older than the content beside it fail by name instead of passing.

An artifact that declares no version fails as a missing field. Treating absence as the initial version would let content say nothing about its contract and still pass, and the silent case is the one this field exists to remove.

The observable behaviour is specified in features/metadata-contract-version.feature and bridged to test/profile_metadata_version_test.rb.

9.4. Publication Selection

A publication target renders the article statuses that target allows, decided in ADR-008. The public target allows published and nothing else. An article of any other status is absent from build/site rather than merely absent from its listings, so it has no URL to reach.

Selection is by article metadata, never by where a file sits, so an article is kept out of a target by what it says about itself. The build asks the profile validator which sources a target excludes while it configures itself, because a render task resolves its sources before any generator runs; a list produced during the build would be missing exactly when a clean build needs it.

The same selection applies to the markdown export, since CI uploads the whole build directory as a workflow artifact of a public repository.

The selection fails closed. An article that must be kept out of a target and whose source file cannot be named stops the build, because a list that silently omits such an article looks complete while leaving out exactly what must not be rendered.

Two checks stand behind the selection rather than beside it. verifyPublicRenderTasksValidateMetadata asserts that every task rendering article sources reaches metadata validation through its dependencies, so the contract survives a refactoring instead of resting on a transitive edge. verifyPublicTargetHasOnlyPublishedArticles inspects the rendered result: an article that must not be public either has output there or it does not, whatever the build did with the selection.

The observable behaviour is specified in features/article-publication-status.feature and bridged to test/profile_publication_status_test.rb.

9.5. Private Content Target

A second target renders the private repository’s content locally, so a draft can be read as a finished page before anything is decided about it. It carries private, preview, and published, which is a wider status set than the public target and safe only because it is a wider set for a target that is deployed nowhere.

Both targets ask the same question of the same validator; only the answer differs, because the status set belongs to the target rather than to the mechanism. The private target renders to HTML alone: the markdown export exists so public articles can be published elsewhere, and a private draft is published nowhere.

The private repository holds content only. Its build reaches this repository’s tooling through a sibling checkout, resolved from PROFILE_PRIVATE_DIR or from the sibling path, and the build stops when that checkout carries a schema, validator, generator, or build of its own. The interface terms and theme come from this repository too, which is why the validator takes the includes tree as a parameter rather than assuming it sits inside the content root.

Every page of the target carries a robots noindex tag and no canonical link. The two belong together: a page that says it must not be indexed while also claiming to be the canonical location of its content states two opposite things, and the build refuses to be asked for both. Neither is a confidentiality control.

A checkout without the sibling is an ordinary state of this repository. Nothing about the private target may fail while the build configures itself, because that would break every Gradle invocation instead of the one task that needs the sibling.

The observable behaviour is specified in features/private-content-target.feature and bridged to test/profile_private_target_test.rb and test/private_checkout_contract_test.rb.

9.6. Observability

CI logs, Gradle task output, and validator reports are the operational feedback loop.

9.7. Error Handling

Failed validation or missing tooling stops the build. Missing publication URLs in AI-generated article summaries are represented as placeholders rather than invented values.

9.8. Optional Article Interaction

The GitHub-backed article-comments feature #44 keeps the website static. Article metadata generates a prefilled issue-form link into the dedicated dieterbaier/profile-artikelkommentare repository. Public-site deployment synchronizes an allowlist derived from published website articles. The comments workflow idempotently creates and assigns the fixed Artikelkommentar label, validates the submitted ID against that allowlist, and assigns the article-ID label. A local, framework-free script lists issues matching both labels, but contacts the GitHub API only after the reader explicitly requests the list. Failure or rate limiting affects only the optional list, never the article content. The observable behaviour is specified in features/article-comments.feature and bridged to test/profile_comments_test.rb.

9.9. Sustainability

Generated output is static. The public site avoids runtime backends, trackers, and unnecessary third-party scripts.

10. Architecture Decisions

10.1. ADR Index

ADR Title Status Notes

ADR-001

AsciiDoc as Primary Source

proposed

Use AsciiDoc as the primary source format for profile and architecture content.

ADR-002

Gradle Build Orchestration

proposed

Use Gradle tasks as the local and CI build orchestration layer.

ADR-003

Containerized Build Runtime

proposed

Use the docs-toolbox container as the reproducible CI runtime.

ADR-004

Pandoc Markdown Export

proposed

Use Pandoc via DocBook for README and article Markdown exports.

ADR-005

Minimal Static Site

proposed

Keep the public website static and avoid unnecessary third-party runtime behavior.

ADR-006

Profile Content Metamodel

proposed

Use a profile-specific metamodel, validator, and generator alongside the architecture metamodel.

ADR-007

Build-Generated Article Summaries

proposed

Generate article summaries as HTML build artifacts under build/summaries.

ADR-008

Private To Public Article Lifecycle

accepted

Use repository boundaries and metadata-driven publication targets for article drafting, preview, and public release.

ADR-009

Private Repository Integration

accepted

Treat the private repository as a separate source component of the profile application and integrate it through explicit content and promotion contracts.

ADR-010

Multilingual Content And Language Fallback

accepted

Resolve language variants and their fallback to the default language at build time in the generator instead of in AsciiDoc markup or at runtime.

ADR-011

Preview Visibility Without Authentication

accepted

Serve the preview target without authentication, keep it unreachable from the public site, and replace the access boundary with a visible notice on every article that is not published.

ADR-012

Reading Context of the Private Target

accepted

Render the private target from private source alone, with this repository’s theme and page chrome, and accept that links out of it do not resolve; collisions with public source are an error and the pipeline keeps non-public targets away from the public deployment.

10.2. ADR-001: AsciiDoc as Primary Source

10.2.1. Status

Proposed (derived).

10.2.2. Decision

Use AsciiDoc as the primary source format for profile and architecture content.

10.2.3. Context

This ADR was derived while bootstrapping the existing project to the architecture-knowledge-toolkit structure. The decision reflects existing repository practice and should be reviewed before being marked accepted.

10.2.4. Pugh Matrix

Criterion Keep existing practice Replace with custom tooling Manual process

Fit to Docs-as-Code

+1

0

-1

Build reproducibility

+1

?

-1

Maintainability

+1

0

-1

Sum

+3

?

-3

10.2.5. Consequences

  • Positive: The project keeps a coherent source-based publishing workflow.

  • Negative: Contributors need to understand the chosen toolchain.

  • Follow-up: Human review should confirm the derived status and rationale.

10.3. ADR-002: Gradle Build Orchestration

10.3.1. Status

Proposed (derived).

10.3.2. Decision

Use Gradle tasks as the local and CI build orchestration layer.

10.3.3. Context

This ADR was derived while bootstrapping the existing project to the architecture-knowledge-toolkit structure. The decision reflects existing repository practice and should be reviewed before being marked accepted.

10.3.4. Pugh Matrix

Criterion Keep existing practice Replace with custom tooling Manual process

Fit to Docs-as-Code

+1

0

-1

Build reproducibility

+1

?

-1

Maintainability

+1

0

-1

Sum

+3

?

-3

10.3.5. Consequences

  • Positive: The project keeps a coherent source-based publishing workflow.

  • Negative: Contributors need to understand the chosen toolchain.

  • Follow-up: Human review should confirm the derived status and rationale.

10.4. ADR-003: Containerized Build Runtime

10.4.1. Status

Proposed (derived).

10.4.2. Decision

Use the docs-toolbox container as the reproducible CI runtime.

10.4.3. Context

This ADR was derived while bootstrapping the existing project to the architecture-knowledge-toolkit structure. The decision reflects existing repository practice and should be reviewed before being marked accepted.

10.4.4. Pugh Matrix

Criterion Keep existing practice Replace with custom tooling Manual process

Fit to Docs-as-Code

+1

0

-1

Build reproducibility

+1

?

-1

Maintainability

+1

0

-1

Sum

+3

?

-3

10.4.5. Consequences

  • Positive: The project keeps a coherent source-based publishing workflow.

  • Negative: Contributors need to understand the chosen toolchain.

  • Follow-up: Human review should confirm the derived status and rationale.

10.5. ADR-004: Pandoc Markdown Export

10.5.1. Status

Proposed (derived).

10.5.2. Decision

Use Pandoc via DocBook for README and article Markdown exports.

10.5.3. Context

This ADR was derived while bootstrapping the existing project to the architecture-knowledge-toolkit structure. The decision reflects existing repository practice and should be reviewed before being marked accepted.

10.5.4. Pugh Matrix

Criterion Keep existing practice Replace with custom tooling Manual process

Fit to Docs-as-Code

+1

0

-1

Build reproducibility

+1

?

-1

Maintainability

+1

0

-1

Sum

+3

?

-3

10.5.5. Consequences

  • Positive: The project keeps a coherent source-based publishing workflow.

  • Negative: Contributors need to understand the chosen toolchain.

  • Follow-up: Human review should confirm the derived status and rationale.

10.6. ADR-005: Minimal Static Site

10.6.1. Status

Proposed (derived).

10.6.2. Decision

Keep the public website static and avoid unnecessary third-party runtime behavior. Optional integrations may use progressive enhancement when the core content and navigation work without them and the reader explicitly triggers any third-party request. The article-comments proof of concept follows this boundary: creating a comment navigates to a structured issue form in the dedicated dieterbaier/profile-artikelkommentare repository, while loading label-matched comment issues requires an explicit button click. This applies ADR-005 to feature #44.

10.6.3. Context

This ADR was derived while bootstrapping the existing project to the architecture-knowledge-toolkit structure. The decision reflects existing repository practice and should be reviewed before being marked accepted.

10.6.4. Pugh Matrix

Criterion Keep existing practice Replace with custom tooling Manual process

Fit to Docs-as-Code

+1

0

-1

Build reproducibility

+1

?

-1

Maintainability

+1

0

-1

Sum

+3

?

-3

10.6.5. Consequences

  • Positive: The project keeps a coherent source-based publishing workflow.

  • Positive: Reading an article does not automatically disclose the visit to the optional GitHub comments integration.

  • Negative: Contributors need to understand the chosen toolchain.

  • Negative: The optional comment list depends on GitHub availability and its unauthenticated API rate limit.

  • Follow-up: Human review should confirm the derived status and rationale.

10.7. ADR-006: Profile Content Metamodel

10.7.1. Status

Proposed (derived).

10.7.2. Decision

Use a profile-specific metamodel, validator, and generator alongside the architecture metamodel.

10.7.3. Context

This ADR was derived while bootstrapping the existing project to the architecture-knowledge-toolkit structure. The decision reflects existing repository practice and should be reviewed before being marked accepted.

10.7.4. Pugh Matrix

Criterion Keep existing practice Replace with custom tooling Manual process

Fit to Docs-as-Code

+1

0

-1

Build reproducibility

+1

?

-1

Maintainability

+1

0

-1

Sum

+3

?

-3

10.7.5. Consequences

  • Positive: The project keeps a coherent source-based publishing workflow.

  • Negative: Contributors need to understand the chosen toolchain.

  • Follow-up: Human review should confirm the derived status and rationale.

10.8. ADR-007: Build-Generated Article Summaries

10.8.1. Status

Proposed.

This ADR is AI-assisted and must not be treated as accepted architecture truth until reviewed and accepted by the accountable owner.

10.8.2. Decision

We propose to generate file-based article summaries as HTML build artifacts under build/summaries. Each summary filename uses the article filename stem plus summary<target> and the .html extension.

These summaries are generated writing aids. They are excluded from generated site and article-export targets, and generated public content must not link to them.

For example:

src-content/profile/site/articles/documentation/doc-as-code.adoc
build/summaries/doc-as-code_summary_linkedin.html
build/summaries/doc-as-code_summary_substack.html

10.8.3. Context

The project already treats profile article sources under src-content/profile/site/articles as product source. AI-assisted article summaries are paste-ready publication aids for LinkedIn and Substack.

An earlier proposal placed written summaries next to the article source. The current desired practice is different: summaries are generated whenever they are requested and written to build/summaries.

This keeps authored article sources clean, makes the workflow reproducible through Gradle, and avoids treating publication drafts as primary source.

10.8.4. Decision Drivers

  • Summary drafts should be reproducible through a build task.

  • Article source directories should contain only primary article sources and metadata.

  • Summary drafts should not become public generated content by accident.

  • Generated build output should remain separate from authored source.

  • The convention should stay simple enough for agents and humans to apply.

  • Future relocation should remain possible through a later ADR and contract update.

10.8.5. Considered Options

Option 1: Build-generated summary files

Generate each platform summary under build/summaries with the naming pattern <article-slug>_summary_<target>.html.

Option 2: Source-adjacent summary files

Store each platform summary next to the article with the naming pattern <article-slug>_summary_<target>.md.

Option 3: Nested build summary files

Store generated HTML summaries in nested directories such as build/summaries/<article-slug>/<target>.html.

Option 4: Central source summary directory

Store summaries under a source-controlled directory such as src-content/profile/site/article-summaries/<article-slug>/<target>.md.

10.8.6. Pugh Matrix

Criterion Build-generated files Source-adjacent files Nested build-directory files Central source directory

Reproducible generation

+1

-1

0

-1

Clean article source directories

+1

-1

+1

+1

Separation from public generated output

+1

+1

+1

+1

Directory simplicity

0

+1

-1

-1

Future relocation flexibility

0

0

0

0

Sum

+3

0

+1

0

10.8.7. Consequences

Positive
  • Summary drafts can be regenerated with ./gradlew generateArticleSummaries.

  • Article source directories stay focused on primary sources and metadata.

  • build/ remains the place for reproducible generated outputs.

  • The build verifies that generated site and article-export outputs do not contain summary files or summary<target> references.

Negative
  • Generated summary files are not visible in ordinary Git diffs unless copied or reviewed separately.

  • The summary generator must be maintained when the article format or supported publication targets change.

  • The verification task must continue to distinguish public generated outputs from build/summaries.

Neutral or Follow-Up
  • A future ADR may move summaries again if they become curated source artifacts.

  • Validators may later add checks for expected summary targets and naming.

10.8.8. Impact

Artifact Impact Rationale

QS-003: Public Artifact Consistency

addressed

Generated summaries remain traceable to article metadata and source files.

RISK-001-metadata-drift

mitigated

Regeneration reduces manual drift between source articles and publication aids.

ADR-004-pandoc-markdown-export

affected

Pandoc exports remain generated Markdown outputs, while summaries become generated HTML outputs.

10.8.9. Traceability

Relation Target Rationale

addresses

QS-003: Public Artifact Consistency

Supports consistency between generated publication aids and source articles.

mitigates

RISK-001-metadata-drift

Reduces hidden drift by regenerating summary artifacts from article source.

affects

ADR-004-pandoc-markdown-export

Clarifies that summary HTML is generated separately from Pandoc article Markdown exports.

10.8.10. Assumptions

  • Summary files are deterministic build artifacts by default.

  • AI-assisted refinements may still be created through the article-summary skill, but generated files remain under build/summaries.

10.8.11. Open Questions

  • Should future validation require one summary file for every supported target?

  • Should generated site or Markdown export tasks explicitly ignore all summary<target> files regardless of extension?

10.8.12. Review Notes

Confirm that generated summaries should stay build artifacts before changing this ADR to accepted or reviewed.

10.9. ADR-008: Private To Public Article Lifecycle

10.9.1. Status

Accepted.

The Basic Auth requirement this record states for the preview target is replaced by ADR-011. Everything else here stands, including Basic Auth for the private target.

10.9.2. Decision

We separate article privacy from article publication quality by combining repository boundaries with metadata-driven build targets.

Private drafts, notes, and protocols start in the private repository dieterbaier/profile-private. Reviewable but not official article content is promoted to the public profile repository with status: preview. Public deployment uses only metadata-selected status: published article content and derives canonical links from a deployment-specific base URL plus each generated output path. Preview and private deployments render their own target-specific sites without canonical links, with noindex metadata, and behind Basic Auth.

10.9.3. Context

The profile repository is public. Deployment protection such as .htaccess can protect generated HTML on a webspace, but it cannot protect source files that are already public in Git. Therefore, the privacy boundary must exist at source level before generated deployment targets are considered.

At the same time, not every unfinished article is confidential. After an article is cleaned enough for feedback, public source visibility is acceptable, while public website visibility and search indexing are still not desired.

This decision supports the lifecycle tracked as GitHub issue #26.

10.9.4. Decision Basis Diagrams

The following diagrams document the decision basis for the lifecycle. While this ADR is proposed, they remain close to the options and consequences. Official architecture views include these diagrams by tag so that the accepted decision can be reused without duplicating the model.

Draft Private Article Runtime
Diagram
Promote Article To Preview Runtime
Diagram
Publish Reviewed Article Runtime
Diagram
Reject Or Continue Private Draft Runtime
Diagram
Publication Target Deployment
Diagram

10.9.5. Options

Criterion Repository boundary plus metadata targets Only .htaccess protected preview/private targets Branch-only review in public repository

Protects truly private source content

+1

-1

-1

Keeps public website deterministic

+1

0

0

Supports rendered review before publication

+1

+1

0

Operational simplicity

0

+1

+1

Fits static hosting and Gradle pipeline

+1

+1

0

Sum

+4

+2

0

10.9.6. Consequences

Positive:

  • Private material is protected before it becomes source in a public repository.

  • Preview becomes a quality state, not a secrecy state.

  • Public website generation can be validated by metadata rules instead of source tree location alone.

  • preview.dieterbaier.eu and private.dieterbaier.eu can share the static-site toolchain while enforcing different content sets.

Negative:

  • A private repository and promotion workflow add operational overhead.

  • Cross-repository promotion can leak content if checks are weak; this is tracked by RISK-004.

  • Incorrect status handling can publish preview content unintentionally; this is tracked by RISK-005.

Neutral or follow-up:

  • Basic Auth is required for generated preview and private deployments, but it is not the source confidentiality mechanism.

  • A local promotion script should be preferred initially. A GitHub Action can be introduced once promotion checks are reviewed.

  • published is the canonical public lifecycle status.

  • The public canonical-URL slice is independent of private-source promotion: it operates only on already selected public output. The first promotion check remains required before promotion automation is implemented or used.

10.9.7. Review Notes

Confirmed on 2026-07-18:

  • the private source repository is dieterbaier/profile-private;

  • preview and private deployments require Basic Auth;

  • published is the canonical public lifecycle status;

  • the public canonical-URL slice may proceed independently of ADR-009 because it neither reads private source nor performs promotion.

Open follow-up:

  • No first promotion check has been performed. Promotion automation remains blocked until checks that prevent private-only content from entering the public repository are defined, implemented, and verified.

10.10. ADR-009: Private Repository Integration

10.10.1. Status

Accepted by the site owner.

10.10.2. Decision

We treat profile-private (dieterbaier/profile-private) as a separate source component of the same profile application, integrated through explicit content and promotion contracts instead of Git submodules or duplicated build logic.

The two repositories are checked out as siblings. The private build reaches the shared tooling through that sibling checkout and owns no copy of it. Private rendering and verification run locally; the private target needs no GitHub Actions deployment.

Promotion moves an article from the private repository to the public one rather than copying it, so that an article has exactly one authoritative source and no second place to edit it.

Move is the outcome, not a property of the operation. Two repositories cannot be written atomically: the promotion is a saga with an ordering, states it can stop in, and a recovery step. The Promotion Saga section below defines them.

The public repository remains the owner of shared build logic, validators, generators, theme assets, profile metadata contracts, and architecture documentation. The private repository owns private article, note, and protocol source content. Private content is rendered by reusing the public repository’s build tooling through a documented checkout or local path contract.

The private repository should not maintain separate architecture documentation unless it introduces architecture decisions that are not part of the overall profile application. It may contain local operational notes, but architecture truth stays in this repository.

10.10.3. Context

The private repository is not just backup storage. It is part of the profile publishing system because it contributes source content to the private site and can promote reviewable articles to the public repository. At the same time, its main architectural reason to exist is privacy: content that must not be public source cannot live in the public repository.

Git submodules could express a repository relationship directly, but they introduce extra workflow complexity, detached version management, and contributor friction. Treating the private repository as a completely independent product would keep boundaries simple, but it would likely duplicate generators, validation rules, theme assets, and deployment behavior.

This decision supports GitHub issue #26 and must be reviewed before implementation slices that create private repository automation.

10.10.4. Decision Basis Diagrams

The component model below documents the preferred repository integration option. The official building block view includes it by tag while the decision is still proposed, so later refinements can happen once at the decision source.

Public And Private Repository Components
Diagram

10.10.5. Options

Criterion Separate source component with explicit contracts Git submodule Independent private product Copy shared tooling manually

Keeps private source out of public Git history

+1

+1

+1

+1

Avoids duplicated generators and theme logic

+1

+1

-1

-1

Keeps day-to-day authoring simple

+1

-1

0

0

Makes coupling explicit and reviewable

+1

0

0

-1

Supports independent private deployment

+1

0

+1

0

Low operational surprise

+1

-1

0

-1

Sum

+6

0

+1

-2

10.10.6. Integration Contract

Contract Rule

Source ownership

Public source stays in profile; private source stays in profile-private.

Tooling ownership

Shared Gradle tasks, generators, validators, schemas, and theme assets are owned by profile-dieterbaier.

Private build input

The private repository provides content roots that match the public profile article metadata contract.

Tooling access

The two repositories are checked out as siblings. The private build uses the public checkout’s tooling directly; no submodule, no packaged version, no vendored copy.

No second implementation

The private repository holds no schema, validator, or generator of its own. Its build stops when it finds one.

Contract version

Every artifact declares metadata_version. The public validator declares which versions it accepts and rejects the rest, so content written against an older contract fails by name.

Promotion

A reviewed script validates an article against the public contract, then transfers its source and sidecar metadata from private to public source and sets status: preview. Validation precedes any write, and the transfer follows the promotion saga below. Its outcome is a move; its execution is not atomic.

Private deployment

Private rendering and verification run locally. No GitHub Actions deployment for the private target.

Architecture documentation

Architecture decisions stay in the public repository unless private-only implementation creates a separate architectural concern.

10.10.7. Promotion Saga

Two Git repositories cannot be written in one transaction. Promotion therefore has a defined order, and every point it can stop at is a state someone has to be able to recognize and finish.

The public repository leads. From the moment the promoted article lands there, it is the authoritative copy. Everything after that point is cleanup of the private side, never a source of truth.

The order follows from which failure is survivable. Removing from private first would put the article nowhere if the public write then fails. Writing to public first can only ever produce a duplicate, and a duplicate is recoverable while a loss is not.

Step Stops here means Recovery

Validate the article against the public contract

Nothing was written. The article is private and unchanged.

None needed. Fix the article and start over.

Write source and sidecar into public, status: preview

The public working tree holds an uncommitted addition.

Discard the working tree change and start over.

Commit and push public

The article is public and authoritative. The private copy still exists.

Continue with the private removal.

Remove the article from private

The private working tree holds an uncommitted deletion.

Discard or complete the deletion; either way the public copy stands.

Commit and push private

Promotion is complete.

None.

The incomplete state is duplication, and it is detectable. The same artifact id present in both repositories is the signal a resumed promotion looks for, which is why artifact IDs are stable and unique across the contract rather than per repository.

The signal is not conclusive on its own. A shared id can also mean a genuine collision — two different articles that ended up with the same identifier — and a resumption that trusted the id alone would overwrite public content with private content. Recognizing an interrupted promotion therefore means matching the public artifact against the private one being promoted, not matching identifiers. The three states a public id can represent, and their opposite responses, are specified in GitHub issue #85.

A promoted article is never rolled back by moving it home. If the article should not have been promoted, it is removed from the public repository like any other public content, and the public Git history keeps the record. Promotion is not reversible by re-running it backwards, because public history cannot be unpublished.

10.10.8. Preventing Contract Drift

Two rules carry the drift concern, and they divide the failure between them.

The no second implementation rule removes drift in the tooling rather than detecting it. A vendored copy keeps looking like the original after it has stopped being one, which is the property that made the divergence in metamodel/artifact.schema.yaml invisible; a repository that holds no copy cannot diverge in one.

The contract version rule covers what remains: a sibling checkout that is simply older than the content beside it. Enforcing metadata_version makes a stale contract fail with a name attached, and makes raising the version the deliberate act it should be when the contract changes.

The rule needs one place where the accepted versions are declared, and it is the metadata_version enum in metamodel/profile-artifact.schema.yaml. The validator reads it from there rather than carrying a second list, and reads it from its own checkout rather than from the tree it was pointed at, so a run against private content answers with the contract of the tooling doing the validating. A build that borrows this repository as its metamodel can read the same file to report what its checkout accepts.

Absence is not read as the initial version. An artifact that declares no metadata_version fails as a missing field, because a default would let content say nothing about its contract and still pass — and a checkout older than that content would then accept it silently, which is the mismatch this rule exists to catch.

Neither rule catches a contract change nobody versioned. That is a discipline gap rather than a mechanism gap, and no inexpensive check closes it.

10.10.9. Implementation Status

The decision holds; the rules it accepts are at different stages, and this section says which is which so the documentation does not claim protection that does not exist yet.

Rule State Where

Sibling checkout as the tooling access mechanism

implemented

buildSitePrivate in build.gradle, resolved from PROFILE_PRIVATE_DIR or the sibling path, #86

No second implementation in the private repository

implemented for the private build

checkPrivateCheckout stops the build when the checkout carries a schema, validator, generator, or build of its own, #86. The promotion checks still have to apply it, #84

metadata_version accepted-version set enforced

implemented

Declared in metamodel/profile-artifact.schema.yaml, enforced by scripts/validate-profile-metamodel.rb, #83

Promotion saga: validate, then public, then private

decided, not implemented

#84 and #85

Private deployment runs locally

decided, not implemented

#87

metadata_version is required on every artifact, and the validator rejects a version outside the accepted set by name. Both drift rules are in force, and the private build applies them: buildSitePrivate validates the private content root with this repository’s validator before rendering it, and reads the accepted versions from this checkout rather than from the tree beside the content.

What the private build does not yet do is run the generators against the private tree, so a private page carries no tags, no article navigation, and no series links. The generators write link registries into the includes tree, which for the private target is this repository’s — a generator run would write into the public checkout while rendering private content. That is a real coupling and not a detail of wiring, so it is left to a slice of its own rather than solved in passing.

This was the gate on private-source work. No slice that reads private source or promotes an article was to run before the accepted-version check existed, because a promotion validated against an unenforced contract is the exact failure this decision claims to prevent. #83 closed that gate; the remaining private slices are ordered by their own dependencies.

10.10.10. Consequences

Positive:

  • The privacy boundary is solved at source level without making the generated deployment the only control.

  • Shared generators and validators remain in one owner repository.

  • The private repository can be developed and deployed independently while still obeying the same metadata contracts.

  • The project avoids Git submodule complexity until there is concrete evidence that it is needed.

Negative:

  • The private repository build must locate the sibling public checkout, and fails without it.

  • A sibling checkout can lag behind the content beside it. The contract version rule turns that into a named failure rather than a silent one.

  • Local development requires two checkouts for private builds.

  • Promotion crosses a repository boundary and cannot be atomic. Its incomplete state is a duplicated article, which someone or something has to notice and finish. The saga puts that state on the recoverable side rather than removing it.

  • A promotion cannot be undone by reversing it. Once the public push has happened, the article is in public history, and withdrawing it is a public change rather than a return.

Neutral or follow-up:

  • A later packaging step may turn shared tooling into a reusable toolkit artifact if local path coupling becomes painful.

  • A Git submodule can be reconsidered if reproducible pinning becomes more important than simple authoring.

10.10.11. Review Outcome

The site owner confirmed the four open points at acceptance:

  • the private repository is dieterbaier/profile-private;

  • tooling access is a sibling checkout, not a packaged version and not a submodule;

  • private deployment runs locally, not in GitHub Actions;

  • drift is prevented by the no-second-implementation rule together with an enforced metadata_version.

The same review corrected promotion from a copy to a move.

A second review round sharpened two claims this record had overstated. A cross-repository move is not atomic, so the promotion is described as a saga with a leading repository, an order, its stop states, and their recovery. The metadata_version protection is decided but not yet built, so the Implementation Status section separates what holds from what runs.

10.11. ADR-010: Multilingual Content And Language Fallback

10.11.1. Status

Accepted.

The decision was drafted AI-assisted, then reviewed and accepted by the accountable owner with all open questions decided and all traceability relations confirmed.

10.11.2. Decision

The site becomes multilingual with de as the default language, and every language-dependent reference is resolved at build time in the profile artifact generator rather than in AsciiDoc markup or at page runtime.

Four resolution classes are distinguished, because they need different fallback semantics:

  • UI chrome (menu labels, table-of-contents title, comment section wording, footer): resolved through an attribute cascade, and required to be complete per language. A missing key fails the build.

  • Content fragments (reusable includes such as profile prose, CV blocks, contact data): resolved through per-language include trees and required to exist in the language of the including page. A fragment that is not available in the page language fails the build; fragments never fall back.

  • References to pages (menu links, footer links, article navigation, tag and skill listings): resolved through a generated per-language link registry. A missing translation falls back to the default-language page, the link is marked with the target language, and the generator warns.

  • Translation provenance (which variant is the original, and whether a translation still matches it): recorded in metadata, derived by the generator, overridable by the author, and rendered as a reader-facing note at the top of a translated page.

Fallback and hard failure are therefore deliberately split along the line of what a reader can make sense of. A link that leads to a German page is usable, so it falls back and says so. A German paragraph inside an English article is not, so it fails the build instead of producing a page in mixed languages.

Every language variant of an artifact is a profile artifact in its own right, with its own stable ID, its own sidecar metadata, and its own comment thread.

Default language and original language are deliberately separate concepts. de is the default language, which means it is the fallback target when a translation is missing. The original is the variant an artifact was authored in first, which may be any language. An article written in English and translated into German afterwards is therefore an English original with a German translation, while German remains the site’s fallback language.

10.11.3. Context

The site currently exists in German only. src-content/profile/includes/docheader.adoc already defaults :lang: to de, and article sources already use en for external links, so the language attribute exists but carries no structural meaning. All chrome is hard-coded German: menue.adoc contains German labels and hard-coded relative page paths, docheader.adoc sets :toc-title: Inhalt, includes/docinfo/docinfo-footer.html links to {basedir}/legal.html, and src-content/theme/article-comments.js carries German status strings.

The authoring concept relies on reusable includes. Writing an article in another language therefore does not only mean translating the article body; the surrounding generated chrome must switch language as well, and the references inside it must point at same-language pages where those exist.

The core constraint is that AsciiDoc has no file-existence conditional. ifdef and ifeval evaluate attributes only, and opts=optional expresses "include or skip", not "include A, otherwise B". A language-aware include or xref cannot be expressed in portable AsciiDoc markup. Only the build knows which language variants exist, and the profile artifact generator (scripts/validate-profile-metamodel.rb) already owns exactly that knowledge: it reads every .profile.yaml, and it already writes per-article navigation, tag, and comment includes plus the article listings.

The comment integration adds a second constraint. Comment threads are keyed by article ID, and generateProfileArtifacts synchronizes an allowlist of published article IDs to the comments repository. Any language model that does not produce a stable ID per language variant would break that contract.

10.11.4. Decision Drivers

  • Authoring must stay previewable in a plain AsciiDoc editor, without a custom build extension.

  • A page reference must never lead nowhere, whether the translation exists or not.

  • A published page must never mix languages in its body text.

  • The reader must be able to tell when a reference leads to another language.

  • Language selection must not require a backend, a redirect layer, or client-side negotiation.

  • Adding a translation must not require manual maintenance of link tables or duplicated chrome.

  • Missing translations must be detectable by validation, not by reading the rendered site.

  • A reader must be able to tell that a page is a translation, which variant it was translated from, and whether it still matches that original.

10.11.5. Considered Options

Option 1: Build-time resolution in the profile artifact generator

The generator resolves language and emits per-language artifacts: a link registry, per-language navigation, listings, comment blocks, and provenance notes. Markup contains only attribute references that always resolve. UI terms use a plain AsciiDoc attribute cascade, since later attribute assignments override earlier ones. Fragment availability is validated rather than filled in.

Option 2: Custom Asciidoctor include processor and inline macro

A Ruby extension registered in the Gradle build handles a custom scheme such as include::i18n:profile/profile.adoc[] and implements the fallback chain in code. No generated stub files are needed.

Option 3: Runtime language negotiation

The published site keeps a single page set and selects language in the browser or in the hosting layer, for example through JavaScript string swapping or server-side rewrites.

Option 4: Fully duplicated language trees without fallback

Each language maintains its own complete copy of chrome, fragments, and pages. No fallback exists; an untranslated page simply does not exist in that language.

10.11.6. Pugh Matrix

Criterion Build-time resolution in generator Asciidoctor extension Runtime negotiation Duplicated trees

Editor preview works without custom tooling

+1

-1

0

+1

References always resolve

+1

+1

0

-1

Fallback is visible and reviewable

+1

0

-1

-1

Keeps the site static (QS-004)

+1

+1

-1

+1

Low authoring effort per translation

+1

+1

0

-1

Missing translations detectable by validation

+1

0

-1

-1

Fits the existing generator responsibility

+1

0

-1

0

Sum

+7

+2

-4

-2

Option 2 loses mainly on tooling: a custom include processor is invisible to the IntelliJ AsciiDoc preview, which breaks the authoring loop for the person who writes the content. Option 3 contradicts ADR-005: Minimal Static Site. Option 4 pushes drift detection to the reader.

10.11.7. Language Contract

Contract Rule

Default language

de. Where fallback applies, it always resolves to the default language, never to an error page or an empty include.

Language declaration

Each artifact declares :lang: in its AsciiDoc source and language: in its .profile.yaml. The profile validator rejects a mismatch between the declared language and the file location.

Source and URL layout

The default language stays at the site root permanently; it is not moved into a /de/ subtree. Each additional language mirrors the structure under a language subtree, for example src-content/profile/site/en/articles/…​ published as /en/articles/…​.

Artifact identity

Each language variant is its own artifact with its own ID, for example ART-003-doc-as-code and ART-003-doc-as-code-en.

Original and translation

An artifact without translation_of is an original. An artifact with translation_of: <artifact-id> is a translation of that original, in any language direction. The original does not need to be in the default language.

Translation freshness

A translation records the digest of the original’s full source content at translation time. The generator recompares it against the current original and marks the translation as outdated on mismatch. An outdated translation stays publishable; it is never a build failure.

Author override

The derived freshness state is a default, not a verdict. The author can re-accept the current original without changing the translation, which clears the outdated state and suppresses its note. This is the intended answer to a cosmetic change in the original, such as a typo fix.

Intentional divergence

Independently of freshness, a translation can declare that it deliberately differs from its original. The declaration is shown to the reader even when the translation is in sync.

Reader-facing provenance

The generator renders a note at the top of every translated page, in the page language: "Das ist eine Übersetzung vom Originalartikel …" / "This is a translation of the original …", linking to the original. Outdated state and declared divergence are shown as additional notes and may appear together. The note is emitted as a generated per-article include, like the existing tag and comment includes.

Language switcher

The switcher is present on a page as soon as the artifact exists in more than one language, and offers the languages in which the artifact really exists.

CV outputs

The CV follows the same language model as site pages, and the PDF build produces one PDF per CV language variant.

UI chrome

includes/i18n/ui-de.adoc defines all keys and is always included first; includes/i18n/ui-<lang>.adoc is included afterwards and overrides. The validator enforces key parity and fails the build on a missing key.

Content fragments

Fragments live under includes/i18n/<lang>/. A page may only include fragments available in its own language. A missing fragment translation fails the build, naming the fragment, the page, and the language. Fragments do not fall back, because a fallback would produce a page in mixed languages.

Page references

Chrome and content reference pages through generated attributes such as {url_articles}, never through hard-coded paths. A companion attribute such as {url_articles_lang} carries the resolved language. A link that falls back to the default language is marked with the target language behind the link text, for example Curriculum Vitae (de).

Build failures

Missing UI key for a configured language; fragment not available in the page language; unresolvable link registry key; metadata violations such as an unknown translation_of target or a language that contradicts the file location.

Warnings

Page references that fall back to the default language because no translation exists, reported per generated language page; untranslated fragments that no page includes yet, reported as translation coverage.

Comments

Each language variant gets its own comment thread, keyed by its own artifact ID. The published-ID allowlist synchronization is unchanged and simply covers more IDs.

HTML metadata

scripts/inject-site-metadata.rb additionally emits rel="alternate" hreflang links per translation group and x-default pointing at the default language, alongside the existing canonical link.

Client-side strings

JavaScript in src-content/theme reads its user-visible strings from data- attributes emitted by the generator instead of carrying literals.

10.11.8. Consequences

Positive
  • A page reference never leads nowhere; the worst case is a default-language page, explicitly marked as such.

  • A published page is never a mix of languages, because the build refuses to produce one.

  • AsciiDoc sources stay standard: attribute references and ordinary includes, previewable in any editor.

  • The generator already owns per-article generated includes and listings, so the language dimension extends an existing responsibility instead of adding a new component.

  • Fragment resolution needs no generated stub files at all: a fragment either exists in the page language or the build stops, which keeps the include trees free of machine-written filler.

  • Language selection produces plain static files with no runtime negotiation, preserving QS-004: Minimal Static Site.

  • Existing default-language URLs remain unchanged, so canonical URLs and the comment allowlist keep working.

  • Translation provenance is explicit for the reader instead of implicit, and staleness is detected mechanically rather than remembered by the author.

  • Because the original is declared per artifact rather than fixed to the default language, articles can be authored in any language without inverting the site’s fallback rules.

Negative
  • The generator, the profile validator, and the profile metamodel all grow a language dimension, and the generated file count multiplies with the number of languages.

  • Language variants can drift apart in content while both remain publishable, which increases RISK-001: Metadata Drift.

  • Hard-coded paths in menue.adoc and docinfo-footer.html and the German literals in docheader.adoc and article-comments.js must be migrated before the first non-default page is published.

  • Discussion about one article is split across language threads.

  • Translation freshness depends on a recorded digest that must be re-accepted deliberately, which adds a step to the translation workflow.

  • Because the digest covers the full source, a cosmetic edit to an original marks its translations as outdated even when the meaning did not change. The author override exists precisely for this case, but it must be exercised.

  • A page cannot be published in a new language before every fragment it includes is translated. This is the intended trade-off against mixed-language pages, but it makes fragment-heavy artifacts such as the CV an all-or-nothing translation effort rather than an incremental one.

Neutral or Follow-Up
  • Translation drift is covered by RISK-001: Metadata Drift; no dedicated risk artifact is created for it.

  • The language: mixed value, used by nine profile artifacts, is resolved to a concrete language when the metamodel gains the language dimension. The value disappears from the vocabulary: a page may only include fragments of its own language, so an artifact is always written in exactly one language.

  • The placement of the language switcher in the chrome is a presentation detail left to implementation.

  • Fragment granularity becomes a design lever: the finer the fragments, the smaller the unit that blocks a translated page from being published.

10.11.9. Assumptions

  • The site is served from the domain root, so root-relative URLs in the generated link registry are valid for the site build while the existing ifdef::buildsite[] branch keeps relative paths for PDF and local builds.

  • Language-dependent attributes are not set through the Gradle attributes(…​) map, because API-set attributes are locked and could not be overridden by :lang: in a document.

  • English is the first additional language; no right-to-left language is planned.

10.11.10. Open Questions

None. The questions raised while drafting this ADR were decided by the accountable owner and folded into the language contract above: outdated translations stay publishable with a visible note, the digest covers the full article source, freshness and divergence notes may appear together, the author can override the derived freshness state, and the language switcher appears whenever an artifact exists in more than one language.

10.11.11. Review Notes

Reviewed and accepted by the accountable owner. The following were confirmed during review:

  • the language subtree layout and the -<lang> ID suffix convention for translated artifacts;

  • translation drift stays covered by RISK-001 without a dedicated risk artifact;

  • the reader-facing wording for a translated page and the (de) marker behind a link that falls back;

  • the split between build failures and warnings recorded in the language contract.

Implementation is tracked in GitHub issue #47 and its slices.

10.12. ADR-011: Preview Visibility Without Authentication

10.12.1. Status

Accepted by the site owner.

10.12.2. Decision

The site owner decides that preview.dieterbaier.eu serves the preview target without authentication. Preview pages carry noindex metadata and no canonical link, and every article whose status is not published carries a visible notice saying that it is not finished.

The public site never links to the preview target. No page, listing, navigation entry, related-article suggestion, or link registry entry generated into dieterbaier.eu points at preview.dieterbaier.eu. A preview URL is something a reviewer is handed, not something a reader can arrive at.

Basic Auth stays on the private target, where it protects content whose source is not public.

10.12.3. What This Replaces In ADR-008

ADR-008 remains accepted. Three statements in it are replaced by this record:

  • its Decision section, where preview and private deployments are described as rendering "without canonical links, with noindex metadata, and behind Basic Auth" — for the preview target the last clause no longer holds;

  • its consequence "Basic Auth is required for generated preview and private deployments";

  • its Review Notes entry confirming that "preview and private deployments require Basic Auth".

ADR-008’s own publication target deployment diagram already shows the preview target as noindex, no canonical with no Basic Auth, and names Basic Auth on the private target only. The diagram agrees with this record; the prose did not.

Everything else in ADR-008 stands, including the repository boundary, the metadata-driven targets, and Basic Auth for the private target.

10.12.4. Context

The preview target is built from the public repository. An article reaches it by being promoted into public source, which means its text is already readable in Git by anyone before any page is deployed. Authentication in front of the rendered pages therefore guards a copy of something already published in another form.

What the preview target does need is that its pages are not mistaken for finished work: not indexed, not canonical, and not read as a statement the author stands behind. The first two are technical and invisible to a reader. The third is what a password was standing in for, badly — a credential prompt says "you may not read this", which is not the message, and once a reviewer has the credential the prompt says nothing at all.

Reachability is a separate question from indexing. noindex keeps a preview page out of search results; a link from the public site would put it in front of readers who never searched for it, and would make an unfinished article part of the site’s navigable surface. The two rules cover the two ways a reader arrives.

The private target is a different case and keeps its access boundary. Its source is not public, so the deployment is the only place its content could leak.

10.12.5. Options

Criterion Open preview with noindex and a visible notice Basic Auth in front of the preview target Unguessable preview URLs

Protects something not already public

0

-1

-1

Tells a reader the article is unfinished

+1

-1

-1

Keeps the page out of search results

+1

+1

+1

Lets a reviewer read it without a credential handshake

+1

-1

+1

Honest about what it protects

+1

-1

-1

Low operational overhead

+1

-1

0

Sum

+5

-4

-1

Basic Auth ties the chosen option on exactly one criterion — keeping the page out of search results — and noindex achieves that without it. On the other five it loses. It costs credential handling, rotation, and a handshake with every reviewer, and it buys protection for content that is public in source. It also misleads: a reader who gets past it reasonably concludes the page was worth protecting.

Unguessable URLs lose for the same reason without the operational cost. Obscurity that is described as protection is the more dangerous of the two, because nothing about it fails visibly.

10.12.6. Consequences

Positive:

  • No credentials to issue, rotate, or revoke for review; a preview link is shareable as a link.

  • The signal a reader needs is on the page, where a reader can act on it, instead of in HTTP metadata that only a crawler reads.

  • The preview target’s access rule now matches ADR-008’s deployment diagram, so the two no longer disagree.

  • The no-link rule needs little new machinery. Public listings, navigation, and related-article suggestions are generated from metadata and already carry published articles only, so a preview article has no generated route into the public site.

Negative:

  • An unfinished article is readable by anyone holding its URL. This raises RISK-005 on the human-reader side; noindex addresses only crawlers.

  • The notice becomes load-bearing. If it is missing, wrong, or unreadable in one language, nothing else tells the reader what they are looking at.

  • The no-link rule holds for generated links by construction, but a hand-written link in article prose is outside that guarantee and needs a check of its own.

Neutral or follow-up:

  • The notice is a signal, not a control. Nothing about preview visibility may be relied on for confidentiality; content that must not be readable does not belong in public source, which is ADR-008’s rule and is unchanged.

  • Wording, per-status text, and language parity for the notice are implementation work tracked as GitHub issue #88.

10.12.7. Review Outcome

The site owner raised the contradiction, confirmed that the deployment diagram and not the prose states the intent, and answered both open points at acceptance:

  • a preview page carrying noindex, no canonical link, and a visible notice is sufficient for the review use case;

  • the preview target must never be reachable from the public site, which is why the no-link rule is part of the decision rather than a follow-up.

10.12.8. Implementation Status

The decision holds; the mechanisms that carry it are at different stages.

Rule State Where

Preview served without authentication

decided

Nothing to implement; the preview deployment is #79

noindex, no canonical link on preview pages

decided, not implemented

#78

No generated link from the public site to preview

implemented

Public listings, navigation, and related entries carry published only

No hand-written link from the public site to preview

decided, not implemented

Checked as part of #79

Visible notice on every article that is not published

decided, not implemented

#88

Until the notice exists, a preview page carries no reader-facing signal at all. The preview deployment therefore waits for #88: the decision removes an access boundary and puts the notice in its place, so deploying the one without the other would leave an unfinished article publicly readable with nothing saying so. This is a blocking dependency of #79, recorded there and in the epic rather than only here.

10.13. ADR-012: Reading Context of the Private Target

10.13.1. Status

Accepted by the site owner on 2026-08-04, after the first private render in GitHub issue #86 raised the question.

10.13.2. Decision

The private target is rendered from the private content root alone, with this repository’s theme, includes, and page chrome. A draft is read as a styled page, and links that leave it — start page, CV, legal notice, article listings — do not resolve, because their targets are not in this repository.

That is accepted rather than worked around. If it turns out to obstruct reading, this record is superseded rather than amended.

Two rules come with it:

  • A collision between private and public source is an error, reported as early as it can be seen rather than when an article is promoted.

  • The pipeline keeps non-public targets away from the public deployment. The safeguard is in CI, not in the shape of the rendered output.

10.13.3. Context

ADR-009 gives the private repository content and nothing else. The first private render therefore produced what the private repository contains: three article pages and the theme, with no site around them. Every link in the page chrome points at a file that is not in the target.

The target exists so a draft can be read as a finished page before anything is decided about it. The open question was whether "as a finished page" requires the finished site around it.

The preview target does not have this problem, and stating that here keeps it from being solved twice. Promotion moves an article into the public repository and sets status: preview there. build/site-preview is built from public source only, as #78 specifies, so it already contains the whole public site plus the preview articles. Only the private target reads from a tree that holds articles alone.

10.13.4. Options

Criterion Private content only Compose public site and private content Generated minimal shell Read only after promotion to preview

A draft is read as the page it will become

-1

+1

0

+1

Links in the site chrome resolve

-1

+1

0

+1

An unfinished article is not published in order to read it

+1

+1

+1

-1

A draft is read without leaving the private repository

+1

+1

+1

-1

No invented structure that matches no real site

+1

+1

-1

+1

Cost of one private render

+1

-1

0

+1

Failure modes are easy to reason about

+1

0

-1

+1

Sum

+3

+4

0

+3

Private content only is what #86 built and what this record accepts. It is the cheapest and the most predictable, and its output is plainly not a site.

Compose public site and private content renders both trees into one target. It scores one point higher, and the point it wins is the reading context.

Generated minimal shell would produce just enough index and menu targets for the links to resolve. It invents a structure that matches no real site, so a draft would be read inside a fourth thing that exists only in this target.

Read only after promotion to preview removes the private reading target entirely. It scores well until the criterion that matters: the article would be publicly reachable — ADR-011 removed the access boundary in front of preview — before its author had read it as a page once.

10.13.5. Why The Matrix Did Not Decide It

The chosen option is not the highest-scoring one, and the difference is a single point on seven criteria. The owner weighed the two criteria that separate them — reading context against cost and predictability — and judged the broken chrome tolerable for a target read by the person who wrote its content and knows why the links fail.

The matrix is kept as it was scored rather than re-weighted to agree with the outcome. A matrix adjusted until it produces the decision already made records nothing.

10.13.6. Review Outcome

The site owner accepted the decision and answered all three open questions:

  • Collision rule: error, as early as possible. The alternative — discovering it when an article becomes reviewable or public — puts the collision at the moment the article is being written into the public repository, which is the worst time to find it. The owner is the only author, so a collision is a mistake rather than a coordination event; if several people ever draft here, the same check is what makes that safe. Implemented by #94.

  • Public articles in the private listings: accepted. The private status set includes published, so they appear. Nothing about that needs a rule.

  • Protection against deployment: in the pipeline. The owner does not regard the merged-looking output as the danger. What must hold is that CI never publishes the private or the preview target to the public destination, which is #95.

10.13.7. Consequences

Positive:

  • The private build stays cheap: three drafts render without rendering the whole public site.

  • The output is obviously not a site, which is itself a safeguard against mistaking it for one.

  • No structure is invented for this target, so nothing in it can drift from a site that exists.

  • The collision rule moves a class of promotion failure to the point where the draft is written instead of the point where it is published.

Negative:

  • Links out of a draft do not resolve, and the reader has to separate the draft’s defects from the target’s.

  • The private reading and the later preview reading have different shapes, so what the private read shows about page context carries over only partly.

  • The safeguard against publishing a non-public target now lives in CI, away from the target it protects. A pipeline change is what would break it, and that is the thing #95 has to make visible.

10.13.8. Implementation Status

The decision holds; the rules it accepts are at different stages.

Rule State Where

Private target rendered from private source alone

implemented

buildSitePrivate in build.gradle, #86

Theme, includes, and page chrome come from this repository

implemented

--includes-dir in scripts/validate-profile-metamodel.rb and the includesdir attribute, #86

Collision between private and public source is an error

decided, not implemented

#94

The pipeline keeps non-public targets out of the public deployment

decided, not implemented

#95

Until #94 closes, a private draft can carry an artifact id or an output path that a public artifact already uses, and nothing says so until the article is promoted. Until #95 closes, no check stops a workflow edit from pointing the public deployment at a non-public target.

11. Quality Requirements

11.2. Quality Goals

11.2.1. Quality Goal Index

ID Priority Quality goal Meaning

QG-001

1

Reproducibility

Same source and environment produce the same generated artifacts.

QG-002

2

Maintainability

Content, presentation, metadata, and build logic have explicit responsibilities.

QG-003

3

Automation

Normal publishing and validation run without manual transformation steps.

QG-004

4

Correctness

Generated public artifacts reflect the intended source content and metadata.

QG-005

5

Sustainability

The site and pipeline avoid unnecessary runtime logic, tracking, and waste.

11.2.2. QG-001: Reproducibility

Meaning

Same source and environment produce the same generated artifacts.

Priority

1

Quality Scenarios
ID Scenario Response measure

QS-001

Reproducible Build

The same task succeeds twice without source changes

11.2.3. QG-002: Maintainability

Meaning

Content, presentation, metadata, and build logic have explicit responsibilities.

Priority

2

Quality Scenarios

No quality scenarios currently refine this quality goal.

11.2.4. QG-003: Automation

Meaning

Normal publishing and validation run without manual transformation steps.

Priority

3

Quality Scenarios

No quality scenarios currently refine this quality goal.

11.2.5. QG-004: Correctness

Meaning

Generated public artifacts reflect the intended source content and metadata.

Priority

4

Quality Scenarios
ID Scenario Response measure

QS-002

Profile Metadata Validity

Validator exits non-zero on missing required fields or broken relations

QS-003

Public Artifact Consistency

Generated indexes list every metadata-backed article and project

QS-005

Publication Visibility Boundary

build/site contains no article whose metadata status is not published; build/site-preview and build/site-private contain no canonical links and contain a noindex robots directive on generated HTML pages

11.2.6. QG-005: Sustainability

Meaning

The site and pipeline avoid unnecessary runtime logic, tracking, and waste.

Priority

5

Quality Scenarios
ID Scenario Response measure

QS-004

Minimal Static Site

No backend call is required for core content display

11.3. Quality Scenarios

11.3.1. Quality Scenario Index

ID Quality goal Source Stimulus Artifact Environment Response Response measure

QS-001

QG-001-reproducibility

Developer or CI runner

runs a build from unchanged sources

Gradle build pipeline

normal build environment

generates equivalent output artifacts

The same task succeeds twice without source changes

QS-002

QG-004-correctness

Contributor

changes profile metadata

profile artifact metadata

pre-render validation

invalid metadata stops the build

Validator exits non-zero on missing required fields or broken relations

QS-003

QG-004-correctness

Reader

opens generated website, CV, README, or article export

generated artifact

after CI deployment

content reflects the source artifact

Generated indexes list every metadata-backed article and project

QS-004

QG-005-sustainability

Website visitor

loads a public page

website output

normal browsing

page works without application backend or trackers

No backend call is required for core content display

QS-005

QG-004-correctness
DOC-01000-introduction-and-goals

Site owner

changes an article status or promotes an article between repositories

generated public, preview, and private site targets

during local build or CI build before deployment

each publication target contains only the statuses allowed for that target; preview and private targets emit noindex metadata and omit canonical links

build/site contains no article whose metadata status is not published; build/site-preview and build/site-private contain no canonical links and contain a noindex robots directive on generated HTML pages

11.3.2. QS-001: Reproducible Build

Scenario
Element Description

Source

Developer or CI runner

Stimulus

runs a build from unchanged sources

Artifact

Gradle build pipeline

Environment

normal build environment

Response

generates equivalent output artifacts

Response Measure

The same task succeeds twice without source changes.

11.3.3. QS-002: Profile Metadata Validity

Quality Goal
Scenario
Element Description

Source

Contributor

Stimulus

changes profile metadata

Artifact

profile artifact metadata

Environment

pre-render validation

Response

invalid metadata stops the build

Response Measure

Validator exits non-zero on missing required fields or broken relations.

11.3.4. QS-003: Public Artifact Consistency

Quality Goal
Scenario
Element Description

Source

Reader

Stimulus

opens generated website, CV, README, or article export

Artifact

generated artifact

Environment

after CI deployment

Response

content reflects the source artifact

Response Measure

Generated indexes list every metadata-backed article and project.

11.3.5. QS-004: Minimal Static Site

Quality Goal
Scenario
Element Description

Source

Website visitor

Stimulus

loads a public page

Artifact

website output

Environment

normal browsing

Response

page works without application backend or trackers

Response Measure

No backend call is required for core content display.

11.3.6. QS-005: Publication Visibility Boundary

Quality Goal
Scenario
Element Description

Source

Site owner

Stimulus

changes an article status or promotes an article between repositories

Artifact

generated public, preview, and private site targets

Environment

during local build or CI build before deployment

Response

each publication target contains only the statuses allowed for that target; preview and private targets emit noindex metadata and omit canonical links

Response Measure

build/site contains no article whose metadata status is not published; build/site-preview and build/site-private contain no canonical links and contain a noindex robots directive on generated HTML pages.

Assumptions

The public half of the response measure is verified: build/site is rendered from a metadata-derived source set that carries published only, specified in features/article-publication-status.feature and bridged to test/profile_publication_status_test.rb.

The preview and private half is not. Those targets do not exist yet, so the noindex and canonical-link part of the measure has nothing to verify against. The mechanism for it is still open and may become a Gradle verification task that inspects generated HTML.

12. Risks and Technical Debt

12.1. Risks

ID Risk Probability Impact Priority Mitigation/action

RISK-001

Metadata Drift

Medium

Medium

Medium

-

RISK-002

Build Toolchain Drift

Medium

High

High

-

RISK-003

Private Data Exposure

Low

High

Medium

-

RISK-004

Source Boundary Leakage

Medium

High

High

QS-005-publication-visibility-boundary

RISK-005

Preview Mispublication

Medium

Medium

Medium

QS-005-publication-visibility-boundary

12.2. RISK-001: Metadata Drift

12.2.1. Risk Statement

Because content and metadata evolve separately, generated indexes may become incomplete, leading to misleading public artifacts.

12.2.2. Assessment

Field Value

Likelihood

Medium

Impact

Medium

Priority

Medium

Timeframe

medium-term

Confidence

medium

12.2.3. Mitigation Options

  • Validate metadata before build and generate indexes from source metadata.

12.3. RISK-002: Build Toolchain Drift

12.3.1. Risk Statement

Because local and CI tool versions may diverge, builds may become non-reproducible, leading to deployment failures.

12.3.2. Assessment

Field Value

Likelihood

Medium

Impact

High

Priority

High

Timeframe

medium-term

Confidence

medium

12.3.3. Mitigation Options

  • Use containerized builds and expose environment checks as Gradle tasks.

12.4. RISK-003: Private Data Exposure

12.4.1. Risk Statement

Because CV builds can inject private contact data, secrets may be exposed if environment handling is wrong.

12.4.2. Assessment

Field Value

Likelihood

Low

Impact

High

Priority

Medium

Timeframe

medium-term

Confidence

medium

12.4.3. Mitigation Options

  • Keep private data in environment variables and exclude local .env files from version control.

12.5. RISK-004: Source Boundary Leakage

12.5.1. Risk Statement

Because private drafts and public preview content are promoted across repository boundaries, private-only source material may be copied into the public repository, leading to irreversible disclosure through public Git history.

12.5.2. Assessment

Field Value

Likelihood

Medium

Impact

High

Priority

High

Timeframe

near-term

Confidence

medium

12.5.3. Mitigation Options

  • Keep truly private content in a private repository until it is intentionally promoted.

  • Start with a local promotion script so the owner reviews the diff before push.

  • Add promotion checks for private-only markers, disallowed paths, and status transitions.

  • Treat preview as reviewable public-source content, not as confidential content.

  • If private material enters public Git history, rotate affected secrets and rewrite history only after an explicit incident decision.

12.6. RISK-005: Preview Mispublication

12.6.1. Risk Statement

Because public, preview, and private builds share article sources and build tooling, preview articles may be included in the public website or indexed by search engines, leading to premature or misleading publication.

12.6.2. Assessment

Field Value

Likelihood

Medium

Impact

Medium

Priority

Medium

Timeframe

near-term

Confidence

medium

12.6.3. Mitigation Options

  • Generate article matrices and source lists from metadata instead of maintaining public links manually.

  • Add a verification task that fails if build/site contains non-published articles.

  • Add a verification task that fails if preview/private HTML contains canonical links or lacks noindex.

  • Keep deployment jobs target-specific so preview artifacts cannot be uploaded to the public webspace by a reused deploy step.

13. Glossary

Term Definition

Profile artifact

A content source that contributes independently meaningful personal profile knowledge.

Sidecar metadata

YAML metadata stored next to include-heavy AsciiDoc fragments to avoid rendering metadata as content.

Generated fragment

Derived AsciiDoc output produced by a generator and not edited manually.

14. Appendix

14.1. Questions and Answers

14.1.1. Q-001 Is the profile metamodel accepted?

Status: open.

The bootstrap derives a profile metamodel from the existing content and the architecture-knowledge-toolkit pattern. A human review should confirm artifact types, required fields, and whether further profile fragments need first-class metadata.

14.1.2. Q-002 Which canonical publication URLs should generated summaries use?

Status: open.

AI-generated article publication packs must not invent URLs. Missing canonical URLs remain placeholders until supplied.

14.1.3. Q-003 How should this project demonstrate the Docs-as-Code Toolkit layers?

Status: open.

Current assumption: The profile project should remain a concrete running example, not a second copy of the organization positioning. It should show that profile content and architecture metadata are authoritative source knowledge, while generated website, README, CV, article, and architecture outputs are derived publication artifacts. docs-toolbox is the reproducible rendering runtime; docToolchain or similar tools could be combined with the same source model in other projects.

14.2. Traceability Matrix

Artifact ID Type Title Status Outgoing relations Incoming relations

ADR-001-asciidoc-primary-source

ADR

AsciiDoc as Primary Source

proposed

addresses → QS-003-public-artifact-consistency

-

ADR-002-gradle-build-orchestration

ADR

Gradle Build Orchestration

proposed

addresses → QS-001-reproducible-build

-

ADR-003-containerized-build-runtime

ADR

Containerized Build Runtime

proposed

addresses → QS-001-reproducible-build

-

ADR-004-pandoc-markdown-export

ADR

Pandoc Markdown Export

proposed

addresses → QS-003-public-artifact-consistency

ADR-007-build-generated-article-summaries → affects

ADR-005-minimal-static-site

ADR

Minimal Static Site

proposed

addresses → QS-004-minimal-static-site

-

ADR-006-profile-content-metamodel

ADR

Profile Content Metamodel

proposed

addresses → QS-002-profile-metadata-validity

ADR-010-multilingual-content-fallback → refines

ADR-007-build-generated-article-summaries

ADR

Build-Generated Article Summaries

proposed

addresses → QS-003-public-artifact-consistency
affects → ADR-004-pandoc-markdown-export
mitigates → RISK-001-metadata-drift

COMP-006-article-summary-generator → depends_on

ADR-008-private-to-public-article-lifecycle

ADR

Private To Public Article Lifecycle

accepted

addresses → QS-005-publication-visibility-boundary
depends_on → ADR-009-private-repository-integration
documents → COMP-001-publication-target-generator
mitigates → RISK-004-source-boundary-leakage
mitigates → RISK-005-preview-mispublication

DOC-06001-private-to-public-article-lifecycle-runtime → depends_on
DOC-01001-profile-goals → documents
ADR-011-preview-visibility-without-authentication → refines
ADR-012-private-reading-context → refines

ADR-009-private-repository-integration

ADR

Private Repository Integration

accepted

addresses → QS-005-publication-visibility-boundary
mitigates → RISK-004-source-boundary-leakage

ADR-008-private-to-public-article-lifecycle → depends_on
ADR-012-private-reading-context → depends_on
DOC-06001-private-to-public-article-lifecycle-runtime → depends_on
DOC-01001-profile-goals → documents

ADR-010-multilingual-content-fallback

ADR

Multilingual Content And Language Fallback

accepted

addresses → QS-003-public-artifact-consistency
addresses → QS-004-minimal-static-site
affects → COMP-001-publication-target-generator
constrains → COMP-005-profile-artifact-generator
introduces_risk → RISK-001-metadata-drift
refines → ADR-006-profile-content-metamodel

-

ADR-011-preview-visibility-without-authentication

ADR

Preview Visibility Without Authentication

accepted

addresses → QS-005-publication-visibility-boundary
introduces_risk → RISK-005-preview-mispublication
refines → ADR-008-private-to-public-article-lifecycle

-

ADR-012-private-reading-context

ADR

Reading Context of the Private Target

accepted

depends_on → ADR-009-private-repository-integration
mitigates → RISK-004-source-boundary-leakage
refines → ADR-008-private-to-public-article-lifecycle

-

COMP-001-publication-target-generator

Component

Publication Target Generator

proposed

addresses → QS-005-publication-visibility-boundary
refines → DOC-05000-building-block-view

ADR-010-multilingual-content-fallback → affects
ADR-008-private-to-public-article-lifecycle → documents

COMP-002-architecture-metamodel-validator

Component

Architecture Metamodel Validator

proposed

refines → DOC-05000-building-block-view

-

COMP-003-profile-metamodel-validator

Component

Profile Metamodel Validator

proposed

addresses → QS-002-profile-metadata-validity
refines → DOC-05000-building-block-view

-

COMP-004-architecture-artifact-generator

Component

Architecture Artifact Generator

proposed

refines → DOC-05000-building-block-view

-

COMP-005-profile-artifact-generator

Component

Profile Artifact Generator

proposed

addresses → QS-003-public-artifact-consistency
refines → DOC-05000-building-block-view

ADR-010-multilingual-content-fallback → constrains

COMP-006-article-summary-generator

Component

Article Summary Generator

proposed

depends_on → ADR-007-build-generated-article-summaries
refines → DOC-05000-building-block-view

-

CANVAS-001-business-model

Document

Business Model Canvas

draft

-

-

CANVAS-002-value-proposition

Document

Value Proposition Canvas

draft

-

-

CANVAS-003-architecture-inception

Document

Architecture Inception Canvas

draft

-

-

CANVAS-004-architecture-communication

Document

Architecture Communication Canvas

draft

-

-

CANVAS-005-techstack

Document

Techstack Canvas

draft

-

-

DOC-001-profile-dieterbaier-arc42

Document

Profile Dieter Baier arc42 Architecture

draft

documents → DOC-01000-introduction-and-goals
documents → DOC-02000-architecture-constraints
documents → DOC-03000-system-scope-and-context
documents → DOC-04000-solution-strategy
documents → DOC-05000-building-block-view
documents → DOC-06000-runtime-view
documents → DOC-07000-deployment-view
documents → DOC-08000-crosscutting-concepts
documents → DOC-09000-architecture-decisions
documents → DOC-10000-quality-requirements
documents → DOC-11000-risks-and-technical-debt
documents → DOC-12000-glossary
documents → DOC-13000-appendix

-

DOC-002-vision-mission

Document

Vision and Mission

draft

-

-

DOC-004-roadmap

Document

Roadmap

draft

-

-

DOC-01000-introduction-and-goals

Document

Introduction and Goals

draft

-

DOC-001-profile-dieterbaier-arc42 → documents
DOC-01001-profile-goals → refines
QS-005-publication-visibility-boundary → refines

DOC-01001-profile-goals

Document

Profile Product Goals

draft

documents → ADR-008-private-to-public-article-lifecycle
documents → ADR-009-private-repository-integration
refines → DOC-01000-introduction-and-goals

-

DOC-02000-architecture-constraints

Document

Architecture Constraints

draft

-

DOC-001-profile-dieterbaier-arc42 → documents

DOC-03000-system-scope-and-context

Document

System Scope and Context

draft

-

DOC-001-profile-dieterbaier-arc42 → documents

DOC-04000-solution-strategy

Document

Solution Strategy

draft

-

DOC-001-profile-dieterbaier-arc42 → documents

DOC-05000-building-block-view

Document

Building Block View

draft

-

DOC-001-profile-dieterbaier-arc42 → documents
COMP-001-publication-target-generator → refines
COMP-002-architecture-metamodel-validator → refines
COMP-003-profile-metamodel-validator → refines
COMP-004-architecture-artifact-generator → refines
COMP-005-profile-artifact-generator → refines
COMP-006-article-summary-generator → refines

DOC-06000-runtime-view

Document

Runtime View

draft

-

DOC-001-profile-dieterbaier-arc42 → documents
DOC-06001-private-to-public-article-lifecycle-runtime → refines

DOC-06001-private-to-public-article-lifecycle-runtime

Document

Private To Public Article Lifecycle Runtime

proposed

depends_on → ADR-008-private-to-public-article-lifecycle
depends_on → ADR-009-private-repository-integration
refines → DOC-06000-runtime-view

-

DOC-07000-deployment-view

Document

Deployment View

draft

-

DOC-001-profile-dieterbaier-arc42 → documents

DOC-08000-crosscutting-concepts

Document

Crosscutting Concepts

draft

-

DOC-001-profile-dieterbaier-arc42 → documents

DOC-09000-architecture-decisions

Document

Architecture Decisions

draft

-

DOC-001-profile-dieterbaier-arc42 → documents

DOC-10000-quality-requirements

Document

Quality Requirements

draft

-

DOC-001-profile-dieterbaier-arc42 → documents
QG-001-reproducibility → refines
QG-002-maintainability → refines
QG-003-automation → refines
QG-004-correctness → refines
QG-005-sustainability → refines

DOC-11000-risks-and-technical-debt

Document

Risks and Technical Debt

draft

-

DOC-001-profile-dieterbaier-arc42 → documents

DOC-12000-glossary

Document

Glossary

draft

-

DOC-001-profile-dieterbaier-arc42 → documents

DOC-13000-appendix

Document

Appendix

draft

-

DOC-001-profile-dieterbaier-arc42 → documents

QG-001-reproducibility

QualityGoal

Reproducibility

proposed

refines → DOC-10000-quality-requirements

QS-001-reproducible-build → refines

QG-002-maintainability

QualityGoal

Maintainability

proposed

refines → DOC-10000-quality-requirements

-

QG-003-automation

QualityGoal

Automation

proposed

refines → DOC-10000-quality-requirements

-

QG-004-correctness

QualityGoal

Correctness

proposed

refines → DOC-10000-quality-requirements

QS-002-profile-metadata-validity → refines
QS-003-public-artifact-consistency → refines
QS-005-publication-visibility-boundary → refines

QG-005-sustainability

QualityGoal

Sustainability

proposed

refines → DOC-10000-quality-requirements

QS-004-minimal-static-site → refines

QS-001-reproducible-build

QualityScenario

Reproducible Build

proposed

refines → QG-001-reproducibility

ADR-002-gradle-build-orchestration → addresses
ADR-003-containerized-build-runtime → addresses

QS-002-profile-metadata-validity

QualityScenario

Profile Metadata Validity

proposed

refines → QG-004-correctness

ADR-006-profile-content-metamodel → addresses
COMP-003-profile-metamodel-validator → addresses

QS-003-public-artifact-consistency

QualityScenario

Public Artifact Consistency

proposed

refines → QG-004-correctness

ADR-001-asciidoc-primary-source → addresses
ADR-004-pandoc-markdown-export → addresses
ADR-007-build-generated-article-summaries → addresses
ADR-010-multilingual-content-fallback → addresses
COMP-005-profile-artifact-generator → addresses

QS-004-minimal-static-site

QualityScenario

Minimal Static Site

proposed

refines → QG-005-sustainability

ADR-005-minimal-static-site → addresses
ADR-010-multilingual-content-fallback → addresses

QS-005-publication-visibility-boundary

QualityScenario

Publication Visibility Boundary

proposed

refines → DOC-01000-introduction-and-goals
refines → QG-004-correctness

ADR-008-private-to-public-article-lifecycle → addresses
ADR-009-private-repository-integration → addresses
ADR-011-preview-visibility-without-authentication → addresses
COMP-001-publication-target-generator → addresses
RISK-004-source-boundary-leakage → affects
RISK-005-preview-mispublication → affects

RISK-001-metadata-drift

Risk

Metadata Drift

proposed

-

ADR-010-multilingual-content-fallback → introduces_risk
ADR-007-build-generated-article-summaries → mitigates

RISK-002-build-toolchain-drift

Risk

Build Toolchain Drift

proposed

-

-

RISK-003-private-data-exposure

Risk

Private Data Exposure

proposed

-

-

RISK-004-source-boundary-leakage

Risk

Source Boundary Leakage

proposed

affects → QS-005-publication-visibility-boundary

ADR-008-private-to-public-article-lifecycle → mitigates
ADR-009-private-repository-integration → mitigates
ADR-012-private-reading-context → mitigates

RISK-005-preview-mispublication

Risk

Preview Mispublication

proposed

affects → QS-005-publication-visibility-boundary

ADR-011-preview-visibility-without-authentication → introduces_risk
ADR-008-private-to-public-article-lifecycle → mitigates