# Language sovereignty -- the user picks, the Director suggests

Fetch when the user discusses programming language choice in
architecture / RFP / Arch / Design phases (workflow phases II
and III), OR when an Analyst extracts a spec that mentions a
language explicitly (signals: "in node", "with django", "react
native", "pure rust", "java backend", "swift app").

## Rule (single source of truth)

The user picks the programming language for each component. The
Director SUGGESTS during architecture (phase III specs / RFP)
but NEVER imposes. If the user insists on a language different
from the one the Director suggested, the Director performs a
SWITCH and persists the choice in `workflow.language_choices`
via `yujin.workflow.set`.

The ONLY exception is when the language the user requested is
not conducive to the component being developed (technical
incompatibility documentable from the table below). In that
case the Director replies with the exact row from this doctrine
+ the valid alternatives + waits for the next user decision. No
"I deny because I dislike", only "I deny because <component-kind>
cannot run on <language> because <technical reason>".

## Suitability table

The table is the single source of truth. The Director MUST read
from it and MUST NOT add opinions on top.

| Component kind          | Conducive languages                                            | NOT conducive (with reason)                                                                       |
|-------------------------|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| spa-web-frontend        | ts, js, wasm-target (rust, go, c#), htmx-fragments              | python, java, php, ruby (server-side runtimes, not a browser runtime)                              |
| mobile-native           | swift (ios), kotlin (android), dart (flutter), ts+rn (expo)     | python, php, c# (no fluid native mobile runtime); html-only (not native)                          |
| mobile-cross-platform   | dart (flutter), ts+rn (expo), kotlin-multiplatform              | jvm-only languages (not a mobile target), php, perl                                                |
| backend-api             | ts, js, python, go, rust, java, kotlin, c#, php, ruby, elixir   | bash, asm (do not scale to productive HTTP servers); assembler (no http stack)                    |
| backend-realtime        | go, rust, elixir, ts (node), kotlin, c# (signalr)               | python (gil bottleneck for sustained ws), php (request-response model)                            |
| kernel-driver           | c, rust, asm                                                    | ts, python, java, c# (managed runtimes do not touch metal)                                        |
| embedded-firmware       | c, rust, c++, micropython (mid-range), zig                      | java, ts, php (heavyweight runtimes for typical mcus)                                              |
| data-pipeline           | python, scala, java, go, sql, kotlin                            | js (poor batch ecosystem), php (no parallel data tooling)                                          |
| ml-training             | python, julia, r, c++ (kernels)                                 | ts, php, ruby (no mature ml ecosystem comparable)                                                  |
| ml-inference            | python, ts, c++, rust, go (onnx)                                | php, perl (no inference runtime)                                                                   |
| db-stored-proc          | sql, pl/pgsql, t-sql, pl/sql (oracle)                           | any general-purpose language (not the db engine model)                                            |
| db-migration            | sql + migration runner (any: rails, alembic, prisma, flyway)   | --                                                                                                |
| cli-tool                | go, rust, ts (node), python, c (oneshot)                        | -- (almost any general-purpose lang works)                                                         |
| static-analysis-plugin  | language of the target compiler                                 | any unrelated language (no compiler integration point)                                            |
| infra-as-code           | terraform/hcl, pulumi (ts/python/go), cloudformation/yaml, cdk  | bash one-offs (do not represent desired state)                                                    |
| iot-edge                | rust, c, go, python (rpi-class), elixir (nerves)                | jvm-only (mostly too heavy), php                                                                  |
| build-system            | go, rust, ts (node), make, bash                                 | -- (almost any works; nuance is in ergonomics, not correctness)                                   |
| docs-site               | markdown + static site gen (any), mdx (react)                   | dynamic backends (overkill; not conducive to content-as-code)                                     |

## Algorithm (Director executes this)

1. User input: "quiero X en LANG".
2. Director: `yujin.doctrine.discover({slugs:["language-sovereignty"]})`.
3. Director: lookup the row for `X` (component-kind). If
   ambiguous, ASK the user one clarifying question (which kind
   of component is X?) before proceeding. Do NOT guess.
4. If `LANG` is in `row.conducive`:
   - switch: set `workflow.language_choices[X] = { lang: LANG,
     decided_at: NOW, decided_by: "user" }` via
     `yujin.workflow.set`.
   - Reply briefly: "Listo, X en LANG. Sigo con el plan."
   - Do NOT discuss the choice further unless the user asks.
5. If `LANG` is NOT in `row.conducive`:
   - Reply with the exact row from this doctrine:
     "LANG no es conducente para X porque <row.NOT_conducive_reason>.
      Las opciones validas son: <row.conducive>. Cual preferis?"
   - Do NOT switch. Wait for the next user decision.
6. If the user counter-argues with NEW technical information
   ("hay un transpiler de LANG a JS / WASM target / etc"):
   - Director re-evaluates. If the new info is verifiable +
     consistent with the table semantics (e.g. WASM target for
     the kind), update the conducive set in memory for THIS
     turn + switch.
   - If the info is opinable / not verifiable, hold the negative
     + explain the gap (mismatched runtime model, no compiler
     integration, etc).

## Defaults when the user has NOT expressed preference

When the user has not yet declared a language for a component
slot, the Director may suggest a default. Defaults are NOT
prescriptions; they exist so the conversation can move forward
without a 12-question survey.

| Slot                | Default suggestion           |
|---------------------|------------------------------|
| frontend            | ts (react / typescript)      |
| mobile              | ts (react native + expo)     |
| backend             | ts (node) for crud-heavy; go for perf-sensitive; python for ml-heavy |
| data-pipeline       | python                       |
| infra               | terraform + hcl              |
| db                  | postgres + sql migrations    |
| ml                  | python                       |

The Director surfaces the default as a SUGGESTION ("propongo TS
para frontend y Python para el pipeline; te sirve?") and
proceeds if the user accepts or stays silent on it (silence is
acceptance of the default). The persisted `decided_by` field
records `"director_suggested"` so a future audit can distinguish
explicit choices from accepted defaults.

## Where the decision lives

```
yujin.forge.json -> workflow.language_choices: {
  frontend:      { lang: "ts",     decided_at: "...", decided_by: "user" },
  backend:       { lang: "go",     decided_at: "...", decided_by: "user" },
  mobile:        { lang: "dart",   decided_at: "...", decided_by: "director_suggested" },
  data_pipeline: { lang: "python", decided_at: "...", decided_by: "user" },
  infra:         { lang: "tf+hcl", decided_at: "...", decided_by: "director_suggested" }
}
```

Read via `yujin.workflow.state`. Write via `yujin.workflow.set
{path: "language_choices.<slot>", value: {...}}`.

## What this doctrine does NOT cover

- Frameworks within a language (vue vs react vs svelte for ts
  frontend). That is a separate user decision the Director
  surfaces but does not constrain via this doctrine.
- Library choices (express vs fastify vs hono for ts backend).
  Same: user decides.
- Build tooling (vite vs webpack vs esbuild). Same.

## What the WORKERS (Analyst, Coder, Tester, Scaffolder) read from this doctrine

- **Analyst**: when extracting a spec, capture explicit language
  signals into `plan.language_signals[]`. The Director processes
  them via the algorithm above.
- **Coder**: read `workflow.language_choices` before proposing
  files. Match the chosen language; do NOT default to TS if the
  table chose Go.
- **Tester**: detect the test framework FROM the chosen language
  + project manifest (`package.json` -> Vitest/Jest, `pyproject.toml`
  -> pytest, `go.mod` -> go test, `Cargo.toml` -> cargo test,
  `composer.json` -> PHPUnit). Do NOT prompt the user for the
  framework when the manifest already says.
- **Scaffolder**: respect file extensions + import statements of
  the chosen language stack. Do not write `.ts` files into a Go
  module.
