Skip to content

brand: adopt the Oxagen house system — palette, marks, and the face - #6473

Merged
macanderson merged 11 commits into
mainfrom
brand/house-system-website
Sep 9, 2026
Merged

macanderson merged 11 commits into
mainfrom
brand/house-system-website

Conversation

@macanderson

@macanderson macanderson commented Sep 9, 2026 •

Copy link
Copy Markdown
Owner

Brings Stella onto the Oxagen house brand system
(macanderson/oxagen-house-brand), so the two products stop shipping two
identities. Paired with macanderson/oxagen#2785, which does the same on
the Oxagen side.

The two repos had diverged into two golds, two grounds and two typefaces,
and a reader crossing between the sites watched the brand change hue —
the exact failure docs/brand/'s own README has recorded three times.
The house kit is generated for both brands from one source and cuts
both wordmarks out of one typeface, which is what ends it.

Stella's lockup is stella*. Its mark is the asterisk and it already
lives inside the word, so that combined form is the whole lockup and
nothing is ever set to its left. The comet — the four-point star with
three trails — is retired, along with cometkit.py and the typing
animation built around it.

The palette: v6.0, and why the clamps moved

design/tokens/stella-tokens.json adopts the house table. This is a
supersession, not a tune: v5.0 was cool-dark and warm-light (a blue-tipped
ground, two cool silvers, a warm paper ramp, three neutral clamps) and the
house system is warm on one axis end to end, which is what lets a
single gold read as metal against both the ink and the paper. The three
neutral clamps collapse into one warm-neutral.

The gold clamp got stronger where it mattered, not weaker:

  • The green ratio moves 78 → 70. The honest account is that 78 was never
    measured — it was the ratio v5.0's own gold happened to sit at. 70 has
    an argument: sRGB hue 30° is the orange the clause excludes, and
    g >= 0.70 r sits six degrees clear of it.
  • gold-shade is new, and is what the ratio was doing badly. gold-ink is
    a dark shade of the gold; darkening compresses channels unevenly, so a
    ratio measures that rather than hue — and holding a shade to a ratio is
    what drags the ratio down. It is anchored to the gold now, like the lift.
  • The lift tolerance moves 3° → 4°, the figure the JSON already argued for
    and had written as 3 only because the one lift on file sat at 2.9.

The 30° hue-separation law forced one more move. The identity shifted
16° toward the warning, so the warning moved away from it: amber,
amber-ink and red-ink are re-cut at the hues that maximise their
smallest gap. There is about two degrees of room in the whole arc. That is
the law working, not an exception to it — a state colour exists to be
unlike the identity.

crates/stella-tui-theme is regenerated from the same JSON, so the
terminal re-tints with the site.

The site

  • Marks come from the kit via scripts/sync-brand-assets.mjs, which
    replaces mirror-brand-icons.py and sync_site.py. docs/brand/ stops
    being Stella's own kit and becomes a vendored mirror of the house one —
    kept, not deleted, because brand-parity.test.ts runs in CI where the
    house kit is not checked out.
  • Type splits at the kit's line: Space Grotesk is the brand face
    (it is what the wordmark is cut from), JetBrains Mono stays for code and
    terminal transcripts only, where column alignment is load-bearing. It
    used to be both faces.
  • Motion is the kit's shimmer, with its timing extracted from the kit's
    own spinner, so the landing page and every other surface the kit renders
    run one animation.
  • #10100f and #f2eee5 leave the parity test's retired list — they are
    the house ink and off-white, live again. v5.0's anchors join it.

Verification

  • check-tokens, check-hue-separation, check-contrast, check-css-vars
    — all pass
  • cargo test -p stella-tui-theme — 21 passed
  • brand-parity.test.ts — 9 passed
  • node scripts/sync-brand-assets.mjs --check — matches house kit 2.1.0
  • website: tsc --noEmit clean, next build clean
  • Screenshots in the paired Oxagen PR's verifications/ directory.

Deleted tests

Two tests are gone, folded into one that covers both:

  • every_gray_token_is_neutral_or_blue_tipped
  • both_silvers_stay_cool

They existed because v5.0 had three neutral families — a blue-tipped dark
ramp and two cool silvers held to a weaker predicate of their own. The house
system has one family, so both are now
every_neutral_token_is_warm_or_neutral, which walks every token carrying
Clamp::WarmNeutral and asserts it fails if none do. Nothing lost coverage;
the two predicates they tested no longer exist.

Stella and Oxagen shipped two palettes and a reader crossing between the
two sites watched the brand change hue. The house system
(macanderson/oxagen-house-brand) is generated for both brands from one
source and cuts both wordmarks out of one typeface; this is that table in
this repo's token schema, with the guards updated to hold it.

The change is structural, not two anchors. v5.0 was cool-dark and
warm-light -- a blue-tipped ground, two cool silvers, a warm paper ramp,
three neutral clamps. The house system is warm on ONE axis end to end,
which is what lets a single gold read as metal against both the ink and
the paper, so the three collapse into one warm-neutral clamp.

The clamps got stronger, not looser, in the places that mattered:

- The gold's green ratio moves 78 -> 70. The honest account is that 78
  was never measured: it was the ratio v5.0's gold happened to sit at.
  70 has an argument -- sRGB hue 30 deg is the orange the clause excludes,
  and g >= 0.70 r sits six degrees clear of it.
- gold-shade is new, and is what the ratio was doing badly. gold-ink is a
  dark shade of the gold; darkening compresses channels unevenly, so a
  ratio measures that instead of hue, and holding a shade to a ratio is
  what drags the ratio down. It is anchored now, like the lift.
- The lift tolerance moves 3 -> 4 deg, the figure the JSON already argued
  for. It was written as 3 while the only lift on file sat at 2.9.

The 30 deg hue-separation law forced one more move, and it is the law
working rather than an exception to it: the identity moved 16 deg toward
the warning, so the warning moved away. amber, amber-ink and red-ink are
re-cut at the hues that maximise their smallest gap. There is about two
degrees of room in the whole arc.

paper-text is folded into text -- the house system has one off-white, so
the second name was dead. ink and paper-ground now declare
shares_value_with, so a deliberate repeat is a statement a reader can
disagree with while an accidental one still fails.

check-tokens, check-hue-separation, check-contrast, check-css-vars: pass
cargo test -p stella-tui-theme: 21 passed
Stella's mark is the ASTERISK, and it already lives inside the word:
stella*, cut from Space Grotesk at the kit's logo weight with the
asterisk in gold. That combined form IS the lockup and it is the only
one, so nothing is set to the left of the word any more -- the nav, the
OG card and the docs header all show the wordmark alone.

The comet goes with it: the four-point star, its three trails, and the
typing lockup that assembled them. So does its generator. docs/brand/
was Stella's own kit (build_marks.py drawing through cometkit.py,
sync_site.py copying into the site, build_social.py composing banners,
plus the spinner and wallpaper builders); it is now a vendored mirror of
the house kit, written by scripts/sync-brand-assets.mjs.

The mirror is kept rather than deleted because brand-parity.test.ts runs
in CI, where the house kit is not checked out. The kit lands in
docs/brand/ offline and the site is held to that copy, which is the
guarantee that caught the site sitting a whole brand version behind.

- scripts/sync-brand-assets.mjs replaces mirror-brand-icons.py and
  sync_site.py. It also generates brand-marks.generated.ts, so the React
  marks are the kit's own path data rather than a hand-copied string, and
  extracts the shimmer's timing from the kit's spinner so the landing
  page's motion and the kit's are one animation.
- MARK_PATH_FLAT is the asterisk with its placing transform solved into
  the coordinates, for Satori, which has no group transform. The OG
  card's corner sweeps were re-solved for it: the house mark centres on
  (48,48) where the comet's star centred on (64,48), so the old numbers
  would have slid both sweeps ~850px off frame.
- Space Grotesk becomes the brand face. JetBrains Mono stays for code and
  terminal transcripts only, where column alignment is load-bearing. It
  used to be both faces on the argument that a terminal product should
  speak in monospace end to end; the house system settles that the other
  way, and the split now falls where the kit puts it.
