Add comprehensive documentation, testing, and validation infrastructure - #73
Draft
ig-shaun wants to merge 9 commits into
Draft
Add comprehensive documentation, testing, and validation infrastructure#73ig-shaun wants to merge 9 commits into
ig-shaun wants to merge 9 commits into
Conversation
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
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
docs/CAPABILITIES.mdanddocs/capabilities.jsonauto-generated from the step catalogue, providing a complete inventory of available steps, their inputs/outputs, and which actions use themAGENTS.mdandRECIPES.mdwith architecture guidance and worked examples for common tasks (rebranding, adding actions, implementing steps)CLAUDE.mdas an entry point for AI agentsscripts/gen-capabilities.tsandscripts/gen-schema.tsto generate documentation and JSON schema from source of truth (the zod schemas)Config Validation & Schema
constants/config.schema.ts: zod schema that is the single source of truth forconfig.jsonshapeconfig.schema.jsonfor IDE validation of config filesscripts/validate-config.tsfor fast config validation with helpful error messages and suggestionstests/config.spec.tswith contract tests on validation error messagesTesting Infrastructure
vitest.config.tsandtests/setup.tsfor unit testing with Vitest (chosen over Jest to avoid SWC conflicts with Next.js)tests/unit/stepCatalogue.spec.tsto ensure parity between STEPS enum, catalogue, and component implementationstests/unit/transactions.spec.tswith golden tests for all message builders (bank send/multisend, staking, governance)tests/unit/currency.spec.tsfor currency conversion utilitiese2e/smoke.spec.tswith data-driven Playwright tests that verify every configured action route renders without console errorsplaywright.config.tswith separate smoke and deployed test projectsCI/CD Workflows
.github/workflows/ci.yml: runs typecheck → lint → format → validate:config → test → build on every PR.github/workflows/e2e.yml: runs Playwright smoke tests; can target deployed URLs via workflow input.github/dependabot.ymlfor automated dependency updates grouped by family.github/pull_request_template.mdwith verification checklistBuild & Package Configuration
package.jsonwith comprehensive scripts:verify(the main command),gen(regenerate docs),validate:config,test,test:watch,test:e2enext.config.jsto lint all source directories (not just pages/components).nvmrcpinning Node to 18.20.8.env.examplewith detailed comments explaining every variable.gitignoreto exclude Playwright artifactsStep Catalogue & Type Safety
constants/stepCatalogue.ts: declarative definition of every step (summary, kind, config schema, data schema, dependencies, messages)TERMINAL_KINDS,TERMINAL_STEP_IDS, andIMPLEMENTED_STEP_IDSfor use in validationtypes/config.tsto derive types from zod schema instead of hand-written interfacesComponent & Error Handling
steps/UnknownStep.tsxto render a helpful error when an action references an unimplemented step (instead of an indefinite spinner)pages/[actionId].tsxto import and useUnknownStepas fallbacksteps/ReviewAndSign.tsxto import error utilities and handle failures gracefullysteps/KadoBuyCrypto.tsxtype signatureMinor Fixes
console.logfromutils/transactions.tshttps://claude.ai/code/session_01Lo46EDzpK4MQzkGjFEG2J3