Skip to content

Add comprehensive documentation, testing, and validation infrastructure - #73

Draft
ig-shaun wants to merge 9 commits into
mainfrom
claude/agent-jambo-development-36kw1x
Draft

Add comprehensive documentation, testing, and validation infrastructure#73
ig-shaun wants to merge 9 commits into
mainfrom
claude/agent-jambo-development-36kw1x

Conversation

@ig-shaun

Copy link
Copy Markdown
Member

Summary

This PR establishes a complete documentation, testing, and validation framework for JAMBO. It introduces machine-readable capability documentation, comprehensive test coverage, build-time config validation, and CI/CD workflows—enabling agents and developers to understand the system's capabilities and constraints without reading source code.

Key Changes

Documentation & Code Generation

  • Added docs/CAPABILITIES.md and docs/capabilities.json auto-generated from the step catalogue, providing a complete inventory of available steps, their inputs/outputs, and which actions use them
  • Added AGENTS.md and RECIPES.md with architecture guidance and worked examples for common tasks (rebranding, adding actions, implementing steps)
  • Added CLAUDE.md as an entry point for AI agents
  • Created scripts/gen-capabilities.ts and scripts/gen-schema.ts to generate documentation and JSON schema from source of truth (the zod schemas)

Config Validation & Schema

  • Added constants/config.schema.ts: zod schema that is the single source of truth for config.json shape
  • Generated config.schema.json for IDE validation of config files
  • Added scripts/validate-config.ts for fast config validation with helpful error messages and suggestions
  • Added tests/config.spec.ts with contract tests on validation error messages
  • Config validation now rejects unimplemented steps and invalid step dependencies at build time instead of rendering spinners

Testing Infrastructure

  • Added vitest.config.ts and tests/setup.ts for unit testing with Vitest (chosen over Jest to avoid SWC conflicts with Next.js)
  • Added tests/unit/stepCatalogue.spec.ts to ensure parity between STEPS enum, catalogue, and component implementations
  • Added tests/unit/transactions.spec.ts with golden tests for all message builders (bank send/multisend, staking, governance)
  • Added tests/unit/currency.spec.ts for currency conversion utilities
  • Added e2e/smoke.spec.ts with data-driven Playwright tests that verify every configured action route renders without console errors
  • Added playwright.config.ts with separate smoke and deployed test projects

CI/CD Workflows

  • Added .github/workflows/ci.yml: runs typecheck → lint → format → validate:config → test → build on every PR
  • Added .github/workflows/e2e.yml: runs Playwright smoke tests; can target deployed URLs via workflow input
  • Added .github/dependabot.yml for automated dependency updates grouped by family
  • Added .github/pull_request_template.md with verification checklist

Build & Package Configuration

  • Updated package.json with comprehensive scripts: verify (the main command), gen (regenerate docs), validate:config, test, test:watch, test:e2e
  • Added Node 18+ engine requirement and yarn 1.22.22 package manager constraint
  • Updated next.config.js to lint all source directories (not just pages/components)
  • Added .nvmrc pinning Node to 18.20.8
  • Updated .env.example with detailed comments explaining every variable
  • Updated .gitignore to exclude Playwright artifacts

Step Catalogue & Type Safety

  • Added constants/stepCatalogue.ts: declarative definition of every step (summary, kind, config schema, data schema, dependencies, messages)
  • Exported TERMINAL_KINDS, TERMINAL_STEP_IDS, and IMPLEMENTED_STEP_IDS for use in validation
  • Updated types/config.ts to derive types from zod schema instead of hand-written interfaces

Component & Error Handling

  • Added steps/UnknownStep.tsx to render a helpful error when an action references an unimplemented step (instead of an indefinite spinner)
  • Updated pages/[actionId].tsx to import and use UnknownStep as fallback
  • Updated steps/ReviewAndSign.tsx to import error utilities and handle failures gracefully
  • Updated steps/KadoBuyCrypto.tsx type signature

Minor Fixes

  • Removed debug console.log from utils/transactions.ts

https://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3

claude added 6 commits August 10, 2026 10:04
An agent working in this repo had no way to learn whether its changes were
correct: type errors were suppressed at build time, no test/typecheck scripts
existed, and there was no CI at all.

- Add `typecheck`, `format:check` and a single `verify` script that chains
  typecheck -> lint -> format:check -> build.
- Widen linting from Next 12's default `pages/` + `components/` to also cover
  utils/, hooks/, steps/, contexts/, types/, constants/ and scripts/, via both
  the lint script and `eslint.dirs` so `next build` checks the same set.
