Skip to content

docs: finish what the hierarchy rename left in the examples and chapter 10 - #119

Merged
ExtraToast merged 4 commits into
mainfrom
docs/115-follow-ups
Sep 15, 2026
Merged

ExtraToast merged 4 commits into
mainfrom
docs/115-follow-ups

Conversation

@ExtraToast

Copy link
Copy Markdown
Contributor

Follows #115.

What this fixes

The three things #115 left for a follow-up, and one more leftover of the same rename found while doing them.

  • The Traefik Service. spec/v1/examples/platform/README.md said "the Traefik Application each tier is", where it means the Kubernetes Service each tier's route targets. It read Service before refactor: rename the authored hierarchy to Project, Application and Process #100's rename and fix: restore the external names the hierarchy rename rewrote #107's restoration pass missed it.
  • The hardening row. The same README's table had no row for the hardening: block platform.intent.yml carries. It has one now, saying the posture is declared once for every container, a Process authors none, and there is no exception surface, linking chapter 14 and ADR 0016.
  • No alertClass: none. spec/v1/examples/data/rendered/README.md said the valkey Application "declares alertClass: none". Chapter 10 says Alert Class has no none member, and data.project.yml opts out by omitting the observability block, which is what the README now says. It was the only occurrence in the repository.
  • A multi-Process Application. Chapter 10's label-set section said a name of the Application "would make every Process of a multi-Process Service selector-ambiguous". The paragraph is about a controller's pod selector, not a Kubernetes Service, and the text read "multi-Workload Service" before refactor: rename the authored hierarchy to Project, Application and Process #100, which converted Workload to Process but left this Service behind. It now says Application, as chapter 30 already does. A re-grep of the chapters found three other bare Service uses, and all three are the Kubernetes object.

The negative fixtures keep their names

The composition fixtures under spec/v1/examples/negative/*/intent*/ do not carry .project.yml, and this keeps it that way on purpose. Chapter 10's "Two artefacts" section now says so: those files stand in for an Intent Fragment that has already been published and is fed to composition, not a file in a repository's platform/ tree, so the suffix rule for what a publish step reads does not apply to them.

Renaming them was the alternative, and it would have been unsafe. The EMF WorkedExamplesTest selects every *.project.yml under the examples, excludes only refusals/, and asserts each parses with no diagnostics. duplicate-process-name is a single-file violation that would very likely raise one, so a rename would either break that test or need its expectations redesigned without Maven to prove the redesign.

The suffix-selecting tests select exactly what they did before: 14 files for the diagram test, 4 cases for the Project Intent test, and 4 worked files for the EMF test, the last reasoned from its source.

Verification

  • npm run lint:links: 178 files clean.
  • npm run lint:meaning: 157 files clean.
  • npm run verify exit 0: 619 tests, coverage unchanged.

…and missing hardening row

The tiers table cell still read "Application" where #100's rename should
have left the Kubernetes Service alone; #107 restored other external
spellings but missed this one. Also add the row for platform.intent.yml's
hardening block, which the table never carried: one posture for every
container, no per-control exception surface, decided by 0016.
…: none

Chapter 10 is explicit that Alert Class has no none member: an omitted
observability block already says "no monitoring", and a member that means
"I wrote the field to say I did not want the field" is ceremony. valkey's
row said the Application "declares alertClass: none", which is not a value
this schema accepts; it declares no observability block at all.
…d convention

spec/v1/examples/negative/*/intent*/ fixtures stand in for Intent Fragments
already handed to composition, not files in a repository's platform/ tree,
and renaming them to .project.yml would pull them into three tests that
discover project files by that suffix, at least one of them
(duplicate-process-name) failing where those tests expect every file to
parse clean. Renaming is left for its own change; this commit instead
states the convention in chapter 10 so the suffix difference reads as a
rule rather than an oversight, and none of the three tests' selections
move: 14 .project.yml files for the diagram test, 4 cases for the Project
Intent test, and (by inspection of its source, without running Maven) 4
files for the EMF worked-examples test, which walks the same suffix
excluding refusals/.
The label-set section's hypothetical alternate labelling reads "would make
every Process of a multi-Process Service selector-ambiguous". Before #100
this said "multi-Workload Service"; the rename converted Workload to
Process but left this one "Service" unconverted, where it names what the
estate now calls an Application (compare "a two-Process Application" in
chapter 30) rather than a Kubernetes Service, which the surrounding
paragraph never mentions: it is about a Deployment's pod selector. It now
reads "multi-Process Application".

