Skip to content

feat: subtype/generalization via subtypeOf (#8)#21

Merged
jbeda merged 2 commits into
mainfrom
feat-subtype
Jul 22, 2026
Merged

feat: subtype/generalization via subtypeOf (#8)#21
jbeda merged 2 commits into
mainfrom
feat-subtype

Conversation

@jbeda

@jbeda jbeda commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Fourth feature from the schema-expressiveness pass. Implements ADR-0004; additive to schema v1 (goldens unchanged — examples have no subtypeOf).

Closes #8.

What changed

  • subtypeOf on an entity (PascalCase, references a defined entity) — an is-a / generalization link the child declares. Schema + Entity.SubtypeOf.
  • Lint (runSubtypes): errors on a subtypeOf naming an undefined entity, and on a self- or transitively-cyclic chain (cycle-safe walk, no infinite loop). Completeness now treats a parent's invariants as covering its subtypes — a subtype that adds no rule of its own is no longer flagged "has no invariants" when an ancestor has them (transitive, cycle-safe).
  • Render (Markdown): each child names its supertype (Subtype of \Parent``); each parent lists its subtypes (sorted, deterministic). Per ADR-0002/0004 the Mermaid ER draws no is-a edge — erDiagram has no generalization notation, so the hierarchy lives in the Markdown.
  • Docs: schema-reference documents subtypeOf and the ER limitation.

Process (agent-workflow.md)

Correctness-critical (schema, struct-sync, lint semantics, completeness change), so a full adversarial round (Opus) ran against hostile fixtures (self/mutual/3-cycles, cycle-not-at-root, transitive inheritance chains, undefined parents, deep acyclic chains).

Result: clean on correctness. One cosmetic fix applied — a node pointing into a cycle was told it "is a subtype of itself"; reworded to be accurate for both cases. One note accepted as-is (an undefined parent yields both the undefined error and a "no invariants" warning — each independently true). Record: .scratch/reviews/subtype.md.

Verification

task check passes end to end (vet, staticcheck, golangci-lint 0 issues, -race tests, lint-models, render-check, validate-plugin). TestSchemaStructSync/TestURLConsistency hold; render --check clean on both fixtures (no drift).

Tests: TestADR_0004_UndefinedSupertype, TestADR_0004_SubtypeInheritsInvariants, TestSubtypeCycleDetected, TestRenderEntity_SubtypeHierarchy.

Also sneaks in a .gitignore entry for /.claude/worktrees/ (subagent worktrees should never be committed).

🤖 Generated with Claude Code

jbeda added 2 commits July 21, 2026 18:12
Implements ADR-0004. Additive to schema v1.

- Schema/model: `subtypeOf` on an entity (PascalCase, references a defined
  entity) — an is-a / generalization link declared by the child.
- Lint: `runSubtypes` errors on a subtypeOf naming an undefined entity and on a
  self- or mutually-cyclic subtypeOf chain (cycle-safe walk). Completeness now
  treats a parent's invariants as covering its subtypes, so a subtype that adds
  no rule of its own is not flagged "has no invariants" when an ancestor has
  them.
- Render/markdown: each child names its supertype ("Subtype of `Parent`") and
  each parent lists its subtypes; deterministic (sorted). Per ADR-0002/0004 the
  Mermaid ER draws no is-a edge (erDiagram has no generalization notation) — the
  hierarchy lives in the Markdown.
- Docs: schema-reference documents subtypeOf and the ER limitation.

Tests: TestADR_0004_UndefinedSupertype, TestADR_0004_SubtypeInheritsInvariants,
TestSubtypeCycleDetected, TestRenderEntity_SubtypeHierarchy. Schema↔struct sync
holds; goldens unchanged (examples have no subtypeOf).

Signed-off-by: Joe Beda <joe@stacklok.com>
Round 1 review (Opus, clean on correctness) noted that an entity whose
subtypeOf chain leads INTO a cycle it isn't part of was told it 'is a subtype
of itself'. Reword to 'has a cyclic subtypeOf chain: an entity cannot be a kind
of itself, directly or transitively', which is accurate for both the in-cycle
and points-into-cycle cases. Test substrings updated.

Signed-off-by: Joe Beda <joe@stacklok.com>
@jbeda
jbeda merged commit 2cf0c68 into main Jul 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Schema: subtype / generalization (is-a) relationships between entities

1 participant