chati-dev 4.5.9 → 4.5.11

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "createdAt": "2026-08-27T12:53:59.633Z",
2
+ "createdAt": "2026-08-27T14:40:11.488Z",
3
3
  "files": {
4
4
  "agents/build/dev.md": {
5
5
  "hash": "13c2b3855c1f815154a2f4020b99fd8ee71e701b41783f5f26696222720a1466",
@@ -17,8 +17,8 @@
17
17
  "type": "md"
18
18
  },
19
19
  "agents/discover/brownfield-wu.md": {
20
- "hash": "b89e20408bdb0c672d6799507b0f9679c76df7f87f183f27b325070174ac490f",
21
- "size": 18120,
20
+ "hash": "fed8f14838b00e867bc932a3c4c212e38d7dc7c8f2e4443eb3242d19e6e3a286",
21
+ "size": 18172,
22
22
  "type": "md"
23
23
  },
24
24
  "agents/discover/greenfield-wu.md": {
@@ -52,8 +52,8 @@
52
52
  "type": "md"
53
53
  },
54
54
  "agents/plan/tasks.md": {
55
- "hash": "d6440ac7e7ecc9a24d89f39b1786e3cb218565a16b12ecba888e8899abe57b48",
56
- "size": 28257,
55
+ "hash": "a42a321ca2834c20364edb663619d340ac29317b236820135f07013cae9a4a82",
56
+ "size": 27960,
57
57
  "type": "md"
58
58
  },
59
59
  "agents/plan/ux-brand-architect.md": {
@@ -97,8 +97,8 @@
97
97
  "type": "md"
98
98
  },
99
99
  "config.yaml": {
100
- "hash": "2a0de0eae5e361cc23b1777573b3dadf4676ce8c231a3022dd14ebac385eb89f",
101
- "size": 2669,
100
+ "hash": "1b1103f17b9c2ba5820d8fae13c388bedf1bb3cceec93d3cd613b1b6e878bfce",
101
+ "size": 2671,
102
102
  "type": "yaml"
103
103
  },
104
104
  "constitution.md": {
@@ -107,8 +107,8 @@
107
107
  "type": "md"
108
108
  },
109
109
  "context/governance.md": {
110
- "hash": "bc39df23d48c2cbb005c8e062dfda53442a91f94ccc33f068e26f98d06fe412b",
111
- "size": 6200,
110
+ "hash": "88b3a3463fc14a26459f3202bf939dc954392642e4ac20830429dffd0f209f1f",
111
+ "size": 6225,
112
112
  "type": "md"
113
113
  },
114
114
  "context/protocols.md": {
@@ -117,18 +117,18 @@
117
117
  "type": "md"
118
118
  },
119
119
  "context/quality.md": {
120
- "hash": "43c417d43e47ab724aa559a5edfa476f555ff8a204fb116e9c2d7ceedb47f927",
121
- "size": 1997,
120
+ "hash": "d6cb70d274425b4385a63bd67c217038de9c12992d973b16000b370442905022",
121
+ "size": 2247,
122
122
  "type": "md"
123
123
  },
124
124
  "context/root.md": {
125
- "hash": "b76681d1bdd7c75e79733a520fc0c929858dc9399348dbb8ba30380e77c32849",
126
- "size": 1217,
125
+ "hash": "adb70c8b7cac6934f2650dd683e5b08a01f6d81dc6f380b0b546e05cce4963d4",
126
+ "size": 1218,
127
127
  "type": "md"
128
128
  },
129
129
  "data/entity-registry.yaml": {
130
- "hash": "c4b2005f61055a6f1d04c674ba369dbb997dda8d7f81991d6012f25b5a89210f",
131
- "size": 34605,
130
+ "hash": "e56261884e29b046f3c4b5f4d0a22b4fa27bceba17d669d0606ea0a25d3c8237",
131
+ "size": 34606,
132
132
  "type": "yaml"
133
133
  },
134
134
  "data/model-limits.json": {
@@ -302,8 +302,8 @@
302
302
  "type": "js"
303
303
  },
304
304
  "hooks/git-push-authority.js": {
305
- "hash": "5f4301a95a9b2ff252dbe2df0debccbbccdf6de7eb1ccec9c1a6015bf2353f6e",
306
- "size": 4243,
305
+ "hash": "cb296129155464438b7f3f8834eb9d62674c34c0ef12ccba8769cd2938da97d8",
306
+ "size": 4666,
307
307
  "type": "js"
308
308
  },
309
309
  "hooks/license-guard.js": {
@@ -377,23 +377,23 @@
377
377
  "type": "js"
378
378
  },
379
379
  "i18n/en.yaml": {
380
- "hash": "b5e9bf16c930a61543b17088a7267caea0c94eb32bfe0ae2e874ba15f5cdb4aa",
381
- "size": 4929,
380
+ "hash": "6b1ede4a442a86194d561d4a7a2fc5bf2259d23b23ae48eeaa973cf8fce79f33",
381
+ "size": 8046,
382
382
  "type": "yaml"
383
383
  },
384
384
  "i18n/es.yaml": {
385
- "hash": "5407e4c6adba697da8bf2e2dbdf39f1fd32935326166852d5b488487148320f1",
386
- "size": 5204,
385
+ "hash": "2542bf4a6b68883cea81021ed5ea84f5f05368be3a47b79d1b797753966a18ed",
386
+ "size": 8632,
387
387
  "type": "yaml"
388
388
  },
389
389
  "i18n/fr.yaml": {
390
- "hash": "2e3b50c56ee5f05f81c4e48fd048c679baae2280e3a9c03c589f8f82989eded4",
391
- "size": 5276,
390
+ "hash": "58c8d34b1d752592dbd6beb32b240475fdf297847558e8589174d713bae439c8",
391
+ "size": 8777,
392
392
  "type": "yaml"
393
393
  },
394
394
  "i18n/pt.yaml": {
395
- "hash": "da65aff46c8f16b1c6fd6be10b89b566253eec71fea0a42094878dcc9e4671fa",
396
- "size": 5147,
395
+ "hash": "fba6466fc35f464344d9a40d1172100d65c5e42d406285e3653c9bbad59b5607",
396
+ "size": 8516,
397
397
  "type": "yaml"
398
398
  },
399
399
  "intelligence/confidence.yaml": {
@@ -472,8 +472,8 @@
472
472
  "type": "md"
473
473
  },
474
474
  "orchestrator/chati.md": {
475
- "hash": "8d55e3e354e7e7cf4d28ca3aba5372df0580ca3b191e2e14696363867eba567a",
476
- "size": 45355,
475
+ "hash": "fbba2ec83ded49329dc33f674accb391cc290cd1687048967a49fc70fb62f3af",
476
+ "size": 46016,
477
477
  "type": "md"
478
478
  },
479
479
  "package.json": {
@@ -857,8 +857,8 @@
857
857
  "type": "json"
858
858
  },
859
859
  "schemas/session.schema.json": {
860
- "hash": "57b0aa1b097b2253664a548cad42652ed3ec759feab6d0c1098062e88048cf1e",
861
- "size": 19069,
860
+ "hash": "0c9218aeec82d4db591e87ffede20ea13eef6c549472f9471a1cee9ee637ba57",
861
+ "size": 19145,
862
862
  "type": "json"
863
863
  },
864
864
  "schemas/task.schema.json": {
@@ -1352,5 +1352,5 @@
1352
1352
  "type": "yaml"
1353
1353
  }
1354
1354
  },
1355
- "version": "4.5.9"
1355
+ "version": "4.5.11"
1356
1356
  }
@@ -1 +1 @@
1
- Hb7c+de1wUKAcc4qkLRzsVYqMzP02CNhfXf+bcp5ezJLqsH+KyZ7sOvQUpRf89mWC57jrLu/dscvchEyVdxQBQ==
1
+ Bj1mLU/kLfzNbNJmeY/BPjrL5bTt0MdOuSyfbjFTPniA8ev8TVO7Om+EQDxO0a6V0PHMYmVXf1GcoYWz8Q/VDw==
@@ -65,7 +65,12 @@ When the user invokes `/chati`, execute this sequence:
65
65
 
66
66
  /chati providers:
67
67
  → Run: node .chati.dev/orchestrator/chati-router.js providers
68
- Display provider table and agent model assignments
68
+ For v2, display three distinct blocks from JSON:
69
+ 1. `planning_agent_models`, discovery and planning routing only
70
+ 2. `rail_execution_pool`, eligible provider and model capabilities for task execution and review
71
+ 3. `release_lane`, explicit authorization policy
72
+ → Never describe `dev`, `qa-implementation`, `qa-visual`, or `devops` as v2 pipeline agents
73
+ → Never infer that a provider with no planning agent assignment is unavailable to RAIL
69
74
  → Stay locked
70
75
 
71
76
  /chati resume:
@@ -75,7 +80,7 @@ When the user invokes `/chati`, execute this sequence:
75
80
  → Display:
76
81
  /chati Start or resume session
77
82
  /chati status Show project dashboard
78
- /chati providers List providers and agent models
83
+ /chati providers Show planning routing and the RAIL execution pool
79
84
  /chati exit Save and exit session
80
85
  /chati help Show this help
81
86
  → Stay locked
@@ -319,7 +324,7 @@ returned `next` state. Do not treat it as an error.
319
324
 
320
325
  Planning phase agents (detail, architect, ux) run simultaneously.