Re-grepped spec/v1/*.md for Service used as the retired hierarchy word: the
three remaining occurrences (10-project-intent.md's IngressRoute paragraph,
30-deliverables.md's adapter table, 16-dependencies.md's diagram node) are
all the Kubernetes object and are left alone.
@ExtraToast ExtraToast added type: docs Documentation-only change. area: deploy homelab-deploy, deploy-v2, manifests, and rollout flow. component: docs Runbook, README, API docs, or documentation tooling. labels Sep 15, 2026
@github-actions

Copy link
Copy Markdown

Shape

bucket files additions deletions
specification 1 +15 -1
examples 2 +3 -2

Coverage

Baseline: main @ 088c931.

metric this branch main diff
statements 99.06% 99.06% 0.00
branches 96.17% 96.17% 0.00
functions 100.00% 100.00% 0.00
lines 98.97% 98.97% 0.00

This pull request changed no covered line.

@ExtraToast
ExtraToast merged commit 5357f2c into main Sep 15, 2026
22 checks passed
@ExtraToast
ExtraToast deleted the docs/115-follow-ups branch September 15, 2026 12:35
ExtraToast added a commit that referenced this pull request Sep 15, 2026
Cover lint-agents.ts's own entrypoint guard and default output parameter
with an in-process module reload, the way
test/package-contents-contract.test.ts and
test/secret-scan-contract.test.ts already do for theirs, so the new gate
is the first to leave nothing uncovered. Rebased onto main again (#119,
docs-only); REQ-030 and RULE-069 remain free. Every metric now sits above
main's floor (99.06/96.17/100/98.97): statements 99.07%, branches 96.25%,
functions 100%, lines 98.99%, and vitest.config.ts and README.md carry the
measured values.
ExtraToast added a commit that referenced this pull request Sep 15, 2026
* docs: route every agent through AGENTS.md

Add AGENTS.md as the one instruction file every coding agent reads: the
estate pointer, the repository map, setup, every npm script, the testing
strategy, code style, the Boundaries section, the repository skills, the
claiming procedure, and the moved emf/ and MDE coursework sections. CLAUDE.md
becomes an import of it plus Claude-specific notes only.

* docs: configure repository skills and agent tooling

Four skills (adr, spec-change, gate-triage, new-rule) live in
.agents/skills, and .claude/skills is a committed symlink to that directory
(git ls-files -s reads mode 120000). .claude/settings.json holds a
read-only permission allowlist plus the one labelled write the claim
procedure needs, and enables the mattpocock-skills, typescript-lsp and
drawio plugins. .mcp.json holds context7. docs/agents/ configures the
mattpocock-skills engineering skills: the GitHub issue tracker with
cross-repository references written in full, the estate status: labels
mapped to the five canonical triage roles, and the single-context domain
doc layout with spec/v1 normative over an ADR.

* test: fail when a script escapes AGENTS.md's script list

Add lint:agents (scripts/lint-agents.ts), wired into the contracts CI job
and npm run verify, with a fixture (test/agents-contract.test.ts) proving it
fires on an undocumented script. Ledger rows REQ-029 and RULE-069 record it,
and docs/architecture.md's Gates table gains a row and its count moves to
nineteen, per 0102 and 0104.

The new gate's own coverage (96%/94.73%/100%/95.83%) carries the same
one-line command-guard gap every sibling gate script already does; the
suite's aggregate moves to 98.98%/96.06%/100%/98.89%, so the ratchet in
vitest.config.ts and README's quoted numbers move with it.

* feat(ci): claim the issue a pull request names

A non-gating workflow that adds status: in-progress (and removes
status: ready-for-agent) on the issue a pull request's title, body or
branch name references, and on its parent, when the pull request opens;
releases the claim when it closes. Labels only: the project board's own
Status field needs a token scope the default GITHUB_TOKEN does not carry,
and the job says so in its own log rather than claiming a fix it cannot
make.

* docs: extend CONTRIBUTING.md with change shape and release flow

Below the existing contribution policy: the shape of a change, when a
change takes a decision, Conventional Commits, what runs where across the
five non-gating workflows beside ci.yml, and release candidates and
releasing.

* test: recompute the coverage ratchet after rebasing onto main

REQ-029 collided with #118's own row landed on main; this branch's
AGENTS.md script-list requirement moves to REQ-030. #118 also moved the
coverage baseline, so the ratchet in vitest.config.ts and README's quoted
numbers are recomputed against the rebased tree rather than the
pre-rebase measurement.

* test: hold the ratchet at main's floor after the AGENTS.md gate

Cover lint-agents.ts's own entrypoint guard and default output parameter
with an in-process module reload, the way
test/package-contents-contract.test.ts and
test/secret-scan-contract.test.ts already do for theirs, so the new gate
is the first to leave nothing uncovered. Rebased onto main again (#119,
docs-only); REQ-030 and RULE-069 remain free. Every metric now sits above
main's floor (99.06/96.17/100/98.97): statements 99.07%, branches 96.25%,
functions 100%, lines 98.99%, and vitest.config.ts and README.md carry the
measured values.

* fix(ci): claim only what closes, fixes or resolves a pull request

The claim workflow read every bare #NNN in a title or body as a claim,
labelling any issue merely mentioned ("follows #115", "blocked by #39",
"unlike #41"), the exact shape of the labelling incident this fixes. It
also released a claimed issue's parent on every child close, and depended
on gh issue view's --json parent field without confirming it exists on
the runner.

scripts/claim-issue.ts (test/claim-issue.test.ts, no gh call) now extracts
candidates from a GitHub closing keyword (closes, fixes, resolves, and
their inflections) immediately naming an issue, or the branch name's
leading number, stripping fenced code blocks first. The workflow calls it
for the candidate list and keeps every gh call in its own bash: on claim,
the parent is claimed too if unclaimed; on release, only the named issue
is released, never the parent, which stays a human's call or a later
workflow's. Dropped synchronize from the triggers, since a push re-runs
the same extraction over the same fields and can only repeat what already
holds.

Confirmed gh issue view --json parent is real and supported: added in gh
CLI 2.94.0 (2026-06-10 changelog), and the ubuntu-latest runner image
ships 2.100.0. A gh issue view failure is now logged with its real error
text rather than assumed to mean "not an issue".

Coverage rises again with the new file's own full coverage (statements
99.09%, branches 96.32%, functions 100%, lines 99.01%), still above
main's floor; vitest.config.ts and README.md carry the measured values.

* fix(test): keep agent configuration out of the mutation sandbox

Stryker copies the repository into its sandbox before running, and its
copyfile call dies on .claude/skills: a symlink to a directory, which
copyfile refuses outright (EISDIR). ignorePatterns (a real, documented
Stryker option, confirmed against node_modules/@stryker-mutator/core's own
stryker-schema.json for the pinned 10.0.0) now excludes /.claude and
/.agents, neither of which the mutation run needs: Claude Code
configuration and skill content, not src/. The symlink itself is
untouched; only the sandbox copy skips it.

* test: hold the ratchet at main's floor after rebasing onto #120

#120 (the Platform document reader) took REQ-030 for an unrelated
behaviour and added REQ-031; this branch's AGENTS.md script-list
requirement moves to REQ-032, the ledger now states 32 rows. #120 added no
npm script, so AGENTS.md's script list needed no change (lint:agents
confirms all 22 scripts still resolve). Coverage rises with #120's own
new, fully-tested code: statements 99.15%, branches 96.49%, functions
100%, lines 99.08%, still above main's floor (99.06/96.17/100/98.97, which
#120 left untouched); vitest.config.ts and README.md carry the measured
values.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: deploy homelab-deploy, deploy-v2, manifests, and rollout flow. component: docs Runbook, README, API docs, or documentation tooling. type: docs Documentation-only change.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant