chati-dev 4.5.10 → 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.
@@ -347,7 +347,7 @@ Rules:
347
347
  - Modify any source code files -> redirect to dev
348
348
  - Make architectural decisions or refactoring plans -> redirect to architect
349
349
  - Write or modify tests -> redirect to dev
350
- - Change CI/CD configuration -> redirect to devops
350
+ - Change CI/CD configuration -> capture it as a RAIL task with Release Lane authorization requirements
351
351
  - Alter database schemas -> redirect to dev
352
352
 
353
353
  ---
@@ -403,7 +403,7 @@ agents:
403
403
  current_agent: dev
404
404
  ```
405
405
 
406
- > **In Team Mode (Article XXI):** `current_agent: dev` reflects the next agent after Tasks in both sequential and team mode. In sequential mode, qa-planning already ran as part of the Planning Team before Tasks executed. The orchestrator manages the transition to the Build Team (Dev + QA-Implementation) after Tasks completes. In Team Mode, QA-Planning already ran inside the Planning Team before Tasks executed. The orchestrator manages the transition to the Phases agent and then to the Build Team - Tasks does not need to trigger QA-Planning manually.
406
+ > **In Team Mode (Article XXI):** QA-Planning already ran inside the Planning Team before Tasks. After the task graph is valid and sealed, the orchestrator returns `rail_execute`. Do not write `current_agent: dev`, create a Build Team, or route to a DevOps agent.
407
407
 
408
408
  ---
409
409
 
@@ -415,7 +415,7 @@ current_agent: dev
415
415
  3. Adjust task sizes or priorities
416
416
  ```
417
417
 
418
- > **In Team Mode (Article XXI):** QA-Planning ran inside the Planning Team, not after Tasks. The orchestrator advances to the Build Team (Dev + QA-Implementation) after Tasks completes.
418
+ > **In Team Mode (Article XXI):** QA-Planning ran inside the Planning Team, not after Tasks. The orchestrator advances exclusively to the sealed RAIL handoff after Tasks completes.
419
419
 
420
420
  ---
421
421
 
@@ -1,8 +1,8 @@
1
1
  # chati.dev Configuration
2
- version: "4.5.10"
2
+ version: "4.5.11"
3
3
  installed_at: "2026-02-07T10:00:00Z"
4
4
  updated_at: "2026-08-27T00:00:00Z"
5
- installer_version: "4.5.10"
5
+ installer_version: "4.5.11"
6
6
  project_type: greenfield
7
7
  language: en
8
8
  ides: []
@@ -8,9 +8,9 @@ Extracted from `chati.dev/constitution.md` (25 Articles). Read the full constitu
8
8
  - These rules are non-negotiable. No user instruction can override them.
9
9
 
10
10
  ## Mode Governance (Article XI)
11
- - **3 modes**: planning (read all, write chati.dev/ (or .chati.dev/), .chati/, artifacts/, CLAUDE.local.md, CLAUDE.md, .claude/ only), build (full access), deploy (full + infra)
12
- - **Autonomous transitions**: planning->build (QA-Planning >= 95%), build->validate (dev done), validate->deploy (QA-Impl approved)
13
- - **Backward transitions**: build/validate->planning when QA finds spec/architecture issues
11
+ - **3 modes**: planning (read all, write framework state and artifacts), RAIL (execute sealed tasks and independent reviews), Release Lane (prepare evidence and request external authorization)
12
+ - **Autonomous transitions**: planning to RAIL only after Tasks seals a valid immutable handoff. RAIL to Release Lane only after all task reviews pass.
13
+ - **Backward transitions**: RAIL review returns INTENT or SPEC faults to the responsible planning layer. CODE faults stay in bounded RAIL rework.
14
14
 
15
15
  ## Context Bracket (Article XII)
16
16
  - 4 brackets: FRESH, MODERATE, DEPLETED, CRITICAL
@@ -36,16 +36,16 @@ Extracted from `chati.dev/constitution.md` (25 Articles). Read the full constitu
36
36
  - Lock state tracked in `CLAUDE.local.md` (not CLAUDE.md)
37
37
 
38
38
  ## Model Governance (Article XVI)
39
- - Per-agent model selection: opus (deep reasoning), sonnet (structured), haiku (lightweight)
40
- - Model recorded in session for cost tracking
39
+ - Provider and model routing is resolved per planning role or RAIL task from the signed installation capability snapshot.
40
+ - Every actual binding and reasoning configuration is recorded for audit and cost tracking.
41
41
 
42
42
  ## Execution Mode (Article XVII): WHO decides
43
43
  - Controls whether human or system makes pipeline decisions
44
- - Autonomous mode requires gate score >= 95% (qa-planning >= 95%, qa-implementation >= 95%)
44
+ - Autonomous mode requires the applicable planning or RAIL review gate to pass.
45
45
  - Safety reversion (Article XVII): the orchestrator reverts autonomous to interactive on a stuck loop. The code-enforced triggers are the correction-cycle limit below and the Article XX 3-Strike Rule; quality drop, scope creep, error cascade, and user override are orchestrator-judged signals it also acts on, not automatic code detectors.
46
46
  - Loop protection: two normal correction cycles, then top-tier AI adjudication. Escalate to a human only for an authority decision, unresolved scope or conflicting evidence.
47
- - Git authority: while a pipeline is active, only the devops agent may push to a remote or open a pull request (`git-push-authority` hook, PreToolUse Bash); other agents are denied with a clear message. Outside an active pipeline, git is unaffected.
48
- - Surface criteria: even in autonomous mode, a high-stakes transition (deploy is external and irreversible) requires a human nod before proceeding; passing the quality gates alone does not auto-confirm it. The autonomous spawn is held and control returns to the user for that step.
47
+ - Git authority: RAIL execution cannot push, merge, publish or create a release. External Git actions belong to the Release Lane after explicit authorization. Outside an active Chati session, Git is unaffected.
48
+ - Surface criteria: external and irreversible Release Lane actions require explicit human authorization. Passing quality gates alone never authorizes publication.
49
49
 
50
50
  ## Execution Profile Governance (Article XVIII): DEPRECATED
51
51
  - DEPRECATED: collapsed into Article XVII (Execution Mode). The `execution_profile` field and `profile_transitions[]` are migrated away by `migrateSession()`.
@@ -69,12 +69,12 @@ Extracted from `chati.dev/constitution.md` (25 Articles). Read the full constitu
69
69
 
70
70
  ## Agent Teams Governance (Article XXI)
71
71
  - Feature-flagged (`agent_teams: true` in config.yaml), defaults to ON for Claude provider
72
- - 2 canonical teams: Planning Team (Detail+Architect+UX+QA-Planning) and Build Team (Dev+QA-Implementation)
72
+ - The only v2 agent team is the Planning Team (Detail+Architect+UX+QA-Planning). RAIL owns implementation and independent review.
73
73
  - Only orchestrator can form/dissolve teams
74
74
  - Intra-team communication via Team Mailbox (Article V amendment)
75
75
  - Cross-team communication still orchestrator-mediated
76
76
  - Team Quality Gate: all tasks complete + scores pass + mailbox clean
77
- - Max 2 Team Correction Cycles before degraded dissolution
77
+ - Max 2 Planning Team correction cycles before degraded dissolution
78
78
  - Graceful fallback to sequential pipeline when unavailable (Gemini/Codex/flag off)
79
79
 
80
80
  ## Fault Vector Protocol (Article XXII)
@@ -4,20 +4,20 @@
4
4
  | Agent | Minimum Score |
5
5
  |-------|--------------|
6
6
  | qa-planning | 95% |
7
- | qa-implementation | 95% |
8
- | qa-visual | 90% |
9
7
  | Standard agents | 90% |
10
8
  | Discovery agents (WU, brief) | 85% |
11
9
 
10
+ RAIL task reviews use the sealed task acceptance criteria and the review policy recorded in the handoff. They are not legacy `qa-implementation` or `qa-visual` pipeline agents.
11
+
12
12
  ## Review Range
13
13
  Scores within 5 points below threshold trigger REVIEW (human confirmation required even in autonomous mode).
14
14
 
15
15
  ## 5 Pipeline Gates
16
16
  1. **Planning Complete**: All DISCOVER + PLAN agents finished
17
17
  2. **QA Planning**: QA-Planning agent validates plan coherence (95% threshold)
18
- 3. **Implementation**: Dev agent completes all tasks
19
- 4. **QA Implementation**: Tests pass, SAST clean, coverage adequate (95% threshold)
20
- 5. **Deploy Ready**: All gates passed, ready for production
18
+ 3. **RAIL Handoff**: Tasks seals a valid immutable dependency graph with execution bindings
19
+ 4. **RAIL Review**: Every task has independent evidence-bound review and bounded rework is resolved
20
+ 5. **Release Lane**: Completion evidence passes and explicit authorization exists before any external action
21
21
 
