Files
kredits-web/AGENTS.md
T
raucao eb74a5e6d1
CI / Lint (pull_request) Successful in 55s
CI / Test (pull_request) Successful in 1m59s
Release Drafter / Update release notes draft (pull_request) Successful in 3s
Require Node.js 22+
Needs a temporary env var for the tasks, until a larger dependency
update
2026-07-26 14:42:59 +02:00

109 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md
Reference for AI agents working in `kredits-web`.
## Overview
Unhosted Ember.js Web UI for [Kosmos Kredits](https://wiki.kosmos.org/Kredits) — a contribution-tracking and community-currency system. The app is serverless: it reads all data from the **Rootstock (RSK)** blockchain via `@kredits/contracts` + `ethers`, fetches contributor metadata from **IPFS**, and caches everything locally with `localforage`. No custom backend.
## Tech Stack
- **Ember.js 3.18** (Octane edition), **ember-cli** build pipeline (Broccoli)
- **Plain JavaScript** (no TypeScript; `jsconfig.json` only enables `experimentalDecorators`)
- **SCSS** for styling (no Tailwind/CSS modules)
- **ethers 5** + **@kredits/contracts** for on-chain reads/writes
- **ember-concurrency** for async sync tasks; **Ember Services** for state (no Redux, no Ember Data)
- **QUnit** + qunit-dom + fetch-mock for tests
- **Node 22** (`.nvmrc`); `>= 22` in engines
## Commands
| Task | Command |
|---|---|
| Dev server (RSK testnet) | `npm start` |
| Dev against local Hardhat devchain | `npm run start:local` |
| Ember/QUnit suite (all tests) | `ember test` |
| Interactive test runner | `ember test --server` |
| All linters | `npm run lint` |
| Lint JS (autofix) | `npm run lint:js -- --fix` |
| Lint Handlebars templates | `npm run lint:hbs` |
| Production build into `release/` | `npm run build-prod` |
| Deploy to 5apps | `npm run deploy` (=`git push 5apps master`) |
No `format` or `typecheck` scripts (no TS, no Prettier).
## Project Structure
```
app/
├── components/ # PODS layout: <name>/component.js + template.hbs
├── controllers/ # Route controllers (dashboard, budget, signup, contributions/*, ...)
├── routes/ # Ember routes (mirror router.js hierarchy)
├── services/ # Singleton services — the core architecture
├── models/ # Plain EmberObject models (contributor, contribution, reimbursement)
├── helpers/ # Template helpers (fmt-*, etc.), dash-case files
├── utils/ # Plain JS utilities (dash-case)
├── styles/ # SCSS: app.scss + partials + components/_<name>.scss
└── templates/ # Route .hbs templates
config/ # environment.js, targets.js, optional-features.json
tests/ # unit/, integration/, fixtures/, helpers/
public/ # Static assets (img/, robots.txt)
release/ # Committed production build output (deployed via 5apps)
```
**Key files**:
- `app/services/kredits.js` (~784 lines) — central state hub; wraps `@kredits/contracts` SDK, holds `contributors`/`contributions`/`reimbursements`/`currentUser`, subscribes to contract events, runs ember-concurrency sync tasks.
- `app/services/browser-cache.js``localforage`-based offline persistence, namespaced per network.
- `app/router.js` — full route map (dashboard, contributions, contributors, signup, budget, reimbursements, about).
- `config/environment.js` — web3/IPFS/GitHub/API config + env-var overrides.
## Architecture
- **State**: The `kredits` service is the single source of truth. Components/controllers inject `@service kredits` and consume `contributors`, `contributions`, `reimbursements`, `currentUser`, and computed derivations (`contributionsConfirmed`, `kreditsByContributor`, …) via `alias`/`filterBy`/`sort` macros.
- **Identity/auth**: No login. The connected wallet address (`window.ethereum` → MetaMask, fallback read-only `JsonRpcProvider`) is mapped to a contributor via `Contributor.getContributorIdByAddress`. `currentUserIsContributor` / `currentUserIsCore` gate UI capabilities.
- **Live updates**: Contract event subscriptions (`ContributorAdded`, `ContributionAdded`, `ContributionVetoed`, `ReimbursementAdded`, `ReimbursementVetoed`, `Token.Transfer`) update local state. A `provider.on('block', …)` tick recomputes confirmed vs. unconfirmed items (confirmation is block-based).
- **Offline**: `browser-cache` loads from localforage on boot (`loadInitialData`), sets `*NeedSync` flags, then background sync tasks refill from chain. Enables fast unhosted operation.
- **Bootstrap**: `routes/application.js``beforeModel: kredits.setup()``model: loadInitialData() + addContractEventHandlers()``afterModel` schedules syncs.
## Conventions
- **Files**: dash-case (`contribution-list/component.js`, `fmt-crypto-currency.js`).
- **Components**: **PODS** layout — each component is a directory under `app/components/<name>/` with `component.js` + `template.hbs`.
- **Component style — prefer Octane for new code**: use `@glimmer/component` with `@tracked`, `@service`, `@action` decorators. The codebase is mid-migration from classic `Component.extend({...})` with `actions` hashes; don't force-rewrite existing classic components, but write new ones in Octane. ESLint and `template-lint` warn (not error) on classic patterns.
- **Styling**: plain SCSS — one partial per component at `app/styles/components/_<name>.scss`, imported by `app/styles/app.scss`. Semantic/BEM-ish class names.
- **Imports**: external libs → `@ember/*` → ember addons → app utils/models → `kredits-web/config/environment`. App imports use the `kredits-web/` absolute module prefix.
- **No jQuery** (`ember/no-jquery: error`). **No Ember Data** — models are plain `EmberObject.extend` with a `serialize()` returning `JSON.stringify`.
- **Indent**: 2 spaces, LF, UTF-8, trim trailing whitespace (`.editorconfig`).
## Config & Environment
Read in `config/environment.js` (lines ~102110). No `.env.example` (`.gitignore` ignores `/.env*`).
| Var | Default | Purpose |
|---|---|---|
| `WEB3_PROVIDER_URL` | `https://rsk-testnet.kosmos.org` | JSON-RPC endpoint |
| `WEB3_CHAIN_ID` | `31` (RSK Testnet) | Target chain ID (used for MetaMask switching) |
| `WEB3_NETWORK_NAME` | `RSK Testnet` | Human-readable name; namespaces the localforage cache |
| `CI` | — | Toggles `testem.js` `--no-sandbox` and IE11 browser target |
## Testing
- **Framework**: QUnit via `ember-qunit` + `qunit-dom`; `@ember/test-helpers` (`render`, `click`, `fillIn`).
- **HTTP mocking**: `fetch-mock``tests/test-helper.js` mocks Bitstamp ticker endpoints globally.
- **Runner**: headless Chrome (`testem.js`); CI uses Node 22 via `.gitea/workflows/ci.yaml`.
- **File patterns**:
- `tests/unit/<type>/<name>-test.js` — unit tests for services, controllers, routes, models, helpers, utils.
- `tests/integration/components/<name>/component-test.js` — component rendering tests (PODS layout).
- `tests/integration/helpers/<name>-test.js` — helper tests.
- `tests/fixtures/<name>.js` — shared fixture data (contributors, contributions, …).
- `tests/helpers/create-component.js` — custom test helper.
## Gotchas
- **Mid-migration codebase**: classic and Octane component styles coexist. Linters warn (not error) on legacy patterns; `.template-lintrc.js` keeps a `pending` list of templates still using `{{action}}`.
- **OpenSSL on Node 17+**: `ember-auto-import@1` bundles webpack 4, which hashes with MD4 — dropped by OpenSSL 3 in Node 17+. The `npm` scripts (`start`, `test`, `build`, `build-prod`, `start:local`) prepend `NODE_OPTIONS=--openssl-legacy-provider` to work around this. Direct `npx ember ...` invocations must set the flag manually. Remove once ember-auto-import is upgraded to v2+.
- **`release/` is committed build output** — don't hand-edit; regenerated by `npm run build-prod` (auto-staged on `npm version`).
- **Deployment is `git push 5apps master`** (5apps static hosting) — pushing to `master` triggers a deploy. Be deliberate about commits/pushes.
- **No `.env.example`** — set `WEB3_*` vars inline (see `npm run start:local` for the pattern).
- **`gitno/`** contains auxiliary non-Ember scripts (bitstamp sync shell, `nostr.html`) — not part of the app.