@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,90 @@
1
+ # Security & auth
2
+
3
+ Auth, tenancy-isolation, and secrets work is the highest-rigor tier: extra precision on
4
+ every change, and a focused security audit after any auth-adjacent change before it's done.
5
+
6
+ ## Authentication (Better Auth, always)
7
+
8
+ - Email/password + Google sign-in is the default configuration; magic link and email OTP
9
+ as flows demand; 2FA TOTP when the product warrants (`allowPasswordless: true` for
10
+ passkey/OAuth-only users). Follow Better Auth's documented APIs — never hand-roll
11
+ session or cache invalidation beside them.
12
+ - The organizations plugin (teams enabled) owns orgs/workspaces/teams/invitations —
13
+ organization maps to workspace (`modelName: "workspace"` pattern); "user groups" of any
14
+ kind are Better Auth teams; never a hand-rolled workspace/membership/groups schema. The
15
+ SSO plugin covers SAML/OIDC when enterprise auth arrives — don't build protocol code.
16
+ - Mobile uses the bearer plugin (token from `set-auth-token`, stored in secure storage) —
17
+ same auth instance, same Postgres, as the web app.
18
+ - Sessions: rolling expiry, Postgres-authoritative. Don't enable cookie-cache-style
19
+ shortcuts without checking Better Auth's current issues — a cookieCache bug once caused
20
+ silent 5-minute logouts in production. Revocation ("sign out everywhere",
21
+ admin-initiated) goes through Better Auth's session-list/revoke APIs server-side.
22
+ - Google OAuth footgun: set `baseURL` explicitly in production config — an unset or wrong
23
+ value silently targets localhost and fails Google's exact-match redirect-URI check.
24
+ - API keys: Better Auth's apiKey plugin by default. Whatever implements them, the
25
+ invariants hold: hash-stored with a recognizable prefix, raw value shown exactly once
26
+ and never cached or logged again, constant-time compare on verify.
27
+
28
+ ## Authorization
29
+
30
+ - Middleware/proxy is never the boundary (CVE-2025-29927 class). The boundary is a
31
+ server-only data-access layer — `requireSession()`/`requireOrgRole()`-style helpers,
32
+ re-checked per resource, on every route, auth before body parse.
33
+ - Every check fails closed: a policy-engine error, throw, or unmatched rule resolves to
34
+ deny, and the deny is still audited. No `DISABLE_AUTH`-style escape hatches, ever;
35
+ dev-only conveniences only when explicitly gated to non-production with zero prod
36
+ behavior change.
37
+ - Role models stay small — Owner/Admin/Member covers most products. A policy engine
38
+ (Cedar-class ABAC) needs a named trigger — fine-grained multi-principal authorization at
39
+ platform scale; today only the flagship platform has earned it. Anyone else proposing
40
+ one: present the trigger and ask the architecture owner.
41
+ - Tenant identity always derives from the authenticated principal — never from a
42
+ client-supplied workspace/org ID (a trusted `?workspaceId=` param caused a real
43
+ cross-tenant IDOR; reject on mismatch). Cross-tenant lookups return 404, never 403 — no
44
+ existence oracle.
45
+ - If ALL UI consumers of a route sit inside the authenticated app shell, the route
46
+ requires auth even when its data "seems public" — trace actual callers before accepting
47
+ "intentionally public". Guest/anonymous write paths get the same rigor as authenticated
48
+ ones — never looser.
49
+ - Tenancy at the data layer: RLS ENABLE+FORCE plus explicit scoping — data.md.
50
+
51
+ ## Secrets
52
+
53
+ - No plaintext secrets anywhere: not in code, config, wrangler.jsonc, generated output,
54
+ logs, audit records (redact to a short prefix), agent state, or JSONB columns.
55
+ Cloudflare Worker secrets and GitHub secrets are the storage; a credential broker
56
+ (envelope AES-256-GCM, AAD bound to org+credential+key-version, fresh per-secret DEK)
57
+ when the product stores third-party credentials.
58
+ - OAuth: authorize/token URLs resolved server-side from a fixed allowlist (never
59
+ caller-supplied); state single-use and session-bound; refresh tokens broker-wrapped.
60
+ - OpenBao/vault infrastructure only on named triggers — self-hosting customer-managed
61
+ secrets, multi-service identity, dynamic DB credentials — never "because production".
62
+ - The operator enters OTP/2FA/credentials themselves, always. Agents never type or
63
+ automate through credential prompts.
64
+
65
+ ## Requests out and untrusted content
66
+
67
+ - All outbound HTTP through one SSRF-hardened egress client: DNS-resolve then deny
68
+ private/loopback/link-local/CGNAT/ULA ranges, pin the socket to the resolved IP, exact
69
+ host allowlist (no suffix matching), re-validate every redirect hop.
70
+ - Body-size limits stream: count bytes as they arrive and abort at the limit — never
71
+ buffer the whole body first.
72
+ - Third-party/webhook/agent content is untrusted input — sanitize before rendering or
73
+ acting on it. The instruction/data separation agent products must keep: ai-agents.md.
74
+
75
+ ## Data protection
76
+
77
+ - Erasure by crypto-shredding: per-subject keys; erase = destroy the key. Implemented
78
+ only when sealing is wired into every PII write path — a defined-but-unused utility is
79
+ a finding, not a feature. Legal hold blocks GC; retention classes derived, not
80
+ hardcoded.
81
+ - Object keys never leak raw user/workspace IDs (data.md). Never log PII beyond user IDs.
82
+ - Audit log: tamper-evident (hash-chained where the product warrants), separate from sync
83
+ and versioning tables, pending-then-settle around side effects. Hash-chain writes lock
84
+ the predecessor row (`SELECT ... FOR UPDATE`) before computing the next hash — unlocked
85
+ concurrent writers silently fork the chain.
86
+
87
+ ## Verifying security findings
88
+
89
+ Scanner/reviewer output follows the review discipline in principles.md — verdict per
90
+ finding, no rounding up. Additionally: every confirmed finding gets a regression test.
@@ -0,0 +1,38 @@
1
+ # Stack — locked decisions
2
+
3
+ The VegaStack default stack. "Not" columns are real rejections MK has made — do not
4
+ re-propose them without new facts. Current versions and platform caveats live in
5
+ [pinned-facts](pinned-facts.md); check there before pinning a version.
6
+
7
+ | Area | Use | Not | Why |
8
+ |---|---|---|---|
9
+ | Web framework | Next.js App Router, one app | NestJS, Hono, a second backend | A properly structured Next app owns web + API + auth for web, Flutter, MCP, and public consumers; a second framework is pure maintenance surface |
10
+ | Runtime / package manager | Bun (default for new projects); pnpm fully sanctioned where chosen | npm, yarn — never | One lockfile discipline per project; dev.md records which. Commit text lockfiles (`bun.lock`, not `bun.lockb`) |
11
+ | Auth | Better Auth, always — default flows, plugins, and the org→workspace mapping: security.md | Hand-rolled auth, custom user-groups schema, Auth0/Clerk | Owner-stated standing rule; prefer the shipped plugins over native builds unless a project records why |
12
+ | Database | PostgreSQL, always self-managed by us: PlanetScale Postgres server or self-hosted (Hetzner or similar), behind Hyperdrive on Workers. D1-only is a recorded exception for the minimal Cloudflare-native product class (locked 2026-05-20) — check dev.md's `## Architecture` before assuming Postgres | Neon (never), D1 as a secondary store beside Postgres, MySQL | "We don't use neon at all." One datastore per product. D1's sanctioned sidecar case: a Worker-scoped idempotency table for Stripe webhooks — Postgres still owns the ledger |
13
+ | ORM | Drizzle + drizzle-kit, single `postgres-js` driver | Prisma | House standard across every repo since Feb 2026 |
14
+ | DB from Workers | Hyperdrive binding, per-request client — discipline in data.md | Global pools, TCP clients (ioredis) in Workers | workerd forbids I/O across requests; a module-level pool is a bug, not a style choice |
15
+ | Storage | Cloudflare R2 (S3-compatible API), presigned URLs, short-lived scoped access | S3 + CloudFront by reflex | R2 egress is $0 at any scale — the cost problem S3+CDN solves doesn't exist here. Keep the S3 protocol boundary so any S3 provider or MinIO works for licensed self-hosted deployments |
16
+ | Cache / Redis | None by default. Rate limiting and caching Postgres-native; Workers KV only with a named trigger (e.g. read-mostly config cache at volumes where a Postgres round-trip measurably hurts — the recorded auth-cache migration); a Redis-class store never correctness-bearing | Upstash Redis (migrated off), Redis "because caching" | "No Redis in P1" — a cache layer is a moving part; Postgres already exists. Never cache authorization/role data aggressively (data.md) |
17
+ | Jobs / cron | pg-boss on the existing Postgres, dispatcher-only — lease/heartbeat/retry state of authority in our own tables. Cron parsing/description: `croner` (DST/IANA-aware) + `cronstrue` | BullMQ+Redis, Temporal, CF Queues by default; `node-cron`, `cron-parser` | Jobs are simple; Postgres is already there. Owning durability state keeps the queue library swappable; a cron-parser swap was made and corrected once already |
18
+ | Agent execution | EVE (Vercel's `eve`) — deploy shapes and constraints: ai-agents.md | EVE inside an OpenNext Worker or any request-scoped/edge function | EVE's Postgres world requires a long-lived worker process; EVE owns agent sessions, pg-boss owns generic jobs — never conflated |
19
+ | AI calls | AI SDK behind a thin adapter; Anthropic default provider; Cloudflare AI Gateway for routing/telemetry when on Cloudflare | Hardcoded model IDs scattered in code, per-provider SDKs everywhere | Adapter keeps providers swappable; the gateway centralizes cost/telemetry without building it |
20
+ | Design system | Consume `@vegastack/design` + `@vegastack/ui` (Base UI primitives, semantic tokens) | Creating/modifying components upstream, raw shadcn edits, Radix for new work | Consume, don't extend — upstream changes are the architecture owner's deliberate decision. Base UI locked over Radix 2026-07 |
21
+ | Frontend state | Server state via RSC + TanStack Query; URL state via nuqs; ephemeral UI state via Zustand | Persisting remote/tenant data client-side (IndexedDB/local-first) for multi-tenant SaaS | Governance: tenant data never rests on the client. Local-first only for a confirmed single-tenant/offline product — ask first |
22
+ | Realtime | SSE tailing an event log first; Durable Objects (SQLite, WebSocket hibernation) when on Cloudflare and bidirectional state is real. One DO per collaboration scope (a `WorkspaceHub` for presence, a per-document `PageSync` for edits) — never one monolithic DO | socket.io + Redis pub/sub, Ably, Pusher | SSE covers most "live" needs with zero new services; hibernating DOs bill ~nothing while idle. Third-party realtime vendors were migrated off |
23
+ | Collaborative editing | Tiptap editor (markdown as source of truth), Yjs CRDT sync via `y-partyserver` inside the document DO; server-side sanitization/versioning applies to every CRDT write | CodeMirror (abandoned 2026-05-21), ElectricSQL (removed), RxDB (reversed to plain Dexie where local cache is needed) | Locked through real reversals; CRDT must never become an XSS/version/audit bypass |
24
+ | Analytics | Self-hosted Plausible (`pb.vegastack.com`), proxied via `next-plausible` where applicable | Google Analytics, Vercel Analytics, PostHog by default | Self-hosted, privacy-clean, already running — recurs across 5 projects |
25
+ | Hosting | Cloudflare Workers via OpenNext, or self-managed servers (Docker; Coolify for push-to-deploy) — recorded in dev.md's Architecture | Vercel as a default (per-project recorded exception only); a parallel Vercel path once committed to Cloudflare | Cost and egress economics favor Cloudflare/self-host; split deploy paths drift. EVE-hosted workloads are a legitimate Vercel exception |
26
+ | Email | AWS SES (behind an adapter); bounces/complaints via the SNS-webhook pattern into a suppression list | Per-vendor SDK sprawl, sending to suppressed addresses | Cheap, boring, proven in-house; unhandled bounces poison sender reputation |
27
+ | Payments | Stripe — money-path mechanics: web.md | Building billing logic inside request handlers | Money paths get the full rigor tier |
28
+ | Monorepo | Turborepo + workspaces (Bun or pnpm) when there is more than one package; apps → packages only, enforced by a boundary check | Deep relative cross-package imports, packages importing apps | Import direction is a guard script, not a convention |
29
+ | i18n | next-intl, `localePrefix: 'as-needed'`; no hardcoded user-facing strings | Ad-hoc string tables | Already the proven pattern; English-only today but scaffolded |
30
+ | APIs | Contract-first: zod schemas → OpenAPI 3.1 — detail: web.md | Route-local validation, raw `request.json()`, unbounded lists | One contract feeds web, Flutter, MCP, and public consumers |
31
+
32
+ ## Choosing between the two hosting targets
33
+
34
+ Cloudflare (OpenNext) when: public web product, global latency matters, R2/DO/Queues fit,
35
+ cost-per-request dominates. Self-managed (Docker/Hetzner) when: self-hosting is a product
36
+ requirement (licensed/enterprise), the workload needs long-lived processes (EVE, heavy
37
+ workers), or platform independence is worth ~30 min/month of ops. Many products use both:
38
+ OpenNext app on Cloudflare + a long-running worker container beside the database.
@@ -0,0 +1,102 @@
1
+ # Web — frontend, backend/API taste
2
+
3
+ ## Project layout
4
+
5
+ - Top-level `server/` (or `packages/*` server code) holds DB, services, integrations —
6
+ never imported from client code, enforced with `server-only` + a boundary check, not
7
+ convention. App router in `src/app/`; features as self-contained modules under
8
+ `src/features/<domain>/`; shared pieces in `src/{components,hooks,lib,store,messages}/`.
9
+ Pages stay thin — they compose features.
10
+ - The house convention scopes agent instructions per directory (a short CLAUDE.md/AGENTS.md
11
+ at each boundary-bearing directory) — follow it where a project already does this.
12
+
13
+ ## Frontend architecture
14
+
15
+ - Server state → RSC + TanStack Query; URL state → nuqs; ephemeral UI state → Zustand.
16
+ Remote/tenant data is never persisted client-side (rejected for multi-tenant SaaS on
17
+ governance grounds; a confirmed single-tenant/offline product is the only exception —
18
+ ask, don't assume).
19
+ - Next config: `output: 'standalone'`, `cacheComponents: true` (PPR flags no longer
20
+ exist — pinned-facts). Query key factories in non-`'use client'` `.keys.ts` modules so
21
+ RSC pages can prefetch. Server Components by default; `'use client'` only at the lowest
22
+ leaf that needs it. Internal navigation uses `next/link`.
23
+ - Request interception on OpenNext/Cloudflare: check pinned-facts before designing around
24
+ `proxy.ts` — and interception is never the auth boundary (security.md).
25
+ - Dependencies: prefer the native/browser API when it covers the need
26
+ (`Intl.RelativeTimeFormat` over date-fns); self-implement simple behavior by composing
27
+ design-system primitives before adding a library; any non-trivial new dependency needs a
28
+ stated justification and approval. **Why:** self-contained code has no supply-chain,
29
+ licensing, or upgrade surface — MK rejects "library because library".
30
+
31
+ ## Design system
32
+
33
+ `vegastack-design-system` owns component choice, tokens, and the do/don't rules;
34
+ first-time wiring is `vegastack-consume`. Consume-don't-extend is a red line (SKILL.md).
35
+ One unresolved item recorded here: touch-target minimum (24px WCAG AA vs 44px mobile-HIG
36
+ both appear in the record) — ask the architecture owner when it matters.
37
+
38
+ ## UI completeness bar (every real surface, before it's "done")
39
+
40
+ - Full state matrix: loading skeleton shaped like the real content, empty state with
41
+ guidance, errors routed to the specific failure (never swallowed into `{}`), success
42
+ feedback, optimistic updates where a mutation changes visible state.
43
+ - Perceived speed is the named "instant shell" pattern: the layout shell renders
44
+ immediately (RSC + `cacheComponents`), every data region streams in behind a
45
+ content-shaped skeleton — never a blank page or full-page spinner.
46
+ - Systemic fixes: a defect found in one component (scroll leaking through an overlay,
47
+ wrong token) is fixed across the whole family in one sweep, with evidence.
48
+ - Long lists (>~50 rows) virtualize with `@tanstack/react-virtual` (approved — still state
49
+ the justification when adding it).
50
+ - Cloning/rebranding a reference site: strip every trace of the source's provenance (class
51
+ names, asset domains, meta) and verify parity by clicking through as a real user —
52
+ screenshot diffing alone doesn't count.
53
+
54
+ ## SEO and metadata (every public-facing app)
55
+
56
+ Every route exports `metadata`/`generateMetadata` — not just the root layout. Public apps
57
+ ship `sitemap.ts` and `robots.ts`; `llms.txt` is generated at build time, never
58
+ hand-maintained; OG images follow the house convention (generated, mono for names/numbers).
59
+ **Why:** MK has demanded "100% SEO" on shipped sites twice — a completeness bar, not an
60
+ enhancement.
61
+
62
+ ## Testing — stack-specific taste (whether tests are required: dev.md's `tests:` knob)
63
+
64
+ - Component/a11y tests run in Vitest browser mode with the Playwright provider — jsdom's
65
+ ARIA/layout gaps produce false a11y results and are not trusted.
66
+ - E2E is a walking skeleton: one real happy path (boot → auth → core mutation → verify)
67
+ plus targeted adversarial invariants — not a blanket suite. DB-gated tests are authored
68
+ even when execution is blocked, behind an env flag (`RUN_DB_TESTS=1`); E2E doubles
69
+ implement real ports, never a parallel mock pathway.
70
+ - VRT/demo content is deterministic (no `Date.now()`/`Math.random()`). "100%" means audit
71
+ completeness, never a coverage metric.
72
+
73
+ ## Backend / API design
74
+
75
+ - Contract-first: zod schemas are the single source → OpenAPI 3.1 under `/api/v1`,
76
+ camelCase, cursor pagination on every list, RFC 9457 problem-details errors with a
77
+ stable error-code catalog. One contract serves web, Flutter, MCP, and public consumers.
78
+ - Every route: auth before parse, through the shared route-handler wrapper — no
79
+ route-local zod, no raw `request.json()`; responses sanitized (secrets and credential
80
+ references never leave the server). Enforced with a CI ratchet, not review.
81
+ - Return what the view renders — no mega-responses; select columns explicitly.
82
+ - Webhooks over polling; the database is the source of truth. Once provider state lands in
83
+ Postgres via webhook, don't re-poll — and no intermediary "waiting" pages; redirect on
84
+ success and let the rest arrive in the background.
85
+ - Money paths (Stripe): isolated intake (own worker or route), idempotency keys on every
86
+ mutation, event dedupe, append-only ledgers as the single cost source of truth, audit
87
+ events in the same transaction as the business change. Pricing math is deterministic
88
+ code with server-enforced invariants — AI drafts and judges; deterministic code
89
+ calculates and enforces.
90
+ - Centralize the boring: one ID helper, one logger (never `console.log`; request/org
91
+ context auto-injected; never log secrets or PII beyond user IDs), one typed fail-hard
92
+ zod env parser validated at startup.
93
+ - Dead endpoints get deleted, not deprecated-and-kept (exception: a versioned `/api/v1`
94
+ contract consumed by a shipped mobile app — mobile.md).
95
+ - Outbound webhooks to customers: defined event catalog, Standard Webhooks-style signing
96
+ with rotatable secrets, delivery via transactional outbox → queue with retries/backoff,
97
+ auto-disable after N consecutive failures, a replay surface — never fired inline from
98
+ request handlers.
99
+ - Platform-native mentions over plain text: resolve and mention the real entity (Slack
100
+ @mentions, Notion Person fields) instead of rendering names as text.
101
+ - Wizard/checkout flow-state persistence (client store vs server-side draft) is not a
102
+ settled house rule — ask the architecture owner when building one.
@@ -0,0 +1,31 @@
1
+ # Freshness contract — dev-architect
2
+
3
+ Most of this skill is durable taste and recorded decisions; it does not go stale on its
4
+ own. The decaying surface is `references/pinned-facts.md` — the verified cache behind
5
+ SKILL.md's verify-before-you-recommend protocol — plus the version claims embedded in
6
+ `references/mobile.md` and the fact-adjacent lines the registry's `affected` lists name.
7
+
8
+ ## Mechanism
9
+
10
+ 1. **Weekly scheduled agent (primary).** A scheduled Claude Code/Codex job re-verifies
11
+ each pinned fact against its stated source URL and opens ONE pull request quoting
12
+ evidence for anything that changed (fact text, version, date, source). The diffing
13
+ judgment lives in the agent run, not in maintained scripts. The PR is human-reviewed —
14
+ never auto-merged.
15
+ 2. **Refresh-on-use (safety net, written into SKILL.md).** The verify protocol: a
16
+ recommendation leaning on a pinned fact older than 60 days re-verifies that one fact
17
+ first and says so; an uncached decision-bearing claim is verified against live docs
18
+ before it is recommended. Never bulk-refresh in-session.
19
+ 3. **Registry baseline (`sources.json`).** The repo-shared refresh runner
20
+ (`tooling/refresh/refresh-evidence.mjs`) keeps checksum baselines for the critical
21
+ source pages so CI can detect upstream drift deterministically between weekly runs.
22
+
23
+ ## Rules
24
+
25
+ - Critical sources (Hyperdrive supported versions, Better Auth docs, eve/Workflow SDK
26
+ deploy constraints) get `critical: true` — drift there warrants a prompt PR, not a
27
+ batch.
28
+ - Every edited fact keeps the pattern: fact → why it changes a decision → source URL →
29
+ verified date. The file carries one blanket verified-date for facts checked together;
30
+ a fact re-verified alone gets its own inline date, superseding the blanket one.
31
+ - A fact that stops being decision-changing is deleted, not kept for completeness.
@@ -0,0 +1,239 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "policy": {
4
+ "criticalTopics": [
5
+ "hyperdrive-postgres",
6
+ "better-auth",
7
+ "eve-deploy"
8
+ ],
9
+ "defaultChecksumScope": "html-text-v1",
10
+ "offline": "Use cached metadata only; fail closed when a critical entry is missing or older than thresholdDays.",
11
+ "drift": "Any drift requires reading the changed page and a human-reviewed PR updating references/pinned-facts.md plus every other file the source's affected list names; never auto-apply.",
12
+ "copyright": "Store claim metadata, URLs, hashes, and concise excerpts only; never archive third-party documentation corpora.",
13
+ "cadence": "weekly scheduled-agent refresh; thresholdDays must be >= 14 (2x cadence) so a single missed run never breaches a threshold"
14
+ },
15
+ "sources": [
16
+ {
17
+ "id": "CF-HYPERDRIVE-VERSIONS",
18
+ "service": "Cloudflare Hyperdrive supported databases",
19
+ "kind": "official-docs",
20
+ "stability": "vendor-docs",
21
+ "thresholdDays": 14,
22
+ "critical": true,
23
+ "urls": {
24
+ "primary": "https://developers.cloudflare.com/hyperdrive/reference/supported-databases-and-features/"
25
+ },
26
+ "versionDetection": {
27
+ "type": "manual-review"
28
+ },
29
+ "topics": [
30
+ "hyperdrive-postgres"
31
+ ],
32
+ "affected": [
33
+ "references/pinned-facts.md",
34
+ "references/data.md"
35
+ ],
36
+ "checksum": "fda2870b95deffce48dca7f3df3707325b1bc4a2d58a17708ee9220bfba5eba1",
37
+ "retrievedAt": "2026-08-29T05:56:59.737Z"
38
+ },
39
+ {
40
+ "id": "CF-R2-PRICING",
41
+ "service": "Cloudflare R2 pricing",
42
+ "kind": "official-docs",
43
+ "stability": "vendor-docs",
44
+ "thresholdDays": 30,
45
+ "critical": false,
46
+ "urls": {
47
+ "primary": "https://developers.cloudflare.com/r2/pricing/"
48
+ },
49
+ "versionDetection": {
50
+ "type": "manual-review"
51
+ },
52
+ "topics": [
53
+ "r2-economics"
54
+ ],
55
+ "affected": [
56
+ "references/pinned-facts.md",
57
+ "references/stack.md"
58
+ ],
59
+ "checksum": "bd5e8d26f7bbf2f92182ad4ac1a59ae9b1d90695120062adaf54c281bd88e81f",
60
+ "retrievedAt": "2026-08-29T05:56:59.737Z"
61
+ },
62
+ {
63
+ "id": "CF-WORKFLOWS-PRICING",
64
+ "service": "Cloudflare Workflows limits and pricing",
65
+ "kind": "official-docs",
66
+ "stability": "vendor-docs",
67
+ "thresholdDays": 30,
68
+ "critical": false,
69
+ "urls": {
70
+ "primary": "https://developers.cloudflare.com/workflows/reference/limits/"
71
+ },
72
+ "versionDetection": {
73
+ "type": "manual-review"
74
+ },
75
+ "topics": [
76
+ "workflows"
77
+ ],
78
+ "affected": [
79
+ "references/pinned-facts.md",
80
+ "references/ai-agents.md"
81
+ ],
82
+ "checksum": "64e10ca4ced4edc042d12a9a24f2e165ea71cb9481c30ac5f067f2f6c9218cda",
83
+ "retrievedAt": "2026-08-29T05:56:59.737Z"
84
+ },
85
+ {
86
+ "id": "BETTER-AUTH-DOCS",
87
+ "service": "Better Auth documentation",
88
+ "kind": "official-docs",
89
+ "stability": "vendor-docs",
90
+ "thresholdDays": 14,
91
+ "critical": true,
92
+ "urls": {
93
+ "primary": "https://better-auth.com/docs/plugins/organization"
94
+ },
95
+ "versionDetection": {
96
+ "type": "npm",
97
+ "package": "better-auth"
98
+ },
99
+ "topics": [
100
+ "better-auth"
101
+ ],
102
+ "affected": [
103
+ "references/pinned-facts.md",
104
+ "references/security.md"
105
+ ],
106
+ "currentVersion": "1.7.2",
107
+ "versionCheckedAt": "2026-08-29T05:56:59.737Z",
108
+ "checksum": "905fef0ef5b216abae2fcb14c7c7ea9b74c4788acc7fd65e8ad72600b13d670f",
109
+ "retrievedAt": "2026-08-29T05:56:59.737Z"
110
+ },
111
+ {
112
+ "id": "EVE-DOCS",
113
+ "service": "Vercel eve agent framework",
114
+ "kind": "official-docs",
115
+ "stability": "beta",
116
+ "thresholdDays": 14,
117
+ "critical": true,
118
+ "urls": {
119
+ "primary": "https://vercel.com/docs/eve"
120
+ },
121
+ "versionDetection": {
122
+ "type": "npm",
123
+ "package": "eve"
124
+ },
125
+ "topics": [
126
+ "eve-deploy"
127
+ ],
128
+ "affected": [
129
+ "references/pinned-facts.md",
130
+ "references/ai-agents.md"
131
+ ],
132
+ "currentVersion": "0.47.3",
133
+ "versionCheckedAt": "2026-08-29T05:56:59.737Z",
134
+ "checksum": "84a499a2bc49274d6736703b0d82b774822c7d5e9d69954a73262eff4af72614",
135
+ "retrievedAt": "2026-08-29T05:56:59.737Z"
136
+ },
137
+ {
138
+ "id": "WORKFLOW-SDK-POSTGRES",
139
+ "service": "Workflow SDK Postgres world deployment constraints",
140
+ "kind": "official-docs",
141
+ "stability": "vendor-docs",
142
+ "thresholdDays": 14,
143
+ "critical": true,
144
+ "urls": {
145
+ "primary": "https://workflow-sdk.dev/worlds/postgres"
146
+ },
147
+ "versionDetection": {
148
+ "type": "npm",
149
+ "package": "@workflow/world-postgres"
150
+ },
151
+ "topics": [
152
+ "eve-deploy"
153
+ ],
154
+ "affected": [
155
+ "references/pinned-facts.md",
156
+ "references/ai-agents.md"
157
+ ],
158
+ "currentVersion": "4.3.5",
159
+ "versionCheckedAt": "2026-08-29T05:56:59.737Z",
160
+ "checksum": "d99e33159e2259f8f71d22f2bd19ffbc284ff55e1d8e9916d4930725fb55461a",
161
+ "retrievedAt": "2026-08-29T05:56:59.737Z"
162
+ },
163
+ {
164
+ "id": "NEXTJS-BLOG",
165
+ "service": "Next.js release blog",
166
+ "kind": "official-docs",
167
+ "stability": "vendor-docs",
168
+ "thresholdDays": 30,
169
+ "critical": false,
170
+ "urls": {
171
+ "primary": "https://nextjs.org/blog"
172
+ },
173
+ "versionDetection": {
174
+ "type": "npm",
175
+ "package": "next"
176
+ },
177
+ "topics": [
178
+ "nextjs"
179
+ ],
180
+ "affected": [
181
+ "references/pinned-facts.md",
182
+ "references/web.md"
183
+ ],
184
+ "currentVersion": "16.3.3",
185
+ "versionCheckedAt": "2026-08-29T05:56:59.737Z",
186
+ "checksum": "5485cefce17dd80d1062364c597dbef4ebe39b7a59e869458b53d618c4f6f57d",
187
+ "retrievedAt": "2026-08-29T05:56:59.737Z"
188
+ },
189
+ {
190
+ "id": "PG-BOSS-NPM",
191
+ "service": "pg-boss job queue",
192
+ "kind": "package-registry",
193
+ "stability": "stable",
194
+ "thresholdDays": 30,
195
+ "critical": false,
196
+ "urls": {
197
+ "primary": "https://registry.npmjs.org/pg-boss/latest"
198
+ },
199
+ "versionDetection": {
200
+ "type": "npm",
201
+ "package": "pg-boss"
202
+ },
203
+ "topics": [
204
+ "jobs"
205
+ ],
206
+ "affected": [
207
+ "references/pinned-facts.md"
208
+ ],
209
+ "currentVersion": "12.28.1",
210
+ "versionCheckedAt": "2026-08-29T05:56:59.737Z",
211
+ "checksumScope": "http-body",
212
+ "checksum": "6d1863268232526ae87582d9cba6f7ba148d91888409456a033c1f6b7e2f00f4",
213
+ "retrievedAt": "2026-08-29T05:56:59.737Z"
214
+ },
215
+ {
216
+ "id": "FLUTTER-RELEASES",
217
+ "service": "Flutter release notes",
218
+ "kind": "official-docs",
219
+ "stability": "vendor-docs",
220
+ "thresholdDays": 30,
221
+ "critical": false,
222
+ "urls": {
223
+ "primary": "https://docs.flutter.dev/release/release-notes"
224
+ },
225
+ "versionDetection": {
226
+ "type": "manual-review"
227
+ },
228
+ "topics": [
229
+ "flutter"
230
+ ],
231
+ "affected": [
232
+ "references/pinned-facts.md",
233
+ "references/mobile.md"
234
+ ],
235
+ "checksum": "833a60e3142d8944af96eb33a6266fb27cdbfedb3f72db9765098f81bdabfa9d",
236
+ "retrievedAt": "2026-08-29T05:56:59.737Z"
237
+ }
238
+ ]
239
+ }
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: dev-chronicle
3
+ description: The project's narrative record — what got built, why, and how it went, in plain language for the operator's future recall. Use when asked to "catch me up on this project", "what did we build here", "what happened in this repo", "tell me the story so far", "what's the project story", when a chronicle entry needs writing for finished work, or when dev-implement's hand-back cites the chronicle format. Not for the consumer-facing changelog (dev-implement writes those per the changelog knob), release notes (dev-ship), or current board state ("what needs me" is dev-status).
4
+ ---
5
+
6
+ # dev-chronicle
7
+
8
+ Act: write the project's story for the operator who returns months later — exact terms, plain language, one honest line on how it went.
9
+
10
+ `.vegastack/chronicle.md` is the project's story, newest first — the answer to "what did I build here and what happened?" months later, when the operator remembers nothing. Entries are **story language for a human**, because the changelog already tells consumers what changed and the chronicle is for the operator who wants to know what happened.
11
+
12
+ Nearest neighbors: `dev-implement` writes the entries at hand-back (the write rule lives there; the format lives here); `dev-status` answers "what needs me now" — this skill answers "how did we get here". `dev-ship`'s ship-gate checks entry presence when dev.md says `chronicle: on`.
13
+
14
+ ## The entry — one per behavior-changing branch
15
+
16
+ ```markdown
17
+ ## DD-MM-YYYY — <title: the change as a human outcome — not a mechanism, not a commit subject> ([#<issue>](<issue url>))
18
+
19
+ - **What:** <2–4 plain sentences: what exists now that didn't, from the operator's point of view>
20
+ - **Why:** <the need that prompted it>
21
+ - **How it went:** <the honest one-liner: smooth / what fought back / what was cut>
22
+ - **Changed:** <the user-visible changes, simple words — sub-bullets or one ·-separated line>
23
+ - **Decisions:** <register lines it produced, or "none">
24
+
25
+ — approved by (<username>) · built by <agent> · branch <name>
26
+ ```
27
+
28
+ - Titles name the outcome ("Invoice reminders now chase late payers"), because the mechanism ("add reminderAt column") is the commit subject's job. Issue references are full markdown links to `…/issues/<n>` (correct for PRs too — GitHub redirects), because file views don't auto-link a bare `#N`.
29
+ - The fields are list items and the footer sits after a blank line — single newlines soft-wrap into one paragraph in rendered markdown; bullets are what guarantee a line per field.
30
+ - Prepend — newest first. File missing → create it with a two-line header naming this skill as the format home.
31
+ - **How it went** is where honesty lives: what fought back, what was cut, what surprised. "Smooth" is a fine answer; silence is not.
32
+ - Research issues get an entry only when the findings changed direction; docs/test-only merges get none (ship-gate's excuse flag covers both records at once).
33
+ - A notable ship event — rollback, failed release — becomes its own short entry on the next branch. Entries are append-only like the register — a typo is the one edit — because a rewritten chapter is a story nobody can trust.
34
+ - An entry runs 80–200 words; the digest scales with the ask.
35
+
36
+ ## Style — tone, not length
37
+
38
+ dev.md's `chronicle-style:` knob (`plain` default · `story` · `witty`) sets the voice and `emoji:` (`none` default · `sparing`) the emoji budget; the rule every style follows, the boundary of `witty`, and one worked example per style live in [styles](references/styles.md). In every style, domain keywords stay exact and every factual field says what it means, because the operator searches the chronicle for the terms they remember.
39
+
40
+ ## The digest — "catch me up"
41
+
42
+ On "catch me up on this project" (or any story-so-far ask), read only the chronicle and the decision register, because the digest is the story as told, not reconstructed, and render three parts, plain language throughout:
43
+
44
+ 1. **The story so far** — 3–5 sentences: what this project is, the arc of what's been built, where it stands.
45
+ 2. **Recent chapters** — the last 3–7 entries, one line each: date, the outcome title, and the one thing worth remembering from How-it-went.
46
+ 3. **Open threads** — pending decisions the register hasn't recorded, entries whose How-it-went named unfinished business, and (when `dev-status` is installed) a one-line pointer to run it for the live board.
47
+
48
+ Length scales with the ask: "catch me up quickly" is one paragraph; a returning-after-months operator gets all three parts. A young project with three entries gets three honest lines, because padding is the mannered prose the style rule excludes.
49
+
50
+ ## Setup
51
+
52
+ The `chronicle:` knob in dev.md (`on` default | `off`) governs whether dev-implement writes entries and ship-gate checks them; `chronicle-style:` and `emoji:` govern how they read; `dev-setup` writes all three. A project that turns it on mid-life starts from now — no retroactive backfill unless the operator asks, and then it's marked as reconstructed.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "dev-chronicle"
3
+ short_description: "The project's story — entries and the catch-me-up digest"
4
+ default_prompt: "Use $dev-chronicle to catch me up on this project."