321
326
 
322
- **NOTE (Article XXI):** When `features.agent_teams: true` in config.yaml, the orchestrator MUST attempt `spawn_team` FIRST for Planning Team (detail, architect, ux, qa-planning) and Build Team (dev, qa-implementation). Only fall back to `spawn_parallel` if team spawning is unavailable or fails. The CLI `orchestrate next` command handles this automatically - when teams are enabled, it returns `spawn_team` instead of `spawn_parallel`.
327
+ **NOTE (Article XXI):** For v2, when `features.agent_teams: true` in config.yaml, the orchestrator MUST attempt `spawn_team` first for the Planning Team (detail, architect, ux, qa-planning). Only fall back to `spawn_parallel` if team spawning is unavailable or fails. After Tasks, RAIL owns execution and review. A Build Team is never formed in v2.
323
328
 
324
329
  1. Display naturally what is happening using user-facing language. Examples (pick one that fits the phase): "Working on the planning phase now." / "Reviewing the architecture and UX in parallel." NEVER say "spawning parallel group", "spawn_parallel", or list internal agent names like "detail, architect, ux".
325
330
  2. If `parallel_spawn_commands` is non-empty, execute every member's `command`
@@ -363,7 +368,7 @@ Parse the JSON response. Extract these values - you will need them in Step 2:
363
368
 
364
369
  If `fallback_required: true` → fall back silently to the "Action: Parallel Spawn" section. Do NOT display error messages.
365
370
 
366
- Display naturally what is happening. For Planning Team: "Working on the planning - designing the requirements, architecture, and user experience together." For Build Team: "Building this with continuous quality review." NEVER expose internal agent names.
371
+ Display naturally what is happening: "Working on the planning, designing the requirements, architecture, and user experience together." Never expose internal agent names.
367
372
 
368
373
  ### MANDATORY Step 2: Spawn Teammates via Agent Tool
369
374
 
@@ -421,6 +426,7 @@ Agent tool call 4:
421
426
  Write your handoff to artifacts/handoffs/qa-planning-handoff.md when done.]
422
427
  ```
423
428
 
429
+ <!-- CHATI_LEGACY_BUILD_START -->
424
430
  **Legacy compatibility only, installations without `.chati/v2/installation.json`: For Build Team** (BUILD phase) - Wave-Based Pipeline:
425
431
 
426
432
  For a v2 installation this section MUST NOT run. The `tasks` handoff returns
@@ -593,6 +599,8 @@ Before dissolving the team, verify both handoffs exist:
593
599
  STOP and return.]
594
600
  2. `artifacts/handoffs/qa-implementation-handoff.md` - should exist from Final QA Wave
595
601
 
602
+ <!-- CHATI_LEGACY_BUILD_END -->
603
+
596
604
  ### Step 3: Collect Results
597
605
 
598
606
  When all Agent tool calls return:
@@ -700,9 +708,9 @@ The user is returning to an active session.
700
708
 
701
709
  ---
702
710
 
703
- ## Action: User Preview
711
+ ## Action: User Preview (legacy runtime only)
704
712
 
705
- QA-Implementation passed. User must approve before deploy.
713
+ This action is invalid when `.chati/v2/installation.json` exists. V2 preview evidence and external authorization belong to the RAIL Release Lane.
706
714
 
707
715
  1. Present QA results to user
708
716
  2. Options:
@@ -718,7 +726,7 @@ QA-Implementation passed. User must approve before deploy.
718
726
 
719
727
  ## Action: Complete
720
728
 
721
- Pipeline finished. Present final project summary. Run `orchestrate exit`.
729
+ The RAIL Release Lane has completed or the legacy pipeline has finished. Present the final project summary. Run `orchestrate exit`.
722
730
  Congratulate the user.
723
731
 
724
732
  ---
@@ -862,12 +870,12 @@ interactive (default):
862
870
  → Agent-driven guided mode. Agent leads, user validates.
863
871
  → All 8 universal protocols apply.
864
872
 
865
- autonomous (Ralph Wiggum mode):
873
+ autonomous:
866
874
  → Requires: most recent QA gate score >= 95% (Article XVII prerequisite).
867
875
  → If quality gate not met, inform user and suggest guided mode instead.
868
876
  → Agent executes without asking unless blocker encountered.
869
- → Primarily for Dev agent during BUILD phase.
870
- QA gates always run regardless of mode.
877
+ → Primarily for dependency-ready RAIL tasks.
878
+ Independent review and rework gates always run regardless of mode.
871
879
  → Activated by user request or orchestrator suggestion post-Brief.
872
880
  ```
873
881
 
@@ -906,12 +914,13 @@ discover/plan phase:
906
914
  BLOCK write to: everything else
907
915
  ALLOW read: everything
908
916
 
