Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
247ecbd
feat(#487): left-navigation layout core and preferences (phase 1)
BorisTyshkevich Jul 29, 2026
acc7317
fix(#487): address ChatGPT review feedback
BorisTyshkevich Jul 29, 2026
6715b42
Merge pull request #571 from Altinity/feat/left-nav-layout-core-487p1
BorisTyshkevich Jul 29, 2026
5f65655
feat(#487): navigation section registry (phase 2)
BorisTyshkevich Jul 29, 2026
6177a51
fix(#487): address ChatGPT review feedback
BorisTyshkevich Jul 29, 2026
c82295b
fix(#487): address ChatGPT second-review minors
BorisTyshkevich Jul 30, 2026
bcceb1e
Merge pull request #573 from Altinity/feat/nav-section-registry-487p2
BorisTyshkevich Jul 30, 2026
a1bea7d
feat(#487): left-nav resize session and centre-width clamp (phase 3, …
BorisTyshkevich Jul 30, 2026
c7e3e74
feat(#487): left navigation controller seam (phase 3, step 2)
BorisTyshkevich Jul 30, 2026
ad36b0c
feat(#487): per-section lower-nav filters and independent rendering (…
BorisTyshkevich Jul 30, 2026
2a115f0
fix(#487): resize-session must compare raw proposals, not clamped ones
BorisTyshkevich Jul 30, 2026
7ff6428
feat(#487): left-rail and left-nav-separator modules (phase 3, step 4a)
BorisTyshkevich Jul 30, 2026
bfdfe8b
feat(#487): wire rail, separator and sidebar re-presentation into app…
BorisTyshkevich Jul 30, 2026
d6d6781
fix(#487): drawer CSS cascade, stale ARIA, and session-authority conf…
BorisTyshkevich Jul 30, 2026
cd97dc0
feat(#487): Escape closes the focused drawer, focus restoration (phas…
BorisTyshkevich Jul 30, 2026
7b487cd
test(#487): e2e coverage for the rail/drawer, raise the drawer floor …
BorisTyshkevich Jul 30, 2026
be9af64
fix(#487): reveal actions must open the focused drawer, not just swit…
BorisTyshkevich Jul 30, 2026
b80ae21
fix(#487): address final ChatGPT PR review findings (phase 3)
BorisTyshkevich Jul 30, 2026
7193837
fix(deploy): allow any HTTPS host on sql.demo.altinity.cloud's connec…
BorisTyshkevich Jul 30, 2026
464597b
Merge pull request #576 from Altinity/fix/demo-connect-src-any-host
BorisTyshkevich Jul 30, 2026
58d415b
fix(#487): address second ChatGPT PR review findings (phase 3)
BorisTyshkevich Jul 30, 2026
d9f6ac5
fix(#487): restore focus on wide-to-rail fold and mobile-hidden-sideb…
BorisTyshkevich Jul 30, 2026
92cb36b
Merge pull request #574 from Altinity/feat/left-rail-focused-drawer-4…
BorisTyshkevich Jul 30, 2026
521d839
chore(skills): promote skills/ to a first-class repo-root directory
BorisTyshkevich Jul 30, 2026
d0a5de5
Merge pull request #579 from Altinity/chore/skills-directory
BorisTyshkevich Jul 30, 2026
3e13fa5
docs: add V2 UX & interaction handover for design redesign
BorisTyshkevich Jul 31, 2026
a69e648
chore(deps-dev): bump @playwright/test in the dev-dependencies group
dependabot[bot] Aug 3, 2026
c2863a6
chore(deps): bump @codemirror/view from 6.43.6 to 6.43.7
dependabot[bot] Aug 3, 2026
f11eb1d
docs: record ADR-0004 — retain vanilla rendering, reject the Preact m…
BorisTyshkevich Aug 3, 2026
5cd3f22
docs(wiki): move the knowledge base in-repo as .wiki/ and reconcile w…
BorisTyshkevich Aug 3, 2026
c470de0
chore(skills): automate ChatGPT review and shipping
BorisTyshkevich Aug 3, 2026
9f41eef
refactor(#586): SurfaceLifecycle primitive + docked right-inspector slot
BorisTyshkevich Aug 3, 2026
4e98a9d
Merge remote-tracking branch 'origin/main' into refactor/surface-life…
BorisTyshkevich Aug 3, 2026
ad88a88
fix(#586): make the non-modal claims falsifiable and stop a lifecycle…
BorisTyshkevich Aug 3, 2026
8a95255
fix(chatgpt-review): preserve UI settings and retry streams
BorisTyshkevich Aug 3, 2026
8539d81
fix(#586): cancel abandoned inspector drags, dock-aware width clamp, …
BorisTyshkevich Aug 3, 2026
dfd9141
docs(#586): record the dock-aware width clamp and fail-closed width p…
BorisTyshkevich Aug 3, 2026
876e346
Publish ChatGPT review skill documentation
BorisTyshkevich Aug 4, 2026
2aa9708
Merge pull request #596 from Altinity/refactor/surface-lifecycle-586
BorisTyshkevich Aug 4, 2026
ab41dcf
refactor(#587): add the pure side-panel manifest and generic UI registry
BorisTyshkevich Aug 4, 2026
0cfbf26
refactor(#587): wire the side-panel registry through state, prefs, sh…
BorisTyshkevich Aug 4, 2026
eb76b54
refactor(#587): give the lower pane's persistent hosts a layout class…
BorisTyshkevich Aug 4, 2026
8562a42
test(#587): assert shell disposal tears every registered panel down
BorisTyshkevich Aug 4, 2026
a43b710
test(#587): AC5's three-way proof — compile-time and source-contract …
BorisTyshkevich Aug 4, 2026
4cae5d8
docs(#587): record the side-panel registry in CHANGELOG and ADR-0004
BorisTyshkevich Aug 4, 2026
c830a96
fix(#587): two-pass showPanel, real-panel freshness tests, raw-seam g…
BorisTyshkevich Aug 4, 2026
8fdbfa0
fix(#587): stop app-shell.ts naming concrete panel defs; wire up acce…
BorisTyshkevich Aug 4, 2026
22097a8
fix(#587): derive UpperPanelId/LowerPanelId from the manifest's pane …
BorisTyshkevich Aug 4, 2026
c80b120
fix(#587): correct an unverified precedent claim in the new type-chec…
BorisTyshkevich Aug 4, 2026
e2fa551
fix(#587): derive schemaPane's upper-pane hosts from the registry; co…
BorisTyshkevich Aug 4, 2026
8eb774b
fix(#587): remove aria-label accessibility regression from side-panel…
BorisTyshkevich Aug 4, 2026
96ff31c
fix(#587): make production side-panel wiring exhaustive over the mani…
BorisTyshkevich Aug 4, 2026
6c4df30
fix(#587): match all quote styles in the literal-panel-id source guard
BorisTyshkevich Aug 4, 2026
84288cd
fix(#587): enforce side-panel id and persisted-key uniqueness
BorisTyshkevich Aug 4, 2026
b0ba89b
Merge pull request #600 from Altinity/refactor/side-panel-registry-587
BorisTyshkevich Aug 4, 2026
f406fb6
chore(skills): rebuild /ship around risk budgets, invariant maps, and…
BorisTyshkevich Aug 4, 2026
d9fdc4f
fix(#591): fail-closed decoders for persisted domain records
BorisTyshkevich Aug 4, 2026
509ca20
docs(skills): default Medium-risk plan review to chatgpt-review
BorisTyshkevich Aug 4, 2026
8cd85af
fix(#591): close prototype-mutation and integer-overflow gaps in fail…
BorisTyshkevich Aug 4, 2026
812eaa4
fix(#591): close the same prototype-mutation hazard in recent-values.…
BorisTyshkevich Aug 4, 2026
e5d17b7
Merge pull request #601 from Altinity/refactor/fail-closed-decoders-591
BorisTyshkevich Aug 4, 2026
573ef7a
test(#599): fix resize-then-measure race in inspector-dock-layout and…
BorisTyshkevich Aug 4, 2026
51bb190
Merge pull request #602 from Altinity/test/e2e-resize-race-599
BorisTyshkevich Aug 4, 2026
de97214
refactor(#588): extract variable strip into src/ui/workbench/variable…
BorisTyshkevich Aug 4, 2026
5b648a9
refactor(#588): promote anchoredPopover, extract save-controller, hoi…
BorisTyshkevich Aug 4, 2026
8a20ada
refactor(#588): extract workspace-session with frozen route-currency …
BorisTyshkevich Aug 4, 2026
7575f68
refactor(#588): extract surface-navigation, relocate route/focus type…
BorisTyshkevich Aug 4, 2026
6c26a94
refactor(#588): replace the as App cast with typed staged constructio…
BorisTyshkevich Aug 4, 2026
0d5e511
fix(#588): repoint two e2e fixtures onto app.nav after wave 4's App-s…
BorisTyshkevich Aug 4, 2026
ca92c0b
docs(#588): reconcile CHANGELOG/wiki, drop orphaned imports, fix stal…
BorisTyshkevich Aug 4, 2026
15f798a
fix(#588): ChatGPT pass-1 findings — stale construction comment, undo…
BorisTyshkevich Aug 4, 2026
00b0378
Merge pull request #604 from Altinity/refactor/decompose-composition-…
BorisTyshkevich Aug 4, 2026
bc5f307
refactor(#589): extract dashboardRepaintPlan with staged memo commits…
BorisTyshkevich Aug 4, 2026
1b216f8
refactor(#589): extract createTileGestureController, preserve mixed-g…
BorisTyshkevich Aug 4, 2026
e26e77c
test(#589): migrate dashboard.ts mechanics assertions to direct unit …
BorisTyshkevich Aug 4, 2026
dd60761
fix(#589): restore optionsSig id, fix engine-switch throw-path sig re…
BorisTyshkevich Aug 5, 2026
0a7823d
fix(#589): commit mobile before the planner call, skip wasted persist…
BorisTyshkevich Aug 5, 2026
934dc56
fix(#589): restore compute/apply interleaving for full throw-path par…
BorisTyshkevich Aug 5, 2026
9b95d47
fix(#589): prove dashboardRepaintPlan/production-sequence equivalence…
BorisTyshkevich Aug 5, 2026
7e645eb
docs(#589): correct stale line-count claim, cross-reference inbox iss…
BorisTyshkevich Aug 5, 2026
e3463de
Merge pull request #608 from Altinity/refactor/dashboard-tile-gesture…
BorisTyshkevich Aug 5, 2026
6bb7f8c
chore(skills): make /ship single-mode with plan/code ChatGPT review l…
BorisTyshkevich Aug 5, 2026
a312a3b
Merge pull request #609 from Altinity/chore/ship-skill-single-mode
BorisTyshkevich Aug 5, 2026
ab64db0
chore(skills): run /ship review loops as Workflow scripts
BorisTyshkevich Aug 5, 2026
4bab0ca
Merge pull request #610 from Altinity/chore/ship-review-loop-workflows
BorisTyshkevich Aug 5, 2026
312b188
chore(skills): fix /ship review-loop workflow args and model split
BorisTyshkevich Aug 5, 2026
69a369e
fix(chatgpt-review): detect completion by content, not DOM element count
BorisTyshkevich Aug 5, 2026
f542dff
Merge pull request #611 from Altinity/chore/ship-model-split-and-revi…
BorisTyshkevich Aug 5, 2026
2fb6263
Merge pull request #612 from Altinity/fix/chatgpt-review-dom-pruning
BorisTyshkevich Aug 5, 2026
9bc119a
refactor(#590): make the committed workspace + main-surface reactive,…
BorisTyshkevich Aug 5, 2026
b39b3a7
refactor(#590): add dashboardTreeUi counting-discipline test, reconci…
BorisTyshkevich Aug 5, 2026
bdc2c12
Merge pull request #613 from Altinity/refactor/reactive-committed-wor…
BorisTyshkevich Aug 5, 2026
7838fa9
feat(skills): add ChatGPT-authored ship planning
BorisTyshkevich Aug 5, 2026
cb9204b
Merge pull request #614 from Altinity/agent/chatgpt-planner-mode
BorisTyshkevich Aug 5, 2026
f25893a
chore(deps-dev): bump @playwright/test in the dev-dependencies group
dependabot[bot] Aug 3, 2026
d1dc5ae
Merge current main into Dependabot PR #583
BorisTyshkevich Aug 5, 2026
5acceb3
Merge pull request #583 from Altinity/dependabot/npm_and_yarn/dev-dep…
BorisTyshkevich Aug 5, 2026
fdf99ff
chore(deps): bump @codemirror/view from 6.43.6 to 6.43.7
dependabot[bot] Aug 3, 2026
ac611b5
Merge current main into Dependabot PR #584
BorisTyshkevich Aug 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# Dependencies (restore the committed lock with `npm ci`)
# No trailing slash so a `node_modules` symlink is ignored too.
/node_modules
/skills/chatgpt-review/node_modules/

# Rendered, secret-bearing config — only the *.tmpl is committed
/deploy/config.json
Expand All @@ -24,9 +25,6 @@
# Reference material (the verbatim deployed SPA, kept locally for diffing)
/reference/

# Project wiki — a separate GitHub repo.wiki.git checkout, not part of this repo
/.wiki/

# impeccable design skill — vendored dev tool + its local state; kept local, not in the repo
/skills/impeccable/
/.impeccable/
Expand Down
67 changes: 67 additions & 0 deletions .wiki/Architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Architecture

Back to [[Home]]. Related: [[Source-Map]], [[Decisions-and-Roadmap]].

## Dependency shape

```text
main.js (bootstrap + concrete adapters)
├─ ui/ (renderers and controller)
├─ editor/ (injected SQL + Spec CodeMirror adapters)
├─ net/ (OAuth and ClickHouse HTTP)
├─ state.js (signals-backed model and operations)
└─ core/ (pure parsing, transforms, layout, formatting)

ui/ → net/state/core net/ → core core/ → nothing
```

`src/main.js` bootstraps the app; `createApp(env)` in `src/ui/app.js` is the
composition root, receiving browser and service dependencies and returning the
`app` controller every render module addresses. Render modules must not import
`app.js`, which prevents cycles. `createApp` builds `app` via one typed object
literal with no `as App` cast — a member missing from construction is a `tsc`
error, not a runtime hole (#588). Four responsibilities that used to live
entirely inside `createApp` are now their own modules the composition root
wires up: workspace persistence/cross-tab sync
(`src/application/workspace-session.js`), `/sql` routing and main-surface
navigation (`src/application/surface-navigation.js`), the Workbench variable
strip (`src/ui/workbench/variable-strip.js`), and the save/conflict cluster
(`src/ui/workbench/save-controller.js`) — `src/application/*` may never import
`src/ui/`, mechanically enforced by `build/check-boundaries.mjs`. The
Dashboard's own render module (`src/ui/dashboard.js`, `renderDashboard`)
follows the same pattern at a smaller scale: its repaint-decision logic is the
pure `src/dashboard/application/dashboard-repaint-plan.js`, and its pointer-
gesture handling (corner-drag resize, Command/Ctrl-drag reorder, modifier cue)
is `src/ui/dashboard-tile-gestures.js`'s `createTileGestureController`, built
fresh per render behind an injected `TileGestureDeps` seam (#589).

## Side-effect seams

- Network functions receive `fetch` or a ClickHouse context.
- PKCE, storage, time, location, and browser globals are parameters.
- CodeMirror is behind explicit injected `app.sqlEditor` and `app.specEditor`
seams; only the composition root chooses the adapters. SQL actions always
address the SQL adapter rather than the currently visible document.
- Chart.js and Dagre are concrete adapters injected as `app.Chart` / `app.Dagre`.
- Signals coordinate state. Imperative/high-frequency surfaces remain adapters.

This pattern keeps tests genuine: plain stubs replace dependencies without broad
module mocking.

## Query path

1. The editor/controller prepares SQL and typed parameters.
2. `src/net/ch-client.js` sends the HTTP request with injected auth/fetch context.
3. `JSONStringsEachRowWithProgress` is folded line by line by pure stream logic.
4. Results resolve through the panel registry to table, chart, logs, KPI, filter,
text, or graph-oriented renderers.
5. One auth refresh is attempted for expired/denied tokens.

## Build shape

`build/build.mjs` bundles `src/main.js` with esbuild, minifies it, and inlines JS
and `src/styles.css` into `build/template.html`. Output is `dist/sql.html`, with
no third-party runtime requests.

Canonical source: [`docs/ARCHITECTURE.md`](../docs/ARCHITECTURE.md) and
[`CLAUDE.md`](../CLAUDE.md).
123 changes: 123 additions & 0 deletions .wiki/Decisions-and-Roadmap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Decisions and roadmap

Back to [[Home]]. Related: [[Architecture]], [[Development-Workflow]].

## Settled decisions

- Incrementally use `@preact/signals-core`; do not introduce a UI framework.
**Reaffirmed 2026-08-03** by ADR-0004: a whole-shell-scale Preact migration
evaluation (#577) was measured and rejected — see below.
- Keep CodeMirror 6 behind `EditorPort` and never run SQL on the keystroke path.
- Keep complex/high-frequency UI islands imperative behind injected seams.
- Extract a shared UI primitive when a second real consumer appears, not before.
- Keep the product a single esbuild artifact with zero third-party requests.
- Use comment-wrapped optional SQL blocks (`/*[ ... ]*/`) for empty-means-no-filter;
the earlier double-square-bracket form conflicted with ClickHouse array syntax.
- The panel configuration registry is the common model for chart/table/logs/KPI/
filter/text views and library persistence.
- **Contracts specify final-state invariants**, not frame-by-frame gesture
behavior, unless a user-visible bug forces otherwise — the retrospective
lesson of ADR-0004 (see below), now also a `CLAUDE.md` Working-discipline rule.

## ADR-0004: the Preact evaluation (#577) — retain vanilla, reject the migration

`#487` (left navigation) and `#488` (right inspector) both pushed on the same
pressure point: increasingly complicated lifecycle/focus coordination in the
hand-rolled `src/ui/` render layer. `#577` asked, with a decision rule fixed
*before* measuring, whether replacing it with Preact would make the shell
materially smaller and simpler. Three tagged, never-merged branches (S0
baseline, S1 vanilla control, S2 Preact treatment — tags `577/baseline`,
`577/control`, `577/treatment`; evidence archived on branch
`docs/preact-shell-evaluation-577`, closed PR #580) were measured together over
one 18-entry file manifest.

**Result (2026-08-03): RETAIN vanilla rendering, REJECT the migration.** The
treatment cost +330 shell-plumbing code lines (+26% over the control) and
+7,755 B gzip, with domain/island line counts unchanged across all three
states — a clean failure of the precommitted code-dominance rule. `#578` (the
phased migration umbrella) closed as **not planned**; the investment redirects
to shared *vanilla* shell primitives instead:

- **#586** — one `SurfaceLifecycle` primitive + a docked right-inspector slot
(the mount/update/dispose contract #488 needs).
- **#587** — a side-panel registry, so a new navigation panel is a one-file
change.
- **#588** — decompose the composition root (`src/ui/app.ts`, `createApp`
spans ~3,000 lines) along four seams.
- **#589** — extract the dashboard tile gesture controller and a pure repaint
plan out of imperative handlers.

Full method, measurements, and retrospective lessons (async DOM-vs-state
writes, a silent-dead-reactivity footgun from importing the wrong signals
package, derived state having no "gesture ended" edge, two regressions e2e
caught that 6,995 green unit tests missed): canonical source
[`docs/ADR-0004-ui-shell.md`](../docs/ADR-0004-ui-shell.md).

## The 2026-08-03 main reset — #487 phases 1–3

`#487`'s phases 1–3 — PRs **#571** (left-nav layout core), **#573** (nav-section
registry), **#574** (left-rail focused drawer) — **merged into `main`** on
2026-07-29/30 and show as MERGED on GitHub, but `main` was **reset on
2026-08-03 to a pre-#487 baseline** during the #577 evaluation window. That
code is **not on `main` today**, but it is not lost — it survives, unmerged,
on:

- `feat/left-nav-layout-core-487p1` — pure `core/left-nav-layout.ts` reducer,
three-layout resize session.
- `feat/nav-section-registry-487p2` — registry decisions (icon-as-factory,
separate `accessibleLabel`, pane-scoped `showSection`, load-boundary key
bridge), now being adapted by #587.
- `feat/left-rail-focused-drawer-487p3` — rail/focused-drawer geometry, 180px
drawer floor, per-section filters.

`#487`'s and `#488`'s issue bodies were rewritten 2026-08-03: the frame-level
focus contract was trimmed to final-state rules, and implementation is now
routed through #586/#587 rather than re-derived independently. Before
resuming #487/#488 work, salvage the branches above rather than re-deriving
proven pure-logic/geometry work; do not assume "merged PR" on GitHub means
"present on `main`" for anything from this window without checking.

## Forward work

Two roadmap tracks are current:

- **V1 roadmap — GitHub issue #68** ("Roadmap to 1.0.0") — the original
structured feature-issue roadmap; still authoritative for V1-scoped work not
superseded by V2. Historical Phase 7 ordering recorded here at wiki creation
(2026-07-12) was #173 → #165 → #170 → #169 → #171/#172, with dashboard filter
panel work tied to #166 and #160 — re-verify current state on GitHub rather
than trusting this order today.
- **V2 roadmap — GitHub issue #582** ("Roadmap to V2 (professional UI
redesign)") — scoped 2026-07-31; supersedes #68 for V2-scoped surfaces only
(most V1 functionality carries over unchanged). Companion working document:
[`docs/V2-UX-HANDOVER.md`](../docs/V2-UX-HANDOVER.md), a shipped-UX +
committed-product-contract inventory for handoff to the redesign.
- **Refactor/umbrella track — #593** ("Umbrella: V2 architecture refactor —
shell primitives, composition root, state reactivity, transport adapter")
sequences the ADR-0004 follow-through into one ordered `/ship` execution
plan, phase by phase (shipped under the pre-2026-08-05 per-phase-PR flow;
`/ship` now integrates units onto one branch/PR per run): Phase 1
#586 (`SurfaceLifecycle` + docked right-inspector slot, unblocks #488),
Phase 2 #587 (side-panel registry, unblocks #487, salvages
`feat/nav-section-registry-487p2`/PR #573), Phase 3 #591 (fail-closed
decoders for persisted domain records — independent early win), Phase 4
#588 (composition-root decomposition), Phase 5 #589 (dashboard gesture/
repaint extraction), Phase 6 #590 (implemented on `wip/590-reactive-workspace`
— `app.currentWorkspace`/`app.mainSurface` are signal-backed accessor pairs,
`dashboardTreeRevision` is retired; see `docs/ADR-0001-reactivity.md`'s #590
addendum), Phase 7 #585/ADR-0005
(`@clickhouse/client-web` transport spike — independent of the shell
track; a "Rejected" outcome still completes the phase). #592 (extend
`check-boundaries` to lock in the shell primitives) is a guardrail issue
alongside the phases. All are labeled `refactor`; re-check
`gh issue list --label refactor` for the current phase status before
planning shell work.
- **#585 / ADR-0005** — adopt `@clickhouse/client-web` behind the SQL Browser
transport adapter; Phase 7 of the #593 umbrella above.

Re-read GitHub before acting because issue state can change; a MERGED PR is
not proof its code is on `main` (see the reset above).

Canonical decision records: [`docs/ADR-0001-reactivity.md`](../docs/ADR-0001-reactivity.md),
[`docs/ADR-0004-ui-shell.md`](../docs/ADR-0004-ui-shell.md). Historical roadmap
context is summarized in [[Operations-Memory]].
82 changes: 82 additions & 0 deletions .wiki/Deployment-and-Security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Deployment and security

Back to [[Home]]. Related: [[Operations-Memory]], [[Product-and-Features]].

## Artifact and routes

The build produces `dist/sql.html`. Deployment copies it to ClickHouse
`user_files` and configures HTTP handlers for the SPA and public `config.json`.
Cluster distribution options and tradeoffs are documented in
[`docs/ASSET-DISTRIBUTION.md`](../docs/ASSET-DISTRIBUTION.md).

## Authentication modes

- Native/Bearer OAuth for ClickHouse variants with token processors and ephemeral
users.
- Basic transport (`username: JWT`) through `ch-jwt-verify` for stock ClickHouse.
- Direct credential login where configured.
- Multiple IdPs are supported; mappings must avoid username collisions.

`config.json` is served to browsers and is therefore public. Prefer PKCE public
clients. If an IdP requires a client secret, treat it as exposed and tightly lock
the redirect URI. Never commit rendered `deploy/config.json`; only
`deploy/config.json.example` belongs in version control.

## Demo-cluster operational rule

Build and upload the HTML without a restart. For ClickHouse `config.d` changes on
ACM-managed demo clusters, use ACM settings plus cluster push; direct ConfigMap
edits are reconciled away. Verify the effective preprocessed ClickHouse config.

**otel runs two replica pods** (`chi-otel-otel-0-0-0` and `chi-otel-otel-0-1-0`,
since ~2026-07-20); `user_files` is per-pod local disk, not shared, and the
ClickHouse Service load-balances across both — a deploy must `kubectl cp` to
**every** `chi-otel-otel-0-*-0` pod, or roughly half of live requests 500 with
`Code 79 INCORRECT_FILE_NAME`. github.demo and antalya remain single-replica.
Re-check replica count (`kubectl get pods -n demo -l clickhouse.altinity.com/chi=otel`)
before every otel deploy — a ClickHouse Operator `chi` can add replicas
independent of any SPA-deploy action. See [[Operations-Memory]].

## Standalone nginx image + Helm chart (sql.demo.altinity.cloud)

Distinct from the three ClickHouse-hosted deploys above: the SPA also ships as a
production **nginx** container image (`ghcr.io/altinity/altinity-sql-browser`,
public, multi-arch) built by `.github/workflows/docker.yml` (`edge`+`sha-<c>` on
every push to `main`; `X.Y.Z`+`latest` on a `vX.Y.Z` tag push — the release
image) and a **Helm chart** (`helm/altinity-sql-browser/`, published to
`oci://ghcr.io/altinity/altinity-sql-browser/helm/altinity-sql-browser` by
`release.yml` on `v*`).
The container is a standalone static server (nginx-unprivileged, uid 101, port
8080); it never proxies — the browser POSTs queries cross-origin to whatever
cluster the login picker selects.

Live at **https://sql.demo.altinity.cloud/sql** (ns `demo`, Helm release
`sql-browser`), exposed via the same edge-proxy Service-annotation pattern used
elsewhere on demo.altinity.cloud (no ingress/DNS/cert-manager — a
`*.demo.altinity.cloud` wildcard cert + SNI routing, `443:tls-to-tcp:8080`):

```sh
export KUBECONFIG=~/tmp/acm-session.kubeconfig
helm upgrade --install sql-browser \
oci://ghcr.io/altinity/altinity-sql-browser/helm/altinity-sql-browser \
--version 0.6.2 \
-n demo -f deploy/helm/values-demo.yaml
kubectl rollout status deploy/sql-browser-altinity-sql-browser -n demo
```

`deploy/helm/values-demo.yaml` pins `image.tag` to the current release
(`"0.6.1"` as of this writing) — bump it as part of every release round rather
than letting it drift on the rolling `edge` tag between releases. Verify the
target tag is actually published first (`gh api
/orgs/Altinity/packages/container/altinity-sql-browser/versions`), then confirm
the rollout via in-pod `wget http://127.0.0.1:8080/sql` /`/healthz` plus a live
agent-Chrome check.

**Known gap:** SSO from sql.demo fails `redirect_uri_mismatch` — the demo Google
OAuth clients (antalya, github.demo) only register redirect URIs on their own
hosts, not `sql.demo.altinity.cloud`; `demo:demo` basic login works without it.

Canonical source: [`docs/DEPLOYMENT.md`](../docs/DEPLOYMENT.md),
[`docs/CLICKHOUSE-OAUTH.md`](../docs/CLICKHOUSE-OAUTH.md),
[`docs/CLICKHOUSE-OSS-OAUTH.md`](../docs/CLICKHOUSE-OSS-OAUTH.md), and
[`SECURITY.md`](../SECURITY.md).
63 changes: 63 additions & 0 deletions .wiki/Development-Workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Development workflow

Back to [[Home]]. Related: [[Project-Skills]], [[Operations-Memory]].

## Routine loop

1. Read `CLAUDE.md`, the relevant issue, and its position in roadmap issue #68.
2. Put pure logic in `src/core`, transport in `src/net`, rendering in `src/ui`,
editor integration behind `EditorPort`, and environment wiring in `main.js`.
3. Add or update the matching `tests/unit/<module>.test.js` in the same change.
4. Run `npm test`, then `npm run build`.
5. For UI-visible work, rely on CI for the full three-engine Playwright gate when
local engines cannot launch; use a local browser harness for visual checks.
6. Reconcile `CHANGELOG.md`, the affected issue, roadmap #68, and ADR addenda when
a substantive decision or behavior changes.

## Quality gate

Vitest uses happy-dom and V8 coverage. Thresholds are per-file: statements and
lines 100%, functions at least 95%, branches at least 90%. Most pure/network/state/
DOM/render modules are expected to remain 100/100/100/100; `ui/app.js` is the
documented glue exception. Do not hide weak files behind aggregate coverage.

## Commands

```sh
npm test
npm run build
npm run test:e2e
npm run local
```

CI uses Node 22 and `npm ci --no-audit --no-fund` against the committed
`package-lock.json`. Lockfile v3 records esbuild's platform packages as optional,
so npm installs the runner's binary while preserving one reproducible dependency
graph across Linux CI and macOS development. Playwright imports `/src` as raw ESM,
so a new bare dependency also needs import-map coverage in every affected E2E
harness.

Fresh worktrees have no `node_modules`; run `npm ci` before the test/build loop.
Use `npm install <package>` only for an intentional dependency update and commit
the resulting lockfile change. On older macOS hosts, all locally
installed Playwright engines may exit with `SIGTRAP` before test code runs even
after `npx playwright install chromium firefox webkit`; distinguish that launch
failure from an application test failure and rely on the three-engine CI gate.
When CI is the only executable browser gate, do not report the ship cycle as
complete until that E2E job has finished successfully; a pending check is not
verification.

## Working discipline

- Preserve user-owned dirty-tree changes.
- Track planned work in GitHub issues, not internal files under published `docs/`.
- File high-signal out-of-scope bugs with the `inbox` label.
- `/ship` never asks to merge on success: it auto-merges once a certified ChatGPT
review exists at the exact PR head with required checks green. It stops for a
human decision only when a review loop exhausts its passes (5 for plans, 3 for
code) or another merge proof condition fails.
- Save genuinely surprising environment/test friction as project memory.

Canonical source: [`CLAUDE.md`](../CLAUDE.md),
[`tests/vitest.config.ts`](../tests/vitest.config.ts), and
[`CONTRIBUTING.md`](../CONTRIBUTING.md).
Loading