Add CI, generate tokens from tokens.json, gate shared/ against client-shared - #7

Merged
brentrager merged 1 commit into
mainfrom
fix/parity
Aug 20, 2026
Merged

Add CI, generate tokens from tokens.json, gate shared/ against client-shared#7
brentrager merged 1 commit into
mainfrom
fix/parity

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Why

This repo had no CI. .github/ held two banner images and nothing else — no build, no test, no lint. A change could land unbuilt, and shared/ could diverge from SmooAI/client-shared with nothing to say so.

It already had diverged. The review that prompted this recorded the two repos' shared/ as byte-identical; on origin/main this morning they were not:

shared/styles.css ui=26ee714d… client-shared=c23abd72… DIFFER
shared/monogram.svg ui=a2db97f8… client-shared=565d6108… DIFFER

The monogram fix in f230808"restore the inner 'S' curve and the dot" — never crossed. client-shared, which is what the th CLI ships in production, served a monogram with no S and no dot. Its styles.css had separately lost the entire .input family, and its class-existence test had drifted along with it so nothing was red. Both repos were green the whole time, because neither ran anything.

The upstream half is fixed in SmooAI/client-shared#7. This PR is the gate that stops it recurring.

What changed

CI exists.github/workflows/rust.yml: cargo fmt --check, clippy --all-targets -D warnings, tests, the module-tree guard, and the shared/ drift gate.

shared/ drift gate. client-shared owns the design system; this repo keeps a copy for observability-studio and smooblue, and CI fails if they differ. It compares git blob SHAs from the GitHub contents API against git ls-tree, so added and deleted files are caught as well as edited ones — there is no hand-maintained filename list to fall behind, which is the failure mode a for f in a b c loop would have.

shared/tokens.json is code input now. It was read by no code in any language while the Rust constants hand-mirrored the same values. rust/build.rs generates the whole tokens module from it via shared/tokens_codegen.rs, so a token cannot exist in the design system and be missing from Rust — nobody types the constants. That immediately surfaced six tokens the CSS declared and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING, SIDEBAR) plus the radius / space / font scales. RADIUS_PX is kept as an alias of the generated RADIUS_MD_PX so observability-studio (pinned at tag = v0.1.0) keeps compiling when it retags.

tokens_match_css could not fail in the direction that mattered. It asserted each constant appeared somewhere in the CSS as a substring — it passes with a constant bound to the wrong custom property, and nothing walked the CSS, so a token the CSS had and Rust lacked was structurally invisible. shared/tokens_css_check.rs parses :root, resolves var() to a fixed point, and asserts both directions.

Module-tree guard.scripts/check-module-tree.py fails if any .rs under rust/src/ is unreachable from a mod declaration — the class that let client-shared ship 174 lines of auth/refresh.rs that never compiled once, invisible to build, clippy, test and coverage alike.

Verification — every new check confirmed red before green

CheckBroken howResult
tokens_match_cssdrifted color.brand.orange in tokens.jsonFAILED
css_colors_are_all_tokensadded --surprise: oklch(…) to styles.cssFAILED
check-module-tree.pyplanted an orphan .rsexit 1, named the file

shared/ verified byte-identical to SmooAI/client-shared@main (7c5b948) — all five blob SHAs equal. Tests 6/6, fmt and clippy --all-targets -D warnings clean.

Judgment calls

  • Gate, not a cargo dependency on client-shared. ui could have become a thin re-export, deleting shared/ outright — both crates are git deps rather than crates.io publishes, so it is possible. But it would put two independently rev-pinned git deps in one graph for any consumer that ever wants both, and ui's live consumer is tag-pinned. The gate closes the silent-divergence defect without the coupling.
  • The gate is one-directional. Bidirectional deadlocks: neither repo's PR could go green until the other merged. client-shared is the source and is ungated.
  • This repo is not archived. See the note in the report — whether ui should eventually fold into client-shared is a call for its owner, not for this PR.

Pearls: th-42c460, th-ea969b

🤖 Generated with Claude Code

https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC

…ient-shared
This repo had NO CI — `.github/` held only banner images — so a change could
land unbuilt and `shared/` could diverge from SmooAI/client-shared with nothing
to say so. It already had: the monogram fix in f230808 ("restore the inner 'S'
curve and the dot") never crossed, so client-shared served a monogram with no S
and no dot, and its styles.css lost the whole `.input` family. Both repos were
green the entire time, because neither ran anything.
- **CI** (`.github/workflows/rust.yml`): fmt, clippy `--all-targets -D warnings`,
tests, the module-tree guard, and the shared/ drift gate.
- **`shared/` drift gate.** SmooAI/client-shared owns the design system (it is
the declared successor and what `th` ships); this repo keeps a copy for
observability-studio and smooblue, and CI fails if the copies differ. The gate
compares git blob SHAs from the contents API against `git ls-tree`, so adds and
deletes are caught too — no filename list to fall behind. Deliberately
one-directional: client-shared is ungated so a change lands there and this repo
follows; a bidirectional gate deadlocks, with neither PR able to green until
the other merges. Chosen over a cargo dependency on client-shared because both
crates are git deps, and depending across would put two independently
rev-pinned git deps in one graph for any consumer wanting both.
- **`shared/tokens.json` was inert** — read by no code in any language while the
Rust constants hand-mirrored the same values. `rust/build.rs` now generates the
whole `tokens` module from it via `shared/tokens_codegen.rs`, so a token cannot
exist in the design system and be missing from Rust. That surfaced six tokens
the CSS had and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING,
SIDEBAR) plus the radius/space/font scales. `RADIUS_PX` stays as an alias of
the generated `RADIUS_MD_PX` so observability-studio keeps compiling.
- **`tokens_match_css` could not fail in the direction that mattered.** It
asserted each constant appeared *somewhere* in the CSS as a substring: it
passes with a constant bound to the wrong property, and nothing walked the CSS,
so a token the CSS had and Rust lacked was invisible. `shared/tokens_css_check.rs`
parses `:root`, resolves `var()`, and checks both directions. Verified red by
drifting a token value and by adding an untracked `--surprise` colour.
- **Module-tree guard** (`scripts/check-module-tree.py`): fails if any `.rs` file
under `rust/src/` is unreachable from a `mod` declaration — the class that let
client-shared ship 174 lines of `auth/refresh.rs` that never compiled.
shared/ is byte-identical to SmooAI/client-shared@main (7c5b948); all five blob
SHAs verified equal. Tests 6/6, fmt + clippy clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC
@changeset-bot

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9305d85

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager
brentrager merged commit 7b71767 into mainAug 20, 2026
3 checks passed
@brentrager
brentrager deleted the fix/parity branch August 20, 2026 18:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brentrager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add CI, generate tokens from tokens.json, gate shared/ against client-shared - #7

Merged
brentrager merged 1 commit into
mainfrom
fix/parity
Aug 20, 2026
Merged

Add CI, generate tokens from tokens.json, gate shared/ against client-shared#7
brentrager merged 1 commit into
mainfrom
fix/parity

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Why

This repo had no CI. .github/ held two banner images and nothing else — no build, no test, no lint. A change could land unbuilt, and shared/ could diverge from SmooAI/client-shared with nothing to say so.

It already had diverged. The review that prompted this recorded the two repos' shared/ as byte-identical; on origin/main this morning they were not:

shared/styles.css ui=26ee714d… client-shared=c23abd72… DIFFER
shared/monogram.svg ui=a2db97f8… client-shared=565d6108… DIFFER

The monogram fix in f230808"restore the inner 'S' curve and the dot" — never crossed. client-shared, which is what the th CLI ships in production, served a monogram with no S and no dot. Its styles.css had separately lost the entire .input family, and its class-existence test had drifted along with it so nothing was red. Both repos were green the whole time, because neither ran anything.

The upstream half is fixed in SmooAI/client-shared#7. This PR is the gate that stops it recurring.

What changed

CI exists.github/workflows/rust.yml: cargo fmt --check, clippy --all-targets -D warnings, tests, the module-tree guard, and the shared/ drift gate.

shared/ drift gate. client-shared owns the design system; this repo keeps a copy for observability-studio and smooblue, and CI fails if they differ. It compares git blob SHAs from the GitHub contents API against git ls-tree, so added and deleted files are caught as well as edited ones — there is no hand-maintained filename list to fall behind, which is the failure mode a for f in a b c loop would have.

shared/tokens.json is code input now. It was read by no code in any language while the Rust constants hand-mirrored the same values. rust/build.rs generates the whole tokens module from it via shared/tokens_codegen.rs, so a token cannot exist in the design system and be missing from Rust — nobody types the constants. That immediately surfaced six tokens the CSS declared and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING, SIDEBAR) plus the radius / space / font scales. RADIUS_PX is kept as an alias of the generated RADIUS_MD_PX so observability-studio (pinned at tag = v0.1.0) keeps compiling when it retags.

tokens_match_css could not fail in the direction that mattered. It asserted each constant appeared somewhere in the CSS as a substring — it passes with a constant bound to the wrong custom property, and nothing walked the CSS, so a token the CSS had and Rust lacked was structurally invisible. shared/tokens_css_check.rs parses :root, resolves var() to a fixed point, and asserts both directions.

Module-tree guard.scripts/check-module-tree.py fails if any .rs under rust/src/ is unreachable from a mod declaration — the class that let client-shared ship 174 lines of auth/refresh.rs that never compiled once, invisible to build, clippy, test and coverage alike.

Verification — every new check confirmed red before green

CheckBroken howResult
tokens_match_cssdrifted color.brand.orange in tokens.jsonFAILED
css_colors_are_all_tokensadded --surprise: oklch(…) to styles.cssFAILED
check-module-tree.pyplanted an orphan .rsexit 1, named the file

shared/ verified byte-identical to SmooAI/client-shared@main (7c5b948) — all five blob SHAs equal. Tests 6/6, fmt and clippy --all-targets -D warnings clean.

Judgment calls

  • Gate, not a cargo dependency on client-shared. ui could have become a thin re-export, deleting shared/ outright — both crates are git deps rather than crates.io publishes, so it is possible. But it would put two independently rev-pinned git deps in one graph for any consumer that ever wants both, and ui's live consumer is tag-pinned. The gate closes the silent-divergence defect without the coupling.
  • The gate is one-directional. Bidirectional deadlocks: neither repo's PR could go green until the other merged. client-shared is the source and is ungated.
  • This repo is not archived. See the note in the report — whether ui should eventually fold into client-shared is a call for its owner, not for this PR.

Pearls: th-42c460, th-ea969b

🤖 Generated with Claude Code

https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC

…ient-shared
This repo had NO CI — `.github/` held only banner images — so a change could
land unbuilt and `shared/` could diverge from SmooAI/client-shared with nothing
to say so. It already had: the monogram fix in f230808 ("restore the inner 'S'
curve and the dot") never crossed, so client-shared served a monogram with no S
and no dot, and its styles.css lost the whole `.input` family. Both repos were
green the entire time, because neither ran anything.
- **CI** (`.github/workflows/rust.yml`): fmt, clippy `--all-targets -D warnings`,
tests, the module-tree guard, and the shared/ drift gate.
- **`shared/` drift gate.** SmooAI/client-shared owns the design system (it is
the declared successor and what `th` ships); this repo keeps a copy for
observability-studio and smooblue, and CI fails if the copies differ. The gate
compares git blob SHAs from the contents API against `git ls-tree`, so adds and
deletes are caught too — no filename list to fall behind. Deliberately
one-directional: client-shared is ungated so a change lands there and this repo
follows; a bidirectional gate deadlocks, with neither PR able to green until
the other merges. Chosen over a cargo dependency on client-shared because both
crates are git deps, and depending across would put two independently
rev-pinned git deps in one graph for any consumer wanting both.
- **`shared/tokens.json` was inert** — read by no code in any language while the
Rust constants hand-mirrored the same values. `rust/build.rs` now generates the
whole `tokens` module from it via `shared/tokens_codegen.rs`, so a token cannot
exist in the design system and be missing from Rust. That surfaced six tokens
the CSS had and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING,
SIDEBAR) plus the radius/space/font scales. `RADIUS_PX` stays as an alias of
the generated `RADIUS_MD_PX` so observability-studio keeps compiling.
- **`tokens_match_css` could not fail in the direction that mattered.** It
asserted each constant appeared *somewhere* in the CSS as a substring: it
passes with a constant bound to the wrong property, and nothing walked the CSS,
so a token the CSS had and Rust lacked was invisible. `shared/tokens_css_check.rs`
parses `:root`, resolves `var()`, and checks both directions. Verified red by
drifting a token value and by adding an untracked `--surprise` colour.
- **Module-tree guard** (`scripts/check-module-tree.py`): fails if any `.rs` file
under `rust/src/` is unreachable from a `mod` declaration — the class that let
client-shared ship 174 lines of `auth/refresh.rs` that never compiled.
shared/ is byte-identical to SmooAI/client-shared@main (7c5b948); all five blob
SHAs verified equal. Tests 6/6, fmt + clippy clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC
@changeset-bot

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9305d85

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager
brentrager merged commit 7b71767 into mainAug 20, 2026
3 checks passed
@brentrager
brentrager deleted the fix/parity branch August 20, 2026 18:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brentrager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add CI, generate tokens from tokens.json, gate shared/ against client-shared - #7

