doc/development/ai_instruction_files_review.md
For broader advice and best practices for code review, see the code review guide.
Every change to a file under .ai/ requires a dedicated review.
These files configure how AI coding agents (GitLab Duo, Claude Code,
OpenCode, and other AGENTS.md-aware tools) behave when working in this
repository, so a mistake affects every contributor's agent sessions
rather than a single runtime code path.
The distilled principles have an additional consumer:
.gitlab/duo/mr-review-instructions.yaml
carries generated fences imported from the distilled principles, so a change
to a distilled principle also changes the automated review feedback Duo posts
on every merge request that matches the principle's file filters.
Fence regeneration is decoupled from distillation. A distilled principle's
merge request updates only the .ai/principles/distilled/*.md file. A separate
daily scheduled job reconciles the fences from the merged master distilled
files by pure projection (it copies each file's distilled_at_sha and
source_checksum front matter and never re-runs distillation) and opens its
own merge request. This keeps a team's distilled merge request and the fence
update independently mergeable, so neither waits on the other to pass CI.
Changes to .ai/ files require approval from an AI harness directly
responsible individual (DRI). This convention is encoded in
.gitlab/CODEOWNERS
under the "Monolith AI harness files" block: these files do not need the
broad * rule approval, but each requires approval from a named DRI. The
review convention is tracked in
work item 20880.
Distilled principle files carry an additional layer of routing: each is also owned by the team that owns its single source of truth (SSOT) documentation, so distillation changes are approved by the relevant domain team. See Reviewing auto-generated sync merge requests.
.ai/ files areThe .ai/ directory holds four categories of file, each reviewed
differently:
.ai/README.md, .ai/ci-cd.md,
.ai/git.md, .ai/merge-requests.md, and .ai/code-review.md.
Human-written instruction prose, referenced from the repository-root
AGENTS.md and CLAUDE.md..ai/principles/distilled/*.md.
Auto-generated from SSOT documentation on docs.gitlab.com by the
AI principles distiller.
These files must not be edited by hand..ai/principles/baselines/*.md. Hand-curated review
rules that are not yet covered by docs.gitlab.com and are merged into
the distilled output during a sync..ai/principles/manifest.yml and
.ai/principles/distillation_prompt.md. The routing and prompt SSOT
that drive the distiller.Apply these to every .ai/ change, regardless of file category:
Do X / Don't do Y
rules over background prose. Vague guidance produces vague agent
behavior.AGENTS.md and CLAUDE.md identical. The repository-root
entry points must not diverge. AGENTS.md is the source of truth. When
a change references a new module, both entry points must be updated
together..ai/. Every hand-authored rule
is a rule that can drift from the docs it duplicates.For .ai/README.md, .ai/ci-cd.md, .ai/git.md,
.ai/merge-requests.md, and .ai/code-review.md:
AGENTS.md and
CLAUDE.md, and that the two entry points remain identical.git add --force), because
the .ai/* pattern is gitignored.For .ai/principles/distilled/*.md:
<!-- Auto-generated ... do not edit manually --> marker. A deliberate
hand-fix is acceptable only on a distiller service-account branch as a
stopgap, and must be called out explicitly in the merge request.baselines/<name>.md) and re-run the
sync, rather than editing the distilled file..ai/principles/baselines/ or links to other principle
files. Baseline content survives distillation verbatim, so such
references leak into the distilled file. Fix them in the source baseline,
not the distilled file.source_checksum, distilled_at_sha) untouched.
These drive drift detection. Hand-editing them masks or forces
re-distillation incorrectly.For .ai/principles/baselines/*.md:
docs.gitlab.com, after which it should be removed from the
baseline.baselines/ folder or
other principle files. Baseline content is merged into the distilled
output verbatim, so any such cross-reference leaks into an agent-facing
principle where it is meaningless. Point readers to the distilled source
instead, or drop the cross-reference.For .ai/principles/manifest.yml and
.ai/principles/distillation_prompt.md:
manifest.yml, validate the entry against the
manifest schema:
required description, sources, and owner_team; correct
file_filters; and a team_slug that does not collide with another
entry.owner_team routes approval to the team that actually owns the
SSOT documentation.# BEGIN GENERATED and # END GENERATED comments in
.gitlab/CODEOWNERS, and with # >>> generated: <name> and
# <<< end generated: <name> comments in
.gitlab/duo/mr-review-instructions.yaml. Regenerate them by running
the sync instead.distillation_prompt.md as the highest-risk change in
this directory. The prompt governs how every principle is distilled,
so a regression silently degrades all distilled files on the next sync
rather than one. Scrutinize prompt changes accordingly: reason through
the effect on the full set of principles, not just the one that
motivated the change, and prefer distilling a sample of principles with
--force on a branch to inspect the output before merging.distillation_prompt.md changes only take effect after
the catalog flow is re-provisioned
(gitlab-ai-principles-distiller-provision-flow). Merging the file
alone does not roll the new prompt out.The weekly distillation sync opens merge requests automatically, labeled
ai-agent and documentation. They are never auto-merged: a human
must verify that the distilled changes faithfully reflect the source-doc
updates before merging.
Each per-team merge request's approval is routed to the SSOT-owning team through generated per-file CODEOWNERS rules. When reviewing one:
The daily fence-reconcile job opens a separate merge request that changes only
.gitlab/duo/mr-review-instructions.yaml. When reviewing one, confirm the
fence directives match the front matter of the distilled files on master. The
job runs no distillation, so the fence body should never diverge from the
committed distilled content.
The ai-duo-review-instructions guard that enforces this always fails on
malformed or orphaned fences, because those are broken on their own ref
regardless of the reconcile. Fence staleness is treated by severity:
master and on other merge requests caught by the
broad manifest trigger (including a team's distilled merge request), where
staleness is expected transient state until the daily reconcile job catches
the fences up. So neither a team's distilled merge request nor an unrelated
doc/**/*.md edit inherits a fence-stale failure it cannot fix on its own
ref.For the full mechanics of the distillation pipeline (the provisioner, the
sync binary, the schedule, and the manifest schema), see
.ai/principles/README.md,
which is the source of truth for how the sync works.
.ai/README.md: how instruction files are organized and loaded.ai/principles/README.md: how the SSOT distillation sync works