You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
5 — extract ClickHouse SQL quoting and type-expression grammar
6 — simplify SQL Browser authentication integration around the package
7 — migrate query execution and export, then delete generic client mechanics
8 — stabilize the standalone package boundary and remove migration scaffolding
/ship owns this checklist and should tick a phase only after that phase's PR is merged and verified on origin/main.
Goal
Replace the rejected @clickhouse/client-web adoption path from #585 with a first-party, Fetch-native ClickHouse client package extracted from generic protocol code SQL Browser already maintains.
The package must preserve the behavior that made the official-driver adaptation unsuitable:
low-level request returns the exact native Response from fetch();
HTTP non-2xx responses resolve normally at the low-level boundary;
response bodies are never consumed unless the caller explicitly chooses a consuming API;
caller SQL is transmitted unchanged;
caller-provided Authorization is opaque and emitted unchanged;
the caller's own AbortSignal controls the real request for the entire response-body lifetime, including after headers arrive;
raw/binary bytes are never forced through text decoding;
ClickHouse progress streams and late-exception framing are reusable protocol primitives.
This is an architectural refactor. User-visible behavior and persisted formats must remain compatible.
/ship execution
This issue is intentionally phased in the format consumed by skills/ship.
Run:
/ship 630
or:
/ship 630 --planner chatgpt
Each phase is one /ship unit: approved plan, branch, PR, code-review certification, merge, checklist/ship-log reconciliation, then automatic continuation from freshly fetched origin/main.
The phases form one ordered dependency spine. There is no authored decision gate between them.
Phase 8 and #639 deliberately operate on opposite sides of a repository boundary.
Phase 8 is still entirely inside altinity-sql-browser
It proves that the extracted package is no longer architecturally dependent on SQL Browser before any cross-repository move occurs. It owns:
package-local build/typecheck/test scripts;
built ESM and TypeScript declaration surface;
npm pack and isolated tarball install/import/typecheck proof;
removal of temporary compatibility adapters/re-exports left by Phases 2–7;
removal of the rejected @clickhouse/client-web dependency and obsolete executable spike wiring;
architecture guards preventing package→SQL Browser imports and SQL Browser deep imports into package internals;
final in-repo ownership cleanup and architecture/ADR/wiki/changelog documentation;
a tested handoff document describing how to extract the package repository.
At the end of Phase 8, the package can be moved without changing its architecture or API, but it still physically lives under packages/clickhouse-http in this repository.
#639 is the actual cross-repository move and release
#639 starts only after Phase 8 is shipped. It owns:
creating Altinity/clickhouse-http;
moving package source/history into that repository;
adding standalone repository CI/release plumbing;
publishing/releasing a version;
changing SQL Browser from workspace dependency to released semver dependency;
deleting the workspace copy only after the external package passes SQL Browser integration/e2e tests.
The separation is intentional because the current /ship skill is scoped to altinity-sql-browser. If Phase 8 were removed, #639 would combine unresolved architecture cleanup, package isolation, repository creation, release, and consumer cutover in one cross-repository operation. That would make rollback and review substantially harder and would move architecture decisions outside the repo-scoped /ship proof loop.
If the future cross-repository shipping workflow can provide the same plan/review/test/rollback guarantees across both repositories, Phase 8 could theoretically be folded into #639. With the current tooling, keeping the in-repo stabilization phase is the safer boundary.
Architecture decision
If behavior answers “how does ClickHouse HTTP / Fetch work?”, it belongs in the package. If it answers “what should SQL Browser do?”, it stays in SQL Browser.
SQL Browser application policy
|
v
@altinity/clickhouse-http
|
v
Fetch
|
v
ClickHouse
The package must never depend on SQL Browser application, UI, state, auth, workspace, dashboard, or editor modules.
Final ownership boundary
@altinity/clickhouse-http owns
endpoint/URL construction;
default_format, ClickHouse settings and caller-supplied query/protocol parameters;
Single artifact remains. SQL Browser still builds one self-contained dist/sql.html.
Auth authority remains fenced. Credential awaits and async auth-body inspection remain bound to the captured credential epoch.
Raw success can stay raw. Export can classify HTTP success/error without consuming a successful response body.
Delivery phases
Phase 1 — freeze native Fetch, Response, and cancellation semantics
Characterization/test-infrastructure phase only. Do not refactor the transport.
Strengthen the existing transport contract and real-browser fault harness to prove:
exactly one injected fetch call, including pre-aborted input;
strict native Response identity;
non-2xx resolves with untouched body;
exact SQL including whitespace/comments/trailing semicolon/authored FORMAT;
opaque Bearer/Basic/custom Authorization;
raw invalid-UTF-8 bytes remain byte-identical;
current URL serialization/encoding including zero/empty values where supported;
live origin() and fetch() accessors;
pre-aborted signal invokes the injected fetch but does not reach the real server;
abort while awaiting headers => AbortError;
after headers, request stays resolved but aborting the original signal makes an in-progress body read reject AbortError;
no later stream callbacks after observable cancellation;
concurrent request remains unaffected;
abort after full body completion has no effect.
The post-header proof must execute end-to-end in Chromium and WebKit using the real browser Fetch stack; an already-errored synthetic ReadableStream is insufficient.
Claims: A1, A3.
Phase 2 — create @altinity/clickhouse-http and move low-level request/URL mechanics
Create packages/clickhouse-http as a private npm workspace package with zero runtime dependencies and public package-name imports.
Move current chUrl() behavior into one authoritative package serializer. Preserve default_format, enable_http_compression=1, settings and existing caller-supplied protocol/query parameters exactly.
Keep the old transport only as a temporary compatibility adapter delegating send() to package request(); stream reading remains local until Phase 3.
Add architecture checks forbidding package imports from root src/** and SQL Browser deep imports into packages/clickhouse-http/src/**.
Claims: A2, A4, A5.
Phase 3 — extract progress-stream and late-exception protocol primitives
Move generic protocol mechanics, not SQL Browser result state.
Package owns:
progress line shape (meta, row, progress, exception);
ensureClickHouseSuccess returns the same successful response without consuming it; only non-2xx consumes error text and throws minimal ClickHouseError.
Add convenience queryJson, queryText, queryProgress composed from exactly one request plus one consumer. They must not know SQL Browser modes such as Table/KPI.
Add stateless killQuery({queryId, authorization, ...}): safely quoted, ASYNC, one request, no registry/retry/credential lookup.
Abort/network/body-reader errors must remain native rather than being wrapped as ClickHouse errors.
Claims: A8, A9.
Phase 5 — extract ClickHouse SQL quoting and type-expression grammar
Move reusable pure ClickHouse language helpers into the package:
Preserve current grammar/test behavior for nested types, literals, Tuple, Enum8/Enum16, Nullable, LowCardinality, arrays, malformed input, wrapper ordering and canonicalization.
killQuery must use the same public string-literal helper.
Keep SQL Browser display formatting, FORMAT detection/preparation, parameter-control policy and Dashboard/UI decisions outside the package.
No duplicate scanner/parser/quoter may remain.
Claims: A10, A11.
Phase 6 — simplify SQL Browser authentication integration around the package
High-risk trust-boundary phase.
Create one SQL Browser-owned authenticated request module that composes credential/epoch/lifecycle policy over package request() and package response consumers.
Preserve:
epoch captured before first await;
settings/params snapshot before credential await;
URL/preparation failure timing before token lookup;
epoch fence after every credential-related await and cloned error-body read;
final epoch fence immediately before actual request side effect;
complete Authorization computed per attempt;
at most one auth refresh retry;
post-confirmation 401/403 remain query outcomes, not sign-out;
only current 2xx reports connected;
only current non-abort network rejection reports offline;
stale work never mutates replacement lifecycle state;
original caller AbortSignal reaches actual Fetch with no derived controller/listener bridge;
frozen cancellation lease path does not read mutable tokens/refresh/lifecycle.
ChCtx and cancellation leases remain SQL Browser-owned.
Real-browser cancellation tests must run through the authenticated path, not only the raw package client.
Claims: A12, A13.
Phase 7 — migrate query execution and export, then delete generic client mechanics
Move application consumers off legacy runQuery / exportQuery / ordinary transport helpers.
QueryExecutionService keeps SQL Browser policy:
Table/KPI/TSV/raw format mapping;
row-limit settings;
fresh query ID per attempt;
SESSION_IS_LOCKED retry;
safe read network retry;
no automatic retry for uncertain non-idempotent work;
script stop-on-first-failure;
owner-scoped cancellation.
ExportService keeps file picker/progress/.partial UX but uses authenticated native Response, package success classification, direct byte streaming, and package findExceptionFrame on retained tail bytes.
Successful raw export must never use response.text().
After all callers migrate, delete superseded generic runQuery, exportQuery, ordinary mutable-context killQuery, old transport stream plumbing and transport seam files when no architectural purpose remains. Keep only SQL Browser product operations/thin authenticated helpers and frozen-lease cancellation policy.
Run full e2e plus Chromium/WebKit cancellation/export streaming proof.
Claims: A14, A15, A16.
Phase 8 — stabilize the standalone package boundary and remove migration scaffolding
This phase does not create another repository and does not publish a release. Its purpose is to leave altinity-sql-browser in a clean state where repository extraction is mechanical.
Make packages/clickhouse-http independently buildable and packable inside the current repository:
built browser-first ESM;
TypeScript declarations;
package-local build, test, check:types scripts;
no root-source imports or workspace-only type resolution;
publication-shaped exports, files, license and version metadata;
no runtime dependency unless explicitly introduced earlier.
Add deterministic isolated-package proof:
build package;
npm pack;
inspect tarball contents;
install tarball into a temporary fixture outside the workspace package tree;
import it as ESM;
compile a TypeScript consumer against its declarations;
prove resolution does not fall back into SQL Browser src/**.
Then remove migration scaffolding:
compatibility-only package/root aliases and transport wrappers no longer needed after Phase 7;
@clickhouse/client-web dependency and executable vendor-spike wiring;
stale import maps/scripts referring to rejected adoption.
Retain ADR/evidence history for #585 and reuse/rename generic browser fault scenarios as first-party package regressions.
Strengthen architecture guards for:
package→SQL Browser imports;
SQL Browser package deep imports;
regrowth of a second generic request/URL implementation;
regrowth of duplicate progress/late-exception parsers;
accidental reintroduction of @clickhouse/client-web without a new decision.
Update package README, docs/ARCHITECTURE.md, CLAUDE.md, relevant .wiki, ADR-0005 addendum and CHANGELOG.md. Add docs/clickhouse-http-repository-extraction.md as the tested handoff for #639.
At Phase 8 completion the package still lives in this repository, but its source/API/build/test boundary is ready to move unchanged.
Claims: A17, A18.
Tests
Every phase must leave the full repository gate green:
npm run check:types
npm run check:arch
npm run check:schemas
npm run check:examples
npm test
npm run build
Phase 1
Run the targeted Chromium/WebKit fault/cancellation suite. Cover pre-abort, awaiting headers, post-header body abort, concurrent isolation, exact Response identity, raw bytes, SQL/Auth/URL fidelity.
Phase 2
Run package/request contract tests plus relevant e2e because workspace/dependency wiring changes.
Test successful non-consuming classification, JSON/text/progress consumption, ClickHouseError on non-2xx, native abort/body errors, one-request convenience APIs, and KILL QUERY quoting/state isolation. Re-run real-browser post-header cancellation.
Phase 5
Run the complete existing type-parser corpus plus quoting edge cases and affected parameter/KPI/dashboard-variable consumers.
Phase 6
Run all auth/epoch/refresh/lifecycle race tests and Chromium/WebKit post-header cancellation through the authenticated path.
Phase 7
Run QueryExecutionService retry/result mapping tests, ExportService raw-byte/late-error/partial-file/cancellation tests, full e2e, and real-browser export cancellation after headers.
Phase 8
Run package-local build/type/test, npm pack, isolated ESM import, isolated TypeScript compile, architecture sabotage tests, root full gate, full e2e, and renamed first-party Chromium/WebKit package regression suite.
Global acceptance criteria
A1 Native Fetch/Response/cancellation contract is characterized in unit and Chromium/WebKit tests.
A2@altinity/clickhouse-http exists as an in-repo package with no SQL Browser source imports.
A3 Low-level request preserves native Response identity, exact SQL/Auth, raw bytes, one Fetch call and caller-signal lifetime.
A4 SQL Browser consumes the package through public package exports.
A5 URL serialization has one package implementation.
A6 Progress-stream decoding has one package implementation while SQL Browser retains result/view state.
A7 ClickHouse HTTP and late exception parsing/framing are package-owned and byte-safe.
A8 Explicit non-consuming and consuming response APIs exist with a minimal ClickHouse error model.
A9 Stateless package KILL QUERY exists without credential lookup/retry/registry ownership.
A10 ClickHouse SQL quoting has one package implementation.
A11 Generic ClickHouse type grammar/parser/canonicalization has one package implementation.
A12 SQL Browser authentication is composed through one authenticated request layer over package request().
A13 Epoch/refresh/lifecycle/cancellation/frozen-lease invariants remain regression-tested and unchanged.
A14 QueryExecutionService owns logical format/cap/retry policy and no longer owns generic HTTP/stream mechanics.
A15 ExportService streams native bytes and uses package late-exception framing while retaining export UX/policy.
A16 Superseded generic transport/client mechanics are deleted rather than retained as a second implementation.
A17 Package can build, pack, install, import and typecheck in isolation with no root-source fallback.
Phases
@altinity/clickhouse-httpand move low-level request/URL mechanicsKILL QUERY/shipowns this checklist and should tick a phase only after that phase's PR is merged and verified onorigin/main.Goal
Replace the rejected
@clickhouse/client-webadoption path from #585 with a first-party, Fetch-native ClickHouse client package extracted from generic protocol code SQL Browser already maintains.The package must preserve the behavior that made the official-driver adaptation unsuitable:
Responsefromfetch();Authorizationis opaque and emitted unchanged;AbortSignalcontrols the real request for the entire response-body lifetime, including after headers arrive;SQL Browser keeps application policy: OAuth/Basic credential acquisition, refresh, credential epochs, connection lifecycle, operation ownership, retry policy, per-tab session policy, result/view policy, product SQL, export UX, and workbench/dashboard state.
This is an architectural refactor. User-visible behavior and persisted formats must remain compatible.
/shipexecutionThis issue is intentionally phased in the format consumed by
skills/ship.Run:
or:
Each phase is one
/shipunit: approved plan, branch, PR, code-review certification, merge, checklist/ship-log reconciliation, then automatic continuation from freshly fetchedorigin/main.The phases form one ordered dependency spine. There is no authored decision gate between them.
Why Phase 8 exists separately from #639
Phase 8 and #639 deliberately operate on opposite sides of a repository boundary.
Phase 8 is still entirely inside
altinity-sql-browserIt proves that the extracted package is no longer architecturally dependent on SQL Browser before any cross-repository move occurs. It owns:
npm packand isolated tarball install/import/typecheck proof;@clickhouse/client-webdependency and obsolete executable spike wiring;At the end of Phase 8, the package can be moved without changing its architecture or API, but it still physically lives under
packages/clickhouse-httpin this repository.#639 is the actual cross-repository move and release
#639 starts only after Phase 8 is shipped. It owns:
Altinity/clickhouse-http;The separation is intentional because the current
/shipskill is scoped toaltinity-sql-browser. If Phase 8 were removed, #639 would combine unresolved architecture cleanup, package isolation, repository creation, release, and consumer cutover in one cross-repository operation. That would make rollback and review substantially harder and would move architecture decisions outside the repo-scoped/shipproof loop.If the future cross-repository shipping workflow can provide the same plan/review/test/rollback guarantees across both repositories, Phase 8 could theoretically be folded into #639. With the current tooling, keeping the in-repo stabilization phase is the safer boundary.
Architecture decision
The package must never depend on SQL Browser application, UI, state, auth, workspace, dashboard, or editor modules.
Final ownership boundary
@altinity/clickhouse-httpownsdefault_format, ClickHouse settings and caller-supplied query/protocol parameters;fetch()invocation;AbortSignalpassthrough;Responselow-level API;X-ClickHouse-Exception-Tag/__exception__late-exception framing;KILL QUERYwire operation;SQL Browser owns
AbortControllerlifecycle;SESSION_IS_LOCKED, safe read retry, non-idempotent uncertainty);Table,KPI, etc.) and format/settings mapping;.partialUX.Hard invariants
response === fetchResponse, including non-2xx.bodyUsed === false.dist/sql.html.Delivery phases
Phase 1 — freeze native Fetch, Response, and cancellation semantics
Characterization/test-infrastructure phase only. Do not refactor the transport.
Strengthen the existing transport contract and real-browser fault harness to prove:
fetchcall, including pre-aborted input;Responseidentity;FORMAT;origin()andfetch()accessors;AbortError;AbortError;The post-header proof must execute end-to-end in Chromium and WebKit using the real browser Fetch stack; an already-errored synthetic
ReadableStreamis insufficient.Claims: A1, A3.
Phase 2 — create
@altinity/clickhouse-httpand move low-level request/URL mechanicsCreate
packages/clickhouse-httpas a private npm workspace package with zero runtime dependencies and public package-name imports.Expose a low-level client equivalent to:
Move current
chUrl()behavior into one authoritative package serializer. Preservedefault_format,enable_http_compression=1, settings and existing caller-supplied protocol/query parameters exactly.Keep the old transport only as a temporary compatibility adapter delegating
send()to packagerequest(); stream reading remains local until Phase 3.Add architecture checks forbidding package imports from root
src/**and SQL Browser deep imports intopackages/clickhouse-http/src/**.Claims: A2, A4, A5.
Phase 3 — extract progress-stream and late-exception protocol primitives
Move generic protocol mechanics, not SQL Browser result state.
Package owns:
meta,row,progress,exception);parseExceptionText();Expose byte-oriented exception framing such as:
Keep
StreamResult, row caps, percentages, raw/result presentation state, editor caret extraction and auth-expiry UI policy in SQL Browser.No duplicate stream/error implementation remains behind compatibility wrappers.
Claims: A6, A7.
Phase 4 — add consuming query APIs, ClickHouse errors, and stateless
KILL QUERYPreserve low-level
request()unchanged.Add explicit higher layers:
ensureClickHouseSuccessreturns the same successful response without consuming it; only non-2xx consumes error text and throws minimalClickHouseError.Add convenience
queryJson,queryText,queryProgresscomposed from exactly one request plus one consumer. They must not know SQL Browser modes such asTable/KPI.Add stateless
killQuery({queryId, authorization, ...}): safely quoted,ASYNC, one request, no registry/retry/credential lookup.Abort/network/body-reader errors must remain native rather than being wrapped as ClickHouse errors.
Claims: A8, A9.
Phase 5 — extract ClickHouse SQL quoting and type-expression grammar
Move reusable pure ClickHouse language helpers into the package:
Preserve current grammar/test behavior for nested types, literals, Tuple, Enum8/Enum16, Nullable, LowCardinality, arrays, malformed input, wrapper ordering and canonicalization.
killQuerymust use the same public string-literal helper.Keep SQL Browser display formatting,
FORMATdetection/preparation, parameter-control policy and Dashboard/UI decisions outside the package.No duplicate scanner/parser/quoter may remain.
Claims: A10, A11.
Phase 6 — simplify SQL Browser authentication integration around the package
High-risk trust-boundary phase.
Create one SQL Browser-owned authenticated request module that composes credential/epoch/lifecycle policy over package
request()and package response consumers.Preserve:
ChCtxand cancellation leases remain SQL Browser-owned.Real-browser cancellation tests must run through the authenticated path, not only the raw package client.
Claims: A12, A13.
Phase 7 — migrate query execution and export, then delete generic client mechanics
Move application consumers off legacy
runQuery/exportQuery/ ordinary transport helpers.QueryExecutionServicekeeps SQL Browser policy:SESSION_IS_LOCKEDretry;ExportServicekeeps file picker/progress/.partialUX but uses authenticated native Response, package success classification, direct byte streaming, and packagefindExceptionFrameon retained tail bytes.Successful raw export must never use
response.text().After all callers migrate, delete superseded generic
runQuery,exportQuery, ordinary mutable-contextkillQuery, old transport stream plumbing and transport seam files when no architectural purpose remains. Keep only SQL Browser product operations/thin authenticated helpers and frozen-lease cancellation policy.Run full e2e plus Chromium/WebKit cancellation/export streaming proof.
Claims: A14, A15, A16.
Phase 8 — stabilize the standalone package boundary and remove migration scaffolding
This phase does not create another repository and does not publish a release. Its purpose is to leave
altinity-sql-browserin a clean state where repository extraction is mechanical.Make
packages/clickhouse-httpindependently buildable and packable inside the current repository:build,test,check:typesscripts;exports,files, license and version metadata;Add deterministic isolated-package proof:
npm pack;src/**.Then remove migration scaffolding:
@clickhouse/client-webdependency and executable vendor-spike wiring;Retain ADR/evidence history for #585 and reuse/rename generic browser fault scenarios as first-party package regressions.
Strengthen architecture guards for:
@clickhouse/client-webwithout a new decision.Update package README,
docs/ARCHITECTURE.md,CLAUDE.md, relevant.wiki, ADR-0005 addendum andCHANGELOG.md. Adddocs/clickhouse-http-repository-extraction.mdas the tested handoff for #639.At Phase 8 completion the package still lives in this repository, but its source/API/build/test boundary is ready to move unchanged.
Claims: A17, A18.
Tests
Every phase must leave the full repository gate green:
npm run check:types npm run check:arch npm run check:schemas npm run check:examples npm test npm run buildPhase 1
Run the targeted Chromium/WebKit fault/cancellation suite. Cover pre-abort, awaiting headers, post-header body abort, concurrent isolation, exact Response identity, raw bytes, SQL/Auth/URL fidelity.
Phase 2
Run package/request contract tests plus relevant e2e because workspace/dependency wiring changes.
Phase 3
Test split/multi-line chunks, UTF-8 split boundaries, malformed lines, trailing remainder, reader error identity, tagged/legacy exception framing, false-positive resistance and invalid-UTF-8 clean bytes.
Phase 4
Test successful non-consuming classification, JSON/text/progress consumption, ClickHouseError on non-2xx, native abort/body errors, one-request convenience APIs, and
KILL QUERYquoting/state isolation. Re-run real-browser post-header cancellation.Phase 5
Run the complete existing type-parser corpus plus quoting edge cases and affected parameter/KPI/dashboard-variable consumers.
Phase 6
Run all auth/epoch/refresh/lifecycle race tests and Chromium/WebKit post-header cancellation through the authenticated path.
Phase 7
Run QueryExecutionService retry/result mapping tests, ExportService raw-byte/late-error/partial-file/cancellation tests, full e2e, and real-browser export cancellation after headers.
Phase 8
Run package-local build/type/test,
npm pack, isolated ESM import, isolated TypeScript compile, architecture sabotage tests, root full gate, full e2e, and renamed first-party Chromium/WebKit package regression suite.Global acceptance criteria
@altinity/clickhouse-httpexists as an in-repo package with no SQL Browser source imports.KILL QUERYexists without credential lookup/retry/registry ownership.request().@clickhouse/client-webexecutable wiring is removed, and follow-up: move @altinity/clickhouse-http into a dedicated Altinity repository #639 has a tested mechanical extraction handoff.Non-goals
@clickhouse/client-webfeature surface.Altinity/clickhouse-httpor publishing npm from this issue; that is follow-up: move @altinity/clickhouse-http into a dedicated Altinity repository #639 after Phase 8 ships.Related
@clickhouse/client-webadoption; Phase 1 transport seam is the starting point.docs/ADR-0005-clickhouse-web-client.md— rejection/cancellation incompatibility evidence.skills/ship/SKILL.md— autonomous delivery contract.