Merged
brentrager merged 1 commit into
mainfrom
fix/parity
Aug 20, 2026
Merged

Add CI, generate tokens from tokens.json, gate shared/ against client-shared#7
brentrager merged 1 commit into
mainfrom
fix/parity

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Why

This repo had no CI. .github/ held two banner images and nothing else — no build, no test, no lint. A change could land unbuilt, and shared/ could diverge from SmooAI/client-shared with nothing to say so.

It already had diverged. The review that prompted this recorded the two repos' shared/ as byte-identical; on origin/main this morning they were not:

shared/styles.css ui=26ee714d… client-shared=c23abd72… DIFFER
shared/monogram.svg ui=a2db97f8… client-shared=565d6108… DIFFER

The monogram fix in f230808"restore the inner 'S' curve and the dot" — never crossed. client-shared, which is what the th CLI ships in production, served a monogram with no S and no dot. Its styles.css had separately lost the entire .input family, and its class-existence test had drifted along with it so nothing was red. Both repos were green the whole time, because neither ran anything.

The upstream half is fixed in SmooAI/client-shared#7. This PR is the gate that stops it recurring.

What changed

CI exists.github/workflows/rust.yml: cargo fmt --check, clippy --all-targets -D warnings, tests, the module-tree guard, and the shared/ drift gate.

shared/ drift gate. client-shared owns the design system; this repo keeps a copy for observability-studio and smooblue, and CI fails if they differ. It compares git blob SHAs from the GitHub contents API against git ls-tree, so added and deleted files are caught as well as edited ones — there is no hand-maintained filename list to fall behind, which is the failure mode a for f in a b c loop would have.

shared/tokens.json is code input now. It was read by no code in any language while the Rust constants hand-mirrored the same values. rust/build.rs generates the whole tokens module from it via shared/tokens_codegen.rs, so a token cannot exist in the design system and be missing from Rust — nobody types the constants. That immediately surfaced six tokens the CSS declared and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING, SIDEBAR) plus the radius / space / font scales. RADIUS_PX is kept as an alias of the generated RADIUS_MD_PX so observability-studio (pinned at tag = v0.1.0) keeps compiling when it retags.

tokens_match_css could not fail in the direction that mattered. It asserted each constant appeared somewhere in the CSS as a substring — it passes with a constant bound to the wrong custom property, and nothing walked the CSS, so a token the CSS had and Rust lacked was structurally invisible. shared/tokens_css_check.rs parses :root, resolves var() to a fixed point, and asserts both directions.

Module-tree guard.scripts/check-module-tree.py fails if any .rs under rust/src/ is unreachable from a mod declaration — the class that let client-shared ship 174 lines of auth/refresh.rs that never compiled once, invisible to build, clippy, test and coverage alike.

Verification — every new check confirmed red before green

CheckBroken howResult
tokens_match_cssdrifted color.brand.orange in tokens.jsonFAILED
css_colors_are_all_tokensadded --surprise: oklch(…) to styles.cssFAILED
check-module-tree.pyplanted an orphan .rsexit 1, named the file

shared/ verified byte-identical to SmooAI/client-shared@main (7c5b948) — all five blob SHAs equal. Tests 6/6, fmt and clippy --all-targets -D warnings clean.

Judgment calls

  • Gate, not a cargo dependency on client-shared. ui could have become a thin re-export, deleting shared/ outright — both crates are git deps rather than crates.io publishes, so it is possible. But it would put two independently rev-pinned git deps in one graph for any consumer that ever wants both, and ui's live consumer is tag-pinned. The gate closes the silent-divergence defect without the coupling.
  • The gate is one-directional. Bidirectional deadlocks: neither repo's PR could go green until the other merged. client-shared is the source and is ungated.
  • This repo is not archived. See the note in the report — whether ui should eventually fold into client-shared is a call for its owner, not for this PR.

Pearls: th-42c460, th-ea969b

🤖 Generated with Claude Code

https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC

…ient-shared
This repo had NO CI — `.github/` held only banner images — so a change could
land unbuilt and `shared/` could diverge from SmooAI/client-shared with nothing
to say so. It already had: the monogram fix in f230808 ("restore the inner 'S'
curve and the dot") never crossed, so client-shared served a monogram with no S
and no dot, and its styles.css lost the whole `.input` family. Both repos were
green the entire time, because neither ran anything.
- **CI** (`.github/workflows/rust.yml`): fmt, clippy `--all-targets -D warnings`,
tests, the module-tree guard, and the shared/ drift gate.
- **`shared/` drift gate.** SmooAI/client-shared owns the design system (it is
the declared successor and what `th` ships); this repo keeps a copy for
observability-studio and smooblue, and CI fails if the copies differ. The gate
compares git blob SHAs from the contents API against `git ls-tree`, so adds and
deletes are caught too — no filename list to fall behind. Deliberately
one-directional: client-shared is ungated so a change lands there and this repo
follows; a bidirectional gate deadlocks, with neither PR able to green until
the other merges. Chosen over a cargo dependency on client-shared because both
crates are git deps, and depending across would put two independently
rev-pinned git deps in one graph for any consumer wanting both.
- **`shared/tokens.json` was inert** — read by no code in any language while the
Rust constants hand-mirrored the same values. `rust/build.rs` now generates the
whole `tokens` module from it via `shared/tokens_codegen.rs`, so a token cannot
exist in the design system and be missing from Rust. That surfaced six tokens
the CSS had and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING,
SIDEBAR) plus the radius/space/font scales. `RADIUS_PX` stays as an alias of
the generated `RADIUS_MD_PX` so observability-studio keeps compiling.
- **`tokens_match_css` could not fail in the direction that mattered.** It
asserted each constant appeared *somewhere* in the CSS as a substring: it
passes with a constant bound to the wrong property, and nothing walked the CSS,
so a token the CSS had and Rust lacked was invisible. `shared/tokens_css_check.rs`
parses `:root`, resolves `var()`, and checks both directions. Verified red by
drifting a token value and by adding an untracked `--surprise` colour.
- **Module-tree guard** (`scripts/check-module-tree.py`): fails if any `.rs` file
under `rust/src/` is unreachable from a `mod` declaration — the class that let
client-shared ship 174 lines of `auth/refresh.rs` that never compiled.
shared/ is byte-identical to SmooAI/client-shared@main (7c5b948); all five blob
SHAs verified equal. Tests 6/6, fmt + clippy clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC
@changeset-bot

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9305d85

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager
brentrager merged commit 7b71767 into mainAug 20, 2026
3 checks passed
@brentrager
brentrager deleted the fix/parity branch August 20, 2026 18:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brentrager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add CI, generate tokens from tokens.json, gate shared/ against client-shared - #7

