mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
codex/motion-hyperframes-retirement
565
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d83d4e032f |
refactor(sidecar): converge product clients on package boundary (#7244)
* refactor(sidecar): own client identity and lifecycle atomics * refactor(sidecar): converge product clients on package boundary * fix(sidecar): use TypeScript process fixture Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(e2e): assert opaque sidecar capability Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(sidecar): preserve resilient client handoffs Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(nix): build sidecar dependencies first Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(sidecar): restore historical desktop handoff Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(packaged): canonicalize mac runtime paths * fix(tools-pack): clean mac launcher namespace * fix(sidecar): harden client lifecycle boundaries * fix(sidecar): isolate concurrent lifecycle generations * fix(sidecar): retain spawned process generations * fix(sidecar): preserve resources across restart * fix(tools-dev): retain dynamically bound daemon port * fix(sidecar): fence Windows process generations * test(packaged): use sidecar client in payload fixture * fix(sidecar): enforce generation ownership * fix(sidecar): persist generation ownership * fix(sidecar): quick-fail ambiguous recovery * fix(tools-pack): ship sidecar supervisor * fix(sidecar): fence lifecycle generations * fix(tools-pack): build platform before sidecar * fix(sidecar): preserve nested stamped resources * refactor(sidecar): model generation authority * fix(sidecar): converge packaged build and headless actions * fix(sidecar): isolate Windows generations * fix(sidecar): retire orphaned generation descendants * fix(sidecar): unify fenced process retirement * fix(sidecar): converge contended launch generations * test(sidecar): model slower packaged fixtures * fix(sidecar): make supervisor the generation boundary * fix(web): bound sidecar HTTP shutdown * fix(pack): verify complete mac shutdown * refactor(sidecar): converge packaged lifecycle sets * fix(sidecar): serialize Windows packaged lifecycle * fix(windows): harden packaged cold-start validation --------- Co-authored-by: Looper <looper@noreply.github.com> Co-authored-by: Amy <58060647+AmyShang-alt@users.noreply.github.com> |
||
|
|
cfc21b404e |
fix(landing): keep reveal content visible without JavaScript (#7502)
* fix(landing): keep reveal content visible without js * chore: retrigger homepage reveal checks * fix(landing): preserve reveal exceptions and template parity * fix(template): preserve reveal enhancement when composing * test(e2e): own landing reveal parity contract --------- Co-authored-by: Maisie <jingding1121@gmail.com> |
||
|
|
1c1ecae470 |
fix(web): preserve HTML preview state across edit handoffs (#7592)
* fix(web): preserve HTML preview state across edit handoffs * test(web): align reload race coverage with URL handoff * test(e2e): target canonical active preview frame * fix(preview): complete URL edit handoff parity * fix(e2e): classify preview round-trip parity * fix(preview): close edit handoff readiness gaps * fix(preview): close remaining handoff regressions * refactor(preview): centralize runtime state contract * test(preview): isolate parity audit runtime |
||
|
|
df84ae5b9e | refactor(web): remove unused component islands (#7591) | ||
|
|
60be43c132 | chore(repo): remove accidental Bun lockfile (#7589) | ||
|
|
a554d017c8 |
fix(web): render fixed-stage recent deck covers (#7556)
* fix(web): render fixed-stage recent deck covers * test(e2e): type visual projects from contracts Bind visual project fixtures to the production Project DTO so contract changes fail at the mocked API boundary.\n\nGenerated-By: looper 0.12.0 (runner=fixer, agent=codex) * test(web): own fixed-stage deck cover fixtures Move the authored deck sample into the e2e resource lane and pin the wrapper normalization in the web-owned srcDoc test. Generated-By: looper 0.12.0 (runner=fixer, agent=codex) |
||
|
|
f9f83f7476 | fix(web): ignore commented slide tags in covers (#7558) | ||
|
|
62b7c66f3e |
fix(web): answer one inline question form exactly once (#7512)
* fix(web): answer one inline question form exactly once The `<question-form>` submit lock lived only in a ref owned by the mounted FormBlock, and a send that parked in the busy conversation's queue was reported back to the form as refused. Leaving the project while the answer was still settling therefore rebuilt the form as "never submitted" — and rolled back any file it had uploaded for a send the queue still pointed at. The user re-answered, the queue drained, and one logical task produced two identical user messages and two assistant runs: duplicate model calls, duplicate billing, and a reply split in two with a mid-turn action bar. The lock now belongs to the form occurrence (project + conversation + assistant message + form id), recorded next to the draft so it outlives the component, and a busy conversation no longer counts as the send being consumed. Only the answer surfacing in history, or an explicit submit failure, re-opens the form. * fix(web): take the form's occurrence lock before the submit, not after it The lock was written from `acceptSubmission`, i.e. only once the host had settled the send. That left every await in between unguarded — uploading the answer's files, and above all the host's own send while it sits in a pre-run gate or parks in a busy conversation's queue. Leaving the project in that window is the reported sequence, and the remount rebuilt the form as "never submitted": the same answer could be sent a second time and both sends ran. The lock is now taken the moment the answer leaves the form, and every failure path releases it. Because a submit can outlive the component that started it, a release has to reach whichever form is mounted when it lands, or an answer refused after the user navigated away would leave the form locked with no way back — so the occurrence lock notifies its subscribers. * fix(web): keep the form's occurrence lock when storage is denied Session storage carries the lock so it also survives a reload, but a private window or an embedded frame refuses it: the write threw, the read returned false, and every remount there read "never submitted" — handing exactly the duplicate submit back to the users whose context denies storage. The lock now falls back to a page-lived set when the write throws. That set is populated only on refusal, so a context with working storage keeps storage as the single record and nothing outlives the tab's own cleanup. * fix(web): identify a question-form answer by its occurrence "At most one user answer message and one non-failed run per sourceAssistantMessageId + formId" was only ever a property of the form's own submit lock, so every path around that lock still doubled the answer: a second tab, a reload where storage is denied, a queue replay. The answer's send ids are now derived from the occurrence the way the Home handoff already derives its own, which hands the guarantee to the layers that already dedupe on identity — the conversation queue keys entries on `clientRequestId`, the answer row keeps one `userMessageId` instead of appending a second, and the daemon's `createOrReuse` returns the first run rather than spawning another. `formId` is plumbed from the form to the host so two forms in one assistant message stay distinct occurrences. Also pins the invariant that keeps the two halves apart: a question form and its answer share a `taskAnalytics` lineage by design, and folding on that lineage would merge a form with the work its answer asked for. Only the daemon-issued `strategyTaskRunIndex` marks a continuation carrying no prompt of its own, which off-mode never emits. * test(e2e): pin one answer per question-form occurrence Covers OPEND-2367's acceptance at the daemon boundary: after a rapid double submit, leaving the project and coming back, and a full reload, the conversation still holds exactly one answer for the occurrence. The docblock states its limit rather than implying more: it stays green on the pre-fix build, because once the answer reaches the message list that build locked the form from history too. The window this bug actually lives in — the answer still in flight — needs the submit promise held open and is covered at the component layer. * fix(web): key only the run on the answer occurrence The occurrence identity was also naming the answer's message rows, but handleSend persists the user row before the daemon answers. A second tab answering the same form differently would upsert the first tab's accepted answer and still lose the run to createOrReuse, leaving a transcript showing one answer while another actually ran. Only clientRequestId is derived now, so a duplicate still costs at most one run while it can no longer rewrite an accepted answer. Making the first answer authoritative for the row too needs the check and the write at one authority; the client cannot make that atomic before the daemon has replied. * fix: claim a question-form answer once at the daemon Keying only the run left the acceptance half met: a second tab still wrote a second answer row for the same occurrence, and "at most one user answer message" is not satisfied by calling the extra row cosmetic. The client cannot decide this. `handleSend` persists the user row before the daemon has answered, and no client-side read-then-write is atomic against another tab. So the answer's row keeps its occurrence-derived id and the write goes out `createOnly`: the daemon, where the check and the write are one operation, keeps whichever answer landed first and returns it. The run is claimed on the same key by `createOrReuse`, and the losing submitter adopts the returned answer so its transcript shows what the surviving run actually read rather than an answer no run ever saw. `saveMessage` now returns the stored row so a caller can tell it lost. * test(e2e): pin the concurrent occurrence claim for a question-form answer The review's remaining worry was a cross-tab race: two submitters answering one occurrence differently, one winning the answer row and the other the run, so the transcript would show an answer no run consumed. This drives that race at the daemon's HTTP boundary instead of arguing about orderings — two concurrent POST /api/runs sharing the occurrence's clientRequestId and userMessageId, with different answers, one of which makes the fake agent finish differently so the terminal run reports which prompt it actually ran. The daemon turns out to be stricter than reuse: the occupancy is a request fingerprint too, so the divergent submitter is refused with 409 IDEMPOTENCY_CONFLICT and never creates a run at all. The spec asserts that, one answer row carrying the occurrence's id, and that the finished run agrees with the stored answer. `ChatRunCreateBody` gains the `userMessageId` the daemon already accepts, so a spec can pin the user row the way the web client does. * fix(daemon): keep a turn's recovery lineage when a rewrite omits it `upsertMessage` bound `task_analytics_json` from the incoming write unconditionally, so any rewrite that carried no opinion about the field erased it. The run-create seed is exactly such a writer: it rebuilds the user row from the request that won the claim, with content, attachments, session mode, run context and plugin snapshot — and no analytics. A client PUT landing first therefore wrote the answer's lineage and the seed immediately nulled it, which is why an accepted clarification read back with `taskAnalytics: null` and lost the logical task it belongs to after a reload. The UPDATE now preserves the stored value when the writer omits the field. An explicit null still clears it, so deliberately dropping lineage stays possible; only silence is treated as silence. Covered both orderings — PUT before the run request and after — in the e2e dialog spec; the first was red before this change. --------- Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com> |
||
|
|
2aeb4c0d18 |
fix(web): keep top-right controls clickable on first launch (#7535)
* fix(web): keep top-right controls clickable on first launch * test(e2e): cover first native chrome action click * test(e2e): click existing cold-launch chrome action Generated-By: looper 0.12.0 (runner=fixer, agent=codex) * fix(web): keep project account actions right aligned |
||
|
|
3af55e9f22 |
fix(daemon): stop blocking a strategy task whose clarification form renders (#7509)
The clarification form has two parsers by design. The web renderer
(`splitOnQuestionForms`) decides what the user sees; the daemon detector
(`scanQuestionForms`) decides what the host believes was asked. The app
boundary forbids `apps/daemon` importing `apps/web/src`, so the daemon side
is a deliberate mirror — and its own header says to keep it in sync or
promote a shared parser once the two drift.
They drifted, and the two answers are load-bearing against each other. The
OD Next coordinator blocks a `clarification_required` turn that rendered no
form (`od_next_clarification_form_missing`), settling the strategy task
terminal + blocked. So when the web renders a form the daemon scored as
absent, the user is left filling in a live form belonging to an
already-terminal task, and submitting it returns 409
STRATEGY_TASK_STATE_MISMATCH.
That is the production report: an agent wrapped its form in a duplicate of
its own open tag. The web treats an outer block that fails to parse but
holds another open marker as a false positive, unwinds to the inner marker
and renders the form. The daemon had no such unwind, charged the outer block
as unrenderable, and blocked the task underneath a form the user could still
fill in.
Mirror both of the renderer's recovery paths — a failed block whose body
holds another marker, and an unterminated marker with another marker after
it — so a marker is only charged once the renderer would also have given up
on it. Recovery is not permissiveness: a wrapper with nothing left to retry
from is still the unrenderable block it always was, and two forms the chat
renders separately stay two, so the one-round protocol still catches the
ambiguity.
Also accept a bare top-level questions array. `parseForm` has always read
`[{…}]` as the questions list, so requiring the `questions` key made another
displayed form invisible to every daemon consumer.
Both changes move the daemon in one direction only: toward counting what the
UI already shows. A 20k-case differential run over randomly assembled markup
now finds no input where the two disagree on either the renderable or the
unrenderable count.
Left deliberately out of scope, as backend-only: the UI hardening the report
also asks for — writing the blocked verdict back during state restoration,
and re-checking task state before submitting. With the parse contract
restored the task is no longer blocked, so the reported 409 does not occur;
those remain worth doing as defence in depth for a task blocked for any
other reason.
Plane: OPEND-2364
Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com>
|
||
|
|
eef3f10ecd |
test(e2e): quarantine workspace role convergence race (#7493)
Co-authored-by: Amy <1184569493@qq.com> |
||
|
|
f2e1bec689 |
test(e2e): close recent release coverage gaps (#7441)
* test(e2e): close recent release coverage gaps * test(e2e): provide hermetic ffmpeg binaries * test(e2e): own ACP refusal fixture * test(e2e): satisfy resource layout guard * test(e2e): stabilize ACP and desktop regressions --------- Co-authored-by: Amy <1184569493@qq.com> |
||
|
|
75e231ec7e |
fix(web): keep signed-out updater in top-right cluster (#7482)
* fix(web): keep signed-out updater in top-right cluster * fix(web): gate updater cluster on rendered control Keep the updater host mounted for status updates while exposing the top-right cluster only when the updater renders a control. Preserve the signed-out and identity-loading project-route no-cluster contract.\n\nGenerated-By: looper 0.12.0 (runner=fixer, agent=codex) |
||
|
|
cd6920f963 |
feat(prompts): require real imagery for factual referents (#7436)
* feat(prompts): require real imagery for factual referents * fix(prompts): preserve content image aspect ratios * fix(prompts): resolve slim charter imagery rule conflicts - Move the factual-integrity carve-out into instruction-priority item 2 so it can actually win a conflict against a skill, instead of sitting after the list as charter prose at priority 4. - Qualify the Conduct copyright rule so it composes with the requirement to acquire real brand marks, covers, and artwork, closing the documented failure where the agent silently substitutes a look-alike. - Restore the charter byte budget at 29_696 (was deleted, previously 25_600) so future growth stays a deliberate, explained decision. --------- Co-authored-by: Mason6660201 <13073456345@163.com> Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com> Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com> |
||
|
|
c7d0dcceb1 |
fix(web): preserve BYOK after Cloud sign-out (#7437)
* fix(web): preserve BYOK after Cloud sign-out * test(e2e): preserve BYOK across Cloud sign-out |
||
|
|
d88941ba0e |
chore(release): bump main to 0.21.1 ahead of stable 0.21.0 (#7407)
Co-authored-by: open-design-release-bot[bot] <open-design-release-bot[bot]@users.noreply.github.com> |
||
|
|
4076a77135 |
fix(web): allow signed-out Local CLI and BYOK onboarding (#7381)
* fix(web): allow signed-out Local CLI and BYOK onboarding * test(web): align onboarding checks with signed-out alternatives * test(e2e): drop obsolete Pricing campaign snapshot * fix(web): restore active onboarding Cloud login Generated-By: looper 0.12.0 (runner=fixer, agent=codex) * fix(web): resume hydrated onboarding login Generated-By: looper 0.12.0 (runner=fixer, agent=codex) * fix(web): preserve direct setup during Cloud hydration Generated-By: looper 0.12.0 (runner=fixer, agent=codex) * fix(web): preserve signed-out local onboarding flows * fix(web): resume hydrated login after onboarding back --------- Co-authored-by: Amy <1184569493@qq.com> |
||
|
|
4831471254 | fix(web): keep resize hitbox off chat scroll edge (#7324) | ||
|
|
172ad3e46a |
test(e2e): retire the Pricing campaign-unlimited assertion with its source (#7383)
main is red and the merge queue is rejecting everything queued after 08:43. The only failing check is this file: FAIL tests/pricing-unlimited-models.test.ts Error: campaignUnlimitedModelNames on the Pricing page not found — did it get renamed? #7349 retired that contract deliberately: it removed campaignUnlimitedModelNames and the per-model access markers from pricing-individual-plans.astro, and updated apps/landing-page/tests/pricing-contract.ts to assert those markers now stay absent. This e2e file is a second copy of the same contract and was missed, so it still scrapes the astro source for a symbol that no longer exists. Drop that assertion and the helper only it used. The two cases that remain are the ones that actually span packages — popular-model ordering, and every Pricing display name mapping to an AMR model id — which is why this file lives in e2e rather than beside the page. |
||
|
|
a8b571ccd5 |
feat(amr): load Coding Plan models dynamically (#7289)
* feat(amr): load coding plan models from Vela * test(web): mark wallet gate fixtures authoritative Generated-By: looper 0.11.8 (runner=fixer, agent=codex) * fix(web): keep personal plan badges out of team workspaces Generated-By: looper 0.11.9 (runner=fixer, agent=codex) |
||
|
|
e52b866d17 | fix(web): hide Go campaign touchpoints (#7361) | ||
|
|
24a10d143c |
feat(landing): focus Individual Unlimited campaign models (#7320)
Co-authored-by: hazel-ye <80680199+hazel-ye@users.noreply.github.com> |
||
|
|
d03bf69d58 |
feat(standalone): complete Terminal exact lifecycle (#7319)
* feat(standalone): bootstrap terminal exact lifecycle * feat(standalone): complete terminal exact lifecycle * fix(ci): build standalone during postinstall * fix(standalone): repair exact lifecycle recovery Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(standalone): make exact updates replay-safe Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(terminal): enforce replay-safe exact carrier Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(standalone): align active update replay state Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(standalone): preserve exact channel lifecycle Generated-By: looper 0.11.2 (runner=fixer, agent=codex) --------- Co-authored-by: Looper <looper@noreply.github.com> |
||
|
|
fb26b5cb8a |
fix(runtimes): support the dsh release line users are actually served (#7339)
* fix(runtimes): support the dsh release line users are actually served Two days after #7153 widened the version policy from one release candidate to the `0.1.0-rc.N` line, upstream published `0.1.1-rc.1` and then `0.1.1-rc.2`, and made the latter the `latest` tag. Scoping to `0.1.0-rc.N` was still a pin, just a longer one: every user on what npm now serves is told their CLI is untested, our installer hands out a version two releases behind, and the connection component's peers do not resolve against the host they land on. That last one is not a warning. semver only lets a prerelease satisfy a range when some comparator carries the SAME major.minor.patch tuple AND a prerelease tag, so `>=0.1.0-rc.6` matches nothing on the 0.1.1 line — a verified `satisfies('0.1.1-rc.2', '>=0.1.0-rc.6') === false`, the same rule that made the original pin uninstallable. Move all three to the 0.1.x line: the policy accepts any `0.1.N`, release candidate or stable; both installers pin `0.1.1-rc.2` with the resolution window reset to just after its publish time; the peers gain a comparator for the new line. This will expire again. A comparator per prerelease line is what semver allows, so `0.1.2-rc.1` will not resolve until someone adds one, and nothing in the repo notices when upstream moves. The check that catches installer/policy drift only compares the two against each other. * ci: watch upstream dsh releases instead of waiting for a bug report Twice in one week `@deepseek-ai/dsh` published a new release line while this repo kept pinning the old one, and both times the detection mechanism was a person reporting that their install did not work. The check added with the first fix compares our installers against our agent def. Both are our own config, so it stays green precisely when they are equally out of date — which is the state that shipped, twice. Nothing here looks at the registry. Add a daily scheduled comparison against npm's `latest` that separates the two failures worth telling apart: `behind` means our installer hands out an older version than the registry serves, everything still works; `unsupported` means a user who has what npm serves is outside what we claim to support, which is an "untested" warning in Settings and peer ranges that do not resolve. The card names all three files that must move together, because forgetting the peer ranges is the one that breaks an install rather than warning about it. Standalone and outside the merge gate, like nix.yml and docker-image.yml: an upstream release is not a reason to fail someone's PR. The workflow runs `self-check` before touching the registry so a broken checker is loud rather than quietly in-sync. * fix(ci,runtimes): stop the drift watch losing what it exists to report Four defects from review, each of which would have made this quieter than having built nothing. The widened policy over-claimed. `/^0\.1\.\d+(?:-rc\.\d+)?$/` accepts `0.1.2-rc.1`, which the peer ranges cannot install — so the daemon would suppress the untested warning for a version whose companion fails to resolve, which is worse than the warning and silent. Narrow prerelease acceptance to the tuples and floors the peers actually carry; stable releases on the line need no comparator and stay accepted. This also retires an over-claim that predates the branch: `0.1.0-rc.2` and `rc.3` are published and below the `>=0.1.0-rc.6` floor. `e2e/tests/dsh-installer-version-policy.test.ts` now runs both authorities over a version matrix and fails when the def accepts anything the peers reject, so the two cannot drift apart again. Importing the script ran the CLI. Collecting the new test fetched the live registry — measured at 354ms with the verdict printed — and once drift existed would have attempted a real Feishu post before any assertion. Move the dispatch behind a direct-entry check and prove it with a test that stubs fetch and re-imports. Feishu answers a rejected webhook with HTTP 200 and a nonzero code, so treating every 2xx as delivered discards the alert and logs success doing it. Mirror the landing notifier's contract: code 0 or absent is delivered, 429/5xx/9499 retry, anything else raises. The workflow chose the landing webhook but signed with an unpaired secret, so a bot that verifies signatures would reject the card. Pair them the way the landing workflow does, and pin the mapping in the topology test. * test(e2e): check each dsh peer range on its own The compatibility guard scanned the whole manifest and merged every comparator into one tuple table, so a single updated peer answered for the other four. A bump that updates one range and forgets the rest — the realistic way this goes wrong — read as compatible, which is the partial state the guard exists to catch. Parse `peerDependencies`, keep the `@deepseek-ai/dsh-*` entries, and require every one of them to admit a version before calling it installable. `cordis` is excluded: it has its own version line and never moves with dsh. Two cases cover it: a fixture where one peer carries the new comparator and the others do not, and the floor itself, since a comparator for the right tuple still rejects a candidate below it. Verified against the real manifest by downgrading four of the five ranges, which reds the matrix with `expected [ '0.1.1-rc.1', '0.1.1-rc.2' ] to deeply equal []`. |
||
|
|
6ea03de5d3 |
fix(web): left-align onboarding Local CLI chips (#7338)
* fix(web): left-align onboarding Local CLI chips The Local Agent step's detected-CLI chips are flex rows that never set `justify-content`, so they inherit `center` from the global `button` primitive. Each chip is a fixed-width grid cell, so the icon+name group floats by however much slack that particular row has — measured live in Chrome, the icon's offset from the card edge ranged 114px to 158px across seven CLIs, and the two columns read ragged. Opt the chip out of the primitive's centering with an explicit `justify-content: flex-start`, which drops the spread to zero. The spec renders the real onboarding flow with `primitives.css` and `entry-layout.css` loaded in app order and asserts the computed `justify-content`, so it fails on the cascade rather than on the text of the stylesheet. * test(e2e): baseline the onboarding Local Agent CLI list The visual suite stops at the onboarding sign-in step, so the one screen whose whole job is to line up a two-column grid of detected CLIs had no pixel coverage. The nearest-sounding case, `visual-avatar-local-agent-list`, captures the avatar menu's agent list — a different component — and sat at 0px through this PR's alignment change, which is exactly how a gap like this stays invisible. |
||
|
|
d85e78ab7d |
fix(runtimes): reach a working agent CLI past a broken shim (#7153)
* fix(runtimes): reach a working agent CLI past a broken shim
Resolving an agent's name on PATH only proves a file exists there, never
that it runs. Detection stopped at the first hit, so a wrapper orphaned by
a half-finished `npm i -g` shadowed a perfectly good CLI of the same name
in a later search directory — and because the not-invocable branch dropped
the resolved path, the picker (which renders an unavailable agent only when
it carries one) hid the agent entirely. The user saw "scan found nothing"
with no way to act.
Detection now walks past candidates that cannot be executed, and reports
the path it tried when every candidate fails, so the row stays actionable.
Also on this chain:
- `@open-design/dsh-runtime` pinned its DeepSeek Harness peers to one exact
release candidate, so it became uninstallable the moment upstream shipped
the next one. Peers now take ranges. `scripts/guard.ts` gains a narrow,
logged exemption for external-host plugin manifests — their peers describe
a host this repo neither installs nor controls, which is the one case
where an exact pin is wrong rather than reproducible.
- The DSH installers pinned only the entry package while its ~190 siblings
used caret ranges over prerelease floors. Once a newer release candidate
existed, the transitive tree floated onto it and its mutually exclusive
peer sets sent npm into an ERESOLVE backtrack that never converged. Both
installers now freeze the resolution window alongside the pin.
- The rescan notice counted agents the list never renders, so it announced
"3 available" above two rows.
* fix(runtimes): make detection's chosen executable the one spawn sites use
The candidate walk taught detection to skip a binary it cannot execute, but
every other resolution — chat, the connection test, the memory summariser,
companion install — called `resolveAgentLaunch` with no skip list and got
back the first file that merely exists on PATH: the broken shim detection
had just rejected. Settings advertised the agent as installed while each
turn exec'd the wrapper, so the fix only ever reached the picker.
Detection is the only stage that spawns anything, so it is the only one that
learns which candidate works. It now publishes that winner, and resolution
prefers it over a fresh first-hit walk. An explicit `*_BIN` override and a
packaged built-in still outrank it, a winner whose file has since vanished
is ignored, and detection clears the entry before each pass so a rescan
after a repair never resolves against a stale one.
This restores the invariant the file already documented — detection probes
the exact path the runtime will spawn — which the walk had broken.
Reported by @mrcfps in review.
* fix(runtimes): keep the remembered executable inside the live candidate set
The winner map made resolution stop being a pure function of the current
environment. It only checked that the remembered file still existed, so a
path learned in a richer environment survived an emptied PATH, a sandboxed
OD_AGENT_HOME, and any other narrowing of the search — resurrecting a binary
the caller could no longer see.
That is not just a test artifact: `GET /api/integrations/vela/status` must
answer `amr-runtime-unavailable` when the vela binary cannot be resolved, and
it started answering "ready" instead, because an earlier detection in the
same process had remembered a path.
The memory now reorders candidates rather than introducing them: it applies
only when the live search already offers the remembered path. An emptied
PATH, a sandbox, and an uninstalled CLI all keep meaning "not found", while
a broken shim shadowing a working binary is still resolved to the winner.
This also settles the scope question raised in review — no separate cache key
is needed, because the candidate set already carries the resolution scope.
* fix(runtimes): treat a launcher whose target is gone as not invocable
A stale global npm wrapper on Windows is a `.CMD`, which OpenDesign runs
through `cmd.exe /d /s /c`. When the package behind it has been
uninstalled, cmd.exe starts, node starts, and only then does node fail to
load the script the wrapper names — a plain exit 1 with MODULE_NOT_FOUND
on stderr. None of the spawn-level signals the classifier looked for
(ENOENT / EACCES / ENOTDIR, exits 126 and 127) can occur along that
chain, so detection read the dead wrapper as a healthy CLI that merely
disliked `--version`, stopped the candidate walk on it, and never reached
the working `dsh.CMD` later on PATH.
Classify invocability from the launcher's own vocabulary as well: cmd.exe's
9009 (the Windows analogue of POSIX 127), node's missing-module report, and
cmd.exe / PowerShell's missing-command and missing-path reports. Exit status
alone still means nothing — a CLI that runs and exits non-zero is a real
answer from the right binary and keeps its place in the walk.
Drop the win32 early return from every case in the fallback suite and write
fixtures per platform, so `dsh.CMD` resolution through PATHEXT and the
cmd.exe wrapping are exercised natively. The orphaned-wrapper regression is
reproduced on every platform — the defect is a classification bug, not an OS
API one, and the daemon suite only runs on Linux in CI.
* fix(runtimes): carry forward the dead paths, not detection's winner
Detection published the candidate it settled on and resolution preferred
it over the first live PATH hit. That pinned the daemon to one binary for
its whole lifetime and let it silently outrank the caller's own PATH: a
CLI that became visible earlier afterwards — a fresh install, a version
manager swapping shims, a caller resolving under its own environment —
could no longer be reached.
The daemon suite caught it on a machine that has a real `vela` installed:
`chat-route` and `connection-test` inject a fake agent CLI ahead of PATH
per case, and the remembered winner outranked it, so 15 cases spawned the
machine's real CLI instead of the fixture. CI never saw it because its
runners have no such CLI to remember.
Carry the proven-dead paths forward instead. Skipping them keeps every
candidate still standing in plain PATH order, while chat, the connection
test, the memory summariser, and companion setup still avoid the shim
detection rejected — which is the whole point of publishing anything.
* docs(runtimes): say why detection keeps its own launcher-error list
`isCliNotInstalledText` in run-failure-classification.ts reads similar
output, and the next reader will reasonably ask why the two are not one.
They answer different questions: that one buckets a finished run for
telemetry, where over-matching costs a mislabelled event, while this one
decides whether to abandon a binary mid-resolution, where over-matching
sends a working CLI's user to a different install.
* fix(runtimes): accept the dsh release line, not one release candidate
This branch moved the installers to 0.1.0-rc.8 and left the agent def
naming 0.1.0-rc.6 as the only supported version, so following our own
one-line install produced "0.1.0-rc.8 has not been tested with this
OpenDesign build" in Settings on a clean machine. That is the same
pinning defect the installers had, one layer up.
`dsh` publishes a stream of release candidates whose sibling packages
peer-require each other, so the version our installer hands the user
moves with upstream. Version policies can now declare the release line
they accept alongside the exact versions exercised, and DeepSeek Harness
declares its own. A version off that line still warns — the point is to
stop pinning, not to stop checking.
Add a cross-boundary guard for the drift itself: the installers and the
agent def cannot import each other, so `e2e/tests` reads both and fails
when an installer pins a version the daemon would not accept.
* docs(contracts): correct what shim-broken now means
The reason's docstring said "exit 126/127", which stopped being the whole
story once detection learned to read a Windows wrapper's stderr: a `.cmd`
starts an interpreter successfully and reports the missing target there,
so no POSIX exit code appears at all.
* test(runtimes): cover the launcher signatures no fixture can produce
`launcherTargetMissing` recognises six ways a launcher says it never
reached its target, and until now two of them had any coverage — the two
a POSIX shell can print. cmd.exe's wording, PowerShell's, and exit 9009
were load-bearing for the Windows fix and untested anywhere.
A fixture cannot close that gap. POSIX masks exit statuses to eight bits,
so a shell asked for 9009 reports 49, and /bin/sh does not speak cmd.exe.
The daemon suite runs on Linux in CI, so driving the classifier directly
is the only way the merge gate can catch a typo in a signature the fix
depends on for the platform the bug came from.
Extract the catch arm verbatim as `classifyVersionProbeFailure` — same
branches, same order, no behaviour change — and feed it the rejection
shape `promisify(execFile)` actually produces. Verified by mutation:
deleting the 9009 branch reds one case, deleting the stderr branch reds
six.
The suite also pins the constraint from the review by name: exit 1 with an
ordinary argument complaint, with no stderr, or with wording that belongs
to run-failure telemetry ("not installed") all stay `spawned`. That last
one is why detection keeps a narrower list than `isCliNotInstalledText`.
|
||
|
|
7320f9b8ab |
feat(daemon): make OD Next the default design strategy (#7016)
* refactor(observability): isolate legacy run exporter
* refactor(daemon): extract internal run creation service
Route /api/runs and compatibility /api/chat through one daemon-owned create, claim,
resume, and start seam. Preserve HTTP responses, SSE timing, session bookkeeping,
and durable Run formats.
Validated with daemon tests/build/typecheck, workspace guard, and workspace typecheck.
* test(plugin-runtime): freeze OD Next strategy fixture boundaries
Add exact pre-Build route and stage cases plus deterministic deletion-boundary mutations without shipping an evaluation harness.
Preserve the current official and community fallback pipeline and composed verification/critique prompt sections as golden witnesses.
Validated with plugin-runtime tests and types, focused daemon prompt composition, daemon types, guard, and workspace typecheck.
* fix(daemon): finalize terminal telemetry delivery
Persist a versioned delivery record so normal failures stop after bounded retries
and only an unfinished crash window resumes on restart.
Reuse the persisted identity across Vela and Relay transport, isolate stable
direct Langfuse event ids by delivery purpose, and log only redacted sink data.
Validated with focused daemon and contracts tests, workspace typechecks, and
guard. The full daemon suite had one timeout; its four-test file passed alone.
* feat(plugins): add OD Next strategy scenario and contracts
Ship the V2 strategy package as inactive bundled content and validate its contracts without changing ordinary plugin manifest compatibility.
Keep bundled strategy bytes out of the catalog and generic apply path until a later hash-gated activation owner binds them.
* feat(observability): add normalized agent observation contract
Define a pure versioned contract for identity, Prompt, usage, timing, and status evidence with explicit availability and per-value provenance.
Add a structured main-Run compatibility adapter that preserves unknown facts, separates clocks, and omits provenance-ambiguous derived totals without changing legacy export behavior.
* feat(plugins): bind OD Next strategy content identity
* feat(daemon): persist OD Next strategy task chains
Add the daemon-owned SQLite truth for immutable strategy task identity, ordered physical Run mappings, canonical Plan Contracts, revision-CAS transitions, and terminal reconciliation.
Pin task-owned Snapshots and exclude their durable references from pruning so mixed GC sweeps remain safe.
Validated with 103 focused daemon tests, contracts tests/typecheck, daemon and workspace typechecks, and guard.
* feat(daemon): gate OD Next runtime capabilities by fixtures
Add exact CLI/adapter/fixture tuple resolution, stable capability snapshots, and offline
replay graph invariants for the four initial runtime paths.
Keep production registry empty until sanitized real fixtures exist; synthetic evidence and
version drift remain unknown.
Validated with contracts tests, focused daemon and e2e boundary suites, guard, and
workspace typecheck.
* feat(daemon): add hash-gated OD Next prompt recipe
Compose the bundled strategy from validated content, one Task Skill, and the exact stage policy.
Fail closed on package, profile, pipeline, or atom drift while preserving ordinary quality paths.
Validated with focused tests, package typechecks, workspace typecheck, and guard.
* feat(observability): aggregate OD Next task observations
Build stable task, Run, Child, model, and tool hierarchies from durable StrategyTaskExecution mappings and normalized facts while keeping missing coverage explicit.
Map task aggregates to legacy ingestion behind the consent gate. Preserve owner/exclude hierarchy while accounting usage once, enforce kind-specific Prompt boundaries, and accept absolute time only from one Unix-epoch evidence record.
Validated with 228 focused daemon tests, 353 contracts tests, daemon/contracts and workspace typechecks, guard, and the task harness.
* feat(daemon): collect bounded Codex child evidence
Read declared Codex rollout sessions without scanning arbitrary homes.
Normalize verified child facts while redacting prompts and preserving Run semantics.
* feat(daemon): add OD Next planning coordinator
Add a daemon-owned planning reducer, deterministic Resolver and Preflight facts, and a stream-safe machine protocol boundary backed by the durable task store.
Lock one clarification and one serialization-only repair across restart, preserve the Plan hash, and keep ordinary Run text unchanged.
* feat(daemon): collect native OpenCode child evidence
* feat(observability): add Langfuse OTLP task exporter
Map normalized task observations to complete OTLP/JSON v4 spans with stable parent context, allowlisted metadata, usage accounting, and bounded terminal delivery.
Fail closed on drift across root and operation input, model, timing, type, hierarchy, status, and complete usage provenance while dual remains a local-only OTLP shadow.
Keep legacy rollback, consent gates, and X5 live self-host verification boundaries intact.
* feat(daemon): normalize Claude native child evidence
Collect matched Claude Task sidechain lifecycle facts without changing main stream turn, stdin, or error bookkeeping, and adapt them to provider-neutral observations with unavailable Prompt and usage evidence.
Bind session, Task id, and parent identity immutably; poison rebinding, session drift, and conflicting terminals so ambiguous evidence cannot reach L2. Keep production capability unknown until X1 supplies an exact-version sanitized real fixture.
* feat(daemon): consume versioned Vela child evidence
Negotiate schema v1 only on AMR and enforce run-scoped start-to-terminal graph, status/source coherence, and exact usage source/completeness bindings. Emit allowlisted Normalized child facts without Vela private I/O.
Pin the unpublished producer golden for offline compatibility while keeping production capability unknown until the X1/X3 evidence gates are satisfied.
Validated with 147 focused daemon tests, 353 contract tests, daemon/workspace typechecks, guard, and the task harness.
* feat(daemon): automate OD Next simple production
Connect parsed OD Next terminal output to the durable coordinator, claim clarification, repair, and production Runs atomically, and start continuations only after the source Run ends.
Converge task failure and cancellation before physical end, require daemon-owned execution facts and canonical delivery, and keep ordinary Runs compatible.
Validated with focused daemon route and coordinator tests, contracts tests, daemon and workspace typechecks, guard, and the task harness.
* feat(daemon): enforce verified complex production
* feat(web): follow OD Next task chains across Web and CLI
Persist daemon-issued task handles and visible predecessor-prefix boundaries, then follow projected active Runs through one terminal outcome in Web and od run.
Normalize reloads to daemon activeRunId before subscribing, transfer controller ownership atomically across successors, discard stale cursors, and preserve visible prefixes without replaying predecessor machine payloads.
Validated with focused Web and daemon tests, contracts tests, workspace typecheck, guard, diff checks, and the task harness.
* feat(daemon): roll out OD Next task observability
* feat(daemon): add controlled OD Next rollout
* fix(daemon): align OpenCode and Vela evidence tuples
Pass GPT-5.6 Sol's verified high variant to OpenCode, pin native evidence to 1.18.18, retain locally recorded success and failure seeds as explicitly unattested, and align the Vela consumer golden to the local versioned producer.
Validated with 5 focused daemon files (175 tests), daemon and workspace typechecks, guard, the real local Vela ACP Sol smoke, and a minimal test-relay telemetry write.
* fix(daemon): honor OpenCode model variant capabilities
Parse the live verbose OpenCode catalog per model and forward only variants advertised by the exact detected binary. Keep fallback and one-off OPENCODE_BIN connection tests on the base model when variant support is unknown.
Record machine-checkable OpenCode 1.18.18 root, tool, child, recovery, cancel, timeout, and resume evidence without promoting the unattested seed into the production capability registry.
Validated with focused runtime and connection tests, host process-group and watchdog tests, contracts tests, daemon/workspace typechecks, guard, and an isolated SIGKILL delivery-recovery exercise.
* feat(daemon): add OD Next analytics operations report
Reduce normalized task aggregates into source-aware operations metrics without
copying the Langfuse dashboard or exposing user trace links.
Preserve main/Child accounting, stage-scoped coverage, unavailable facts, and
external-signal-only quality inputs.
Validated with focused and contract tests, the full daemon suite, guard,
workspace typecheck, and exact-trace Langfuse readback.
* feat(observability): complete child prompt and runtime provenance
Export bounded redacted child-injected Prompt content and exact CLI, companion, and
adapter versions through task-level legacy and OTLP mappings.
Register the OpenCode 1.18.18 best-effort replay tuple while keeping unknown versions
and incomplete Codex evidence fail closed.
Validated with 353 contracts tests, 196 focused daemon tests, daemon/workspace
typechecks, and guard.
* fix(daemon): recognize Codex child failure terminals
Parse Codex 0.147.0 Child task_complete.error as the runtime-owned failed terminal, honor session_meta id over its legacy root session_id, and map interrupt_agent aborts to canceled.
Add a redacted seven-path best-effort replay and exact Codex capability tuple. Unknown versions remain fail-closed and ordinary Codex runs are unchanged.
Validated with 92 focused daemon tests, 353 contracts tests, daemon and workspace typechecks, guard, and the 25-unit task harness.
* fix(daemon): report verified child runtime versions
Use versions observed from resolved CLI probes or Vela's ACP handshake when annotating Child observations. Correct Native OpenCode's allowlist key, add Claude and Vela/OpenCode companion provenance, reject Vela probe/handshake disagreement, and ignore the legacy 0.0.0 placeholder.
Validated with 252 focused daemon tests, daemon and workspace typechecks, guard, the task harness, and a real local Vela --version/-v/ACP initialize build smoke.
* test(observability): record Vela child evidence matrix
Add a sanitized provider-backed Terra replay for main, tool, child success, child failure with parent recovery, cancel, timeout, and cross-process resume. Keep the unpublished Vela tuple outside the production registry while pinning its exact candidate and OpenCode companion evidence.
Validated with focused Vela consumer/capability tests, daemon typecheck, workspace typecheck, guard, and the task harness.
* feat(daemon): enable OD Next production profiles
Make prototype, presentation, marketing, and HyperFrames the exact global OD Next production set while preserving explicit off/observe, user plugin authority, and fail-closed runtime capability gates. Feed runtime-owned profile, route, output, input, and capability facts into the planning contract and keep all other artifact kinds on ordinary Open Design.
Use bounded executable-scoped CLI version probes so headless and restarted daemons can resolve exact tuples without a prior agent-list request or cross-binary cache contamination.
Validated with four provider-backed Terra CLI chains, focused daemon/contracts/plugin-runtime suites, daemon and workspace typechecks, guard, and the 25-unit task harness.
* feat(daemon): classify OD Next exact agent inputs
Route prompt contributors through one daemon-owned choke point and version their final-text, semantic-fact, transport, and exclusion ownership for the forthcoming canonical bundle. Send OD Next continuation stages directly through native sessions without rewrapping or re-seeding stable instructions.
Validated with focused daemon and contracts tests, daemon/workspace typechecks, and guard.
* fix(daemon): close OD Next input ownership gaps
Bind semantic request facts to their real daemon-system or request producers, cover the DSH profile exact-text transport, and reject lookalike prompt-bundle root names.
Validated with focused exact-input, production-server and DSH tests, daemon typecheck, task harness, and guard.
* feat(daemon): emit canonical OD Next prompt bundles
Add strict pure TypeScript Bundle and request-Turn serializers, then route OD Next request and continuation text through their versioned XML contracts.
Split Web prior transcript from the current turn without text subtraction, keep mutable Skill and attachment inputs explicitly pending, and preserve ordinary prompt behavior.
Validated with focused contracts, daemon and web tests, the real server fixture, package and workspace typechecks, and guard.
* fix(daemon): align OD Next bundle input ownership
Register the real canonical Bundle contributors and keep current-turn, prior-transcript, pending-fact, and research-query ownership explicit at the production seam.
Reject continuation task_run_index zero and cover Web framing, ordinary compatibility, fail-closed contributor drift, and real server prompt capture.
* feat(daemon): freeze OD Next user skills
Capture explicit Skill bodies and audited side files before task claim, persist verified task-owned bytes, and replay only the frozen package for OD Next runs.
Keep legacy single-Skill CLI requests and ordinary live staging compatible while adding Web/CLI multi-Skill coverage and fail-closed snapshot checks. Validated with focused server, contract, CLI, Web, typecheck, and guard suites.
* fix(daemon): harden OD Next skill snapshots
Require an explicit frozen package for every mapped OD Next task and fail corrupt task state before any live Skill staging.
Use bounded no-follow descriptor reads with race checks, preserve legacy CLI compatibility, and make materialized roots collision-resistant. Validated with focused, real-server, workspace typecheck, and guard suites.
* feat(daemon): freeze OD Next task input snapshots
Freeze OD Next attachments and canonical configuration before the atomic run/task claim, then route runtime access through durable task-scoped snapshots while preserving ordinary attachment behavior.
Validate immutable identities, transport references, continuation propagation, tamper failure, and the four production task types with focused contract and daemon coverage.
* fix(daemon): isolate OD Next runtime input projections
Keep canonical task snapshots daemon-only, rebuild a read-only per-Run projection, and deliver frozen document resources through ACP or OD_TASK_INPUT_DIR.
Harden source and snapshot reads with no-follow same-fd identity checks and bounded caps. Validated with focused runtime tests, contracts, daemon/workspace typechecks, guard, and the task harness.
* fix(daemon): harden OD Next snapshot transport boundaries
Reject intermediate symlinks and realpath escapes while loading canonical snapshot files, including identical-byte replacement targets.
Preserve ordinary ACP duplicate image ordering while suppressing only OD Next resource/image cross-duplicates. Validated with focused tests, daemon typecheck, guard, and the task harness.
* fix(daemon): preserve structured OD Next snapshot failures
Defer frozen Skill package errors from the Task 04 preclaim lookup to the existing post-claim failure boundary so mapped Runs remain durable JSON failures without orphan input snapshots.
Validated with the combined real-server repair, missing-package, and tampered-package regressions.
* fix(daemon): keep OD Next continuation cwd stable
Separate authoritative project cwd resolution from best-effort file inventory and tolerate entries deleted between directory enumeration and stat.
This keeps native-session identity stable while frozen task inputs replace live attachments during continuation.
* feat(daemon): persist exact OD Next prompt inputs
Persist the canonical first-turn Bundle and every deterministic continuation Turn with UTF-8 and SHA-256 identities, then send only those validated texts through runtime transports.
Fail closed on mapped task drift, tamper, legacy NULL identities, missing mappings, and cold continuation while leaving ordinary Runs on the existing live composer.
Validated with the 15-case automatic server acceptance twice, 223 focused daemon tests, daemon typecheck, guard, and the task harness.
* feat(daemon): observe exact OD Next prompt inputs
* feat(daemon): bind and observe Claude native subagents
Register the replay-verified Claude 2.1.233 tuple, bind complex Build Packages to daemon-issued native Agent handles, and derive completion only from durable child lifecycle evidence.
Export bounded parent-tool, Child Agent, and Child-tool spans through the existing legacy and OTLP task hierarchy without exposing raw tool inputs, outputs, provider ids, paths, or credentials.
Validated with focused contracts and daemon suites, the 17-case real-server chain, workspace typecheck, guard, and the 26-unit task harness.
* fix(daemon): repair OD Next rollout authority and controls
* fix(daemon): harden OD Next public canaries
* refactor(daemon): extract OD Next prompt preparation
* test(daemon): align plugin fixture with project schema
* test(e2e): assert daemon-owned prototype routing
* fix(daemon): restore OD Next task observability
Default eligible OD Next tasks to privacy-aware Langfuse hierarchy delivery through relay or direct sinks, with a versioned safe quality projection.
Persist lightweight representation ownership before suppressing mapped Runs, retain best-effort startup retry semantics, and preserve single-Run compatibility when Task delivery is not eligible.
Adopt the active payload relay for historical packaged launchers and cover the real sidecar environment handoff.
Validated with the full daemon suite, focused contracts/packaged/e2e/tools-pack suites, workspace guard, and workspace typecheck.
* fix(daemon): make OD Next capability fallback invisible
Admit capability-certified runtime adapters across CLI version drift while keeping recorded fixture versions as auditable evidence and reserving a narrow incompatibility denylist.
Fall back before Run claim on preparation failures, keep continuation bound to the frozen capability snapshot, and report truthful child coverage across Langfuse legacy and OTLP without replaying an active task.
Validated with focused contracts and daemon suites, the automatic server suite, pnpm guard, pnpm typecheck, daemon typecheck, LSP diagnostics, and git diff --check.
* fix(strategy): isolate OD Next automatic task routing
* fix(daemon): persist blocked strategy-task attribution
Every blocked strategy task now stores its gate reason codes and the
agent-visible turn text (blocked_reason_codes_json / blocked_visible_text)
so operators can diagnose a blocked task from the store alone. Covers the
coordinator blockTask gate, the automatic-continuation fail-closed path,
and startup run-terminal reconciliation.
* fix(daemon): accept form-only clarification turns by deterministic inference
A first Full-Plan request turn that renders exactly one question form and no
machine-protocol block has exactly one valid protocol meaning: outcome
clarification_required with an unlocked execution mode. The coordinator now
recovers that state deterministically instead of dead-ending the task as
blocked, stamps od_next_protocol_runtime_state_inferred into the accepted
state's reasonCodes for attribution, and keeps every ambiguous shape (extra
forms, recovered plan blocks, later stages, spent clarification budget,
any other protocol issue) fail-closed. This closes the observed field
failure where the agent's first reply produced a discovery form, the task
went blocked, and the user's form answer then failed with 409
STRATEGY_TASK_STATE_MISMATCH.
* feat(contracts,daemon): project blocked strategy-task attribution to clients
A task the OD Next protocol gate settles as blocked persists its
attribution (reasonCodes + agent-visible text) since
|
||
|
|
993f2e1a9e |
feat(ci): add workload convergence shadow (#7311)
* feat(ci): add workload convergence shadow * fix(ci): identify convergence public reads * refactor(ci): move convergence policy into scripts * fix(ci): fail open on malformed convergence reads Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(ci): preserve safe convergence observations Generated-By: looper 0.11.2 (runner=fixer, agent=codex) --------- Co-authored-by: Looper <looper@noreply.github.com> |
||
|
|
05f5b33ef5 |
chore(release): bump main to 0.20.3 ahead of stable 0.20.2 (#7253)
Co-authored-by: open-design-release-bot[bot] <open-design-release-bot[bot]@users.noreply.github.com> |
||
|
|
01169bcd99 |
feat(pricing): add DeepSeek vision unlimited access (#7250)
* docs: specify pricing vision model update * docs: plan pricing vision model update * docs: record pricing vision usage estimate * test(pricing): cover vision model entitlement * feat(pricing): add DeepSeek vision unlimited model * fix(pricing): fit long vision model name in plan cards * fix(pricing): widen usage chart label spacing * feat(pricing): explain model entitlement activation --------- Co-authored-by: zhanghuihua <zhanghuihua@zhanghuihuadeMacBook-Pro.local> |
||
|
|
57de743607 |
ci: surface merge-queue CI failure ejections as a PR comment (#7229)
* ci: surface merge-queue CI failure ejections as a PR comment A failed workload in a merge_group run ejects the queued PR just as invisibly as a blocking label does: the required check fails on the queue's transient ref, the PR's own checks stay green, and the queue entry silently disappears. The needs-validation gate (#5519) announces its own ejections, but every other ejection left no trace on the PR. The validate job now produces a handoff/comment artifact after the gate has already failed in a merge group, naming the failed jobs and steps, a log excerpt of the failing assertions, the run, and the PRs batched ahead in the group. comment.atom.yml already consumes merge_group handoffs. merge_policy exposes whether it already announced a label ejection so a label-only ejection still gets exactly one comment. * ci: keep the merge-queue log excerpt working on gh >= 2.97 gh 2.97.0 (GHSA-3m3g-3wcr-px46) refuses to print an API body that contains terminal escape sequences unless --allow-escape-sequences is passed, and CI job logs are full of them, so the excerpt fetch returned nothing on a current gh. Older gh rejects the flag as unknown. Probe the flag once and pass it only when supported; the fake gh in the topology test now enforces both behaviors so dropping the probe goes red. * ci: make the merge-queue notice probe survive pipefail and replays Capturing gh api --help before grepping keeps an early grep exit from SIGPIPE-ing gh and reading as "flag unsupported" under pipefail; strip CRLF from job logs; never list the gate job itself as a failed job when a completed run is replayed. The fake gh pads its help text past the pipe buffer so the old probe shape stays red. |
||
|
|
2feaee4237 |
fix(web): validate onboarding runtime on Continue (#7164)
* fix(web): validate onboarding runtime on continue * chore: ignore local pi state * refactor(web): remove unused onboarding code * test(e2e): align onboarding Continue expectations * fix(web): discard onboarding validation results the user moved past Continue awaits a validation round trip before it persists the runtime configuration and finishes onboarding, but its closure is frozen at click time and nothing rechecked the intent afterwards. Two ways that surfaced: - Back stays enabled while the request is in flight. Clicking it returned the user to the model-source chooser, then the late success persisted the configuration they had just abandoned and completed onboarding under them. - The inputs being validated stay editable. A success for a replaced BYOK key still completed onboarding against the click-time configuration. `inputKey` already guarded what gets DISPLAYED — `visibleAgentTestState` and `visibleProviderTestState` fall back to idle once it drifts. Apply the same rule to what gets PERSISTED: `continueAttemptStillCurrent` requires the attempt to still be on the runtime setup step, on the same runtime, and against the inputs it validated, reading them from a ref the continuation can actually ask. |
||
|
|
abcdc8a991 |
fix(web): separate updater from account capsule (#7237)
* fix(web): separate updater from account capsule * fix(web): anchor standalone updater prompt Generated-By: looper 0.11.8 (runner=fixer, agent=codex) * fix(web): keep updater prompt visible in RTL Generated-By: looper 0.11.8 (runner=fixer, agent=codex) |
||
|
|
4b48f94b7c |
refactor(ci): align tools-pack sources with scope boundaries (#7230)
* refactor(ci): align tools-pack sources with scope boundaries * refactor(tools-pack): unify test source imports * fix(tools-pack): resolve workspace root from package Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * test(tools-pack): exercise bundled CLI directly --------- Co-authored-by: Looper <looper@noreply.github.com> |
||
|
|
f3e4a3abbc |
ci: refine plan orchestration and Windows payload routing (#7223)
* docs(ci): define test set orchestration * docs(ci): make orchestration guidance incremental * ci: separate merge policy from validation * ci: narrow Windows payload validation * docs(ci): restructure orchestration methodology * docs(ci): treat mapping shape as architecture feedback |
||
|
|
8b18210103 |
chore(release): bump main to 0.20.2 ahead of stable 0.20.1 (#7201)
Co-authored-by: open-design-release-bot[bot] <open-design-release-bot[bot]@users.noreply.github.com> |
||
|
|
ce791edbbb |
fix(web): hide a subscriber's zero balance, and badge unlimited models by plan tier (#7190)
* fix(web): hide the zero balance for subscribed plans
On Go / Plus / Pro / Max the popular models a user works with every day are
unlimited, so the wallet only meters flagship calls and legitimately sits at
$0.00. The workbench's top-right credits pill rendered that zero permanently
next to the avatar, which reads as "you are out of money" to a subscriber who
is not.
`shouldShowCreditsBalance` hides the pill for a subscribed tier whose balance
is exactly zero. Three cases deliberately keep it: a free plan (where zero is
the number that explains why hosted models are unavailable), an unresolved
plan (so the pill cannot flicker in and out as billing lands), and an
overdrawn wallet (the one case the user has to act on).
The tier table it asks lives in `state/plan-unlimited-models.ts` — the same
per-tier unlimited sets the model badge uses — so "which plans may hide their
zero" and "which models are unlimited" cannot drift apart.
* fix(web): badge unlimited models by plan tier, not just the campaign
The 「无限使用」 tag in the model chip and the compact model list was wired to
the DeepSeek V4 campaign alone, so it marked exactly two models for everyone.
What the product actually sells is an unlimited SET PER TIER — 3 models on Go,
4 on Plus, 5 on Pro, all 8 on Max — so a Pro subscriber saw no tag on Kimi
K2.7 Code (unlimited on their plan) while the metered MiniMax M2.7 sat right
next to it looking identical.
The badge now has two sources and the campaign stays first: inside the
campaign window DeepSeek V4 Pro / V4 Flash keep the campaign copy and tooltip
for everyone, free plans included. Only when the model is not a campaign model
does the badge ask the subscription, via `isUnlimitedModelForPlanTier`. BYOK
execution claims nothing — those calls are billed by the user's own provider.
Pricing had the Pro row wrong in the same place: it listed MiniMax M2.7 as
unlimited and GLM-5.2 as metered, the reverse of what Pro includes. Fixed
there too, and `e2e/tests/pricing-unlimited-models.test.ts` now pins the
marketing table and the runtime table together across the app boundary so
editing one side alone fails.
* fix(web): drop the invented tooltip from the plan unlimited badge
The plan branch shipped a tooltip line nobody wrote as product copy (and 19
translations of it). The campaign branch's tooltip is campaign copy with a real
source; the plan case has none, so it now renders the badge text alone rather
than an invented explanation. `od-tooltip` and the aria-label only attach when
there is real copy behind them.
* fix(e2e): type the pricing-parity guard under noUncheckedIndexedAccess
`Typecheck workspaces` failed on the new guard: the e2e package compiles with
noUncheckedIndexedAccess, where every regex capture is `string | undefined`, so
the `match![1]` shape from the first draft does not compile there even though
apps/web accepts it.
Capture access now goes through `captureOne` / `captureAll` / `tierEntry`,
which throw a message naming what could not be found instead of asserting
non-null — so a renamed constant fails as "unlimitedByTier not found — did it
get renamed?" rather than as a TypeError.
* fix(web): reuse the shipped Unlimited wording in ru/uk
The plan badge's Russian and Ukrainian values were written from scratch
("Без лимитов" / "Без обмежень") while the campaign badge beside them — the
same word, already shipped and translated — says "Безлимит" / "Безліміт".
Two different words for one state in the same popover is a translation bug,
so the badge now reuses the shipped wording. The other 17 locales already
matched.
* refactor(web): fold the badge's plan table into the one #7187 landed
#7187 shipped `runtime/amr-unlimited-models.ts` — the same per-tier unlimited
sets this branch had added under `state/plan-unlimited-models.ts` — while this
branch was open. Two copies of a product promise is exactly the drift the
cross-app guard exists to prevent, so the duplicate is gone and everything
reads the surviving table.
The badge's extra needs moved into it rather than staying in a second file:
`planUnlimitedTier` reads the tier out of a team-namespaced id (`team_pro`,
`team_max_yearly`) and `isUnlimitedModelForPlanTier` compares the model slug
past any provider prefix. `isUnlimitedAmrModelForPlan` is untouched — the
balance preflight matches the personal ladder exactly on purpose, since Team
workspaces spend their own balance (#7187), and folding the two questions
together would have changed that behaviour silently.
`e2e/tests/pricing-unlimited-models.test.ts` now parses the surviving table,
including the `...GO_UNLIMITED_MODELS` spreads it composes tiers with, so the
Pricing page and the runtime stay pinned to each other.
|
||
|
|
84381efab9 |
feat(ci): add global hash skip planning (#7185)
* feat(ci): add global hash skip planning * refactor(ci): remove superseded hash skip cache * refactor(ci): keep scope trust in planner * fix(ci): run scope goldens for planner changes Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(ci): publish hash state after successful validation Generated-By: looper 0.11.2 (runner=fixer, agent=codex) --------- Co-authored-by: Looper <looper@noreply.github.com> |
||
|
|
5459ffbc0f |
fix(pricing): align Go plan launch experience (#7167)
* fix(pricing): align Go plan launch experience * test(e2e): dismiss Go launch campaign in UI specs * fix(go-plan): finish launch campaign polish * test(e2e): preserve Go campaign visual coverage --------- Co-authored-by: Amy <1184569493@qq.com> |
||
|
|
90741ab238 |
fix(web): restore About update row spacing and stop the project frame flashing twice (#7174)
* fix(web): restore Settings About update action row spacing The About page's update row holds two sibling buttons — the primary update action and "View release notes". #6142 dropped the .settings-about-update-actions rule group while SettingsDialog.tsx kept emitting the class, leaving the container at display: block. JSX puts no whitespace text node between adjacent elements, so the two inline-flex buttons rendered with their borders touching at exactly 0px. Give the container back its flex row and 8px gap. Adds a Playwright UI regression guard that stubs the packaged-desktop host bridge with a downloaded update so both buttons render, then asserts the declared gap and the distance the buttons actually land at. * fix(web): stop the project frame flashing twice on entry from Home Submitting from Home hands off through ProjectCreationPendingView while POST /api/projects settles, then swaps in ProjectView. Both components owned their own `div.app`, so React mounted a second element on the hand-off and .app's 180ms od-fade-in entrance restarted from zero. The pending surface only lives ~150ms — shorter than the animation it starts — so the frame never finished its first fade before the second one began, which reads as the project view flashing twice. Move the .app shell up to App.tsx and render it around both branches. React now reconciles one element across the hand-off: the entrance plays once, continuously, while the contents swap underneath. Measured in a real browser on the Home -> project transition: before t=260 .app#1 od-fade-in@0 -> t=408 .app#2 od-fade-in@0 (restart) after t=200 .app#1 od-fade-in@0 -> t=317 .app#1 od-fade-in@117 (continuous) |
||
|
|
f6331fb33a |
feat(web): add Go campaign entries and route upgrades to Pricing (#7122)
* feat(web): add Go campaign entries and route upgrades to pricing * fix(web): localize Go campaign copy Generated-By: looper 0.11.8 (runner=fixer, agent=codex) * feat(web): match Go campaign demo UI * test(web): align Go campaign source contract * fix(web): align Go modal model icons * fix(web): preserve Z.ai mark in Go modal list * fix(web): localize and fit Go campaign modal Generated-By: looper 0.11.8 (runner=fixer, agent=codex) * fix(web): clamp Go campaign modal height * fix(web): use local Go campaign icons Generated-By: looper 0.11.8 (runner=fixer, agent=codex) * test(e2e): stabilize Go campaign viewport assertion Generated-By: looper 0.11.8 (runner=fixer, agent=codex) |
||
|
|
479078b44c |
fix(web): keep first Home run output live (#7071)
* fix(web): keep first Home run output live * test(e2e): bound design system restoration probe * fix(web): preserve live turn through authority reload Generated-By: looper 0.11.8 (runner=fixer, agent=codex) |
||
|
|
16fad1c4d8 |
revert(design-systems): remove structured runtime workflow (#7110)
* revert: remove structured design system runtime * fix: preserve authority boundaries during revert |
||
|
|
cdcd2af7b7 |
fix(release): derive DSH bootstrap version from installer bytes (#7107)
The production landing-page promotion pinned DSH_BOOTSTRAP_VERSION: v1 in the workflow, while the publisher refuses to overwrite an already-published immutable version with different bytes. PR #6983 renamed the brand inside install-dsh.ps1 and install-dsh.sh without bumping that pin, so the next production deploy failed closed on immutable bootstrap object already exists with different content: bootstrap/dsh/v1/install-dsh.ps1 and skipped every later step, including the Cloudflare Pages deploy. The version is a function of the installer bytes, so maintaining it as a hand-written constant guarantees this drift. The publisher now resolves the version itself: it probes published versions in order using SHA256SUMS as the fingerprint for the whole set, reuses the version that already holds these exact bytes, and mints the next one when the bytes changed. Every published version keeps if-none-match: * and the immutable cache header, so history stays permanent and unoverwritable -- only the manual bump is gone. A mutable bootstrap/dsh/latest.json pointer records the current version, per-file sha256, and the publishing run, and the verify step reads the resolved version instead of naming one. DSH_BOOTSTRAP_VERSION survives as an explicit escape hatch: setting it forces one version and still fails closed on a content mismatch. |
||
|
|
b084ab075b |
fix(release): allow Chromium sandbox in prerelease E2E (#7090)
* fix(release): allow Chromium sandbox in prerelease E2E * fix(web): cancel stale preview recovery probes |
||
|
|
879236e614 |
fix(workspace): restore last selection after restart (#7067)
* fix(workspace): restore the last selection on restart * test(workspace): cover persisted restart default * fix(workspace): reuse directory authority for context reads * test(workspace): align unknown seat invite behavior * test(workspace): cover unknown seat invite fallback * fix(workspace): preserve unknown invite capacity * fix(workspace): commit selection after persistence * fix(workspace): preserve unknown seat analytics * fix(workspace): guard stale selection cleanup * fix(workspace): make stale recovery atomic |
||
|
|
42faa84d0c |
fix(web): recover aborted srcdoc previews (#7055)
* fix(web): recover aborted srcdoc previews * fix(web): parse only top-level stylesheet imports * fix(web): preserve font import conditions * fix(web): latch srcdoc parser completion * fix(web): defer recovery while srcdoc parses * fix(web): confirm blank previews before reporting * test(e2e): cover preview white-screen monitoring * test(e2e): select the requested preview file |
||
|
|
f1d6c5c4e4 | feat(ci): preload hash skip and notification primitives (#7064) | ||
|
|
67576cb0b6 |
fix(collab): keep workspace projects usable during outages and first open (#7027)
* fix(daemon): decouple local data plane from workspace authority * test(e2e): align workspace authority contracts * fix(daemon): preserve read-only team mirrors offline * test(daemon): preserve retryable result literal * fix(web): keep projects usable during workspace outages * test(web): keep collaboration mounted during outages * test(daemon): keep viewer comments local during outages * fix(collab): open team projects during materialization * fix(collab): keep project placeholders fail-closed |
||
|
|
ab77eafeb1 |
refactor(release): retire dedicated betas and preview lanes (#7058)
* refactor(release): retire dedicated betas and preview lanes * fix(updater): consume dynamic release channels * fix(e2e): narrow counted release versions structurally |