@vegastack/vegafactory 0.19.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 (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +417 -0
  3. package/dist/dashboard-artifact.json +21240 -0
  4. package/dist/index.js +22752 -0
  5. package/dist/run-wrapper.js +377 -0
  6. package/package.json +46 -0
  7. package/skill/dev-architect/SKILL.md +106 -0
  8. package/skill/dev-architect/agents/openai.yaml +4 -0
  9. package/skill/dev-architect/references/ai-agents.md +96 -0
  10. package/skill/dev-architect/references/conventions.md +113 -0
  11. package/skill/dev-architect/references/data.md +89 -0
  12. package/skill/dev-architect/references/infra.md +99 -0
  13. package/skill/dev-architect/references/mobile.md +75 -0
  14. package/skill/dev-architect/references/pinned-facts.md +133 -0
  15. package/skill/dev-architect/references/principles.md +117 -0
  16. package/skill/dev-architect/references/security.md +90 -0
  17. package/skill/dev-architect/references/stack.md +38 -0
  18. package/skill/dev-architect/references/web.md +102 -0
  19. package/skill/dev-architect/refresh/REFRESH.md +31 -0
  20. package/skill/dev-architect/refresh/sources.json +239 -0
  21. package/skill/dev-chronicle/SKILL.md +52 -0
  22. package/skill/dev-chronicle/agents/openai.yaml +4 -0
  23. package/skill/dev-chronicle/references/conventions.md +113 -0
  24. package/skill/dev-chronicle/references/styles.md +75 -0
  25. package/skill/dev-chronicle/refresh/REFRESH.md +3 -0
  26. package/skill/dev-chronicle/refresh/sources.json +6 -0
  27. package/skill/dev-debug/SKILL.md +43 -0
  28. package/skill/dev-debug/agents/openai.yaml +4 -0
  29. package/skill/dev-debug/references/conventions.md +113 -0
  30. package/skill/dev-debug/references/loop-ladder.md +20 -0
  31. package/skill/dev-debug/refresh/REFRESH.md +3 -0
  32. package/skill/dev-debug/refresh/sources.json +6 -0
  33. package/skill/dev-implement/SKILL.md +81 -0
  34. package/skill/dev-implement/agents/openai.yaml +4 -0
  35. package/skill/dev-implement/assets/workflows/implement-children.js +12 -0
  36. package/skill/dev-implement/references/ask-route.md +80 -0
  37. package/skill/dev-implement/references/changelog-and-chronicle.md +21 -0
  38. package/skill/dev-implement/references/conventions.md +113 -0
  39. package/skill/dev-implement/references/ledger-and-resume.md +52 -0
  40. package/skill/dev-implement/references/parallel-children.md +35 -0
  41. package/skill/dev-implement/references/worktrees.md +63 -0
  42. package/skill/dev-implement/refresh/REFRESH.md +3 -0
  43. package/skill/dev-implement/refresh/sources.json +6 -0
  44. package/skill/dev-implement/scripts/children.mjs +479 -0
  45. package/skill/dev-implement/scripts/effective-policy.mjs +694 -0
  46. package/skill/dev-implement/scripts/evidence-check.mjs +107 -0
  47. package/skill/dev-implement/scripts/evidence-upload.mjs +181 -0
  48. package/skill/dev-implement/scripts/learning.mjs +41 -0
  49. package/skill/dev-implement/scripts/lib/approval.mjs +1182 -0
  50. package/skill/dev-implement/scripts/lib/gh.mjs +101 -0
  51. package/skill/dev-implement/scripts/preflight.mjs +153 -0
  52. package/skill/dev-implement/scripts/questions.mjs +381 -0
  53. package/skill/dev-implement/scripts/reclaim.mjs +116 -0
  54. package/skill/dev-implement/scripts/recovery.mjs +213 -0
  55. package/skill/dev-implement/scripts/worktree.mjs +991 -0
  56. package/skill/dev-intake/SKILL.md +80 -0
  57. package/skill/dev-intake/agents/openai.yaml +4 -0
  58. package/skill/dev-intake/references/ask-route.md +80 -0
  59. package/skill/dev-intake/references/brief-template.md +89 -0
  60. package/skill/dev-intake/references/conventions.md +113 -0
  61. package/skill/dev-intake/refresh/REFRESH.md +3 -0
  62. package/skill/dev-intake/refresh/sources.json +6 -0
  63. package/skill/dev-intake/scripts/brief-lint.mjs +87 -0
  64. package/skill/dev-intake/scripts/lib/approval.mjs +1182 -0
  65. package/skill/dev-intake/scripts/questions.mjs +381 -0
  66. package/skill/dev-plan/SKILL.md +54 -0
  67. package/skill/dev-plan/agents/openai.yaml +4 -0
  68. package/skill/dev-plan/references/ask-route.md +80 -0
  69. package/skill/dev-plan/references/conventions.md +113 -0
  70. package/skill/dev-plan/references/plan-format.md +82 -0
  71. package/skill/dev-plan/refresh/REFRESH.md +3 -0
  72. package/skill/dev-plan/refresh/sources.json +6 -0
  73. package/skill/dev-plan/scripts/lib/approval.mjs +1182 -0
  74. package/skill/dev-plan/scripts/plan-lint.mjs +319 -0
  75. package/skill/dev-plan/scripts/questions.mjs +381 -0
  76. package/skill/dev-review/SKILL.md +76 -0
  77. package/skill/dev-review/agents/openai.yaml +4 -0
  78. package/skill/dev-review/assets/review-known-patterns.md.template +32 -0
  79. package/skill/dev-review/references/conventions.md +113 -0
  80. package/skill/dev-review/references/cross-agent.md +39 -0
  81. package/skill/dev-review/references/dispatch-prompts.md +121 -0
  82. package/skill/dev-review/references/security-axis.md +45 -0
  83. package/skill/dev-review/refresh/REFRESH.md +5 -0
  84. package/skill/dev-review/refresh/sources.json +6 -0
  85. package/skill/dev-setup/SKILL.md +113 -0
  86. package/skill/dev-setup/agents/openai.yaml +4 -0
  87. package/skill/dev-setup/assets/agents-section.md.template +24 -0
  88. package/skill/dev-setup/assets/dev-profile.md.template +102 -0
  89. package/skill/dev-setup/assets/factory-board.yml.template +162 -0
  90. package/skill/dev-setup/assets/hooks/decision-nudge.mjs +30 -0
  91. package/skill/dev-setup/assets/hooks/prompt-skill-mention.mjs +29 -0
  92. package/skill/dev-setup/assets/hooks/session-end.mjs +14 -0
  93. package/skill/dev-setup/assets/hooks/session-start.mjs +178 -0
  94. package/skill/dev-setup/assets/hooks/ship-guard.mjs +738 -0
  95. package/skill/dev-setup/assets/hooks/skill-activated.mjs +47 -0
  96. package/skill/dev-setup/assets/hooks/stop-heartbeat.mjs +14 -0
  97. package/skill/dev-setup/assets/workflows/implement-children.js +12 -0
  98. package/skill/dev-setup/references/ask-route.md +80 -0
  99. package/skill/dev-setup/references/conventions.md +113 -0
  100. package/skill/dev-setup/references/github-app.md +286 -0
  101. package/skill/dev-setup/references/harness-facts.md +169 -0
  102. package/skill/dev-setup/references/stack-playbooks.md +124 -0
  103. package/skill/dev-setup/refresh/REFRESH.md +13 -0
  104. package/skill/dev-setup/refresh/sources.json +602 -0
  105. package/skill/dev-setup/scripts/effective-policy.mjs +694 -0
  106. package/skill/dev-setup/scripts/questions.mjs +381 -0
  107. package/skill/dev-setup/scripts/ship-policy.mjs +195 -0
  108. package/skill/dev-ship/SKILL.md +56 -0
  109. package/skill/dev-ship/agents/openai.yaml +4 -0
  110. package/skill/dev-ship/references/conventions.md +113 -0
  111. package/skill/dev-ship/references/runbook.md +72 -0
  112. package/skill/dev-ship/refresh/REFRESH.md +3 -0
  113. package/skill/dev-ship/refresh/sources.json +6 -0
  114. package/skill/dev-ship/scripts/lib/approval.mjs +1182 -0
  115. package/skill/dev-ship/scripts/ship-gate.mjs +485 -0
  116. package/skill/dev-status/SKILL.md +56 -0
  117. package/skill/dev-status/agents/openai.yaml +4 -0
  118. package/skill/dev-status/references/conventions.md +113 -0
  119. package/skill/dev-status/refresh/REFRESH.md +3 -0
  120. package/skill/dev-status/refresh/sources.json +6 -0
  121. package/skill/dev-status/scripts/effective-policy.mjs +694 -0
  122. package/skill/dev-status/scripts/status.mjs +311 -0
  123. package/skill/skill-maintainer/SKILL.md +82 -0
  124. package/skill/skill-maintainer/agents/openai.yaml +4 -0
  125. package/skill/skill-maintainer/references/release-ops.md +61 -0
  126. package/skill/skill-maintainer/references/standards.md +137 -0
  127. package/skill/skill-maintainer/refresh/REFRESH.md +40 -0
  128. package/skill/skill-maintainer/refresh/sources.json +103 -0
  129. package/skill/skill-scan/SKILL.md +59 -0
  130. package/skill/skill-scan/agents/openai.yaml +4 -0
  131. package/skill/skill-scan/refresh/REFRESH.md +18 -0
  132. package/skill/skill-scan/refresh/sources.json +133 -0
  133. package/skill/skill-scan/scripts/lib/skillspector.mjs +226 -0
  134. package/skill/skill-scan/scripts/skill-scan.mjs +1002 -0
  135. package/skill/skillify/SKILL.md +108 -0
  136. package/skill/skillify/agents/openai.yaml +4 -0
  137. package/skill/skillify/assets/templates/README.md.template +26 -0
  138. package/skill/skillify/assets/templates/REFRESH.md.template +16 -0
  139. package/skill/skillify/assets/templates/SKILL.md.template +27 -0
  140. package/skill/skillify/assets/templates/evals.json.template +12 -0
  141. package/skill/skillify/assets/templates/openai.yaml.template +4 -0
  142. package/skill/skillify/assets/templates/skill.test.ts.template +27 -0
  143. package/skill/skillify/assets/templates/sources.json.template +6 -0
  144. package/skill/skillify/assets/templates/trigger-queries.json.template +1 -0
  145. package/skill/skillify/references/authoring.md +103 -0
  146. package/skill/skillify/references/eval-playbook.md +112 -0
  147. package/skill/skillify/refresh/REFRESH.md +23 -0
  148. package/skill/skillify/refresh/sources.json +6 -0
  149. package/skill/skillify/scripts/scaffold-skill.mjs +334 -0
  150. package/skill/skillify/scripts/trigger-check.mjs +209 -0
  151. package/skill/vegafactory-setup/SKILL.md +77 -0
  152. package/skill/vegafactory-setup/agents/openai.yaml +4 -0
  153. package/skill/vegafactory-setup/assets/control-room/boards.md.template +17 -0
  154. package/skill/vegafactory-setup/assets/control-room/decisions.md.template +5 -0
  155. package/skill/vegafactory-setup/assets/control-room/group.md.template +44 -0
  156. package/skill/vegafactory-setup/assets/control-room/onboarding/dispatcher-box.md.template +86 -0
  157. package/skill/vegafactory-setup/assets/control-room/onboarding/new-repo.md.template +13 -0
  158. package/skill/vegafactory-setup/assets/control-room/onboarding/new-teammate.md.template +12 -0
  159. package/skill/vegafactory-setup/assets/control-room/org.md.template +60 -0
  160. package/skill/vegafactory-setup/assets/control-room/people.csv.template +2 -0
  161. package/skill/vegafactory-setup/assets/control-room/repos.md.template +7 -0
  162. package/skill/vegafactory-setup/assets/control-room/rules/CODEOWNERS.template +6 -0
  163. package/skill/vegafactory-setup/assets/control-room/rules/README.md.template +9 -0
  164. package/skill/vegafactory-setup/assets/control-room/rules/stats-privacy.md.template +44 -0
  165. package/skill/vegafactory-setup/assets/control-room/templates/README.md.template +9 -0
  166. package/skill/vegafactory-setup/references/control-room.md +149 -0
  167. package/skill/vegafactory-setup/references/conventions.md +113 -0
  168. package/skill/vegafactory-setup/refresh/REFRESH.md +5 -0
  169. package/skill/vegafactory-setup/refresh/sources.json +6 -0
  170. package/skill-integrity.json +252 -0
@@ -0,0 +1,113 @@
1
+ # Workflow conventions
2
+
3
+ Artifact authority.
4
+
5
+ Defaults resolve repo, group, then org; locks require explicit org delegation. Repository dispatch/commands never inherit; registers concatenate. Policy/migration uses dev-setup's `scripts/effective-policy.mjs` and vegafactory-setup's control-room reference.
6
+
7
+ ## Comment metadata markers
8
+
9
+ Comments open:
10
+
11
+ ```markdown
12
+ <!-- vsk:v1 type=<type> rev=<n> [key=value ...] -->
13
+ ## <Human title> (v<n>)
14
+ ```
15
+
16
+ | type | required keys | instances |
17
+ |---|---|---|
18
+ | `approval` | `scope=<brief\|brief+plan\|plan\|consolidated\|none>` and schema-v2 JSON | one per approval event |
19
+ | `questions` | `rev` | one per ask round; earlier rounds stay as record (dev-setup's `references/ask-route.md`) |
20
+ | `plan` | `rev` | one, edited in place |
21
+ | `ledger` | `branch` | one, edited in place |
22
+ | `evidence` | `rev branch sha` | one, edited in place |
23
+ | `review` | `round sha agent=<claude\|codex> verdict=<clean\|needs-fixes>` | one per review cycle, rounds appended inside |
24
+ | `decision` | — | one per decision proposal |
25
+ | `handback` | — | one per stop event |
26
+
27
+ `rev=<n>` and `(v<n>)` start at 1 only on brief, plan, questions and evidence; other types have neither. Locate by marker, never heading/legacy fallback.
28
+
29
+ ## Operator identity
30
+
31
+ Use parenthesized GitHub usernames:
32
+
33
+ - Approval: `Approved by (<username>) on DD-MM-YYYY: "<their words>"`
34
+ - Register line: `- DD-MM-YYYY (<username>) — <decision>`
35
+
36
+ Approval.mjs’s publisher/relay contract lets current-policy provider-envelope publishers attest listed operators’ session words. Other recorders may only relay independently read identical operator-published scoped grants within complete authority history, without lifecycle mutations. Relays inherit source authority/lifecycle; account attestation cannot authenticate off-platform speech.
37
+
38
+ ## Scoped approval records
39
+
40
+ Use only dev-implement’s `scripts/lib/approval.mjs` and follow its contract. Refresh current policy and complete GitHub histories. `ArtifactRef={repo,issue,kind,artifactId,rev,digest}` binds brief issue-node or unique plan/protocol comment-node identity, revision and canonical SHA-256.
41
+
42
+ Post exactly one approval comment: matching scope marker, then one fenced JSON `ApprovalRecord={schemaVersion:2,id,operator,scope,source:{kind,ref,quote},artifacts,supersedes,revokes}`. Exclude outer Markdown fences, future alternatives and unresolved source locators; validate the whole body with approval.mjs's `parseApproval`. Source kind is `session` or `github-comment`, with inspectable words. Reuse valid current grants/relays; avoid counterfactual plan-only or redundant approvals. Scope is `brief`, `plan` or `brief+plan`; planning requires brief, implementation both, research execution also its protocol. Empty-artifact revocations remove exact earlier IDs. Conflicts explicitly supersede; newest never wins.
43
+
44
+ Preserve legacy comments. Without writes, inventory refusals/current digests and request reconfirmation. For duplicate canonical plans preserve both identities/bodies and request record-preserving reconciliation; never delete to clear ambiguity. Follow approval.mjs’s exact correction schema, operator-publisher and target checks. Only malformed or demonstrably invalid-source targets qualify, never valid authority or unavailable/inconsistent facts. Resolve source facts first; corrections grant no scope. Exact child checkpoint actions bind one selected code child's branch/ref/base/tasks/files; other actions never substitute.
45
+
46
+ Consolidated parent events bind frozen manifests, canonical artifacts and exact task/action subsets. Use inline UTF-8 or immutable repository/commit/path plus blob hash, never local paths. Canonical `approvalBindings` authorize; requested `recordBinding` only audits. Follow approval.mjs’s preparation/research/recovery provenance, receipts, adapters, counted attempts and fresh admission; retain immutable history and unverified legacy records. Keep checkpoint/private/live/shipping gates separate.
47
+
48
+ Canonicalization normalizes CRLF; its only exceptions are structural plan checkboxes and one validated JSON `{tasks:[{id,evidenceUrls}]}` block between `<!-- vsk:progress:start -->` / `<!-- vsk:progress:end -->`. IDs must exist; URLs are HTTP(S); unknown fields/duplicates refuse. Stable task IDs/order, interfaces, actions, revisions and all other bytes remain scope. Brief/protocol bodies have no mutable fields; fenced examples stay immutable and grant no authority.
49
+
50
+ ## Revision markers
51
+
52
+ Scope edits increment marker/heading revisions and append `Revisions: v2 — DD-MM-YYYY: <change>, per (<username>) correction`; preserve earlier lines and obtain fresh approval. Validated progress changes need neither.
53
+
54
+ ## Scope classes
55
+
56
+ Intake explains scope; operator overrides:
57
+
58
+ - **`research`** — inquiry; throwaway code allowed, never merged. No branch/PR/changelog; evidence comment contains findings and recommendation.
59
+ - **`quick-build`** — existing flow: draft brief+plan together, approve both, then `ready`.
60
+ - **`full-plan`** — new ground: approve brief, `needs-plan`, separate grounded planning session, `needs-operator`, approve plan, `ready`. Split multiple deliverables into independently classified epic children.
61
+
62
+ Scope ratchet: `dev-plan`.
63
+
64
+ ## Labels
65
+
66
+ One state; flips set assignees (colors: dev-setup):
67
+
68
+ | label | meaning | assignee |
69
+ |---|---|---|
70
+ | `needs-operator` | question, brief or plan approval, proposal | the operator |
71
+ | `needs-plan` | brief approved; awaiting planning (full-plan only) | the operator |
72
+ | `ready` | approved — an agent may start | nobody |
73
+ | `working` | claimed; ledger shows live progress | the runner |
74
+ | `for-operator` | done — evidence posted, awaiting operator review | the operator |
75
+
76
+ Modifiers coexist with state: `risky` · scope `research` / `quick-build` / `full-plan` · `epic` (map parents without a native Epic type). Boards mirror states one-way.
77
+
78
+ ## Titles, types, hierarchy
79
+
80
+ - **Title prefixes:** issues, branches and PRs use dev.md's `branch:` types plus `research:`; PR title = issue title.
81
+ - **Native issue types/fields:** Feature (feat) · Bug (fix) · Task (docs/chore/refactor/research) · Epic for parents (else label); intake sets Priority/Effort. Scope classes stay labels.
82
+ - **Hierarchy:** epic parent = map only (Destination · Decisions so far · Not clear yet · Out of scope), with native child sub-issues. Issues are work units (brief, approvals, branch, PR, evidence); tasks are checkboxes **only in the plan comment**. Blockers use dependencies; phases use milestones. Only non-epic issues get `ready`.
83
+
84
+ ## The ledger
85
+
86
+ One implementation ledger:
87
+
88
+ ```markdown
89
+ <!-- vsk:v1 type=ledger branch=<branch> -->
90
+ ## Ledger — <branch>
91
+ - <issue>-T<N>: complete (commits <base7>..<head7>[, review clean | K parked])
92
+ - <issue>-T<N>: fix round <R>/3 (<X> addressed, <Y> open — <one-liners>; commits <a>..<b>)
93
+ - Ruling: <what> — <why> — cost if wrong: <cost>
94
+ - <issue>-T<N>: parked — <finding> — Ruling: <why the code stands>
95
+ - Deferred minor: <one-liner>
96
+ ```
97
+
98
+
99
+ **Resume protocol:** brief → plan comment → ledger → `git log`; then reconcile task IDs, canonical approval history, edited authority, source/evidence, ownership and delivery effects. Preserve completed work/provenance; stale heartbeat is not stop proof. Preparation never implies issue completion. Dev-implement's ledger reference owns recovery detail.
100
+
101
+ ## `.vegastack/` workspaces
102
+
103
+ Drafts/reports: `.vegastack/.tmp/<issue-number>-<title-slug>/` (pre-issue: `intake-<slug>`), self-ignored by a `.gitignore` containing `*`. Branch checkouts: root-ignored `.vegastack/.worktrees/<issue-number>-<title-slug>/`; main stays on its default branch. Keep both outside `.git/`. Subagents save full reports and return short status. `<path-to-this-skill>` means SKILL.md’s directory.
104
+
105
+ ## Verification gate
106
+
107
+ Prove claims with fresh command output and exit codes; report failures and skips. Delegate only substantial independent parallel work, never your own verification; keep spawn counts low. Guards block machine-verifiable failures (exit 2); heuristics warn. Guards contain no AI inference; unverifiable state fails closed.
108
+
109
+ ## Review bindings
110
+
111
+ One fenced JSON each: `{"reviewBinding":{sha,baseSha,scopeDigest,verdict,findings:[{id,status}]}}` in review; `{"adjudication":{sha,reviewCommentId,operator,source:{kind,ref,quote},findings:[{id,disposition,reason}]}}` in evidence. Use full commit IDs and canonical-plan scopeDigest; status=open/resolved; disposition=accept-risk. Every open finding requires same-review operator acceptance. dev-ship’s README defines source checks. No prose exceptions.
112
+
113
+ Communicate starts/findings/direction plainly; self-contained outcomes include paths and remaining checks. Avoid invented labels/arrows; visualize usefully. Challenge ambiguity with options; never guess silently.
@@ -0,0 +1,89 @@
1
+ # Data — Postgres, tenancy, storage, caching
2
+
3
+ ## Database
4
+
5
+ - PostgreSQL, always managed by us (a PlanetScale Postgres server or self-hosted
6
+ Hetzner-class) — never Neon. Behind Hyperdrive, target the highest Postgres major
7
+ Hyperdrive supports (pinned-facts — PG 18 is a known trap). Drizzle ORM with the single
8
+ `postgres-js` driver — never mix in `pg`. Stay on GA/stable majors of core DB deps;
9
+ verify vendor version-support claims per SKILL.md's verify protocol before locking a
10
+ decision.
11
+ - From Workers: Hyperdrive binding + per-request client (`prepare: false, max: 1`,
12
+ request-scoped via the execution context) — a module-level pool in a Worker or Durable
13
+ Object is a bug, not a style choice (workerd forbids cross-request I/O). One Hyperdrive
14
+ per environment shared across services hitting the same database — never one per
15
+ service. On long-running Node services (Docker), pool normally.
16
+ - Migrations run via CI only, never pushed from a dev machine — with one loud exception:
17
+ pre-launch databases with zero real users get clean resets instead of migration chains
18
+ (principles.md). Runners apply in journal order, idempotent on re-run. Iterate schema
19
+ with `db:push` during development; run `db:generate` exactly once right before shipping
20
+ — repeated generates mid-iteration create conflicting DDL.
21
+
22
+ ## Multi-tenancy
23
+
24
+ - Shared schema with RLS, not database-per-tenant: every tenant-scoped table (and every
25
+ partition) gets `ENABLE` + `FORCE` ROW LEVEL SECURITY, driven by exactly one fail-closed
26
+ GUC accessor (`NULLIF(current_setting('app.current_org_id', true), '')::uuid`), defined
27
+ in one idempotent migration location.
28
+ - RLS is one layer, never the only layer: every query also scopes explicitly by
29
+ `org_id`/`workspace_id` in the data-access layer. Two roles — the app role has no
30
+ `BYPASSRLS`; a separate system role does. `withOrgContext`/`withSystemContext` (or
31
+ equivalent) are the only query entry points; the raw client is never exported. **Why:**
32
+ RLS misses TimescaleDB chunks and misconfigurations fail open; two independent layers
33
+ fail closed.
34
+ - Tenant identity comes from the authenticated principal, never client-supplied fields —
35
+ the IDOR precedent and rule live in security.md.
36
+ - Better Auth's organization plugin owns the workspace/member/invitation schema
37
+ (security.md); its columns stay snake_case as generated, with native `uuid` columns —
38
+ Better Auth does not force text IDs.
39
+
40
+ ## Caching
41
+
42
+ - No cache layer by default: rate limiting, dedupe, and most "cache" needs are
43
+ Postgres-native. Workers KV only with a named trigger; any Redis-class store is
44
+ optional and **never correctness-bearing**.
45
+ - Never let auth, role, or permission data live in a cache long enough to serve stale
46
+ permissions — a revoked member seeing tenant data is a security bug, not a staleness
47
+ bug. Anything cached has a defined cache-outage story (fall back to DB, never
48
+ stale-forever).
49
+
50
+ ## Search, knowledge, files
51
+
52
+ - Search and embeddings live in Postgres itself: `STORED` generated `tsvector` + GIN for
53
+ full-text; pgvector (`halfvec` + HNSW, `hnsw.iterative_scan = relaxed_order`) for
54
+ embeddings; hybrid fusion via RRF (k=60 starting constant). Embedding default: BGE-M3
55
+ self-hosted; bulk embedding calls skip the AI Gateway (the one carve-out from the
56
+ blanket gateway rule). No dedicated vector DB or SaaS search service.
57
+ - Object storage: R2 by default, always behind an S3-compatible layer so AWS S3/Azure
58
+ Blob/MinIO work for licensed self-hosted deployments. Short-lived scoped access
59
+ (presigned), size/type validation on upload. Keys never expose raw user/workspace IDs —
60
+ documented prefix + nanoid (`wl_`, `ua_` style).
61
+ - Blob GC deletes the storage object before the DB reference row — a crash mid-delete
62
+ must never leave a live dangling reference.
63
+ - TimescaleDB is opportunistic, never required: feature-detect and fall back to native
64
+ `PARTITION BY RANGE` + BRIN in the same migration, so self-hosted installs work on
65
+ plain Postgres.
66
+
67
+ ## Schema discipline
68
+
69
+ - Extend an existing table before creating a new one; when two tables serve the same job,
70
+ merge. Keep sync (`*_change_log`), audit (`audit_log`), and versioning (`*_revisions`)
71
+ as three separate, non-overlapping tables — never substitute one for another.
72
+ - IDs: one global helper — UUIDs (v7 where ordering matters) internal, prefixed nanoids
73
+ public-facing. Timestamps `timestamptz` UTC, rendered in the user's timezone; money in
74
+ integer minor units; durations integer milliseconds. Prefer checked text over Postgres
75
+ enums for evolving vocabularies.
76
+ - Secrets and PII never sit in plaintext columns (including inside JSONB) —
77
+ broker-wrapped envelope encryption only (security.md).
78
+ - JSONB-on-row vs. dedicated table has no house default — MK has ruled both ways by
79
+ context. Present the tradeoff (volume, query needs, audit requirements) and ask.
80
+
81
+ ## Backups and recovery
82
+
83
+ - Self-managed Postgres ships with WAL archiving + scheduled base backups from day one —
84
+ a database without a tested restore path is not production, whatever else is true.
85
+ - A restore runbook (where backups live, how to restore, measured time) is a phase-0
86
+ deliverable for any live product; note RPO/RTO in dev.md's `## Architecture`. Test the
87
+ restore, don't assume it.
88
+ - R2/object storage: no bucket versioning by default — deletion protection is the
89
+ object-before-row GC discipline plus lifecycle rules, planned explicitly.
@@ -0,0 +1,99 @@
1
+ # Infra — hosting, CI cost, observability
2
+
3
+ ## Hosting targets
4
+
5
+ Two sanctioned targets, recorded in dev.md's `## Architecture`:
6
+
7
+ - **Cloudflare Workers via OpenNext** — the default for public web products. Once
8
+ committed to Cloudflare, never stand up a parallel Vercel deploy path (deploy paths
9
+ drift); shared packages must still *work* on Vercel for downstream consumers —
10
+ portability, not a deploy target.
11
+ - **Self-managed servers** (Hetzner-class, Docker; Coolify for push-to-deploy) — when
12
+ self-hosting is a product requirement, the workload needs long-lived processes (EVE,
13
+ heavy workers), or platform independence justifies ~30 min/month of real ops.
14
+ - Vercel only by explicit, recorded per-project exception (e.g. EVE-hosted workloads).
15
+ Internal/admin apps needing no edge features may run plain `next start` in Docker —
16
+ OpenNext is the default, not a ritual.
17
+
18
+ ## Cloudflare discipline
19
+
20
+ - Provision only what the current phase uses. Queues, KV, Durable Objects, Workflows are
21
+ trigger-gated — DO when live collaboration actually ships, Queues when webhook/event
22
+ volume is real — never scaffolded ahead of need. Delete stale Workers, Hyperdrive
23
+ configs, and buckets, with an explicit create/delete accounting first.
24
+ - Multi-env via the Workers environments feature (`env.*`) — never separate top-level
25
+ Workers per environment; environments are `dev` / `preview` / `production`, and the
26
+ `staging` git branch deploys to `preview`. One Hyperdrive per environment, shared by
27
+ every service hitting the same database. Naming: hyphen-only; purge inconsistent legacy
28
+ names.
29
+ - Domains: production on `vegastack.com`; non-prod under `*.vegastack.dev`. Every Worker
30
+ sets `workers_dev: false` + `custom_domain: true` — the default `*.workers.dev` origin
31
+ bypasses Cloudflare Access, exposing a gated Worker unauthenticated (security, not
32
+ cosmetics). Preview environments access-protected by default; only production is public.
33
+ - Secrets per environment (`wrangler secret put X --env production`). Local env files:
34
+ `.env` feeds `next dev`, `.dev.vars` feeds `wrangler dev` — both gitignored, both with
35
+ committed `.example` companions.
36
+ - No Cloudflare Images — optimize with `sharp` at build/upload time and serve from R2 (the
37
+ paid add-on solves a problem `sharp` + free egress already solve).
38
+ - Runtime constraints are architectural facts: no TCP clients, per-request DB connections
39
+ (data.md), and OpenNext trails vanilla Next.js — check OpenNext docs per feature
40
+ (current adapter caveats: pinned-facts).
41
+ - Edge/CDN caching never bypasses live authorization — revoked, expired, or
42
+ password-protected content is re-checked even when the artifact is cached.
43
+ - Cloudflare Tunnel is the default for exposing self-hosted services and remote dev
44
+ previews (already paid for). Cloudflare Access gates paths by data sensitivity with
45
+ email allowlists — not one site-wide gate.
46
+
47
+ ## Self-managed discipline
48
+
49
+ - Containers hardened: non-root user, `cap_drop: [ALL]`, `read_only: true` where runtime
50
+ paths allow; healthchecks on every service. Migrations and storage provisioning run in
51
+ the app container's own entrypoint under a Postgres advisory lock (safe when replicas
52
+ race on boot) — not a separate bootstrap deployable unless a project records why.
53
+ - Ship `docker compose up` as the self-host story: bundle only Postgres + MinIO-class
54
+ essentials; everything else (vector store, gateway, vault) is connect-your-own.
55
+ - Server sizing: SSD, compute, and RAM over disk capacity, within the budget the architecture
56
+ owner states for that server (ask if none stated — don't guess); Coolify's own footprint:
57
+ pinned-facts. Cloud infra beyond Cloudflare goes through Terraform, IAM scoped narrowly per
58
+ purpose.
59
+ - Any infra cleanup (disks, stale resources, runners) needs explicit authorization first,
60
+ scoped to verified-stale items — never blanket cleanup.
61
+
62
+ ## CI cost and verification (workflow gates and ship mechanics live in dev.md)
63
+
64
+ - CI is cost-managed, not maximal: path-condition expensive jobs so docs-only changes skip
65
+ them; push heavy verification into local hooks and self-hosted runners; hosted Actions
66
+ minutes are a tracked budget. Verification is local-first — CI verifies it happened via
67
+ a committed receipt bound to the git tree hash rather than re-executing browser suites;
68
+ a receipt is attestation, not proof — skipping a gate becomes visible, not impossible.
69
+ - Wire every architectural guard script (import boundaries, runtime gravity, route-wrapper
70
+ ratchet) into the one composed check command — a separate script someone forgets to run
71
+ doesn't exist.
72
+ - Green CI is necessary, never sufficient: a unit is done after a real
73
+ boot → auth → reach → mutate → verify pass against live infra. On CI failure: diagnose
74
+ and report the root cause first; fix second.
75
+
76
+ ## Observability (small-team baseline)
77
+
78
+ - Structured JSON logs (pino) to stdout with OTel-shaped fields (trace/request/org context
79
+ auto-injected) — OTel-compatible by convention now; the full OTel SDK/collector only
80
+ when cross-service trace correlation becomes a real pain.
81
+ - The append-only Postgres event log is the authoritative telemetry source; metrics/log
82
+ tables are derived and disposable (30-day raw, 13-month rollups, compress after ~7 days;
83
+ TimescaleDB opportunistic with partition+BRIN fallback).
84
+ - On Cloudflare, use the built-in free tier first — Workers Logs and the analytics
85
+ dashboard (current limits and billing dates: pinned-facts).
86
+ - Alerting honest to team size: an uptime monitor (Uptime Kuma-class) + a cron checking
87
+ error-rate thresholds in the event table posting to Slack, plus a dead-man's-switch ping
88
+ for silent job failures. No paging/SLO/on-call ceremony — deliberately undefined until
89
+ the team needs it; propose per project, don't invent doctrine.
90
+
91
+ ## Incidents
92
+
93
+ - Active production incident: diagnose first (root cause with evidence), fix second — the
94
+ standing rule applies under pressure too. Rolling back a deploy IS a deploy: it needs the
95
+ operator's go-ahead — bring them the evidence and the recommended rollback; mitigation
96
+ that doesn't deploy (feature-level disable, traffic block) can proceed and be reported.
97
+ - Every real incident gets a short postmortem in the project's `docs/postmortems/`: what
98
+ happened, root cause, the guard that now prevents it. An incident that doesn't change a
99
+ rule or a check will repeat.
@@ -0,0 +1,75 @@
1
+ # Mobile — Flutter production doctrine
2
+
3
+ **The Flutter app is a separate repo from the web/API repo** — it consumes the Next.js
4
+ REST/OpenAPI contract as one more client, never a `mobile/` directory in the web monorepo
5
+ (register-recorded, corroborated three ways; an expensive structural call to get wrong).
6
+
7
+ Flutter is the production mobile framework when a product needs a mobile app (not all do —
8
+ dev.md's Architecture `mobile:` line records it). Baseline: Flutter 3.44.x stable;
9
+ Impeller is the default renderer on iOS and Android API 29+ (falls back below 29 — test
10
+ one API<29 device before shipping). Package versions here were verified 2026-08; re-verify
11
+ on pub.dev before pinning. Rules from the shipped VegaStack app are stated plainly; rules
12
+ from official-docs research the architecture owner has not ratified are tagged
13
+ "(inferred)" — confirm on first use, per SKILL.md.
14
+
15
+ ## Architecture (official-guidance derived — inferred where the shipped app is silent)
16
+
17
+ - MVVM per official flutter.dev/app-architecture: View (widget, no logic) → ViewModel
18
+ (state + commands, 1:1 with its View) → Repository (source of truth, caching/retry,
19
+ never depends on another repository) → Service (thin stateless API/platform wrapper).
20
+ Skip the optional domain/use-case layer until logic is reused across ≥2 ViewModels.
21
+ - Layer-first structure: `lib/{data, domain, ui, routing, config}`, feature folders inside
22
+ `ui/`, shared widgets in `ui/core/`. (Blog "feature-first is the standard" claims don't
23
+ survive verification — the official reference app is layer-first; feature-first is a
24
+ valid escalation at scale.) Class names mirror roles: `HomeViewModel`, `UserRepository`.
25
+
26
+ ## State management — lean by default
27
+
28
+ - Plain `ChangeNotifier`/`ValueNotifier` ViewModels with hand-wired constructor injection
29
+ in `main.dart` — zero extra dependencies, zero codegen; matches official guidance and
30
+ what VegaStack has shipped.
31
+ - Escalate to Riverpod only when async state genuinely needs sharing across ≥3 widgets,
32
+ tests need provider-override mocking, or a second app shares a state-heavy module. No
33
+ get_it — official docs steer away from service locators; `provider` is the official DI
34
+ pick if constructor wiring gets unwieldy.
35
+
36
+ ## Networking & auth
37
+
38
+ - One `dio` client centralized in a single `ApiClient` — never scattered HTTP calls; the
39
+ app consumes the same contract-first REST API as the web app (web.md). That versioned
40
+ `/api/v1` contract outlives web deploy cycles: app-store install lag keeps old clients
41
+ alive, so a shipped mobile app counts as real users even while the web side iterates
42
+ freely — the one exception to pre-launch delete-not-migrate and to dead-endpoint
43
+ deletion.
44
+ - Auth is the same Better Auth instance as web, via the bearer plugin: capture the token
45
+ from the `set-auth-token` response header on sign-in; store in `flutter_secure_storage`
46
+ (never shared_preferences); attach `Authorization: Bearer` via a dio interceptor; clear
47
+ storage and route to sign-in on 401.
48
+ - Do NOT depend on `better_auth_flutter` (0.1.0, negligible adoption as of 2026-08) —
49
+ hand-roll the interceptor; revisit at a real 1.0. `shared_preferences` for non-secret
50
+ local metadata only; `drift` only for genuine offline/relational needs — never
51
+ speculatively.
52
+
53
+ ## Design system on mobile
54
+
55
+ Semantic colors via a hand-authored `ColorScheme` plus `ThemeExtension` for tokens outside
56
+ Material's roles — mirroring the web design-system token names 1:1 from one Dart source of
57
+ truth. House taste mapped: `CardTheme(elevation: 0)` with `outlineVariant` borders (flat,
58
+ borders-only); TextTheme capped at `FontWeight.w600`, never bold; subtle motion with
59
+ reduced-motion respected; Lucide-style iconography.
60
+
61
+ ## Navigation, models, testing, deploy
62
+
63
+ - Navigation: the shipped app uses plain `Navigator`; adopt `go_router` when deep
64
+ links/route state demand it, not by reflex (inferred).
65
+ - Models: hand-written by default (what the shipped app does); freezed +
66
+ json_serializable only when codegen demonstrably earns its build cost (inferred).
67
+ - Test where MVVM pays off: unit-test ViewModels and Repositories; widget-test critical
68
+ screens; integration tests only for can't-ship-broken flows (sign-in, payment). Golden
69
+ tests, if adopted: `alchemist` over the discontinued `golden_toolkit` (inferred).
70
+ - Deploy: GitHub Actions is the house CI; Fastlane for store signing/upload and real build
71
+ flavors (`--flavor` + per-env entry points) are the researched defaults (inferred — no
72
+ shipped store-deploy precedent; confirm before wiring).
73
+
74
+ Undecided (ask the architecture owner rather than assume): push-notification provider,
75
+ offline/sync expectations per product, store-release cadence.
@@ -0,0 +1,133 @@
1
+ # Pinned platform facts
2
+
3
+ Dated, source-verified facts that change architecture decisions and that models routinely
4
+ get wrong from stale training data. This file is the verified cache behind SKILL.md's
5
+ verify-before-you-recommend protocol, and the ONLY file in this skill that goes stale by
6
+ itself — the weekly refresh (see refresh/REFRESH.md) re-verifies it. When a recommendation
7
+ leans on a fact older than 60 days, re-verify that one fact against its source first.
8
+
9
+ Facts carry their own verification dates — sections verified 2026-08-12 unless a
10
+ bullet says otherwise; Better Auth and EVE re-verified 2026-08-29.
11
+
12
+ ## Cloudflare
13
+
14
+ - **R2 egress is $0 at any volume.** A 500GB-stored / 2TB-served workload is ~$191/mo on
15
+ S3 vs ~$7.50/mo on R2. Reaching for S3+CloudFront "to control egress" solves a problem R2
16
+ doesn't have. Lifecycle rules, bucket locks, event notifications, and Infrequent Access
17
+ are all live — note IA transitions are one-way via lifecycle (IA→Standard needs a manual
18
+ CopyObject). [developers.cloudflare.com/r2/pricing]
19
+ - **Hyperdrive supports PostgreSQL 9.0–17.x — PG 18 is NOT supported.** Target Postgres 17
20
+ for any Hyperdrive-fronted database; re-check the supported-versions page before ever
21
+ moving to 18. Hyperdrive also supports MySQL (GA 2026-08-07) and private DBs via Workers
22
+ VPC. [developers.cloudflare.com/hyperdrive/reference/supported-databases-and-features]
23
+ - **Durable Objects default to SQLite storage** (real SQL, transactions, point-in-time
24
+ recovery), and **hibernating WebSocket DOs bill ~$0 while idle** — one-DO-per-room is
25
+ cost-competitive with Redis+socket.io for mostly-idle connections.
26
+ [developers.cloudflare.com/durable-objects]
27
+ - **Workflows: 10,000 steps default / 25,000 max, 365-day sleeps — and per-step billing
28
+ since 2026-08-10.** High-step-count designs now have a real cost dimension.
29
+ [developers.cloudflare.com/workflows]
30
+ - **Browser Rendering is metered by duration AND concurrency** ($0.09/browser-hour + $2 per
31
+ extra concurrent browser) — batch scraping through a queue; it is not free headless Chrome.
32
+ [developers.cloudflare.com/browser-rendering]
33
+ - **R2/KV/Workflows event notifications route through Queues "event subscriptions"** — a
34
+ Queue consumer covers platform state changes; don't build a bespoke webhook receiver.
35
+ - **D1 read replication only helps via the Sessions API** (`withSession(bookmark)`);
36
+ without it every query still hits the primary.
37
+
38
+ ## Next.js (16.3, released 2026-08-03)
39
+
40
+ - **PPR flags are gone** — `experimental.ppr` no longer exists; partial prerendering is
41
+ part of `cacheComponents: true` (which also replaced `dynamicIO`). [nextjs.org/blog]
42
+ - **`middleware.ts` is replaced by `proxy.ts` running on Node** — full fs/crypto/native
43
+ package access in request interception.
44
+ - **`proxy.ts`/Node middleware does NOT work on OpenNext Cloudflare as of 2026-08** (open
45
+ issues opennextjs-cloudflare#962/#1277, workers-sdk#13755/#13937) — don't design a
46
+ Cloudflare-hosted feature around `proxy.ts`; re-check the trackers before assuming it
47
+ shipped. Turbopack is supported since adapter v1.15.0 (the old breakage is fixed;
48
+ re-verify only on older pins).
49
+ - **The Adapter API is stable since 16.2** — Vercel's adapter and Cloudflare's OpenNext
50
+ adapter share the same public contract, but Cloudflare's still trails on newest features;
51
+ check the deployment feature matrix per feature, don't assume parity.
52
+ [nextjs.org/docs/app/guides/deploying-to-platforms]
53
+
54
+ ## Cloudflare Workers hard limits
55
+
56
+ - **Memory 128MB per isolate (hard), CPU 30s default / 5min max (configurable), 1000
57
+ subrequests per request (paid).** Heavy transforms (image processing, big parses)
58
+ belong in a container/worker tier, not a Worker. Workers Logs free tier: 200k
59
+ events/day, 3-day retention; automatic tracing starts billing 2026-10-01; OTLP export
60
+ needs Workers Paid. [developers.cloudflare.com/workers/platform/limits]
61
+
62
+ ## Better Auth (1.7.2, 2026-08-29)
63
+
64
+ - **1.7 is stable** — 1.7.0 shipped 2026-08-18, current 1.7.2 (2026-08-26, npm latest).
65
+ The former 1.6.x hold is retired; adopt 1.7.x via the official migration steps. Notable
66
+ 1.7.0 breaks beyond the bullets below: accounts rekeyed on `(issuer, accountId)` with a
67
+ migration backfill; `validAudiences` → `resources` + `oauthClientResource`;
68
+ `experimental.joins` → `advanced.database.joins`; generic OAuth rewritten on OAuth 2.1
69
+ defaults. [github.com/better-auth releases, verified 2026-08-29]
70
+ - **The MCP plugin lives in `@better-auth/mcp` since 1.7.0** — requires
71
+ `@better-auth/cimd` and the `jwt()` plugin; `withMcpAuth` → `requireMcpAuth`,
72
+ `mcpHandler` → `createMcpProtectedRequestHandler`; options are flat, no `oidcConfig`
73
+ nesting. [releases/tag/v1.7.0]
74
+ - **SAML IdP-initiated sign-in is default-off since 1.7.0** (`saml.allowIdpInitiated`
75
+ defaults to false); opt back in explicitly only where the IdP flow is required.
76
+ [releases/tag/v1.7.0, verified 2026-08-29]
77
+ - **The organizations plugin models teams, invitations, and custom RBAC end-to-end**
78
+ (`teams: { enabled: true }`, `inviteMember` with `teamId`, `createAccessControl`) —
79
+ unchanged in 1.7 (SCIM was decoupled from it; irrelevant unless SCIM is used).
80
+ [better-auth.com/docs, verified 2026-08-29]
81
+ - **The apiKey plugin is the standalone `@better-auth/api-key` package** (lockstep
82
+ 1.7.2) — import from `@better-auth/api-key`, not `better-auth/plugins` (core's exports
83
+ no longer carry it). Default to the plugin for new projects; the flagship platform's
84
+ native implementation is a recorded project decision, not the house default. The
85
+ `bearer` plugin stays in core and covers token session transport (the mobile/Flutter
86
+ mechanism). [better-auth.com/docs/plugins/api-key, verified 2026-08-29]
87
+ - **`twoFactor` supports `allowPasswordless: true`** for users without password accounts
88
+ (passkey/OAuth/magic-link signups). Since 1.7.0, `enableTwoFactor` takes and returns a
89
+ discriminated `method: "otp" | "totp"` (default totp) — pass `method: "otp"` for OTP
90
+ enrollment.
91
+
92
+ ## Claude API (verified 03-09-2026)
93
+
94
+ - **Thinking blocks bind to their conversation prefix on Claude Fable 5.1**: replaying one after
95
+ the system prompt, tools, or an earlier message changed returns a 400 for accounts created on
96
+ or after 31-08-2026 (future models: everyone); keep product agent loops append-only.
97
+ **Assistant prefill on the last turn is a 400 from Claude 4.6 onward** — use structured outputs.
98
+ [platform.claude.com/docs/en/build-with-claude/prompt-engineering]
99
+
100
+ ## Agents & jobs
101
+
102
+ - **EVE (`eve` on npm, github.com/vercel/eve) is Vercel's durable-agent framework — v0.47.3
103
+ (2026-08-28), still beta/pre-GA, shipping multiple releases per day; pin behavior, not
104
+ minor versions.** Filesystem-first agents; every session a durable, resumable
105
+ workflow. [vercel.com/docs/eve]
106
+ - **Self-hosted EVE durability (`@workflow/world-postgres`, stable 4.3.x) explicitly
107
+ requires a long-lived worker process — "not compatible with serverless platforms".**
108
+ The 5.0.0-beta channel exists; don't pin it without a documented reason. Internally it
109
+ uses graphile-worker — it is not pg-boss and doesn't replace it.
110
+ [workflow-sdk.dev/worlds/postgres]
111
+ - **pg-boss is at 12.x** — Postgres-native (`SKIP LOCKED`), no Redis. The right default
112
+ for simple background jobs/cron on this stack; BullMQ only when a genuinely complex job
113
+ graph (flows, dependencies, rate-limited pipelines) demands Redis. [npm: pg-boss]
114
+ - **trigger.dev v4 is Apache-2.0 and self-hostable free with unlimited runs** — the
115
+ credible escape hatch when a job needs multi-hour runtimes off-platform. [trigger.dev]
116
+
117
+ ## Databases & mobile
118
+
119
+ - **PlanetScale Postgres is built on Neki, not Vitess** — a newer product (GA 2025-09);
120
+ don't transfer Vitess/MySQL assumptions. No free tier (the free Hobby plan died April
121
+ 2024); Postgres pricing is SKU-based from PS-5 non-HA at $5/mo, HA from ~$15-50/mo.
122
+ ($39/mo figures seen elsewhere are the Vitess/MySQL PS-10 tier — a different product.)
123
+ [planetscale.com/pricing]
124
+ - **Flutter's default renderer is Impeller on iOS, Android, and macOS** — "disable
125
+ Impeller on Android" guidance is stale. [docs.flutter.dev]
126
+
127
+ ## Self-hosting
128
+
129
+ - **Coolify needs ~2GB RAM for its own control plane and requires active patching** —
130
+ two critical-CVE waves in 2026 alone (Jan: beta.445/451; Jun-Jul: CVE-2026-34047/49/50,
131
+ fixed in beta.471) — a recurring pattern, not a closed incident. Size Hetzner VMs
132
+ accordingly (CX22/4GB is the floor for Coolify + one small app); ~30 min/month real
133
+ maintenance, not zero. [coolify.io]
@@ -0,0 +1,117 @@
1
+ # Principles — how VegaStack decides, reviews, and talks
2
+
3
+ Distilled from seven months of MK's sessions and repos. Each rule carries its why — apply
4
+ the reasoning, not just the rule.
5
+
6
+ ## Build lean first
7
+
8
+ One deployable until a concrete requirement forces a split. A single, properly structured
9
+ Next.js app owns the UI, RSC, route handlers, auth, and the REST/OpenAPI control plane — no
10
+ NestJS or Hono beside it, no speculative queue, cache, or worker; a notification sender is
11
+ a cron handler inside the app, not its own deployable. **Why:** agents habitually propose
12
+ services MK then strips out; on a 3-4 person team every moving part is real maintenance,
13
+ and the lean version is usually also the faster, more reliable one. Lean-first governs the
14
+ *count of moving parts*, not the rigor inside each — money, auth, tenancy, audit, and
15
+ secrets always get full rigor.
16
+
17
+ ## Every moving part names its trigger
18
+
19
+ Propose infrastructure only together with the trigger that justifies it, stated in the
20
+ recommendation: "a separate worker WHEN jobs exceed request timeouts", "a queue WHEN volume
21
+ makes inline processing lossy". Provision only what the current build phase actually uses —
22
+ infrastructure tracks real usage, never anticipated usage. **Why:** MK stated "OpenBao
23
+ mandatory in production" and walked it back five days later. Blanket mandates rot; triggers
24
+ stay true as projects differ.
25
+
26
+ ## Pre-launch means delete, not migrate
27
+
28
+ Zero real users = no backward compatibility, no legacy shims, no deprecation windows, no
29
+ feature flags hiding unfinished work. Delete outright; reset the dev database rather than
30
+ writing migration chains. This is the DEFAULT — most VegaStack projects are pre-launch at
31
+ any given time; expand/migrate/contract discipline begins when real users exist. **Why:**
32
+ carrying compatibility for users who don't exist is pure bloat. One exception: a versioned
33
+ API contract consumed by a shipped mobile app counts as real users even while the web side
34
+ iterates freely — app-store install lag keeps old clients alive (mobile.md).
35
+
36
+ ## Reuse before you build new
37
+
38
+ Extend the existing table, service, or spine (ACL, change-log, outbox, realtime channel)
39
+ before creating a parallel one; when two components do the same job, merge them. Promote a
40
+ util to `/lib` the moment a second feature uses it. **Why:** two sources of the same truth
41
+ always drift — the reference-architecture mistake MK explicitly engineers against.
42
+
43
+ ## Enforce boundaries mechanically
44
+
45
+ Architectural boundaries that matter get a CI guard script that fails the build — monorepo
46
+ import direction (apps → packages, never the reverse; workers never import UI/React),
47
+ runtime gravity, server/client separation. **Why:** convention alone was tried and failed;
48
+ a guard script is cheaper than re-reviewing the same violation forever.
49
+
50
+ ## Runtime gravity: long-running work never lives in the request tier
51
+
52
+ Anything that can run long, hold a connection, or outlive a request — agent execution, job
53
+ processing, media pipelines — runs in a separate worker/runner tier, never inside a route
54
+ handler or an OpenNext Worker. **Why:** request-scoped tiers have timeouts and body limits;
55
+ executing workflows inside Next.js handlers is the anti-pattern MK cites most.
56
+
57
+ ## Effort scales with stakes, not habit
58
+
59
+ Security, auth, tenancy, money, and foundations get generous, adversarial treatment.
60
+ Routine features on a small team get medium thoroughness — a cheap reconnaissance pass
61
+ before full-cost work, and no governance ceremony a 3-4 person team won't exercise.
62
+ **Why:** MK's own latest self-correction — his previous architecture skill "got too
63
+ complicated" by applying flagship-platform rigor everywhere.
64
+
65
+ ## Decisions are verified, recorded, and reversible for a reason
66
+
67
+ - Resolve uncertainty with evidence, never the safer-sounding guess (SKILL.md's verify
68
+ protocol). MK refused an unverified "Better Auth forces text UUID columns" claim — false.
69
+ - Present decisions as 2-3 options with a clear recommendation and the tradeoff that
70
+ matters; record the choice as one dated register line — the reader learns the reasoning.
71
+ - MK reverses when: a verified assumption proves false · cleverness regresses UX · a
72
+ heavyweight mandate meets a simple project · his own tooling over-complicates. He
73
+ reverses toward less machinery on low stakes, toward more rigor only on a concrete bug
74
+ or vulnerability. Anticipate this: don't defend machinery he'd delete.
75
+
76
+ ## Context gates rigor — ask, don't assume
77
+
78
+ Rigor flexes on facts, not labels: pre-launch or live · internal, client, or OSS ·
79
+ self-hosted or managed · money/PII or not. These live in dev.md's `## Architecture`
80
+ section. When two recorded decisions conflict or the facts can't answer, ask the
81
+ architecture owner with a recommendation instead of assuming.
82
+
83
+ ## Review discipline (design reviews, audits)
84
+
85
+ - Adversarial by default: assume the work is wrong until disproven — findings or verified
86
+ absence of findings, never praise. Evidence or it doesn't exist: every finding cites
87
+ file:line actually read, quoted verbatim; detection is never a claim of absence.
88
+ - Verify every candidate finding before reporting: verdict true-positive / false-positive
89
+ / duplicate / lower-severity, with disproving evidence for the false positives.
90
+ - Severity, three tiers with required actions: **critical** — exploitable or data-losing;
91
+ blocks ship, the architecture owner signs off on the fix. **production-gate** — fixed
92
+ before the surface serves real users; fine behind pre-launch. **consider** — advisory; log
93
+ and move on. Never round up; judge severity against the project's Architecture facts —
94
+ never surface platform-scale concerns as defects on a simple project.
95
+ - Cheap deterministic checks belong in every review: dead exports, unpaginated lists,
96
+ `SELECT *` at API boundaries, missing tenant/FK indexes, fresh-clone buildability.
97
+ - Coverage without bias: evaluate the review's scope, not just what changed or what you
98
+ built. End honestly: open questions, not-verified items, accepted risks named as such.
99
+
100
+ ## Advise, never gate
101
+
102
+ You recommend; the architecture owner and the team decide. A departure from a
103
+ recommendation becomes one dated accepted-risk line proposed for the register, reported
104
+ honestly in later reviews — never silenced, never blocked on, never re-litigated.
105
+
106
+ ## Voice — a team briefing, not a compliance report
107
+
108
+ Plain language, short sentences, terms defined on first use. Recommendation first, then
109
+ the one risk that matters, then detail in bullets and tables. Plain markdown, no JSON
110
+ blocks. No em dashes, emojis, or hashtags in copy the architecture owner will publish.
111
+
112
+ ## Client engagements (`kind: client`)
113
+
114
+ Same stack defaults, gates, and honesty — a client never gets a looser standard. Scope
115
+ inversion is named the moment it's seen (SOW drift surfaces with options, never silently
116
+ absorbed); client-driven stack overrides are dated register lines with the architecture
117
+ owner's sign-off; the register plus the issue briefs are the handover record.