- Remove `typescript.ignoreBuildErrors`, which made `strict: true` decorative.
  Fixing the 15 resulting errors: 11 were one root cause (duplicate
  @types/react, root 18.0.9 vs 18.2.78 hoisted under five packages, so
  JSX.Element from one copy was not assignable to ReactNode from the other) and
  are resolved by pinning @types/react. The remaining four were real:
    - KadoBuyCrypto declared onSuccess against select_token_and_amount while
      the call site passes handleOnNext<kado_buy_crypto>, and passed a
      one-argument callback straight to Footer's zero-argument onForward.
    - termsAndConditions passed an `allowBack` prop that HeaderProps does not
      declare, so the intended back affordance never rendered.
    - _document used crossOrigin='true', which is not a valid value.
    - QRScanner passed a loose IObjectKeys where a camera-scan config is
      expected; now cast explicitly with a note on why the shapes differ.
- Fix a pre-existing build failure: `pages/404.tsx` imported react-lottie-player
  at module scope, and lottie-web touches `document` on import, which crashed
  static page-data collection. `yarn build` did not work on this repo before
  this commit; it is now loaded client-side only.
- Remove `experimental.runtime: 'edge'` (and its paired NEXT_USE_NETLIFY_EDGE).
  It gave an SSG app nothing, conflicts with the Node-flavoured API route and
  Buffer usage, and blocks `output: 'standalone'` later.
- Pin the toolchain: .nvmrc, `engines` and `packageManager`, plus NODE_VERSION
  in netlify.toml, since @netlify/plugin-nextjs@4.7.1 predates Node 20+.
- Document all seven live env vars in .env.example, which previously listed
  four. Notably NEXT_PUBLIC_KADO_API_KEY, without which the first shipped
  action is broken, and NEXT_PUBLIC_WC_PROJECT_ID. Set the local-chain port to
  0: the previous 26658 silently replaced the chain-registry lookup with
  hardcoded ixo-on-localhost, so any fork copying the example verbatim broke.
- Add CI running each rung as a separately named step, plus dependabot and a PR
  template.
- Correct the package.json license to Apache-2.0 to match LICENSE.

Refs IXO-4287

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3
`types/config.ts` was `type Config = typeof config` — the type was inferred from
the very artefact it was meant to constrain, so any config was definitionally
valid and the type system could never object. There was no JSON Schema, no
runtime validation, and no `$schema` key. A misspelled step id fell through
`getStepComponent`'s default arm to `<EmptySteps loading />`: an indefinite
spinner with no error and no console output.

- Add `constants/stepCatalogue.ts`, one entry per STEPS member describing its
  summary, kind, accepted config, captured data, prerequisites and emitted
  message. This is the declarative half of a step registry: pure data, no
  components, so it is safe to import from build scripts and getStaticPaths, and
  it carries no risk to the eight shipped actions. It is also the source the
  generated capability docs will read, so docs cannot drift from what the
  validator enforces.
- Add `constants/config.schema.ts` (zod). Chosen over hand-written JSON Schema +
  ajv because that would maintain schema and types separately and reintroduce
  drift in a new place; zod gives one source and two derivations. Pinned to zod
  3.22 because zod 4 requires TS 5 and this repo is on 4.7. Deliberately free of
  Node built-ins so it is safe to import from a page module.
- Validate inside `getStaticPaths`, which Next strips from the client bundle, so
  `next build` is a hard gate at zero cost to shipped JavaScript. Verified: a
  config with a missing prerequisite fails the build with the exact path, even
  when the prebuild hook is bypassed.
- Beyond shape, the schema enforces what the shape cannot: unique URL-safe action
  ids, every action ending in a step that completes the flow, and steps appearing
  after the steps whose data they read.
- Steps that accept no config now reject one rather than silently ignoring it, so
  a misplaced config block is visible rather than looking effective.
- `scripts/validate-config.ts` reports positional paths with Levenshtein "did you
  mean" suggestions, and checks that each action's image exists on disk — a check
  kept out of the schema so the schema stays importable from the client.
- Model the Kado on-ramp honestly: it legitimately ends an action without
  emitting a chain message, so step kinds are now input | review | external
  rather than forcing every action to end in a transaction.
- Replace the silent default arm with `steps/UnknownStep.tsx`, which names the
  offending step id and its location on screen and in the console.
- The four step ids declared in the enum but never wired up (check_user_balance,
  define_amount, review_and_sign, send_token_to_receiver) are marked
  implemented: false and rejected by the validator. They stay in the enum because
  review_and_sign is still used as a type parameter.
- Replace the literal placeholder strings ("config.siteName", "config.siteUrl",
  "config.about") that rendered verbatim in the header, browser tab and OG tags.
- Generate config.schema.json from the zod schema for editor validation, with a
  CI check that regenerating produces no diff.
