@nac3/forge-cli 0.2.0-alpha.59z.121 → 0.2.0-alpha.59z.158

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 (161) hide show
  1. package/dist/agents/bridge.d.ts +69 -0
  2. package/dist/agents/bridge.d.ts.map +1 -0
  3. package/dist/agents/bridge.js +295 -0
  4. package/dist/agents/bridge.js.map +1 -0
  5. package/dist/agents/cli_real.d.ts +38 -0
  6. package/dist/agents/cli_real.d.ts.map +1 -0
  7. package/dist/agents/cli_real.js +46 -0
  8. package/dist/agents/cli_real.js.map +1 -0
  9. package/dist/agents/config_schema.d.ts +1 -1
  10. package/dist/agents/config_schema.d.ts.map +1 -1
  11. package/dist/agents/config_schema.js +16 -0
  12. package/dist/agents/config_schema.js.map +1 -1
  13. package/dist/agents/defaults.d.ts.map +1 -1
  14. package/dist/agents/defaults.js.map +1 -1
  15. package/dist/agents/delegate.d.ts +36 -0
  16. package/dist/agents/delegate.d.ts.map +1 -0
  17. package/dist/agents/delegate.js +68 -0
  18. package/dist/agents/delegate.js.map +1 -0
  19. package/dist/agents/iterative_step.d.ts +95 -0
  20. package/dist/agents/iterative_step.d.ts.map +1 -0
  21. package/dist/agents/iterative_step.js +249 -0
  22. package/dist/agents/iterative_step.js.map +1 -0
  23. package/dist/agents/llm_step.d.ts +50 -0
  24. package/dist/agents/llm_step.d.ts.map +1 -0
  25. package/dist/agents/llm_step.js +28 -0
  26. package/dist/agents/llm_step.js.map +1 -0
  27. package/dist/agents/real_step_factory.d.ts +95 -0
  28. package/dist/agents/real_step_factory.d.ts.map +1 -0
  29. package/dist/agents/real_step_factory.js +51 -0
  30. package/dist/agents/real_step_factory.js.map +1 -0
  31. package/dist/agents/runner.d.ts +14 -0
  32. package/dist/agents/runner.d.ts.map +1 -1
  33. package/dist/agents/runner.js +10 -1
  34. package/dist/agents/runner.js.map +1 -1
  35. package/dist/agents/runtime.d.ts +115 -0
  36. package/dist/agents/runtime.d.ts.map +1 -0
  37. package/dist/agents/runtime.js +429 -0
  38. package/dist/agents/runtime.js.map +1 -0
  39. package/dist/agents/system_prompt.d.ts +36 -0
  40. package/dist/agents/system_prompt.d.ts.map +1 -0
  41. package/dist/agents/system_prompt.js +101 -0
  42. package/dist/agents/system_prompt.js.map +1 -0
  43. package/dist/agents/types.d.ts +12 -1
  44. package/dist/agents/types.d.ts.map +1 -1
  45. package/dist/agents/types.js.map +1 -1
  46. package/dist/bin/yf.d.ts.map +1 -1
  47. package/dist/bin/yf.js +9 -0
  48. package/dist/bin/yf.js.map +1 -1
  49. package/dist/chat/claude_cli.d.ts +42 -0
  50. package/dist/chat/claude_cli.d.ts.map +1 -0
  51. package/dist/chat/claude_cli.js +299 -0
  52. package/dist/chat/claude_cli.js.map +1 -0
  53. package/dist/chat/graph_snapshot.d.ts +38 -0
  54. package/dist/chat/graph_snapshot.d.ts.map +1 -0
  55. package/dist/chat/graph_snapshot.js +159 -0
  56. package/dist/chat/graph_snapshot.js.map +1 -0
  57. package/dist/chat/ingest_session.d.ts +24 -4
  58. package/dist/chat/ingest_session.d.ts.map +1 -1
  59. package/dist/chat/ingest_session.js +139 -4
  60. package/dist/chat/ingest_session.js.map +1 -1
  61. package/dist/chat/panel.d.ts.map +1 -1
  62. package/dist/chat/panel.js +1358 -0
  63. package/dist/chat/panel.js.map +1 -1
  64. package/dist/chat/server.d.ts +21 -0
  65. package/dist/chat/server.d.ts.map +1 -1
  66. package/dist/chat/server.js +904 -4
  67. package/dist/chat/server.js.map +1 -1
  68. package/dist/chat/tools/nac3_invoke.d.ts +5 -0
  69. package/dist/chat/tools/nac3_invoke.d.ts.map +1 -1
  70. package/dist/chat/tools/nac3_invoke.js +1 -0
  71. package/dist/chat/tools/nac3_invoke.js.map +1 -1
  72. package/dist/chat/tools.d.ts.map +1 -1
  73. package/dist/chat/tools.js +12 -0
  74. package/dist/chat/tools.js.map +1 -1
  75. package/dist/commands/agent.d.ts +12 -1
  76. package/dist/commands/agent.d.ts.map +1 -1
  77. package/dist/commands/agent.js +92 -1
  78. package/dist/commands/agent.js.map +1 -1
  79. package/dist/commands/chat.d.ts +10 -0
  80. package/dist/commands/chat.d.ts.map +1 -1
  81. package/dist/commands/chat.js +12 -1
  82. package/dist/commands/chat.js.map +1 -1
  83. package/dist/commands/director.d.ts +37 -0
  84. package/dist/commands/director.d.ts.map +1 -0
  85. package/dist/commands/director.js +290 -0
  86. package/dist/commands/director.js.map +1 -0
  87. package/dist/commands/keys_setup.d.ts +1 -0
  88. package/dist/commands/keys_setup.d.ts.map +1 -1
  89. package/dist/commands/keys_setup.js +61 -11
  90. package/dist/commands/keys_setup.js.map +1 -1
  91. package/dist/core/pilot_setup.d.ts +14 -1
  92. package/dist/core/pilot_setup.d.ts.map +1 -1
  93. package/dist/core/pilot_setup.js +62 -1
  94. package/dist/core/pilot_setup.js.map +1 -1
  95. package/dist/core/provider_keys.d.ts.map +1 -1
  96. package/dist/core/provider_keys.js +12 -5
  97. package/dist/core/provider_keys.js.map +1 -1
  98. package/dist/design/yujin_design_resolver.d.ts +77 -0
  99. package/dist/design/yujin_design_resolver.d.ts.map +1 -0
  100. package/dist/design/yujin_design_resolver.js +113 -0
  101. package/dist/design/yujin_design_resolver.js.map +1 -0
  102. package/dist/docs/doctrine/default-design-system.md +120 -0
  103. package/dist/docs/doctrine/language-sovereignty.md +143 -0
  104. package/dist/docs/doctrine/testing-policy.md +211 -0
  105. package/dist/nac3/approval_queue.d.ts +8 -0
  106. package/dist/nac3/approval_queue.d.ts.map +1 -1
  107. package/dist/nac3/approval_queue.js.map +1 -1
  108. package/dist/nac3/internal_manifest.d.ts.map +1 -1
  109. package/dist/nac3/internal_manifest.js +178 -0
  110. package/dist/nac3/internal_manifest.js.map +1 -1
  111. package/dist/office/excel.d.ts +22 -0
  112. package/dist/office/excel.d.ts.map +1 -1
  113. package/dist/office/excel.js +82 -0
  114. package/dist/office/excel.js.map +1 -1
  115. package/dist/qa/brain_matrix/fixtures/scenario_a11y_focus_after_open.json +22 -0
  116. package/dist/qa/brain_matrix/fixtures/scenario_a11y_label_unlabeled_button.json +26 -0
  117. package/dist/qa/brain_matrix/fixtures/scenario_agent_director_consults_workflow_before_dispatch.json +42 -0
  118. package/dist/qa/brain_matrix/fixtures/scenario_analyst_extracts_then_returns.json +28 -0
  119. package/dist/qa/brain_matrix/fixtures/scenario_coder_consults_nac_spec_before_writing.json +28 -0
  120. package/dist/qa/brain_matrix/fixtures/scenario_default_design_system_override_shadcn.json +45 -0
  121. package/dist/qa/brain_matrix/fixtures/scenario_default_design_system_use_card_template.json +44 -0
  122. package/dist/qa/brain_matrix/fixtures/scenario_director_delegates_to_analyst.json +26 -0
  123. package/dist/qa/brain_matrix/fixtures/scenario_director_no_self_delegation.json +20 -0
  124. package/dist/qa/brain_matrix/fixtures/scenario_director_terminal_done_marker.json +19 -0
  125. package/dist/qa/brain_matrix/fixtures/scenario_doctrine_discover_default_design_system.json +50 -0
  126. package/dist/qa/brain_matrix/fixtures/scenario_doctrine_discover_language_sovereignty.json +53 -0
  127. package/dist/qa/brain_matrix/fixtures/scenario_doctrine_discover_testing_policy.json +55 -0
  128. package/dist/qa/brain_matrix/fixtures/scenario_language_sovereignty_not_conducive_spa_python.json +61 -0
  129. package/dist/qa/brain_matrix/fixtures/scenario_language_sovereignty_user_picks_python.json +48 -0
  130. package/dist/qa/brain_matrix/fixtures/scenario_mcp_invoke_destructive_pauses_for_r8.json +42 -0
  131. package/dist/qa/brain_matrix/fixtures/scenario_mcp_invoke_happy_path_writes_pizarron.json +76 -0
  132. package/dist/qa/brain_matrix/fixtures/scenario_mcp_invoke_network_timeout_graceful.json +41 -0
  133. package/dist/qa/brain_matrix/fixtures/scenario_no_hardcoded_color_use_token.json +47 -0
  134. package/dist/qa/brain_matrix/fixtures/scenario_office_excel_update_cell_picks_right_verb.json +26 -0
  135. package/dist/qa/brain_matrix/fixtures/scenario_scaffolder_destructive_pauses_for_approval.json +23 -0
  136. package/dist/qa/brain_matrix/fixtures/scenario_sumie_icon_pick_from_catalog.json +56 -0
  137. package/dist/qa/brain_matrix/fixtures/scenario_tester_uses_shell_exec_only.json +27 -0
  138. package/dist/version.d.ts +1 -1
  139. package/dist/version.js +1 -1
  140. package/dist/workflow/graph.d.ts +59 -0
  141. package/dist/workflow/graph.d.ts.map +1 -0
  142. package/dist/workflow/graph.js +137 -0
  143. package/dist/workflow/graph.js.map +1 -0
  144. package/dist/workflow/state.d.ts +34 -0
  145. package/dist/workflow/state.d.ts.map +1 -1
  146. package/dist/workflow/state.js.map +1 -1
  147. package/docs/doctrine/default-design-system.md +120 -0
  148. package/docs/doctrine/language-sovereignty.md +143 -0
  149. package/docs/doctrine/testing-policy.md +211 -0
  150. package/package.json +102 -100
  151. package/src/i18n/catalogs/ar.json +20 -1
  152. package/src/i18n/catalogs/de.json +20 -1
  153. package/src/i18n/catalogs/en.json +20 -21
  154. package/src/i18n/catalogs/es.json +20 -21
  155. package/src/i18n/catalogs/fr.json +20 -1
  156. package/src/i18n/catalogs/hi.json +20 -1
  157. package/src/i18n/catalogs/it.json +20 -1
  158. package/src/i18n/catalogs/ja.json +20 -1
  159. package/src/i18n/catalogs/pt.json +20 -1
  160. package/src/i18n/catalogs/zh.json +20 -1
  161. package/templates/sumi-context.template.md +57 -0
@@ -0,0 +1,143 @@
1
+ # Language sovereignty -- the user picks, the Director suggests
2
+
3
+ Fetch when the user discusses programming language choice in
4
+ architecture / RFP / Arch / Design phases (workflow phases II
5
+ and III), OR when an Analyst extracts a spec that mentions a
6
+ language explicitly (signals: "in node", "with django", "react
7
+ native", "pure rust", "java backend", "swift app").
8
+
9
+ ## Rule (single source of truth)
10
+
11
+ The user picks the programming language for each component. The
12
+ Director SUGGESTS during architecture (phase III specs / RFP)
13
+ but NEVER imposes. If the user insists on a language different
14
+ from the one the Director suggested, the Director performs a
15
+ SWITCH and persists the choice in `workflow.language_choices`
16
+ via `yujin.workflow.set`.
17
+
18
+ The ONLY exception is when the language the user requested is
19
+ not conducive to the component being developed (technical
20
+ incompatibility documentable from the table below). In that
21
+ case the Director replies with the exact row from this doctrine
22
+ + the valid alternatives + waits for the next user decision. No
23
+ "I deny because I dislike", only "I deny because <component-kind>
24
+ cannot run on <language> because <technical reason>".
25
+
26
+ ## Suitability table
27
+
28
+ The table is the single source of truth. The Director MUST read
29
+ from it and MUST NOT add opinions on top.
30
+
31
+ | Component kind | Conducive languages | NOT conducive (with reason) |
32
+ |-------------------------|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
33
+ | spa-web-frontend | ts, js, wasm-target (rust, go, c#), htmx-fragments | python, java, php, ruby (server-side runtimes, not a browser runtime) |
34
+ | mobile-native | swift (ios), kotlin (android), dart (flutter), ts+rn (expo) | python, php, c# (no fluid native mobile runtime); html-only (not native) |
35
+ | mobile-cross-platform | dart (flutter), ts+rn (expo), kotlin-multiplatform | jvm-only languages (not a mobile target), php, perl |
36
+ | 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) |
37
+ | backend-realtime | go, rust, elixir, ts (node), kotlin, c# (signalr) | python (gil bottleneck for sustained ws), php (request-response model) |
38
+ | kernel-driver | c, rust, asm | ts, python, java, c# (managed runtimes do not touch metal) |
39
+ | embedded-firmware | c, rust, c++, micropython (mid-range), zig | java, ts, php (heavyweight runtimes for typical mcus) |
40
+ | data-pipeline | python, scala, java, go, sql, kotlin | js (poor batch ecosystem), php (no parallel data tooling) |
41
+ | ml-training | python, julia, r, c++ (kernels) | ts, php, ruby (no mature ml ecosystem comparable) |
42
+ | ml-inference | python, ts, c++, rust, go (onnx) | php, perl (no inference runtime) |
43
+ | db-stored-proc | sql, pl/pgsql, t-sql, pl/sql (oracle) | any general-purpose language (not the db engine model) |
44
+ | db-migration | sql + migration runner (any: rails, alembic, prisma, flyway) | -- |
45
+ | cli-tool | go, rust, ts (node), python, c (oneshot) | -- (almost any general-purpose lang works) |
46
+ | static-analysis-plugin | language of the target compiler | any unrelated language (no compiler integration point) |
47
+ | infra-as-code | terraform/hcl, pulumi (ts/python/go), cloudformation/yaml, cdk | bash one-offs (do not represent desired state) |
48
+ | iot-edge | rust, c, go, python (rpi-class), elixir (nerves) | jvm-only (mostly too heavy), php |
49
+ | build-system | go, rust, ts (node), make, bash | -- (almost any works; nuance is in ergonomics, not correctness) |
50
+ | docs-site | markdown + static site gen (any), mdx (react) | dynamic backends (overkill; not conducive to content-as-code) |
51
+
52
+ ## Algorithm (Director executes this)
53
+
54
+ 1. User input: "quiero X en LANG".
55
+ 2. Director: `yujin.doctrine.discover({slugs:["language-sovereignty"]})`.
56
+ 3. Director: lookup the row for `X` (component-kind). If
57
+ ambiguous, ASK the user one clarifying question (which kind
58
+ of component is X?) before proceeding. Do NOT guess.
59
+ 4. If `LANG` is in `row.conducive`:
60
+ - switch: set `workflow.language_choices[X] = { lang: LANG,
61
+ decided_at: NOW, decided_by: "user" }` via
62
+ `yujin.workflow.set`.
63
+ - Reply briefly: "Listo, X en LANG. Sigo con el plan."
64
+ - Do NOT discuss the choice further unless the user asks.
65
+ 5. If `LANG` is NOT in `row.conducive`:
66
+ - Reply with the exact row from this doctrine:
67
+ "LANG no es conducente para X porque <row.NOT_conducive_reason>.
68
+ Las opciones validas son: <row.conducive>. Cual preferis?"
69
+ - Do NOT switch. Wait for the next user decision.
70
+ 6. If the user counter-argues with NEW technical information
71
+ ("hay un transpiler de LANG a JS / WASM target / etc"):
72
+ - Director re-evaluates. If the new info is verifiable +
73
+ consistent with the table semantics (e.g. WASM target for
74
+ the kind), update the conducive set in memory for THIS
75
+ turn + switch.
76
+ - If the info is opinable / not verifiable, hold the negative
77
+ + explain the gap (mismatched runtime model, no compiler
78
+ integration, etc).
79
+
80
+ ## Defaults when the user has NOT expressed preference
81
+
82
+ When the user has not yet declared a language for a component
83
+ slot, the Director may suggest a default. Defaults are NOT
84
+ prescriptions; they exist so the conversation can move forward
85
+ without a 12-question survey.
86
+
87
+ | Slot | Default suggestion |
88
+ |---------------------|------------------------------|
89
+ | frontend | ts (react / typescript) |
90
+ | mobile | ts (react native + expo) |
91
+ | backend | ts (node) for crud-heavy; go for perf-sensitive; python for ml-heavy |
92
+ | data-pipeline | python |
93
+ | infra | terraform + hcl |
94
+ | db | postgres + sql migrations |
95
+ | ml | python |
96
+
97
+ The Director surfaces the default as a SUGGESTION ("propongo TS
98
+ para frontend y Python para el pipeline; te sirve?") and
99
+ proceeds if the user accepts or stays silent on it (silence is
100
+ acceptance of the default). The persisted `decided_by` field
101
+ records `"director_suggested"` so a future audit can distinguish
102
+ explicit choices from accepted defaults.
103
+
104
+ ## Where the decision lives
105
+
106
+ ```
107
+ yujin.forge.json -> workflow.language_choices: {
108
+ frontend: { lang: "ts", decided_at: "...", decided_by: "user" },
109
+ backend: { lang: "go", decided_at: "...", decided_by: "user" },
110
+ mobile: { lang: "dart", decided_at: "...", decided_by: "director_suggested" },
111
+ data_pipeline: { lang: "python", decided_at: "...", decided_by: "user" },
112
+ infra: { lang: "tf+hcl", decided_at: "...", decided_by: "director_suggested" }
113
+ }
114
+ ```
115
+
116
+ Read via `yujin.workflow.state`. Write via `yujin.workflow.set
117
+ {path: "language_choices.<slot>", value: {...}}`.
118
+
119
+ ## What this doctrine does NOT cover
120
+
121
+ - Frameworks within a language (vue vs react vs svelte for ts
122
+ frontend). That is a separate user decision the Director
123
+ surfaces but does not constrain via this doctrine.
124
+ - Library choices (express vs fastify vs hono for ts backend).
125
+ Same: user decides.
126
+ - Build tooling (vite vs webpack vs esbuild). Same.
127
+
128
+ ## What the WORKERS (Analyst, Coder, Tester, Scaffolder) read from this doctrine
129
+
130
+ - **Analyst**: when extracting a spec, capture explicit language
131
+ signals into `plan.language_signals[]`. The Director processes
132
+ them via the algorithm above.
133
+ - **Coder**: read `workflow.language_choices` before proposing
134
+ files. Match the chosen language; do NOT default to TS if the
135
+ table chose Go.
136
+ - **Tester**: detect the test framework FROM the chosen language
137
+ + project manifest (`package.json` -> Vitest/Jest, `pyproject.toml`
138
+ -> pytest, `go.mod` -> go test, `Cargo.toml` -> cargo test,
139
+ `composer.json` -> PHPUnit). Do NOT prompt the user for the
140
+ framework when the manifest already says.
141
+ - **Scaffolder**: respect file extensions + import statements of
142
+ the chosen language stack. Do not write `.ts` files into a Go
143
+ module.
@@ -0,0 +1,211 @@
1
+ # Testing policy (Forge product principle)
2
+
3
+ **Lock date:** 2026-06-04. Replaces the earlier draft
4
+ `tdd-first.md` which proposed TDD-first universally.
5
+
6
+ Pablo's review: TDD-first as a universal product principle has
7
+ more downsides than upsides. The cost is real (tokens, latency,
8
+ arquitectural lock-in, frustrated UX, ambiguous specs) and only
9
+ pays off in a narrow slice (backends + middleware + finance +
10
+ regulatory + external integrations). For the majority of Forge
11
+ projects (UI / dashboards / CRUDs / demos / glue), TDD-first
12
+ slows down iteration without adding value.
13
+
14
+ This doctrine sets the new rule.
15
+
16
+ ## The rule
17
+
18
+ 1. **Tests are required POST-implementation for every change**
19
+ that lands in a Forge-managed project. The default order is:
20
+ spec -> code -> tests -> scaffold-with-gate.
21
+ 2. **TDD-first is opt-in**, not default. It applies when the
22
+ slice falls into the **TDD-first scope** (see below).
23
+ 3. **The scaffold gate** requires the test suite to be green
24
+ before writing to disk, regardless of order. The order
25
+ determines WHEN tests are authored, not whether they exist.
26
+
27
+ ## TDD-first scope (the narrow slice)
28
+
29
+ Only these domains warrant test-first:
30
+
31
+ - **APIs / backend endpoints with public contract**. The
32
+ request/response shape is the most stable thing about the
33
+ feature; the implementation will rewrite.
34
+ - **Data transformers / validators / parsers** where input
35
+ -> output is the whole point.
36
+ - **Financial / regulatory / mathematical logic** (tax calc,
37
+ retention, currency conversion, legal rules). The test
38
+ is the spec.
39
+ - **External-service integrations** (Stripe webhook, Polar,
40
+ OAuth, NAC bridges to MCP). Tests capture "this payload
41
+ produces this effect" which is the only stable contract
42
+ when the provider changes internals.
43
+
44
+ The Analyst marks the spec extraction with `tdd_first: true`
45
+ when:
46
+
47
+ - The plan contains `endpoints` AND no `components`.
48
+ - OR any extraction tag matches /financial|tax|regulatory|
49
+ integration|webhook|validator|transformer/i.
50
+ - OR the user explicitly passes `--tdd` to `yf spec extract`.
51
+
52
+ When `tdd_first: false` (default), the Tester slot runs AFTER
53
+ the Coder. When true, the Tester slot runs BEFORE the Coder.
54
+
55
+ ## TDD-first explicitly NOT for
56
+
57
+ - UI / frontend / drag-drop / animations / layout.
58
+ - Glue code / scripts / one-off transforms.
59
+ - Demos / prototypes / spikes (always `--mode=spike`).
60
+ - Stateful interactive surfaces (games, audio, sensor).
61
+ - Generated code from templates (Next.js scaffolds, etc.) --
62
+ the template has its own tests; user-added features follow
63
+ the default order.
64
+
65
+ Trying to TDD any of the above is fiction-writing. The tests
66
+ end up tautological (matching implementation) or vacuous
67
+ (asserting things that are trivially true). Forge will not
68
+ force users into that.
69
+
70
+ ## Order of the 5 Director agents (canonical)
71
+
72
+ The Tester is NOT a single-shot slot -- it is re-entrant. It
73
+ runs at every checkpoint where the suite needs to be authored
74
+ or verified. In TDD-first, that's twice. In code-first, that's
75
+ once. The diagram below names the runs explicitly.
76
+
77
+ ### TDD-first mode (Analyst tags `tdd_first: true`)
78
+
79
+ ```
80
+ Director (orchestrator)
81
+ |
82
+ v
83
+ Analyst -- extract spec + tag tdd_first=true
84
+ |
85
+ v
86
+ Tester #1 -- write the test suite (RED). Suite fails because
87
+ no code exists yet.
88
+ |
89
+ v
90
+ Coder -- implement until the suite turns green. Iterates
91
+ with the brain until red->green or max iter cap.
92
+ |
93
+ v
94
+ Tester #2 -- verify green + add edge-case pass (3 boundary
95
+ tests). If any test still red after edge pass,
96
+ back to Coder (capped by max iter).
97
+ |
98
+ v
99
+ Scaffolder -- apply to disk ONLY when Tester #2 reports
100
+ all-green (happy + edge cases).
101
+ ```
102
+
103
+ ### Code-first mode (Analyst tags `tdd_first: false`, the default)
104
+
105
+ ```
106
+ Director (orchestrator)
107
+ |
108
+ v
109
+ Analyst -- extract spec + tag tdd_first=false
110
+ |
111
+ v
112
+ Coder -- implement straight from the plan.
113
+ |
114
+ v
115
+ Tester -- write the test suite POST-implementation +
116
+ verify green + edge-case pass.
117
+ |
118
+ v
119
+ Scaffolder -- apply to disk ONLY when Tester reports
120
+ all-green.
121
+ ```
122
+
123
+ In both modes, the **last Tester pass before Scaffolder is the
124
+ hard gate**. The flag `tdd_first` decides whether there is also
125
+ a FIRST Tester pass that writes red. The Scaffolder gate
126
+ (green-required) is identical in both modes.
127
+
128
+ The bridge ACL stays the same regardless of mode:
129
+ `Director.can_call = [Analyst, Tester, Coder, Scaffolder]`
130
+ and each worker accepts only from Director. Mode is encoded
131
+ in the messages, not in the topology.
132
+
133
+ ## The scaffold gate (regardless of order)
134
+
135
+ `Scaffolder` refuses to write to disk when:
136
+
137
+ - The Tester report does not exist for the slice.
138
+ - Any test in the new suite is red.
139
+
140
+ The gate is enforced in `src/chat/spec_scaffold.ts`. Test
141
+ runner auto-detected from `package.json` / `pyproject.toml` /
142
+ etc.
143
+
144
+ ## Loop protection (when iterating to green)
145
+
146
+ If the Coder iterates more than `agent.director.max_iters_to_green`
147
+ times (default 5) without turning the test suite green, the
148
+ agent stops and reports the failure to the Director, which
149
+ opens an R8 modal with three options for the user:
150
+
151
+ 1. Mark suite as accepted-red (the user knows the tests are
152
+ wrong; accept the implementation as-is). Audit-logged.
153
+ 2. Re-extract spec (back to Analyst with clarification).
154
+ 3. Cancel the slice.
155
+
156
+ This is the answer to "what does the agent do when it cannot
157
+ turn red into green". No silent application. No infinite
158
+ iteration.
159
+
160
+ ## Mitigations against test pathologies
161
+
162
+ Even POST-implementation testing can produce bad suites if
163
+ left to the Tester LLM alone. Required mitigations:
164
+
165
+ - **Contract framing in Tester system prompt**: the prompt
166
+ explicitly says "tests assert CONTRACT (input -> output,
167
+ effect on world), NOT implementation. Internal refactors
168
+ should not break a well-written test."
169
+ - **Edge-case pass**: after the happy-path tests are green,
170
+ the Tester runs a second pass generating 3 edge cases
171
+ (nulls, empty, boundary, error). Adds them to the suite.
172
+ Coder iterates again.
173
+ - **No mocking-of-self**: tests cannot mock the function
174
+ they are testing. Mocking external deps (network, fs) is
175
+ allowed.
176
+ - **Tester report includes coverage line + branch %.**
177
+ Below configurable threshold (default 80% line, 70%
178
+ branch) is logged as a warning, not a block.
179
+
180
+ ## What this doctrine covers + does NOT cover
181
+
182
+ This doctrine covers **functional correctness via tests**.
183
+ It does NOT cover:
184
+
185
+ - **UX quality** -- panel review, manual walkthroughs.
186
+ - **Accessibility** -- separate doctrine `accessibility.md`
187
+ + `yujin.a11y.*` verbs.
188
+ - **Performance** -- separate (future doctrine, not blocking).
189
+ - **Security** -- separate.
190
+ - **Design / aesthetic** -- not testable by code.
191
+
192
+ "100% E2E tests passed" in the Director objective means
193
+ "functional contract passes". Other quality dimensions are
194
+ gated separately or via human review.
195
+
196
+ ## How Sumi enforces this on Forge itself (dogfood)
197
+
198
+ Sumi-self ships every Forge slice with tests. The order
199
+ follows the same rule:
200
+
201
+ - Backend / middleware / integration slice -> TDD-first
202
+ (test then code). The bridge.ts slice was this kind, and
203
+ the bug found by the test is the evidence the order paid
204
+ off there.
205
+ - UI / panel / cosmetic slice -> code-first then test. The
206
+ legal modal opacity fix, the audit log preview, the
207
+ Limites alignment -- all of these were code-first because
208
+ the shape was visual and exploratory.
209
+
210
+ Sumi decides the order per slice. The graph rule #12 reflects
211
+ this.
package/package.json CHANGED
@@ -1,100 +1,102 @@
1
- {
2
- "name": "@nac3/forge-cli",
3
- "version": "0.2.0-alpha.59z.121",
4
- "description": "Yujin Forge -- voice-first NAC-3 React development framework. CLI + chat panel + spec ingest + 10-format document reader + voice loop.",
5
- "license": "SEE LICENSE IN LICENSE",
6
- "author": "Pablo Kuschnirof <pablo@rpaforce.com>",
7
- "homepage": "https://github.com/yujinapp/yujin-forge#readme",
8
- "bugs": {
9
- "url": "https://github.com/yujinapp/yujin-forge/issues"
10
- },
11
- "bin": {
12
- "yf": "./dist/bin/yf.js"
13
- },
14
- "type": "module",
15
- "exports": {
16
- ".": "./dist/index.js"
17
- },
18
- "dependencies": {
19
- "@anthropic-ai/sdk": "^0.32.1",
20
- "@aws-sdk/client-ecs": "^3.1053.0",
21
- "commander": "12.1.0",
22
- "dotenv": "^17.4.2",
23
- "kleur": "4.1.5",
24
- "mammoth": "^1.8.0",
25
- "node-html-parser": "6.1.13",
26
- "pdfjs-dist": "^4.10.38",
27
- "prompts": "2.4.2",
28
- "typescript": "5.7.2",
29
- "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
30
- },
31
- "devDependencies": {
32
- "@playwright/test": "^1.60.0",
33
- "@types/node": "22.10.2",
34
- "@types/prompts": "2.4.9",
35
- "acorn": "^8.16.0",
36
- "pixelmatch": "^7.2.0",
37
- "pngjs": "^7.0.0",
38
- "tsx": "4.19.2",
39
- "vitest": "2.1.8"
40
- },
41
- "engines": {
42
- "node": ">=18.17",
43
- "pnpm": ">=9"
44
- },
45
- "files": [
46
- "dist",
47
- "templates",
48
- "docs/manuals",
49
- "docs/SQ.md",
50
- "docs/SQ_BY_STEP.yaml",
51
- "docs/doctrine",
52
- "src/i18n/catalogs",
53
- "README.md",
54
- "LICENSE"
55
- ],
56
- "keywords": [
57
- "nac",
58
- "nac-3",
59
- "react",
60
- "framework",
61
- "claude-code",
62
- "yujin",
63
- "voice",
64
- "whisper",
65
- "elevenlabs",
66
- "tts",
67
- "stt",
68
- "document-reader",
69
- "pdf",
70
- "docx",
71
- "epub",
72
- "spec-ingest",
73
- "scaffolding",
74
- "accessibility",
75
- "voice-first",
76
- "adip"
77
- ],
78
- "repository": {
79
- "type": "git",
80
- "url": "git+https://github.com/yujinapp/yujin-forge.git"
81
- },
82
- "publishConfig": {
83
- "access": "public",
84
- "tag": "alpha"
85
- },
86
- "scripts": {
87
- "build": "tsc && node scripts/copy_qa_assets.mjs",
88
- "dev": "tsx watch src/bin/yf.ts",
89
- "typecheck": "tsc --noEmit",
90
- "typecheck:all": "pnpm typecheck && pnpm --filter @yujin/hito4-server typecheck && pnpm --filter @yujin/forge-mobile typecheck",
91
- "test": "vitest run",
92
- "test:watch": "vitest",
93
- "lint": "tsc --noEmit",
94
- "yf": "tsx src/bin/yf.ts",
95
- "catalog:lint": "node scripts/catalog_lint.mjs",
96
- "pairing:check": "node scripts/check_pairing_sync.mjs",
97
- "brain-matrix:gate": "node scripts/brain_matrix_gate.mjs",
98
- "hito4:lint": "node scripts/hito4_lint_bundle.mjs"
99
- }
100
- }
1
+ {
2
+ "name": "@nac3/forge-cli",
3
+ "version": "0.2.0-alpha.59z.158",
4
+ "description": "Yujin Forge -- voice-first NAC-3 React development framework. CLI + chat panel + spec ingest + 10-format document reader + voice loop.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "author": "Pablo Kuschnirof <pablo@rpaforce.com>",
7
+ "homepage": "https://github.com/yujinapp/yujin-forge#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/yujinapp/yujin-forge/issues"
10
+ },
11
+ "bin": {
12
+ "yf": "./dist/bin/yf.js"
13
+ },
14
+ "type": "module",
15
+ "exports": {
16
+ ".": "./dist/index.js"
17
+ },
18
+ "scripts": {
19
+ "build": "tsc && node scripts/copy_qa_assets.mjs",
20
+ "dev": "tsx watch src/bin/yf.ts",
21
+ "typecheck": "tsc --noEmit",
22
+ "typecheck:all": "pnpm typecheck && pnpm --filter @yujin/hito4-server typecheck && pnpm --filter @yujin/forge-mobile typecheck",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "lint": "tsc --noEmit",
26
+ "yf": "tsx src/bin/yf.ts",
27
+ "catalog:lint": "node scripts/catalog_lint.mjs",
28
+ "pairing:check": "node scripts/check_pairing_sync.mjs",
29
+ "brain-matrix:gate": "node scripts/brain_matrix_gate.mjs",
30
+ "hito4:lint": "node scripts/hito4_lint_bundle.mjs",
31
+ "prepublishOnly": "pnpm run build && pnpm run typecheck && pnpm run catalog:lint && pnpm run test && pnpm run brain-matrix:gate && pnpm run hito4:lint"
32
+ },
33
+ "dependencies": {
34
+ "@anthropic-ai/sdk": "^0.32.1",
35
+ "@aws-sdk/client-ecs": "^3.1053.0",
36
+ "commander": "12.1.0",
37
+ "dotenv": "^17.4.2",
38
+ "kleur": "4.1.5",
39
+ "mammoth": "^1.8.0",
40
+ "node-html-parser": "6.1.13",
41
+ "pdfjs-dist": "^4.10.38",
42
+ "prompts": "2.4.2",
43
+ "typescript": "5.7.2",
44
+ "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
45
+ },
46
+ "devDependencies": {
47
+ "@playwright/test": "^1.60.0",
48
+ "@types/node": "22.10.2",
49
+ "@types/prompts": "2.4.9",
50
+ "acorn": "^8.16.0",
51
+ "pixelmatch": "^7.2.0",
52
+ "pngjs": "^7.0.0",
53
+ "tsx": "4.19.2",
54
+ "vitest": "2.1.8"
55
+ },
56
+ "engines": {
57
+ "node": ">=18.17",
58
+ "pnpm": ">=9"
59
+ },
60
+ "packageManager": "pnpm@11.3.0",
61
+ "files": [
62
+ "dist",
63
+ "templates",
64
+ "docs/manuals",
65
+ "docs/SQ.md",
66
+ "docs/SQ_BY_STEP.yaml",
67
+ "docs/doctrine",
68
+ "src/i18n/catalogs",
69
+ "README.md",
70
+ "LICENSE"
71
+ ],
72
+ "keywords": [
73
+ "nac",
74
+ "nac-3",
75
+ "react",
76
+ "framework",
77
+ "claude-code",
78
+ "yujin",
79
+ "voice",
80
+ "whisper",
81
+ "elevenlabs",
82
+ "tts",
83
+ "stt",
84
+ "document-reader",
85
+ "pdf",
86
+ "docx",
87
+ "epub",
88
+ "spec-ingest",
89
+ "scaffolding",
90
+ "accessibility",
91
+ "voice-first",
92
+ "adip"
93
+ ],
94
+ "repository": {
95
+ "type": "git",
96
+ "url": "git+https://github.com/yujinapp/yujin-forge.git"
97
+ },
98
+ "publishConfig": {
99
+ "access": "public",
100
+ "tag": "alpha"
101
+ }
102
+ }
@@ -127,5 +127,24 @@
127
127
  "error.project_not_registered.what": "لا يوجد مشروع adopter مسجل.",
