Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Bedbricks
Manage your Databricks jobs from your phone. πŸ›

A mobile-first Google Apps Script web app for Databricks β€”
dashboard, run history, AI-powered diagnostics, and notebook fixes,
without ever opening Databricks.

No server. No infra. Just a Web App URL you open on your phone.

Version Platform License

Bedbricks β€” failed job β†’ AI diagnosis β†’ fix β†’ repair, from your phone


Screenshots

πŸ“Έ Full screenshot gallery β†’ docs/SCREENSHOTS.md β€” dashboard, AI diagnosis & fix, connection setup, and the optional AI hint.


Guides

πŸ“„ Setup & user guide β€” English Β· EspaΓ±ol β€” how to access, install on iOS/Android, and a full feature tour. Share it with your team.


Features

v1 β€” Core control

  1. Job dashboard with status dots (last 5 runs per job)
  2. Global run history (Runs tab)
  3. Job detail: expandable task list (SUCCESS) or AI diagnosis + Repair (FAILED)
  4. Trigger any job (β–Ά Run Now) and repair failed tasks without opening Databricks
  5. Think Fix β€” an LLM inspects real table schemas from Hive Metastore via the Clusters Execute Command API and proposes a concrete find β†’ replace patch for your notebook. Optional hint field: before diagnosing you can type a free-text suggestion (e.g. "the table was renamed to X") that the LLM weighs heavily β€” leave it empty for a fully automatic diagnosis
  6. Multi-task failures, sub-jobs, and chained repairs (latest_repair_id auto-detected)

v2 β€” Dashboard

  1. Pinned job widget β€” fixed card above the job list: status badge + progress bar + elapsed time. Tap β†’ detail view.
  2. Auto-refresh 30s β€” activates when a job is RUNNING. Countdown in the header. Stops automatically.
  3. Quick filters β€” All Β· Failed Β· Running chips. Client-side, combined with text search.
  4. Favorites β˜… β€” toggle on any job row. Favorites float to the top. Persisted cross-device via UserProperties.
  5. Cancel run β€” βœ• Cancel button when a job is RUNNING. Confirm dialog + auto-refresh.
  6. Run history β€” last 10 runs per job in the detail view (date, duration, status).
  7. Fixes tab β€” log of all applied patches: notebook path, timestamp, find β†’ replace preview. Max 50, FIFO.
  8. Dark/light mode β€” SVG toggle, persisted in localStorage.

v3 β€” Multi-user

  1. Per-user config β€” host, token, LLM endpoint, and pinned job stored per-user in UserProperties. Credentials never shared via ScriptProperties.
  2. Setup wizard β€” first-run full-screen modal. App blocks until host + token are configured.
  3. Pin Job (πŸ“Œ) β€” pin any job as the featured widget. Toggle from the job row or the Settings tab.
  4. Settings tab (βš™) β€” edit connection, choose LLM endpoint from a live dropdown, manage pinned job.
  5. Configurable LLM endpoint β€” populated dynamically from /api/2.0/serving-endpoints (READY only).
  6. Marquee for long names β€” job names that overflow scroll horizontally.

v4 β€” i18n + New features

  1. Language toggle EN/ES β€” full bilingual UI in Settings tab, persisted per-user in UserProperties. LLM prompts in pensarFix also switch language.
  2. Diff visual before applying fix β€” unified diff (LCS line-by-line) replaces the two separate <pre> blocks. Lines removed in red (βˆ’), lines added in green (+).
  3. Tab Clusters β€” live list of all workspace clusters with state badges (Running/Terminated/Starting/Error). Start and Terminate buttons without leaving the app.
  4. Scheduler one-shot β€” schedule any job to run at a specific date/time. Uses GAS time-based triggers (ScriptApp.newTrigger). Pending scheduled runs visible and cancellable from Settings.
  5. Performance chart β€” SVG bar chart of the last 10 run durations (colored by state) with fail-rate badge, shown in every job detail view.
  6. Task DAG β€” visual dependency graph rendered as SVG in the job detail view. Nodes colored by state (green/red/blue), cubic BΓ©zier edges, horizontal column layout by depth.

v6 β€” Power features

  1. Drill into failed sub-jobs β€” when a failed task is a run_job (sub-job), the detail view shows an "open child job β†’" button that navigates straight to the child run (where the real notebook failure + Think Fix live). Backend resolves subjob_id + subjob_name.
  2. Repair continues the DAG β€” repairRun now sends a flat rerun_tasks array (fixed a [["task"]] nesting bug β†’ 400) plus rerun_dependent_tasks: true, so repairing a failed task also re-runs the downstream tasks it had blocked. latest_repair_id persisted per-run in UserProperties (the API never returns repair_history).
  3. Parameter editor β€” βš™ button in the detail header opens a modal of editable key-value rows (pre-filled from the job's default base_parameters). Triggers the job with notebook_params override via triggerJob(jobId, params).
  4. Global search β€” magnifier in the header opens a full-screen overlay that searches job names, job IDs, run IDs and run job names across loaded data. Tap a result to jump to its detail.
  5. Run comparison β€” collapsible picker of two runs of the same job in the detail view. Side-by-side table of per-task duration with a signed delta in seconds (green faster / red slower). Backend adds per-task duration_ms to run_history_with_tasks.

v7 β€” Multi-workspace

  1. Multiple workspaces β€” save several Databricks workspaces (name + host + token) and switch the active one from Settings β†’ Workspaces. The whole app (jobs, runs, clusters, fixes) reloads for the active workspace. Each workspace keeps its own token, LLM endpoint and pinned job; language is global. Config migrates automatically from the old single-workspace shape. All per-user in UserProperties β€” no shared credentials, no backend.
  2. Universal icon UI + spinners β€” loading states render an animated spinner; primary actions are compact icons (β–Ά run, βœ“ apply, βœ• cancel, β€Ή back, πŸ”§ Repair, πŸ’‘ Think Fix) with localized tooltips (data-i18n-title). Fully bilingual EN/ES including the AI diagnosis and the Task Code Viewer (previously hard-coded). Expandable Fixes entries with full red/green diffs.

v8 β€” Large jobs & sentinel tasks

  1. Paginated run detail β€” runs/get returns at most 100 tasks per page. Every run read (getJobDetail, _fetchRunsWithTasks, getOrchestratorStatus) now follows next_page_token, so jobs with hundreds of tasks show every failed task, the full DAG and a real progress bar.
  2. Director jobs (tasks that are themselves jobs) β€” a job whose tasks are all run_job_task is handled as a first-class case. The sentinel is detected structurally (the DAG leaf running run_if=ALL_DONE) instead of by name, so a rename cannot hide it. Drilling into a failed sub-job opens the run that parent actually triggered, not the child job's latest run. Repair warns before it re-runs a whole child job. The progress bar labels sub-jobs as sub-jobs, and the parameter editor reads job-level parameters, not only task base_parameters.
  3. Sentinel-aware diagnosis β€” a sentinel task (one that exists only to force the run to FAILED when something inside it failed, because run_if=ALL_DONE would otherwise close the run green) is detected, pushed to the bottom of the failed-task list and rendered with a sentinel β€” not the cause chip: no AI diagnosis, no Think Fix, excluded from Repair. The real failure becomes the primary card. If the sentinel is the only failure, it is treated as a normal failure and diagnosed.

Architecture

Phone
  β”‚
  β–Ό
[Web App URL]
  β”‚  google.script.run.*()
  β–Ό
[codigo.gs β€” Apps Script V8]
  β”‚
  β”œβ”€ getDashboard()
  β”‚    └─ /api/2.1/jobs/list + /api/2.1/jobs/runs/list (last 5 runs per job)
  β”‚
  β”œβ”€ getOrchestratorStatus()
  β”‚    └─ /api/2.1/jobs/runs/list?job_id={pinned_job_id}&limit=1
  β”‚    └─ (if RUNNING) /api/2.1/jobs/runs/get β†’ count SUCCESS tasks
  β”‚
  β”œβ”€ getJobDetail(jobId)
  β”‚    └─ /api/2.1/jobs/runs/list?limit=10 β†’ run_history
  β”‚    └─ _getRunDetailPaged() β†’ /api/2.2/jobs/runs/get (follows next_page_token, 100 tasks/page)
  β”‚    └─ (if FAILED) tasks + thinkFix (sentinel tasks skipped)
  β”‚
  β”œβ”€ triggerJob(jobId)          └─ POST /api/2.1/jobs/run-now
  β”œβ”€ repairRun(runId, taskKeys) └─ POST /api/2.1/jobs/runs/repair
  β”œβ”€ cancelRun(runId)           └─ POST /api/2.1/jobs/runs/cancel
  β”‚
  β”œβ”€ pensarFix(runId, taskKey, lang, userHint)
  β”‚    └─ (optional userHint injected into both LLM passes via _buildHintBlock)
  β”‚    └─ LLM decides which DESCRIBE TABLE to run
  β”‚    └─ /api/1.2/contexts/create + /api/1.2/commands/execute (Hive Metastore)
  β”‚    └─ LLM proposes find β†’ replace with real schema
  β”‚
  β”œβ”€ aplicarFix(path, find, replace)
  β”‚    └─ /api/2.0/workspace/export β†’ replace β†’ /api/2.0/workspace/import
  β”‚
  β”œβ”€ getFavorites() / toggleFavorite(jobId)    β€” UserProperties
  β”œβ”€ getFixHistory()                            β€” UserProperties
  β”œβ”€ getConfig() / saveConfig(config)          β€” UserProperties 'bedbricks_config'
  β”œβ”€ getServingEndpoints()                      β€” /api/2.0/serving-endpoints (READY only)
  β”œβ”€ setPinnedJob(jobId, jobName) / clearPinnedJob()
  β”‚
  └─ doGet() β†’ Index.html

[Index.html β€” mobile UI]
  β”œβ”€ Header: logo + dark/light toggle + ↻ auto-refresh countdown
  β”œβ”€ Tab Jobs: pinned job widget + search + filters + job list with β˜… πŸ“Œ
  β”œβ”€ Tab Runs: global run history
  β”œβ”€ Tab Fixes: applied patches log
  β”œβ”€ Tab Settings (βš™): connection, LLM endpoint, pinned job
  └─ Detail view: status + run history + Cancel + AI diagnosis + Repair + Think Fix

Persistence

All data lives in Google's UserProperties β€” per-user, cross-device, no external database.

Key Contents Limit
bedbricks_config {active, lang, workspaces: [{name, host, token, llm_endpoint, pinned_job_id, pinned_job_name}]} β€”
repair_<runId> last repair_id for chained repairs β€”
favorites JSON array of job IDs β€”
fix_history [{ts, notebook_path, find_preview, replace_preview}] 50 (FIFO)

Access control

By default, anyone with the link can access the app. To restrict to specific emails, add ALLOWED_EMAILS in Apps Script β†’ Project Settings β†’ Script Properties:

Key:   ALLOWED_EMAILS
Value: alice@example.com,bob@example.com

Leave empty (or don't add it) to allow any Google account.


Setup

You need: a Databricks workspace + personal access token (dapi...) and a Google account. Everything else depends on which install method you choose.

First-time deployment (any method): after the code is in Apps Script you must create the Web App once manually β€” see Create the Web App below.


Method A β€” Manual (no tools required)

The simplest path. Just copy two files into the Apps Script editor.

  1. Go to script.google.com β†’ New project β†’ rename it to Bedbricks
  2. Delete the default Code.gs content. Paste the contents of apps_script/codigo.gs
  3. File β†’ New β†’ HTML file β†’ name it Index β†’ paste the contents of apps_script/Index.html
  4. File β†’ New β†’ Script file (optional) or edit appsscript.json via Project Settings β†’ Show appsscript.json β†’ paste apps_script/appsscript.json
  5. Save β†’ proceed to Create the Web App

Method B β€” CLASP (Node.js CLI)

Google's official CLI. Handles auth automatically β€” no Cloud Console setup needed.

npm install -g @google/clasp
clasp login                      # opens browser for Google OAuth
clasp create --title "Bedbricks" --type webapp --rootDir apps_script
clasp push

Then proceed to Create the Web App.

Re-deploying after changes:

clasp push

Method C β€” Python deploy script (automation / no Node.js)

No CLASP, no Node. Pure Python β€” useful for scripting or CI.

One-time setup:

  1. Create a Google Cloud project at console.cloud.google.com
  2. APIs & Services β†’ Credentials β†’ Create credentials β†’ OAuth 2.0 Client ID β†’ Application type: Desktop App
  3. Enable the Google Apps Script API for your project
  4. Copy Client ID and Client Secret
cp deploy_config.example.py deploy_config.py
# Fill in: SCRIPT_ID (from script.google.com β†’ Project Settings)
#          CLIENT_ID and CLIENT_SECRET (from step above)

Authenticate (first time only):

python deploy_apps_script.py --auth

Opens a browser for Google OAuth. Token saved to ~/.apps_script_token.json.

Deploy:

python deploy_apps_script.py

Then proceed to Create the Web App. After the first deployment, add DEPLOYMENT_ID to deploy_config.py and re-run to keep it updated automatically.


Create the Web App

This step is the same regardless of install method. Do it once in the Apps Script editor:

  1. Deploy β†’ New deployment β†’ Type: Web App
  2. Execute as: User accessing the web app ← important (see note below)
  3. Who has access: Anyone within <your org> (or "Anyone with a Google Account")
  4. Deploy β†’ copy the Web App URL

Open the URL on your phone. The setup wizard will ask for your Databricks host and token β€” that's it.

⚠ Execute as β€” must be "User accessing the web app". Bedbricks stores each person's host/token/workspaces in their own UserProperties, which is keyed to the effective user. If you deploy with "Execute as: Me", every visitor runs as you and shares your token and workspaces β€” the per-user model breaks. "User accessing" makes each person run as themselves (their own token, isolated). The trade-off: each user authorizes the OAuth scopes once on first open (a normal Google consent screen).

This setting is UI-only. The webapp.executeAs/access values in appsscript.json are not applied when you create/update a deployment via the API or clasp β€” Google fixes them from the dialog at deployment-creation time. Set them in Deploy β†’ New deployment (or Manage deployments β†’ Edit) in the browser.

Updating (after code changes): use Deploy β†’ Manage deployments β†’ Edit β†’ New version instead of creating a new deployment, so your URL stays the same. (Changing Execute as on an existing deployment is often locked β€” if so, create a New deployment, which yields a new URL.)


Optional: AI features (Think Fix)

Think Fix requires a Databricks Model Serving endpoint. If you have one, you can select it from the Settings tab (βš™) β€” it's populated automatically from your workspace. The app works fully without it; Think Fix is just hidden.


Development

File structure

bedbricks/
β”œβ”€β”€ README.md
β”œβ”€β”€ deploy_apps_script.py        ← deploy via Apps Script API (no CLASP needed)
β”œβ”€β”€ deploy_config.example.py     ← template: copy to deploy_config.py
β”œβ”€β”€ deploy_config.py             ← gitignored β€” your Script ID + Deployment ID
β”œβ”€β”€ .gitignore
β”œβ”€β”€ apps_script/
β”‚   β”œβ”€β”€ appsscript.json          ← manifest: OAuth scopes, runtime V8
β”‚   β”œβ”€β”€ codigo.gs                ← backend: GAS functions
β”‚   └── Index.html               ← frontend: mobile UI, embedded logo
└── tests/
    └── test_logic.js            ← 78 pure-logic tests (no GAS APIs required)

Tests

node tests/test_logic.js
# 141 passed, 0 failed

Tests cover all pure helpers: _parseConfig, _isConfigComplete, _parseServingEndpoints, _buildLlmPayload, _parseLlmResponse, _parseFailedTask, _parseFailedTasks, _buildRepairPayload, _parseFavorites, _toggleFavoriteLogic, _buildCancelPayload, _appendFixLog, _parseRunHistory, _extractTaskMeta, _getTaskChipType, _computeFlakiness, _gestureDelta, _buildManifest, _buildRunComparison, _globalSearch, _paramsToMap, _buildRunNowPayload, _buildHintBlock, _mergeRunPages, _runPageUrl, _dedupTasksByKey, _isSentinelTask, _sortFailedTasks, _sentinelKeys, _childRunId, _mergeJobParams, _countsAreSubjobs.


Changelog

Date Version Change
2026-06-04 v1 Initial: TDD scaffold, full GAS backend, dark mode frontend, deploy script
2026-06-04 v2–v7 Remote control: dashboard, job detail, trigger. Tab Jobs, Tab Runs, detail view.
2026-06-04 v8–v12 repairRun() with auto latest_repair_id. Timezone. Refresh button.
2026-06-04 v13–v18 pensarFix() β€” LLM + Hive Metastore β†’ concrete find β†’ replace.
2026-06-04 v19–v22 Multi-task failures, sub-jobs, Repair All.
2026-06-04 v23–v25 Databricks-inspired theme: CSS vars, square dots, bordered badges.
2026-06-05 v26 Widget, auto-refresh 30s, filters, favorites, cancel run, run history, Fixes tab.
2026-06-05 v27–v30 Bedbricks rebrand: embedded logo, dark/light SVG toggle.
2026-06-05 v31–v33 Multi-user: per-user config, setup wizard, Settings tab (βš™), Pin Job (πŸ“Œ), configurable LLM endpoint, English UI, marquee for long names.
2026-06-05 v42 Full i18n EN/ES: STRINGS dict + t(key) + setLang(), data-i18n attrs, persisted in UserProperties. LLM prompts bilingual.
2026-06-05 v43 Diff visual (LCS), Tab Clusters (start/terminate), Scheduler one-shot (GAS triggers), Perf chart (SVG), Task DAG (SVG).
2026-06-05 v44 Replace emoji icons (clusters tab, schedule btn) with minimalist SVG icons.
2026-06-05 v45 Critical fix: detail view stuck on "Loading..." for any run. Root cause: var t = data.tasks[i] inside renderDetail() hoisted t, shadowing the global i18n t() β†’ TypeError: t is not a function in the success handler (silent). Fix: rename loop var t β†’ task.
2026-06-05 v46–v49 Logo doubled in visible size (58px β†’ 96px); header switched to height:auto to fit the logo.
2026-06-09 v50 PWA installable (manifest + iOS/Android meta tags). Swipe gestures: tab navigation, swipe-row quick actions (β–Ά trigger / β˜… favorite), pull-to-refresh. Per-task flakiness score: % badge in task list and DAG (red >20%, amber 10–20%).
2026-06-09 v51–v52 Task Code Viewer: { } code / β†— subjob chips per task row. Notebook tasks: 30-line preview with fade + expand to full code (on-demand workspace/export). run_job tasks: panel with referenced job name + direct navigation. _extractTaskMeta() + getNotebookPreview(path, full). Light mode as default theme. 51 tests.
2026-06-09 v53 Light-mode fix: diagnosis card strong and inline code used fixed dark colors (invisible on light bg). Fixed to var(--text) / explicit light color.
2026-06-10 v54 Drill into failed sub-jobs: a failed run_job task now shows an "open child job β†’" button that navigates to the child run. _parseFailedTasks captures subjob_id; getJobDetail resolves subjob_name.
2026-06-10 v55 Repair fixes: rerun_tasks was sent nested ([["task"]]) β†’ 400 MALFORMED_REQUEST (Repair was fully broken). Now a flat array + rerun_dependent_tasks:true (the DAG continues). latest_repair_id persisted in UserProperties (the API never returns repair_history). Verified against live Databricks.
2026-06-10 v56 Critical fix β€” blank FAILED screen: flakiness (declared in renderDetail) was used inside renderFailedUI (a separate function) β†’ ReferenceError β†’ the whole FAILED UI failed to render for jobs with a DAG of β‰₯2 tasks. Broken since v50. Fix: pass flakiness as a parameter.
2026-06-10 v57 V6 β€” Power features: parameter editor (key-value modal β†’ notebook_params), global search (header overlay: jobs/runs/IDs), run comparison (two-run picker + per-task duration table with delta). 69 tests.
2026-06-10 v58 Fix iOS auto-zoom: search and params inputs to font-size:16px (the full-screen overlay pushed Back off-screen).
2026-06-10 v59 Fix run comparison "?": _fetchRunsWithTasks used the 2.1 endpoint (400 on large runs) and parsed the error body as an empty run. Now 2.2 + HTTP-code check + fallback. Also fixes flakiness on large jobs.
2026-06-10 v60 Comparison table table-layout:fixed + Task column with ellipsis (no longer overflows the screen).
2026-06-10 v61–v62 Per-job status dots: runs/list?job_id&limit=5 per job in parallel (mirrors Databricks' last 5). Reverted global limit=100 β†’ 25 (the endpoint max).
2026-06-10 v63 Universal UI: loading spinner, icon set (β–Ά βœ“ βœ• β€Ή), fixed hard-coded translations (Repair, Task Code Viewer, Install) + data-i18n-title for localized tooltips.
2026-06-10 v64 πŸ”§ Repair / πŸ’‘ Think Fix with text labels (more intuitive); sun icon with a filled center (no longer mistaken for the settings gear); bilingual AI diagnosis (ES/EN prompt + getJobDetail(jobId, lang)).
2026-06-10 v65 Smarter Think Fix for missing tables: searches the metastore (SHOW TABLES) for the correct one, or comments out the line as a last resort (never an empty fix).
2026-06-10 v66 Fix: switchTab did not close the detail-view overlay β†’ it stayed underneath when changing tabs.
2026-06-10 v67 Detailed Fixes log: expandable entries with full red/green diffs; backend stores find/replace (cap 800) + a UserProperties size guard.
2026-06-10 v68 Multi-workspace: config refactored to {active, lang, workspaces[]} with automatic migration; add/edit/delete/switch from Settings; _getConfig_() returns the active workspace flattened (no call-site changes). 78 tests.
2026-06-10 v69 Removed model-specific labels: AI diagnosis card now reads "AI Diagnosis" / "DiagnΓ³stico IA" (the endpoint is configurable).
2026-06-10 v71 Log out / reset button in Settings β†’ resetConfig() clears all saved workspaces & tokens and returns to the setup wizard.
2026-06-10 v72 Setup screen polish: corrected tagline, Connect button as a single arrow, and trimmed the logo (was 480Γ—251 with ~64% transparent margin β†’ cropped to content) so it renders crisp and the layout is balanced.
2026-06-11 v73 Think Fix hint: tapping πŸ’‘ Think Fix now opens an optional free-text field β€” give the AI a suggestion (e.g. "the table was renamed to X") before it diagnoses. The hint is injected into both LLM passes (schema discovery + fix) and bilingual; the system prompt weighs it heavily but still validates against the real schemas. Empty hint = fully automatic, same as before. New pure helper _buildHintBlock(hint, lang) + 7 tests (81 total).
2026-06-11 v74 Public-repo hygiene: replaced internal example identifiers with neutral ones (analytics.customers_v2, generic job IDs) across the hint placeholder and test_logic.js fixtures, so nothing real-world ships in the open-source release. No behaviour change.
2026-09-01 v79 runs/get was never paginated. The API caps at 100 tasks per page; on an orchestrator job with hundreds of tasks only page 1 was read, so the only FAILED task Bedbricks could see was the sentinel (the single DAG leaf, which lands on page 1) β€” Think Fix always diagnosed its useless traceback while the real failures sat on page 2. Fixed in all three readers (getJobDetail, _fetchRunsWithTasks, getOrchestratorStatus; the last one also moved off API 2.1, which returns 400 Resources with more than 100 tasks... β€” swallowed by a catch, so the widget progress bar had always read 0/0). Sentinel tasks are now detected, sorted last, shown with a sentinel β€” not the cause chip and skipped for diagnosis/Think Fix/Repair. tasks also carries one entry per attempt, not per task (after repairs, roughly twice as many entries as tasks) β†’ deduped by task_key keeping the last attempt. 95 tests.
2026-09-06 v81 The v79 pagination fix reached 3 of the 4 readers — pensarFix() was the one left behind. Think Fix kept calling runs/get unpaginated, so on a >100-task orchestrator the actually-failing task (page 2) was never found: it silently fell through with notebookPath = null, the LLM diagnosed with an empty notebookContent, and the find→replace it invented could never match — surfacing as "the code to replace was not found in the notebook", which reads like a notebook problem and is really a paging one. The paginated helper (_getRunDetailPaged) already existed and every other reader used it. Fixed by extracting _findTaskInRun() — paginate and dedupe by task_key keeping the last attempt, since with retries the first entry carries the wrong traceback. Now fails loudly when the task isn't in the run instead of diagnosing blind: a diagnosis without code is a made-up patch. 6 regression tests (101).
2026-09-06 v82 Same bug, the other endpoint: jobs/get was still on API 2.1. 2.1 does not degrade β€” it returns 400 Resources with more than 100 tasks can only be handled by API 2.2, and a catch turned that into "no data" silently: getJobParams() returned {} on any >100-task job, so the custom Run Now parameter editor was always empty. Switching to 2.2 alone is not enough either β€” it answers 200 but truncates settings.tasks to 100 (measured on a real job: only a fraction of the tasks that carry base_parameters land on page 1). Added _getJobDetailPaged() / _mergeJobPages(), which accumulate settings.tasks across pages while keeping the page-1-only top-level fields (name, parameters, job_clusters). Both 2.1 callers migrated (getJobParams, sub-job name lookup); zero raw runs/get or jobs/get calls left in the file. 6 more tests (107).
2026-09-06 v83 Think Fix sent the LLM "the first N characters" of the notebook β€” the worst possible heuristic, because the error is almost never at the top. Measured on a real notebook: ~21k chars, with the failing cell starting just past the 10,000-char cut, so even after v81 handed the LLM real code, it was the wrong half. Replaced by _selectRelevantCode(): split the notebook on Databricks' # COMMAND separators, pull anchors from the traceback (ANSI colour stripped first, or roster and mroster count as two), score each cell by how many rare anchors it holds (a pocket tf-idf β€” spark in a PySpark notebook distinguishes nothing), and emit the winning cells in original order, verbatim, with explicit [ ... N cells omitted ... ] markers; the system prompt now forbids spanning a marker in find. A cell bigger than the budget is cropped around its anchor, not from the top. Two defects the real-data check caught and the unit tests hadn't: adjacent chosen cells were glued without their # COMMAND ----------, producing text that exists nowhere in the notebook (so find could never match β€” the very bug being fixed); and the gap markers weren't counted against the budget (144 chars returned for a 140 budget). Result on that notebook: the failing cell now makes it in at both the 3,000 and 8,000 budgets, and every emitted section is verbatim. 13 more tests (120), including a budget sweep over 77 sizes and a guard that the inline test copy hasn't drifted from codigo.gs.
2026-09-07 v84 A job whose tasks are all sub-jobs β€” a "director" β€” was the one shape where every safety net missed at once. Four independent defects, all of them silent. (1) Sentinel detection was by name. _isSentinelTask matched the substrings centinela/sentinel; rename the task and it becomes invisible. On a director that is not cosmetic: every other failed task is a run_job, and run_job tasks are deliberately not diagnosed β€” so the undetected sentinel became the only task with an AI diagnosis, a Think Fix button and a slot in Repair, i.e. exactly the v79 bug re-entering through a rename. Replaced by a structural rule β€” the DAG leaf whose run_if is ALL_DONE with β‰₯2 dependencies β€” calibrated against a real multi-hundred-task DAG where it isolates the sentinel with zero false positives (of its ALL_DONE tasks only the sentinel is a leaf; the wide gate has dozens of dependents, so it is not one). The name match is kept as an OR, never as the only signal. (2) Drilling into a failed sub-job opened the wrong run. The button called showDetail(job_id) β†’ runs/list β†’ runs[0], the child's latest run β€” a different run whenever you open an older parent run from the history, after a repair, or after someone re-ran the child by hand, with nothing on screen saying so. The child run id is not in runs/get (run_job_output is null there, measured): it comes from runs/get-output on the parent task's run_id. New getRunDetail(runId) + showRunDetail() navigate to that exact run, falling back to the job with a different tooltip when it cannot be resolved. (3) Repair silently changed meaning. Repairing a run_job task re-runs the entire child job, not the task that failed inside it β€” a whole wave. Now it confirms first. (4) The parameter editor was empty and the progress bar mislabelled. getJobParams only merged notebook_task.base_parameters; a director has none, and its parameters live in job-level settings.parameters (which run_job_task does propagate) β€” same symptom v82 fixed, different cause. And the bar counted waves while calling them "tasks", off by more than an order of magnitude. Plus a generalized sync guard: 36 pure helpers live duplicated between codigo.gs and the tests, and the old guard covered one block. SYNC2/SYNC3 now compare every shared helper modulo comments β€” which surfaced 5 pre-existing drifts where the inline copy certifies a superseded version (_buildFixLogEntry, _appendFixLog, _buildLlmPayload, _parseConfig, _parseLlmResponse), documented so the list can only shrink. 21 more tests (141).
2026-06-12 v78 Think Fix no longer auto-focuses the hint textarea (the mobile keyboard popped up on every tap). The hint field just reveals + scrolls into view; the keyboard opens only if the user taps it β€” the hint is optional.
2026-06-12 v77 After a successful Apply Fix, that task's Think Fix button also locks (disabled) β€” not just the apply button β€” so a patched notebook can't be re-diagnosed by accident.
2026-06-12 v76 Code-error warning banner reworked: dropped the stray ! text prefix, added a proper ⚠ icon in a flex layout (icon aligned to the first line), and reworded the message to point at Think Fix (EN/ES).
2026-06-12 v75 Three fixes. (1) Per-task AI diagnosis: when a job fails with several FAILED tasks, each task now gets its OWN error trace + its OWN diagnosis card (before, only the first task was diagnosed). getJobDetail diagnoses per task; renderFailedUI renders one diagnosis card per failed task (+ its own raw log and Think Fix). (2) Removed swipe-to-switch-tabs: the horizontal tab-swipe gesture hijacked horizontal scroll when reading full notebook code in the detail view β€” removed; pull-to-refresh and swipe-on-a-row quick actions stay. (3) Apply-fix button locks: each Think Fix panel owns its apply button (unique id + per-panel fix in _fixByPanel) and stays disabled after a successful apply β€” no more repeat taps (previously all panels shared btn-apply-fix/_pendingFix, so in multi-task only the first button disabled).

Releases

Packages

Contributors

Languages