Skip to content

release: v0.0.5 — consolidation of 59 PRs + changelog + evidence review - #176

Open
lucastononro wants to merge 218 commits into
mainfrom
release/v0.0.5
Open

release: v0.0.5 — consolidation of 59 PRs + changelog + evidence review#176
lucastononro wants to merge 218 commits into
mainfrom
release/v0.0.5

Conversation

@lucastononro

@lucastononro lucastononro commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Summary

Promotion of release/v0.0.5 to main: consolidation of 59 merged PRs/issues on top of the v0.0.4 line, plus CHANGELOG.md and a fresh evidence review (docs/evidence/v0.0.5-review.md). Per-PR merge detail (Greptile follow-ups, conflict resolutions, per-wave test counts) is in STAGING-v0.0.5.md on this branch.

Product features

Compute: RunPod provider (#145); per-project GPU allowance (#160)

Frontend: session-scoped SSE bus (#148), chat memoization (#150), autoscroll pinning (#152), typed SSE union (#157), error boundaries (#159), page.tsx split (#166), react-resizable-panels v4 migration (#24)

Security: opt-in bearer auth (#138), CORS fail-fast (#140), prod compose secrets + localhost binds (#136), .env 0600 (#134), S3 upload bounds + bucket allowlist (#141/#143/#147)

Reliability: LLM timeout normalization (#142), concurrent readyz probes (#144), Sentry capture fix (#151), boto3/DuckDB event-loop offload (#143/#139)

Database: Alembic migration framework (#153) + revisions for training_config (#164), budget_usd (#165), deployments provider columns (#145)

CLI: status/logs/--version + Docker preflight (#126), first CLI test suite + env perms (#134), README refresh (#129)

CI/tooling: pinned ruff (#137/#135), coverage gate (#149), Postgres CI (#154), advisory vuln scans (#156), actions major bumps (#7#10), hashed requirements.lock (#173/#128)

Dependencies: fastapi 0.136.1, sqlalchemy 2.0.49, sse-starlette 3.3.4, python-multipart 0.0.28, python-dotenv 1.2.2, react-markdown 10, react-syntax-highlighter 16, postcss 8.5, prettier 3.9.6

Evidence (fresh runs on this branch, 2026-07-29)

  • Backend pytest: 647 passed, 8 skipped (70s)
  • CLI pytest: 12 passed
  • ruff@0.15.22 check + format --check: clean (220 files)
  • Frontend: vitest 20/20, tsc --noEmit clean, next lint clean, prettier clean, production next build OK (8 routes, / prerenders)
  • Alembic: fresh-sqlite upgrade head through 4 revisions, single head b7d3f5a91c02; migration tests 7/7
  • docker compose config -q: dev + prod OK with dummy secrets (fails closed without)
  • CLI smoke: --version / --help OK (note: wheel still reports 0.0.4 — version bump expected at release cut)
  • Boot smoke: uvicorn boots on temp sqlite without Modal/S3 creds; /api/health ok; /api/readyz fail-closed not_ready without S3; bearer gate 401 without / 200 with token

NOT evidenced — honest gap list

Greptile Summary

This release consolidates the v0.0.5 feature set across backend compute, migrations, security, workspace tooling, frontend streaming and UI, CLI behavior, dependencies, and CI.

  • Adds RunPod compute, workspace exports, dataset tooling, training controls, budgets, approvals, session resume, and snapshot reproduction.
  • Introduces Alembic migrations, opt-in bearer authentication, production secret hardening, and S3 validation.
  • Refactors the frontend around a session-scoped SSE bus and new workspace/chat components.
  • Adds pinned dependency lockfiles, expanded tests, coverage enforcement, and advisory vulnerability scans.

Confidence Score: 4/5

The authentication integration must be fixed before merging because setting the advertised API_AUTH_TOKEN makes the bundled frontend’s API requests and SSE connection fail with 401 responses.

The backend now requires a shared credential on all protected API traffic, but neither the frontend API client nor its EventSource construction can provide that credential.

Files Needing Attention: backend/auth.py, frontend/src/lib/api.ts, frontend/src/lib/useSessionStream.ts

Important Files Changed

Filename Overview
backend/auth.py Adds a global opt-in bearer gate, but enabling it blocks the bundled frontend because no client credential path is wired.
frontend/src/lib/api.ts Expands the typed API surface but does not support the newly introduced bearer-auth configuration.
frontend/src/lib/useSessionStream.ts Adds consolidated session streaming, but its EventSource URL remains unauthenticated when API_AUTH_TOKEN is enabled.
backend/routers/download.py Adds session and project workspace ZIP endpoints consistent with the repository's current shared-token, single-user authorization model.
backend/db.py Replaces boot-time schema mutation with Alembic upgrade and legacy-schema stamping logic.
backend/services/compute/runpod_provider/serving.py Adds RunPod-backed serving lifecycle support as part of the new compute-provider abstraction.
frontend/src/lib/useSessionStream.ts Centralizes session SSE state and event handling, with the authentication integration gap noted above.
.github/workflows/ci.yml Expands CI with PostgreSQL-backed tests, coverage artifacts, frontend tests, and advisory dependency and image scans.

Sequence Diagram

sequenceDiagram
    participant B as Browser frontend
    participant P as Next.js /api rewrite
    participant A as FastAPI auth middleware
    participant R as API/SSE route
    B->>P: GET /api/... (no bearer token)
    P->>A: Forward unchanged request
    alt API_AUTH_TOKEN configured
        A-->>B: 401 Unauthorized
    else API_AUTH_TOKEN unset
        A->>R: Forward request
        R-->>B: API response or SSE stream
    end
Loading

Fix All in Claude Code

Reviews (1): Last reviewed commit: "docs(release): v0.0.5 changelog + eviden..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used:

  • Context used - AGENTS.md (source)

dependabot Bot and others added 30 commits April 21, 2026 14:29
Bumps [docker/login-action](https://github.com/docker/login-action) from 3 to 4.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](docker/login-action@v3...v4)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@v4...v8)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](docker/setup-buildx-action@v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 7.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](docker/build-push-action@v5...v7)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [sse-starlette](https://github.com/sysid/sse-starlette) from 2.1.0 to 3.3.4.
- [Release notes](https://github.com/sysid/sse-starlette/releases)
- [Commits](sysid/sse-starlette@v2.1.0...v3.3.4)

---
updated-dependencies:
- dependency-name: sse-starlette
  dependency-version: 3.3.4
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [python-dotenv](https://github.com/theskumar/python-dotenv) from 1.0.1 to 1.2.2.
- [Release notes](https://github.com/theskumar/python-dotenv/releases)
- [Changelog](https://github.com/theskumar/python-dotenv/blob/main/CHANGELOG.md)
- [Commits](theskumar/python-dotenv@v1.0.1...v1.2.2)

---
updated-dependencies:
- dependency-name: python-dotenv
  dependency-version: 1.2.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [prettier](https://github.com/prettier/prettier) from 3.8.1 to 3.8.3.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](prettier/prettier@3.8.1...3.8.3)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.8.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.115.0 to 0.136.1.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](fastapi/fastapi@0.115.0...0.136.1)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-version: 0.136.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [python-multipart](https://github.com/Kludex/python-multipart) from 0.0.12 to 0.0.28.
- [Release notes](https://github.com/Kludex/python-multipart/releases)
- [Changelog](https://github.com/Kludex/python-multipart/blob/main/CHANGELOG.md)
- [Commits](Kludex/python-multipart@0.0.12...0.0.28)

---
updated-dependencies:
- dependency-name: python-multipart
  dependency-version: 0.0.26
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.8 to 8.5.14.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](postcss/postcss@8.5.8...8.5.14)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels) from 2.1.9 to 4.11.0.
- [Release notes](https://github.com/bvaughn/react-resizable-panels/releases)
- [Changelog](https://github.com/bvaughn/react-resizable-panels/blob/main/CHANGELOG.md)
- [Commits](bvaughn/react-resizable-panels@2.1.9...4.11.0)

---
updated-dependencies:
- dependency-name: react-resizable-panels
  dependency-version: 4.10.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [react-syntax-highlighter](https://github.com/react-syntax-highlighter/react-syntax-highlighter) from 15.6.6 to 16.1.1.
- [Release notes](https://github.com/react-syntax-highlighter/react-syntax-highlighter/releases)
- [Changelog](https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/CHANGELOG.MD)
- [Commits](react-syntax-highlighter/react-syntax-highlighter@v15.6.6...v16.1.1)

---
updated-dependencies:
- dependency-name: react-syntax-highlighter
  dependency-version: 16.1.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [react-markdown](https://github.com/remarkjs/react-markdown) from 9.1.0 to 10.1.0.
- [Release notes](https://github.com/remarkjs/react-markdown/releases)
- [Changelog](https://github.com/remarkjs/react-markdown/blob/main/changelog.md)
- [Commits](remarkjs/react-markdown@9.1.0...10.1.0)

---
updated-dependencies:
- dependency-name: react-markdown
  dependency-version: 10.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.35 to 2.0.49.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

---
updated-dependencies:
- dependency-name: sqlalchemy
  dependency-version: 2.0.49
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Adds `GET /api/sessions/{sid}/download` and `GET /api/projects/{pid}/download`
that stream a self-contained zip of the agent's workspace: every file under
`/sessions/{sid}/` plus a synthetic `trainable_local.py` shim, a filtered
`requirements.txt`, and a runbook README. The shim lets downloaded scripts
that import `from trainable import log, log_image, ...` run on a vanilla
Python install — calls land in `./trainable_out/` instead of the Modal
volume.

The zip is streamed via `StreamingResponse` over an in-memory buffer
drained per write, so multi-hundred-MB sessions don't materialize on
disk or in RAM. A 2 GB uncompressed cap with a trailing `__truncated.txt`
marker keeps a runaway walk bounded.

Frontend ships two entry points:
- Download icon in the WorkspaceSidebar header → session zip
- Download icon on every Project row in the sidebar → project zip

Closes #79.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The cli/README.md was stale against v0.0.4: it described a single-provider
(Anthropic + Modal) setup, claimed the wizard downloaded the compose file
from raw.githubusercontent.com, and omitted `trainable reconfigure`. Update
it to match the current implementation:

- multi-provider selection (Claude API key / subscription OAuth, OpenAI,
  Gemini, LiteLLM-routed backends) as a flat picker, with a Providers table
- compose file bundled inside the wheel (offline installs)
- version-pinned images on `trainable up`
- `trainable reconfigure` command and the existing-config merge flow

Assisted-by: Claude Code
…t:8000 (#96)

Replace getSSEBase()/getBackendUrl() (which returned http://host:8000)
with relative /api paths at all EventSource and raw file/image/PDF fetch
sites, routing through the existing Next rewrite proxy so HTTPS deploys
no longer hit mixed-content blocking or an unexposed port. Both helpers
are deleted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wp66uSq9saSpRq9Bbmjxj4
…tores (#90)

docker-compose.prod.yml shipped hardcoded default credentials
(trainable/trainable, minioadmin/minioadmin) and published Postgres
(5432), MinIO (9000/9001) and the auth-less Jaeger UI (16686) on
0.0.0.0 — direct data-store compromise with well-known creds on any
networked host, and Jaeger traces leak prompts/paths/token counts.

- Replace every hardcoded secret with required env-var interpolation
  that fails loudly if unset: POSTGRES_USER/PASSWORD/DB and
  MINIO_ROOT_USER/PASSWORD via `${VAR:?set VAR in .env}`. The backend's
  DATABASE_URL and AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY are now
  derived from those same vars, so app creds always match the
  datastores (var names match what backend/config.py reads).
- Bind Postgres, MinIO API+console and the Jaeger UI to 127.0.0.1 so
  they are reachable only from the host / internal compose network,
  never from another machine. Drop the Jaeger OTLP host publishes
  (4317/4318) — the backend reaches them by service name internally.
  frontend (3000) and backend (8000) stay published as before.
- Document the newly-required vars in .env.example with placeholders
  and a note that prod has no defaults.

Validated: `docker compose -f docker-compose.prod.yml config` parses
and interpolates with a filled .env, and fails fast with a clear
message when the secrets are unset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wp66uSq9saSpRq9Bbmjxj4
…erving

Introduce services/compute with per-concern interfaces (SandboxProvider,
KernelTransport, StorageBackend, ServingBackend) selected by the new
COMPUTE_PROVIDER setting. sandbox.py/volume.py/kernel_manager.py/deploy.py
become facades that keep their public signatures; Modal mechanics move to
services/compute/modal_provider/*. Billing rows now record the actual
provider, sandbox.yml gains runpod rates, and deployments get
provider/provider_endpoint_id columns.

Refs #130

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GrGFBtdZv7qtgn8KQEQEB
Add an opt-in shared-secret gate for the API. When API_AUTH_TOKEN is
unset (the default) nothing changes — every endpoint stays open, so
local dev and the frontend are unaffected. When set, all /api/*
requests must send `Authorization: Bearer <token>`.

- backend/auth.py: pure-ASGI BearerTokenAuthMiddleware (no
  BaseHTTPMiddleware wrapping, so SSE streaming is untouched);
  constant-time token comparison; 401 + WWW-Authenticate on failure.
- Exempt: /api/health, /api/readyz, CORS preflight (OPTIONS).
- SSE: EventSource cannot send headers, so the stream endpoint
  (/api/sessions/{id}/stream) additionally accepts ?token=<token>;
  the query param is stream-only and does not unlock other routes.
- config.py: api_auth_token setting (env API_AUTH_TOKEN, default None).
- Middleware added before CORSMiddleware so CORS stays outermost and
  answers preflights before auth runs.
- .env.example: new "Optional — security" section documenting it.
- tests/test_api_auth.py: middleware unit tests (open-by-default,
  401/200 matrix, exemptions, stream query token, preflight).

Closes #88

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wp66uSq9saSpRq9Bbmjxj4
…edentials

Replace the `cors_origins=["*"]` + `allow_credentials=True` default
(any web page could script credentialed cross-origin requests against
localhost:8000) with a safe, configurable policy:

- config.py: default origins are the local frontend
  (http://localhost:3000, http://127.0.0.1:3000); overridable via
  CORS_ORIGINS as a comma-separated env var (NoDecode + validator so
  plain CSV works, not just JSON).
- main.py: if `*` is configured it is honored but credentials are
  disabled (allow_credentials=False) with a startup warning — the
  wildcard+credentials combination is now impossible.
- .env.example: document CORS_ORIGINS.

No impact on the standard setup: the frontend proxies /api through
Next.js rewrites (same-origin), so CORS is not involved there.

Closes #89

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wp66uSq9saSpRq9Bbmjxj4
- Stream uploads to S3 in bounded 8 MB chunks (single put_object for small
  bodies, multipart upload for larger ones) instead of reading the whole
  body into RAM; abort the multipart upload on any failure.
- Enforce the configured max_upload_size_bytes cap incrementally while
  reading (413 without buffering the oversize body).
- Validate bucket against the app-provisioned allowlist (new
  settings.s3_allowed_buckets, also used by startup bucket init) and
  reject traversal/absolute/backslash keys; write endpoints (upload +
  put-presign) are scoped to the datasets/projects/ prefix.
- Authentication is intentionally NOT added here — it lands globally via
  issue #88.

Closes #91

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wp66uSq9saSpRq9Bbmjxj4
A stalled provider HTTP call had no wall-clock limit: the runner's outer
asyncio.timeout was deliberately removed (the Modal sandbox timeout only
bounds tool execution), and every provider accepted timeout_seconds but
discarded it. A hung network call left the session's background task
alive forever — spinner never cleared, task-registry entry never freed.

Thread the existing timeout_seconds into each provider around the HTTP
call only, so tool-execution time is never counted:

- base.py: new enforce_wall_clock() helper — asyncio.wait_for around a
  single provider call, raising builtin TimeoutError on expiry.
- openai: per-request SDK timeout via client.with_options(timeout=...)
  plus the hard wall-clock cap (SDK retries can't stretch past it).
- gemini: hard cap around generate_content (google-genai has no default
  request timeout at all).
- litellm: keep the existing per-attempt timeout= knob, add the hard cap
  so backend retries/fallbacks can't exceed the budget.
- claude: query() runs the whole tool loop internally, so it must not be
  wrapped wholesale; bound each underlying API HTTP request via Claude
  Code's API_TIMEOUT_MS env instead (caller-supplied value wins).

On expiry the providers let TimeoutError propagate: run_agent's existing
TimeoutError handler publishes agent_timeout, moves the session to
timed_out, and the finally block frees the task registry — the run ends
cleanly instead of hanging.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wp66uSq9saSpRq9Bbmjxj4
…ostcss-8.5.12

# Conflicts:
#	frontend/package-lock.json
#	frontend/package.json
…eact-syntax-highlighter-16.1.1

# Conflicts:
#	frontend/package-lock.json
# Conflicts:
#	frontend/package-lock.json
#	frontend/package.json
…n /frontend

# Conflicts:
#	frontend/package-lock.json
#	frontend/package.json
#128)

requirements.txt stays the human-edited input; requirements.lock is
generated via `uv pip compile requirements.txt -o requirements.lock
--generate-hashes --universal` (149 pinned + hashed packages). The
backend Dockerfile and the CI backend-test / backend-vuln-scan install
steps now install with --require-hashes from the lock, so CI, the
published image, and contributor venvs resolve identical versions.
cmd_up/cmd_down previously exec'd straight into `docker compose`, so an
installed-but-stopped Docker daemon surfaced a raw connection error.
_require_config now runs a `docker info` liveness preflight with friendly
guidance, covering up/down/status/logs. New subcommands: `trainable
status` (compose ps), `trainable logs` (compose logs), and `trainable
--version`. Tests mock all docker subprocesses — no real Docker needed.
…/ edit-file / run-file + notebook cell edit/delete/rerun (closes #85)

New skills (purely additive; no signature changes to execute-code or the
existing notebook skills):

- write-file(path, content, overwrite=true) — wraps volume.write_to_volume;
  emits file_created / file_updated; does NOT write into scripts/ (authoring
  is not an execution step).
- edit-file(path, mode=replace|overwrite, old/new|content) — exact-anchor
  replace (must match exactly once) or whole-file overwrite; emits
  file_updated, never file_created.
- run-file(path, heavy=false) — runpy.run_path(path, run_name='__main__')
  through services.sandbox.run_code under the existing preamble (src/ on
  sys.path, session cwd); auto-saves a one-line runpy audit entry to
  scripts/step_NN_run_<name>.py; heavy picks the training sandbox profile
  with the owner max_timeout cap (same as execute-code post-#160).
- edit-notebook-cell / delete-notebook-cell — wrap new
  notebook_store.edit_cell / delete_cell helpers (outputs preserved on
  edit, same as apply_source_update; both persist under the existing
  _lock + save path); broadcast notebook.structure.changed for live UI.
- rerun-notebook-cell — looks a cell up by cell_id, re-executes its
  current source against the persistent kernel via
  kernel_manager.execute_and_wait (kernel cell_started clears outputs;
  earlier-cell variables stay in scope); same result envelope as
  run-notebook-cell.

Shared resolve_session_path() helper in services/skills/state.py
(session-relative, volume-absolute, and /data/-prefixed paths; rejects
escapes). All six skills registered in chat, eda, data_prep, feature_eng,
trainer, orchestrator YAMLs, whose 'Workspace is a real Python repo'
blocks are replaced by the prescriptive 'Authoring vs running' block
(rule of three; edit don't repaste; import from src/ works in kernel
cells) keeping the per-agent example bullets. execute-code SKILL.md now
documents its one-shot contract. Frontend: file_updated added to the
typed SSE union + handled in useSessionStream (tree update, no
auto-open).

Tests: 48 new (skill handlers happy/failure paths incl. invalid path,
non-matching/ambiguous anchor, missing file, cell_id not found, markdown
rerun, kernel timeout; notebook_store edit_cell output preservation +
delete_cell volume persistence; path resolution). Full backend suite:
647 passed, 8 skipped. ruff 0.15.22 check + format clean. Frontend:
vitest 20/20, tsc, next lint, prettier clean.
@lucastononro

Copy link
Copy Markdown
Owner Author

@greptile-apps review

Comment thread backend/auth.py
Comment on lines +50 to +67
async def __call__(self, scope, receive, send):
if scope["type"] not in ("http", "websocket"):
await self.app(scope, receive, send)
return

path = scope["path"]
if (
not path.startswith("/api/")
# WebSocket scopes have no "method" key.
or path in EXEMPT_PATHS
or scope.get("method") == "OPTIONS"
):
await self.app(scope, receive, send)
return

if self._authorized(scope):
await self.app(scope, receive, send)
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Bearer gate blocks the frontend

When API_AUTH_TOKEN is configured, this middleware requires credentials on every non-exempt /api request, but the bundled API client and session EventSource provide no token, causing normal UI requests and session streaming to fail with 401 responses.

Context Used: AGENTS.md (source)

Fix in Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants