diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..98b1aa744 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,95 @@ +# AGENTS.md - GovTool agent guide + +Cardano GovTool monorepo: lets ada holders use the CIP-1694 governance features - +register as DRep, delegate voting power, submit and vote on governance actions. +Production is https://gov.tools. + +## Guides + +docs/ai/ARCHITECTURE_MAP.md: topology, data flow, env vars, CI gates +docs/ai/PLAYBOOKS.md: which files to touch, per change type +govtool/frontend/AGENTS.md: React/TS conventions and traps +govtool/backend/AGENTS.md: Haskell conventions and traps +tests/AGENTS.md: E2E, API and load suites + +## Packages, and what belongs in each + +- govtool/frontend: React 19 + Vite + TS, the gov.tools app. All UI, routes, forms, + wallet connection and transaction building. Main surface. +- govtool/backend: Haskell + Servant, read-only REST over cardano-db-sync. All + endpoints, db-sync queries, response shapes. +- govtool/metadata-validation: NestJS. Rules for what makes CIP-100/108/119 off-chain + metadata valid. +- govtool/analytics-dashboard: Next.js internal usage dashboard. Not part of the + gov.tools user flow; touch only when asked about analytics. +- tests: Playwright E2E, Python API tests, Gatling load tests, test infra. +- docker: compose for the whole stack. docs: architecture, style guides, ops. +- gov-action-loader: dev utility to bulk-submit governance actions to a testnet. + +A new governance action type is a frontend change. See PLAYBOOKS, "add a new +governance action type". + +Three dependencies cannot be fixed from this repo: @intersect.mbo/pdf-ui and +@intersect.mbo/govtool-outcomes-pillar-ui, npm packages that render the Proposal +Discussion and Outcomes pillars inside the frontend, and +govtool-outcomes-pillar-backend, an image pinned in docker/docker-compose.yaml. + +## Workflow rules + +From CONTRIBUTING.md, enforced by CI or review: + +1. Base branch is develop, never main. Promotion: develop, test, staging, main. +2. Branch name is type/issue-number-description, e.g. feat/123-add-voting-ui. Types: + feat, fix, chore, docs. +3. Every PR updates CHANGELOG.md under "[Unreleased]", in the right subsection + (Added, Fixed, Changed, Removed), with a link to the GitHub issue. +4. Never hand-bump versions. package.json, vva-be.cabal and the Dockerfiles are + bumped together by update-govtool-version.yml on manual dispatch. All at 2.0.29. +5. Commit subject: imperative, 50 chars or fewer, capitalized, no trailing period, + issue number in subject or body. + +## Verification + +Run the checks for the package you touched. CI runs exactly these. + +```bash +# govtool/frontend - all three gate the PR +npm run lint && npm run tsc && npx vitest run # NOT npm run test: that is watch mode + +# govtool/metadata-validation +npm run lint && npm test + +# govtool/backend - GHC 9.2.8 + cabal, easiest inside nix +cabal build all && pre-commit run --all-files hlint && pre-commit run --all-files stylish-haskell +``` + +The Haskell toolchain is not installed here - no cabal, ghc, stack or nix on PATH - +and the backend needs a real cardano-db-sync Postgres to run, with no fixture path. +Edit and reason about backend code freely; never claim a build passed. + +## Docs that are wrong + +- docs/operations/HANDLE_NEW_GOVERNANCE_ACTION_TYPE.md: stale paths and line numbers. + Corrected in PLAYBOOKS, "add a new governance action type". +- docs/architecture/README.md: dated 2024-04-30, predates React 19, TanStack Query v5, + React Router v8 and the Outcomes pillar. Concepts hold, versions do not. + +## Local stack + +```bash +cd docker && cp .env.example .env # fill DBSYNC_POSTGRES_*, IPFS_GATEWAY, PDF_API_URL +docker compose up -d --build +``` + +Frontend on 80, backend on 9999 with Swagger at /swagger-ui, metadata-validation on +3000, outcomes on 3001. Compose also needs govtool/backend/example-config.json, +govtool/metadata-validation/.env and docker/.envs to exist. See docker/README.md. + +Skip all of it for frontend-only work: point VITE_BASE_URL at a deployed environment +and run npm run dev. + +## Style + +Match the surrounding file first. Guides in docs/style-guides for react, css-in-js and +css-sass, all Airbnb-based. Haskell: .stylish-haskell.yaml plus hlint. Prettier: semi +true, singleQuote false, trailingComma all, printWidth 80. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..43c994c2d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 01009d4aa..42df2b777 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,49 +1,47 @@ # Contributing to the `GovTool` project -โš ๏ธ This is a work in progress document, more instruction on how-to contribute to come! - -Contributing todo: - -- [ ] Align with latest OSC policies -- [ ] Refactor to reflect reality -- [ ] Make more friendly to open source contributors - Thanks for considering contributing and helping us on creating GovTool! ๐Ÿ˜Ž -The best way to contribute right now is to try things out and provide feedback, but we also accept contributions to the documentation and the obviously to the code itself. +The best way to contribute right now is to try things out and provide feedback, but we +also accept contributions to the documentation and obviously to the code itself. -This document contains guidelines to help you get started and how to make sure your contribution gets accepted, making you our newest GovTool contributor! +This document gets you from "I want to help" to a merged pull request. ## Table of Contents -- [Contributing to the `GovTool` project](#contributing-to-the-govtool-project) - - [Table of Contents](#table-of-contents) - - [Code of Conduct](#code-of-conduct) - - [Ask for Help](#ask-for-help) - - [Roles and Responsibilities](#roles-and-responsibilities) - - [I Want To Contribute](#i-want-to-contribute) - - [Before Submitting a Bug Report](#before-submitting-a-bug-report) - - [How Do I Submit a Good Bug Report?](#how-do-i-submit-a-good-bug-report) - - [Your First Code Contribution](#your-first-code-contribution) - - [Working Conventions](#working-conventions) - - [Pull Requests](#pull-requests) - - [Branch Naming](#branch-naming) - - [Commit Messages](#commit-messages) - - [Rationale](#rationale) - - [Merge Commit PRs and Rebase Branches on top of Main](#merge-commit-prs-and-rebase-branches-on-top-of-main) - - [Rationale](#rationale-1) - - [Versioning](#versioning) - - [Changelog](#changelog) - - [Style Guides](#style-guides) - - [React](#react) - - [CSS in Javascript](#css-in-javascript) - - [CSS / SASS](#css--sass) - - [Haskell](#haskell) - - [Development Processes](#development-processes) - - [Developer workflow](#developer-workflow) - - [QA Workflow](#qa-workflow) - - [PO Workflow](#po-workflow) - - [Tech Lead Workflow](#tech-lead-workflow) +- [Code of Conduct](#code-of-conduct) +- [Ask for Help](#ask-for-help) +- [Ways to Contribute](#ways-to-contribute) +- [Reporting Bugs](#reporting-bugs) + - [Before Submitting a Bug Report](#before-submitting-a-bug-report) + - [How Do I Submit a Good Bug Report?](#how-do-i-submit-a-good-bug-report) +- [Your First Code Contribution](#your-first-code-contribution) + - [1. Set up your environment](#1-set-up-your-environment) + - [2. Find an issue to work on](#2-find-an-issue-to-work-on) + - [3. Claim the issue](#3-claim-the-issue) + - [4. Fork the repository, if needed](#4-fork-the-repository-if-needed) + - [5. Create a branch](#5-create-a-branch) + - [6. Make your changes](#6-make-your-changes) + - [7. Run the checks](#7-run-the-checks) + - [8. Commit your changes](#8-commit-your-changes) + - [9. Update the changelog](#9-update-the-changelog) + - [10. Open a pull request](#10-open-a-pull-request) + - [11. Address feedback](#11-address-feedback) +- [Working Conventions](#working-conventions) + - [Branches and Environments](#branches-and-environments) + - [Pull Requests](#pull-requests) + - [Branch Naming](#branch-naming) + - [Commit Messages](#commit-messages) + - [Merge Commit PRs and Rebase Branches on top of the Base Branch](#merge-commit-prs-and-rebase-branches-on-top-of-the-base-branch) + - [Changelog](#changelog) + - [Versioning](#versioning) + - [Style Guides](#style-guides) +- [Roles and Responsibilities](#roles-and-responsibilities) +- [Development Processes](#development-processes) + - [Developer workflow](#developer-workflow) + - [QA Workflow](#qa-workflow) + - [PO Workflow](#po-workflow) + - [Tech Lead Workflow](#tech-lead-workflow) ## Code of Conduct @@ -52,44 +50,58 @@ By participating, you are expected to uphold this code. ## Ask for Help -See [`SUPPORT.md`](./SUPPORT.md) should you have any questions or need some help in getting set up. +See [`SUPPORT.md`](./SUPPORT.md) should you have any questions or need some help in +getting set up. Questions are best asked in +[GitHub Discussions](https://github.com/IntersectMBO/govtool/discussions), where others +can benefit from the answer too. -## Roles and Responsibilities +Security vulnerabilities are different: do not open an issue, follow +[`SECURITY.md`](./SECURITY.md) instead. -We maintain a [CODEOWNERS file](./CODEOWNERS) which provides information who should review a contributing PR. -Note that you might need to get approvals from all code owners (even though GitHub doesn't give a way to enforce it). +## Ways to Contribute -## I Want To Contribute +- Use GovTool on [mainnet](https://gov.tools/) or [preview](https://preview.gov.tools/) and report what does not work. +- Improve documentation, in this repository or on [docs.gov.tools](https://docs.gov.tools/). +- Fix a bug or implement a feature idea, see [Your First Code Contribution](#your-first-code-contribution). -#### Before Submitting a Bug Report +## Reporting Bugs + +### Before Submitting a Bug Report A good bug report shouldn't leave others needing to chase you up for more information. -Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. -Please complete the following steps in advance to help us fix any potential bug as fast as possible. +Therefore, we ask you to investigate carefully, collect information and describe the +issue in detail in your report. +Please complete the following steps in advance to help us fix any potential bug as fast +as possible. - Make sure that you are using the latest version. -- Determine if your bug is really a bug and not an error on your side. +- Determine if your bug is really a bug and not an error on your side, e.g. using incompatible environment components/versions. - If you are looking for support, you might want to check [this section](#i-have-a-question). -- To see if other users have experienced (and potentially already solved) the same issue you are having. + If you are looking for support, see [Ask for Help](#ask-for-help). +- Search the [open issues](https://github.com/IntersectMBO/govtool/issues) to see if + other users have experienced (and potentially already solved) the same issue. - Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue. - Collect information about the bug: - Stack trace (Traceback) - OS, Platform and Version (Windows, Linux, macOS, x86, ARM) + - Browser and wallet extension, with versions, for frontend issues - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant. - Possibly your input and the output - Can you reliably reproduce the issue? And can you also reproduce it with older versions? -#### How Do I Submit a Good Bug Report? +### How Do I Submit a Good Bug Report? We use GitHub issues to track bugs and errors. If you run into an issue with the project: -- Open an [Issue](https://github.com/IntersectMBO/govtool/issues/new). - (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) +- Open an [Issue](https://github.com/IntersectMBO/govtool/issues/new/choose) using the + ๐Ÿ› Bug report template. + (Since we can't be sure at this point whether it is a bug or not, we ask you not to + talk about a bug yet and not to label the issue.) - Explain the behavior you would expect and the actual behavior. - Please provide as much context as possible. - Describe the _reproduction steps_ that someone else can follow to recreate the issue on their own. + Describe the _reproduction steps_ that someone else can follow to recreate the issue + on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. - Provide the information you collected in the previous section. @@ -98,78 +110,134 @@ Once it's filed: - The project team will label the issue accordingly. - A team member will try to reproduce the issue with your provided steps. - If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps. + If there are no reproduction steps or no obvious way to reproduce the issue, the team + will ask you for those steps. The issue would then be marked as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced. - If the team is able to reproduce the issue, it will be marked `needs-fix`. It may possibly be marked with other tags (such as `critical`). The issue will then be left to be [implemented by someone](#your-first-code-contribution). -#### Your First Code Contribution +## Your First Code Contribution + +Welcome! Whether you're fixing a bug, adding a feature, or improving documentation, +we're excited to have you on board. The steps below take you through one full +contribution. + +### 1. Set up your environment + +Follow [Getting started](./README.md#-getting-started) in the README and set up only the +package you intend to change: + +- UI work: [`govtool/frontend`](./govtool/frontend/README.md), Node is pinned by + `.nvmrc` and you can run against a deployed backend, so no local chain data is needed. +- API work: [`govtool/backend`](./govtool/backend/README.md), needs access to a + `cardano-db-sync` Postgres database. +- Metadata rules: [`govtool/metadata-validation`](./govtool/metadata-validation/README.md). +- Everything at once: [`docker/`](./docker/README.md). + +Make sure you're working from the latest `develop` to avoid potential conflicts. + +### 2. Find an issue to work on + +- Browse open issues on the [GovTool GitHub Issues page](https://github.com/IntersectMBO/govtool/issues). +- Look for issues labeled [`๐Ÿ› Bug`](https://github.com/IntersectMBO/govtool/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%90%9B+Bug%22) + or [`๐Ÿ’ก Feature idea`](https://github.com/IntersectMBO/govtool/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%92%A1+Feature+idea%22). +- For existing feature idea tasks, comment on the issue to express interest or share thoughts. +- No issue for what you want to change? Open one first, so the approach can be agreed + before you write code. + +### 3. Claim the issue + +- Comment on the issue to let maintainers know you're working on it. This helps avoid + duplicate efforts. +- Move the issue from `todo` to `in progress` on the project board (if you have permissions). + +### 4. Fork the repository, if needed + +This step applies only if you lack permission to create branches in the `govtool` +repository. Fork it on GitHub to create a copy under your account. + +### 5. Create a branch + +Create a new branch from `develop`, never from `main`, with a descriptive name +(see [Branch Naming](#branch-naming)). -Welcome to contributing to `GovTool`! Whether you're fixing a bug, adding a feature, or improving documentation, weโ€™re excited to have you on board. This section guides you through your first code contribution to make the process smooth and rewarding. +```bash +git checkout develop +git pull +git checkout -b feat/123-add-voting-ui +``` -#### Getting Started +### 6. Make your changes -1. **Set Up Your Environment**: - - Follow the instructions in [`README.md`](./README.md) file and navigate to the specific folder corresponding to the form you want to fix or enhance. - - Follow the setup instructions to clone the repository and install the necessary dependencies. - - Make sure you're using the latest version of the project to avoid potential conflicts. +- Write clean, well-documented code following the [Style Guides](#style-guides) for + React, Haskell, CSS, or other relevant technologies. +- Match the conventions of the files around you. +- Add or update tests to ensure your changes are robust. +- Keep your changes focused and aligned with the issue's scope. -2. **Find an Issue to Work On**: - - Browse open issues on the [GovTool GitHub Issues page](https://github.com/IntersectMBO/govtool/issues). - - Look for issues labeled `๐Ÿ› Bug` or `๐Ÿ’ก Feature idea`. - - For existing feature idea tasks, comment on the issue to express interest or share thoughts. +### 7. Run the checks -3. **Claim an Issue**: - - Comment on the issue to let maintainers know youโ€™re working on it. This helps avoid duplicate efforts. - - Move the issue from `todo` to `in progress` on the project board (if you have permissions). - +Run the checks for the package you touched before pushing, CI runs the same ones. -4. **Fork the Repository**: - - This step applies only if you lack permission to create branches or perform actions in the `govtool` repository. - - Fork the `govtool` repository on GitHub to create a copy under your account. +```bash +# govtool/frontend +npm run lint && npm run tsc && npx vitest run # note: npm run test is watch mode +# govtool/metadata-validation +npm run lint && npm test -4. **Create a Branch**: - - Create a new branch from the `develop` branch with a descriptive name (see [Branch Naming](#branch-naming)). - - Example: `feat/123-add-voting-ui` or `fix/456-update-api-endpoint`. +# govtool/backend (inside the nix shell) +cabal build all && pre-commit run --all-files hlint && pre-commit run --all-files stylish-haskell +``` -5. **Make Your Changes**: - - Write clean, well-documented code following the [Style Guides](#style-guides) for React, Haskell, CSS, or other relevant technologies. - - Add or update tests to ensure your changes are robust. - - Keep your changes focused and aligned with the issueโ€™s scope. +### 8. Commit your changes -6. **Commit Your Changes**: - - Write clear, concise commit messages following the [Commit Messages](#commit-messages) guidelines. +Write clear, concise commit messages following the [Commit Messages](#commit-messages) +guidelines, and reference the issue number. -7. **Submit a Pull Request**: +### 9. Update the changelog - `If You Have Push Permissions to the Original Repository` - - Push your branch to the repository and create a pull request (PR) to the `develop` branch. +Add a line to [`CHANGELOG.md`](./CHANGELOG.md) under `[Unreleased]`, in the matching +`Added` / `Fixed` / `Changed` / `Removed` subsection, with a link to the issue. See +[Changelog](#changelog). - `If You Donโ€™t Have Push Permissions (Using a Forked Repository)` - - Push your branch to the forked repository and create a pull request (PR) in the `govtool` main repository, using the `govtool` `develop` branch as the base and your forkโ€™s branch name as the head repository. +### 10. Open a pull request - - Use the PR template provided in the repository and link the related issue (e.g., `issues #123`). - - Describe your changes clearly, including why theyโ€™re needed and how they were tested. - - If your PR isnโ€™t ready for review, mark it as a draft. +- If you have push permissions: push your branch to this repository and open a pull + request against `develop`. +- If you work from a fork: push your branch to your fork and open a pull request in the + `IntersectMBO/govtool` repository, with `develop` as the base branch and your fork's + branch as the head. +- Fill in the PR template and link the related issue (e.g. `issues #123`). +- Describe your changes clearly, including why they're needed and how they were tested. +- If your PR isn't ready for review, open it as a draft. -8. **Address Feedback**: - - Expect reviews from maintainers or other contributors (see [CODEOWNERS](./CODEOWNERS)). - - Respond to feedback promptly and make requested changes. - - Once approved, your PR will be merged into `develop` by the author after passing tests. +### 11. Address feedback -9. **Celebrate Your Contribution!**: - - Once merged, your changes will move through the [Development Processes](#development-processes) (QA, staging, etc.). - - Youโ€™re now a `GovTool` contributor! Share your achievement and consider tackling another issue. +- Expect reviews from maintainers or other contributors (see [CODEOWNERS](./CODEOWNERS)). +- Respond to feedback promptly and make requested changes. +- Once approved and green, the PR is merged into `develop` by the author. +๐ŸŽ‰ Once merged, your changes move through the [Development Processes](#development-processes) +(QA, staging, release). You're now a `GovTool` contributor, consider tackling another issue! ## Working Conventions +### Branches and Environments + +Changes flow in one direction through four long-lived branches: + +`develop` โ†’ `test` โ†’ `staging` โ†’ `main` + +- `develop` is where all contributions land, and it is the base branch for every PR. +- `test` and `staging` are promotion branches driven by QA and the tech lead. +- `main` tracks what has been released, never branch off it and never PR into it directly. + ### Pull Requests -Thank you for contributing your changes by opening a pull requests! +Thank you for contributing your changes by opening a pull request! To get something merged we usually require: @@ -185,7 +253,7 @@ Please reuse the branch naming for the pull request naming. ### Branch Naming - When creating your branches please create informative names. -- Using prefixes such as `feat/`, `fix/`, `chore/`, `docs/` for branch names are a good start. +- Use prefixes such as `feat/`, `fix/`, `chore/`, `docs/`. - Using the related issue number after the prefix is required. Examples: @@ -198,14 +266,18 @@ Examples: ### Commit Messages Please make informative commit messages! -It makes it much easier to work out why things are the way they are when youโ€™re debugging things later. +It makes it much easier to work out why things are the way they are when you're +debugging things later. -A commit message is communication, so as usual, put yourself in the position of the reader: what does a reviewer, or someone reading the commit message later need to do their job? +A commit message is communication, so as usual, put yourself in the position of the +reader: what does a reviewer, or someone reading the commit message later need to do their job? Write it down! -It is even better to include this information in the code itself, but sometimes it doesnโ€™t belong there (e.g. ticket info). +It is even better to include this information in the code itself, but sometimes it +doesn't belong there (e.g. ticket info). Also, include any relevant meta-information, such as issue numbers. -If a commit completely addresses a issue, you can put that in the headline if you want, but itโ€™s fine to just put it in the body. +If a commit completely addresses an issue, you can put that in the headline if you want, +but it's fine to just put it in the body. Here are seven rules for great git commit messages: @@ -217,71 +289,89 @@ Here are seven rules for great git commit messages: 6. Wrap the body at 72 characters (hard limit) 7. Use the body to explain what and why vs. how -There is plenty to say on this topic, but broadly the guidelines in [this post](https://cbea.ms/git-commit/) are good. - -#### Rationale +There is plenty to say on this topic, but broadly the guidelines in +[this post](https://cbea.ms/git-commit/) are good. -Git commit messages are our only source of why something was changed the way it was changed. -So we better make the readable, concise and detailed (when required). +**Rationale:** git commit messages are our only source of why something was changed the +way it was changed. So we better make them readable, concise and detailed (when required). -### Merge Commit PRs and Rebase Branches on top of Main +### Merge Commit PRs and Rebase Branches on top of the Base Branch -When closing branches / PRs use merge commits, so we have a history of PRs also in the git history. -Do not merge main into side branches, instead rebase them on top of main. -Try to keep branches up-to-date with main (not strict requirement though). -Once merged to main, please delete the branch. +When closing branches / PRs use merge commits, so we have a history of PRs also in the +git history. +Do not merge the base branch (usually `develop`) into side branches, instead rebase them +on top of it. +Try to keep branches up-to-date with the base branch (not a strict requirement though). +Once merged, please delete the branch. -**Tip:** Use Github's merge button in PRs to merge with commit. -This strategy helps us operate on the commits you've delivered: it's easier to [cherry-pick a merge commit](https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt--mltparent-numbergt) than a series of commits, and it's also easier to [revert changes using a merge commit](https://git-scm.com/docs/git-revert#Documentation/git-revert.txt--mparent-number) instead of a series of reverts. -If a branch is outdated, use the rebase button in PRs to rebase feature branches (NOT update via merge). - -#### Rationale - -Keeping branches ahead of main not only make the git history a lot nicer to process, it also makes conflict resolutions easier. -Merging main into a branch repeatedly is a good recipe to introduce invalid conflict resolutions and loose track of the actual changes brought by a the branch. - -### Versioning +**Tip:** Use GitHub's merge button in PRs to merge with commit. +This strategy helps us operate on the commits you've delivered: it's easier to +[cherry-pick a merge commit](https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt--mltparent-numbergt) +than a series of commits, and it's also easier to +[revert changes using a merge commit](https://git-scm.com/docs/git-revert#Documentation/git-revert.txt--mparent-number) +instead of a series of reverts. +If a branch is outdated, use the rebase button in PRs to rebase feature branches (NOT +update via merge). -Not all releases are declared stable. -Releases that aren't stable will be released as pre-releases and will append a -pre tag indicating it is not ready for running on production networks. +**Rationale:** keeping branches ahead of the base branch not only makes the git history +a lot nicer to process, it also makes conflict resolutions easier. +Merging the base branch into a branch repeatedly is a good recipe to introduce invalid +conflict resolutions and lose track of the actual changes brought by the branch. ### Changelog -During development, on every PR; +On every PR, keep [`CHANGELOG.md`](./CHANGELOG.md) up-to-date with a high-level, +technical, but user-focused list of changes, according to +[keepachangelog](https://keepachangelog.com/en/1.0.0/). -- Make sure `CHANGELOG.md` is kept up-to-date with high-level, technical, but user-focused list of changes according to [keepachangelog](https://keepachangelog.com/en/1.0.0/). -- Bump `UNRELEASED` version in `CHANGELOG.md` according to [semver](https://semver.org/). +Add your line under `[Unreleased]`, in the `Added`, `Fixed`, `Changed` or `Removed` +subsection, and link the issue: -### Style Guides +```markdown +### Fixed -#### React +- Fix disappearing proposals in the governance actions list for the same tx hashes [Issue 3918](https://github.com/IntersectMBO/govtool/issues/3918) +``` -Please see [React Style Guide](./docs/style-guides/react/). +### Versioning -#### CSS in Javascript +Do not hand-bump version numbers. `package.json`, `vva-be.cabal` and the Dockerfiles are +bumped together by the `Update GovTool Version and Changelog` workflow on manual +dispatch, which also turns the `[Unreleased]` changelog section into a released one. -Please see [CSS in Javascript Style Guide](./docs/style-guides/css-in-js/). +Releases follow [semver](https://semver.org/). Not all releases are declared stable: +releases that aren't stable are published as pre-releases and append a `-pre` tag +indicating they are not ready for running on production networks. -#### CSS / SASS +### Style Guides -Please see [CSS / SASS Style Guide](./docs/style-guides/css-sass/). +- React: [React Style Guide](./docs/style-guides/react/) +- CSS in JavaScript: [CSS in Javascript Style Guide](./docs/style-guides/css-in-js/) +- CSS / SASS: [CSS / SASS Style Guide](./docs/style-guides/css-sass/) +- Haskell: [stylish-haskell configuration](./govtool/backend/.stylish-haskell.yaml) plus `hlint` -#### Haskell +## Roles and Responsibilities -Please see [stylish-haskell configuration](./govtool/backend/.stylish-haskell.yaml). +We maintain a [CODEOWNERS file](./CODEOWNERS) which provides information who should +review a contributing PR. +Note that you might need to get approvals from all code owners (even though GitHub +doesn't give a way to enforce it). ## Development Processes +These describe how the core team moves a ticket from idea to release. As an external +contributor you are only involved in the developer workflow. + ### Developer workflow - Choose ticket/issue to work on from the project, move ticket from `todo` to `in progress`. -- Create [well named](#branch-naming) branch from `develop` add changes, then make a pull request back to the `develop` branch. +- Create [well named](#branch-naming) branch from `develop`, add changes, then make a pull request back to the `develop` branch. - If the changes are not ready for review then feel free to create a draft PR, and link this to the ticket/issue. - When the PR is ready for review move the ticket from `in progress` to `in review`. Remember to change the state of the PR from draft to actual PR. - Developers should review each other's pull requests, and should be requested via [CODEOWNERS](./CODEOWNERS). - Unit tests are run on each pull request to `develop`. -- After a review remember to address all the requests of changes since they are blocking PR from being merged. -- Once tests pass and peer review is done the branch can be merged into `develop` by author and then deployed to the dev environment (manually). +- After a review remember to address all the requests of changes since they are blocking the PR from being merged. +- Once tests pass and peer review is done the branch can be merged into `develop` by the author and then deployed to the dev environment (manually). - The ticket status can then be moved to `in QA` making sure that the PR/branch has been added to the ticket/issue as a comment. ### QA Workflow @@ -306,3 +396,10 @@ Please see [stylish-haskell configuration](./govtool/backend/.stylish-haskell.ya - Merge `staging` branch into the `main` branch. - Deploy tagged build to `beta` environment. - Move tickets from staging status to done status. + +--- + +This document is still evolving. Known gaps we intend to close: + +- [ ] Align with latest OSC policies +- [ ] Keep the development processes in sync with how the team actually works diff --git a/README.md b/README.md index 3ccb1ea55..748a9f81e 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,137 @@ -

