From 81c511240909a9829cd0314c999b75c78454d485 Mon Sep 17 00:00:00 2001 From: Daniel Noulet Date: Wed, 7 Oct 2026 15:05:48 +0200 Subject: [PATCH] chore: adding docs --- .gitignore | 2 - data/docs/go-bananas-0001-embedded-sqlite.md | 182 +++++ data/docs/go-bananas-0002-runtime-delivery.md | 148 ++++ .../docs/go-bananas-0003-stateful-sessions.md | 107 +++ .../go-bananas-0004-typescript-version.md | 76 ++ .../go-bananas-0005-shared-token-contract.md | 125 ++++ data/docs/go-bananas-api.md | 403 +++++++++++ data/docs/go-bananas-architecture.md | 354 +++++++++ data/docs/go-bananas-deployment.md | 553 ++++++++++++++ data/docs/go-bananas-development.md | 229 ++++++ data/docs/go-bananas-integration.md | 571 +++++++++++++++ data/docs/go-bananas-operations.md | 279 ++++++++ data/docs/go-bananas-security.md | 253 +++++++ data/docs/go-bananas-standards.md | 672 ++++++++++++++++++ data/docs/go-bananas-styling-contract.md | 223 ++++++ data/docs/go-bananas.md | 227 ++++++ data/docs/go-first.md | 197 +++++ data/docs/go-fish.md | 101 +++ data/docs/go-guess-copilot-instructions.md | 124 ++++ data/docs/go-guess-readme.md | 124 ++++ data/docs/go-guess.md | 208 ++++++ data/docs/go-loose-architecture.md | 86 +++ data/docs/go-loose-client-login.md | 246 +++++++ data/docs/go-loose-coding-standards.md | 53 ++ data/docs/go-loose-installation.md | 99 +++ data/docs/go-loose-integration.md | 70 ++ data/docs/go-loose-operations.md | 86 +++ data/docs/go-loose-password-authentication.md | 56 ++ data/docs/go-loose-roles.md | 97 +++ data/docs/go-loose.md | 220 ++++++ data/docs/go-mad-installation.md | 102 +++ data/docs/go-mad-operations.md | 116 +++ data/docs/go-mad.md | 141 ++++ data/docs/go-places-developer.md | 168 +++++ data/docs/go-places-infrastructure.md | 108 +++ data/docs/go-places.md | 149 ++++ data/docs/go-tell-changelog.md | 106 +++ data/docs/go-tell-readme.md | 26 + data/docs/go-tell.md | 172 +++++ 39 files changed, 7257 insertions(+), 2 deletions(-) create mode 100644 data/docs/go-bananas-0001-embedded-sqlite.md create mode 100644 data/docs/go-bananas-0002-runtime-delivery.md create mode 100644 data/docs/go-bananas-0003-stateful-sessions.md create mode 100644 data/docs/go-bananas-0004-typescript-version.md create mode 100644 data/docs/go-bananas-0005-shared-token-contract.md create mode 100644 data/docs/go-bananas-api.md create mode 100644 data/docs/go-bananas-architecture.md create mode 100644 data/docs/go-bananas-deployment.md create mode 100644 data/docs/go-bananas-development.md create mode 100644 data/docs/go-bananas-integration.md create mode 100644 data/docs/go-bananas-operations.md create mode 100644 data/docs/go-bananas-security.md create mode 100644 data/docs/go-bananas-standards.md create mode 100644 data/docs/go-bananas-styling-contract.md create mode 100644 data/docs/go-bananas.md create mode 100644 data/docs/go-first.md create mode 100644 data/docs/go-fish.md create mode 100644 data/docs/go-guess-copilot-instructions.md create mode 100644 data/docs/go-guess-readme.md create mode 100644 data/docs/go-guess.md create mode 100644 data/docs/go-loose-architecture.md create mode 100644 data/docs/go-loose-client-login.md create mode 100644 data/docs/go-loose-coding-standards.md create mode 100644 data/docs/go-loose-installation.md create mode 100644 data/docs/go-loose-integration.md create mode 100644 data/docs/go-loose-operations.md create mode 100644 data/docs/go-loose-password-authentication.md create mode 100644 data/docs/go-loose-roles.md create mode 100644 data/docs/go-loose.md create mode 100644 data/docs/go-mad-installation.md create mode 100644 data/docs/go-mad-operations.md create mode 100644 data/docs/go-mad.md create mode 100644 data/docs/go-places-developer.md create mode 100644 data/docs/go-places-infrastructure.md create mode 100644 data/docs/go-places.md create mode 100644 data/docs/go-tell-changelog.md create mode 100644 data/docs/go-tell-readme.md create mode 100644 data/docs/go-tell.md diff --git a/.gitignore b/.gitignore index 3f4422f..3909223 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,6 @@ !/backend/webui/dist/index.html # Flat-file store: the seed document is tracked, generated fixtures are not -/data/docs/* -!/data/docs/go-doc.md /data/comments/ !data/comments/.gitkeep diff --git a/data/docs/go-bananas-0001-embedded-sqlite.md b/data/docs/go-bananas-0001-embedded-sqlite.md new file mode 100644 index 0000000..758f125 --- /dev/null +++ b/data/docs/go-bananas-0001-embedded-sqlite.md @@ -0,0 +1,182 @@ + +Imported from docs/adr/0001-embedded-sqlite.md in the go-bananas repository. + +--- + +# ADR 0001: Embedded SQLite rather than a client/server database + +- **Status:** accepted +- **Date:** 2026-10-04 +- **Deciders:** platform team +- **Applies to:** all deployments of the current major version + +## Context + +go-bananas stores themes. A theme is a slug, some metadata and a JSON document of +design tokens, typically 10–60 kB. A useful instance has tens to a few thousand of +them. + +The workload shape is: + +- **Reads:** constant and bursty. Every page view in every consuming application + requests CSS. A service with ten consumer applications and a few hundred users + can easily see thousands of stylesheet requests a minute, and 95%+ of them are + the same handful of bytes served from a render cache. +- **Writes:** rare. A theme is edited by a human. A busy week might be fifty writes. +- **Total size:** a few megabytes. Even ten thousand large themes would fit in + fifty. + +The conventional answer for a service with this shape is PostgreSQL. That answer is +right for almost every service and wrong for this one, for reasons worth writing +down rather than rediscovering in a year. + +## Decision + +Use SQLite through `modernc.org/sqlite`, a pure-Go translation, on a local volume. + +## Rationale + +### What a client/server database would add + +For this dataset, Postgres buys nothing and costs: + +- **A network hop per query.** A local SQLite read is microseconds; a query over a + pod network is a millisecond and a tail-latency distribution with a long right + tail. For a stylesheet endpoint on the critical path of every page view, that + tail is the difference between a fast site and a janky one. +- **A connection pool to get wrong.** Every deployment needs its own tuning, and the + failure mode of under-tuning is a saturated pool under load. +- **Credentials to create, distribute, rotate and revoke.** Another secret in + Kubernetes, another thing to get wrong. +- **A schema migration process.** For a dataset that fits in a few megabytes, this + is a disproportionate amount of ceremony. +- **A second thing to operate.** Backups, upgrades, monitoring, incident response, + for a component whose whole job is to hold some JSON. +- **A second failure mode.** The database becoming unavailable is now the most + likely way the theme service goes down. + +### What SQLite gives up + +Honesty requires stating the costs: + +- **One writer at a time.** Correct at this write rate. A theme edit is a + millisecond-scale transaction; even a hundred concurrent editors would not + saturate it. +- **Single-node.** This is the real constraint. See Scaling below. +- **No network access.** A consuming application cannot query the database directly. + It does not need to; the runtime API is the interface. + +### Why this particular configuration is fast + +The default SQLite configuration is not fast. These settings are what make it fast +enough that the question does not arise: + +| Setting | Effect | +| --- | --- | +| `journal_mode=WAL` | Readers never block the writer and the writer never blocks readers. This is the single most important setting. | +| `synchronous=NORMAL` | In WAL mode this is durable across an application crash and risks only the last transactions on an operating-system crash. A lost publish is re-doable; an outage is not. | +| `cache_size=-16000` | A 16 MiB page cache, which holds the entire dataset. | +| `mmap_size=268435456` | The file is read through the page cache directly, removing a copy from the read path. | +| `temp_store=MEMORY` | Sort and group-by spill stays in memory. | +| Max 8 connections | SQLite serialises writers. A large pool adds contention, not throughput. This is counter-intuitive and correct. | +| In-memory render cache | The steady-state runtime request performs **no database access at all**. | + +The render cache is the decisive optimisation. Rendering a theme traverses its whole +token document; serving the cached string is one map lookup. Because the cache key +includes the theme version, publishing invalidates exactly one theme's entries and +disturbs nothing else. The database is consulted only on a cache miss, which is a +publish rather than a read. + +### Why `modernc.org/sqlite` rather than `mattn/go-sqlite3` + +`mattn/go-sqlite3` is a cgo binding to the C library. It is faster, and it means: + +- `CGO_ENABLED=1`, so the release image needs a C toolchain at build time and libc + at run time, and cross-compilation needs a cross-C toolchain; +- a statically linked binary is impossible, so the image carries a C library whose + CVEs need tracking; +- `go install` from source fails on a machine without a compiler. + +`modernc.org/sqlite` is a transpiled Go implementation: no cgo, a fully static +binary, and cross-compilation is `GOOS=linux GOARCH=arm64` and nothing else. The +performance difference is irrelevant here because the hot path is the in-memory +render cache, not SQLite. + +## Alternatives considered + +**PostgreSQL.** The conventional choice. Rejected on the grounds above. It remains +the right answer if the product changes shape — see Scaling. + +**Redis.** Excellent for this read-heavy workload, and a good fit for a render +cache. But it is another service to operate for a cache that is already +process-local and loses nothing on restart, because the source of truth is the +database. Rejected as unnecessary operational surface. Worth revisiting if a +many-node deployment needs a shared cache; the render cache is an interface, so +that change is contained. + +**bbolt.** Faster than SQLite for pure key/value workloads and simpler. Rejected +because the schema needs indexes for listing, filtering and sorting, and because +SQLite's query planner handles the "a few hundred rows filtered by status and +ordered by date" shape without the ceremony of managing bbolt buckets by hand. + +**A JSON file per theme on disk.** No dependency at all. Rejected because concurrent +writes need locking, and because every listing requires reading and parsing every +file. SQLite is a better version of this idea that is already written. + +**An in-memory store with write-through to object storage.** Attractive for scale. +Rejected for v1: it removes the transactional guarantee that makes version history +and rollback correct, and it adds an object-storage dependency. The write path +here is not hot enough to justify losing the invariant. + +## Scaling + +SQLite on a local volume is single-node. Two shapes are supported: + +1. **One replica, one volume.** The recommended production shape. Horizontally + scalable only in the sense that the service is so cheap that one replica serves + a very large number of consumers. + +2. **Many replicas, a shared filesystem.** Works when the storage class supports + concurrent mounts — NFS, CephFS, or a ReadWriteMany block device. WAL mode + requires the filesystem to support shared memory mapping, which these do. The + Helm chart warns when a ReadWriteOnce volume is combined with more than one + replica, because that combination cannot work. + +If the workload ever outgrows both — genuinely concurrent multi-writer access across +nodes — the migration is to Postgres. It is contained: `internal/store` is the only +package that writes SQL, and its interface is defined by the methods it exposes +rather than by SQLite's features. + +## Consequences + +**Good** + +- One less thing to operate, back up, monitor and secure. +- No credential to leak. +- A static binary and a small image with no libc. +- A backup is `cp` of one file. There is no consistent-snapshot dance. +- The render cache can be process-local because there is only one process. + +**Bad** + +- Cannot scale horizontally past one node without a shared filesystem. +- A shared-filesystem deployment is slower than local disk, and WAL over NFS is + sensitive to latency. Measure before choosing it. +- Single-writer means a pathological write loop would queue. Nothing in the + application can produce one. + +**Neutral** + +- The `store` package has a wider interface than a repository might otherwise need, + because it must express listing, filtering and sorting. Moving to another engine + means reimplementing that surface. + +## Revisit when + +- A single instance cannot serve the load, and profiling shows the database rather + than the render cache is the bottleneck. +- Concurrent writers become a real requirement, for example if themes can be edited + by an automated pipeline from several deployments. +- The dataset outgrows a few hundred megabytes, at which point the time to back up + by copying a file becomes its own problem. + diff --git a/data/docs/go-bananas-0002-runtime-delivery.md b/data/docs/go-bananas-0002-runtime-delivery.md new file mode 100644 index 0000000..4a602e0 --- /dev/null +++ b/data/docs/go-bananas-0002-runtime-delivery.md @@ -0,0 +1,148 @@ + +Imported from docs/adr/0002-runtime-delivery.md in the go-bananas repository. + +--- + +# ADR 0002: Deliver themes at run time rather than as a package + +- **Status:** accepted +- **Date:** 2026-10-04 + +## Context + +The conventional way to ship a design system to applications is a package: an npm +package, a Go package, a Maven artifact. The consuming application installs it, +imports from it, and rebuilds when it changes. + +That model is good, and it is the right answer for most design-system artefacts. +The question here is whether it is the right answer for *design tokens specifically*. + +## Decision + +Themes are served over HTTP at run time as CSS custom properties and JSON. The +runtime SDKs are ES modules loaded by URL through an import map. Nothing is +installed by the consumer. + +## Rationale + +### The cost of the package model, for tokens specifically + +Design tokens are unusual among design-system artefacts. They are: + +- **entirely data.** No behaviour to tree-shake, no components to code-split; +- **consumed at a single moment.** At first paint, by a stylesheet link; +- **changing on a different cadence from code.** A designer adjusts a colour; no + application code needs to change. + +The package model imposes costs that fit code perfectly and fit data badly: + +| Cost | Fits code? | Fits tokens? | +| --- | --- | --- | +| Version resolution and peer conflicts | Yes | No. Nothing here has a peer. | +| A rebuild of every consumer | Necessary | Pure overhead. The CSS output is byte-identical. | +| Coordinated deploys on version skew | Necessary | Avoidable: a runtime fetch has no skew. | +| A published version to track | Necessary | A database row, which an operator already tracks. | +| Install friction | Usually fine | Often the blocker. A Go service with a templated frontend cannot `npm install` at all. | +| Tree-shaking and chunk sizing | Necessary | Nothing to shake. | + +The specific motivating case: a service that serves server-rendered HTML cannot +consume a design-system npm package without introducing a Node build into its +release pipeline. That is a large change made to deliver a CSS file. Serving the +CSS directly removes it. + +### What runtime delivery buys + +**Republishing is a database write.** A designer adjusts a colour, clicks publish, +and every application picks it up on its next request. There is no version to cut, +no build to trigger, no consumer to upgrade. + +**No skew.** An application never holds a stale copy of a theme in its +`node_modules`, because it never held one. Rolling back a theme is a database write +too, and is atomic across every consumer. + +**No supply chain.** A token file is not code. It cannot execute anything, it has +no transitive dependencies, and it cannot be compromised in the way an npm package +can. This is a genuinely large reduction in attack surface. + +**Language neutrality.** Any environment that can make an HTTP request and read CSS +can consume a theme: Go, Rust, PHP, Jinja, Django templates, a static site +generator, a server-rendered email. + +### What it costs + +Honesty requires stating the costs: + +**A network hop at first paint.** A stylesheet link is a blocking request. Mitigated +by immutable-by-URL caching: a CDN serves it from an edge, a publish changes the +version and therefore the URL, and a revalidation costs a few milliseconds and no +body. This is the same cost model as a web font, which every application already +pays. + +**A runtime dependency.** The service must be up for a consuming application to look +right. Mitigated by a hard failure being a *fallback to the application's own +styles*, not an error page: a failed theme load leaves the page unstyled rather than +broken, which is exactly the right degradation. + +**CSS custom properties as the only interface.** A token cannot be compiled into a +Tailwind class, a Sass mixin or a styled-component prop. Anything that needs +compilation is not served by this system. That is the boundary of the approach, and +it is a real boundary rather than a detail. + +**A version-pinning story is needed.** Runtime delivery means a consumer can be +mid-request while a theme is republished. The answer is that a published theme's +version never changes — a publish creates version N+1 — so a URL pinned to version +N is immutable forever, and `/rt/v1/themes/{slug}` resolves to the latest. This is +the same model as an immutable container tag. + +## Alternatives considered + +**An npm package of design tokens.** Rejected for the reasons above. It remains the +right answer for anything requiring compilation, and the two models compose: an +application can build a Tailwind preset from a theme at its own build time, using +`/rt/v1/theme.json`, and then use the runtime for the parts that must be live. + +**A CSS file on a CDN, uploaded by hand.** The delivery mechanism without the +management. It removes the coupling but gives up atomic publish, rollback, history +and per-theme authorisation. This is what the service automates. + +**Server-side rendering of inline styles.** Each application renders the variables +into its own HTML. Zero latency, but every application must implement a fetcher, a +cache, a fallback and a revalidation policy, and they will all get it subtly +different. Centralising it means one correct implementation. + +**Web Components for the tokens.** Not applicable. Tokens are not components. + +**A GraphQL API.** Rejected: the payload is one object, so a single GET is simpler, +cacheable at the HTTP layer, and readable with `curl`. GraphQL would add a schema, +a server and a client library to deliver a JSON document. + +## Consequences + +**Good** + +- Publishing is immediate, atomic and reversible. +- No consumer rebuild, no version skew, no package to track. +- Consumers in any language, including ones that cannot use npm at all. +- A theme cannot execute code. +- Caching is a solved problem rather than a design decision, because URLs are + immutable. + +**Bad** + +- A network hop on the critical rendering path, mitigated by aggressive caching. +- Only expressible as CSS custom properties. Anything needing compilation is out of + scope. +- The service is a runtime dependency for every consumer's first paint. +- Debugging "why does this consumer look wrong" requires knowing which theme and + version it resolved. Mitigated by `X-Theme-Version` and `X-Theme-Updated-At` + response headers, and by the `sourceURL` comment in the generated CSS that makes + browser devtools navigate back to the endpoint. + +**Neutral** + +- The admin UI is itself a consumer, which means the delivery path is exercised on + every interaction. A change that breaks delivery breaks the tool that maintains + themes, which is a useful continuous check. +- The runtime SDKs are built and served rather than published, so they are versioned + by the service's release rather than by a registry. + diff --git a/data/docs/go-bananas-0003-stateful-sessions.md b/data/docs/go-bananas-0003-stateful-sessions.md new file mode 100644 index 0000000..5ce9459 --- /dev/null +++ b/data/docs/go-bananas-0003-stateful-sessions.md @@ -0,0 +1,107 @@ + +Imported from docs/adr/0003-stateful-sessions.md in the go-bananas repository. + +--- + +# ADR 0003: Stateless signed session cookies + +- **Status:** accepted +- **Date:** 2026-10-04 + +## Context + +The administration API needs a session. The service may run more than one replica +behind a load balancer, and the login callback may be handled by a different pod +from the request that started it. + +## Decision + +A session is a signed JSON payload in an `HttpOnly`, `Secure`, `SameSite` cookie. +There is no server-side session table. + +## Rationale + +**The load-balancer problem.** With a server-side session store, a session created +by one replica must be readable by another. That means either sticky sessions (which +lose the pod on every rollout) or a shared store (Redis or a database table), which +is the operational surface [ADR 0001](0001-embedded-sqlite.md) set out to avoid. +A signed cookie removes the question entirely. + +**The transaction state has the same problem.** The OIDC flow carries a PKCE +verifier and a return path from the authorize redirect to the callback. Keeping that +in server-side state reintroduces the same requirement. It is a second signed cookie, +read and consumed exactly once. + +**The lifetime is short.** An administrative session is eight hours by default. +The cost of not being able to revoke a session before it expires is bounded by that +window. + +**What it costs.** Two things, stated plainly: + +1. **Revocation.** A stolen session cannot be invalidated before it expires. The + mitigations are an eight-hour lifetime and, for a full invalidation, rotating the + signing key, which logs out every session at once. Rotating is a deployment, not + a button. +2. **Payload size.** The cookie carries identity and roles. That is a few hundred + bytes, comfortably inside the 4 kB browser limit. A larger claim set would push + against it. + +## What was rejected + +**A server-side session table in SQLite.** Would work, and would add revocation. +Rejected because it adds a table, a write on every login, a sweep job, and a +migration path for a problem the eight-hour window already bounds. + +**Redis.** Would work. Rejected as operational surface for the same reason the +render cache stayed process-local. + +**JWT as a bearer token in localStorage.** Rejected on security grounds. A bearer +token in `localStorage` is readable by any script on the page, so one XSS bug +becomes a credential theft. An `HttpOnly` cookie is not readable by script, and +`SameSite=Lax` plus the session cookie's scope mean a cross-site form post cannot +ride it. The session is a cookie precisely so that it is not script-accessible. + +**Signed but not encrypted.** The payload is readable by the user holding the +cookie. It contains a subject, an email, a display name and roles — no secrets. It +is signed, not encrypted, so that a client can read it and debugging is possible. +Encrypting it would add a key for no confidentiality gain on this content. + +## Implementation requirements + +Every one of these is a security property, and each has a test: + +- HMAC-SHA256 signature, compared with `subtle.ConstantTimeCompare`. A + byte-by-byte early return leaks the correct prefix length to an attacker measuring + responses. +- `HttpOnly`, so script cannot read it. +- `Secure` in any non-development environment, enforced by configuration + validation. +- `SameSite`, defaulted to `Lax`. `None` requires `Secure`, enforced by validation, + because browsers reject the combination and the session would silently never + survive a redirect. +- Payload expiry, checked server-side on every request. The cookie's own `Max-Age` + is a client hint, not a control. +- An absent or invalid role set defaults to viewer. A cookie carrying no roles must + never be treated as carrying privileges. +- The signing secret is at least 32 bytes, and an ephemeral one is generated in + development rather than requiring configuration that does not matter locally. + +## Consequences + +**Good** + +- Any replica can authenticate any request, with no stickiness and no shared store. +- A rollback of the Deployment does not log anyone out. +- Nothing to sweep, nothing to migrate. + +**Bad** + +- No per-session revocation. Bounded by the session lifetime. +- Rotating the key logs out everyone, not one session. + +**Neutral** + +- Logout must clear the cookie rather than delete a record, so a client that keeps + a copy can still replay until expiry. Mitigated by the short lifetime. +- The payload is readable by its holder. Acceptable because it contains no secrets. + diff --git a/data/docs/go-bananas-0004-typescript-version.md b/data/docs/go-bananas-0004-typescript-version.md new file mode 100644 index 0000000..c8c765d --- /dev/null +++ b/data/docs/go-bananas-0004-typescript-version.md @@ -0,0 +1,76 @@ + +Imported from docs/adr/0004-typescript-version.md in the go-bananas repository. + +--- + +# ADR 0004: TypeScript 6.0.3 rather than 7.x + +- **Status:** accepted +- **Date:** 2026-10-04 +- **Revisit when:** typescript-eslint ships TypeScript 7 support + +## Context + +The brief was to take the latest version of everything. For TypeScript, the latest +release is 7.0.2, which is the native Go port of the compiler (`tsgo`). + +## Decision + +Pin TypeScript to 6.0.3 in both `web/` and `sdk/`. + +## Rationale + +TypeScript 7.0.2 ships `tsc.js` and a version module. It does **not** ship a +JavaScript compiler API: + +```console +$ npm install typescript@7.0.2 +$ node -e "const ts=require('typescript'); console.log(typeof ts.createProgram)" +undefined +``` + +That API is what every type-aware linting tool consumes. `typescript-eslint@8.71.0`, +the current release, declares: + +``` +peerDependencies: { typescript: ">=4.8.4 <6.1.0" } +``` + +So TypeScript 7 fails to install at all alongside the standard linting stack, and +would fail at run time if forced past the peer check. + +The choice is therefore between: + +- **TypeScript 7.x** — a much faster compiler, and no type-aware ESLint rules at all, + because the tooling that provides them cannot run. `npm install` fails outright + without `--legacy-peer-deps`, which is a smell in a project rather than a solution. +- **TypeScript 6.0.3** — the newest release that ships both the CLI and the + JavaScript API, satisfying every tool in the toolchain. + +## Why not upgrade anyway and lose the linting + +The project's standards require type-aware linting: `@typescript-eslint` rules that +need the compiler's type information, which is what catches the mistakes that matter +in a codebase using `unknown` at API boundaries. Dropping it to gain compiler speed +is a bad trade: the speed is a developer-experience improvement, and the linting is +a correctness control. + +## What this costs + +TypeScript 6.0.3 is slower than 7.0.2. On this codebase the typecheck completes in +well under a second, so the difference is not observable, and the compilation cost +that matters is Vite's, which uses its own transform. + +## When to revisit + +When typescript-eslint declares support for TypeScript 7, which requires the +JavaScript API or a native alternative to it. That is a one-line version bump plus +a `make check` run. This ADR exists so that the pin is a decision with a reason +rather than an accident that outlives its justification. + +## A related note: `baseUrl` is deprecated + +TypeScript 6 deprecates `compilerOptions.baseUrl`. The project resolves `paths` +relative to the tsconfig instead, which is the documented replacement and removes a +compiler option that only ever existed to make `paths` work. + diff --git a/data/docs/go-bananas-0005-shared-token-contract.md b/data/docs/go-bananas-0005-shared-token-contract.md new file mode 100644 index 0000000..a52fe81 --- /dev/null +++ b/data/docs/go-bananas-0005-shared-token-contract.md @@ -0,0 +1,125 @@ + +Imported from docs/adr/0005-shared-token-contract.md in the go-bananas repository. + +--- + +# ADR 0005: One token contract for every application, with a legacy alias layer + +- **Status:** accepted +- **Date:** 2026-10-05 +- **Revisit when:** an application adopts Tailwind v4 as its only styling mechanism, which + would let the alias layer be dropped + +## Context + +Six applications consume design decisions today, each in its own dialect: + +| Application | Mechanism | Palettes | Light/dark | Token indirection | +| --- | --- | --- | --- | --- | +| `go-doc` | Tailwind v4 `@theme` | 1, dark hardcoded | none | `--color-ink-*` | +| `go-guess` | one 2800-line CSS file | 1, light hardcoded | none | 11 `:root` vars, mostly bypassed by ~120 hardcoded hexes | +| `go-loose` | plain CSS | 4 × 2 modes | works | `[data-theme=X]` + `[data-mode=light]` | +| `go-mad` | plain CSS, copied from `go-loose` | 1 × 2 modes | CSS present, JS dead | `[data-mode=X]` | +| `go-places` | JS-injected `