stacklok / stacklok/modelith

Schema: exhaustiveness/disjointness markers for subtypeOf (sealed/abstract)

Open
#24 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
32
Forks
5
PR merge metrics
No merged PRs in 30d

Description

ADR-0004 deferred exhaustiveness and disjointness markers (sealed, abstract) "until a second model needs them". This is a report that a second model needs them.

The use case

A model of a software-development methodology: the artifacts a team's process produces, and how they relate. One entity is a Commitment — a decision an agent must not re-make — which is exactly one of two kinds:

  • Invariant — a property of the artifact, mechanically checkable. For example "dependencies point inward only", enforced by an import-graph test plus a linter allowlist.
  • Practice — a behavioural or process rule that no test can check. For example "never edit a test to make it pass", or "rebase, never merge". Enforceable by a hook or a review step, or not at all.

subtypeOf (thank you, #8/#21) expresses the hierarchy, and having the parent's invariants cover both children is exactly right. Two things it cannot say:

  1. Exhaustive. Invariant and Practice are the complete enumeration. Nothing stops a bare Commitment that is neither.
  2. Abstract. A Commitment should never be instantiated directly; every one is an Invariant or a Practice.

Why it bites this model specifically

The whole point of the split is that each kind gets a different enforcement path: an Invariant must have a mechanical enforcement, a Practice may not be able to have one. An unclassified Commitment is precisely the thing that escapes both rules, so the classification being provably complete is load-bearing rather than decorative.

Without a marker, the model's own prose has to carry the claim, which is the situation ADR-0004 describes for the is-a link before subtypeOf existed: "the is-a claim lived only in definition prose, so the hierarchy was invisible to the renderer... and the linter could not check the enumeration."

What would help

Whatever shape you prefer, the checks we would want are:

  • an abstract: true parent errors (or warns) if anything references it as a concrete instance;
  • a sealed: true parent renders its children as a closed enumeration, so a reader and an agent both know the list is complete.

Rendering suggestion: the Markdown hierarchy section already lists a parent's subtypes; a closed list could simply say so.

Related: #6 (flag invariants not covered by a test) is the same shape of check one level down, and the two compose well for this model.


🤖 Filed by Claude Code on behalf of a modelith user, from a real modelling session that hit this gap.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ADR-0004 and the existing subtypeOf implementation to understand the schema and validation conventions. Then trace the Markdown hierarchy section and related checks described in the issue. Done means abstract parents cannot be used as concrete instances and sealed parents present their subtypes as a closed enumeration, with tests covering both markers.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.