909
- build/validate phase:
917
+ RAIL execution/review:
910
918
  ALLOW write to: everything
911
919
  ALLOW read: everything
912
920
 
913
- deploy phase:
914
- ALLOW everything + infra operations
921
+ Release Lane:
922
+ ALLOW evidence preparation
923
+ REQUIRE explicit human authorization before external action
915
924
  ```
916
925
 
917
926
  ### Backward Transitions
@@ -923,10 +932,10 @@ If QA finds spec or architecture issues:
923
932
 
924
933
  ### Root Layer Routing (Article XXII)
925
934
 
926
- When QA-Implementation classifies a fault via the Fault Vector Protocol:
935
+ When the independent RAIL reviewer classifies a fault:
927
936
  - `INTENT` → escalate to user via deviation protocol (requirement is flawed)
928
937
  - `SPEC` → backward transition to Architect, UX, or Tasks agent
929
- - `CODE` → Dev agent silent correction loop (existing behavior)
938
+ - `CODE` → bounded RAIL rework using the task's sealed execution binding
930
939
  - `DEFER` → add to session backlog, do not block approval
931
940
 
932
941
  ---
@@ -936,13 +945,13 @@ When QA-Implementation classifies a fault via the Fault Vector Protocol:
936
945
  ### EXCLUSIVE (only orchestrator)
937
946
  - Route messages to agents
938
947
  - Activate/deactivate agents
939
- - Execute mode transitions (planningbuildvalidatedeploy)
948
+ - Execute mode transitions (discoveryplanningRAILRelease Lane)
940
949
  - Manage session lock (activate/deactivate)
941
950
  - Handle deviations and re-routing
942
951
  - Manage backlog items
943
952
  - Spawn parallel terminals
944
953
  - Decide execution mode (interactive vs autonomous)
945
- - Form and dissolve Agent Teams (Team Lead role, Article XXI)
954
+ - Form and dissolve the Planning Team (Team Lead role, Article XXI)
946
955
  - Monitor shared task list and mailbox during team execution
947
956
  - Detect and respond to echo events within teams (Article XXII)
948
957
  - Route corrections to correct layer via Root Layer Routing (Article XXII)
@@ -955,11 +964,11 @@ When QA-Implementation classifies a fault via the Fault Vector Protocol:
955
964
  - Generate session digests
956
965
 
957
966
  ### BLOCKED (never do this - redirect instead)
958
- - Write code or implementation files → route to dev
967
+ - Write code or implementation files outside RAIL → route through the sealed RAIL task
959
968
  - Write specification documents → route to detail
960
969
  - Make architectural decisions → route to architect
961
- - Write tests → route to dev
962
- - Deploy or configure infrastructure → route to devops
970
+ - Write tests outside RAIL → route through the sealed RAIL task
971
+ - Deploy or configure infrastructure directly → route to the Release Lane authorization gate
963
972
  - Modify constitution or config files
964
973
 
965
974
  ---
@@ -20,7 +20,7 @@
20
20
  "description": "Normalized active_model recorded when /chati-update last completed. Article XXIV: when active_model differs from this, the orchestrator advises a harness re-audit. The advisory is driven entirely by this comparison; there is no separate flag."
21
21
  },
22
22
  "mode": {
23
- "enum": ["discover", "plan", "build", "validate", "deploy", "completed"],
23
+ "enum": ["discover", "plan", "rail", "release", "build", "validate", "deploy", "completed"],
24
24
  "default": "discover",
25
25
  "description": "Flat pipeline macro-phase (mirrors project.state). Read by prism-engine and mode-governance hooks."
26
26
  },
@@ -67,7 +67,7 @@
67
67
  "properties": {
68
68
  "name": { "type": "string", "description": "Project name" },
69
69
  "type": { "enum": ["greenfield", "brownfield"], "description": "Project type" },
70
- "state": { "enum": ["discover", "plan", "build", "validate", "deploy", "completed"], "description": "Current pipeline macro-phase" }
70
+ "state": { "enum": ["discover", "plan", "rail", "release", "build", "validate", "deploy", "completed"], "description": "Current pipeline macro-phase" }
71
71
  }
72
72
  },
73
73
  "execution_mode": {
@@ -235,8 +235,8 @@
235
235
  "required": ["timestamp", "from", "to", "trigger"],
236
236
  "properties": {
237
237
  "timestamp": { "type": "string", "format": "date-time" },
238
- "from": { "enum": ["discover", "plan", "build", "validate", "deploy", "completed"] },
239
- "to": { "enum": ["discover", "plan", "build", "validate", "deploy", "completed"] },
238
+ "from": { "enum": ["discover", "plan", "rail", "release", "build", "validate", "deploy", "completed"] },
239
+ "to": { "enum": ["discover", "plan", "rail", "release", "build", "validate", "deploy", "completed"] },
240
240
  "trigger": { "type": "string", "description": "What caused the transition (agent completion, override, backward)" },
241
241
  "type": { "enum": ["automatic", "backward", "override"], "description": "Transition classification" },
242
242
  "skipped_agents": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chati-dev",
3
- "version": "4.5.9",
3
+ "version": "4.5.11",
4
4
  "description": "AI-Powered Multi-Agent Orchestration System - Structured vibe coding for Full Stack Development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -201,11 +201,19 @@ function buildReplacementConfig(provider) {
201
201
  * @returns {string} Adapted content for the target provider
202
202
  */
203
203
  export function adaptFrameworkFile(content, filePath, provider) {
204
- if (provider === 'claude') return content;
205
204
  if (!content) return content;
206
205
 
207
- const config = buildReplacementConfig(provider);
208
206
  let result = content;
207
+ if (filePath === 'orchestrator/chati.md') {
208
+ result = result.replace(
209
+ /<!-- CHATI_LEGACY_BUILD_START -->[\s\S]*?<!-- CHATI_LEGACY_BUILD_END -->\s*/g,
210
+ 'For installations without `.chati/v2/installation.json`, use the separately bundled legacy compatibility runtime.\n\n',
211
+ );
212
+ }
213
+
214
+ if (provider === 'claude') return result;
215
+
216
+ const config = buildReplacementConfig(provider);
209
217
 
210
218
  // 1. Apply ordered string replacements (most specific first)
211
219
  for (const [search, replace] of config.strings) {
@@ -15,7 +15,9 @@ const STATE_TO_GOVERNANCE_MODE = {
15
15
  planning: 'planning',
16
16
  build: 'build',
17
17
  validate: 'build',
18
+ rail: 'build',
18
19
  deploy: 'deploy',
20
+ release: 'deploy',
19
21
  completed: 'deploy',
20
22
  };
21
23
 
@@ -560,16 +560,16 @@ Governance rules are in \`.chati.dev/constitution.md\`.
560
560
  \`\`\`
561
561
  DISCOVER: WU -> Brief
562
562
  PLAN: [Planning Team: Detail + Architect + UX + QA-Planning] -> Phases -> Tasks
563
- BUILD: [Build Team: Dev + QA-Implementation] -> QA-Visual
564
- DEPLOY: DevOps
563
+ RAIL: Sealed task graph -> Execute -> Independent Review -> Bounded Rework
564
+ RELEASE: Evidence gate -> Explicit human authorization -> External action
565
565
  \`\`\`
566
566
 
567
567
  ## Agents
568
568
  - DISCOVER: .chati.dev/agents/discover/
569
569
  - PLAN: .chati.dev/agents/plan/
570
- - Quality: .chati.dev/agents/quality/
571
- - BUILD: .chati.dev/agents/build/
572
- - DEPLOY: .chati.dev/agents/deploy/
570
+ - Planning Quality: .chati.dev/agents/quality/qa-planning.md
571
+ - RAIL Runtime: .chati.dev/_cli/ and .chati/v2/rail/
572
+ - Release Lane: RAIL completion evidence plus explicit authorization
573
573
  `;