128
128
  "error.project_not_registered.why": "يتطلب verb المستدعى project root نشط لكن ~/.yujin-forge/projects.json ليس به إدخالات.",
129
129
  "error.project_not_registered.fix": "أنشئ واحدا بـ yujin.project.init أو سجل واحدا موجودا بـ yf projects add.",
130
- "error.project_not_registered.cmd": "yf projects scan"
130
+ "error.project_not_registered.cmd": "yf projects scan",
131
+ "panel.button.agents": "الوكلاء",
132
+ "panel.agents.title": "الوكلاء",
133
+ "panel.agents.subtitle": "المدير والوكلاء الفرعيون المسجلون في هذا المشروع",
134
+ "panel.agents.empty.title": "لا يوجد وكلاء بعد",
135
+ "panel.agents.empty.body": "قم بتشغيل `yf agent install --defaults` لتسجيل المدير ووكلائه الفرعيين الأربعة (المحلل، المبرمج، المختبر، المُجهز).",
136
+ "panel.agents.legend.eco": "اقتصادي",
137
+ "panel.agents.legend.managed": "مُدار",
138
+ "panel.agents.legend.frontier": "حدود",
139
+ "panel.agents.status.disabled": "وقت تشغيل الوكلاء غير مُشغّل",
140
+ "panel.agents.event.running": "قيد التشغيل",
141
+ "panel.agents.event.idle": "خامل",
142
+ "panel.agents.tick.live": "متصل",
143
+ "panel.agents.tick.lost": "الاتصال مفقود",
144
+ "workflow.phase.i": "الفرز والنية",
145
+ "workflow.phase.ii": "التوضيح (الوظيفي + 4 NFR + الكتل)",
146
+ "workflow.phase.iii": "المواصفات (RFP + المعمارية + التصميم + الموافقات)",
147
+ "workflow.phase.iv": "التهيئة",
148
+ "workflow.phase.v": "تطوير الكتل",
149
+ "workflow.phase.vi": "النشر + ما بعد الإطلاق"
131
150
  }
