Skip to content

release: website dev to main - #81

Closed
jm-rivera wants to merge 16 commits into
mainfrom
dev
Closed

jm-rivera wants to merge 16 commits into
mainfrom
dev

Conversation

@jm-rivera

Copy link
Copy Markdown
Contributor

Releases dev at 6dc181b8 to production: eight pull requests and three direct commits, each deployed and green on staging.

Studio: #73, #74, #76 and #80 show a plan's scope choices and planner decisions as switchable rows on the plan card, and keep the approve bar on screen, reading the events dfo-ai #88 and #95 send. Search: #66 groups entity-choice menus by entity_choice, and #69 and #70 vendor and re-lock the client at sdk 97df221. #68 forwards the SDK clients' X-DFO-Client label upstream. The direct commits 41dd678 and a887a6a add noindex headers (next.config.ts, src/lib/crawl.ts), a sitemap and crawlable gallery previews, and 9f24d1f waits for test connections to close.

The deploy runs no migration and adds no env var or workflow change. Merge after the ai release has deployed, since the plan card's switches need the harness events it sends.

jm-rivera and others added 13 commits October 25, 2026 17:30
An option or slot token carrying entity_choice joins the
no_data.ambiguous_entities group of that index, so two groups written
alike ("aid from Congo to Congo") open two menus, each from its own
slot. Without the field, menus group by mention text as before.

Co-authored-by: Claude <noreply@anthropic.com>
The client tarball is built from DevelopmentFinanceObservatory/sdk
97df221, whose wire types carry entity_choice on NoDataOption and
SentenceToken, so src/lib/search/types.ts re-exports both from the
client again.

Co-authored-by: Claude <noreply@anthropic.com>
… links