Merged
brentrager merged 1 commit into
mainfrom
fix/parity
Aug 20, 2026
Merged

Add CI, generate tokens from tokens.json, gate shared/ against client-shared#7
brentrager merged 1 commit into
mainfrom
fix/parity

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Why

This repo had no CI. .github/ held two banner images and nothing else — no build, no test, no lint. A change could land unbuilt, and shared/ could diverge from SmooAI/client-shared with nothing to say so.

It already had diverged. The review that prompted this recorded the two repos' shared/ as byte-identical; on origin/main this morning they were not:

shared/styles.css ui=26ee714d… client-shared=c23abd72… DIFFER
shared/monogram.svg ui=a2db97f8… client-shared=565d6108… DIFFER

The monogram fix in f230808"restore the inner 'S' curve and the dot" — never crossed. client-shared, which is what the th CLI ships in production, served a monogram with no S and no dot. Its styles.css had separately lost the entire .input family, and its class-existence test had drifted along with it so nothing was red. Both repos were green the whole time, because neither ran anything.

The upstream half is fixed in SmooAI/client-shared#7. This PR is the gate that stops it recurring.

What changed

CI exists.github/workflows/rust.yml: cargo fmt --check, clippy --all-targets -D warnings, tests, the module-tree guard, and the shared/ drift gate.

shared/ drift gate. client-shared owns the design system; this repo keeps a copy for observability-studio and smooblue, and CI fails if they differ. It compares git blob SHAs from the GitHub contents API against git ls-tree, so added and deleted files are caught as well as edited ones — there is no hand-maintained filename list to fall behind, which is the failure mode a for f in a b c loop would have.

shared/tokens.json is code input now. It was read by no code in any language while the Rust constants hand-mirrored the same values. rust/build.rs generates the whole tokens module from it via shared/tokens_codegen.rs, so a token cannot exist in the design system and be missing from Rust — nobody types the constants. That immediately surfaced six tokens the CSS declared and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING, SIDEBAR) plus the radius / space / font scales. RADIUS_PX is kept as an alias of the generated RADIUS_MD_PX so observability-studio (pinned at tag = v0.1.0) keeps compiling when it retags.

tokens_match_css could not fail in the direction that mattered. It asserted each constant appeared somewhere in the CSS as a substring — it passes with a constant bound to the wrong custom property, and nothing walked the CSS, so a token the CSS had and Rust lacked was structurally invisible. shared/tokens_css_check.rs parses :root, resolves var() to a fixed point, and asserts both directions.

Module-tree guard.scripts/check-module-tree.py fails if any .rs under rust/src/ is unreachable from a mod declaration — the class that let client-shared ship 174 lines of auth/refresh.rs that never compiled once, invisible to build, clippy, test and coverage alike.

Verification — every new check confirmed red before green

CheckBroken howResult
tokens_match_cssdrifted color.brand.orange in tokens.jsonFAILED
css_colors_are_all_tokensadded --surprise: oklch(…) to styles.cssFAILED
check-module-tree.pyplanted an orphan .rsexit 1, named the file

shared/ verified byte-identical to SmooAI/client-shared@main (7c5b948) — all five blob SHAs equal. Tests 6/6, fmt and clippy --all-targets -D warnings clean.

Judgment calls

  • Gate, not a cargo dependency on client-shared. ui could have become a thin re-export, deleting shared/ outright — both crates are git deps rather than crates.io publishes, so it is possible. But it would put two independently rev-pinned git deps in one graph for any consumer that ever wants both, and ui's live consumer is tag-pinned. The gate closes the silent-divergence defect without the coupling.
  • The gate is one-directional. Bidirectional deadlocks: neither repo's PR could go green until the other merged. client-shared is the source and is ungated.
  • This repo is not archived. See the note in the report — whether ui should eventually fold into client-shared is a call for its owner, not for this PR.

Pearls: th-42c460, th-ea969b

🤖 Generated with Claude Code

https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC

…ient-shared
This repo had NO CI — `.github/` held only banner images — so a change could
land unbuilt and `shared/` could diverge from SmooAI/client-shared with nothing
to say so. It already had: the monogram fix in f230808 ("restore the inner 'S'
curve and the dot") never crossed, so client-shared served a monogram with no S
and no dot, and its styles.css lost the whole `.input` family. Both repos were
green the entire time, because neither ran anything.
- **CI** (`.github/workflows/rust.yml`): fmt, clippy `--all-targets -D warnings`,
tests, the module-tree guard, and the shared/ drift gate.
- **`shared/` drift gate.** SmooAI/client-shared owns the design system (it is
the declared successor and what `th` ships); this repo keeps a copy for
observability-studio and smooblue, and CI fails if the copies differ. The gate
compares git blob SHAs from the contents API against `git ls-tree`, so adds and
deletes are caught too — no filename list to fall behind. Deliberately
one-directional: client-shared is ungated so a change lands there and this repo
follows; a bidirectional gate deadlocks, with neither PR able to green until
the other merges. Chosen over a cargo dependency on client-shared because both
crates are git deps, and depending across would put two independently
rev-pinned git deps in one graph for any consumer wanting both.
- **`shared/tokens.json` was inert** — read by no code in any language while the
Rust constants hand-mirrored the same values. `rust/build.rs` now generates the
whole `tokens` module from it via `shared/tokens_codegen.rs`, so a token cannot
exist in the design system and be missing from Rust. That surfaced six tokens
the CSS had and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING,
SIDEBAR) plus the radius/space/font scales. `RADIUS_PX` stays as an alias of
the generated `RADIUS_MD_PX` so observability-studio keeps compiling.
- **`tokens_match_css` could not fail in the direction that mattered.** It
asserted each constant appeared *somewhere* in the CSS as a substring: it
passes with a constant bound to the wrong property, and nothing walked the CSS,
so a token the CSS had and Rust lacked was invisible. `shared/tokens_css_check.rs`
parses `:root`, resolves `var()`, and checks both directions. Verified red by
drifting a token value and by adding an untracked `--surprise` colour.
- **Module-tree guard** (`scripts/check-module-tree.py`): fails if any `.rs` file
under `rust/src/` is unreachable from a `mod` declaration — the class that let
client-shared ship 174 lines of `auth/refresh.rs` that never compiled.
shared/ is byte-identical to SmooAI/client-shared@main (7c5b948); all five blob
SHAs verified equal. Tests 6/6, fmt + clippy clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC
@changeset-bot

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9305d85

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager
brentrager merged commit 7b71767 into mainAug 20, 2026
3 checks passed
@brentrager
brentrager deleted the fix/parity branch August 20, 2026 18:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brentrager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add CI, generate tokens from tokens.json, gate shared/ against client-shared - #7

