Sprint 19 closed: a closed set of layers for any team that makes something

Meronq's engineering model has an axis called layer — the plane a piece of knowledge sits on when you look at a project as a stack. Until this sprint it held whatever the translators wrote: apps, packages, docs/adr, git, github/issues, github/pulls.

Look at that list twice and the problem appears. apps and packages describe a structural role. git and github/issues describe a source system. In a software repo those nearly coincide, so nothing hurts. They separate the moment one artifact arrives through several sources — and the scanner already detects game engines, creative tools and embedded projects, where they separate immediately.

The unit Meronq serves

To fix the axis we first had to say, without reference to software, who Meronq is for:

A team is a set of workers — one or more — who produce a product over time.

A worker is a person who can direct an AI assistant; that is the addressable population, and everyone in it can point an assistant at the same memory. A product is something that leaves the producing set. Over time is why memory exists rather than context. Solo counts — a set of one.

Software teams, game studios, bands, illustrators, film crews, writers' rooms, research labs, hardware teams. Different products, same shape of question.

Seven layers, closed

Such a team is a controlled process, and the layer set is closed because the loop has no eighth part:

Layer Question Loop role
intent Why are we doing this, and what counts as done? setpoint
decision What have we committed to, and why? control policy
structure What is the thing made of? plant
resource What do we depend on that we did not make? inputs
activity What actually happened? actuation
artifact What left the team? output
validation How do we know it is any good? measurement

Everything else a project accumulates is who operated the loop (an actor), when (time), or a property of a node (licence, format, size). Keeping those three off the axis is what makes the set finite.

Vocabulary stays open, planes stay fixed: a domain pack registers entity types and maps each to one of the seven. It may never register a layer. Science fills all seven — preregistration and grant aims as intent, protocol and statistical plan as decision, instruments and reagent lots as resource, replication and peer review as validation — which is the useful stress test, because it is the domain furthest from software.

Closure has to be falsifiable or it is a preference, so the test is written down: take a new domain, fill all seven with its real vocabulary, then name knowledge the team demonstrably keeps that fits none. If it is not an actor, a timestamp or a property, the set is broken — and the fix is a superseding ADR that names the domain that broke it (ADR-0019, argued in full in the layers exploration).

What that looks like in the code

Translators now emit only canonical values, and one function at the end of CEM assembly rewrites every containment relation from its target's type — so a snapshot restored from memory when the GitHub cache is cold cannot smuggle a stale layer through. An entity whose type has no mapping gets no layer rather than a convenient default, and shows up ungrouped on the map: a visible modelling gap instead of a quiet lie.

Two things fell out of the migration that were true before it:

  • evidenced_with edges were writing an evidence kind (commit, adr, source_file) into the layer field. Harmless while the axis was arbitrary, actively wrong once it is closed. Those edges carry evidenceKind now.
  • A component reachable by both a live containment edge and an archived one used to land in one bucket because both said apps. After the migration the live edge says structure and the archived one still says apps, so the node was emitted twice and the map crashed. Leaves are claimed by the first bucket in canonical order.

Archived rows keep their old strings on purpose — an archived relation is a fact as recorded — and render as amber off-axis hubs when Show archived is on.

Two empty layers, filled

Adopting the set made two absences measurable. intent existed only as text in the handshake payload, and resource was modelled nowhere at all.

intent now comes from the roadmap: every sprint becomes an entity, with task entities only for the sprint in flight — all ~130 historic tasks would nearly double the graph and drown vitality, so closed sprints keep their checklist in properties. Parsing moved into the scanner, because a sprint has to reach the CEM and the CEM is built there; the handshake's findCurrentSprint kept its exact signature, and its test passing unmodified is the proof the move changed no behaviour. The sprint in flight is pinned into the briefing focus — a brand-new sprint is cold, and vitality ranking would drop the one thing a session most needs.

resource comes from manifests: npm, cargo, pypi and pub, deduplicated and capped. Dev dependencies and workspace-internal packages are out of v0 by choice — volume, not a modelling claim — and transitive trees are out by design, because the layer records what the team chose, not what a resolver produced. Licences cannot come from the declaring manifest (no manifest states its dependency's licence), so they are read from the installed package; under pnpm that means the declaring member's node_modules first, which took coverage from 0 of 9 to 9 of 9 here.

On this repository the axis now reads:

layer live containment relations
activity 45
intent 33
decision 20
structure 17
resource 9
validation 7
artifact 0

artifact is empty and that is honest: nothing in the repo produces a build-result entity yet.

The map reads the same in any domain

The Temperature Map groups by the canonical layers in control-loop order — intent, decision, structure, resource, activity, artifact, validation — rather than alphabetically by whichever tool the data came from. structure splits into app and package sub-hubs, because app-versus-package is a property, not a plane. Off-axis and unmapped members get amber defect hubs that stay visible even when the focus budget packs their members away: a gap you can see beats a gap you cannot.

New this sprint, and the reason the map is usable at all on a 137-entity graph: layer filtering. Chips carry entity counts, clicking one takes that layer out of the view, and hidden layers leave the focus pool — filter to structure alone and the whole budget goes to structure (focus 18 of 105), instead of being burnt on nodes you cannot see. Filtering changes what you look at, never what is stored.

Review notes

The sprint's own code review found four issues worth fixing before the code shipped, all in the new manifest reader and all reproduced rather than argued: @babel/core vanished whenever a workspace directory was called core; URL and VCS lines in requirements.txt became entities named git and https; Cargo's [dependencies.tauri] sub-table form — the one Tauri projects actually use — was invisible; and the pinned sprint repeated the checklist the handshake already carried, 1332 bytes of duplication in a payload the previous sprint had just shrunk. All four are fixed. Two lower-severity findings are recorded and deliberately not fixed yet.

Still open

Sprint 19 is closed on its own tasks; the stretch items are deferred rather than dropped. Evidence kinds for binary and session-shaped work (asset_file, session, review_note) and a domain pack skeleton are what turn "the model could serve a studio or a lab" into "it does", and they deserve a sprint rather than a footnote in this one.

Sprint 20 goes somewhere adjacent: the tool surface is grouped by connector — the same conflation this sprint removed from the layer axis — and it costs about 3k tokens on every request, which nobody had measured. Fixing that also fills the last empty layer, because project_test and project_build currently throw their results away instead of recording them as validation and artifact.

296 tests, up from 235 when the sprint opened.

Principle

An axis is only worth having if it means the same thing everywhere. Close the set, keep the vocabulary open, and make the gaps visible instead of convenient.

← Back to blog