Skip to content

docs(patterns): on-chain / off-chain boundary guide + feat(design-system): shared versioned tokens package - #289

Merged
salazarsebas merged 2 commits into
salazarsebas:mainfrom
Spagero763:docs/boundary-guide-and-design-tokens
Aug 15, 2026
Merged

docs(patterns): on-chain / off-chain boundary guide + feat(design-system): shared versioned tokens package#289
salazarsebas merged 2 commits into
salazarsebas:mainfrom
Spagero763:docs/boundary-guide-and-design-tokens

Conversation

@Spagero763

@Spagero763 Spagero763 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Closes #251
Part of #260

Updated per review: build output is no longer committed, and the showcase integration has moved to its own PR. The commits are still split one per issue.

Changes since the first round

Review point What changed
Don't commit built artifacts packages/tokens/dist/ is gitignored and gone from the diff. It is built by npm run build, and automatically by a prepare script on install and before npm pack/npm publish, so an installing consumer still gets built output without running the build.
Wire up a build step Added a path-filtered Design Tokens workflow that verifies, builds, loads the built module to confirm both modes resolve, and fails if dist/ ever becomes tracked.
tools/preview-gen isn't part of this Split out into #291, linked below.
Rebase onto main See note at the end.

One consequence worth calling out

build.js --check used to diff the committed dist/ against a fresh build. With nothing committed to diff, that check would be vacuous in a clean clone, so it now guards the drift that actually remains: tokens.json against docs/BRAND.md, which is the declared source of truth and the one pair nothing else stops being edited apart.

$ node build.js --check
Token source does not match docs/BRAND.md:
  - radius-md is 8px in docs/BRAND.md but 9px in tokens.json

BRAND.md is the source of truth. Update it first, then mirror it in tokens.json.

Verified it catches both a changed color and a changed scale value, and passes on a clean tree with no dist/ present.