Merged
brentrager merged 1 commit into
mainfrom
fix/parity
Aug 20, 2026
Merged

Add CI, generate tokens from tokens.json, gate shared/ against client-shared#7
brentrager merged 1 commit into
mainfrom
fix/parity

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Why

This repo had no CI. .github/ held two banner images and nothing else — no build, no test, no lint. A change could land unbuilt, and shared/ could diverge from SmooAI/client-shared with nothing to say so.

It already had diverged. The review that prompted this recorded the two repos' shared/ as byte-identical; on origin/main this morning they were not:

shared/styles.css ui=26ee714d… client-shared=c23abd72… DIFFER
shared/monogram.svg ui=a2db97f8… client-shared=565d6108… DIFFER

The monogram fix in f230808"restore the inner 'S' curve and the dot" — never crossed. client-shared, which is what the th CLI ships in production, served a monogram with no S and no dot. Its styles.css had separately lost the entire .input family, and its class-existence test had drifted along with it so nothing was red. Both repos were green the whole time, because neither ran anything.

The upstream half is fixed in SmooAI/client-shared#7. This PR is the gate that stops it recurring.

What changed

CI exists.github/workflows/rust.yml: cargo fmt --check, clippy --all-targets -D warnings, tests, the module-tree guard, and the shared/ drift gate.

shared/ drift gate. client-shared owns the design system; this repo keeps a copy for observability-studio and smooblue, and CI fails if they differ. It compares git blob SHAs from the GitHub contents API against git ls-tree, so added and deleted files are caught as well as edited ones — there is no hand-maintained filename list to fall behind, which is the failure mode a for f in a b c loop would have.

shared/tokens.json is code input now. It was read by no code in any language while the Rust constants hand-mirrored the same values. rust/build.rs generates the whole tokens module from it via shared/tokens_codegen.rs, so a token cannot exist in the design system and be missing from Rust — nobody types the constants. That immediately surfaced six tokens the CSS declared and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING, SIDEBAR) plus the radius / space / font scales. RADIUS_PX is kept as an alias of the generated RADIUS_MD_PX so observability-studio (pinned at tag = v0.1.0) keeps compiling when it retags.

tokens_match_css could not fail in the direction that mattered. It asserted each constant appeared somewhere in the CSS as a substring — it passes with a constant bound to the wrong custom property, and nothing walked the CSS, so a token the CSS had and Rust lacked was structurally invisible. shared/tokens_css_check.rs parses :root, resolves var() to a fixed point, and asserts both directions.

Module-tree guard.scripts/check-module-tree.py fails if any .rs under rust/src/ is unreachable from a mod declaration — the class that let client-shared ship 174 lines of auth/refresh.rs that never compiled once, invisible to build, clippy, test and coverage alike.

Verification — every new check confirmed red before green

CheckBroken howResult
tokens_match_cssdrifted color.brand.orange in tokens.jsonFAILED
css_colors_are_all_tokensadded --surprise: oklch(…) to styles.cssFAILED
check-module-tree.pyplanted an orphan .rsexit 1, named the file

shared/ verified byte-identical to SmooAI/client-shared@main (7c5b948) — all five blob SHAs equal. Tests 6/6, fmt and clippy --all-targets -D warnings clean.

Judgment calls

  • Gate, not a cargo dependency on client-shared. ui could have become a thin re-export, deleting shared/ outright — both crates are git deps rather than crates.io publishes, so it is possible. But it would put two independently rev-pinned git deps in one graph for any consumer that ever wants both, and ui's live consumer is tag-pinned. The gate closes the silent-divergence defect without the coupling.
  • The gate is one-directional. Bidirectional deadlocks: neither repo's PR could go green until the other merged. client-shared is the source and is ungated.
  • This repo is not archived. See the note in the report — whether ui should eventually fold into client-shared is a call for its owner, not for this PR.

Pearls: th-42c460, th-ea969b

🤖 Generated with Claude Code

https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC

…ient-shared
This repo had NO CI — `.github/` held only banner images — so a change could
land unbuilt and `shared/` could diverge from SmooAI/client-shared with nothing
to say so. It already had: the monogram fix in f230808 ("restore the inner 'S'
curve and the dot") never crossed, so client-shared served a monogram with no S
and no dot, and its styles.css lost the whole `.input` family. Both repos were
green the entire time, because neither ran anything.
- **CI** (`.github/workflows/rust.yml`): fmt, clippy `--all-targets -D warnings`,
tests, the module-tree guard, and the shared/ drift gate.
- **`shared/` drift gate.** SmooAI/client-shared owns the design system (it is
the declared successor and what `th` ships); this repo keeps a copy for
observability-studio and smooblue, and CI fails if the copies differ. The gate
compares git blob SHAs from the contents API against `git ls-tree`, so adds and
deletes are caught too — no filename list to fall behind. Deliberately
one-directional: client-shared is ungated so a change lands there and this repo
follows; a bidirectional gate deadlocks, with neither PR able to green until
the other merges. Chosen over a cargo dependency on client-shared because both
crates are git deps, and depending across would put two independently
rev-pinned git deps in one graph for any consumer wanting both.
- **`shared/tokens.json` was inert** — read by no code in any language while the
Rust constants hand-mirrored the same values. `rust/build.rs` now generates the
whole `tokens` module from it via `shared/tokens_codegen.rs`, so a token cannot
exist in the design system and be missing from Rust. That surfaced six tokens
the CSS had and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING,
SIDEBAR) plus the radius/space/font scales. `RADIUS_PX` stays as an alias of
the generated `RADIUS_MD_PX` so observability-studio keeps compiling.
- **`tokens_match_css` could not fail in the direction that mattered.** It
asserted each constant appeared *somewhere* in the CSS as a substring: it
passes with a constant bound to the wrong property, and nothing walked the CSS,
so a token the CSS had and Rust lacked was invisible. `shared/tokens_css_check.rs`
parses `:root`, resolves `var()`, and checks both directions. Verified red by
drifting a token value and by adding an untracked `--surprise` colour.
- **Module-tree guard** (`scripts/check-module-tree.py`): fails if any `.rs` file
under `rust/src/` is unreachable from a `mod` declaration — the class that let
client-shared ship 174 lines of `auth/refresh.rs` that never compiled.
shared/ is byte-identical to SmooAI/client-shared@main (7c5b948); all five blob
SHAs verified equal. Tests 6/6, fmt + clippy clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC
@changeset-bot

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9305d85

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager
brentrager merged commit 7b71767 into mainAug 20, 2026
3 checks passed
@brentrager
brentrager deleted the fix/parity branch August 20, 2026 18:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brentrager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add CI, generate tokens from tokens.json, gate shared/ against client-shared - #7

