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.
Files changed (122) hide show
  1. package/README.md +453 -2
  2. package/assets/baseline-rules.yml +60 -0
  3. package/assets/caller-workflow.yml +26 -0
  4. package/assets/review-action.yml +27 -0
  5. package/assets/review-config.json +4 -0
  6. package/assets/review-workflow.yml +443 -0
  7. package/assets/review.mjs +721 -0
  8. package/bin/techbro.js +9 -0
  9. package/package.json +23 -4
  10. package/src/cli.js +690 -0
  11. package/src/cluster.js +134 -0
  12. package/src/config.js +35 -0
  13. package/src/doctor.js +125 -0
  14. package/src/github.js +152 -0
  15. package/src/init.js +167 -0
  16. package/src/kube-project.js +468 -0
  17. package/src/kube.js +317 -0
  18. package/src/managed.js +63 -0
  19. package/src/project.js +202 -0
  20. package/src/review.js +165 -0
  21. package/src/secret.js +80 -0
  22. package/src/setup.js +91 -0
  23. package/src/team-workflow.js +106 -0
  24. package/src/team.js +145 -0
  25. package/src/templates.js +44 -0
  26. package/src/upgrade.js +94 -0
  27. package/templates/_default/.agents/skills/README.md +36 -0
  28. package/templates/_default/.agents/skills/database-migrations/SKILL.md +72 -0
  29. package/templates/_default/.agents/skills/frontend-feature/SKILL.md +65 -0
  30. package/templates/_default/.agents/skills/release-safe-change/SKILL.md +75 -0
  31. package/templates/_default/.agents/skills/rust-backend-feature/SKILL.md +68 -0
  32. package/templates/_default/.editorconfig +15 -0
  33. package/templates/_default/.env.example +9 -0
  34. package/templates/_default/.github/workflows/ci.yml +60 -0
  35. package/templates/_default/.nvmrc +1 -0
  36. package/templates/_default/Cargo.lock +1834 -0
  37. package/templates/_default/Cargo.toml +3 -0
  38. package/templates/_default/README.md +257 -0
  39. package/templates/_default/TEMPLATE.md +29 -0
  40. package/templates/_default/_dockerignore +8 -0
  41. package/templates/_default/_gitignore +8 -0
  42. package/templates/_default/apps/backend/Cargo.toml +22 -0
  43. package/templates/_default/apps/backend/Dockerfile +34 -0
  44. package/templates/_default/apps/backend/README.md +13 -0
  45. package/templates/_default/apps/backend/src/config.rs +111 -0
  46. package/templates/_default/apps/backend/src/controllers/health.rs +36 -0
  47. package/templates/_default/apps/backend/src/controllers/items.rs +79 -0
  48. package/templates/_default/apps/backend/src/controllers/mod.rs +2 -0
  49. package/templates/_default/apps/backend/src/error.rs +115 -0
  50. package/templates/_default/apps/backend/src/lib.rs +7 -0
  51. package/templates/_default/apps/backend/src/main.rs +71 -0
  52. package/templates/_default/apps/backend/src/models/item.rs +94 -0
  53. package/templates/_default/apps/backend/src/models/mod.rs +1 -0
  54. package/templates/_default/apps/backend/src/routes.rs +94 -0
  55. package/templates/_default/apps/backend/src/state.rs +12 -0
  56. package/templates/_default/apps/backend/src/views/items.rs +18 -0
  57. package/templates/_default/apps/backend/src/views/mod.rs +1 -0
  58. package/templates/_default/apps/backend/tests/api.rs +234 -0
  59. package/templates/_default/apps/frontend/.env.example +2 -0
  60. package/templates/_default/apps/frontend/.prettierignore +3 -0
  61. package/templates/_default/apps/frontend/.prettierrc +1 -0
  62. package/templates/_default/apps/frontend/Dockerfile +15 -0
  63. package/templates/_default/apps/frontend/README.md +39 -0
  64. package/templates/_default/apps/frontend/_dockerignore +3 -0
  65. package/templates/_default/apps/frontend/_gitignore +24 -0
  66. package/templates/_default/apps/frontend/components.json +25 -0
  67. package/templates/_default/apps/frontend/eslint.config.js +37 -0
  68. package/templates/_default/apps/frontend/index.html +13 -0
  69. package/templates/_default/apps/frontend/jsconfig.json +7 -0
  70. package/templates/_default/apps/frontend/nginx.conf +30 -0
  71. package/templates/_default/apps/frontend/package-lock.json +4338 -0
  72. package/templates/_default/apps/frontend/package.json +49 -0
  73. package/templates/_default/apps/frontend/public/favicon.svg +1 -0
  74. package/templates/_default/apps/frontend/src/App.jsx +11 -0
  75. package/templates/_default/apps/frontend/src/App.test.jsx +40 -0
  76. package/templates/_default/apps/frontend/src/api/client.js +50 -0
  77. package/templates/_default/apps/frontend/src/api/client.test.js +70 -0
  78. package/templates/_default/apps/frontend/src/api/items.js +26 -0
  79. package/templates/_default/apps/frontend/src/api/system.js +11 -0
  80. package/templates/_default/apps/frontend/src/components/ItemList.jsx +20 -0
  81. package/templates/_default/apps/frontend/src/components/ItemList.test.jsx +18 -0
  82. package/templates/_default/apps/frontend/src/components/Layout.jsx +35 -0
  83. package/templates/_default/apps/frontend/src/components/StatusBadge.jsx +22 -0
  84. package/templates/_default/apps/frontend/src/components/StepList.jsx +19 -0
  85. package/templates/_default/apps/frontend/src/components/ui/button.jsx +52 -0
  86. package/templates/_default/apps/frontend/src/components/ui/card.jsx +83 -0
  87. package/templates/_default/apps/frontend/src/components/ui/input.jsx +19 -0
  88. package/templates/_default/apps/frontend/src/hooks/useBackendStatus.js +33 -0
  89. package/templates/_default/apps/frontend/src/hooks/useItems.js +47 -0
  90. package/templates/_default/apps/frontend/src/index.css +130 -0
  91. package/templates/_default/apps/frontend/src/lib/config.js +2 -0
  92. package/templates/_default/apps/frontend/src/lib/utils.js +6 -0
  93. package/templates/_default/apps/frontend/src/main.jsx +11 -0
  94. package/templates/_default/apps/frontend/src/models/items.js +24 -0
  95. package/templates/_default/apps/frontend/src/models/items.test.js +24 -0
  96. package/templates/_default/apps/frontend/src/pages/ItemsPage.jsx +41 -0
  97. package/templates/_default/apps/frontend/src/pages/WelcomePage.jsx +156 -0
  98. package/templates/_default/apps/frontend/src/pages/WelcomePage.test.jsx +55 -0
  99. package/templates/_default/apps/frontend/src/routes.jsx +17 -0
  100. package/templates/_default/apps/frontend/src/shadcn.css +655 -0
  101. package/templates/_default/apps/frontend/src/test/setup.js +1 -0
  102. package/templates/_default/apps/frontend/vite.config.js +25 -0
  103. package/templates/_default/database/Dockerfile +9 -0
  104. package/templates/_default/database/README.md +15 -0
  105. package/templates/_default/database/migrations/postgres/20261008120000_create_items.sql +5 -0
  106. package/templates/_default/database/migrations/sqlite/20261008120000_create_items.sql +5 -0
  107. package/templates/_default/docker-compose.prod.yml +53 -0
  108. package/templates/_default/docker-compose.yml +69 -0
  109. package/templates/_default/docs/api/README.md +41 -0
  110. package/templates/_default/docs/api/openapi.yaml +152 -0
  111. package/templates/_default/docs/decisions/0001-adopt-engineering-convention.md +13 -0
  112. package/templates/_default/docs/decisions/README.md +6 -0
  113. package/templates/_default/docs/deployment.md +46 -0
  114. package/templates/_default/docs/docker.md +63 -0
  115. package/templates/_default/docs/getting-started.md +214 -0
  116. package/templates/_default/package.json +15 -0
  117. package/templates/_default/rust-toolchain.toml +3 -0
  118. package/templates/_default/scripts/check +10 -0
  119. package/templates/_default/scripts/db-migrate +25 -0
  120. package/templates/_default/scripts/install-hooks +5 -0
  121. package/templates/_default/scripts/pre-push +3 -0
  122. 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,15 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ indent_style = space
7
+ indent_size = 2
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+
11
+ [*.rs]
12
+ indent_size = 4
13
+
14
+ [Makefile]
15
+ indent_style = tab
@@ -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