Every guestgraph service has the same shape by check, not by hand. SERVICE.md is the list of what a service has, whatever it is written in; a directory per stack, spring/ today, holds the files that make the list true for that stack: the parent build every service's pom.xml takes by path, the source rules, the architecture rules as one test class, the diagram script, the workflow, the sync script, and the block a service's agent file opens with after the family's. Every decision lives in the engine repository, under specs/007-service-conventions/, and this repository builds what that specification says.
A service names the release in service-conventions.json at its root, with its stack, its package root, the parameter every repository query carries and the schema its diagram is drawn from:
{ "repo": "guestgraph/service-conventions", "tag": "v0.1.0", "stack": "spring",
"root": "io.guestgraph.engine", "scope": "tenantId", "schema": "engine", "jdbcClientAllowed": [] }sh service-conventions/service-conventions-sync sync writes service-conventions/ from the pinned release and puts four of its files where a tool reads them: the workflow in .github/workflows/verify.yml, Dependabot's configuration in .github/dependabot.yml, the rules test in src/test/java/ServiceRulesTest.java, and the block in AGENTS.md. It also writes the Maven wrapper the runtime module builds with, mvnw, mvnw.cmd and .mvn/wrapper/maven-wrapper.properties, so every service runs the Maven the parent is tested on. The service's pom.xml names service-conventions/pom.xml as its parent by relativePath, at the version the tag names. sh service-conventions/service-conventions-sync check says whether the copy still matches the release, and the workflow runs it on every pull request beside the service's own suite and the family's prose check. The pin is editorial: it moves when the owner decides, in a commit that says why, and a service that is behind is behind on purpose.
What a service serves at /api-docs is one file, src/main/resources/api/openapi.yaml, generated by sh service-conventions/regen-api from the sources sources.json beside it names: a path in the same repository for a frozen record, owner/repo@commit:path for a file another repository owns, or the service's own document in the same folder. The service-conventions job regenerates it and fails on drift, the way the diagram is held to the migrations, so a reader finds the whole API in one file and it is never older than its sources.
spring/runtime/ holds the package io.guestgraph.service, the few classes every service on the Spring stack carries: the writer that gives a refusal its shape, the exception that carries its answer, the advice that turns what nobody foresaw into a problem, the size filter, the document controller, a bearer guard present only where a token is configured, and the post-processor that loads service-defaults.yaml, the settings every service shares, beneath a service's own. The sync copies the classes into src/main/java/io/guestgraph/service/ and the two resources into src/main/resources/, and the check names a copy that differs. The check's error-shape item reads the service's own sources for a refusal written by hand, a status exception, a problem built or its media type written directly, an advice or a filter answering through its writer, and names the file, so the one place a refusal takes its shape stays the shared package. A module beside them builds and tests them under the same parent a service uses; it is never vendored. A published library would replace the copies without a rename, once the family has a repository to publish to.
Every version a service builds with is declared in the parent here, so a bump belongs here and reaches a service by a release and a re-sync. Dependabot cannot know that: Maven follows the parent a service takes by relativePath, so it finds the vendored copy and proposes a bump inside it, which the sync check then refuses. The configuration that tells it to leave those artifacts alone is vendored like everything else, because the list is a fact about the parent rather than a preference a service holds, and sh tests/run holds the list to what the parent actually pins. The same goes for the two other vendored files Dependabot reads. The Maven version is the runtime module's wrapper, which this repository's own Dependabot bumps; the actions the vendored workflow uses are ignored in a service and watched here through tests.yml, which sh tests/run holds to the versions the vendored workflow names, so a bump of one goes red until the other follows. Everything a service declares for itself stays watched.
In this repository, once: edit the stack's file, run sh tests/run, open a pull request, and tag the release with notes in the prose register. A change to any file under a stack's directory is at least a minor release, because it makes every service's copy stale; a change that asks a service to do more than re-sync is a major, and the notes say which. A service then moves its pin and re-syncs, and nothing else.
The runtime's example, walked once: the detail sentence of the payload-too-large problem changed in the shared size filter, the runtime module verified, and v0.7.1 was tagged. Each service then moved its pin, ran the sync and opened one pull request, whose diff was the pin, the parent's version where the service's pom.xml and the vendored copies name it, and the filter class. No service edited a line of its own, and both suites stayed green.
sh new-service spring <name> [directory] writes a service of the Spring stack that passes the family's sync check, the stack's sync check and the service check on its first run: the pin with the root package and the schema derived from the name, both vendored sets, a pom.xml naming the parent, the application class, the api package with nothing of its own yet, since the document controller and the size filter arrive vendored in the shared package, the configuration with its local profile and its schema above the shared defaults, the compose file, an empty contract, a first migration, the diagram, the README with its required sections and the agent file. It leaves one thing to the person: the paragraph in the README that says what the service is for, which no script can write.
sh tests/run holds the scripts to their fixtures: a service equal to its pin passes, one character of drift fails naming the file, a missing file fails naming it. sh conventions/conventions-check holds the prose to the family's conventions, and AGENTS.md says how to work here.