Release v0.17.0: Mobile UX, Writer VC, Privacy, Playwright CI#138
Release v0.17.0: Mobile UX, Writer VC, Privacy, Playwright CI#138ywatanabe1989 merged 150 commits intomainfrom
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code skill system ignores name in sub-skill files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ngo conventions, vite, NAS safety Add 3 new skill files (development-environment, django-conventions, vite-frontend) and enrich 3 existing ones (deployment-production with NAS cgroup/safety lessons, refactoring-rules with correct thresholds, deployment-staging with make rebuild). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Server-side provider data is now passed as a data attribute on the settings page element, eliminating the client-side API fetch on every page load. All users (including first-time onboarding) get instant form rendering. Background sync removed — server Django cache handles the litellm import cost once per deployment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Updated comment header to reflect planned migration from mousedown to pointerdown for mobile touch support. Code unchanged — implementation will be done in scitex-ui package. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Hide password requirements on login page until password field focused - Change post-login redirect from profile page to /workspace/ - Hide site footer on mobile (<=768px) in workspace pages
- Show @username at top of mobile menu when authenticated - Add Workspace navigation link - Style user identity item with bold text
Authenticated non-visitor users at / now get workspace-page without landing-page class, so the mobile footer-hide CSS applies correctly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add VITE_HOST_IP env var with auto-detection support - Document full setup in deployment/docs/11_MOBILE_DEV_TESTING.md - Update mobile-testing skill with real device instructions - Add known mobile issues (touch resizer, swipe stuck) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VisitorPool decorators used timezone.now() without importing timezone, causing "name 'timezone' is not defined" error on visitor allocation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Hide password requirements on blur (prevents overlay on Sign In button on mobile) - Add missing timezone import in visitor pool decorators Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously visitors at / were redirected back to landing page, creating a frustrating loop with "Go to Workspace" button. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Log full traceback when visitor auto-login fails (was only logging str(e)) - Upgrade hub index unauthenticated redirect log from info to error - Comment clarifies failure could be bug, not just pool exhaustion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove border-bottom from session tabs and border-top from input area on mobile viewports. Also reduces padding for better mobile space usage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Terminal now starts in the user's project directory (or user home) instead of /tmp. Falls back to /tmp only if neither path exists. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Project names like "default-project" were truncated to "default-proje..." due to max-width:140px. Increased to 200px for better readability. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These action buttons clutter the small session tab bar on mobile. Only delete remains visible; others accessible via hamburger menu. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reduce padding and gaps in session/console tab bars for mobile viewports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Safari doesn't properly handle background shorthand in @Keyframes. Removed background from animation — keep SVG static, only animate transform and box-shadow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
User confirmed: remove the horizontal line above input area on both desktop and mobile, not just mobile. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Module tab switcher injects HTML but never called initNewResizers(), so resizer elements in dynamically loaded content (Files, Scholar, Tools) were not initialized. FigRecipe worked because it uses React hooks instead of data-attribute resizers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the mobile drawer pattern (hidden sidebar + hamburger menu) with an always-visible icon-only rail (52px). Users no longer need to discover a hidden hamburger to access workspace navigation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The root URL / triggers redirects and visitor allocation, which can timeout under load. Use the lightweight /healthz/ endpoint and increase timeout from 3s to 10s to prevent false unhealthy marks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace mousedown/mousemove/mouseup with pointerdown/pointermove/pointerup which works on mouse, touch, and pen. Add setPointerCapture for reliable tracking and touch-action:none CSS to prevent browser gesture interception. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tic 1.0.0 BUILD_ID was never set in prod, so all CSS URLs had ?v=1.0.0 forever. Now reads .build-timestamp from Vite output, falling back to current timestamp. This ensures browsers fetch fresh CSS after each deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No-fallbacks: /tmp fallback now logs error with full paths so admins can see exactly which directories are missing. Previously silently fell back without any indication. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
os.chdir() to host paths fails silently inside Docker container, causing /tmp fallback. Use srun --chdir flag to set working directory on the host side where SLURM jobs actually run. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…isible) Revert icon-only rail approach. Mobile now uses same sidebar as desktop: collapsed by default with icons+labels, expandable via toggle. Header is visible on mobile workspace pages for project context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…spy stacks on 504 spikes - Add timeout (5s for exec, 10s for logs) so a hung docker call never freezes the sampler (one such hang caused the timer to stall for 30min on first deploy). - When nginx 504_1m >= 3 and cooldown elapsed, dump daphne stacks via py-spy into logs/obs/stacks/<ts>_504-N.txt so we capture a stack signature aligned with each outage window. Related: #147 #152
…#147) Under daphne/ASGI, a sync middleware chain wraps in one sync_to_async and the innermost sync call to the async view bridges back via async_to_sync. py-spy (see #152 day-0 analysis) showed this bridge pair serialized ~6 concurrent requests through the single event loop: one thread blocked in run_until_future waiting for the loop to pick up the async_to_sync callback, one thread in thread_handler holding the sync chain. nginx upstream_connect_timeout expired at 120s → 504. Fix: mark Visitor* + OnSiteAuth + GuestSession middlewares as sync_capable=async_capable=True with markcoroutinefunction(). Under ASGI Django keeps the chain async end-to-end, so there is no outer sync_to_async wrap and no async_to_sync(view) hop. Each middleware's sync body is kept intact as a private method and the async path runs it once via sync_to_async(thread_sensitive=True) before awaiting the next layer. Behaviour is byte-for-byte identical; only the bridging changes. Expected effect on 504 rate (see #152): drops from sustained 8-10/min to near zero at current load. Observer will confirm post-deploy. Related: #147 #152
…header (fix #149) bot crawl (Bytespider, Googlebot, bingbot, ...) was the primary driver of concurrent request load hitting the sync middleware chain; see #147/#152 for the chain that converts that load into 504s. Approach is a positive signal, not UA-based whitelisting: - bots.conf defines $is_bot (UA regex denylist of known crawlers) and $is_scitex_agent (any non-empty X-SciTeX-Agent header). - $bot_block = 1 iff is_bot AND NOT is_scitex_agent. - Location / in nginx_prod.conf returns 429 when $bot_block is set. Dev agents (apptainer-hosted, MCP tools, local CLIs) just need to send X-SciTeX-Agent: <anything> on their HTTP calls → always allowed, never UA-guessed. Mirrors the existing X-SciTeX-OnSite pattern used by OnSiteAuthMiddleware for MCP auth. docker-compose.prod.yml: bind-mount bots.conf into the nginx container alongside nginx.conf. Related: #147 #149 #152
Additional commits: 504 outage fix (#147, #149) + observer scaffoldPushed 5 more commits on top of this PR (tip now
Context: scitex.ai was returning widespread 504s. Day-0 observation (see #152) ruled out memory / CPU / network — the signature was flat 8–10 × 504 per minute regardless of request volume, with py-spy showing the event loop parked in The fix converts Nginx bot block (fix #149) cuts the load driver: UA denylist for Bytespider/Googlebot/bingbot/GPTBot/ClaudeBot/etc.; dev + MCP agents bypass by sending CodeQL35 alerts surfaced but all are in pre-existing files ( Post-merge deploy + validationThe observer timer is already live on the NAS; it'll write the post-fix |
Adds a non-destructive path so scitex-cloud can start consuming the
shared `scitex_writer._django` implementation without touching the
existing legacy writer_app routes.
- `writer_app/urls/writer_django.py` — new wrapper module mirroring the
`figrecipe_app/urls/figrecipe.py` pattern: `@login_required` views that
inject `working_dir` from the user's current project and delegate to
`scitex_writer._django.views.{editor_page, viewer_page, api_dispatch}`.
Routes:
/apps/writer/editor-v2/ — canonical editor
/apps/writer/viewer-v2/ — live-paper viewer (unblocks #133)
/apps/writer/v2/<path:endpoint> — API dispatcher
- `writer_app/urls/__init__.py` — include the new wrapper before the
legacy include lines
- `pyproject.toml` — declare `scitex-writer>=2.14.0` as a dep
Stacked on ywatanabe1989/scitex-writer#84-#89 (PR1-PR6). Flipping the
default route from the legacy TemplateView to the v2 wrapper is a
follow-up commit once the v2 UX is reviewed.
This PR is the consumer side of scitex-writer#82 — closes most of
scitex-cloud#146.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI for PR #157 failed because scitex-writer isn't on PyPI yet — the sibling-installer only knew about scitex-ui and figrecipe. Adding scitex-writer to .scitex-apps.json so install_apps.sh pip-installs it via the sibling checkout in the CI runner. Also harden writer_app/urls/__init__.py: guard the v2 include() behind a try-import so the URL conf still loads in environments without scitex-writer (e.g., a rollback or a minimal production slice that hasn't pulled the dep yet). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause of the pre-rebuild warning "Permission fix needs sudo":
sandbox contains ~2.4k files owned by sub-UIDs left behind by prior
`apptainer --fakeroot` sessions that ywatanabe cannot chmod from the
host. The old `chmod -R a+rX || echo warning` masked the real failure
and still printed "Sandbox permissions fixed".
Changes:
- Preflight: fail fast if `apptainer` is missing or /etc/subuid lacks
an entry for $USER (fakeroot requires the subuid map); surface setup
problems before wasting time on docker build.
- Replace host-side chmod with `apptainer exec --fakeroot --writable
--contain --no-home --no-mount home,tmp,cwd` running
`find / -xdev -not -path /proc* -not -path /sys* -not -path /dev*
-exec chmod a+rX {} +` inside the sandbox. Fakeroot owns the sub-UID
files so chmod succeeds without sudo. --contain/--no-home prevents
walking into host $HOME (~/.scitex/...); -xdev + -not -path skips
kernel pseudo-filesystems that no one can chmod.
- Fail loud on genuine errors instead of printing a misleading success.
Result: step 6 drops from effectively-hung/failing to ~35s and exits 0.
- Bump version 0.17.0-alpha → 0.17.1-alpha - CHANGELOG entry for deploy/rebuild.sh fix, CI fix, and writer v2 wrapper - releases/v0.17.1-alpha/RELEASE_NOTES.md
Migration 0008 was amended to also create the Comment model with its three
indexes, but 0009 still carried the original `CreateModel(name="Comment")`
plus three `AddIndex` ops. Applying 0009 on a fresh DB raised::
django.db.utils.ProgrammingError:
relation "writer_app_comment" already exists
Applying writer_app.0009_add_comment_model...
This was the sole cause of the 16+ days of red "E2E Mobile Tests" CI on
develop (and on every PR that re-triggered it).
Keep 0009 as an empty graph node so 0010_add_comment_anchor_fields's
`dependencies = [("writer_app", "0009_add_comment_model")]` still resolves.
`python manage.py makemigrations --check --dry-run writer_app` reports
"No changes detected" with the model state fully satisfied by 0008.
Replaces click.prompt/click.confirm in 6 CLI sites with required
flags backed by SCITEX_CLOUD_* env vars and an optional config
file at ~/.scitex/scitex-cloud/config.yaml. Precedence per spec
§6b: --flag > env var > config file.
Missing credentials or destructive-action confirmation now exit
with code 2 and a guidance message to stderr, instead of blocking
on stdin. This lets scitex-cloud's CLI run under CI, agent
fleets, and cron without hanging.
Sites:
- _workspace_auth.py::get_jwt_token — env SCITEX_CLOUD_WORKSPACE_{USER,PASSWORD,URL}
- _gitea_auth.py::login — env SCITEX_CLOUD_GITEA_{TOKEN,USER,PASSWORD,URL}
- _gitea_auth.py::logout --delete-token — env SCITEX_CLOUD_GITEA_PASSWORD
- setup.py — env SCITEX_CLOUD_ENV
- project.py::delete — requires --yes (no fallback prompt)
- _gitea_repo.py::delete — requires --yes (no fallback prompt)
Adds scitex_cloud/_config/_loader.py — minimal non-failing YAML
loader with spec §6b precedence; returns {} on missing config.
Adds tests/scitex_cloud/test_no_interactive_prompts.py — 8 tests
covering each refactored site, including positive env-var paths
and exit-2 fail-fast paths.
Fixes item C1 in cli-skill-audit-2026-04-22-verified.md.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- SKILL.md: document actual CLI groups, MCP tool counts (55 across 6 categories), Python API, and list all 21 sub-skills (previously only 15 were indexed) - README: fix stale MCP tool table (was: 2 categories/23 tools; now: 6 categories/55 tools), stale hardcoded version string, and wrong docker subcommands (docker status/logs -> docker up/down/ps/build/ restart) - MANIFEST.in: belt-and-braces _skills inclusion for sdist on top of existing pyproject.toml [tool.setuptools.package-data] glob Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| } | ||
| } | ||
|
|
||
| // Update mobile hamburger menu countdown |
Check warning
Code scanning / CodeQL
Useless assignment to local variable
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
To fix this cleanly without changing functionality, remove the unused timeStr variable and its associated timestamp formatting block in updateServerHealth().
Best single fix in this snippet:
- In
static/shared/ts/components/header.ts, insideupdateServerHealth:- Keep
timestamponly if it is used elsewhere. In this snippet it is not, so remove it as well. - Remove the entire “Format timestamp for display” block (
let timeStr = "now"; ...).
- Keep
- Leave tooltip behavior unchanged (
Server: ${statusMsg}), since that reflects current functionality.
No new imports, methods, or dependencies are needed.
| @@ -352,18 +352,7 @@ | ||
|
|
||
| const status = data.status; // "healthy" | "warning" | "error" | "starting" | ||
| const statusColor = data.color; // Hex color from API | ||
| const timestamp = data.timestamp; // ISO timestamp from API | ||
|
|
||
| // Format timestamp for display | ||
| let timeStr = "now"; | ||
| if (timestamp) { | ||
| const date = new Date(timestamp); | ||
| timeStr = date.toLocaleTimeString([], { | ||
| hour: "2-digit", | ||
| minute: "2-digit", | ||
| }); | ||
| } | ||
|
|
||
| // Build simple tooltip: just overall status | ||
| let statusMsg = "healthy"; | ||
| if (status === "starting") { |
pip install <pkg> alone enables `import <mod> as sio` only; the scitex.<subname> namespaced form requires `pip install scitex` as well. Every package SKILL.md now documents both forms (or standalone- only when the umbrella has no re-export, confirmed empirically 2026-04-23 in a python:3.11-slim container). Cross-references ../general/02_interface-python-api.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…surface Enumerates all 6 MCP categories (project_*, repo_*, cloud_sdk_*, api_*, app_*, onsite_*) with representative trigger phrases and drop-in replacements (curl+git+Playwright against the Django instance). Fixes stale allowed-tools to mcp__scitex__cloud_*. Verified against actual async-def tool inventory; stays under the 4096B index size cap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ella scitex.browser Tests should depend on the leaf package (scitex_browser) rather than the umbrella namespace re-export. Same runtime behavior, narrower dep scope.
…hrases and drop-in replacements
- public_status.py: take main (richer docstring + issue #82 ref + cache-backend-unavailable fallback) - context_processors.py: hybrid — develop's .build-timestamp chain + main's SCITEX_CLOUD_BUILD_ID env name - settings_dev.py: hybrid — develop's VITE_USE_BUILD + 'auto' gateway detection + main's env-prefix (SCITEX_CLOUD_VITE_HOST_IP / SCITEX_CLOUD_VITE_USE_BUILD) - nginx_prod.conf: take develop (variable-based proxy_pass for Docker DNS re-resolution; NAS-reboot-resilient pattern already in use for gitea/crossref/ws_ssh_proxy/umami) - pyproject.toml: bump to 0.17.2-alpha
Encodes which interface (python/cli/mcp/hook/mixed) a user should reach for first, without dropping or reordering the existing 5-interface template. Machine-readable via frontmatter; human-visible via callout at top of body.
Replaces single primary_interface label with a full ratings dict:
interfaces:
python: 0..3
cli: 0..3
mcp: 0..3
skills: 0..3
hook: 0..3
http: 0..3
0=absent, 1=thin wrapper, 2=useful, 3=primary.
Callout in body uses stars (e.g. `Python ⭐⭐⭐ (primary) · CLI ⭐`).
Derived primary_interface field preserved for single-value consumers.
Drops the kanji (守破離) from the section heading in favour of the already-used Shu-Ha-Ri transliteration. Line 93 of the same file already writes "This is the Shu-Ha-Ri principle"; the header now matches, and the new audit_english_only.py auditor no longer flags this file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Commits
340 commits from develop since last merge (PR #132)
Test plan
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com