Merged
brentrager merged 1 commit into
mainfrom
fix/parity
Aug 20, 2026
Merged

Add CI, generate tokens from tokens.json, gate shared/ against client-shared#7
brentrager merged 1 commit into
mainfrom
fix/parity

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Why

This repo had no CI. .github/ held two banner images and nothing else — no build, no test, no lint. A change could land unbuilt, and shared/ could diverge from SmooAI/client-shared with nothing to say so.

It already had diverged. The review that prompted this recorded the two repos' shared/ as byte-identical; on origin/main this morning they were not:

shared/styles.css ui=26ee714d… client-shared=c23abd72… DIFFER
shared/monogram.svg ui=a2db97f8… client-shared=565d6108… DIFFER

The monogram fix in f230808"restore the inner 'S' curve and the dot" — never crossed. client-shared, which is what the th CLI ships in production, served a monogram with no S and no dot. Its styles.css had separately lost the entire .input family, and its class-existence test had drifted along with it so nothing was red. Both repos were green the whole time, because neither ran anything.

The upstream half is fixed in SmooAI/client-shared#7. This PR is the gate that stops it recurring.

What changed

CI exists.github/workflows/rust.yml: cargo fmt --check, clippy --all-targets -D warnings, tests, the module-tree guard, and the shared/ drift gate.

shared/ drift gate. client-shared owns the design system; this repo keeps a copy for observability-studio and smooblue, and CI fails if they differ. It compares git blob SHAs from the GitHub contents API against git ls-tree, so added and deleted files are caught as well as edited ones — there is no hand-maintained filename list to fall behind, which is the failure mode a for f in a b c loop would have.

shared/tokens.json is code input now. It was read by no code in any language while the Rust constants hand-mirrored the same values. rust/build.rs generates the whole tokens module from it via shared/tokens_codegen.rs, so a token cannot exist in the design system and be missing from Rust — nobody types the constants. That immediately surfaced six tokens the CSS declared and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING, SIDEBAR) plus the radius / space / font scales. RADIUS_PX is kept as an alias of the generated RADIUS_MD_PX so observability-studio (pinned at tag = v0.1.0) keeps compiling when it retags.

tokens_match_css could not fail in the direction that mattered. It asserted each constant appeared somewhere in the CSS as a substring — it passes with a constant bound to the wrong custom property, and nothing walked the CSS, so a token the CSS had and Rust lacked was structurally invisible. shared/tokens_css_check.rs parses :root, resolves var() to a fixed point, and asserts both directions.

Module-tree guard.scripts/check-module-tree.py fails if any .rs under rust/src/ is unreachable from a mod declaration — the class that let client-shared ship 174 lines of auth/refresh.rs that never compiled once, invisible to build, clippy, test and coverage alike.

Verification — every new check confirmed red before green

CheckBroken howResult
tokens_match_cssdrifted color.brand.orange in tokens.jsonFAILED
css_colors_are_all_tokensadded --surprise: oklch(…) to styles.cssFAILED
check-module-tree.pyplanted an orphan .rsexit 1, named the file

shared/ verified byte-identical to SmooAI/client-shared@main (7c5b948) — all five blob SHAs equal. Tests 6/6, fmt and clippy --all-targets -D warnings clean.

Judgment calls

  • Gate, not a cargo dependency on client-shared. ui could have become a thin re-export, deleting shared/ outright — both crates are git deps rather than crates.io publishes, so it is possible. But it would put two independently rev-pinned git deps in one graph for any consumer that ever wants both, and ui's live consumer is tag-pinned. The gate closes the silent-divergence defect without the coupling.
  • The gate is one-directional. Bidirectional deadlocks: neither repo's PR could go green until the other merged. client-shared is the source and is ungated.
  • This repo is not archived. See the note in the report — whether ui should eventually fold into client-shared is a call for its owner, not for this PR.

Pearls: th-42c460, th-ea969b

🤖 Generated with Claude Code

https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC

…ient-shared
This repo had NO CI — `.github/` held only banner images — so a change could
land unbuilt and `shared/` could diverge from SmooAI/client-shared with nothing
to say so. It already had: the monogram fix in f230808 ("restore the inner 'S'
curve and the dot") never crossed, so client-shared served a monogram with no S
and no dot, and its styles.css lost the whole `.input` family. Both repos were
green the entire time, because neither ran anything.
- **CI** (`.github/workflows/rust.yml`): fmt, clippy `--all-targets -D warnings`,
tests, the module-tree guard, and the shared/ drift gate.
- **`shared/` drift gate.** SmooAI/client-shared owns the design system (it is
the declared successor and what `th` ships); this repo keeps a copy for
observability-studio and smooblue, and CI fails if the copies differ. The gate
compares git blob SHAs from the contents API against `git ls-tree`, so adds and
deletes are caught too — no filename list to fall behind. Deliberately
one-directional: client-shared is ungated so a change lands there and this repo
follows; a bidirectional gate deadlocks, with neither PR able to green until
the other merges. Chosen over a cargo dependency on client-shared because both
crates are git deps, and depending across would put two independently
rev-pinned git deps in one graph for any consumer wanting both.
- **`shared/tokens.json` was inert** — read by no code in any language while the
Rust constants hand-mirrored the same values. `rust/build.rs` now generates the
whole `tokens` module from it via `shared/tokens_codegen.rs`, so a token cannot
exist in the design system and be missing from Rust. That surfaced six tokens
the CSS had and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING,
SIDEBAR) plus the radius/space/font scales. `RADIUS_PX` stays as an alias of
the generated `RADIUS_MD_PX` so observability-studio keeps compiling.
- **`tokens_match_css` could not fail in the direction that mattered.** It
asserted each constant appeared *somewhere* in the CSS as a substring: it
passes with a constant bound to the wrong property, and nothing walked the CSS,
so a token the CSS had and Rust lacked was invisible. `shared/tokens_css_check.rs`
parses `:root`, resolves `var()`, and checks both directions. Verified red by
drifting a token value and by adding an untracked `--surprise` colour.
- **Module-tree guard** (`scripts/check-module-tree.py`): fails if any `.rs` file
under `rust/src/` is unreachable from a `mod` declaration — the class that let
client-shared ship 174 lines of `auth/refresh.rs` that never compiled.
shared/ is byte-identical to SmooAI/client-shared@main (7c5b948); all five blob
SHAs verified equal. Tests 6/6, fmt + clippy clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC
@changeset-bot

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9305d85

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager
brentrager merged commit 7b71767 into mainAug 20, 2026
3 checks passed
@brentrager
brentrager deleted the fix/parity branch August 20, 2026 18:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brentrager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add CI, generate tokens from tokens.json, gate shared/ against client-shared - #7

