Skip to content

PRDCT-487: Getting Started rebuilt on Boolabean, every task page on three tabs - #1116

Open
Iamfle4ka wants to merge 112 commits into
mainfrom
PRDCT-487-rebuild-wave1
Open

Iamfle4ka wants to merge 112 commits into
mainfrom
PRDCT-487-rebuild-wave1

Conversation

@Iamfle4ka

@Iamfle4ka Iamfle4ka commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator

What this PR does

Rebuilds the Getting Started section around one question a small business would actually ask, with a sample world that is fictional where it can be and live where it counts:

Which day next week will leave a café short-handed?

Six cafés, one summer of sales, rosters and daily weather in five CSV files (public/getting-started/boolabean/), plus a live seven-day forecast from open-meteo (no account, one URL). The pipeline: two HTTP configurations → one SQL transformation (staffing_outlook, 42 rows = 6 cafés × 7 days) → Kai answers the question → a grid app → a 06:00 flow → a verification page.

Every task page then offers that task three ways on one page, and the reader's choice follows them from page to page.

Preview: https://connection-docs-git-prdct-487-rebuild-wave1-keboola-engineering.vercel.app/getting-started/

The three tabs

Michal asked it directly on 2 September, "Mám tam nějaký prompt? Jak to zpracuje to CLI?", and Jordan had agreed the same mechanism on 5 August. A page that shows only the prompt and the clicks answers neither.

tab what it is
Prompt what you paste into Kai
UI the same task click by click
CLI / API the same task from a terminal with kbagent, and the raw request beside it

Starlight's <Tabs syncKey="howto"> remembers the choice under the label text, so the three strings are a byte-exact contract across all nine tabbed pages (src/components/getting-started/pathintro.mjs holds it, and the same wording in plain text, because the markdown twin has to carry the "these are alternatives, not steps" sentence too: without it an agent reads the three flattened sections as three jobs and builds the pipeline three times).

Where one of the three genuinely cannot do the job, the page says so instead of showing an empty tab. write/ and load/googlesheets/ carry two tabs, because the Google consent screen and the Drive picker are dialogs in the reader's own account. The branch tutorial carries none, because kbagent branch merge does not merge: it prints the URL of the merge screen, and the partial-merge checkboxes are where the decision is made.

Pages

