Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/commands/newcomer-tour.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
# SPDX-License-Identifier: LicenseRef-OpenLBM-Docs-1.0
# SPDX-FileCopyrightText: 2026 FutureBuild, Inc. and OpenLBM contributors
description: Get oriented in the Gable codebase — the layout, the 41 backend modules, the connector seam, and the Lit frontend.
description: Get oriented in the Gable codebase. Covers the layout, the 47 backend modules, the connector seam, and the Lit frontend.
argument-hint: [optional: a module, screen, or area you care about]
---

Expand Down
2 changes: 1 addition & 1 deletion .claude/commands/write-a-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ Use the **add-a-test** skill.

Target: $ARGUMENTS

If no target was given, compute the untested-module list first and recommend one by value (invoice tax/rounding, account.PostTransaction, the ERP/portal money boundary, or frontend formatCents). Read the source before writing. Prove the test can fail. House rules apply: no tautological tests, characterization tests are labelled, a discovered bug gets an issue rather than a `want:` that encodes it.
If no target was given, compute the untested-module list first and recommend one by value (invoice tax/rounding, the account package's posting methods, the ERP/portal money boundary, or frontend formatCents). Read the source before writing. Prove the test can fail. House rules apply: no tautological tests, characterization tests are labelled, a discovered bug gets an issue rather than a `want:` that encodes it.
8 changes: 5 additions & 3 deletions .claude/skills/add-a-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ already covered it:
1. **`invoice`** — tax resolution and rounding. `invoice.CreateInvoice` resolves the rate from
the branch (`locations.default_tax_rate`) and falls back to `invoice.DefaultTaxRate =
0.0825`. Both paths, plus rounding, are worth pinning down.
2. **`account`** — `PostTransaction` is the *single writer* to the AR subledger and
`customers.balance_due`. It should be the best-tested function in the repo.
2. **`account`**: the package is the *single writer* to the AR subledger and
`customers.balance_due`, through its posting methods (`PostInvoice`, `PostCreditMemo`,
`RecordPayment`, `Apply`, `WriteOff`, and the voids, refunds and reversals). They should be
among the best-tested code in the repo.
3. **`customer`** — the credit-limit gate feeds order blocking.
4. **`quote`** — line totals, discounts, expiry.
5. **`portal`** — the dollars-vs-cents boundary with the ERP side (see the money table in
Expand All @@ -63,7 +65,7 @@ Then study a module that already tests well — copy its idiom rather than inven

```bash
sed -n '1,80p' core/internal/tax/service_test.go # mock repo + table-driven
ls core/internal/purchase_order/*_test.go # 6 test files, good examples
ls core/internal/purchase_order/*_test.go # 8 test files, good examples
ls core/internal/pricing/*_test.go core/internal/gl/*_test.go
```

Expand Down
32 changes: 19 additions & 13 deletions .claude/skills/check-my-contribution/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ make help
| CI job | Gate? | What it runs |
|---|---|---|
| **Backend** | ✅ merge gate | `go vet ./...`, `go build ./...`, `go run ./cmd/migrate`, `go test -race -coverprofile=… ./...` |
| **Frontend** | ✅ merge gate | `npm ci`, `npx tsc --noEmit`, `npm run lint`, tests with coverage, `npm run build` |
| **Frontend** | ✅ merge gate | `npm ci` (from `web/`), `npm run typecheck`, `npm run lint`, tests with coverage, `npm run build` |
| **App Shape** | ✅ merge gate | `bash scripts/check-shape.sh`: required paths, manifest fields, CLAUDE.md import, JSON validity, no stray root artefacts |
| **Tauri shell** | ✅ merge gate | builds the front door, runs the Tauri unit tests, builds a Linux debug bundle, smoke-tests it under a virtual display |
| **License (REUSE)** | ✅ merge gate | `reuse lint` (report) then `python3 .github/scripts/reuse_gate.py` (the gate) |
| **Docker Build** | ✅ merge gate | builds `core/Dockerfile` and `web/apps/desk/Dockerfile` |
| **Playwright (E2E)** | ✅ merge gate | full stack built (dev auth), Postgres migrated + seeded, `core serve` with `AUTH_MODE=dev`, nginx, then `npx playwright test` |
| **Local stack** | ✅ merge gate | `make up` (the same compose file a developer uses), then `make smoke` |
| **Docker Build** | ✅ merge gate | builds `core/Dockerfile` and `web/Dockerfile` (both bundles, one nginx) |
| **Vulnerabilities** | ⚠️ **advisory only** | pinned `govulncheck` — `continue-on-error`, also runs nightly |

`govulncheck` is deliberately **not** a merge gate: an upstream CVE landing overnight must not
Expand Down Expand Up @@ -75,12 +79,12 @@ make test # cd core && go test -race ./... (needs Postgres)
make test-short # cd core && go test -short ./... (no Postgres — DB tests self-skip)
make cover # coverage profile + function summary

# Frontend
make fe-install # cd web/apps/desk && npm ci
make fe-typecheck # cd web/apps/desk && npx tsc --noEmit
make fe-lint # cd web/apps/desk && npm run lint
make fe-test # cd web/apps/desk && npm run test -- --run
make fe-build # cd web/apps/desk && npm run build
# Frontend (web/ is one npm workspace: apps/*, packages/*)
make fe-install # cd web && npm ci
make fe-typecheck # cd web && npm run typecheck
make fe-lint # cd web && npm run lint
make fe-test # cd web && npm run test
make fe-build # cd web && npm run build

# Licensing
make license-check # reuse lint (report) + .github/scripts/reuse_gate.py (the gate)
Expand Down Expand Up @@ -130,8 +134,10 @@ Rotate the credential first, then rewrite the branch (`git reset` + recommit, or
`git rebase -i`) before pushing.

Also confirm you have not introduced `AUTH_MODE=dev` into any config that could reach a public
host. It is fine in `.do/app-demo.yaml` and `.do/app-staging.yaml` (demo data only); never
anywhere else. See [`SECURITY.md`](../../../SECURITY.md).
host. The current `.do/app-demo.yaml` and `.do/app-staging.yaml` both run with
`AUTH_MODE=production` (with placeholder `JWKS_URL`); leave them that way. `AUTH_MODE=dev` is
for local development only, never to be committed anywhere it could be picked up by a deploy.
See [`SECURITY.md`](../../../SECURITY.md).

## 4 · SPDX header must match the directory

Expand Down Expand Up @@ -212,9 +218,9 @@ From [`AGENTS.md`](../../../AGENTS.md) § Pre-Flight Checks — verify each that
additive and reversible; applies from empty.
- [ ] **New endpoint**: under the correct prefix (`/api/v1`, `/api/portal/v1`,
`/api/integration`, `/api/v1/a2a`) **and** wired into a `RegisterRoutes` call in
`core/cmd/server/main.go`. An endpoint that isn't wired in silently doesn't exist.
- [ ] **New public path**: if it must skip auth, it has to be in the whitelist in
`core/cmd/server/main.go` — and adding one deserves a security review.
`core/internal/app/serve/serve.go`. An endpoint that isn't wired in silently doesn't exist.
- [ ] **New public path**: if it must skip auth, it has to be in the `publicAPIPaths` whitelist
in `core/internal/app/serve/serve.go`, and adding one deserves a security review.
- [ ] **Money on ERP pages**: rendered with `formatCents()` from `web/apps/desk/src/lib/utils.ts`, never
`.toFixed(2)` on a cents field. Portal pages receive dollars and must not use it.
- [ ] **UI**: design tokens from `web/apps/desk/tailwind.config.js`, no hardcoded colours; JetBrains
Expand Down
7 changes: 4 additions & 3 deletions .claude/skills/describe-a-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Before the interview, spend a few minutes finding out what Gable does today, so
"what's different about yours?" instead of "how does quoting work?".

```bash
ls core/internal/ # 41 domain modules — find the closest one
ls core/internal/ # 47 domain modules: find the closest one
ls web/apps/desk/src/pages/ # the screens that exist today
grep -rn "status" core/internal/order/model.go | head -30
```
Expand All @@ -41,7 +41,8 @@ Useful orientation reads:
Two known facts worth having in hand:

- Orders today flow `DRAFT → CONFIRMED → FULFILLED`, plus `ON_HOLD` (a valid `orders.status`
as of migration 071). There is **no** pickup/will-call path and **no** `PICKED` step.
as of migration 071), `BACKORDERED` and `CANCELLED`. A will-call order is `delivery_type` `pickup`, fulfilled
with `picked_up_by` (ADR 0005 section 5.5); there is **no** `PICKED` step.
- Fulfilling an order posts one tax-inclusive invoice plus a balanced
`DR Accounts Receivable / CR Sales Revenue` GL entry and an AR subledger debit, in one
transaction. Anything that creates or cancels a sale has to respect that.
Expand Down Expand Up @@ -178,7 +179,7 @@ What number tells the owner this is working.
- [ ] **Money/data integrity** — single writer, one transaction, no partial writes
- [ ] **Migrations additive + reversible** — forward and down; idempotent
- [ ] **No secrets committed**
- [ ] **Boot wiring** — registered via `RegisterRoutes` in `core/cmd/server/main.go`
- [ ] **Boot wiring**: registered via `RegisterRoutes` in `core/internal/app/serve/serve.go`
- [ ] **Rollback plan**
- [ ] **Observability** — what gets logged/measured; CI actually runs the new tests

Expand Down
21 changes: 11 additions & 10 deletions .claude/skills/explain-this-code/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: LicenseRef-OpenLBM-Docs-1.0
# SPDX-FileCopyrightText: 2026 FutureBuild, Inc. and OpenLBM contributors
name: explain-this-code
description: Orient a newcomer in the Gable codebase — the modular-monolith layout, the 41 domain modules under core/internal, the core/pkg/apps connector seam, and the Lit web-component frontend — and teach them how to find things themselves. Use when the user says "where do I start", "how does this codebase work", "give me a tour", "I'm new here", "explain the architecture", "where does the order code live", "how does the frontend work", "what is core/pkg/apps", "how do I find the code for the delivery screen", "/newcomer-tour", or asks what any module or file does.
description: Orient a newcomer in the Gable codebase. Covers the modular-monolith layout, the 47 domain modules under core/internal, the core/pkg/apps connector seam, and the Lit web-component frontend. Teaches them how to find things themselves. Use when the user says "where do I start", "how does this codebase work", "give me a tour", "I'm new here", "explain the architecture", "where does the order code live", "how does the frontend work", "what is core/pkg/apps", "how do I find the code for the delivery screen", "/newcomer-tour", or asks what any module or file does.
---

# explain-this-code — teach the map, not just the answer
Expand All @@ -23,12 +23,12 @@ Give this once, at the start, tailored to what they care about.

```
gable/
core/ Go 1.26 — one binary, ~41 domain modules (the "modular monolith")
core/ Go 1.26: one binary, 47 domain modules (the "modular monolith")
cmd/core/ the one binary: `core serve` (wires every module
repo→service→handler→routes), `core worker` (background
jobs), `core migrate` (applies core/migrations/*.sql in
order), `core seed` (the demo dataset)
internal/ the 41 modules. THIS is where business logic lives.
internal/ the 47 modules. THIS is where business logic lives.
pkg/ shared plumbing (database, middleware, audit, httputil, metrics…)
pkg/apps/ the connector seam — the stable surface third parties plug into
migrations/ plain numbered SQL: 001_…, 002_…
Expand Down Expand Up @@ -82,7 +82,7 @@ If a route isn't in `routes.ts`, it belongs to a **converted app** and is declar
### A backend behaviour

```bash
ls core/internal/ # the 41 modules
ls core/internal/ # the 47 domain modules and seven support directories
grep -rn "func (s \*Service)" core/internal/order/service.go | head -20
```

Expand Down Expand Up @@ -167,8 +167,9 @@ Conversion recipe and phases: [`docs/modularization-blueprint.md`](../../../docs
`@property({ attribute: 'route-id' })`.
- **State:** `@state()` for internal, `@property()` for external. Cross-component state lives
in framework-agnostic singleton services under `web/apps/desk/src/services/`.
- **Never hardcode a colour.** Tokens are in `web/apps/desk/tailwind.config.js`. JetBrains Mono for all
numbers, SKUs, prices, and dimensions; Inter for body text.
- **Never hardcode a colour.** Tokens are in `web/apps/desk/tailwind.config.js` (theme defined
in `web/packages/design-system/tailwind/theme.js`). JetBrains Mono for all numbers, SKUs,
prices, and dimensions; Outfit for body text.
- **Money:** `formatCents()` in `web/apps/desk/src/lib/utils.ts` for ERP surfaces. Portal surfaces get
dollars from the API and format directly. Mixing them renders `$738.87` as `$73,887.00`.

Expand All @@ -178,10 +179,10 @@ Conversion recipe and phases: [`docs/modularization-blueprint.md`](../../../docs

- **Router is stdlib `net/http.ServeMux`** (Go 1.22+ pattern matching), not Chi or gorilla.
- **Default Postgres port is 5434**, matching `docker-compose.yml` — not 5432.
- **Money conventions are not uniform.** ERP orders/invoices and the `account` module use
`int64` cents; portal, quotes, and DailyTill use `float64` dollars — on some of the same
columns. `AGENTS.md` § "Money convention is not uniform across modules" has the table. Read
it before touching anything financial.
- **Money conventions are not uniform.** ERP orders/invoices, the `account` module, quotes,
and customers wire fields as `int64` cents; portal and DailyTill use `float64` dollars on
some of the same columns. `AGENTS.md` § "Money convention is not uniform across modules"
has the table. Read it before touching anything financial.
- **AR balance is derived, not read.** Compute from open invoices
(`invoice.OpenInvoiceStatuses`), not from `customers.balance_due` — that column is a
secondary subledger figure and can drift.
Expand Down
7 changes: 3 additions & 4 deletions .claude/skills/improve-docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,9 @@ These are real inconsistencies that a docs contributor can legitimately fix:
- **Money conventions.** `AGENTS.md` says money-as-cents is *the target, not current reality*
— ERP orders/invoices and `account` use `int64` cents while portal, quotes, and DailyTill
use `float64` dollars. Any doc that states "money is cents" flatly is overstating it.
- **Branch target.** `CONTRIBUTING.md`, the PR template, and `README.md` all say contributions
target **`staging`**. `AGENTS.md`'s branch table still says "Community PRs target
[`community`]". That's stale — but `AGENTS.md` is a high-traffic file, so fix it in its own
small PR with the evidence, don't bundle it.
- **Branch target.** `CONTRIBUTING.md`, the PR template, `README.md` and `AGENTS.md` all say
contributions target **`staging`** (with `main` as the fast-forward target). Older notes
referred to `community`; that branch model is retired.
- **Coverage claims.** Check them against reality rather than repeating them:
`find web/apps/desk/src -name '*.test.ts' | wc -l` and, for the backend,
`for d in core/internal/*/; do [ -z "$(ls $d*_test.go 2>/dev/null)" ] && echo "$d"; done`.
Expand Down
11 changes: 8 additions & 3 deletions .claude/skills/licensing-check/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,14 @@ specifications — that's an IP problem this project has explicitly cleaned up o
## 5 · If the mapping itself looks wrong

If a file's header disagrees with `LICENSE-MAP.md`/`REUSE.toml`, or a directory isn't covered
at all, that's a real finding worth reporting. Note that `REUSE.toml` currently annotates
`core/**`, `web/apps/desk/**`, and `docs/**` — root-level markdown and `.claude/**` are **not**
covered by annotations, which is why files in those places carry their headers inline.
at all, that's a real finding worth reporting. Note that `REUSE.toml` annotates the major
source components (`core/internal/**`, `core/pkg/**` with the `core/pkg/apps/**` carve-out,
`core/cmd/**`, `core/migrations/**`, `web/apps/**`, `web/packages/**`, `tauri/**`, `docs/**`)
plus several root-level paths via globs (`*.md`, `NOTICE`, `.github/**`, `.do/**`,
`.claude/**`, `Makefile`, `scripts/**`, `manifest.yaml`, `docker-compose.yml`,
`core/go.mod`, `core/go.sum`, `core/Dockerfile`, `core/scripts/**`). The inline SPDX
headers you see in `.claude/skills/*/SKILL.md` and in `CLAUDE.md`/`AGENTS.md` are kept for
clarity even where an annotation would suffice; the REUSE gate reads both.

Do **not** edit `REUSE.toml` or `LICENSE-MAP.md` to "fix" it yourself — those are maintainer-
owned. Use **`report-an-issue`** and include the exact file, the header it has, and the header
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/report-an-issue/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ The surface trees are:
| "the till", "the front counter", "POS" | `/pos` | `web/apps/desk/src/pages/pos/` |

If the problem is in data rather than display, the backend module is under
`core/internal/<module>/` — there are 41 of them. `ls core/internal/` and pick the
`core/internal/<module>/`; there are 47 domain modules. `ls core/internal/` and pick the
obvious one (`order`, `invoice`, `delivery`, `inventory`, `pricing`, `payment`, `pos`…).

Record the **directory**, not just a guess: the issue template asks for it, and Gable is
Expand Down
Loading