atris 3.15.52 → 3.15.53
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/atris/features/README.md +10 -7
- package/commands/computer.js +19 -3
- package/commands/mission.js +9 -3
- package/commands/radar.js +48 -2
- package/commands/worktree.js +20 -1
- package/package.json +1 -1
package/atris/features/README.md
CHANGED
|
@@ -79,17 +79,20 @@ cp atris/features/_templates/validate.md.template atris/features/your-feature-na
|
|
|
79
79
|
|
|
80
80
|
### Active Features
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
Close remaining audit gaps from self-audit
|
|
84
|
-
- **Files:** atris/team/*.md, atris/features/README.md
|
|
85
|
-
- **Status:** complete
|
|
86
|
-
- **Keywords:** audit, persona, cleanup
|
|
87
|
-
- **What:** Add PERSONA.md reference to all 5 agent specs, clean up stale feature statuses
|
|
82
|
+
None.
|
|
88
83
|
|
|
89
84
|
---
|
|
90
85
|
|
|
91
86
|
### Completed Features
|
|
92
87
|
|
|
88
|
+
#### audit-gaps
|
|
89
|
+
Close remaining audit gaps from self-audit
|
|
90
|
+
- **Files:** atris/team/*/MEMBER.md, atris/features/README.md, atris/features/audit-gaps/*
|
|
91
|
+
- **Status:** complete
|
|
92
|
+
- **Keywords:** audit, persona, cleanup
|
|
93
|
+
- **What:** Agent member specs reference PERSONA.md for communication style, and stale feature statuses are cleaned up
|
|
94
|
+
- **Completed:** 2026-05-19
|
|
95
|
+
|
|
93
96
|
#### endstate
|
|
94
97
|
Public benchmark for proving a coordinated stack beats a pinned single-model baseline
|
|
95
98
|
- **Files:** atris/features/endstate/*, commands/autopilot.js, commands/experiments.js, commands/loop.js, lib/wiki.js
|
|
@@ -116,7 +119,7 @@ Local-first project wiki with cloud opt-in
|
|
|
116
119
|
|
|
117
120
|
#### self-improving-loop
|
|
118
121
|
Make Atris recursive — validate.md lessons feed back into the next idea.md
|
|
119
|
-
- **Files:** atris/lessons.md (new), atris.md, atris/team/navigator.md, atris/team/validator.md, atris/MAP.md
|
|
122
|
+
- **Files:** atris/lessons.md (new), atris.md, atris/team/navigator/MEMBER.md, atris/team/validator/MEMBER.md, atris/MAP.md
|
|
120
123
|
- **Status:** complete
|
|
121
124
|
- **Keywords:** recursion, lessons, feedback-loop, self-improving, lessons.md
|
|
122
125
|
- **What:** lessons.md accumulates validated learnings; navigator reads them before planning; validator harvests them after validating
|
package/commands/computer.js
CHANGED
|
@@ -1260,6 +1260,18 @@ function formatWorkspaceRef(workspace) {
|
|
|
1260
1260
|
return workspace.name ? `${workspace.name} (${workspace.id})` : workspace.id;
|
|
1261
1261
|
}
|
|
1262
1262
|
|
|
1263
|
+
function workspaceMatchesInput(workspace, input) {
|
|
1264
|
+
if (!workspace || !input) return false;
|
|
1265
|
+
const wanted = String(input).trim().toLowerCase();
|
|
1266
|
+
if (!wanted) return false;
|
|
1267
|
+
return String(workspace.id || '').toLowerCase() === wanted
|
|
1268
|
+
|| String(workspace.name || '').toLowerCase() === wanted;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
function resolveWorkspaceFromList(workspaces, input) {
|
|
1272
|
+
return (workspaces || []).find((workspace) => workspaceMatchesInput(workspace, input)) || null;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1263
1275
|
function formatLeaseAge(seconds) {
|
|
1264
1276
|
const value = Number(seconds);
|
|
1265
1277
|
if (!Number.isFinite(value) || value < 0) return '-';
|
|
@@ -1519,7 +1531,11 @@ async function computerStatus(token, ctx = null) {
|
|
|
1519
1531
|
if (d.endpoint) console.log(` Endpoint: ${d.endpoint}`);
|
|
1520
1532
|
const workspaces = await listBusinessWorkspaces(token, ctx);
|
|
1521
1533
|
const defaultWorkspace = workspaces.find((workspace) => workspace.is_default);
|
|
1522
|
-
const
|
|
1534
|
+
const resolvedTargetWorkspace = resolveWorkspaceFromList(workspaces, ctx.workspaceId);
|
|
1535
|
+
const targetWorkspace = resolvedTargetWorkspace || (ctx.workspaceId ? { id: ctx.workspaceId } : null);
|
|
1536
|
+
const probeCtx = resolvedTargetWorkspace?.id
|
|
1537
|
+
? { ...ctx, workspaceId: resolvedTargetWorkspace.id }
|
|
1538
|
+
: ctx;
|
|
1523
1539
|
console.log(` Default workspace: ${formatWorkspaceRef(defaultWorkspace)}`);
|
|
1524
1540
|
console.log(` Target workspace: ${formatWorkspaceRef(targetWorkspace)}`);
|
|
1525
1541
|
const attachedFromStatus = d.attached_workspace_id
|
|
@@ -1534,8 +1550,8 @@ async function computerStatus(token, ctx = null) {
|
|
|
1534
1550
|
console.log(` Lease age: ${formatLeaseAge(d.lease_age_seconds)}`);
|
|
1535
1551
|
if (d.takeover_hint) console.log(` Takeover hint: ${d.takeover_hint}`);
|
|
1536
1552
|
}
|
|
1537
|
-
if (status === 'running' && d.endpoint &&
|
|
1538
|
-
const attached = await probeAttachedWorkspace(token,
|
|
1553
|
+
if (status === 'running' && d.endpoint && probeCtx.workspaceId) {
|
|
1554
|
+
const attached = await probeAttachedWorkspace(token, probeCtx);
|
|
1539
1555
|
if (!attachedFromStatus) {
|
|
1540
1556
|
const attachedWorkspace = workspaces.find((workspace) => workspace.id === attached.workspaceId) || (attached.workspaceId ? { id: attached.workspaceId } : null);
|
|
1541
1557
|
console.log(` Attached workspace: ${formatWorkspaceRef(attachedWorkspace)}`);
|
package/commands/mission.js
CHANGED
|
@@ -748,12 +748,18 @@ function missionVerifierPassed(mission) {
|
|
|
748
748
|
|
|
749
749
|
function missionDueAt(mission, now = new Date()) {
|
|
750
750
|
const cadenceSeconds = parseCadenceSeconds(mission.cadence);
|
|
751
|
-
if (!mission.last_tick_at
|
|
751
|
+
if (!mission.last_tick_at) return true;
|
|
752
|
+
if (cadenceSeconds === 0) return !(mission.always_on && missionVerifierPassed(mission));
|
|
752
753
|
const lastTickAt = Date.parse(mission.last_tick_at);
|
|
753
754
|
if (!Number.isFinite(lastTickAt)) return true;
|
|
754
755
|
return now.getTime() - lastTickAt >= cadenceSeconds * 1000;
|
|
755
756
|
}
|
|
756
757
|
|
|
758
|
+
function missionSelectableForLoop(mission, now = new Date()) {
|
|
759
|
+
return missionIsRunnable(mission)
|
|
760
|
+
&& !(mission.always_on && missionVerifierPassed(mission) && !missionDueAt(mission, now));
|
|
761
|
+
}
|
|
762
|
+
|
|
757
763
|
function secondsUntilMissionDue(mission, now = new Date()) {
|
|
758
764
|
const cadenceSeconds = parseCadenceSeconds(mission?.cadence);
|
|
759
765
|
if (!mission || !mission.last_tick_at || cadenceSeconds === 0) return 0;
|
|
@@ -780,7 +786,7 @@ function missionSortTime(mission) {
|
|
|
780
786
|
|
|
781
787
|
function selectDueMission(root = process.cwd(), now = new Date()) {
|
|
782
788
|
const candidates = listMissions(root)
|
|
783
|
-
.filter(
|
|
789
|
+
.filter((mission) => missionSelectableForLoop(mission, now))
|
|
784
790
|
.filter((mission) => mission.verifier)
|
|
785
791
|
.filter((mission) => mission.always_on || !missionVerifierPassed(mission))
|
|
786
792
|
.filter((mission) => missionDueAt(mission, now));
|
|
@@ -799,7 +805,7 @@ function selectDueMission(root = process.cwd(), now = new Date()) {
|
|
|
799
805
|
|
|
800
806
|
function selectCodexGoalMission(root = process.cwd(), now = new Date()) {
|
|
801
807
|
const candidates = listMissions(root)
|
|
802
|
-
.filter(
|
|
808
|
+
.filter((mission) => missionSelectableForLoop(mission, now));
|
|
803
809
|
|
|
804
810
|
candidates.sort((a, b) => {
|
|
805
811
|
const aCaller = runnerUsesCallerSession(a.runner) ? 1 : 0;
|
package/commands/radar.js
CHANGED
|
@@ -432,6 +432,31 @@ function ownerForTask(task) {
|
|
|
432
432
|
return task.assigned_to || task.claimed_by || task.metadata?.assigned_to || '-';
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
function taskSessionReason(task) {
|
|
436
|
+
if (!task) return null;
|
|
437
|
+
if (task.status === 'review') return task.metadata?.agent_certified ? 'certified review' : 'review task';
|
|
438
|
+
if (ownerActionRequired(task)) return 'owner action required';
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function taskSessionAction(agent, task, taskWorkspaceRoot) {
|
|
443
|
+
if (!task) return null;
|
|
444
|
+
const ref = taskRef(task);
|
|
445
|
+
const pid = agent?.pid || '?';
|
|
446
|
+
const actor = agent?.agent || 'agent';
|
|
447
|
+
const reviewCommand = taskWorkspaceRoot
|
|
448
|
+
? `cd ${shellQuote(taskWorkspaceRoot)} && atris task reviews --limit 5`
|
|
449
|
+
: 'atris task reviews --limit 5';
|
|
450
|
+
if (task.status === 'review') {
|
|
451
|
+
if (task.metadata?.agent_certified) {
|
|
452
|
+
return `handoff complete for ${ref}; close pid ${pid} or claim fresh work as ${actor}`;
|
|
453
|
+
}
|
|
454
|
+
return `review or hand off ${ref}: ${reviewCommand}`;
|
|
455
|
+
}
|
|
456
|
+
if (ownerActionRequired(task)) return `owner-gated ${ref}; close pid ${pid} or wait for owner action`;
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
|
|
435
460
|
function summarize(tasks, missions, worktrees, agents) {
|
|
436
461
|
const count = (rows, pred) => rows.filter(pred).length;
|
|
437
462
|
return {
|
|
@@ -505,7 +530,7 @@ function collectRadar(options = {}) {
|
|
|
505
530
|
const taskWorkspaceRoot = findTaskWorkspaceRoot(agent.cwd, deps);
|
|
506
531
|
const agentTasks = taskWorkspaceRoot ? loadTasksCached(taskWorkspaceRoot, deps, taskCache) : [];
|
|
507
532
|
const task = taskForCwd(agentTasks, agent.cwd, taskWorkspaceRoot);
|
|
508
|
-
const taskReason = task ?
|
|
533
|
+
const taskReason = task ? taskSessionReason(task) : untaskedReason(agent, taskWorkspaceRoot, agentTasks);
|
|
509
534
|
return {
|
|
510
535
|
...agent,
|
|
511
536
|
task: taskRef(task),
|
|
@@ -513,7 +538,7 @@ function collectRadar(options = {}) {
|
|
|
513
538
|
owner: ownerForTask(task),
|
|
514
539
|
task_workspace: taskWorkspaceRoot ? repoLabel(taskWorkspaceRoot) : null,
|
|
515
540
|
task_reason: taskReason,
|
|
516
|
-
task_action: task ?
|
|
541
|
+
task_action: task ? taskSessionAction(agent, task, taskWorkspaceRoot) : untaskedAction(agent, taskWorkspaceRoot, agentTasks),
|
|
517
542
|
};
|
|
518
543
|
});
|
|
519
544
|
const osState = {
|
|
@@ -624,6 +649,11 @@ function sortedAgents(agents = []) {
|
|
|
624
649
|
function agentProcessNextAction(agents = [], fallback = 'no obvious process action') {
|
|
625
650
|
const stopped = agents.filter(agent => agent.status !== 'active').length;
|
|
626
651
|
if (stopped > 0) return `inspect ${stopped} stopped agent session${stopped === 1 ? '' : 's'}`;
|
|
652
|
+
const ownerGated = agents.filter(agent => agent.task_reason === 'owner action required');
|
|
653
|
+
if (ownerGated.length > 0) {
|
|
654
|
+
const tasks = [...new Set(ownerGated.map(agent => agent.task).filter(Boolean))].slice(0, 3).join(', ');
|
|
655
|
+
return `owner gate blocks ${ownerGated.length} session${ownerGated.length === 1 ? '' : 's'}${tasks ? ` on ${tasks}` : ''}; wait for owner action or close idle sessions`;
|
|
656
|
+
}
|
|
627
657
|
const taskLoad = summarizeTaskLoad(agents);
|
|
628
658
|
const reviewBound = taskLoad.find(row => row.status.split(/,\s*/).includes('review'));
|
|
629
659
|
if (reviewBound) return `close or hand off ${reviewBound.sessions} session${reviewBound.sessions === 1 ? '' : 's'} still bound to review task ${reviewBound.task}`;
|
|
@@ -744,6 +774,22 @@ function renderAgentTop(data) {
|
|
|
744
774
|
lines.push(`- ${agent.pid} ${agent.repo || agent.cwd || '-'}: ${agent.task_reason || 'unmapped'} -> ${agent.task_action || 'inspect session'}`);
|
|
745
775
|
}
|
|
746
776
|
}
|
|
777
|
+
const ownerGatedAgents = payload.agents.filter(row => row.task_reason === 'owner action required').slice(0, 8);
|
|
778
|
+
if (ownerGatedAgents.length) {
|
|
779
|
+
lines.push('');
|
|
780
|
+
lines.push(`Owner-gated: ${ownerGatedAgents.length} session${ownerGatedAgents.length === 1 ? '' : 's'} waiting on owner-only tasks.`);
|
|
781
|
+
for (const agent of ownerGatedAgents) {
|
|
782
|
+
lines.push(`- ${agent.pid} ${agent.repo || agent.cwd || '-'} ${agent.task}: ${agent.task_action || 'wait for owner action'}`);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
const reviewBoundAgents = payload.agents.filter(row => row.task_status === 'review').slice(0, 8);
|
|
786
|
+
if (reviewBoundAgents.length) {
|
|
787
|
+
lines.push('');
|
|
788
|
+
lines.push(`Review-bound: ${reviewBoundAgents.length} session${reviewBoundAgents.length === 1 ? '' : 's'} still tied to Review tasks.`);
|
|
789
|
+
for (const agent of reviewBoundAgents) {
|
|
790
|
+
lines.push(`- ${agent.pid} ${agent.repo || agent.cwd || '-'} ${agent.task}: ${agent.task_reason || 'review'} -> ${agent.task_action || 'close or hand off session'}`);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
747
793
|
const taskLoadRows = payload.task_load.filter(row => row.attention).slice(0, 8);
|
|
748
794
|
if (taskLoadRows.length) {
|
|
749
795
|
lines.push('');
|
package/commands/worktree.js
CHANGED
|
@@ -285,6 +285,12 @@ function createOrFindPr(root, branch, targetRef, title, dryRun) {
|
|
|
285
285
|
return created.stdout.trim();
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
function prMergeRef(prOutput) {
|
|
289
|
+
const text = String(prOutput || '').trim();
|
|
290
|
+
if (!text || text.startsWith('dry-run:')) return '';
|
|
291
|
+
return text.split(/\s+/)[0];
|
|
292
|
+
}
|
|
293
|
+
|
|
288
294
|
function shipWorktree(args) {
|
|
289
295
|
const root = repoRoot();
|
|
290
296
|
const dryRun = hasFlag(args, '--dry-run');
|
|
@@ -359,8 +365,20 @@ function shipWorktree(args) {
|
|
|
359
365
|
if (merge) {
|
|
360
366
|
console.log('merge: requested');
|
|
361
367
|
if (!dryRun) {
|
|
362
|
-
const
|
|
368
|
+
const mergeRef = prMergeRef(pr);
|
|
369
|
+
const mergeArgs = ['pr', 'merge'];
|
|
370
|
+
if (mergeRef) mergeArgs.push(mergeRef);
|
|
371
|
+
mergeArgs.push('--merge');
|
|
372
|
+
const merged = spawnSync('gh', mergeArgs, { cwd: root, encoding: 'utf8' });
|
|
363
373
|
if (merged.status !== 0) throw new Error((merged.stderr || merged.stdout || 'gh pr merge failed').trim());
|
|
374
|
+
console.log('merge: merged');
|
|
375
|
+
const deleted = runGit(['push', 'origin', '--delete', branch], { cwd: root, check: false });
|
|
376
|
+
if (deleted.status === 0) {
|
|
377
|
+
console.log(`merge: remote branch deleted ${branch}`);
|
|
378
|
+
} else {
|
|
379
|
+
const deleteOutput = (deleted.stderr || deleted.stdout || 'remote branch delete failed').trim();
|
|
380
|
+
console.log(`merge: remote branch delete skipped: ${deleteOutput}`);
|
|
381
|
+
}
|
|
364
382
|
}
|
|
365
383
|
}
|
|
366
384
|
} else {
|
|
@@ -461,6 +479,7 @@ module.exports = {
|
|
|
461
479
|
defaultWorktreePath,
|
|
462
480
|
parseWorktrees,
|
|
463
481
|
normalizeTargetRef,
|
|
482
|
+
prMergeRef,
|
|
464
483
|
slugify,
|
|
465
484
|
statusCounts,
|
|
466
485
|
swarloClaim,
|