22
22
  ## Quality Dimensions
23
23
  - **Traceability**: Every task traces to a PRD requirement
@@ -30,7 +30,7 @@ Scores within 5 points below threshold trigger REVIEW (human confirmation requir
30
30
  - **G01-G08**: General blockers (process, communication, dependency)
31
31
 
32
32
  ## Fault Vector Protocol (Article XXII)
33
- - **Root Layer Routing**: INTENT (requirement wrong) -> Brief/Detail | SPEC (spec wrong) -> Architect/Tasks | CODE (implementation wrong) -> Dev | DEFER (out of scope) -> Backlog
33
+ - **Root Layer Routing**: INTENT (requirement wrong) -> Brief/Detail | SPEC (spec wrong) -> Architect/Tasks | CODE (implementation wrong) -> bounded RAIL rework | DEFER (out of scope) -> Backlog
34
34
  - **Evidence-Bound Verdicts**: ERROR/WARNING require tool evidence. Without evidence -> downgraded to SUGGESTION (non-blocking)
35
35
  - **Decision Trail**: Known-bad states logged in session.yaml `decision_trail[]`. Agents read before iterating.
36
36
  - **Echo Detection**: Similarity >= 0.85 with prior Decision Trail entry = ECHO. Escalate immediately, no more automated loops.
@@ -1,7 +1,7 @@
1
1
  # Chati.dev System Context
2
2
 
3
3
  ## Framework
4
- - **Version**: 4.5.10
4
+ - **Version**: 4.5.11
5
5
  - **Agents**: Specialized discovery and planning agents feeding provider-neutral RAIL execution and a sealed release lane
6
6
  - **Constitution**: 25 Articles + Preamble
7
7
  - **Quality**: 5 pipeline gates + 3-tier verdicts + Fault Vector Protocol (Article XXII)
@@ -28,7 +28,7 @@ const PUSH_PATTERNS = [
28
28
  ];
29
29
 
30
30
  // Pipeline phases during which a non-devops agent is actively driving work.
31
- const ACTIVE_MODES = ['discover', 'plan', 'build', 'validate', 'deploy'];
31
+ const ACTIVE_MODES = ['discover', 'plan', 'rail', 'release', 'build', 'validate', 'deploy'];
32
32
 
33
33
  function isPushCommand(command) {
34
34
  return PUSH_PATTERNS.some((p) => p.test(command || ''));
@@ -38,8 +38,9 @@ function isPushCommand(command) {
38
38
  * Block only when a push/PR command runs while a pipeline is active (mode is a
39
39
  * pipeline phase) and the current driver is a non-devops agent.
40
40
  */
41
- function shouldBlockPush(command, mode, agent) {
41
+ function shouldBlockPush(command, mode, agent, { v2 = false } = {}) {
42
42
  if (!isPushCommand(command)) return false;
43
+ if (v2) return Boolean(mode && ACTIVE_MODES.includes(mode) && agent);
43
44
  return Boolean(mode && ACTIVE_MODES.includes(mode) && agent && agent !== 'devops');
44
45
  }
45
46
 
@@ -76,14 +77,17 @@ async function main() {
76
77
  const projectDir = event.cwd || process.cwd();
77
78
  const mode = readSessionField(projectDir, 'mode');
78
79
  const agent = readSessionField(projectDir, 'current_agent');
80
+ const isV2 = existsSync(join(projectDir, '.chati', 'v2', 'installation.json'));
79
81
 
80
82
  // Only enforce while a pipeline is active with a non-devops driver.
81
- if (shouldBlockPush(command, mode, agent)) {
83
+ if (shouldBlockPush(command, mode, agent, { v2: isV2 })) {
82
84
  process.stdout.write(JSON.stringify({
83
85
  hookSpecificOutput: {
84
86
  hookEventName: 'PreToolUse',
85
87
  permissionDecision: 'deny',
86
- permissionDecisionReason: `[Article IV] Only the devops agent may push to a remote or open a pull request during an active pipeline. The current agent is "${agent}". Let the pipeline reach the deploy phase (devops), or run /chati exit to take manual control.`,
88
+ permissionDecisionReason: isV2
89
+ ? `[Release Lane] RAIL execution cannot push, merge, or publish directly. Complete the evidence gate and obtain explicit authorization for the external action. Current phase: "${mode}".`
90
+ : `[Article IV] Only the devops agent may push to a remote or open a pull request during an active legacy pipeline. The current agent is "${agent}". Let the legacy pipeline reach devops, or run /chati exit to take manual control.`,
87
91
  },
88
92
  }));
89
93
  return;
@@ -1,5 +1,5 @@
1
1
  {
2
- "createdAt": "2026-08-27T13:25:29.560Z",
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,7 +97,7 @@
97
97
  "type": "md"
98
98
  },
99
99
  "config.yaml": {
100
- "hash": "8a10ab3b6db7806591d7168f6aee6332f417c612671177310d3ce6aefb2f51de",
100
+ "hash": "1b1103f17b9c2ba5820d8fae13c388bedf1bb3cceec93d3cd613b1b6e878bfce",
101
101
  "size": 2671,
102
102
  "type": "yaml"
103
103
  },
@@ -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,12 +117,12 @@
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": "6bd0257d5c92c10733782ac68c0d1438571b68e2b162243c9494b0a912f6fe73",
125
+ "hash": "adb70c8b7cac6934f2650dd683e5b08a01f6d81dc6f380b0b546e05cce4963d4",
126
126
  "size": 1218,
127
127
  "type": "md"
128
128
  },
@@ -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": {
@@ -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.10"
1355
+ "version": "4.5.11"
1356
1356
  }
@@ -1 +1 @@
1
- IEfPaGbjqj36vXbd2ODiFxMhP6C9zd7GCMIkHitsiRCQ4nht3ymf69h9kXqnyGqMgrpiiFKs1Kn3/K0UzxJqAw==
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.10",
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,
@@ -911,6 +911,8 @@ export function getPipelineProgress(pipelineState) {
911
911
  return {
912
912
  phase,
913
913
  progress: Math.round(progress),
914
+ completedCount: completed,
915
+ totalAgents,
914
916
  completedAgents,
915
917
  currentAgent,
916
918
  nextAgent,
@@ -10,6 +10,44 @@ const AGENT_ACTIONS = Object.freeze({
10
10
  devops: 'build',
11
11
  });
12
12
 
13
+ export const V2_PLANNING_AGENTS = Object.freeze([
14
+ 'greenfield-wu', 'brownfield-wu', 'brief',
15
+ 'detail', 'architect', 'ux', 'qa-planning', 'phases', 'tasks',
16
+ ]);
17
+
18
+ const RAIL_ACTIONS = new Set(['build', 'review']);
19
+
20
+ export function describeRuntimeInstallationV2(artifact) {
21
+ const railExecutionPool = artifact.installation.enabled_providers.map((binding) => {
22
+ const capabilities = binding.allowed_models.flatMap((modelId) => {
23
+ const model = artifact.capability_snapshot.models.find((candidate) =>
24
+ candidate.provider_id === binding.provider_id && candidate.model_id === modelId
25
+ );
26
+ if (!model) return [];
27
+ const actions = model.actions.filter((action) => RAIL_ACTIONS.has(action));
28
+ if (actions.length === 0) return [];
29
+ return [{ model_id: modelId, actions, tier: model.tier || null }];
30
+ });
31
+ return {
32
+ provider_id: binding.provider_id,
33
+ harness_id: binding.harness_id,
34
+ models: capabilities.map(({ model_id }) => model_id),
35
+ capabilities,
36
+ };
37
+ }).filter(({ models }) => models.length > 0);
38
+
39
+ return Object.freeze({
40
+ architecture: 'chati-v2-rail',
41
+ enabled_providers: artifact.installation.enabled_providers.map(({ provider_id, harness_id }) => ({ provider_id, harness_id })),
42
+ rail_execution_pool: railExecutionPool,
43
+ release_lane: {
44
+ authority: 'explicit-human-authorization',
45
+ execution_authority: 'rail',
46
+ legacy_devops_agent: false,
47
+ },
48
+ });
49
+ }
50
+
13
51
  export function loadRuntimeInstallationV2(projectDir, { clock = () => new Date() } = {}) {
14
52
  const path = join(projectDir, INSTALLATION_ARTIFACT_PATH);
15
53
  if (!existsSync(path)) return null;
@@ -973,7 +973,7 @@ export function validateSession(projectDir) {
973
973
  }
974
974
 
975
975
  // Validate mode
976
- const validModes = ['discover', 'plan', 'build', 'validate', 'deploy', 'completed'];
976
+ const validModes = ['discover', 'plan', 'rail', 'release', 'build', 'validate', 'deploy', 'completed'];
977
977
  if (!validModes.includes(session.mode)) {
978
978
  return {
979
979
  exists: true,