Local markdown-it footnote plugin: superscript references jump to an
ordered endnotes section and back to their exact reference. Note ids are
namespaced per transcript message so identical notes stay distinct.
No new dependencies.
* refactor(mantis): reuse authorized desktop captures
* fix(mantis): budget desktop authorization failures
* chore(mantis): bound desktop proof retries
* fix(e2e): drop unused recorder failure fact type export
* fix(ci): route Mantis desktop teardown through the recorder wrapper
Cleanup invoked the internal recorder executable as mantis-sut, which is
deliberately kept out of the docker group and cannot read the
recorder-owned session file; teardown therefore failed and blocked
safe_to_release. The cleanup step already runs as the recorder user, so
call the public wrapper whose exec shim cds into the session root.
* fix(e2e): make recorder failure fact lane-readable; document v2 lifecycle
The Mantis workflow runs the recorder as the desktop user while the lane
reads the authorization-failure fact as mantis-sut; 0600 made that read
fail EACCES and silently disabled the two-attempt retry budget. Write the
fact 0644 — the 0770 attempt directory bounds visibility.
Update the mantis doc's recorder section for the v2 session lifecycle:
required --session handle with healthy-session reuse, capture-only stop,
and teardown owning authorization termination and lease release.
Centralize untrusted audio transcript framing across channel plugins and keep Discord voice transcripts out of typed command provenance.
Co-authored-by: xialonglee <li.xialong@xydigit.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Preserve opaque source strings across tool-result replay while retaining masking for direct .env reads, registered or recognizable secrets, structured sensitive fields, and payment data.
* perf(mantis): parallelize proof builds and warm caches
* fix(mantis): size build image for the copied pnpm store
* chore(ui): refresh startup JS gzip baseline after streamed-markdown perf work
Identical source measures 345034-345058 B across builds while the
committed baseline left only a 9 B margin under the 512 B ratchet
tolerance, so build-artifacts flips on gzip nondeterminism (green on
main run 32559609413, red on PR run 32559442295, red locally).
Regenerated with scripts/check-control-ui-performance.mts
--update-baseline; the 350 KiB hard ceiling still bounds creep.
`models status --agent` selected the agent's primary and fallbacks but
resolved aliases from global defaults only, so a bare per-agent alias was
displayed and probed as a different provider than the runtime selects.
Status, --check, auth diagnostics, provider-use reporting and --probe could
all inspect the wrong route and report false missing or auth state.
The canonical resolvers already merge per-agent model rows when given an
agentId, so this passes the agentId that was already resolved locally and
deletes the synthetic global-default config that carried the agent primary.
Displayed aliases now come from the effective agent-scoped index, which
applies per-agent replacement instead of reading agents.defaults.models.
Unscoped status keeps reporting global defaults.
Refs #127585
Preserve existing unrestricted host files when a write or edit fails partway through by updating regular files in place and restoring the original prefix and length.
Writable-but-unreadable regular files now fail before mutation.
Co-authored-by: yetval <yetvald@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Prevent ClawHub skill updates from silently replacing locally modified files. Verify recorded install digests at the rollback boundary, restore on mismatch, and require explicit --force for unverifiable legacy installs.
Co-authored-by: yetval <yetvald@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
`openclaw onboard --json` wrote a JSON object to stdout on success, and the
top-level CLI handler emitted {"ok":false,"error":...} for thrown errors, but
every option-validation rejection printed to stderr and exited 1 with an empty
stdout. Automation parsing stdout got a valid object for some bad flags and a
parse error for others, with no way to tell a rejected flag from a crash.
Measured before this change, all with --json and all with empty stdout:
--mode, --flow, --daemon-runtime, --gateway-bind, --gateway-auth,
--gateway-token-ref-env, a missing --gateway-password, and --auth-choice.
Routes every onboarding option rejection through one shared primitive that
emits { ok: false, phase: "options", message } when --json is set, then reports
and exits exactly as before. 'options' joins the existing phase vocabulary
('daemon-install', 'gateway-health'). Every human message, stderr stream, and
exit code is unchanged; this only adds the stdout object that was missing.
## What Problem This Solves
Auto-triggered Mantis proof runs (label/`clawsweeper_label` and other non-comment request sources) end with no PR comment at all. The durable evidence publisher runs with `--create-missing false` and only edits an existing marker comment, but the inline status comment carrying that marker was only created when `request_source == 'issue_comment'`. Label-triggered runs therefore published nothing and logged the misleading "Skipped stale Mantis QA evidence comment because its status is no longer active" — observed on PR #127735. This is the silent-failure class: a Mantis run completes and the PR shows no visible outcome.
## Why This Change Was Made
- `.github/workflows/mantis-telegram-desktop-proof.yml`: the status ack comment (👀 + active-job link + run-scoped marker) is now created for every request source that resolves to a PR (`pr_number != ''`), not only `issue_comment`. The 👀 *reaction* stays `issue_comment`-only (it lives in `mantis-resolve-request.yml`, untouched — there is no triggering comment to react to on label runs).
- The start-failure fallback comment and the existing-artifact republish path now use the same run-scoped marker `<!-- mantis-telegram-desktop-proof:${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT} -->` as the status comment and the main publisher, so every publisher edits the single run-owned comment (ack → progress → final proof; no comment spam). The republish path gets an explicit `--create-missing false` to match. The design invariant that makes `false` safe: the fallback status-comment step is not `continue-on-error`, so a run in which no marker comment could be created fails `resolve_request` and never reaches publish.
- `scripts/mantis/publish-pr-evidence.mjs`: the two skip cases now log honestly — "no existing comment found" vs "could not update existing comment" — instead of one misleading stale-status message.
## User Impact
Operators triggering Mantis via labels (ClawSweeper flows) now get the same single evolving PR comment as comment-triggered runs: an immediate 👀 ack with the running job link, edited in place into the final proof evidence. No more runs that finish invisibly.
## Evidence
- Focused suite: `node scripts/run-vitest.mjs test/scripts/mantis-telegram-desktop-proof-workflow.test.ts` — 28/28, including new assertions that the status/failure comment gates use `pr_number != ''` (and not `request_source`) and that both publishers pass the run-scoped marker with `--create-missing false`.
- `node scripts/check-changed.mjs -- <touched files>` green; `git diff --check` clean.
- Marker alignment verified across all five sites in the workflow (status comment, prior-attempt cleanup regex, fallback comment, failure report, both publisher invocations): all use `mantis-telegram-desktop-proof:${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}`.
- Live-run proof of the label-triggered path requires a merged workflow (GitHub runs the workflow from the default branch for these triggers), so the first post-merge label-triggered Mantis run is the live verification; stated here as the known evidence gap.
Production LOC delta: −4 (workflow/tooling); tests +13.
Makes the Mantis Telegram Desktop proof agent programmable at its trust boundaries: declarative Bot API fault rules (drop/status per method), per-request scripted mock-provider responses, observe-until predicates (post-cursor events/text, cumulative provider count), and bounded proxy-side recording of outbound Bot API requests as trusted lane facts. Adds a reusable recipe library under .github/codex/prompts/mantis-recipes/ and raises the proof agent's reasoning effort to high.
Security: the SUT container shadows proxy-control with an inaccessible tmpfs so candidate PR code sharing the mantis-sut uid cannot read or rewrite the proxy's recorded evidence; unmount is blocked by cap-drop/no-new-privileges. Proof doctrine now treats proxy-recorded Bot API facts as trusted comparison evidence and provider request logs as diagnostics.
Follow-up named in PR: move the mock OpenAI server out of the SUT container so provider request facts also become candidate-tamper-proof.
Onboarding answered an unknown --import-from with "Migration provider \"x\" is
not installed or bundled. Install it before starting the transactional import."
— no list of valid ids, no next command, and advice to go install something
that is usually just a typo.
Its siblings already do this correctly: resolveMigrationProvider appends
"Available providers: ..." and the migrate selection helper appends
"Available skills: ...". The missing --import-from case in onboarding itself
already points at `openclaw migrate list`. Only the unknown-id case dead-ended.
Collapses the two duplicated throws into one guard used by every path, which
also closes a gap: the no-back-navigation branch returned the picker result
without validating it at all.
Allow exact upstream prompt provenance to use the existing 512 KiB settled-projection budget while preserving ordinary text bounds and aggregate accounting.
Co-authored-by: Marvinthebored <marvin.assistant@lindsey.jp>
Non-interactive onboarding accepted `--gateway-bind custom` with no
`gateway.customBindHost`, wrote `bind: "custom"` to the config, and reported
success. The Gateway refuses that config outright:
Gateway failed to start: gateway.bind=custom requires gateway.customBindHost.
`doctor` and `config validate` both call the written config healthy, so the
first sign of trouble is a Gateway that will not start.
Every other onboarding flag already rejects a mode whose companion value is
missing (--gateway-auth password, --auth-choice openai-api-key, --auth-choice
custom-api-key). This restores the same contract for bind, reusing the same
validateDottedDecimalIPv4Input the interactive wizard and `openclaw configure`
apply to this field. Pre-seeding gateway.customBindHost keeps working.
Allow healthy current-version state databases to open without taking SQLite writer locks while preserving schema repair, legacy cron migration, and external ownership fencing.
Co-authored-by: Jason O'Neal <jason.allen.oneal@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Repair managed OpenClaw host links under the startup migration lease before plugin state migrations, preventing retained Codex upgrades from repeating the same readiness failure.
Co-authored-by: Jason O'Neal <jason.allen.oneal@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Preserve parser-owned bounded diagnostics in managed Claude live turns and share the canonical output-limit formatter.
Co-authored-by: Marvinthebored <peter@lindsey.jp>
Use canonical session-key normalization during subagent reconciliation so case-distinct Matrix and Signal peers cannot be reported as completed. Preserve structural casing reconciliation and remove the O(N) fallback scan.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Alix-007 <li.long15@xydigit.com>
* fix(release): preserve validation plan across reruns
* test(release): align rerun plan assertions
* refactor(release): use canonical plan cache action
* style(test): format release plan cache assertion