tech-stack
Technology guidance and stack selection: starting points, viable alternatives, decision-making method, and proposal template for new projects and modules.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Technology guidance and stack selection: starting points, viable alternatives, decision-making method, and proposal template for new projects and modules.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Schema and migration semantics for /dr-doctor — thin one-liner contract, 6-pass migration, data-loss safety, conflict resolution. Loaded by self-heal.
Core Datarim rules. Load this entry first, then only the fragment needed for paths, storage, numbering, backlog, routing, or archive behavior.
Post-QA hardening — detects task type (code, docs, research, legal, content, infra) and applies the matching verification checklist before archiving.
Testing pyramid, frameworks, mocking. Load first; then the fragment for the active gate (live smoke, silent failure, bats, legacy triage).
Preserve Datarim task continuity while orchestrated Claude Code or Codex sessions compact or clear context at deterministic pressure thresholds.
Immutability contract for all pipeline stages: artefact freeze, V-AC parity, non-code parity, anti-tautological rule, and return-to-source transition.
| name | tech-stack |
| description | Technology guidance and stack selection: starting points, viable alternatives, decision-making method, and proposal template for new projects and modules. |
TL;DR: When creating a new project, service, or module, use the Default Recommendation from the table below as your starting point. For non-routine cases (cross-domain, stack-migration, operator-request), generate a Stack Proposal with 2-3 candidates with explicit trade-offs and a recommendation. The operator chooses; the choice is recorded as a decision note in the plan and bound by the immutability contract.
Before selecting a stack, classify the task using this table. The classifier produces an explicit output token: Trigger: FULL (generate proposal) or Trigger: SKIP (use Default Recommendation).
| Signal | Action | Example |
|---|---|---|
| New project or service scaffold | FULL — generate proposal | Creating a new microservice, CLI tool, or frontend app |
| New component whose domain differs from the incumbent stack | FULL — generate proposal | Adding WebSocket pub/sub to an HTTP-only NestJS service; adding a CLI tool to a web-service project; adding a frontend to a backend-only project |
| Stack migration | FULL — generate proposal | Task explicitly names a stack change (Express → Fastify, REST → GraphQL, JS → TS) |
| Operator explicitly requests | FULL — generate proposal | Operator asks "compare X vs Y for Z" or "what should we use for..." |
| Routine work in the same domain | SKIP — use incumbent or Default Recommendation | Bug fix, minor feature, same-domain CRUD endpoint in an existing service |
| L1 tasks (any kind) | SKIP — no proposal for L1 | Quick fixes, single-file changes |
Default catch-all: When the classifier cannot unambiguously categorise a task (ambiguous domain boundary, uncertain incumbent fit), default to Trigger: FULL. The cost of an unnecessary proposal is lower than the cost of suppressing a legitimate choice.
Sticky choices: Once a stack is chosen for a project, subsequent same-project components auto-inherit the choice unless explicitly overridden. Child tasks inherit the parent's stack for the same component automatically. A new proposal fires only when a child introduces a component in a domain the parent did not address.
Each row shows the Default Recommendation (starting point for routine work), Viable Alternatives (for proposal generation), and When to Reconsider (verifiable litmus tests — if the test passes, generate a proposal).
| Domain | Default Recommendation | Viable Alternatives | When to Reconsider |
|---|---|---|---|
| Static Landing | HTML, CSS, Tailwind CSS, Alpine.js (opt). NO SPA. Docker optional. | Astro (content-heavy), Hugo (Markdown-driven) | More than 3 distinct page templates with shared navigation and blog/news section |
| Static Multi-Page | PHP, HTML, Tailwind CSS. SSR templates. NO SPA. Docker required. | Astro + MDX, Eleventy | Content changes more than weekly; non-technical editors need a CMS |
| Web Frontend (SEO) | Next.js, React, Tailwind, shadcn/ui, Vite, pnpm, Vitest, Playwright. Docker. | Remix/React Router v7 (simpler mental model, web-standards-first), Astro (content-heavy, zero-JS-by-default) | More than 80% of pages are static content; interactive SPA sections on fewer than 20% of routes; Shopify/Hydrogen storefront |
| SPA / Dashboard | Vite, React or Vue, Tailwind, TanStack Query, Vitest. Docker. | MUI + Emotion (enterprise data grids, Material Design acceptable), Mantine (free data table, 60+ hooks, first-class dark mode), shadcn/ui + Radix (full design control, Tailwind-native) | More than 5 distinct table views with sorting, filtering, and inline editing; dark mode is a requirement; dedicated design system needed |
| Domain | Default Recommendation | Viable Alternatives | When to Reconsider |
|---|---|---|---|
| Microservice API | NestJS, Fastify, PostgreSQL, Prisma, Redis, NATS, Docker Compose. | Fastify-plain (higher throughput, less boilerplate), Encore.ts (AI-agent productivity, infra-as-code), Hono (edge-deployable, multi-runtime) | Edge deployment required; team size <3 (NestJS DI overhead); AI writes >30% of backend code |
| High-Load HTTP API | Node.js, Fastify, PostgreSQL, Redis, k6. Docker. | Go + stdlib + chi (throughput-critical, lower tail latency), Rust + axum (memory-constrained, CPU-bound) | P99 latency budget <10ms; sustained >50k req/s per instance; GC pause sensitivity |
| Python API (Modern) | Python, FastAPI, uvicorn, uv, ruff, pydantic, sqlalchemy, alembic, pytest. Docker. | Litestar (lower overhead, native dependency injection), Django Ninja (Django ecosystem needed) | Django ORM / admin required; real-time WebSocket-heavy (Django Channels) |
| API Gateway / BFF | Node.js, Fastify, Zod, OpenAPI. Docker. | Hono (edge-deployable, lower cold-start), Envoy + ext_authz (data-plane proxy) | Deploying to CDN edge (Cloudflare Workers); need request transformation at proxy layer |
Ecosystem backend mandate:
documentation/architecture/backend-stack-standards.mdis the authoritative mandate for Arcanada backend projects. The rows above function as guidance within that constraint — the backend-standards document takes precedence where they differ.
| Domain | Default Recommendation | Viable Alternatives | When to Reconsider |
|---|---|---|---|
| AI / LLM API | Python, FastAPI, uv, ruff, OpenAI/OpenRouter SDK, Redis. Docker. | Go + OpenAI Go SDK (lower memory, single-binary deploy), TypeScript + Vercel AI SDK (full-stack JS shop) | Multi-model orchestration across >3 providers; streaming aggregation needed |
| AI Pipelines / RAG | Python, FastAPI, uv, ruff, LangChain/LlamaIndex, pgvector/Qdrant, Redis. Docker Compose. | Haystack (document-processing pipelines), custom + pgvector (lighter weight, fewer abstractions) | Pipeline has <3 steps (LangChain overhead > value); embedding model is fine-tuned (custom inference path) |
| Search / Semantic | Python, FastAPI, uv, ruff, pgvector/Qdrant/Weaviate. Docker Compose. | Meilisearch (full-text-first, simpler ops), Typesense (typo-tolerant, lower memory) | Hybrid search (vector + keyword) is the primary query pattern |
| Domain | Default Recommendation | Viable Alternatives | When to Reconsider |
|---|---|---|---|
| Real-Time Chat | Node.js, Socket.IO or ws, Redis. Docker Compose. | Elixir + Phoenix (high-concurrency, fault-tolerant), Go + gorilla/websocket (lower memory per connection) | >10k concurrent connections per instance; soft-real-time latency budget <50ms |
| Audio / Video | Node.js, WebRTC, mediasoup/LiveKit, Redis. Docker Compose. | LiveKit Cloud (managed SFU, no self-host ops), Janus Gateway (C-based, lowest latency) | SFU self-hosting ops cost exceeds managed service cost; sub-50ms latency requirement |
| WebSockets-Only | Node.js, ws or uWebSockets.js, Redis Pub/Sub. Docker. | Go + gorilla/websocket + Redis, Elixir + Phoenix Channels | >50k persistent connections; broadcast-to-all pattern dominant |
| Domain | Default Recommendation | Viable Alternatives | When to Reconsider |
|---|---|---|---|
| Background Jobs | Python, FastAPI, Celery/Dramatiq, Redis/Kafka. Docker Compose. | Go + asynq/temporal.io (type-safe workflows, lower memory), BullMQ + Node.js (JS-native, simpler ops) | Workflow DAG complexity >10 nodes; exactly-once semantics required; multi-hour job durations |
| Python Workers | Python, Celery/Dramatiq, Redis/RabbitMQ, uv, ruff. Docker Compose. | Dramatiq (simpler API, lower overhead, no Celery flower dependency), SAQ (async-native, FastAPI integration) | Task rate >1k/s (Celery broker overhead); async-only codebase (no sync workers) |
| Event-Driven | NestJS or FastAPI, NATS/Kafka, OpenTelemetry. Docker Compose. | Go + watermill (type-safe, lower memory), Rust + lapin/rdkafka (throughput-critical) | Event throughput >100k msg/s; event replay and long-term retention needed (Kafka over NATS) |
| Domain | Default Recommendation | Viable Alternatives | When to Reconsider |
|---|---|---|---|
| File / Media Processing | Python, FFmpeg, Celery, S3-compatible storage. Docker Compose. | Go + ffmpeg-go bindings (lower memory, single binary), Rust + ffmpeg-next (CPU-bound transcoding) | Processing >100 files/hour; GPU transcoding pipeline (NVENC/VAAPI) |
| Streaming Platform | Node.js, WebRTC, mediasoup, FFmpeg, CDN. Docker Compose. | Go + pion (WebRTC in Go, lower memory), Elixir + Membrane (pipeline-native, fault-tolerant) | Transcoding-per-stream model (not just relay); adaptive bitrate ladder >5 rungs |
| Domain | Default Recommendation | Viable Alternatives | When to Reconsider |
|---|---|---|---|
| Monorepo | Nx, TypeScript, pnpm, shared libraries, CI/CD. Docker. | Turborepo (simpler caching model, faster for small repos), Bazel (multi-language, very large repos) | Multi-language repo (Rust + TypeScript + Python); repo exceeds 500 packages |
| Auth / Identity | NestJS, Passport, JWT, OAuth2, Redis. Docker. | Ory Stack (Kratos + Hydra, API-first, zero custom auth code), Keycloak (enterprise, self-hosted, SAML/OIDC) | Multi-tenant with >10 OIDC providers; FAPI-compliant (financial-grade) required |
| Prototyping / MVP | Next.js, API Routes, PostgreSQL, Prisma. Docker Compose. | Rails (convention-over-configuration, fastest MVP velocity), Encore.ts (infra auto-provisioned, zero DevOps) | Team has zero React/Next.js experience; MVP must become production within 30 days |
| Domain | Default Recommendation | Viable Alternatives | When to Reconsider |
|---|---|---|---|
| Server backups | restic + Backblaze B2 (native backend, client-side encryption, dedup, snapshots). Config via /etc/restic/, systemd timer for daily backups, backup-healthcheck.sh for monitoring. First backup + restore test is mandatory. Use binary-installed restic (apt version lacks self-update). | BorgBackup (compression, append-only repos), Kopia (GUI, wider cloud backend support) | Backup size >10 TB (restic prune performance); compliance requires WORM (write-once-read-many) storage |
| Database backups | Per-engine tooling (pg_dump, mysqldump, mongodump) piped into restic — captures logical dump as a named file in the repo. | WAL-G (continuous archiving, point-in-time recovery), pgBackRest (parallel, incremental, enterprise PostgreSQL) | Point-in-time recovery required (not just daily snapshots); multi-TB database with <1h RPO |
Source: prior incident reflection — restic + B2 proven across arcana-www/prod/db; standardize to avoid revisiting the choice per-server.
| Domain | Default Recommendation | Viable Alternatives | When to Reconsider |
|---|---|---|---|
| Cross-Platform CLI | Go + Cobra | Rust + clap (compile-time correctness, performance-critical), Zig (ultra-low binary size, C-interop, <2MB target) | Cold-start latency <30ms required; binary size budget <5MB; C library interop needed |
| Desktop Application | Tauri (Rust) + React | Go + Fyne (simpler language, smaller bundle, 9.2MB binary), Electron (web-tech team, hot-reload, largest ecosystem) | Platform-specific native look required (Fyne renders own widgets); <20MB bundle hard constraint; need OS-level system tray / global hotkeys |
| Systems Daemon / Service | Rust + tokio | Go + standard library (faster development, simpler concurrency), Zig (minimal footprint, ~150KB idle RSS) | GC-pause tolerance unknown; team has zero Rust experience; rapid prototype needed in <1 week |
| Data / ML Pipeline | Python + FastAPI + Celery | Go + temporal.io (type-safe workflows, durable execution), Rust + rayon (CPU-bound transforms, zero-GC) | Throughput >10k items/s CPU-bound; GPU orchestration (Python stays king for GPU); exactly-once processing semantics |
| WASM Module | Rust + wasm-bindgen | Go (tinygo), Zig | Host runtime is JavaScript-only (Rust toolchain overhead > value); binary size floor <50KB (tinygo or Zig win); needs DOM access (wasm-bindgen + web-sys) |
Security guidance coverage for new domains: The framework's security baseline (S1-S11) provides a general floor, but domain-specific hardening guidance for CLI, Desktop, Systems, Data/ML, and WASM domains is currently minimal. When selecting a non-default stack from these domains, verify security posture manually: run the ecosystem's native audit tool (
cargo audit,govulncheck,npm audit), review the sandbox/isolation model, and consult domain-specific hardening references. Seeskills/security-baseline/SKILL.mdfor the general contract.
These are ecosystem-wide quality-floor choices, not per-project architecture decisions. Default to these for their respective language ecosystems.
graph LR
Py["Python"] --> UV["uv"]
Py --> Ruff["ruff"]
Py --> Pytest["pytest"]
Py --> Mypy["mypy (opt)"]
Py --> Hooks["pre-commit"]
uv — dependency & env managementruff — lint + format (replaces flake8, isort, black)pytest — testingmypy — typing (if used)pre-commit hooksPrefer uv over bare pip. Prefer ruff over flake8/isort/black. Deviate with a one-line rationale.
graph LR
Node["Node/TS"] --> PNPM["pnpm"]
Node --> TS["TypeScript"]
Node --> ESLint["eslint"]
Node --> Prettier["prettier"]
Node --> Vitest["vitest"]
Node --> PW["playwright"]
Node --> Build["tsup/swc"]
Node --> Hooks["lefthook/husky"]
pnpm — package managerTypeScript — mandatoryeslint + prettier — lint + formatvitest — testingplaywright — E2E (frontend)tsup/swc — buildlefthook/husky — git hooksThese rules are guidance, not mandates. Each carries a rationale. Deviate when the rationale does not apply to your project.
When generating a Stack Proposal, use the method below. Its purpose is to make trade-offs visible and challengeable — not to algorithmically pick a winner.
Record the operator's choice as a decision note in the plan's § Decisions section (not a standalone ADR file). Format:
### Decision: Technology Stack for {Component}
**Chosen stack:** {stack description}
**Alternatives considered:** {brief list}
**Rationale:** {1-paragraph synthesis of the key trade-off}
**Escape velocity:** {low/medium/high — how hard to migrate away later}
**Bound by:** immutability contract — revisable via Return-to-Plan amendment
Score each candidate 1-5 across the standard factors. Weight by project priorities. The total is indicative — it surfaces anomalies, it does not algorithmically decide.
| Factor | Weight | Candidate 1 | Candidate 2 | Candidate 3 |
|---|---|---|---|---|
| Fit for domain | ||||
| Ecosystem maturity | ||||
| Team/AI familiarity | ||||
| Performance profile | ||||
| Security posture | ||||
| Licence compatibility | ||||
| Cost | ||||
| Bundle/runtime cost | ||||
| Operational fit (Arcanada) | ||||
| Escape velocity |
For ecosystem-level posture, annotate each domain row with a radar classification:
The Default Recommendation column carries an implicit "Adopt" classification.
When the trigger classifier returns Trigger: FULL, generate a proposal using this mandatory shape. The proposal presents candidates and trade-offs; the operator chooses.
## Stack Proposal for {Component}
### Context
{1-2 sentences: what is being built, key constraints, domain classification}
### Candidate Stacks
#### Option A: {Stack Name}
| Factor | Assessment |
|--------|-----------|
| Fit for domain | {rationale} |
| Ecosystem maturity | {evidence — version, community size, maintenance cadence} |
| Team/AI familiarity | {assessment — note: agent self-assessment of "familiarity" is non-falsifiable; treat as weak signal} |
| Performance profile | {assessment — cite benchmarks where available} |
| Security posture | {CVE history (past 24 months), sandbox/isolation model, supply-chain trust (signed releases, SLSA attestation), audit-tooling availability, secure-configuration baseline maturity} |
| Licence compatibility | {licence family, copyleft/patent implications, transitive-licence risk} |
| Cost | {monetary cost — free, paid tier, per-seat, usage-based} |
| Bundle/runtime cost | {binary size, cold-start time, memory at idle, Docker image size} |
| Operational fit (Arcanada) | {coherence with existing ecosystem — does Arcanada already run this? shared CLIs, shared infra?} |
| Escape velocity | {low/medium/high — how hard to migrate away; vendor lock-in, skill availability, portability} |
#### Option B: {Alternative Name}
{same structure}
#### Option C: {Alternative Name} (if applicable)
{same structure}
### Trade-off Summary
{1-paragraph synthesis of the key differentiators — what you gain and lose with each option}
### Recommendation
{Agent's recommendation with rationale — stated here, not implied by candidate ordering. Candidates are listed alphabetically or by neutral sort.}
### Operator Decision
- [ ] Option A — {short label}
- [ ] Option B — {short label}
- [ ] Option C — {short label}
- [ ] Other (operator specifies)
A stack choice recorded in the plan's § Decisions is bound by the immutability contract (skills/immutability/SKILL.md). It must not be silently swapped mid-implementation.
If a concrete constraint makes the chosen stack unsatisfiable (not implementation difficulty — a genuine impossibility: library incompatibility, missing required feature, critical CVE with active exploitation), follow this sequence:
/dr-do work — do not commit workarounds against the wrong stack./dr-plan or /dr-prd with the recorded reason./dr-do with the updated decision note.If a CVSS 9+ or CISA KEV (Known Exploited Vulnerability) is published against the chosen stack during implementation, the agent MAY propose and implement an alternative without prior operator approval, provided:
This fast-track does NOT apply to medium/low CVEs or to implementation difficulty disguised as a security concern. The normal immutability process applies to all non-security artefacts.
After creating a new project in Projects/*/code/:
git init — initialize standalone repo (parent arcanada gitignores Projects/*/code/)pnpm outdated / uv pip list --outdated — zero outdated = pass.gitignore covers node_modules/, dist/, .envSource: prior incident — Model Connector code had no .git for weeks; discovered only at archive time.
latest tagspnpm add foo installs a major version with breaking changes, adapt the code to the new API instead of downgrading. Downgrading to a previous major is only acceptable if the latest version has a critical, documented, unresolved bug.engines field, pnpm-lock.yamlpyproject.toml, uv.lockpnpm outdated (or uv pip list --outdated) immediately after project init. Zero outdated packages = pass.package.json / pyproject.toml, verify the latest major via npm view <pkg> version (or pip index versions <pkg>). Prior incident: AI proposed Prisma 6 when Prisma 7 was already the latest stable — caught only at audit, cost rework.pnpm add / uv add during implementation, run pnpm outdated (or equivalent). If any dependency shows a newer major, update immediately — do not defer.This skill is loaded when:
graph TD
Start["NEW PROJECT /<br>SERVICE / MODULE"] --> Classify{"Trigger<br>Classifier"}
Classify -->|"FULL"| Proposal["GENERATE STACK<br>PROPOSAL"]
Classify -->|"SKIP"| Default["USE DEFAULT<br>RECOMMENDATION"]
Proposal --> Context["Document Context<br>& Constraints"]
Context --> Candidates["Generate 2-3<br>Candidate Stacks"]
Candidates --> Factors["Assess 10 Factors<br>per Candidate"]
Factors --> Tradeoff["Write Trade-off<br>Summary"]
Tradeoff --> Recommend["State Recommendation<br>(separate from ordering)"]
Recommend --> Operator["Operator Chooses"]
Operator --> ADR["Record Decision Note<br>in Plan § Decisions"]
ADR --> Bound["Bound by Immutability<br>Contract"]
Default --> Incumbent{"Incumbent<br>Stack Exists?"}
Incumbent -->|"Yes"| UseIncumbent["Use Incumbent Stack"]
Incumbent -->|"No"| UseTable["Use Default<br>Recommendation"]
For stack-specific scaffolds (NestJS, Django, Rails, etc.) consult the relevant project's CLAUDE.md or its per-project templates/ directory. The Datarim framework does not ship stack-specific scaffolds — this skill is the designated technology guidance file (whitelisted in skills/evolution/stack-agnostic-gate.md per the rationale: names concrete technologies to give actionable recommendations while presenting alternatives and trade-offs rather than single mandated answers).