chati-dev 4.5.28 → 4.5.29

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 (172) hide show
  1. package/bin/chati.js +151 -123
  2. package/framework/agents/build/dev.md +4 -4
  3. package/framework/agents/deploy/devops.md +3 -3
  4. package/framework/agents/discover/brief.md +18 -6
  5. package/framework/agents/discover/brownfield-wu.md +3 -3
  6. package/framework/agents/discover/greenfield-wu.md +4 -4
  7. package/framework/agents/plan/architect.md +2 -2
  8. package/framework/agents/plan/detail.md +4 -4
  9. package/framework/agents/plan/phases.md +3 -3
  10. package/framework/agents/plan/tasks.md +47 -19
  11. package/framework/agents/plan/ux-brand-architect.md +1 -1
  12. package/framework/agents/plan/ux.md +26 -2
  13. package/framework/agents/quality/qa-implementation.md +8 -8
  14. package/framework/agents/quality/qa-planning.md +58 -4
  15. package/framework/agents/quality/qa-visual.md +4 -4
  16. package/framework/config.yaml +2 -2
  17. package/framework/constitution.md +12 -1
  18. package/framework/context/governance.md +2 -1
  19. package/framework/context/root.md +3 -1
  20. package/framework/domains/constitution.yaml +1 -1
  21. package/framework/domains/global.yaml +1 -1
  22. package/framework/domains/workflows/standard-flow.yaml +7 -8
  23. package/framework/hooks/advance-trigger.js +3 -2
  24. package/framework/hooks/brief-validator.js +1 -1
  25. package/framework/hooks/post-dev.js +1 -1
  26. package/framework/hooks/team-quality-gate.js +9 -2
  27. package/framework/i18n/en.yaml +1 -0
  28. package/framework/i18n/es.yaml +1 -0
  29. package/framework/i18n/fr.yaml +1 -0
  30. package/framework/i18n/pt.yaml +1 -0
  31. package/framework/manifest.json +83 -83
  32. package/framework/manifest.sig +1 -1
  33. package/framework/orchestrator/chati-router.js +14 -6
  34. package/framework/orchestrator/chati.md +61 -23
  35. package/framework/rules/human-writing-style.md +7 -0
  36. package/framework/schemas/session.schema.json +25 -2
  37. package/framework/schemas/task.schema.json +26 -0
  38. package/framework/tasks/orchestrator-handoff.md +10 -10
  39. package/framework/tasks/orchestrator-resume.md +3 -3
  40. package/framework/workflows/brownfield-fullstack.yaml +6 -3
  41. package/framework/workflows/brownfield-service.yaml +5 -3
  42. package/framework/workflows/brownfield-ui.yaml +5 -3
  43. package/framework/workflows/greenfield-fullstack.yaml +6 -3
  44. package/framework/workflows/standard-flow.yaml +8 -3
  45. package/node_modules/@chati/core/native/browser-proof-wire.h +29 -0
  46. package/node_modules/@chati/core/native/darwin-browser-listener.c +36 -0
  47. package/node_modules/@chati/core/native/darwin-browser-listener.h +32 -0
  48. package/node_modules/@chati/core/native/darwin-browser-proof.h +86 -0
  49. package/node_modules/@chati/core/native/darwin-code-identity.h +35 -0
  50. package/node_modules/@chati/core/native/darwin-launch-messages.h +37 -0
  51. package/node_modules/@chati/core/native/darwin-launcher.c +312 -0
  52. package/node_modules/@chati/core/native/descriptor-fs.c +407 -0
  53. package/node_modules/@chati/core/native/execution-admission.h +59 -0
  54. package/node_modules/@chati/core/native/execution-gate.c +41 -0
  55. package/node_modules/@chati/core/native/linux-process.c +41 -0
  56. package/node_modules/@chati/core/native/prebuilds/darwin-arm64/darwin-launcher +0 -0
  57. package/node_modules/@chati/core/native/prebuilds/darwin-arm64/descriptor-fs.node +0 -0
  58. package/node_modules/@chati/core/native/prebuilds/darwin-arm64/execution-gate +0 -0
  59. package/node_modules/@chati/core/native/prebuilds/linux-x64/descriptor-fs.node +0 -0
  60. package/node_modules/@chati/core/native/prebuilds/linux-x64/execution-gate +0 -0
  61. package/node_modules/@chati/core/native/process-info-pipe.c +91 -0
  62. package/node_modules/@chati/core/package.json +2 -1
  63. package/node_modules/@chati/core/scripts/build-native.js +36 -0
  64. package/node_modules/@chati/core/src/darwin-browser-proof.js +87 -0
  65. package/node_modules/@chati/core/src/darwin-host-boot.js +58 -0
  66. package/node_modules/@chati/core/src/darwin-process.js +79 -0
  67. package/node_modules/@chati/core/src/darwin-supervisor-channels.js +82 -0
  68. package/node_modules/@chati/core/src/darwin-terminal-receipt.js +111 -0
  69. package/node_modules/@chati/core/src/descriptor-fs.js +222 -0
  70. package/node_modules/@chati/core/src/execution-gate.js +8 -0
  71. package/node_modules/@chati/core/src/host-boot-marker.js +45 -0
  72. package/node_modules/@chati/core/src/index.js +18 -0
  73. package/node_modules/@chati/core/src/linux-host-boot.js +218 -0
  74. package/node_modules/@chati/core/src/linux-process.js +133 -0
  75. package/node_modules/@chati/planning/src/browser-verification-plan.js +71 -0
  76. package/node_modules/@chati/planning/src/index.js +33 -0
  77. package/node_modules/@chati/planning/src/planning-material.js +64 -0
  78. package/node_modules/@chati/planning/src/verification-plan.js +57 -0
  79. package/node_modules/@chati/rail/package.json +1 -0
  80. package/node_modules/@chati/rail/src/execution-restoration.js +92 -0
  81. package/node_modules/@chati/rail/src/index.js +812 -167
  82. package/node_modules/@chati/rail/src/provider-processing.js +118 -0
  83. package/node_modules/@chati/rail/src/terminal-containment.js +23 -0
  84. package/node_modules/@chati/release-lane/src/index.js +40 -43
  85. package/node_modules/@chati/review-council/src/index.js +223 -0
  86. package/node_modules/@chati/tracking-clickup/src/index.js +67 -58
  87. package/package-artifact-manifest.json +1 -1
  88. package/package-artifact-manifest.sig +1 -1
  89. package/package.json +3 -2
  90. package/scripts/verify-real-harness-e2e.js +20 -8
  91. package/src/installer/core.js +147 -107
  92. package/src/installer/package-artifact.js +7 -0
  93. package/src/installer/preflight.js +13 -0
  94. package/src/installer/provider-overlay.js +1 -2
  95. package/src/installer/templates.js +58 -0
  96. package/src/installer-v2/catalog-client.js +31 -1
  97. package/src/installer-v2/clickup-connection-storage.js +92 -0
  98. package/src/installer-v2/clickup-mcp-client.js +155 -0
  99. package/src/installer-v2/clickup-oauth-loopback.js +96 -0
  100. package/src/installer-v2/clickup-oauth.js +233 -0
  101. package/src/installer-v2/index.js +141 -15
  102. package/src/installer-v2/installation-authority.js +98 -4
  103. package/src/installer-v2/provider-executable.js +117 -12
  104. package/src/installer-v2/wizard-installation.js +11 -0
  105. package/src/orchestrator/agent-selector.js +39 -1
  106. package/src/orchestrator/browser-artifact-store.js +131 -0
  107. package/src/orchestrator/cli.js +762 -205
  108. package/src/orchestrator/clickup-mcp-observer.js +226 -0
  109. package/src/orchestrator/clickup-runtime.js +45 -24
  110. package/src/orchestrator/council-gate-runtime.js +903 -0
  111. package/src/orchestrator/evidence-authority-enrollment.js +75 -0
  112. package/src/orchestrator/evidence-authority-request.js +46 -0
  113. package/src/orchestrator/handoff-engine.js +4 -11
  114. package/src/orchestrator/pipeline-manager.js +42 -4
  115. package/src/orchestrator/planning-contract-revision.js +711 -0
  116. package/src/orchestrator/planning-reconciliation.js +302 -0
  117. package/src/orchestrator/planning-runtime.js +476 -29
  118. package/src/orchestrator/rail-adjudication-evidence.js +15 -24
  119. package/src/orchestrator/rail-human-effort-authority.js +46 -0
  120. package/src/orchestrator/rail-human-effort.js +149 -0
  121. package/src/orchestrator/rail-runtime.js +291 -47
  122. package/src/orchestrator/rail-verification.js +278 -0
  123. package/src/orchestrator/review-runtime.js +37 -2
  124. package/src/orchestrator/runtime-installation-v2.js +143 -24
  125. package/src/orchestrator/session-manager.js +1457 -29
  126. package/src/tasks/handoff.js +57 -33
  127. package/src/terminal/adapters/claude-adapter.js +3 -0
  128. package/src/terminal/adapters/codex-adapter.js +13 -0
  129. package/src/terminal/adapters/grok-adapter.js +1 -0
  130. package/src/terminal/agent-attempt-budget.js +35 -0
  131. package/src/terminal/browser-endpoint-proxy.js +174 -0
  132. package/src/terminal/browser-observer-driver.js +94 -0
  133. package/src/terminal/browser-observer-supervisor.js +73 -0
  134. package/src/terminal/browser-observer.js +95 -0
  135. package/src/terminal/browser-process-facts.js +75 -0
  136. package/src/terminal/browser-runtime.js +131 -0
  137. package/src/terminal/darwin-authority-policy.js +213 -0
  138. package/src/terminal/darwin-browser-application.js +116 -0
  139. package/src/terminal/darwin-browser-observer.js +57 -0
  140. package/src/terminal/darwin-execution.js +185 -0
  141. package/src/terminal/executable-snapshot.js +202 -0
  142. package/src/terminal/handoff-parser.js +48 -2
  143. package/src/terminal/isolation.js +14 -13
  144. package/src/terminal/prompt-builder.js +176 -15
  145. package/src/terminal/provider-preflight.js +47 -27
  146. package/src/terminal/rail-execution-worktree.js +22 -6
  147. package/src/terminal/rail-process-observer.js +10 -0
  148. package/src/terminal/rail-prompts.js +21 -3
  149. package/src/terminal/rail-readonly-workspace.js +54 -5
  150. package/src/terminal/rail-worktree-checkpoint.js +315 -0
  151. package/src/terminal/rail-worktree-restoration.js +485 -0
  152. package/src/terminal/run-agent.js +164 -42
  153. package/src/terminal/run-council-gate.js +338 -0
  154. package/src/terminal/run-planning-reconciliation.js +49 -0
  155. package/src/terminal/run-rail-adjudication.js +29 -7
  156. package/src/terminal/run-rail-recovery.js +97 -0
  157. package/src/terminal/run-rail-review.js +36 -15
  158. package/src/terminal/run-rail-rework.js +26 -15
  159. package/src/terminal/run-rail-task.js +22 -9
  160. package/src/terminal/runtime-native-helper.js +85 -0
  161. package/src/terminal/spawner.js +899 -142
  162. package/src/terminal/verification-process-facts.js +49 -0
  163. package/src/upgrade/entrypoints.js +68 -0
  164. package/src/upgrade/generation.js +194 -0
  165. package/src/upgrade/maintenance.js +79 -0
  166. package/src/upgrade/paths.js +81 -0
  167. package/src/upgrade/upgrade.js +267 -0
  168. package/src/utils/execution-role.js +15 -0
  169. package/src/utils/handoff-paths.js +59 -0
  170. package/src/wizard/i18n.js +1 -0
  171. package/src/wizard/index.js +17 -5
  172. package/src/wizard/questions.js +24 -12
