atris 3.52.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/engines/SKILL.md +8 -7
- package/atris/skills/x-search/SKILL.md +24 -22
- package/atris/skills/youtube/SKILL.md +89 -40
- package/atris/team/_template/SOUL.md +40 -0
- package/atris/team/validator/MEMBER.md +1 -2
- package/ax +592 -91
- package/bin/atris +21 -0
- package/bin/atris.js +580 -378
- 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/computer.js +2 -2
- 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 +339 -74
- package/commands/now.js +25 -6
- 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 +96 -40
- 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 -33
- package/lib/account-bound.js +47 -0
- package/lib/auto-accept-certified.js +10 -2
- 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/engine-ask.js +11 -3
- package/lib/engine-registry.js +32 -7
- 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-ledger-compact.js +127 -0
- package/lib/mission-root.js +4 -2
- package/lib/mission-runtime-loop.js +30 -2
- package/lib/next-moves.js +34 -34
- package/lib/noninteractive.js +88 -0
- package/lib/permission-grants.js +10 -1
- package/lib/policy-lessons.js +4 -1
- package/lib/revision-metric.js +279 -0
- package/lib/runner-command.js +5 -4
- package/lib/scratch-root.js +48 -0
- package/lib/skill-eval-gate.js +249 -0
- package/lib/task-db.js +22 -2
- package/lib/task-proof.js +75 -9
- package/lib/task-receipt.js +5 -1
- 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 +4 -2
- package/scripts/det/checklist-score.js +191 -0
- package/scripts/det/ytsearch +31 -0
- package/scripts/outbound-artifact-gate.js +227 -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,
|
|
@@ -53,6 +58,7 @@ const {
|
|
|
53
58
|
runsPruneLines,
|
|
54
59
|
formatBytes,
|
|
55
60
|
} = require('../lib/runs-prune');
|
|
61
|
+
const { compactMissionLedger } = require('../lib/mission-ledger-compact');
|
|
56
62
|
const autolandLib = require('../lib/autoland');
|
|
57
63
|
const { gateForHuman, landingWhyClause } = require('../lib/voice-gate');
|
|
58
64
|
const { operatorReady, hasAgentJargon } = require('./autoland');
|
|
@@ -82,6 +88,7 @@ const {
|
|
|
82
88
|
resolveDefaultVerifier,
|
|
83
89
|
} = require('../lib/default-verifier');
|
|
84
90
|
const { resolveWorkspaceRoot, redirectToWorkspaceRoot } = require('../lib/mission-root');
|
|
91
|
+
const { isFreshWorkspace, speakFirstMinute } = require('../lib/first-minute');
|
|
85
92
|
const { readJson, writeJson } = require('../lib/json-file');
|
|
86
93
|
const {
|
|
87
94
|
normalizeHumanAsks,
|
|
@@ -426,8 +433,22 @@ function applyMissionRunnerProfile(runner) {
|
|
|
426
433
|
}
|
|
427
434
|
|
|
428
435
|
function exitMissionError(message, code = 1, asJson = false) {
|
|
429
|
-
if (asJson)
|
|
430
|
-
|
|
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
|
+
}
|
|
431
452
|
process.exit(code);
|
|
432
453
|
}
|
|
433
454
|
|
|
@@ -517,7 +538,7 @@ function assertMissionVerifier(command, asJson = false) {
|
|
|
517
538
|
|
|
518
539
|
function stripKnownFlags(args, valueNames, booleanNames = []) {
|
|
519
540
|
const valueSet = new Set(valueNames);
|
|
520
|
-
const booleanSet = new Set(booleanNames);
|
|
541
|
+
const booleanSet = new Set(['--full', '--verbose', ...booleanNames]);
|
|
521
542
|
const out = [];
|
|
522
543
|
for (let i = 0; i < args.length; i += 1) {
|
|
523
544
|
const arg = String(args[i]);
|
|
@@ -568,6 +589,8 @@ const MISSION_RUN_VALUE_FLAGS = [
|
|
|
568
589
|
];
|
|
569
590
|
const MISSION_RUN_BOOLEAN_FLAGS = [
|
|
570
591
|
'--json',
|
|
592
|
+
'--full',
|
|
593
|
+
'--verbose',
|
|
571
594
|
'--due',
|
|
572
595
|
'--detach',
|
|
573
596
|
'--headless',
|
|
@@ -726,7 +749,72 @@ function missionXpTaskTitle(objective) {
|
|
|
726
749
|
return `${prefix}${shortClause || 'mission work'}`;
|
|
727
750
|
}
|
|
728
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
|
+
|
|
729
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
|
+
}
|
|
730
818
|
const taskDb = loadTaskDb(asJson);
|
|
731
819
|
const db = taskDb.open();
|
|
732
820
|
const workspaceRoot = taskDb.workspaceRoot(root);
|
|
@@ -781,6 +869,7 @@ function createMissionXpTask(mission, root = process.cwd(), asJson = false) {
|
|
|
781
869
|
owner_resolution: ownerResolution.reason,
|
|
782
870
|
executed_by: ownerResolution.executed_by ? normalizeOwnerSlug(ownerResolution.executed_by) : null,
|
|
783
871
|
inserted: result.inserted !== false,
|
|
872
|
+
reused: false,
|
|
784
873
|
projection_path: outPath,
|
|
785
874
|
};
|
|
786
875
|
}
|
|
@@ -1237,11 +1326,27 @@ function exitMissingMission(ref, code = 1, asJson = false, root = process.cwd())
|
|
|
1237
1326
|
} catch { /* best-effort hint only */ }
|
|
1238
1327
|
}
|
|
1239
1328
|
if (asJson) {
|
|
1240
|
-
const
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
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
|
+
);
|
|
1245
1350
|
} else {
|
|
1246
1351
|
console.error(error);
|
|
1247
1352
|
if (hints.length) {
|
|
@@ -1347,6 +1452,11 @@ function saveMission(mission, root = process.cwd(), eventType = 'mission_updated
|
|
|
1347
1452
|
updated_at: stampIso(),
|
|
1348
1453
|
});
|
|
1349
1454
|
appendJsonLine(paths.missionsJsonl, next);
|
|
1455
|
+
// Self-maintenance: once history outweighs live missions 4:1 the ledger is
|
|
1456
|
+
// rewritten to one row per mission (latest state, first display number —
|
|
1457
|
+
// exactly what loadMissionMap reconstructs). Every status/tick path parses
|
|
1458
|
+
// this file several times, so unbounded snapshot history taxes everything.
|
|
1459
|
+
try { compactMissionLedger(paths.missionsJsonl); } catch {}
|
|
1350
1460
|
const event = appendEvent(eventType, next, payload, root);
|
|
1351
1461
|
renderMissionStatus(root);
|
|
1352
1462
|
renderMemberMissionState(next.owner, root);
|
|
@@ -1682,8 +1792,26 @@ function verifierTimeoutSeconds(verifierResult) {
|
|
|
1682
1792
|
return Number.isFinite(ms) ? Math.round(ms / 1000) : 0;
|
|
1683
1793
|
}
|
|
1684
1794
|
|
|
1685
|
-
function
|
|
1686
|
-
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
|
+
}
|
|
1687
1815
|
if (verifierResult.mode === 'engine-unavailable') {
|
|
1688
1816
|
return 'VERIFY FAILED: the engine verify pass was unavailable; treat this increment as unproven.';
|
|
1689
1817
|
}
|
|
@@ -1709,8 +1837,15 @@ function missionVerifierCheckedText(verifierResult, mission) {
|
|
|
1709
1837
|
return `VERIFY FAILED: ${command}.`;
|
|
1710
1838
|
}
|
|
1711
1839
|
|
|
1712
|
-
function missionVerifierHighLevelTestText(verifierResult, mission) {
|
|
1713
|
-
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
|
+
}
|
|
1714
1849
|
if (verifierResult.mode === 'engine-unavailable') {
|
|
1715
1850
|
return 'No changed-surface commands were confirmed because the engine verify pass was unavailable.';
|
|
1716
1851
|
}
|
|
@@ -1761,11 +1896,12 @@ function missionFallbackChangedText(mission, status, tickIndex, { ranTicks = nul
|
|
|
1761
1896
|
return `${mission.objective} recorded tick ${tickIndex}.`;
|
|
1762
1897
|
}
|
|
1763
1898
|
|
|
1764
|
-
function missionTickResultLines(mission, tickIndex, receiptPath, verifierResult = null, stepSummary = '') {
|
|
1899
|
+
function missionTickResultLines(mission, tickIndex, receiptPath, verifierResult = null, stepSummary = '', guard = null) {
|
|
1765
1900
|
return missionLandingLines(missionReceiptLanding(mission, {
|
|
1766
|
-
tick: { tick_index: tickIndex },
|
|
1901
|
+
tick: { tick_index: tickIndex, protected_lane_guard: guard },
|
|
1767
1902
|
summary: stepSummary,
|
|
1768
1903
|
verifier_result: verifierResult,
|
|
1904
|
+
protected_lane_guard: guard,
|
|
1769
1905
|
}, receiptPath));
|
|
1770
1906
|
}
|
|
1771
1907
|
|
|
@@ -1795,6 +1931,9 @@ function missionReceiptStatus(mission, result) {
|
|
|
1795
1931
|
function missionReceiptNextText(mission, result, receiptPath = '') {
|
|
1796
1932
|
if (result?.next) return String(result.next);
|
|
1797
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;
|
|
1798
1937
|
if (result?.verifier_result?.passed === true) {
|
|
1799
1938
|
if (!mission?.always_on && receiptPath) {
|
|
1800
1939
|
return `Review proof, then run: atris mission complete ${mission.id} --proof "${receiptPath}".`;
|
|
@@ -1811,8 +1950,9 @@ function missionReceiptLanding(mission, result, receiptPath = '') {
|
|
|
1811
1950
|
const summary = missionReceiptSummaryText(result);
|
|
1812
1951
|
const changed = missionLandingStepSummary(summary)
|
|
1813
1952
|
|| missionFallbackChangedText(mission, status, missionReceiptTickIndex(mission, result));
|
|
1814
|
-
const
|
|
1815
|
-
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);
|
|
1816
1956
|
const reason = missionHumanReasonText(mission, changed);
|
|
1817
1957
|
return {
|
|
1818
1958
|
schema: 'atris.result_landing.v1',
|
|
@@ -2367,20 +2507,25 @@ function missionRunSummaryLines(mission, ranTicks, effectiveMaxTicks, finalRecei
|
|
|
2367
2507
|
const changed = missionRunChangedText(mission, ranTicks, effectiveMaxTicks, ticks, createdNext);
|
|
2368
2508
|
const reason = missionHumanReasonText(mission, changed);
|
|
2369
2509
|
const lastTick = ticks[ticks.length - 1] || null;
|
|
2510
|
+
const guard = lastTick?.protected_lane_guard || null;
|
|
2370
2511
|
const verifier = lastTick && lastTick.verifier_passed == null ? null : mission.verifier_result;
|
|
2371
2512
|
const checked = verifier
|
|
2372
|
-
? missionVerifierCheckedText(verifier, mission)
|
|
2373
|
-
:
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
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.');
|
|
2378
2520
|
const tested = verifier
|
|
2379
|
-
? missionVerifierHighLevelTestText(verifier, mission)
|
|
2380
|
-
:
|
|
2381
|
-
?
|
|
2382
|
-
:
|
|
2383
|
-
|
|
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);
|
|
2384
2529
|
const lines = [
|
|
2385
2530
|
'Landing:',
|
|
2386
2531
|
` Changed: ${changed}`,
|
|
@@ -2654,6 +2799,7 @@ function missionFromArgs(args) {
|
|
|
2654
2799
|
...(ownerResolution.requested_owner && ownerResolution.requested_owner !== owner ? { requested_owner: ownerResolution.requested_owner } : {}),
|
|
2655
2800
|
...(ownerResolution.executed_by ? { executed_by: normalizeOwnerSlug(ownerResolution.executed_by) } : {}),
|
|
2656
2801
|
...(ownerResolution.proposed_member ? { proposed_member: ownerResolution.proposed_member } : {}),
|
|
2802
|
+
...(ownerResolution.remap_reason ? { remap_reason: ownerResolution.remap_reason } : {}),
|
|
2657
2803
|
status: 'planning',
|
|
2658
2804
|
cadence,
|
|
2659
2805
|
runner,
|
|
@@ -3965,9 +4111,23 @@ function startMission(args, options = {}) {
|
|
|
3965
4111
|
const xpTask = createMissionXpTask(mission, process.cwd(), asJson);
|
|
3966
4112
|
mission.xp_task = xpTask;
|
|
3967
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;
|
|
3968
4116
|
if (!mission.verifier && !mission.always_on) {
|
|
3969
4117
|
mission.next_action = `work task then run: atris task current-step --goal-id ${mission.id} --as ${mission.owner} --proof "<proof>" --json`;
|
|
3970
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
|
+
}
|
|
3971
4131
|
}
|
|
3972
4132
|
if (typeof options.beforeMissionSave === 'function') {
|
|
3973
4133
|
mission = options.beforeMissionSave(mission) || mission;
|
|
@@ -4002,6 +4162,7 @@ function startMission(args, options = {}) {
|
|
|
4002
4162
|
dirty_count: worktreeBaseline.dirty_count,
|
|
4003
4163
|
dirty_hash: worktreeBaseline.dirty_hash,
|
|
4004
4164
|
} : null,
|
|
4165
|
+
...(saved.remap_reason ? { remap_reason: saved.remap_reason } : {}),
|
|
4005
4166
|
};
|
|
4006
4167
|
if (!options.silent) {
|
|
4007
4168
|
printJsonOrText(
|
|
@@ -4010,6 +4171,7 @@ function startMission(args, options = {}) {
|
|
|
4010
4171
|
`Started mission: ${saved.objective}`,
|
|
4011
4172
|
`Owner: ${saved.owner}`,
|
|
4012
4173
|
`State: ${saved.status}`,
|
|
4174
|
+
...(saved.remap_reason ? [`Remap: ${saved.remap_reason}`] : []),
|
|
4013
4175
|
...(saved.worktree ? [`Worktree: ${saved.worktree.path}`, `Branch: ${saved.worktree.branch}`] : []),
|
|
4014
4176
|
...warnings.map((warning) => `Warning: ${warning.message}`),
|
|
4015
4177
|
...(saved.xp_task ? [`AgentXP task: ${saved.xp_task.ref}`] : []),
|
|
@@ -4130,6 +4292,8 @@ async function startMissionFromRunObjective(objective, args) {
|
|
|
4130
4292
|
const xpTask = createMissionXpTask(mission, process.cwd(), asJson);
|
|
4131
4293
|
mission.xp_task = xpTask;
|
|
4132
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;
|
|
4133
4297
|
}
|
|
4134
4298
|
if (landRun) {
|
|
4135
4299
|
const repoPath = path.resolve(readFlag(args, '--repo', process.cwd()));
|
|
@@ -4256,14 +4420,16 @@ async function startMissionFromRunObjective(objective, args) {
|
|
|
4256
4420
|
function attachMissionTask(args) {
|
|
4257
4421
|
const asJson = wantsJson(args);
|
|
4258
4422
|
if (hasFlag(args, '--help') || hasFlag(args, '-h') || String(args[0] || '').trim() === 'help') {
|
|
4259
|
-
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.');
|
|
4260
4425
|
console.log('Run `atris mission --help` for the full option list.');
|
|
4261
4426
|
process.exit(0);
|
|
4262
4427
|
}
|
|
4263
|
-
const ref = stripKnownFlags(args, [], ['--json'])[0] || '';
|
|
4428
|
+
const ref = stripKnownFlags(args, ['--task'], ['--json'])[0] || '';
|
|
4264
4429
|
if (!ref) {
|
|
4265
|
-
exitMissionError('Usage: atris mission attach-task <id> [--json]', 1, asJson);
|
|
4430
|
+
exitMissionError('Usage: atris mission attach-task <id> [--task <task-id>] [--json]', 1, asJson);
|
|
4266
4431
|
}
|
|
4432
|
+
const explicitTaskIds = readRepeatedFlag(args, '--task');
|
|
4267
4433
|
let mission = resolveMission(ref);
|
|
4268
4434
|
if (!mission) {
|
|
4269
4435
|
exitMissingMission(ref, 1, asJson);
|
|
@@ -4281,6 +4447,13 @@ function attachMissionTask(args) {
|
|
|
4281
4447
|
exitMissionError(`Mission "${ref}" is ${mission.status}; task spines attach only to active missions.`, 2, asJson);
|
|
4282
4448
|
}
|
|
4283
4449
|
|
|
4450
|
+
if (explicitTaskIds.length) {
|
|
4451
|
+
mission = {
|
|
4452
|
+
...mission,
|
|
4453
|
+
task_ids: Array.from(new Set([...(mission.task_ids || []), ...explicitTaskIds])),
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4284
4457
|
const existingSpine = missionTaskSpine(mission);
|
|
4285
4458
|
if (existingSpine?.has_task) {
|
|
4286
4459
|
let currentMission = mission;
|
|
@@ -4356,6 +4529,8 @@ function attachMissionTask(args) {
|
|
|
4356
4529
|
xp_task_enabled: true,
|
|
4357
4530
|
xp_task: xpTask,
|
|
4358
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,
|
|
4359
4534
|
};
|
|
4360
4535
|
if (nextMission.status === 'ready' && nextMission.receipt_path) {
|
|
4361
4536
|
nextMission.next_action = missionXpReadyAction(nextMission, nextMission.receipt_path) || nextMission.next_action;
|
|
@@ -4367,17 +4542,17 @@ function attachMissionTask(args) {
|
|
|
4367
4542
|
nextMission.next_action = `work task then run: atris task current-step --goal-id ${nextMission.id} --as ${nextMission.owner} --proof "<proof>" --json`;
|
|
4368
4543
|
}
|
|
4369
4544
|
|
|
4370
|
-
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 });
|
|
4371
4546
|
const memberState = renderMemberMissionState(saved.owner);
|
|
4372
|
-
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', {
|
|
4373
4548
|
mission: saved.objective,
|
|
4374
4549
|
task: xpTask.ref,
|
|
4375
4550
|
});
|
|
4376
4551
|
const view = missionStatusView(saved);
|
|
4377
4552
|
printJsonOrText(
|
|
4378
|
-
{ 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 },
|
|
4379
4554
|
[
|
|
4380
|
-
`Attached task spine: ${saved.objective}`,
|
|
4555
|
+
xpTask.reused ? `Bound existing task spine: ${saved.objective}` : `Attached task spine: ${saved.objective}`,
|
|
4381
4556
|
`Task: ${xpTask.ref}`,
|
|
4382
4557
|
`Next: ${view.task_spine.current_step_command}`,
|
|
4383
4558
|
],
|
|
@@ -4810,27 +4985,60 @@ function missionTickVerification(tick) {
|
|
|
4810
4985
|
return { verified: false, state: ran ? 'unchecked' : 'skipped', unchecked: ran };
|
|
4811
4986
|
}
|
|
4812
4987
|
|
|
4813
|
-
//
|
|
4814
|
-
//
|
|
4815
|
-
//
|
|
4816
|
-
//
|
|
4817
|
-
|
|
4818
|
-
|
|
4988
|
+
// Per-process index of mission run receipts, keyed by runs dir. Reading and
|
|
4989
|
+
// parsing ~1400 receipt files per mission made `mission list` scale as
|
|
4990
|
+
// O(missions × runs-files); building the mission_id → receipts map once per
|
|
4991
|
+
// invocation keeps outputs identical while scanning the dir a single time.
|
|
4992
|
+
// The dir mtime guards the memo so a receipt written mid-process (new file in
|
|
4993
|
+
// the dir) invalidates it; malformed receipts stay silently skipped, matching
|
|
4994
|
+
// readJson's existing tolerance.
|
|
4995
|
+
const missionRunsIndexCache = new Map();
|
|
4996
|
+
|
|
4997
|
+
function missionRunsReceiptIndex(root = process.cwd()) {
|
|
4998
|
+
const runsDir = statePaths(root).runsDir;
|
|
4999
|
+
let dirMtime = -1;
|
|
5000
|
+
try {
|
|
5001
|
+
dirMtime = fs.statSync(runsDir).mtimeMs;
|
|
5002
|
+
} catch {
|
|
5003
|
+
dirMtime = -1;
|
|
5004
|
+
}
|
|
5005
|
+
const cached = missionRunsIndexCache.get(runsDir);
|
|
5006
|
+
if (cached && cached.dirMtime === dirMtime) return cached.byMission;
|
|
5007
|
+
|
|
4819
5008
|
let files = [];
|
|
4820
5009
|
try {
|
|
4821
|
-
files = fs.readdirSync(
|
|
5010
|
+
files = fs.readdirSync(runsDir)
|
|
4822
5011
|
.filter((file) => file.startsWith('mission-') && file.endsWith('.json'))
|
|
4823
|
-
.map((file) => path.join(
|
|
5012
|
+
.map((file) => path.join(runsDir, file));
|
|
4824
5013
|
} catch {
|
|
4825
5014
|
files = [];
|
|
4826
5015
|
}
|
|
5016
|
+
const byMission = new Map();
|
|
5017
|
+
for (const file of files) {
|
|
5018
|
+
const receipt = readJson(file);
|
|
5019
|
+
if (!receipt || !receipt.mission_id) continue;
|
|
5020
|
+
let bucket = byMission.get(receipt.mission_id);
|
|
5021
|
+
if (!bucket) {
|
|
5022
|
+
bucket = [];
|
|
5023
|
+
byMission.set(receipt.mission_id, bucket);
|
|
5024
|
+
}
|
|
5025
|
+
bucket.push({ file, receipt });
|
|
5026
|
+
}
|
|
5027
|
+
missionRunsIndexCache.set(runsDir, { dirMtime, byMission });
|
|
5028
|
+
return byMission;
|
|
5029
|
+
}
|
|
5030
|
+
|
|
5031
|
+
// Roll every ran tick for a mission into a verification-debt tally. `unchecked`
|
|
5032
|
+
// counts ran ticks that recorded no verifier result at all — the "tick recorded
|
|
5033
|
+
// but nothing was checked" red the report and now.md rollup surface and count
|
|
5034
|
+
// against the mission.
|
|
5035
|
+
function missionVerificationDebt(mission, root = process.cwd()) {
|
|
5036
|
+
const entries = missionRunsReceiptIndex(root).get(mission.id) || [];
|
|
4827
5037
|
let ran = 0;
|
|
4828
5038
|
let verified = 0;
|
|
4829
5039
|
let unchecked = 0;
|
|
4830
5040
|
const seen = new Set();
|
|
4831
|
-
for (const
|
|
4832
|
-
const receipt = readJson(file);
|
|
4833
|
-
if (!receipt || receipt.mission_id !== mission.id) continue;
|
|
5041
|
+
for (const { receipt } of entries) {
|
|
4834
5042
|
for (const tick of missionReceiptTicks(receipt)) {
|
|
4835
5043
|
if (!tick || tick.status !== 'ran') continue;
|
|
4836
5044
|
const key = `${tick.tick_index || ''}:${tick.finished_at || tick.started_at || ''}`;
|
|
@@ -4853,21 +5061,10 @@ function missionVerificationDebtLine(debt) {
|
|
|
4853
5061
|
}
|
|
4854
5062
|
|
|
4855
5063
|
function missionReportTimeline(mission, root = process.cwd(), limit = 6) {
|
|
4856
|
-
const
|
|
4857
|
-
let files = [];
|
|
4858
|
-
try {
|
|
4859
|
-
files = fs.readdirSync(paths.runsDir)
|
|
4860
|
-
.filter((file) => file.startsWith('mission-') && file.endsWith('.json'))
|
|
4861
|
-
.map((file) => path.join(paths.runsDir, file));
|
|
4862
|
-
} catch {
|
|
4863
|
-
files = [];
|
|
4864
|
-
}
|
|
4865
|
-
|
|
5064
|
+
const entries = missionRunsReceiptIndex(root).get(mission.id) || [];
|
|
4866
5065
|
const items = [];
|
|
4867
5066
|
const seen = new Set();
|
|
4868
|
-
for (const file of
|
|
4869
|
-
const receipt = readJson(file);
|
|
4870
|
-
if (!receipt || receipt.mission_id !== mission.id) continue;
|
|
5067
|
+
for (const { file, receipt } of entries) {
|
|
4871
5068
|
const receiptPath = path.relative(root, file);
|
|
4872
5069
|
for (const tick of missionReceiptTicks(receipt)) {
|
|
4873
5070
|
const summary = missionTickReportSummary(tick);
|
|
@@ -5160,6 +5357,14 @@ function missionReportFor(mission, root = process.cwd()) {
|
|
|
5160
5357
|
? 'The last verifier passed; full-budget work is continuing.'
|
|
5161
5358
|
: mission.operator_outcome
|
|
5162
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
|
+
}
|
|
5163
5368
|
return {
|
|
5164
5369
|
id: mission.id,
|
|
5165
5370
|
objective: mission.objective,
|
|
@@ -5173,7 +5378,7 @@ function missionReportFor(mission, root = process.cwd()) {
|
|
|
5173
5378
|
operator_outcome: operatorOutcome,
|
|
5174
5379
|
verification: verificationDebt,
|
|
5175
5380
|
worker: mission.worker || missionWorkerLabel(mission),
|
|
5176
|
-
worker_summary:
|
|
5381
|
+
worker_summary: workerSummary,
|
|
5177
5382
|
timeline,
|
|
5178
5383
|
worker_receipt_path: workerReceiptPath,
|
|
5179
5384
|
verifier_receipt_path: verifierReceiptPath,
|
|
@@ -9500,6 +9705,7 @@ async function executeMissionRunTicksPhase(context) {
|
|
|
9500
9705
|
tick: tickRecord,
|
|
9501
9706
|
frozen,
|
|
9502
9707
|
verifier_result: verifierResult,
|
|
9708
|
+
protected_lane_guard: result.protected_lane_guard || null,
|
|
9503
9709
|
rate_limit_info: lastRateLimit,
|
|
9504
9710
|
worktree: tickWorktree,
|
|
9505
9711
|
});
|
|
@@ -9557,7 +9763,7 @@ async function executeMissionRunTicksPhase(context) {
|
|
|
9557
9763
|
last_tick_index: tickIdx,
|
|
9558
9764
|
last_tick_layer: result.layer,
|
|
9559
9765
|
last_tick_layer_source: result.layer_source,
|
|
9560
|
-
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,
|
|
9561
9767
|
last_check_feedback: verifierResult
|
|
9562
9768
|
? extractCheckFeedback(verifierResult)
|
|
9563
9769
|
: latestOnDisk.last_check_feedback || '',
|
|
@@ -10068,9 +10274,7 @@ function tickMission(args) {
|
|
|
10068
10274
|
const protectedLaneGuard = tickRecord.protected_lane_guard || { allowed: true };
|
|
10069
10275
|
if (!protectedLaneGuard.allowed) {
|
|
10070
10276
|
status = 'paused';
|
|
10071
|
-
nextAction = protectedLaneGuard.
|
|
10072
|
-
? `repair the mission diff tooling failure, then rerun: atris mission run ${mission.id}`
|
|
10073
|
-
: `review the protected diff and receipt before resuming: atris mission run ${mission.id}`;
|
|
10277
|
+
nextAction = missionGuardNextText(protectedLaneGuard, mission.id);
|
|
10074
10278
|
} else if (verifierResult?.passed && nextGoalChain && !nextGoalChain.pause_ready) {
|
|
10075
10279
|
status = 'running';
|
|
10076
10280
|
nextAction = missionGoalChainNextAction(nextGoalChain);
|
|
@@ -10108,7 +10312,7 @@ function tickMission(args) {
|
|
|
10108
10312
|
last_tick_index: tickIdx,
|
|
10109
10313
|
last_tick_layer: tickRecord.layer,
|
|
10110
10314
|
last_tick_layer_source: tickRecord.layer_source,
|
|
10111
|
-
verifier_result: verifierResult || mission.verifier_result || null,
|
|
10315
|
+
verifier_result: verifierResult || (verify && !protectedLaneGuard.allowed ? null : mission.verifier_result) || null,
|
|
10112
10316
|
last_check_feedback: verifierResult
|
|
10113
10317
|
? extractCheckFeedback(verifierResult)
|
|
10114
10318
|
: mission.last_check_feedback || '',
|
|
@@ -10141,16 +10345,64 @@ function tickMission(args) {
|
|
|
10141
10345
|
const outputMission = continuationGoal?.parent || saved;
|
|
10142
10346
|
const atrisGoalState = refreshAtrisGoalController(process.cwd(), { missionId: outputMission.id });
|
|
10143
10347
|
const codexGoalState = refreshCodexGoalController(process.cwd());
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
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;
|
|
10154
10406
|
} finally {
|
|
10155
10407
|
releaseMissionLock(lock);
|
|
10156
10408
|
}
|
|
@@ -11121,6 +11373,18 @@ function missionCommand(args) {
|
|
|
11121
11373
|
return require('./human-missions').answerCommand(args.slice(1));
|
|
11122
11374
|
}
|
|
11123
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
|
+
}
|
|
11124
11388
|
return require('./human-missions').currentMissionCommand(args);
|
|
11125
11389
|
}
|
|
11126
11390
|
const subcommand = isBareMission ? 'status' : (args[0] || 'status');
|
|
@@ -11272,6 +11536,7 @@ module.exports = {
|
|
|
11272
11536
|
missionVerifierTimeoutMs,
|
|
11273
11537
|
missionLandingStepSummary,
|
|
11274
11538
|
missionLandingLines,
|
|
11539
|
+
missionGuardNextText,
|
|
11275
11540
|
missionVerifierCheckedText,
|
|
11276
11541
|
missionVerifierHighLevelTestText,
|
|
11277
11542
|
buildEngineVerifyPrompt,
|