atris 3.53.0 → 3.55.0
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/FOR_AGENTS.md +7 -0
- package/README.md +1 -1
- package/atris/policies/ANTISLOP.md +2 -1
- package/atris/skills/copy-editor/SKILL.md +2 -1
- package/atris/skills/design/SKILL.md +1 -1
- package/atris/skills/x-search/SKILL.md +24 -22
- package/atris/skills/youtube/SKILL.md +82 -33
- package/atris/team/_template/SOUL.md +40 -0
- package/atris/team/validator/MEMBER.md +1 -2
- package/bin/atris +21 -0
- package/bin/atris.js +563 -373
- package/commands/api-key.js +170 -0
- package/commands/auth.js +453 -43
- package/commands/autoland.js +3 -1
- package/commands/autopilot-front.js +58 -11
- package/commands/avail.js +5 -4
- package/commands/balance.js +55 -0
- package/commands/bench.js +29 -1
- package/commands/brain.js +63 -12
- package/commands/brainstorm.js +18 -0
- package/commands/business.js +149 -1
- package/commands/chat-scan.js +17 -4
- package/commands/close.js +5 -1
- package/commands/doctor.js +174 -0
- package/commands/dream.js +7 -0
- package/commands/drill.js +34 -2
- package/commands/engine.js +47 -35
- package/commands/errors.js +58 -8
- package/commands/experiments.js +14 -0
- package/commands/feedback.js +61 -2
- package/commands/fleet-report.js +27 -7
- package/commands/fleet.js +98 -11
- package/commands/founder.js +60 -21
- package/commands/human-missions.js +79 -4
- package/commands/improve.js +54 -4
- package/commands/init.js +68 -92
- package/commands/install.js +40 -0
- package/commands/integrations.js +399 -30
- package/commands/interview.js +8 -0
- package/commands/land.js +48 -17
- package/commands/learn.js +29 -4
- package/commands/log.js +51 -0
- package/commands/member.js +68 -10
- package/commands/mission.js +287 -50
- package/commands/now.js +15 -3
- package/commands/orb.js +97 -2
- package/commands/pack.js +83 -9
- package/commands/playbook.js +381 -0
- package/commands/plugin.js +16 -0
- package/commands/radar.js +31 -7
- package/commands/recap.js +128 -21
- package/commands/release.js +102 -2
- package/commands/review.js +5 -10
- package/commands/revisions.js +55 -0
- package/commands/run-front.js +12 -4
- package/commands/scout.js +12 -1
- package/commands/search.js +123 -28
- package/commands/sign.js +2 -2
- package/commands/signup.js +12 -3
- package/commands/site-deploy.js +17 -4
- package/commands/site.js +2 -2
- package/commands/skill-eval.js +289 -0
- package/commands/skill.js +56 -6
- package/commands/social.js +603 -0
- package/commands/spaceship.js +69 -6
- package/commands/stream.js +8 -1
- package/commands/study.js +32 -19
- package/commands/sync.js +28 -22
- package/commands/task.js +418 -60
- package/commands/teach.js +72 -63
- package/commands/terminal.js +25 -9
- package/commands/topup.js +88 -0
- package/commands/truth.js +52 -6
- package/commands/usage.js +91 -0
- package/commands/verify.js +3 -2
- package/commands/version.js +3 -1
- package/commands/voice.js +5 -1
- package/commands/who.js +69 -11
- package/commands/wiki.js +7 -4
- package/commands/wish.js +76 -5
- package/commands/workflow.js +327 -145
- package/commands/worktree.js +1 -0
- package/commands/write.js +5 -0
- package/commands/x-search.js +340 -0
- package/commands/xp.js +5 -1
- package/commands/youtube.js +551 -25
- package/lib/account-bound.js +47 -0
- package/lib/auto-accept-certified.js +8 -1
- package/lib/auto-lessons.js +186 -0
- package/lib/autoland.js +4 -2
- package/lib/chat-log-scan.js +7 -4
- package/lib/cli-json.js +77 -0
- package/lib/cli-scope.js +26 -0
- package/lib/codex-flight.js +1 -0
- package/lib/conductor-artifacts.js +1 -1
- package/lib/context-gatherer.js +11 -0
- package/lib/developer-api.js +116 -0
- package/lib/feedback-triage.js +274 -0
- package/lib/first-minute.js +398 -0
- package/lib/fleet.js +115 -34
- package/lib/functional-owner.js +22 -0
- package/lib/known-commands.js +31 -5
- package/lib/member-scaffold.js +197 -0
- package/lib/mission-root.js +4 -2
- package/lib/noninteractive.js +88 -0
- package/lib/policy-lessons.js +4 -1
- package/lib/revision-metric.js +279 -0
- package/lib/scratch-root.js +48 -0
- package/lib/skill-eval-gate.js +249 -0
- package/lib/task-db.js +4 -0
- package/lib/task-proof.js +69 -8
- package/lib/verifier-quality.js +69 -0
- package/lib/wish-audit.js +2 -2
- package/lib/wish-delegate.js +10 -1
- package/lib/workspace-scaffold.js +270 -0
- package/package.json +3 -2
- package/scripts/det/checklist-score.js +191 -0
- package/scripts/det/ytsearch +31 -0
- package/utils/update-check.js +15 -0
package/commands/mission.js
CHANGED
|
@@ -6,6 +6,11 @@ const crypto = require('crypto');
|
|
|
6
6
|
const readline = require('readline');
|
|
7
7
|
const { spawn, spawnSync } = require('child_process');
|
|
8
8
|
const { hasFlag, readFlag, readIntFlag } = require('../lib/arg-parser');
|
|
9
|
+
const {
|
|
10
|
+
compactErrorPayload,
|
|
11
|
+
compactSuccessPayload,
|
|
12
|
+
printCliJson,
|
|
13
|
+
} = require('../lib/cli-json');
|
|
9
14
|
const escapeRegExp = require('../lib/escape-regexp');
|
|
10
15
|
const {
|
|
11
16
|
appendBriefRecord,
|
|
@@ -83,6 +88,7 @@ const {
|
|
|
83
88
|
resolveDefaultVerifier,
|
|
84
89
|
} = require('../lib/default-verifier');
|
|
85
90
|
const { resolveWorkspaceRoot, redirectToWorkspaceRoot } = require('../lib/mission-root');
|
|
91
|
+
const { isFreshWorkspace, speakFirstMinute } = require('../lib/first-minute');
|
|
86
92
|
const { readJson, writeJson } = require('../lib/json-file');
|
|
87
93
|
const {
|
|
88
94
|
normalizeHumanAsks,
|
|
@@ -427,8 +433,22 @@ function applyMissionRunnerProfile(runner) {
|
|
|
427
433
|
}
|
|
428
434
|
|
|
429
435
|
function exitMissionError(message, code = 1, asJson = false) {
|
|
430
|
-
if (asJson)
|
|
431
|
-
|
|
436
|
+
if (asJson) {
|
|
437
|
+
printCliJson(
|
|
438
|
+
{
|
|
439
|
+
ok: false,
|
|
440
|
+
error: message,
|
|
441
|
+
reason: 'mission_error',
|
|
442
|
+
detail: message,
|
|
443
|
+
selected_ref: null,
|
|
444
|
+
next_command: null,
|
|
445
|
+
},
|
|
446
|
+
compactErrorPayload({ reason: 'mission_error', detail: message }),
|
|
447
|
+
process.argv.slice(2),
|
|
448
|
+
);
|
|
449
|
+
} else {
|
|
450
|
+
console.error(message);
|
|
451
|
+
}
|
|
432
452
|
process.exit(code);
|
|
433
453
|
}
|
|
434
454
|
|
|
@@ -518,7 +538,7 @@ function assertMissionVerifier(command, asJson = false) {
|
|
|
518
538
|
|
|
519
539
|
function stripKnownFlags(args, valueNames, booleanNames = []) {
|
|
520
540
|
const valueSet = new Set(valueNames);
|
|
521
|
-
const booleanSet = new Set(booleanNames);
|
|
541
|
+
const booleanSet = new Set(['--full', '--verbose', ...booleanNames]);
|
|
522
542
|
const out = [];
|
|
523
543
|
for (let i = 0; i < args.length; i += 1) {
|
|
524
544
|
const arg = String(args[i]);
|
|
@@ -569,6 +589,8 @@ const MISSION_RUN_VALUE_FLAGS = [
|
|
|
569
589
|
];
|
|
570
590
|
const MISSION_RUN_BOOLEAN_FLAGS = [
|
|
571
591
|
'--json',
|
|
592
|
+
'--full',
|
|
593
|
+
'--verbose',
|
|
572
594
|
'--due',
|
|
573
595
|
'--detach',
|
|
574
596
|
'--headless',
|
|
@@ -727,7 +749,72 @@ function missionXpTaskTitle(objective) {
|
|
|
727
749
|
return `${prefix}${shortClause || 'mission work'}`;
|
|
728
750
|
}
|
|
729
751
|
|
|
752
|
+
function normalizeMissionTaskMatchText(text) {
|
|
753
|
+
return String(text || '')
|
|
754
|
+
.replace(/^Mission XP:\s*/i, '')
|
|
755
|
+
.toLowerCase()
|
|
756
|
+
.replace(/\s+/g, ' ')
|
|
757
|
+
.trim();
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function missionObjectiveTaskMatch(objective, title) {
|
|
761
|
+
const want = normalizeMissionTaskMatchText(objective);
|
|
762
|
+
const have = normalizeMissionTaskMatchText(title);
|
|
763
|
+
if (!want || !have) return false;
|
|
764
|
+
if (want === have) return true;
|
|
765
|
+
// First clause of a long objective often matches the claimed task title.
|
|
766
|
+
const wantClause = want.split(/:|[.!?](?=\s|$)/)[0].trim();
|
|
767
|
+
const haveClause = have.split(/:|[.!?](?=\s|$)/)[0].trim();
|
|
768
|
+
if (wantClause && haveClause && (wantClause === haveClause || want.startsWith(haveClause) || have.startsWith(wantClause))) {
|
|
769
|
+
return true;
|
|
770
|
+
}
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function taskRowToMissionXpBinding(task, ownerResolution, outPath, { inserted = false, reused = true } = {}) {
|
|
775
|
+
return {
|
|
776
|
+
task_id: task.id,
|
|
777
|
+
ref: missionTaskRef(task) || task.id,
|
|
778
|
+
title: task.title,
|
|
779
|
+
operator_title_warning: warnIfTaskTitleNeedsOperatorWhy(task.title),
|
|
780
|
+
status: task.status || 'claimed',
|
|
781
|
+
assigned_to: task.claimed_by || ownerResolution.owner,
|
|
782
|
+
owner_resolution: ownerResolution.reason,
|
|
783
|
+
executed_by: ownerResolution.executed_by ? normalizeOwnerSlug(ownerResolution.executed_by) : null,
|
|
784
|
+
inserted,
|
|
785
|
+
reused,
|
|
786
|
+
projection_path: outPath,
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function findOpenTaskMatchingMissionObjective(mission, root = process.cwd(), asJson = false) {
|
|
791
|
+
const taskDb = loadTaskDb(asJson);
|
|
792
|
+
const db = taskDb.open();
|
|
793
|
+
const workspaceRoot = taskDb.workspaceRoot(root);
|
|
794
|
+
const rows = taskDb.withTaskDisplayRefs(taskDb.listTasks(db, { workspaceRoot }));
|
|
795
|
+
const open = rows.filter((row) => row && ['open', 'claimed', 'review'].includes(row.status));
|
|
796
|
+
const explicitIds = Array.isArray(mission.task_ids) ? mission.task_ids.filter(Boolean) : [];
|
|
797
|
+
for (const id of explicitIds) {
|
|
798
|
+
const hit = open.find((row) => row.id === id || row.display_id === id || missionTaskRef(row) === id);
|
|
799
|
+
if (hit) return hit;
|
|
800
|
+
}
|
|
801
|
+
return open.find((row) => missionObjectiveTaskMatch(mission.objective, row.title)) || null;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function bindExistingMissionTask(mission, task, root = process.cwd(), asJson = false) {
|
|
805
|
+
const taskDb = loadTaskDb(asJson);
|
|
806
|
+
const db = taskDb.open();
|
|
807
|
+
const workspaceRoot = taskDb.workspaceRoot(root);
|
|
808
|
+
const ownerResolution = resolveMissionOwner(mission, workspaceRoot);
|
|
809
|
+
const { outPath } = writeMissionTaskProjection(taskDb, db, workspaceRoot);
|
|
810
|
+
return taskRowToMissionXpBinding(task, ownerResolution, outPath, { inserted: false, reused: true });
|
|
811
|
+
}
|
|
812
|
+
|
|
730
813
|
function createMissionXpTask(mission, root = process.cwd(), asJson = false) {
|
|
814
|
+
const existing = findOpenTaskMatchingMissionObjective(mission, root, asJson);
|
|
815
|
+
if (existing) {
|
|
816
|
+
return bindExistingMissionTask(mission, existing, root, asJson);
|
|
817
|
+
}
|
|
731
818
|
const taskDb = loadTaskDb(asJson);
|
|
732
819
|
const db = taskDb.open();
|
|
733
820
|
const workspaceRoot = taskDb.workspaceRoot(root);
|
|
@@ -782,6 +869,7 @@ function createMissionXpTask(mission, root = process.cwd(), asJson = false) {
|
|
|
782
869
|
owner_resolution: ownerResolution.reason,
|
|
783
870
|
executed_by: ownerResolution.executed_by ? normalizeOwnerSlug(ownerResolution.executed_by) : null,
|
|
784
871
|
inserted: result.inserted !== false,
|
|
872
|
+
reused: false,
|
|
785
873
|
projection_path: outPath,
|
|
786
874
|
};
|
|
787
875
|
}
|
|
@@ -1238,11 +1326,27 @@ function exitMissingMission(ref, code = 1, asJson = false, root = process.cwd())
|
|
|
1238
1326
|
} catch { /* best-effort hint only */ }
|
|
1239
1327
|
}
|
|
1240
1328
|
if (asJson) {
|
|
1241
|
-
const
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1329
|
+
const nextCommand = hints[0] && hints[0].command ? hints[0].command : null;
|
|
1330
|
+
printCliJson(
|
|
1331
|
+
{
|
|
1332
|
+
ok: false,
|
|
1333
|
+
error,
|
|
1334
|
+
reason: 'mission_not_found',
|
|
1335
|
+
detail: error,
|
|
1336
|
+
selected_ref: ref || null,
|
|
1337
|
+
next_command: nextCommand,
|
|
1338
|
+
...(hints.length ? { workspace_hint: hints[0] } : {}),
|
|
1339
|
+
...(hints.length > 1 ? { workspace_hints: hints } : {}),
|
|
1340
|
+
...(candidates.length ? { nearest_candidates: candidates } : {}),
|
|
1341
|
+
},
|
|
1342
|
+
compactErrorPayload({
|
|
1343
|
+
reason: 'mission_not_found',
|
|
1344
|
+
detail: error,
|
|
1345
|
+
selected_ref: ref || null,
|
|
1346
|
+
next_command: nextCommand,
|
|
1347
|
+
}),
|
|
1348
|
+
process.argv.slice(2),
|
|
1349
|
+
);
|
|
1246
1350
|
} else {
|
|
1247
1351
|
console.error(error);
|
|
1248
1352
|
if (hints.length) {
|
|
@@ -1688,8 +1792,26 @@ function verifierTimeoutSeconds(verifierResult) {
|
|
|
1688
1792
|
return Number.isFinite(ms) ? Math.round(ms / 1000) : 0;
|
|
1689
1793
|
}
|
|
1690
1794
|
|
|
1691
|
-
function
|
|
1692
|
-
if (!
|
|
1795
|
+
function missionGuardBlockedText(guard) {
|
|
1796
|
+
if (!guard || guard.allowed !== false) return '';
|
|
1797
|
+
const detail = String(guard.detail || '').trim();
|
|
1798
|
+
const reason = String(guard.reason || 'the check was held').trim();
|
|
1799
|
+
return `VERIFY DID NOT RUN: ${reason}${detail ? ` (${detail})` : ''}. Treat this increment as unproven.`;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
function missionGuardNextText(guard, missionId) {
|
|
1803
|
+
if (!guard || guard.allowed !== false || !missionId) return '';
|
|
1804
|
+
if (guard.unreadable) {
|
|
1805
|
+
return `repair the mission diff tooling failure, then rerun: atris mission tick ${missionId} --verify`;
|
|
1806
|
+
}
|
|
1807
|
+
return `review the protected diff and receipt before resuming: atris mission run ${missionId}`;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
function missionVerifierCheckedText(verifierResult, mission, guard = null) {
|
|
1811
|
+
if (!verifierResult) {
|
|
1812
|
+
return missionGuardBlockedText(guard)
|
|
1813
|
+
|| 'UNVERIFIED: tick recorded but nothing was checked; treat this increment as unproven.';
|
|
1814
|
+
}
|
|
1693
1815
|
if (verifierResult.mode === 'engine-unavailable') {
|
|
1694
1816
|
return 'VERIFY FAILED: the engine verify pass was unavailable; treat this increment as unproven.';
|
|
1695
1817
|
}
|
|
@@ -1715,8 +1837,15 @@ function missionVerifierCheckedText(verifierResult, mission) {
|
|
|
1715
1837
|
return `VERIFY FAILED: ${command}.`;
|
|
1716
1838
|
}
|
|
1717
1839
|
|
|
1718
|
-
function missionVerifierHighLevelTestText(verifierResult, mission) {
|
|
1719
|
-
if (!verifierResult)
|
|
1840
|
+
function missionVerifierHighLevelTestText(verifierResult, mission, guard = null) {
|
|
1841
|
+
if (!verifierResult) {
|
|
1842
|
+
if (guard && guard.allowed === false) {
|
|
1843
|
+
const detail = String(guard.detail || '').trim();
|
|
1844
|
+
const reason = String(guard.reason || 'the check was held').trim();
|
|
1845
|
+
return `No automated verifier ran: ${reason}${detail ? ` (${detail})` : ''}. Clear the hold, then rerun to get a real verdict.`;
|
|
1846
|
+
}
|
|
1847
|
+
return 'No automated verifier ran for this receipt; judge it from the receipt, changed files, and next action.';
|
|
1848
|
+
}
|
|
1720
1849
|
if (verifierResult.mode === 'engine-unavailable') {
|
|
1721
1850
|
return 'No changed-surface commands were confirmed because the engine verify pass was unavailable.';
|
|
1722
1851
|
}
|
|
@@ -1767,11 +1896,12 @@ function missionFallbackChangedText(mission, status, tickIndex, { ranTicks = nul
|
|
|
1767
1896
|
return `${mission.objective} recorded tick ${tickIndex}.`;
|
|
1768
1897
|
}
|
|
1769
1898
|
|
|
1770
|
-
function missionTickResultLines(mission, tickIndex, receiptPath, verifierResult = null, stepSummary = '') {
|
|
1899
|
+
function missionTickResultLines(mission, tickIndex, receiptPath, verifierResult = null, stepSummary = '', guard = null) {
|
|
1771
1900
|
return missionLandingLines(missionReceiptLanding(mission, {
|
|
1772
|
-
tick: { tick_index: tickIndex },
|
|
1901
|
+
tick: { tick_index: tickIndex, protected_lane_guard: guard },
|
|
1773
1902
|
summary: stepSummary,
|
|
1774
1903
|
verifier_result: verifierResult,
|
|
1904
|
+
protected_lane_guard: guard,
|
|
1775
1905
|
}, receiptPath));
|
|
1776
1906
|
}
|
|
1777
1907
|
|
|
@@ -1801,6 +1931,9 @@ function missionReceiptStatus(mission, result) {
|
|
|
1801
1931
|
function missionReceiptNextText(mission, result, receiptPath = '') {
|
|
1802
1932
|
if (result?.next) return String(result.next);
|
|
1803
1933
|
if (result?.landing?.next) return String(result.landing.next);
|
|
1934
|
+
const guard = result?.protected_lane_guard || result?.tick?.protected_lane_guard || null;
|
|
1935
|
+
const guardNext = missionGuardNextText(guard, mission?.id);
|
|
1936
|
+
if (guardNext) return guardNext;
|
|
1804
1937
|
if (result?.verifier_result?.passed === true) {
|
|
1805
1938
|
if (!mission?.always_on && receiptPath) {
|
|
1806
1939
|
return `Review proof, then run: atris mission complete ${mission.id} --proof "${receiptPath}".`;
|
|
@@ -1817,8 +1950,9 @@ function missionReceiptLanding(mission, result, receiptPath = '') {
|
|
|
1817
1950
|
const summary = missionReceiptSummaryText(result);
|
|
1818
1951
|
const changed = missionLandingStepSummary(summary)
|
|
1819
1952
|
|| missionFallbackChangedText(mission, status, missionReceiptTickIndex(mission, result));
|
|
1820
|
-
const
|
|
1821
|
-
const
|
|
1953
|
+
const guard = result?.protected_lane_guard || result?.tick?.protected_lane_guard || null;
|
|
1954
|
+
const checked = missionVerifierCheckedText(verifierResult, mission, guard);
|
|
1955
|
+
const tested = missionVerifierHighLevelTestText(verifierResult, mission, guard);
|
|
1822
1956
|
const reason = missionHumanReasonText(mission, changed);
|
|
1823
1957
|
return {
|
|
1824
1958
|
schema: 'atris.result_landing.v1',
|
|
@@ -2373,20 +2507,25 @@ function missionRunSummaryLines(mission, ranTicks, effectiveMaxTicks, finalRecei
|
|
|
2373
2507
|
const changed = missionRunChangedText(mission, ranTicks, effectiveMaxTicks, ticks, createdNext);
|
|
2374
2508
|
const reason = missionHumanReasonText(mission, changed);
|
|
2375
2509
|
const lastTick = ticks[ticks.length - 1] || null;
|
|
2510
|
+
const guard = lastTick?.protected_lane_guard || null;
|
|
2376
2511
|
const verifier = lastTick && lastTick.verifier_passed == null ? null : mission.verifier_result;
|
|
2377
2512
|
const checked = verifier
|
|
2378
|
-
? missionVerifierCheckedText(verifier, mission)
|
|
2379
|
-
:
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2513
|
+
? missionVerifierCheckedText(verifier, mission, guard)
|
|
2514
|
+
: missionGuardBlockedText(guard)
|
|
2515
|
+
|| (pauseReason === 'no-progress'
|
|
2516
|
+
? `Run stopped: ${mission.stop_reason || 'no progress across consecutive ticks'}.`
|
|
2517
|
+
: pauseReason
|
|
2518
|
+
? `Run paused: ${pauseReason}.`
|
|
2519
|
+
: 'UNVERIFIED: run recorded but nothing was checked; treat this increment as unproven.');
|
|
2384
2520
|
const tested = verifier
|
|
2385
|
-
? missionVerifierHighLevelTestText(verifier, mission)
|
|
2386
|
-
:
|
|
2387
|
-
?
|
|
2388
|
-
:
|
|
2389
|
-
|
|
2521
|
+
? missionVerifierHighLevelTestText(verifier, mission, guard)
|
|
2522
|
+
: (guard && guard.allowed === false)
|
|
2523
|
+
? missionVerifierHighLevelTestText(null, mission, guard)
|
|
2524
|
+
: mission.verifier
|
|
2525
|
+
? `Verifier was configured but not completed: ${mission.verifier}.`
|
|
2526
|
+
: 'No verifier command was recorded for this mission.';
|
|
2527
|
+
const nextLine = missionGuardNextText(guard, mission?.id)
|
|
2528
|
+
|| missionRunCreatedNextLine(createdNext, continuationGoal, mission);
|
|
2390
2529
|
const lines = [
|
|
2391
2530
|
'Landing:',
|
|
2392
2531
|
` Changed: ${changed}`,
|
|
@@ -2660,6 +2799,7 @@ function missionFromArgs(args) {
|
|
|
2660
2799
|
...(ownerResolution.requested_owner && ownerResolution.requested_owner !== owner ? { requested_owner: ownerResolution.requested_owner } : {}),
|
|
2661
2800
|
...(ownerResolution.executed_by ? { executed_by: normalizeOwnerSlug(ownerResolution.executed_by) } : {}),
|
|
2662
2801
|
...(ownerResolution.proposed_member ? { proposed_member: ownerResolution.proposed_member } : {}),
|
|
2802
|
+
...(ownerResolution.remap_reason ? { remap_reason: ownerResolution.remap_reason } : {}),
|
|
2663
2803
|
status: 'planning',
|
|
2664
2804
|
cadence,
|
|
2665
2805
|
runner,
|
|
@@ -3971,9 +4111,23 @@ function startMission(args, options = {}) {
|
|
|
3971
4111
|
const xpTask = createMissionXpTask(mission, process.cwd(), asJson);
|
|
3972
4112
|
mission.xp_task = xpTask;
|
|
3973
4113
|
mission.task_ids = Array.from(new Set([...(mission.task_ids || []), xpTask.task_id]));
|
|
4114
|
+
mission.current_task_id = xpTask.task_id;
|
|
4115
|
+
mission.task_ref = xpTask.ref;
|
|
3974
4116
|
if (!mission.verifier && !mission.always_on) {
|
|
3975
4117
|
mission.next_action = `work task then run: atris task current-step --goal-id ${mission.id} --as ${mission.owner} --proof "<proof>" --json`;
|
|
3976
4118
|
}
|
|
4119
|
+
} else if (!(mission.task_ids && mission.task_ids.length)) {
|
|
4120
|
+
// Bind a live open/claimed task with the same objective instead of leaving
|
|
4121
|
+
// task_ids empty and forcing attach-task to spawn a Mission XP twin later.
|
|
4122
|
+
const existing = findOpenTaskMatchingMissionObjective(mission, process.cwd(), asJson);
|
|
4123
|
+
if (existing) {
|
|
4124
|
+
const bound = bindExistingMissionTask(mission, existing, process.cwd(), asJson);
|
|
4125
|
+
mission.task_ids = [bound.task_id];
|
|
4126
|
+
mission.current_task_id = bound.task_id;
|
|
4127
|
+
mission.task_ref = bound.ref;
|
|
4128
|
+
mission.xp_task = bound;
|
|
4129
|
+
mission.xp_task_enabled = true;
|
|
4130
|
+
}
|
|
3977
4131
|
}
|
|
3978
4132
|
if (typeof options.beforeMissionSave === 'function') {
|
|
3979
4133
|
mission = options.beforeMissionSave(mission) || mission;
|
|
@@ -4008,6 +4162,7 @@ function startMission(args, options = {}) {
|
|
|
4008
4162
|
dirty_count: worktreeBaseline.dirty_count,
|
|
4009
4163
|
dirty_hash: worktreeBaseline.dirty_hash,
|
|
4010
4164
|
} : null,
|
|
4165
|
+
...(saved.remap_reason ? { remap_reason: saved.remap_reason } : {}),
|
|
4011
4166
|
};
|
|
4012
4167
|
if (!options.silent) {
|
|
4013
4168
|
printJsonOrText(
|
|
@@ -4016,6 +4171,7 @@ function startMission(args, options = {}) {
|
|
|
4016
4171
|
`Started mission: ${saved.objective}`,
|
|
4017
4172
|
`Owner: ${saved.owner}`,
|
|
4018
4173
|
`State: ${saved.status}`,
|
|
4174
|
+
...(saved.remap_reason ? [`Remap: ${saved.remap_reason}`] : []),
|
|
4019
4175
|
...(saved.worktree ? [`Worktree: ${saved.worktree.path}`, `Branch: ${saved.worktree.branch}`] : []),
|
|
4020
4176
|
...warnings.map((warning) => `Warning: ${warning.message}`),
|
|
4021
4177
|
...(saved.xp_task ? [`AgentXP task: ${saved.xp_task.ref}`] : []),
|
|
@@ -4136,6 +4292,8 @@ async function startMissionFromRunObjective(objective, args) {
|
|
|
4136
4292
|
const xpTask = createMissionXpTask(mission, process.cwd(), asJson);
|
|
4137
4293
|
mission.xp_task = xpTask;
|
|
4138
4294
|
mission.task_ids = Array.from(new Set([...(mission.task_ids || []), xpTask.task_id]));
|
|
4295
|
+
mission.current_task_id = xpTask.task_id;
|
|
4296
|
+
mission.task_ref = xpTask.ref;
|
|
4139
4297
|
}
|
|
4140
4298
|
if (landRun) {
|
|
4141
4299
|
const repoPath = path.resolve(readFlag(args, '--repo', process.cwd()));
|
|
@@ -4262,14 +4420,16 @@ async function startMissionFromRunObjective(objective, args) {
|
|
|
4262
4420
|
function attachMissionTask(args) {
|
|
4263
4421
|
const asJson = wantsJson(args);
|
|
4264
4422
|
if (hasFlag(args, '--help') || hasFlag(args, '-h') || String(args[0] || '').trim() === 'help') {
|
|
4265
|
-
console.log('Usage: atris mission attach-task <id> [--json]');
|
|
4423
|
+
console.log('Usage: atris mission attach-task <id> [--task <task-id>] [--json]');
|
|
4424
|
+
console.log('Binds an open task with the same objective when one exists; pass --task to choose explicitly.');
|
|
4266
4425
|
console.log('Run `atris mission --help` for the full option list.');
|
|
4267
4426
|
process.exit(0);
|
|
4268
4427
|
}
|
|
4269
|
-
const ref = stripKnownFlags(args, [], ['--json'])[0] || '';
|
|
4428
|
+
const ref = stripKnownFlags(args, ['--task'], ['--json'])[0] || '';
|
|
4270
4429
|
if (!ref) {
|
|
4271
|
-
exitMissionError('Usage: atris mission attach-task <id> [--json]', 1, asJson);
|
|
4430
|
+
exitMissionError('Usage: atris mission attach-task <id> [--task <task-id>] [--json]', 1, asJson);
|
|
4272
4431
|
}
|
|
4432
|
+
const explicitTaskIds = readRepeatedFlag(args, '--task');
|
|
4273
4433
|
let mission = resolveMission(ref);
|
|
4274
4434
|
if (!mission) {
|
|
4275
4435
|
exitMissingMission(ref, 1, asJson);
|
|
@@ -4287,6 +4447,13 @@ function attachMissionTask(args) {
|
|
|
4287
4447
|
exitMissionError(`Mission "${ref}" is ${mission.status}; task spines attach only to active missions.`, 2, asJson);
|
|
4288
4448
|
}
|
|
4289
4449
|
|
|
4450
|
+
if (explicitTaskIds.length) {
|
|
4451
|
+
mission = {
|
|
4452
|
+
...mission,
|
|
4453
|
+
task_ids: Array.from(new Set([...(mission.task_ids || []), ...explicitTaskIds])),
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4290
4457
|
const existingSpine = missionTaskSpine(mission);
|
|
4291
4458
|
if (existingSpine?.has_task) {
|
|
4292
4459
|
let currentMission = mission;
|
|
@@ -4362,6 +4529,8 @@ function attachMissionTask(args) {
|
|
|
4362
4529
|
xp_task_enabled: true,
|
|
4363
4530
|
xp_task: xpTask,
|
|
4364
4531
|
task_ids: Array.from(new Set([...(baseMission.task_ids || []), xpTask.task_id])),
|
|
4532
|
+
current_task_id: xpTask.task_id,
|
|
4533
|
+
task_ref: xpTask.ref,
|
|
4365
4534
|
};
|
|
4366
4535
|
if (nextMission.status === 'ready' && nextMission.receipt_path) {
|
|
4367
4536
|
nextMission.next_action = missionXpReadyAction(nextMission, nextMission.receipt_path) || nextMission.next_action;
|
|
@@ -4373,17 +4542,17 @@ function attachMissionTask(args) {
|
|
|
4373
4542
|
nextMission.next_action = `work task then run: atris task current-step --goal-id ${nextMission.id} --as ${nextMission.owner} --proof "<proof>" --json`;
|
|
4374
4543
|
}
|
|
4375
4544
|
|
|
4376
|
-
const { mission: saved } = saveMission(nextMission, process.cwd(), 'mission_task_spine_attached', { task_id: xpTask.task_id, task_ref: xpTask.ref });
|
|
4545
|
+
const { mission: saved } = saveMission(nextMission, process.cwd(), 'mission_task_spine_attached', { task_id: xpTask.task_id, task_ref: xpTask.ref, reused: xpTask.reused === true });
|
|
4377
4546
|
const memberState = renderMemberMissionState(saved.owner);
|
|
4378
|
-
const logPath = appendMemberLog(saved.owner, 'Mission task spine attached', {
|
|
4547
|
+
const logPath = appendMemberLog(saved.owner, xpTask.reused ? 'Mission task spine bound to existing task' : 'Mission task spine attached', {
|
|
4379
4548
|
mission: saved.objective,
|
|
4380
4549
|
task: xpTask.ref,
|
|
4381
4550
|
});
|
|
4382
4551
|
const view = missionStatusView(saved);
|
|
4383
4552
|
printJsonOrText(
|
|
4384
|
-
{ ok: true, action: 'mission_task_spine_attached', mission: view, task: xpTask, task_spine: view.task_spine, member_state: memberState, log_path: logPath },
|
|
4553
|
+
{ ok: true, action: xpTask.reused ? 'mission_task_spine_bound' : 'mission_task_spine_attached', mission: view, task: xpTask, task_spine: view.task_spine, member_state: memberState, log_path: logPath },
|
|
4385
4554
|
[
|
|
4386
|
-
`Attached task spine: ${saved.objective}`,
|
|
4555
|
+
xpTask.reused ? `Bound existing task spine: ${saved.objective}` : `Attached task spine: ${saved.objective}`,
|
|
4387
4556
|
`Task: ${xpTask.ref}`,
|
|
4388
4557
|
`Next: ${view.task_spine.current_step_command}`,
|
|
4389
4558
|
],
|
|
@@ -5188,6 +5357,14 @@ function missionReportFor(mission, root = process.cwd()) {
|
|
|
5188
5357
|
? 'The last verifier passed; full-budget work is continuing.'
|
|
5189
5358
|
: mission.operator_outcome
|
|
5190
5359
|
|| (verifierPassed ? 'Verifier passed.' : mission.status === 'complete' ? 'Mission is complete.' : mission.status === 'blocked' ? 'Mission is blocked.' : 'Mission is still in progress.');
|
|
5360
|
+
// Keep summary aligned with outcome: never claim a Codex handoff when the
|
|
5361
|
+
// same report also says no receipt / no live worker.
|
|
5362
|
+
let workerSummary = missionWorkerSummary(mission, receipt);
|
|
5363
|
+
if (awaitingFirstWorker || noLiveWorker) {
|
|
5364
|
+
workerSummary = awaitingFirstWorker
|
|
5365
|
+
? 'Waiting for the first worker check-in; no receipt yet.'
|
|
5366
|
+
: 'No worker receipt yet.';
|
|
5367
|
+
}
|
|
5191
5368
|
return {
|
|
5192
5369
|
id: mission.id,
|
|
5193
5370
|
objective: mission.objective,
|
|
@@ -5201,7 +5378,7 @@ function missionReportFor(mission, root = process.cwd()) {
|
|
|
5201
5378
|
operator_outcome: operatorOutcome,
|
|
5202
5379
|
verification: verificationDebt,
|
|
5203
5380
|
worker: mission.worker || missionWorkerLabel(mission),
|
|
5204
|
-
worker_summary:
|
|
5381
|
+
worker_summary: workerSummary,
|
|
5205
5382
|
timeline,
|
|
5206
5383
|
worker_receipt_path: workerReceiptPath,
|
|
5207
5384
|
verifier_receipt_path: verifierReceiptPath,
|
|
@@ -9528,6 +9705,7 @@ async function executeMissionRunTicksPhase(context) {
|
|
|
9528
9705
|
tick: tickRecord,
|
|
9529
9706
|
frozen,
|
|
9530
9707
|
verifier_result: verifierResult,
|
|
9708
|
+
protected_lane_guard: result.protected_lane_guard || null,
|
|
9531
9709
|
rate_limit_info: lastRateLimit,
|
|
9532
9710
|
worktree: tickWorktree,
|
|
9533
9711
|
});
|
|
@@ -9585,7 +9763,7 @@ async function executeMissionRunTicksPhase(context) {
|
|
|
9585
9763
|
last_tick_index: tickIdx,
|
|
9586
9764
|
last_tick_layer: result.layer,
|
|
9587
9765
|
last_tick_layer_source: result.layer_source,
|
|
9588
|
-
verifier_result: verifierResult || latestOnDisk.verifier_result || null,
|
|
9766
|
+
verifier_result: verifierResult || (verifyEach && result.protected_lane_guard && result.protected_lane_guard.allowed === false ? null : latestOnDisk.verifier_result) || null,
|
|
9589
9767
|
last_check_feedback: verifierResult
|
|
9590
9768
|
? extractCheckFeedback(verifierResult)
|
|
9591
9769
|
: latestOnDisk.last_check_feedback || '',
|
|
@@ -10096,9 +10274,7 @@ function tickMission(args) {
|
|
|
10096
10274
|
const protectedLaneGuard = tickRecord.protected_lane_guard || { allowed: true };
|
|
10097
10275
|
if (!protectedLaneGuard.allowed) {
|
|
10098
10276
|
status = 'paused';
|
|
10099
|
-
nextAction = protectedLaneGuard.
|
|
10100
|
-
? `repair the mission diff tooling failure, then rerun: atris mission run ${mission.id}`
|
|
10101
|
-
: `review the protected diff and receipt before resuming: atris mission run ${mission.id}`;
|
|
10277
|
+
nextAction = missionGuardNextText(protectedLaneGuard, mission.id);
|
|
10102
10278
|
} else if (verifierResult?.passed && nextGoalChain && !nextGoalChain.pause_ready) {
|
|
10103
10279
|
status = 'running';
|
|
10104
10280
|
nextAction = missionGoalChainNextAction(nextGoalChain);
|
|
@@ -10136,7 +10312,7 @@ function tickMission(args) {
|
|
|
10136
10312
|
last_tick_index: tickIdx,
|
|
10137
10313
|
last_tick_layer: tickRecord.layer,
|
|
10138
10314
|
last_tick_layer_source: tickRecord.layer_source,
|
|
10139
|
-
verifier_result: verifierResult || mission.verifier_result || null,
|
|
10315
|
+
verifier_result: verifierResult || (verify && !protectedLaneGuard.allowed ? null : mission.verifier_result) || null,
|
|
10140
10316
|
last_check_feedback: verifierResult
|
|
10141
10317
|
? extractCheckFeedback(verifierResult)
|
|
10142
10318
|
: mission.last_check_feedback || '',
|
|
@@ -10169,16 +10345,64 @@ function tickMission(args) {
|
|
|
10169
10345
|
const outputMission = continuationGoal?.parent || saved;
|
|
10170
10346
|
const atrisGoalState = refreshAtrisGoalController(process.cwd(), { missionId: outputMission.id });
|
|
10171
10347
|
const codexGoalState = refreshCodexGoalController(process.cwd());
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10348
|
+
const verifyToolingFailed = Boolean(verify && !verifierResult && protectedLaneGuard.unreadable);
|
|
10349
|
+
const skipDetail = missionGuardBlockedText(protectedLaneGuard)
|
|
10350
|
+
|| 'VERIFY DID NOT RUN: the check could not start. Treat this increment as unproven.';
|
|
10351
|
+
const fullTick = {
|
|
10352
|
+
ok: !verifyToolingFailed,
|
|
10353
|
+
action: 'mission_tick',
|
|
10354
|
+
mission: outputMission,
|
|
10355
|
+
tick: tickRecord,
|
|
10356
|
+
verifier_result: verifierResult,
|
|
10357
|
+
blocker,
|
|
10358
|
+
receipt_path: receiptPath,
|
|
10359
|
+
log_path: logPath,
|
|
10360
|
+
atris_goal_state: atrisGoalState,
|
|
10361
|
+
codex_goal_state: codexGoalState,
|
|
10362
|
+
continuation_goal: continuationGoal,
|
|
10363
|
+
operator_summary_warning: operatorSummaryWarning,
|
|
10364
|
+
cached: Boolean(tickRecord.cached),
|
|
10365
|
+
next_command: outputMission.next_action || null,
|
|
10366
|
+
...(verifyToolingFailed ? {
|
|
10367
|
+
reason: 'verify_did_not_run',
|
|
10368
|
+
detail: skipDetail,
|
|
10369
|
+
} : {}),
|
|
10370
|
+
};
|
|
10371
|
+
if (asJson) {
|
|
10372
|
+
printCliJson(
|
|
10373
|
+
fullTick,
|
|
10374
|
+
verifyToolingFailed
|
|
10375
|
+
? compactErrorPayload({
|
|
10376
|
+
reason: 'verify_did_not_run',
|
|
10377
|
+
detail: skipDetail,
|
|
10378
|
+
selected_ref: outputMission.id,
|
|
10379
|
+
next_command: outputMission.next_action || null,
|
|
10380
|
+
})
|
|
10381
|
+
: compactSuccessPayload({
|
|
10382
|
+
action: 'mission_tick',
|
|
10383
|
+
ids: {
|
|
10384
|
+
mission_id: outputMission.id,
|
|
10385
|
+
tick_index: tickIdx,
|
|
10386
|
+
status: outputMission.status,
|
|
10387
|
+
receipt_path: receiptPath,
|
|
10388
|
+
},
|
|
10389
|
+
next_command: outputMission.next_action || null,
|
|
10390
|
+
}),
|
|
10391
|
+
args,
|
|
10392
|
+
);
|
|
10393
|
+
} else {
|
|
10394
|
+
printJsonOrText(
|
|
10395
|
+
fullTick,
|
|
10396
|
+
[
|
|
10397
|
+
...(tickRecord.cached ? [`Reused receipt for tick ${tickIdx}: ${receiptPath}`] : []),
|
|
10398
|
+
...missionTickResultLines(outputMission, tickIdx, receiptPath, verifierResult, summary || tickRecord.summary, protectedLaneGuard),
|
|
10399
|
+
...(missionBlockerReceiptLine(blocker) ? [missionBlockerReceiptLine(blocker)] : []),
|
|
10400
|
+
...(continuationGoal?.mission ? [`Next goal: ${continuationGoal.mission.objective}`] : []),
|
|
10401
|
+
],
|
|
10402
|
+
false,
|
|
10403
|
+
);
|
|
10404
|
+
}
|
|
10405
|
+
if (verifyToolingFailed) process.exitCode = 2;
|
|
10182
10406
|
} finally {
|
|
10183
10407
|
releaseMissionLock(lock);
|
|
10184
10408
|
}
|
|
@@ -11149,6 +11373,18 @@ function missionCommand(args) {
|
|
|
11149
11373
|
return require('./human-missions').answerCommand(args.slice(1));
|
|
11150
11374
|
}
|
|
11151
11375
|
if (isBareMission && !hasLocalMissionState(resolveWorkspaceRoot())) {
|
|
11376
|
+
const root = resolveWorkspaceRoot();
|
|
11377
|
+
// Empty folder talks like bare atris. After init, stay on the current
|
|
11378
|
+
// card or an honest no-mission, and do not bounce back to init.
|
|
11379
|
+
if (isFreshWorkspace(root)) {
|
|
11380
|
+
const code = speakFirstMinute({
|
|
11381
|
+
root,
|
|
11382
|
+
fresh: true,
|
|
11383
|
+
asJson: args.includes('--json'),
|
|
11384
|
+
});
|
|
11385
|
+
process.exitCode = code;
|
|
11386
|
+
return code;
|
|
11387
|
+
}
|
|
11152
11388
|
return require('./human-missions').currentMissionCommand(args);
|
|
11153
11389
|
}
|
|
11154
11390
|
const subcommand = isBareMission ? 'status' : (args[0] || 'status');
|
|
@@ -11300,6 +11536,7 @@ module.exports = {
|
|
|
11300
11536
|
missionVerifierTimeoutMs,
|
|
11301
11537
|
missionLandingStepSummary,
|
|
11302
11538
|
missionLandingLines,
|
|
11539
|
+
missionGuardNextText,
|
|
11303
11540
|
missionVerifierCheckedText,
|
|
11304
11541
|
missionVerifierHighLevelTestText,
|
|
11305
11542
|
buildEngineVerifyPrompt,
|
package/commands/now.js
CHANGED
|
@@ -579,7 +579,7 @@ function refreshNowFile(root = process.cwd(), options = {}) {
|
|
|
579
579
|
function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
|
|
580
580
|
const help = args.includes('--help') || args.includes('-h') || args[0] === 'help';
|
|
581
581
|
if (help) {
|
|
582
|
-
console.log('Usage: atris now [--init|--refresh|--all|--path]');
|
|
582
|
+
console.log('Usage: atris now [--init|--refresh|--all|--path] [--json]');
|
|
583
583
|
console.log('');
|
|
584
584
|
console.log('Show the current operating truth for this workspace.');
|
|
585
585
|
console.log('');
|
|
@@ -588,6 +588,7 @@ function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
|
|
|
588
588
|
console.log(' atris now --refresh Regenerate a small local now.md');
|
|
589
589
|
console.log(' atris now --all Refresh this parent and every child Atris workspace');
|
|
590
590
|
console.log(' atris now --path Print the file path only');
|
|
591
|
+
console.log(' atris now --json Emit path and content as JSON');
|
|
591
592
|
return;
|
|
592
593
|
}
|
|
593
594
|
|
|
@@ -595,6 +596,7 @@ function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
|
|
|
595
596
|
const refresh = args.includes('--refresh');
|
|
596
597
|
const all = args.includes('--all');
|
|
597
598
|
const pathOnly = args.includes('--path');
|
|
599
|
+
const asJson = args.includes('--json');
|
|
598
600
|
|
|
599
601
|
let result;
|
|
600
602
|
if (all) {
|
|
@@ -603,7 +605,7 @@ function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
|
|
|
603
605
|
refreshNowFile(workspace.root);
|
|
604
606
|
}
|
|
605
607
|
result = refreshNowFile(root);
|
|
606
|
-
if (!pathOnly) {
|
|
608
|
+
if (!pathOnly && !asJson) {
|
|
607
609
|
console.log(`Refreshed ${workspaces.length} child workspace${workspaces.length === 1 ? '' : 's'}.`);
|
|
608
610
|
console.log('');
|
|
609
611
|
}
|
|
@@ -621,12 +623,22 @@ function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
|
|
|
621
623
|
return;
|
|
622
624
|
}
|
|
623
625
|
|
|
626
|
+
const content = fs.readFileSync(result.path, 'utf8').trimEnd();
|
|
627
|
+
if (asJson) {
|
|
628
|
+
console.log(JSON.stringify({
|
|
629
|
+
ok: true,
|
|
630
|
+
path: rel,
|
|
631
|
+
created: Boolean(result.created),
|
|
632
|
+
content,
|
|
633
|
+
}, null, 2));
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
624
637
|
if (result.created) {
|
|
625
638
|
console.log(`Created ${rel}`);
|
|
626
639
|
console.log('');
|
|
627
640
|
}
|
|
628
641
|
|
|
629
|
-
const content = fs.readFileSync(result.path, 'utf8').trimEnd();
|
|
630
642
|
console.log(content);
|
|
631
643
|
}
|
|
632
644
|
|