ADR-015: Shape registry as code (`shapes.yml`)
Status: Accepted Date: 2026-05-22
Context
Section titled “Context”PRD §4 lists the supported shapes as a Markdown table. That’s fine for human reading but inadequate as a runtime source — agents need to validate plan shape: fields, the orchestrator needs to map shape→plugin, the shape-detection helper (ADR-008: Shape detection — .copier-answers.yml with sniffing fallback) needs the canonical list of valid identifiers, and /shared:new-service needs the template path per shape. Today all of this is implicit in §4’s prose.
Decision
Section titled “Decision”Add shapes.yml at the platform repo root as the canonical machine-readable shape registry. PRD §4’s table is generated from shapes.yml (or maintained alongside with a CI consistency check; see Consequences). Agents read shapes.yml directly.
Schema
Section titled “Schema”# shapes.yml — canonical shape registry for ika100-claudeshapes: - id: service-python plugin: svc template: service-python deployable: true library: false detection: copier_src: templates/service-python sniff: - "pyproject.toml + Dockerfile" default_stack: language: python version: "3.12" framework: fastapi status: stable adr: null # null for shapes that pre-date the ADR log
- id: library-python plugin: svc template: library-python deployable: false library: true detection: copier_src: templates/library-python sniff: - "pyproject.toml without Dockerfile" default_stack: language: python version: "3.12" status: stable adr: null
- id: web-nextjs plugin: web template: web-nextjs deployable: true library: false detection: copier_src: templates/web-nextjs sniff: - "next.config.*" - "package.json contains \"next\"" default_stack: language: typescript runtime: node-lts framework: nextjs-app-router status: planned adr: [002, 003, 004, 005]
- id: gitops-app plugin: gitops template: gitops-app deployable: false # gitops-app is the GitOps source, not a deployable library: false detection: copier_src: templates/gitops-app sniff: - "applications/*/applicationset.yaml" default_stack: kind: kustomize+argocd status: planned adr: [006, 014]
- id: service-java plugin: svc-java template: service-java deployable: true library: false detection: copier_src: templates/service-java sniff: - "pom.xml" - "build.gradle*" default_stack: language: java version: "21" build_tool: maven framework: spring-boot-3 status: planned adr: [009]
- id: service-go plugin: svc-go template: service-go deployable: true library: false detection: copier_src: templates/service-go sniff: - "go.mod" default_stack: language: go version: "1.22" router: chi status: planned adr: [010]Field reference
Section titled “Field reference”| Field | Meaning |
|---|---|
id |
Shape identifier — the value used in plan shape: fields, in /shared:new-service --type, and in shape-detection results |
plugin |
Marketplace plugin that owns the shape’s agents |
template |
Copier template directory under templates/ |
deployable |
If true, /shared:new-service tags the repo deployable-service for ArgoCD discovery |
library |
If true, no Docker, no k8s, publishable artifact |
detection.copier_src |
The _src_path tail in .copier-answers.yml that identifies this shape (primary detection) |
detection.sniff |
Ordered sniffing-fallback markers (per ADR-008 fallback rules) |
default_stack |
Free-form per-shape stack metadata (language, framework, version) — used for documentation and template defaults |
status |
stable / planned / deprecated |
adr |
List of ADR numbers governing this shape’s design decisions |
Adding a shape — updated §4.1 contract
Section titled “Adding a shape — updated §4.1 contract”The §4.1 extensibility contract now reads:
- Add a row to
shapes.ymlat the top. - Copier template under
templates/<id>/. - Plugin at
plugins/<plugin>/(or extend an existing one). - Update ADR-008: Shape detection —
.copier-answers.ymlwith sniffing fallback sniffing table (theshapes.ymldetection.snifffield is the source; ADR-008’s table is rendered from it OR maintained alongside with a CI check).
PRD §4’s Markdown table is then either regenerated by a scripts/render-shapes.py script, or kept in sync by a CI check that compares the two.
Rationale
Section titled “Rationale”- Agents need a machine-readable list to validate
shape:fields. Today the architect could emitshape: web-nextjz(typo) and no orchestrator step would catch it before routing.shapes.ymlplus a simple validator closes that hole. - Orchestrator routing becomes data, not code.
shape: service-java→plugin: svc-javais a lookup, not an if/else chain that gets edited each time a shape is added. /shared:new-servicebecomes uniform. It readsshapes.ymlto find the template path, deployability flag, and plugin to enable (ADR-013: Plugin auto-enable on/shared:new-service). No per-shape special-casing in the command.- Single source of truth. Without
shapes.yml, the same shape list appears in PRD §4, ADR-008’s tables,/shared:new-servicelogic, and individual agent prompts — guaranteed to drift.
Consequences
Section titled “Consequences”- A small platform script (
scripts/validate-shapes.py) runs in CI to validateshapes.ymlagainst a JSON Schema and to check that PRD §4 + ADR-008 tables match. - Adding a shape becomes mostly file edits —
shapes.yml, then template, then plugin. The platform docs that reference shapes can be generated. - The shape registry is versioned via git like everything else; bumping a
statusfromplannedtostableis a one-line PR. - Existing prose in PRD §4 stays for human readability but is marked as derived from
shapes.yml. - Discoverability:
/shared:new-servicerejects an unknown--typewith the list of valid shapes read fromshapes.yml. A separate/shared:shapescommand was proposed and dropped (2026-10-07) as not worth its always-on prompt cost.
References
Section titled “References”- platform-vision.md §4, §4.1
- ADR-008: Shape detection —
.copier-answers.ymlwith sniffing fallback — detection rules sourced fromshapes.yml - ADR-013: Plugin auto-enable on
/shared:new-service— plugin auto-enable readsshapes.yml - end-to-end-scenario.md Q5, Q20, Q21