docs/platform-boundary.md
The platform boundary — a measured inventory
Not a packaging plan. This asks which concepts belong to Cognithamus, which belong to HQ, and which belong to a consuming application — and states the evidence for each placement. Packaging is downstream of this document and is not discussed here.
Method
Two independent measurements, because either alone misleads:
- Import closure — does a module depend on domain? Measured with
grepover import specifiers. - Content — does a module contain domain identifiers even when its imports are clean? Measured separately.
plots.ts passes (1) and fails (2). Classifying on imports alone would have
placed it on the platform side.
1. Platform concepts — evidence: closed, and universal by inspection
src/core — closed
Measured: core imports nothing outside core. Not one specifier.
| Module | Concepts |
|---|---|
subject.ts |
SubjectRef, SUBJECT_KINDS, urn |
condition.ts |
CONDITIONS, ConditionId, familyOf |
fact.ts |
Fact (4 families), factIdentity, collapse |
evidence.ts |
Evidence, citeAll |
time.ts |
Instant, Window, Lens |
world.ts |
World, worldAt |
ingest.ts |
Observation, ConnectorManifest, Gap, ingest |
The vocabularies are domain-free by inspection, not by assertion:
subjects: resource, work, actor, agreement, artifact, environment
conditions: unreachable, degraded, unavailable | stalled, blocked, queued,
abandoned | saturated, overcommitted, idle | unprotected,
misconfigured, drifted, unverified | exposed, vulnerable,
unauthorised | overspend, waste, unmonetised
Nothing names code, commits, repositories or agents. Every term applies to a hospital ward, a logistics fleet or a production line without translation.
Derived-geometry mechanics — clean by both measures
metric.ts, layout.ts, allocation.ts, slots.ts, hue.ts import no domain
and contain no domain identifiers. They operate on a weighted directed graph
over opaque ids:
- coupling quasi-metric from exchange weights
- MDS layout with an emitted residual
- stable allocation guided by the metric, honouring a recorded register
- ring slot geometry; identity→colour
These are the strongest platform candidates outside core, and unlike core
they have a demonstrated consumer need: any application rendering related
entities needs proximity that is derived rather than chosen.
2. HQ concepts — belong to the HQ application, not the platform
src/lib/hq — entirely HQ
roster.ts (which agents exist), estate.ts (which products exist),
telemetry.ts (which repositories are read), state.ts, findings.ts,
view.ts, layout.ts. These are HQ's answers to HQ's questions.
The captured lib/world modules
Measured — these import lib/hq:
| Module | HQ imports |
|---|---|
structure.ts |
AGENTS, PRODUCTS/Product, RepoStat |
build.ts |
AGENTS, HqState |
answers.ts |
STALE_DAYS |
substrate/telemetry.ts |
STALE_DAYS/HqState, PRODUCTS |
substrate/runtime.ts |
storedTaskKey |
This is not misplacement. These are the composition and acquisition layer — exactly where a specific domain should be wired into generic machinery. They are correctly HQ-owned and should never be published.
plots.ts, constitution.ts, amendments.ts
plots.ts is clean by imports and carries HQ instance data: academy,
charisma, hq, platform, elite-server, emp, plus agent ids and building
names. The mechanism (an append-only allocation register) is platform-shaped;
this file's contents are HQ's.
constitution.ts and amendments.ts are HQ's own governance. A consuming
application would have its own or none.
3. Enterprise XI concepts
Measured from its fork: PLANS, COURSE/MODULES, its own db, plus
marketplace, pricing and academy surfaces. Cognithamus has no concept of a
subscription plan or a course, and must not acquire one.
4. Modules that cross the boundary incorrectly
Only two placements are actually wrong. The rest are correct-but-entangled.
4.1 types.ts — the contract is HQ-shaped (the significant one)
world/types.ts is what every renderer, substrate and projection compiles
against. Measured contents:
export type DistrictKind = "civic" | "product";
kind: "alert" | "handoff" | "meeting" | "deploy" | "commit"; // WorldEvent
"deploy" and "commit" are software-delivery concepts in the shared type
contract. A hospital has no commits. DistrictKind encodes HQ's spatial
taxonomy. BuildingKind likewise enumerates hall, library, datacenter, security.
This is the exact defect Article V already caught once, in a different guise:
tests/actor-identity.test.ts forbids world/types.ts importing HQ's roster,
because "the contract must not depend on a tenant roster." The import was
removed; the taxonomy was not. The test enforces the letter and the vocabulary
still carries the domain.
4.2 lib/hq/view.ts imports lib/world — the cycle
lib/world imports lib/hq in five modules and lib/hq/view.ts imports back.
Neither is a layer; they are one component in two directories. Any extraction
must break this cycle first, and it cannot be broken by moving files alone.
5. The three-application test
Would a hospital, a logistics fleet and a factory need each concept?
| Concept | Hospital | Fleet | Factory | Verdict |
|---|---|---|---|---|
SubjectRef, urn |
✓ | ✓ | ✓ | platform |
CONDITIONS (20 ids) |
✓ | ✓ | ✓ | platform |
Fact, factIdentity, collapse |
✓ | ✓ | ✓ | platform |
Evidence, citeAll |
✓ | ✓ | ✓ | platform |
Lens, worldAt (bi-temporal) |
✓ | ✓ | ✓ | platform |
ingest, Gap, ConnectorManifest |
✓ | ✓ | ✓ | platform |
| coupling metric / layout / residual | ✓ | ✓ | ✓ | platform |
source: live | unreachable | not-wired |
✓ | ✓ | ✓ | platform |
DistrictKind, BuildingKind |
✗ | ✗ | ✗ | application |
WorldEvent.kind incl. deploy/commit |
✗ | ✗ | ✗ | application |
AGENTS, PRODUCTS, RepoStat |
✗ | ✗ | ✗ | application |
PLANS, COURSE |
✗ | ✗ | ✗ | Enterprise XI |
The test is a thought experiment and is labelled as one. Its value is that it separates concepts by a criterion other than what this repository happens to contain — which is the failure mode of inferring a boundary from one consumer.
6. The smallest stable public API
Ordered by strength of evidence, not by usefulness.
Tier 1 — publish first. The provenance contract:
source: "live" | "unreachable" | "not-wired". Copied verbatim and unmodified
by the only real consumer; domain-free; it is what stops a dashboard rendering
zero as a fact. A type and an invariant, not a module.
Tier 2 — the ontology. SubjectRef/urn, ConditionId/familyOf,
Fact/factIdentity/collapse, Evidence, Lens/worldAt,
Observation/ConnectorManifest/Gap/ingest. Universal by inspection,
closed by measurement, heavily verified — but zero measured demand: the one
consumer took none of it.
Tier 3 — derived geometry. The coupling metric, layout with residual, and stable allocation. Clean by both measures, and the plainest need an application has. Depends on nothing above except opaque subject ids.
Excluded from any public surface: types.ts as it stands, structure.ts,
build.ts, answers.ts, both substrates, plots.ts contents,
constitution.ts, amendments.ts, all of lib/hq.
7. What this does not resolve
types.tsmust be split before anything ships. A publishableWorldcannot enumerate"commit". Whether the split is a generic core plus an application-supplied taxonomy, or two separate types, is undecided.- The
lib/world↔lib/hqcycle must break first, and moving files will not break it. - Tier 2 has no demonstrated demand. Publishing an unrequested interface creates a compatibility obligation with no corresponding benefit. It is listed because it is correct, not because it is wanted — and those were conflated once already.
- Package versus API is untouched here, deliberately.