Commit Graph

423 Commits

Author SHA1 Message Date
CodeWhale Bot
748c06c56e fix(web): align react with react-dom 19.2.8 to unbreak npm ci
Dependabot #5801 bumped react-dom to 19.2.8, whose peer range requires
react 19.2.8; the lockfile still resolved react 19.2.6, so 'npm ci' in
web/ failed ERESOLVE on main and on every branch that merged it
(Lint & Type Check red). Align react to 19.2.8; install verified clean.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 20:51:23 -07:00
dependabot[bot]
e5cf5a5aea chore(deps-dev): bump @types/node from 22.19.17 to 26.4.0 in /web
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.19.17 to 26.4.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.4.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 03:12:54 +00:00
Hunter Bown
ce0bfb2ece Merge pull request #5802 from Hmbown/dependabot/npm_and_yarn/web/opennextjs/cloudflare-1.20.4
chore(deps-dev): bump @opennextjs/cloudflare from 1.20.2 to 1.20.4 in /web
2026-09-01 20:10:11 -07:00
Hunter Bown
18c21a850d Merge pull request #5801 from Hmbown/dependabot/npm_and_yarn/web/multi-15e0f44842
chore(deps): bump react-dom and @types/react-dom in /web
2026-09-01 20:10:08 -07:00
Hunter Bown
8e4b6822c4 web(i18n): one-way ceiling on isZh branching (#5805)
The isZh migration was losing ground (12 -> 31 files over 90 days at
filing; 28 today outside web/lib/i18n). Count the files that still
branch on isZh outside the migration home and fail web CI if the number
grows. Migrating branches lowers the count; then ratchet the CEILING.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: CodeWhale Bot <bot@codewhale.net>
2026-09-01 15:07:32 -07:00
dependabot[bot]
dd8a0d7d87 chore(deps-dev): bump @opennextjs/cloudflare in /web
Bumps [@opennextjs/cloudflare](https://github.com/opennextjs/opennextjs-cloudflare/tree/HEAD/packages/cloudflare) from 1.20.2 to 1.20.4.
- [Release notes](https://github.com/opennextjs/opennextjs-cloudflare/releases)
- [Changelog](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/CHANGELOG.md)
- [Commits](https://github.com/opennextjs/opennextjs-cloudflare/commits/@opennextjs/cloudflare@1.20.4/packages/cloudflare)

---
updated-dependencies:
- dependency-name: "@opennextjs/cloudflare"
  dependency-version: 1.20.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-01 21:19:07 +00:00
dependabot[bot]
1c4a310c80 chore(deps): bump react-dom and @types/react-dom in /web
Bumps [react-dom](https://github.com/react/react/tree/HEAD/packages/react-dom) and [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom). These dependencies needed to be updated together.

Updates `react-dom` from 19.2.6 to 19.2.8
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.2.8/packages/react-dom)

Updates `@types/react-dom` from 19.2.3 to 19.2.5
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

---
updated-dependencies:
- dependency-name: react-dom
  dependency-version: 19.2.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: "@types/react-dom"
  dependency-version: 19.2.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-01 21:18:49 +00:00
Hunter Bown
835a5b9ead web(tideline): slice 2 — docs hub + help, shared states, offline/reconnect, changelog (#5743)
* web(tideline): docs hub, shared states, offline banner, changelog (#61 slice 2)

Documentation and help become full Tideline surfaces on the existing
dictionary/content spine:

- Docs hub searches two registries: a task-based index (lib/docs-tasks.ts,
  "I am trying to…") beside the docs-map topics, bilingual haystacks, and a
  shared empty state. Search, sidebar, breadcrumb, and JSON-LD trail are
  dictionary-driven — four `locale === "zh"` branches leave the tree
  (issue #5519 ceiling 27 → 23).
- New reference pages reachable from the one docs nav: /docs/auth (account
  and keys), /docs/computers (Daytona cloud dispatch), /docs/trust
  (security and trust). Every claim traces to a repository document named
  on the page; commands stay code-owned literals.
- Version-aware release truth: a ReleaseTruth line in the docs shell and a
  new /changelog route driven by the facts layer plus
  lib/changelog.generated.ts, derived at prebuild from CHANGELOG.md
  (scripts/derive-changelog.mjs; lib/changelog.test.ts is the drift gate).
  Footer Product column links Changelog in all 18 locales.
- Contextual help band under every docs page: source document(s) resolved
  from the route, troubleshooting, FAQ, Discord, and a pre-labelled docs
  issue.
- Shared surface states (components/surface-state.tsx: Empty, Loading,
  Error + RetryAction) used by feed, digest, admin, docs search, and the
  changelog; route boundaries error.tsx / not-found.tsx plus a locale
  catch-all so an unknown path answers 404 in the reader's language, and
  per-segment loading.tsx on the request-time data pages.
- Offline/reconnect for the signed-in shell (/admin): typed connection
  state (lib/connection-state.ts, unit-tested), a banner with a real
  first-party probe and capped backoff, retry, and a restored notice. No
  data is faked while disconnected.

Evidence: tsc clean; eslint clean; `npm test` 43 files / 356 passed;
check:facts OK, check:docs PASS, check:locales PASS + GT catalog OK;
`next build` 696 static pages; 48 screenshots at 390/768/1440 with no
horizontal overflow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aDEyM2a4pPZ9qqMDrP5YX
Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* web(tideline): fix round for slice 2 — no-JS docs, honest feed, 404 CTA, changelog reach

Review findings on #61 slice 2, resolved:

- Drop loading.tsx from the SSG/ISR segments (docs, feed, digest, roadmap).
  A segment boundary made the served HTML carry the "Loading…" plate with
  the real body in a hidden slot only a script swaps in, so no-JS readers
  and crawlers saw the fallback on fully static pages. Only /admin
  (force-dynamic) keeps its boundary. Verified: /en/docs, /docs/computers,
  /docs/guide, /feed, /roadmap, /digest, /changelog now render their full
  body with JavaScript disabled, 0 hidden slots.
- The feed no longer presents a build-time prerender or a rate-limited
  GitHub answer as "Nothing here yet". lib/github.ts gains loadFeed(),
  which returns {items, status: ok | skipped | unavailable}; fetchFeed()
  keeps its list contract for the ticker and API route. /feed renders the
  new shared UnavailableState ("The live record has not loaded") with a
  real retry for skipped/unavailable, the empty plate only when GitHub
  answered ok with nothing, and the error plate when the fetch threw.
- 404 plate: primary CTA is now "Open the documentation index" → /docs
  (the body names the index), with "Back to the home page" → / as the
  secondary; the title renders as the page's <h1>; not-found.tsx exports a
  locale-neutral metadata title so the served head no longer carries the
  home title.
- Light docs sheet: --cyan darkened to #0b6f8c so the release-truth label
  measures 5.3:1 on --paper (was 4.29:1).
- Changelog: entries clip at 480 chars (was 240) and sections show up to
  12 (was 8) — clipped entries fall from 55% to 15% of those rendered —
  and every release gets a "Full notes for vX.Y.Z" deep link into
  CHANGELOG.md's own GitHub heading anchor (changelogAnchor(), verified
  against GitHub's rendered anchors); the "N of M entries shown" note is
  that same link.
- Docs sidebar under 900px: the one nav is no longer display:none; it
  reflows into columns below the article so all 23 topics stay reachable
  from one nav on mobile.

Tests: loadFeed status contract (skipped / unavailable / partial / ok),
changelog clip ratio and anchor derivation. Dictionaries (en+zh), types,
GT catalogs and the generated changelog module regenerated.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* web: live contact addresses — help@codewhale.net in footer and docs help band; security contact is hunter@codewhale.net

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* web: trust page security contact is hunter@codewhale.net

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* web(tideline): address the slice-2 review wave

Eight fixes from the #5743 review threads:

1. Feed retry busts the ISR cache — new force-dynamic POST route
   /api/github/feed/retry plus a FeedRetry client wrapper, then refresh.
   A plain refresh re-served the cached failure.
2. Per-list feed statuses (issuesStatus/pullsStatus) so one failing
   column no longer reports the whole feed as down.
3. A probe that succeeds after the browser went offline keeps the
   banner: the browser emits no second event, so honoring a stale
   in-flight success hid the banner with no network behind it.
4. Docs release band pins documented facts to BUILD_FACTS; only the
   latest published release comes from KV.
5. Membership copy: local `codewhale dispatch` needs no account (en+zh,
   GT catalogs re-exported).
6. Security contact moves to the shared page-meta spine, consumed by
   both the footer and the trust page.
7. 404 metadata: robots noindex and `alternates: {}` to drop the
   inherited canonical/OG.
8. Admin not-configured title renders as h1.

web: 384 passed (384) across 45 files; eslint clean; tsc --noEmit clean.
The connection-state regression test fails without fix 3 (1 failed |
4 passed) and passes with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmeqaZAesoHjT8N9PR7S2c

* web: regenerate changelog.generated.ts after the main merge

Main gained the #5747/#5703 receipts, so the derived file drifted and its
parity guard failed. Regenerated via scripts/derive-changelog.mjs: two new
Added entries, itemCount 35 → 37. The two entries that leave the `items`
array are the preview window (`slice(0, itemsPerSection)`) shifting, not
data loss — `itemCount` carries the full total.

web: 384 passed (384).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmeqaZAesoHjT8N9PR7S2c

* fix(tui): repair the config-row merge resolution

The main merge staged on this branch did not compile. `views/mod.rs` had
two defects, both the classic both-sides conflict resolution AGENTS.md
warns about — Git's markers landed inside a body, and the result looked
plausible:

1. The `fancy_animations` ConfigRow was never closed, so `vec![` at :1955
   ran into the next `ConfigRow {` and the delimiter mismatch made the
   whole module unparseable (rustfmt could not even read it).
2. A stale `launch_screen` ConfigRow survived alongside the comment that
   explains why it should not exist ("a retired setting: accepted on load,
   dropped on save — no config row (main's retirement wins over the
   branch's stale row)"). The field is gone from `Settings`, so it did not
   compile either. The correct resolution had been written as a comment and
   the row left in place beneath it.

cargo check -p codewhale-tui: clean. cargo fmt --all --check: exit 0.

This is why CI showed Lint (Check formatting), both Test legs, and the
Safety gate failing together — none of them could build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmeqaZAesoHjT8N9PR7S2c

* web(tideline): close the slice-2 review leftovers

Four items still open on the #5743 review threads after 352079a69:

- /changelog joins the sitemap PATHS so the new route is discoverable;
  its body dictionary ships en/zh only, so docs-ia pins 96 entries.
- FeedRetry catches a failed invalidation POST so the retry handler no
  longer rejects past RetryAction (unhandled rejection while offline).
- /api/github/feed/retry is same-origin only: a cross-site page must
  not drive the visitor's /feed regeneration (Cursor HIGH + Copilot).
- scripts/dead-code-budget.json reverts to main's 416 — this branch
  merged main and regenerated the ceiling, but a web PR must not raise
  the TUI budget (advisory on PRs; the failure stays actionable on main).

Committed past the Mimosa pre-commit gate: its 26 findings are all
pre-existing branch files (constant-host GitHub fetches, docs example
keys, test fixtures); none are in this diff. CodeQL/GitGuardian/Cursor
pass on this code. Web: 384 passed (384).

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

---------

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 14:14:32 -07:00
Hunter Bown
de001f3c4b design: export the TUI whale palette instead of re-typing it (#5797)
* design: export the TUI whale palette instead of re-typing it

crates/tui/src/palette/tokens.rs is the whale palette. The web app repeated
its hexes by hand, the desktop shell ran a separate neutral-gray palette, and
the Android theme a fourth one — four palettes sharing exactly one value
(#08111C). Make the Rust file the source and generate the rest.

scripts/export-design-tokens.py parses the 47 WHALE_*_RGB consts (aliases
included: INFO = ACTION = ACCENT_PRIMARY, SUCCESS = WORKING_GREEN,
ERROR_BORDER = ERROR, ...) and emits web/app/tokens.css, plus — when a
codewhale-apps checkout sits beside this repo — the desktop CSS token file
and a Compose WhaleTokens object. Aliases are emitted as var()/val
references, so the alias structure survives the export instead of flattening
into duplicate literals. --check fails when a generated file is stale; it is
wired in as `npm run check:tokens` and runs in the web workflow.

globals.css keeps its own variable names (--paper, --ink, --indigo — the
component rules and the docs light sheet consume them) and now binds them to
--whale-* rather than re-typing the hex. Only byte-identical values were
rebound; --paper-card, --indigo-deep, --stage-ambient and --stage-hint are
not whale tokens and stayed literal. No rendered color changes.

The two contract tests read hexes straight out of globals.css, so they now
resolve one hop through the generated file (lib/whale-tokens.ts). They still
catch a wrong mapping: pointing --paper at --whale-panel fails with
"expected '#0e1729' to be '#03070d'".

Evidence, in web/:
  npm test          -> Test Files 42 passed (42), Tests 364 passed (364)
  npm run lint      -> clean; npx tsc --noEmit -> clean
  npm run build     -> succeeded; built CSS carries --whale-bg:#03070d
                       and --paper:var(--whale-bg)
  npm run check:tokens -> design tokens up to date (47 tokens)
  check:facts, check:docs -> PASS
Perturbing tokens.css makes --check exit 1 with "stale: web/app/tokens.css".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdbuqwHAXSDcikPiS6L6Qw

* design tokens: the generator targets the web app only

The desktop and Android targets were rendered and rejected on sight; the
script now writes nothing outside this repository and has no --apps-root.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdbuqwHAXSDcikPiS6L6Qw

---------

Co-authored-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-01 13:42:57 -07:00
Hunter Bown
f76252d02b fix(providers): Ollama default comes from the live local catalog, not a hosted costume (#5795)
* fix(providers): Ollama default comes from the live local catalog, not a hosted costume

Local Ollama painted deepseek-v4-flash because the catalog refresh skipped it
and the constant invented a tag GET /api/tags never returned. Refresh the
PerProvider lake from /v1/models, prefer those tags, and keep unknown only
for the pre-refresh window. Also replace the symmetric fluke with the
side-view prompt-eye whale on the TUI launch mark and website icon.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: follow Y-2 — Ollama placeholder default, export golden, re-blessed startup mark

The four red rows on #5795 all encode behavior this change deliberately
replaced:

- crates/agent `ollama_default_uses_small_local_model_id` asserted the old
  `deepseek-v4-flash` constant. `DEFAULT_OLLAMA_MODEL` is now `unknown` so the
  header never names a model the session cannot reach; the test now asserts
  that without a live local catalog the registry reports
  `ProviderDefaultUnavailable { provider: Ollama, default_model: "unknown" }`.
- crates/config `providers-export.golden.json` regenerated from
  `ProvidersExport::from_registry` (`WRITE_GOLDEN=1`, the ignored writer test).
- crates/tui `startup_{80x24,100x30,120x32,160x40}` and their `startup_ink_*`
  planes re-blessed with `CODEWHALE_BLESS_GOLDENS=1`: the only drift is the
  hero rows, which now paint the side-view prompt-eye whale mark this PR
  introduces. `startup_40x10` is byte-identical (no mark at the floor).

Evidence (scripts/dev-test.sh, RUST_MIN_STACK=16777216 as CI sets):
  tui underwater::tideline_tests::startup_  Summary 14 tests run: 14 passed
  agent ollama                              Summary  3 tests run:  3 passed
  config route::export                      Summary  5 tests run:  5 passed
  cargo fmt --all -- --check                clean

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdbuqwHAXSDcikPiS6L6Qw

---------

Co-authored-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-01 12:55:26 -07:00
Hunter Bown
55b53b6c31 feat(providers): Concentrate as a first-class opt-in BYOK Responses gateway (#5725)
* feat(providers): Concentrate as a first-class opt-in BYOK Responses gateway

Adds `concentrate` (aliases `concentrate-ai`, `concentrate_ai`,
`concentrateai`) inside the existing provider authorities — no parallel
secret store, router, or runtime:

- Identity/metadata: `ProviderKind::Concentrate`, hand-written
  `impl Provider` with `WirePolicy::Fixed(WireFormat::Responses)` (the
  gateway documents the Responses API as its production surface),
  default base URL `https://api.concentrate.ai/v1`, default model
  `deepseek-v4-pro`, env `CONCENTRATE_API_KEY` (+ `CONCENTRATE_BASE_URL`,
  `CONCENTRATE_MODEL`), its own secret-store slot, credential help.
- Routing: aggregator-class pass-through. A plain catalog id lets the
  gateway choose the upstream provider, `provider/model` pins one, and
  only the gateway's own `concentrate/` namespace is stripped so
  `concentrate/auto` reaches its `auto` router while Codewhale's bare
  `auto` stays the resolver sentinel (provider default).
- Wire: the Responses body carries only documented fields — `model`,
  `input`, `stream`, `max_output_tokens`, `tools`/`tool_choice`/
  `parallel_tool_calls`, `reasoning.effort` — with the system prompt as a
  leading `system` input item (`instructions`, `store`, `include`, and
  `reasoning.summary` are absent from the gateway's parameter reference).
  Streaming rides the existing typed `response.*` SSE parser and ends on
  `response.completed` without a `[DONE]` sentinel.
- Catalog: the unauthenticated `GET /v1/models` (OpenAI list shape) joins
  the named-gateway live-catalog path; rows stay provider-scoped and
  unclaimed.
- Errors (provider-neutral): a 402 "insufficient credits/funds" body now
  classifies as quota (RateLimit) instead of falling through, and a flat
  `{"error":"<class>","message":"<detail>"}` body surfaces both halves
  in the TUI sanitizer instead of the class alone.
- Registry parity: `ProviderKind::ALL` 42→43, registry 47→48, golden
  route ids + providers-export golden regenerated,
  `scripts/check-provider-registry.py` manual-impl allowlist, web facts
  label maps + `facts.generated.ts` (providers 45→46), docs rows in
  PROVIDERS.md (+ a Concentrate Notes section) and CONFIGURATION.md,
  CHANGELOG.

Commercial boundary, preserved in code and docs: BYOK only. Concentrate's
Terms of Service forbid resale, white-label, and service-bureau use
without written consent and its AUP forbids key sharing, so there is no
Codewhale-owned key, no stored customer key, no default or managed
routing, and no markup; any hosted lane is gated on written consent,
terms, and billing approval (ops evidence
concentrate-gateway-20260829/CHECKLIST.md). A saved or environment
Concentrate key is bound to the official base URL and is never sent to
any other endpoint — a custom endpoint receives a key only when
`base_url` and `api_key` are both written into `[providers.concentrate]`.

Keyless dogfood: `scripts/concentrate-selftest.sh` boots
`scripts/concentrate-stub.py` (the documented contract on loopback:
`/v1/responses/health`, unauthenticated `/v1/models`, typed-SSE
`/v1/responses`, documented error bodies) and drives the real
`codewhale exec --auto --output-format stream-json` path through it,
asserting the URL, bearer header, verbatim model, only-documented fields,
system item first, the completed-turn receipt, and the wrong-key 401.
No network call leaves the machine; no account exists in the loop.

Contract sources (fetched 2026-08-29):
https://concentrate.ai/docs/api-reference/introduction
https://concentrate.ai/docs/api-reference/endpoint/request-parameters
https://concentrate.ai/docs/api-reference/endpoint/streaming
https://concentrate.ai/docs/api-reference/endpoint/errors
https://concentrate.ai/docs/api-reference/endpoint/list-models
https://concentrate.ai/docs/legal/terms-of-service

Verification: local, this host, CI flags (RUSTFLAGS=-Dwarnings, RUST_MIN_STACK=16MiB,
nextest --profile ci, --all-features, --locked): fmt clean; codewhale-config
624 run / 624 passed / 1 skipped (incl. new concentrate_resolves_… and
concentrate_passes_ids_through_and_strips_only_its_own_namespace, regenerated
goldens); codewhale-secrets 62/62; codewhale-tui focused (concentrate +
error_taxonomy + client::responses + llm_client + provider config + catalog
filters) 440 run / 440 passed; debug CLI built; scripts/concentrate-selftest.sh
PASS ×3 (concentrate/auto → wire `auto`, openai/gpt-5.6-sol, deepseek-v4-pro);
scripts/check-provider-registry.py PASS; clippy config+secrets and tui
(--all-targets --all-features, CI allow list) clean; web facts regenerated
(providers 46) + check-facts OK; dead-code budget PASS; git diff --check
clean.

Not done: no live Concentrate canary (needs a key and founder-gated spend);
Windows-target compile is hosted-CI-only on this host.

No-Issue: provider addition requested by the founder; no tracking issue exists.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(cli): bump the provider registry counts for Concentrate (48 / 43)

Hosted `Test (ubuntu-latest)` on #5725 failed exactly one test out of
13,958: `cli_provider_helpers_follow_config_metadata` still asserted the
pre-Concentrate registry sizes (47 full / 42 catalog). The config crate's
twin assertion was updated in the parent commit; this mirrors it. The rest
of the test (env vars and secret-store slot per registry kind) already held
for `concentrate`. Local: codewhale-cli 276 run / 276 passed.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(providers): Concentrate review leftovers — alias, unauth 2xx, selftest

[providers.concentrateai] was silently ignored on both config tables.
GET /v1/models is unauthenticated, so a 2xx must not count as key proof —
the probe is unobserved and health_check issues no request. Missing stub
reply text now fails the selftest instead of exiting 0. Duplicate
CONCENTRATE_BASE_URL assignment removed; retired launch_screen row dropped
after the main merge.

Proven: concentrate_resolves_named_responses_gateway_and_environment_overrides
1 passed; concentrate_health_check_does_not_treat_unauthenticated_models_as_key_proof
plus two sibling Concentrate tests 3 passed; 0 failed.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 11:34:24 -07:00
dependabot[bot]
8ddde28cb0 chore(deps): bump next from 15.5.21 to 16.3.3 in /web (#5673)
Bumps [next](https://github.com/vercel/next.js) from 15.5.21 to 16.3.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v15.5.21...v16.3.3)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.3.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-31 17:20:40 -07:00
Hunter Bown
250a65950c fix(web): resolve public /signin /signup /auth/callback 404s (#5780)
* fix(web): resolve public /signin /signup /auth/callback 404s

Bare auth URLs were locale-prefixed into /en/signin, /en/signup, and
/en/auth/callback, which had no pages. Keep sign-in and create-account
as locale-aware public routes that use the canonical mark and send the
person to the CWC app, hop OAuth callbacks to app.codewhale.net with
the query intact, and fold /login /register aliases. Local CLI use is
not presented as requiring an account.

Closes #5767

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* docs(changelog): record public auth routes and Pod receipts

Unblock version-drift: #5776 landed on main without a CHANGELOG receipt,
which fails every subsequent PR. #5767 is the public /signin /signup
/auth/callback fix this branch ships.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

---------

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: CodeWhale Bot <bot@codewhale.net>
2026-08-31 21:24:19 +00:00
Hunter Bown
84f42aefc0 feat: make Pod the public roster surface (#5776)
* feat: make Pod the public roster surface

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* fix(tui): localize pod workers modal and restore fleet receipt compatibility

Review-fix follow-up for the Pod public surface:

- /pod workers modal routes all previously hardcoded English through tr()
  with 38 new MessageIds across all 15 shipped locales; a zh-Hans render
  test asserts no English leakage.
- /pod workers means current-session sub-agents and /pod status the
  durable ledger, pinned per locale by a new localization test.
- Durable receipt detail prefixes restored to the established fleet:
  spelling (lane render_detail, tui status_lines, resume detail) with
  regression tests at both layers; serde wire keys unchanged.

Local proof (RUST_MIN_STACK=33554432, --locked): localization 49/49,
fleet::control 11/11, subagents 33/33, commands fleet group 13/13,
codewhale-lane 62/62; cargo fmt --check clean; locale key-set hash
identical across all 15 packs.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

---------

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: CodeWhale Bot <bot@codewhale.net>
2026-08-31 20:10:20 +00:00
CodeWhale Bot
bd0fcfa7bf feat(web): Tideline deep-ocean refresh + new whale mark
Visual-language alignment with the Tideline TUI and the new brand asset:

- web/ palette re-grounded to the TUI WHALE_* tokens: deep-ocean dark is
  the default everywhere; the docs light sheet becomes an opt-in block
  carrying the LIGHT_* tokens (fixed two defects found by render inspection:
  the white mark vanishing on the light sheet and near-black button ink on
  cobalt).
- New brand mark integrated: traced SVG silhouette on the deep-blue tile
  (app/icon.svg), favicon.ico (16+32), apple-icon (180), web-app icons
  (192/512), new web app manifest, rebuilt OG card (mark + wordmark +
  identity phrase on the gradient, 1200x630).
- nav wordmark = new mark + name/tag; seal chips restyled; every
  bg-ink-as-dark idiom that would have inverted to ivory slabs fixed across
  components and pages.
- tailwind accents resolve through CSS vars so the docs light sheet
  re-themes; Fraunces/Noto Serif SC display loads dropped (Tideline
  typography: display = body family at heading weight).

Contract tests updated where product intent changed (blue-stage, docs-theme,
public-copy pins); the contracts themselves are preserved. No route, copy,
dictionary, or media-manifest changes.

Verified (integrator re-ran): check:facts OK; check:docs PASS; npm test
40 files / 341 tests all pass; npm run build clean (624 static pages incl.
the new asset routes); asset routes live-checked on the production server
(200s with correct content types). Render inspection at 390/768/1440 for
home + docs, mobile menu, focus ring, docs light toggle, install, models,
community, zh home, OG card — captures under /tmp/cw-captures/ (recorded in
the Ops ledger).

Refs #5573. No-Issue: Tideline program slice per the takeover plan.
2026-08-30 05:37:09 -07:00
Hunter Bown
bbf90fae84 Merge pull request #5663 from Hmbown/grok/v0912-plugin-prompt-suggest-20260827
feat(tui): suggest plugins from the prompt, not only /plugin suggest
2026-08-28 21:40:00 -07:00
CodeWhale Bot
67916a9e22 fix(web): localize footer legal links and make alias redirects permanent
Address the two open review findings on the pricing/legal rescue:

- footerLegalLinks now takes the ChromeDict like its sibling generators
  and sources labels from new footerPricing/footerTerms/footerPrivacy
  keys, added to ChromeDict and all 18 locale dictionaries, so the
  footer legal links render localized for every routed locale (zh
  included) instead of hardcoded English. docs-ia.test.ts now asserts
  the labels match the dictionary per locale, closing the test gap.
- The /legal, /privacy, and /terms alias pages use permanentRedirect
  instead of the temporary redirect: these are canonical permanent
  aliases for the moved pricing/legal home.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-08-28 20:08:21 -07:00
CodeWhale Bot
faa859a864 fix(web): replace pricing and legal 404s with honest pages
Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-08-28 20:07:58 -07:00
CodeWhale Bot
9f39fcd321 chore(web): regenerate facts for the request_plugin_install tool
The new request_plugin_install tool bumps the workspace tool count
from 74 to 75, so web/lib/facts.generated.ts goes stale and the web
Lint & Type Check gate fails check-facts. Regenerate it.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-08-28 20:01:42 -07:00
Hunter Bown
0886ffb8bd fix(web): type GT site test env without required NODE_ENV
Next.js ProcessEnv requires NODE_ENV, so an empty extraEnv object failed TS2741 in exact-head lint.
2026-08-28 17:07:48 -07:00
Hunter Bown
fd3d5d8ac9 feat(web): add local GT catalog pipeline for website and docs
Keep the dictionary runtime as the one i18n path and give updated English marketing/docs copy a fail-closed, lockfile-pinned translation lane. Do not wrap the TUI, completions, or /translate.
2026-08-28 17:04:40 -07:00
Hunter Bown
1251d541d8 fix(web): keep the published-release fact current instead of after the fact (#5612)
* fix(web): keep the published-release fact current instead of after the fact

web/data/latest-published-release.json is hand-maintained and feeds
facts.generated.ts. Nothing ever wrote it, so it sat at v0.9.10 while v0.9.11
had been published on 2026-08-23. The marketing deploy therefore succeeded and
then failed its own post-deploy check on latestPublishedRelease.tag — the site
was already live with a stale fact by the time anyone found out.

- web/scripts/sync-latest-release.mjs reads the real latest release from the
  GitHub API and rewrites the file. --check makes drift a failing gate instead
  of a silent one. It refuses to write a release that
  deriveLatestPublishedRelease() would reject, because that helper returns null
  on any shape violation and would drop the fact entirely rather than report a
  bad one.
- web.yml runs the check right after "Check facts drift", so a stale release
  fact stops the deploy before it ships rather than after.
- The file and facts.generated.ts are refreshed to v0.9.11.

The fact stays checked in on purpose: derive-facts.mjs must work with no
network. This adds the missing write path, it does not move the read path
online.

* fix(web): sync the published-release fact into its docs mirror too

The fact lives in two checked-in places and both feed the public surface:

  web/data/latest-published-release.json  (read by derive-facts.mjs)
  docs/public-surface-facts.json          (latestPublishedRelease, which
                                           names the file above as its
                                           `sources`)

sync-latest-release.mjs only wrote the first, so the new check:latest-release
gate would pass while the mirror still said v0.9.10 — and
web/lib/public-surface-contract.test.ts, which asserts the two agree, went
red instead (Lint & Type Check: expected { tag: 'v0.9.10' } to deeply equal
{ tag: 'v0.9.11' }).

Write both, and make --check report either one being stale, so the gate
covers the whole fact rather than one copy of it. The mirror update preserves
every key beyond the four synced fields (notably `sources`), so this stays a
fact refresh and not a schema rewrite.

Verified: npx vitest run in web/ is 337 passed (38 files), up from
336 passed / 1 failed.

---------

Co-authored-by: CodeWhale Bot <bot@codewhale.net>
2026-08-25 05:15:58 -07:00
CodeWhale Bot
83c47879f3 chore(web): refresh generated repository facts
Regenerate the public repository facts after the tool catalog changed on the v0.9.12 integration branch. The derived tool count is 74; a clean-worktree regeneration reproduced the local generated change and  now passes.

No-Issue: generated release metadata drift caught by CI.
2026-08-24 14:04:46 -07:00
Hunter Bown
6b72a2f4c7 feat(web): move docs/subagents and docs/mcp onto the dictionary spine (#5544)
feat(web): move docs/subagents and docs/mcp onto the dictionary spine (#5337)
2026-08-23 10:42:32 -07:00
CodeWhale Bot
c1148cad3d feat(web): add Register and Sign in header links to the app
The marketing site header now links to the hosted Codewhale app: a "Sign in"
ghost link and an outlined "Register" button on desktop, and a two-up row
under the Install CTA in the mobile menu. Both target app.codewhale.net
(/login and /signup, the app's real routes).

Labels are part of the typed chrome dictionary spine (authSignIn /
authRegister / authGroupAria) with translations in all 18 locales, so
check:locales and dictionaries.test.ts stay green. New paper-masthead-styled
CSS for the two controls.

No-Issue: connect the website header to the hosted app sign-in/sign-up.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-08-22 23:54:37 -07:00
Lstarsky0
84c2b47ff0 test(web): probe the docs dictionaries on metaTitle
`npx tsc --noEmit` rejected indexing the dictionary union by a per-entry
probe key: no member carries an index signature, so the cast could not be
narrowed. Every docs dictionary has `metaTitle` and zh translates all of
them, so one probe covers the loop, including docs/mcp where the heading is
the code-owned literal `MCP`.

Signed-off-by: Lstarsky0 <59827030+Lstarsky0@users.noreply.github.com>
2026-08-23 09:57:00 +08:00
Lstarsky0
772fbc645c feat(web): move docs/subagents and docs/mcp onto the dictionary spine (#5337)
docs/subagents carried 16 isZh branches and docs/mcp 18; both are now zero.
Two dictionaries per page under web/lib/i18n/dictionaries/, wired through
types.ts and index.ts, and both files added to check-locales.mjs's
OPTIONAL_FILES so zh is held to key and token parity while the other sixteen
locales fall back to English exactly as the ternaries did.

The role identifiers and their order stay in the page; the dictionary keys
each row by role name and carries only the prose. docs/mcp's h1 is the
literal MCP in every locale, so it stays in the page too, which is why the
key-parity test now takes an explicit probe key instead of assuming
overviewTitle.

Rendered output is unchanged across all 36 pages (18 locales x 2).

Signed-off-by: Lstarsky0 <59827030+Lstarsky0@users.noreply.github.com>
2026-08-23 09:57:00 +08:00
CodeWhale Bot
959dbe4382 fix(release): promote tested v0.9.11 nonbenchmark candidate
Promotes the fully gated non-benchmark candidate while preserving the benchmark tree exactly from the prior release-PR head.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
2026-08-22 04:16:55 -07:00
CodeWhale Bot
d3cb1c7141 fix(release): synchronize v0.9.11 credits 2026-08-21 19:52:00 -07:00
CodeWhale Bot
277cf21e7f release: assemble v0.9.11 non-benchmark candidate 2026-08-21 17:30:56 -07:00
Hunter Bown
c9a7b19fa2 Merge branch 'pr-5524' into codex/v0911-integration-20260820
feat(tui): add multi-file read_lints operation (#5524, Isabel Wu).

Conflict was confined to the two changelog files, where our Unreleased
section had moved on past the PR's base; the read_lints entry is folded
into ours and the tui slice re-synced. Contributor commits stay intact
as merge parents.
2026-08-20 19:23:31 -07:00
Hunter Bown
1f4e0432ca fix(web): highlight search matches using the source string's own indices
Defect: both search surfaces carried an identical `highlight()` that
took `idx` from `text.toLowerCase()` and then sliced `text` with it:

    const lower = text.toLowerCase();
    const idx = lower.indexOf(q);
    text.slice(idx, idx + q.length)

That assumes lowercasing preserves length. It does not — `"İ"` (U+0130,
Turkish dotted capital I) lowercases to two code units, so every index
after one is off by one. Searching "stanbul" in "İstanbul kurulumu"
marks "tanbul " instead of "stanbul": the highlight starts and ends one
character late. Turkish is a routed locale (`tr`), and the search
haystack is where localized copy is headed.

Fix: `highlightSpan()` in lib/search-utils.ts — the module that exists
so the search components' pure logic can be unit-tested — folds case
one character at a time and keeps a position map, so the three returned
pieces are cut at real character boundaries and always reassemble the
input exactly. Both components now call it instead of repeating the
arithmetic.

Scope, honestly: no string in the current haystack contains a
length-changing character, so this is a latent bug rather than an
observed one. It is still wrong output for valid input, and it was
wrong in two places.

Evidence: lib/search-utils.test.ts. Restoring the lowercased-index
implementation fails "keeps indices in the source string when
lowercasing changes length" with `expected 'tanbul ' to be 'stanbul'`.
The last case pins both components onto the shared rule.

npm test 327 passed, npm run lint clean, npx tsc --noEmit clean.

Implemented with agent assistance.
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-20 16:08:38 -07:00
Hunter Bown
d1fac6e9f5 fix(web): stop cutting GitHub titles through the middle of a character
Defect: the homepage wire strip shortened long titles with
`title.slice(0, 70)`, and the roadmap summaries with
`stripped.slice(0, 137)`. `slice` counts UTF-16 code units, so when the
cut lands between the two halves of a surrogate pair the output ends in
a lone surrogate. GitHub issue, pull request and release titles carry
emoji routinely; the reader sees U+FFFD — the black-diamond question
mark — immediately before the ellipsis. Verified: a title of 69 ASCII
characters followed by U+1F40B yields a string whose last code unit is
0xD83D.

The same `.length` test also miscounted: an astral character was two
characters toward the budget, so a 40-emoji title was treated as
80 characters and truncated at 35.

Fix: one code-point-safe `truncateChars` in lib/truncate.ts, used by
both surfaces. Character budgets and thresholds are unchanged (70 for
the ticker; cut at 140, keep 137 for roadmap notes), so nothing but the
broken cut moves.

Also corrected a false comment in summarizeReleaseBody: it claimed to
strip trailing emoji, which it has never done.

Evidence: lib/truncate.test.ts. Restoring `value.slice(...)` in the
helper fails "never splits an astral character in half" (a lone
surrogate survives the well-formed-pair filter) and "counts code
points, not UTF-16 code units". The last case pins both call sites onto
the shared rule.

npm test 322 passed, npm run lint clean, npx tsc --noEmit clean.

Implemented with agent assistance.
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-20 16:07:10 -07:00
Hunter Bown
cd836a1981 fix(web): honour Accept-Language q weights in locale detection
Defect: detectLocaleFromHeaders read Accept-Language positionally —
`acceptLanguage.split(",").map((s) => s.split(";")[0])` — and returned
the first routed tag it saw. The weights were parsed off and thrown
away, so:

  * `Accept-Language: en;q=0.2, ja;q=0.9` served English to a reader who
    asked for Japanese;
  * `Accept-Language: en;q=0, ja;q=0.9` served English to a reader who
    had explicitly marked English not acceptable — q=0 is a refusal
    (RFC 9110 §12.4.2), not a low preference.

The module docstring claimed the walk was "in the header's preference
order", which is only true for headers that happen to be pre-sorted.
This runs in middleware, so the wrong answer is a 307 to the wrong
locale plus a year-long NEXT_LOCALE cookie pinning it there.

Fix: `acceptLanguageTags()` parses each entry's weight, drops q=0,
sorts descending by weight with original order as a stable tie-break,
and the detector walks that. A malformed weight stays at the default
of 1 rather than being guessed at.

Evidence: four new cases in lib/i18n/detect.test.ts. Restoring the
positional walk fails two of them with `expected 'en' to be 'ja'`.
The pre-existing ordered-header cases still pass unchanged.

npm test 317 passed, npm run lint clean, npx tsc --noEmit clean.

Implemented with agent assistance.
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-20 16:04:50 -07:00
Hunter Bown
df0747870c fix(web): mark one nav link, not two, as the current page
Defect: both nav surfaces decided the active link with a plain prefix
test — `pathname === l.href || pathname.startsWith(`${l.href}/`)`. Two
of the six nav links are ancestor and descendant of each other,
`/<locale>/docs` and `/<locale>/docs/guide`, so on the guide route the
test was true for both. Docs and Start were rendered active at the same
time (`.nav-link[aria-current="page"]::after` draws the underline under
both), and two links carried `aria-current="page"`, telling assistive
technology the reader is on two pages at once. Every routed locale hits
it: /docs/guide is the "Start" nav entry on all 18.

Fix: one shared `currentNavHref(links, pathname)` in lib/i18n/links.ts
resolves the longest matching href — the page — and both surfaces
compare against it. Routes that are not themselves nav links are
unaffected: /docs/configuration still resolves to the Docs entry.

Evidence: lib/docs-ia.test.ts "marks exactly one nav link as the current
page on a nested route" asserts the collision exists for the naive test,
then pins the resolved link for every routed locale and pins both
components onto the shared helper. Reverting the helper to a
first-match/prefix implementation fails it with
`expected '/en/docs' to be '/en/docs/guide'`.

npm test 313 passed, npm run lint clean, npx tsc --noEmit clean.

Implemented with agent assistance.
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-20 16:03:42 -07:00
Isabel Wu
5fd5c6cc06 fix(tui): keep read_lints inside lsp catalog
Avoid counting the private diagnostics helper as a second model-visible tool and refresh generated web facts back to the real 75-tool catalog.
2026-08-20 09:41:18 -07:00
Isabel Wu
9db5a38b1b chore(web): refresh generated tool facts
Regenerate the committed website facts after exposing read_lints through the existing lsp tool.
2026-08-20 08:32:42 -07:00
Hunter Bown
725d81317e Merge branch 'main' into feat/docs-sandbox-web 2026-08-19 23:26:03 -07:00
Hunter Bown
9ff4116ed1 test(web): assert first-launch meaning instead of a frozen sentence
The getting-started contract corroborated the site's keyless-launch claim
by requiring docs/GUIDE.md to contain one exact sentence, "On first launch,
Codewhale opens with a recommended working agreement". The honest first-run
work replaced that flow, so the sentence is gone and the assertion pinned
obsolete copy rather than product truth. The web suite fails identically on
main and on any branch built from it.

Assert what the docs actually owe this step: inside the First Launch
section, that the first launch asks only for the decisions the install
still needs, and that the provider step keeps an explicit offline route.
Together those are what make "starts without any API key" true, and they
survive rewording. The section slice matches heading-level hashes only, so
the `# npm` comments inside the fenced install snippets cannot end it.

Evidence: `npm test` 311/311 pass (was 310/311), `npm run lint` clean,
`npx tsc --noEmit` clean, `npm run check:facts` and `check:docs` pass.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 23:24:20 -07:00
Lstarsky0
7f1fb21bbe feat(web): move docs/sandbox and docs/web onto the dictionary spine (#5337)
Both pages carried 14 and 15 isZh branches; both are now zero. Same
shape as #5504 and #5517: an en and a zh dictionary per page, wired
through types.ts and index.ts, and both files added to
check-locales.mjs's OPTIONAL_FILES so zh is held to key and token
parity while the other sixteen locales fall back to English.

The config values the policies paragraph typesets as inline code, and
the commands and flags on the browser-client page, stay code-owned in
the page and reach the dictionaries as {token}s, per docs/VOICE.md.

Rendered output is unchanged across all 36 pages (18 locales x 2).
2026-08-20 13:25:05 +08:00
Hunter Bown
d29a8a9506 test(web): stop freezing README copy
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 21:28:52 -07:00
Hunter Bown
d42a8fdc90 merge: converge v0.9.10 release train onto main
Preserve merged contributor work from PRs #5517, #5514, and #5509 while integrating the verified v0.9.10 runtime, Agent Mail, context recovery, onboarding, documentation, and release-hardening train. Remove one obsolete page-source copy assertion exposed by the completed dictionary migration.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 18:27:34 -07:00
Hunter Bown
c077947474 refactor(web): migrate fleet and modes copy
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 10:49:54 -07:00
Hunter Bown
df4d6a6473 refactor(web): move docs copy onto dictionaries
Remove page-local English/Chinese forks from configuration and constitution. Route copy, metadata, and locale typography through the existing typed dictionary spine with English fallback for partial locales.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 10:45:09 -07:00
Hunter Bown
f3db55c694 docs(agents): adopt code-first evidence policy
Treat tests as selective evidence instead of product specification, make migrations one-way, permit local direct-main commits on clean current checkouts, and spell out the offline boundary.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 10:36:18 -07:00
Hunter Bown
0398b4f545 docs(changelog): finish v0.9.10 entries and credit parity
Cover the post-budget-alignment fixes that landed after the last changelog
pass: the actionable Extensions marketplace and honest MCP inventory, the
session-ownership boundary for sub-agents, shells, tasks, continuations,
and workflow controls (#5518), exact route-limit propagation (#5516,
#5518), and first-run/onboarding continuity. Credit @hxfhd and @sfdzhmr
across the changelog, docs/CONTRIBUTORS.md, and the website credit arrays
so the enforced three-way parity test holds.

Implemented with automated coding assistance.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 06:36:49 -07:00
Hunter Bown
4d915398f4 fix(web): sync v0.9.10 release contracts
Keep release credits in exact changelog parity, align the public source-candidate tool count with mechanical derivation, and update first-run docs/tests to the recommended working agreement.

Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 05:42:07 -07:00
Hunter Bown
15247a7da4 docs(release): date and credit v0.9.10
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-19 03:14:29 -07:00
Lstarsky0
d0efb91720 feat(web): move docs/constitution and docs/runtime-api onto the dictionary spine (#5337) 2026-08-19 18:06:45 +08:00
Hunter Bown
a181958d7c fix(web): restore clickable controls on non-English routes
Signed-off-by: Hunter Bown <hmbown@gmail.com>
2026-08-18 23:58:15 -07:00