@@ -37,6 +37,13 @@ Avoid: "comprehensive, robust, scalable solution". Use the adjective only if it
37
37
 
38
38
  ## Multilingual application
39
39
 
40
+ This style guide does not select the output language. Article VII governs it:
41
+ authored artifact prose, including both handoff layers, stays English; conversation
42
+ and the terminal handoff envelope use the session language. Clearly identified
43
+ quoted original input stays verbatim, without style edits or translation. The
44
+ format exemptions above are style exemptions, not language exemptions for newly
45
+ authored prose. Do not copy localized terminal summaries into artifact files.
46
+
40
47
  Framework supports en/pt/es/fr (Article VII). The English patterns above are EXEMPLARS, not exhaustive enumeration. When producing output in pt-BR / es / fr, apply the SAME PRINCIPLES to language-equivalent forms:
41
48
 
42
49
  - Filler verbs: pt "elucidar / alavancar / facilitar"; es "profundizar / aprovechar / facilitar"; fr "approfondir / exploiter / faciliter"
@@ -56,7 +56,9 @@
56
56
  "properties": {
57
57
  "score": { "type": "number", "minimum": 0, "maximum": 100 },
58
58
  "verdict": { "enum": ["APPROVED", "REVIEW", "NEEDS_WORK", "PASS", "FAIL", null] },
59
- "completed_at": { "type": ["string", "null"], "format": "date-time" }
59
+ "completed_at": { "type": ["string", "null"], "format": "date-time" },
60
+ "execution_result_status": { "enum": ["complete"], "description": "Explicit successful routed result, required before v2 advance. Missing legacy markers are ambiguous, not success." },
61
+ "execution_failure": { "type": "object", "description": "Negative settlement of a legacy ambiguous result; never a completed agent or gate." }
60
62
  }