@@ -127,5 +127,24 @@
127
127
  "error.project_not_registered.what": "Kein Adopter-Projekt registriert.",
128
128
  "error.project_not_registered.why": "Das aufgerufene Verb benoetigt einen aktiven Project-Root, aber ~/.yujin-forge/projects.json hat keine Eintraege.",
129
129
  "error.project_not_registered.fix": "Erstellen Sie eines mit yujin.project.init oder registrieren Sie ein existierendes mit yf projects add.",
130
- "error.project_not_registered.cmd": "yf projects scan"
130
+ "error.project_not_registered.cmd": "yf projects scan",
131
+ "panel.button.agents": "Agenten",
132
+ "panel.agents.title": "Agenten",
133
+ "panel.agents.subtitle": "Director und Sub-Agenten registriert in diesem Projekt",
134
+ "panel.agents.empty.title": "Noch keine Agenten",
135
+ "panel.agents.empty.body": "Fuehre `yf agent install --defaults` aus, um den Director und seine 4 Sub-Agenten (Analyst, Coder, Tester, Scaffolder) zu registrieren.",
136
+ "panel.agents.legend.eco": "Eco",
137
+ "panel.agents.legend.managed": "Managed",
138
+ "panel.agents.legend.frontier": "Frontier",
139
+ "panel.agents.status.disabled": "Agent-Runtime nicht gestartet",
140
+ "panel.agents.event.running": "Laeuft",
141
+ "panel.agents.event.idle": "Inaktiv",
142
+ "panel.agents.tick.live": "Verbunden",
143
+ "panel.agents.tick.lost": "Verbindung verloren",
144
+ "workflow.phase.i": "Triage und Intention",
145
+ "workflow.phase.ii": "Klaerung (funktional + 4 NFR + Bloecke)",
146
+ "workflow.phase.iii": "Spezifikationen (RFP + Arch + Design + Genehmigungen)",
147
+ "workflow.phase.iv": "Bootstrap",
148
+ "workflow.phase.v": "Blockentwicklung",
149
+ "workflow.phase.vi": "Deploy + Post-Launch"
131
150
  }