Skip to content

feat(ts): enforce the architecture rules, one deadline per command, wait by default - #1046

Open
gummy789j wants to merge 113 commits into
developfrom
feat/improve-engineering-quality
Open

gummy789j wants to merge 113 commits into
developfrom
feat/improve-engineering-quality

Conversation

@gummy789j

@gummy789j gummy789j commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Brings ts/ in line with its architecture rules, makes the rules enforceable, and changes how long a command may
run and when it waits for confirmation. Java is unchanged.

Architecture rules and their enforcement

  • ts/docs/dev/rules/ (architecture and comments) is the single source of truth for ts/.
  • dependency-cruiser enforces the layer, SDK, integration and cycle rules, with a baseline of known violations
    that fails on new or stale entries; every rule has a firing test.
  • ESLint bulk suppressions plus local rules for closed port types, environment reads and server imports.

Correctness and safety fixes found by the rules audit

  • Dry-run and signing boundaries are checked on every path that moves funds; signing has one entry that refuses a
    payload whose chain identity does not match the selected network (EVM chainId, EIP-712 domain, TRON reference
    block).
  • Adapters normalize node and SDK facts once; malformed node data fails instead of becoming zero or empty values.
  • Data stores validate on load and write and publish each change under one lock.
  • An ambiguous --token symbol is refused on every family instead of picking the first entry.
  • A blocked write names the lock file, its holder and how to clear it; a lock is taken over only when its holder
    process is dead.
  • External JSON services share one HTTP helper that refuses redirects, bounds the response size and reports rate
    limits as provider_rate_limited with the server's Retry-After.

One deadline per command, waiting by default

  • --timeout is one deadline for the whole command (default 90000 ms, 0 = no limit). Human prompts pause it;
    Ledger confirmation counts. A request whose time has run out is never sent.
  • Commands that submit a transaction wait for confirmation by default; --no-wait returns after submit.
    --wait-timeout is removed.
  • An error whose request may have acted carries error.details.outcome: "unknown"; a TRON duplicate broadcast of
    the same signed transaction reports alreadyKnown: true and is confirmed like any submit.
  • Waiting for confirmation stops on an error that retrying cannot fix and returns the submitted result with a
    warning; flows that need the confirmation (x402 Permit2, SunSwap and SunPump approvals) fail instead.

Docs

Help, ts/docs/machine-interface.md and the command pages describe the current behavior.

Breaking changes

Several error codes, exit codes and result fields change (for example invalid option combinations report
invalid_option, malformed node data fails, --wait-timeout is gone, waiting is the default). They are listed in
the release notes for this version.

Test plan

  • npm run typecheck, depcruise, format:check, lint, test:unit (6055 tests), build
  • Golden suite (506 passed, 2 live cases skipped)
  • Live writes on Nile and Sepolia: default waiting, --no-wait, short timeouts, imported broadcast and its
    duplicate (alreadyKnown), ambiguous --token refused, GasFree,
    multisig and TronLink, Ledger on Speculos, x402 first Permit2 approval
  • SunSwap live writes (router returned 403 during testing)

gummy789j and others added 30 commits October 27, 2026 19:38
Comments under ts/ now say what the code does today, in as few words as a
reviewer needs. Removed history, decision records, incident notes, and
comments that restate the code; rewrote comments that disagreed with the
code. Comment-only: a TypeScript printer diff with comments stripped shows
no code change in any of the 592 files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ts/docs/dev/rules/ holds the target architecture and comment rules for ts/.
CLAUDE.md imports them instead of restating an architecture table, and the
new AGENTS.md points other agents to the same files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… audit

Tests are exempt from the dependency rules, a family's own codec may be used
directly by family-specific code, protocol integrations keep their own
directories and can be family-specific, identifier-keyed maps of closed types
are closed, storage ports may take the user's path, and a repository commit
point must survive the process being killed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…place tests with their subject

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native backup and address generate may write their secret to a 0600 file that
never overwrites or follows an existing path; stdout only on an explicit option.
Also ignore the local .team/ working directory.
Credentials that config stores for external services are read only from stdin,
stored in a 0600 file, and masked in every output.
…dd an architecture diagram