- Drop the dead ConfigData/PartialConfigInfo types, which nothing imported.

Refs IXO-4288

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3
This repo has never had a test runner, a test file, or a testing dependency —
while README.md tells contributors their change must pass "all of the local
tests".

Vitest rather than Jest, and the reason is blast radius rather than speed. Jest
on Next 12 means either next/jest, which pins an old SWC, or babel-jest with
next/babel — and the moment a .babelrc appears at the repo root, Next silently
abandons SWC for the *application* build too, changing production output as a
side effect of adding tests. vitest.config.ts never touches the Next build.
vite-tsconfig-paths also reads the 11 aliases from tsconfig directly rather than
mirroring them, and vite-plugin-svgr matches the existing @svgr/webpack rule.
Versions are pinned to the TypeScript 4.7 ceiling; vitest 2/3 and
@testing-library/react 15+ require TS 5.

51 tests over the code where a bug loses money:

- Golden tests for all nine message builders, asserting the exact
  {typeUrl, value}. Includes the multi-send input aggregation, which has to
  collapse same-denom sends into one input coin or the transaction is rejected
  for unbalanced inputs and outputs.
- Currency and amount conversion, including precision cases where float
  arithmetic would drift.
- Catalogue parity: every STEPS member has an entry, review steps emit a
  message, input steps do not, prerequisites resolve. Crucially it also reads
  pages/[actionId].tsx and asserts every step marked implemented has a `case` in
  getStepComponent — the omission that used to produce a silent spinner is now a
  named test failure.
- The config contract, asserting on message text rather than just failure, since
  the wording is the point of the schema.

Two things the tests surfaced:

- `calculateMaxTokenAmount` does not do what its comment says. Callers pass a
  micro amount, but the "subtract 0.3 for gas fees" happens before the conversion
  to display units, so the reserve is 0.3 uixo (3e-7 IXO) rather than 0.3 IXO,
  and the formatter then rounds it away entirely. "Max" therefore offers the whole
  balance and the transaction can fail at broadcast for want of gas. Current
  behaviour is locked in by a test and documented; not changed here, because how
  much a user sends is a product decision rather than a test fix.
- `getMicroAmount` defaults to 6 decimals and ReviewAndSign never passes the
  token's real decimals, though it holds the token object. Documented in the
  test as the contract that call site breaks.

Also removes a stray console.log that fired on every proposal submission.

Refs IXO-4289

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3
An agent had no way to discover what JAMBO can express. The step catalogue and
config example in README.md were PNG screenshots — literally invisible — and
there was no AGENTS.md, CLAUDE.md or machine-readable inventory of any kind.

Splits the docs by how they are maintained, because mixing those is how
DEVELOPER.md's worked example drifted from types/steps.ts in the first place:

- AGENTS.md is process and opinion, hand-written. The verify ladder and what each
  failing rung means, the architecture, the invariants, the fork checklist, the
  traps, and — since JAMBO is deliberately ixo-first — a table of the nine places
  where ixo is baked into logic rather than config, so an agent knows those are
  choices rather than bugs to fix. Also records the known defects found while
  building this, so nobody rediscovers them.
- docs/CAPABILITIES.md and docs/capabilities.json are inventory, generated from
  constants/stepCatalogue.ts by scripts/gen-capabilities.ts. Every step with its
  config fields, captured data, emitted message, prerequisites and the shipped
  actions that use it. CI regenerates and fails on any diff, so it structurally
  cannot drift.
- docs/RECIPES.md carries four worked examples: rebrand only, new action from
  existing steps, new step with a new message type, and targeting another chain.
- CLAUDE.md is a pointer, not a copy, so there is nothing to keep in sync.

README.md's config example becomes a fenced JSON block, its step catalogue points
at the generated doc while keeping the screenshots as illustration, and its env
section now covers all seven live variables rather than three — including the
warning that NEXT_PUBLIC_USE_LOCAL_BLOCKCHAIN_PORT must stay 0.

Refs IXO-4287, IXO-4288

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3
Groundwork for AuthHub-driven end-to-end testing, plus the parts of Phase 3 that
do not depend on AuthHub's wire protocol.

Transaction errors were swallowed. `steps/ReviewAndSign.tsx` caught into
`console.error` and did nothing else, and separately treated a null hash from
`broadCastMessages` as a no-op — but the wallet adapters catch their own failures
and return null, so null *is* the failure path. Either way the user sat on the
review screen with no indication anything had happened, and a test had nothing to
assert on. Both now set an error state that renders a titled failure screen with a
`transaction-error` test id and a "Try again" action.