Merged
brentrager merged 1 commit into
mainfrom
fix/parity
Aug 20, 2026
Merged

Add CI, generate tokens from tokens.json, gate shared/ against client-shared#7
brentrager merged 1 commit into
mainfrom
fix/parity

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Why

This repo had no CI. .github/ held two banner images and nothing else — no build, no test, no lint. A change could land unbuilt, and shared/ could diverge from SmooAI/client-shared with nothing to say so.

It already had diverged. The review that prompted this recorded the two repos' shared/ as byte-identical; on origin/main this morning they were not:

shared/styles.css ui=26ee714d… client-shared=c23abd72… DIFFER
shared/monogram.svg ui=a2db97f8… client-shared=565d6108… DIFFER

The monogram fix in f230808"restore the inner 'S' curve and the dot" — never crossed. client-shared, which is what the th CLI ships in production, served a monogram with no S and no dot. Its styles.css had separately lost the entire .input family, and its class-existence test had drifted along with it so nothing was red. Both repos were green the whole time, because neither ran anything.

The upstream half is fixed in SmooAI/client-shared#7. This PR is the gate that stops it recurring.

What changed

CI exists.github/workflows/rust.yml: cargo fmt --check, clippy --all-targets -D warnings, tests, the module-tree guard, and the shared/ drift gate.

shared/ drift gate. client-shared owns the design system; this repo keeps a copy for observability-studio and smooblue, and CI fails if they differ. It compares git blob SHAs from the GitHub contents API against git ls-tree, so added and deleted files are caught as well as edited ones — there is no hand-maintained filename list to fall behind, which is the failure mode a for f in a b c loop would have.

shared/tokens.json is code input now. It was read by no code in any language while the Rust constants hand-mirrored the same values. rust/build.rs generates the whole tokens module from it via shared/tokens_codegen.rs, so a token cannot exist in the design system and be missing from Rust — nobody types the constants. That immediately surfaced six tokens the CSS declared and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING, SIDEBAR) plus the radius / space / font scales. RADIUS_PX is kept as an alias of the generated RADIUS_MD_PX so observability-studio (pinned at tag = v0.1.0) keeps compiling when it retags.

tokens_match_css could not fail in the direction that mattered. It asserted each constant appeared somewhere in the CSS as a substring — it passes with a constant bound to the wrong custom property, and nothing walked the CSS, so a token the CSS had and Rust lacked was structurally invisible. shared/tokens_css_check.rs parses :root, resolves var() to a fixed point, and asserts both directions.

Module-tree guard.scripts/check-module-tree.py fails if any .rs under rust/src/ is unreachable from a mod declaration — the class that let client-shared ship 174 lines of auth/refresh.rs that never compiled once, invisible to build, clippy, test and coverage alike.

Verification — every new check confirmed red before green

CheckBroken howResult
tokens_match_cssdrifted color.brand.orange in tokens.jsonFAILED
css_colors_are_all_tokensadded --surprise: oklch(…) to styles.cssFAILED
check-module-tree.pyplanted an orphan .rsexit 1, named the file

shared/ verified byte-identical to SmooAI/client-shared@main (7c5b948) — all five blob SHAs equal. Tests 6/6, fmt and clippy --all-targets -D warnings clean.

Judgment calls

  • Gate, not a cargo dependency on client-shared. ui could have become a thin re-export, deleting shared/ outright — both crates are git deps rather than crates.io publishes, so it is possible. But it would put two independently rev-pinned git deps in one graph for any consumer that ever wants both, and ui's live consumer is tag-pinned. The gate closes the silent-divergence defect without the coupling.
  • The gate is one-directional. Bidirectional deadlocks: neither repo's PR could go green until the other merged. client-shared is the source and is ungated.
  • This repo is not archived. See the note in the report — whether ui should eventually fold into client-shared is a call for its owner, not for this PR.

Pearls: th-42c460, th-ea969b

🤖 Generated with Claude Code

https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC

…ient-shared
This repo had NO CI — `.github/` held only banner images — so a change could
land unbuilt and `shared/` could diverge from SmooAI/client-shared with nothing
to say so. It already had: the monogram fix in f230808 ("restore the inner 'S'
curve and the dot") never crossed, so client-shared served a monogram with no S
and no dot, and its styles.css lost the whole `.input` family. Both repos were
green the entire time, because neither ran anything.
- **CI** (`.github/workflows/rust.yml`): fmt, clippy `--all-targets -D warnings`,
tests, the module-tree guard, and the shared/ drift gate.
- **`shared/` drift gate.** SmooAI/client-shared owns the design system (it is
the declared successor and what `th` ships); this repo keeps a copy for
observability-studio and smooblue, and CI fails if the copies differ. The gate
compares git blob SHAs from the contents API against `git ls-tree`, so adds and
deletes are caught too — no filename list to fall behind. Deliberately
one-directional: client-shared is ungated so a change lands there and this repo
follows; a bidirectional gate deadlocks, with neither PR able to green until
the other merges. Chosen over a cargo dependency on client-shared because both
crates are git deps, and depending across would put two independently
rev-pinned git deps in one graph for any consumer wanting both.
- **`shared/tokens.json` was inert** — read by no code in any language while the
Rust constants hand-mirrored the same values. `rust/build.rs` now generates the
whole `tokens` module from it via `shared/tokens_codegen.rs`, so a token cannot
exist in the design system and be missing from Rust. That surfaced six tokens
the CSS had and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING,
SIDEBAR) plus the radius/space/font scales. `RADIUS_PX` stays as an alias of
the generated `RADIUS_MD_PX` so observability-studio keeps compiling.
- **`tokens_match_css` could not fail in the direction that mattered.** It
asserted each constant appeared *somewhere* in the CSS as a substring: it
passes with a constant bound to the wrong property, and nothing walked the CSS,
so a token the CSS had and Rust lacked was invisible. `shared/tokens_css_check.rs`
parses `:root`, resolves `var()`, and checks both directions. Verified red by
drifting a token value and by adding an untracked `--surprise` colour.
- **Module-tree guard** (`scripts/check-module-tree.py`): fails if any `.rs` file
under `rust/src/` is unreachable from a `mod` declaration — the class that let
client-shared ship 174 lines of `auth/refresh.rs` that never compiled.
shared/ is byte-identical to SmooAI/client-shared@main (7c5b948); all five blob
SHAs verified equal. Tests 6/6, fmt + clippy clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC
@changeset-bot

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9305d85

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager
brentrager merged commit 7b71767 into mainAug 20, 2026
3 checks passed
@brentrager
brentrager deleted the fix/parity branch August 20, 2026 18:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brentrager
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add CI, generate tokens from tokens.json, gate shared/ against client-shared - #7

Merged
brentrager merged 1 commit into
mainfrom
fix/parity
Aug 20, 2026
Merged

Add CI, generate tokens from tokens.json, gate shared/ against client-shared#7
brentrager merged 1 commit into
mainfrom
fix/parity

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Why

This repo had no CI. .github/ held two banner images and nothing else — no build, no test, no lint. A change could land unbuilt, and shared/ could diverge from SmooAI/client-shared with nothing to say so.

It already had diverged. The review that prompted this recorded the two repos' shared/ as byte-identical; on origin/main this morning they were not:

shared/styles.css ui=26ee714d… client-shared=c23abd72… DIFFER
shared/monogram.svg ui=a2db97f8… client-shared=565d6108… DIFFER

The monogram fix in f230808"restore the inner 'S' curve and the dot" — never crossed. client-shared, which is what the th CLI ships in production, served a monogram with no S and no dot. Its styles.css had separately lost the entire .input family, and its class-existence test had drifted along with it so nothing was red. Both repos were green the whole time, because neither ran anything.

The upstream half is fixed in SmooAI/client-shared#7. This PR is the gate that stops it recurring.

What changed

CI exists.github/workflows/rust.yml: cargo fmt --check, clippy --all-targets -D warnings, tests, the module-tree guard, and the shared/ drift gate.

shared/ drift gate. client-shared owns the design system; this repo keeps a copy for observability-studio and smooblue, and CI fails if they differ. It compares git blob SHAs from the GitHub contents API against git ls-tree, so added and deleted files are caught as well as edited ones — there is no hand-maintained filename list to fall behind, which is the failure mode a for f in a b c loop would have.

shared/tokens.json is code input now. It was read by no code in any language while the Rust constants hand-mirrored the same values. rust/build.rs generates the whole tokens module from it via shared/tokens_codegen.rs, so a token cannot exist in the design system and be missing from Rust — nobody types the constants. That immediately surfaced six tokens the CSS declared and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING, SIDEBAR) plus the radius / space / font scales. RADIUS_PX is kept as an alias of the generated RADIUS_MD_PX so observability-studio (pinned at tag = v0.1.0) keeps compiling when it retags.

tokens_match_css could not fail in the direction that mattered. It asserted each constant appeared somewhere in the CSS as a substring — it passes with a constant bound to the wrong custom property, and nothing walked the CSS, so a token the CSS had and Rust lacked was structurally invisible. shared/tokens_css_check.rs parses :root, resolves var() to a fixed point, and asserts both directions.

Module-tree guard.scripts/check-module-tree.py fails if any .rs under rust/src/ is unreachable from a mod declaration — the class that let client-shared ship 174 lines of auth/refresh.rs that never compiled once, invisible to build, clippy, test and coverage alike.

Verification — every new check confirmed red before green

CheckBroken howResult
tokens_match_cssdrifted color.brand.orange in tokens.jsonFAILED
css_colors_are_all_tokensadded --surprise: oklch(…) to styles.cssFAILED
check-module-tree.pyplanted an orphan .rsexit 1, named the file

shared/ verified byte-identical to SmooAI/client-shared@main (7c5b948) — all five blob SHAs equal. Tests 6/6, fmt and clippy --all-targets -D warnings clean.

Judgment calls

  • Gate, not a cargo dependency on client-shared. ui could have become a thin re-export, deleting shared/ outright — both crates are git deps rather than crates.io publishes, so it is possible. But it would put two independently rev-pinned git deps in one graph for any consumer that ever wants both, and ui's live consumer is tag-pinned. The gate closes the silent-divergence defect without the coupling.
  • The gate is one-directional. Bidirectional deadlocks: neither repo's PR could go green until the other merged. client-shared is the source and is ungated.
  • This repo is not archived. See the note in the report — whether ui should eventually fold into client-shared is a call for its owner, not for this PR.

Pearls: th-42c460, th-ea969b

🤖 Generated with Claude Code

https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC

…ient-shared
This repo had NO CI — `.github/` held only banner images — so a change could
land unbuilt and `shared/` could diverge from SmooAI/client-shared with nothing
to say so. It already had: the monogram fix in f230808 ("restore the inner 'S'
curve and the dot") never crossed, so client-shared served a monogram with no S
and no dot, and its styles.css lost the whole `.input` family. Both repos were
green the entire time, because neither ran anything.
- **CI** (`.github/workflows/rust.yml`): fmt, clippy `--all-targets -D warnings`,
tests, the module-tree guard, and the shared/ drift gate.
- **`shared/` drift gate.** SmooAI/client-shared owns the design system (it is
the declared successor and what `th` ships); this repo keeps a copy for
observability-studio and smooblue, and CI fails if the copies differ. The gate
compares git blob SHAs from the contents API against `git ls-tree`, so adds and
deletes are caught too — no filename list to fall behind. Deliberately
one-directional: client-shared is ungated so a change lands there and this repo
follows; a bidirectional gate deadlocks, with neither PR able to green until
the other merges. Chosen over a cargo dependency on client-shared because both
crates are git deps, and depending across would put two independently
rev-pinned git deps in one graph for any consumer wanting both.
- **`shared/tokens.json` was inert** — read by no code in any language while the
Rust constants hand-mirrored the same values. `rust/build.rs` now generates the
whole `tokens` module from it via `shared/tokens_codegen.rs`, so a token cannot
exist in the design system and be missing from Rust. That surfaced six tokens
the CSS had and Rust omitted (MUTED, MUTED_FOREGROUND, ACCENT, INPUT, RING,
SIDEBAR) plus the radius/space/font scales. `RADIUS_PX` stays as an alias of
the generated `RADIUS_MD_PX` so observability-studio keeps compiling.
- **`tokens_match_css` could not fail in the direction that mattered.** It
asserted each constant appeared *somewhere* in the CSS as a substring: it
passes with a constant bound to the wrong property, and nothing walked the CSS,
so a token the CSS had and Rust lacked was invisible. `shared/tokens_css_check.rs`
parses `:root`, resolves `var()`, and checks both directions. Verified red by
drifting a token value and by adding an untracked `--surprise` colour.
- **Module-tree guard** (`scripts/check-module-tree.py`): fails if any `.rs` file
under `rust/src/` is unreachable from a `mod` declaration — the class that let
client-shared ship 174 lines of `auth/refresh.rs` that never compiled.
shared/ is byte-identical to SmooAI/client-shared@main (7c5b948); all five blob
SHAs verified equal. Tests 6/6, fmt + clippy clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC
@changeset-bot

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 9305d85

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager
brentrager merged commit 7b71767 into mainAug 20, 2026
3 checks passed
@brentrager
brentrager deleted the fix/parity branch August 20, 2026 18:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@brentrager