Family code is what lives in tron/ or evm/; protocols keep their own directory in
every layer and commands are exempt from placement. The inbound fallback covers
both unknown and either codes. The diagram shows the ts/ layers at 657e04b.
Family directories are a placement rule (5.3), not a dependency wall. Rule 5.1
now names the directories directly.
…rectories

Folders inside a layer are classification, not a rule. Rule 5.1 now states that a
tron/ or evm/ directory holds only that chain's own protocol; 5.4 becomes 5.3.
The archify output is generated; reformatting it breaks format:check.
Cover the six existing rules missing firing tests in A-10 under rule 1.7.
Each isolated tree asserts its named rule reports every alternative of
its target pattern; the cycle case exercises a two-module cycle.

No baseline (0 violations).

Run from ts/ with npm test (includes the golden project, as in CI), or
run node_modules/.bin/vitest run --config <standalone-config.mjs> locally
with a config containing:
export default { test: { include: ["test/depcruise/rules.test.ts"] } };
The standalone config skips golden setup; test:unit excludes this file.
Implement the architecture rules preamble's depcruise-baseline mechanism
for A-18 as its consumer. Use native --ignore-known to reject new
violations and native violation keys to detect stale or duplicate entries.

The baseline has 0 entries because all 10 currently configured rules
report no violations. A-18's existing violations will enter the baseline
when their missing rules are added.

A fixed violation leaves a stale entry, so the gate fails until baseline
regeneration removes it. Metadata-only changes retain the same identity.

From ts/, run npm run depcruise to check the gate. Run
npm run depcruise:baseline to regenerate and format the baseline, review
the diff, then rerun the gate.

Validation: 23 standalone depcruise tests and 4419 unit tests passed;
depcruise, typecheck, lint with the documented local exclusions, and
format:check passed.
Enforce rule 1.2 for A-18 with the depcruise rule
outbound-imports-no-service-or-use-case: production modules under
src/adapters/outbound/ may not import src/application/services/ or
src/application/use-cases/. Type-only imports count. *.test.ts files are
exempt, as section 1 constrains production modules only.

The baseline holds 4 known violations, the A-2 imports:
- chain/evm/evm.ts -> application/services/broadcast-guard.ts
- chain/tron/tron.ts -> application/services/broadcast-guard.ts
- x402/payment-client.ts -> application/services/signer/index.ts
  (type-only)
- x402/payment-client.ts -> application/services/x402/payer-signer.ts

A new importer fails the gate. A fixed edge leaves a stale entry, so the
gate fails until baseline regeneration removes it.

Rule 1.7: rules.test.ts adds a firing case with one importer per
forbidden target (services, use-cases).

From ts/, run npm run depcruise to check the gate, and
npm run depcruise:baseline to regenerate the baseline, review the diff,
then rerun the gate.

Validation: 24 standalone depcruise tests and 4419 unit tests passed;
depcruise, typecheck, lint with the documented local exclusions, and
format:check passed.
Enforce rule 1.3 for A-18 by extending ports-are-innermost.
- Source: production modules under src/application/ports/ and
  src/application/contracts/. *.test.ts files are exempt, as section 1
  constrains production modules only.
- Target: every src/application/ path except ports and contracts, so root
  files and directories added later are covered too, not only today's
  services and use-cases.
Edges to adapters and bootstrap stay with application-owns-ports.

No baseline entries: 0 violations. The baseline is unchanged at the 4
rule-1.2 entries.

Rule 1.7: rules.test.ts fires the rule from ports and from contracts into
services, use-cases, a root application file, and an unlisted directory.
An allowed case shows that ports and contracts may import domain and each
other, and that their tests may import services and use cases.

From ts/, run npm run depcruise to check the gate, and
npm run depcruise:baseline to regenerate the baseline, review the diff,
then rerun the gate.

Validation: 25 standalone depcruise tests and 4419 unit tests passed;
depcruise, typecheck, lint with the documented local exclusions, and
format:check passed.
Enforce rule 1.4 for A-18 with the depcruise rule
application-imports-no-use-case.
- Source: every production module under src/application/. *.test.ts
  files are exempt, as section 1 constrains production modules only.
