atris 3.15.46 → 3.15.49

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/commands/sync.js CHANGED
@@ -172,6 +172,85 @@ function ensureWorkspaceStateFiles(targetRoot, params, options = {}) {
172
172
  return created;
173
173
  }
174
174
 
175
+ function renderBusinessAgentAdapter(bizMeta = {}, targetRoot = '.') {
176
+ const name = bizMeta.name || bizMeta.slug || 'this business';
177
+ const slug = bizMeta.slug || 'business';
178
+ const rootHint = targetRoot || '.';
179
+ return [
180
+ `# AGENTS.md - ${name} Atris Workspace`,
181
+ '',
182
+ `You are operating inside the shared Atris workspace for ${name} (${slug}).`,
183
+ '',
184
+ '## Start Here',
185
+ '',
186
+ 'Run these first from the workspace root:',
187
+ '',
188
+ '```bash',
189
+ 'atris',
190
+ 'atris business start',
191
+ 'atris radar',
192
+ 'atris task next',
193
+ 'atris member activate operator',
194
+ '```',
195
+ '',
196
+ 'If no active mission exists, start the first bounded business loop:',
197
+ '',
198
+ '```bash',
199
+ 'atris mission status --status active --json',
200
+ `atris mission start "Run the first useful loop for ${name}" --owner operator --runner codex_goal --lane business --verify "atris business check" --stop "first proof recap recorded"`,
201
+ 'atris member goal-from-mission operator',
202
+ 'atris do',
203
+ '```',
204
+ '',
205
+ '## Core Files',
206
+ '',
207
+ '| File | Purpose |',
208
+ '|------|---------|',
209
+ '| `atris/atris.md` | Workspace boot protocol |',
210
+ '| `atris/MAP.md` | Navigation and where-is-X index |',
211
+ '| `.atris/state/tasks.projection.json` | Current task projection |',
212
+ '| `atris/TODO.md` | Rendered task fallback only |',
213
+ '| `atris/team/START_HERE.md` | Team lanes and first-run flow |',
214
+ '| `atris/wiki/` | Business context and source-backed briefs |',
215
+ '| `atris/reports/` | Proof recaps and share handoffs |',
216
+ '',
217
+ '## Rules',
218
+ '',
219
+ '- Check `atris/MAP.md` before broad code or file search.',
220
+ '- Use `atris task` for ownership, notes, proof, and review state.',
221
+ '- Use `atris mission` when work should survive the current chat.',
222
+ '- Put completed agent work in Review with `atris task ready <id> --proof "<receipt>".`',
223
+ '- Do not run `atris task accept` or claim XP unless a human approved the proof.',
224
+ '- Do not mix another business into this workspace.',
225
+ '- No external sends, spend, or launches without operator approval.',
226
+ '',
227
+ '## Proof Loop',
228
+ '',
229
+ '```bash',
230
+ 'atris business check',
231
+ 'atris business record atris/reports/<recap>.md --outcome mixed --metric "operator speed"',
232
+ 'atris business share --write',
233
+ '```',
234
+ '',
235
+ `Workspace root at creation: ${rootHint}`,
236
+ '',
237
+ ].join('\n');
238
+ }
239
+
240
+ function ensureBusinessRootAgentAdapters(targetRoot, bizMeta = {}, options = {}) {
241
+ const dryRun = options.dryRun === true;
242
+ const written = [];
243
+ const adapter = renderBusinessAgentAdapter(bizMeta, targetRoot);
244
+ const files = ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md'];
245
+ for (const file of files) {
246
+ const fullPath = path.join(targetRoot, file);
247
+ if (fs.existsSync(fullPath)) continue;
248
+ written.push(file);
249
+ if (!dryRun) fs.writeFileSync(fullPath, adapter, 'utf8');
250
+ }
251
+ return written;
252
+ }
253
+
175
254
  /**
176
255
  * Sync canonical business template files into a business workspace.
177
256
  * Used when .atris/business.json is present (business mode).
@@ -243,6 +322,7 @@ function syncWorkspaceTemplate(targetRoot, bizMeta, options = {}) {
243
322
  }
244
323
 
245
324
  const stateAddedList = ensureWorkspaceStateFiles(targetRoot, params, { dryRun });
325
+ const agentAdapterList = ensureBusinessRootAgentAdapters(targetRoot, params, { dryRun });
246
326
 
247
327
  // Skills: sync the canonical skill set from atris-cli package into the
248
328
  // customer workspace. Business-starter template ships skill infra (README,
@@ -277,16 +357,34 @@ function syncWorkspaceTemplate(targetRoot, bizMeta, options = {}) {
277
357
  stateAddedList.forEach(p => console.log(` + ${p}`));
278
358
  console.log('');
279
359
  }
360
+ if (agentAdapterList.length > 0) {
361
+ console.log(' Root agent adapters:');
362
+ agentAdapterList.forEach(p => console.log(` + ${p}`));
363
+ console.log('');
364
+ }
280
365
 
281
366
  if (dryRun) {
282
367
  console.log(' (--dry-run, no changes made)');
283
- } else if (added === 0 && updated === 0 && stateAddedList.length === 0) {
368
+ } else if (added === 0 && updated === 0 && stateAddedList.length === 0 && agentAdapterList.length === 0) {
284
369
  ensureWikiScaffold(targetRoot);
285
370
  console.log(' ✓ Already up to date');
286
371
  } else {
287
372
  ensureWikiScaffold(targetRoot);
288
373
  console.log(` ✓ Local workspace updated. Run \`atris align ${params.slug} --fix\` to push to EC2.`);
289
374
  }
375
+
376
+ return {
377
+ added,
378
+ updated,
379
+ preserved,
380
+ skipped,
381
+ skillsUpdated,
382
+ addedList,
383
+ updatedList,
384
+ preservedList,
385
+ stateAddedList,
386
+ agentAdapterList,
387
+ };
290
388
  }
291
389
 
292
390
  function syncBusinessCanonical(targetRoot, bizMeta, options = {}) {
@@ -862,4 +960,6 @@ module.exports = {
862
960
  syncWorkspaceTemplate,
863
961
  resolveWorkspaceTemplate,
864
962
  ensureWorkspaceStateFiles,
963
+ renderBusinessAgentAdapter,
964
+ ensureBusinessRootAgentAdapters,
865
965
  };
package/commands/task.js CHANGED
@@ -79,6 +79,7 @@ atris task - durable local task state (SQLite, gitignored)
79
79
  atris task done <id> [--failed] [--proof "..."] Mark complete (or failed), optionally reviewed
80
80
  atris task finish <id> [--proof "..."] Legacy alias for done
81
81
  atris task review <id> --reward <n> Write review event + RSI episode
82
+ atris task reviews [--limit <n>] Show certified Review items for human accept/revise
82
83
  atris task status [--json] [--history] Compact live status for web/Swarlo
83
84
  atris task setup [--import-todo] Create/refresh task projection
84
85
  atris task serve [--port <n>] Open local task factory board
@@ -878,6 +879,86 @@ function taskStatusSummary(projection, { history = false } = {}) {
878
879
  return status;
879
880
  }
880
881
 
882
+ function reviewQueueLimit(args, total) {
883
+ if (hasFlag(args, '--all')) return total;
884
+ const raw = flag(args, '--limit');
885
+ const limit = raw && raw !== true ? Number(raw) : 12;
886
+ return Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 12;
887
+ }
888
+
889
+ function reviewQueueItem(task) {
890
+ const ref = taskRef(task);
891
+ return {
892
+ id: task.id,
893
+ display_id: task.display_id || null,
894
+ title: task.title,
895
+ tag: task.tag || null,
896
+ updated_at: task.updated_at || null,
897
+ review_pass_count: task.review?.agent_review_pass_count || null,
898
+ proof: task.review?.proof || null,
899
+ accept_command: `atris task accept ${ref}`,
900
+ revise_command: `atris task revise ${ref} --note "<what must change>"`,
901
+ };
902
+ }
903
+
904
+ function taskReviewQueue(projection, args = []) {
905
+ const reviewTasks = (projection.tasks || [])
906
+ .map(compactTaskForStatus)
907
+ .filter(task => task && task.status === 'review' && task.review && task.review.approval_status === 'pending')
908
+ .sort((a, b) => Number(b.updated_at || 0) - Number(a.updated_at || 0));
909
+ const blocking = reviewTasks.filter(task => task.review?.handoff?.next_action === 'agent_review_again');
910
+ const certified = reviewTasks.filter(task => task.review?.handoff?.next_action === 'continue_work' || task.review?.agent_certified === true);
911
+ const limit = reviewQueueLimit(args, certified.length);
912
+ const items = certified.slice(0, limit).map(reviewQueueItem);
913
+ return {
914
+ schema: 'atris.task_review_queue.v1',
915
+ generated_at: projection.generated_at,
916
+ workspace_root: projection.workspace_root,
917
+ counts: {
918
+ review: reviewTasks.length,
919
+ certified: certified.length,
920
+ blocking: blocking.length,
921
+ shown: items.length,
922
+ },
923
+ items,
924
+ };
925
+ }
926
+
927
+ function cmdReviews(args) {
928
+ const taskDb = getTaskDb();
929
+ const db = taskDb.open();
930
+ const { projection, outPath } = writeDefaultProjection(taskDb, db);
931
+ const queue = taskReviewQueue(projection, args);
932
+ if (wantsJson(args)) {
933
+ printJson({
934
+ ok: true,
935
+ action: 'review_queue',
936
+ projection_path: outPath,
937
+ queue,
938
+ });
939
+ return;
940
+ }
941
+ console.log('CERTIFIED REVIEW QUEUE');
942
+ console.log(`${queue.counts.certified} certified / ${queue.counts.blocking} need agent review / ${queue.counts.review} total review`);
943
+ if (!queue.items.length) {
944
+ console.log('No certified review items.');
945
+ return;
946
+ }
947
+ queue.items.forEach((item, index) => {
948
+ const tag = item.tag ? ` [${item.tag}]` : '';
949
+ const passes = item.review_pass_count ? ` (${item.review_pass_count} reviews)` : '';
950
+ console.log('');
951
+ console.log(`${index + 1}. ${item.display_id || taskRef(item.id)}${tag}${passes}: ${item.title}`);
952
+ if (item.proof) console.log(` proof: ${item.proof}`);
953
+ console.log(` accept: ${item.accept_command}`);
954
+ console.log(` revise: ${item.revise_command}`);
955
+ });
956
+ if (queue.counts.shown < queue.counts.certified) {
957
+ console.log('');
958
+ console.log(`Showing ${queue.counts.shown}/${queue.counts.certified}; rerun with --all or --limit ${queue.counts.certified}.`);
959
+ }
960
+ }
961
+
881
962
  function humanEventType(type) {
882
963
  return String(type || 'event').replace(/_/g, ' ');
883
964
  }
@@ -2743,6 +2824,9 @@ async function run(args) {
2743
2824
  case 'finish': return cmdFinish(rest);
2744
2825
  case 'fail': return cmdDone([...rest, '--failed']);
2745
2826
  case 'review': return cmdReview(rest);
2827
+ case 'reviews':
2828
+ case 'review-queue':
2829
+ return cmdReviews(rest);
2746
2830
  case 'status': return cmdStatus(rest);
2747
2831
  case 'setup': return cmdSetup(rest);
2748
2832
  case 'serve': return cmdServe(rest);
package/lib/task-db.js CHANGED
@@ -567,17 +567,38 @@ function reviewTask(db, { id, actor, reward, lesson, nextTask, proof, careerXpEl
567
567
  const row = getTask(db, id);
568
568
  if (!row) return { reviewed: false, reason: 'not_found' };
569
569
  const numericReward = Number.isFinite(Number(reward)) ? Number(reward) : 0;
570
+ const now = Date.now();
571
+ const reviewer = actor || process.env.ATRIS_AGENT_ID || process.env.USER || null;
570
572
  const metadata = row.metadata && typeof row.metadata === 'object' ? { ...row.metadata } : {};
573
+ const reviewingPendingProof = row.status === 'review'
574
+ && metadata.approval_status === 'pending'
575
+ && numericReward <= 0
576
+ && metadata.agent_certified !== true;
577
+ let reviewPassCount = Number(metadata.agent_review_pass_count || 0);
578
+ if (reviewingPendingProof) {
579
+ reviewPassCount += 1;
580
+ metadata.agent_review_pass_count = reviewPassCount;
581
+ metadata.agent_reviewed_at = new Date(now).toISOString();
582
+ metadata.agent_reviewed_by = reviewer;
583
+ if (reviewPassCount >= AGENT_CERTIFICATION_REVIEW_PASSES) {
584
+ metadata.agent_certified = true;
585
+ metadata.agent_certified_at = new Date(now).toISOString();
586
+ metadata.agent_certified_by = reviewer;
587
+ metadata.agent_certification_policy = `${AGENT_CERTIFICATION_REVIEW_PASSES}_agent_review_passes`;
588
+ }
589
+ }
571
590
  if (numericReward > 0 && row.status === 'done') {
572
591
  metadata.approval_status = 'accepted';
573
592
  metadata.accepted_at = new Date().toISOString();
574
- metadata.accepted_by = actor || process.env.ATRIS_AGENT_ID || process.env.USER || null;
593
+ metadata.accepted_by = reviewer;
594
+ }
595
+ if (reviewingPendingProof || (numericReward > 0 && row.status === 'done')) {
575
596
  withBusyRetry(() => db.prepare(`
576
597
  UPDATE tasks
577
598
  SET metadata = ?,
578
599
  updated_at = ?
579
600
  WHERE id = ?
580
- `).run(JSON.stringify(metadata), Date.now(), id));
601
+ `).run(JSON.stringify(metadata), now, id));
581
602
  }
582
603
  const payload = {
583
604
  reward: numericReward,
@@ -586,6 +607,11 @@ function reviewTask(db, { id, actor, reward, lesson, nextTask, proof, careerXpEl
586
607
  proof: String(proof || '').trim() || null,
587
608
  career_xp_eligible: Boolean(careerXpEligible),
588
609
  };
610
+ if (reviewingPendingProof) {
611
+ payload.review_pass_count = reviewPassCount;
612
+ payload.agent_certified = metadata.agent_certified === true;
613
+ payload.agent_certification_policy = metadata.agent_certification_policy || null;
614
+ }
589
615
  const clearedReviewFields = Array.isArray(clearedFields)
590
616
  ? Array.from(new Set(clearedFields.filter(field => field === 'lesson' || field === 'next_task')))
591
617
  : [];
@@ -593,7 +619,7 @@ function reviewTask(db, { id, actor, reward, lesson, nextTask, proof, careerXpEl
593
619
  const event = appendTaskEvent(db, {
594
620
  taskId: id,
595
621
  workspaceRoot: row.workspace_root,
596
- actor: actor || null,
622
+ actor: reviewer,
597
623
  eventType: 'reviewed',
598
624
  payload,
599
625
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.15.46",
3
+ "version": "3.15.49",
4
4
  "main": "bin/atris.js",
5
5
  "bin": {
6
6
  "atris": "bin/atris.js",
@@ -13,6 +13,7 @@
13
13
  "commands/",
14
14
  "utils/",
15
15
  "lib/",
16
+ "templates/",
16
17
  "README.md",
17
18
  "AGENTS.md",
18
19
  "atris.md",
@@ -56,6 +56,7 @@ Keep owner metadata in `.atris/business.json`; keep computer memory, files, tool
56
56
  | Path | What |
57
57
  |------|------|
58
58
  | `atris/team/` | Role lenses inside the shared business environment |
59
+ | `atris/team/START_HERE.md` | Collaborator guide for which member lane to wake first |
59
60
  | `atris/team/_template/MEMBER.md` | Starter member template for real humans or new lanes |
60
61
  | `atris/team/ops/MEMBER.md` | Default operating lane |
61
62
  | `atris/team/operator/MEMBER.md` | Default owner for the business computer |
@@ -5,6 +5,9 @@ Anything durable and structured belongs under `atris/`.
5
5
 
6
6
  ## Role Lenses
7
7
 
8
+ Start with `atris/team/START_HERE.md`.
9
+ It explains which lane to wake first and when proof needs validator review.
10
+
8
11
  Create lanes that match the real business workflow.
9
12
  Examples: intake, scheduling, reactivation, revops, content, partnerships, support.
10
13
 
@@ -0,0 +1,45 @@
1
+ # Team Start Here — {{name}}
2
+
3
+ Use this when a collaborator opens the shared business workspace for the first time.
4
+
5
+ ## Default Path
6
+
7
+ 1. Run `atris business start` from the workspace root.
8
+ 2. Run `atris radar` to see agents, tasks, missions, XP, team lanes, and proof state.
9
+ 3. Claim the seeded first task with `atris task next`.
10
+ 4. Wake `operator` with `atris member activate operator`.
11
+ 5. Resume an active mission, or start the first bounded loop if none exists.
12
+ 6. Execute the loop with `atris do`.
13
+ 7. Use `ops`, `research`, or `comms` only when the next loop needs that lens.
14
+ 8. Ask `validator` to check proof before reward, launch, external send, or spend.
15
+
16
+ ## Mission Loop
17
+
18
+ ```bash
19
+ atris mission status --status active --json
20
+ # If no active mission exists:
21
+ atris mission start "Run the first useful loop for this business" --owner operator --runner codex_goal --lane business --verify "atris business check" --stop "first proof recap recorded"
22
+ atris member goal-from-mission operator
23
+ atris do
24
+ ```
25
+
26
+ ## Lanes
27
+
28
+ | Lane | Use when |
29
+ |------|----------|
30
+ | `operator` | Someone needs to pick and drive the next concrete action |
31
+ | `validator` | A result needs proof, cost-safety, or external-readiness review |
32
+ | `ops` | The work is blocked on owner, date, status, or a crisp next step |
33
+ | `research` | The workspace lacks sourced facts for the next decision |
34
+ | `comms` | The next artifact is a message, update, reminder, or draft |
35
+
36
+ ## Proof Rule
37
+
38
+ Record the first real run with:
39
+
40
+ ```bash
41
+ atris business record atris/reports/<recap>.md --outcome mixed --metric "operator speed"
42
+ atris business share --write
43
+ ```
44
+
45
+ No XP or external action until a human approves the proof.