tuiti - #484
Open
svobodavid-svg wants to merge 145 commits into
Open
tuiti#484svobodavid-svg wants to merge 145 commits into
svobodavid-svg wants to merge 145 commits into
Conversation
…config - providers/: AbstractLLMProvider protocol + Claude and Gemini implementations - core/router.py: LLMRouter with static/failover/round_robin strategies - agents/: AgentRole, AgentOutput, SingularitySwarm with multi-LLM routing - config/settings.py: dual-API-key config (Anthropic + Gemini) - memory/embeddings.py: offline HashEmbeddingFunction (128-dim, no ONNX) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
Full implementation of the Singularity external extension built on Omega:
- core/telemetry.py: structlog + Prometheus metrics (graceful no-op fallback)
- core/limiter.py: ProviderRateLimiter token bucket per provider (aiolimiter)
- core/router.py: 6 routing strategies (static/failover/round_robin/
cost_optimized/latency_optimized/quality_first) + self-healing cooldown
- providers/: cost/latency/quality metadata + health/cooldown self-healing
- agents/swarm.py: rate-limited invocation, failover, latency telemetry
- core/graph.py: SingularityCore LangGraph loop + provider_log tracking
- api/main.py: lifespan handler + /providers, /router/strategy, /metrics,
/tasks/{id}/providers, force_provider
- memory/evals/rag: ported from Omega with all 8 critical fixes preserved
- tests/: 37 offline tests (unit/chaos/perf) all green, plus integration
- pyproject.toml, .env.example, README.md, scripts/run_tests.sh
All 8 Omega critical fixes preserved. 37/37 offline tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
…-extension-1x7ppe
- core/session_store.py: thread-safe in-memory session store s konverzační
historií a kumulativními náklady (estimate_cost per provider)
- core/graph.py: přidán run_stream() AsyncGenerator — node-level progress
events přes graph.astream(); zachováno zpětně kompatibilní run()
- api/main.py: POST /task/stream (SSE), GET /sessions/{uid},
GET /sessions; WebSocket přepnut na run_stream() → klient vidí
progress po každém uzlu (plan/execute/critique/synthesize/reflect)
- memory/embeddings.py: opravena kompatibilita s ChromaDB >=0.6
(HashEmbeddingFunction nyní implementuje name() a is_legacy())
- tests: 11 nových unit testů (session_store × 7, streaming × 4)
— 48/48 offline testů zelených
…-extension-1x7ppe
… dashboard - core/health_monitor.py: background asyncio task, periodický health check všech providerů (default 30s); při obnově po cooldownu volá record_success() - api/dashboard.py: self-contained admin dashboard HTML (vanilla JS, žádné CDN) — live provider status tabulka, session přehled s náklady, Prometheus raw, runtime změna strategie; auto-refresh každých 5s - core/graph.py: SingularityState rozšířen o session_context; _plan_node injektuje posledních 3 turny konverzace jako přidaný kontext plánovači - api/main.py: _build_session_context(), HealthMonitor spuštěn v lifespan, GET /health/providers (okamžitý check), GET /dashboard - tests: 10 nových unit testů (health_monitor × 5, multiturn × 5) — 58/58 offline testů zelených
…-extension-1x7ppe
…on export
- core/task_queue.py: in-memory async fronta (QUEUED→RUNNING→COMPLETED/FAILED),
jeden worker, start/stop v lifespan; v produkci nahradit Celery/arq
- api/main.py: POST /task/async (okamžitá odpověď s task_id),
GET /task/{id}/status, GET /task/{id}/result,
POST /task/compare (paralelní Claude vs Gemini přes asyncio.gather),
GET /sessions/{uid}/export (JSON download s eval_scores)
- core/session_store.py: ConversationTurn rozšířen o eval_scores: dict
(zpětně kompatibilní, default={}); to_dict() zahrnuje eval_scores
- tests: 10 nových unit testů (task_queue × 5, compare × 5)
— 68/68 offline testů zelených
…-extension-1x7ppe
…llbacks
- Add BudgetManager with thread-safe per-user USD cost limits; blocks task
submission when limit would be exceeded (HTTP 402)
- Add callback_url to TaskRequest and QueuedTask; _fire_webhook() posts result
via httpx.AsyncClient after task completes or fails (graceful failure)
- Add POST /task/batch endpoint (max 10 tasks, budget-checked per task)
- Add GET /queue/status endpoint returning current queue depth
- Add POST/GET/DELETE /budget/{uid} endpoints for runtime limit management
- Wire budget_manager into POST /task/async enforcement
- Add 5 unit tests for BudgetManager and 3 unit tests for webhook callbacks
(70 unit tests total, all offline)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
…-extension-1x7ppe
…ong-poll wait
- TaskPriority enum (CRITICAL > HIGH > NORMAL > LOW); upgrade TaskQueue to
asyncio.PriorityQueue; priority field on QueuedTask + TaskRequest
- Event-based wait() in TaskQueue — GET /task/{id}/wait long-polls without
busy-polling, clamped 1–300 s, returns 408 on timeout
- UserRateLimiter: sliding-window (60 s) RPM counter per user_id; enforced in
POST /task/async (HTTP 429); POST/GET/DELETE /rate-limits/{uid} endpoints
- Fix HashEmbeddingFunction: name()/supported_spaces()/get_config() as
@classmethod to satisfy ChromaDB >=0.6 call convention; 0 warnings now
- 10 new unit tests (5 priority queue + 5 user limiter) → 89 total
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
…-extension-1x7ppe
- RetryPolicy: immutable config (max_attempts, backoff_base, max_backoff,
jitter); delay_for_attempt() computes capped exponential + jitter delay
- TaskQueue: failed tasks with remaining attempts sleep+requeue with backoff;
after exhaustion → DLQ dict; TaskStatus.RETRYING + TaskStatus.DLQ added
- retry_from_dlq(): reset attempt counter, re-enqueue from DLQ
- AuditLog: thread-safe ring buffer (deque maxlen=1000) recording
task_submitted/completed/failed/retried/dlq and budget/rate-limit events
- New API endpoints: GET /audit-log (filterable), GET /dead-letter-queue,
POST /dead-letter-queue/{id}/retry; max_retries field on TaskRequest
- audit_log wired into lifespan and task_queue.start()
- 10 new unit tests (5 retry + 5 audit log) → 99 total, all offline
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
data/ directories generated by ChromaDB during test runs were showing up as untracked. Covered by the per-project .gitignores inside each quickstart, but the root had none — adding one to prevent re-occurrence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
…-extension-1x7ppe
- api/auth.py: FastAPI dependency verify_api_key via X-API-Key header; passes through anonymously when require_api_key=False (dev mode) - core/api_keys.py: ApiKeyManager — thread-safe in-memory store, prefix sk-sg-, create/revoke/validate/list/delete_user_keys - api/main.py: POST/GET/DELETE /api-keys endpoints; Depends(verify_api_key) guard on /task and /task/async; lifespan wires set_manager + num_workers - config/settings.py: require_api_key (default False) + task_workers (default 1) - core/task_queue.py: replaced single _worker_task with _worker_tasks list; start() accepts num_workers, spawns N asyncio worker coroutines - tests/unit/test_api_keys.py: 7 tests for ApiKeyManager - tests/unit/test_multi_worker.py: 3 tests for multi-worker parallelism All 100 unit tests pass. Co-Authored-By: Claude <noreply@anthropic.com>
…-extension-1x7ppe
- core/request_context.py: contextvars-based per-request state (request_id, user_id); set/get/clear helpers - api/middleware.py: RequestContextMiddleware — generates/propagates X-Request-ID header, adds X-Response-Time, logs every request at DEBUG - api/main.py: mount RequestContextMiddleware; add GET /health/live (always 200) and GET /health/ready (503 until core initialised) - Dockerfile: multi-stage build, non-root user (uid 1001), HEALTHCHECK via /health/live - docker-compose.yml: singularity service + optional prometheus profile - prometheus.yml: scrape config for /metrics - .dockerignore: excludes tests/, data/, __pycache__, .env - tests/unit/test_middleware.py: 5 tests — header injection, echo, propagation into handler, uniqueness per request All 105 unit tests pass. Co-Authored-By: Claude <noreply@anthropic.com>
…-extension-1x7ppe
- cli/main.py: Typer CLI with subcommands:
serve, health, keys create/list/revoke, queue status, dlq list/retry
Set SINGULARITY_URL env var or pass --url to target a running server.
Registered as 'singularity' entry point in pyproject.toml.
- core/graceful_shutdown.py: GracefulShutdown — waits for asyncio
PriorityQueue.join() (up to timeout_s), then cancels workers;
registers SIGTERM/SIGINT handlers on the running event loop
- api/main.py: lifespan now uses GracefulShutdown.drain() instead of
bare task_queue.stop(), ensuring in-flight tasks complete on shutdown
- pyproject.toml: typer>=0.12.0 dep + 'singularity' script entry point +
'cli' added to hatch wheel packages
- tests: test_cli.py (6 tests, patches _get/_post/_delete helpers),
test_graceful_shutdown.py (5 tests)
All 116 unit tests pass.
Co-Authored-By: Claude <noreply@anthropic.com>
…l-extension-1x7ppe
- core/log_buffer.py: LogBuffer — thread-safe deque ring buffer that doubles as a structlog processor; captures event_dict before renderer - core/logging_config.py: configure_logging() — wires structlog with merge_contextvars (propagates X-Request-ID from Fáze 8), add_log_level, TimeStamper, optional LogBuffer, and JSON or ConsoleRenderer - config/settings.py: log_format (default "console", "json" for prod) + log_buffer_size (default 500) - api/main.py: log_buffer singleton; configure_logging() called first in lifespan; GET /logs/recent?limit=N&level=L endpoint - tests/unit/test_logging_config.py: 6 tests (capture, maxlen, level filter, limit, json format, clear) All 122 unit tests pass. Co-Authored-By: Claude <noreply@anthropic.com>
…l-extension-1x7ppe
- core/task_events.py: new TaskEventBus (asyncio pub/sub, lazy Lock,
per-subscriber asyncio.Queue, subscribe/unsubscribe/publish/subscriber_count)
- core/task_queue.py: integrate TaskEventBus — publish state transitions
(RUNNING, COMPLETED, RETRYING, FAILED, DLQ) to all live subscribers
- api/main.py: GET /task/{task_id}/stream SSE endpoint — streams live
task lifecycle events; returns cached result immediately for finished tasks,
auto-unsubscribes on disconnect or 300 s timeout
- tests/unit/test_task_events.py: 5 offline tests for TaskEventBus
127 unit tests passing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
…l-extension-1x7ppe
…oints - core/cache.py: ResponseCache — SHA-256-keyed, async-safe OrderedDict with LRU eviction, per-entry TTL, hit/miss/eviction stats, hit_rate - config/settings.py: enable_cache, cache_ttl_s (300s), cache_max_size (1000) - api/main.py: cache lookup before core.run() in POST /task; store result after successful LLM call; GET /cache/stats + DELETE /cache endpoints - tests/unit/test_cache.py: 10 offline tests (TTL expiry, LRU eviction, invalidate, clear, deterministic keys, hit_rate) 137 unit tests passing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
…l-extension-1x7ppe
…aph node
- core/tracing.py: setup_tracing() (in-memory or OTLP gRPC), get_tracer(),
get_finished_spans(limit), clear_spans(); lazy InMemorySpanExporter in dev mode
- core/graph.py: each LangGraph node (plan, execute, critique, synthesize,
reflect) wrapped in a named OTel span with session_id / provider / risk_score
attributes; self.tracer initialised in __init__
- config/settings.py: enable_tracing (True) and otlp_endpoint ("") settings
- api/main.py: setup_tracing() called in lifespan; GET /traces endpoint returns
last N finished spans from in-memory exporter
- tests/unit/test_tracing.py: 8 offline tests (session-scoped provider init,
per-test exporter clear, span attributes, duration, limit, parent/child)
145 unit tests passing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
…l-extension-1x7ppe
Golden-dataset evaluation with a pass/fail gate for CI regression guarding. Register labelled cases, run the system-under-test with a scoring function, and get an aggregate report plus a boolean gate (mean score >= threshold) CI can fail on. Core module (core/eval_harness.py): - EvalHarness: add_case/add_cases/clear; run(predict_fn, scorer, threshold, pass_score) → EvalReport (total/passed/failed/mean_score/ pass_rate/gate_passed/per-case) - predict_fn may be sync or async; predictor exceptions become case failures (not crashes) - Built-in deterministic scorers: exact_match, contains, jaccard, numeric_close(tolerance) — offline - Metrics: cases, runs, gate_failures CI gate (.github/workflows/singularity-tests.yaml): - New workflow scoped to singularity/** — installs and runs the full offline unit+integration suite on PRs and pushes to main, failing the build on any regression (no API keys needed) API endpoints (api/main.py +1): - POST /evals/score — score pre-computed expected/actual pairs with a named scorer and return the pass/fail gate report Tests (tests/unit/test_eval_harness.py): - 19 offline tests, all passing - Scorers (exact/contains/jaccard/numeric_close), case mgmt, all-pass gate, below-threshold gate fail, partial-credit, async predict, predictor-exception-as-failure, per-case pass_score, invalid threshold, empty harness, report shape, metrics Total: 1413 tests passing (1394 + 19 new). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
Production Kubernetes manifests wiring the existing container (port
8001) into a scalable, self-healing deployment. The Dockerfile already
existed; this adds the orchestration layer.
deploy/k8s/:
- deployment.yaml — 2 replicas, resource requests/limits, and probes:
* liveness → GET /healthz (aggregated subsystem health, Fáze
57; 503 on required-component-down → restart)
* readiness → GET /health/ready (gate traffic until lifespan startup
finishes, Fáze 8)
Sets STATE_BACKEND=redis / REDIS_URL so state is shared across
replicas via the State Store (Fáze 62); API key from a Secret.
- service.yaml — ClusterIP :80 → named http port
- hpa.yaml — HPA CPU 70%, 2–10 replicas (note on scaling by SLO burn
rate via Prometheus Adapter)
- README.md — build/apply steps + probe/state rationale
Tests (tests/unit/test_deploy_manifests.py):
- 13 validation tests, all passing
- Manifests present + parse; Deployment kind; container port matches
the Dockerfile's 8001; liveness=/healthz, readiness=/health/ready;
PROBE PATHS EXIST AS REAL APP ROUTES; resource limits; redis env;
Service selector matches pod labels + named targetPort; HPA targets
the Deployment with sane replica bounds
Total: 1426 tests passing (1413 + 13 new).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
In-memory dense retriever: index documents by their embedding (via the pluggable EmbeddingProvider, Fáze 61) and retrieve top-k by cosine similarity. The semantic counterpart to the lexical BM25 Retriever (Fáze 37) — the two fuse via the Hybrid Reranker (Fáze 38) for hybrid search. Core module (core/vector_store.py): - VectorStore(embedder): add / add_many / remove / clear / size / dim - search(query, top_k, min_score) → cosine k-NN, score-sorted with stable doc_id tie-break, per-hit rank + metadata - embeds text on ingest; shares the API's embedding provider so a real embedder swaps in transparently; class swappable for an ANN index behind the same add/search surface at scale - Metrics: indexed, dim, searches, total_hits, avg_hits - Thread-safe via threading.Lock API endpoints (api/main.py +4): - POST /vectors/index — bulk index (embedded on ingest) - POST /vectors/search — semantic cosine k-NN - DELETE /vectors — clear index - GET /vectors/metrics — store metrics Tests (tests/unit/test_vector_store.py): - 18 offline tests, all passing - Indexing (add/many/overwrite/remove/clear/dim), search (empty/ invalid-top_k/semantic-ranking/top_k/sequential-ranks/min_score/ metadata), hit shape, injected embedder, metrics Total: 1444 tests passing (1426 + 18 new). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
) Add a typed async SDK over the Singularity HTTP API: - sdk/client.py: SingularityClient async httpx wrapper with typed methods for health, NLP, embeddings/vectors, RAG (lexical), and utility endpoints. Accepts an injectable transport so tests drive the in-process app offline. - sdk/__init__.py: package exports (SingularityClient, export_openapi). - sdk/export_openapi.py: CLI to dump the OpenAPI schema for codegen. - tests/unit/test_sdk_client.py: 13 offline tests via ASGITransport. Completes the v2.0 improvement plan (#1 embeddings through #10 SDK). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
Serve a self-contained HTML page at GET /ui (mirrors the /dashboard pattern in api/dashboard.py). The form posts same-origin to POST /task, so it works live with no CORS/CSP friction once the server runs. Shows the response, provider log, eval scores, and readable 4xx/5xx error details (invalid key, budget, low credit). Optional X-API-Key and Base URL fields for non-default setups. - api/task_ui.py: get_task_ui_html() - api/main.py: GET /ui route + endpoint doc header - tests/unit/test_task_ui.py: 2 offline tests (route 200/html + html shape) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
TaskQueue.submit() gains optional backoff_base/max_backoff/jitter params (None → existing RetryPolicy defaults, so production behaviour is unchanged). Retry unit tests pass a near-zero backoff instead of really sleeping through 2s+4s exponential delays, cutting test_retry.py from ~8s to ~0.2s. Optimization round 1 (test/CI speed). Full suite green: 1459 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
….py split PoC) Start the maintainability refactor of the api/main.py monolith (3743 lines): - api/state.py: shared runtime singletons (embedding_provider, vector_store), imported by both main and routers to avoid circular imports. - api/routers/vectors.py: the 4 /vectors endpoints as an APIRouter, registered via app.include_router. Routes and behaviour identical (verified live 200s). - api/main.py: 3743 -> 3702 lines; unused imports removed. - test_deploy_manifests: read route .path defensively (include_router adds a mount entry without .path). Optimization round 3a (maintainability). Full suite green: 1459 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
The build workflow triggered on any .github/** change, so adding the singularity test workflow dragged PR #45 into a computer-use-demo Docker build on 16-core amd64/arm64 runners the fork doesn't have — the jobs sat queued forever. Narrow the trigger to computer-use-demo/** so unrelated changes no longer kick off (and get stuck on) the image build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
…l-extension-1x7ppe
Spustit (launch) the new multimedia studio with full capabilities: - Image generation with customizable styles, emotions, and filters - Video generation with async processing and progress tracking - AI chat interface with character profile support - Gallery management for all generated content - Settings panel with API key and gems management - Dark theme UI with Tailwind CSS and shadcn components - Zustand store for client-side state management - Promptchan API integration for all multimedia operations - TypeScript support for type safety and better DX Features: - Multiple image styles (Cinematic, Anime, Hyperreal, etc.) - Video aspects (Portrait, Landscape, Square) - Quality levels (Ultra, Extreme, Max) - Customizable emotions and filters - Real-time video processing status - Persistent API key storage - Responsive design Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Continue the main.py split: move Embeddings, State, Streaming, Tenancy, Coalescer, and Evals endpoints into api/routers/*, with their singletons (state_store, stream_metrics, tenants, coalescer) in api/state.py. Snapshot stays in main.py (coupled to the feature-flags singleton). Routes and behaviour identical — all 194 OpenAPI paths preserved and every extracted endpoint verified live (200s, incl. /state/metrics route ordering and SSE streaming). api/main.py: 3702 -> 3479 lines. Optimization round 3b (maintainability). Full suite green: 1459 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
…l-extension-1x7ppe
Plots compass azimuths from a GPS point onto a satellite-image snapshot (Esri World Imagery, no API key needed), for aiming a directional/sector antenna. A best-effort obstruction-height estimate is derived from shadows detected in the image combined with the astronomically-true sun position — public satellite basemaps don't expose per-tile sensor viewing geometry, so this is a documented heuristic rather than a rigorous photogrammetric correction. Includes a .claude/skills/azimuth-satellite skill that drives the CLI conversationally and publishes the result as an Artifact. Claude-Session: https://claude.ai/code/session_01FcK727sEgu9Y93wNqeXisu Co-authored-by: Claude <noreply@anthropic.com>
Testing the skill's actual publish step (Artifact tool) surfaced a real gap: render_html() emitted a full <!doctype html><html><head><body> document, but Artifact expects a content fragment it wraps in its own skeleton — publishing the old output risked nested/broken markup. Rebuilds the output as a themed instrument-panel fragment: light/dark CSS custom properties per the three-state (system/light/dark) contract, IBM Plex Sans/Mono via Google Fonts (the one external host Artifact's CSP allows), and a proper readout panel (ray chips, sun reading, a confidence-pill shadow-estimate card) in place of the old plain-text legend lines. The SVG ray/wedge/compass/scale-bar drawing logic is unchanged. Claude-Session: https://claude.ai/code/session_01FcK727sEgu9Y93wNqeXisu Co-authored-by: Claude <noreply@anthropic.com>
Continue the api/main.py split: move the Fáze 34–54 endpoint block (57 routes)
into four themed APIRouters — api/routers/{retrieval,nlp,text_ops,stats}.py —
with their 21 singletons relocated to api/state.py. Singletons keep their
underscore-prefixed names so the extracted endpoint bodies (which alias e.g.
a local 'chunker' alongside the '_chunker' singleton) stay verbatim.
Routes and behaviour identical — all 194 OpenAPI paths preserved, full suite
green (1459 passed), and every extracted endpoint verified live (incl. the
/chunk custom-parameter branch). api/main.py: 3478 -> 2734 lines.
Optimization round 3c (maintainability).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NKdy2R9mbVdE7WNucVwYfv
* Add antenna-azimuth-webapp: real-time GPS azimuth mapper A Next.js companion to antenna-azimuth-mapper that adds the one thing a Claude/Cowork session structurally can't do: live browser GPS. Renders an interactive Esri satellite map (react-leaflet) with azimuth rays/wedges computed from navigator.geolocation.watchPosition, and an on-demand shadow-based obstruction-height estimate. The geodesic and solar-position math (lib/geometry.ts, lib/solar.ts) is a 1:1 TypeScript port of the CLI's geometry.py/solar.py, cross-checked against it. Shadow detection has no OpenCV available, so lib/shadow.ts reimplements the same threshold -> connected-components -> PCA-orientation heuristic by hand, and runs server-side (app/api/shadow-estimate/route.ts, Node runtime for `sharp`) since reading tile pixels client-side would hit canvas CORS tainting. Pinned to next@15.5.23 rather than the 14.2.x line the other quickstarts use: 14.2.15 has a critical disclosed vulnerability with no 14.2.x fix, and 15.5.23 (the maintained 15.x backport) clears it. The two remaining high-severity advisories are inside Next's own internally-vendored postcss/sharp copies (build tooling and the unused next/image optimizer, respectively) — not this app's own direct dependencies or usage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FcK727sEgu9Y93wNqeXisu * Document the live antenna-azimuth-webapp deployment Adds the public URL and a note that Vercel turns on Vercel Authentication for new projects, which has to be disabled for the deployment to be reachable by anyone not on the owning team. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FcK727sEgu9Y93wNqeXisu --------- Co-authored-by: Claude <noreply@anthropic.com>
* Optimize shadow detection and map re-rendering Profiled the shadow-estimate pipeline on a 512x512 crop first: morphology dominated at 56-62ms of a 75-122ms run, with connected-components second at up to 51ms. Both were naive implementations, so they were rewritten rather than tuned: - Morphology now runs as two separable 1D passes over reused ping-pong buffers. A 3x3 rectangular structuring element is separable, so this is arithmetically identical to the old 9-neighbour scan while touching a third of the memory and allocating nothing per pass. - Connected components writes flat pixel indices into a single Int32Array (which doubles as the BFS queue) and returns ranges into it, instead of building an [x, y] tuple array per blob — that was hundreds of thousands of small allocations on blob-heavy imagery. - Grayscale, the Otsu histogram and the threshold comparison are fused into two passes, using the same integer luma weights OpenCV applies. Measured on 512x512: 88.5ms -> 38.7ms (2.3x) on clustered blobs, 48.8ms -> 29.3ms (1.7x) on worst-case speckle. Verified byte-identical observations against the previous implementation across 10 image/size combinations. On the client, a high-accuracy geolocation watch reports a position about once a second and consumer GPS jitters well under a metre, so the map was re-projecting every ray continuously while standing still. Fixes reported closer than 0.5m now reuse the previous state object (via the already exported but until now unused haversineDistanceM), accuracy is rounded to the metre it is displayed at, and the per-ray geodesy is memoised. Also scopes the .leaflet-container override under .map-shell. Leaflet's own stylesheet ships in the dynamically imported map's chunk, which loads after the layout CSS holding the override; at equal specificity Leaflet's rule was winning, so the map background came from its #ddd default rather than the theme token. This is a correctness fix, not a size one — the duplicate leaflet.css import removed alongside it was already deduplicated by the bundler in production builds (36,872 -> 36,883 bytes of CSS, a wash). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FcK727sEgu9Y93wNqeXisu * Add drag-to-reposition for the origin marker The map had two ways to set the origin (live GPS, typing lat/lon) but no direct on-map interaction. The origin Marker is now draggable; a dragend handler reads its new position and feeds it through the same setManualOrigin path the coordinate inputs already use, so dragging is just another way to enter manual-override mode — "Use live GPS instead" reverts it exactly as before. RecenterOnFirstFix only fires once, so a drag doesn't get snapped back by the auto-recenter effect. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FcK727sEgu9Y93wNqeXisu --------- Co-authored-by: Claude <noreply@anthropic.com>
The shadow feature was built against a misread requirement. The original goal was to account for "the distortion caused by the angle the satellite image is taken from"; that became an obstruction-height estimate, which is not what it is for. It now serves the correction it was always meant to. What is and isn't distorted, stated plainly in code and docs because part of the expectation was a misconception: a satellite basemap and a vector basemap are both Web Mercator, north-up, on the same tile grid, and bearings come from lat/lon rather than pixels — so for two points on the ground the azimuth is identical on either layer, and nothing needs correcting. What is displaced is anything above the ground. Orthorectification places terrain correctly from a bare-earth model, but a mast or rooftop is not in that model: its top images along the slant to the satellite and lands h·cot(E_sat) away along A_sat+180°. Picking an antenna link's far end by its mast top — the normal way to pick it — therefore yields a bearing that is off: ~2° at 500 m for a 30 m object at 30° off-nadir, ~5° at 200 m, nothing at long range. lib/relief.ts recovers the missing sensor geometry from the image itself. A shadow and a lean are the same radial geometry with the sun swapped for the satellite, so a measured shadow plus the astronomically-computed sun gives the object's height, and that height plus the object's measured lean gives E_sat and A_sat. The user marks one upright object's base and apparent top; every elevated target then reports raw and corrected bearings and the delta. Verified by vitest (30 specs, new): the calibration round-trips a synthetic lean back to the satellite geometry that produced it, and the corrections reproduce the hand-worked 2°-at-500 m and 5°-at-200 m figures and decay to under 0.2° by 10 km. Also lands the rest of the agreed upgrade list: session state and shareable links (lib/persist.ts, encoded in the URL fragment so a position never reaches a server log), an SVG export mirroring the CLI's renderer, a bounded tile cache and adaptive zoom probing so a location Esri doesn't serve at z19 degrades instead of 502-ing, PWA manifest/icons/app-shell worker, retry and offline handling on the probe, and a facing indicator. Magnetic declination was deliberately left out — aiming here is visual, by landmark. Vitest pinned to 4.x: the 2.x line carries a critical advisory. The three remaining npm audit highs are the pre-existing ones inside Next's own vendored postcss/sharp, unchanged by this work. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FcK727sEgu9Y93wNqeXisu
…orrection-we7sk0
The live app has been stuck on a pre-#53 build because the only route to Vercel from an automated session is an MCP server that is approval-gated, and api.vercel.com is unreachable from the sandbox, so the CLI is not a fallback either. A GitHub Actions job runs from a host that can reach both. The workflow mirrors the shape the other workflows here use: push to main, a paths filter scoped to one quickstart, one job, working-directory set to that directory. Lint and the Vitest suite gate the deploy, and `vercel deploy --prebuilt` ships the build those steps ran against rather than rebuilding remotely. workflow_dispatch is deliberate: the paths filter means merging the workflow alone will not trigger it, so the first deploy is a manual run from the Actions tab. Deploying to the existing project through the CLI rather than linking the repository in the dashboard leaves Deployment Protection untouched, so the already-public URL stays public. The project and team IDs live in the workflow's env block; they are identifiers rather than credentials, and a guard on github.repository keeps forks from deploying into them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FcK727sEgu9Y93wNqeXisu
Replaces Esri World Imagery with the Mapy.cz REST API's aerial mapset, per request — both the on-screen Leaflet layer and the server-side tile fetch that feeds shadow detection and the SVG export. Python antenna-azimuth-mapper is untouched and keeps Esri. lib/mapycz.ts resolves the tile URL template, attribution, and zoom range from the aerial mapset's tiles.json at runtime rather than hardcoding a guessed literal URL: api.mapy.cz's own documentation was unreachable from this environment (confirmed egress-blocked, same as server.arcgisonline.com earlier), so the design leans on tiles.json being what the provider's docs describe it as being for, with defensive parsing and fallback defaults if the real response shape differs from what search-engine snippets of the docs describe. The client never sees the Mapy.cz URL or API key: AzimuthMap.tsx's TileLayer points at this app's own /api/basemap/[z]/[x]/[y] passthrough proxy (edge runtime, validates z/x/y before building the outbound request, wraps x the same way fetchSnapshot already does), and a separate /api/basemap-meta route supplies just the attribution text, since Mapy.cz's docs say that text can change and it shouldn't be hand-copied. Leaflet has no live setter for TileLayer's attribution/maxZoom, so AzimuthMap.tsx keys the layer on whether the real metadata has loaded to force a clean remount once it has, rather than silently no-opping on the prop change. lib/tiles.ts keeps its LRU cache and mosaic/crop math (both already provider-agnostic Web Mercator), swapping only the tile source and seeding deepestAvailableZoom's probe from the tileset's own reported max zoom. chooseZoomForSpan's ceiling moves from Esri's practical 19 to Mapy.cz's documented Czech Republic ceiling of 20. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FcK727sEgu9Y93wNqeXisu
…orrection-we7sk0
…l-extension-1x7ppe
A Next.js chat app that turns an elite VIP wedding coordinator persona prompt into a working demo: a parameter intake form (date, venue, budget, guest count, style, priorities) fills the system prompt, and a streaming chat interface renders Claude's plan with Markdown tables for the budget breakdown and day-of timeline. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L1zyDH8YM34SYKmdUPQojR
…rdination-z0vbj4
Packages a system-instructions generator as a skill with a JSON intermediate representation as the source of truth, so retargeting a profile to another platform or budget is a recompile rather than a repeated analysis. The compiler counts characters exactly and drops whole rules ascending by evidence x impact when over budget, never truncating sentences. The linter mechanizes the self-check that a model reviewing its own output tends to wave through: provenance leaks, undocumented rules, bare prohibitions, portability violations and budget overflow. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ea33xyGvXxKX19nnoWojzW
…-version-hulfzq Add masterprompt skill for generating System Instructions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Quickstart
Type of Change
Testing
Screenshots
Additional Notes