- Target: a *-service.ts module at any depth under
  src/application/use-cases/, which is how rule 1.4 defines a use case.
Other modules in use-cases/ are helpers, not targets: use cases and
services may import them. A *-service.ts outside use-cases/ is not a use
case.

The baseline adds 4 entries (8 in total, 4 of them from rule 1.2). These
are the A-4 imports:
- use-cases/bai-wallet-binding.ts -> use-cases/message-service.ts
  (type-only)
- use-cases/tron/multisig-collaboration-service.ts ->
  use-cases/tron/multisig-service.ts
- use-cases/tron/multisig-service.ts -> use-cases/tron/sig-service.ts
  (type-only)
- use-cases/tron/vote-service.ts -> use-cases/tron/stake-service.ts

A new importer fails the gate. A fixed edge leaves a stale entry, so the
gate fails until baseline regeneration removes it.

Rule 1.7: rules.test.ts fires the rule from a use case, a helper, a
nested use case, services, ports, contracts, and an unlisted application
directory. An allowed case covers use cases and services importing
helpers, a use case importing a service, and tests and an inbound
adapter importing use cases.

From ts/, run npm run depcruise to check the gate, and
npm run depcruise:baseline to regenerate the baseline, review the diff,
then rerun the gate.

Validation: 27 standalone depcruise tests and 4419 unit tests passed;
depcruise, typecheck, lint with the documented local exclusions, and
format:check passed.
…ound

Enforce rule 1.5 for A-18 with two depcruise rules. Both exempt *.test.ts
files, as section 1 constrains production modules only.

