atris 3.74.21 → 3.74.22
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/autoland.js +24 -10
- package/commands/mission.js +9 -1
- package/commands/task.js +29 -11
- package/lib/auto-accept-certified.js +4 -3
- package/lib/roster-runs.js +5 -1
- package/lib/roster-suggest.js +1 -1
- package/lib/roster-swap.js +1 -1
- package/lib/router-brain.js +1 -1
- package/package.json +1 -1
package/commands/autoland.js
CHANGED
|
@@ -449,7 +449,7 @@ function wishSweepSummaryLine(summary, error = null) {
|
|
|
449
449
|
return line;
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
-
function evaluateQueue(root, { strictVerify, acceptAll }) {
|
|
452
|
+
function evaluateQueue(root, { strictVerify, acceptAll, includeProtected }) {
|
|
453
453
|
return readProjection(root)
|
|
454
454
|
.filter((task) => task && task.status === 'review')
|
|
455
455
|
.filter((task) => String(task.review?.approval_status || task.metadata?.approval_status || 'pending') === 'pending')
|
|
@@ -458,7 +458,7 @@ function evaluateQueue(root, { strictVerify, acceptAll }) {
|
|
|
458
458
|
.map((task) => {
|
|
459
459
|
const evaluation = evaluateAutoAccept(
|
|
460
460
|
{ ...task, workspace_root: root },
|
|
461
|
-
{ strictVerify, acceptAll, executeVerify: false },
|
|
461
|
+
{ strictVerify, acceptAll, includeProtected, executeVerify: false },
|
|
462
462
|
);
|
|
463
463
|
return {
|
|
464
464
|
...evaluation,
|
|
@@ -472,6 +472,7 @@ function protectedReviewWaiting(root, policy = {}, waiting = []) {
|
|
|
472
472
|
return evaluateQueue(root, {
|
|
473
473
|
strictVerify: policy.strict_verify !== false,
|
|
474
474
|
acceptAll: Boolean(policy.accept_all),
|
|
475
|
+
includeProtected: Boolean(policy.accept_protected),
|
|
475
476
|
})
|
|
476
477
|
.filter((row) => /^denied_tag_/.test(String(row.reason || '')))
|
|
477
478
|
.filter((row) => !alreadyWaiting.has(row.ref));
|
|
@@ -487,7 +488,8 @@ function showStatus(root, args, deps = {}) {
|
|
|
487
488
|
const enabled = Boolean(policy && policy.enabled);
|
|
488
489
|
const strictVerify = policy ? policy.strict_verify !== false : true;
|
|
489
490
|
const acceptAll = Boolean(policy && policy.accept_all);
|
|
490
|
-
const
|
|
491
|
+
const includeProtected = Boolean(policy && policy.accept_protected);
|
|
492
|
+
const results = evaluateQueue(root, { strictVerify, acceptAll, includeProtected })
|
|
491
493
|
.filter((r) => r.reason !== 'not_in_review');
|
|
492
494
|
const readyForRecheck = results.filter((r) => r.action === 'would_accept');
|
|
493
495
|
const wouldLand = readyForRecheck.filter((r) => r.verification_pending !== true);
|
|
@@ -525,7 +527,8 @@ function showStatus(root, args, deps = {}) {
|
|
|
525
527
|
: `on (flipped by ${policyOwner}${policy.enabled_at ? ` on ${String(policy.enabled_at).slice(0, 10)}` : ''})`)
|
|
526
528
|
: 'off - everything waits for you';
|
|
527
529
|
console.log(` policy: ${policyText}`);
|
|
528
|
-
if (enabled &&
|
|
530
|
+
if (enabled && includeProtected) console.log(' bar: protected work (money, deploys, security, customer, outward) also lands itself; sends and payments still need you.');
|
|
531
|
+
else if (enabled && acceptAll) console.log(' bar: everything lands except the protected lanes (money, deploys, security, customer, outward)');
|
|
529
532
|
console.log(` heartbeat: ${autoland.heartbeatStatusText(root, policy)}`);
|
|
530
533
|
if (policy && policy.imessage_to) console.log(` daily message: ${policy.imessage_to} at ${policy.digest_hour ?? autoland.DEFAULT_DIGEST_HOUR}:00`);
|
|
531
534
|
const reapTrouble = autoland.readState(root).last_reap_error;
|
|
@@ -634,6 +637,7 @@ function turnOn(root, args) {
|
|
|
634
637
|
alarm_hours: Number.isFinite(alarmHours) && alarmHours > 0 ? alarmHours : autoland.DEFAULT_ALARM_HOURS,
|
|
635
638
|
strict_verify: !args.includes('--no-strict-verify'),
|
|
636
639
|
accept_all: acceptAll,
|
|
640
|
+
accept_protected: args.includes('--include-protected'),
|
|
637
641
|
daily_experiment: previous.daily_experiment !== false,
|
|
638
642
|
});
|
|
639
643
|
const cronOk = autoland.installCron(root);
|
|
@@ -644,9 +648,11 @@ function turnOn(root, args) {
|
|
|
644
648
|
});
|
|
645
649
|
console.log('');
|
|
646
650
|
console.log('autoland is on.');
|
|
647
|
-
if (acceptAll) console.log(` everything in review now lands itself, accepted as ${owner}
|
|
651
|
+
if (acceptAll && policy.accept_protected) console.log(` everything in review now lands itself, accepted as ${owner}.`);
|
|
652
|
+
else if (acceptAll) console.log(` everything in review now lands itself, accepted as ${owner}; only the protected lanes wait.`);
|
|
648
653
|
else console.log(` certified, verified, reversible work now lands itself, accepted as ${owner}.`);
|
|
649
|
-
console.log(' protected
|
|
654
|
+
if (policy.accept_protected) console.log(' protected work (money, deploys, security, customer, outward) also lands itself; sends and payments still need you.');
|
|
655
|
+
else console.log(' protected lanes (money, deploys, security, customer, outward) still wait for you.');
|
|
650
656
|
console.log(` heartbeat: ${cronOk ? 'installed, runs hourly' : 'could not install cron; run atris autoland tick yourself'}`);
|
|
651
657
|
if (policy.imessage_to) {
|
|
652
658
|
console.log(` daily message to ${policy.imessage_to} at ${policy.digest_hour}:00; anything waiting on you past ${policy.alarm_hours}h pings you.`);
|
|
@@ -1020,6 +1026,14 @@ function digestSentToday(state, now = new Date()) {
|
|
|
1020
1026
|
return state.last_digest_date === utcDay(now) && state.last_digest_date !== utcDay(yesterday);
|
|
1021
1027
|
}
|
|
1022
1028
|
|
|
1029
|
+
function acceptCommandArgs(policy) {
|
|
1030
|
+
const args = ['task', 'auto-accept-certified', '--json', '--certify-first'];
|
|
1031
|
+
if (policy.accept_protected) args.push('--include-protected');
|
|
1032
|
+
if (policy.accept_all) args.push('--all');
|
|
1033
|
+
else if (policy.strict_verify === false) args.push('--no-strict-verify');
|
|
1034
|
+
return args;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1023
1037
|
async function runTickBody(root, { json, policy, receipt, engineValidationDeps = {} }) {
|
|
1024
1038
|
|
|
1025
1039
|
try {
|
|
@@ -1044,9 +1058,7 @@ async function runTickBody(root, { json, policy, receipt, engineValidationDeps =
|
|
|
1044
1058
|
// Refresh origin once, before anything is accepted: the landing proof reads
|
|
1045
1059
|
// origin's target, and a stale ref would call landed work unlanded.
|
|
1046
1060
|
refreshLandingRefs(root);
|
|
1047
|
-
const cliArgs =
|
|
1048
|
-
if (policy.accept_all) cliArgs.push('--all');
|
|
1049
|
-
else if (policy.strict_verify === false) cliArgs.push('--no-strict-verify');
|
|
1061
|
+
const cliArgs = acceptCommandArgs(policy);
|
|
1050
1062
|
const pendingReviewRows = readProjection(root)
|
|
1051
1063
|
.filter((task) => task && task.status === 'review')
|
|
1052
1064
|
.filter((task) => String(task.review?.approval_status || task.metadata?.approval_status || 'pending') === 'pending')
|
|
@@ -1442,7 +1454,7 @@ function showHelp() {
|
|
|
1442
1454
|
console.log('');
|
|
1443
1455
|
console.log('finished work that passed its checks and two independent reviews lands');
|
|
1444
1456
|
console.log('automatically with a receipt. money, deploys, security, customer, and');
|
|
1445
|
-
console.log('outward-facing work
|
|
1457
|
+
console.log('outward-facing work waits for you unless you include protected work.');
|
|
1446
1458
|
console.log('');
|
|
1447
1459
|
console.log('recommended agent flight checks:');
|
|
1448
1460
|
console.log(' fast suite: npm run test:fast');
|
|
@@ -1456,6 +1468,7 @@ function showHelp() {
|
|
|
1456
1468
|
console.log(' atris autoland on --all lower the bar: everything lands except');
|
|
1457
1469
|
console.log(' the protected lanes; a failing recorded');
|
|
1458
1470
|
console.log(' check still blocks');
|
|
1471
|
+
console.log(' atris autoland on --include-protected include protected work in the policy');
|
|
1459
1472
|
console.log(' atris autoland off back to approving every item');
|
|
1460
1473
|
console.log(' atris autoland digest [--send] the daily message, now');
|
|
1461
1474
|
console.log(' atris autoland tick [--json] one heartbeat (what the hourly cron runs)');
|
|
@@ -1501,6 +1514,7 @@ module.exports = {
|
|
|
1501
1514
|
hasAgentJargon,
|
|
1502
1515
|
runEngineAnswerValidation,
|
|
1503
1516
|
runTickBody,
|
|
1517
|
+
acceptCommandArgs,
|
|
1504
1518
|
runSshAgentSweep,
|
|
1505
1519
|
sshAgentSweepNote,
|
|
1506
1520
|
showStatus,
|
package/commands/mission.js
CHANGED
|
@@ -520,6 +520,13 @@ function recordMissionEngineTickOutcome(engineId, result, root = process.cwd())
|
|
|
520
520
|
// a closed credit window is credit out. Never throws into the tick.
|
|
521
521
|
function missionTickRunOutcome(result, verifierResult, maxSeconds) {
|
|
522
522
|
if (result && result.status === 'ran') {
|
|
523
|
+
const review = verifierResult?.referee;
|
|
524
|
+
if (verifierResult?.passed === false && verifierResult.status === 0 && !verifierResult.timed_out
|
|
525
|
+
&& review?.mode === 'engine-unavailable' && review.reason !== 'code changed during review') {
|
|
526
|
+
return { outcome: 'review unavailable', detail: review.timed_out
|
|
527
|
+
? 'the build check passed, but the second review timed out'
|
|
528
|
+
: 'the build check passed, but the second review was unavailable' };
|
|
529
|
+
}
|
|
523
530
|
return verifierResult && verifierResult.passed === false
|
|
524
531
|
? { outcome: 'failed', detail: 'the verifier failed' }
|
|
525
532
|
: { outcome: 'landed' };
|
|
@@ -11103,7 +11110,8 @@ async function executeMissionRunTicksPhase(context) {
|
|
|
11103
11110
|
next_push: result.next_push || undefined,
|
|
11104
11111
|
engine: result.engine_id || runtimeMission.runner || mission.runner || null,
|
|
11105
11112
|
task_type: 'executor',
|
|
11106
|
-
verified_passed:
|
|
11113
|
+
verified_passed: missionTickRunOutcome(result, verifierResult).outcome === 'review unavailable'
|
|
11114
|
+
? undefined : verifierResult && typeof verifierResult.passed === 'boolean' ? verifierResult.passed : undefined,
|
|
11107
11115
|
duration_ms: Math.max(0, (Date.parse(finishedAt) || 0) - (Date.parse(tickStart) || 0)),
|
|
11108
11116
|
}).mission;
|
|
11109
11117
|
if (rotateSessionForContext) {
|
package/commands/task.js
CHANGED
|
@@ -31,6 +31,7 @@ const {
|
|
|
31
31
|
parseVerifyCommand,
|
|
32
32
|
runVerifyCommand,
|
|
33
33
|
DENIED_TAGS,
|
|
34
|
+
protectedLaneOf,
|
|
34
35
|
} = require('../lib/auto-accept-certified');
|
|
35
36
|
const { evaluateAcceptVerify, nonFalsifyingIssue } = require('../lib/accept-verify-gate');
|
|
36
37
|
const { checkedCodeSha, runVerifyAtJobCode } = require('../lib/job-code-verify');
|
|
@@ -10247,6 +10248,7 @@ function cmdCertifyVerified(args, options = {}) {
|
|
|
10247
10248
|
const asJson = wantsJson(args);
|
|
10248
10249
|
const silent = options.silent === true;
|
|
10249
10250
|
const verifyCache = options.verifyCache || null;
|
|
10251
|
+
const includeProtected = options.includeProtected === true;
|
|
10250
10252
|
const actor = String(flag(args, '--as') || 'autoland-verifier');
|
|
10251
10253
|
const limitRaw = flag(args, '--limit');
|
|
10252
10254
|
const max = limitRaw && limitRaw !== true ? Math.max(1, Number(limitRaw) || 6) : 6;
|
|
@@ -10284,7 +10286,7 @@ function cmdCertifyVerified(args, options = {}) {
|
|
|
10284
10286
|
continue;
|
|
10285
10287
|
}
|
|
10286
10288
|
const tag = String(task.tag || '').toLowerCase();
|
|
10287
|
-
if (DENIED_TAGS.has(tag)) {
|
|
10289
|
+
if (!includeProtected && DENIED_TAGS.has(tag)) {
|
|
10288
10290
|
results.push({ ref, action: 'skipped', reason: `denied_tag_${tag}` });
|
|
10289
10291
|
continue;
|
|
10290
10292
|
}
|
|
@@ -10302,7 +10304,7 @@ function cmdCertifyVerified(args, options = {}) {
|
|
|
10302
10304
|
// second time per row before the real run. executeVerify: false as well,
|
|
10303
10305
|
// an untrusted trust tier re-enables the strict block internally even
|
|
10304
10306
|
// with strictVerify off, which is how this probe spawned a verify per row.
|
|
10305
|
-
const evaluation = evaluateAutoAccept(task, { strictVerify: false, executeVerify: false });
|
|
10307
|
+
const evaluation = evaluateAutoAccept(task, { strictVerify: false, includeProtected, executeVerify: false });
|
|
10306
10308
|
if (evaluation.eligible) {
|
|
10307
10309
|
results.push({ ref, action: 'skipped', reason: 'already_landable' });
|
|
10308
10310
|
continue;
|
|
@@ -10415,6 +10417,7 @@ function cmdLanding(args) {
|
|
|
10415
10417
|
function cmdAutoAcceptCertified(args) {
|
|
10416
10418
|
const dryRun = hasFlag(args, '--dry-run');
|
|
10417
10419
|
const acceptAll = hasFlag(args, '--all');
|
|
10420
|
+
const includeProtected = hasFlag(args, '--include-protected');
|
|
10418
10421
|
const certifyFirst = hasFlag(args, '--certify-first');
|
|
10419
10422
|
const strictVerify = !hasFlag(args, '--no-strict-verify') && !acceptAll;
|
|
10420
10423
|
const actorFlag = flag(args, '--as');
|
|
@@ -10479,7 +10482,7 @@ function cmdAutoAcceptCertified(args) {
|
|
|
10479
10482
|
// never persisted: a later heartbeat must prove the checkout again.
|
|
10480
10483
|
const verifyCache = new Map();
|
|
10481
10484
|
const certification = certifyFirst && !dryRun
|
|
10482
|
-
? cmdCertifyVerified([], { verifyCache, silent: true })
|
|
10485
|
+
? cmdCertifyVerified([], { verifyCache, silent: true, includeProtected })
|
|
10483
10486
|
: null;
|
|
10484
10487
|
|
|
10485
10488
|
const taskDb = getTaskDb();
|
|
@@ -10514,7 +10517,7 @@ function cmdAutoAcceptCertified(args) {
|
|
|
10514
10517
|
results.push({ ...proofBoundary, action: 'skipped' });
|
|
10515
10518
|
continue;
|
|
10516
10519
|
}
|
|
10517
|
-
const evaluation = evaluateAutoAccept(task, { strictVerify, acceptAll, verifyCache });
|
|
10520
|
+
const evaluation = evaluateAutoAccept(task, { strictVerify, acceptAll, includeProtected, verifyCache });
|
|
10518
10521
|
if (!evaluation.eligible) {
|
|
10519
10522
|
results.push({ ...evaluation, action: 'skipped' });
|
|
10520
10523
|
continue;
|
|
@@ -10547,7 +10550,9 @@ function cmdAutoAcceptCertified(args) {
|
|
|
10547
10550
|
}
|
|
10548
10551
|
const accepted = acceptReviewTask(taskDb, db, task.id, {
|
|
10549
10552
|
actor,
|
|
10550
|
-
proof:
|
|
10553
|
+
proof: includeProtected && protectedLaneOf(task)
|
|
10554
|
+
? `${evaluation.proof}\nprotected lane accepted by owner policy (${protectedLaneOf(task)})`
|
|
10555
|
+
: evaluation.proof,
|
|
10551
10556
|
reward: parsedReward.value,
|
|
10552
10557
|
lesson: String(task.review?.lesson || task.metadata?.latest_agent_lesson || ''),
|
|
10553
10558
|
nextTask: String(task.review?.next_task || task.metadata?.latest_agent_next_task || ''),
|
|
@@ -10632,7 +10637,17 @@ function autoAcceptSweepNormalizedTokens(value) {
|
|
|
10632
10637
|
return [normalized, ...normalized.split(/[^a-z0-9-]+/).filter(Boolean)];
|
|
10633
10638
|
}
|
|
10634
10639
|
|
|
10635
|
-
function
|
|
10640
|
+
function autoAcceptSweepLane(task) {
|
|
10641
|
+
for (const value of autoAcceptSweepLabelValues(task)) {
|
|
10642
|
+
for (const token of autoAcceptSweepNormalizedTokens(value)) {
|
|
10643
|
+
const lane = [...SWEEP_AUTO_ACCEPT_PROTECTED].find((denied) => token === denied || token.replace(/s$/, '') === denied);
|
|
10644
|
+
if (lane) return lane;
|
|
10645
|
+
}
|
|
10646
|
+
}
|
|
10647
|
+
return null;
|
|
10648
|
+
}
|
|
10649
|
+
|
|
10650
|
+
function autoAcceptSweepDeniedReason(task, includeProtected = false) {
|
|
10636
10651
|
for (const value of autoAcceptSweepLabelValues(task)) {
|
|
10637
10652
|
const tokens = autoAcceptSweepNormalizedTokens(value);
|
|
10638
10653
|
for (const token of tokens) {
|
|
@@ -10642,7 +10657,7 @@ function autoAcceptSweepDeniedReason(task) {
|
|
|
10642
10657
|
const protectedLane = [...SWEEP_AUTO_ACCEPT_PROTECTED].find((denied) =>
|
|
10643
10658
|
token === denied || token.replace(/s$/, '') === denied
|
|
10644
10659
|
);
|
|
10645
|
-
if (protectedLane) return 'protected_lane';
|
|
10660
|
+
if (protectedLane && !includeProtected) return 'protected_lane';
|
|
10646
10661
|
}
|
|
10647
10662
|
}
|
|
10648
10663
|
return null;
|
|
@@ -10689,7 +10704,7 @@ function autoAcceptSweepHappened(task) {
|
|
|
10689
10704
|
);
|
|
10690
10705
|
}
|
|
10691
10706
|
|
|
10692
|
-
function evaluateSweepAutoAccept(task, root) {
|
|
10707
|
+
function evaluateSweepAutoAccept(task, root, includeProtected = false) {
|
|
10693
10708
|
const ref = taskRef(task);
|
|
10694
10709
|
if (!task) return { eligible: false, ref, reason: 'task_not_found' };
|
|
10695
10710
|
if (task.status !== 'review') return { eligible: false, ref, reason: 'not_in_review' };
|
|
@@ -10698,7 +10713,7 @@ function evaluateSweepAutoAccept(task, root) {
|
|
|
10698
10713
|
const approval = String(review.approval_status || metadata.approval_status || 'pending').toLowerCase();
|
|
10699
10714
|
if (approval !== 'pending') return { eligible: false, ref, reason: `approval_${approval}` };
|
|
10700
10715
|
if (metadata.auto_accepted_at) return { eligible: false, ref, reason: 'already_auto_accepted' };
|
|
10701
|
-
const denied = autoAcceptSweepDeniedReason(task);
|
|
10716
|
+
const denied = autoAcceptSweepDeniedReason(task, includeProtected);
|
|
10702
10717
|
if (denied) return { eligible: false, ref, reason: denied };
|
|
10703
10718
|
const proof = autoAcceptSweepLatestProof(task);
|
|
10704
10719
|
if (!proof) return { eligible: false, ref, reason: 'no_proof' };
|
|
@@ -10819,14 +10834,17 @@ function cmdSweep(args) {
|
|
|
10819
10834
|
for (const item of pendingReview) {
|
|
10820
10835
|
const fullProjection = enrichTaskProjection(taskDb.taskProjection(db, { taskId: item.id }));
|
|
10821
10836
|
const task = fullProjection.tasks[0] || null;
|
|
10822
|
-
const
|
|
10837
|
+
const includeProtected = hasFlag(args, '--include-protected');
|
|
10838
|
+
const evaluation = evaluateSweepAutoAccept(task, root, includeProtected);
|
|
10823
10839
|
if (!evaluation.eligible) {
|
|
10824
10840
|
results.push({ ...evaluation, action: 'skipped', task_id: task?.id || item.id || null });
|
|
10825
10841
|
continue;
|
|
10826
10842
|
}
|
|
10827
10843
|
const accepted = acceptReviewTask(taskDb, db, task.id, {
|
|
10828
10844
|
actor,
|
|
10829
|
-
proof:
|
|
10845
|
+
proof: includeProtected && (protectedLaneOf(task) || autoAcceptSweepLane(task))
|
|
10846
|
+
? `${evaluation.proof}\nprotected lane accepted by owner policy (${protectedLaneOf(task) || autoAcceptSweepLane(task)})`
|
|
10847
|
+
: evaluation.proof,
|
|
10830
10848
|
reward: parsedReward.value,
|
|
10831
10849
|
lesson: String(task.review?.lesson || task.metadata?.latest_agent_lesson || ''),
|
|
10832
10850
|
nextTask: String(task.review?.next_task || task.metadata?.latest_agent_next_task || ''),
|
|
@@ -1177,6 +1177,7 @@ function evaluateAutoAccept(task, options = {}) {
|
|
|
1177
1177
|
strictVerify = true,
|
|
1178
1178
|
minPasses = AGENT_CERTIFICATION_REVIEW_PASSES,
|
|
1179
1179
|
acceptAll = false,
|
|
1180
|
+
includeProtected = false,
|
|
1180
1181
|
executeVerify = true,
|
|
1181
1182
|
verifyCache = null,
|
|
1182
1183
|
} = options;
|
|
@@ -1200,7 +1201,7 @@ function evaluateAutoAccept(task, options = {}) {
|
|
|
1200
1201
|
// strip, so a tag variant never slips money/deploy work past the human.
|
|
1201
1202
|
const tag = String(task.tag || '').trim().toLowerCase();
|
|
1202
1203
|
const deniedTag = deniedTagOf(task);
|
|
1203
|
-
if (deniedTag) return { eligible: false, ref, reason: `denied_tag_${deniedTag}` };
|
|
1204
|
+
if (deniedTag && !includeProtected) return { eligible: false, ref, reason: `denied_tag_${deniedTag}` };
|
|
1204
1205
|
|
|
1205
1206
|
// A protected lane declared in prose is not a protection, it is a note.
|
|
1206
1207
|
// WEB-410 said "PROTECTED LANE: auth/session minting ... never self-land"
|
|
@@ -1208,7 +1209,7 @@ function evaluateAutoAccept(task, options = {}) {
|
|
|
1208
1209
|
// safety through a tag field that authors leave empty fails open, silently.
|
|
1209
1210
|
// So read the declaration where authors actually write it.
|
|
1210
1211
|
const declared = declaredProtectedLane(task);
|
|
1211
|
-
if (declared) {
|
|
1212
|
+
if (declared && !includeProtected) {
|
|
1212
1213
|
return {
|
|
1213
1214
|
eligible: false,
|
|
1214
1215
|
ref,
|
|
@@ -1236,7 +1237,7 @@ function evaluateAutoAccept(task, options = {}) {
|
|
|
1236
1237
|
// same outage as failing open, just quieter. Instead: when there is no
|
|
1237
1238
|
// tag to route on, read the task text for the denied lanes themselves.
|
|
1238
1239
|
// WEB-406 (Stripe checkout + paywall) landed untagged this way.
|
|
1239
|
-
if (!tag) {
|
|
1240
|
+
if (!tag && !includeProtected) {
|
|
1240
1241
|
const sniffed = sniffedProtectedLane(task);
|
|
1241
1242
|
if (sniffed) {
|
|
1242
1243
|
return {
|
package/lib/roster-runs.js
CHANGED
|
@@ -21,7 +21,7 @@ const RUNS_FILE = path.join('.atris', 'state', 'roster_runs.jsonl');
|
|
|
21
21
|
const ROTATED_NAME = /^roster_runs\.\d.*\.jsonl$/;
|
|
22
22
|
const ROTATED_KEEP = 3;
|
|
23
23
|
const ROTATED_READ = 2;
|
|
24
|
-
const OUTCOMES = Object.freeze(['landed', 'failed', 'stalled', 'credit out']);
|
|
24
|
+
const OUTCOMES = Object.freeze(['landed', 'failed', 'stalled', 'credit out', 'review unavailable']);
|
|
25
25
|
const DEFAULT_DAYS = 7;
|
|
26
26
|
const TAIL_BYTES = 512 * 1024;
|
|
27
27
|
// Past this size the log is renamed aside before the append, never trimmed:
|
|
@@ -131,6 +131,7 @@ function runReason(run) {
|
|
|
131
131
|
if (given) return given.slice(0, 240);
|
|
132
132
|
if (run.outcome === 'stalled') return 'it did not finish in time';
|
|
133
133
|
if (run.outcome === 'credit out') return 'the engine hit its usage limit';
|
|
134
|
+
if (run.outcome === 'review unavailable') return 'the second review was unavailable';
|
|
134
135
|
return 'it failed and the tool gave no reason';
|
|
135
136
|
}
|
|
136
137
|
|
|
@@ -340,12 +341,15 @@ function summarizeRuns(runs, { days = DEFAULT_DAYS } = {}) {
|
|
|
340
341
|
handed_over: list.filter((run) => run.handed_over_to).length,
|
|
341
342
|
median_seconds: median(list.map((run) => Number(run.seconds)).filter((value) => value >= 0)),
|
|
342
343
|
};
|
|
344
|
+
const reviewUnavailable = count('review unavailable');
|
|
345
|
+
if (reviewUnavailable) summary.review_unavailable = reviewUnavailable;
|
|
343
346
|
const tokens = list.map((run) => run.tokens).filter((value) => Number.isFinite(value));
|
|
344
347
|
if (tokens.length) summary.tokens = tokens.reduce((sum, value) => sum + value, 0);
|
|
345
348
|
const parts = [`${summary.runs} run${summary.runs === 1 ? '' : 's'}`];
|
|
346
349
|
if (summary.landed) parts.push(`${summary.landed} landed`);
|
|
347
350
|
if (summary.stalled) parts.push(`${summary.stalled} stalled`);
|
|
348
351
|
if (summary.failed) parts.push(`${summary.failed} failed`);
|
|
352
|
+
if (summary.review_unavailable) parts.push(`${summary.review_unavailable} review unavailable`);
|
|
349
353
|
if (summary.credit_out) parts.push(`${summary.credit_out} credit out`);
|
|
350
354
|
if (summary.median_seconds !== null) parts.push(`median ${durationText(summary.median_seconds)}`);
|
|
351
355
|
summary.text = `last ${days} day${days === 1 ? '' : 's'}: ${parts.join(', ')}`;
|
package/lib/roster-suggest.js
CHANGED
|
@@ -26,7 +26,7 @@ const BOOT_TAIL_BYTES = 64 * 1024;
|
|
|
26
26
|
// engine health already benches it, so credit out runs are left out.
|
|
27
27
|
function judgedRuns(runs) {
|
|
28
28
|
return runs
|
|
29
|
-
.filter((run) => run.outcome !== 'credit out')
|
|
29
|
+
.filter((run) => run.outcome !== 'credit out' && run.outcome !== 'review unavailable')
|
|
30
30
|
.map((run, index) => ({ run, index }))
|
|
31
31
|
.sort((a, b) => (Date.parse(b.run.at) - Date.parse(a.run.at)) || (b.index - a.index))
|
|
32
32
|
.slice(0, SUGGEST_WINDOW_RUNS)
|
package/lib/roster-swap.js
CHANGED
|
@@ -261,7 +261,7 @@ function swapMember(root, memberName, engineWords, modelWords, options = {}) {
|
|
|
261
261
|
// model this machine offers that can do the member's work, grouped by tool.
|
|
262
262
|
// A model's last 7 days in the owner's words: the phone's swap card shows it
|
|
263
263
|
// under the model, so it says finished, never the team's own word for it.
|
|
264
|
-
const recordWords = seen => `${seen.landed} of ${seen.runs} finished this week`;
|
|
264
|
+
const recordWords = seen => `${seen.landed} of ${seen.runs - (seen.review_unavailable || 0)} finished this week${seen.review_unavailable ? `, ${seen.review_unavailable} awaiting review` : ''}`;
|
|
265
265
|
|
|
266
266
|
// The current model is on top; models with a record in the last 7 days come
|
|
267
267
|
// next, most landed first; the rest keep --available's order.
|
package/lib/router-brain.js
CHANGED
|
@@ -219,7 +219,7 @@ function readRosterRunObservations(root, now) {
|
|
|
219
219
|
if (!fs.existsSync(file) && !rotatedRunsPaths(root).length) return [];
|
|
220
220
|
const source = path.relative(root, file);
|
|
221
221
|
return readRosterRuns(root, { days: ROSTER_RUN_DAYS, ...(now !== undefined ? { now } : {}) }).flatMap((run) => {
|
|
222
|
-
if (run.outcome === 'credit out') return [];
|
|
222
|
+
if (run.outcome === 'credit out' || run.outcome === 'review unavailable') return [];
|
|
223
223
|
const words = String(run.job || '').trim().toLowerCase().split(/\s+/);
|
|
224
224
|
const role = ROSTER_JOB_ROLES[words[words.length - 1]];
|
|
225
225
|
const atMs = Date.parse(run.at);
|
package/package.json
CHANGED