Skip to content

docs: requests that outlive a turn, and SDK 1.28.0 (#1635) - #1860

Merged
jhgaylor merged 2 commits into
mainfrom
stack/1635-9-docs
Sep 11, 2026
Merged

jhgaylor merged 2 commits into
mainfrom
stack/1635-9-docs

Conversation

@lex00

@lex00 lex00 commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

The permissions page, ADR 0014, the CHANGELOG and SDK 1.28.0.

The version has moved twice while this stack was open: 1.26.0 went to #1634 (#1839) and 1.27.0 to #1637 (#1849), and both are tagged. So this bump is 1.28.0, and whichever SDK release lands next re-bumps again. The bump is four edits, not three: package.json, the lockfile's two entries, the USER_AGENT in src/http.ts and the changelog heading, which sits above the 1.27.0 entry rather than replacing it.

Part 9 of 9 for #1635, on #1859.

🤖 Generated with Claude Code

https://claude.ai/code/session_01S9jevFQT5MkF3rJUieeiHW

Closes #1635

@jhgaylor jhgaylor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes. One blocker, and it's the kind that passes every gate and then quietly does nothing.

The SDK version collides with one already released.

This PR bumps the TypeScript SDK to 1.26.0. main is already at 1.26.0 — the acp runtime stack took that number, the tag sdk-v1.26.0 exists, and main's changelog has its own ## [1.26.0] — 2026-09-10 entry:

main:      ## [1.26.0] — 2026-09-10   ### Changed   (runtime_command, acp runtime, Agent["model"] nullable)
this PR:   ## [1.26.0] — 2026-09-10   ### Added     (pending_requests, turn waiting, ask_timeout)

The gate is green here only because it compares against this PR's base, which is still 1.25.0. After the rebase onto main the version is unchanged relative to main, so nothing publishes — the detached-request types land on main unreleased, which is precisely the state the gate exists to prevent. Nobody finds out until someone installs the SDK and the types aren't there. git merge-tree already flags sdk/typescript/CHANGELOG.md as a conflict against main, which is the visible half of the same problem.

Please move this to 1.27.0 across all four edits:

  • sdk/typescript/package.json
  • sdk/typescript/package-lock.json
  • USER_AGENT in sdk/typescript/src/http.ts (currently fountain-sdk-js/1.26.0, identical to main's)
  • the sdk/typescript/CHANGELOG.md heading

Worth noting npm's latest published is still 1.24.0 while main carries 1.26.0 and the tag exists, so it's worth checking that the 1.25.0/1.26.0 publishes actually completed before adding another on top. A failed publish is invisible to every gate we have.

(The Elixir static analysis red is #1858's deleted blank line.)


The prose is genuinely good and I'd merge it as-is otherwise. The ADR 0014 amendment does the thing the ADR rules ask for and that amendments usually skip: it says what the original paragraph was right about (a request held inside a running turn, the idle bound, the cost rule) before saying what it doesn't reach. Anyone reading 0014 top to bottom now ends up with an accurate model instead of two paragraphs that appear to contradict each other. The "Nothing is resolved that cannot be delivered" paragraph in particular is the invariant the whole stack is built around, and having it stated once in the ADR rather than only in five code comments is what makes it survivable.

Small thing, take it or leave it: 0014's frontmatter still carries verified: 2026-08-24 and the index row is unchanged. The description didn't change so scripts/decisions-index.sh regenerates identically, but the ADR now describes behaviour that verification date predates.

@jhgaylor jhgaylor changed the title docs: requests that outlive a turn, and SDK 1.26.0 (#1635) docs: requests that outlive a turn, and SDK 1.27.0 (#1635) Sep 11, 2026
@jhgaylor jhgaylor changed the title docs: requests that outlive a turn, and SDK 1.27.0 (#1635) docs: requests that outlive a turn, and SDK 1.28.0 (#1635) Sep 11, 2026
@jhgaylor

Copy link
Copy Markdown
Collaborator

⚠️ SDK version collision — two open stacks both claim 1.28.0.

main is at 1.27.0 and npm's latest is 1.27.0, so both are individually correct today and both release gates are green. They cannot both merge.

This is the same trap that already bit each stack once: 1.26.0 was taken by #1634 mid-flight, then 1.27.0 was taken by #1849/#1637 mid-flight. The gate compares against the PR's base, not main, so a stale-but-green bump stays green right up until it merges and silently publishes nothing.

Before merging whichever of these goes first: leave its number alone, then re-number the other to 1.29.0 across all four edits — sdk/typescript/package.json, sdk/typescript/package-lock.json (two places), USER_AGENT in sdk/typescript/src/http.ts, and the CHANGELOG.md heading. Bumps have to land in ascending order, so the second one cannot simply keep 1.28.0 and be fixed afterwards.

Two further re-cut stacks (#1565 reapply, #1125 OAuth self-service) are in flight and will each want a number too. Worth deciding the merge order for all four in one go rather than discovering it a gate at a time.

@jhgaylor

Copy link
Copy Markdown
Collaborator

⚠️ Updated: this is now a three-way SDK version collision.

All three of these claim 1.28.0, against a main (and npm) at 1.27.0:

PR stack branch
#1860 detached permission requests (#1635) stack/1635-9-docs
#1876 bounded sandbox queue (#1033) stack/1033-7-docs
#1885 self-service OAuth clients (#1125) stack/1125-9-docs

Each is individually correct and all three release gates are green, because the gate compares against the PR's base, not main. Only the first to merge can keep the number.

Merging any one of these obliges re-numbering the other two before they merge1.29.0 and 1.30.0 in whatever order they land. Four edits each: sdk/typescript/package.json, sdk/typescript/package-lock.json (two places), USER_AGENT in sdk/typescript/src/http.ts, and the CHANGELOG.md heading. Bumps must land in ascending order, so this cannot be corrected after the fact.

A fourth stack (#1565, conversation reapply) is still being cut and will want a number too.

Given that, it is probably worth picking the merge order for all four now and assigning 1.28.01.31.0 up front, rather than resolving this one gate at a time. Each of these stacks has already been re-numbered once or twice mid-flight for exactly this reason (1.26.0 went to #1634, then 1.27.0 to #1637/#1849).

@jhgaylor

Copy link
Copy Markdown
Collaborator

⚠️ Updated again: four stacks now claim 1.28.0. main and npm are both at 1.27.0.

PR stack branch
#1860 detached permission requests (#1635) stack/1635-9-docs
#1876 bounded sandbox queue (#1033) stack/1033-7-docs
#1885 self-service OAuth clients (#1125) stack/1125-9-docs
#1893 conversation reapply (#1565) stack/1565-8-docs-sdks

All four are individually correct and all four release gates are green, because the gate compares against each PR's base, not main. Only the first to merge keeps the number, and bumps have to land in ascending order — so this cannot be corrected after the fact.

Recommendation: take the version bump out of all four, and do one SDK release afterwards.

Assigning 1.28.01.31.0 up front works only if the four stacks then merge in exactly that order, and each of these has already been re-numbered once or twice mid-flight precisely because merge order kept changing (1.26.0 went to #1634, then 1.27.0 to #1637/#1849). Pinning an order now just sets the trap again.

Instead:

  1. Label all four final PRs sdk-no-release and drop the four-file version bump from each, keeping their generated openapi.ts changes.
  2. Land the stacks in whatever order suits.
  3. Open one SDK release PR bumping 1.27.01.28.0 once, with a changelog entry covering all four features.

That removes the ordering constraint entirely, gives users one coherent release note instead of four near-simultaneous minors, and means no stack can silently publish nothing by merging second.

The cost is that the SDK types for whichever features land first sit unreleased on main until that final PR merges. That is exactly what sdk-no-release exists to express, and it is a far smaller risk than a bump that merges green and publishes nothing.

@BinaryBourbon BinaryBourbon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. The right documents changed and nothing describes behaviour that is not in the stack.

The 0014 amendment is the piece that had to be got right, and it is: it does not quietly rewrite the paragraph about the five-minute ceiling, it says that paragraph is still correct for a request held inside a running turn and that a second shape now exists beside it. It also keeps the cost rule this ADR exists to defend explicit — "No sandbox is held open, so the cost rule this ADR keeps is untouched: what changes is that the card outlives the turn, not the machine" — and records the one genuine departure (the connection not surviving the detach, against #817's sandbox-scoped rule) with the per-turn request-id collision as the reason. That is the sentence someone will need in six months. okf validate is green.

docs/concepts/permissions.md folds the new timeout into the existing "Nobody is also an answer" rule rather than appending a section that contradicts it, which is what the acceptance criterion asked for, and the deadline precedence is given as the three-item list with the shorter-wins rule and why (the request is written inside the sandbox, the policy belongs to the tenant). The note that a client can tell a resume turn from a typed prompt by the prompt being one JSON object under fountain/permission_answer is a useful thing to have written down, since origin stays user.

The SDK bump is a correct four-part edit — package.json, package-lock.json, USER_AGENT in src/http.ts, and the ## [1.28.0] heading — and it is in the last PR of the stack, which is the rule that keeps concurrent bumps from racing on main.

One small thing, non-blocking: the CHANGELOG entry and the docs both explain that answering opens a new turn, but neither says that the resume turn is a turn like any other for billing — it burns turn time, and an expiry sweep does too. Worth a clause if it is cheap; a deterministic operation whose approvals arrive as separate turns is the shape this feature invites, and the person budgeting it will ask.

No findings.

@jhgaylor

Copy link
Copy Markdown
Collaborator

Pushed aae4bf83 on top of your approval — one paragraph, because the fix on #1852 and #1854 gave this page something to say.

ask_timeout is now bounded at a year, refused at both doors that store a policy. The deadline precedence list here explained items 1, 2 and 3 but not the bound, so a reader who set a longer one would have met the refusal with nothing written down to explain it. The new paragraph says what the limit is, where it is enforced, and what it is not: a database timestamp cannot hold a longer deadline, so it is not a statement about how long a wait is useful. It also records that a per-request _meta.fountain.timeout above the limit falls back rather than being refused, since that half has no door to refuse it at.

The CHANGELOG line gained "and at most a year either way" in the same place it already described the two timeouts.

Prose gates, measured before and after on this page: vale STE goes 115 → 117 problems, and the delta is two STE.Vocabulary suggestions ("save" → "keep", "back" → "rear"), which CLAUDE.md says advise rather than gate — and both replacements would be worse English here. Zero new errors and zero new warnings; the 5 sentence-length errors on the page are the ones this PR already had. destink reports 112 pages clean, and docs-style.py finds nothing on this page.

Nothing else in the PR moved. The 0014 amendment, the SDK 1.28.0 bump and the rest of the page are as you reviewed them.

@BinaryBourbon BinaryBourbon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approving for aae4bf83. The ceiling needed a sentence on the page that explains the deadline, and this is the right one: it says what the limit is, where it is enforced, and what it is not, and it records that a per-request _meta.fountain.timeout above the same limit falls back rather than being refused — which is the asymmetry a reader would otherwise have to discover. The CHANGELOG clause ("and at most a year either way") lands in the right paragraph.

The new paragraph is clean under all three prose gates. I ran them rather than taking the commit message's word for it.

Non-blocking: the page picked up three STE sentence-length errors, and they are from the earlier commit

vale --config=.vale-ste.yml docs/concepts/permissions.md reports 5 STE.SentenceLength errors on this branch against 2 on origin/main. None of the three new ones are in aae4bf83; they came in with 4264c3e4, and I missed them on the first read.

docs/concepts/permissions.md:130  Sentence has 34 words; the description limit is 25.
docs/concepts/permissions.md:136  Sentence has 36 words; the description limit is 25.
docs/concepts/permissions.md:158  Sentence has 27 words; the description limit is 25.
  • 130 — "A client that wants to render this as a system event and not as something a person typed can tell it by the prompt itself, which is one JSON object whose only key is fountain/permission_answer."
  • 136 — "The old peer is still holding that request, and the runtimes number their requests from 0 on each turn, so keeping it would make the resume turn's first request collide with the one it still holds."
  • 158 — "The request is written inside the sandbox and the policy belongs to the tenant, so an agent can bound its own wait and cannot extend the tenant's."

prose-advice.sh exits 0 unconditionally, so none of this blocks and I am not holding the stack for it. But .valeignore is empty and the rule is that every published page is written in STE, so this is three lines of advisory report that will now sit on the page indefinitely. Each is a comma splice away from being two sentences and losing nothing. Worth a follow-up commit here while the page is open, rather than an issue.

(Separately: python3 scripts/docs-style.py reports two findings on docs/catalog/runtimes/codex.md:62 and docs/concepts/environment.md:73. Both are already on main and neither file is touched by this stack. Not yours.)

Standing from the previous read

Still no clause saying the resume turn is a turn like any other for billing — it burns turn time, and so does the expiry sweep's. A deterministic operation whose approvals arrive as separate turns is exactly the shape this feature invites, and the person budgeting it will ask. Cheap to add beside the timeout paragraph; still not blocking.

The 0014 amendment, the deadline precedence list, the origin: user note and the four-part SDK bump all read the same as they did. okf validate green.

@jhgaylor
jhgaylor changed the base branch from stack/1635-8-api to main September 11, 2026 12:40
@jhgaylor
jhgaylor enabled auto-merge September 11, 2026 12:47
lex00 and others added 2 commits September 11, 2026 08:49
The permissions page gets the waiting stop reason, the deadline rule and the answer
that opens a resume turn; ADR 0014 records the second shape of request and why the
answer travels as one line of JSON in the prompt rather than a _meta field the peer
has no hook for. The TypeScript SDK ships the generated types as 1.28.0.

The number has moved twice while this stack was open: 1.26.0 went to #1634 and 1.27.0
to #1637, and both are tagged. Whichever release lands next re-bumps again. The bump is
four edits, not three: package.json, the lockfile's two entries, the USER_AGENT in
http.ts and the changelog heading, which sits above the 1.27.0 entry rather than
replacing it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S9jevFQT5MkF3rJUieeiHW
The bound added in this stack's base commit is refused at two doors and
named in the generated API reference, but the manual page that explains
the deadline did not mention it. A reader who set a longer one would
meet the refusal with nothing written down to explain it.

Says what the limit is, where it is enforced, and what it is not: a
database timestamp cannot hold a longer deadline, so this is not a
statement about how long a wait is useful. Also records that a
per-request `_meta.fountain.timeout` above the limit falls back rather
than being refused, because that half has no door to be refused at.

vale, destink and docs-style add no new findings on the page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011zS1mqiBmjRa4q956LTKDG
@jhgaylor
jhgaylor added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit 4fc8515 Sep 11, 2026
25 checks passed
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.

Permission requests that outlive a turn: end the turn waiting, suspend the sandbox, resume on the answer

3 participants