BREAKING CHANGE: GET /sse and POST /messages now answer 404. Every client
connects over Streamable HTTP at /mcp. The sse remote is removed from
server.json.
- Add unauthenticated GET /health ({status, version}); no session, no
rate-limit touch.
- Add any-origin CORS across the whole HTTP surface, incl. OPTIONS
preflight and error responses; mcp-session-id exposed to browsers.
- Unify every version string at 2.0.0; both McpServer identities now read
the version from package.json at build time instead of hardcoding it.
- Raise @modelcontextprotocol/sdk floor to ^1.30.0 and sync the local
install with the lockfile.
- Tests: retire the SSE-shaped auth-gating suite (its unique
process-liveness test is ported to streamable-http-gating), move the
shared harness and rate-limit suite to StreamableHTTPClientTransport,
and add http-surface.test.ts for CORS//health//sse-404 coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The README described both hosted instances as requiring a bearer token, and
/mcp as self-hosted-only pending a future deploy. Both statements are stale:
the hosted fleet now admits anonymous callers with per-IP rate limiting, and
/mcp is live on mainnet and testnet.
- Endpoint table and the primary connection example now use /mcp.
- Connection examples drop the Authorization header for the hosted URLs.
- /sse stays documented as the legacy transport, also tokenless for hosted.
- MCP_AUTH_TOKENS remains documented for self-hosted deployments; that
feature is unchanged, it is simply unset on the hosted fleet.
Custody language is deliberately untouched. Keyless (no server-side signing
keys) and authless (no bearer token) are separate properties and the text
keeps them separate.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Name cased to the org login (io.github.Apex-Fusion/vector - the registry
grant is case-sensitive) and description shortened to the registry's
100-char limit. Published as v0.1.0 on 2026-08-04.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Registry record for io.github.apex-fusion/vector: both hosted remotes
(streamable-http /mcp + legacy sse /sse) behind a network variable
(mainnet default, testnet choice), repository + docs links, 24-tool
keyless description.
Publish flow (after the public-access flip): mcp-publisher login github,
then mcp-publisher publish from the repo root. The registry requires the
remote URLs to be publicly accessible at publish time.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The root postinstall runs scripts/link-libsodium.mjs, but the Dockerfile
never copied scripts/ into either stage - npm ci ran with only the
package.json files present, so the script was missing and npm exited 1.
This was latent until the postinstall stopped ending in `|| true`. The old
shell one-liner swallowed its own failure, so a missing script looked like a
successful install; making it fail-loud surfaced the gap on the first build
after that change.
CI did not catch it because actions/checkout gives the workflow a full
working tree, so scripts/ is always present there. Only the image build
exercises the COPY set, and only the deploy workflow builds the image.
Verified: with scripts/ present the postinstall exits 0 ("already matches the
source, nothing to do"); without it, node throws MODULE_NOT_FOUND, which is
the exact failure in the deploy logs. libsodium-sumo is a production
dependency (dev=false), so the --omit=dev runtime stage installs it and the
script finds its source there too.
apexToLovelace's own unit tests prove the helper is exact, but nothing
proved any of its 5 call sites actually invoke it - reverting
buildSendApex's call site to Math.floor(amountApex * 1_000_000) left the
full build suite at 26/26 green, because every existing amountApex/
apexAmount/stakeApex fixture is a whole integer.
Adds one call-site pin each for buildSendApex, buildSendTokens' apexAmount
path (both build.test.ts), and buildEndorse (gov-build.test.ts, the
simpler of the two stake fixtures), each using a fractional amount
confirmed via node -e to actually lose a lovelace under the old
Math.floor path, decoding the built tx and asserting the recipient/script
output's coin is the exact expected lovelace. buildSendTokens needed its
own defect value (1.005 AP3X undershoots this output's min-UTxO once it
carries a native asset, an unrelated ledger rule) - reused buildEndorse's
verified 8.03 AP3X instead. buildCritique and buildProposalLock's call
sites are structurally identical to buildEndorse's and already covered by
apexToLovelace's own exhaustive unit tests; one representative call-site
pin plus the two build.ts sites is the load-bearing set, not all five.
Proved each pin load-bearing by reverting its call site to Math.floor one
at a time: each mutation failed exactly the matching test (naming the lost
lovelace: 1004999 vs 1005000, 8029999 vs 8030000, 8029999 vs 8030000) and
no other test, then was reverted byte-identical (confirmed via git diff).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Math.floor(amountApex * 1_000_000) silently drops a lovelace whenever the
IEEE-754 product lands just under the intended integer (empirically
confirmed on this Node build: 1.005, 2.05, and 8.11 AP3X all lose exactly
1 lovelace this way). Adds apexToLovelace(amountApex): bigint in
packages/shared/src/tx.ts - a string-exact conversion via
Number.prototype.toString() (not toFixed(), which reveals the same binary
noise this function exists to avoid) that rejects non-finite, negative,
and >6-decimal-place amounts instead of silently truncating.
Replaces every AP3X-to-lovelace float multiply on a money path:
buildSendApex and buildSendTokens' apexAmount path in build.ts, and the
three stake conversions (critique/endorse/proposal-lock) in gov-build.ts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Regenerating the lockfile picked up a newer @cardano-sdk/crypto release that
dropped its errant npm-CLI runtime dependency, clearing the critical tar
advisory and 42 other advisories that traced back to it (44 -> 1, the
remainder a dev-only esbuild low). A defensive overrides pin on tar and
brace-expansion is retained per plan but is empirically not what resolved
this - documented honestly in README.md and the task report.
engines.node raised to >=22 everywhere (root + all three packages), matching
the CI workflow's node-version and the Dockerfile's node:22-alpine base
(both already agreed; no discrepancy to reconcile).
postinstall no longer symlinks libsodium with `ln -sf ... || true`, which
silently no-opped on Windows and left a runtime ERR_MODULE_NOT_FOUND.
scripts/link-libsodium.mjs copies the file cross-platform instead, logs one
line, and exits non-zero on genuine failure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
David dispatched the mainnet cutover this morning: deploy-mainnet.yml ran successfully at
2026-07-31T11:00:06Z-11:01:20Z, deploying commit 04e410b (the PR #11 merge, itself the tip of
main after PRs #9/#10/#11 all merged 10:39:29Z-10:39:59Z the same morning). Testnet had already
auto-deployed the identical commit at 10:40:01Z-10:41:50Z. The non-custodial migration is now
complete on both hosted instances, not just in the code - the exact moment the prior docs pass's
deliberately-hedged notice was written for.
Verified directly before writing anything, not taken on faith from the dispatch: git/gh confirm
the merge timestamps and that both deploy workflow runs targeted 04e410b exactly; the custody
grep and safety.ts's absence were reconfirmed on this exact commit; both hosted endpoints
(https://mcp.vector.mainnet.apexfusion.org/sse and the testnet equivalent) were probed directly
with a bare curl request and both returned HTTP 401 right now - meaningful evidence on its own,
since the pre-PR#8 image had no auth at all, so a 401 today proves post-migration code is what's
actually running, not just what the deploy logs claim.
- README.md: the deployment-status callout rewritten with the dated, both-networks-current
facts, replacing the custody caveat with an access caveat - both instances require a bearer
token from the operators now (an access control, not a custody signal; neither instance would
accept a mnemonic from an authenticated caller either, since the tool schemas that would carry
one no longer exist). The connect command now shows the bearer-token header syntax, verified
against Claude Code's own current `claude mcp add` documentation before writing it (`--transport
sse` accepts `--header "Authorization: Bearer <token>"`, same as `--transport http`). The
security notice rescoped from "this codebase, not every deployment" to a dated claim: complete
in this codebase and on both hosted instances as of the 2026-07-31 cutover deploy specifically,
explicitly not a "forever" claim - a future deploy is a separate operational action.
- packages/signer/README.md: the two passages scoped to "this codebase" in the prior docs pass
now scoped to "any instance running the 2026-07-31 deploy or later," mainnet included, with the
same access-control caveat about the bearer token.
- docs/architecture/non-custodial-split.md: PR-sequence row 9 (mainnet cutover) gets the same
landed-marker style as rows 6-8, dated; section 11's rollout paragraph gets its one now-historical
clause corrected to past tense with a dated annotation appended, following the established
forward-only annotation convention rather than rewriting the paragraph.
Open public access (no token, per-IP rate limiting) remains a planned follow-up, not yet an open
PR or branch - checked directly (gh pr list, git branch -a) rather than assumed, and stated in
the docs as planned, not implied as already in progress.
Critical truthfulness defect found by review: README.md's security notice claimed, in
unconditional present tense, that "the non-custodial migration is complete... No tool on this
hosted server accepts a mnemonic... this server never holds one" - sitting directly below the
README's own mainnet hosted-URL connect example. Per docs/architecture/non-custodial-split.md's
own rollout section, mainnet stays on its last custodial image until a separate, deliberate
cutover deploy ships - and nothing on this branch (or the two PRs it stacks on) has even merged
yet. A reader who followed the mainnet connect line and trusted the notice as written would be
told something the repo's own architecture doc directly contradicts. The rewrite had also
dropped the prior notice's hedge ("prefer self-hosting until the migration completes") and
replaced it with a strictly stronger, zero-caveat claim - the exact failure mode this migration
exists to prevent, reproduced at the documentation layer.
Fixed by rescoping every claim to the codebase and adding an explicit deployment-status caveat:
- README.md: added a deployment-status note directly beneath the hosted-servers URL table
(before the connect command), stating plainly that testnet auto-deploys from main but mainnet
stays on its last custodial image until a separate cutover deploy, and to treat the mainnet
instance's signing tools as custodial until then. The security notice itself now says "as of
this release, the non-custodial migration is complete in this codebase" and "no server running
this release ever holds one," with an explicit closing line that this is a claim about the
code, not about every deployment of it. The "Local signer" section got the same rescoping
(it made the identical unscoped claim). The Self-Improvement Module features bullet was also
adjusted: it had "all keyless" sitting directly next to a pre-existing "(live on Vector
mainnet)" parenthetical, risking the same conflation between the on-chain module's blockchain
deployment and this codebase's MCP-server code state - now stated as two separate facts.
- packages/signer/README.md: the "keyless across the board" pairing-status bullet and the
"keeps your mnemonic off the hosted server" closing line both rescoped to "in this codebase" /
"off any server running this release," each with an explicit note that the mainnet hosted
instance is not yet running this release.
- .superpowers/sdd/pr9-body.md (gitignored, not part of this commit): opening paragraph and the
stacked-PR timing note both updated with the same scoping - merging this PR does not deploy
it anywhere, and the mainnet cutover is a separate future step.
Grepped the tree afterward for other unscoped "hosted server ... keyless"-class claims: found
none beyond what's fixed above. Pre-existing, unrelated mentions of "hosted server" in
docs/architecture/non-custodial-split.md (the historical problem statement) and README.md (a
self-hosting logistics note) were checked and left alone - correctly scoped by their own
surrounding context already.
README, packages/signer/README.md, docs/VECTOR.md, and docs/architecture/non-custodial-split.md
updated for the family-3 (self-improvement) keyless migration - the last of the three families
this migration ever touched:
- README: security notice rewritten as the migration-complete statement (zero tools accept key
material, every signing operation happens on the local signer, enforced by the now-empty,
size-zero-pinned custody boundary allowlist); tool count 23 to 24; Self-Improvement Module
table replaced with browse/analyze plus the four build_* rows, the two-step proposal pair
marked as two transactions, agent-orchestrated; features and safety copy updated to
signer-owned spend limits everywhere; Configuration table's three dead spend-limit/audit-log
rows removed (nothing reads those env vars any more); Safety Layer box removed from the
architecture diagram entirely (box-drawing alignment verified programmatically, not by eye);
Testing section names tier-1 vs gated E2E vs LEGACY_FULL for all three keyless families
including their stake costs.
- packages/signer/README.md: pairing-status bullet and closing line rewritten - all four
families are keyless now, this signer is the single signing point, nothing is left open.
- docs/VECTOR.md: four new tool sections for the self-improvement build_* family (this doc never
documented self-improvement under any name before, so these are additions, not renames - same
situation as the registry family in the prior PR's docs task); Safety Controls section's
custodial-family list corrected to reflect that none remain.
- docs/architecture/non-custodial-split.md: family-3 landed marker on the PR-sequence table,
plus the four content-alignment fixes pre-assigned to this PR by
rehaul-handoff-2026-07-30-content-alignment.md (the "governance" to "self-improvement" renames
in the two-step-flow heading, the "last family" sentence, the family table label, and the
rollout row), plus the "Float -> bigint amount math" wording fix.
- .env.example: verified Task 5's edit already states the migration-complete scope correctly
with a pointer to packages/signer/README.md; no further change needed.
Content rules followed for all new copy in this commit: no "governance" (GOV_* env-var names
exempted, pre-existing deployed config keys), no em dashes, no "funds", research-themed
examples where new examples were needed. Caught and fixed one self-introduced violation before
committing: docs/VECTOR.md's four new tool sections initially copied the file's own pre-existing
em-dash-based parameter-bullet convention; replaced with colons in exactly those new lines,
verified zero em dashes remain in the added range.
Task 6 review findings, all verified before fixing:
- self-improvement-keyless.test.ts: the proposal_spend negative test's inner
timing bound (30_000ms) equaled the test's own timeout, making the
assertion's diagnostic message unreachable - the test runner's generic
timeout would always win first. Tightened to 15_000ms.
- self-improvement-e2e.test.ts: added a deadline-sanity pin next to the
"valid until" regex match (the signing window must be meaningfully in the
future, not just present and Z-suffixed) and strengthened the critique/
endorse browse assertions to require a non-zero section count, so a
zero-result response can no longer pass.
- run.test.ts: reworded a banner comment ("funds" -> "AP3X"), matching this
branch's content rule against "funds"/"pay"/"earn" vocabulary.
- self-improvement.ts: harmonized all four build_self_improvement_* tools'
agentDid parameter description to make the trap unmistakable - this field
is the trailing 64-hex asset-name segment of an agent's DID, NOT the full
did:vector:agent:... string (confirmed live in Task 6: passing the full
string throws a Lucid serialization error). Description-only change;
regenerated the snapshot and confirmed the delta touches exactly these
four entries, nothing else.
Also corrected two printed numbers in task-6-report.md's narrative (a
gitignored file, not part of this commit): the itemized fee subtotal was
stated as 49.444332, recomputes to 49.444305; the inferred register-fee
residue was stated as 0.321503, recomputes to 0.321530. Verified by hand
before editing. The report's table and top-line balance delta were already
correct.
Adds the three remaining integration layers for spec PR 8 (self-improvement
family keyless): a tier-1 suite exercising all four build_* tools against the
live testnet module with zero secrets (build-only, nothing signed or
submitted), a gated full lifecycle E2E that actually submits, and a fresh
self-improvement section in the legacy run.test.ts suite.
THE MONEY ASSERTION: the gated E2E's proposal-spend step confirmed on-chain
(tx 312ffdef8beebfb8ba94868b0bf289b5fa7b4f8f9f743eefbf6aaef7360d3246) - the
deployed module validator accepted a keyless-built, validated submit (script
spend + CIP-33 reference scripts + dual mint) for the first time ever. First
attempt succeeded; the documented rebuild-once-on-window-expiry recovery was
never triggered. Full lifecycle (register -> proposal lock -> proposal spend
-> critique -> endorse -> deregister), 9/9 tests, wallet reconciled to the
lovelace: 982.280960 -> 932.515125 AP3X (49.765835 one-way; the 10 AP3X
registration deposit round-tripped; 5 of 6 fees recovered exactly from
transaction history, the 6th closes the arithmetic within 0.001 AP3X).
Tier-1 (self-improvement-keyless.test.ts): 8/8 live, no secrets - browse and
analyze_metrics render real chain data, critique/endorse/proposal_lock build
unsigned CBOR at the exact expected AP3X amounts with datum round-trips,
proposal_spend's negative path (unknown lockTxHash) fails fast pointing at
vector_await_transaction, bogus changeAddress fails fast locally.
Legacy suite (run.test.ts): run.test.ts has never had a self-improvement
section in its history (checked full git log before writing anything, per
the dispatch's own fallback instruction) - added one fresh, following the
file's established conventions, with its own dedicated agent lifecycle
(Agent Network Tools' agent is already deregistered by the time this section
runs, so reusing its DID would reference a burned NFT). Default path
(register -> browse -> analyze -> critique -> endorse -> deregister, ~19-20
AP3X one-way) ran live, 23/23 pass. The full two-step proposal submission is
written behind LEGACY_FULL=1 (typechecked, not executed this run - the E2E
above already proves that exact path live; re-running it here would spend
~29 AP3X a second time for no new information), correctly skipped (1 skip).
24 tests total across all 6 describe blocks, 0 failures.
Found and fixed two issues before shipping, neither a src/ change: the
self-improvement family's agentDid wire param is the hex asset-name segment
only (not the full did:vector:agent:... string the registry family's
agent_id means) - pre-existing since before spec PR 8, confirmed against
commit 8162673; and a Markdown-bold-asterisk regex gap in my own
analyze_metrics assertion (same class of gotcha registry-keyless.test.ts
already documents). Content-rule self-review caught and fixed a literal
"governance" string in a code comment and seven new em dashes before commit.
Gates: typecheck 0 (3 packages), unit 392/392, smoke 27/27 (24 tools, 0
drift), auth 12/12, boundary 5/5 - all identical to the entering baseline
(test files only, no src/ changes). Full report with all tx hashes, explorer
links, and the wallet reconciliation at .superpowers/sdd/task-6-report.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Opus review round on the self-improvement-keyless capstone commit, 2
Important + 3 Minor, all residue (no custody defects):
- env.d.ts: delete the three dead VECTOR_SPEND_LIMIT_PER_TX/_DAILY/
VECTOR_AUDIT_LOG_PATH ambient declarations - safety.ts was their only
reader and it's deleted. The capstone custody PR should not leave
live-looking spend-limit declarations behind.
- boundary.test.ts: new mechanical pin asserting self-improvement.ts never
references localUPLCEval - the test-only eval escape hatch on
buildProposalSpend may never be passed from a tool handler. Proved
load-bearing: applied the mutation (a 4th { localUPLCEval: true } arg),
watched it fail by name, reverted, confirmed a byte-identical diff against
the mutation (git diff --stat showed only the unrelated storageUri
simplification below), re-ran green.
- rate-limit-gating.test.ts: reworded the now-false "self-improvement.ts is
the remaining compiler-blind case" comment - both tool-registration files
are typed now; the timing-based hermeticity rationale stands on its own.
- types.ts + gov-build.ts + self-improvement.ts: added optional storageUri
to VectorBuildStakeResult, set by buildCritique only; the critique tool
handler now renders r.storageUri directly instead of reconstructing
ipfs://${ipfsCid}, removing the duplicated URI-format knowledge. Result
types are not wire schemas - zero snapshot drift confirmed.
- registry-build.ts:303: "governance family" -> "self-improvement family".
Gate: typecheck 0 (3 packages) - unit 392/392 (391 + the new eval-escape-hatch
pin) - smoke 27/27, 24 tools, zero snapshot drift - auth 12/12 - boundary
5/5 (4 custody + the new pin). Custody grep re-confirmed empty.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rewrites self-improvement.ts (1024 -> 556 lines): typed, no @ts-nocheck,
imports build logic from gov-build.ts (Tasks 3-4). Splits the old single-shot
vector_self_improvement_submit_proposal into two keyless build tools
(proposal_lock, proposal_spend) since the deployed module validator requires
a confirmed lock before the spend+mint step can reference it; renames
critique/endorse to vector_build_self_improvement_*. browse and
analyze_metrics stay behavior-identical read-only tools (render strings
verified byte-for-byte against the custodial originals).
Deletes safety.ts (its last importer). Removes SpendLimits/SpendStatus/
AuditEntry from shared types (grep-verified zero references first). Drops
the now-dead spend-limit/audit-log env vars. Empties the custody boundary
allowlist with a size-0 pin - every builder family is now keyless.
Tool count 23 -> 24. Snapshot delta is surgical: 3 removed, 4 added, 19 of
the other 20 byte-identical (analyze_metrics' description had to drop the
literal word "mnemonic" to satisfy the custody grep - the one disclosed
exception).
Headline: grep -rni "mnemonic|seed|bip39|fromSeed" packages/builder/src
returns zero lines. This was the last custodial family.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
README.md Testing section claimed registry-keyless.test.ts (tier-1) builds "all five
agent-registry build_* tools" - false. Verified directly against the test source: it
successfully builds and CBOR-checks only register and message; update appears solely on
its non-owner-rejection path (no CBOR extracted); transfer and deregister don't appear in
the file at all - both are covered by the gated E2E suite and the legacy suite instead.
Reworded to state exactly what tier-1 covers and point the full lifecycle claim at the E2E
suite, where it's actually true.
docs/architecture/non-custodial-split.md:137 ("Builder — 24 tools, zero keys") is the
doc's original full-migration target table, easy to misread as current-state next to
README's "23" today. Added "at full migration" so it can't be read as stale.
Checked pr7-body.md for the same all-five-tier-1 claim per the reviewer's note - it does
not repeat it (the Tier-1 bullet only states the 6/6 pass count, no per-tool breakdown),
so no change needed there.
README, .env.example, packages/signer/README.md, and docs/architecture/non-custodial-split.md
updated for the family-2 keyless migration (agent-registry build_* tools, no mnemonic
parameters): security notice narrowed to name the Self-Improvement Module as the only
remaining custodial family; agent-registry tool table renamed to the five build_* rows;
features bullets, self-hosting copy, Configuration/spend-limit scope notes, and the
architecture diagram's Safety Layer footnote all updated to "self-improvement only"; signer
README's pairing-status bullet and closing line extended to cover wallet/tx/contract AND
agent-registry; architecture doc's PR-sequence table marks family 2 as landed.
docs/VECTOR.md gains five new tool sections (register/update/transfer/deregister/message) -
this doc never documented the registry family under any name before, so these are additions
rather than renames; keyless framing (changeAddress, no mnemonic, unsigned CBOR + 4-call flow)
matches the style of the existing wallet/tx/contract sections. Safety Controls section's
custodial-family list also corrected to self-improvement only.
Closes an Opus review round on Task 5's registry integration work.
Adds packages/builder/test/sign-helper.test.ts (top-level test/, picked up
by the unit glob) as the first hermetic gate that would fail if
signWithMnemonic's witness-preservation fix regressed. Before this, the
fix had zero coverage under any required gate: sign-helper.ts lives under
test/integration/, outside the unit glob; run.test.ts's
assertSuccessOrKnownError swallows the resulting "Failed to submit
transaction" as an indistinguishable known error; and the E2E's
hash-equality check can't catch it either, since dropping witnesses never
changes the transaction body hash - only the ledger tells the two apart,
and this test never talks to it. The new test builds a synthetic,
fully-offline script-bearing transaction directly via CML (one input/
output, a witness set carrying one PlutusV3 script, one map-form redeemer,
one datum), signs it with a public, well-known, never-funded BIP39 test
vector (computed from the installed bip39 package rather than transcribed
from memory - the first guess had the wrong checksum word and failed
loudly), and asserts every witness category plus the vkey witness survive
signing with the body hash unchanged. Proved it's load-bearing: reverted
the fix to the buggy from-scratch witness set, watched the test fail
naming the exact lost witness, reverted back (byte-identical, zero diff),
re-ran green.
registry-keyless.test.ts: the non-owner-update assertion drops its second
regex alternative (`Failed to build agent update`), which was a superset
match able to hide a real, unrelated build failure behind the same
"ownership was correctly refused" label. Now asserts the specific
`Ownership check failed` message only.
run.test.ts: buildSignSubmit now hard-asserts `Transaction Submitted` on
the submit leg when walletHasAda is true, instead of routing through the
tolerant assertSuccessOrKnownError - on a funded wallet a submit failure
is a real regression, not indistinguishable from an unfunded wallet, and
should fail the test rather than pass silently as a "known error". The
graceful path is kept for the unfunded case. Also documents, in a comment,
the known/accepted risk that a register-succeeds/deregister-fails run in
this file leaves the 10 AP3X test deposit locked on-chain (same shape as
the pre-keyless suite).
Hermetic only this round, per instruction: no live network calls, no
transaction submissions, no real secrets. Full account in
.superpowers/sdd/task-5-report.md's Addendum section.
Gates: typecheck 0, unit 351/351 (350 + 1 new), smoke 26/26, auth 12/12,
boundary 4/4 (confirms the new test's mnemonic constant sits outside the
custody scan by design, same as the rest of test/, and is labeled
TEST_ONLY_PUBLIC_MNEMONIC regardless per public-repo hygiene).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the three remaining integration layers for spec PR 7 (agent-registry
keyless): a tier-1 suite that exercises the five build_* tools against the
live testnet registry with zero secrets (build-only, nothing signed or
submitted), a gated full lifecycle E2E (register -> update -> deregister)
that actually submits, and a shared signWithMnemonic() helper extracted
from keyless-e2e.test.ts so registry-e2e.test.ts and run.test.ts's
registry section don't each re-inline the same CML signing sequence.
run.test.ts's "Agent Network Tools" section is rewritten: each of the five
old custodial calls is now build_* -> signWithMnemonic -> submit, via a
new buildSignSubmit() helper that reuses the file's existing
assertSuccessOrKnownError convention at both the build and submit legs.
Found and fixed one load-bearing bug while proving this live, not just in
review: the inline signing block moved verbatim per the brief's Step 1
built the signed tx's witness set from scratch, which is lossless only for
a script-less payment (the one case it had been proven against). The
registry family's build_* tools attach a minting policy and/or spending
validator, so their unsigned CBOR already carries scripts/redeemers that
the from-scratch witness set silently dropped - the ledger rejected the
first live E2E attempt with MissingScriptWitnessesUTXOW /
PPViewHashesDontMatch. Verified zero funds were lost (balance identical
before/after the rejected submission - a mempool-level rejection never
reaches a block) before fixing it by merging into the tx's existing
witness set via CML's add_all_witnesses instead of discarding it. Re-run
clean: all 3 E2E transactions confirmed on-chain, plus all 5 of the legacy
suite's registry operations submitted successfully too (independent
confirmation across both the mint and spend redeemer paths). Also widened
one tier-1 regex that didn't tolerate the live tool's Markdown-bold label
formatting.
Live evidence (testnet, funded wallet, 8 real transactions total across
the gated E2E and the legacy suite): balance 986.689510 -> 982.280960
AP3X, fully reconciled against transaction fees plus one message delivery
to an owner-derived enterprise address. All tx hashes, explorer links, and
the full reconciliation are in task-5-report.md. mnemonic.txt and the two
temporary evidence-capture scripts were removed and confirmed absent
before this commit.
Gates: typecheck 0, unit 350/350, smoke 26/26, auth 12/12, boundary 4/4 -
all re-verified after cleanup. E2E skip-path (VECTOR_E2E_SUBMIT unset)
exits 0 with tests 0 for both keyless-e2e.test.ts and registry-e2e.test.ts,
confirmed with mnemonic.txt genuinely absent.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>