burgee
Concepts

The family and its layers

One job per package: six leaves that depend on nothing, three packages that compose them, dependencies that point one way, nothing installed from outside the family, and the locks that hold each rule.

What it is

burgee is one package of ten published from this repository. Each owns one job, and has a drop-in path for the packages that do that job today; the package map lists them. They split into three layers:

  • Six leaves depend on nothing: bellpull (subprocesses), closeout (exit handlers and terminal restore), linegauge (text width), paratext (terminal escapes beyond colour), roundel (colour and the output policy) and seniority (configuration).
  • Three compose them: burgee (the CLI framework), flagstaff (rendering) and caique (prompts).
  • One composes those: controlroom (screens), which lays out flagstaff's components and reads keys through caique.

Why it exists

A program can adopt one layer without the others: a CLI that only needs text measured installs linegauge and nothing else. That is only true while the split is real. The moment burgee measures a string's width itself, or reaches for chalk instead of roundel, the layers become a directory layout. The rules below are what keep it real, and each one is a test rather than a convention.

The graph

The family's dependency graph: burgee depends on bellpull, closeout, linegauge, roundel, seniority; caique depends on bellpull, closeout, linegauge, paratext, roundel; controlroom depends on caique, closeout, flagstaff, linegauge, paratext, roundel; flagstaff depends on closeout, linegauge, paratext, roundel; oxmutate depends on bellpull, burgee. The 6 leaves depend on nothing.

5 packages compose, 6 are leaves: 22 dependency edges inside the family, every one pointing down at least one layer, and none outside it. Generated by npm run layers:page from each public package's package.json; do not edit by hand.

PackageLayerDepends onUsed byOutside the family
burgeecomposesbellpull, closeout, linegauge, roundel, seniorityoxmutatenothing
caiquecomposesbellpull, closeout, linegauge, paratext, roundelcontrolroomnothing
controlroomcomposescaique, closeout, flagstaff, linegauge, paratext, roundel—react (optional peer), react-reconciler (optional peer)
flagstaffcomposescloseout, linegauge, paratext, roundelcontrolroomnothing
oxmutatecomposesbellpull, burgee—vitest (optional peer)
bellpullleafnothingburgee, caique, oxmutatenothing
closeoutleafnothingburgee, caique, controlroom, flagstaffnothing
linegaugeleafnothingburgee, caique, controlroom, flagstaffnothing
paratextleafnothingcaique, controlroom, flagstaffnothing
roundelleafnothingburgee, caique, controlroom, flagstaffnothing
seniorityleafnothingburgeenothing

The rules

1. Nothing from outside the family. A published package's dependencies, peerDependencies and optionalDependencies name only packages published from this repository. What a caller installs comes from one repository and one supply chain. Pinned by installs nothing from outside this repository — dependencies, peers or optional (D-111) in package-shape-lock.test.ts, by holds zero external runtime dependencies across the family in layer-boundaries-lock.test.ts, and, against a real install of each package on its own, by installs nothing but itself and the in-family packages it declares in independence-install-lock.test.ts. A README, a description or a docs page that claims a dependency count the manifest contradicts fails dependency-claim-lock.test.ts.

2. Dependencies point one way. package-shape-lock reads the family bottom-up — linegauge, seniority, bellpull, closeout, paratext, roundel, flagstaff, caique, controlroom, burgee — and a package may depend only on one earlier in that order: has no external runtime dependencies, and same-repo ones only point up the family (K1, U1, U6). All six leaves are its foundation tier, graded by %s depends on nothing: it is the floor.

3. Never leaf to leaf. A leaf that depended on another leaf would stop being a leaf, and every package that uses it would sit a third tier up. package-shape-lock states it outright: %s has no edge to a sibling, in any dependency field checks every leaf's dependencies, devDependencies, peerDependencies and optionalDependencies against every workspace in the repo, and fails when %s gains a sibling dependency shows the check going red. The generator of the graph above refuses it too: every edge must end at a package that depends on nothing in the family, and npm run layers:page -- --check runs in the fast job the required Quality Gate reads. paratext is the case that shows the cost: it is a leaf, so its hyperlink detection keeps a declared fork of supports-color rather than importing roundel.

4. No package depends on an incumbent a sibling layer covers. The list of what each layer covers is LAYERS in compat-oracle/src/demand.ts, and that list is the definition of each layer's job: needing string-width is the same thing as needing linegauge. layer-boundaries-lock derives the rule from it — %s depends on no incumbent another layer replaces — so the job table is written once.

5. A sibling owns each mechanism. A dependency is the easy half to check. The expensive half is writing a sibling's code by hand, which leaves no dependency behind. inline-implementation-lock.test.ts scans every package's source for 17 shapes that can only be a layer's job, and fails when a file outside the owner has one:

OwnerMechanisms it owns
bellpullspawning a subprocess; resolving an executable across platforms; looking a command up on PATH
closeoutregistering a signal handler; hiding or showing the cursor; switching raw mode; hooking process exit
roundelreading a colour variable (NO_COLOR, FORCE_COLOR, COLORTERM, CLICOLOR); deciding whether this is CI
senioritywalking up the directory tree; naming a .env file
caiquedecoding keypresses
flagstaffcarrying spinner frames
linegaugematching ANSI escapes with a regex; counting painted rows by their newlines; measuring, wrapping or stripping text by hand
paratext, linegauge, roundel, closeoutspelling a CSI or OSC sequence

A drop-in façade may reproduce an incumbent's API and behaviour; it may not reimplement the mechanism a layer owns (Drop-ins has the rule). The files that still do are rows in the lock's KNOWN table, each with its reason, and the table may only shrink: only goes down and keeps the list honest — an entry that no longer offends must be removed. The lock says plainly what it cannot see: measuring display text with .length looks like any other .length, and still needs a reader.

6. Every layer is used. A layer nothing else uses has never been proven to fit the stack. composition-lock.test.ts reads the band .sdlc/bands/composition.json: the count of edges inside the family may only go up (has at least as many edges as the last time this was recorded), and every package but burgee is used by another or is listed as awaiting one with a reason (%s is used by another package, or is declared as awaiting one). A package that gains a consumer must leave that list. Today flagstaff, caique and controlroom are awaiting: burgee renders no spinner, box or table, and draws no prompt, so an edge to either would be manufactured.

Run it

Each leaf installs and loads on its own. This file imports two leaves and nothing that composes them:

leaves.mjs
import { width } from 'linegauge';
import { colorLevel } from 'roundel/policy';

console.log(width('古池や'), width('\u001B[31mred\u001B[39m'));
console.log(colorLevel({ env: { FORCE_COLOR: '2' }, isTTY: { stdout: false } }));
node leaves.mjs
6 3
2

Where the rules live

On this page