- #10100f and #f2eee5 leave the parity test's retired list -- they are the
  house ink and off-white, live again. v5.0's own anchors join it.

website tsc: clean · next build: clean
brand-parity.test.ts: 9 passed
check-tokens, check-hue-separation, check-contrast, check-css-vars: pass

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @macanderson, your pull request is larger than the review limit of 150,000 diff characters

@macanderson macanderson added the closes-nothing Substantial change that closes no issue by design (SCR-003) label Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

SCR-003 DoD check waived by the closes-nothing label — this PR closes no issue by design.

make brand-check runs the sync in --check mode. The kit is a separate
repository and CI checks out this one alone, so --check now SKIPS when it
is absent instead of exiting 2 — a gate that fails on every machine
without a second clone is a gate people learn to ignore. It skips loudly:
the line names what was not verified.

Little is lost by that. brand-parity.test.ts still holds the site to
docs/brand/ on every run, offline, and that is the check that has actually
caught this repo shipping a stale brand. This one adds the outer link --
that docs/brand/ is still the kit.
Three CI guards were reading the world as it was before the house system.

- check-light-clamp.py resolved every light role to the 'warm-paper'
  family, which no longer exists: the three neutral clamps collapsed into
  warm-neutral. Repointed, and the ratchet retightens on 34 entries --
  the light surfaces now clear clamps they used to sit outside, which is
  the change improving them rather than a bound being loosened.
- docs-guards ran docs/brand/social/build_social.py --check, holding a
  banner's install line to README.md. The banners are no longer composed
  here; they are the kit's, vendored. The claim to check moved with them,
  so the step now runs the brand sync in --check mode.
- One comment cited an issue number where the prose guard wants the fact.

make light-clamp-update: 34 entries retightened
check-light-clamp: 6 judged, none off its declared clamp
Four guards were reading the world as it stood before the house system.

- check-light-clamp.py resolved every light role to the "warm-paper"
  family, which the three-into-one collapse removed. Repointed, and the
  ratchet retightens on 34 entries: the light surfaces clear clamps they
  used to sit outside.

- docs-guards ran a builder for social art this repo no longer composes.
  The step now runs the brand sync in --check mode, which is the claim
  that replaced it: every vendored file still matches what the kit emits.

- check-prose.py read #10100F as issue number 10100. The rule matched a
  hash and two digits, and the palette before this one hid the flaw: its
  ground broke after one digit, so the rule looked exact while resting on
  how one colour was spelled. The house ramp spells six stops digit-first
  and the rule started reporting the canvas as a ticket. Length separates
  them exactly -- every colour here is a six-digit hex, an issue number is
  two to five digits and then something that is not a hex digit -- so the
  two exemptions are #RRGGBB and #RRGGBBAA and nothing else. #629 is still
  a hit, which is right. The guard's own 92 self-tests still pass.

- test-contrast.sh measured its fixtures against the cool ground. The
  repaint values were cool greys too, which the warm palette they now test
  cannot contain; re-cut warm at the same lightnesses, same cases.

Reading the retired-value list from the palette's own ban list rather than
restating it found stale art nobody had looked at: vision.html and
investor-deck.html each carried the v4.0 comet on Obsidian as an inline
data-URI favicon. Both now carry the house mark.

The rest is prose. The vocabulary, reading-grade and header-length gates
all hold this branch to the same bar as the tree, and the writing here was
over it -- too much "used to", sentences too long, one header too big.

cargo test -p stella-tui-theme: 21 passed
brand-parity.test.ts: 9 passed · website tsc: clean
check-tokens, hue-separation, contrast, css-vars, light-clamp, doc-links,
gate-parity, brand-sync: pass
test-contrast, test-prose-guard, test-tokens self-tests: pass
every_gray_token_is_neutral_or_blue_tipped and both_silvers_stay_cool are
deleted, folded into every_neutral_token_is_warm_or_neutral.

They existed because v5.0 had three neutral families: a blue-tipped dark
ramp, and two silvers sitting off neutral that carried a weaker predicate
of their own. The house system has one family, so one test walks every
token carrying Clamp::WarmNeutral and fails if none do. Neither predicate
they asserted still exists.