1. On-chain / off-chain boundary guide (#251)

Unchanged from the first round. New docs/ONCHAIN_OFFCHAIN_BOUNDARY.md, the filename recommended in docs/strategy/04-onchain-gaming-research.md problem #1.

A five-question framework for placing one piece of game state or logic at a time, then three worked examples written against the current source rather than an idealized version:

Example What it demonstrates
battleship Commitments and Merkle roots on-chain, board layout, salt, and tree off-chain. Traces the cheat reveal_cell actually closes through leaf_hash and Sha256MerkleProofVerifier.
snake A full on-chain simulation that is turn-based, not real-time. One tick per invocation, one transaction per direction change.
blind_auction A proof rather than a hash, because sealed_bid binds the reveal to the auction ID and max_bid in the same verification.

Per the definition of done, the guide names the shortcuts the source review turned up rather than writing around them: battleship verifies a revealed cell against the commitment but never checks the committed board is a legal fleet, and snake has no require_auth anywhere in src/ and derives food placement from the tick counter. It also states plainly what Soroban throughput cannot support, and cross-links PERFORMANCE.md and PRIVACY_MODEL.md rather than restating them.

The definition of done asks for publication in the docs site's Learn section linked from the tutorial's next steps. The scaffold (#249) and tutorial (#250) have not landed, so the guide sits in docs/ and is linked from the README documentation list, the PATTERNS.md problem table, and a new Related section in PERFORMANCE.md. It slots into Learn unchanged when #249 lands.

2. Shared, versioned tokens package (Part of #260)

packages/tokens (cougr-tokens v1.0.0) encodes every value in docs/BRAND.md: four neutrals, primary and accent, three maturity-tier colors, two font stacks, the eight-step spacing scale, four radii, and the four fixed logo tones. Verified mechanically that nothing in BRAND.md is missing and no color was invented.

Format decision. tokens.json is the single hand-edited source; a zero-dependency build.js emits both dist/tokens.css and dist/tokens.js. CSS custom properties alone are preferable unless there is a build-time transform need, and there is one: anything producing a standalone artifact (an SVG, a PNG, terminal output) is consumed outside a document, so custom properties declared by a host page never resolve. Both outputs come from the same build and cannot disagree.

Style Dictionary was considered and skipped: it would add a dependency and a toolchain to a repository whose only other Node tool is deliberately install-free, for a transform that is about forty lines.

Theming. Light and dark are complete for every themed token. Light is the default on :root, dark applies under prefers-color-scheme unless the document opts out with data-theme="light", and an explicit data-theme on the root element always wins.

Versioning. Semver against the token surface rather than the code, with bump rules and the change procedure in the package README and a CHANGELOG.md. Each consumer pins its own range, so one can upgrade ahead of the other and the divergence is a fact in a lockfile rather than something noticed by eye.

docs/BRAND.md had a line saying a code package was out of scope and tracked separately; it now points at the package and restates that BRAND.md remains the source of truth.

Why this says "Part of #260" rather than "Closes"

#260's definition of done has two items. This PR ships the first (a versioned package with light and dark values for every token). The second is "at least one real site/build consumes it and visibly reflects a token change when the package is updated" — that was what the tools/preview-gen integration was doing here, since the docs-site scaffold (#249) hasn't landed and the showcase generator was the only real consumer available.

Splitting it out is fine, but it does mean #260 shouldn't auto-close on this PR with its second item outstanding. So the follow-up carries Closes #260, and this one carries Part of #260. Say the word if you'd rather this PR close it and the follow-up just reference it.

Follow-up PR

The showcase integration is now #291, stacked on this branch. It converts the four preview renderers from their hand-copied slate palettes to the shared tokens and includes the regenerated previews, which is the end-to-end proof for #260's second item. It also handles the consequence of un-committing dist/: generate.js builds the tokens on first run, so node generate.js <game> still works from a fresh clone with no setup step.

It is stacked, so it will show only its own diff once this merges. Happy to reorder if you'd rather review them the other way round.

Validation

  • node build.js and node build.js --check, both clean, including --check on a tree with no dist/ present.
  • Negative-tested the drift check by changing a color and a scale value in tokens.json: both fail with a specific message, and pass again when reverted.
  • Ran each step of the new workflow locally, including the module load assertion and the "dist is not tracked" guard.
  • Cross-checked every hex and scale value in docs/BRAND.md against tokens.json: none missing, none added.
  • All relative links in the new and edited Markdown resolve.
  • cargo fmt --check clean. No Rust source is touched, so Core CI's path filter does not select this PR.

On the rebase

main is still at be4f2f2 ("feat(cli): implement 'cougr new' project generator", 2026-07-27), and this branch was already based directly on it, so there was nothing to rebase onto. I re-fetched and confirmed against the API rather than assume. The force-push above is the restructuring, not a rebase. If you were expecting newer commits on main, something may not have landed yet, and I'll rebase as soon as it does.

Notes for review

  • No changes to any example's code, per the documentation-only scope of docs(patterns): write the on-chain / off-chain boundary guide #251.
  • I did not update the status rows in docs/strategy/12-documentation-architecture.md that still mark the boundary guide and design tokens as gaps. Those read as point-in-time analysis documents, so I left them alone; happy to update them if you would rather they track shipped state.

Adds docs/ONCHAIN_OFFCHAIN_BOUNDARY.md, the guide recommended in
docs/strategy/04-onchain-gaming-research.md problem salazarsebas#1 and listed as the
second-priority Learn-section gap in the documentation architecture.

The guide gives a five-question framework for placing a single piece of game
state or logic, then works it through three shipped examples: battleship
(commitments on-chain, placement and Merkle tree off-chain), snake (a full
on-chain simulation that is turn-based rather than real-time, stated honestly),
and blind_auction (a proof standing in for data, with the Experimental maturity
of Groth16 verification named). Every claim is drawn from the current source of
the example it describes, including the shortcuts: battleship does not validate
fleet legality, and snake has no authorization and derives food placement from
the tick counter.

It closes with an explicit list of what Soroban throughput cannot support, so
the guide does not imply on-chain real-time gameplay is achievable. Resource
cost and privacy tiers are cross-linked to PERFORMANCE.md and PRIVACY_MODEL.md
rather than re-derived, and the Phase 2 GameHarness resource-reporting work is
referenced as planned rather than available.

Linked from the README documentation list, the PATTERNS.md problem table, and
a Related section in PERFORMANCE.md.
Copilot AI review requested due to automatic review settings July 28, 2026 10:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@drips-wave

drips-wave Bot commented Jul 28, 2026

Copy link
Copy Markdown

@Spagero763 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@salazarsebas

Copy link
Copy Markdown
Owner

The on-chain/off-chain boundary guide (#251) is thorough and reads well. For the tokens package (#260): could you drop packages/tokens/dist/tokens.css and dist/tokens.js from the PR (built output) and instead wire up a build step, so we're not committing compiled artifacts? Also, tools/preview-gen isn't part of either #251 or #260 — if it's needed, could you file/link the issue it belongs to, or split it into its own PR? Please rebase onto main as well.

Adds packages/tokens (cougr-tokens), a versioned artifact encoding every value
defined in docs/BRAND.md so the documentation site and the showcase consume one
source instead of hand-copying values.

Format decision: tokens.json is the single hand-edited source, and a
zero-dependency build script emits both dist/tokens.css and dist/tokens.js. CSS
custom properties alone are not sufficient because some consumers need literal
values at generation time: anything producing a standalone artifact is consumed
outside a document, so custom properties declared by a host page never reach it.
Both outputs come from the same build, so they cannot disagree.

dist/ is generated, not committed. It is built by npm run build, and by the
prepare script on install and before pack/publish, so an installing consumer
gets built output without running the build and nothing in git can fall out of
step with the source.

Because there is no committed dist/ to diff against, --check no longer compares
build output. It now validates the source and a dry-run build, and fails when
tokens.json has drifted from docs/BRAND.md. That moves the guarantee to where
the risk actually is: BRAND.md is the declared source of truth and nothing
otherwise stops the two being edited apart.

Light and dark sets are complete for every themed token. Light is the default on
:root, dark applies under prefers-color-scheme unless the document opts out, and
an explicit data-theme attribute on the root element always wins.

Adds a path-filtered Design Tokens workflow that runs the drift check, builds,
loads the built module to confirm both modes resolve, and asserts dist/ is not
tracked.
@Spagero763

Copy link
Copy Markdown
Contributor Author

@salazarsebas kindly review and merge

@salazarsebas salazarsebas left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

LGTM

@salazarsebas
salazarsebas merged commit 8fe0445 into salazarsebas:main Aug 15, 2026
1 check passed
Sign up for free to 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.

docs(patterns): write the on-chain / off-chain boundary guide

3 participants