Spaces:
Running
Running
Commit History
-
Merge remote-tracking branch 'hf-test/main' into spike/portal-template Running
4d97c9f -
docs(sprint): payload logging entry + ISS-1..9 triage issue logβ
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>c23ae42 -
feat(logging): capture full run payload in session_logs on every runβ
Every run (App tab + Chat tab) now logs its complete payload to Supabase session_logs in meta.payload of the 'run started' events: - raw GO form inputs (controller._run_payload set in defined_go) - resolved company/use case/vertical/line/function/context - secret-redacted settings snapshot Attached in _create_run_loggers() (guaranteed even when a run dies before research) and again in run_research_streaming() once fully resolved. New sanitize_payload() in session_logger.py redacts key/secret/password/ token/credential/auth values and truncates >2000-char strings; 5 tests in tests/test_run_payload_logging.py. Chat-tab runs reset stale App-tab _run_source/_run_payload at initialization. Query guide in dev_notes/logging_channels.md (local docs, not tracked). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>63e8227 -
Merge remote-tracking branch 'hf-test/main' into developβ
# Conflicts: # sprint_2026_04.md6bb7908 -
Move liveboard/data/triage agents to personal scope (~/.claude/agents/)β
Matches how reviewer/tester/architect are installed, so all 6 subagents behave the same way (personal, available in every project) instead of three being project-scoped to this repo. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>27dd4fe -
Merge branch 'claude/youthful-lederberg-e31760' into developβ
# Conflicts: # chat_interface.pydc44f67 -
feat: post-load data-quality gate for zero/null measure columnsβ
Profile every MEASURE column after each Snowflake load (both dataset-first and legacy paths) via INFORMATION_SCHEMA + per-table aggregates. Fail the run loudly, naming the columns, when any measure is entirely zero/null (recurring Yodeck/Triumph zero-measure defect). Mostly-zero and empty-table cases surface as warnings in the completion panel and session_logs meta. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>fa79ccd -
Install project subagents (liveboard, data, triage) and skills (sprint-close, grade-runs)β
Adds an empty Issues section to the current sprint doc and fixes stale sprint pointers/dates in CLAUDE.md (dropped dangling sprint_2026_01.md refs, added missing sprint_2026_03.md, updated Last Updated date). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>9e15d34
-
feat(mcp): KEY_VAR ENV: indirection + scheme-less/port-tolerant host matchingβ
Folds in what the Space-side hotfix (0b84b8a) had that the TS_ENV table didn't: techpartners support and robust URL parsing. KEY_VAR may now be ENV:<NAME> to dereference a secret that already exists under its own name (the techpartners key lives only as a Space secret), and ts_url may omit the scheme or carry a port. Unknown clusters still fail loud with the known-environment list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>e27ea87
-
refactor(mcp): resolve trusted-auth keys from the shared TS_ENV_<n> tableβ
The MCP server had its own two-key mapping ('sebe' in url -> SEBE_SE_TA_KEY, else SE_PRIMARY_TA_KEY) β any third instance (e.g. the new pmm env) silently got the secloud key and failed auth. _resolve_ts_target now looks the build's ts_url host up in the same numbered TS_ENV_<n>_LABEL/URL/KEY_VAR table the app dropdown reads: one environment scheme for both entry points; unknown instances fail loud listing the known environments. TS_ENV_URL_DEFAULT keeps its no-URL fallback role. 4 unit tests. DEPLOY NOTE: before this reaches the MCP Space, add the TS_ENV_<n>_* secrets there (it currently has only the legacy key pair); legacy SEBE_SE_TA_KEY / SE_PRIMARY_TA_KEY become unused after that. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>5ce76f0 -
feat(liveboard): 4 KPIs per liveboard (was 2)β
NUM_KPIS 2 -> 4: AI question prompt asks for 4 KPI questions, and the no-AI fallback now generates NUM_KPIS KPIs cycling measures across weekly/monthly/quarterly/yearly grains instead of two hardcoded ones. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>ac4c74f
-
test(mcp): retry brief fetch on network errors + --brief-file escape hatchβ
Venue WiFi/proxy flaps were killing the burn-in with SSL errors before the first build even fired. Fetch now retries 5x with backoff and fails with a clear message; --brief-file runs the same brief from a local snapshot when Supabase is unreachable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>a423344
-
test(mcp): repeatability burn-in β fire the latest brief N times, report pass rateβ
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>9a955a5 -
docs(sprint): record CHECKOUT sanitizer bug fix + MCP deployβ
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>e7c6ebb -
fix(ddl): CHECK sanitizer no longer deletes columns named CHECKOUT_*β
The chunk-level CHECK-constraint detector used prefix matching (upper.startswith("CHECK")), so any column whose name begins with CHECK β CHECKOUT_INITIATED, CHECKOUT_ATTEMPTS, CHECKOUT_CONVERSION_RATE, CHECKOUT_ABANDONMENT_RATE β was silently dropped from CREATE TABLE while the generated dataset still carried its values. The INSERT then failed on columns the table didn't have. Root cause of the "random" schema/data-mismatch build failures (Cloudscape, Atlas Freight partial, Tixr run 02ec3e7baba6) β it fired whenever the LLM blueprint author named a column CHECKOUT_*, which checkout-funnel briefs (Tixr) invite. Detector now requires the CHECK keyword followed by a parenthesized expression, and the CONSTRAINT branch uses a word-boundary match so an FK named CHECKOUT_FK survives. Inline CHECK removal already used \bCHECK\b and was safe. 4 regression tests added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>e32850b -
docs: build-time deep dive notes (root cause, verified results, next bottleneck)β
Captures the 2026-08-10 research: scoped-connection root cause + evidence, what shipped and where (hf-test + MCP Space df44611, rollback 3d735c7), verified timings (e2e 655s, MCP 9.2min), API research findings, and the model-semantics phase (~5 min of sequential LLM batches) as the documented next bottleneck with optimization options β deferred, no code changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>2d09335 -
fix(e2e): remove remaining hardcoded DEMOBUILD refs in test harnessesβ
Follow-up to the rotating-demo-database change: the data-grading fallback, geo-scope check, and settings_test row check still pointed at DEMOBUILD. All now derive the database from the model TML (via check_snowflake_schema's new database field) or get_demo_database(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>a22a600 -
fix(e2e): resolve Snowflake database from model TML in schema checkβ
check_snowflake_schema hardcoded DEMOBUILD; demos now live in the rotating <base>_<YYYY_MM> database, so the check reported "Object does not exist" on runs that succeeded. The database now comes from the same model-TML resolution that supplies the schema (with get_demo_database() as the session-log path's source), and the check refuses to guess when neither is available. Verified against live run ed0dfa99: DEMOBUILD_2026_08.TST_TRE_08102144_P6D_sch, 10,037 rows / 7 tables found. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>80facca -
perf(deploy): scope TS connection to demo DB + monthly-rotating database (250s -> <1s table imports)β
Root cause of the ~250s-per-call table TML imports (found 2026-08-10, corrects the July "server-side, not reducible" conclusion): ThoughtSpot table import scans all external metadata VISIBLE TO THE CONNECTION. Our connections had no database property, so every import scanned ~507 Snowflake DBs. Measured on the same instance with the same trivial table: unscoped 246s, scoped to DEMOBUILD (1,003 schemas of demo debris) 33s, scoped to a fresh tiny DB 0.7s. Prod secloud showed identical ~250s unscoped β never instance-specific. - thoughtspot_deployer: create_connection_tml/create_connection_with_reconcile now REQUIRE a database and emit a `database` property in the connection TML. - snowflake_auth: get_demo_database() -> <SNOWFLAKE_DATABASE>_<YYYY_MM> (memoized per process); ensure_demo_database() creates it on connect. Session params + cdw_connector + env injection (supabase_client) all use the rotated DB so write and deploy paths agree. Admin setting stays the base name. - chat_β¦21ee86d -
Merge remote-tracking branch 'origin/develop_dg' into develop_dg
baf44f7
-
merge: MCP hardening (share-with-operator + relative-date tolerance) into trunkβ
Pulled origin/deploy/mcp's real fixes back onto trunk instead of letting them live only on the deploy shim β deploy/mcp had drifted from a pure frontmatter shim into carrying unique engine/validator/blueprint fixes: - mcp_server: every MCP build shares the created model + liveboard with the operator (MCP_SHARE_WITH, default mike.boone@thoughtspot.com) so demos are visible no matter who ran them. - engine + validator: _safe_date() tolerates a non-ISO insight window bound (e.g. an LLM-invented relative token) instead of crashing dataset generation. - blueprint.from_dict: also drop non-numeric seasonality.monthly values. Kept trunk's README (gradio/app.py) β the merge initially took deploy/mcp's docker/MCP frontmatter, which would have broken the app Space deploy; restored before this commit finalized. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>2f533a0 -
fix(mcp+pipeline): share demos with operator + harden data-quality consumersβ
- mcp_server: every MCP build shares the created model + liveboard with the operator (MCP_SHARE_WITH, default mike.boone@thoughtspot.com) so demos are visible no matter who ran them. Owner unchanged β the build still authenticates AS owner_email, so the caller passed in owns the objects. - blueprint.from_dict: drop non-numeric seasonal `monthly` params too (line 261) β the merged author can emit a dict there like it did for params. - engine + validator: _safe_date() tolerates a non-ISO insight window bound (e.g. the LLM token 'relative_minus_8_months') instead of crashing on date.fromisoformat β the second merge bug that failed Cloudscape. Root theme: the merged blueprint author emits richer output than the parser/engine expected; harden the consumers to tolerate it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>2eaf446 -
fix(mcp+pipeline): share demos with operator + harden data-quality consumersβ
- mcp_server: every MCP build shares the created model + liveboard with the operator (MCP_SHARE_WITH, default mike.boone@thoughtspot.com) so demos are visible no matter who ran them. Owner unchanged β the build still authenticates AS owner_email, so the caller passed in owns the objects. - blueprint.from_dict: drop non-numeric seasonal `monthly` params too (line 261) β the merged author can emit a dict there like it did for params. - engine + validator: _safe_date() tolerates a non-ISO insight window bound (e.g. the LLM token 'relative_minus_8_months') instead of crashing on date.fromisoformat β the second merge bug that failed Cloudscape. Root theme: the merged blueprint author emits richer output than the parser/engine expected; harden the consumers to tolerate it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>3d735c7
-
merge: origin/develop_dg (blueprint params fix) into local guard work
6435820 -
guard: catch CREATE/INSERT column mismatches before and during Snowflake loadβ
The Gap e2e failure: FACT_CAMPAIGN_PERFORMANCE's row loader referenced CHECKOUT_INITIATED, a column CREATE TABLE never declared -> Snowflake "invalid identifier", ~10 minutes into the run, no indication of which table/column. Root mechanism wasn't pinned with certainty (DDL and the loader both derive columns from the same DatasetBundle object, so they can't diverge in the code as read) β these guards catch the failure class immediately and name the exact column, regardless of cause. - validator.py: _check_schema_consistency() β in-memory, at validate_bundle() time. Flags (a) two column names that collide after SQL-identifier normalization (duplicate column in CREATE TABLE), and (b) a generated row carrying a key no declared column covers (INSERT would reference an identifier CREATE TABLE never defined). Runs before any DDL is derived or Snowflake is touched. - dataset_writer.py: _verify_table_columns() β right before each table's INSERT, DESCRIBE TABLE and diff against thβ¦2cc63cd -
fix(blueprint): from_dict drops non-numeric measure params (fixes float(dict) DDL crash)β
The merged blueprint author occasionally emits a measure whose `params` value is a dict; DemoBlueprint.from_dict did `float(v)` on every value β TypeError β crashed every build at DDL generation (shared pipeline; hit app + MCP). params is only ever numeric distribution config (mean/sigma, low/high β per the author examples + engine), so keep numeric values and drop anything non-numeric; the engine already backfills sane defaults for missing params. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>8d8e6bc -
fix(blueprint): from_dict drops non-numeric measure params (fixes float(dict) DDL crash)β
The merged blueprint author occasionally emits a measure whose `params` value is a dict; DemoBlueprint.from_dict did `float(v)` on every value β TypeError β crashed every build at DDL generation (shared pipeline; hit app + MCP). params is only ever numeric distribution config (mean/sigma, low/high β per the author examples + engine), so keep numeric values and drop anything non-numeric; the engine already backfills sane defaults for missing params. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>1907a8d -
chore: check in async-TML-import investigation (repro/perf harness + handoff docs)β
- tests/ts_import_sync_vs_async.py, ts_slow_import_repro.py, ts_table_perf.py: repro/perf harness behind the async TML import fix - tests/perf_results/: captured sync-vs-async timings - docs/async_tml_import_handoff.md, sre_slow_tml_import.md: investigation + SRE handoff notes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>72b93af -
deploy(mcp): MCP-server frontmatter (docker/app_port) over unified trunk
5b89665 -
merge: liveboard geo-map (a15663c) into integration
a060e36 -
liveboard: auto-build a geo map from model lat/lon (Path B)β
When the model has latitude/longitude columns and the board has no geo viz, synthesize a GEO_BUBBLE so the layout places it full-width under the KPIs ("geo on almost anything, when the data supports it"). golden_layout.py: - detect_geo_columns(): find lat/lon (word-boundary match, so "Platform" isn't a false hit) + a geo attribute + a bubble measure from model columns - _build_geo_viz(): synthesize a GEO_BUBBLE answer (clone a sibling's model ref; search_query [Lat] [Lon] sum [Measure] [GeoDim]; minimal mapview client_state that auto-fits) - _maybe_build_geo_viz(): only fires when lat/lon exist and no geo viz present - build_dashboard_plan gains model_columns; apply_golden_two_tab_layout adds the synthesized viz so it lands in the Geographic View group liveboard_creator.py: _fetch_model_columns() resolves the board's model (export w/ fqn -> model TML) and passes its columns to the plan; best-effort (returns [] on failure so liveboard creation never breaks). Verified live: stβ¦a15663c -
merge: unify MCP (async TML import + concurrency + mcp_server) into integrationβ
- keep app README frontmatter (gradio) on trunk; MCP docker frontmatter stays on hf-mcp-deploy - build_demo.py: blueprint LLM cap/timeout reconciled to 16384 / 300s (superset of both sides) - adds mcp_server.py, tests/e2e_mcp.py, MCP deps; async-only TML import pathc3e3f8b -
merge: liveboard-quality into integration/mcp-lb
ee1ce2a -
liveboard: balance group chunks + nicer continuation namesβ
Avoid a lone chart landing in its own numbered group ("Performance Drivers 2" with 1 tile): _balanced_chunks folds a trailing orphan into the prior chunk (4 -> 2+2). Continuation groups now read "Additional Drivers" instead of "... 2". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>26a1772 -
liveboard quality: fix flat-72 defects + Tixr-style layoutβ
Reshape the MCP + enhance golden layout to fix the 5 structural grader defects and adopt the SE's Tixr Executive Analytics template. golden_layout.py: - note tile: full company name (was single-letter "N"/"L"), deduped; dropped by default (keep_overview_note flag) so the KPI row leads - colors: color ADVANCED_* charts by dimension via custom_chart_config "slice-with-color" (real per-category colors + legends; was all-blue, since seriesColors/chartLevelColorConfig don't drive the new engine) - per-tile tile_brand_color cycling + tile_kpi_color on KPIs - force CHART_MODE where a chart renders as a data grid - clean raw display names (Dim / Usd / Pct / doubled words) - trend detection: time-only series -> dedicated Performance Trends - geo: GEO_* -> its own full-width row right under the KPI row - asymmetric group sizing (5/4/3, 7/5) + Tixr styling (kpi L font, hidden group descriptions) liveboard_creator.py / thoughtspot_deployer.py: thread logo_url/url into build_dashboard_plaβ¦f7f2b52 -
chore: README, e2e grader, share-permission fix, sprint + quality resultsβ
- README: chat_interface.py is the full app (App tab primary), app.py entry point; updated structure/config - tests/e2e_quality.py: date-aware grader, COMPLETENESS rubric rewrite, --ts-environment override, hard-fail on missing dropdown label - thoughtspot_deployer.py: share-permission 400 fix β notify_on_share=false + empty message to satisfy the GraphQL $message contract - sprint_2026_04.md: progress updates - tests/quality_results: run artifacts incl. 2026-07-28 82.9/B run Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>fcde472
-
fix(mcp): log/persist payload once per ACCEPTED build, not on busy-retriesβ
log_mcp_payload + the full-brief stdout dump ran in build_demo_from_brief BEFORE the slot/busy check, so a client re-polling a queued build wrote a duplicate durable row every retry (a --limit 4 run logged Vantage ~12x while it waited for a slot; a batch of 6 would spray 100+). Move both into _run_build, which only runs for builds that actually acquired a slot β so exactly one durable row per accepted build, tagged with its run_id. The lightweight CALL line still records every attempt in the ephemeral log. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>c9b5cac -
fix(mcp): durable payload logging with NO new table β reuse demo_historyβ
The mcp_payloads approach was blocked on a table the anon key can't create (no DDL access), so durable capture never actually turned on and real runs kept rolling off (e.g. iPipeline run c93bc9f040f0 β gone after an HF container restart). Pivot log_mcp_payload() to the EXISTING demo_history table (anon key already writes it): rows tagged status='mcp_intake' with a sentinel user_email (so they never pollute a real user's history) and the full payload (brief + company/use_case/url/ ts_url/owner/run_id) in results. Verified end-to-end (insert + readback). Now every intake persists on deploy with zero setup β query: select * from demo_history where status='mcp_intake' order by created_at desc; Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>a0a8307 -
feat(mcp): durably persist full intake payload to Supabase (real logging)β
Ephemeral stdout logging (~1 day) is not a system of record β it's why Jack's CopperPoint/Surescripts briefs were lost. Add durable capture: log_mcp_payload() in supabase_client inserts the full brief + metadata into an `mcp_payloads` table, called from build_demo_from_brief on receipt (before validation, so even rejected payloads are kept). Best-effort and non-raising β a persist failure never blocks or breaks a build (verified: with the table absent it returns False gracefully, PGRST205). Uses the existing anon-key client pattern. Requires a one-time table (anon key can't run DDL): create table if not exists public.mcp_payloads ( id bigint generated always as identity primary key, created_at timestamptz not null default now(), run_id text, company_name text, use_case text, company_url text, ts_url text, owner_email text, brief text, brief_len integer ); SQL-created tables have RLS off by default, so the anon key can write. Co-Authored-By: Claude Opus 4.8 <noreplβ¦4b20a09 -
feat(mcp): log the FULL brief (research payload) on receipt, not a 200-char previewβ
We only logged brief_preview=brief[:200], so real payloads couldn't be recovered or replayed β the full CopperPoint (~3.9k) and Surescripts (~4k) briefs Jack/others sent are effectively lost. Now build_demo_from_brief dumps the COMPLETE brief to the log stream on receipt, delimited PAYLOAD_BRIEF_BEGIN/END (so a multi-line brief reassembles despite newline-splitting) and BEFORE validation (so even rejected payloads are captured). Same retained log stream we already read via the HF logs API. Caveat: HF logs retain ~1 day; durable Supabase capture is the follow-up (needs a table β pending boone's OK on the schema). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>1655c3f
-
fix(test): make e2e_mcp resilient to transient network blipsβ
A single httpx.ConnectTimeout on one status() poll (hf.space dropped a TLS connect ~3 min into a run) crashed the whole harness. Over a ~1h run doing hundreds of network polls, transient blips are expected. call() now retries (4x, 3s backoff) around any transient exception, and the fire/poll sites in run() tolerate an exhausted call β a failed poll skips the cycle (builds keep running server-side; results persist in recent_builds) instead of ending the run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>095d53e -
test(mcp): e2e_mcp harness β drive N live MCP builds end-to-end and grade eachβ
Replaces the local-pipeline e2e for the MCP path. Fires build_demo_from_brief for each of 8 rich briefs against the live server, respects the server's bounded concurrency (fires until 'busy', backs off, refills as slots free), polls status(), collects each result by run_id from recent_builds, and grades PASS/FAIL (success + liveboard produced + no 13122). Two briefs deliberately exercise the connectivity guard: "Summit Mutual" (two subject areas -> keep primary star, set aside the other) and "GridLink" (time-series heavy -> orphan date dim dropped). Writes a JSON report to tests/quality_results/ and exits non-zero if any build fails. Run: python tests/e2e_mcp.py [--limit N]. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>8abca30 -
feat(mcp): per-run recent_builds store in status() for multi-build result collectionβ
status() kept only a single _last_build slot; with bounded concurrency (cap 3) a client that fires several builds can't reliably collect each result β later completions overwrite the slot before it's read. Add a compact, capped (_RECENT_BUILDS_CAP=50) OrderedDict of finished builds keyed by run_id, recorded in _run_build's result() and exposed as status().recent_builds (newest last, no ddl to keep the payload small). _last_build kept for back-compat. In-memory; resets on restart (durable Supabase logging still a follow-up). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>dcc1160 -
fix(deploy): connectivity guard β scope model to one connected join graph (fixes 13122)β
A ThoughtSpot model must form a single connected join graph; a disconnected graph is rejected on import with schema-validation error 13122. Two real builds hit this: one with two disjoint fact stars (underwriting + HR, no shared dim), one with an orphan date dimension (DIM_TIME_PERIOD, no fact FK). The deployer's only 13122 handling was to re-import without model-table joins, which can't fix a connectivity problem β so both failed twice and gave up (no model -> no liveboard). Add _select_model_component(): compute connected components of the FK graph and scope the MODEL to the primary component (the star with the most facts). Orphan tables with no joins are dropped; secondary fact stars are set aside and surfaced in results['set_aside_components'] / warnings (not silently lost). All tables are still created in Snowflake + as TS logical tables (Batch 1/2 unchanged) β only the model composition is scoped. Applied to both the primary model build and the no-joins retry. Shared deploy_allβ¦d0b4398
-
fix(mcp): status tracks real deploy progress β drop UI heartbeat bannerβ
The app yields a 2s spinner banner ("...Creating connection & tables...") while deploy_all runs in its worker thread. It reached _set_progress and (a) pinned phase to "creating connection" (banner contains "connection"), (b) clobbered the real per-line detail from on_progress every 2s, and (c) its 200-char truncation dropped the banner's own "Recent:" tail β so status froze on "creating connection" for the whole build. Route every yield/line through _apply_progress(), which drops the banner so the on_progress stream (Creating connection, [async] import, Model created, synonyms, liveboard) drives phase/detail instead. Also two phase-mapping fixes exposed by the replay: - check [async] before the retry bucket: async import lines say "polling" as normal operation, so a healthy import was mislabeled "waiting on ThoughtSpot (retrying)". - check "creating tables" before "loading data" so TS table-create isn't shown as a Snowflake data load. Verified with a replay test against the reβ¦620405d
-
feat(mcp): concurrent builds (bounded) β remove single-flight lockβ
The build pipeline is already concurrency-safe (app/QA have run concurrent builds for months); the MCP's single-flight _build_lock was an unnecessary v1 guard. - Replace _build_lock with a BoundedSemaphore (MCP_MAX_CONCURRENT_BUILDS, default 3): builds run in parallel; only a call past the cap gets "busy". - Track multiple builds: _current_build -> _active_builds{run_id: info}; _set_progress targets a run_id; status returns active_builds[] + active_count + capacity (plus a back-compat current_build when exactly one runs). - Remove the vestigial os.environ["DEMOPREP_DEV_USER_EMAIL"]=owner write β owner is per-instance, so concurrent builds don't race on it (audited). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>296fba8 -
feat(mcp): real-time status via deploy progress bridge + return DDL earlyβ
- Progress bridge: _run_thoughtspot_deployment gains an optional on_progress hook chained onto its internal progress_callback; the MCP passes one that updates _set_progress, so status.current_build.detail/phase track LIVE (importing tables -> creating model -> creating liveboard) instead of freezing between the generator's sparse yields. [async] lines map to an "importing tables" phase. App/test callers pass no on_progress (default None) β unaffected. - DDL returned early: _run_build stashes ddl_text in _current_build the moment run_ddl_creation returns (~5 min in) and includes it in last_build, so a caller polling status() gets the schema well before the ~15-min TS deploy finishes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>cbd0efa -
refactor(deploy): make TML import async-only (remove flag + sync branches)β
Both table phases now call import_tmls_async directly β no TS_TML_IMPORT_MODE flag, no sync path selection. The old sync closures (_import_tml_chunk, _import_tmls_chunked, _resolve_/_verify_*_after_timeout) are now unreferenced dead code; they'll be excised in a follow-up once one async build is validated end-to-end (kept in git history as the fallback). Shared deploy_all β app + MCP. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>c19146f -
feat(deploy): async TML import mode (TS_TML_IMPORT_MODE) β eliminates gateway 504sβ
Adds ThoughtSpotDeployer.import_tmls_async: submit to the async TML import API, poll the light status endpoint to a terminal state, then resolve name->guid from the connection β SAME return shape as the sync path, so deploy_all's downstream processing is unchanged. Both table-create (Batch 1) and joins (Batch 2) branch on TS_TML_IMPORT_MODE (default "sync"). Reliability/observability fix, not a speed fix (server-side ~4-5 min/phase unchanged). Shared code: the app and MCP both call deploy_all, so both get it. Sync path intact for instant rollback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>aac7ab6
-
feat(mcp): granular build status (detail + finer phases); use_case optionalβ
- status: capture the progress the Snowflake-load and TS-deploy generators already yield (previously discarded) β set a finer phase via keyword mapping (creating tables / loading data / 504-retry / creating model / model created / creating liveboard / liveboard TML phase) and expose the latest raw line as current_build.detail (_set_progress / _progress_text / _phase_from_text). - use_case is now optional: only brief + company_name are required. When omitted, the demo is authored purely from the brief and labeled "Custom Analytics". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>daaa123