574
574
  }
575
575
 
@@ -62,11 +62,12 @@ export function generateSessionYaml(config) {
62
62
  reasoning_escalations: [],
63
63
  };
64
64
 
65
- // Initialize all 13 agent statuses
65
+ // V2 ends the classic agent pipeline at Tasks. RAIL owns implementation,
66
+ // independent review, bounded rework and the Release Lane after handoff.
66
67
  const agentNames = [
67
68
  'greenfield-wu', 'brownfield-wu', 'brief', 'detail',
68
69
  'architect', 'ux', 'phases', 'tasks',
69
- 'qa-planning', 'dev', 'qa-implementation', 'qa-visual', 'devops',
70
+ 'qa-planning',
70
71
  ];
71
72
 
72
73
  for (const agent of agentNames) {
@@ -53,7 +53,10 @@ import {
53
53
  recordEvent, EventType, clearTimeline,
54
54
  } from '../intelligence/timeline.js';
55
55
  import { updateClaudeMd } from '../memory/magic-docs.js';
56
- import { loadRuntimeInstallationV2, resolveRuntimeInvocationV2 } from './runtime-installation-v2.js';
56
+ import {
57
+ loadRuntimeInstallationV2, resolveRuntimeInvocationV2,
58
+ describeRuntimeInstallationV2, V2_PLANNING_AGENTS,
59
+ } from './runtime-installation-v2.js';
57
60
  import { compilePlanningHandoff, PLANNING_V2_TASKS_PATH } from './planning-runtime.js';
58
61
  import { persistRailHandoff } from './rail-runtime.js';
59
62
 
@@ -84,6 +87,16 @@ const RESUME_MESSAGES = {
84
87
  fr: 'Session sauvee. Tapez /chati pour reprendre.',
85
88
  };
86
89
 
90
+ function getRuntimeAgentPipeline(projectDir, session = null) {
91
+ if (!loadRuntimeInstallationV2(projectDir)) return AGENT_PIPELINE;
92
+ const projectType = session?.project_type || session?.project?.type || 'greenfield';
93
+ const selectedWu = projectType === 'brownfield' ? 'brownfield-wu' : 'greenfield-wu';
94
+ return AGENT_PIPELINE.filter(({ name }) =>
95
+ V2_PLANNING_AGENTS.includes(name)
96
+ && (!name.endsWith('-wu') || name === selectedWu)
97
+ );
98
+ }
99
+
87
100
  // ---------------------------------------------------------------------------
88
101
  // QA-Visual scope helpers (Fase 6 — scope isolation)
89
102
  //
@@ -516,8 +529,13 @@ function errorResult(message, code) {
516
529
  */
517
530
  function sessionToPipelineState(session, projectDir) {
518
531
  const agents = {};
532
+ const installationV2 = loadRuntimeInstallationV2(projectDir);
533
+ const allowedAgents = installationV2
534
+ ? new Set(getRuntimeAgentPipeline(projectDir, session).map(({ name }) => name))
535
+ : null;
519
536
  if (session.agents) {
520
537
  for (const [name, data] of Object.entries(session.agents)) {
538
+ if (allowedAgents && !allowedAgents.has(name)) continue;
521
539
  agents[name] = {
522
540
  status: data.status || 'pending',
523
541
  score: data.score ?? null,
@@ -536,7 +554,9 @@ function sessionToPipelineState(session, projectDir) {
536
554
  startedAt: session.started_at || new Date().toISOString(),
537
555
  completedAt: null,
538
556
  agents,
539
- completedAgents: session.completed_agents || [],
557
+ completedAgents: allowedAgents
558
+ ? (session.completed_agents || []).filter((name) => allowedAgents.has(name))
559
+ : (session.completed_agents || []),
540
560
  currentAgent: session.current_agent || null,
541
561
  modeTransitions: session.mode_transitions || [],
542
562
  history: [],
@@ -767,11 +787,12 @@ async function handleNext(projectDir) {
767
787
  const activating = ['activate_interactive', 'spawn_routed_interactive', 'spawn_autonomous', 'spawn_team', 'spawn_parallel'];
768
788
  if (activating.includes(result.action)) {
769
789
  try {
790
+ const runtimePipeline = getRuntimeAgentPipeline(projectDir, result.session);
770
791
  writeSessionLock(projectDir, result.agent, {
771
792
  phase: result.phase || 'discover',
772
793
  mode: result.session?.execution_mode || 'interactive',
773
794
  position: result.pipeline_progress?.completedAgents?.length ?? 0,
774
- total: AGENT_PIPELINE.length,
795
+ total: runtimePipeline.length,
775
796
  progress: result.pipeline_progress?.progress ?? 0,
776
797
  });
777
798
  } catch { /* non-fatal */ }
@@ -798,6 +819,38 @@ async function _handleNextInner(projectDir) {
798
819
  return errorResult(`Session invalid: ${validation.reason}`, 'SESSION_INVALID');
799
820
  }
800
821
 
822
+ const installationV2 = loadRuntimeInstallationV2(projectDir);
823
+ const runtimePipeline = getRuntimeAgentPipeline(projectDir, session);
824
+ if (installationV2 && session.rail_handoff_id) {
825
+ const completedPlanning = runtimePipeline.length;
826
+ return {
827
+ action: 'rail_execute',
828
+ agent: 'rail',
829
+ phase: 'rail',
830
+ handoff_id: session.rail_handoff_id,
831
+ rail_state: session.rail_state || 'ready',
832
+ pipeline_progress: {
833
+ phase: 'rail', progress: 100, completedCount: completedPlanning,
834
+ totalAgents: completedPlanning, completedAgents: session.completed_agents || [],
835
+ currentAgent: 'rail', nextAgent: null,
836
+ },
837
+ session: {
838
+ language: session.language,
839
+ project_type: session.project_type || session.project?.type,
840
+ execution_mode: session.execution_mode,
841
+ },
842
+ status_summary: 'Planning is sealed. Resume execution through RAIL.',
843
+ };
844
+ }
845
+
846
+ const legacyExecutionAgents = new Set(['dev', 'qa-implementation', 'qa-visual', 'devops']);
847
+ if (installationV2 && legacyExecutionAgents.has(session.current_agent)) {
848
+ return errorResult(
849
+ 'A v2 installation cannot resume a legacy Build/Deploy agent. Re-seal Tasks to create the RAIL handoff.',
850
+ 'V2_RAIL_HANDOFF_REQUIRED',
851
+ );
852
+ }
853
+
801
854
  const pipelineState = sessionToPipelineState(session, projectDir);
802
855
 
803
856
  if (isPipelineComplete(pipelineState)) {
@@ -836,7 +889,7 @@ async function _handleNextInner(projectDir) {
836
889
  handoff_status: { valid: true, missing: [], warnings: ['Resuming dev as Build Team'] },
837
890
  gate_status: { canAdvance: true, reason: 'Build Team formation (Article XXI)' },
838
891
  model_info: resolveAgentModel(agent, projectDir),
839
- context_bracket: estimateContextBracket(completedAgents.length, AGENT_PIPELINE.length),
892
+ context_bracket: estimateContextBracket(completedAgents.length, runtimePipeline.length),
840
893
  pipeline_progress: getPipelineProgress(pipelineState),
841
894
  session: { language: session.language, project_type: session.project_type || session.project?.type, execution_mode: session.execution_mode, user_level: session.user_level || 'auto' },
842
895
  status_summary: `Forming Build Team: dev + qa-implementation.`,
@@ -858,7 +911,7 @@ async function _handleNextInner(projectDir) {
858
911
  handoff_status: { valid: true, missing: [], warnings: ['Resuming in-progress agent'] },
859
912
  gate_status: { canAdvance: true, reason: 'Agent already in progress' },
860
913
  model_info: modelInfo,
861
- context_bracket: estimateContextBracket(completedAgents.length, AGENT_PIPELINE.length),
914
+ context_bracket: estimateContextBracket(completedAgents.length, runtimePipeline.length),
862
915
  pipeline_progress: getPipelineProgress(pipelineState),
863
916
  session: { language: session.language, project_type: session.project_type || session.project?.type, execution_mode: session.execution_mode, user_level: session.user_level || 'auto' },
864
917
  status_summary: `Resuming ${agent} (in progress).`,
@@ -887,7 +940,7 @@ async function _handleNextInner(projectDir) {
887
940
  handoff_status: { valid: true, missing: [], warnings: [] },
888
941
  gate_status: { canAdvance: true, reason: 'First agent in pipeline' },
889
942
  model_info: modelInfo,
890
- context_bracket: estimateContextBracket(0, AGENT_PIPELINE.length),
943
+ context_bracket: estimateContextBracket(0, runtimePipeline.length),
891
944
  pipeline_progress: getPipelineProgress(pipelineState),
892
945
  session: { language: session.language, project_type: projectType, execution_mode: session.execution_mode, user_level: session.user_level || 'auto' },
893
946
  status_summary: `Starting pipeline with ${firstAgent}.`,
@@ -927,6 +980,13 @@ async function _handleNextInner(projectDir) {
927
980
  const agentDef = getAgentDefinition(nextAgent);
928
981
  const isInteractive = INTERACTIVE_AGENTS.includes(nextAgent);
929
982
 
983
+ if (installationV2 && legacyExecutionAgents.has(nextAgent)) {
984
+ return errorResult(
985
+ 'Planning reached the v2 execution boundary without a sealed RAIL handoff. Complete or re-run Tasks instead of entering Build/Deploy.',
986
+ 'V2_RAIL_HANDOFF_REQUIRED',
987
+ );
988
+ }
989
+
930
990
  // Validate handoff from previous agent
931
991
  let handoffStatus;
932
992
  try {
@@ -1011,7 +1071,7 @@ async function _handleNextInner(projectDir) {
1011
1071
  recordEvent(projectDir, EventType.AGENT_ACTIVATED, nextAgent, { action, phase: agentDef?.phase });
1012
1072
 
1013
1073
  const progress = getPipelineProgress(pipelineState);
1014
- const bracket = estimateContextBracket(completedAgents.length, AGENT_PIPELINE.length);
1074
+ const bracket = estimateContextBracket(completedAgents.length, runtimePipeline.length);
1015
1075
 
1016
1076
  const result = {
1017
1077
  action,
@@ -1851,6 +1911,8 @@ async function handleAdvance(projectDir, args) {
1851
1911
  // the handoff identifier and must enter the project-local RAIL lifecycle.
1852
1912
  if (handoffData.rail_handoff) {
1853
1913
  const railState = {
1914
+ mode: 'rail',
1915
+ project: { ...session.project, state: 'rail' },
1854
1916
  current_agent: 'rail',
1855
1917
  last_handoff: agent,
1856
1918
  pipeline_position: (session.completed_agents || []).length + 1,
@@ -1860,12 +1922,28 @@ async function handleAdvance(projectDir, args) {
1860
1922
  };
1861
1923
  const saved = await updateSession(projectDir, railState);
1862
1924
  if (saved && saved.saved === false) return errorResult(`Session update failed: ${saved.error}`, 'SESSION_WRITE_FAILED');
1925
+ const runtimePipeline = getRuntimeAgentPipeline(projectDir, session);
1926
+ try {
1927
+ writeSessionLock(projectDir, 'rail', {
1928
+ phase: 'rail', mode: session.execution_mode || 'interactive',
1929
+ position: runtimePipeline.length, total: runtimePipeline.length, progress: 100,
1930
+ });
1931
+ updateClaudeMd(projectDir, {
1932
+ currentAgent: 'rail',
1933
+ progress: { progress: 100, completedCount: runtimePipeline.length, totalAgents: runtimePipeline.length },
1934
+ });
1935
+ } catch { /* non-fatal: the canonical session state has already been persisted */ }
1863
1936
  return {
1864
1937
  advanced: true,
1865
1938
  agent_completed: agent,
1866
1939
  score,
1867
1940
  rail_ready: true,
1868
1941
  rail_handoff: handoffData.rail_handoff,
1942
+ pipeline_progress: {
1943
+ phase: 'rail', progress: 100, completedCount: runtimePipeline.length,
1944
+ totalAgents: runtimePipeline.length, completedAgents: [...(session.completed_agents || []), agent],
1945
+ currentAgent: 'rail', nextAgent: null,
1946
+ },
1869
1947
  next: {
1870
1948
  action: 'rail_execute',
1871
1949
  handoff_id: handoffData.rail_handoff.handoff.handoff_id,
@@ -2277,8 +2355,10 @@ async function handleStatus(projectDir) {
2277
2355
  }
2278
2356
 
2279
2357
  // Build model map for all agents
2358
+ const runtimePipeline = getRuntimeAgentPipeline(projectDir, session);
2359
+ const runtimeAgentNames = new Set(runtimePipeline.map(({ name }) => name));
2280
2360
  const agentModels = {};
2281
- for (const agentDef of AGENT_PIPELINE) {
2361
+ for (const agentDef of runtimePipeline) {
2282
2362
  agentModels[agentDef.name] = resolveAgentModel(agentDef.name, projectDir);
2283
2363
  }
2284
2364
 
@@ -2309,9 +2389,9 @@ async function handleStatus(projectDir) {
2309
2389
  handoff_history: handoffHistory,
2310
2390
  deviation_count: (session.deviations || []).length,
2311
2391
  backlog: session.backlog || [],
2312
- agents: session.agents || {},
2392
+ agents: Object.fromEntries(Object.entries(session.agents || {}).filter(([name]) => runtimeAgentNames.has(name))),
2313
2393
  agent_models: agentModels,
2314
- context_bracket: estimateContextBracket((session.completed_agents || []).length, AGENT_PIPELINE.length),
2394
+ context_bracket: estimateContextBracket((session.completed_agents || []).length, runtimePipeline.length),
2315
2395
  context: {
2316
2396
  model: activeModel,
2317
2397
  provider: activeProvider,
@@ -2434,15 +2514,25 @@ async function handleExit(projectDir, args) {
2434
2514
  }
2435
2515
 
2436
2516
  async function handleProviders(projectDir) {
2517
+ const artifact = loadRuntimeInstallationV2(projectDir);
2518
+ if (artifact) {
2519
+ const { session } = loadSession(projectDir);
2520
+ const planningAgentModels = {};
2521
+ for (const { name } of getRuntimeAgentPipeline(projectDir, session)) {
2522
+ planningAgentModels[name] = resolveAgentModel(name, projectDir);
2523
+ }
2524
+ return {
2525
+ ...describeRuntimeInstallationV2(artifact),
2526
+ planning_agent_models: planningAgentModels,
2527
+ };
2528
+ }
2529
+
2437
2530
  const agentModels = {};
2438
2531
  for (const agentDef of AGENT_PIPELINE) {
2439
2532
  agentModels[agentDef.name] = resolveAgentModel(agentDef.name, projectDir);
2440
2533
  }
2441
2534
 
2442
- const artifact = loadRuntimeInstallationV2(projectDir);
2443
- const enabledProviders = artifact
2444
- ? artifact.installation.enabled_providers.map(({ provider_id, harness_id }) => ({ provider_id, harness_id }))
2445
- : [...new Set(Object.values(agentModels).map(({ provider }) => provider).filter(Boolean))].map((harness_id) => ({ harness_id }));
2535
+ const enabledProviders = [...new Set(Object.values(agentModels).map(({ provider }) => provider).filter(Boolean))].map((harness_id) => ({ harness_id }));
2446
2536
 
2447
2537
  return {
2448
2538
  enabled_providers: enabledProviders,