- -

- -

- Monorepo containing Cardano GovTool and supporting utilities -

- -
- -[![npm](https://img.shields.io/npm/v/npm.svg?style=flat-square)](https://www.npmjs.com/package/npm) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=intersect-govtool&metric=ncloc)](https://sonarcloud.io/summary/overall?id=intersect-govtool) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=intersect-govtool&metric=coverage)](https://sonarcloud.io/summary/overall?id=intersect-govtool) [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=intersect-govtool&metric=sqale_index)](https://sonarcloud.io/summary/overall?id=intersect-govtool) - -
- -
- -## ๐ŸŒ„ Purpose - -The Cardano GovTool enables ada holders to experience the governance features described in [CIP-1694](https://github.com/cardano-foundation/CIPs/blob/master/CIP-1694/README.md). - -### Instances - -#### Mainnet - -- [gov.tools](https://gov.tools/) - -#### Preview Testnet - -- [preview.gov.tools](https://preview.gov.tools/) - -### Documentation - -Learn more; [docs.gov.tools](https://docs.gov.tools/cardano-govtool/using-govtool). - -## ๐Ÿ“ Navigation - -- [Backend](./govtool/backend/README.md) -- [Frontend](./govtool/frontend/README.md) -- [In repo documentation](./docs/) -- [Tests](./tests/) - -### Utilities - -- [Governance Action Loader](./gov-action-loader/) - -### Backend - -GovTool backend implements an API wrapper around an instance of [DB-Sync](https://github.com/IntersectMBO/cardano-db-sync) which interfaces with a [Cardano Node](https://github.com/IntersectMBO/cardano-node). -The API exposes endpoints making the querying of governance related data from DB-Sync straight forward. - -### Frontend - -GovTool frontend web app communicates with the backend over a REST interface, reading and displaying on-chain governance data. -Frontend is able to connect to Cardano wallets over the [CIP-30](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0030/README.md) and [CIP-95](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0095/README.md) standards. - -## ๐Ÿณ Running locally with docker - -This repository includes a Docker Compose setup for running GovTool services locally. -For local setup instructions, see the [Docker Compose README](docker/README.md). - -## ๐Ÿค Contributing - -Thanks for considering contributing and helping us on creating GovTool! ๐Ÿ˜Ž - -Please checkout our [Contributing Documentation](./CONTRIBUTING.md). \ No newline at end of file +

+ +

+ +

+ Monorepo containing Cardano GovTool and supporting utilities +

+ +
+ +[![npm](https://img.shields.io/npm/v/npm.svg?style=flat-square)](https://www.npmjs.com/package/npm) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=intersect-govtool&metric=ncloc)](https://sonarcloud.io/summary/overall?id=intersect-govtool) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=intersect-govtool&metric=coverage)](https://sonarcloud.io/summary/overall?id=intersect-govtool) [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=intersect-govtool&metric=sqale_index)](https://sonarcloud.io/summary/overall?id=intersect-govtool) + +
+ +
+ +## ๐ŸŒ„ Purpose + +The Cardano GovTool enables ada holders to use the governance features described in +[CIP-1694](https://github.com/cardano-foundation/CIPs/blob/master/CIP-1694/README.md): +register as a DRep, delegate voting power, submit governance actions and vote on them. + +### Instances + +- Mainnet: [gov.tools](https://gov.tools/) +- Preview testnet: [preview.gov.tools](https://preview.gov.tools/) + +### Documentation + +- User documentation: [docs.gov.tools](https://docs.gov.tools/cardano-govtool/using-govtool) +- In-repo documentation: [`docs/`](./docs/) - [architecture](./docs/architecture/), [style guides](./docs/style-guides/), [operations](./docs/operations/) +- AI agent guide: [`AGENTS.md`](./AGENTS.md) - orientation, playbooks and conventions for AI coding agents + +## ๐Ÿ—‚๏ธ Repository layout + +| Path | What lives there | +| --- | --- | +| [`govtool/frontend`](./govtool/frontend/) | React + TypeScript + Vite web app behind gov.tools. All UI, routes, forms, wallet connection and transaction building. | +| [`govtool/backend`](./govtool/backend/) | Haskell + Servant read-only REST API over [cardano-db-sync](https://github.com/IntersectMBO/cardano-db-sync). | +| [`govtool/metadata-validation`](./govtool/metadata-validation/) | NestJS service that validates CIP-100/108/119 off-chain metadata. | +| [`govtool/analytics-dashboard`](./govtool/analytics-dashboard/) | Next.js internal usage dashboard, not part of the gov.tools user flow. | +| [`tests`](./tests/) | Playwright end-to-end tests, Python API tests, Gatling load tests, test infrastructure. | +| [`docker`](./docker/) | Docker Compose setup for running the whole stack locally. | +| [`gov-action-loader`](./gov-action-loader/) | Dev utility for bulk-submitting governance actions to a testnet. | + +The frontend talks to the backend over REST and to Cardano wallets over the +[CIP-30](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0030/README.md) and +[CIP-95](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0095/README.md) standards. +Every on-chain write is a transaction the frontend builds and the wallet signs; the +backend only reads. + +## ๐Ÿš€ Getting started + +Pick the smallest setup that covers the change you want to make. + +### Frontend only - the fastest path + +No Cardano node and no database needed: run the app locally against an already +deployed backend. This covers most UI work. + +```bash +cd govtool/frontend +nvm use # Node 22.22.0, pinned by .nvmrc +npm ci +cp .env.example .env +# then set VITE_BASE_URL in .env to a deployed backend, +# e.g. https://govtool.cardanoapi.io/api +npm run dev # http://localhost:5173 +``` + +To connect a wallet you need a browser extension that supports CIP-95, see the +[compatible wallets list](https://docs.gov.tools/cardano-govtool/using-govtool/getting-started/compatible-wallets). +More detail in the [frontend README](./govtool/frontend/README.md). + +### ๐Ÿณ Whole stack with Docker + +Requires Docker and a reachable `cardano-db-sync` Postgres instance. + +```bash +cd docker +cp .env.example .env # fill in DBSYNC_POSTGRES_*, IPFS_GATEWAY, PDF_API_URL +docker compose up -d --build +``` + +Frontend on port 80, backend on 9999 (Swagger UI at `/swagger-ui`), +metadata-validation on 3000, outcomes on 3001. +See the [Docker Compose README](./docker/README.md) for the config files the stack +expects. + +### Backend on its own + +Haskell, GHC 9.2.8, needs access to a `cardano-db-sync` Postgres database; a Nix shell +is the supported setup. See the [backend README](./govtool/backend/README.md). + +## โœ… Verifying a change + +Run the checks for the package you touched, CI runs the same ones. + +```bash +# govtool/frontend +npm run lint && npm run tsc && npx vitest run # note: npm run test is watch mode + +# govtool/metadata-validation +npm run lint && npm test + +# govtool/backend (inside the nix shell) +cabal build all && pre-commit run --all-files hlint && pre-commit run --all-files stylish-haskell +``` + +End-to-end, API and load suites live in [`tests/`](./tests/) and run against a deployed +environment rather than your working tree. + +## ๐Ÿค Contributing + +Thanks for considering contributing and helping us on creating GovTool! ๐Ÿ˜Ž + +Start with the [Contributing Documentation](./CONTRIBUTING.md). The short version: + +- Branch off `develop`, never `main`, and name the branch `type/issue-or-feature-description`. +- Add an entry to [`CHANGELOG.md`](./CHANGELOG.md) under `[Unreleased]` with a link to the issue. +- Open the PR against `develop` and fill in the PR template. + +Good first issues are labelled [`๐Ÿ› Bug`](https://github.com/IntersectMBO/govtool/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%90%9B+Bug%22) +or [`๐Ÿ’ก Feature idea`](https://github.com/IntersectMBO/govtool/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%92%A1+Feature+idea%22). + +## ๐Ÿ’ฌ Support + +Questions and setup help: [`SUPPORT.md`](./SUPPORT.md) and +[GitHub Discussions](https://github.com/IntersectMBO/govtool/discussions). +Security vulnerabilities: [`SECURITY.md`](./SECURITY.md). +Everyone participating is expected to follow the [Code of Conduct](./CODE-OF-CONDUCT.md). + +## ๐Ÿ“„ License + +[Apache 2.0](./LICENSE) diff --git a/docs/ai/ARCHITECTURE_MAP.md b/docs/ai/ARCHITECTURE_MAP.md new file mode 100644 index 000000000..45c9145a2 --- /dev/null +++ b/docs/ai/ARCHITECTURE_MAP.md @@ -0,0 +1,206 @@ +# Architecture map + +Orientation only. Conventions and traps are in the per-package AGENTS.md; recipes in +docs/ai/PLAYBOOKS.md. Paths whose purpose is obvious from the name are omitted. + +## Topology + +frontend to wallet extension, CIP-30/95: all chain writes happen here +frontend to backend, REST /api/*: backend is read-only +frontend to metadata-validation: POST /validate +frontend to outcomes-pillar-backend and pdf/budget API: external, other repos +backend to cardano-db-sync Postgres: read-only SQL, TTL-cached +backend to Pinata: IPFS pin, the one backend write +cardano-node to cardano-db-sync: follows the chain, not run by us + +The backend never writes to chain. Every state change is a transaction the frontend +builds with cardano-serialization-lib and the wallet signs. + +## govtool/frontend + +Vite aliases: @ for src, plus @pages @consts @context @hooks @models @services @utils +@mock, and @atoms @molecules @organisms for src/components/*. There is no @types or +@config alias, so use @/types/... and @/config/env. Many feature directories expose files through index.ts barrels. When a directory already uses a barrel, add new public exports to it; otherwise follow the surrounding direct-import pattern. + +- App.tsx: the route table. Every route registers here. +- config/env.ts: the only env accessor. Reads window.__ENV__, then import.meta.env. +- consts/paths.ts: PATHS, PDF_PATHS, OUTCOMES_PATHS, USER_PATHS, + BUDGET_DISCUSSION_PATHS +- consts/queryKeys.ts: QUERY_KEYS and MUTATION_KEYS. Every new key registers here. +- consts/governanceAction/fields.ts: GA form schemas, driving both rendering and + hashing +- services/API.ts:is the shared Axios instance for the GovTool backend and redirects backend HTTP 500 responses to the error + page.Metadata validation uses a separate Axios client because it has a separate base URL. +- i18n/locales/en.json: is the only locale and should be the source of new user-facing copy. Some existing hardcoded strings + remain. +- components: atomic design. atoms have no GovTool dependencies, molecules compose + atoms without fetching, organisms are feature blocks that may use contexts and hooks. + +Contexts in src/context: + +- wallet.tsx: about 1600 lines. CIP-30/95 connection plus every buildXCert and + buildXGovernanceAction, funnelling into buildSignSubmitConwayCertTx. The chain-write + boundary. +- appContext.tsx: protocol phase. isInBootstrapPhase, isFullGovernance, + isAppInitializing. +- featureFlag.tsx: the VITE_IS_X_ENABLED flags, and phase-derived visibility for which + vote totals show for which GA type +- pendingTransaction: in-flight txs, so the UI can poll /transaction/status/:txId +- governanceAction.tsx: GA list and detail state +- contextProviders.tsx: composes the main application providers. ChatwootProvider is mounted separately in main.tsx, and + pending-transaction state is integrated through the wallet context. + +## govtool/backend + +Layers, outermost first: route in src/VVA/API.hs, handler in the same file, the +domain function in the matching src/VVA module using withPool plus a query, then +sql/*.sql. + +Handlers return API types from API/Types.hs; domain modules return internal types from +Types.hs. The explicit mapping between them keeps the public OpenAPI schema decoupled +from db-sync's shape. Do not shortcut it. + +- app/Main.hs: config, connection pool, CacheEnv, CORS, Sentry, error-to-status map, + Swagger mount +- src/VVA/API.hs: the VVAApi route type and every handler +- src/VVA/Types.hs: internal types, the App monad, AppError, CacheEnv +- src/VVA/Pool.hs: withPool, borrow a connection +- src/VVA/Cache.hs: cacheRequest, the only caching primitive +- src/VVA/Ipfs.hs: Pinata upload, the only write path +- sql/views.sql: shared views the other queries build on +- Domain modules: DRep, AdaHolder, Proposal, Epoch, Transaction, Network, Account +- Config.hs uses Conferer; CommandLine.hs defines -c/--config, start-app, show-config + +Route groups. Read src/VVA/API.hs line 59, or /swagger-ui, for the authoritative list: +/drep/* for list, info, getVotes, get-voting-power and voting-power-list; +/ada-holder/* for get-current-delegation and get-voting-power; /proposal/* for list, +get and enacted-details; /epoch/params; /transaction/status; +/network/metrics, /network/info, /network/total-stake; /account/:stakeKey; +/ipfs/upload; and /throw500 as a deliberate error probe. + +Error to status, in handleErrors in app/Main.hs: ValidationError 400, NotFoundError +404, CriticalError and InternalError 500, AppIpfsError OtherIpfsError 400, other +AppIpfsError 503. + +CacheEnv is built once in app/Main.hs, each field a Data.Cache with a TTL from config. +cachedurationseconds defaults to 20s, dreplistcachedurationseconds to 600s. + +Pool is 2 stripes of 60 connections with a 1s idle timeout; Warp has a 300s request +timeout and 60s graceful shutdown. Long queries are a real failure mode, so push work +into SQL and cache the result rather than holding a connection. + +## govtool/metadata-validation + +A NestJS service that validates off-chain metadata. It fetches metadata from a URL, +parses the JSON, accepts a supplied CIP standard or attempts to identify CIP-108 or +CIP-119, validates the required fields for a recognized standard, and compares a +Blake2b-256 hash of the exact fetched content with the submitted hash. + +POST /validate is defined in app.controller.ts, orchestration is handled by +app.service.ts, and validation logic lives in utils/getStandard.ts, +utils/validateCIP108body.ts, and utils/validateMetadataStandard.ts. +enums/ValidationError.ts defines the validation statuses consumed by the frontend, +so check frontend usage before renaming or removing one. + +Both metadata validation and the frontend contain utils/canonizeJSON.ts. The +metadata-validation copy is currently unused by the production validation flow. The +frontend uses canonicalization for signature verification, not for the metadata hash submitted to POST /validate. + +## Environment variables + +### Frontend application configuration + +The frontend reads configuration from window.__ENV__ when running in a container, +falling back to Vite's import.meta.env values for local development and build-time +configuration. + +When adding a frontend runtime variable, update all applicable configuration points: + +1. govtool/frontend/.env.example +2. govtool/frontend/src/config/env.ts +3. govtool/frontend/docker-entrypoint.sh +4. The govtool-frontend service in docker/docker-compose.yaml + +Active frontend variables: + +- VITE_BASE_URL: GovTool backend REST API base URL. +- VITE_METADATA_API_URL: metadata-validation service base URL. +- VITE_PDF_API_URL: Proposal Discussion and Budget Discussion API base URL. +- VITE_OUTCOMES_API_URL: Governance Outcomes API base URL. +- VITE_IPFS_GATEWAY: gateway used to resolve ipfs:// resources. +- VITE_IPFS_PROJECT_ID: optional project identifier sent when accessing the + configured IPFS gateway. +- VITE_NETWORK_FLAG: Cardano network ID; 0 selects a test network and 1 + selects mainnet. +- VITE_APP_ENV: deployment environment name supplied to services such as Sentry. +- VITE_IS_DEV: enables development behavior, including React Query devtools and + bypassing production maintenance checks. +- VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED: enables the Proposal Discussion + pillar. +- VITE_IS_GOVERNANCE_OUTCOMES_PILLAR_ENABLED: enables the Governance Outcomes + pillar. +- VITE_SENTRY_DSN: optional Sentry data source name. +- VITE_CHATWOOT_URL: base URL from which the Chatwoot SDK is loaded. +- VITE_CHATWOOT_WEBSITE_TOKEN: Chatwoot website token used to initialize the + feedback widget. + +### Frontend container and analytics configuration +The frontend container also accepts variables that are handled by +docker-entrypoint.sh and Nginx rather than exposed to React through window.__ENV__: + +- UMAMI_URL: base URL of the Umami analytics service. +- UMAMI_WEBSITE_ID: Umami website identifier. +- UMAMI_SSL_VERIFY: controls TLS certificate verification for the Umami proxy; + defaults to true. +- TRUSTED_PROXY_CIDRS: trusted proxy address ranges used when resolving the + original client IP. Set it to none to disable real-IP resolution. +- REAL_IP_HEADER: header used to obtain the original client IP; defaults to + X-Forwarded-For. + +Umami is enabled only when both UMAMI_URL and UMAMI_WEBSITE_ID are configured. + +### Metadata-validation service + +The metadata-validation service reads these variables directly from process.env: + +- PORT: HTTP port used by the NestJS service. +- IPFS_GATEWAY: gateway used to resolve ipfs:// metadata URLs. +- IPFS_PROJECT_ID: optional project identifier sent to the configured IPFS + gateway. + +The local template is govtool/metadata-validation/.env.example. + +### Backend configuration + +The Haskell backend loads configuration from example-config.json, or from the file +supplied with --config / -c. Conferer also supports environment-variable overrides +prefixed with VVA_. + +Backend configuration keys: + +- dbsyncconfig.host: cardano-db-sync PostgreSQL host. +- dbsyncconfig.dbname: database name. +- dbsyncconfig.user: database user. +- dbsyncconfig.password: database password. +- dbsyncconfig.port: PostgreSQL port. +- port: backend HTTP port; example-config.json uses 9999. +- host: backend bind address. +- cachedurationseconds: default endpoint-cache lifetime in seconds. +- dreplistcachedurationseconds: DRep-list cache lifetime in seconds. +- pinataapijwt: optional Pinata API JWT used by the IPFS upload endpoint. +- sentrydsn: backend Sentry data source name. +- sentryenv: backend Sentry environment name. + +## CI gates, in .github/workflows + +- code_check_frontend.yml: on push touching govtool/frontend. Runs npm run test, + npm run lint and npm run tsc as parallel jobs. +- code_check_backend.yml: on push touching govtool/backend. hlint then + stylish-haskell via pre-commit. +- pr.yaml: on PR to develop, test, staging or main. hadolint per Dockerfile, optional + lint.sh and unit-test.sh per package, docker build, Dockle image scan. +- test_storybook.yml: Storybook must build +- test_integration_playwright.yml and test_backend.yml: E2E and Python API tests + against a deployment +- frontend_sonar_scan.yml, lighthouse.yml: SonarCloud, Lighthouse +- update-govtool-version.yml: manual dispatch, bumps every version. Never by hand. diff --git a/docs/ai/PLAYBOOKS.md b/docs/ai/PLAYBOOKS.md new file mode 100644 index 000000000..aa2dee788 --- /dev/null +++ b/docs/ai/PLAYBOOKS.md @@ -0,0 +1,219 @@ +# Playbooks + +Which files to touch, per change type. Conventions are in the per-package AGENTS.md; +this is the sequence. Where a file already shows the pattern, this points at it +rather than reproducing it, so the exemplar cannot go stale. + +## Frontend: consume a new backend endpoint + +Exemplar: getNetworkMetrics and useGetNetworkMetrics. Four files: + +1. src/models/api.ts: response type, fields snake_case, mirroring backend JSON +2. src/services/requests/getThing.ts: via the shared API instance, never axios +3. src/consts/queryKeys.ts: add to QUERY_KEYS +4. src/hooks/queries/useGetThingQuery.ts + +Plus each folder's index.ts. TanStack Query v5 takes object syntax only: +useQuery({ queryKey, queryFn }). The positional v3/v4 form will not compile. Every +value the query depends on goes in the queryKey, not just the closure: see +useGetProposalQuery, keyed on [key, dRepID, proposalId]. Return a named object like +{ networkMetrics, fetchNetworkMetrics }, not raw { data, refetch }. + +## Frontend: add a page or route + +1. src/consts/paths.ts: camelCase key, snake_case URL, :param for params. PATHS for + the core app; PDF_PATHS, OUTCOMES_PATHS, USER_PATHS, BUDGET_DISCUSSION_PATHS for + pillars. +2. src/pages/MyPage.tsx +3. src/App.tsx: where you register decides access. Top level is public, inside the + Dashboard element route gets connected chrome, wrapped in PublicRoute redirects + when a wallet is connected. + +Route placement controls layout and connected variants, but does not by itself define +all access requirements: + +- Public browsing routes are registered at the top level. +- Routes nested under Dashboard receive the connected dashboard layout. +- PublicRoute currently redirects public DRep-directory routes to their + /connected equivalents when a wallet is connected. +- Wallet-dependent transaction pages, including registration and governance-action + creation, are currently top-level routes and enforce their requirements through + their page and wallet logic. +Keep the page thin. It should wire hooks and state to feature components; follow the +surrounding page and organism structure. + +## Frontend: add a component + +Choose the existing component layer whose responsibilities most closely match the new +component. The atoms, molecules and organisms directories express increasing feature +composition, but the boundaries are not strict: existing atoms and molecules may use +shared hooks or contexts. Match nearby components before introducing a new layering +rule. +For a reusable component, add or update a Storybook story under src/stories, using +.stories.ts or .stories.tsx depending on whether the story contains JSX. +Storybook build and interaction tests run in CI. + +## Frontend: add user-facing copy + +src/i18n/locales/en.json, read with t("key"), or I18n.t("key") outside components as +in consts/governanceAction/fields.ts. Interpolation is {{name}} with +t("key", { name }). No hardcoded strings in JSX. + +## Frontend: add an environment variable + +A new React-visible `VITE_` runtime variable is complete only after updating all four +applicable configuration points: + +1. .env.example +2. src/config/env.ts, as MY_VAR: getEnv("VITE_MY_VAR") +3. docker-entrypoint.sh, into the window.__ENV__ block. +4. docker/docker-compose.yaml, passed into the govtool-frontend service + +The entrypoint is easy to miss: without runtime injection, a variable may work through +import.meta.env during local development but be undefined in a deployed container. + +Container-only variables such as Umami and Nginx proxy configuration do not belong in +src/config/env.ts or window.__ENV__; document and wire those through +docker-entrypoint.sh and Compose instead. + +## Frontend: add a feature flag + +In src/context/featureFlag.tsx: add to FeatureFlagContextType, the createContext +default, and the useMemo value. A deploy toggle is a VITE_IS_X_ENABLED var, so do the +env-var recipe first, and compare against both the string "true" and boolean true +since runtime injection can give either. A protocol-phase toggle derives from +appContext's isInBootstrapPhase or isFullGovernance as a useCallback returning a +predicate; exemplar areDRepVoteTotalsDisplayed. + +## Frontend: build a new transaction or certificate + +Shared Cardano serialization, wallet interaction and transaction submission live in +src/context/wallet.tsx. Form-specific validation and selection of the appropriate +builder remain in the calling form hook or page. + +Add the appropriate builder beside its siblings: +- buildXCert for certificates +- buildXGovernanceAction for governance-action proposal builders +- A voting builder for votes + +Expose the builder through CardanoContextType and the provider value. Pass the +result to buildSignSubmitConwayCertTx through the matching argument: +certBuilder, govActionBuilder or votingBuilder. + +buildSignSubmitConwayCertTx owns maintenance checks, UTxO selection, change, +signing, submission and pending-transaction registration. Supply the correct +transaction type and, when applicable, resourceId; it records the submitted hash +so usePendingTransaction can poll /transaction/status/:txId. + +Extend an existing builder rather than adding a parallel path, and verify on a +testnet. This code moves real ada and has no unit tests. + +## Frontend: write a unit test + +Utils go in src/utils/tests/.test.ts and normally import the +utility from the parent barrel with from "..". Other tests are generally colocated +with the code under test, for example context/featureFlag.test.tsx. + +Run a focused test with npx vitest run .Coverage comes from components, consts, context, hooks, +services and utils; pages and models are excluded, so do not chase coverage there. + +## Backend: add a REST endpoint + +Copy the shape of sql/get-network-total-stake.sql, then Network.networkTotalStake, +then getNetworkTotalStake in API.hs. Six edits, all required: + +1. sql/get-my-thing.sql, positional ? placeholders +2. vva-be.cabal, the new file under extra-source-files +3. src/VVA/Types.hs, internal type +4. the matching src/VVA module, domain function. Copy the header of src/VVA/Network.hs + TemplateHaskell pragma and local sqlFrom. A new module also needs exposed-modules. +5. src/VVA/API/Types.hs, response type with fields prefixed by the type name and + ToJSON, FromJSON and ToSchema instances. ToSchema is what puts it in Swagger. +6. src/VVA/API.hs, route on VVAApi and handler, in matching positional order + +Consequences of getting 2 or 6 wrong are in govtool/backend/AGENTS.md under "rules +that will bite you". Then mirror it on the frontend and add a case to +tests/govtool-backend/test_cases. + +## Backend: add caching to an endpoint + +Add a field to CacheEnv in src/VVA/Types.hs, initialize it in the cacheEnv +block in `app/Main.hs`, and use it at the handler call site. + +Use newCache for the standard configured TTL. Use the DRep-list TTL only when the +endpoint is deliberately intended to share that longer cache duration; rename or add +a dedicated constructor binding when introducing another cache category. + +```haskell +CacheEnv {myThingCache} <- asks vvaCache +cacheRequest myThingCache cacheKey $ do ... +``` + +## Backend: change a db-sync query + +Change a SELECT list and you must change the domain function's +case result of [(a, b, c)] pattern in lockstep. Decoding is positional, so the same +arity in the wrong order compiles and fails at runtime. sql/views.sql holds shared +views the other queries build on. There is no local fixture: validate against a +db-sync instance, and say so if you could not. + +## Add a new governance action type + +docs/operations/HANDLE_NEW_GOVERNANCE_ACTION_TYPE.md covers this but its paths and +line numbers are stale.A genuinely new governance-action type can affect both frontend and backend. + +Frontend locations: + +1. src/types/governanceAction.ts: add the GovernanceActionType member, field + types, schema extending SharedGovernanceActionFieldSchema, and the schema union + member. +2. src/consts/governanceAction/fields.ts: define the fields, meaning components, + i18n keys and validation rules. +3. src/consts/governanceAction/filters.ts: add the type when it should appear in + governance-action filters. +4. src/i18n/locales/en.json: add every label, placeholder, tip, error and display + key used by the new type. +5. src/utils: add any custom validation and update exhaustive mappings such as + getGovActionVotingThresholdKey.ts. +6. src/context/featureFlag.tsx: define voting and vote-total behavior for bootstrap + and full-governance phases. +7. src/context/wallet.tsx: add the Cardano serialization builder and expose it + through the wallet context. +8. src/hooks/forms/useCreateGovernanceActionForm.ts: add the new type to the + buildTransaction switch and construct the builder arguments. +9. Update details rendering, tests and Storybook fixtures wherever behavior differs + by governance-action type. + +Backend locations: + +1. src/VVA/API/Types.hs: add the type to GovernanceActionType. This affects JSON, + query-parameter parsing and the OpenAPI enum. +2. Review src/VVA/API.hs filtering, response conversion and enacted-details logic + for type-specific mappings. +3. Update backend API tests and response examples that enumerate or assume the + existing action types. + +Verify creation at /create_governance_action, backend filtering and response +decoding, voting behavior, details rendering and protocol phase visibility.Add +frontend unit tests and backend/E2E coverage where applicable. +Actions must comply with CIP-100 and CIP-108. +Afterward, update the stale operations document rather than preserving conflicting +paths. + +## Change metadata validation rules + +File layout and the two cross-package constraints are in docs/ai/ARCHITECTURE_MAP.md +under govtool/metadata-validation. Verify with npm run lint && npm test. + +## Add E2E coverage + +tests/govtool-frontend/playwright, against a deployed environment. The spec goes in +the numbered feature folder that matches; reuse lib/pages page objects over raw +selectors and lib/datafactory for data. Setup and the wallet-bootstrap dependency +chain are in tests/AGENTS.md. + +## Finishing any change + +Run the checks for what you touched, listed in AGENTS.md under Verification. Frontend +needs lint, tsc and vitest run all green. Add the CHANGELOG.md entry under +"[Unreleased]" with an issue link. Update any doc your change contradicts. diff --git a/govtool/backend/AGENTS.md b/govtool/backend/AGENTS.md new file mode 100644 index 000000000..38edf8193 --- /dev/null +++ b/govtool/backend/AGENTS.md @@ -0,0 +1,50 @@ +# AGENTS.md - govtool/backend + +A read-only Haskell REST API over cardano-db-sync. Servant + warp, postgresql-simple, +Conferer config, Data.Cache TTL caching, Sentry. Its only write is pinning to IPFS via +Pinata. + +The executable is still vva-be, the historical Voltaire Voting App name. Renaming it +to govtool-backend is a known outstanding chore; see README.md. + +## Commands + +```bash +direnv allow # from this directory, after filling .envrc +cabal update && cabal build all +cabal run vva-be -- --config CONFIG.json start-app # or show-config +pre-commit run --all-files hlint # <- CI gate +pre-commit run --all-files stylish-haskell # <- CI gate +``` + +GHC 9.2.8 and cabal 3.8.1.0, which is what CI uses. Plain cabal outside nix +historically fails on openapi3, hence nix plus direnv. stylish-haskell reads +.stylish-haskell.yaml here and produces the aligned import and record blocks you see +everywhere, so match the surrounding formatting: a reformat-everything diff will fail +review. + +CONFIG.json above is a file you create by copying example-config.json. Conferer also accepts +environment variable overrides prefixed with VVA_. Swagger UI is at /swagger-ui, generated +from the VVAApi type, declaring /api and / as server bases. + +## Rules that will bite you + +1. Every .sql file used with embedFile must be listed in vva-be.cabal under extra-source-files. The + sqlFrom embedFile call is a compile-time splice, so an unlisted file breaks the + build and the release tarball even when a local build works. +2. A new module must be added to exposed-modules in the cabal file. +3. The :<|> order in server must match the VVAApi type positionally. A mismatch + produces a wall of Servant types, so check ordering before anything else. +4. Postgres decoding is positional. Change a SELECT list and you must change the + case result of [(a, b, c)] pattern in lockstep; the same arity in the wrong order + compiles and fails at runtime. +5. A cache key must include every parameter that affects the result, or one user's + response is served to another. +6. A new cache is two edits: a CacheEnv field in Types.hs,initialisation in + app/Main.hs and use at the relevant handler call site. +7. Throw the right AppError. The HTTP status is derived from the constructor. +8. CORS allows only GET, HEAD, POST and OPTIONS, set in vvaCorsResourcePolicy in + app/Main.hs. A PUT or DELETE route needs that widened too. +9. Keep API types and internal types separate. Handlers return API/Types.hs types, + domain modules return Types.hs types. That mapping is what keeps the OpenAPI schema + decoupled from db-sync's shape. diff --git a/govtool/backend/CLAUDE.md b/govtool/backend/CLAUDE.md new file mode 100644 index 000000000..43c994c2d --- /dev/null +++ b/govtool/backend/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/govtool/frontend/AGENTS.md b/govtool/frontend/AGENTS.md new file mode 100644 index 000000000..864e0f321 --- /dev/null +++ b/govtool/frontend/AGENTS.md @@ -0,0 +1,67 @@ +# AGENTS.md - govtool/frontend + +The gov.tools web app. React 19 + TS + Vite 6, MUI 5 + Emotion, TanStack Query v5, +React Router v8, react-hook-form + yup, i18next, cardano-serialization-lib. + +## Commands + +```bash +npm ci # run this first if anything looks mysteriously missing +npm run dev # vite, port 5173 +npm run lint # eslint --max-warnings 0 <- CI gate +npm run tsc # tsc --noEmit --skipLibCheck <- CI gate +npx vitest run [path] # one-shot tests <- CI gate +npm run format # prettier --write src +npm run storybook # port 6006 +``` + +npm run test is watch mode and will not exit, so use npx vitest run. Coverage is +enabled in vite.config.ts, so every run emits coverage; that is expected. + +Node is pinned to 22.22.0 by .nvmrc. Setup is cp .env.example .env plus VITE_BASE_URL, +which can be any deployed backend, and VITE_METADATA_API_URL. No local backend is +needed for UI work. postinstall runs patch-package against patches, so add a patch +rather than editing node_modules. + +## Conventions + +Import through the Vite aliases, and add every new file to its folder's index.ts +barrel or it will not resolve through them. Components are arrow functions, one per +file, PascalCase, layered atoms then molecules then organisms by composition; pages +stay thin. Data fetching goes only through hooks/queries, then services/requests, then +services/API.ts: never axios directly, never a fetch inside a component. Copy comes +only from i18n/locales/en.json, env only from the env object in config/env.ts. Style +with MUI sx or Emotion using theme.ts and consts/colors.ts; no new stylesheets. + +## Lint + +Airbnb plus TS, react-hooks, jest and storybook. Turned off locally: +react-hooks/exhaustive-deps, no-nested-ternary, no-param-reassign, no-plusplus, +import/prefer-default-export. Still errors, and easy to trip: + +- no-console: only console.warn and console.error +- @typescript-eslint/no-unused-vars, no-shadow, no-redeclare +- react/jsx-filename-extension: JSX only in .tsx +- react/function-component-definition: arrow functions only, so a function declaration + for a component is an error +- import/no-extraneous-dependencies: devDependencies only in test and stories files +- semi always, linebreak-style unix + +## Traps + +- src/context/wallet.tsx is the chain-write boundary: about 1600 lines, no tests, and + it moves real ada. Extend an existing buildX function and verify on a testnet. +- Recent migrations changed import paths. Use react-router, not react-router-dom, for + v8, and @tanstack/react-query, not react-query, for v5 with object-syntax useQuery + only. The old forms do not compile. +- The pillars are npm packages, @intersect.mbo/pdf-ui and + @intersect.mbo/govtool-outcomes-pillar-ui, mounted as routes. A bug inside them + cannot be fixed from this repo. +- src/utils/canonizeJSON.ts is duplicated at + govtool/metadata-validation/src/utils/canonizeJSON.ts and must behave identically, + or metadata hash checks diverge. Change one, change both.The metadata-validation copy is currently unused. The frontend copy is used for signature verification. +- Stale local node_modules is common. If npm run tsc cannot find a module that is in + package.json, run npm ci before debugging further. +- Adding a VITE_ variable touches .env.example, src/config/env.ts, + docker-entrypoint.sh, and docker/docker-compose.yaml. Missing the runtime + wiring can make it work locally but remain undefined in containers. diff --git a/govtool/frontend/CLAUDE.md b/govtool/frontend/CLAUDE.md new file mode 100644 index 000000000..43c994c2d --- /dev/null +++ b/govtool/frontend/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/tests/AGENTS.md b/tests/AGENTS.md new file mode 100644 index 000000000..fa19a2c8b --- /dev/null +++ b/tests/AGENTS.md @@ -0,0 +1,77 @@ +# AGENTS.md - tests + +Four suites, none of which is the frontend unit-test suite. That one lives in +govtool/frontend, runs with npx vitest run, and is what gates frontend PRs. + +- govtool-frontend/playwright: Playwright + TS E2E against a deployed GovTool +- govtool-backend: Python + pytest against a deployed backend /api +- load-testing: Gatling + Maven, JDK 17, against a deployed /api +- test-metadata-api: Node support service that hosts JSON metadata during tests +- test-infrastructure: compose files and scripts that deploy the test stack + +Every suite here needs a running deployment, a real testnet, funded wallets and API +keys. None runs offline, so do not claim one did. + +## Which suite gets my change + +- A util, hook, component, context or service: govtool/frontend vitest, not here +- A backend endpoint or response shape: govtool-backend +- A user-visible flow such as registration, delegation, voting or submission: the + matching numbered folder in govtool-frontend/playwright/tests +- Metadata validation rules: govtool/metadata-validation jest tests + +## Playwright E2E + +```bash +cp .env.example .env && npm install && npx playwright install +npm run test # all, UI mode; npm run test:headless for CI-style +npm run test:delegation-pillar / :voting-pillar / :proposal-pillar / :outcomes +npm run allure:serve # report +``` + +lib/constants/environments.ts is the source of truth for env. Required: HOST_URL, the +deployed frontend under test, with the README listing dev, QA, preview, pre-prod and +mainnet URLs; NETWORK, preview by default, which derives the Blockfrost, faucet and +Kuber URLs and networkId; BLOCKFROST_API_KEY for the matching network; KUBER_API_KEY; +and FAUCET_ADDRESS, FAUCET_PAYMENT_PRIVATE and FAUCET_STAKE_PRIVATE from +npm run generate-faucet-wallet. Optional: TX_TIMEOUT default 240000, TEST_WORKERS, +CARDANOAPI_METADATA_URL, DOCS_URL, CI. + +Fund the faucet wallet. An unfunded one produces wallet-bootstrap failures that read +as flaky tests. Timeout is 90s per test, 180s on preview, with zero retries, so a +failure is real - but so is an empty wallet. + +lib/pages holds page objects; use those over raw selectors. lib/_mock holds JSON +fixtures. The fake CIP-30/95 wallet is not local: it is the npm dependency +@cardanoapi/cardano-test-wallet, injected by lib/fixtures/loadExtension.ts from +node_modules/@cardanoapi/cardano-test-wallet/script.js, so wallet behaviour changes +mean bumping that package, not editing this repo. The package-wallet script in +package.json is dead - it points at lib/_mock/cardano-test-wallet/types.ts, which no +longer exists. playwright.config.ts wires the setup and teardown +files as Playwright projects with dependencies: wallet bootstrap, then auth setups, +then feature suites, then teardowns. A new suite needing an authenticated wallet +declares that dependency instead of duplicating login. Details in +govtool-frontend/playwright/DEVELOPERS_GUIDE.md and +govtool-frontend/playwright/README.md. + +## Backend API tests + +```bash +python -m venv venv && source venv/bin/activate && pip install -r requirements.txt +export KUBER_API_URL=โ€ฆ KUBER_API_KEY=โ€ฆ && python3 ./setup.py # writes test_data.json +export BASE_URL=โ€ฆ/api METRICS_URL=โ€ฆ METRICS_API_SECRET=โ€ฆ && pytest -v +``` + +setup.py registers the DReps and stakes the tests assume, and needs its main wallet +funded; the address is in govtool-backend/README.md. Add a case to test_cases whenever +you add or change a public backend endpoint. + +## Load tests and infrastructure + +load-testing needs JDK 17: export API_URL then run via mvnw. Not in PR CI. + +test-infrastructure stands up cardano-node, db-sync, govtool and gov-action-loader. +gen-configs.sh renders configs_template and secrets_template into the rendered yml +files. Env files here hold secrets, so check .gitignore before adding one. An +untracked .env-mainet currently exists in this working tree and should not be +committed. diff --git a/tests/CLAUDE.md b/tests/CLAUDE.md new file mode 100644 index 000000000..43c994c2d --- /dev/null +++ b/tests/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md