scripts/check-prose.py sat three lines under the 1500-line ceiling, and
the hex exemption pushed it over. The comment is cut to two lines and the
pattern to two, which lands the file at exactly 1500. The rule and its
92 self-tests are unchanged.

check-file-size: pass
check-prose: pass, 92 guard self-tests pass
design_token_parity caught two drifts a hex sweep cannot see, both in
surfaces the palette change had already reached everywhere else.

The washes are written in decimal. rgba(224, 104, 122, .12) is the old
red, rgba(231, 141, 84, ...) the old amber, rgba(150, 33, 60, ...) the old
red ink -- nine of them across the Observatory's index.html and the
transcript stylesheet, each a colour re-typed by channel rather than
derived from its token, which is exactly why they outlive the recolours
that should carry them. Re-cut against the verdict hues the 30 deg
separation law moved.

The Observatory's palette block argues from measured angles, and every one
of them moved with the identity:

  --bad          12.8 deg -> 11.2
  identity       90.8 deg -> 74.8
  warn->identity 39.1 deg -> 31.8
  warn->bad      38.9 deg -> 31.8
  ok->identity   63.1 deg -> 79.0
  bad->identity  78.0 deg -> 63.7
  warn contrast  7.85 / 7.60 / 7.10 -> 7.51 / 7.06 / 6.50

The block now also states the reason the warning sits where it does: the
house gold is 16 deg nearer --bad than the gold before it, so the arc
between them is 63.7 deg wide and holds about two degrees of room for a
warning 30 deg from both ends. The parity table's rows move with the
prose, which is what holds the two together.

cargo test -p stella-cli --test design_token_parity: 13 passed
cargo test -p stella-tui-theme -p stella-observatory -p stella-transcript: pass
cargo fmt --check: clean
check-tokens, hue-separation, contrast, css-vars, light-clamp, prose,
file-size, brand-sync: pass
The terminal's dark ramp aliases the token table, so it moved with the
palette. Its PAPER ramp did not: those were hexes derived on their own,
which the token JSON recorded as an open question -- the paper theme "not
yet reconciled against this ramp". Half a palette warm and half cool is
the one outcome neither argument permits, so this closes it.

PAPER, SNOW, PAPER_RAISED, PAPER_HAIRLINE, INK_MUTED and HAIRLINE_STRONG
now come from the token table. paper-ground, paper-raised, paper-row,
paper-seam and ink-muted carry `rust` names to make that possible; five
stops still have none, and the doc says which and why. INK_DIM and
INK_EMPHASIS stay written out, because this ramp's name for the tier and
the dark ramp's name for that value are different words.

The neutral law inverts, and the reason is worth having in one place. It
read "cool" while the accent was a lemon: a warm neutral beside that
accent sits in its own family and the screen reads muddy on a cheap panel.
The house gold is a deeper metal and the kit answers the same question the
other way -- one warm axis end to end, which is what lets a single gold
read as metal against both the ink and the paper.

Ten RETIRED_WARM_* bans come off the list with it. The v2.0 warm ground,
void, surface, raised, hairline, three text tiers, paper and snow are live
again; a value leaves that list when a later kit restores it, and ten did
at once.

The neutral test needed a better instrument. A channel span cannot answer
"does this have a hue" across a ramp from near-black to near-white -- the
hairline on ink spends 7 levels of 41 and the seam on paper spends 27 of
216, and both are the same grey, so a span admitting one reads the other
as a colour. oklch::chroma is lightness-independent and answers it: every
neutral here sits at or under 0.026 and every chromatic one at or above
0.090. The visible-cast checks keep absolute spans, which is the right
instrument for a different question.

Every stated angle in palette.rs moved with the identity, and the parity
table's rows moved with the prose: the brand hue 90.8 -> 74.8, warning
51.7 -> 43.0, danger 12.8 -> 11.2, and each separation and contrast
figure they argue from.

