atris 3.15.13 → 3.15.22
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/AGENTS.md +84 -8
- package/README.md +5 -1
- package/atris/AGENTS.md +46 -1
- package/atris/CLAUDE.md +36 -1
- package/atris/GEMINI.md +14 -1
- package/atris/atris.md +12 -1
- package/atris/atrisDev.md +3 -2
- package/atris/context/README.md +11 -0
- package/atris/features/company-brain-sync/validate.md +5 -5
- package/atris/learnings.jsonl +1 -0
- package/atris/policies/atris-design.md +2 -0
- package/atris/skills/aeo/SKILL.md +2 -2
- package/atris/skills/atris/SKILL.md +15 -62
- package/atris/skills/design/SKILL.md +2 -0
- package/atris/skills/imessage/SKILL.md +19 -2
- package/atris/skills/loop/SKILL.md +6 -5
- package/atris/skills/magic-inbox/SKILL.md +1 -1
- package/atris/team/_template/MEMBER.md +23 -1
- package/atris/team/brainstormer/START_HERE.md +6 -0
- package/atris/team/executor/MEMBER.md +13 -0
- package/atris/team/executor/START_HERE.md +6 -0
- package/atris/team/launcher/START_HERE.md +6 -0
- package/atris/team/mission-lead/MEMBER.md +39 -0
- package/atris/team/mission-lead/MISSION.md +33 -0
- package/atris/team/mission-lead/START_HERE.md +6 -0
- package/atris/team/navigator/MEMBER.md +11 -0
- package/atris/team/navigator/START_HERE.md +6 -0
- package/atris/team/opus-overnight/MEMBER.md +39 -0
- package/atris/team/opus-overnight/MISSION.md +61 -0
- package/atris/team/opus-overnight/START_HERE.md +6 -0
- package/atris/team/opus-overnight/STEERING.md +35 -0
- package/atris/team/researcher/START_HERE.md +6 -0
- package/atris/team/validator/MEMBER.md +26 -6
- package/atris/team/validator/START_HERE.md +6 -0
- package/atris/wiki/concepts/agent-activation-contract.md +79 -0
- package/atris/wiki/concepts/workspace-initialization-contract.md +73 -0
- package/atris/wiki/index.md +27 -0
- package/atris/wiki/sources/atris-labs-2026-05-10.txt +17 -0
- package/atris/wiki/sources/atris-labs-goals-2026-05-10.txt +15 -0
- package/atris/wiki/sources/atrisos-generative-ui-product-surface-2026-05-10.txt +10 -0
- package/atris/wiki/sources/jack-dorsey-2026-05-10.txt +12 -0
- package/atris.md +49 -13
- package/bin/atris.js +660 -22
- package/commands/activate.js +12 -3
- package/commands/aeo.js +1 -1
- package/commands/align.js +10 -10
- package/commands/analytics.js +9 -4
- package/commands/app.js +2 -0
- package/commands/apps.js +276 -0
- package/commands/auth.js +1 -1
- package/commands/autopilot.js +74 -5
- package/commands/brain.js +536 -61
- package/commands/brainstorm.js +12 -12
- package/commands/business-sync.js +142 -24
- package/commands/clean.js +9 -6
- package/commands/codex-goal.js +311 -0
- package/commands/errors.js +11 -1
- package/commands/feedback.js +55 -17
- package/commands/fork.js +2 -2
- package/commands/gm.js +376 -0
- package/commands/init.js +80 -3
- package/commands/integrations.js +524 -0
- package/commands/learn.js +25 -16
- package/commands/lesson.js +41 -0
- package/commands/lifecycle.js +2 -2
- package/commands/member.js +2416 -9
- package/commands/mission.js +1776 -0
- package/commands/now.js +48 -7
- package/commands/play.js +425 -0
- package/commands/publish.js +2 -1
- package/commands/pull.js +72 -29
- package/commands/push.js +199 -17
- package/commands/review.js +51 -13
- package/commands/skill.js +2 -2
- package/commands/soul.js +19 -13
- package/commands/status.js +6 -1
- package/commands/sync.js +5 -4
- package/commands/task.js +1041 -147
- package/commands/terminal.js +5 -5
- package/commands/verify.js +7 -5
- package/commands/visualize.js +7 -0
- package/commands/wiki.js +53 -16
- package/commands/workflow.js +298 -54
- package/commands/workspace-clean.js +1 -1
- package/commands/worktree.js +468 -0
- package/commands/xp.js +1608 -0
- package/lib/manifest.js +34 -4
- package/lib/scorecard.js +3 -2
- package/lib/task-db.js +408 -27
- package/lib/todo-fallback.js +28 -2
- package/lib/todo.js +5 -3
- package/package.json +23 -2
- package/utils/update-check.js +51 -1
package/commands/brain.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
+
const { refreshNowFile } = require('./now');
|
|
4
5
|
|
|
5
6
|
const GENERATED_START = '<!-- ATRIS_BRAIN_COMPILE:START -->';
|
|
6
7
|
const GENERATED_END = '<!-- ATRIS_BRAIN_COMPILE:END -->';
|
|
8
|
+
const GENERATED_LOAD_ORDER_FILES = [
|
|
9
|
+
'atris/now.md',
|
|
10
|
+
'atris/brain/STATUS.md',
|
|
11
|
+
'atris/brain/self_improvement_ledger.md',
|
|
12
|
+
];
|
|
13
|
+
const OPTIONAL_LOAD_ORDER_FILES = [
|
|
14
|
+
'atris/wiki/concepts/agent-activation-contract.md',
|
|
15
|
+
'atris/skills/atris/SKILL.md',
|
|
16
|
+
'atris/PERSONA.md',
|
|
17
|
+
'atris/MAP.md',
|
|
18
|
+
'atris/TODO.md',
|
|
19
|
+
'atris/wiki/index.md',
|
|
20
|
+
];
|
|
7
21
|
|
|
8
22
|
function parseArgs(args) {
|
|
9
23
|
const options = {
|
|
@@ -128,16 +142,56 @@ function readJsonlStats(filePath) {
|
|
|
128
142
|
};
|
|
129
143
|
}
|
|
130
144
|
|
|
145
|
+
function readJsonlRows(filePath) {
|
|
146
|
+
const text = readText(filePath);
|
|
147
|
+
const rows = [];
|
|
148
|
+
for (const line of text.split('\n')) {
|
|
149
|
+
if (!line.trim()) continue;
|
|
150
|
+
try {
|
|
151
|
+
rows.push(JSON.parse(line));
|
|
152
|
+
} catch {
|
|
153
|
+
// Bad rows stay visible in stats; callers only use valid rows.
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return rows;
|
|
157
|
+
}
|
|
158
|
+
|
|
131
159
|
function countTodoItems(todoText) {
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
160
|
+
const text = String(todoText || '');
|
|
161
|
+
const hasRenderedSections = /^##\s+(Backlog|In Progress|Blocked|Completed)\s*$/m.test(text);
|
|
162
|
+
let section = null;
|
|
163
|
+
let unchecked = 0;
|
|
164
|
+
let checked = 0;
|
|
165
|
+
let titled = 0;
|
|
166
|
+
let legacyOpen = 0;
|
|
167
|
+
let renderedOpen = 0;
|
|
168
|
+
let renderedDone = 0;
|
|
169
|
+
|
|
170
|
+
for (const line of text.split(/\r?\n/)) {
|
|
171
|
+
const heading = line.match(/^##\s+(.+?)\s*$/);
|
|
172
|
+
if (heading) {
|
|
173
|
+
section = heading[1];
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const isUnchecked = /^\s*-\s+\[[ ]\]/.test(line);
|
|
178
|
+
const isChecked = /^\s*-\s+\[[xX]\]/.test(line);
|
|
179
|
+
const isTitled = /^\s*-\s+(?:\[[ xX]\]\s+)?\*\*[^*]+:?\*\*/.test(line);
|
|
180
|
+
if (isUnchecked) unchecked += 1;
|
|
181
|
+
if (isChecked) checked += 1;
|
|
182
|
+
if (!hasRenderedSections && (isUnchecked || (isTitled && !isChecked))) legacyOpen += 1;
|
|
183
|
+
if (!isTitled) continue;
|
|
184
|
+
|
|
185
|
+
titled += 1;
|
|
186
|
+
if (hasRenderedSections && ['Backlog', 'In Progress', 'Blocked'].includes(section)) renderedOpen += 1;
|
|
187
|
+
if (hasRenderedSections && section === 'Completed') renderedDone += 1;
|
|
188
|
+
}
|
|
189
|
+
|
|
136
190
|
return {
|
|
137
|
-
open:
|
|
191
|
+
open: hasRenderedSections ? renderedOpen : legacyOpen,
|
|
138
192
|
checked,
|
|
139
193
|
titled,
|
|
140
|
-
done,
|
|
194
|
+
done: hasRenderedSections ? renderedDone : checked + (text.match(/~~|DONE|✅/g) || []).length,
|
|
141
195
|
};
|
|
142
196
|
}
|
|
143
197
|
|
|
@@ -167,6 +221,16 @@ function firstHeading(text, fallback) {
|
|
|
167
221
|
return match ? match[1].trim() : fallback;
|
|
168
222
|
}
|
|
169
223
|
|
|
224
|
+
function scorecardTs(row) {
|
|
225
|
+
return String(row?.ts || row?.episode_created_at || '');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function isNextMoveScorecard(row) {
|
|
229
|
+
if (!row) return false;
|
|
230
|
+
if (row.type === 'scorecard') return true;
|
|
231
|
+
return row.schema === 'atris.brain.scorecard.v1' && row.source === 'operator_feedback';
|
|
232
|
+
}
|
|
233
|
+
|
|
170
234
|
function collectState(root) {
|
|
171
235
|
const atrisDir = path.join(root, 'atris');
|
|
172
236
|
const stateDir = path.join(root, '.atris', 'state');
|
|
@@ -180,6 +244,7 @@ function collectState(root) {
|
|
|
180
244
|
const stateFiles = [
|
|
181
245
|
'events.jsonl',
|
|
182
246
|
'episodes.jsonl',
|
|
247
|
+
'task_episodes.jsonl',
|
|
183
248
|
'scorecards.jsonl',
|
|
184
249
|
'agent_tasks.jsonl',
|
|
185
250
|
'agent_mail.jsonl',
|
|
@@ -190,6 +255,10 @@ function collectState(root) {
|
|
|
190
255
|
|
|
191
256
|
const totalRows = stateFiles.reduce((sum, item) => sum + item.rows, 0);
|
|
192
257
|
const validRows = stateFiles.reduce((sum, item) => sum + item.validRows, 0);
|
|
258
|
+
const latestScorecard = readJsonlRows(path.join(stateDir, 'scorecards.jsonl'))
|
|
259
|
+
.filter(isNextMoveScorecard)
|
|
260
|
+
.sort((a, b) => scorecardTs(a).localeCompare(scorecardTs(b)))
|
|
261
|
+
.pop() || null;
|
|
193
262
|
const latestStateTs = stateFiles
|
|
194
263
|
.map(item => item.latestTs)
|
|
195
264
|
.filter(Boolean)
|
|
@@ -212,31 +281,84 @@ function collectState(root) {
|
|
|
212
281
|
stateFiles,
|
|
213
282
|
totalRows,
|
|
214
283
|
validRows,
|
|
284
|
+
latestScorecard: latestScorecard ? {
|
|
285
|
+
task_title: latestScorecard.task_title || latestScorecard.recommendation || null,
|
|
286
|
+
reward: latestScorecard.reward,
|
|
287
|
+
next_task_suggestion: latestScorecard.next_task_suggestion || null,
|
|
288
|
+
ts: scorecardTs(latestScorecard) || null,
|
|
289
|
+
} : null,
|
|
215
290
|
latestStateTs,
|
|
216
291
|
};
|
|
217
292
|
}
|
|
218
293
|
|
|
294
|
+
function prepareBrainState(root) {
|
|
295
|
+
refreshNowFile(root);
|
|
296
|
+
return collectState(root);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function countStateRows(state, names) {
|
|
300
|
+
const wanted = new Set(Array.isArray(names) ? names : [names]);
|
|
301
|
+
return state.stateFiles
|
|
302
|
+
.filter(item => wanted.has(path.basename(item.path)))
|
|
303
|
+
.reduce((sum, item) => sum + item.rows, 0);
|
|
304
|
+
}
|
|
305
|
+
|
|
219
306
|
function strongestSignal(state) {
|
|
220
|
-
const mail = state
|
|
221
|
-
const tasks = state
|
|
222
|
-
const scorecards = state
|
|
223
|
-
const episodes = state
|
|
307
|
+
const mail = countStateRows(state, 'agent_mail.jsonl');
|
|
308
|
+
const tasks = countStateRows(state, 'agent_tasks.jsonl');
|
|
309
|
+
const scorecards = countStateRows(state, 'scorecards.jsonl');
|
|
310
|
+
const episodes = countStateRows(state, ['episodes.jsonl', 'task_episodes.jsonl']);
|
|
224
311
|
if (scorecards > 0 && episodes > 0) return `${scorecards} scorecard row(s) and ${episodes} episode row(s) are available for feedback-driven learning.`;
|
|
225
312
|
if (scorecards > 0) return `${scorecards} scorecard row(s) are available for outcome scoring.`;
|
|
313
|
+
if (episodes > 0) return `${episodes} episode row(s) are available; compile them into scorecards and next-action memory.`;
|
|
226
314
|
if (mail > 0) return `${mail} agent-mail row(s) are available; compile them into decisions, follow-ups, and CRM memory.`;
|
|
227
315
|
if (tasks > 0) return `${tasks} agent-task row(s) are available; use them to choose the next action.`;
|
|
228
316
|
return 'Workspace has structure, but little scored state yet; first improvement is to create scorecards and episodes.';
|
|
229
317
|
}
|
|
230
318
|
|
|
319
|
+
function isActionableScorecardNextMove(value) {
|
|
320
|
+
const text = String(value || '').trim();
|
|
321
|
+
if (text.length < 12) return false;
|
|
322
|
+
|
|
323
|
+
const metaPatterns = [
|
|
324
|
+
/\bcompiled business reward\b/i,
|
|
325
|
+
/\bcompleted business loop\b/i,
|
|
326
|
+
/\bfallback\b/i,
|
|
327
|
+
/\binstead of\b/i,
|
|
328
|
+
/\bbrain\s+(scorecard|compile|feedback|approval|yes|edit|no)\b/i,
|
|
329
|
+
/\bcompile the brain\b/i,
|
|
330
|
+
/\bcompletion audit\b/i,
|
|
331
|
+
/\bnext (move|task|action|operator loop)\b/i,
|
|
332
|
+
/\bonly when there is\b/i,
|
|
333
|
+
/\bprocess work\b/i,
|
|
334
|
+
/\bbefore taking new work\b/i,
|
|
335
|
+
/\brepeating? the completed\b/i,
|
|
336
|
+
/\bscorecard suggestion\b/i,
|
|
337
|
+
];
|
|
338
|
+
if (metaPatterns.some(pattern => pattern.test(text))) return false;
|
|
339
|
+
|
|
340
|
+
return /\b(add|answer|archive|assign|build|call|choose|claim|clean|clear|close|compile|create|debug|delete|draft|edit|finish|fix|implement|ingest|open|patch|pick|pull|push|record|replace|resolve|retire|review|run|ship|sync|test|triage|update|validate|verify|write)\b/i.test(text);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function operatorActivationNextMove(state) {
|
|
344
|
+
return `Run \`atris brain activate --member <name> --root ${state.root} --verify\` to bind the operator and get a concrete work block.`;
|
|
345
|
+
}
|
|
346
|
+
|
|
231
347
|
function nextMove(state) {
|
|
232
|
-
|
|
348
|
+
const scorecards = countStateRows(state, 'scorecards.jsonl');
|
|
349
|
+
const episodes = countStateRows(state, ['episodes.jsonl', 'task_episodes.jsonl']);
|
|
350
|
+
if (state.totalRows > 0 && scorecards === 0) {
|
|
351
|
+
if (episodes > 0) return 'Turn existing episode rows into the first scorecard so the next run has reward, not just traces.';
|
|
233
352
|
return 'Turn existing state rows into the first scorecard so the next run has a reward signal, not just memory.';
|
|
234
353
|
}
|
|
235
|
-
if (
|
|
354
|
+
if (episodes === 0) {
|
|
236
355
|
return 'Capture one operator approval, edit, or rejection as an episode so the brain has a learning trace.';
|
|
237
356
|
}
|
|
238
357
|
if (state.todo.open > 0) return 'Pick the highest-leverage open TODO item and leave a scorecard when done.';
|
|
239
|
-
|
|
358
|
+
if (state.latestScorecard && isActionableScorecardNextMove(state.latestScorecard.next_task_suggestion)) {
|
|
359
|
+
return state.latestScorecard.next_task_suggestion;
|
|
360
|
+
}
|
|
361
|
+
return operatorActivationNextMove(state);
|
|
240
362
|
}
|
|
241
363
|
|
|
242
364
|
function rewardForRating(rating) {
|
|
@@ -257,9 +379,12 @@ function loadBrainState(root) {
|
|
|
257
379
|
return readJson(path.join(root, 'atris', 'brain', 'state.json')) || collectState(root);
|
|
258
380
|
}
|
|
259
381
|
|
|
382
|
+
function normalizeMemberSlug(memberSlug) {
|
|
383
|
+
return String(memberSlug || '').toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
|
384
|
+
}
|
|
385
|
+
|
|
260
386
|
function readMemberContext(root, memberSlug) {
|
|
261
|
-
|
|
262
|
-
const slug = String(memberSlug).toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
|
387
|
+
const slug = normalizeMemberSlug(memberSlug);
|
|
263
388
|
if (!slug) return null;
|
|
264
389
|
const memberDir = path.join(root, 'atris', 'team', slug);
|
|
265
390
|
const memberText = readText(path.join(memberDir, 'MEMBER.md'));
|
|
@@ -267,11 +392,60 @@ function readMemberContext(root, memberSlug) {
|
|
|
267
392
|
return {
|
|
268
393
|
slug,
|
|
269
394
|
name: firstHeading(memberText, slug),
|
|
395
|
+
profile: memberText,
|
|
270
396
|
startHere: readText(path.join(memberDir, 'START_HERE.md')),
|
|
271
397
|
goals: readText(path.join(memberDir, 'goals.md')),
|
|
272
398
|
};
|
|
273
399
|
}
|
|
274
400
|
|
|
401
|
+
function parseContributionCard(text, member) {
|
|
402
|
+
if (!text || !member) return null;
|
|
403
|
+
const firstName = String(member.name || member.slug || '').split(/\s+/)[0].toLowerCase();
|
|
404
|
+
const sections = String(text).split(/\n(?=##\s+)/);
|
|
405
|
+
const memberSections = sections.filter(section => new RegExp(`^##\\s+${firstName}\\b`, 'i').test(section.trim()));
|
|
406
|
+
const section = (
|
|
407
|
+
memberSections.find(candidate => /current_score_signal\s*:/i.test(candidate))
|
|
408
|
+
|| memberSections[0]
|
|
409
|
+
|| ''
|
|
410
|
+
);
|
|
411
|
+
const fields = {};
|
|
412
|
+
for (const line of section.split('\n')) {
|
|
413
|
+
const match = line.match(/^\s*([a-z_]+):\s*(.+?)\s*$/i);
|
|
414
|
+
if (match) fields[match[1].toLowerCase()] = match[2].trim();
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const tableRow = text.split('\n').find(line => {
|
|
418
|
+
if (!line.trim().startsWith('|')) return false;
|
|
419
|
+
return line.toLowerCase().includes(`| ${firstName} |`);
|
|
420
|
+
});
|
|
421
|
+
if (tableRow) {
|
|
422
|
+
const cells = tableRow.split('|').map(cell => cell.trim()).filter(Boolean);
|
|
423
|
+
if (cells.length >= 5) {
|
|
424
|
+
fields.operator ||= cells[0];
|
|
425
|
+
fields.current_score_signal ||= cells[2];
|
|
426
|
+
fields.current_signal ||= cells[3];
|
|
427
|
+
fields.next_rep ||= cells[4];
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const score = fields.current_score_signal || fields.score_signal || fields.score;
|
|
432
|
+
const nextRep = fields.next_rep || fields.proof_needed || fields.next_move;
|
|
433
|
+
if (!score && !nextRep) return null;
|
|
434
|
+
|
|
435
|
+
return {
|
|
436
|
+
score,
|
|
437
|
+
nextRep,
|
|
438
|
+
proofNeeded: fields.proof_needed || '',
|
|
439
|
+
why: fields.why || fields.current_signal || '',
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function memberScoreContext(root, member) {
|
|
444
|
+
if (!member) return null;
|
|
445
|
+
const contributionScore = readText(path.join(root, 'atris', 'state', 'contribution-score.md'));
|
|
446
|
+
return parseContributionCard(contributionScore, member);
|
|
447
|
+
}
|
|
448
|
+
|
|
275
449
|
function listMemberSlugs(root) {
|
|
276
450
|
const teamDir = path.join(root, 'atris', 'team');
|
|
277
451
|
if (!fs.existsSync(teamDir)) return [];
|
|
@@ -303,7 +477,7 @@ function rememberOperator(root, member) {
|
|
|
303
477
|
}, null, 2) + '\n', 'utf8');
|
|
304
478
|
}
|
|
305
479
|
|
|
306
|
-
function memberNextMove(member) {
|
|
480
|
+
function memberNextMove(member, state = null) {
|
|
307
481
|
if (!member) return null;
|
|
308
482
|
const name = member.name || member.slug;
|
|
309
483
|
const context = `${member.startHere}\n${member.goals}`;
|
|
@@ -312,7 +486,41 @@ function memberNextMove(member) {
|
|
|
312
486
|
return `${name}: run one customer-moving GTM rep, update the relevant workspace state within 10 minutes, and leave a scorecard.`;
|
|
313
487
|
}
|
|
314
488
|
if (member.slug === 'keshav' || /keshav/i.test(member.name || '')) {
|
|
315
|
-
return `${name}:
|
|
489
|
+
return `${name}: act as Customer 0: choose one allocation, narrative, system, or talent leverage move; leave proof and route the next owner.`;
|
|
490
|
+
}
|
|
491
|
+
if (/opus|overnight/i.test(identity)) {
|
|
492
|
+
return `${name}: run the next zero-spend ` +
|
|
493
|
+
'`rl-exp2` tick, land one artifact, name the 1% delta, and record the mission receipt.';
|
|
494
|
+
}
|
|
495
|
+
if (/mission[- ]lead|mission lead/i.test(identity)) {
|
|
496
|
+
return `${name}: choose or create one bounded mission step, run its verifier, and close it with proof, a scorecard, and the next move.`;
|
|
497
|
+
}
|
|
498
|
+
if (/validator|reviewer/i.test(identity)) {
|
|
499
|
+
if ((state?.todo?.open || 0) === 0 && (state?.todo?.done || 0) === 0) {
|
|
500
|
+
return `${name}: wait for one concrete artifact or ask Navigator to create a reviewable task with verifier, proof target, and residual-risk checklist.`;
|
|
501
|
+
}
|
|
502
|
+
return `${name}: review the highest-risk open or recently completed task, run its verifier, name residual risk, and approve or block with proof.`;
|
|
503
|
+
}
|
|
504
|
+
if (/executor|builder/i.test(identity)) {
|
|
505
|
+
if ((state?.todo?.open || 0) === 0) {
|
|
506
|
+
return `${name}: ask Navigator to create one bounded task with files, verifier, and stop rule before making a patch.`;
|
|
507
|
+
}
|
|
508
|
+
return `${name}: execute the highest-leverage claimed task one scoped step at a time, run the verifier after the patch, and hand off proof for review.`;
|
|
509
|
+
}
|
|
510
|
+
if (/navigator|planner/i.test(identity)) {
|
|
511
|
+
return `${name}: turn one messy or unclaimed intent into a MAP-backed plan with ASCII visualization, exact files, verifier, rollback, and a review-ready task.`;
|
|
512
|
+
}
|
|
513
|
+
if (/launcher|closer/i.test(identity)) {
|
|
514
|
+
if ((state?.todo?.done || 0) === 0) {
|
|
515
|
+
return `${name}: wait for one validated task receipt before closeout, or ask Validator to produce a review decision with proof.`;
|
|
516
|
+
}
|
|
517
|
+
return `${name}: close one validated task into release-ready proof: summarize the shipped change, capture the lesson, update MAP or journal if needed, and name the publish step.`;
|
|
518
|
+
}
|
|
519
|
+
if (/brainstormer|idea shaper|reality shaper/i.test(identity)) {
|
|
520
|
+
return `${name}: shape one raw idea into a concise vision: current reality, 1-2 options, constraints, success criteria, and a navigator-ready next step.`;
|
|
521
|
+
}
|
|
522
|
+
if (/researcher|research/i.test(identity)) {
|
|
523
|
+
return `${name}: answer one explicit research question with primary sources, source-backed findings, unverified gaps, and a short So What handoff.`;
|
|
316
524
|
}
|
|
317
525
|
if (/gtm|forward deployed/i.test(identity) || /gtm|forward deployed|customer-moving|customer move/i.test(context)) {
|
|
318
526
|
return `${name}: run one customer-moving GTM rep, update the relevant workspace state within 10 minutes, and leave a scorecard.`;
|
|
@@ -320,6 +528,16 @@ function memberNextMove(member) {
|
|
|
320
528
|
if (/ceo|lab/i.test(identity) || /ceo|lab|synthesis loop|decision queue|building|closing|investor/i.test(context)) {
|
|
321
529
|
return `${name}: make one high-leverage CEO move: ship product, close a strategic loop, or make a queued decision; leave proof and a scorecard.`;
|
|
322
530
|
}
|
|
531
|
+
if (/rl-exp2|zero-spend|zero spend|tick-prefixed|1%/i.test(context)) {
|
|
532
|
+
return `${name}: run the next zero-spend ` +
|
|
533
|
+
'`rl-exp2` tick, land one artifact, name the 1% delta, and record the mission receipt.';
|
|
534
|
+
}
|
|
535
|
+
if (/validation checklist|signoff|reject|falsifiable|residual risk|anti-slop|review/i.test(context)) {
|
|
536
|
+
return `${name}: review the highest-risk open or recently completed task, run its verifier, name residual risk, and approve or block with proof.`;
|
|
537
|
+
}
|
|
538
|
+
if (/bounded mission|mission step|proof target|verifier/i.test(context)) {
|
|
539
|
+
return `${name}: choose or create one bounded mission step, run its verifier, and close it with proof, a scorecard, and the next move.`;
|
|
540
|
+
}
|
|
323
541
|
return `${name}: use your START_HERE, complete the first concrete work block, and leave a scorecard.`;
|
|
324
542
|
}
|
|
325
543
|
|
|
@@ -358,10 +576,56 @@ function modeNextMove(member, mode) {
|
|
|
358
576
|
return null;
|
|
359
577
|
}
|
|
360
578
|
|
|
579
|
+
function memberProfileIssues(member) {
|
|
580
|
+
if (!member) return [];
|
|
581
|
+
const profile = String(member.profile || '');
|
|
582
|
+
const issues = [];
|
|
583
|
+
if (/\(Define how this member communicates/i.test(profile)) issues.push('persona is still template text');
|
|
584
|
+
if (/^\s*1\.\s+Step one\s*$/im.test(profile)) issues.push('workflow is still template text');
|
|
585
|
+
if (/^\s*1\.\s+Rule one\s*$/im.test(profile)) issues.push('rules are still template text');
|
|
586
|
+
return issues;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function renderMissingMemberCard(state, memberSlug) {
|
|
590
|
+
const slug = normalizeMemberSlug(memberSlug) || '<name>';
|
|
591
|
+
const members = listMemberSlugs(state.root);
|
|
592
|
+
const available = members.length > 0 ? members.join(', ') : 'none';
|
|
593
|
+
return `CONTEXT: ${state.name} Brain
|
|
594
|
+
OPERATOR: ${slug} (missing)
|
|
595
|
+
NEXT MOVE: Create atris/team/${slug}/MEMBER.md or rerun with an existing member.
|
|
596
|
+
WHY: Activation can only route by operator after the member profile exists locally.
|
|
597
|
+
PROOF: Re-run atris brain activate --member ${slug} --root ${state.root} --verify and get an operator-specific work block.
|
|
598
|
+
AVAILABLE MEMBERS: ${available}
|
|
599
|
+
FEEDBACK: yes / edit / no`;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function renderPlaceholderMemberCard(state, member, issues) {
|
|
603
|
+
const slug = member.slug || '<name>';
|
|
604
|
+
return `CONTEXT: ${state.name} Brain
|
|
605
|
+
OPERATOR: ${member.name || slug} (not ready)
|
|
606
|
+
NEXT MOVE: Replace placeholder sections in atris/team/${slug}/MEMBER.md with the member's real workflow, rules, and proof standard.
|
|
607
|
+
WHY: Activation should not turn template text into fake operator work.
|
|
608
|
+
PROOF: Re-run atris brain activate --member ${slug} --root ${state.root} --verify and get an operator-specific work block.
|
|
609
|
+
PROFILE ISSUES: ${issues.join('; ')}
|
|
610
|
+
FEEDBACK: yes / edit / no`;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function renderMissingStartHereCard(state, member) {
|
|
614
|
+
const slug = member.slug || '<name>';
|
|
615
|
+
return `CONTEXT: ${state.name} Brain
|
|
616
|
+
OPERATOR: ${member.name || slug} (not ready)
|
|
617
|
+
NEXT MOVE: Create atris/team/${slug}/START_HERE.md with the member's first concrete work block, verifier, and proof target.
|
|
618
|
+
WHY: Activation should not tell an operator to use START_HERE until that local contract exists.
|
|
619
|
+
PROOF: Re-run atris brain activate --member ${slug} --root ${state.root} --verify and get an operator-specific work block.
|
|
620
|
+
FEEDBACK: yes / edit / no`;
|
|
621
|
+
}
|
|
622
|
+
|
|
361
623
|
function renderActivationCard(state, options = {}) {
|
|
362
624
|
const requestedMember = options.member || readRememberedOperator(state.root);
|
|
363
625
|
const member = readMemberContext(state.root, requestedMember);
|
|
364
|
-
|
|
626
|
+
if (!member && requestedMember) {
|
|
627
|
+
return renderMissingMemberCard(state, requestedMember);
|
|
628
|
+
}
|
|
365
629
|
if (!member && !options.member) {
|
|
366
630
|
return `CONTEXT: ${state.name} Brain
|
|
367
631
|
OPERATOR: unknown
|
|
@@ -370,14 +634,25 @@ WHY: The brain should route work by operator, customer, and proof path before it
|
|
|
370
634
|
PROOF: Activation re-runs with a known operator and produces a specific work block.
|
|
371
635
|
FEEDBACK: yes / edit / no`;
|
|
372
636
|
}
|
|
373
|
-
|
|
637
|
+
const profileIssues = memberProfileIssues(member);
|
|
638
|
+
if (profileIssues.length > 0) {
|
|
639
|
+
return renderPlaceholderMemberCard(state, member, profileIssues);
|
|
640
|
+
}
|
|
641
|
+
if (!String(member.startHere || '').trim()) {
|
|
642
|
+
return renderMissingStartHereCard(state, member);
|
|
643
|
+
}
|
|
644
|
+
if (options.remember !== false) rememberOperator(state.root, member);
|
|
374
645
|
const modeMove = modeNextMove(member, options.mode);
|
|
375
|
-
const next = modeMove?.move || memberNextMove(member) ||
|
|
376
|
-
const
|
|
646
|
+
const next = modeMove?.move || memberNextMove(member, state) || nextMove(state);
|
|
647
|
+
const scoreContext = memberScoreContext(state.root, member);
|
|
648
|
+
const proof = modeMove?.proof || scoreContext?.proofNeeded || scoreContext?.nextRep || `After the move, record feedback and recompile: atris brain yes|edit|no "note" --root ${state.root} --verify && atris brain compile --root ${state.root} --verify`;
|
|
649
|
+
const scoreLines = scoreContext
|
|
650
|
+
? `${scoreContext.score ? `\nSCORE: ${scoreContext.score}` : ''}${scoreContext.nextRep ? `\nNEXT REP: ${scoreContext.nextRep}` : ''}`
|
|
651
|
+
: '';
|
|
377
652
|
return `CONTEXT: ${state.name} Brain${member ? `\nOPERATOR: ${member.name}` : ''}${modeMove ? `\nMODE: ${modeMove.label}` : ''}
|
|
378
653
|
NEXT MOVE: ${next}
|
|
379
654
|
WHY: This is the next business workflow to improve from atris/now.md, the compiled brain, MAP, TODO, wiki, state rows, and reward history.
|
|
380
|
-
PROOF: ${proof}
|
|
655
|
+
PROOF: ${proof}${scoreLines}
|
|
381
656
|
FEEDBACK: yes / edit / no`;
|
|
382
657
|
}
|
|
383
658
|
|
|
@@ -389,7 +664,38 @@ TEAM: no members found
|
|
|
389
664
|
NEXT MOVE: Add a member under atris/team/<name>/MEMBER.md, then run activation again.`;
|
|
390
665
|
}
|
|
391
666
|
|
|
392
|
-
return slugs.map(slug => renderActivationCard(state, { member: slug })).join('\n\n---\n\n');
|
|
667
|
+
return slugs.map(slug => renderActivationCard(state, { member: slug, remember: false })).join('\n\n---\n\n');
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function galleryReadinessIssues(gallery) {
|
|
671
|
+
return String(gallery || '')
|
|
672
|
+
.split(/\n---\n/)
|
|
673
|
+
.flatMap(card => activationCardReadinessIssues(card));
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function activationCardReadinessIssues(card) {
|
|
677
|
+
const operator = (String(card || '').match(/^OPERATOR:\s*(.+)$/m) || [null, 'unknown member'])[1];
|
|
678
|
+
if (/\((missing|not ready)\)/.test(operator)) return [operator];
|
|
679
|
+
return [];
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function verifyActivationGallery(gallery) {
|
|
683
|
+
const issues = galleryReadinessIssues(gallery);
|
|
684
|
+
if (issues.length > 0) {
|
|
685
|
+
throw new Error(`brain gallery not-ready member activation cards: ${issues.join(', ')}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function verifyActivationCard(card) {
|
|
690
|
+
const issues = activationCardReadinessIssues(card);
|
|
691
|
+
if (issues.length > 0) {
|
|
692
|
+
throw new Error(`brain activate non-executable member activation card: ${issues.join(', ')}`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function printVerifyFailure(error) {
|
|
697
|
+
console.error(error && error.message ? error.message : String(error));
|
|
698
|
+
process.exit(1);
|
|
393
699
|
}
|
|
394
700
|
|
|
395
701
|
function recordFeedback(options) {
|
|
@@ -499,6 +805,90 @@ function recordApproval(options) {
|
|
|
499
805
|
return approval;
|
|
500
806
|
}
|
|
501
807
|
|
|
808
|
+
function taskEpisodeScorecard(root, episode, workspace, ts = new Date().toISOString()) {
|
|
809
|
+
const episodeId = episode.episode_id || shortHash(JSON.stringify(episode));
|
|
810
|
+
const reward = Number(episode.reward && episode.reward.value);
|
|
811
|
+
return {
|
|
812
|
+
ts,
|
|
813
|
+
schema: 'atris.brain.task_scorecard.v1',
|
|
814
|
+
type: 'scorecard',
|
|
815
|
+
scorecard_id: `task-scorecard-${episodeId}`,
|
|
816
|
+
source: 'task_review_episode',
|
|
817
|
+
source_episode_id: episodeId,
|
|
818
|
+
workspace: workspace.slug || path.basename(root),
|
|
819
|
+
business_id: workspace.business_id || null,
|
|
820
|
+
workspace_id: workspace.workspace_id || null,
|
|
821
|
+
task_id: episode.task_id || null,
|
|
822
|
+
task_title: episode.state && episode.state.title || null,
|
|
823
|
+
task_tag: episode.state && episode.state.tag || null,
|
|
824
|
+
actor: episode.action && episode.action.actor || null,
|
|
825
|
+
reward: Number.isFinite(reward) ? reward : 0,
|
|
826
|
+
reward_source: episode.reward && episode.reward.source || 'task_review',
|
|
827
|
+
lesson: episode.lesson || '',
|
|
828
|
+
proof: episode.proof || '',
|
|
829
|
+
next_task_suggestion: episode.next_task_suggestion || null,
|
|
830
|
+
episode_created_at: episode.created_at || episode.ts || null,
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function latestTaskEpisodes(taskEpisodes) {
|
|
835
|
+
const byTask = new Map();
|
|
836
|
+
for (const episode of taskEpisodes) {
|
|
837
|
+
const episodeId = episode.episode_id || shortHash(JSON.stringify(episode));
|
|
838
|
+
const key = episode.task_id || episodeId;
|
|
839
|
+
byTask.set(key, { ...episode, episode_id: episodeId });
|
|
840
|
+
}
|
|
841
|
+
return Array.from(byTask.values());
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function recordTaskEpisodeScorecards(options) {
|
|
845
|
+
const root = options.root;
|
|
846
|
+
const stateDir = path.join(root, '.atris', 'state');
|
|
847
|
+
const taskEpisodesPath = path.join(stateDir, 'task_episodes.jsonl');
|
|
848
|
+
const scorecardsPath = path.join(stateDir, 'scorecards.jsonl');
|
|
849
|
+
const workspace = readJson(path.join(root, '.atris', 'business.json')) || {};
|
|
850
|
+
const taskEpisodes = readJsonlRows(taskEpisodesPath)
|
|
851
|
+
.filter(row => row && row.schema === 'atris.task_episode.v1');
|
|
852
|
+
const scoreableEpisodes = latestTaskEpisodes(taskEpisodes);
|
|
853
|
+
const existing = readJsonlRows(scorecardsPath);
|
|
854
|
+
const seenEpisodeIds = new Set(existing
|
|
855
|
+
.map(row => row.source_episode_id)
|
|
856
|
+
.filter(Boolean));
|
|
857
|
+
|
|
858
|
+
const written = [];
|
|
859
|
+
for (const episode of scoreableEpisodes) {
|
|
860
|
+
const episodeId = episode.episode_id;
|
|
861
|
+
if (seenEpisodeIds.has(episodeId)) continue;
|
|
862
|
+
const scorecard = taskEpisodeScorecard(root, episode, workspace);
|
|
863
|
+
appendJsonl(scorecardsPath, scorecard);
|
|
864
|
+
seenEpisodeIds.add(episodeId);
|
|
865
|
+
written.push(scorecard);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
return {
|
|
869
|
+
taskEpisodes: taskEpisodes.length,
|
|
870
|
+
written: written.length,
|
|
871
|
+
scorecards: written,
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
function verifyTaskEpisodeScorecards(root) {
|
|
876
|
+
const stateDir = path.join(root, '.atris', 'state');
|
|
877
|
+
const taskEpisodes = readJsonlRows(path.join(stateDir, 'task_episodes.jsonl'))
|
|
878
|
+
.filter(row => row && row.schema === 'atris.task_episode.v1');
|
|
879
|
+
const scoreableEpisodes = latestTaskEpisodes(taskEpisodes);
|
|
880
|
+
const scorecards = readJsonlRows(path.join(stateDir, 'scorecards.jsonl'));
|
|
881
|
+
const scorecardEpisodeIds = new Set(scorecards
|
|
882
|
+
.map(row => row.source_episode_id)
|
|
883
|
+
.filter(Boolean));
|
|
884
|
+
const missing = scoreableEpisodes
|
|
885
|
+
.map(row => row.episode_id)
|
|
886
|
+
.filter(id => !scorecardEpisodeIds.has(id));
|
|
887
|
+
if (missing.length > 0) {
|
|
888
|
+
throw new Error(`task episode scorecards missing: ${missing.join(', ')}`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
502
892
|
function verifyFeedback(root, decisionId) {
|
|
503
893
|
const scorecards = readText(path.join(root, '.atris', 'state', 'scorecards.jsonl'));
|
|
504
894
|
const episodes = readText(path.join(root, '.atris', 'state', 'episodes.jsonl'));
|
|
@@ -514,6 +904,25 @@ function verifyApproval(root, approvalId) {
|
|
|
514
904
|
}
|
|
515
905
|
}
|
|
516
906
|
|
|
907
|
+
function brainLoadOrderFiles(state) {
|
|
908
|
+
const root = state.root;
|
|
909
|
+
const existing = OPTIONAL_LOAD_ORDER_FILES
|
|
910
|
+
.filter(rel => fs.existsSync(path.join(root, rel)));
|
|
911
|
+
return [...GENERATED_LOAD_ORDER_FILES, ...existing];
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function renderNumberedLoadOrder(state) {
|
|
915
|
+
return brainLoadOrderFiles(state)
|
|
916
|
+
.map((rel, index) => `${index + 1}. \`${rel}\``)
|
|
917
|
+
.join('\n');
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function renderBulletedLoadOrder(state) {
|
|
921
|
+
return brainLoadOrderFiles(state)
|
|
922
|
+
.map(rel => `- \`${rel}\``)
|
|
923
|
+
.join('\n');
|
|
924
|
+
}
|
|
925
|
+
|
|
517
926
|
function renderStatus(state) {
|
|
518
927
|
return `# Atris Brain Status
|
|
519
928
|
|
|
@@ -549,16 +958,9 @@ ${nextMove(state)}
|
|
|
549
958
|
|
|
550
959
|
## Load Order For Future Agents
|
|
551
960
|
|
|
552
|
-
|
|
553
|
-
2. \`atris/brain/STATUS.md\`
|
|
554
|
-
3. \`atris/brain/self_improvement_ledger.md\`
|
|
555
|
-
4. \`atris/wiki/concepts/sync-language.md\`
|
|
556
|
-
5. \`atris/skills/activation/SKILL.md\`
|
|
557
|
-
6. \`atris/MAP.md\`
|
|
558
|
-
7. \`atris/TODO.md\`
|
|
559
|
-
8. \`atris/wiki/index.md\`
|
|
961
|
+
${renderNumberedLoadOrder(state)}
|
|
560
962
|
|
|
561
|
-
First-message rule:
|
|
963
|
+
First-message rule: lead with the move before writing to the operator.
|
|
562
964
|
Purpose: optimize for decision-speed; lead with the move, then use descriptions only when they help the operator act.
|
|
563
965
|
Shape: \`<operator>, today is about <move>\` -> \`I picked this because <why now>\` -> \`Ready: <draft/proof/context>\` -> \`Go deeper: <paths>\`.
|
|
564
966
|
Definitions: operator = current person or agent; move = one concrete high-leverage workflow; why now = business reason; ready = prepared action or proof; paths = 2-4 optional deeper views.
|
|
@@ -619,15 +1021,9 @@ On session start, activate it first:
|
|
|
619
1021
|
\`atris brain activate --root ${state.root} --verify\`
|
|
620
1022
|
|
|
621
1023
|
Load these first:
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
-
|
|
625
|
-
- \`atris/wiki/concepts/sync-language.md\`
|
|
626
|
-
- \`atris/skills/activation/SKILL.md\`
|
|
627
|
-
- \`atris/MAP.md\`
|
|
628
|
-
- \`atris/TODO.md\`
|
|
629
|
-
|
|
630
|
-
First-message rule: follow the sync-language contract before writing to the operator.
|
|
1024
|
+
${renderBulletedLoadOrder(state)}
|
|
1025
|
+
|
|
1026
|
+
First-message rule: lead with the move before writing to the operator.
|
|
631
1027
|
Purpose: optimize for decision-speed; lead with the move, then use descriptions only when they help the operator act.
|
|
632
1028
|
Shape: \`<operator>, today is about <move>\` -> \`I picked this because <why now>\` -> \`Ready: <draft/proof/context>\` -> \`Go deeper: <paths>\`.
|
|
633
1029
|
Definitions: operator = current person or agent; move = one concrete high-leverage workflow; why now = business reason; ready = prepared action or proof; paths = 2-4 optional deeper views.
|
|
@@ -707,16 +1103,27 @@ function verifyBrain(root) {
|
|
|
707
1103
|
}
|
|
708
1104
|
}
|
|
709
1105
|
|
|
1106
|
+
function brainUsageLines() {
|
|
1107
|
+
return [
|
|
1108
|
+
'Usage: atris brain compile [--root <workspace>] [--verify] [--json]',
|
|
1109
|
+
' atris brain activate [--member <slug>] [--root <workspace>] [--verify] [--json]',
|
|
1110
|
+
' atris brain gallery [--root <workspace>] [--verify] [--json]',
|
|
1111
|
+
' atris brain go|hold [note] [--recommendation <text>] [--root <workspace>] [--verify]',
|
|
1112
|
+
' atris brain approval go|edit|hold [note] [--recommendation <text>] [--root <workspace>] [--verify]',
|
|
1113
|
+
' atris brain scorecard [--root <workspace>] [--verify] [--json]',
|
|
1114
|
+
' atris brain feedback --rating approve|edit|reject [--recommendation <text>] [--note <text>] [--root <workspace>] [--verify]',
|
|
1115
|
+
' atris brain yes|edit|no [note] [--root <workspace>] [--verify]',
|
|
1116
|
+
];
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function printBrainUsage(stream = console.log) {
|
|
1120
|
+
for (const line of brainUsageLines()) stream(line);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
710
1123
|
function brainCommand(args = process.argv.slice(3)) {
|
|
711
1124
|
const { subcommand, options } = parseArgs(args);
|
|
712
|
-
if (subcommand === 'help' || subcommand === '--help') {
|
|
713
|
-
|
|
714
|
-
console.log(' atris brain activate [--member <slug>] [--root <workspace>] [--verify] [--json]');
|
|
715
|
-
console.log(' atris brain gallery [--root <workspace>] [--verify] [--json]');
|
|
716
|
-
console.log(' atris brain go|hold [note] [--recommendation <text>] [--root <workspace>] [--verify]');
|
|
717
|
-
console.log(' atris brain approval go|edit|hold [note] [--recommendation <text>] [--root <workspace>] [--verify]');
|
|
718
|
-
console.log(' atris brain feedback --rating approve|edit|reject [--recommendation <text>] [--note <text>] [--root <workspace>] [--verify]');
|
|
719
|
-
console.log(' atris brain yes|edit|no [note] [--root <workspace>] [--verify]');
|
|
1125
|
+
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h' || args.includes('--help') || args.includes('-h')) {
|
|
1126
|
+
printBrainUsage();
|
|
720
1127
|
return;
|
|
721
1128
|
}
|
|
722
1129
|
if (subcommand === 'yes') {
|
|
@@ -766,48 +1173,112 @@ function brainCommand(args = process.argv.slice(3)) {
|
|
|
766
1173
|
console.log('');
|
|
767
1174
|
return;
|
|
768
1175
|
}
|
|
1176
|
+
|
|
1177
|
+
if (subcommand === 'scorecard' || subcommand === 'scorecards') {
|
|
1178
|
+
const result = recordTaskEpisodeScorecards(options);
|
|
1179
|
+
if (options.verify) verifyTaskEpisodeScorecards(options.root);
|
|
1180
|
+
if (options.json) {
|
|
1181
|
+
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
console.log('');
|
|
1185
|
+
console.log('Atris brain scorecards recorded.');
|
|
1186
|
+
console.log(` Task episodes: ${result.taskEpisodes}`);
|
|
1187
|
+
console.log(` New scorecards: ${result.written}`);
|
|
1188
|
+
console.log(' Wrote: .atris/state/scorecards.jsonl');
|
|
1189
|
+
console.log(' Next: atris brain compile');
|
|
1190
|
+
if (options.verify) console.log(' Verify: passed');
|
|
1191
|
+
console.log('');
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
769
1195
|
if (subcommand === 'activate') {
|
|
770
|
-
const state =
|
|
1196
|
+
const state = prepareBrainState(options.root);
|
|
771
1197
|
writeBrain(state);
|
|
772
1198
|
if (options.verify) verifyBrain(options.root);
|
|
773
1199
|
const card = renderActivationCard(state, options);
|
|
774
1200
|
if (options.json) {
|
|
1201
|
+
if (options.verify) {
|
|
1202
|
+
try {
|
|
1203
|
+
verifyActivationCard(card);
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
console.log(JSON.stringify({
|
|
1206
|
+
ok: false,
|
|
1207
|
+
error: error && error.message ? error.message : String(error),
|
|
1208
|
+
state,
|
|
1209
|
+
card,
|
|
1210
|
+
}, null, 2));
|
|
1211
|
+
process.exit(1);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
775
1214
|
console.log(JSON.stringify({ ok: true, state, card }, null, 2));
|
|
776
1215
|
return;
|
|
777
1216
|
}
|
|
778
1217
|
console.log('');
|
|
779
1218
|
console.log(card);
|
|
780
|
-
if (options.verify)
|
|
1219
|
+
if (options.verify) {
|
|
1220
|
+
try {
|
|
1221
|
+
verifyActivationCard(card);
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
printVerifyFailure(error);
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const operator = (card.match(/^OPERATOR:\s*(.+)$/m) || [null, 'unknown'])[1];
|
|
1227
|
+
if (operator === 'unknown') {
|
|
1228
|
+
console.log('VERIFY: brain artifacts present');
|
|
1229
|
+
} else {
|
|
1230
|
+
console.log('VERIFY: brain artifacts and member activation executable');
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
781
1233
|
console.log('');
|
|
782
1234
|
return;
|
|
783
1235
|
}
|
|
784
1236
|
if (subcommand === 'gallery') {
|
|
785
|
-
const state =
|
|
1237
|
+
const state = prepareBrainState(options.root);
|
|
786
1238
|
writeBrain(state);
|
|
787
1239
|
if (options.verify) verifyBrain(options.root);
|
|
788
1240
|
const gallery = renderActivationGallery(state);
|
|
1241
|
+
if (options.verify) {
|
|
1242
|
+
try {
|
|
1243
|
+
verifyActivationGallery(gallery);
|
|
1244
|
+
} catch (error) {
|
|
1245
|
+
if (options.json) {
|
|
1246
|
+
console.log(JSON.stringify({
|
|
1247
|
+
ok: false,
|
|
1248
|
+
error: error && error.message ? error.message : String(error),
|
|
1249
|
+
members: listMemberSlugs(options.root),
|
|
1250
|
+
gallery,
|
|
1251
|
+
}, null, 2));
|
|
1252
|
+
process.exit(1);
|
|
1253
|
+
}
|
|
1254
|
+
printVerifyFailure(error);
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
789
1258
|
if (options.json) {
|
|
790
1259
|
console.log(JSON.stringify({ ok: true, members: listMemberSlugs(options.root), gallery }, null, 2));
|
|
791
1260
|
return;
|
|
792
1261
|
}
|
|
793
1262
|
console.log('');
|
|
794
1263
|
console.log(gallery);
|
|
795
|
-
if (options.verify) console.log('\nVERIFY: brain artifacts present');
|
|
1264
|
+
if (options.verify) console.log('\nVERIFY: brain artifacts and member readiness present');
|
|
796
1265
|
console.log('');
|
|
797
1266
|
return;
|
|
798
1267
|
}
|
|
799
1268
|
if (subcommand !== 'compile' && subcommand !== 'status') {
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
1269
|
+
if (options.json) {
|
|
1270
|
+
console.log(JSON.stringify({
|
|
1271
|
+
ok: false,
|
|
1272
|
+
error: `unknown brain subcommand: ${subcommand}`,
|
|
1273
|
+
usage: brainUsageLines(),
|
|
1274
|
+
}, null, 2));
|
|
1275
|
+
process.exit(1);
|
|
1276
|
+
}
|
|
1277
|
+
printBrainUsage(console.error);
|
|
807
1278
|
process.exit(1);
|
|
808
1279
|
}
|
|
809
1280
|
|
|
810
|
-
const state =
|
|
1281
|
+
const state = prepareBrainState(options.root);
|
|
811
1282
|
const written = writeBrain(state);
|
|
812
1283
|
if (options.verify) verifyBrain(options.root);
|
|
813
1284
|
|
|
@@ -830,11 +1301,15 @@ function brainCommand(args = process.argv.slice(3)) {
|
|
|
830
1301
|
module.exports = {
|
|
831
1302
|
brainCommand,
|
|
832
1303
|
collectState,
|
|
1304
|
+
prepareBrainState,
|
|
833
1305
|
renderStatus,
|
|
834
1306
|
renderLedger,
|
|
835
1307
|
renderActivationCard,
|
|
836
1308
|
renderActivationGallery,
|
|
1309
|
+
recordTaskEpisodeScorecards,
|
|
837
1310
|
recordFeedback,
|
|
838
1311
|
recordApproval,
|
|
1312
|
+
verifyActivationCard,
|
|
1313
|
+
verifyActivationGallery,
|
|
839
1314
|
verifyBrain,
|
|
840
1315
|
};
|