Playwright is configured with two projects that answer different questions:
`smoke` builds and serves locally, `deployed` runs the same specs against
PLAYWRIGHT_BASE_URL so a preview deploy can be verified rather than assumed. The
browser is resolved via an optional CHROMIUM_PATH so environments that ship a
Chromium whose build number does not match this Playwright version can point at it
instead of downloading a second copy.

The smoke suite is data-driven off constants/config.json, so a fork that changes
its actions gets its actions checked with no test to update. 16 tests covering:
every action route responds and renders the connect-wallet prompt when signed out;
an unlisted action id 404s, which is what fallback: false should do; the home page
renders the configured site name and description; no page logs an unexpected
console error; and there are no leftover "config.*" placeholder strings in the
document, which is what the shipped config used to render verbatim.

Full capture-review-sign flows need an authenticated wallet and land once the
AuthHub adapter exists. Everything above runs today, with no wallet and no secrets.

Refs IXO-4290

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
jambo-yoma Ready Ready Preview Aug 10, 2026 11:24am

Request Review

…ing format:check

Two failures from the first CI run on this branch.

The Playwright job failed at browser install:

    Package 'libasound2' has no installation candidate
    Failed to install browsers

`ubuntu-latest` is now Ubuntu 24.04 (noble), where libasound2 became a virtual
package provided by libasound2t64. Playwright 1.44's `--with-deps` dependency
list predates that rename. Upgrading to 1.55.1 picks up noble support.

Upgrading rather than pinning the runner to ubuntu-22.04, because that image is
itself on the way out and pinning would trade one expiry date for another.
Verified locally that 1.55.1 still drives an externally supplied Chromium via
CHROMIUM_PATH, so environments with a preinstalled browser keep working: all 16
smoke tests pass.

Separately, `yarn verify` failed on a clean checkout after running the e2e suite,
because Playwright writes test-results/.last-run.json and prettier does not read
.gitignore. Running the tests and then verifying — the obvious sequence — broke
the build with a formatting error about a generated artifact. Added the four
Playwright output directories to .prettierignore.

Also verified the Vercel preview deploy for this branch serves correctly: every
action route and static page returns 200, an unlisted action id returns 404 as
fallback: false requires, and the configured site name and description render
with no leftover "config.*" placeholders.

Refs IXO-4290

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3
All 16 Playwright tests failed in CI with:

    page.goto: Protocol error (Page.navigate): Cannot navigate to invalid URL
      - navigating to "/termsAndConditions"

The paths are relative, so baseURL was empty. The e2e workflow passes
`PLAYWRIGHT_BASE_URL: ${{ inputs.base_url }}`, and GitHub Actions materialises an
unprovided workflow_dispatch input as an empty string rather than leaving the
variable unset. `process.env.PLAYWRIGHT_BASE_URL ?? fallback` only falls back on
null or undefined, so on a pull_request run baseURL became '' and every
navigation had nothing to resolve against.

It passed locally because the variable was genuinely absent there — precisely the
kind of environment difference CI exists to catch, and it would have kept passing
locally forever.

Reads env vars through a helper that treats empty and whitespace-only as unset,
applied to PLAYWRIGHT_BASE_URL, PLAYWRIGHT_PORT and CHROMIUM_PATH, since an empty
CHROMIUM_PATH would otherwise have produced executablePath: ''.

Verified by reproducing the failure locally with PLAYWRIGHT_BASE_URL='' (1 failed
with the identical error), then confirming all 16 pass under the same condition.

Refs IXO-4290

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3
The baseURL fix got 7 of 16 tests green; the 9 action-route tests then failed on
the console-error assertion:

    Access to XMLHttpRequest at 'https://registry.ping.pub/impacthub/chain.json'
    from origin 'http://127.0.0.1:3210' has been blocked by CORS policy

That is not the app's fault. Chain info is resolved from third-party
infrastructure by @ixo/cosmos-chain-resolver, and registry.ping.pub sends no
Access-Control-Allow-Origin header, so the browser rejects it on a clean network.
Behind a proxy the same request fails earlier as a resource load error, which the
filter already ignored — which is exactly why this passed locally and failed on a
GitHub runner. Asserting zero console errors on a page that makes third-party
calls makes the suite a function of the network it happens to run on.

The assertion is worth keeping — it catches React errors, uncaught exceptions and
the UnknownStep logging — so it is now scoped to application faults rather than
removed. Verified both directions: the exact CI error string is matched by the new
patterns, and four representative app faults (undefined property access, a React
key warning, a minified React error, and jambo's own unknown-step log) still come
through.

Recorded in AGENTS.md so the next person does not investigate these console errors
as a regression. The app degrades gracefully here — getChainOptions uses
Promise.allSettled — and every action page still rendered correctly; only the
console assertion tripped.

Refs IXO-4290

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3
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.

2 participants