chati-dev 4.1.6 → 4.2.1
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.
- package/README.md +6 -4
- package/framework/agents/build/dev.md +54 -1
- package/framework/agents/plan/architect.md +88 -25
- package/framework/agents/plan/detail.md +44 -0
- package/framework/agents/plan/ux.md +109 -34
- package/framework/agents/quality/qa-implementation.md +162 -2
- package/framework/config.yaml +9 -3
- package/framework/constitution.md +270 -2
- package/framework/context/governance.md +24 -1
- package/framework/context/quality.md +14 -1
- package/framework/context/root.md +4 -3
- package/framework/hooks/license-guard.js +13 -6
- package/framework/hooks/prism-engine.js +31 -1
- package/framework/hooks/settings.json +8 -0
- package/framework/hooks/team-quality-gate.js +145 -0
- package/framework/orchestrator/chati.md +172 -2
- package/framework/schemas/session.schema.json +160 -0
- package/framework/templates/team-build-tasks.yaml +56 -0
- package/framework/templates/team-planning-tasks.yaml +73 -0
- package/package.json +1 -1
- package/scripts/validate-package.js +110 -0
- package/src/autonomy/safety-net.js +32 -0
- package/src/installer/core.js +87 -128
- package/src/installer/templates.js +34 -0
- package/src/orchestrator/cli.js +404 -7
- package/src/terminal/run-team.js +349 -0
- package/src/terminal/team-task-list.js +226 -0
|
@@ -19,7 +19,7 @@ import { homedir } from 'os';
|
|
|
19
19
|
|
|
20
20
|
const API_BASE = 'https://chati.dev/api';
|
|
21
21
|
const TELEMETRY_ENDPOINT = 'https://chati.dev/api/telemetry';
|
|
22
|
-
const CACHE_TTL_MS =
|
|
22
|
+
const CACHE_TTL_MS = 4 * 60 * 60 * 1000; // 4 hours — shorter window to catch expirations faster
|
|
23
23
|
const PING_THROTTLE_MS = 5 * 60 * 1000; // 5 minutes
|
|
24
24
|
|
|
25
25
|
const GLOBAL_DIR = join(homedir(), '.chati-dev');
|
|
@@ -31,9 +31,9 @@ async function main() {
|
|
|
31
31
|
for await (const chunk of process.stdin) input += chunk;
|
|
32
32
|
|
|
33
33
|
try {
|
|
34
|
-
// Read global license file
|
|
34
|
+
// Read global license file — REQUIRED for operation
|
|
35
35
|
if (!existsSync(LICENSE_PATH)) {
|
|
36
|
-
|
|
36
|
+
block('No license found. Run: npx chati-dev activate --key=YOUR-KEY\nGet a license at https://chati.dev/pricing');
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -41,7 +41,7 @@ async function main() {
|
|
|
41
41
|
const licenseKey = readYamlField(licenseRaw, 'key');
|
|
42
42
|
|
|
43
43
|
if (!licenseKey || licenseKey === 'null') {
|
|
44
|
-
|
|
44
|
+
block('No license key configured. Run: npx chati-dev activate --key=YOUR-KEY\nGet a license at https://chati.dev/pricing');
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -89,8 +89,15 @@ async function main() {
|
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
block(buildMessage(data.status, data.reason));
|
|
92
|
-
} catch {
|
|
93
|
-
|
|
92
|
+
} catch {
|
|
93
|
+
// API unreachable — check if we have a recent VALID cache to fall back on
|
|
94
|
+
// If cache is less than 24h old AND was VALID, allow (grace period for network issues)
|
|
95
|
+
// Otherwise block — we cannot verify the license
|
|
96
|
+
if (age < 24 * 60 * 60 * 1000 && status === 'VALID') {
|
|
97
|
+
allow(); // grace: last check was recent and valid, allow despite network issue
|
|
98
|
+
} else {
|
|
99
|
+
block('Unable to verify license (network issue). If this persists, check your connection.\nRun: npx chati-dev activate --key=YOUR-KEY');
|
|
100
|
+
}
|
|
94
101
|
}
|
|
95
102
|
} catch { /* expected: stdin parse may fail — fail open */
|
|
96
103
|
allow();
|
|
@@ -26,6 +26,17 @@ function readSessionState(projectDir) {
|
|
|
26
26
|
return match ? match[1].trim().replace(/^["']|["']$/g, '') : null;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
// Detect active team (Article XXI)
|
|
30
|
+
let activeTeamId = null;
|
|
31
|
+
let activeTeamPhase = null;
|
|
32
|
+
const teamStatusMatch = raw.match(/teams:[\s\S]*?status:\s*(active|forming)/m);
|
|
33
|
+
if (teamStatusMatch) {
|
|
34
|
+
const teamIdMatch = raw.match(/team_id:\s*(TM-\d{8}-[a-z]{3})/m);
|
|
35
|
+
activeTeamId = teamIdMatch ? teamIdMatch[1] : null;
|
|
36
|
+
const teamPhaseMatch = raw.match(/phase:\s*(planning|build)/m);
|
|
37
|
+
activeTeamPhase = teamPhaseMatch ? teamPhaseMatch[1] : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
29
40
|
return {
|
|
30
41
|
mode: extract('mode') || 'discover',
|
|
31
42
|
currentAgent: extract('current_agent') || null,
|
|
@@ -33,6 +44,8 @@ function readSessionState(projectDir) {
|
|
|
33
44
|
pipelinePosition: extract('pipeline_position') || null,
|
|
34
45
|
turnCount: parseInt(extract('turn_count') || '0', 10),
|
|
35
46
|
provider: extract('provider') || 'claude',
|
|
47
|
+
activeTeamId,
|
|
48
|
+
activeTeamPhase,
|
|
36
49
|
};
|
|
37
50
|
}
|
|
38
51
|
|
|
@@ -81,12 +94,12 @@ async function main() {
|
|
|
81
94
|
|
|
82
95
|
// Primary: turn-count tracks cumulative context consumption across the session.
|
|
83
96
|
// Fallback: prompt-length for first turn or missing session data.
|
|
97
|
+
const promptText = event.prompt || '';
|
|
84
98
|
let remainingPercent;
|
|
85
99
|
const maxTurns = 40;
|
|
86
100
|
if (session.turnCount > 0) {
|
|
87
101
|
remainingPercent = Math.max(0, Math.round((1 - session.turnCount / maxTurns) * 100));
|
|
88
102
|
} else {
|
|
89
|
-
const promptText = event.prompt || '';
|
|
90
103
|
const estimatedTokens = Math.ceil(promptText.length / 4);
|
|
91
104
|
const contextLimit = hookResolveLimit(inferredModel, session.provider);
|
|
92
105
|
remainingPercent = Math.max(0, Math.round((1 - estimatedTokens / contextLimit) * 100));
|
|
@@ -111,6 +124,22 @@ async function main() {
|
|
|
111
124
|
}
|
|
112
125
|
}
|
|
113
126
|
|
|
127
|
+
// L6 Team Roster Layer (Article XII, Article XXI) — injected when a team is active
|
|
128
|
+
// Max 500 tokens, flat budget. Contains team metadata + shared task status only.
|
|
129
|
+
let teamContextBlock = '';
|
|
130
|
+
if (session.activeTeamId && bracket !== 'CRITICAL') {
|
|
131
|
+
const taskListPath = join(projectDir, '.chati', 'teams', session.activeTeamId, 'tasks.yaml');
|
|
132
|
+
if (existsSync(taskListPath)) {
|
|
133
|
+
// Read and cap at 800 chars (~200 tokens) to stay within 500-token budget
|
|
134
|
+
const rawTasks = readFileSync(taskListPath, 'utf-8').trim().slice(0, 800);
|
|
135
|
+
teamContextBlock = [
|
|
136
|
+
` <team-context team-id="${session.activeTeamId}" phase="${session.activeTeamPhase}">`,
|
|
137
|
+
` ${rawTasks.split('\n').join('\n ')}`,
|
|
138
|
+
' </team-context>',
|
|
139
|
+
].join('\n');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
114
143
|
// Frustration detection — adapt response style when user is frustrated
|
|
115
144
|
const frustrationDetected = detectFrustration(promptText);
|
|
116
145
|
|
|
@@ -127,6 +156,7 @@ async function main() {
|
|
|
127
156
|
session.currentAgent ? ` <agent>${session.currentAgent}</agent>` : '',
|
|
128
157
|
session.pipelinePosition ? ` <pipeline-position>${session.pipelinePosition}</pipeline-position>` : '',
|
|
129
158
|
memoryBlock,
|
|
159
|
+
teamContextBlock,
|
|
130
160
|
bracket === 'CRITICAL' ? ' <advisory>Context running low. Consider handoff or summary.</advisory>' : '',
|
|
131
161
|
microcompactAdvisory,
|
|
132
162
|
frustrationDetected ? ' <advisory priority="high">User shows signs of frustration. Be more direct, acknowledge the issue explicitly, focus on the solution, avoid repeating previous suggestions.</advisory>' : '',
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
{
|
|
6
6
|
"matcher": ".*",
|
|
7
7
|
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "node chati.dev/hooks/license-guard.js"
|
|
11
|
+
},
|
|
8
12
|
{
|
|
9
13
|
"type": "command",
|
|
10
14
|
"command": "node chati.dev/hooks/prism-engine.js"
|
|
@@ -55,6 +59,10 @@
|
|
|
55
59
|
{
|
|
56
60
|
"type": "command",
|
|
57
61
|
"command": "node chati.dev/hooks/style-guard.js"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"type": "command",
|
|
65
|
+
"command": "node chati.dev/hooks/team-quality-gate.js"
|
|
58
66
|
}
|
|
59
67
|
]
|
|
60
68
|
},
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Team Quality Gate Hook — PreToolUse (Write|Edit)
|
|
4
|
+
*
|
|
5
|
+
* Prevents cross-member artifact contamination in team mode.
|
|
6
|
+
* When a team is active, each teammate can only write to paths
|
|
7
|
+
* associated with their assigned tasks.
|
|
8
|
+
*
|
|
9
|
+
* If no team is active (CHATI_TEAM_ID absent, or no active team
|
|
10
|
+
* in session.yaml), this hook allows immediately — zero overhead.
|
|
11
|
+
*
|
|
12
|
+
* Article XXI: Agent Teams Governance
|
|
13
|
+
* Enforcement: BLOCK — writes to another member's artifact scope are rejected.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { existsSync, readFileSync } from 'fs';
|
|
17
|
+
import { join } from 'path';
|
|
18
|
+
|
|
19
|
+
// Artifact scope per agent — defines which output paths each agent owns
|
|
20
|
+
const AGENT_ARTIFACT_SCOPES = {
|
|
21
|
+
detail: [
|
|
22
|
+
'chati.dev/artifacts/2-PRD/',
|
|
23
|
+
'chati.dev/artifacts/handoffs/detail-',
|
|
24
|
+
'prd.md',
|
|
25
|
+
],
|
|
26
|
+
architect: [
|
|
27
|
+
'chati.dev/artifacts/3-Architecture/',
|
|
28
|
+
'chati.dev/artifacts/handoffs/architect-',
|
|
29
|
+
'architecture.md',
|
|
30
|
+
],
|
|
31
|
+
ux: [
|
|
32
|
+
'chati.dev/artifacts/4-UX/',
|
|
33
|
+
'chati.dev/artifacts/handoffs/ux-',
|
|
34
|
+
'ux-specification.md',
|
|
35
|
+
'brandbook.md',
|
|
36
|
+
'brandbook.html',
|
|
37
|
+
'component-discovery-log.md',
|
|
38
|
+
'reference-analysis.md',
|
|
39
|
+
],
|
|
40
|
+
dev: [
|
|
41
|
+
'src/',
|
|
42
|
+
'lib/',
|
|
43
|
+
'app/',
|
|
44
|
+
'pages/',
|
|
45
|
+
'components/',
|
|
46
|
+
'utils/',
|
|
47
|
+
'hooks/',
|
|
48
|
+
'styles/',
|
|
49
|
+
'public/',
|
|
50
|
+
'test/',
|
|
51
|
+
'tests/',
|
|
52
|
+
'__tests__/',
|
|
53
|
+
'chati.dev/artifacts/8-Implementation/',
|
|
54
|
+
'chati.dev/artifacts/handoffs/dev-',
|
|
55
|
+
],
|
|
56
|
+
'qa-implementation': [
|
|
57
|
+
'chati.dev/artifacts/9-QA-Implementation/',
|
|
58
|
+
'chati.dev/artifacts/handoffs/qa-implementation-',
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Shared paths any teammate can write to
|
|
63
|
+
const SHARED_PATHS = [
|
|
64
|
+
'.chati/',
|
|
65
|
+
'chati.dev/artifacts/handoffs/teams/',
|
|
66
|
+
'chati.dev/artifacts/decisions/',
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
async function main() {
|
|
70
|
+
let input = '';
|
|
71
|
+
for await (const chunk of process.stdin) {
|
|
72
|
+
input += chunk;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const event = JSON.parse(input);
|
|
77
|
+
const projectDir = event.cwd || process.cwd();
|
|
78
|
+
|
|
79
|
+
// Quick exit: no team active → allow everything
|
|
80
|
+
const teamId = process.env.CHATI_TEAM_ID;
|
|
81
|
+
if (!teamId) {
|
|
82
|
+
process.stdout.write(JSON.stringify({ result: 'allow' }));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Detect current agent from env or session
|
|
87
|
+
const currentAgent = process.env.CHATI_TEAM_MEMBER || detectAgentFromSession(projectDir);
|
|
88
|
+
if (!currentAgent) {
|
|
89
|
+
// Cannot determine agent → allow (fail-open for safety)
|
|
90
|
+
process.stdout.write(JSON.stringify({ result: 'allow' }));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get the file path being written
|
|
95
|
+
const filePath = event.tool_input?.file_path || '';
|
|
96
|
+
if (!filePath) {
|
|
97
|
+
process.stdout.write(JSON.stringify({ result: 'allow' }));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Normalize path relative to project
|
|
102
|
+
const relativePath = filePath.replace(projectDir + '/', '').replace(projectDir, '');
|
|
103
|
+
|
|
104
|
+
// Check shared paths first — always allowed
|
|
105
|
+
if (SHARED_PATHS.some(sp => relativePath.startsWith(sp))) {
|
|
106
|
+
process.stdout.write(JSON.stringify({ result: 'allow' }));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check if another agent owns this path
|
|
111
|
+
for (const [agent, scopes] of Object.entries(AGENT_ARTIFACT_SCOPES)) {
|
|
112
|
+
if (agent === currentAgent) continue;
|
|
113
|
+
for (const scope of scopes) {
|
|
114
|
+
if (relativePath.startsWith(scope) || (!scope.includes('/') && relativePath.endsWith(scope))) {
|
|
115
|
+
process.stdout.write(JSON.stringify({
|
|
116
|
+
result: 'block',
|
|
117
|
+
reason: `[Team Quality Gate] ${currentAgent} cannot write to ${relativePath} — this path belongs to ${agent}. Use the team mailbox to coordinate changes (Article XXI).`,
|
|
118
|
+
}));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Path not owned by anyone specific → allow
|
|
125
|
+
process.stdout.write(JSON.stringify({ result: 'allow' }));
|
|
126
|
+
} catch (err) {
|
|
127
|
+
// Fail-open: hook errors should not block execution
|
|
128
|
+
process.stderr.write(`[chati] team-quality-gate: ${err.message}\n`);
|
|
129
|
+
process.stdout.write(JSON.stringify({ result: 'allow' }));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function detectAgentFromSession(projectDir) {
|
|
134
|
+
const sessionPath = join(projectDir, '.chati', 'session.yaml');
|
|
135
|
+
if (!existsSync(sessionPath)) return null;
|
|
136
|
+
const raw = readFileSync(sessionPath, 'utf-8');
|
|
137
|
+
const match = raw.match(/current_agent:\s*(.+)/m);
|
|
138
|
+
return match ? match[1].trim().replace(/^["']|["']$/g, '') : null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Only run main when executed directly
|
|
142
|
+
import { fileURLToPath } from 'url';
|
|
143
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
144
|
+
main();
|
|
145
|
+
}
|
|
@@ -77,6 +77,7 @@ Parse the JSON output. The `action` field tells you what to do:
|
|
|
77
77
|
| `activate_interactive` | Action: Interactive Agent |
|
|
78
78
|
| `spawn_autonomous` | Action: Autonomous Agent |
|
|
79
79
|
| `spawn_parallel` | Action: Parallel Spawn |
|
|
80
|
+
| `spawn_team` | Action: Spawn Team (Article XXI) |
|
|
80
81
|
| `resume` | Action: Resume |
|
|
81
82
|
| `user_preview` | Action: User Preview |
|
|
82
83
|
| `complete` | Action: Complete |
|
|
@@ -237,6 +238,8 @@ If terminal spawning fails (CLI not found, system error), fall back **silently**
|
|
|
237
238
|
|
|
238
239
|
Planning phase agents (detail, architect, ux) run simultaneously.
|
|
239
240
|
|
|
241
|
+
**NOTE (Article XXI):** When `features.agent_teams: true` in config.yaml, the orchestrator MUST attempt `spawn_team` FIRST for GROUP 1 agents (detail, architect, ux) and BUILD pair (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`.
|
|
242
|
+
|
|
240
243
|
1. Display: "Spawning parallel group: {agents}"
|
|
241
244
|
2. Execute `parallel_spawn_command` via Bash tool
|
|
242
245
|
3. Parse consolidated JSON output
|
|
@@ -256,6 +259,156 @@ Planning phase agents (detail, architect, ux) run simultaneously.
|
|
|
256
259
|
|
|
257
260
|
---
|
|
258
261
|
|
|
262
|
+
## Action: Spawn Team (Article XXI)
|
|
263
|
+
|
|
264
|
+
Agent Teams enable real-time peer communication between agents. The orchestrator acts as Team Lead, spawning teammates as native Claude Code sub-agents (Agent tool), and coordinating their work through a shared task list and filesystem-based mailbox.
|
|
265
|
+
|
|
266
|
+
### Step 1: Register Team in Session
|
|
267
|
+
|
|
268
|
+
Run via Bash to create team directories, task list, and session entry:
|
|
269
|
+
```
|
|
270
|
+
node packages/chati-dev/bin/chati.js orchestrate spawn-team --team-type {planning|build} --previous-agent {last_completed_agent}
|
|
271
|
+
```
|
|
272
|
+
Parse JSON. If `fallback_required: true` → fall back silently to `spawn_parallel`. Do NOT display error messages.
|
|
273
|
+
|
|
274
|
+
Display: "Forming {team_type} team: {members}"
|
|
275
|
+
|
|
276
|
+
### Step 2: Spawn Teammates via Agent Tool
|
|
277
|
+
|
|
278
|
+
Use the Claude Code **Agent tool** to spawn each teammate as a sub-agent. This is the same mechanism used by `chati-update.md` for SCAN/COMPARE phases.
|
|
279
|
+
|
|
280
|
+
**For Planning Team** (post-Brief): spawn 3 agents IN PARALLEL by making all Agent tool calls in a SINGLE message:
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
Agent tool call 1:
|
|
284
|
+
description: "Detail agent — expand PRD"
|
|
285
|
+
prompt: [Read and follow chati.dev/agents/plan/detail.md.
|
|
286
|
+
Team mode active: team_id={team_id}.
|
|
287
|
+
Read shared task list at {task_list_path} — your task is TT-PLN-001.
|
|
288
|
+
Mailbox directory: {mailbox_path}
|
|
289
|
+
Cross-review target: architect (send cross_review_request after completing PRD).
|
|
290
|
+
Previous agent handoff: chati.dev/artifacts/handoffs/brief-handoff.md
|
|
291
|
+
CRITICAL: Produce your PRD INDEPENDENTLY first (sealed-bid). Only read teammates' outputs during cross-review.
|
|
292
|
+
Write your handoff to chati.dev/artifacts/handoffs/detail-handoff.md when done.]
|
|
293
|
+
|
|
294
|
+
Agent tool call 2:
|
|
295
|
+
description: "Architect agent — design architecture"
|
|
296
|
+
prompt: [Read and follow chati.dev/agents/plan/architect.md.
|
|
297
|
+
Team mode active: team_id={team_id}.
|
|
298
|
+
Read shared task list at {task_list_path} — your task is TT-PLN-002.
|
|
299
|
+
Mailbox directory: {mailbox_path}
|
|
300
|
+
Cross-review target: ux (send cross_review_request after completing architecture).
|
|
301
|
+
Previous agent handoff: chati.dev/artifacts/handoffs/brief-handoff.md
|
|
302
|
+
CRITICAL: Produce your architecture INDEPENDENTLY first (sealed-bid).
|
|
303
|
+
Write your handoff to chati.dev/artifacts/handoffs/architect-handoff.md when done.]
|
|
304
|
+
|
|
305
|
+
Agent tool call 3:
|
|
306
|
+
description: "UX agent — design UX specification"
|
|
307
|
+
prompt: [Read and follow chati.dev/agents/plan/ux.md.
|
|
308
|
+
Team mode active: team_id={team_id}.
|
|
309
|
+
Read shared task list at {task_list_path} — your task is TT-PLN-003.
|
|
310
|
+
Mailbox directory: {mailbox_path}
|
|
311
|
+
Cross-review target: detail (send cross_review_request after completing UX spec).
|
|
312
|
+
Previous agent handoff: chati.dev/artifacts/handoffs/brief-handoff.md
|
|
313
|
+
CRITICAL: Produce your UX specification INDEPENDENTLY first (sealed-bid).
|
|
314
|
+
Write your handoff to chati.dev/artifacts/handoffs/ux-handoff.md when done.]
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**For Build Team** (BUILD phase): spawn 2 agents IN PARALLEL:
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
Agent tool call 1:
|
|
321
|
+
description: "Dev agent — implement tasks"
|
|
322
|
+
prompt: [Read and follow chati.dev/agents/build/dev.md.
|
|
323
|
+
Team mode active: team_id={team_id}.
|
|
324
|
+
Read shared task list at {task_list_path}.
|
|
325
|
+
Mailbox directory: {mailbox_path}
|
|
326
|
+
Per-task QA mode: after each task, write task_ready_for_review to mailbox.
|
|
327
|
+
Continue to next task immediately — do NOT wait for QA response.
|
|
328
|
+
Poll mailbox at task boundaries for QA findings.
|
|
329
|
+
Previous agent handoff: chati.dev/artifacts/handoffs/qa-planning-handoff.md
|
|
330
|
+
Write your handoff to chati.dev/artifacts/handoffs/dev-handoff.md when done.]
|
|
331
|
+
|
|
332
|
+
Agent tool call 2:
|
|
333
|
+
description: "QA-Implementation agent — per-task review"
|
|
334
|
+
prompt: [Read and follow chati.dev/agents/quality/qa-implementation.md.
|
|
335
|
+
Team mode active: team_id={team_id}.
|
|
336
|
+
Read shared task list at {task_list_path}.
|
|
337
|
+
Mailbox directory: {mailbox_path}
|
|
338
|
+
Per-task review mode: poll mailbox for task_ready_for_review from Dev.
|
|
339
|
+
Run Evidence Gate (Phase 4c) and Root Layer Classification (Phase 4d) per task.
|
|
340
|
+
Write task_review_findings to mailbox for each reviewed task.
|
|
341
|
+
Run full Triple Review Protocol (Phase 5) only after the FINAL task.
|
|
342
|
+
Previous context: chati.dev/artifacts/handoffs/qa-planning-handoff.md (plan overview)
|
|
343
|
+
Write your handoff to chati.dev/artifacts/handoffs/qa-implementation-handoff.md when done.]
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Step 3: Collect Results
|
|
347
|
+
|
|
348
|
+
When all Agent tool calls return:
|
|
349
|
+
|
|
350
|
+
1. Read each agent's handoff from `chati.dev/artifacts/handoffs/`
|
|
351
|
+
2. Read the shared task list for final scores:
|
|
352
|
+
```
|
|
353
|
+
node packages/chati-dev/bin/chati.js orchestrate team-status --team-id {team_id}
|
|
354
|
+
```
|
|
355
|
+
3. If any agent failed: note which one and its error
|
|
356
|
+
4. Dissolve the team:
|
|
357
|
+
```
|
|
358
|
+
node packages/chati-dev/bin/chati.js orchestrate team-dissolve --team-id {team_id}
|
|
359
|
+
```
|
|
360
|
+
5. Parse dissolution JSON — check `quality_gate.passed`:
|
|
361
|
+
- If `true`: present Completion Options (Team variant)
|
|
362
|
+
- If `false`: display `quality_gate.failures` and offer correction options
|
|
363
|
+
|
|
364
|
+
6. Present **Completion Options** (Team variant):
|
|
365
|
+
```
|
|
366
|
+
{team_type} team completed. Team score: {team_score}%.
|
|
367
|
+
Members: {member1}: {score1}%, {member2}: {score2}%, ...
|
|
368
|
+
Quality Gate: {PASSED | FAILED: {failures}}
|
|
369
|
+
|
|
370
|
+
1. Approve and continue to {next_agent} (Recommended)
|
|
371
|
+
2. Review artifacts from a specific team member
|
|
372
|
+
3. Re-run a specific team member for improvements
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Echo Escalation
|
|
376
|
+
|
|
377
|
+
When reviewing the team-dissolve JSON, if `echo_events` is non-empty and count >= 3 for any member:
|
|
378
|
+
```
|
|
379
|
+
Team member {agent} appears stuck in a repetition loop (Article XXII Echo Detection).
|
|
380
|
+
|
|
381
|
+
1. Intervene directly — provide specific guidance (Recommended)
|
|
382
|
+
2. Remove {agent} from team — complete its work sequentially
|
|
383
|
+
3. Dissolve entire team — fall back to sequential pipeline
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Team Transition Protocol
|
|
387
|
+
|
|
388
|
+
After Planning Team (detail + architect + ux) completes and user approves:
|
|
389
|
+
1. Dissolve Planning Team: `team-dissolve --team-id {planning-team-id}`
|
|
390
|
+
2. Continue pipeline normally through Phases, Tasks, QA-Planning (these are NOT parallelizable and run sequentially as interactive or autonomous agents)
|
|
391
|
+
3. When BUILD phase activates: attempt to form Build Team (dev + qa-implementation) following the same Steps 1-3 above
|
|
392
|
+
4. Build Team uses per-task QA mode: QA reviews each Dev task as it completes, not as a batch
|
|
393
|
+
|
|
394
|
+
**Team cleanup order:** Dissolve current team BEFORE creating next team. Never two teams active simultaneously.
|
|
395
|
+
|
|
396
|
+
### Team Fallback
|
|
397
|
+
|
|
398
|
+
If the Agent tool is unavailable or any sub-agent spawn fails:
|
|
399
|
+
```
|
|
400
|
+
1. Fall back silently to spawn_parallel (GROUP 1) or spawn_autonomous (BUILD)
|
|
401
|
+
Use the existing Action: Parallel Spawn or Action: Autonomous Agent flow
|
|
402
|
+
2. Do NOT display any error or fallback message to the user
|
|
403
|
+
3. Run: node packages/chati-dev/bin/chati.js orchestrate team-dissolve --team-id {team_id}
|
|
404
|
+
to mark the team as degraded in session.yaml
|
|
405
|
+
4. All quality gates and thresholds remain unchanged
|
|
406
|
+
5. Alternatively, if spawn_parallel also fails, use Sequential Fallback:
|
|
407
|
+
read each agent .md file and execute sequentially in-conversation
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
259
412
|
## Action: Resume
|
|
260
413
|
|
|
261
414
|
The user is returning to an active session.
|
|
@@ -315,6 +468,10 @@ DEPLOY:
|
|
|
315
468
|
Current Agent: {current_agent}
|
|
316
469
|
Backlog: {count} items ({high_priority} high priority)
|
|
317
470
|
Context: {bracket} ({remaining}%)
|
|
471
|
+
|
|
472
|
+
TEAMS (if active):
|
|
473
|
+
{team_id}: {status} — members: {roster} — score: {team_score}%
|
|
474
|
+
Decision Trail: {decision_trail_count} entries ({unresolved} unresolved)
|
|
318
475
|
```
|
|
319
476
|
|
|
320
477
|
---
|
|
@@ -454,8 +611,9 @@ The orchestrator enforces `chati.dev/constitution.md`:
|
|
|
454
611
|
|
|
455
612
|
| Level | Action | Articles |
|
|
456
613
|
|-------|--------|----------|
|
|
457
|
-
| **BLOCK** | Halt agent on violation | I, II, III, IV, VII, VIII, X, XI, XV |
|
|
458
|
-
| **
|
|
614
|
+
| **BLOCK** | Halt agent on violation | I, II, III, IV, VII, VIII, X, XI, XII, XIII, XV, XVIII, XX, XXI, XXII |
|
|
615
|
+
| **STRICT** | Must not bypass quality gates | XVII |
|
|
616
|
+
| **GUIDE** | Correct without halting | V, IX, XIV, XVI, XIX |
|
|
459
617
|
| **WARN** | Generate warning in QA | VI |
|
|
460
618
|
|
|
461
619
|
---
|
|
@@ -483,6 +641,14 @@ If QA finds spec or architecture issues:
|
|
|
483
641
|
- issue_type "architecture" → route to architect agent
|
|
484
642
|
- issue_type "code" → fix in build mode (no backward transition)
|
|
485
643
|
|
|
644
|
+
### Root Layer Routing (Article XXII)
|
|
645
|
+
|
|
646
|
+
When QA-Implementation classifies a fault via the Fault Vector Protocol:
|
|
647
|
+
- `INTENT` → escalate to user via deviation protocol (requirement is flawed)
|
|
648
|
+
- `SPEC` → backward transition to Architect, UX, or Tasks agent
|
|
649
|
+
- `CODE` → Dev agent silent correction loop (existing behavior)
|
|
650
|
+
- `DEFER` → add to session backlog, do not block approval
|
|
651
|
+
|
|
486
652
|
---
|
|
487
653
|
|
|
488
654
|
## Authority Boundaries
|
|
@@ -496,6 +662,10 @@ If QA finds spec or architecture issues:
|
|
|
496
662
|
- Manage backlog items
|
|
497
663
|
- Spawn parallel terminals
|
|
498
664
|
- Decide execution mode (interactive vs autonomous)
|
|
665
|
+
- Form and dissolve Agent Teams (Team Lead role, Article XXI)
|
|
666
|
+
- Monitor shared task list and mailbox during team execution
|
|
667
|
+
- Detect and respond to echo events within teams (Article XXII)
|
|
668
|
+
- Route corrections to correct layer via Root Layer Routing (Article XXII)
|
|
499
669
|
|
|
500
670
|
### ALLOWED
|
|
501
671
|
- Read any file in the project (for state detection)
|
|
@@ -194,6 +194,166 @@
|
|
|
194
194
|
"reason": { "type": "string", "description": "User reason (override only) or QA finding (backward only)" }
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
|
+
},
|
|
198
|
+
"teams": {
|
|
199
|
+
"type": "array",
|
|
200
|
+
"default": [],
|
|
201
|
+
"description": "Agent Teams state tracking (Article XXI). Empty array when teams are not active.",
|
|
202
|
+
"items": {
|
|
203
|
+
"type": "object",
|
|
204
|
+
"required": ["team_id", "phase", "roster", "status"],
|
|
205
|
+
"properties": {
|
|
206
|
+
"team_id": {
|
|
207
|
+
"type": "string",
|
|
208
|
+
"pattern": "^TM-\\d{8}-[a-z]{3}$",
|
|
209
|
+
"description": "Unique team identifier (e.g., TM-20260411-pln)"
|
|
210
|
+
},
|
|
211
|
+
"phase": {
|
|
212
|
+
"enum": ["planning", "build"],
|
|
213
|
+
"description": "Pipeline phase this team serves"
|
|
214
|
+
},
|
|
215
|
+
"mission": {
|
|
216
|
+
"type": "string",
|
|
217
|
+
"description": "Team Mission Statement (one sentence, measurable outcome)"
|
|
218
|
+
},
|
|
219
|
+
"lead": {
|
|
220
|
+
"type": "string",
|
|
221
|
+
"default": "orchestrator",
|
|
222
|
+
"description": "Team Lead agent name"
|
|
223
|
+
},
|
|
224
|
+
"roster": {
|
|
225
|
+
"type": "array",
|
|
226
|
+
"items": { "type": "string" },
|
|
227
|
+
"description": "Agent names enrolled in this team (excluding lead)"
|
|
228
|
+
},
|
|
229
|
+
"status": {
|
|
230
|
+
"enum": ["forming", "active", "completing", "dissolved", "degraded"],
|
|
231
|
+
"description": "Team lifecycle state"
|
|
232
|
+
},
|
|
233
|
+
"task_list_path": {
|
|
234
|
+
"type": ["string", "null"],
|
|
235
|
+
"description": "Path to shared task list YAML"
|
|
236
|
+
},
|
|
237
|
+
"mailbox_path": {
|
|
238
|
+
"type": ["string", "null"],
|
|
239
|
+
"description": "Path to team mailbox directory"
|
|
240
|
+
},
|
|
241
|
+
"formed_at": {
|
|
242
|
+
"type": ["string", "null"],
|
|
243
|
+
"format": "date-time"
|
|
244
|
+
},
|
|
245
|
+
"dissolved_at": {
|
|
246
|
+
"type": ["string", "null"],
|
|
247
|
+
"format": "date-time"
|
|
248
|
+
},
|
|
249
|
+
"team_score": {
|
|
250
|
+
"type": ["number", "null"],
|
|
251
|
+
"minimum": 0,
|
|
252
|
+
"maximum": 100,
|
|
253
|
+
"description": "Aggregate team score (average of member task scores)"
|
|
254
|
+
},
|
|
255
|
+
"member_scores": {
|
|
256
|
+
"type": "object",
|
|
257
|
+
"description": "Per-member score at dissolution",
|
|
258
|
+
"patternProperties": { ".*": { "type": "number" } }
|
|
259
|
+
},
|
|
260
|
+
"correction_cycles": {
|
|
261
|
+
"type": "integer",
|
|
262
|
+
"default": 0,
|
|
263
|
+
"description": "Number of Team Correction Cycles consumed (max 2)"
|
|
264
|
+
},
|
|
265
|
+
"echo_events": {
|
|
266
|
+
"type": "array",
|
|
267
|
+
"default": [],
|
|
268
|
+
"description": "Echo Detection events within this team (Article XXII)",
|
|
269
|
+
"items": {
|
|
270
|
+
"type": "object",
|
|
271
|
+
"properties": {
|
|
272
|
+
"detected_at": { "type": "string", "format": "date-time" },
|
|
273
|
+
"member": { "type": "string" },
|
|
274
|
+
"similarity": { "type": "number" },
|
|
275
|
+
"action_taken": { "type": "string" }
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
"fallback_used": {
|
|
280
|
+
"type": "boolean",
|
|
281
|
+
"default": false,
|
|
282
|
+
"description": "True if team spawning failed and sequential fallback was used"
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
"team_events": {
|
|
288
|
+
"type": "array",
|
|
289
|
+
"default": [],
|
|
290
|
+
"description": "Audit trail of team lifecycle events (Article XXI)",
|
|
291
|
+
"items": {
|
|
292
|
+
"type": "object",
|
|
293
|
+
"required": ["timestamp", "event", "team_id"],
|
|
294
|
+
"properties": {
|
|
295
|
+
"timestamp": { "type": "string", "format": "date-time" },
|
|
296
|
+
"event": { "enum": ["formed", "dissolved", "degraded", "echo_detected", "fallback_activated", "correction_cycle"] },
|
|
297
|
+
"team_id": { "type": "string" },
|
|
298
|
+
"trigger": { "type": "string", "description": "What caused this event" },
|
|
299
|
+
"state": { "enum": ["clean", "degraded"], "description": "Dissolution state (for dissolved/degraded events)" }
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
"decision_trail": {
|
|
304
|
+
"type": "array",
|
|
305
|
+
"default": [],
|
|
306
|
+
"description": "Known-bad state log to prevent cyclical re-introduction of defects (Article XXII). Append-only.",
|
|
307
|
+
"items": {
|
|
308
|
+
"type": "object",
|
|
309
|
+
"required": ["id", "trigger", "fault_origin", "what_was_wrong", "avoid", "logged_at"],
|
|
310
|
+
"properties": {
|
|
311
|
+
"id": {
|
|
312
|
+
"type": "string",
|
|
313
|
+
"pattern": "^DT-\\d{4}$",
|
|
314
|
+
"description": "Decision Trail entry ID (e.g., DT-0001)"
|
|
315
|
+
},
|
|
316
|
+
"trigger": {
|
|
317
|
+
"type": "string",
|
|
318
|
+
"description": "What caused this revision (QA finding, user rejection, echo event)"
|
|
319
|
+
},
|
|
320
|
+
"fault_origin": {
|
|
321
|
+
"enum": ["INTENT", "SPEC", "CODE", "DEFER"],
|
|
322
|
+
"description": "Root Layer classification (Article XXII)"
|
|
323
|
+
},
|
|
324
|
+
"routed_to": {
|
|
325
|
+
"type": "string",
|
|
326
|
+
"description": "Agent the correction was routed to"
|
|
327
|
+
},
|
|
328
|
+
"what_was_wrong": {
|
|
329
|
+
"type": "string",
|
|
330
|
+
"description": "Precise description of the defect or known-bad state"
|
|
331
|
+
},
|
|
332
|
+
"avoid": {
|
|
333
|
+
"type": "string",
|
|
334
|
+
"description": "What NOT to do on the next iteration"
|
|
335
|
+
},
|
|
336
|
+
"evidence_hash": {
|
|
337
|
+
"type": ["string", "null"],
|
|
338
|
+
"description": "SHA-1 of the evidence excerpt for Echo Detection matching"
|
|
339
|
+
},
|
|
340
|
+
"correction_loop": {
|
|
341
|
+
"type": "integer",
|
|
342
|
+
"minimum": 1,
|
|
343
|
+
"maximum": 3,
|
|
344
|
+
"description": "Which correction loop iteration produced this entry"
|
|
345
|
+
},
|
|
346
|
+
"logged_at": {
|
|
347
|
+
"type": "string",
|
|
348
|
+
"format": "date-time"
|
|
349
|
+
},
|
|
350
|
+
"resolved": {
|
|
351
|
+
"type": "boolean",
|
|
352
|
+
"default": false,
|
|
353
|
+
"description": "True when the correcting agent has addressed this entry"
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
197
357
|
}
|
|
198
358
|
}
|
|
199
359
|
}
|