# Contributing to FlintTrade FlintTrade is an AGPL-3.0, open-source self-hosted trading software project with a native broker-gateway contract and a local sandbox. The repository is built in the open so developers can inspect, test, and improve the code together. Whether you're fixing a typo, shipping a new broker adapter, translating the UI into Hindi, or rewriting an entire widget — you're welcome here. This guide tells you everything you need to start. ## Before you start Please read these first. They're short: - [`code-of-conduct.md`](code-of-conduct.md) — how we behave with one another. - [`security.md`](security.md) — how to report a vulnerability privately. - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — the 10-minute tour of how FlintTrade fits together. If you're filing a security issue, please do **not** open a public GitHub issue. Use the channels in `security.md`. ## Development setup FlintTrade is developed on **any operating system** — Linux, macOS, and Windows are all first-class. There is no single "blessed" machine and no develop-here/test-there pipeline: CI and the contributor pool validate every platform. Pick whatever you already run. ### Prerequisites (any OS) | Tool | Version | How we manage it | |---|---|---| | **Python** | `>=3.14` | [`uv`](https://docs.astral.sh/uv/) — one fast installer for the interpreter and all Python deps. | | **Node.js** | `>=22.23.4` (we develop against 24) | [`pnpm`](https://pnpm.io/) — the repo pins the package manager. | | **Rust** | stable (latest) | [`rustup`](https://rustup.rs/) — only needed to build the `core/ticks` PyO3 tick engine. | Those floors are not maintained here. `[requirements]` in [`flint.toml`](flint.toml) is the single source of truth for every version floor and target, and `tests/test_minimum_requirements_single_source.py` fails — naming the file and the disagreeing value — when a tracked surface drifts from it. `uv` and `pnpm` install cleanly on Linux, macOS, and Windows; follow each tool's own cross-platform instructions. The repo pins **pnpm 10.34.5** in the `packageManager` field of the root `package.json`, integrity-hashed, and `packageManagerStrictVersion` in `pnpm-workspace.yaml` turns a mismatch into an error rather than a warning. Any of these gets you that version: ```bash npm install -g pnpm@10.34.5 # explicit install, works on every Node version pnpm --version # must print 10.34.5 ``` ```bash corepack enable # optional: only if your Node still bundles Corepack ``` Corepack is an accelerator, not a prerequisite. Node unbundled it in 25.0.0, so on a current Node there is no `corepack` binary to enable and the command simply fails — install pnpm directly instead. `scripts/ft.py` already resolves whichever you have, trying Corepack, then a pinned `pnpm` on `PATH`, then `npx --yes pnpm@10.34.5`. > All pnpm settings live in `pnpm-workspace.yaml`. From pnpm 10 onwards `.npmrc` > is read for registry and auth keys only, so a resolution setting parked there > silently does nothing — that is why the repo carries no `.npmrc` at the root or > in `packages/apps/terminal`. ### Install ```bash git clone https://github.com/navaneeshnagarajan/FlintTrade.git cd FlintTrade uv sync # Python interpreter + all Python package deps pnpm install # JS/TS workspace deps (terminal, site, …) ``` Do not copy `.env.example` as a normal native-app setup step. Use the app's Setup and Settings UI for native brokers, LLM, notification, and storage configuration. Copy `.env.example` to `.env` only for advanced Docker/systemd server deployments or contributor experiments that deliberately exercise env fallbacks. To build the Rust `ticks` tick engine (optional unless you touch it): ```bash cd packages/core/ticks cargo build --release ``` User preferences live in `workspace.json` under your FlintTrade home, generated from defaults on first run. A sanitised, fully-commented reference lives at [`workspace.example.json`](workspace.example.json) — secrets there are shown as `_ref` placeholders, never real values (mirror that discipline in your own). Detailed, platform-specific walkthroughs live under [`docs/setup/`](docs/setup/): - [`docs/setup/windows.md`](docs/setup/windows.md) - [`docs/setup/macos.md`](docs/setup/macos.md) - [`docs/setup/linux.md`](docs/setup/linux.md) - [`docs/setup/QUICKSTART.md`](docs/setup/QUICKSTART.md) — the short version ## How to run tests FlintTrade has a large Python and TypeScript test suite. `python scripts/ft.py ` is the cross-platform runner — it needs no make and no bash, and behaves identically on Windows, macOS and Linux. `make ` is the POSIX alias for the same targets. ```bash python scripts/ft.py check # affected checks while editing python scripts/ft.py check --full # exhaustive local gate before pushing python scripts/ft.py test # all pytest tests python scripts/ft.py test-fast # stop on first failure python -m pytest packages/core/core/tests/ -v --import-mode=importlib # single package pnpm --filter @flinttrade/terminal test # all Vitest tests ``` To run a single file or a single test by name: ```bash python -m pytest packages/core/core/tests/test_foo.py -v --import-mode=importlib python -m pytest packages/core/core/tests/test_foo.py::test_name -v --import-mode=importlib cd packages/apps/terminal npx vitest run src/widgets/orderpad/OrderPad.test.tsx npx vitest run -t "places a market order" ``` `--import-mode=importlib` is required when you invoke `pytest` directly (the flat-package layout needs it). The `scripts/ft.py` and `make` targets set it for you, so prefer `python scripts/ft.py test` unless you're iterating on a single file. Both test commands accept paths and pytest flags, with at most four workers by default. Use `--workers 0` for serial debugging or `check --dry-run` to inspect the affected plan. See [`docs/CI.md`](docs/CI.md) for selection and required checks. Lint and type-checks are part of CI too — run them locally before pushing: ```bash python scripts/ft.py lint # ruff (Python) + the terminal's react-hooks gate pnpm --filter @flinttrade/terminal typecheck # tsc --noEmit, strict mode ``` `ft.py lint` runs both languages: `ruff check packages/ tests/`, then the terminal's `lint` script (`eslint src --max-warnings=0`, enforcing `react-hooks/rules-of-hooks` and `react-hooks/exhaustive-deps`) — the same command CI runs in `node-core-tests`. Both halves always run, so one report cannot hide the other, and the first non-zero exit code is returned. A missing toolchain is reported and skipped rather than failed: ruff is optional for a terminal-only change exactly as Node is for a Python-only one. The hooks half is skipped with a hint if `packages/apps/terminal/node_modules` is absent, so run `pnpm install --frozen-lockfile` first if you want it. ## How to build ```bash # Terminal (React + Vite) pnpm --filter @flinttrade/terminal build ``` ```bash # Rust ticks tick engine (PyO3 bindings) cd packages/core/ticks cargo build --release ``` The terminal build runs `tsc --noEmit` followed by `vite build`. A clean build is required before any commit that touches `packages/apps/terminal/`. ## Continuous integration CI runs on GitHub Actions. The full policy and per-workflow breakdown is in [`docs/CI.md`](docs/CI.md) — read it before adding or editing any workflow. The guardrails keep runner usage bounded and predictable. Keep within them: - **Per-push CI is Linux-only.** The essential quality gate (tests, lint, type-check) runs on Ubuntu for every push and non-draft PR. Contributors are multi-OS, so we *do* keep cross-platform coverage — but it does not run on push or pull-request. - **macOS and Windows runners run weekly (cron) or on demand only** — `nightly-cross-platform.yml` plus `workflow_dispatch`, never on push / PR / daily. macOS minutes bill at 10x and Windows at 2x, so frequent multi-platform matrices are too expensive for routine CI. - **Every job sets `timeout-minutes`.** A job with no timeout can consume the six-hour runner default if it hangs. Sensible values: lint ~10, tests ~30, cross-platform ~45. - **Least-privilege permissions.** Workflows default to `contents: read`; a job adds only what it needs (e.g. `pull-requests: write` solely where it comments). - **`concurrency` with `cancel-in-progress: true`** on push/PR workflows so a follow-up push cancels the superseded run. - **`test.yml` does not skip on documentation edits.** `python-tests`, `node-core-tests` and `secrets-check` always run (they include the guards that police docs). The expensive lanes — widget shards, Rust ticks, Electron desktop — gate on the `changed-surfaces` classifier. See [`docs/CI.md`](docs/CI.md). If you find yourself adding a daily cron or putting macOS/Windows on `push`, stop — that is the regression these guardrails prevent. ## Branch and commit conventions FlintTrade follows [Conventional Commits](https://www.conventionalcommits.org/). The shape is: ``` type(scope): short imperative summary Optional body — explain the WHY, not just the WHAT. Wrap at 72 characters. Reference issues with #123. ``` Allowed types and examples: | Type | When to use it | Example | |---|---|---| | `feat` | New user-visible behaviour | `feat(terminal): add sector rotation widget` | | `fix` | Bug fix | `fix(engine): rate limiter not resetting after market close` | | `test` | Tests added or refactored, no behaviour change | `test(screener): cover option chain Greeks edge cases` | | `chore` | Tooling, dependencies, repo housekeeping | `chore: bump vite to 6.4.2` | | `refactor` | Code restructure with no behaviour change | `refactor(ditto): extract margin calculator into its own module` | | `perf` | Performance improvement | `perf(ticks): avoid allocating per-tick in hot path` | | `ci` | CI configuration only | `ci: shard widget tests across four runners` | Scopes are package names (`terminal`, `engine`, `gateway`, `screener`, …) or focus areas (`docs`, `ci`, `repo`). **Pre-1.0 branching:** while FlintTrade is pre-1.0, contributors commit to `main` directly for trivial fixes (typos, doc tweaks, one-line bug fixes). Pull requests are welcome at any time and encouraged for anything larger than a tiny patch. After v1.0.0 this changes — feature branches and pull requests become mandatory. Never use `git add -A` or `git add .`. Stage the files you actually changed. **Never commit personal infrastructure or secrets.** This means no IP addresses, hostnames, VPN configs or endpoints, broker account names or IDs, fund amounts, order IDs, API keys, or `.env`. FlintTrade is self-hosted open-source software: anything that points at *your* machine, network, or accounts stays out of the repo. Use placeholders (``, ``) in examples, keep real values in your private, gitignored `.env` only for dev/server fallback paths, and store secrets as keyring/env `_ref` references — never plaintext. The `secrets-check` CI job is an inline two-pattern grep (not gitleaks) — a backstop, not a substitute for not staging the file. There is no tracked pre-commit secret hook. ## Pull request flow 1. **Fork** the repository on GitHub. 2. **Create a branch** from `main`. Name it after what it does: `feat/terminal-sector-rotation`, `fix/engine-rate-limit-reset`. 3. **Make your change.** Keep commits focused; one logical change per commit. 4. **Run tests locally** before pushing. CI will catch failures but it's faster to find them yourself. 5. **Push** your branch and **open a pull request** against `main`. 6. **Fill in the checklist** in the PR template (tests added, docs updated, conventional commit format). 7. **Address reviewer feedback.** Push follow-up commits rather than force-pushing — it makes review diffs easier to read. 8. **Merge.** Maintainers will squash and merge once CI is green and review is approved. Tiny doc fixes (typos, broken links) can skip the PR for now and go straight to `main` — but only while we're pre-1.0. ## Issue labels | Label | Meaning | | --- | --- | | `needs-triage` | Needs evaluation | | `needs-info` | Waiting on the reporter | | `ready-for-agent` | Specified enough for an automated change | | `ready-for-human` | Needs a human implementation | | `wontfix` | Will not be actioned | ## Code style and lint ### Python - Follow PEP 8. Lint with `ruff` — `python scripts/ft.py lint` (POSIX alias: `make lint`) must be clean before commit. That target lints the terminal too; see [TypeScript and React](#typescript-and-react) below. - Type hints on every public function and class attribute. - Google-style docstrings (`Args:`, `Returns:`, `Raises:`). - Absolute imports for anything cross-package or top-level (`flinttrade_....`). Intra-package relative imports (`from .sibling import x`) are fine. - The floor is Python 3.14 (`python_requires` in `flint.toml`), and CI runs that same series. Write to the floor — `list[int]` and `X | None` — and do not assume an older interpreter is executing. ### TypeScript and React - Strict mode is on and stays on. No `any`. No `@ts-ignore`. No `@ts-expect-error` without an issue link in the comment. - All new code lives in `.ts` or `.tsx`. No new `.js` or `.jsx` files. - Functional components and hooks only. - Use `shadcn/ui` components and `lucide-react` icons. No raw HTML `