cargo test -p stella-tui --lib: 1524 passed
cargo test -p stella-cli --test design_token_parity: 13 passed
cargo fmt --check: clean
check-tokens, hue-separation, contrast, css-vars, light-clamp, prose,
file-size, brand-sync: pass
test-light-clamp's W2 case asserts the count --update reports, and the
count moved when the ratchet retightened: the house system's light
surfaces clear clamps the previous ones sat outside, so seven entries
left the ledger.

scripts/test-light-clamp.sh: 19 passed
test-contrast, test-prose-guard, test-tokens: pass
Promoting paper-ground, paper-raised, paper-row, paper-seam and ink-muted
to rust names put them in token::ALL, and every token there owes SPEC 3.5
a row saying what it becomes at sixteen colours. Without one, they reach a
sixteen-colour terminal as truecolor through the match's catch-all.

The paper ramp collapses hardest of all: at sixteen colours a paper ground
IS white, and there is no lighter tier to tell a canvas from a panel, so
the four light surfaces go to bright white together and the two seams take
the one gray left. ink_muted is text on that white and goes to black with
ink.

cargo test -p stella-tui --test spec_palette: 5 passed
cargo test -p stella-tui -p stella-tui-theme: all pass
cargo fmt --check: clean
every colour, doc, prose and size guard: pass
clippy -D warnings caught the arm as unreachable. Under the house system
one off-white serves as the light page and as the ink on the dark canvas,
so token::PAPER_GROUND and token::TEXT hold the same value and the earlier
arm already matches it. Both land on white, which is what SPEC 3.5 says
for each. Same shape as INK, which is BG.

clippy --workspace --all-targets -D warnings: clean
stella-tui + stella-tui-theme suites: pass
design_token_parity: 13 passed
cargo fmt --check: clean
check-prose, check-file-size: pass
@macanderson
macanderson merged commit cd7d53c into main Sep 9, 2026
30 checks passed
@macanderson
macanderson deleted the brand/house-system-website branch September 9, 2026 16:57
macanderson added a commit that referenced this pull request Sep 12, 2026
… Oxagen (#6509)

docs(readme): fix the broken logo and state where Stella sits next to
Oxagen

The README header pointed at docs/brand/logo/svg/lockup-color-light.svg,
which #6473 (the house-system adoption) deleted along with the rest of
the
comet kit, so GitHub rendered a broken image above the badges. It now
uses
the vendored stella-wordmark-{dark,light}.svg pair behind
prefers-color-scheme
sources, with the light file as the fallback.

The copy is brought onto the same position the site and the Oxagen repo
already state:

- The tagline is the site's h1 ("a terminal coding agent that proves its
  work finished") in place of "Reference Grade Agent Loop".
- A short "Stella and Oxagen" section states the split from ADR-043 and
ADR-053 on the Oxagen side: Stella runs the agent, Oxagen governs the
run;
  Stella is the coding agent and the CGP/trace reference implementation,
  Oxagen is the governance plane with no agent loop; the two meet at the
opt-in evidence drain and at stella-serve, which Oxagen's in-app
assistant
  runs on. It also names the shared house brand and the stella* mark.
- The workspace-layout paragraph that called
crates/stella-tui/src/palette.rs
the hand-maintained palette mirrored by tokens.css is replaced with what
is
true since v5.0 and #6473: design/tokens/stella-tokens.json is
normative,
  gen-tokens.py emits the terminal tokens and the CSS, and
  sync-brand-assets.mjs vendors the marks from the house kit.

Verified: check-doc-links, check-invariants, check-command-docs,
check-brand-case, check-line-citations, check-rendering-facts and
make tokens all pass; every relative link and image path in the README
resolves.

## Summary by Sourcery

Update the README to restore the logo, accurately position Stella
alongside Oxagen, and document the current branding workflow.

Bug Fixes:
- Fix the README logo rendering by using the vendored dark and light
Stella wordmarks with a light-theme fallback.

Enhancements:
- Clarify Stella’s relationship with Oxagen, including their distinct
responsibilities, integration points, and shared branding.
- Update the workspace documentation to describe the normative token
source and generated brand assets accurately.

Documentation:
- Refresh the README tagline, privacy wording, product positioning, and
workspace layout guidance.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

closes-nothing Substantial change that closes no issue by design (SCR-003)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant