atris 3.74.26 → 3.74.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/rating.js +7 -2
- package/commands/task.js +116 -10
- package/commands/worktree.js +526 -19
- package/lib/auto-accept-certified.js +56 -0
- package/lib/campaign-keep.js +5 -1
- package/lib/fleet.js +357 -49
- package/lib/gm-season.js +45 -9
- package/lib/release-kind.js +27 -0
- package/lib/review-integrity.js +7 -0
- package/lib/roster-runs.js +11 -5
- package/lib/roster-suggest.js +3 -2
- package/lib/roster-swap.js +3 -1
- package/lib/router-brain.js +7 -6
- package/lib/task-db.js +113 -3
- package/lib/voice-gate.js +1 -0
- package/lib/worktree-space.js +357 -0
- package/package.json +1 -1
package/commands/rating.js
CHANGED
|
@@ -39,7 +39,10 @@ function readHere(deps = {}) {
|
|
|
39
39
|
}
|
|
40
40
|
if (!played || played.error || played.no_history) return null;
|
|
41
41
|
const { rosterRows } = require('../lib/gm-season');
|
|
42
|
-
|
|
42
|
+
// A member whose only misses were later confirmed not needed, or whose only jobs
|
|
43
|
+
// wait on a pull request, has no finished job, but still shows, so those jobs
|
|
44
|
+
// have somewhere to be counted.
|
|
45
|
+
const rows = rosterRows({ outcomes: played.outcomes || [], limit: Number.MAX_SAFE_INTEGER }).filter((row) => row.finished > 0 || row.not_needed > 0 || row.held > 0);
|
|
43
46
|
if (!rows.length) return null;
|
|
44
47
|
let lineup = deps.lineup;
|
|
45
48
|
if (lineup === undefined) lineup = require('../lib/team-lineup').readLineupSafe(root);
|
|
@@ -54,7 +57,7 @@ function readHere(deps = {}) {
|
|
|
54
57
|
}
|
|
55
58
|
const miss_reasons = [...reasons].map(([reason, count]) => ({ reason, count }))
|
|
56
59
|
.sort((a, b) => b.count - a.count || a.reason.localeCompare(b.reason)).slice(0, 3);
|
|
57
|
-
return { member: row.name, ovr: row.ovr, landed: row.landed, finished: row.finished, job: seat.job || null, model: seat.model || null, ...(miss_reasons.length ? { miss_reasons } : {}) };
|
|
60
|
+
return { member: row.name, ovr: row.ovr, landed: row.landed, finished: row.finished, ...(row.not_needed ? { not_needed: row.not_needed } : {}), ...(row.held ? { held: row.held } : {}), job: seat.job || null, model: seat.model || null, ...(miss_reasons.length ? { miss_reasons } : {}) };
|
|
58
61
|
});
|
|
59
62
|
const buildJob = ((lineup && lineup.ok && lineup.jobs) || []).find((row) => row.job === 'build' && !row.key);
|
|
60
63
|
const buildModel = buildJob && buildJob.now_runs ? buildJob.now_runs.model : null;
|
|
@@ -111,6 +114,8 @@ function renderMember(m, card) {
|
|
|
111
114
|
'',
|
|
112
115
|
` landed ${m.landed} of ${m.finished} finished jobs in the last 30 days`,
|
|
113
116
|
];
|
|
117
|
+
if (m.not_needed) lines.push(` not needed: ${m.not_needed} (confirmed by someone else, so ${m.not_needed === 1 ? 'it does' : 'they do'} not count either way)`);
|
|
118
|
+
if (m.held) lines.push(` waiting on a pull request: ${m.held} (the work is done and a person has to land it, so ${m.held === 1 ? 'it does' : 'they do'} not count either way)`);
|
|
114
119
|
if (m.ovr == null) lines.push(' rated once it has 3 finished jobs');
|
|
115
120
|
if (m.miss_reasons && m.miss_reasons.length) {
|
|
116
121
|
lines.push(' top miss reasons:');
|
package/commands/task.js
CHANGED
|
@@ -27,6 +27,7 @@ const {
|
|
|
27
27
|
evaluateAutoAccept,
|
|
28
28
|
isAgentCertified,
|
|
29
29
|
isAutoCertifyVerifyCommandAllowed,
|
|
30
|
+
openPullRequestRefusal,
|
|
30
31
|
parseVerifyChain,
|
|
31
32
|
parseVerifyCommand,
|
|
32
33
|
runVerifyCommand,
|
|
@@ -226,7 +227,7 @@ atris task - durable local task state (SQLite, gitignored)
|
|
|
226
227
|
atris task list [--all] [--everywhere] [--status <s>]
|
|
227
228
|
list tasks in this workspace; --everywhere spans workspaces
|
|
228
229
|
atris task claim <id> [--as <owner>] Atomic claim
|
|
229
|
-
atris task release <id> [--as <owner>] Release your own mistaken claim back to open
|
|
230
|
+
atris task release <id> [--as <owner>] [--reason "<why>"] Release your own mistaken claim back to open
|
|
230
231
|
atris task capabilities [--json] Read-only task CLI/API capability contract
|
|
231
232
|
atris task capabilities-check [--json] Verify task capability contract conformance
|
|
232
233
|
atris task review-lane-drain [--json] Pick next safe Review-lane agent action
|
|
@@ -252,9 +253,12 @@ atris task - durable local task state (SQLite, gitignored)
|
|
|
252
253
|
atris task step <id> [--json] Refine chat, then advance one safe Plan/Do/Review step
|
|
253
254
|
atris task done <id> --proof "..." Mark complete with proof
|
|
254
255
|
atris task done <id> --failed [--proof "..."] Mark failed, optionally reviewed
|
|
255
|
-
atris task archive <id> --reason "..." [--from-failed]
|
|
256
|
+
atris task archive <id> --reason "..." [--from-failed] [--not-needed]
|
|
256
257
|
Sweep off-roadmap/duplicate work as archived (not failed);
|
|
257
258
|
--from-failed opts in to relabel a fail-closed row (never done)
|
|
259
|
+
--not-needed: a person confirms the job did not need doing, so a
|
|
260
|
+
drop on it stops counting against the member (needs --reason;
|
|
261
|
+
refused inside an agent session or a dispatched worker)
|
|
258
262
|
atris task reopen <id> [--reason "..."] Send a done, waiting, failed, or archived task back to open
|
|
259
263
|
atris task clear-done [--before <days>] [--dry-run] [--json] Archive completed rows, oldest first
|
|
260
264
|
atris task prune-test-junk [--yes] [--vacuum] [--json]
|
|
@@ -366,6 +370,28 @@ function agentProofOnlyMode() {
|
|
|
366
370
|
return AGENT_ENV_MARKERS.some((marker) => String(process.env[marker] || '').trim() !== '');
|
|
367
371
|
}
|
|
368
372
|
|
|
373
|
+
// Everything an engine process carries: what the engine tools export in the
|
|
374
|
+
// shells they run, and what a mission flight sets on the drivers and engines it
|
|
375
|
+
// starts. ATRIS_AGENT_ID is left out on purpose: it is the owner's own id, which
|
|
376
|
+
// a person may export. The fleet's own dispatch adds no marker of its own, so
|
|
377
|
+
// it relies on the engine tool's.
|
|
378
|
+
const WORKER_ENV_MARKERS = [
|
|
379
|
+
...AGENT_ENV_MARKERS,
|
|
380
|
+
'CODEX_THREAD_ID',
|
|
381
|
+
'CODEX_SESSION_ID',
|
|
382
|
+
'ATRIS_MISSION_DRIVER_DETACHED',
|
|
383
|
+
'ATRIS_MISSION_DRIVER_PARENT_PID',
|
|
384
|
+
'ATRIS_DISPATCH_DEPTH',
|
|
385
|
+
];
|
|
386
|
+
|
|
387
|
+
// The first worker marker set in this shell, or ''. Unlike agentProofOnlyMode
|
|
388
|
+
// it cannot be switched off with ATRIS_AGENT_PROOF_ONLY=0.
|
|
389
|
+
function workerEnvMarker(env = process.env) {
|
|
390
|
+
const found = WORKER_ENV_MARKERS.find((marker) => String(env[marker] || '').trim() !== '');
|
|
391
|
+
if (found) return found;
|
|
392
|
+
return String(env.ATRIS_AGENT_PROOF_ONLY || '').trim() === '1' ? 'ATRIS_AGENT_PROOF_ONLY' : '';
|
|
393
|
+
}
|
|
394
|
+
|
|
369
395
|
function failAgentProofOnly(label, detail) {
|
|
370
396
|
failTask(
|
|
371
397
|
label,
|
|
@@ -575,6 +601,23 @@ function landingFlags(args) {
|
|
|
575
601
|
};
|
|
576
602
|
}
|
|
577
603
|
|
|
604
|
+
// The pull request a flight opened or merged, so the job record names it.
|
|
605
|
+
// Only a merge commit makes it count as landed. A number or link alone is an
|
|
606
|
+
// open pull request still waiting for a person to land it.
|
|
607
|
+
function readyMergeFlags(args) {
|
|
608
|
+
const commit = textFlag(args, ['--merge-commit']);
|
|
609
|
+
const number = textFlag(args, ['--pr-number']);
|
|
610
|
+
const url = textFlag(args, ['--pr-url']);
|
|
611
|
+
if (!commit && !number && !url) return null;
|
|
612
|
+
return {
|
|
613
|
+
merge_commit: commit,
|
|
614
|
+
number,
|
|
615
|
+
url,
|
|
616
|
+
merged_at: textFlag(args, ['--merged-at']),
|
|
617
|
+
self_landed: hasFlag(args, '--self-landed'),
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
578
621
|
function normalizedLandingSentence(value) {
|
|
579
622
|
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
580
623
|
}
|
|
@@ -737,13 +780,18 @@ function positional(args) {
|
|
|
737
780
|
});
|
|
738
781
|
}
|
|
739
782
|
|
|
783
|
+
// options.root is for a caller that is not the command line, the dispatch flight: it
|
|
784
|
+
// keeps the list for its own workspace instead of for the directory this process is in.
|
|
740
785
|
function writeDefaultProjection(taskDb, db, options = {}) {
|
|
741
|
-
const
|
|
786
|
+
const root = options.root ? path.resolve(options.root) : null;
|
|
787
|
+
const workspaceRoot = root
|
|
788
|
+
? (taskScopeEverywhere([], options) ? null : taskDb.workspaceRoot(root))
|
|
789
|
+
: scopedWorkspaceRoot(taskDb, [], options);
|
|
742
790
|
const projection = enrichTaskProjection(taskDb.taskProjection(db, {
|
|
743
791
|
workspaceRoot,
|
|
744
792
|
limit: options.all ? null : 500,
|
|
745
793
|
}));
|
|
746
|
-
const outPath = path.resolve(
|
|
794
|
+
const outPath = path.resolve(root || process.cwd(), '.atris', 'state', 'tasks.projection.json');
|
|
747
795
|
const output = JSON.stringify(projection, null, 2) + '\n';
|
|
748
796
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
749
797
|
let shouldWrite = true;
|
|
@@ -6313,26 +6361,39 @@ function cmdRelease(args) {
|
|
|
6313
6361
|
failTask('atris task release', 'missing_id', 'id required');
|
|
6314
6362
|
}
|
|
6315
6363
|
const owner = flag(args, '--as') || DEFAULT_OWNER;
|
|
6364
|
+
const reasonFlag = flag(args, '--reason');
|
|
6365
|
+
// This command takes no kind and no pull request. A release that says its work is
|
|
6366
|
+
// waiting on a person would keep a miss out of the rating, and any worker holding a
|
|
6367
|
+
// failed job could type that to excuse itself. Only the dispatch flight, which
|
|
6368
|
+
// opened the pull request itself, records that release, in its own process through
|
|
6369
|
+
// releaseTask. Flags this command does not know are ignored, so a release that still
|
|
6370
|
+
// passes them is a plain release and is rated as a drop.
|
|
6316
6371
|
const taskDb = getTaskDb();
|
|
6317
6372
|
const db = taskDb.open();
|
|
6318
6373
|
const taskId = requireTaskId(taskDb, db, id, 'atris task release');
|
|
6319
|
-
const result = taskDb.releaseTask(db, {
|
|
6374
|
+
const result = taskDb.releaseTask(db, {
|
|
6375
|
+
id: taskId,
|
|
6376
|
+
actor: String(owner),
|
|
6377
|
+
reason: typeof reasonFlag === 'string' ? reasonFlag : undefined,
|
|
6378
|
+
});
|
|
6320
6379
|
if (result.released) {
|
|
6321
6380
|
const { projection, outPath } = writeDefaultProjection(taskDb, db);
|
|
6322
6381
|
const task = compactTaskFromProjection(projection, taskId);
|
|
6382
|
+
const keptReason = result.event && result.event.payload && result.event.payload.reason || null;
|
|
6323
6383
|
if (wantsJson(args)) {
|
|
6324
6384
|
printJson({
|
|
6325
6385
|
ok: true,
|
|
6326
6386
|
action: 'released',
|
|
6327
6387
|
task_id: taskId,
|
|
6328
6388
|
owner: String(owner),
|
|
6389
|
+
reason: keptReason,
|
|
6329
6390
|
projection_path: outPath,
|
|
6330
6391
|
task,
|
|
6331
6392
|
event_version: result.event && result.event.version || null,
|
|
6332
6393
|
});
|
|
6333
6394
|
return;
|
|
6334
6395
|
}
|
|
6335
|
-
console.log(`released ${taskRef(task)} from ${owner}`);
|
|
6396
|
+
console.log(`released ${taskRef(task)} from ${owner}${keptReason ? ` (${keptReason})` : ''}`);
|
|
6336
6397
|
console.log(`Next: atris task next --as ${owner}`);
|
|
6337
6398
|
return;
|
|
6338
6399
|
}
|
|
@@ -9237,9 +9298,27 @@ function cmdArchive(args) {
|
|
|
9237
9298
|
if (!id) {
|
|
9238
9299
|
failTask('atris task archive', 'missing_id', 'id required');
|
|
9239
9300
|
}
|
|
9301
|
+
const notNeeded = hasFlag(args, '--not-needed');
|
|
9302
|
+
// The note clears a drop off the member's record, and --as is only a name. A
|
|
9303
|
+
// worker that dropped the job could name someone else and clear its own miss,
|
|
9304
|
+
// so inside an agent session or a dispatched worker it is refused outright.
|
|
9305
|
+
const workerMarker = notNeeded ? workerEnvMarker() : '';
|
|
9306
|
+
if (workerMarker) {
|
|
9307
|
+
failTask(
|
|
9308
|
+
'atris task archive',
|
|
9309
|
+
'not_needed_needs_a_person',
|
|
9310
|
+
`atris task archive --not-needed is for a person to confirm, and this shell looks like an agent session or a dispatched worker (${workerMarker} is set). Run it from a plain terminal.`,
|
|
9311
|
+
);
|
|
9312
|
+
}
|
|
9240
9313
|
const reason = textFlag(args, ['--reason']);
|
|
9241
9314
|
if (!reason) {
|
|
9242
|
-
failTask(
|
|
9315
|
+
failTask(
|
|
9316
|
+
'atris task archive',
|
|
9317
|
+
'missing_reason',
|
|
9318
|
+
notNeeded
|
|
9319
|
+
? 'atris task archive --not-needed requires --reason "<why this job did not need doing>"'
|
|
9320
|
+
: 'atris task archive requires --reason "<why this is being swept, not failed>"',
|
|
9321
|
+
);
|
|
9243
9322
|
}
|
|
9244
9323
|
const taskDb = getTaskDb();
|
|
9245
9324
|
const db = taskDb.open();
|
|
@@ -9249,8 +9328,13 @@ function cmdArchive(args) {
|
|
|
9249
9328
|
// loop-tick orphans fail-closed before 'archived' existed). Without the
|
|
9250
9329
|
// flag, failed rows stay failed; done rows are never archivable.
|
|
9251
9330
|
const fromFailed = hasFlag(args, '--from-failed');
|
|
9252
|
-
|
|
9253
|
-
|
|
9331
|
+
// --not-needed also writes who said so, which clears a dropped job off the
|
|
9332
|
+
// member's record when it is someone other than the member. A job that is
|
|
9333
|
+
// already archived only gets that note.
|
|
9334
|
+
const result = notNeeded
|
|
9335
|
+
? taskDb.archiveNotNeeded(db, { id: taskId, actor, reason, fromFailed })
|
|
9336
|
+
: taskDb.archiveTask(db, { id: taskId, actor, reason, fromFailed });
|
|
9337
|
+
if (result.archived || result.recorded) {
|
|
9254
9338
|
const { projection, outPath } = writeDefaultProjection(taskDb, db);
|
|
9255
9339
|
if (wantsJson(args)) {
|
|
9256
9340
|
printJson({
|
|
@@ -9258,6 +9342,7 @@ function cmdArchive(args) {
|
|
|
9258
9342
|
action: 'archived',
|
|
9259
9343
|
task_id: taskId,
|
|
9260
9344
|
reason,
|
|
9345
|
+
...(notNeeded ? { not_needed: true, not_needed_by: actor, already_archived: !result.archived } : {}),
|
|
9261
9346
|
archived_from: result.row && result.row.metadata && result.row.metadata.archived_from || null,
|
|
9262
9347
|
projection_path: outPath,
|
|
9263
9348
|
task: compactTaskFromProjection(projection, taskId),
|
|
@@ -9267,7 +9352,12 @@ function cmdArchive(args) {
|
|
|
9267
9352
|
const fromNote = result.row && result.row.metadata && result.row.metadata.archived_from
|
|
9268
9353
|
? ` (was ${result.row.metadata.archived_from})`
|
|
9269
9354
|
: '';
|
|
9270
|
-
|
|
9355
|
+
const ref = taskRef(compactTaskFromProjection(projection, taskId));
|
|
9356
|
+
if (notNeeded && !result.archived) {
|
|
9357
|
+
console.log(`${ref} was already archived; recorded as not needed by ${actor}: ${reason}`);
|
|
9358
|
+
} else {
|
|
9359
|
+
console.log(`archived ${ref}${fromNote}${notNeeded ? ' as not needed' : ''}: ${reason}`);
|
|
9360
|
+
}
|
|
9271
9361
|
} else {
|
|
9272
9362
|
const hint = result.reason === 'already_failed'
|
|
9273
9363
|
? ' (use --from-failed to archive a fail-closed duplicate/off-roadmap row)'
|
|
@@ -9650,6 +9740,7 @@ function cmdReady(args) {
|
|
|
9650
9740
|
const lesson = flag(args, '--lesson') || '';
|
|
9651
9741
|
const nextTaskInput = normalizeReviewNextTaskInput(typeof flag(args, '--next') === 'string' ? flag(args, '--next') : '');
|
|
9652
9742
|
const landing = landingFlags(args);
|
|
9743
|
+
const merge = readyMergeFlags(args);
|
|
9653
9744
|
guardExplicitActor('atris task ready', flag(args, '--as'));
|
|
9654
9745
|
const actor = String(flag(args, '--as') || DEFAULT_OWNER);
|
|
9655
9746
|
const resultFields = {
|
|
@@ -9697,6 +9788,7 @@ function cmdReady(args) {
|
|
|
9697
9788
|
nextTask: nextTaskInput.nextTask,
|
|
9698
9789
|
resultTrace: resultTrace && resultTrace.trace,
|
|
9699
9790
|
landing,
|
|
9791
|
+
merge,
|
|
9700
9792
|
result: resultSentence,
|
|
9701
9793
|
});
|
|
9702
9794
|
if (!result.ready) {
|
|
@@ -10036,6 +10128,13 @@ function stampAutoAcceptMetadata(taskDb, db, task, actor, policy) {
|
|
|
10036
10128
|
|
|
10037
10129
|
function acceptReviewTask(taskDb, db, taskId, { actor, proof, reward, lesson = '', nextTask = '', autoAccepted = false }) {
|
|
10038
10130
|
const task = taskDetail(taskDb, db, taskId);
|
|
10131
|
+
// The last door for the machine: whatever decided this job was eligible, a job
|
|
10132
|
+
// whose pull request is still open is never completed without a person. A
|
|
10133
|
+
// person's own accept (autoAccepted false) is untouched.
|
|
10134
|
+
if (autoAccepted) {
|
|
10135
|
+
const pullRequestOpen = openPullRequestRefusal(task, taskId);
|
|
10136
|
+
if (pullRequestOpen) return { ok: false, reason: pullRequestOpen.reason, pull_request: pullRequestOpen.pull_request };
|
|
10137
|
+
}
|
|
10039
10138
|
const missionXpIssue = missionXpEndToEndProofIssue(task, proof, taskDb.workspaceRoot());
|
|
10040
10139
|
if (missionXpIssue) {
|
|
10041
10140
|
return { ok: false, reason: MISSION_XP_END_TO_END_REASON, detail: missionXpIssue };
|
|
@@ -10719,6 +10818,10 @@ function evaluateSweepAutoAccept(task, root, includeProtected = false) {
|
|
|
10719
10818
|
if (metadata.auto_accepted_at) return { eligible: false, ref, reason: 'already_auto_accepted' };
|
|
10720
10819
|
const denied = autoAcceptSweepDeniedReason(task, includeProtected);
|
|
10721
10820
|
if (denied) return { eligible: false, ref, reason: denied };
|
|
10821
|
+
// A job whose pull request is still open has not landed, whatever its stored check
|
|
10822
|
+
// says: the same refusal as auto-accept-certified, before any check is run.
|
|
10823
|
+
const pullRequestOpen = openPullRequestRefusal(task, ref);
|
|
10824
|
+
if (pullRequestOpen) return pullRequestOpen;
|
|
10722
10825
|
const proof = autoAcceptSweepLatestProof(task);
|
|
10723
10826
|
if (!proof) return { eligible: false, ref, reason: 'no_proof' };
|
|
10724
10827
|
const storedVerify = typeof metadata.verify === 'string' ? metadata.verify.trim() : '';
|
|
@@ -13327,4 +13430,7 @@ module.exports = {
|
|
|
13327
13430
|
certifyVerifyCandidate,
|
|
13328
13431
|
landingProofVerify,
|
|
13329
13432
|
stampAutoAcceptMetadata,
|
|
13433
|
+
acceptReviewTask,
|
|
13434
|
+
evaluateSweepAutoAccept,
|
|
13435
|
+
writeDefaultProjection,
|
|
13330
13436
|
};
|