techbro 0.0.0-stage → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +453 -2
- package/assets/baseline-rules.yml +60 -0
- package/assets/caller-workflow.yml +26 -0
- package/assets/review-action.yml +27 -0
- package/assets/review-config.json +4 -0
- package/assets/review-workflow.yml +443 -0
- package/assets/review.mjs +721 -0
- package/bin/techbro.js +9 -0
- package/package.json +23 -4
- package/src/cli.js +690 -0
- package/src/cluster.js +134 -0
- package/src/config.js +35 -0
- package/src/doctor.js +125 -0
- package/src/github.js +152 -0
- package/src/init.js +167 -0
- package/src/kube-project.js +468 -0
- package/src/kube.js +317 -0
- package/src/managed.js +63 -0
- package/src/project.js +202 -0
- package/src/review.js +165 -0
- package/src/secret.js +80 -0
- package/src/setup.js +91 -0
- package/src/team-workflow.js +106 -0
- package/src/team.js +145 -0
- package/src/templates.js +44 -0
- package/src/upgrade.js +94 -0
- package/templates/_default/.agents/skills/README.md +36 -0
- package/templates/_default/.agents/skills/database-migrations/SKILL.md +72 -0
- package/templates/_default/.agents/skills/frontend-feature/SKILL.md +65 -0
- package/templates/_default/.agents/skills/release-safe-change/SKILL.md +75 -0
- package/templates/_default/.agents/skills/rust-backend-feature/SKILL.md +68 -0
- package/templates/_default/.editorconfig +15 -0
- package/templates/_default/.env.example +9 -0
- package/templates/_default/.github/workflows/ci.yml +60 -0
- package/templates/_default/.nvmrc +1 -0
- package/templates/_default/Cargo.lock +1834 -0
- package/templates/_default/Cargo.toml +3 -0
- package/templates/_default/README.md +257 -0
- package/templates/_default/TEMPLATE.md +29 -0
- package/templates/_default/_dockerignore +8 -0
- package/templates/_default/_gitignore +8 -0
- package/templates/_default/apps/backend/Cargo.toml +22 -0
- package/templates/_default/apps/backend/Dockerfile +34 -0
- package/templates/_default/apps/backend/README.md +13 -0
- package/templates/_default/apps/backend/src/config.rs +111 -0
- package/templates/_default/apps/backend/src/controllers/health.rs +36 -0
- package/templates/_default/apps/backend/src/controllers/items.rs +79 -0
- package/templates/_default/apps/backend/src/controllers/mod.rs +2 -0
- package/templates/_default/apps/backend/src/error.rs +115 -0
- package/templates/_default/apps/backend/src/lib.rs +7 -0
- package/templates/_default/apps/backend/src/main.rs +71 -0
- package/templates/_default/apps/backend/src/models/item.rs +94 -0
- package/templates/_default/apps/backend/src/models/mod.rs +1 -0
- package/templates/_default/apps/backend/src/routes.rs +94 -0
- package/templates/_default/apps/backend/src/state.rs +12 -0
- package/templates/_default/apps/backend/src/views/items.rs +18 -0
- package/templates/_default/apps/backend/src/views/mod.rs +1 -0
- package/templates/_default/apps/backend/tests/api.rs +234 -0
- package/templates/_default/apps/frontend/.env.example +2 -0
- package/templates/_default/apps/frontend/.prettierignore +3 -0
- package/templates/_default/apps/frontend/.prettierrc +1 -0
- package/templates/_default/apps/frontend/Dockerfile +15 -0
- package/templates/_default/apps/frontend/README.md +39 -0
- package/templates/_default/apps/frontend/_dockerignore +3 -0
- package/templates/_default/apps/frontend/_gitignore +24 -0
- package/templates/_default/apps/frontend/components.json +25 -0
- package/templates/_default/apps/frontend/eslint.config.js +37 -0
- package/templates/_default/apps/frontend/index.html +13 -0
- package/templates/_default/apps/frontend/jsconfig.json +7 -0
- package/templates/_default/apps/frontend/nginx.conf +30 -0
- package/templates/_default/apps/frontend/package-lock.json +4338 -0
- package/templates/_default/apps/frontend/package.json +49 -0
- package/templates/_default/apps/frontend/public/favicon.svg +1 -0
- package/templates/_default/apps/frontend/src/App.jsx +11 -0
- package/templates/_default/apps/frontend/src/App.test.jsx +40 -0
- package/templates/_default/apps/frontend/src/api/client.js +50 -0
- package/templates/_default/apps/frontend/src/api/client.test.js +70 -0
- package/templates/_default/apps/frontend/src/api/items.js +26 -0
- package/templates/_default/apps/frontend/src/api/system.js +11 -0
- package/templates/_default/apps/frontend/src/components/ItemList.jsx +20 -0
- package/templates/_default/apps/frontend/src/components/ItemList.test.jsx +18 -0
- package/templates/_default/apps/frontend/src/components/Layout.jsx +35 -0
- package/templates/_default/apps/frontend/src/components/StatusBadge.jsx +22 -0
- package/templates/_default/apps/frontend/src/components/StepList.jsx +19 -0
- package/templates/_default/apps/frontend/src/components/ui/button.jsx +52 -0
- package/templates/_default/apps/frontend/src/components/ui/card.jsx +83 -0
- package/templates/_default/apps/frontend/src/components/ui/input.jsx +19 -0
- package/templates/_default/apps/frontend/src/hooks/useBackendStatus.js +33 -0
- package/templates/_default/apps/frontend/src/hooks/useItems.js +47 -0
- package/templates/_default/apps/frontend/src/index.css +130 -0
- package/templates/_default/apps/frontend/src/lib/config.js +2 -0
- package/templates/_default/apps/frontend/src/lib/utils.js +6 -0
- package/templates/_default/apps/frontend/src/main.jsx +11 -0
- package/templates/_default/apps/frontend/src/models/items.js +24 -0
- package/templates/_default/apps/frontend/src/models/items.test.js +24 -0
- package/templates/_default/apps/frontend/src/pages/ItemsPage.jsx +41 -0
- package/templates/_default/apps/frontend/src/pages/WelcomePage.jsx +156 -0
- package/templates/_default/apps/frontend/src/pages/WelcomePage.test.jsx +55 -0
- package/templates/_default/apps/frontend/src/routes.jsx +17 -0
- package/templates/_default/apps/frontend/src/shadcn.css +655 -0
- package/templates/_default/apps/frontend/src/test/setup.js +1 -0
- package/templates/_default/apps/frontend/vite.config.js +25 -0
- package/templates/_default/database/Dockerfile +9 -0
- package/templates/_default/database/README.md +15 -0
- package/templates/_default/database/migrations/postgres/20261008120000_create_items.sql +5 -0
- package/templates/_default/database/migrations/sqlite/20261008120000_create_items.sql +5 -0
- package/templates/_default/docker-compose.prod.yml +53 -0
- package/templates/_default/docker-compose.yml +69 -0
- package/templates/_default/docs/api/README.md +41 -0
- package/templates/_default/docs/api/openapi.yaml +152 -0
- package/templates/_default/docs/decisions/0001-adopt-engineering-convention.md +13 -0
- package/templates/_default/docs/decisions/README.md +6 -0
- package/templates/_default/docs/deployment.md +46 -0
- package/templates/_default/docs/docker.md +63 -0
- package/templates/_default/docs/getting-started.md +214 -0
- package/templates/_default/package.json +15 -0
- package/templates/_default/rust-toolchain.toml +3 -0
- package/templates/_default/scripts/check +10 -0
- package/templates/_default/scripts/db-migrate +25 -0
- package/templates/_default/scripts/install-hooks +5 -0
- package/templates/_default/scripts/pre-push +3 -0
- package/templates/_default/scripts/setup +26 -0
package/src/upgrade.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { parseRepoUrl } from "./github.js";
|
|
2
|
+
import { MANAGED_VERSION, REVIEW_WORKFLOW, classifyManaged, managedFiles } from "./managed.js";
|
|
3
|
+
|
|
4
|
+
const HELPER = ".github/techbro/review.mjs";
|
|
5
|
+
const ACTION = ".github/actions/techbro/action.yml";
|
|
6
|
+
const TRIO = [REVIEW_WORKFLOW, ACTION, HELPER];
|
|
7
|
+
|
|
8
|
+
export const upgradeBranch = () => `techbro/upgrade-v${MANAGED_VERSION}`;
|
|
9
|
+
const SCOPE_HINT =
|
|
10
|
+
"the token needs push access, and the `workflow` scope (classic token) or Workflows: write (fine-grained) to change .github/workflows; protected branches are never bypassed";
|
|
11
|
+
|
|
12
|
+
export async function upgradeTeam({ teamName, config, client, dryRun = false }) {
|
|
13
|
+
const name = teamName ?? config.currentTeam;
|
|
14
|
+
const team = name && config.teams[name];
|
|
15
|
+
if (!team) throw new Error(name ? `unknown team "${name}"` : "no current team; run techbro team create or team join first");
|
|
16
|
+
const { owner: o, repo: r } = parseRepoUrl(team.url);
|
|
17
|
+
const repo = await client.getRepo(o, r);
|
|
18
|
+
if (!repo.permissions?.pull) throw new Error(`no read access to ${o}/${r}`);
|
|
19
|
+
if (!dryRun && !repo.permissions?.push) throw new Error(`no write access to ${o}/${r}; use --dry-run or ask a writer to upgrade`);
|
|
20
|
+
const base = repo.defaultBranch;
|
|
21
|
+
|
|
22
|
+
const actions = [];
|
|
23
|
+
const changes = [];
|
|
24
|
+
const params = { owner: o, repo: r };
|
|
25
|
+
const files = managedFiles(params);
|
|
26
|
+
const state = new Map();
|
|
27
|
+
for (const f of files) {
|
|
28
|
+
const cur = await client.getFileText(o, r, f.path, base);
|
|
29
|
+
state.set(f.path, classifyManaged(f.path, cur, params));
|
|
30
|
+
}
|
|
31
|
+
const pairHeld = TRIO.some((p) => state.get(p) === "customized");
|
|
32
|
+
for (const f of files) {
|
|
33
|
+
let kind = state.get(f.path);
|
|
34
|
+
if (kind === "customized" && f.seed) kind = "current";
|
|
35
|
+
const paired = TRIO.includes(f.path);
|
|
36
|
+
if (kind === "missing") {
|
|
37
|
+
actions.push({ path: f.path, action: "add" });
|
|
38
|
+
changes.push({ path: f.path, content: f.content });
|
|
39
|
+
} else if (kind === "pristine-old" && paired && pairHeld) {
|
|
40
|
+
const candidate = `${f.path}.techbro-new`;
|
|
41
|
+
actions.push({ path: f.path, action: "customized", candidate });
|
|
42
|
+
if ((await client.getFileText(o, r, candidate, base)) !== f.content) changes.push({ path: candidate, content: f.content });
|
|
43
|
+
} else if (kind === "pristine-old") {
|
|
44
|
+
actions.push({ path: f.path, action: "update" });
|
|
45
|
+
changes.push({ path: f.path, content: f.content });
|
|
46
|
+
} else if (kind === "current") {
|
|
47
|
+
actions.push({ path: f.path, action: "skip" });
|
|
48
|
+
} else {
|
|
49
|
+
const candidate = `${f.path}.techbro-new`;
|
|
50
|
+
actions.push({ path: f.path, action: "customized", candidate });
|
|
51
|
+
if ((await client.getFileText(o, r, candidate, base)) !== f.content) changes.push({ path: candidate, content: f.content });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const result = { team: name, repo: `${o}/${r}`, base, actions };
|
|
55
|
+
if (!changes.length) return { ...result, status: "current" };
|
|
56
|
+
if (dryRun) return { ...result, status: "dry-run", changes: changes.map((c) => c.path) };
|
|
57
|
+
|
|
58
|
+
const branch = upgradeBranch();
|
|
59
|
+
const wrap = async (fn) => {
|
|
60
|
+
try {
|
|
61
|
+
return await fn();
|
|
62
|
+
} catch (err) {
|
|
63
|
+
if ([401, 403, 404, 409, 422].includes(err.status)) throw new Error(`${err.message}\n${SCOPE_HINT}`);
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
return wrap(async () => {
|
|
68
|
+
let head = await client.getBranchSha(o, r, branch);
|
|
69
|
+
let toWrite = changes;
|
|
70
|
+
if (head === null) {
|
|
71
|
+
head = await client.getBranchSha(o, r, base);
|
|
72
|
+
await client.createRef(o, r, branch, head);
|
|
73
|
+
} else {
|
|
74
|
+
toWrite = [];
|
|
75
|
+
for (const c of changes) if ((await client.getFileText(o, r, c.path, branch)) !== c.content) toWrite.push(c);
|
|
76
|
+
}
|
|
77
|
+
if (toWrite.length) {
|
|
78
|
+
const commit = await client.commitFiles(o, r, { baseSha: head, files: toWrite, message: `techbro: upgrade managed files (v${MANAGED_VERSION})` });
|
|
79
|
+
await client.updateRef(o, r, branch, commit);
|
|
80
|
+
}
|
|
81
|
+
const open = await client.listPulls(o, r, { head: branch, base });
|
|
82
|
+
if (open.length) return { ...result, status: "pr-existing", prUrl: open[0].url, branch, committed: toWrite.map((c) => c.path) };
|
|
83
|
+
const lines = actions.map((a) =>
|
|
84
|
+
a.action === "customized" ? `- ${a.path}: customized, kept; review ${a.candidate} and merge by hand, then delete it` : `- ${a.path}: ${a.action}`,
|
|
85
|
+
);
|
|
86
|
+
const pr = await client.createPull(o, r, {
|
|
87
|
+
title: `techbro: upgrade managed files (v${MANAGED_VERSION})`,
|
|
88
|
+
head: branch,
|
|
89
|
+
base,
|
|
90
|
+
body: `Managed file upgrade.\n\n${lines.join("\n")}\n\nmembers.json and projects.json are never touched.`,
|
|
91
|
+
});
|
|
92
|
+
return { ...result, status: "pr-created", prUrl: pr.url, branch, committed: toWrite.map((c) => c.path) };
|
|
93
|
+
});
|
|
94
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Portable project skills
|
|
2
|
+
|
|
3
|
+
| Skill | Use when |
|
|
4
|
+
| --- | --- |
|
|
5
|
+
| [frontend-feature](frontend-feature/SKILL.md) | Adding or fixing React pages, client state, or API interactions |
|
|
6
|
+
| [rust-backend-feature](rust-backend-feature/SKILL.md) | Implementing Axum endpoints and model behavior |
|
|
7
|
+
| [database-migrations](database-migrations/SKILL.md) | Planning approved schema changes and verifying migrations |
|
|
8
|
+
| [release-safe-change](release-safe-change/SKILL.md) | Verifying a change for release, compatibility, and graceful draining |
|
|
9
|
+
|
|
10
|
+
`.agents/skills/` is portable storage, not a universal automatic-discovery directory.
|
|
11
|
+
Explicitly ask your agent to load the relevant `SKILL.md`, or configure its
|
|
12
|
+
documented skill discovery mechanism to use this directory. Load more than one
|
|
13
|
+
when a change crosses concerns. Do not assume a skill was loaded merely because
|
|
14
|
+
it is present in the repository.
|
|
15
|
+
|
|
16
|
+
The Engineering Convention in the template `README.md` is authoritative. Keep a
|
|
17
|
+
link to that convention if replacing the README during project setup. Skills
|
|
18
|
+
summarize workflows, not exceptions: preserve established project choices and
|
|
19
|
+
follow explicit project instructions. Obtain tech team approval before schema
|
|
20
|
+
or API contract changes, authentication/permission changes, and high-risk work.
|
|
21
|
+
|
|
22
|
+
## Provenance and references
|
|
23
|
+
|
|
24
|
+
These are original, project-authored instructions grounded in this template's
|
|
25
|
+
convention, scripts, and sample code. No third-party skill text was copied;
|
|
26
|
+
no third-party skill license or endorsement is claimed. Official documentation
|
|
27
|
+
links are references only:
|
|
28
|
+
|
|
29
|
+
- [shadcn/ui JavaScript](https://ui.shadcn.com/docs/javascript)
|
|
30
|
+
- [Axum](https://docs.rs/axum/latest/axum/)
|
|
31
|
+
- [SQLx](https://docs.rs/sqlx/latest/sqlx/)
|
|
32
|
+
- [SQLx CLI](https://github.com/transact-rs/sqlx/blob/main/sqlx-cli/README.md)
|
|
33
|
+
|
|
34
|
+
Use installed versions, manifests, toolchain pins, and lockfiles as the source
|
|
35
|
+
of truth. The `latest` documentation URLs do not authorize upgrades or guarantee
|
|
36
|
+
that an example matches the project's installed version.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: database-migrations
|
|
3
|
+
description: Plan approved independent SQLx migrations and verify PostgreSQL and SQLite schema compatibility safely.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Database migrations
|
|
7
|
+
|
|
8
|
+
## Approval gate
|
|
9
|
+
|
|
10
|
+
- Read the Engineering Convention and existing schema/migration history first.
|
|
11
|
+
- Obtain tech team approval BEFORE implementing any new or changed schema.
|
|
12
|
+
Document the logical change, affected queries, compatibility, and target engines.
|
|
13
|
+
- Destructive execution additionally requires explicit authorization for the
|
|
14
|
+
target environment. Never silently drop, truncate, reset, or overwrite data.
|
|
15
|
+
- Escalate sensitive-data, permission, authentication, or availability risks.
|
|
16
|
+
Continue only independent low-risk work while a decision is pending.
|
|
17
|
+
|
|
18
|
+
## Author an independent migration
|
|
19
|
+
|
|
20
|
+
- Keep SQL outside application source in the paired directories
|
|
21
|
+
`database/migrations/postgres/` and `database/migrations/sqlite/`.
|
|
22
|
+
- Use ordered timestamp filenames following existing migrations. Keep logical
|
|
23
|
+
schema intent aligned across engines and document unavoidable differences.
|
|
24
|
+
- Never edit/delete a migration already applied to a shared environment;
|
|
25
|
+
add a corrective migration instead.
|
|
26
|
+
- Keep SQLx as the runner, with migration history and locking. Arrange one
|
|
27
|
+
migration job per target database; filenames alone do not prevent races.
|
|
28
|
+
- Do not run migrations on application startup, in `/health`, or in `/ready`.
|
|
29
|
+
The runner must work without building or starting the backend.
|
|
30
|
+
- Parameterize application SQL with SQLx bind values; keep persistence in
|
|
31
|
+
backend models. Do not concatenate user input into SQL or migration commands.
|
|
32
|
+
- Review constraints, indexes, transaction boundaries, locks, and data volume.
|
|
33
|
+
Document long-running/backfill work rather than assuming it is instantaneous.
|
|
34
|
+
|
|
35
|
+
## Expand/contract compatibility
|
|
36
|
+
|
|
37
|
+
1. Add compatible structures first; old and new application versions must work.
|
|
38
|
+
2. Backfill with a reviewed, restartable data transformation where needed.
|
|
39
|
+
3. Switch application reads/writes after validation and approved deployment.
|
|
40
|
+
4. Remove obsolete structures in a later, separately approved contract change.
|
|
41
|
+
|
|
42
|
+
For destructive steps, record backup and restore procedures, cut-over criteria,
|
|
43
|
+
and recovery limits. A reverse migration cannot recover deleted data by magic.
|
|
44
|
+
Do not infer rollback safety from the existence of a down script.
|
|
45
|
+
|
|
46
|
+
## Run only against explicitly selected test databases
|
|
47
|
+
|
|
48
|
+
From repository root, with `DATABASE_URL` set to an isolated disposable target:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
scripts/db-migrate postgres
|
|
52
|
+
scripts/db-migrate sqlite
|
|
53
|
+
# Equivalent documented entry points:
|
|
54
|
+
npm run db:migrate:postgres
|
|
55
|
+
npm run db:migrate:sqlite
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Run each engine with its own URL; do not reuse a production `.env`. The script
|
|
59
|
+
loads `.env` when no URL is set, invokes `sqlx database create`, then
|
|
60
|
+
`sqlx migrate run --source database/migrations/<engine>`.
|
|
61
|
+
|
|
62
|
+
## Verification and evidence
|
|
63
|
+
|
|
64
|
+
- Verify fresh installation AND upgrade from the previous supported schema.
|
|
65
|
+
- Seed representative existing data before upgrade; assert preservation,
|
|
66
|
+
constraints, query behavior, and old/new application compatibility afterward.
|
|
67
|
+
- Run actual PostgreSQL integration tests for PostgreSQL-bound changes;
|
|
68
|
+
SQLite tests and static SQL inspection are not substitute evidence.
|
|
69
|
+
- State databases/versions used, commands, results, and any integration gaps.
|
|
70
|
+
- Consult [SQLx CLI](https://github.com/transact-rs/sqlx/blob/main/sqlx-cli/README.md)
|
|
71
|
+
and [SQLx](https://docs.rs/sqlx/latest/sqlx/) for installed-version behavior.
|
|
72
|
+
Pins and lockfiles control; do not blindly follow latest docs or upgrade tools.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-feature
|
|
3
|
+
description: Implement and test React JavaScript features within the existing Vite frontend and approved API contracts.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Frontend feature
|
|
7
|
+
|
|
8
|
+
## Before editing
|
|
9
|
+
|
|
10
|
+
- Read the Engineering Convention and frontend README; preserve project choices.
|
|
11
|
+
- Use JavaScript/JSX, React, Vite, Tailwind, and the existing shadcn components.
|
|
12
|
+
Do not introduce TypeScript, Next.js, or a second styling system.
|
|
13
|
+
- Inspect `apps/frontend/package.json`, its lockfile, `components.json`, and
|
|
14
|
+
nearby tests before choosing APIs or adding dependencies.
|
|
15
|
+
- Confirm the required behavior uses an approved API contract. New or changed
|
|
16
|
+
contracts require tech team approval before dependent implementation.
|
|
17
|
+
- Escalate authentication, permissions, sensitive data, payments, or other
|
|
18
|
+
security/availability risks before implementation. UI hiding is not authorization.
|
|
19
|
+
|
|
20
|
+
## Test first
|
|
21
|
+
|
|
22
|
+
1. Add a page/component test describing the user-visible outcome or reproducing
|
|
23
|
+
the bug. Use Vitest and React Testing Library, following nearby tests.
|
|
24
|
+
2. Run the focused test and actually observe the expected behavioral failure.
|
|
25
|
+
Fix setup/import failures before treating a test as the red step.
|
|
26
|
+
3. Implement the smallest passing change, then refactor while tests stay green.
|
|
27
|
+
4. Cover useful loading, empty, success, and error cases; mock `fetch` for API
|
|
28
|
+
tests rather than depending on a live backend.
|
|
29
|
+
|
|
30
|
+
## Place responsibilities
|
|
31
|
+
|
|
32
|
+
- Register navigation in `apps/frontend/src/routes.jsx` using React Router's
|
|
33
|
+
existing browser-history setup; keep path-based resource URLs and deep links.
|
|
34
|
+
- Put route views in `src/pages/`, reusable views in `src/components/`, and
|
|
35
|
+
reuse `src/components/ui/` before creating replacements.
|
|
36
|
+
- Put HTTP calls in `src/api/`, coordination in `src/hooks/`, and pure data
|
|
37
|
+
transformations/client state helpers in `src/models/`.
|
|
38
|
+
- Follow `src/api/client.js` for response/error handling. Keep secrets out of
|
|
39
|
+
bundles; the frontend never accesses the database directly.
|
|
40
|
+
- Keep backend validation authoritative. Retain camelCase payloads and the
|
|
41
|
+
documented error contract rather than inventing a parallel client contract.
|
|
42
|
+
- Preserve existing styles, layout conventions, tokens, accessibility, and
|
|
43
|
+
component behavior. Avoid opportunistic restyling during feature work.
|
|
44
|
+
- Prefer focused functions and existing helpers; add JSDoc for exported APIs
|
|
45
|
+
and non-obvious shared data shapes, not comments on every line.
|
|
46
|
+
|
|
47
|
+
## Verify from repository root
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm --prefix apps/frontend run test -- --run src/api/client.test.js
|
|
51
|
+
npm run check:frontend
|
|
52
|
+
npm --prefix apps/frontend run build
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The first command is an existing focused-test example; substitute the changed
|
|
56
|
+
test path relative to `apps/frontend`. Run checks after refactoring, not just
|
|
57
|
+
after the first green test. Report commands, outcomes, and any unverified
|
|
58
|
+
browser/deep-link behavior; do not silently bypass a failing check.
|
|
59
|
+
|
|
60
|
+
## References and completion
|
|
61
|
+
|
|
62
|
+
- Consult [shadcn JavaScript docs](https://ui.shadcn.com/docs/javascript) only
|
|
63
|
+
for the installed configuration; lockfiles control versions, not latest docs.
|
|
64
|
+
- Update API documentation only within approved scope. Include tests, changed
|
|
65
|
+
behavior, approval references, and verification evidence in the handoff.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release-safe-change
|
|
3
|
+
description: Verify test-first changes, release checks, backward compatibility, health identity, readiness, and SIGTERM draining.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Release-safe change
|
|
7
|
+
|
|
8
|
+
## Scope and authority
|
|
9
|
+
|
|
10
|
+
- Read the Engineering Convention, project READMEs, and `docs/deployment.md`.
|
|
11
|
+
Preserve established stack, branching, and release choices.
|
|
12
|
+
- Confirm approvals for schema/API contracts and high-risk authentication,
|
|
13
|
+
permissions, sensitive-data, destructive, availability, or cost changes.
|
|
14
|
+
- Record exceptions and scope. Never bypass a failing gate without explicit
|
|
15
|
+
approval and follow-up work; routine implementation follows normal review.
|
|
16
|
+
|
|
17
|
+
## Red, green, refactor
|
|
18
|
+
|
|
19
|
+
1. Write an outside-in test for the expected behavior or regression.
|
|
20
|
+
2. Run it BEFORE implementation and actually observe the intended failure.
|
|
21
|
+
Compilation/setup failures do not establish a red behavioral test.
|
|
22
|
+
3. Make the smallest passing change; rerun the test and observe green.
|
|
23
|
+
4. Refactor while green, then run the full applicable checks below.
|
|
24
|
+
5. Report the red and green commands/results, not merely that tests were added.
|
|
25
|
+
|
|
26
|
+
## Full checks from repository root
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
scripts/check
|
|
30
|
+
npm --prefix apps/frontend run build
|
|
31
|
+
cargo build --locked -p backend
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`scripts/check` runs rustfmt, locked Clippy with warnings denied, locked Rust
|
|
35
|
+
tests, frontend Prettier check, ESLint, and Vitest. `npm run check` is the root
|
|
36
|
+
equivalent. Use committed toolchain pins/lockfiles; do not resolve floating
|
|
37
|
+
latest dependencies as part of release verification.
|
|
38
|
+
|
|
39
|
+
Add target-engine PostgreSQL checks for database changes, plus contract and
|
|
40
|
+
browser checks appropriate to risk. State what the baseline does not test.
|
|
41
|
+
Keep check/build results separate from deployed-system evidence.
|
|
42
|
+
|
|
43
|
+
## Application readiness
|
|
44
|
+
|
|
45
|
+
- Keep processes stateless and interchangeable; no local-only sessions/uploads
|
|
46
|
+
that fail when two instances serve concurrently. Shared deployments use Postgres.
|
|
47
|
+
- Keep runtime configuration environment-driven and use the same built image
|
|
48
|
+
across environments. Do not bake secrets or environment-specific settings in.
|
|
49
|
+
- Verify `/health` returns liveness plus running `version` and `build` identity.
|
|
50
|
+
It must not expose secrets or run migrations.
|
|
51
|
+
- Verify `/ready` fails with `503` when required dependencies are unavailable
|
|
52
|
+
and while draining; dependency checks must be bounded and inexpensive.
|
|
53
|
+
- Exercise SIGTERM on a running process: readiness becomes `503`, the configured
|
|
54
|
+
drain period elapses, in-flight requests finish, the pool closes, and exit is `0`.
|
|
55
|
+
A unit test of a flag alone is not live shutdown evidence.
|
|
56
|
+
- Preserve tracing JSON logs and request IDs without sensitive payloads.
|
|
57
|
+
|
|
58
|
+
## Cut-over and rollback
|
|
59
|
+
|
|
60
|
+
- Run independent approved migrations before starting the new instance.
|
|
61
|
+
- Verify old/new code can coexist against the expanded schema and within the
|
|
62
|
+
same API version. Defer removals until a later approved contract release.
|
|
63
|
+
- Confirm rollback can serve against the resulting schema; otherwise require
|
|
64
|
+
an approved exception and explicit cut-over/recovery plan.
|
|
65
|
+
- Do not claim zero downtime from passing tests, readiness code, or this skill.
|
|
66
|
+
That claim needs live load-balancer evidence under traffic during cut-over
|
|
67
|
+
and drain, including request outcomes and instance version/build identity.
|
|
68
|
+
- If live evidence is unavailable, report application readiness separately and
|
|
69
|
+
label end-to-end zero-downtime behavior unverified.
|
|
70
|
+
|
|
71
|
+
## Handoff
|
|
72
|
+
|
|
73
|
+
Summarize behavior, approvals, compatibility, exact checks and outcomes,
|
|
74
|
+
remaining operational risks, and deployment/rollback evidence. Do not push,
|
|
75
|
+
deploy, or execute destructive operations unless specifically authorized.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rust-backend-feature
|
|
3
|
+
description: Implement tested Axum and Tokio backend behavior with SQLx persistence and existing JSON contracts.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rust backend feature
|
|
7
|
+
|
|
8
|
+
## Establish scope
|
|
9
|
+
|
|
10
|
+
- Read the Engineering Convention, backend README, `Cargo.toml`, `Cargo.lock`
|
|
11
|
+
when present, and `rust-toolchain.toml`; preserve established choices.
|
|
12
|
+
- Keep one modular-monolith backend using Axum, Tokio, Serde, and SQLx.
|
|
13
|
+
Avoid an ORM, microservice split, or generic repository framework.
|
|
14
|
+
- Obtain tech team approval BEFORE new/changed schema or API contracts.
|
|
15
|
+
Authentication, permissions, sensitive data, payments, destructive operations,
|
|
16
|
+
and material availability/cost changes also need explicit risk approval.
|
|
17
|
+
- Existing approval covers its recorded scope, not unrelated improvements.
|
|
18
|
+
|
|
19
|
+
## Outside-in tests
|
|
20
|
+
|
|
21
|
+
1. Start with a contract/regression test in `apps/backend/tests/api.rs` for the
|
|
22
|
+
expected HTTP method, status, headers, and JSON behavior.
|
|
23
|
+
2. Run it and observe the intended behavioral failure before implementation.
|
|
24
|
+
A compiler error or unavailable database is not proof of a failing behavior.
|
|
25
|
+
3. Add model tests for validation and edge cases; implement the smallest fix.
|
|
26
|
+
4. Refactor with tests green. Keep tests isolated, using the existing SQLite
|
|
27
|
+
fixture for lightweight checks, not shared mutable databases.
|
|
28
|
+
5. For SQL, constraints, concurrency, or PostgreSQL-specific behavior, also
|
|
29
|
+
test against an actual isolated PostgreSQL database. SQLite is insufficient.
|
|
30
|
+
|
|
31
|
+
## Implementation boundaries
|
|
32
|
+
|
|
33
|
+
- Register endpoints in `apps/backend/src/routes.rs`.
|
|
34
|
+
- Keep `src/controllers/` thin: extract/validate input, enforce authorization,
|
|
35
|
+
call models, and map results. Never treat client-side checks as security.
|
|
36
|
+
- Keep domain rules and SQLx persistence in `src/models/`; use `src/views/`
|
|
37
|
+
for response representations when a separate type clarifies the contract.
|
|
38
|
+
- Bind SQL parameters, use transactions for atomic related writes, and retain
|
|
39
|
+
appropriate constraints. Never interpolate untrusted SQL values.
|
|
40
|
+
- Use existing `src/error.rs` handling and request ID propagation. Keep JSON
|
|
41
|
+
fields camelCase, collection pagination, and consistent HTTP error statuses.
|
|
42
|
+
- Preserve `{ "error": { "code": "...", "message": "..." }, "requestId": "..." }`;
|
|
43
|
+
optional details must not expose internal errors or credentials.
|
|
44
|
+
- Avoid unchecked `unwrap()`/`expect()` on request paths. Handle failures with
|
|
45
|
+
the established error mapping and bounded external-service timeouts.
|
|
46
|
+
- Preserve tracing JSON stdout logs and request correlation; log useful
|
|
47
|
+
outcomes without passwords, tokens, full payloads, or unnecessary personal data.
|
|
48
|
+
- Never add startup migrations. Use the independent migration workflow.
|
|
49
|
+
- Keep `/health`, `/ready`, configuration, and graceful shutdown compatible.
|
|
50
|
+
|
|
51
|
+
## Verify from repository root
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
cargo test --locked -p backend --test api
|
|
55
|
+
npm run check:backend
|
|
56
|
+
cargo build --locked -p backend
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Use a focused test filter during red/green iterations. Report PostgreSQL checks
|
|
60
|
+
separately from the default SQLite tests, including any checks not performed.
|
|
61
|
+
|
|
62
|
+
## References and handoff
|
|
63
|
+
|
|
64
|
+
- [Axum](https://docs.rs/axum/latest/axum/) and
|
|
65
|
+
[SQLx](https://docs.rs/sqlx/latest/sqlx/) are references, not upgrade mandates.
|
|
66
|
+
Select documentation matching installed versions and committed lockfiles.
|
|
67
|
+
- Record contract/schema approvals, changed behavior, relevant API examples,
|
|
68
|
+
and actual test/build evidence. Do not claim unrun integration checks passed.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
APP_NAME=my-app
|
|
2
|
+
APP_PORT=8080
|
|
3
|
+
# Local development (SQLite). PostgreSQL form:
|
|
4
|
+
# DATABASE_URL=postgres://user:password@localhost:5432/my_app
|
|
5
|
+
DATABASE_URL=sqlite://local.db?mode=rwc
|
|
6
|
+
RUST_LOG=info
|
|
7
|
+
# Seconds /ready reports 503 after SIGTERM before the listener closes (default 5)
|
|
8
|
+
SHUTDOWN_DRAIN_SECONDS=5
|
|
9
|
+
CORS_ALLOWED_ORIGINS=http://localhost:5173
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
# Templates pin actions to tags for readability; teams should pin to commit SHAs.
|
|
8
|
+
jobs:
|
|
9
|
+
backend:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
timeout-minutes: 20
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
15
|
+
with:
|
|
16
|
+
toolchain: 1.95.0
|
|
17
|
+
components: rustfmt, clippy
|
|
18
|
+
- uses: Swatinem/rust-cache@v2
|
|
19
|
+
- run: cargo fmt --all --check
|
|
20
|
+
- run: cargo clippy --locked --all-targets -- -D warnings
|
|
21
|
+
- run: cargo test --locked
|
|
22
|
+
|
|
23
|
+
frontend:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
timeout-minutes: 15
|
|
26
|
+
defaults:
|
|
27
|
+
run:
|
|
28
|
+
working-directory: apps/frontend
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: actions/setup-node@v4
|
|
32
|
+
with:
|
|
33
|
+
node-version-file: .nvmrc
|
|
34
|
+
cache: npm
|
|
35
|
+
cache-dependency-path: apps/frontend/package-lock.json
|
|
36
|
+
- run: npm ci
|
|
37
|
+
- run: npm run format:check
|
|
38
|
+
- run: npm run lint
|
|
39
|
+
- run: npm run test -- --run
|
|
40
|
+
- run: npm run build
|
|
41
|
+
|
|
42
|
+
docker:
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
timeout-minutes: 30
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: docker/setup-buildx-action@v3
|
|
48
|
+
- uses: docker/build-push-action@v6
|
|
49
|
+
with:
|
|
50
|
+
context: .
|
|
51
|
+
file: apps/backend/Dockerfile
|
|
52
|
+
push: false
|
|
53
|
+
- uses: docker/build-push-action@v6
|
|
54
|
+
with:
|
|
55
|
+
context: apps/frontend
|
|
56
|
+
push: false
|
|
57
|
+
- uses: docker/build-push-action@v6
|
|
58
|
+
with:
|
|
59
|
+
context: database
|
|
60
|
+
push: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
22
|