domain-and-application-use-allowlisted-packages: production modules
under src/domain/ and src/application/ import nothing outside src/
except node:crypto, @noble/*, and @scure/*. Other Node core modules,
other npm packages, and unresolvable packages are violations.

sdks-and-http-clients-only-in-outbound: outside src/adapters/outbound/,
no production module imports the chain, device, and protocol SDKs named
in 1.5 (tronweb, ethers, @sun-protocol/*, @bankofai/*, @ledgerhq/*) or
an HTTP client package (axios, undici, node-fetch, got, ky). This also
covers SDKs in bootstrap. node:http and node:https are not listed: they
also serve, which 1.9 places in inbound.

The baseline adds 7 entries (15 in total):
- allowlist (A-6): domain/erc8004/revert.ts -> ethers,
  application/use-cases/evm/transaction-service.ts -> ethers,
  domain/errors/redact.ts -> node:os,
  domain/async/index.ts -> node:async_hooks
- SDK (A-6): the same two ethers edges
- SDK (A-7): bootstrap/composition.ts -> @bankofai/x402-core
No pure library outside the allowlist is imported today, so the
allowlist is not widened.

A new importer fails the gate. A fixed edge leaves a stale entry, so the
gate fails until baseline regeneration removes it.

Rule 1.7: rules.test.ts fires each rule. The allowlist case uses npm,
core, look-alike, unresolvable, and type-only imports. The SDK case
covers every listed package from inbound, bootstrap, domain, and
application. Allowed cases cover the allowlisted packages (resolved and
unresolved), test exemptions, outbound SDK imports, and look-alike names.

From ts/, run npm run depcruise to check the gate, and
npm run depcruise:baseline to regenerate the baseline, review the diff,
then rerun the gate.

Validation: 31 standalone depcruise tests and 4419 unit tests passed;
depcruise, typecheck, lint with the documented local exclusions, and
format:check passed.
Enforce rule 1.8 for A-18 with three depcruise rules. All exempt *.test.ts
files, as section 1 constrains production modules only.

integrations-do-not-import-each-other: within src/adapters/outbound/, an
integration imports only itself and the shared http/ and persistence/
plumbing. An integration is chain/<family>, or any other directory under
adapters/outbound/. Since http/ and persistence/ are integrations too,
the rule makes them import only themselves and each other, which is
1.8's "imports none".

dependency-cruiser inserts a from.path capture into to.pathNot unescaped,
so the config lists the integration directories of the cruised tree when
it loads and generates one rule per integration, with the name regex-
escaped. A new directory is covered on the next run. Only a missing
directory yields no integrations; any other read error aborts the run
instead of silently dropping rules.

outbound-modules-outside-integrations-import-only-plumbing: a module
directly in adapters/outbound/ or chain/ belongs to no integration and,
within outbound, imports only http/ and persistence/. This stops a
chain/ root module from bridging the families.

sdks-only-inside-integrations: such loose modules and the shared
plumbing import no chain, device, or protocol SDK; HTTP clients stay
allowed in plumbing. The SDK and HTTP-client package lists are now
shared constants, also used by the rule 1.5 SDK rule, which matches the
same packages as before. SDKs in bootstrap stay covered by that rule.

The baseline adds 1 entry (16 in total), A-11:
- adapters/outbound/ledger/index.ts ->
  adapters/outbound/chain/tron/tx-integrity.ts
A new importer fails the gate. A fixed edge leaves a stale entry, so the
gate fails until baseline regeneration removes it.

Rule 1.7: rules.test.ts fires each rule. The cases include
family-to-family, nested, dotted-name (foo.v2 vs fooXv2), plumbing-to-
integration, and SDK imports in loose modules and plumbing. Allowed
cases cover own modules, plumbing, inner layers, HTTP clients in
plumbing, and tests. Discovery tests cover a missing directory, a
non-directory chain/, and an unreadable outbound/.

From ts/, run npm run depcruise to check the gate, and
npm run depcruise:baseline to regenerate the baseline, review the diff,
then rerun the gate.

Validation: 39 standalone depcruise tests and 4419 unit tests passed;
depcruise, typecheck, lint with the documented local exclusions, and
format:check passed.
Remove sunswap-does-not-import-sunpump and
sunpump-does-not-import-sunswap. integrations-do-not-import-each-other
(rule 1.8) already rejects production imports between the two
integrations in both directions, runtime and type-only.

The old pair had no *.test.ts exemption, so it also rejected test
imports that section 1 allows. With the pair removed, those test imports
pass.

The baseline is unchanged: neither rule had entries (16 in total).

Rule 1.7: the four fixtures and the two firing cases that existed only
for the pair are removed from rules.test.ts; the integration rule keeps
its own firing tests. Coverage was checked in scratch trees with the
real config: SunSwap -> SunPump at runtime and SunPump -> SunSwap
type-only are both reported by integrations-do-not-import-each-other,
and the same imports from *.test.ts files pass.

From ts/, run npm run depcruise to check the gate, and
npm run depcruise:baseline to regenerate the baseline, review the diff,
then rerun the gate.

Validation: 37 standalone depcruise tests and 4419 unit tests passed;
depcruise, typecheck, lint with the documented local exclusions, and
format:check passed.
…appers

Enforce the module-level part of rule 6.4 for C-16 with three depcruise
rules. 6.4 is not section 1, so *.test.ts files are not exempt.

ledger-apps-only-in-ledger-wrapper: only adapters/outbound/ledger/
index.ts imports the Ledger app modules that sign: hw-app-trx (root,
Trx, TIP712) and hw-app-eth (root, Eth, modules/EIP712). Helpers and
data such as hw-app-trx/utils, hw-app-eth/services/*, and
modules/EIP712/utils stay allowed, as do the transports.

erc8004-sdk-only-in-registry-wrapper: only adapters/outbound/erc8004/
sdk-registry.ts imports @bankofai/8004-sdk at runtime. Type-only and
typeof import() edges reach no capability and are allowed.

x402-payment-modules-only-in-payment-client: only adapters/outbound/
x402/payment-client.ts imports the x402 modules that sign, send, or
settle:
- x402-fetch
- x402-core client and facilitator
- the x402-tron and x402-evm client, facilitator, batch-settlement
  server, and exact/upto entries
- x402-evm/adapters/agent-wallet
Server schemes, storage modules (file-storage, redis-storage), and
x402-core types, utils, schemas, and wallets stay allowed.

Each module was classified from the installed SDKs: its runtime
exports, and the sign/send/settle call sites reachable through the
package's own imports. The capability subpaths are listed per package
in the config. Each pattern matches the bare specifier and the resolved
lib/, lib-es/, or dist/ file, and is anchored so that sibling and helper
modules do not match. The lists follow the installed versions, so an
SDK upgrade needs the export maps rechecked.

Not covered here, pending name- and member-level lint rules:
- the mixed modules: the tronweb and ethers roots; the x402-tron,
  x402-evm, and x402-core roots; x402-tron/gasfree and
  /batch-settlement; x402-evm/v1; x402-core/server and /http
- C-14 and C-15

The baseline adds 3 entries (19 in total), all test edges:
- x402/gasfree-errors.test.ts -> x402-fetch
- x402/gasfree-errors.test.ts -> x402-tron/gasfree/client
- x402/nile-settlement.test.ts -> x402-tron/exact/facilitator
A new importer fails the gate. A fixed edge leaves a stale entry, so the
gate fails until baseline regeneration removes it.

Rule 1.7: rules.test.ts fires each rule with the real SDK exports,
including deep dist and lib paths and tests. A separate test removes the
fixture's node_modules link and checks that unresolved roots are still
reported by their bare specifier. Allowed cases cover the wrappers, the
helper, data, storage, and server modules, type imports of the ERC-8004
SDK, and look-alike names. The rule 1.5 allowed fixture now uses a
Ledger transport instead of hw-app-trx.

From ts/, run npm run depcruise to check the gate, and
npm run depcruise:baseline to regenerate the baseline, review the diff,
then rerun the gate.

Validation: 42 standalone depcruise tests and 4419 unit tests passed;
depcruise, typecheck, lint with the documented local exclusions, and
format:check passed.
…ccess by review

An import graph cannot tell a server from a client, or signing from encoding in the
same SDK module, so rules 1.9 and 6.4 use lint for those checks. Rule 3.5's inbound
clause becomes review: the repository ports have no name a tool can rely on.
Implement the architecture rules preamble: enable automated checks with a
baseline of known violations, and remove entries as violations are fixed.
Catch count growth, stale counts, and missing, ignored, or noncanonical
files with native ESLint bulk suppressions and a freshness wrapper.

The baseline has 0 entries and no 020 IDs yet. This is infrastructure for
A-18, B-46, C-18, C-20, and C-16 checks. Native suppressions track counts per
file and rule: fixing one violation and adding another of the same rule in
the same file can preserve the count and pass.

From ts/, run lint and regenerate with matching ignore patterns:
  npm run lint -- --ignore-pattern 'audit-claude/**' --ignore-pattern 'docs/superpowers/**'
  npm run lint:baseline -- --ignore-pattern 'audit-claude/**' --ignore-pattern 'docs/superpowers/**'
Review the baseline diff and rerun lint after regeneration.

Validation: depcruise, typecheck, lint, format:check, 4419 unit tests, and
83 standalone tooling tests pass. Focused native CLI probes, boolean-ignore
regressions, and five focused mutations pass. Golden project not run.
Enforce architecture rule 6.5 with lexical scope checks that distinguish
runtime environment reads from write-only setup and cleanup. Preserve real
value shadowing and ignore erased TypeScript references.

Allow src/bootstrap/** and exactly the four C-18 exception files:
- src/adapters/inbound/cli/input/prompt/index.ts
- src/adapters/inbound/cli/help/index.ts
- src/adapters/outbound/config/index.ts
- src/adapters/outbound/ledger/index.ts
Paths above are relative to ts/; neighboring modules are not exempt.

Baseline 79 existing reads in 32 test/helper/tooling files omitted by the
C-18 audit. There is no production baseline. Native suppressions track
file/rule/count, so same-count replacements within one file can pass.
Lexical analysis does not propagate aliases, resolve dynamic names or
imports/re-exports, infer arbitrary API side effects, or inspect generated
source strings.

From ts/, run lint with the local ignore patterns:
npm run lint -- --ignore-pattern 'audit-claude/**' --ignore-pattern 'docs/superpowers/**'
After fixing known reads, regenerate with the same patterns:
npm run lint:baseline -- --ignore-pattern 'audit-claude/**' --ignore-pattern 'docs/superpowers/**'
Review the exact file/rule/count diff and rerun lint with matching patterns.

Round 15 APPROVE: QA passed 4519 unit tests, 83 standalone tooling tests,
59 independent probes, and focused mutations. Validation runtime was
Node v24.7.0; verification on a supported runtime remains outstanding.
Enforce rule 1.9 with ESLint's stock no-restricted-imports for named
Node HTTP/HTTPS/HTTP2/net/TLS and ws server capabilities, including
HTTP/2 createSecureServer. Allow explicit type imports, inbound
adapters, and section 1's test exemption; retain outbound clients.

Baseline one createServer import in outbound/x402/server.ts (A-13).
Preserve all 79 existing rule-6.5 counts. Cover stock namespace and
default-import limits and native baseline growth/removal behavior.

Run npm run lint and npm run test:unit from ts/. Regenerate known
counts with npm run lint:baseline after reviewing actual violations.
Retain transaction IDs when a submission's outcome is unknown: TxPipeline,
TRON tx broadcast and EVM tx broadcast attach details.txId on timeout,
rpc_error and invalid_node_response, and unreadable node responses are
reported as invalid_node_response instead of transaction_rejected.

Route the x402 Permit2 approve through the application's TRON write path.
The SDK no longer broadcasts it: the approve runs through TxPipeline with
the broadcast guard, expiry and signer gates, confirmation within
waitTimeoutMs, an allowance re-check, and the chain's own error codes.

Move x402 signer resolution, the dry-run/pay decision and the payment
signing policy (payer, GasFree fee ceiling, deadline, approve intent and
budget) into application; the adapter only translates SDK payloads.

Inject the broadcast guard into the TRON and EVM gateways, GasFree, SunPump
launch, TronLink submit and x402 pay instead of importing it from
application services; each refuses during dry-run. The CLI bars broadcasts
for every --dry-run input, on the chain and neutral paths.

Validate the SunSwap liquidity protocol (V2, V3, V4) in the domain for every
caller instead of routing unknown values to V2.

Validation on Node v24.22.1: typecheck, depcruise (15 known, was 19), lint,
format:check, 4790 unit tests, and the golden project. Independent QA ran
13 funds-safety mutation probes.
Repair the B2 user-data violations of the architecture rules.

- Keystore: delete publishes the index before removing the secret and
  reports secretRemoved truthfully; derive stores its label in the same
  locked write; change-password is a journaled write with a commit point
  and recovery on the next command.
- wallets.json, tokens.json and contacts.json are validated on load with
  the same domain rules the write paths apply; token ids are stored in
  canonical form; contact names are unique across families under the lock.
- Service credentials are read only from --api-key-stdin, config.yaml
  must be 0600 when any is set, and error output masks them.
- Config values, the password policy and protocol bounds have one domain
  owner applied by loading, config set, flags and use cases.

Credential keys passed as command-line values are now rejected with
invalid_option; there is no transition period.

Validation on Node v24.22.1: typecheck, depcruise, lint, format:check,
4926 unit tests, and the golden project. Independent QA ran 12 mutation
probes and a 21-point SIGKILL probe of the password-change journal.
Add SigningService.sign(scope, {network, origin, signer, payload}) as the
only signing entry; pipelines, TRON sign and multisig, message and typed
data, GasFree, SunSwap and x402 all use it.

- Refuse typed data whose domain.chainId is not the selected network's
  chain, and EVM transactions with another or no chain id.
- Bind TRON transactions to the network by their reference block in tx
  sign, approvals, broadcast and multisig; a lagging node is rpc_error,
  offline signing warns network_not_verified.
- Sign an x402 EVM transaction only when it is the Permit2 approve of
  the payment token.
- Move EVM transaction decoding, ERC-8004 revert decoding and the
  request deadline into adapters, inject the redaction home directory,
  and silence the x402 SDK logger inside its integration.
- Remove TronRpcClient.tronweb; tests reach the SDK through a helper.

x402 TRON Permit2 approvals also get the reference-block check (one
extra node read per approve).

Validation on Node v24.22.1: typecheck, depcruise (8 known, was 15),
lint, format:check, 5005 unit tests, and the golden project. Independent
QA ran signing-safety mutation probes.
Facts the user sees are derived once, by their owner, and carried in
closed types; renderers only format.

- TRON: one execution fact for --wait, tx status and tx info; contract
  calls need an execution result; permission update and tx sign
  --transaction run the post-sign checks; account history maps
  transport failures to retryable codes; head block, blocks, prices,
  node info, stake totals, resources, votes and token text come
  normalized from the adapter.
- EVM: blocks and transactions are decoded once in the adapter; ERC-20
  calldata decoding lives in domain/evm.
- Risk facts and totals (unlimited approvals, zero minimums, fee
  collection, received amounts, units, intervals, permit lifetime)
  are result fields.
- Report secret and backup files left after a commit; GasFree receipt
  mismatches keep their trace id; fix the GasFree error-code docs.
- Close port, contract and transaction types; chain facts (hash
  format, zero address, units, address comparison) come from
  domain/family; amounts crossing ports carry their unit.

JSON results gain fields for these facts; missing node facts now fail
with invalid_node_response or provider_error instead of reading as 0.

Validation on Node v24.22.1: typecheck, depcruise, lint, format:check,
5194 unit tests, and the golden project. Independent QA ran 10 mutation
probes and fake-node probes for every reported-fact entry.
Snapshot the --json-schema catalogs and the success/error envelopes. Declare
each exclusive flag group once and derive validation, help and the catalog
from it; group violations are invalid_option. Give each capability one
application predicate (supported, needs-inputs, unsupported) that both the
execution gate and a new per-network `support` field in --json-schema read;
ERC-8004 support comes from its deployment table and B.AI recharge from its
chain table, so neither passes the gate and fails later. A builtin network ×
capability test checks the gate and adapter checks against the predicate.

Validation on Node v24.22.1: typecheck, depcruise, lint, format:check,
5218 unit tests, and the golden project. Independent QA checked that the
snapshots fail on schema changes and probed the network/capability matrix.
… list

2.3 and the 6.5 schema check have no test, so they are review. Inbound
reaches devices only through use cases (3.5). With no command deadline a
request may wait as long as its transport allows (3.7).
@gummy789j
gummy789j marked this pull request as ready for review November 2, 2026 06:43
…in params examples

chain prices text now matches chain params and the published params[].unit value.
The chain params examples use current Nile values, and the four longest
machine-interface paragraphs are split into bullets without changing their content.
A /triggerconstantcontract reply with result.result true and the transaction
marked FAILED is a contract revert, with or without revert data. It now fails
with execution_reverted (retry never) on every estimate and constant-call path
instead of falling through to rpc_error. revertData is set only when the node
returned some.

The constant-call helper no longer reports every non-success reply as
execution_error: a reply without a call result (for example a non-JSON body) is
invalid_node_response, and a node API error is rpc_error. Token readers still
treat a revert as missing token metadata.
…etails

vote cast reads getMaintenanceTimeInterval from the node and omits the
field when the read fails. The x402 submit-admission refusal carries
phase payment_request, paymentStatus not_sent and retryPayment false. A
Permit2 approval that was broadcast and not confirmed is reported with
its txId and outcome unknown, even when the deadline ends the payment
before the approval's own error arrives.
…ead output

account history reports a TRC10 transfer with its asset id, symbol and
units instead of TRX. A TRON block without a header is
invalid_node_response. address generate maps a directory it cannot
create to io_error. stake delegated --resource returns only that
resource's cap. sunswap price prints (none) for no rows.
Ctrl-C at a prompt ends the command with an aborted error envelope
instead of exiting 130. The private-key prompt applies the secp256k1
range check and asks again. A provider's DEADLINE_OR_CLOCK_SKEW reply is
tx_expired. --max-gasfree-fee-raw uses the domain's raw amount rule.
bai recharge refuses more than 6 decimal places before any request.
…tadata reads

The x402 Permit2 approval reports its transaction id once its broadcast
request is dispatched, so a deadline that ends the payment while the
broadcast response is pending fails with the txId and outcome unknown.
Admission refusals, preflight failures and definite node rejections add
neither.

A TRC10 asset lookup in account history that the command deadline ends
after it was sent now fails the command with timeout instead of falling
back to a record without metadata.
…transfers

- The signing entry recovers every Ledger signature (TRON and EVM transactions,
  messages, typed data) to the signing account over the payload it sent, and
  refuses a malformed or foreign one with signed_payload_mismatch before it is
  returned, written or submitted. A multisig check covers only the appended
  signature and requires earlier approvals unchanged.
- stake undelegate reads the delegation to the receiver for the resource and
  fails with insufficient_stake before signing, in every mode.
- TRC20 and ERC20 tx send read the sender's token balance and fail with
  insufficient_token_balance before the fee estimate, in every mode.
- tx multisig: --create without exactly one of --hex / --file, and --hex /
  --file without --create, are invalid_option like the other input groups.
- block: a non-numeric positional is an invalid block number (invalid_value)
  instead of an unknown subcommand; no request is sent.
- Ledger hash-signing fallback warning no longer mentions a URI; it says the
  device shows only the hash and the transaction must be verified here.
- derive --index <existing> --label <different> is refused with invalid_value
  and points to rename; nothing is written. The same label stays idempotent.
- x402 pay: a payment limit is bound to the asset it was converted for, so
  pay-time selection cannot switch to another asset; policy refusals keep
  their own code and paymentStatus not_sent.
- Bump zod to ^4.6.6 and follow-redirects to 1.16.2.
- config.yaml YAML warnings are no longer printed, so the source line and any
  credential on it never reach stderr.
- Redact bare 64+ hex in error text; escape terminal controls on stderr.
- Refuse a wallet file whose dklen is not 32.
- Create CLI directories 0700.
- tx sign --out creates a new 0600 file and refuses an existing path with
  output_exists; a failed write leaves no file.
- Inherited object names in --network and EIP-712 types get the documented
  error codes; address generate help states what --print-secret does.
- The route API adapter drops routes quoted at zero output; with none left,
  sunswap swap fails no_matching_route instead of sending a zero floor.
- sunswap swap --min-out sets amountOutMinimum in tokenOut base units, as on
  sunpump buy/sell; it excludes --slippage and --quote. A floor above the
  expected output fails slippage_exceeded before any approval or signature.
- The application computes the floor and the router encoder takes it as is.
- --min-out is kept as canonical digits, so a leading zero no longer fails
  the router call check.
Dev-only lockfile update that covers dependabot #1039 and #1033 (source-map-js CVE-2026-93749); rolldown and postcss follow vite.
An optional read that the command deadline stopped no longer degrades to a
partial success: SunSwap position prices, EVM chain node fields, the EVM
suggested tip, and the optional nonce reads of an EVM build now fail with
timeout, while ordinary failures keep their fallback. SunSwap Permit2 and
router planning keep classified errors instead of re-wrapping them as
provider_error, and the SDK clock request now runs under the command's
dispatch check and signal.

Document the Ledger TRON app 0.7.7-0.8.1 limit on some permission updates.
Fix exit codes, error codes, output fields and examples that no longer match the code, and add the refusals
this branch introduced to the affected exit lists.
Governance commands declare conditional auth, since dry-run and build-only need no password; add-liquidity help
says V4 requires the tokens with --position-id; x402 names --decimals when --asset is missing; a missing or
invalid positional is named by its placeholder.
… warning

A command deadline that ended during the optional getContractInfo read was
swallowed, so contract info exited 0 without its info. It now fails timeout;
other info failures still only omit it.

bai report-recharge returned its short-budget warning as an internal message
object; it is now the documented string, as in bai recharge.
The optional TRC-20 name() read swallowed a command deadline, so position-info
returned a position without names or prices. The vote maintenance interval read
did the same before any send; it now fails timeout in dry-run, build-only and
sign-only, and still only omits the value after the vote may have been sent.
x402 pay with --body or --body-file and --method GET reached fetch, which refused
the body, and failed provider_error after nothing was sent. It is now
invalid_option before any request.

A missing or invalid positional was reported as an option (missing required
option --uri), although several positionals have no flag form. It is now named
as the usage line shows it: missing required argument <uri>, invalid <uri>.
TronWeb reads delegations, the delegatable maximum, the withdrawable amount
and the unfreeze count from the solidity node by default. That state lags
the head, so stake undelegate and stake withdraw refused a delegation or
expiry that was already in a block.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant