The Typecheck step lived inside the smoke-and-tests job and
typecheck:type-tests ran inside `bun run check`, so type errors were
buried mid-job and serialized behind the build. They now run as a
dedicated parallel `typecheck` job (tsc --noEmit + the focused type
tests) with its own status check, and `check` slims to
smoke + test:full so nothing runs twice in CI. Local scripts
(typecheck, typecheck:type-tests, hardening:strict) are unchanged.
Review feedback: the new job's checkout sets
persist-credentials: false (no credentials needed), and
CONTRIBUTING.md now documents typecheck as a CI-enforced check
instead of a recommended-local-only one.
Validation: workflow YAML parses (jobs: smoke-and-tests, typecheck,
web); typecheck exit 0; type-tests green; `bun run check` green.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
* ci(typecheck): add error-count ratchet toward zero tsc errors
tsc --noEmit currently reports 697 pre-existing errors (issue #473), so
PRs cannot be gated on a clean typecheck yet. This adds
scripts/typecheck-ratchet.ts and a per-file baseline: CI fails when the
count rises above the baseline (listing exactly which files regressed),
passes at or below it, and --update lowers the baseline to lock in
gains. Wired into pr-checks as its own step; once the baseline reaches
zero the step becomes a plain `bun run typecheck`.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(typecheck): mechanical sweep — 697 → 624 tsc errors
Type-only fixes with no runtime behavior change, except the deliberate
NODE_ENV restorations:
- Restore process.env.NODE_ENV comparisons that the source snapshot had
baked into the literal "production", making the conditions constant
(AutoUpdater dev/test skip, useTypeahead, ink devtools injection,
interactiveHelpers onboarding skip, TestingPermissionTool.isEnabled —
the last now correctly enables under bun test, +3 tests run green)
- Type stream read helpers in openaiShim/codexShim as
Bun.ReadableStreamDefaultReadResult<Uint8Array<ArrayBuffer>> and
annotate throwClassifiedTransportError as never-returning, clearing
the reader/response undefined cascades (29 errors)
- Delete 14 stale @ts-expect-error directives
- Widen useState/useRef/array generics inferred from null/[] literals
- as-const notification priority/color literals to match Priority
- Accept readonly Tool[] in checkLocalModelContextLoad/getCombinedTools
Baseline lowered via typecheck:ratchet --update; full suite green
(3690 tests), smoke + bundle guard green.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(typecheck): recreate missing modules — 624 → 415 tsc errors
The open snapshot never mirrored ~60 modules; the bundler noop-stubs
them at build time (() => null named exports), so every recreated
module here is runtime-inert by construction: no import-time side
effects, gated features stay off (isAssistantMode/isSkillSearchEnabled
→ false, tools isEnabled → false, dialogs render null), lookups return
empty, telemetry no-ops. Types are honest and derived from importer
usage — no any.
Highlights:
- sdk: runtimeTypes re-exports/aliases, sdkUtilityTypes
(NonNullableUsage), settingsTypes.generated; coreTypes.generated
usage fields regenerated as a self-contained structural type (the
consumer package ships without sdkUtilityTypes/@anthropic-ai/sdk, so
the generated file must stay dependency-free — generator override
updated to match, package-consumer-types tests green)
- services: contextCollapse operations/persist/stats, compact
cachedMicrocompact state/types + reactiveCompact, skillSearch (7
modules), oauth/types, lsp/types, sessionTranscript
- cli/server/daemon: Transport interface, parseConnectUrl, server/*
(7), daemon/*, bg/templateJobs/runners; assistant/* (KAIROS), ssh/*
- tools/components: WorkflowTool trio, ReviewArtifact pair,
OverflowTest/TerminalCapture/VerifyPlanExecution/DiscoverSkills,
WebBrowserPanel, task dialogs, message variants, ink events/cursor
- types: statusLine, fileSuggestion, notebook, messageQueueTypes;
SerializedMessage rebuilt as distributed Omit-union so transcript
guards narrow again; vitest-compat.d.ts mirrors Bun's runtime
'vitest' → 'bun:test' aliasing
- TS2304 names: ant-model helpers imported from existing antModels.ts,
inert Ultraplan/Gates/LogoV2 stubs, PromiseWithResolvers local type
- build.ts: ACCEPTABLE_RUNTIME_STUBS emptied — both grandfathered
bundle-reaching stubs (MonitorMcpDetailDialog,
VerifyPlanExecutionTool/constants) are now real typed modules, so
the degrade-on-use debt the guard tracked is retired
Validation: full suite 3690 green, smoke + bundle guard green,
typecheck:type-tests green, sdk package-consumer tests green; baseline
lowered via typecheck:ratchet --update.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(typecheck): reconstruct Message discriminated union — 415 → 342 tsc errors
src/types/message.ts was a stub where all ~40 message type aliases were
'export type X = any'. Bare-any aliases break the one thing the union is
for: narrowing. Type predicates like isHookAttachmentMessage collapsed to
'never' in guard chains, cascading TS2339/TS2345 through utils/messages.ts,
messageFilters.ts, groupToolUses.ts, collapseReadSearch.ts, REPL.tsx,
compact.ts, stopHooks.ts and the message components.
Envelope design (permissive-body discriminated union):
- Each variant declares its literal discriminant(s) — message.type for the
envelope union (user/assistant/attachment/progress/system), subtype for
the 17-variant System family — plus the properties constructor functions
in utils/messages.ts actually populate, with '[key: string]: any' as an
escape hatch so unreconstructed properties never error.
- UserMessage<C> / AssistantMessage<T> are generic over content shape so
NormalizedUserMessage / NormalizedAssistantMessage<T> reuse the envelope
without Omit (Omit over an index-signature type collapses keyof to
string and silently drops the discriminant, breaking narrowing).
- AssistantMessage.message is a structural AssistantMessageContent<T>, not
the SDK's BetaMessage: synthetic constructors don't populate every
SDK-required field (stop_details), and SDK-facing consumers need
assignability to Record<string, unknown>-style bodies.
- AttachmentMessage<T = Attachment> / ProgressMessage<T = Progress> stay
generic over their payloads (utils/attachments.ts and Tool.ts types).
- UI wrappers (GroupedToolUseMessage, CollapsedReadSearchGroup,
CollapsibleMessage, RenderableMessage) and stream/control envelopes
(StreamEvent over BetaRawMessageStreamEvent, RequestStartEvent,
TombstoneMessage, ToolUseSummaryMessage) reconstructed from call sites.
- logs.ts SerializedMessage switched from the Omit<Message, never> trick
(only sound against an any stub) to an Extract-based distributed union,
keeping TranscriptMessage assignable to Message.
All other touched files are type-level-only adjustments (annotations on
evolving arrays that inferred never[], predicate types, casts in SDK wire
adapters and test fixtures) — no runtime logic changed anywhere; the full
bun test suite passes 3690/0 before and after.
Result: 415 → 342 tsc errors, every never-cascade in the message pipeline
resolved, no file above its per-file baseline (ratchet updated).
Part of issue #473.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(typecheck): narrow unknowns and fix signature drift — 342 → 94 tsc errors
Clears every remaining non-test error. Honest fixes dominate: evolving
array/let/useState/useRef annotations (the repo's noImplicitAny:false
disables evolving types), real type guards over unknown wire payloads,
hoisted react-compiler-style params annotated with their components'
real Props, and callee signature corrections (useRegisterOverlay
optional param, generic useVoiceState<T>, growthbook shim's accepted
refresh-interval param) that each cleared several call sites. Targeted
reason-commented casts only at SDK/stub/wire boundaries; no any, no
new suppressions.
Runtime deviations are confined to already-broken paths: benchmark.ts
imported a function name that never existed (module-load crash),
caches.ts called stub methods unguarded (TypeError for ant-gated
users), messageActions returned undefined from a string function;
CACHE_EDITING_BETA_HEADER is a best-effort reconstruction of a
squash-lost constant, reachable only behind feature-gated first-party
paths (flagged for review).
Also: ConnectorTextBlock gains its wire-proven optional signature
field; MCP server factory ambient types gain close(); ink
render-node-to-output's nodeType cast fixed (intersection was
collapsing the intended widening); upstreamproxy relay normalizes the
socket data union.
Validation: full suite 3690 green, smoke + bundle guard green;
remaining 94 errors are all in test files (PR 5). Baseline lowered via
typecheck:ratchet --update.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(typecheck): clean test typing, gate CI on zero tsc errors — 94 → 0
Closes the typecheck burn-down (issue #473): bun run typecheck now
exits 0 across the whole repo and CI fails on any new error.
Test typing: new src/test/typedMocks.ts centralizes the two bun:test
gaps (asMockFetch — Mock<T> lacks fetch.preconnect; callArgs —
argless-signature mocks collapse mock.calls to []). Beyond the
helpers, fixes are honest: discriminated-union narrowing before
member access, fixture typing with boundary casts, assertion-type
corrections, and two tests realigned to production signatures they
had drifted from (requestLogging logApiCallEnd args,
incrementalTokenCounter tokenBudget rename) with identical assert
outcomes. No assertion semantics changed; all touched suites pass.
CI: the ratchet served its purpose and is retired — pr-checks now
runs a plain `bun run typecheck` step; ratchet script and baseline
deleted.
Burn-down summary across the series: 697 → 624 (mechanical sweep) →
415 (recreate ~60 missing modules) → 342 (Message discriminated
union) → 94 (narrowing + signature drift) → 0 (this PR).
Validation: tsc --noEmit exit 0, full suite 3690 green, smoke +
bundle guard green.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(typecheck): reconcile with upstream parallel typecheck fixes
Upstream landed #1591/#1592/#1595 while this series was in flight,
fixing some of the same errors differently. Rebase resolutions prefer
upstream where it is authoritative: their CACHE_EDITING_BETA_HEADER
value ('cache-editing-2025-12-01', unconditional) replaces this
series' feature-gated reconstruction; their cachedMicrocompact stub
shapes (with their new test file) replace ours, with boundary casts in
claude.ts where the stub's unknown[] edits meet the local pinned
delete-edit shape; their reader/ReadResult stream typing in openaiShim
replaces ours. MessageWithoutProgress now matches its name
(Exclude<NormalizedMessage, ProgressMessage>), reconciling upstream's
RenderableMessage GroupingResult with this series' message union; the
@ts-expect-error upstream added for settingsTypes.generated is removed
since the module now exists.
tsc exit 0; full suite 3697 green (incl. upstream's new
cachedMicrocompact tests); smoke + bundle guard green.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(sdk): keep result usage counters required, fix assistant stub exports
Addresses jatmn's and chioarub's review on the typecheck PR:
1. SDK usage contract restored: the generated result types' usage now
keeps input_tokens, output_tokens, cache_creation_input_tokens, and
cache_read_input_tokens as REQUIRED numbers — result messages are
populated from QueryEngine.totalUsage (initialized from
EMPTY_USAGE), so they are always present at runtime and strict
consumers may sum them without undefined guards. The richer nested
metadata (cache_creation, server_tool_use, service_tier) is modeled
explicitly instead of hiding behind the index signature; the nested
objects carry no index signature so the SDK's interface types stay
assignable. Generator override updated and artifacts regenerated; a
new package-consumer type test sums the counters and reads the
nested fields so this contract cannot silently regress. The
sessionHistory test fixture now carries all four counters, matching
runtime shape.
2. Assistant install wizard stub mismatch fixed: dialogLaunchers
imported NewInstallWizard/computeDefaultInstallDir through a module
shape cast, but the assistant stub only exported default — a
guaranteed runtime crash if the gated path lit up. The stub now
provides real typed exports: a wizard that cancels immediately (so
the launcher resolves null/user-cancelled instead of hanging on an
empty dialog) and an inert computeDefaultInstallDir; the unsafe
cast in dialogLaunchers is gone.
Validation: tsc exit 0; full suite 3698 green (incl. the new consumer
counters test); smoke + bundle guard green.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
---------
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
The release workflow ran `bun test` before `bun run build`, but the
bundle regression tests in scripts/missing-module-stub.test.ts read the
shipped dist/cli.mjs. On a fresh release-tag checkout dist/ (gitignored)
does not exist yet, so both tests threw "dist/cli.mjs not found" and
failed the npm publish job. pr-checks.yml already builds first (via
`bun run smoke`); reorder release.yml to match.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
* fix(test): isolate provider-related attribution and preconnect tests
Remove process-global provider mocks from apiPreconnect tests and exercise real env-based provider resolution with hermetic first-party setup.
Reset bootstrap/settings state around attribution tests and reload the attribution module per test so provider and client state cannot leak across suites.
Verification: bun test --max-concurrency=1 src/utils/apiPreconnect.test.ts src/utils/attribution.test.ts
* Fix full local check failures
Add a check script that runs smoke plus the full single-concurrency Bun test suite, and wire it into CONTRIBUTING, the PR template, and PR checks.
Fix Windows/full-suite failures by preferring Git Bash over the WSL bash launcher, normalizing settings paths before source matching, making path and warning-glyph tests platform-aware, and restoring persistent Bun module mocks for AgentTool and hook-chain tests.
Verified with bun test src\tools\BashTool\BashTool.errorOutput.test.ts --max-concurrency=1 and bun run check.
* fix(test): eliminate mock.module() leaks and platform-specific test failures
## Problem
The full test suite (bun test --max-concurrency=1) had 10 failing tests on
Windows. Investigation revealed 4 distinct root causes, all stemming from
bun's mock.module() not being fully reversible by mock.restore(). When a
test file replaces a shared module via mock.module(), stale bindings persist
in already-imported modules even after mock.restore() is called. This is a
known bun limitation.
The CI (Ubuntu) only showed 1 consistent failure (the attribution test),
but the Windows-local failures exposed real bugs that could surface in CI
under different test ordering.
## Changes
### src/utils/hookChains.integration.test.ts (root polluter)
This file was the biggest source of test pollution with 9 mock.module()
calls replacing shared modules (analytics, growthbook, policyLimits,
teammateMailbox, teammate, AgentTool, replBridge, etc.) with partial
surfaces. For example, the teammateMailbox mock only exported writeToMailbox
but the real module has 20+ exports including isIdleNotification,
createIdleNotification, readMailbox, etc. When mock.restore() didn't fully
undo these mocks, downstream tests got undefined for missing exports.
Fix: Import real modules via cache-busted dynamic imports before setting up
mocks, then spread the real module surface into each mock.module() call.
This way even if the mock leaks, downstream tests see the full module
surface with only the intended overrides. All 9 mock.module calls now
spread their real module counterparts.
Also fixed: the test was failing in isolation with SyntaxError because
attachments.ts transitively imports isIdleNotification from
teammateMailbox.js, which was missing from the partial mock.
### src/utils/settings/changeDetector.test.ts (Windows path normalization)
4 tests failed because getSourceForPath() normalizes paths using
path.normalize() which converts forward slashes to backslashes on Windows.
The test hardcoded Unix-style paths (/tmp/openclaude/user/settings.json)
but path.normalize produces \tmp\openclaude\user\settings.json on
Windows. The path comparison always failed, so handleChange() returned
early without triggering any callbacks or debounce timers.
Fix: Import normalize from 'path' and apply it to all test path constants
(pathsBySource, getManagedSettingsDropInDir). This matches what the
production code does.
### src/utils/exportFormats.test.ts (Windows path separator)
resolveExportFilepath() uses path.join() which produces backslash-separated
paths on Windows. The test expected forward-slash paths.
Fix: Import join from 'path' and use it in the expected value so the
assertion is platform-agnostic.
### src/utils/file.test.ts (growthbook mock leak)
importFileModuleWithKillswitchEnabled() mocked growthbook.js with only
getFeatureValue_CACHED_MAY_BE_STALE: () => killswitchEnabled. When
killswitchEnabled was false, this poisoned isAgentSwarmsEnabled() for all
downstream tests because agentSwarmsEnabled.ts has a static import of
getFeatureValue_CACHED_MAY_BE_STALE that captured the mock binding.
Fix: Import the real growthbook module and spread it into the mock, so
all exports remain available even if the mock leaks.
### src/utils/plugins/officialMarketplaceStartupCheck.test.ts (same pattern)
Same growthbook mock leak pattern. Top-level mock.module with only
getFeatureValue_CACHED_MAY_BE_STALE: () => true.
Fix: Import real growthbook module and spread into mock.
### src/tools/AgentTool/AgentTool.teammateModel.test.ts (transitive mock binding)
4 tests failed with 'Agent Teams is not yet available on your plan' because
isAgentSwarmsEnabled() returned false. The function checks
getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_flint', true) from
growthbook.js, but the static import binding in agentSwarmsEnabled.ts was
captured from a leaked mock that returned false.
Cache-busting the AgentTool.js import doesn't help because
agentSwarmsEnabled.ts is a transitive dependency that keeps its
already-loaded (mocked) growthbook binding.
Fix: Add mock.module for agentSwarmsEnabled.js in importAgentToolWithSpawnMock()
to pin isAgentSwarmsEnabled to true, matching the test's intent (it sets
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1).
## Verification
- bun run smoke: passes
- bun test --max-concurrency=1: 3019 pass, 0 fail (verified twice)
- No skipped tests (test.skip/it.skip/describe.skip), no test.todo,
no flaky markers, no test exclusions in config
## Known remaining risks
6 test files still have partial mock.module() calls on providers.js
(withRetry, officialRegistry, domainCheck, conversationRecovery, fastMode)
that don't spread the real module. These don't cause failures under current
test ordering but are latent risks if bun changes file execution order.
* Fix remaining provider mock leak risks
Address the known remaining risks from 7583157 by making provider mocks in withRetry, officialRegistry, and fastMode tests spread and restore the real providers module surface.
Verified with the targeted provider-mock test group and bun run check.
* Harden smoke test coverage
Remove the CI-only skip and unrelated error swallowing from the SDK query lifecycle tests so fork/resume behavior is asserted in CI and local runs.
Isolate test-suite global state by disabling built-in SDK agents for the lifecycle test, restoring MACRO presence exactly, clearing the agent cache, restoring axios mocks, and protecting xAI loopback tests from proxy/fetch leakage.
Make the provider API test script run serially to match the shared env/proxy mutation surface.
Validation: bun run check; CI=1 bun test tests\\sdk\\query-lifecycle.test.ts --max-concurrency=1; bun run test:provider; npm run test:provider-recommendation; bun run security:pr-scan -- --base upstream/main; bun run web:typecheck; bun run web:build; python -m pytest -q -p no:cacheprovider python/tests.
* Expose hidden SDK test failures
Tighten SDK test drains so they only suppress expected lifecycle abort errors instead of swallowing arbitrary init and bootstrap failures.
Replace no-op test assertions with real checks and add V2 lifecycle isolation for MACRO, built-in agents, and agent cache state.
Fix SDK V2 sendMessage to fast-exit when the caller-provided AbortController is already aborted, preventing aborted sessions from submitting work and producing result messages.
Validation: bun test scripts\\feature-flags-source-guard.test.ts tests\\sdk\\query-concurrency.test.ts tests\\sdk\\sdk-v2-lifecycle.test.ts --max-concurrency=1; bun test tests\\sdk\\query-concurrency.test.ts tests\\sdk\\query-lifecycle.test.ts tests\\sdk\\sdk-v2-lifecycle.test.ts --max-concurrency=1; bun run check.
* Fix CI smoke test failures
Respect SDK context null session project directories so regenerated SDK sessions do not fall back to global project state.
Isolate attribution tests from CI provider/model environment and replace nondeterministic live query permission checks with direct assertions against the SDK permission machinery.
Validation: bun test src\\utils\\attribution.test.ts tests\\sdk\\query-lifecycle.test.ts tests\\sdk\\permissions.test.ts --max-concurrency=1; bun test tests\\sdk\\sdk-context-isolation.test.ts tests\\sdk\\query-concurrency.test.ts --max-concurrency=1; bun run check.
* Stabilize attribution contract test
Assert that includeCoAuthoredBy emits the default co-author trailer without pinning the active provider's model label, which can legitimately differ in CI provider environments.
Validation: bun test src\\utils\\attribution.test.ts --max-concurrency=1; ANTHROPIC_MODEL=claude-sonnet-4-5-20250929 CLAUDE_CODE_USE_BEDROCK=1 bun test src\\utils\\attribution.test.ts --max-concurrency=1; bun run check.
* chore: centralize Bun version and refresh CI tool pins
- add .bun-version as the shared Bun source of truth for workflows and Docker builds
- update PR and release workflows to read Bun from bun-version-file
- refresh pinned GitHub Actions and Docker action SHAs to newer low-risk releases
- align contributor docs with Bun 1.3.13 guidance
* test: stabilize reset and provider profile persistence
Harden knowledge graph reset behavior across Windows file-lock scenarios by improving SQLite and JSON reset signaling, preserving a safe JSON source of truth when SQLite cannot be cleared, and adding direct storage regression coverage.
Also centralize deterministic config-home handling for tests, tighten provider profile persistence path resolution and cleanup semantics, isolate environment-sensitive suites with the env mutex, and remove flaky external npx dependency from the SDK consumer type test.
* test: fix Codex OAuth callback flake
Investigate the real provider smoke failure from GitHub Actions and fix the root cause instead of patching the symptom.
- make Codex OAuth callback host explicit and consistent across redirect URI generation and listener binding
- allow safe loopback host overrides for localhost, 127.0.0.1, and ::1
- harden Codex OAuth tests with env/fetch isolation so they do not poison neighboring provider suites
- pin the OAuth callback tests to 127.0.0.1 to avoid localhost IPv4/IPv6 family mismatch flakes in CI
Validated with bun test src/services/api/codexOAuth.test.ts, bun test src/services/api/providerConfig.codexSecureStorage.test.ts, and bun run test:provider.
* test: harden Codex OAuth callback tests
Investigate the recurring provider-smoke OAuth failures across multiple PR runs and fix the flaky callback test design at the root.
- remove the free-port reservation race from Codex OAuth tests
- add bounded callback retry only for loopback listener warm-up during the in-process OAuth test flow
- move ephemeral callback port support into an explicit CodexOAuthService test seam instead of widening production env parsing
- keep runtime callback-port semantics unchanged while adding regression coverage for callback host and port parsing
Validated with targeted Codex OAuth tests and repeated provider-bucket reruns to check for recurring flake.
* test: serialize provider shared-state suites
Fix the recurring provider smoke flake at the root cause by serializing test suites that mutate process.env or globalThis.fetch.
Add a shared test mutation lock and wire it into the provider bucket so Codex OAuth no longer races with unrelated provider/config/openai shim tests under Bun's parallel test execution. Cleanup now releases the lock in finally blocks, and the shared lock waits indefinitely by default to avoid timeout-based CI flakes.
* test: fix smoke root causes and noisy suites
Replace the Codex OAuth test's live loopback listener dependency with an injected listener seam, avoid module-mock leakage across provider suites, and clean up the auth-code listener test setup.
Also harden noisy storage and search tests by asserting expected log output, isolating SQLite masterpiece persistence per test cwd, and removing routine benchmark/stress logging from passing runs.
* build: harden Bun version install in Docker
Validate the repo-tracked .bun-version value before using it in the Docker build stage, strip line endings, and install Bun through a quoted semver-only variable instead of raw shell expansion.
* test: replace flaky conversation arc benchmark
Fix the recurring smoke failure caused by an absolute wall-clock assertion in the normal unit suite. Replace the CI-speed-sensitive conversation arc benchmark with deterministic regression coverage that verifies repeated fact extraction, expected entity shapes, bounded graph growth, and populated-summary behavior.
* test: isolate shared-state smoke suites
* test: restore codex credential mocks between suites
* test: fix shared-state and provider init-order flakes
* test: isolate remaining shared-state smoke suites
Serialize the remaining smoke-sensitive suites that mutate process env, CLAUDE_CONFIG_DIR, fetch, or SDK session globals.
Add shared lock coverage to discovery, agent/skills loading, platform storage, and SDK lifecycle/preserved-segment tests. Restore session and cwd state inside the lock boundary so parallel files cannot leak bootstrap state into knowledge graph and SDK isolation tests.
Validated with repeated smoke and full-suite passes:
- bun run smoke (2x)
- bun test
- bun test --max-concurrency=1
- bun run test:provider
- python -m pytest -q python/tests
- npm run test:provider-recommendation
* feat(web): openclaude landing — runs anywhere, uses anything
A new marketing site for openclaude under web/, plus the minimal root
infrastructure to build, ignore, and gate it without affecting the
published npm package.
Landing page (web/)
- Vite + React 19 with monospace gitlawb typography (sf mono / fira code).
- Hero: pill, two-line wordmark "runs anywhere. / uses anything.",
copy-to-clipboard install command, github cta.
- Six feature rows in hermes-style "title — sentence" format on hairline
dividers (any model, real tools, profiles per repo, streaming,
gateway routing, editor + server modes).
- Install block: same copyable command + three numbered steps.
- One-line footer with brand, version, gitlawb link, and license.
- Light theme is the default with a no-flash bootstrap script and a
☀ / ☾ toggle persisted to localStorage.
- New orange terminal-face logo at 36px in the nav.
- Body wash: dual orange radial gradients for warmth on both themes.
Root infra
- web/ excluded from npm publish via .npmignore (belt-and-suspenders
alongside the existing files whitelist).
- web/ excluded from docker context (.dockerignore).
- web:dev / web:build / web:preview / web:typecheck scripts in
package.json that delegate via --cwd web (no root deps added).
- web typecheck + build added to the pr-checks workflow.
- web/dist/ and web/*.tsbuildinfo ignored.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* added vercel in .gitignore
---------
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
* feat: add Docker image build and push to GHCR on release
Add Dockerfile (multi-stage build with node:22-slim) and a new docker
job in the release workflow that builds and pushes to ghcr.io when
release-please creates a tag.
* feat(docker): run as non-root user and add smoke test
Run the container as a non-root appuser to reduce blast radius.
Add a smoke test step that runs --version before pushing to GHCR.
Pin all GitHub Actions to commit SHA instead of mutable version tags
to prevent supply chain attacks via tag poisoning. This is especially
important for third-party actions like oven-sh/setup-bun.