From 2ddba29b668e0f97826441b7789466411317db0c Mon Sep 17 00:00:00 2001 From: Sudip Bhattarai Date: Thu, 27 Aug 2026 12:52:24 +0545 Subject: [PATCH 1/3] docs: wip update md files, add agents.md files --- AGENTS.md | 95 +++++++++ CLAUDE.md | 1 + CONTRIBUTING.md | 375 +++++++++++++++++++++++------------- README.md | 204 +++++++++++++------- docs/ai/ARCHITECTURE_MAP.md | 135 +++++++++++++ docs/ai/PLAYBOOKS.md | 164 ++++++++++++++++ govtool/backend/AGENTS.md | 50 +++++ govtool/backend/CLAUDE.md | 1 + govtool/frontend/AGENTS.md | 66 +++++++ govtool/frontend/CLAUDE.md | 1 + tests/AGENTS.md | 77 ++++++++ tests/CLAUDE.md | 1 + 12 files changed, 964 insertions(+), 206 deletions(-) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 docs/ai/ARCHITECTURE_MAP.md create mode 100644 docs/ai/PLAYBOOKS.md create mode 100644 govtool/backend/AGENTS.md create mode 100644 govtool/backend/CLAUDE.md create mode 100644 govtool/frontend/AGENTS.md create mode 100644 govtool/frontend/CLAUDE.md create mode 100644 tests/AGENTS.md create mode 100644 tests/CLAUDE.md 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..aa467cc77 --- /dev/null +++ b/docs/ai/ARCHITECTURE_MAP.md @@ -0,0 +1,135 @@ +# 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. Every folder has an index.ts +barrel and new files must be added to it. + +- 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: the single axios instance, with the 500-to-error-page interceptor +- i18n/locales/en.json: all user-facing copy, about 925 lines, the only locale +- 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 all of them. modal, snackbar, pagination and + dataActionsBar are UI plumbing; adaHandle, usersnap and proposalDiscussion are + integrations. + +## 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 + +NestJS. One job: fetch metadata at a URL, canonize the JSON-LD, hash it, validate the +body against a CIP standard. POST /validate is in app.controller.ts, orchestration in +app.service.ts, rules in utils/getStandard.ts, utils/validateCIP108body.ts and +utils/validateMetadataStandard.ts. enums/ValidationError.ts holds the error codes the +frontend switches on, so grep the frontend before renaming one. + +utils/canonizeJSON.ts is duplicated at govtool/frontend/src/utils/canonizeJSON.ts and +must behave identically: the frontend hashes metadata before submission and this +service re-hashes it. Change one, change both. + +## Environment variables + +Frontend. .env.example, src/config/env.ts and docker-entrypoint.sh must all agree. + +- VITE_BASE_URL: backend REST base URL +- VITE_METADATA_API_URL: metadata-validation service +- VITE_PDF_API_URL, VITE_OUTCOMES_API_URL: pillar APIs +- VITE_IPFS_GATEWAY, VITE_IPFS_PROJECT_ID: IPFS reads +- VITE_NETWORK_FLAG: Cardano network id, 0 is testnet +- VITE_APP_ENV, VITE_IS_DEV: environment banners and dev affordances +- VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED, + VITE_IS_GOVERNANCE_OUTCOMES_PILLAR_ENABLED: pillar flags +- VITE_SENTRY_DSN, VITE_GTM_ID, VITE_USERSNAP_SPACE_API_KEY + +Backend. example-config.json, or the same keys as env vars via Conferer: +dbsyncconfig with host, dbname, user, password and port; port 9999; host; +cachedurationseconds; dreplistcachedurationseconds; pinataapijwt; sentrydsn; +sentryenv. + +## 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..b236df74d --- /dev/null +++ b/docs/ai/PLAYBOOKS.md @@ -0,0 +1,164 @@ +# 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. + +Keep the page thin. It wires hooks to organisms; markup belongs in organisms. + +## Frontend: add a component + +The only real decision is the layer. atoms for anything with no GovTool dependencies, +molecules for compositions of atoms that do not fetch, organisms for feature blocks +that may use contexts and query hooks. Add a .stories.tsx if it is reusable, since +Storybook builds 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 + +Real only once it is in all four: + +1. .env.example +2. src/config/env.ts, as MY_VAR: getEnv("VITE_MY_VAR") +3. docker-entrypoint.sh, into the window.__ENV__ block. This is the forgotten one: + skip it and the var is undefined in every container while working locally. +4. docker/docker-compose.yaml, passed into the govtool-frontend service + +## 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 + +All of it lives in src/context/wallet.tsx. Add a buildXCert or +buildXGovernanceAction beside its siblings, hand the certificates to +buildSignSubmitConwayCertTx which owns builder, UTxO selection, change address, +signing and submit, expose it on the context value, and register the result with +pendingTransaction so the UI 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/util.test.ts importing from the barrel, as from "..". +Everything else is co-located, e.g. context/featureFlag.test.tsx. Iterate with +npx vitest run and a path. 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 + +Two edits plus the call site: a field on CacheEnv in src/VVA/Types.hs, its +initialisation in the cacheEnv block in app/Main.hs using newCache for the standard +TTL or newDRepListCache for the long one, then in the handler: + +```haskell +CacheEnv {myThingCache} <- asks vvaCache +cacheRequest myThingCache cacheKey $ do ... +``` + +The key must include every parameter affecting the result. cacheRequest needs +Hashable; compound keys use hash or hashWithSalt. + +## 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. Current locations: + +1. src/types/governanceAction.ts: the GovernanceActionType member, any new field type, + a schema extending SharedGovernanceActionFieldSchema, and that schema added to the + GovernanceActionFieldSchemas union +2. src/consts/governanceAction/fields.ts: the field declaration, meaning component, + labelI18nKey, placeholderI18nKey, tipI18nKey and rules. The doc says + src/constants/governanceActionFields.ts, which does not exist. +3. src/i18n/locales/en.json: every key the schema references +4. Custom validation, if any: a helper in src/utils such as numberValidation.ts or + isValidFormat.ts, used as validate in rules. The doc's + src/utils/govActionValidations does not exist. +5. src/context/featureFlag.tsx: how the type behaves in bootstrap versus full + governance. Skipping this is how a new type ships silently unvotable. +6. src/context/wallet.tsx: a buildXGovernanceAction if it needs a new certificate + +Verify at /create_governance_action, then fix the ops doc, which asks you to. The +schema drives both rendering in CreateGovernanceActionForm.tsx and hashing and +validation in useCreateGovernanceActionForm.ts, so getting it right is most of the +work. Actions must comply with CIP-100 and CIP-108. + +## 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..2e3ce933b --- /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. Config can +also come from env vars using the same keys, since Conferer reads both. 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 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, and its initialisation in + app/Main.hs. +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..4e159727a --- /dev/null +++ b/govtool/frontend/AGENTS.md @@ -0,0 +1,66 @@ +# 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. +- 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_ var touches four files, including docker-entrypoint.sh. Miss it and + it works locally but is undefined in every container. 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 From c0f6c60311a1b3a3685bea6928d02a1c82fccbf8 Mon Sep 17 00:00:00 2001 From: Kushal Acharya Date: Mon, 31 Aug 2026 15:49:50 +0545 Subject: [PATCH 2/3] docs: update md and agent files --- AGENTS.md | 2 +- docs/ai/ARCHITECTURE_MAP.md | 133 ++++++++++++++++++++++++++-------- docs/ai/PLAYBOOKS.md | 139 +++++++++++++++++++++++++----------- govtool/backend/AGENTS.md | 10 +-- govtool/frontend/AGENTS.md | 7 +- 5 files changed, 209 insertions(+), 82 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 98b1aa744..4fb9ef376 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,7 @@ tests/AGENTS.md: E2E, API and load suites 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 +- govtool/metadata-validation: NestJS. Rules for what makes CIP-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. diff --git a/docs/ai/ARCHITECTURE_MAP.md b/docs/ai/ARCHITECTURE_MAP.md index aa467cc77..45c9145a2 100644 --- a/docs/ai/ARCHITECTURE_MAP.md +++ b/docs/ai/ARCHITECTURE_MAP.md @@ -20,8 +20,7 @@ builds with cardano-serialization-lib and the wallet signs. 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. Every folder has an index.ts -barrel and new files must be added to it. +@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. @@ -30,8 +29,10 @@ barrel and new files must be added to it. - 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: the single axios instance, with the 500-to-error-page interceptor -- i18n/locales/en.json: all user-facing copy, about 925 lines, the only locale +- 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. @@ -46,9 +47,8 @@ Contexts in src/context: 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 all of them. modal, snackbar, pagination and - dataActionsBar are UI plumbing; adaHandle, usersnap and proposalDiscussion are - integrations. +- 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 @@ -91,34 +91,105 @@ into SQL and cache the result rather than holding a connection. ## govtool/metadata-validation -NestJS. One job: fetch metadata at a URL, canonize the JSON-LD, hash it, validate the -body against a CIP standard. POST /validate is in app.controller.ts, orchestration in -app.service.ts, rules in utils/getStandard.ts, utils/validateCIP108body.ts and -utils/validateMetadataStandard.ts. enums/ValidationError.ts holds the error codes the -frontend switches on, so grep the frontend before renaming one. +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. -utils/canonizeJSON.ts is duplicated at govtool/frontend/src/utils/canonizeJSON.ts and -must behave identically: the frontend hashes metadata before submission and this -service re-hashes it. Change one, change both. +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. .env.example, src/config/env.ts and docker-entrypoint.sh must all agree. - -- VITE_BASE_URL: backend REST base URL -- VITE_METADATA_API_URL: metadata-validation service -- VITE_PDF_API_URL, VITE_OUTCOMES_API_URL: pillar APIs -- VITE_IPFS_GATEWAY, VITE_IPFS_PROJECT_ID: IPFS reads -- VITE_NETWORK_FLAG: Cardano network id, 0 is testnet -- VITE_APP_ENV, VITE_IS_DEV: environment banners and dev affordances -- VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED, - VITE_IS_GOVERNANCE_OUTCOMES_PILLAR_ENABLED: pillar flags -- VITE_SENTRY_DSN, VITE_GTM_ID, VITE_USERSNAP_SPACE_API_KEY - -Backend. example-config.json, or the same keys as env vars via Conferer: -dbsyncconfig with host, dbname, user, password and port; port 9999; host; -cachedurationseconds; dreplistcachedurationseconds; pinataapijwt; sentrydsn; -sentryenv. +### 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 diff --git a/docs/ai/PLAYBOOKS.md b/docs/ai/PLAYBOOKS.md index b236df74d..aa2dee788 100644 --- a/docs/ai/PLAYBOOKS.md +++ b/docs/ai/PLAYBOOKS.md @@ -29,14 +29,29 @@ useGetProposalQuery, keyed on [key, dRepID, proposalId]. Return a named object l Dashboard element route gets connected chrome, wrapped in PublicRoute redirects when a wallet is connected. -Keep the page thin. It wires hooks to organisms; markup belongs in organisms. +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 -The only real decision is the layer. atoms for anything with no GovTool dependencies, -molecules for compositions of atoms that do not fetch, organisms for feature blocks -that may use contexts and query hooks. Add a .stories.tsx if it is reusable, since -Storybook builds in CI. +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 @@ -46,14 +61,21 @@ t("key", { name }). No hardcoded strings in JSX. ## Frontend: add an environment variable -Real only once it is in all four: +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. This is the forgotten one: - skip it and the var is undefined in every container while working locally. +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 @@ -65,20 +87,34 @@ predicate; exemplar areDRepVoteTotalsDisplayed. ## Frontend: build a new transaction or certificate -All of it lives in src/context/wallet.tsx. Add a buildXCert or -buildXGovernanceAction beside its siblings, hand the certificates to -buildSignSubmitConwayCertTx which owns builder, UTxO selection, change address, -signing and submit, expose it on the context value, and register the result with -pendingTransaction so the UI can poll /transaction/status/:txId. +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/util.test.ts importing from the barrel, as from "..". -Everything else is co-located, e.g. context/featureFlag.test.tsx. Iterate with -npx vitest run and a path. Coverage comes from components, consts, context, hooks, +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 @@ -101,18 +137,18 @@ tests/govtool-backend/test_cases. ## Backend: add caching to an endpoint -Two edits plus the call site: a field on CacheEnv in src/VVA/Types.hs, its -initialisation in the cacheEnv block in app/Main.hs using newCache for the standard -TTL or newDRepListCache for the long one, then in the handler: +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 ... ``` -The key must include every parameter affecting the result. cacheRequest needs -Hashable; compound keys use hash or hashWithSalt. - ## Backend: change a db-sync query Change a SELECT list and you must change the domain function's @@ -124,26 +160,45 @@ 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. Current locations: - -1. src/types/governanceAction.ts: the GovernanceActionType member, any new field type, - a schema extending SharedGovernanceActionFieldSchema, and that schema added to the - GovernanceActionFieldSchemas union -2. src/consts/governanceAction/fields.ts: the field declaration, meaning component, - labelI18nKey, placeholderI18nKey, tipI18nKey and rules. The doc says - src/constants/governanceActionFields.ts, which does not exist. -3. src/i18n/locales/en.json: every key the schema references -4. Custom validation, if any: a helper in src/utils such as numberValidation.ts or - isValidFormat.ts, used as validate in rules. The doc's - src/utils/govActionValidations does not exist. -5. src/context/featureFlag.tsx: how the type behaves in bootstrap versus full - governance. Skipping this is how a new type ships silently unvotable. -6. src/context/wallet.tsx: a buildXGovernanceAction if it needs a new certificate - -Verify at /create_governance_action, then fix the ops doc, which asks you to. The -schema drives both rendering in CreateGovernanceActionForm.tsx and hashing and -validation in useCreateGovernanceActionForm.ts, so getting it right is most of the -work. Actions must comply with CIP-100 and CIP-108. +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 diff --git a/govtool/backend/AGENTS.md b/govtool/backend/AGENTS.md index 2e3ce933b..38edf8193 100644 --- a/govtool/backend/AGENTS.md +++ b/govtool/backend/AGENTS.md @@ -23,13 +23,13 @@ historically fails on openapi3, hence nix plus direnv. stylish-haskell reads 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. Config can -also come from env vars using the same keys, since Conferer reads both. Swagger UI is at /swagger-ui, generated +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 must be listed in vva-be.cabal under extra-source-files. The +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. @@ -40,8 +40,8 @@ from the VVAApi type, declaring /api and / as server bases. 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, and its initialisation in - app/Main.hs. +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. diff --git a/govtool/frontend/AGENTS.md b/govtool/frontend/AGENTS.md index 4e159727a..864e0f321 100644 --- a/govtool/frontend/AGENTS.md +++ b/govtool/frontend/AGENTS.md @@ -59,8 +59,9 @@ import/prefer-default-export. Still errors, and easy to trip: 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. + 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_ var touches four files, including docker-entrypoint.sh. Miss it and - it works locally but is undefined in every container. +- 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. From 4ae87b9fa1b1ccc787e65f53f4b958ea70691196 Mon Sep 17 00:00:00 2001 From: Kushal Acharya Date: Mon, 31 Aug 2026 16:39:50 +0545 Subject: [PATCH 3/3] update CIP-100 in AGENTS.md --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 4fb9ef376..98b1aa744 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,7 @@ tests/AGENTS.md: E2E, API and load suites 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-108/119 off-chain +- 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.