61
63
  }
62
64
  }
@@ -81,6 +83,11 @@
81
83
  "default": [],
82
84
  "description": "List of enabled CLI providers"
83
85
  },
86
+ "council_gates": {
87
+ "type": "object",
88
+ "default": {},
89
+ "description": "Content-addressed outcome for automatic cross-provider planning-quality gates."
90
+ },
84
91
  "current_agent": {
85
92
  "type": "string",
86
93
  "description": "Currently active agent name"
@@ -142,7 +149,23 @@
142
149
  "type": "object",
143
150
  "required": ["status", "score", "criteria_count"],
144
151
  "properties": {
145
- "status": { "enum": ["pending", "in_progress", "waiting_for_user", "awaiting_advance", "completed", "skipped", "needs_revalidation"] },
152
+ "status": { "enum": ["pending", "pending_rework", "in_progress", "waiting_for_user", "awaiting_advance", "completed", "skipped", "needs_revalidation"] },
153
+ "content_review_pending": { "type": "boolean", "description": "Unapproved content feedback retained until canonical completion, including retry and interaction relays." },
154
+ "content_review_task_id": { "type": "string" },
155
+ "content_rejections": {
156
+ "type": "array", "maxItems": 2,
157
+ "description": "Bounded negative reviews of genuine complete executions. Not process failures, QA outcomes or approvals.",
158
+ "items": {
159
+ "type": "object", "required": ["kind", "reason", "rejected_at", "binding", "execution"],
160
+ "properties": {
161
+ "kind": { "const": "content_rejection" },
162
+ "reason": { "type": "string", "minLength": 1, "maxLength": 8192, "description": "Runtime also enforces an 8192 UTF-8 byte bound." },
163
+ "rejected_at": { "type": "string", "format": "date-time" },
164
+ "binding": { "type": "object" },
165
+ "execution": { "type": "object" }
166
+ }
167
+ }
168
+ },
146
169
  "score": { "type": "number", "minimum": 0, "maximum": 100 },
147
170
  "criteria_count": { "type": "integer", "minimum": 0 },
148
171
  "completed_at": { "type": ["string", "null"], "format": "date-time" }
@@ -56,9 +56,35 @@
56
56
  },
57
57
  "requirement_refs": {
58
58
  "type": "array",
59
+ "minItems": 1,
59
60
  "items": { "type": "string" },
60
61
  "description": "PRD requirement IDs this task addresses"
61
62
  },
63
+ "execution_binding": {
64
+ "type": "object",
65
+ "description": "Sealed build provider, harness, model and reasoning selected from the installation catalog."
66
+ },
67
+ "review_binding": {
68
+ "type": "object",
69
+ "description": "Sealed independent review provider, harness, model and reasoning selected from the installation catalog."
70
+ },
71
+ "required_independence_level": {
72
+ "enum": ["A", "B", "C"],
73
+ "default": "A",
74
+ "description": "Minimum required separation between the task builder and reviewer. A is cross-provider."
75
+ },
76
+ "effort_plan": {
77
+ "type": "object",
78
+ "description": "p50/p80 estimate that separately includes AI build, review, rework reserve, human active and human wait time."
79
+ },
80
+ "routing_profile": {
81
+ "type": "object",
82
+ "required": ["risk_level"],
83
+ "properties": {
84
+ "risk_level": { "enum": ["low", "medium", "high", "critical"] }
85
+ },
86
+ "description": "Task characteristics consumed by the signed catalog router at planning seal time."
87
+ },
62
88
  "blocker": {
63
89
  "type": ["string", "null"],
64
90
  "description": "Blocker code (C01-C15, G01-G08) if status is blocked"
@@ -77,16 +77,16 @@ Run the router's canonical completion or `advance --agent <agent> --score <score
77
77
 
78
78
  ### 7. Determine Next Agent
79
79
  Based on pipeline position and mode:
80
- - **DISCOVER → PLAN**: Follow sequence (wu → brief → [Planning Team: detail + architect + ux + qa-planning] → phases → tasks)
81
- - **BUILD**: devqa-implementation
82
- - **DEPLOY**: devops
83
- - Handle special transitions (e.g., qa-planningdev requires mode switch)
80
+ - **DISCOVER → PLAN**: Follow sequence (wu → brief → Discovery Council → [Planning Team: detail + architect + ux + qa-planning] → Planning Council → phases → tasks → Execution Design Council)
81
+ - **RAIL**: after the Execution Design Council accepts the sealed task graph, the orchestrator returns `rail_execute`; tasks run through the project-local RAIL runtime (execute independent review → bounded rework), not through pipeline agents
82
+ - **RELEASE**: the Release Lane consumes RAIL completion evidence and requires explicit human authorization before any external action
83
+ - Handle special transitions (e.g., tasksRAIL requires the sealed handoff and council acceptance)
84
84
 
85
85
  ### 8. Check Quality Gates
86
86
  Evaluate if mode transition should occur:
87
- - **After QA-Planning**: If score >= 95%, eligible for planningbuild
88
- - **After Dev**: If complete, eligible for buildvalidate
89
- - **After QA-Implementation**: If approved, eligible for validatedeploy
87
+ - **After QA-Planning**: If score >= 95%, eligible to continue to phases tasks
88
+ - **After Tasks**: If the sealed graph passes the Execution Design Council, eligible for planningrail
89
+ - **After RAIL completion**: If every task has accepted review evidence, eligible for railrelease
90
90
 
91
91
  ### 9. Prepare Next Agent Context
92
92
  Package context for the next agent:
@@ -138,9 +138,9 @@ If expected artifacts are not present:
138
138
 
139
139
  ### Handoff Write Failure
140
140
  If handoff document can't be saved:
141
- - Try alternate location (`.chati/backups/handoffs/`)
142
- - Log to session decisions as fallback
143
- - Escalate with write error details
141
+ - Return a structured `error` with the actual write failure. Do not claim completion.
142
+ - Preserve produced artifacts. Do not write outside the agent-owned handoff directory or edit session state.
143
+ - The router retains the original question/response for retry; a legacy ambiguous hold requires exact negative settlement, never a fabricated handoff or successful advance.
144
144
 
145
145
  ### Session Update Failure
146
146
  If the router reports a session update failure:
@@ -47,9 +47,9 @@ Find the most recent agent with `status: completed`:
47
47
 
48
48
  ### 4. Determine Next Agent
49
49
  Based on pipeline position and last completed agent:
50
- - **DISCOVER → PLAN pipeline**: greenfield-wu → brief → [Planning Team: detail + architect + ux + qa-planning] → phases → tasks
51
- - **BUILD pipeline**: devqa-implementation
52
- - **DEPLOY pipeline**: devops
50
+ - **DISCOVER → PLAN pipeline**: greenfield-wu → brief → Discovery Council → [Planning Team: detail + architect + ux + qa-planning] → Planning Council → phases → tasks → Execution Design Council
51
+ - **RAIL runtime**: sealed task graph execute → independent review → bounded rework (project-local RAIL, resumed through `rail_execute`, never through pipeline agents)
52
+ - **RELEASE lane**: completion evidence → explicit human authorization
53
53
  - Handle special cases (skipped, needs_revalidation)
54
54
 
55
55
  ### 5. Load Latest Handoff
@@ -13,6 +13,7 @@ phases:
13
13
  sequence:
14
14
  - { agent: brownfield-wu, action: deep-discovery, creates: [artifacts/0-WU/wu-operational-report.md] }
15
15
  - { agent: brief, action: problem-extraction, creates: [artifacts/1-Brief/brief-report.md] }
16
+ - { gate: council-discovery, action: cross-provider-council, runtime: automatic }
16
17
  - phase: 1
17
18
  name: Planning
18
19
  category: PLAN
@@ -20,14 +21,16 @@ phases:
20
21
  - { agent: architect, action: map-and-design, creates: [artifacts/3-Architecture/architecture.md] }
21
22
  - { agent: detail, action: create-prd, creates: [artifacts/2-PRD/prd.md] }
22
23
  - { agent: ux, action: define-experience, creates: [artifacts/4-UX/ux-specification.md] }
24
+ - { agent: qa-planning, action: validate-planning, creates: [artifacts/7-QA-Planning/qa-planning-report.md] }
25
+ - { gate: council-planning, action: cross-provider-council, runtime: automatic }
23
26
  - { agent: phases, action: create-roadmap, creates: [artifacts/5-Phases/phases.md] }
24
27
  - { agent: tasks, action: seal-task-graph, creates: [artifacts/6-Tasks/tasks.md] }
25
- - { agent: qa-planning, action: validate-planning, creates: [artifacts/7-QA-Planning/qa-planning-report.md] }
28
+ - { gate: council-execution-design, action: cross-provider-council, runtime: automatic }
26
29
  - { phase: 2, name: RAIL Execution, category: RAIL, runtime: project-local-rail, contract: .chati/v2/rail/handoffs }
27
30
  - { phase: 3, name: Release Lane, category: RELEASE, requires: [rail_complete, independent_review_approved, explicit_human_authorization] }
28
31
 
29
32
  transitions:
30
- discover_to_plan: { trigger: "brief.status == completed", target: plan }
31
- plan_to_rail: { trigger: "qa-planning.score >= 95 && tasks.handoff == sealed", target: rail }
33
+ discover_to_plan: { trigger: "brief.status == completed && council.discovery.verdict == accept", target: plan }
34
+ plan_to_rail: { trigger: "qa-planning.score >= 95 && tasks.handoff == sealed && council.execution_design.verdict == accept", target: rail }
32
35
  rail_to_release: { trigger: "rail.all_tasks_approved == true", target: release }
33
36
  release_to_completed: { trigger: "release.receipt.status == completed", target: completed }
@@ -8,7 +8,7 @@ workflow:
8
8
  planning_focus: [api, data, security, operations]
9
9
 
10
10
  phases:
11
- - { phase: 0, name: Deep Discovery, category: DISCOVER, sequence: [{ agent: brownfield-wu, action: deep-discovery }, { agent: brief, action: problem-extraction }] }
11
+ - { phase: 0, name: Deep Discovery, category: DISCOVER, sequence: [{ agent: brownfield-wu, action: deep-discovery }, { agent: brief, action: problem-extraction }, { gate: council-discovery, action: cross-provider-council, runtime: automatic }] }
12
12
  - phase: 1
13
13
  name: Planning
14
14
  category: PLAN
@@ -16,12 +16,14 @@ phases:
16
16
  - { agent: architect, action: service-design }
17
17
  - { agent: detail, action: create-prd }
18
18
  - { agent: ux, action: api-consumer-experience }
19
+ - { agent: qa-planning, action: validate-planning }
20
+ - { gate: council-planning, action: cross-provider-council, runtime: automatic }
19
21
  - { agent: phases, action: create-roadmap }
20
22
  - { agent: tasks, action: seal-task-graph }
21
- - { agent: qa-planning, action: validate-planning }
23
+ - { gate: council-execution-design, action: cross-provider-council, runtime: automatic }
22
24
  - { phase: 2, name: RAIL Execution, category: RAIL, runtime: project-local-rail }
23
25
  - { phase: 3, name: Release Lane, category: RELEASE, requires: [rail_complete, independent_review_approved, explicit_human_authorization] }
24
26
 
25
27
  transitions:
26
- plan_to_rail: { trigger: "qa-planning.score >= 95 && tasks.handoff == sealed", target: rail }
28
+ plan_to_rail: { trigger: "qa-planning.score >= 95 && tasks.handoff == sealed && council.execution_design.verdict == accept", target: rail }
27
29
  rail_to_release: { trigger: "rail.all_tasks_approved == true", target: release }
@@ -8,7 +8,7 @@ workflow:
8
8
  planning_focus: [design-system, accessibility, responsive-behavior, visual-regression]
9
9
 
10
10
  phases:
11
- - { phase: 0, name: Deep Discovery, category: DISCOVER, sequence: [{ agent: brownfield-wu, action: deep-discovery }, { agent: brief, action: problem-extraction }] }
11
+ - { phase: 0, name: Deep Discovery, category: DISCOVER, sequence: [{ agent: brownfield-wu, action: deep-discovery }, { agent: brief, action: problem-extraction }, { gate: council-discovery, action: cross-provider-council, runtime: automatic }] }
12
12
  - phase: 1
13
13
  name: Planning
14
14
  category: PLAN
@@ -16,12 +16,14 @@ phases:
16
16
  - { agent: architect, action: frontend-design }
17
17
  - { agent: detail, action: create-prd }
18
18
  - { agent: ux, action: design-system-and-flows }
19
+ - { agent: qa-planning, action: validate-planning }
20
+ - { gate: council-planning, action: cross-provider-council, runtime: automatic }
19
21
  - { agent: phases, action: create-roadmap }
20
22
  - { agent: tasks, action: seal-task-graph }
21
- - { agent: qa-planning, action: validate-planning }
23
+ - { gate: council-execution-design, action: cross-provider-council, runtime: automatic }
22
24
  - { phase: 2, name: RAIL Execution, category: RAIL, runtime: project-local-rail }
23
25
  - { phase: 3, name: Release Lane, category: RELEASE, requires: [rail_complete, independent_review_approved, explicit_human_authorization] }
24
26
 
25
27
  transitions:
26
- plan_to_rail: { trigger: "qa-planning.score >= 95 && tasks.handoff == sealed", target: rail }
28
+ plan_to_rail: { trigger: "qa-planning.score >= 95 && tasks.handoff == sealed && council.execution_design.verdict == accept", target: rail }
27
29
  rail_to_release: { trigger: "rail.all_tasks_approved == true", target: release }
@@ -13,6 +13,7 @@ phases:
13
13
  sequence:
14
14
  - { agent: greenfield-wu, action: operational-discovery, creates: [artifacts/0-WU/wu-operational-report.md] }
15
15
  - { agent: brief, action: problem-extraction, creates: [artifacts/1-Brief/brief-report.md] }
16
+ - { gate: council-discovery, action: cross-provider-council, runtime: automatic }
16
17
  - phase: 1
17
18
  name: Planning
18
19
  category: PLAN
@@ -20,14 +21,16 @@ phases:
20
21
  - { agent: detail, action: create-prd, creates: [artifacts/2-PRD/prd.md] }
21
22
  - { agent: architect, action: design-architecture, creates: [artifacts/3-Architecture/architecture.md] }
22
23
  - { agent: ux, action: define-experience, creates: [artifacts/4-UX/ux-specification.md] }
24
+ - { agent: qa-planning, action: validate-planning, creates: [artifacts/7-QA-Planning/qa-planning-report.md] }
25
+ - { gate: council-planning, action: cross-provider-council, runtime: automatic }
23
26
  - { agent: phases, action: create-roadmap, creates: [artifacts/5-Phases/phases.md] }
24
27
  - { agent: tasks, action: seal-task-graph, creates: [artifacts/6-Tasks/tasks.md] }
25
- - { agent: qa-planning, action: validate-planning, creates: [artifacts/7-QA-Planning/qa-planning-report.md] }
28
+ - { gate: council-execution-design, action: cross-provider-council, runtime: automatic }
26
29
  - { phase: 2, name: RAIL Execution, category: RAIL, runtime: project-local-rail, contract: .chati/v2/rail/handoffs }
27
30
  - { phase: 3, name: Release Lane, category: RELEASE, requires: [rail_complete, independent_review_approved, explicit_human_authorization] }
28
31
 
29
32
  transitions:
30
- discover_to_plan: { trigger: "brief.status == completed", target: plan }
31
- plan_to_rail: { trigger: "qa-planning.score >= 95 && tasks.handoff == sealed", target: rail }
33
+ discover_to_plan: { trigger: "brief.status == completed && council.discovery.verdict == accept", target: plan }
34
+ plan_to_rail: { trigger: "qa-planning.score >= 95 && tasks.handoff == sealed && council.execution_design.verdict == accept", target: rail }
32
35
  rail_to_release: { trigger: "rail.all_tasks_approved == true", target: release }
33
36
  release_to_completed: { trigger: "release.receipt.status == completed", target: completed }
@@ -13,18 +13,23 @@ phases:
13
13
  category: DISCOVER
14
14
  sequence:
15
15
  - { agent: brief, action: problem-extraction, creates: [artifacts/1-Brief/brief-report.md] }
16
+ - { gate: council-discovery, action: cross-provider-council, runtime: automatic, creates: [.chati/v2/council/discovery-<content-addressed>.json] }
16
17
  - phase: 1
17
18
  name: Planning
18
19
  category: PLAN
19
20
  sequence:
20
21
  - { agent: detail, action: create-prd, creates: [artifacts/2-PRD/prd.md] }
21
22
  - { agent: architect, action: focused-architecture, creates: [artifacts/3-Architecture/architecture.md] }
22
- - { agent: tasks, action: seal-task-graph, creates: [artifacts/6-Tasks/tasks.md] }
23
+ - { agent: ux, action: define-experience, creates: [artifacts/4-UX/ux-specification.md] }
23
24
  - { agent: qa-planning, action: validate-planning, creates: [artifacts/7-QA-Planning/qa-planning-report.md] }
25
+ - { gate: council-planning, action: cross-provider-council, runtime: automatic, creates: [.chati/v2/council/planning-<content-addressed>.json] }
26
+ - { agent: phases, action: create-roadmap, creates: [artifacts/5-Phases/phases.md] }
27
+ - { agent: tasks, action: seal-task-graph, creates: [artifacts/6-Tasks/tasks.md, .chati/v2/planning/tasks.json] }
28
+ - { gate: council-execution-design, action: cross-provider-council, runtime: automatic, creates: [.chati/v2/council/execution-design-<content-addressed>.json] }
24
29
  - { phase: 2, name: RAIL Execution, category: RAIL, runtime: project-local-rail }
25
30
  - { phase: 3, name: Release Lane, category: RELEASE, requires: [rail_complete, independent_review_approved, explicit_human_authorization] }
26
31
 
27
32
  transitions:
28
- discover_to_plan: { trigger: "brief.status == completed", target: plan }
29
- plan_to_rail: { trigger: "qa-planning.score >= 95 && tasks.handoff == sealed", target: rail }
33
+ discover_to_plan: { trigger: "brief.status == completed && council.discovery.verdict == accept", target: plan }
34
+ plan_to_rail: { trigger: "qa-planning.score >= 95 && tasks.handoff == sealed && council.execution_design.verdict == accept", target: rail }
30
35
  rail_to_release: { trigger: "rail.all_tasks_approved == true", target: release }
@@ -0,0 +1,29 @@
1
+ #ifndef CHATI_BROWSER_PROOF_WIRE_H
2
+ #define CHATI_BROWSER_PROOF_WIRE_H
3
+ #include <stdint.h>
4
+ #include <string.h>
5
+
6
+ #define CHATI_BROWSER_REQUEST_SIZE 104
7
+ #define CHATI_BROWSER_RESPONSE_SIZE 116
8
+
9
+ static int chati_browser_hex(const unsigned char *bytes, size_t length) {
10
+ for (size_t i = 0; i < length; i++) {
11
+ if (!((bytes[i] >= '0' && bytes[i] <= '9') || (bytes[i] >= 'a' && bytes[i] <= 'f'))) return 0;
12
+ }
13
+ return 1;
14
+ }
15
+ static unsigned int chati_browser_port(const unsigned char *bytes) {
16
+ return ((unsigned int)bytes[0] << 8) | bytes[1];
17
+ }
18
+ static int chati_browser_request_valid(const unsigned char *request, const char *nonce, const unsigned char *previous) {
19
+ return !memcmp(request, "CBP1", 4) && !memcmp(request + 4, nonce, 64)
20
+ && chati_browser_hex(request + 68, 32) && memcmp(request + 68, previous, 32) > 0
21
+ && chati_browser_port(request + 100) > 0 && chati_browser_port(request + 102) >= 1024;
22
+ }
23
+ static void chati_browser_response(unsigned char *response, const unsigned char *request, int proved, uint64_t generation) {
24
+ memcpy(response, request, CHATI_BROWSER_REQUEST_SIZE);
25
+ response[104] = proved ? 1 : 0;
26
+ memset(response + 105, 0, 3);
27
+ for (int i = 0; i < 8; i++) response[108 + i] = (unsigned char)(generation >> (56 - 8 * i));
28
+ }
29
+ #endif
@@ -0,0 +1,36 @@
1
+ /* Included by the Node-API boundary, never by the application. */
2
+ #include "darwin-browser-listener.h"
3
+
4
+ static napi_value create_darwin_browser_listener(napi_env env, napi_callback_info info) {
5
+ napi_value args[2], result;
6
+ int port;
7
+ if (!arguments(env, info, 1, args) || !integer(env, args[0], &port)) return NULL;
8
+ if (port < 1024 || port > 65535) return failure(env, "browser listener port", EINVAL);
9
+ int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
10
+ if (fd < 0) return failure(env, "browser listener socket", errno);
11
+ struct sockaddr_in address = { .sin_len = sizeof(address), .sin_family = AF_INET,
12
+ .sin_port = htons((uint16_t)port), .sin_addr.s_addr = htonl(INADDR_LOOPBACK) };
13
+ uint64_t generation;
14
+ int saved = 0;
15
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0
16
+ || bind(fd, (struct sockaddr *)&address, sizeof(address)) < 0
17
+ || listen(fd, 128) < 0) saved = errno;
18
+ else if (!chati_darwin_listener(getpid(), fd, (unsigned int)port, &generation)) saved = EINVAL;
19
+ if (saved) { close(fd); return failure(env, "create exact browser listener", saved); }
20
+ if (napi_create_int32(env, fd, &result) != napi_ok) {
21
+ close(fd); napi_throw_error(env, "NATIVE_STORAGE_API_ERROR", "could not return browser listener"); return NULL;
22
+ }
23
+ return result;
24
+ }
25
+
26
+ static napi_value inspect_darwin_browser_listener(napi_env env, napi_callback_info info) {
27
+ napi_value args[3], result;
28
+ int fd, port;
29
+ uint64_t generation;
30
+ if (!arguments(env, info, 2, args) || !integer(env, args[0], &fd) || !integer(env, args[1], &port)) return NULL;
31
+ if (!chati_darwin_listener(getpid(), fd, (unsigned int)port, &generation)) {
32
+ return failure(env, "invalid exact browser listener", EINVAL);
33
+ }
34
+ CHECK(napi_create_bigint_uint64(env, generation, &result));
35
+ return result;
36
+ }
@@ -0,0 +1,32 @@
1
+ /* Exact loopback listener capability. Seatbelt's localhost bind filter is not
2
+ * an address restriction. Only the trusted parent may create this socket. */
3
+ #ifndef CHATI_DARWIN_BROWSER_LISTENER_H
4
+ #define CHATI_DARWIN_BROWSER_LISTENER_H
5
+ #include <errno.h>
6
+ #include <libproc.h>
7
+ #include <netinet/in.h>
8
+ #include <stdint.h>
9
+ #include <string.h>
10
+ #include <sys/socket.h>
11
+ #include <sys/proc_info.h>
12
+
13
+ static int chati_darwin_listener(pid_t owner, int fd, unsigned int port, uint64_t *generation) {
14
+ struct socket_fdinfo info;
15
+ memset(&info, 0, sizeof(info));
16
+ if (port < 1024 || port > 65535 || fd < 0
17
+ || proc_pidfdinfo(owner, fd, PROC_PIDFDSOCKETINFO, &info, sizeof(info)) != (int)sizeof(info)) return 0;
18
+ const struct socket_info *socket = &info.psi;
19
+ const struct tcp_sockinfo *tcp = &socket->soi_proto.pri_tcp;
20
+ const struct in_sockinfo *ip = &tcp->tcpsi_ini;
21
+ // Darwin does not implement getsockopt(SO_ACCEPTCONN). The kernel socket
22
+ // record provides the real TCP state, not a JavaScript readiness assertion.
23
+ if (socket->soi_kind != SOCKINFO_TCP || socket->soi_family != AF_INET
24
+ || socket->soi_type != SOCK_STREAM || socket->soi_protocol != IPPROTO_TCP
25
+ || (socket->soi_options & SO_REUSEPORT) || tcp->tcpsi_state != TSI_S_LISTEN
26
+ || ip->insi_vflag != INI_IPV4 || ip->insi_lport != htons((uint16_t)port)
27
+ || ip->insi_laddr.ina_46.i46a_addr4.s_addr != htonl(INADDR_LOOPBACK)
28
+ || ip->insi_gencnt == 0) return 0;
29
+ *generation = ip->insi_gencnt;
30
+ return 1;
31
+ }
32
+ #endif
@@ -0,0 +1,86 @@
1
+ /* Included only by the Darwin launcher. The helper owns child and never reaps
2
+ * it while this service is open. A request cannot choose a process or address.
3
+ * fd9=request, fd10=reply. Neither descriptor reaches the application/reaper.
4
+ */
5
+ #ifndef CHATI_DARWIN_BROWSER_PROOF_H
6
+ #define CHATI_DARWIN_BROWSER_PROOF_H
7
+ #include <libproc.h>
8
+ #include <netinet/in.h>
9
+ #include <sys/proc_info.h>
10
+ #include "browser-proof-wire.h"
11
+
12
+ static int chati_browser_child_state(pid_t child, siginfo_t *exited) {
13
+ memset(exited, 0, sizeof(*exited));
14
+ int status;
15
+ do { status = waitid(P_PID, (id_t)child, exited, WEXITED | WNOHANG | WNOWAIT); } while (status < 0 && errno == EINTR);
16
+ if (status < 0) return -1;
17
+ if (exited->si_pid != 0 && exited->si_pid != child) { errno = EIO; return -1; }
18
+ return exited->si_pid == child ? 1 : 0;
19
+ }
20
+
21
+ static int chati_browser_peer(pid_t child, unsigned int local_port, unsigned int remote_port, uint64_t *generation) {
22
+ struct proc_fdinfo descriptors[4097];
23
+ int size = proc_pidinfo(child, PROC_PIDLISTFDS, 0, descriptors, sizeof(descriptors));
24
+ if (size <= 0 || size >= (int)sizeof(descriptors) || size % sizeof(descriptors[0])) return 0;
25
+ long long started = chati_admission_monotonic_ms();
26
+ if (started < 0) return 0;
27
+ for (size_t i = 0; i < (size_t)size / sizeof(descriptors[0]); i++) {
28
+ long long now = chati_admission_monotonic_ms();
29
+ if (now < started || now - started > 250) return 0;
30
+ if (descriptors[i].proc_fdtype != PROX_FDTYPE_SOCKET) continue;
31
+ struct socket_fdinfo info;
32
+ memset(&info, 0, sizeof(info));
33
+ if (proc_pidfdinfo(child, descriptors[i].proc_fd, PROC_PIDFDSOCKETINFO, &info, sizeof(info)) != (int)sizeof(info)) continue;
34
+ const struct socket_info *socket = &info.psi;
35
+ const struct tcp_sockinfo *tcp = &socket->soi_proto.pri_tcp;
36
+ const struct in_sockinfo *ip = &tcp->tcpsi_ini;
37
+ if (socket->soi_kind != SOCKINFO_TCP || socket->soi_family != AF_INET
38
+ || socket->soi_type != SOCK_STREAM || socket->soi_protocol != IPPROTO_TCP
39
+ || tcp->tcpsi_state != TSI_S_ESTABLISHED || ip->insi_vflag != INI_IPV4
40
+ || ip->insi_lport != htons((uint16_t)remote_port) || ip->insi_fport != htons((uint16_t)local_port)
41
+ || ip->insi_laddr.ina_46.i46a_addr4.s_addr != htonl(INADDR_LOOPBACK)
42
+ || ip->insi_faddr.ina_46.i46a_addr4.s_addr != htonl(INADDR_LOOPBACK)
43
+ || ip->insi_gencnt == 0) continue;
44
+ *generation = ip->insi_gencnt;
45
+ return 1;
46
+ }
47
+ return 0;
48
+ }
49
+
50
+ /* Serial queries and waitid run in the original helper, never in a competing
51
+ * reaper thread. The caller MUST close fd9/fd10 before any waitpid, including
52
+ * every failure path. A dead child can never authorize a recycled PID.
53
+ */
54
+ static int chati_browser_wait(pid_t child, const char *nonce, siginfo_t *exited) {
55
+ unsigned char request[CHATI_BROWSER_REQUEST_SIZE], response[CHATI_BROWSER_RESPONSE_SIZE];
56
+ unsigned char previous[32]; memset(previous, '0', sizeof(previous));
57
+ size_t offset = 0;
58
+ while (1) {
59
+ int state = chati_browser_child_state(child, exited);
60
+ if (state != 0) return state < 0 ? -1 : 0;
61
+ struct pollfd input = { .fd = 9, .events = POLLIN | POLLHUP };
62
+ int available = poll(&input, 1, 25);
63
+ if (available < 0) { if (errno == EINTR) continue; return -1; }
64
+ if (available == 0) continue;
65
+ if (input.revents & (POLLHUP | POLLERR | POLLNVAL)) { errno = EPIPE; return -1; }
66
+ if (!(input.revents & POLLIN)) continue;
67
+ ssize_t count = read(9, request + offset, sizeof(request) - offset);
68
+ if (count < 0 && errno == EINTR) continue;
69
+ if (count <= 0) { if (count == 0) errno = EPIPE; return -1; }
70
+ offset += (size_t)count;
71
+ if (offset < sizeof(request)) continue;
72
+ if (!chati_browser_request_valid(request, nonce, previous)) { errno = EINVAL; return -1; }
73
+ memcpy(previous, request + 68, sizeof(previous)); offset = 0;
74
+ state = chati_browser_child_state(child, exited);
75
+ if (state != 0) return state < 0 ? -1 : 0;
76
+ uint64_t generation = 0;
77
+ int proved = chati_browser_peer(child, chati_browser_port(request + 100), chati_browser_port(request + 102), &generation);
78
+ state = chati_browser_child_state(child, exited);
79
+ if (state != 0) return state < 0 ? -1 : 0;
80
+ chati_browser_response(response, request, proved, generation);
81
+ // One bounded PIPE_BUF-sized write, never stdout and never caller payload.
82
+ do { count = write(10, response, sizeof(response)); } while (count < 0 && errno == EINTR);
83
+ if (count != (ssize_t)sizeof(response)) { if (count >= 0) errno = EIO; return -1; }
84
+ }
85
+ }
86
+ #endif
@@ -0,0 +1,35 @@
1
+ #ifndef CHATI_DARWIN_CODE_IDENTITY_H
2
+ #define CHATI_DARWIN_CODE_IDENTITY_H
3
+ #include <errno.h>
4
+ #include <stdint.h>
5
+ #include <string.h>
6
+ #include <sys/types.h>
7
+
8
+ /* XNU csops ABI, also used by Security's cskernel implementation. The SDK does
9
+ * not expose this declaration consistently. No pathname-derived hash fallback.
10
+ * https://github.com/apple-oss-distributions/xnu/blob/main/bsd/kern/kern_proc.c
11
+ * https://github.com/apple-oss-distributions/xnu/blob/main/osfmk/kern/cs_blobs.h
12
+ */
13
+ extern int csops(pid_t, unsigned int, void *, size_t);
14
+ enum { CHATI_CS_OPS_STATUS = 0, CHATI_CS_OPS_CDHASH = 5 };
15
+ #define CHATI_CS_VALID UINT32_C(0x00000001)
16
+ #define CHATI_CS_KILLED UINT32_C(0x01000000)
17
+
18
+ /* Only for our own suspended, unreaped child and an expected CDHASH produced
19
+ * by full static validation of its immutable snapshot outside the sandbox.
20
+ * This is not general signature validation for mutable or arbitrary code. */
21
+ static int chati_darwin_code_identity(pid_t child, const char *expected) {
22
+ if (child <= 1 || !expected || strlen(expected) != 40
23
+ || strspn(expected, "0123456789abcdef") != 40) return EINVAL;
24
+ unsigned char hash[20];
25
+ uint32_t status = 0;
26
+ if (csops(child, CHATI_CS_OPS_CDHASH, hash, sizeof(hash)) != 0) return errno ? errno : EIO;
27
+ if (csops(child, CHATI_CS_OPS_STATUS, &status, sizeof(status)) != 0) return errno ? errno : EIO;
28
+ if (!(status & CHATI_CS_VALID) || (status & CHATI_CS_KILLED)) return EACCES;
29
+ static const char hex[] = "0123456789abcdef";
30
+ for (size_t i = 0; i < sizeof(hash); i++) {
31
+ if (expected[i * 2] != hex[hash[i] >> 4] || expected[i * 2 + 1] != hex[hash[i] & 15]) return EACCES;
32
+ }
33
+ return 0;
34
+ }
35
+ #endif
@@ -0,0 +1,37 @@
1
+ /* Exact private wire format used by the native Darwin supervisor. This is
2
+ * serialization only, not process observation, signing or admission policy. */
3
+ #ifndef CHATI_DARWIN_LAUNCH_MESSAGES_H
4
+ #define CHATI_DARWIN_LAUNCH_MESSAGES_H
5
+ #include <stdio.h>
6
+
7
+ static int chati_format_darwin_ready(char *message, size_t capacity,
8
+ const char *nonce, int helper, int provider, const char *cdhash) {
9
+ return snprintf(message, capacity,
10
+ "{\"schema_version\":1,\"phase\":\"ready\",\"nonce\":\"%s\","
11
+ "\"helper_pid\":%d,\"provider_pid\":%d,\"verified_cdhash\":\"%s\","
12
+ "\"lifetime_kind\":\"darwin_process_group_v1\"}\n",
13
+ nonce, helper, provider, cdhash);
14
+ }
15
+
16
+ struct chati_darwin_completion {
17
+ long long elapsed_ms;
18
+ int exit_code;
19
+ int signal;
20
+ int supervisor_signal;
21
+ int reaper_acknowledged;
22
+ };
23
+
24
+ static int chati_format_darwin_completion(char *message, size_t capacity,
25
+ const char *nonce, int helper, int provider, const char *cdhash,
26
+ const struct chati_darwin_completion *result) {
27
+ return snprintf(message, capacity,
28
+ "{\"schema_version\":1,\"phase\":\"completed\",\"nonce\":\"%s\","
29
+ "\"helper_pid\":%d,\"provider_pid\":%d,\"verified_cdhash\":\"%s\","
30
+ "\"lifetime_kind\":\"darwin_process_group_v1\",\"elapsed_ms\":%lld,"
31
+ "\"exit_code\":%d,\"signal\":%d,\"supervisor_signal\":%d,"
32
+ "\"reaper_result\":\"%s\",\"process_group_empty\":true,\"detached_descendants\":\"unknown\"}\n",
33
+ nonce, helper, provider, cdhash, result->elapsed_ms, result->exit_code,
34
+ result->signal, result->supervisor_signal,
35
+ result->reaper_acknowledged ? "acknowledged" : "eperm_then_group_empty");
36
+ }
37
+ #endif