Search Console reported pages indexed though blocked by robots.txt, 404s
and other 4xx answers. robots.txt blocked /s, /embed, /studio, /history,
/login and /register, so Google could not read their noindex. Those
routes now leave the block list and send X-Robots-Tag: noindex from one
list in src/lib/crawl.ts, which next.config.ts serves on every response,
redirects and errors included. A /data?share= page is indexable only
when a dataset item in GALLERY_ITEMS names that share. robots.txt now
blocks /mcp and /s/*/download and names a new sitemap of the public
pages. Docs pages advertise and link a .md copy only when the page has a
corpus file, so pydeflate and resolvekit no longer point at a 404.
A gallery search at /data?share=<id> loads its result in the browser
from /api/share, which robots.txt blocks, so crawlers saw only the empty
Get Data screen. The page now looks up the gallery item with
gallerySearch, the same lookup the robots rule uses, and the server
writes its title, description and data sources into the page, with the
title as the page's h1, and into the page title and Open Graph tags.
DataSearch renders without a Suspense boundary, since the route is
dynamic and the first HTML can hold the whole view. Other shares get no
summary and stay noindex.
The test harness ended its pools and then terminated every backend left on
the test database. pg-pool's end() resolves once it has asked its clients
to close, before their connections have. A backend that had not yet read
its client's Terminate got the terminate first and sent the client a FATAL
error, which the ended pool emitted with no listener. CI failed
limit-counter.test.ts this way while every subtest passed.

The harness now records each connection its pools open and teardown waits
for all of them to close before it terminates stragglers.
…indexing

fix(seo): let crawlers see noindex, add a sitemap, stop dead Markdown links
…ounter-flake

fix(db): wait for test connections to close before terminating them
The platform proxy now carries x-dfo-client to the APIs, cleaned as the
APIs clean it (non-printable characters and edge space dropped, cut to 256
characters) and left out when blank. Every other x-dfo-* request header
stays blocked. The label is caller-chosen and serves usage counting only.
* feat(studio): switch how a plan figure is counted from the plan card

Decision rows that carry a scope choice list its options: the active one
marked, available ones selectable, partial ones with their years and
unavailable ones disabled with their reason. A pick sends a switch with
the plan revision through the interrupt stream, keeps the card until the
re-offered plan arrives, and shows what moved. A stale revision or an
unavailable option leaves the card with a notice. The wire mirror gains
the plan revision, requirements, operations and asks it lagged on.

* fix(studio): keep the switch control usable after a refusal

A stale-revision refusal stops further switches on that card and says
why; options in flight keep focus; the moved note and notice stay
mounted so screen readers announce them; a plain detail body reaches
the notice.

* fix(studio): replace a stale plan card with the one the harness holds

A switch or approve refused as plan_revision_stale now takes the
current_plan the harness returns and shows it with the refusal's
detail, so switching and approving continue on the plan actually
pending. Approve sends the plan revision the user saw.

* feat(studio): render reading chips after an answer and switch on a click

Studio shows the harness's apply_choice chips beside the follow-up
prompts on the latest answer. A click sends a turn carrying the chosen
option and no message, shows the chip label as the user's side, and
streams the answer like any turn; a refused chip shows the reason and
is removed.

* docs(studio): tidy the plan switch and chip comments

* feat(studio): send the turn and reading a chip was offered for

A chip carries the turn whose answer offered it and the spec_id of the
reading it switches, and the click sends both in apply_choice, so the
harness can refuse a chip whose answer or reading is no longer current.

* fix(studio): keep a stale plan unapprovable and own turn starts and ends in one place

A switch refused as stale keeps the card's revision, so an approve is
refused, and only the harness's switch refusals count as refused
switches; any other failure ends the turn. stream() guards every refusal
against a stopped or superseded turn, send and applyChoice open a turn
through one startTurn, a chip clicked mid-request queues, a forked
conversation shows no chips, and the card shows its notice.

* fix(studio): count a chip click as a prompt

A chip passes the guest prompt limit and is tracked as studio.prompt
with intent apply_choice, through the one wrapper typed prompts use.
…approved plan under its answer (#74)

* feat(studio): lead the plan card with what is counted and keep the approved plan under its answer

The plan card shows the plan's title, a one-line summary, each scope choice with Change, the step that gives the answer and a View full plan button. A failed switch keeps the card and states its reason under the choice it concerns. A changed value carries a Changed tag. plan.approved attaches an approved-plan pill to the answer, stored with the message and opened read-only. A harness that sends none of the new fields still gets a card.

* fix(studio): settle a switch that ends without a plan and make the choice rows accessible

A switch stream that closes, errors or is stopped without a plan returns the card to idle with its notice and leaves the paused reply untouched. Every refusal shows the harness's detail; 401 and 429 offer no retry. Options are buttons with aria-pressed in a group, focus returns to the switched row, and the notice sits in a live region mounted empty. An older harness's card leads with the question.

* docs(studio): tighten the plan card's comments

* fix(studio): mark a row Changed against the turn's first plan

A switch re-pins a part, so a choice's id carries a new spec id; rows are matched by their axis. A row is Changed when its option differs from the turn's first plan, so two switches mark both rows and a switch back marks none.

* fix(studio): keep the plan card live only on a server pause and key choices on their stable ids

Stop on a switch sends cancel and keeps reading: a re-offered plan makes the card live again, and a stream that ends without one leaves it read-only with its Activity entry closed. Try again follows switch_failed.retryable or the refusal's retry mode. Choices are keyed on choice_id, with the axis only for an id missing from the earlier plan, and a stale refusal keeps the turn's first plan as the baseline. Map.groupBy is replaced for older Safari. Conversation search matches message text only. The approved plan shows its caveats.

* fix(studio): end a gone pause read-only, show caveats before approval and quiet a stopped switch

A refusal for a pause that no longer exists ends the card read-only. A choice falls back to its axis only when its id was replaced. 401 and 429 never offer an immediate Try again, and a busy refusal offers it after its retry delay. Moved choices with no row, each choice's reason and the plan's caveats show on the card, and a stopped switch is a quiet note. Switch settling moves out of stream() into an onEnd callback, notices share one builder, and the card and full plan share one approve bar. Conversation search also matches a user message's card title, with a database test.

* fix(studio): keep a switch's card live when the harness was unreachable and match choices by id only

A switch whose request got no response keeps the card live with Try again; only a server-confirmed end makes it read-only. Every failed switch clears the steps-updated mark. Choices match on choice_id alone. The fallback answer step is the last answering step in plan order, the headline takes the harness's title when it has one, caveats key by index, and the three switch notices share one builder.

* fix(studio): give a stopped switch time to be re-offered and time retries from the notice

A switch's stop grace (15 s) outlasts the harness's 10 s re-offer, and repeated Stop clicks send one cancel. A 429 with a retry delay offers Try again after it, timed from when the notice arrived. The switch-ended path reuses endTurn, stream() handles a caller-owned end in one place, and the live card and approved view share the answer-step rule and the caveats list.

* fix(studio): end a switch read-only unless its refusal left the pause alone

Only a busy, option-unavailable or rate-limited refusal keeps the card live; every other refusal ends it read-only with a reader sentence, and a proxy's raw text goes to the console. Try again appears only when the harness asks for a retry after a delay. done and error frames end the turn when they arrive, a stopped switch keeps its Stopping line, a Stop after a fresh plan sends no cancel, and a switch stream's Activity entry closes by how the harness ended it.

* fix(studio): keep the plan live when the site proxy refused a switch

A switch the proxy refused before the harness answered (its 502 with
no upstream status, or its 429) left a live pause behind a read-only
card or a row without Try again. The proxy's 502 now names the
upstream status when the harness had answered, the card stays live
otherwise, and a proxy 429 offers Try again after Retry-After. A live
row shows detail only from a harness ToolError, and a stopped switch's
notice is quiet.

* fix(studio): let Stop end a switch still waiting to be sent

A switch picked while the paused stream was still open waited with no
halt, so Stop did nothing. The halt is published when the stream is
queued, and a Stop before the request starts skips it and keeps the
card live with a quiet note. A Stop followed by a refusal ends as
stopped, and the approved view guards stored steps and choices.

* fix(studio): end a stopped switch as stopped whatever answers it

A refusal after Stop ends the switch with the quiet stopped note, a
Stop on an unsent switch clears its halt so a later switch can be
stopped, and only a switch publishes its halt while queued. A proxy
429 offers Try again only with a positive Retry-After and reads
"Studio is busy."

* fix(studio): end a stopped switch as stopped when its fetch fails

A switch the user stopped whose fetch then rejected came back as a live
card reading "Studio could not be reached." It now ends as stopped, as
a refused one does.

* fix(studio): announce a switch's outcome and say steps changed only when they did

The card keeps one polite live region that states what a switch
changed, as the moved-choices note did, and "Steps updated to match."
shows only when the step labels differ from the previous plan's.

* docs(studio): tidy the plan card and switch comments
…screen (#80)

* fix(studio): let a tall plan card scroll so the approve bar stays on screen

The plan stack had flex-shrink: 0, so a plan taller than the chat foot
pushed the "Start building?" bar and the prompt input below the clipped
100dvh section. The card now gives up height and scrolls inside; the
bar, input and note keep their natural height.

* fix(studio): keep the plan card usable on short screens

At 844x350 to 370 the plan card's scrollport collapsed to zero height,
so View full plan and the scope controls could not be reached, even by
keyboard. The card now stops shrinking at a floor of rem(120), its
wrapper takes no box of its own (display: contents), and below that
floor the chat foot scrolls as a whole, with bottom scroll padding so
focus brings the input fully into view.
…ard (#76)

* feat(studio): show planner decisions as switchable rows on the plan card

The harness sends a planner decision the user can switch as a plan-card
row with choice_id decision:<slot>. The ledger groups those rows under
"Choices the plan made", apart from the scope rows under "Counted as",
and a decision switch shows "Replanning" until the harness answers,
since it runs a planner replan. switch_failed and Stop take the scope
switch's paths.

* fix(studio): read planner decision rows by the decision/<slot> id

* fix(studio): show a planner question asked during a switch

A question the replan asked while a planner-decision switch waited kept
the "Replanning" card on screen and hid the picker. The question now
takes the card's place and its pause carries the waiting plan; the
answer resumes as the same switch, so a fresh plan, switch_failed, Stop
and a second question still end it.
…approved planner decisions (#83)

* fix: keep crawlers out of event counts, keep preview cards, and file approved planner decisions

track drops bot requests, page openGraph keeps the site name and root card, /data share and spec URLs send X-Robots-Tag, approved choices file planner decisions by choice_id, a refused chip refunds the guest prompt, shared wordings are numbered in the entity menu, and a stale switch without a current card ends read-only.

* fix: refund any refused guest turn and decide /data noindex in one function

stream refunds a metered turn's prompt on any refusal, as meterTurn does. src/proxy.ts sets /data's X-Robots-Tag from dataPageRobots, the function the page metadata uses. Preview cards carry their size and type, and a gallery dataset previews with its own card.
The client asks the file selector only for its keys and reads nested columns. Shares accept the name_corrected assumption and the view states it as a corrected name.
@jm-rivera

Copy link
Copy Markdown
Contributor Author

Replaced by the release PR from the cut branch release/2026-10-29 at 24e53dc.

@jm-rivera jm-rivera closed this Oct 29, 2026

This branch was successfully deployed

1 active deployment
dev — 24e53dcf Deployed Oct 29, 2026 by jm-rivera via deploy / deploy #45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant