mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
main
57 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
34ac405bfe |
fix(release): gate the Thursday patch cut on the previous patch, not the minor base (#7977)
* refactor(release): extract the patch-cut pre-flight out of inline shell Faithful port, no behavior change: resolve-patch-cut.ts computes the same version and the same X.Y.0 gate target the shell did, and runs the same gh release lookup. Moving it out of YAML is what makes the gate decision testable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(release): pin the patch cut on the previous patch, not the minor base Red against the current guard: with release/v0.22.2 cut and unshipped, the pre-flight gates open-design-v0.22.0, finds it published, and cuts v0.22.3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(release): gate the Thursday patch cut on the previous patch The guard checked the minor base X.Y.0, so it only ever guarded a line's first patch. Once X.Y.0 shipped, X.Y.2, X.Y.3, ... all passed no matter how many unshipped releases had stacked up beneath them. Gate on the release the cut actually stacks on — the highest release/vX.Y.Z below it — which is the same target cut-release.yml uses for the Tuesday minor, and still gives the answer the old minor-base comment wanted (cutting 0.15.1 checks open-design-v0.15.0, not the older 0.14.0). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(release): surface patch gate lookup failures --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
dd278cea65 |
fix(release): allow publishing whats new from release branches (#7902)
Co-authored-by: zhouzheng <zhouzheng@refly.ai> |
||
|
|
3ddff71205 |
feat(ci): publish the What's New card from the repository (#7892)
* feat(ci): publish the What's New card from the repository Move the post-update highlight document into the repo and publish it from GitHub Actions, so changing the card is a pull request instead of a task that needs personal Cloudflare credentials. - docs/whats-new.json is the source of truth, next to its spec doc. - pnpm guard validates it through the daemon's own parser on every PR, so a document that would silently show no card cannot merge. - whats-new-publish.yml uploads on merge to main (and on manual dispatch, with a read-only dry-run mode) and verifies the object by reading it back from the origin the daemon fetches, past the edge cache. * docs: name the CI job that actually runs the guard check * fix(ci): gate the What's New publish behind a main-only environment A workflow_dispatch runs the workflow file from the ref it is dispatched against, so the in-workflow main-only assertion was editable by whoever triggered it: anyone with write access could publish unreviewed content to every installed client from their own branch. Split the workflow so credentials live in one place and that place is gated by GitHub rather than by YAML. `validate` holds no secrets and runs on any ref; `publish` declares the `whats-new-publish` environment, whose deployment-branch policy allows main only and which owns the R2 secrets. Dropping the environment declaration to escape the branch policy also drops the secrets. Also points the card at the new Arena URLs supplied by product. * fix(ci): pin the What's New publish trust boundary in a check that runs The missing-credentials error still told operators the R2 keys are repository secrets. Following that string is the exact setup the previous revision closed: repository secrets are readable from any job on any branch, so a modified workflow dispatched from an unreviewed ref could publish to every installed client — or just take the keys. Point the error at the `whats-new-publish` environment and refuse the repository path in the same breath. The boundary itself had no coverage: nothing read the workflow, so moving `environment:` or a `secrets.*` expression back onto the arbitrary-ref job would stay green while still publishing correctly from main. That coverage cannot be lane-routed. Running the planner over `.github/workflows/whats-new-publish.yml` selects neither `web_tests_required` nor `ui_p0_validation_required`, so the `e2e_vitest` workload does not run for a workflow-only edit — an assertion living only there would be skipped for exactly the change class it guards. The check therefore lives in `pnpm guard`, which CI invokes from the unconditional preflight job, same reasoning as the document guard next to it. It asserts that the credential-bearing job declares the environment by its exact name, needs `validate` and is gated on its output with no `always()`/`cancelled()` escape, reads all four bucket bindings, and that no other job attaches an environment or reads any secret at all. The e2e suite drives it over the real workflow mutated into each of those regressions in turn, so every assertion is proven able to fail; gutting the checker turns all eleven red. * chore(whats-new): seed the document from the live card Bringing the workflow in with new copy attached ties the infrastructure to a content decision. The Arena CTA this document pointed at is not live — `/llm-arena-for-design/` and `/zh/llm-arena/llm-arena-for-design/` both 404 — and the card renders regardless, so merging would have put a dead button on every client's Home. Seed the repository copy from the object that is already serving instead: byte-identical to `https://whatsnew.open-design.ai/whats-new.json`, id `0.21.0`. That puts the current state under version control and makes the first automatic publish idempotent — it uploads exactly what is already there, so the merge exercises the whole chain (validate, upload, read-back against the origin the daemon fetches) without changing anything a user sees. The Arena copy is not lost; it moves to the PR body as a follow-up for the release owner, who can now change the card through this workflow without holding personal Cloudflare credentials. * fix(ci): detect secrets by context, not by access shape The guard added last round matched `${{ secrets.NAME }}` with a regex over the raw workflow text. A guard whose whole job is to stop a secret reaching the arbitrary-ref job had a blind spot for most of the ways to read one. Verified against the previous implementation, each returning zero violations while a secret reached `validate`: `secrets['NAME']`, `secrets["NAME"]`, `secrets [ 'NAME' ]`, `secrets . NAME`, `toJSON(secrets)`, a bare `secrets`, and `fromJSON(toJSON(secrets)).NAME`. Extending the regex would have repeated the mistake that caused it. One bypass proves why: a double-quoted YAML scalar may carry a backslash line continuation, so STOLEN: "${{ sec\ rets.NAME }}" parses to a live secret read while the source text contains no such substring. No text scanner can see that, however many shapes it lists. So the workflow is read through a YAML parser, and detection is a single property instead of an enumeration: GitHub's expression language has no string-to-context evaluation, so a secret cannot be reached without naming the `secrets` context as an identifier. Finding that identifier inside any `${{ … }}` expression covers every access form, including ones nobody has thought of. Parsing also retires the hand-rolled job splitter and its own failure modes, and lets the check distinguish "no violations" from "nothing was examined" — an unparseable document, a flow-style jobs mapping, or a renamed publish job now fail closed rather than passing quietly. Two holes the shape list never covered are closed as a side effect: a job-level `secrets:` key passes credentials to a called workflow with no expression at all, and workflow-level `env:` is inherited by every job. `yaml@2.9.0` becomes a root devDependency. It was already in the lockfile via `tools/release` and pinned repository-wide by the existing pnpm override, so the lockfile gains only the root importer entry. Test coverage moves with it: 26 of 46 cases now go red when the checker is gutted, up from 11. --------- Co-authored-by: lefarcen <ontf116@gmail.com> |
||
|
|
a07dc736fd |
perf(ci): take prerelease validation off the release pipeline, and report it on one live card (#7842)
* perf(ci): make prerelease mac Intel opt-in build_mac_intel is the prerelease critical path: ~33 min on the macos-15-intel fleet against ~21 min for mac arm64 and Windows, which stretches every release-branch push to ~56 min end to end. Gate it behind a new `enable_mac_x64` input that defaults to false, the same shape build_linux already uses, and let publish tolerate a skipped Intel job. Push-triggered prereleases now finish in ~21 min; a manual dispatch can still turn Intel back on. The stable promotion gate is unchanged and still hard-requires platforms.macIntel in the promoted prerelease metadata, so an Intel-less prerelease fails promotion loudly rather than shipping unvalidated. * perf(ci): stop the prerelease pipeline waiting on work it does not need Run 34102146769 took 56m, 42m of which survived turning macOS Intel off. Four things in that 42m were wall clock nobody was buying anything with. Tests no longer sit in front of packaging. functional_e2e, e2e_vitest, daemon_unit_tests and verify take ~11 min; packaging takes ~21 min, and the build jobs needed all four before they could start. They now need only metadata and run alongside the tests. Nothing about the safety of a release changes: `publish` is what writes metadata.json, and it already required all four gates to be `success` — a red suite still cannot ship. What it costs is CI minutes, since a failing gate now stops builds that have already run. The Feishu card stops cloning the repository to read one `git log`. That checkout was `fetch-depth: 0` against a 2.2 GiB pack and took 4m09s to produce a `PREV..CUR` commit list. The list now comes from the compare API, reproducing `git log --no-merges --pretty=format:'%s (%h)'` line for line — verified against 35 commits of real history, where the only difference was a leading space that feishu.ts trims anyway. Every fallback the old script had (no baseline, unreachable commit) still produces an empty changelog rather than a failed notification. Packaged smoke and Apple notarization become opt-in, like Intel already is. Smoke is advisory — `continue-on-error`, so it has never blocked publication — and costs ~2.5 min on the Windows critical path. Notarization is a round-trip to Apple that no prerelease consumer needs. Signing is NOT optional and stays on: prepare-stable.ts requires platforms.mac.signed == true, so an unsigned prerelease could never be promoted. `mac_sign_mode: no` is offered for parity with beta and now correctly reports signed=false rather than lying in the metadata. Windows drops setup-node's pnpm store cache and every job checks out shallow. Measured across four prerelease runs the store cache missed 3 times out of 4, costing 100s/66s/43s to save with no install speedup, and on its one hit traded a 32s restore for a 36s faster install — while competing for the 10 GB cache budget against tools-pack caches worth ~11 min each. Nothing in this workflow reads git history. * perf(ci): let a prerelease ship on the build's schedule, not the tests' prerelease is the internal channel. Its job is to put a package in a colleague's hands so a human can test it; automated CI is a second, parallel opinion about the same commit. Wiring the two in series meant a single flaky suite cost the team a whole day's package — the expensive failure, and the one this pipeline kept producing. The four test jobs (functional_e2e, e2e_vitest, daemon_unit_tests, verify) now gate nothing. They are absent from `build_*` and from `publish`. They still run on every prerelease and their results leave the workflow as outputs — functional_e2e_result and friends, plus a tests_all_green / failed_test_jobs rollup — so the notification layer can raise the alarm without holding the package hostage. Stable is untouched: its promotion gate reads the prerelease metadata and runs its own checks. The packaged smoke suites move for the same reason. They were already `continue-on-error`, so a smoke failure never failed the job — but they sat between the build and the R2 upload, which meant the package waited on them and a hung smoke could still eat the job. Every smoke now runs AFTER "Publish <platform> platform" and "Upload <platform> publish manifest", and every step after that manifest upload is continue-on-error, so nothing post-delivery can fail a build job and retroactively block publish. The results travel through a `test_signals` job rather than the `jobs.<job_id>.result` expression GitHub documents for workflow_call outputs, because actionlint has rejected that property since 2023 (rhysd/actionlint#343) and ci.yml runs actionlint at a pinned version. One consequence to hand to the notification work: a failed advisory job now makes the overall workflow conclusion red even though the package shipped, so `needs.build.result` is no longer the right thing to colour a card by. `version_metadata_url` is the authoritative "did a package ship?" signal and is documented as such. * perf(ci): move prerelease validation out of the pipeline it validates release-prerelease.yml holds one repository-wide concurrency group with cancel-in-progress: false, so any job that outlives `publish` keeps that group held and stops the NEXT prerelease from starting. Making the test jobs advisory did not fix that: they still held the group for 45 minutes after the package had shipped. The pipeline is now metadata -> build_* -> publish plus two dispatcher jobs that nothing depends on. The four code suites, the packaged mac/Windows smoke, and the release card each run in a workflow of their own, keyed on the origin run. The packaged smoke no longer installs electron-builder's output directory on the machine that produced it. It reads the published version metadata, downloads the DMG / setup.exe out of R2, verifies its sha256 sidecar, and writes it to the single path `tools-pack <platform> install` reads - which is why the specs need no changes. That is both a better test and what lets the smoke live on a different runner at all. The release card becomes progressive. A Feishu application bot can PATCH a message it sent, so one card is created when the first platform lands in R2 and edited in place as the rest of the platforms, the code tests, and the smoke report in. A PATCH replaces the whole card, so exactly one job may write it: the watcher owns the message and polls the three runs. The card is withheld until a package exists, because a "building..." card posted at dispatch time becomes a permanent lie when a build dies - with total build failure as the one exception, where silence would hide an incident. macOS Intel goes back to on-by-default: stable ships Intel and stable's gate is a validated prerelease artifact, so an Intel-less prerelease cannot be promoted at all. The switch stays, forwarded as `github.event_name != 'workflow_dispatch' || inputs.enable_mac_x64` - `inputs.enable_mac_x64 || true` would be unturnoffable, because a dispatch with the box unchecked also yields true. Also fixes the signal batch 2 left wrong: a reusable workflow reports failure if any job inside it failed, including work that runs after publication, so `needs.build.result` painted the download card red while its own buttons worked. `version_metadata_url` is non-empty only when publish wrote metadata, and now drives both the header colour and the smoke annotations. Two topology tests were already red on this branch - batch 2 changed the workflow without updating them. Both now assert the new invariant. The existing webhook card stays until the new one has carried a couple of real releases; a gap in release notification is worse than a duplicate. |
||
|
|
d18ada9e35 |
fix(ci): make the DSH upstream drift card readable (#7824)
The maintainer who received this alert could not tell what it was. The headline jumped straight to a verdict, so nothing said an upstream release had been detected or where the message came from; the body opened with semver theory; the blocking branch never mentioned the version we pin, making the most urgent card the least informative one; and nothing said when upstream shipped, so there was no way to tell a release from this morning apart from one that had been sitting for a fortnight. The headline now names the check and only the check — severity travels in the header template, which Feishu renders before any text. The first sentence is three facts with no reasoning attached: what upstream released, when, and what we still pin, on both severities. The consequence follows, and the semver rationale is compressed into a parenthetical behind it. The three-file checklist is untouched. Reading the publish date means asking npm for the full packument: the compact one (`application/vnd.npm.install-v1+json`) carries no `time` map at all. `readPublishedAt` degrades to null for every shape short of that, because the date is one sentence of context on an alert whose entire reason to exist is being sent. Also replaces a self-check assertion that could never fail: `String(card.header).length === 0` is "[object Object]".length whatever the card says. It now reads the fields a human reads. Claude-Session: https://claude.ai/code/session_01B8MaA1ABkvbnfaYUKw3UH3 |
||
|
|
8ab7c3d5f1 |
feat(standalone): establish the exact lifecycle and Terminal carrier (#7716)
* feat(standalone): add shell-neutral exact lifecycle * feat(terminal): add native exact cold-start carrier * test(terminal): exercise mutable exact channel heads * feat(standalone): model synced blobs and Shell updates * feat(terminal): exercise the complete Shell lifecycle * feat(release): add the independent exact Terminal line * test(exact): run the full local Terminal release line * feat(terminal): harden exact Sidecar lifecycle * feat(standalone): prove exact update transactions * refactor(standalone): separate shell lifecycle dimensions * refactor(e2e): decouple terminal exact drivers * refactor(terminal): isolate exact sidecar fixture host * feat(terminal): gate handoff on physical retirement * refactor(release): gate exact activation on public acceptance * test(terminal): formalize guarded lifecycle algebra * fix(terminal): guard shell installer handoff * feat(terminal): add exact bootloader handoff * feat(terminal): integrate exact sidecar lifecycle * refactor(exact): rename validation channels * test(terminal): harden exact release acceptance * fix(postinstall): build standalone dependencies first Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(ci): build the native Terminal scene Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * test(ci): cover exact convergence handoff routing Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(exact): isolate terminal validation from main CI * fix(terminal): complete shell update confirmation Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(exact): bind acceptance to installed manifest Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(standalone): block occupied shell installs Generated-By: looper 0.11.2 (runner=fixer, agent=codex) --------- Co-authored-by: Looper <looper@noreply.github.com> |
||
|
|
1d2150007b |
chore: remove extracted landing-page from the monorepo (#7727)
* chore: remove extracted landing-page from the monorepo The marketing site now lives in a separate repository. Drop the Astro app, its GitHub workflows/scripts, and the remaining monorepo coupling so this workspace no longer builds or deploys it. * fix: drop leftover landing-page coupling from Teams and skills Point development enterprise URLs at the deployed marketing origin, remove nextjs-app skill outputs that no longer emit a page, and stop advertising apps/landing-page in plugin marketplace metadata. Generated-By: looper 0.12.0 (runner=fixer, agent=omp) * fix(ci): restore DSH installer E2E Vitest routing Removing dsh-bootstrap-identity dropped the only planner route from tools/release/resources/dsh-bootstrap/ to web_tests_required, so installer-only PRs skipped e2e/tests/dsh-installer-version-policy.test.ts. Generated-By: looper 0.12.0 (runner=fixer, agent=omp) * docs: drop leftover landing-page paths from template assets The skill contract no longer advertises the extracted Astro app, but styles.css, compose.ts, and example.html still named deleted apps/landing-page sources. Describe the standalone template behavior instead and keep the bundled plugin example in sync. Generated-By: looper 0.12.0 (runner=fixer, agent=omp) * docs: keep landing-page off review and test surfaces Add apps/landing-page to the forbidden-surface review list and stop naming the extracted path in plugin registry acceptance tables. Generated-By: looper 0.12.0 (runner=fixer, agent=omp) * fix: drop leftover landing-page refs and bump plugin version Stop naming the deleted apps/landing-page in the landing-deck demo and plugin-registry plan. Bump example-open-design-landing to 0.1.1 and keep the official catalog version aligned with the bundled manifest. Generated-By: looper 0.12.0 (runner=fixer, agent=omp) |
||
|
|
241f520037 |
feat(release): publish versioned catalog and DSH snapshots (#7617)
* feat(release): publish versioned catalog and DSH snapshots Add tools-release catalog export/pack/publish and a canonical DSH bootstrap source so landing can later consume immutable R2 snapshots without globbing the monorepo tree. * fix(release): harden catalog snapshot publishing * fix(release): use portable tar ownership flags * fix(release): resolve catalog root from git checkout * fix(release): stabilize catalog preview assets * fix(release): close catalog publish determinism gaps * fix(release): remove mutable catalog runtime metadata * fix(release): prevent stale catalog pointer updates * fix(release): fail all-fallback catalog previews * fix(release): fail previews with blocked dependencies * fix(release): export localized design system bodies * fix(release): pin catalog preview environment * fix(release): close catalog and bootstrap rollback gaps * fix(release): fail partial catalog preview renders * fix(release): render remote catalog entries locally * fix(release): keep DSH bootstrap publishes fresh * fix(release): parallelize catalog snapshot uploads * fix(release): preserve hidden catalog assets * fix(release): trigger catalog publish on storage helpers Include common.ts and s3-upload.ts in catalog-publish and catalog-validate path filters so publisher CAS, signing, and retry changes republish. * fix(release): gate catalog provenance and DSH helper triggers Validate complete provenance against the packed bundle before any immutable upload. Include common.ts and s3-upload.ts in the standalone DSH publisher path filter. * fix(release): match provenance recordCounts to catalog Derive counts from catalog.records and require an exact match before any immutable upload so empty or stale provenance cannot become latest. |
||
|
|
49cc51058f | chore(nix): retire official Nix distribution (#7644) | ||
|
|
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 []`. |
||
|
|
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> |
||
|
|
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 |
||
|
|
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> |
||
|
|
f1d6c5c4e4 | feat(ci): preload hash skip and notification primitives (#7064) | ||
|
|
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 |
||
|
|
f1a0b60c6c |
chore: unify product name display to OpenDesign repo-wide (#6998)
* chore: unify product name display to OpenDesign repo-wide Rename the user-facing product name from "Open Design" to "OpenDesign" across documentation, web/desktop UI strings, i18n locales, prompts, plugin/skill/design-system content, changelogs, and specs, matching the landing-page rename already in flight. Deliberately excluded so installed-app identity stays untouched: - packages/release PRODUCT_NAME and everything derived from it (app bundle names, installer/registry identity, updater feeds) - tools/pack, tools/release, apps/packaged, landing page (PR #6983) - mocks/ recorded traces and LICENSE - test fixtures and comments that encode real on-disk install paths (Open Design.app, Program Files\Open Design, AppData paths) Validated with pnpm guard, pnpm typecheck, and targeted web/desktop/host test runs over the renamed strings. * fix(web): keep two-letter author monogram for CamelCase brand names authorInitials split words on whitespace only, so the renamed single-token author name "OpenDesign" produced a one-letter monogram. Treat lower-to-upper CamelCase boundaries as word breaks so the avatar fallback stays "OD". * fix(release): restore spaced Windows source artifact names in win.ps1 tools/pack still derives builder outputs from the unchanged PRODUCT_NAME ("Open Design"), so the asset script must look for the spaced filenames. Matches the mac/mac-intel/linux asset scripts, which were already kept on the original names. --------- Co-authored-by: Joey <236967869+joeylee12629-star@users.noreply.github.com> |
||
|
|
dba64f2e45 |
fix(landing): short HTML edge TTL + host purge after production deploy (#6552)
* fix(landing): shorten HTML edge TTL and host-purge after production deploy Long s-maxage=3600 + stale-while-revalidate=86400 left locale HTML (/zh/pricing/, /de/pricing/, …) on different edge objects after landing-page-production, so some languages looked deployed and others did not. Cap HTML/edge docs at s-maxage=60 without multi-hour SWR, and purge open-design.ai by hostname after pages deploy (token needs Zone Cache Purge). Hashed _astro assets stay immutable. * ci(landing): add manual host-purge workflow for edge cache recovery Allows testing CLOUDFLARE_API_TOKEN Cache Purge permission and one-click recovery without a full production rebuild. Production deploy still purges after pages deploy. * ci(landing): support purge-only production workflow mode Lets us exercise CLOUDFLARE_API_TOKEN Cache Purge without a full rebuild, and keeps host purge as a post-deploy job after a real promote. * ci(landing): move edge-cache purge helper to workflow-owned TypeScript pnpm guard rejects project-owned .mjs, so the new purge helper blocked Validate workspace. Convert it to TypeScript and move it next to the other landing workflow helpers under .github/scripts/, invoked with --experimental-strip-types like landing-page-deploy-feishu.ts. Also drop the purge-only production mode: blog-indexing-on-deploy listens on successful landing-page-production runs and cannot tell a cache-only recovery run from a real deploy. Purging without a rebuild already has a dedicated entry point in landing-edge-cache-purge, so production purge now runs only after a successful deploy. |
||
|
|
028bde50ad |
fix(release): harden prerelease E2E validation (#6755)
* fix(release): harden prerelease E2E validation
* fix(daemon): refresh active run tool tokens
Generated-By: looper 0.11.8 (runner=fixer, agent=codex)
* chore(release): keep Feishu notification unchanged
* fix(daemon): retain media tasks for active runs
Associate token-authored media tasks with their owning run and defer terminal cleanup while that run still has an active tool token. Correct the prerelease status document to reflect the final Feishu notification topology.
Generated-By: looper 0.11.8 (runner=fixer, agent=codex)
* test(e2e): defer system theme coverage
* test(e2e): remove retired design system settings flows
* fix(e2e): close prerelease coverage gaps
* fix(release): remove unrelated UI changes
Generated-By: looper 0.11.8 (runner=fixer, agent=codex)
* fix(e2e): restore remaining prerelease coverage
* docs(e2e): record remaining scoped gaps
* test(e2e): isolate workflow command fixtures
Generated-By: looper 0.11.8 (runner=fixer, agent=codex)
* test(e2e): preserve Node in workflow fixture PATH
Generated-By: looper 0.11.8 (runner=fixer, agent=codex)
* fix(ci): skip Playwright apt deps on Blacksmith runners
Generated-By: looper 0.11.8 (runner=fixer, agent=codex)
* Revert "fix(ci): skip Playwright apt deps on Blacksmith runners"
This reverts commit
|
||
|
|
7ecd35a746 |
fix(release): guard automated release trains (#6729)
* fix(release): guard automated release trains * fix(release): resolve beta recovery before checkout * style(release): group workflow outputs * fix(release): publish daily beta snapshots without promotion |
||
|
|
4e47119edb |
ci: right-size UI P0 runners to medium (#6502)
Measured UI P0 shards fit nexu-runners-medium; drop the blanket xlarge assignment and keep only project-collab on ui_p0_heavy (xlarge) because multi-client collab has hit large/xlarge memory ceilings. |
||
|
|
2a971e4f0e | ci: add Blacksmith runner mode (#6533) | ||
|
|
fef0ddf88e |
ci: auto-rerun once on runner/spot cancel (#6453)
* ci: auto-rerun once when runner/spot cancel kills ci jobs Add rerun.atom.yml as a trusted workflow_run consumer that requests one `gh run rerun --failed` when completed pull_request/merge_group ci runs lose leaf jobs to infra cancel (shutdown signal / cancelled runner), while skipping ordinary assertion failures, stale heads, and ejected merge-queue entries. * fix(ci): grant rerun atom read scopes and refuse mixed failures Add checks/pull-requests read permissions so the helper can fingerprint infra cancels, and require every non-success leaf to be infra-classified before authorizing gh run rerun --failed. Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build) * fix(ci): require open PR for live-head stale gate Stop treating a same-repo branch tip as live-PR evidence. Closed PRs can leave the source branch at the failed run SHA with no open association; query open pulls by repository-qualified head ref instead, and cover the closed-PR + unchanged tip shape in self-check. Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build) * fix(ci): require infra marker for cancel rerun Bare cancelled jobs without shutdown/spot annotations are ordinary (manual cancel) and must not authorize gh run rerun --failed. Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build) * fix(ci): ignore aggregate gate jobs in infra-cancel classify Validate workspace and Runtime summary fail as always() mirrors of leaf outcomes and never carry runner/spot markers. Counting them as ordinary blocked pure infra-cancel reruns whenever the workflow still reached the gate. Exclude those names from classification and cover the shape in self-check. Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build) * fixer: address PR #6453 follow-up items Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build) * fix(ci): refuse mixed ordinary+infra job annotations Jobs that record both a real failure annotation and a later runner shutdown marker must classify as ordinary, not pure infra-cancel, so gh run rerun --failed cannot auto-retry assertion failures. Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build) * fix(ci): paginate check-run annotations before infra classify GitHub defaults annotations to 30 results; a later ordinary failure must still block gh run rerun --failed. Walk pages like paginate_jobs and cover the split with a multi-page request fixture. Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build) * fix(ci): drop tracked pycache from infra-cancel helper PerishCode BLOCKING on #6453: remove the accidental CPython 3.14 bytecode under .github/scripts/__pycache__/ and ignore __pycache__/ plus *.py[cod] so local self-check/compile output stays out of git. * fix(ci): open-PR merge_group freshness; ignore warning annotations Merge-group infra-cancel retries no longer require the synthetic head to still sit in the merge queue — workflow_run completed fires after required checks already ejected it. Freshness is an open PR still associated with the run (commit→pulls or gh-readonly-queue pr-N ref). Warning-level annotations (e.g. scopes.ts full-plan fallback) no longer count as ordinary failures that suppress pure infra-cancel retry. Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build) * fix(ci): match merge_group PR heads and arm rerun e2e Vitest Compare the live open PR head with the PR head SHA encoded in the merge-queue branch before authorizing infra-cancel retry, and enroll the rerun atom surfaces in scopes so PR CI runs the topology self-check. Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build) |
||
|
|
8e3853f0bc |
ci: route UI P0 jobs onto nexu-runners-xlarge (#6466)
UI P0 (especially project-collab) regularly drives the large runner container working set to the 16Gi limit. Add a dedicated ui_p0 profile on nexu-runners-xlarge and keep lighter browser jobs on large. |
||
|
|
d87ba8e5b9 |
ci: route Linux validation to Nexu runners (#6407)
* ci: route UI P0 to Nexu runners * ci: route Linux validation to Nexu runners * ci: size Nexu runners by workload * ci: preserve economic runner bootstrap * test: budget scope process coverage * ci: run browser validation on large runners * ci: align runner mode bootstrap semantics * ci: cap Playwright workers on large runners * ci: match hosted Playwright concurrency * test(desktop): accept full retry after interrupted download * test(web): stabilize privacy choice click * ci: use preseeded Playwright on Nexu runners |
||
|
|
088ea9041f |
ci(landing): post a Feishu card after production deploys (#5290)
`landing-page-production` promotes the live site (open-design.ai) but never announces it — the landing/QA group only learns the site changed by noticing. This adds a `notify` job that fires once the deploy settles (on success AND failure) and posts an interactive Feishu card, the production-deploy analog of notify-release-feishu. The card carries: status (green/red), the exact deployed commit + its HEAD subject, who promoted it, their "why promote now?" reason, a live "打开 open-design.ai" button, and the landing-relevant changelog since the previous successful production deploy (non-landing commits on main are filtered out as noise). Reuses FEISHU_LANDING_WEBHOOK with the release webhook as fallback, matching landing-page-daily-feishu. Card generation lives in .github/scripts/landing-page-deploy-feishu.ts with a `self-check` command (asserts copy, sanitization, and the failure/cold-start fallbacks) run as a workflow step before posting. |
||
|
|
e0d7a77230 |
fix(release): align stable notes public origin (#5037)
Co-authored-by: Siri-Ray <2667192167@qq.com> |
||
|
|
514ddba8ed |
ci: consolidate runner selection contract (#4803)
* ci: consolidate runner selection contract * test: stabilize connector settings privacy state |
||
|
|
8b9b169673 |
ci: add cost-sensitive runner tiering (#4764)
* test persistent contabo runner poc * test persistent runner web workspace poc * test persistent runner local cache setup * test persistent runner aggressive setup poc * test persistent runner web postinstall profile * test restore stable persistent runner poc * test keep persistent runner web tests default * test route persistent poc to zxiyun runner * test relax web vitest timeout on persistent poc * test harden web vitest cases for persistent runner * test add runner perf probe * test isolate runner perf probe dispatch * test pin runner perf probe node * test expand runner perf probe matrix * ci: route heavy jobs through runner modes * ci: keep web workspace tests on blacksmith * test: update web runner mode assertion * ci: route cost-sensitive runner tiers * ci: move preflight to hosted runner * ci: downshift blacksmith ui runners to 4vcpu * test: fix file viewer timeout after merge * ci: route e2e vitest to hosted default * ci: centralize runner profile selection * test: type runner profile assertions * ci: route e2e vitest to default blacksmith * ci: remove runner experiment probes * test: restore postinstall guard coverage Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex) * test: cover postinstall script contract * test: move postinstall guard to e2e --------- Co-authored-by: Looper <looper@noreply.github.com> |
||
|
|
4c6f6549c6 | ci: add landing QA Feishu summary (#4763) | ||
|
|
0bf1b6d6b8 |
[codex] converge release workflows and stable dry-runs (#4390)
* fix(tools-pack): use junctions for Windows standalone peer deps * fix(desktop): expose IPC during startup * fix(tools-pack): preserve Windows inspect diagnostics * fix(tools-pack): report Windows inspect status errors * fix(packaged): use Electron net fetch for app protocol * fix(packaged): load Windows renderer from web sidecar * fix(desktop): show Windows packaged window during startup * fix(packaged): disable Windows GPU startup * fix(tools-pack): keep Windows core smoke observable * fix(packaged): remove Windows startup probes * fix(tools-pack): trace Windows desktop IPC status * fix(tools-pack): add Windows IPC diagnose loop * fix(release): default beta-s Windows updater feed * chore: clean merged test eof * refactor(release): unify prerelease channel model * chore(release): close prerelease doc escape hatches * refactor(release): converge release channel workflows * fix(release): install toolchain in metadata jobs * fix(release): build release package before contracts * chore(release): bump development version to 0.10.1 * fix(e2e): seed windows packaged smoke runtime config * fix(release): install toolchain for metadata publish * fix(release): materialize betas metadata checkout * chore(release): bump development version to 0.10.2 * fix(release): allow betas metadata cold start from s3 * fix(e2e): support betas packaged update scenarios * fix(release): pass betas channel into packaged smoke * fix(release): set betas channel during self-hosted builds * fix(release): verify counted channel reservations * fix(release): use pnpm cmd for betas windows publish * fix(release): add betas manifest artifact fallback * fix(release): skip beta-s public metadata fetch * fix(release): read beta-s manifests from storage * fix(release): cache beta windows tools-pack builds * fix(release): inline beta mac tools-pack builds * fix(pack): deep sign unsigned mac bundles * docs(pack): document payload-first beta updater validation * fix(release): align preview tools-pack cache flow * fix(release): align prerelease tools-pack cache flow * fix(release): pass github token to prerelease metadata * fix(release): setup pnpm before feishu notify * fix(release): add stable dry-run prepublish flow * fix(release): accept completed prerelease metadata gate * fix(release): require stable release branches * fix(release): converge r2 access checks * fix(updater): use release channel parser for defaults * fix(updater): harden windows payload relaunch * fix(release): converge updater smoke fixture contract * test(e2e): require silent updater fixture output * fix(release): align stable windows smoke build path * fix(ci): include release workspace in validation * fix(ci): repair release validation lanes Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex) * fix(ci): restore zero-install Feishu notification Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex) --------- Co-authored-by: Looper <looper@noreply.github.com> |
||
|
|
fa544f2836 |
[codex] Prune unused automation and repair metrics publishing (#4612)
* chore(workflows): prune unused automation * chore(workflows): update github app token action * chore(workflows): use github app client id |
||
|
|
c1701f709a |
[codex] codify workflow handoff capabilities (#4503)
* codify workflow handoff capabilities * Harden workflow marker jq lookups Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex) * harden autofix head validation * Preserve Nix hash fallback guidance Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex) * Gate Nix hash fallback comments Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex) * Clear stale Nix fallback comments after autofix Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex) --------- Co-authored-by: Looper <looper@noreply.github.com> |
||
|
|
7a1ad1a438 |
feat(packaged): add launcher payload updates (#3812)
* feat(packaged): add launcher payload runtime * ci: focus launcher payload windows smoke * ci: publish beta mac x64 launcher payload * test(e2e): harden payload updater smoke * fix(pack): inject launch config for installed windows smoke * fix(daemon): allow packaged payload resource roots * fix(updater): relaunch after payload update * test(e2e): refresh windows updater UI status * test(e2e): ensure windows smoke reaches main shell * test(e2e): skip onboarding before windows updater smoke * fix(updater): detach windows payload relaunch helper * test(e2e): run payload smoke without updater dry run * fix(desktop): relaunch windows payload updates on quit * fix(updater): preserve installed payload relaunch target * fix(updater): require launcher target for payload updates Generated-By: looper 0.9.5 (runner=fixer, agent=codex) * test(e2e): align mac x64 artifact mode assertion * test(e2e): canonicalize launcher relaunch root * fix(updater): validate launcher payload config before activation Generated-By: looper 0.9.5 (runner=fixer, agent=codex) * fix(updater): require live launcher target for payload updates Generated-By: looper 0.9.5 (runner=fixer, agent=codex) --------- Co-authored-by: Looper <looper@noreply.github.com> |
||
|
|
8f6cdaaba1 |
Harden release-beta-s self-hosted beta lane (#3641)
* Fix Windows smoke launcher payload layout
* Harden release-beta-s runner probe
* Use cmd shell for release-beta-s probe
* Add win beta build script for self-hosted lane
* Keep release-beta-s checkout lightweight
* Use explicit local beta version for release-beta-s
* Install dependencies in beta build script
* Repair Electron dist before Windows beta build
* Add release-beta-s timings and diagnostics
* Polish release-beta-s observability guardrails
* Trace tools-pack cache materialization
* Reuse materialized workspace build cache
* Trace Windows packaging segments
* Trace Windows archive process details
* Reuse installer unpacked materialization
* Preserve reusable Windows unpacked projection
* Add fast smoke telemetry for self-hosted beta
* Cache Windows archive outputs for beta lane
* Tighten fast Windows smoke lane
* Scope Windows beta artifact reporting by target
* Clean stale Windows target artifacts
* Wire Windows Authenticode signing
* Expose self-hosted beta validation modes
* Add core Windows smoke profile
* Decouple core smoke from updater fixture version
* Trace Windows smoke lifecycle steps
* Skip unpacked materialization on NSIS cache hit
* Add Nexu S3 beta publisher
* Harden self-hosted Windows beta lane
* Use Windows PowerShell for publish probe
* Run publish probe via repo script
* Require explicit self-hosted S3 public origin
* Run real installer acceptance for external feeds
* Skip ready-prompt checks for external feeds
* Relax optional self-hosted update inputs
* Tolerate BOM in self-hosted publish probe
* Converge self-hosted beta metadata and signing flow
* Run self-hosted beta metadata prep on the runner lane
* Use Windows PowerShell for self-hosted beta metadata checks
* Bypass execution policy in self-hosted beta metadata checks
* Split self-hosted beta metadata publish
* Suppress mc output in beta publish helpers
* Split Windows NSIS payload cache layers
* Require fresh tool dist metadata
* Preserve Windows overlay payload paths
* Default beta-s gate to core
* Optimize win beta packaging reuse
* simplify windows full smoke lifecycle
* preserve onboarding state after windows upgrade smoke
* fix windows tools-pack test portability
* recover stale tools-pack locks
* Allow auto signing fallback for beta builds
* Add self-hosted beta mac arm64 lane
* Quote self-hosted signing choices
* Run self-hosted beta metadata on mac lane
* Use shallow metadata checkout on self-hosted beta
* Use sparse beta metadata checkout
* Make mac release profile bash-compatible
* Allow file sparse metadata checkout
* Isolate self-hosted metadata checkout
* Clear sparse skip-worktree before mac build
* Isolate self-hosted mac build checkout
* Run self-hosted beta publish without aws cli
* Use unsigned beta prefix for mixed platform publish
* Sparse checkout self-hosted beta publish
* Publish mac beta platform before metadata
* Pass mac publish release profile
* Probe mac signing keychain import
* Expand mac signing preflight variants
* Use sudo keychain helper for self-hosted mac signing
* Route self-hosted mac codesign through sudo wrapper
* Normalize mac signing identity name
* Harden self-hosted mac notarization
* Optimize self-hosted mac release cache
* Fix mac signing helper cleanup
* Retry transient mac notarization uploads
* Keep mac notarization fail-fast
* Reduce mac symlink diagnostics noise
* Normalize self-hosted beta platform inputs
* chore: bump beta base version to 0.9.1
* chore: tune release tools-pack cache retention
* test: add temporary cache validation marker
* chore: key windows nsis base payload by content
* Revert "test: add temporary cache validation marker"
This reverts commit
|
||
|
|
2305a35768 |
ci(release): split Feishu streams — daily=beta, release-push=nightly (#3474)
Follow-up to #3462. Two corrections: - The per-push release/** stream must build the nightly channel (release-stable), not beta. The daily 09:00 stream stays on the lighter beta channel. - The nightly card was missing the macOS Intel download button; release-stable builds mac arm64 + mac Intel (signed) + win unconditionally, so switching that stream to it adds the Intel button for free. Changes: - release-stable.yml: add workflow_call (channel/nightly_version/ref inputs + outputs incl. per-platform download URLs and previous_commit); add ref to every checkout; pin GITHUB_SHA to the resolved build HEAD in metadata/publish; capture the previous nightly commit before publish for the changelog baseline. - release-beta.yml: expose mac_arm64_url + win_url as workflow_call outputs (the rest of its workflow_call interface from #3462 is unchanged; daily still uses it). - notify-release-feishu.yml: call release-stable channel=nightly; CHANNEL_LABEL=Nightly. - notify-daily-feishu.yml: keep calling release-beta; CHANNEL_LABEL=Beta. - notify.ts: channel-agnostic title/changelog via CHANNEL_LABEL; download buttons from explicit per-platform URL outputs (mac_arm64/mac_intel/win/linux) instead of re-fetching metadata.json — guarantees the Intel button on nightly. Ref plumbing mirrors release-beta: scheduled callers run on main but build a release branch, so checkouts take inputs.ref and metadata/publish pin GITHUB_SHA so metadata.json (and the next run's changelog baseline) records the real commit. |
||
|
|
6ffa75aa5b |
ci(release): build beta + notify Feishu on release push and daily (#3462)
Reuse the release-beta pipeline as a workflow_call and add two thin caller
workflows that build a full beta ("nightly") package and post a Feishu card
with download links and the changelog since the previous beta:
- notify-release-feishu: triggers on every push to release/**, debounced
(concurrency cancel-in-progress), notifies FEISHU_RELEASE_WEBHOOK.
- notify-daily-feishu: cron 01:00 UTC (09:00 Asia/Shanghai) + manual; resolves
the active release branch (highest-semver release/vX.Y.Z) and builds it via
the new ref input, notifies FEISHU_DAILY_WEBHOOK.
release-beta gains a workflow_call entry (enable_* booleans + optional ref +
outputs) so callers reuse one build source. Because scheduled callers run on
main but must build a release branch, every checkout takes inputs.ref and the
metadata/publish jobs pin GITHUB_SHA to the resolved build HEAD so the published
metadata.json records the real built commit (and the next run's changelog
baseline stays correct).
The changelog needs zero extra state: the baseline is the previous beta's commit
read from beta/latest/metadata.json before this run overwrites it; download links
come from this build's version metadata.json. notify.ts renders a Feishu
interactive card and supports optional signed bots (FEISHU_*_SIGN_SECRET).
|
||
|
|
551f967d2c |
ci(agent-pr-explore): rewrite prompt — non-lazy disposition + mandatory probe list (#3156)
* ci(agent-pr-explore): rewrite prompt — non-lazy disposition + mandatory probe list Background: on PR #2355 (large AMR runtime add) the agent stopped at smoke level because the positive path was gated on a missing `vela` binary. The old prompt explicitly instructed "if setup prerequisites block, return inconclusive immediately" + "do not spend more than two attempts on test data" + "do not run arbitrary host shell commands", which made the agent give up rather than: - use the PR's own `tests/fixtures/fake-vela.mjs` - set the `VELA_BIN` env the runtime reads - probe `/api/integrations/vela/*` directly via fetch This rewrite shifts disposition + adds 4 structured unblock steps: - **Mindset**: each /explore is a precious, expensive run; be thorough, not lazy. - **STEP 0** Read PR body for `## Test Plan` section — declared cases = MUST-COVER. - **STEP 1** Extract diff-driven probe list (new routes / components / env vars / fixtures / CLI flags). Anything skipped requires explicit written reason. - **STEP 2** Before giving up, try (a) PR-provided fixtures, (b) build minimal stub inside container, (c) probe APIs directly via page.evaluate fetch, (d) search repo / related PRs / docs for unknown terms. - **STEP 3** 4-7 cases for substantive PRs (was hard cap 3). - **STEP 4** Login / multi-tab / OAuth — use Playwright multi-page handling; read creds from env, never echo. - **SECURITY** strengthened: env vars matching common secret patterns are confidential; never echo / log / write to file / page.evaluate / report. - **Report** new required §Mitigations Attempted for Inconclusive verdicts — must list what was tried + why each didn't unblock. Kept unchanged: 3-min keepalive constraint, untrusted-data rule, no-host-shell rule, report markdown structure (✅/⚠️/❌/⚪ + case emoji). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(agent-pr-explore): truncation-aware + soft-request protocol + capability fix Addresses review feedback from @mrcfps: (1) STEP 1 — diff probe list was instructed to enumerate "every new route/component/env/fixture/flag" without acknowledging that the harness already truncates the diff upstream (file_patch_max_chars + context_max_bytes). On exactly the large PRs this prompt targets, the agent only sees a slice. Fix: prompt now explicitly tells the agent the context MAY be truncated and to (a) note the truncation in §🧭 Scope and (b) emit §📎 Needs to ask the maintainer to attach the missing source files into the private workspace for the next run. (2) STEP 2 — old text told the agent to "create stubs inside the sandbox container", "rewire env / PATH", and "run gh / grep searches". The harness does not expose docker exec or arbitrary shell to the agent (capabilities are fs:write on host + Playwright on host driving the dockerized app via HTTP). The instructions promised things the agent literally cannot do. Fix: STEP 2 now spells out the actual capabilities (host fs:write, Playwright page.evaluate / page.request, host-side $WORKSPACE_DIR if maintainer pre- attached one). The "unblock by stub" path is rewritten honestly: build a host-side stub if useful, but acknowledge container env is fixed at docker run and signal what's needed via §🔑 Needs / §📎 Needs for the next iteration. The "search repo for unknown term" step (which required gh/grep) is dropped in favor of using $WORKSPACE_DIR materials. (3) Soft-request protocol (new): The agent is READ-ONLY for secrets and workspace — it cannot self-attach. But it can SIGNAL what was needed via two new optional report sections: - §🔑 Needs — secret request ("VELA_RUNTIME_KEY: needed to verify ...") - §📎 Needs — workspace file request ("amr-auth-spec.md: clarifies ...") The dashboard (synclo platform; see nexu-io/synclo#79 RFC §6.8) will parse these structurally and surface as one-click attach hints to the maintainer on the run detail screen. Pure passive signal; no auto-action; zero prompt- injection risk (no code path takes the values). Hard rules: - No pasting of existing secret values (security) - Each item MUST tie back to a specific blocked case in §🧪 Cases — not speculative - Workspace privacy: agent may reference workspace files BY PURPOSE ("verified positive-1 from test-plan.md") but NEVER paste their content into the report This commit is non-trivial (~65 lines net) but the changes are tightly scoped: honesty about capabilities + a new signaling channel that replaces the impossible direct-action promises. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(agent-pr-explore): remove gh-search + container-exec language from prompt MINDSET bullet: replace 'gh search issues/prs/code' with in-scope materials (PR body, diff context, workspace files) plus page.request/page.evaluate probes, matching actual harness capabilities. SECURITY bullet: replace the contradictory 'You may run commands INSIDE the sandbox container' with a clear statement that the agent has no shell or container exec access, only the Playwright browser. Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) * ci(agent-pr-explore): fix Needs section misuse and unawaited fetch body Move fixture env-var wiring request from §🔑 Needs to §📎 Needs and remove the concrete host path from the example; §🔑 Needs is secret-name-only and must not carry filesystem paths. Await r.text() in the page.evaluate fetch example so the body field resolves to a string instead of an unresolved Promise. Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) * ci(agent-pr-explore): broaden 📎 Needs to cover env/config wiring alongside file attachments Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) * ci(agent-pr-explore): fix step-2b Needs routing and split AMR_USER/AMR_PASS example Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) * ci(agent-pr-explore): fix mindset bullet — env var cannot be set mid-run Replace "set the env var" in the MINDSET mitigations list with "identify the env var and request the needed startup wiring in §📎 Needs" to match the actual capability boundary: container env is fixed at docker run time and cannot be changed by the agent mid-run. Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ae7a417208 |
ci: add idempotent provision script for the agent-pr-explore runner (#3122)
* ci: add idempotent provision script for the agent-pr-explore runner The self-hosted runner's setup was hand-assembled and easy to lose on a rebuild — most dangerously the codex-acp pin: expect-cli bundles codex-acp 0.10, which is incompatible with ChatGPT-account auth (every model rejected); we run 0.15, but any expect-cli reinstall silently reverts it and breaks the agent. Add a self-contained, idempotent provision script that brings the runner's config layer back to a working state and is safe to re-run: codex model pin (gpt-5.4), the codex-acp 0.15 pin (npm pack + extract + chmod), deploy-key generation, base-repo git mirror seed/refresh, pnpm-store/reports dirs, the weekly image-refresh helper + cron, and the readiness self-check helper. The header documents the manual/secret steps it intentionally does not automate (base toolchain + colima, the interactive `codex login`, registering the deploy key on the repo, and registering the Actions runner service). Verified idempotent against the live runner (all checks pass, no config disturbed). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: provision — update codex model key in place, don't truncate config.toml Review: step 2 overwrote the whole ~/.codex/config.toml with just the model line whenever the exact pin wasn't already present, dropping any other Codex settings on a re-run — destructive, contradicting the idempotent goal. Now: replace an existing `model =` line in place (sed), append only when the key is absent, and leave the rest of config.toml untouched. Verified preservation locally. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: provision — create ~/.ssh before ssh-keygen on fresh host Review: on the fresh-rebuild path this script targets, ~/.ssh usually does not exist, so `ssh-keygen -f ~/.ssh/od_agent_deploy` fails with "No such file or directory" and the deploy key (and downstream mirror bootstrap) never gets created. mkdir -p the key's parent dir (chmod 700) before keygen, and only print the pubkey when it actually exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
54f225d6b3 |
ci: retry PR-context gh calls so a transient API blip doesn't abort the run (#3128)
* ci: retry PR-context gh calls so a transient API blip doesn't abort the run The early PR-context gathering calls `gh pr diff`, `gh pr view`, and `gh api .../files`. gh hits api.github.com under the hood, and a single transient timeout/5xx there aborts the whole run before any exploration (seen on #3083: "could not find pull request diff: Get \"https://api. github.com/...\": net/http timeout"). These were the only network calls in the run without a retry (source fetch + npm already retry). Add a small gh_retry helper (4 attempts, linear backoff) and wrap the three read-only context calls. gh writes nothing to stdout on a failed API call, so retrying is safe even for the calls piped into the context file; the retry warning goes to stderr. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: address review — buffer gh retries to files (no paginated duplication) Review (Siri-Ray): wrapping `gh api --paginate` retries inline while the context block is redirected to the file means a mid-pagination failure leaves partial pages in the context, and the retry appends them again — duplicating the patches section and burning the context budget the agent reads. Replace the in-pipe gh_retry with gh_retry_file: each call buffers to its own file per attempt (`>` truncates on open, so a failed/partial attempt is discarded before the next), and the context block just cats the finished files. Fetch PR body + patches to files up front, then assemble. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
a6a56099ca |
ci: show per-case pass/fail status emoji in agent report (#3118)
Reviewers asked for at-a-glance outcomes. Instruct the agent to begin each "Cases Tested" bullet with a status emoji (✅ pass / ❌ fail / ⚠️ warning / ⚪ inconclusive) and a bold case name, so the report shows which checks passed or failed without reading each line. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
bf61a39cb5 |
ci: clean agent report (write-to-file) + slim artifacts/uploads (#3116)
* ci: clean agent report (write-to-file) + slim artifacts/uploads Four related cleanups to the agent PR exploration output: 1. Clean report. The PR comment / report.md was assembled by dumping the entire verbose expect.log (ACP init logs, "Git failed" warnings, the ~24KB echoed prompt, ANSI codes, progress checklist) under the trace header -- ~28KB of noise. Instead, instruct the agent to write its final Markdown report to a file via its file-write tool, and have the runner read that file directly. Verified: Codex writes a clean report to the given absolute path. Falls back to an inconclusive note if the agent did not finish. 2. Drop duplicate trace/video. The script copied playwright-smoke-trace.zip -> playwright-trace.zip (a ~28MB legacy duplicate) and the webm likewise, and uploaded both to R2. Keep only the canonical smoke-named artifacts. 3. Slim the GitHub artifact. The trace zips and videos are already on R2; exclude *.zip / *.webm from the uploaded artifact so it drops from ~56MB to <1MB (report + logs only). 4. Persist report on the runner. Copy the report / agent-report / expect.log / trace URL to a stable host dir ($HOME/.cache/agent-pr-explore/reports/pr-<n>) so dry runs (skip_comment) can be inspected without downloading the artifact. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: address review — keep advisory reports + recursive artifact excludes Review findings on the report/artifact cleanup: 1. Regression fix: the non-app-surface and deterministic-verifier branches write their pre-baked advisory report (Inconclusive / Pass / Fail) and never run the agent, so they don't produce agent-report.md. After switching write_agent_report_artifact to read only agent-report.md they fell through to the "agent did not write a final report" fallback, dropping the real advisory (and mis-reporting on .github-only PRs like this one). Fix: those branches now write their advisory directly to $agent_report_file — single source of truth for the report body. 2. Recursive artifact excludes: the source Playwright recording lives at artifacts/playwright-video/<uuid>.webm; non-recursive !*.webm / !*.zip didn't match the subdirectory. Use **/*.zip and **/*.webm so the slim actually holds. 3. Drop the now-dangling summary.legacyTrace field (the legacy trace copy is no longer produced), matching the legacyVideo removal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
995601de9c |
ci: make agent exploration finalize promptly (avoid inactivity abort) (#3100)
Real Codex runs (#3060) explored correctly — verifying 3-4 UI cases with DOM evidence — but Codex over-planned (6 steps), executed the high-value ones, then went silent chasing a remaining planned step and tripped expect-cli's ~180s no-output watchdog, aborting the turn before it emitted a final report. The run then fell back to an advisory artifact, so the real findings never reached report.md. Tighten the prompt so Codex finishes and submits before going idle: - cap at 3 cases (was 6) and target 2-3, quality over breadth; - add a CRITICAL instruction stating the runner aborts with no report after ~3 min of no output, so Codex must stop after 2-3 cases and emit the complete report in one final turn rather than leaving planned steps pending or retrying silently. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
fed464509b |
ci: drive agent PR exploration with the Codex ACP backend (#3086)
expect-cli defaults to the Claude Code ACP provider, which is not installed on the self-hosted runner, so the exploration step errored (AcpProviderNotInstalledError) and fell back to a reachability-only smoke trace instead of real UI exploration. Pass `-a codex` to expect-cli so it drives the Codex agent (installed on the runner, authenticated via CODEX_HOME). Configurable via OD_EXPECT_AGENT (set to empty to use expect-cli's default). When the agent is unavailable the existing smoke-trace fallback still applies, so this is safe even before Codex is authenticated. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
114be63a4e |
ci: route agent sandbox installs through the China npm mirror (#3084)
After fixing source acquisition (#3078), the #3060 validation run reached the container and got through most of `pnpm install`, then failed building the better-sqlite3 native module: prebuild-install could not reach github releases and the node-gyp fallback could not fetch node headers from nodejs.org (ECONNRESET). The electron postinstall hits the same blocked hosts, and package tarballs from npmjs were throttled to ~20 KB/s. The runner's network to npmjs / nodejs.org / github releases is throttled or reset by GFW; the China npm mirror (npmmirror.com) is fast and complete (verified from the runner: registry ~2.4 MB/s, node headers ~3.6 MB/s, better-sqlite3 prebuilt present). Point the in-container install at it via registry + disturl (node-gyp headers) + electron / electron-builder / better-sqlite3 binary mirrors + Playwright download host. Package integrity is still verified against the lockfile, so the mirror only changes transport. Once a native module builds, pnpm's side-effects cache in the persistent store keeps it warm for later runs. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
12141648e4 |
ci: fetch agent sandbox PR source on the host over SSH via a local mirror (#3078)
The sandbox checked out PR code with `git fetch https://github.com/...` *inside* the container. The self-hosted runner's bandwidth to github.com is throttled across every transport (HTTPS/SSH/codeload/API, all ~30-90 KB/s) and the HTTPS handshake is frequently RST'd, so a from-scratch fetch of this ~200MB repo is impractical and unreliable per run (run 26491460889 failed here with repeated GnuTLS resets). Move source acquisition to the trusted host and make it incremental: - Keep a persistent bare mirror of the base repo ($HOME/.cache/agent-pr-explore/open-design.git, overridable via OD_SANDBOX_REPO_MIRROR). Each run fetches only the PR's delta via `refs/pull/<n>/head` over SSH -- the one transport GFW doesn't reset -- using a read-only deploy key (OD_SANDBOX_GIT_SSH_KEY). - Take the head from the BASE repo's pull ref so fork PRs work without depending on the head fork, and verify it equals the resolved HEAD_SHA. - Check the PR head into a per-run worktree and mount it read-only into the container; the container copies it into a writable workdir and no longer needs (or has) any github access. The deploy key stays on the trusted host and is never exposed to the untrusted PR code. The mirror must be seeded once on the runner (the error message prints the exact clone command if it is missing). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
2ed93e9c5d |
ci: reuse cached docker image and persist pnpm store for agent sandbox (#3074)
* ci: skip docker pull when agent sandbox image is already cached The agent PR exploration script ran an unconditional `docker pull "$image"` before `docker run`. Under `set -e`, a transient registry timeout (the self-hosted runner's network to docker.io is unreliable) aborts the whole run even when the base image (node:24-bookworm) is already cached locally — which is what happened on run 26490782540. Skip the pull entirely when the image is already present, and only pull when it is missing. This avoids both the failure and the wasted pull timeout on every run, and keeps a run's base image stable. Refreshing the cached image is a separate, explicit operation on the runner. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: persist agent sandbox pnpm store across runs The pnpm store was placed under $RUNNER_TEMP, which the Actions runner wipes per job, so every agent exploration re-downloaded all dependencies from the npm registry — slow, and as fragile as the runner's docker.io access (the same network class that already broke the docker pull). Move the store to a persistent host path ($HOME/.cache/agent-pr-explore/ pnpm-store, overridable via OD_SANDBOX_PNPM_STORE) so a warm, content-addressed store is reused across runs. `rm -rf "$root"` no longer touches it since it lives outside the per-run root. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
7b8bf0d9fb |
ci: map agent trace upload to existing R2 secrets (#3013)
* ci: map agent trace upload to existing R2 secrets * ci: make agent report comments macos-compatible * ci: ensure Playwright browsers for agent traces |
||
|
|
b5bf28060b | Add sandboxed agent PR exploration (#2604) | ||
|
|
bfcafc81fd |
feat(pack): add Windows portable zip target alongside NSIS installer (#2937)
Adds a new `--to zip` (and `--to all`) tools-pack Windows build target that produces a portable `.zip` from the cached `win-unpacked` tree using the bundled 7z. The zip lays files at the archive root so users can extract it anywhere and launch `Open Design.exe` without going through the NSIS installer, addressing the no-install download request. Release plumbing is updated to publish the portable zip and its sha256 beside the existing installer on R2 for beta, preview, and stable channels (default on, gated by `WINDOWS_INCLUDE_ZIP`/`WIN_INCLUDE_ZIP`). The electron-updater `latest.yml` feed continues to point only at the installer; the zip is a manual-download convenience and is intentionally excluded from the in-app updater. Closes #1121 Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code) Co-authored-by: libertecode <libertecode@proton.me> |