page change
getting-started/ rewritten: question → clip → what you get → how every page works → where to start instead
project/ Get a project is its own page again (restored 14 Sept; it is the one step with no prompt and no CLI form, which made it wrong as a hub section)
load/ five files in one configuration + the forecast as a second one; three tabs; the CLI tab defines <your-stack> and $KBC_TOKEN and carries the forecast row's JSON in full
transform/ new SQL in Snowflake (run live with the page's own input mapping) and BigQuery (translation, VERIFY(owner)); half-open bands; the output-mapping case trap with its exact error; three tabs
ask/ two prompts run live; three tabs (UI is workspace SQL, CLI is kbagent kai ask)
app/ the six-by-seven grid; built live to a running draft; production deploy flagged (see below); three tabs
automate/ three-phase flow built by Kai from the page's prompt; three tabs, with where each of the four IDs comes from
check/ structural checks and the first live run as the worked example of a red run with green phases; no tabs, plus a read-only terminal section
load/database/ three tabs; the old "Do it with Kai" box became the Prompt tab; sample credentials hoisted above the tabs so every path reaches them
load/googlesheets/ two tabs; same box became the Prompt tab; says why a terminal cannot start the task
transform/workspace/ rewritten out of the legacy voice; three tabs; workspace create / load / query / detail / delete
branches/ no tabs; one section on what the CLI does and what it hands back to the browser
write/, going-further/ text on the new table; tab labels aligned with the main path

Verified live in project 264

2026-09-08/09. Control run, four prompts in one chat, plan mode off: 14.4 min, 12 approvals, 0 nudges. Plan-mode run: 14.3 min, 15 approvals, 0 nudges. Every number Kai reported matched an offline reference to the decimal (Storage API: 6 / 18 / 9,761 / 552 / 552 rows; forecast 42; staffing_outlook 42 rows, 42 distinct café-days, 0 empty cells, 4 short-handed). The page's hand-built Snowflake SQL run through a temporary transformation with the documented input mapping (job 103114885, 61 s): identical output. Flow built by Kai: three phases, real Scheduler configuration (0 6 * * * Europe/Prague), phases 1 and 2 success, phase 3 (app deploy) error. App built through the Apps builder to a running draft in 5 min 44 s, one approval.

2026-09-15, the CLI tabs. Snowflake data source created, three table rows added, run to success (job 104350143): OPPORTUNITY 639, ACCOUNT 275, USER 28, each matching the repo's own CSVs. A workspace created, loaded, queried, inspected and deleted. workspace create prints a private key once and says it cannot be retrieved later. --read-only defaults to on, the opposite of the dialog's unchecked box. workspace from-transformation refuses a configuration whose input mapping is empty, which is what transformation create leaves behind.

Every object created for these walks was deleted the same minute.

Review passes (CLAUDE.md standing authorization)

  • fact-checker, 9 Sept: ~45 claims confirmed; two errors fixed (the band with no history is cold+dry, not cold+wet; "more than 42 rows = BETWEEN trap" cannot happen with CASE bands), plus eleven drifts.
  • guide-tester, 15 Sept, on the three-tab conversion: three blockers, all in the new CLI path. The markdown twin dropped PathIntro, so an agent would have built everything three times. The forecast row's JSON was named and never shown. The flow YAML needed a data-app configuration ID no page produced.
  • fact-checker + guide-tester, 15 Sept, on the side trips: one blocker, and it was in the example the live run had not covered. The workspace page's one worked query selected cafe_name and forecast_date; the table has STORE_NAME and DATE. Fixing it also corrected the casing rule beside it, now measured four ways: the table is stored lower case and needs quotes, the columns are stored upper case and break when quoted lower case. Three product facts went the same way (the read-only default, the Connect button opening Snowflake's Snowsight rather than a Keboola console and needing a dedicated backend, and the workspace picker's labels).

118 claims confirmed on that last pass, including the tab-label contract across all nine tabbed pages.

Open VERIFY(owner) by owner

  • Apps team: "Publish to production" produced no visible effect and two production deploys of the grid app failed. kbagent data-app runs gives the real reasons the UI hid: one run died on a missing keboola-config/nginx/ directory, two on fatal: Authentication failed against the app's git remote. Exception IDs in app/index.mdx's comment and MISSING.md.
  • Kai team: plan mode's contract (measured: one plan approval, then every per-change approval); whether Kai can create a workspace, which its documented use cases do not mention; whether Kai can traverse a flow run's child jobs.
  • Docs / PAYG owner: first-month Free Plan allowance; whether the Free Plan includes data apps; Direct Mode gate. Also whether a Free Plan reader can reach a Snowflake workspace at all, since /workspace/ says direct access needs a dedicated backend.
  • Someone with a BigQuery project: run the BigQuery block on transform/.
  • Nikita: the Google Sheets side trip still loads level.csv, the pre-Boolabean sample, so the section reads as two datasets; swapping it needs a live Google authorization to reshoot steps 5 to 12.

Media

26 stills and two clips shot from the live pipeline in project 264 as element captures without the top bar. The hero clip was re-rendered on 15 Sept without the mascot, as a spotlight and a caption chip, per Nikita on 14 Sept. Not shot: the Sheets captures (need a Google OAuth) and the flow builder's component picker.

Left out, and why

  • Ad-hoc analysis: no tabs. Most of that page is the Google Cloud console, service accounts and IAM roles, which neither Kai nor the CLI can drive. It also still calls workspaces sandboxes. It needs a rewrite, and that is its own piece of work.
  • The agent line ("reading this with an agent? give it the .md") is not in the section. Every page already has View as Markdown in its title bar, and the line landed on three pages out of twelve. It goes on all of them or on none.
  • Timings: budgets are word counts plus measured Kai wall time. A stopwatch with a real reader is still owed.

Known, not fixed here

_data/cli/command-reference.md is generated from kbagent v0.76.1 while the installed CLI is v0.93.2, so npm run check:cli runs seventeen minor versions behind. It rejected a true sentence this week: kbagent config restore exists and the reference has never heard of it.

DECISIONS.md, CONSERVATION-REPORT.md and MISSING.md carry the rationale, the block-by-block conservation map and the gaps.

Landed after this description was first written

Four commits, none of them content rewrites:

  • The hero clip was re-rendered after a frame-by-frame review. Every cut-out was placed as if its coordinates were a corner while the code reads them as a centre, so all three tour stops lit the row above their target. Three blue markers pointed at one thing and one of them covered the glyph its caption explains. The caption sat on the payoff row in every beat. Act 2 drew the question over a white wash straight onto the grid, which made the poster frame two layers of text. Act 1 showed four files while every caption said five.
  • The Kai clip on ask/ was masked. It carried the organization name, project name, branch name and the signed-in avatar in the top bar for its whole length, which is what PRDCT-616 exists for and what the 26 stills already avoid. The mask follows the clip's own dimming rather than painting a white rectangle through it. map-tour.mp4 and its poster were deleted: the octopus tour, retired on 3 September, referenced by no page since.
  • Two links now point at the page instead of its redirect (catalog/multi-project/, the generic extractor's SSH proxy page). A third of the same kind in storage/api/tde-exporter/ is deliberately untouched because PRDCT-582: merge the last 8 dev pages into help, keeping what is current and not already there #1120 edits that file.
  • The Kai allowance sentence was rewritten. kai/pricing.md says the message counter was replaced by PPU credits on 15 September 2026; kai/getting-started.md still opens its Rate Limits section with "Kai is free during the public beta period" and still gives 150 turns a month, 50 on Pay As You Go. Both are published and they disagree. This guide no longer depends on either, and carries it as VERIFY(Kai team).

Verification, and what a reviewer can reproduce

Check Result Reproducible from the repo
npm run build 367 pages, 282 redirects, 366 markdown twins, 0 skipped yes
npm run check:cli 0 findings yes
node scripts/audit-phase2.mjs 0 missing images, 0 unclosed fences, 0 malformed tables yes
broken internal links 29, all pre-existing and all fixed by #1120 yes
link and redirect check 1 blocker (that same #1120 set), 2 warnings in a file #1120 owns no — local tool, not in this repo
live pipeline in project 264 see below no — needs the project

The link and redirect check is a local script outside this repository, so that row cannot be reproduced from a checkout. Everything else in the table can.

git merge-tree against #1120 and #1094 reports no conflicts. The diff is identical with and without --ignore-all-space, so there is no line-ending or whitespace churn in it.

The acceptance criteria, re-run on 21 September

The pipeline in 264 still runs every morning and still matches the spec, with two deliberate exceptions:

Criterion Expected 21 Sept
stores / products / sales / staffing / weather_daily 6 / 18 / 9,761 / 552 / 552 exact match
"Boolabean sales" rows, "Boolabean forecast" rows 5, 1 5, 1
forecast 42 rows, 7 dates 42, 7
transformation outputs 1 1
staffing_outlook 42 rows, 6 cafés × 7 dates, no duplicate café-day 42, 6 × 7, 42 distinct
BETWEEN trap absent, half-open bands absent
expected_units_per_person 30–120 32 to 60.1
short_handed 2–8 of 42 0
every column filled yes 16 of 42 empty

The last two rows are the live forecast doing what the guide says it can do. check/ already states that a cool, wet week can flag nothing and that the grid is then right to be quiet, and that an empty expected_units means the summer had no day in that café's weather band. ask/ says the same about Kai's answer. So the pages hold; it is the acceptance range in the original brief that was too narrow, since reality produced zero.

The daily flow's third phase still fails on the data-app deploy, which is the open item for the Apps team below.

🤖 Generated with Claude Code

Nikita and others added 30 commits August 5, 2026 01:42
…d arc

Michal's 07-28 priority: Getting Started framed as the customer lifecycle,
written for readers (and agents) who know nothing and may land on a deep link.

Structure:
- /tutorial/ -> /getting-started/, redirect_from on every page
- steps renamed off "Part N": project (new), load, transform, write, automate,
  next-steps (new); optional deep-dives grouped under "Going Further"
- /tutorial/onboarding/ -> /overview/onboarding/ (SECTION-MAP U2: the subtree is
  explanation content, not tutorial steps), google-data-policy added to the nav
- description: frontmatter on all 22 pages (feeds search, RAG and the lede)
- 169 dead duplicate assets removed from src/content/docs — public/ is what
  serves absolute image paths; a single copy now lives under public/
- 431 path references repointed across 69 files; help.keboola.com self-links
  made relative

Supersedes #1012. Prose and screenshots follow.
Every arc page now carries the same shape, per the section-rework standard and
Michal's deep-link premise (basics -> steps -> verify -> troubleshoot):
lede + type marker, "What you need", numbered steps with one image each,
"Check it worked", "If it goes wrong", an "Or ask Kai" aside, and a Next: footer.

- index: a real overview — what you build (table), prerequisites, the 6 steps,
  the optional side trips, and a pointer to Platform Onboarding for rollouts
- project (new): free plan / invite / sales, what a project and a stack are,
  the five places in the UI that matter
- load: CSV Import with a destination table per file, verify in Storage
- transform: input/output mapping explained before the SQL; BigQuery variant
  kept with a VERIFY(owner) note (unverified, and the original's `Level`.`Level`
  reference looks wrong)
- write: was 66 lines with no headings; now the full page shape, plus the
  Update-rows vs Append-rows distinction
- automate: rewritten against Conditional Flows (phases/tasks/Builder/plus icon,
  conditions, Set Schedule, Notification tasks) — the old page documented the
  legacy builder
- next-steps (new): routes by intent, then the three agent surfaces
  (Kai / MCP / kbagent CLI)
- home: "Where to Start" now routes by intent into the arc instead of repeating
  the overview; overview: fixed the malformed `[/tutorial/automate/#main-header]`
  link and the absolute help.keboola.com self-links
- kept "Going Further" pages: footers normalised, stale "part of the tutorial"
  wording fixed, one malformed link repaired in transform/workspace

Screenshots are still the legacy captures — reshoot pass next.
- /overview/ had no link into the arc at all — added one under the lede
- /management/payg-project/ (where free-plan signups land) now points at
  Get a Project
- components/writers/ and the Tableau connector page both promised a tutorial on
  "writing data from Keboola to Tableau" and linked to the Google Sheets
  walkthrough — reworded to what the page actually shows
The blocker: the arc recruited readers to the Free Plan, then handed them
Snowflake. New Pay As You Go projects default to the BigQuery backend
(storage/index.md:13), so the majority of new readers hit step 3 and stop.
transform now tells you to read which SQL transformation your project offers and
gives the two query blocks as peers instead of burying BigQuery in a note. The
BigQuery SQL still carries VERIFY(owner) — it cannot be run in demo project 264
(Snowflake), and it is now the block most readers will use, so it needs a real
Free Plan project before this ships.

Both agents independently caught the same two errors:
- the output bucket is PLURAL — `out.c-denormalize-opportunities` — derived from
  the transformation name; the singular form was inherited from the old page and
  the rewrite had hardened it into a table ID the write step tells you to select
  verbatim (4 call sites)
- a Notification task fires when its phase runs, i.e. on success. Failure alerts
  come from the flow's Notifications tab / Edit Notifications
  (management/notifications). The arc promised "emails you if it breaks" and
  configured the opposite; condition-driven notification tasks are now the
  advanced note, not the baseline.

Also, against the pages' own screenshots and sibling pages:
- CSV Import's Destination is three controls (stage / bucket / table name), not
  one dotted string with a pen icon
- Storage shows the bucket as `csv-import` with an IN badge; `in.c-csv-import` is
  the ID, not the label
- New Table Input / New Table Output; New Code names a code inside Block 1
- "Recently updated by" is a column in the table list, not a field on the detail
- pick Google Sheets *data destination* (`keboola.wr-google-sheets`) — the search
  returns source and destination side by side and only the old screenshot's red
  arrow disambiguated them
- the Add Sheet wizard has three tabs; spreadsheet title and sheet title are
  different fields
- Jobs shows a job per task plus the flow's own orchestration job, not six
- backends are not strictly per-project (each transformation can differ; DuckDB
  is in beta), Free Plan runtime is *up to* 60 min and tracks last month's usage,
  Free Plan has no dbt, invites are Users & Settings > Invite User, kbagent is in
  beta, and Storage columns are text only until typed
- dropped the unverifiable "write access to spreadsheets only" OAuth claim
- 30 minutes -> under an hour; the "skip the Google account" promise now says
  what you lose

Screenshots on automate are flagged in-page: all 15 are legacy-flow captures
whose captions describe the intended new shots. Blocked on a logged-in session.
redirect_from generates page stubs only, so moving the section's non-page assets
broke their URLs. The four sample CSVs are linked from the live tutorial today and
are the kind of URL people bookmark or hotlink, so they get explicit 301s, as do
the two branches CSVs and the usage-blueprint .docx.

Images are not redirected — 169 of them, referenced only from pages in this repo,
all updated in the restructure commit.
CSV Import is deprecated — verified live in project 264: "This component has been
deprecated. New configuration cannot be created", stage PRIVATE, and it no longer
appears in component search. The arc's step 2 was built entirely on it, so the
guide told every new reader to configure something nobody can configure.

Step 2 now uses the **HTTP** data source connector, pulling the four sample CSVs
from their published URLs on help.keboola.com. This keeps the arc intact — it is a
real connector, so it re-runs, schedules, and works as a flow task in step 5 —
and it is reproducible in any project, which a local file upload is not.

Shape follows the product rather than the old page: one configuration holding four
tables as configuration rows, not four configurations. Consequences threaded
through the arc:

- the destination bucket is the connector's own (`keboola-ex-http-<id>`), so the
  hardcoded `in.c-csv-import.*` IDs are gone. transform now leans on the input
  mapping's Table name, which is what the SQL actually depends on and what the
  reader controls — and says so.
- automate is three phases with one task each. The parallelism lesson is now
  taught where it is true: a second connector goes in phase 1, and row-based
  components parallelize internally.
- index, next-steps, and five inbound links elsewhere no longer promise a manual
  CSV upload; ad-hoc's dead `#manually-loading-data` anchor is gone too.

Field names and behaviour are sourced from
components/extractors/storage/http/index.md, which documents this connector
against these exact sample files. Live labels and screenshots still pending.
Walked the whole step in demo project 264 with keboola.ex-http and ran it: the
config fetched all four sample CSVs from help.keboola.com and landed
opportunity 639 rows, account 275, user 28, level 28. Seven fresh screenshots
replace the eight legacy CSV-Import captures.

Labels the walk corrected — several of which our own connector page still gets
wrong (components/extractors/storage/http/index.md says "Add Table"; the UI says
"Add Row"):

- searching HTTP returns two components; you have to pick plain HTTP over
  HTTP Advanced (BETA)
- Add Component opens a dialog first: Connect To My Data vs Use With Demo Data
- the base URL lives under "Base URL and Connection Options" and needs its own
  Save; the section also holds Maximum Redirects
- rows are Rows / Add Row / Create, not tables
- a row is Download Settings (Path) + Save Settings (Table name, Incremental
  load, Delimiter, Enclosure) + Header & Primary Key
- Read Header ALREADY defaults to "Read the header from the file(s) header", so
  the step is to leave it, not to change it
- Run Component asks for confirmation before it runs
- the run took 2 min 4 s for four rows, fetched sequentially — the page said
  "well under a minute"; Parallel jobs on the configuration is what changes that
- the Data Sample is a tab on the table page

Also corrected on automate: a flow run produces a Conditional Flows job, a
Conditional Flows phase job per phase, AND the component jobs — visible in the
project's job list.
…tures

Walked the create-transformation path in project 264. The label dispute between
our own pages resolves as "both, at different moments":

- the BUTTON is **Create Transformation** (this page was right)
- the DIALOG is **New Transformation** (transformations/duckdb was right)

That dialog turns out to carry the answer to the backend question the
guide-tester flagged as blocking: it lists what the project can actually run
(Snowflake SQL / Python / R / DuckDB beta, or BigQuery SQL elsewhere), so the
page now uses it as the reader's signal for which SQL block to use.

Three labels in my draft were simply wrong:

- section **Table Input Mapping**, button **Add Table Input** (not "New Table
  Input"; the legacy screenshot's "New Table Input" was also out of date)
- **Queries → Create Multiple Queries**, not "New Code" — and SQL lives in a
  *code* inside a *block*, which the page now says
- **Source** is multi-select with checkboxes, so all four tables go in at once;
  **Table name** auto-fills only for a single selection

Also replaced `transformations-intro.png`, which the fact-checker caught showing
the pre-2020 UI (a "NEW TRANSFORMATION BUCKET" button and an Orchestrations nav
item), with a current Transformations list.

The transformation itself is configured in project 264 — four inputs mapped to
opportunity/account/user/level, output to
out.c-denormalize-opportunities.opportunity_denorm, SQL in Block 1 — but the run
and the remaining seven captures were cut short by a network outage. Those
references stay on their legacy images so the branch keeps building clean;
the in-page marker says exactly which shots are fresh and which are not.
Verified live in project 264: the nav is Dashboard / Flows / Storage / Apps /
Components / Workspaces / Transformations / Data Catalog / Semantic Layer (Beta) /
Jobs, Storage's tabs are Tables & Buckets / Files / Data Streams / Storage Jobs /
Events, and the assistant's button reads **Kai Agent** with a Beta pill — the page
said "Kai".

The type marker now states what is verified versus what is sourced from
/management/payg-project/, and says plainly that the sign-up wizard is
deliberately not walked, rather than a blanket "pending live verification".
…fresh

Ran it twice — jobs 95584510 (53s, via API) and 95584854 (43s, from the UI so the
screenshot is attributed to a person rather than "the MCP server"). Both produced
out.c-denormalize-opportunities.opportunity_denorm with 639 rows and 23 columns.

Facts this settles:

- the output bucket IS plural — visible in the output mapping as
  "OUT denormalize-opportunities / opportunity_denorm", in the job's Mapping panel,
  and as the Storage bucket itself. Both agents deduced this from screenshots; it
  is now run-verified.
- 639 rows out of 639 in, so the three joins matched every row without duplicating
  any — the page now gives that as the reader's sanity check, plus the eight
  columns the SQL adds.
- the transformation is safely re-runnable; the page says so.
- the code UI: "Create Multiple Queries" is what an EMPTY Queries section offers;
  once a block exists you get Block 1 with New Code / New Code Block, and the
  Queries header switches to Copy Code / Edit Code. My earlier "New Code" was not
  wrong, just incomplete — both are now described.
- Run Transformation asks for confirmation, and a "Snowflake SQL job has been
  scheduled" toast with a Show job link follows.
- "Recently Updated By" on the table list reads "Denormalize opportunities /
  Snowflake SQL".

The job log also quotes the mapping model back at us — "Loading 4 tables to
workspace", "Cloned table ... into workspace WORKSPACE_... as opportunity" — so the
page's explanation section now cites it.

Twelve superseded legacy captures deleted, including the pre-2020
transformations-intro. Only mapping.png (a diagram, not a UI shot) and the
workspace captures survive.
…w captures

Built and ran [TUTORIAL] Opportunity pipeline (flow 01kz5yndzrc4zvkwch85qg4x28) in
project 264: HTTP config -> transformation, saved, scheduled, run. A manual run
succeeded in 2 min 59 s and the schedule I set fired on its own two minutes later,
which is how the "scheduled run" row in All Runs got there.

Four claims on this page were wrong, and two of them our other pages still get
wrong too:

- **phases are "Phase 1", "Phase 2"** — the legacy captures said "Step 1"
- **the + between phases adds a PHASE, not a task** — this was the page's
  outstanding VERIFY(owner); now described
- **scheduling is Schedules tab > Create Schedule > Set Up Schedule**, with
  predefined intervals or a custom one, several schedules allowed per flow, each
  independent. It is NOT "Set Schedule" — /flows/ still says that.
- **the Notifications tab edits in place** with Success / Errors / Processing
  cards, each taking Email or Webhook. There is no "Edit Notifications" button and
  no Warnings card, contrary to /management/notifications/.

Also newly described, all observed: the Builder holds changes as a draft until you
press **Save** (Reset discards; Run flow is disabled until saved); **Run flow**
confirms before running and offers **Terminate flow** while in progress; a task's
settings carry **Run rows** (so a flow can run a subset of a row-based
component's rows) and **Continue on Failure: Handled via Conditions**; **All Runs**
gives a duration chart plus a run table; and the Builder has Kai in it —
**Build with Kai** in the Add Task menu, **Modify with Kai** in the header.

Nine fresh captures replace fourteen legacy ones. automate1.png stays: the legacy
flows page still uses it, which is where it belongs.

The third phase (Google Sheets destination) is not in the screenshots yet — it
needs an OAuth authorization only the owner can create. Noted in the page marker.
Schedule left disabled so the demo project has no daily job.
Searching "Sheets" in Components returns five results, and the first two are
Google Sheets Data Source and Google Sheets Data Destination — adjacent cards,
near-identical names, opposite jobs. That is exactly the coin-flip guide-tester
flagged, so step 1 now names the type and the component ID explicitly, with a
fresh capture.

The rest of the page (steps 4-12) still needs a Google OAuth authorization that
does not exist in project 264 and that I will not create, so those captures stay
legacy. The page marker now says precisely which step is verified and which are
not, instead of a blanket "pending".
One link regression this branch caused, and one wrong number in a marker:

- `transformations/mappings/index.md` deep-linked `#input-mapping` and
  `#output-mapping` on the transform page. My rewrite gave those headings a verb
  ("Set the input mapping"), so both links silently landed at the top of the page
  instead of the example they promise. Repointed. audit-phase2 does not check
  fragments, so only a careful read caught it.
- the load page's marker still claimed the run took 32s — that was the single-row
  probe. The four-row run took 2 min 4 s, which the body already said.

Stale wording the load rewrite left behind: the arc index and the Google Sheets
side trip still framed themselves against "a manual upload" / "uploading a file by
hand", which nothing in the arc does any more. Both now contrast a credentialed
source with step 2's public URL.

Accuracy tightening, all from the same review:
- the bucket is named component + configuration ID, not "after the configuration"
- one spelling of the Read Header option (the live one), and Incremental load is
  now mentioned, since the reader sees it
- do not assert the BigQuery tile label or Snowflake-only job strings
  unconditionally on a page whose default reader is on BigQuery
- write: the duplicated "Add Component" click, and the marker now admits steps 2-3
  are unverified even though they need no OAuth
- two dead anchors on lines this branch already touched
  (`#using-bigquery-extractor`, `#extend-the-transformation`)
- dropped the semantic layer from a shipped HTML comment

New VERIFY(owner) notes where the review found a claim resting on the old page
rather than on evidence: whether the Google Sheets *destination* shows the Connect
To My Data dialog at all (it does not declare has-sample-data, unlike the HTTP
connector), the exact label of the append option, and what a Free Plan project's
runtime allowance is in its first month.
The sharpest finding was reasoned from my own measurements rather than from the
UI: the two SQL blocks disagree about `_timestamp`. BigQuery does
`SELECT * EXCEPT (_timestamp)`, Snowflake a plain `SELECT *`, and the Snowflake
run produced exactly 15 + 8 = 23 columns — so no `_timestamp` reached the query
there. If BigQuery stages inputs the same way, the EXCEPT fails with "column
_timestamp not found", on the block most Free Plan readers will use.

I cannot settle that without a BigQuery project, so the page no longer leaves the
reader to discover it: the troubleshooting list now covers both directions (drop
the EXCEPT if the column is absent, restore it if output mapping rejects an
unexpected one), and the VERIFY names this as the specific thing to check.

The input-mapping section contradicted itself: it offered multi-select, then said
"with a single table selected, Table name fills in automatically" — so a reader
taking the shortcut never saw the field the whole arc depends on. Rewritten as one
route, using the UI's own **Input Table** wording, with the single-table Table name
field mentioned as the variant.

Also:
- write now hands over literal spreadsheet and sheet names; every other step in the
  arc gives exact strings, so an unattended agent had nothing to type there
- the hub page no longer promises "the Free Plan is enough for everything here" —
  it states the arc's ~6 minutes of runtime and makes the allowance the condition,
  since our PAYG page never says what month one grants
- skipping the Google account no longer costs step 5: a two-phase flow exercises it
- "the section is titled Conditional Flows" is now conditional, matching the project
  page; Save/Reset only appear while there are unsaved changes; the schedule step
  picks Once a day, matching the index's "runs daily" promise
- flagged in-page that the flow capture shows two of the three phases
- said once that the UI hides the in.c-/out.c- prefix behind a stage badge
- the Snowflake job-log quote is now attributed to the Snowflake run

Not acted on: the report inferred the captures were taken in a development branch
from a "New Development Branch" breadcrumb item. That item is an action button —
get_project_info reports branch_name "Main", is_development_branch false. The
screenshots are from production.
The blocker turned out to be answerable without a BigQuery project at all.

`SELECT * EXCEPT (_timestamp)` in the BigQuery block looked like a BigQuery
quirk. It is not. Per transformations/mappings/index.md:210-238, `_timestamp` is
added when an input table is staged by CLONE, exists on both backends (differing
only in type), and is "not present on tables loaded using the copy method". So
whether you see it depends on staging, not on the SQL dialect — and my Snowflake
run producing exactly 15 + 8 = 23 columns simply means those inputs were
copy-staged.

Consequences, both applied:

- the EXCEPT is gone, so the two SQL blocks are now symmetric and the BigQuery
  reader is no longer handed a clause that fails when the column is absent
- the troubleshooting entry is rewritten around the real failure — output mapping
  rejecting `_timestamp` — and points at the documented fix, the
  `dropTimestampColumn` input-mapping option, rather than telling the reader to
  edit the query

What remains unverified on the BigQuery block is now narrow and honest: dialect
only — the CTE form, the CAST syntax, unquoted identifiers. The VERIFY says exactly
that, and records both resolved traps so nobody reinstates them.
Checked without authorizing anything: from the catalog, Add Component on the
Google Sheets **Data Destination** card does open a dialog, and its only button is
**Connect To My Data**. The fact-checker's suspicion was half right — it inferred
this component might skip the dialog entirely because it lacks the has-sample-data
feature. The dialog appears; what that feature adds is the second button, **Use
With Demo Data**, which the HTTP connector shows in step 2 and this one does not.

The step now says so, which also stops the two pages looking like they contradict
each other. Nothing was created in the project.
AGENTS.md now documents production as GitHub Actions syncing `dist` to
s3://help.keboola.com, not Vercel. If that is right, the `vercel.json` redirects I
added for the moved sample CSVs never fire on help.keboola.com — a static S3
bucket has no redirect layer, and only the PR previews would honour them.

Rather than bet on which host is authoritative, the four sample CSVs, the two
branches CSVs and the usage-blueprint .docx now exist at BOTH paths as real files.
268 kB total, and the old URLs keep working on S3 or Vercel either way. The
vercel.json 301s stay, so on Vercel the old paths still redirect rather than serve
a duplicate.

This is deliberate duplication of data files people may have hotlinked, not the
same thing as the 169 dead image copies removed earlier — those were never
referenced by anything.
…arted

# Conflicts:
#	src/content/docs/components/extractors/database/bigquery/index.md
#	src/content/docs/components/extractors/storage/index.md
#	src/content/docs/getting-started/ad-hoc/index.md
#	src/content/docs/getting-started/branches/project-diff.md
#	src/content/docs/storage/tables/csv-files.md
#	src/content/docs/tutorial/manipulate/index.md
Kai sat in an "Or ask Kai" block at the end of five pages, after the reader had
already done everything by hand — a bolt-on, which is what the house style says
not to build. Jordan's ask on the 08-05 sync was to unify Getting Started with
Kai, so each step now carries the assistant next to the steps it replaces.

- "Do it with Kai" moves up to sit right after What you need, holding one
  copy-pasteable prompt that reaches the same end state as the numbered steps,
  plus the check that confirms it — same "one action, two ways" framing as the
  UI/curl idea in PRDCT-609.
- Prompts follow kai/use-cases.md and link the capability they rely on, so every
  claim is traceable: Integration Setup (load, write, the two side trips),
  SQL Transformations (transform), Complex Workflows (automate), Data
  Exploration (project), Troubleshooting (the failure paths).
- Troubleshooting prompts move into "If it goes wrong", where reading a job log
  is genuinely faster than doing it yourself.
- write/ and load/googlesheets say plainly that the Google authorization is the
  reader's to do — a consent screen in their own account is not something the
  assistant can click through.
- project/ keeps an orientation block instead: Kai lives inside a project, so it
  cannot create one. It says so, and points at the blocks that follow.
- getting-started/index.md frames the two paths once and explains why the
  clicking stays primary — Kai is Public Beta and admin-gated, and the later
  steps assume you have seen where a configuration and a mapping live.
- branches/ and transform/workspace get nothing: neither branch merging nor
  workspaces is a documented Kai capability, and inventing one is worse than
  leaving the page alone.

Build clean, audit unchanged (0 missing images; the 35 broken links are the
known forward-refs to unmerged sibling units).
One critical and several overstatements, all from writing the blocks faster than
I checked them against our own kai/ pages.

- load/database.md told the reader to hand Kai a password in chat. Kai's own
  guidance forbids exactly that — it prompts for credentials through a secure
  form — so the prompt now says what to connect, not what the secrets are, and
  the page says plainly never to paste one. The prompt also asked for one table
  where the manual steps load three (OPPORTUNITY, ACCOUNT, USER), and promised
  a "query" the simple path does not have.
- The hub claimed every step works either way. Two do not: creating the project,
  and the Google authorization. It now says which, up front.
- "The button may simply not be there yet" was wrong — every user on a supported
  stack sees it; the feature is what an admin enables, and there are three routes
  to that, plus a monthly message allowance that matters on the Free Plan.
- write/ had the order backwards: authorize first (steps 4-6, not 4-7), then hand
  the configuration to Kai.
- load/googlesheets: the Drive file picker is the reader's too, not just the
  consent screen — both happen inside Google.
- Softened three claims with no source behind them: configuration rows, per-phase
  flow construction, and Kai picking the SQL dialect from the backend. The end
  states stay; the certainty about how Kai gets there does not.
- VERIFY(owner) on the two inferred limits: that Kai cannot create a project, and
  that it cannot grant an OAuth consent.

Build clean, audit unchanged.
From the guide-tester walk: the prompts were shorter than the manual steps they
replace, so a reader in the Kai lane could not hit the checks the pages give.

- transform: the prompt asked for "one wide table" but the check says 23 columns,
  which only holds with ProbabilityClass, the S/M/J expansion and the specific
  join columns. Those are now in the prompt, and the check says what to do when
  the count differs.
- write: the prompt dropped the spreadsheet name, the sheet name and Update rows
  — and step 5 schedules this daily, so append mode quietly grows the sheet by
  639 rows a day. All three are named now.
- load: "one configuration with four rows" is the shape step 5 depends on; the
  prompt now says so and the check looks at the Rows list.
- automate: names the flow and the destination configuration, offers the
  two-phase variant for readers who skipped step 4, and replaces "drag them
  apart" — an interaction the page never documents — with the + phase control it
  does document.
- googlesheets: had no second prompt for the hand-back and used "sheet" for both
  the Drive file and the tab. Both fixed.
- Every block now ends with a check and a rejoin path, and the hub mentions the
  approval dialog Kai raises before it changes anything.
- project/ no longer claims every following step is Kai-replaceable; step 4 is
  not, and step 6 has no block.

Build clean, audit unchanged.
Michal's standing note on Getting Started is about shape, not accuracy: build
it as a customer journey — why should I care, then what, then how — and "not
feature lists". His bar is that a new user arrives, understands what is
happening, and can run it. The arc was accurate and mechanical: every page
opened with platform plumbing, and the index led with a table of what you would
build.

The sample data turns out to carry a real question. 639 opportunities across
275 accounts and 28 reps; 347 closed won, 192 still open — and 145 of those,
about 89% of the open amount, sit below 50% probability. The transformation the
arc already writes computes exactly that (ProbabilityClass) and turns OwnerId
into a person's name. The guide has been answering a real question all along
without ever asking it.

- The index now opens on the question — "where is the money in this pipeline,
  and how much of what is still open is unlikely to land?" — and on the state it
  starts from: four exports where a deal identifies its owner by an ID. Then the
  payoff (45 minutes, in the browser, nothing to install), then the existing
  "What you will build" table, which now reads as the how.
- Steps are named after what the reader gets, not the feature used: Get Your
  Data In, Join It into an Answer, Deliver the Answer, Run It on a Schedule.
  Slugs are untouched, so no redirects and no link churn a week after the
  /tutorial/ move. Get a Project and Where to Go Next were already outcomes.
- Cross-page references to the old titles updated, including three outside the
  arc (components/writers, the Tableau page, components/extractors).

The six page ledes follow in a separate pass. Numbered steps, screenshots and
the Kai blocks are untouched here.

Build clean, 310 pages; audit unchanged — 0 missing images, still the 35 known
/extend/ forward-refs. src/sidebar.mjs is unchanged on purpose: the generator
emits a bare slug when the nav title matches the page title, so Starlight takes
the label from frontmatter.
…y asks

Second half of the narrative pass. The index now opens on a question; these six
pages each carry one move in answering it, replacing the platform-mechanics
opener each of them had.

- project — you cannot answer anything without somewhere to work; kept as the
  shortest lede, since this page stands between the reader and the interesting
  part.
- load — a deal row carries an OwnerId, not a rep's name, and the four exports
  answer nothing apart. "The sample data" now says what the tables are rather
  than listing them.
- transform — the pivot, and the one lede that earns a number: 145 of the 192
  open deals, about 89% of the open amount, sit below 50% probability. The SQL
  below already computes exactly that.
- write — whoever asked will look in a spreadsheet, not in Keboola.
- automate — the answer is true this morning because you ran it by hand;
  tomorrow it is a screenshot.
- next-steps — the question is answered and stays answered.

Written by six parallel agents, one file each, then reconciled: dropped
"stalled" (below 50% probability is not the same claim), cut the third
repetition of the 145/89% figure so it lands once where it motivates the join,
fixed a three-files-versus-four slip, and normalised one contraction to the
arc's "cannot".

Every figure re-derived from public/getting-started/*.csv before committing:
639 / 275 / 28, 347 won, 192 open, 145 below 50% = 89% of the open amount,
Q1 2015. Build clean, 310 pages; audit unchanged — 0 missing images, the same
35 known /extend/ forward-refs.
Seven findings, and the pattern behind them is worth naming: writing a story
around the data made the prose drift from what the data and the SQL actually
do. Every number held; four claims about what they *mean* did not.

- **The index promised a column that does not exist.** It said the joined table
  carries the rep's seniority. The SQL joins tmp_level but projects nothing from
  it — that is why the arc's own check is 23 columns (15 + ProbabilityClass + 3
  user + 4 account), and why the reader would have ended up looking at a sheet
  with no seniority in it. Now: account and region, rep name and market, and a
  verdict on likelihood.
- **"Nothing says which deals are going anywhere" was false.** Probability is in
  the raw export and fully determined by StageName; the transformation only
  relabels it. The honest gap is that no file carries names next to the deals.
- **"You cannot see that in four separate tables"** — the 145/89% figure comes
  from opportunity.csv alone. The join buys who and where, not how much. The
  transform lede now says that.
- **"Four configurations" is three.** Four CSVs are four rows inside one HTTP
  configuration; the same page's What you need, its Kai prompt and its numbered
  steps all say three. Carried over from the old lede, kept by mine.
- "Q1 2015" now says **created in** Q1 2015: CreatedDate is entirely within the
  quarter, CloseDate runs 2014-07-31 to 2015-10-14.
- "145 — about 89% of the open amount" apposed a count to a percentage; by count
  it is 76%. Now "145 of them, carrying about 89% of the open amount".
- "About 45 minutes" had nothing behind it — reverted to the previous "under an
  hour" rather than flagging a number nobody has measured.

Also: "four exports where nothing lines up" — they line up exactly, 639 rows and
zero orphans. Now "none of which holds the whole picture".

Re-derived after editing: CreatedDate 2015-01-01…2015-03-31; 145/192 = 76% by
count, 89% by amount; Probability present in the raw file. Build clean, 310
pages; audit unchanged.
The arc opens on a question — where the money in this pipeline is, and how much
of what is still open is unlikely to land — and every step answers a piece of
it. The reader never asks it, though. They finish step 5 with a scheduled flow
and a Google Sheet, and the number stays something the guide asserts rather than
something their own project told them.

Step 5 now closes on that. A final section hands Kai the table the
transformation wrote and asks the question outright: how many deals are still
open, how many are Poor, and what share of the open amount those carry. The
check is 192 / 145 / about 89% — re-derived from the sample CSVs, with the note
that Amount carries no currency unit. A one-line follow-up asks which reps hold
the at-risk amount, and its check is deliberately soft (the top rep alone holds
roughly a third), because rep ordering is not worth making brittle.

This also balances how Kai appears in the guide. Until now it only stood in for
clicking — a builder substituting for the manual steps. Reading what you built
is the other half, and the reason most people keep the panel open. Grounded in
/kai/ ("Query databases, calculate metrics"), /kai/use-cases/#data-exploration,
and /kai/settings/#tool-permissions for the claim that this raises no approval
dialog: read-only tools default to Always allow. The prompts have not been run
against Kai in the demo project — the same standard as the arc's other five Kai
blocks, and the page's type marker says so.

next-steps is untouched on purpose. Its lede already opens "It is answered, and
it stays answered", which now describes something that happened on the page
before rather than something implied, and it stays a routing page with no steps
of its own. The arc index gains one paragraph, so the Kai contract set out in
"Two ways to do most steps" mentions the closing question.

Build clean, 310 pages; audit unchanged — 0 missing images, the same 35 known
/extend/ forward-refs. All three new anchors resolve in dist.
Reading the six ledes in sequence in the preview — the check no automated
pass covers — turned up one contradiction. The index and transform both say
the deals export alone gives you how much is at stake and how likely each
deal is to close, and that what it withholds is whose and where, since owners
and accounts are IDs. load's lede still said "None of them answers anything
alone", which is the claim 5ff5b5b corrected everywhere else on the arc and
did not reach here.

Now: "Apart, the deals file will tell you how much is open and how likely it
is to close, but not whose it is or where." Named the deals file rather than
"they" — only opportunity.csv carries Amount, StageName and Probability, and
both sibling ledes are already precise on exactly that point.

fact-checker: no criticals, seven claims confirmed against the sample CSVs,
including that the human-looking names in opportunity.csv do not leak the
real owner (the owner's surname stem coincides in 3 rows of 639, the account
name in 1). The three ledes now tell one story. guide-tester not run: no step,
prompt or screenshot changed, and there is no platform session to walk.

The rest of the sequence read holds — step 5 ends on the answer rather than on
troubleshooting, and 145/89% still appears exactly twice. Build clean, 310
pages; audit unchanged.
The two closing prompts had never been run against Kai — the one gap left in
the arc, and the block most worth testing, because it is the only Kai block
that asserts specific numbers come back. Ran both in project 264 today.

Prompt 1 came back exactly as the check promises: 192 open deals, 145 of them
Poor, 89.37% of the open Amount (355,597 of 397,888). Prompt 2 returned the
rep ranking with the top rep alone on nearly a third (113K across 12 deals)
and the top four on ~76% — "concentrated, not spread" holds as written.

No approval dialog for either prompt: Kai fetched the table detail and ran the
query unprompted. That is the sentence the fact-checker had flagged as thinly
cited ("expect an answer rather than an approval dialog"), and it is now
verified against the product rather than inferred from /kai/settings/.

One thing the run adds: Kai prints the totals with a $ sign the sample data
does not carry. The bare-numbers caveat already in the check covers it, so no
prose changes — only the type marker, which no longer says "not run".

Build clean, 310 pages. guide-tester not run: no step, prompt or screenshot
changed, only the marker.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Went through every page the migration touched, old against new, item by
item — headings, notes, callouts, links, product claims. The arc kept
almost all of it and grew 3-5x doing so: the transformation execution
model, mapping-as-safeguard, data lineage, temporary output tables,
"Changed in Last", "Recently updated by", reverse ETL, external
authorization, the fictitious-characters joke. The branches pages, ad-hoc
and workspace changed only in link paths and frontmatter. The best-practices
cheat sheet moved to /overview/onboarding/ and the arc now links it twice,
where before nothing in the tutorial did.

Three things had genuinely dropped out.

Component developers lost their entry point. The old index sent anyone
wanting to build a connector to a development project; nothing in the arc
did. next-steps now routes them to the Quick Start, and names publishing as
the separate approval-gated step it actually is.

The load step lost the reason its side trips exist. The old page framed
loading by project stage — quickest path for a proof of concept, connectors
once it is production. That framing is back under Going further, in terms of
what exists today rather than the deprecated CSV Import it used to describe.

The flow page promises parallelism it never shows. Two spots say tasks can
share phase 1; neither says how to put a second task into an existing phase,
and /flows/ asserts the capability without a UI path either. The old answer
was drag-into-the-Step-1-box, which is Legacy Flow Builder language and does
not belong in a Conditional Flows walkthrough. Flagged VERIFY(owner) rather
than guessed.

fact-checker caught two of my own claims and both are fixed here: the Quick
Start does not cover publishing, and the development project comes with
joining or creating a vendor, hedged in the source — not with "registering
as a developer", and with no documented isolation guarantee.

Build clean, 310 pages; audit unchanged at 0 missing images and the same 35
known /extend/ forward-refs, with both new links resolving. guide-tester not
run: no step, prompt or screenshot changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Checked every link the arc emits. Internal side is clean: 0 broken links
out of 19 built pages, 38 cross-link targets across 14 sections, and no
orphans — the branches sub-pages are densely cross-linked within their own
section and every page is in the sidebar, including the Going Further group.
Inbound, 37 links from 13 sections point back into the arc, most of them at
load/database and load/.

The external side had rot, all of it on ad-hoc — the page the migration
carried over nearly untouched. Fixed against live checks:

- bigquery.cloud.google.com is a retired domain; the deep link into the
  classic BigQuery UI now bounces to a sign-in page. Repointed at the console,
  and reworded, because the console opens on the table's details view and
  Preview is a tab the reader has to click.
- The Bureau of Labor Statistics public-data page is gone and redirects to a
  generic index. Repointed at the Marketplace product, and the dataset is now
  named the way the reader will actually type it.
- rstudio.com redirects to posit.co after the rebrand; jupyter.org was http.

The service-account link needed two passes. Dropping the dead
#service_accounts anchor was wrong: the page behind it is now "Authenticate to
BigQuery", which covers impersonation and steers away from JSON keys — the
opposite of what step 3 below tells the reader to download. It points at the
IAM create-a-service-account page instead.

One thing the checker found that predates all of this: the page said to
"create an account, and enable billing", linking a page that says the first
terabyte a month is free precisely *without* billing. Corrected to match.

The two bls.gov links are left alone. They answer 403 to every automated
request from here, with no redirect, even with full browser headers — that
reads as datacenter-IP filtering rather than a dead page, so it is flagged
VERIFY(owner) for one look from an ordinary browser rather than guessed at.

Still stale on that page and out of scope for a link pass: "Google Cloud
Platform Console" and "Google Storage bucket" naming, and the Sandbox vs
Workspace drift the caution at the top already warns about.

Build clean, 310 pages; audit unchanged. guide-tester not run: no step,
prompt or screenshot changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Opened both in a real browser on the same host that curl gets 403 from:
/cps/tables.htm serves "CPS Tables" and /web/empsit/cpseea10.htm serves
"A-10. Unemployment rates by age, sex, and marital status, seasonally
adjusted" — which is the table the page describes.

So BLS filters on request fingerprint, not on IP. The VERIFY(owner) asking
for that check is replaced by the result, plus a note that a 403 on these two
from a link checker running here should be read as a false positive. Whoever
runs the audit next would otherwise re-open the same question.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…oes not

The page said R work is done in RStudio. /workspace/ owns workspace types and
says scripting languages get JupyterLab with the matching kernel, naming no
separate R environment. Two of our own published pages disagreed.

The sentence now names no tool and sends the reader to the page that owns the
answer, so it is right either way. Whether RStudio is retired outright is still
the workspace owner's call, and the note records that "rstudio" survives
elsewhere only as a sandbox-type enum in the telemetry data dictionary, which
is not a statement about what the UI offers.

That clears the last flag on this page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"How every page works" told the reader their choice follows them from page to
page. That sentence is the one thing about this section a reader has to take on
trust, because a screenshot cannot show it and the reader has not yet met a
second page. Now there is a GIF of it: pick CLI / API on Get your data in, open
Transform data, and it is already on CLI / API, with the sidebar highlight
moving to prove the page changed.

It follows the pattern the component docs already use for UI behaviour rather
than the two large GIFs elsewhere in the repo. Cropped to the region that
carries the point, five states rather than smooth motion, held by repeated
frames that gifsicle collapses back out: 1000x384, 7.2 s, 104 KB. For scale,
kai/Capabilities.gif is 2.5 MB and the MCP one is 9.5 MB.

Recorded against the local preview of this branch, so it needed no project and
carries no project data.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The rule these follow came from studying what the repo already has. The Kai
page's GIF is a 2.5 MB designed motion graphic, the MCP page's a 9.5 MB screen
recording, and the six on the component UI-options pages are the pattern worth
copying: cropped to one control, a handful of states, under 250 KB, placed
under the sentence they prove. A GIF earns its place when the reader cannot
click the real thing and a still cannot carry it, which on these pages means
hover-revealed UI.

automate/ gets add-task.gif, 860x430, 42 KB, five states from the live
Boolabean morning outlook flow: the standalone + above the first phase, the
tooltip hover reveals, its Add Task menu, and the component picker Component
opens. It replaces the still of the menu, which the conservation report logs.

load/ gets forecast-row-code-editor.gif, 1230x258, 19 KB, two states: the
forecast row's disabled Visual Editor button and the note that only appears on
hover, "Can't close the code editor, the configuration is not compatible.
Revert your changes to allow switching back to the visual editor."

The walk also settled a flag, though not the way my first edit said. There are
two Add Task menus. The phase toolbar's + offers Component, Notification,
Variable, exactly what the page had always stated. The standalone + between
phases also offers Conditions, because a condition sits between phases rather
than inside one. My first edit put the four-item list on the toolbar control I
had not clicked; the fact-checker caught it against the retired still, which
shows the toolbar's three, and the page now names both controls. Neither menu
has a Build with Kai entry in this project, so that unverified clause is gone.
The empty-canvas menu of step 2 stays flagged: settling it means creating a
flow.

Every frame is cropped below the top bar, so the organization, project and
branch names never enter a frame. Nothing was saved in the flow or the row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Nikita and others added 2 commits September 22, 2026 20:29
…lly serves

The PRDCT-616 mask edited public/tutorial/**. This section had moved to
public/getting-started/** three weeks earlier, so the mask missed every copy
the guide serves. Checked by hash rather than by eye: all 44 files on this
branch were byte-identical to the unmasked originals.

Forty-three now carry the masked blobs, each verified to hash exactly to its
counterpart in af990ae.

The forty-fourth is deleted. ex-bigquery-6.png showed a pasted Google
service-account JSON with a private key in it, and the sentence above it
already tells the reader what to paste, so masking it would have left an image
of nothing. Both our house style and Google's own guide say not to use images
of text or code. The key was confirmed inactive on 2026-08-27 and that
escalation stays closed; this is not a re-raise.

This does not fix production. PR #1100 is still open with zero reviews, main
does not carry the mask, and the old path still answers 200 with the pre-mask
byte count. That is a merge, not an edit.

The ad-hoc page also gains a caution that its Google Cloud steps describe the
2019 console: read them as a list of what to create, not what to click. That is
the third-party rule from the usability test, applied where the drift is widest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…enshots

Sixteen files carried something that should not ship and none were in the
scrub's list. Four were saved by cropping and checked by eye afterwards: two
list views repeating a colleague's name down a Last Change column are now just
the Components header and the button their steps point at; one panel now starts
below its "Authorized by" line; one now starts below the top bar.

Ten could not be saved. Five are Google's own screens, three of those with a
colleague's address in frame. Three are workspace list views with a name in the
Last Change, Last Use or Versions column. One is the Connect dialog showing a
live Snowflake host, user, database and schema, and stale besides, since it
shows password auth where the workspace now issues a key pair. The last had an
unmasked top bar and contradicted its own step: the text says the click opens
the spreadsheet, the picture showed a Keboola page.

The Google steps are the first use of the rule the usability test produced.
Michal lost time on a Google screen whose button had moved since we wrote the
step. Those steps now say what to do there and why, and link Google's own help,
instead of naming buttons we do not control and shipping a picture of them.
What Keboola shows still gets a screenshot; that part is ours and verifiable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every remaining image went through contact sheets: 55 files outside the two
directories the scrub covers, plus a strip of the top 56 pixels of every
screenshot that could carry a header. Three more had the organization, project,
branch and avatar in frame, all on the Sheets writer page. All three now start
at the breadcrumb.

What the sweep cleared is worth recording so nobody repeats it: the flow and
Storage screens carry internal job labels and scheduler IDs rather than people,
the Storage list views show component names in "Recently Updated By", and the
notifications screen's mention of colleagues is interface text with no address
in it.

Three unreferenced files go with this: one lost its page in the rewrite, one was
superseded by the GIF that replaced it, and one was byte-identical to the copy
the page actually links. A fourth looked unreferenced from inside Getting
Started and was not, since the legacy Flows page still uses it; it was restored
before committing. Check the whole content tree, not the section.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…that exposed

Michal pressed Publish to production, saw nothing happen, and could not tell
whether the button had worked or he had missed a step. The page already said so,
but after the click. Step 5 now says it first: expect no dialog, then go to Apps
and press Deploy App.

Two more from the same test. A cold-landing sentence for readers arriving from
the Apps announcement without the six previous pages behind them. And the
authorization boundary stated plainly rather than hedged, because product said
it on the call and kbagent data-app create --auth accepts only password or
public.

The fact-check then caught four things, three of which the rewrite caused.

The cold-landing sentence told readers to swap the table name and read the rest
as written; the prompt hard-codes seven column names, so they would have handed
Kai six columns that do not exist. It now says to adapt the columns too and
points at the from-scratch Apps page.

The page opened by promising an app with its own URL and a password while step 5
now ends on the draft preview. The promise matches the step now.

Naming four OIDC providers read as the complete set of sign-in gates. There are
six methods, and GitHub, GitLab and JumpCloud also need a client registered in
the provider's console.

The check page's summary table called the app "the grid anyone can open", which
contradicted both the password gate and a deploy that has never succeeded.

Also corrected in the notes: today's container run cloned successfully and
failed only on the missing nginx directory, so the Git credentials are working
again and nginx is the durable cause. I had written "nothing has changed", which
was wrong.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…id what success is

Step 5 told the reader what not to expect and never what to expect. It also ran
two different facts together: no confirmation dialog is designed behaviour, no
deploy job is the defect this page documents. The step now separates them, names
the positive confirmation, and drops the hedge on the status.

Two steps on the Sheets writer leaned entirely on pictures that are now gone.
Step 4 said "authorize the account" and named no control; it names the section
and the button. Step 12 said to click the spreadsheet name without saying where
it appears, which matters because the Prompt guard routes readers straight there
without ever opening the configuration.

That page's numbering also argued against its own tabs: steps 1 to 6 are shared,
7 to 10 are UI only, 11 and 12 are shared again, and flattened into the markdown
twin that reads as one run of twelve. An agent could run the prompt and then
also walk the wizard, creating the sheet twice. A guard now sits before the UI
steps as well as after them, and it survives into the twin.

Three smaller ones. The Sheets UI tab never named the configuration while the
Prompt tab used the name twice. Step 8 kept the what and lost the why when the
vendor button names went; the rationale is back with the consequence of
under-granting. And one sentence used "Connect" for both a menu and an item
inside it, which the deleted capture used to settle.

The app page's prerequisite also stopped demanding a table from a page the cold
reader skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…y could place

Michal saved a password, saw a long unfamiliar value come back, and read it as a
bug. It is encryption. The database side trip now says so in the step where it
happens, and names the rule: any field whose name starts with # behaves this
way, including the JSON in the CLI tab. Warning after the surprise is not
warning.

Two flags hedged whether Kai can click through an OAuth consent screen, on the
grounds that Kai's use cases mention setting up a connector "with OAuth
authentication". That is configuring auth, not granting it. Product settled it
on the call, and kbagent data-app create --auth accepts only password or public,
so one flag closes and the unverified "and so does Kai" clause comes out of the
Sheets side trip rather than standing as a claim nobody checked.

Three images had no sentence placing the reader. The configured transformation
and the new Storage table each gained one, with alt text in the house format.
The third, Advanced Mode, was decorative by its own step's admission, so it goes
and the sentence says where the button is instead.

The image budget was measured rather than assumed: four files exceed 250 KB, all
four are 2x retina captures, and downscaling the two widest made them larger,
because the originals compress better than ffmpeg manages and no optimizer is
installed. They stay, with the reasoning recorded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…blocked

The PR's base was changed to main, which had moved 91 commits ahead, so it
stopped merging. Three files conflicted.

navigation.yml was the only one needing judgement: main still carries the whole
/tutorial/ tree that this branch retires, and had added "Our APIs" under
Overview, which this branch never saw. Both decisions are kept. sidebar.mjs was
regenerated from the resolved source rather than merged, since it is generated.
custom.css was not a real conflict: this branch appends the Getting Started
blocks, main appends print rules, neither touches the other.

The merge paid for itself. Broken internal links went from 29 to 0 and audit
issues from 116 to 59, because main brought the pages those links were waiting
on. The command reference is now generated from kbagent v0.91.0 instead of
v0.76.1, so the gate no longer runs fifteen minor versions behind.

It also closed a deferral. Two links pointed at developers.keboola.com for
encryption and the API overview, left deliberately because help had no
equivalent and retargeting would have broken them. Both pages now exist, at
/extend/encryption/ and /overview/api/, so the links point inward and the
section has no dev-domain links left.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…f them hid

A humanizer pass over the whole section. The dash was doing the work a period,
a colon or a comma should do: 55 of them in reader prose, plus five more in the
shared components, so every page carried the habit whether or not its author
did.

Two of them survive on purpose. The em dash in the hub table is the "no value"
column marker, and "Google Sheets — Data Source" is the string the component
picker actually renders, quoted from the screen beside it.

Counting the commands while rewriting turned up a real error: the CLI tab on
app/ announced "Three more worth knowing" above four code blocks (start, logs,
runs, password). It says four now.

Two other rewrites where the wording was staging rather than stating: ask/ led
a follow-up prompt with "An answer is a place to start, not to stop", which
says nothing about what the prompt does, and closed the multi-prompt section
with a contrast against a claim nobody made.

Contrasts that correct something the reader actually believes are kept as they
were: "two tabs here, not the three the main path uses", "an account, not a
public URL", "alternatives, not steps".

Legacy pages (branches/, ad-hoc/) got the double-hyphen typography only, not a
rewrite, since they are already booked for one in MISSING. "Storage -- Files"
became "the Files tab in Storage" so it reads as navigation, and one "but is
was not" typo went with it.

Build, CLI gate and audit unchanged at baseline (59 issues, 0 missing images);
link check 0 blockers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… collisions

#1120 landed the dev-docs retirement while this branch was in review, and it
touched two files this branch also owns.

tutorial/index.md: main repointed one developer link inside the page; this
branch deletes the page, because the section it holds now lives at
/getting-started/. The deletion stands. The link target #1120 introduced,
/extend/development-project/, is reached from main's own pages, so nothing is
stranded by dropping this one instance. Its ancient `redirect_from:
/getting-started/` goes with it, which is what frees the slug this branch needs.

management/jobs/index.md: both sides edited the same line. Main's side is the
reviewed fix, dropping a three-state list that contradicted the Jobs API page
and pointing at the canonical /extend/jobs/#job-status. Main's text wins; this
branch's change was only /tutorial/ -> /getting-started/, so that repoint is
re-applied on top.

One more repoint the move had left behind: storage/api/tde-exporter linked to
/tutorial/write/, the last inbound link to a retired slug. It points at
/getting-started/write/ now, which takes the link check to zero warnings.

Verified: build 376 pages; audit 55 issues (down from 59 on main's fixes), 0
broken internal links, 0 missing images; CLI gate 0 findings; link & redirect
check 0 blockers, 0 warnings. CRLF preserved in jobs/index.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — c5b60afe Deployed Sep 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants