atris 3.74.29 → 3.76.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/atris/skills/calendar/SKILL.md +9 -0
- package/atris/skills/drive/SKILL.md +7 -0
- package/atris/skills/email-agent/SKILL.md +7 -0
- package/atris/skills/magic-inbox/SKILL.md +7 -0
- package/atris/skills/slides/SKILL.md +7 -0
- package/bin/atris.js +14 -2
- package/commands/engine.js +2 -2
- package/commands/integrations.js +539 -54
- package/commands/mission.js +2 -2
- package/commands/rating.js +29 -5
- package/commands/task.js +72 -5
- package/commands/worktree.js +66 -18
- package/lib/fleet.js +602 -55
- package/lib/gm-season.js +47 -10
- package/lib/held-pull-requests.js +589 -0
- package/lib/known-commands.js +1 -1
- package/lib/pull-request-link.js +25 -0
- package/lib/release-kind.js +31 -7
- package/lib/review-integrity.js +52 -5
- package/lib/task-db.js +703 -160
- package/lib/worktree-space.js +4 -45
- package/package.json +1 -1
package/lib/gm-season.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { atrisRating } = require('./atris-rating');
|
|
14
|
-
const { isProofNotSaved } = require('./release-kind');
|
|
14
|
+
const { isProofNotSaved, isCouldNotStart } = require('./release-kind');
|
|
15
15
|
|
|
16
16
|
const HOUR_MS = 60 * 60 * 1000;
|
|
17
17
|
const DAY_MS = 24 * HOUR_MS;
|
|
@@ -22,13 +22,14 @@ const ACTIVE_MISSION_STATUSES = new Set(['running', 'ready']);
|
|
|
22
22
|
const KEPT_ACTIONS = new Set(['accepted', 'merged']);
|
|
23
23
|
const FINISHED = new Set(['landed', 'dropped', 'sent_back', 'abandoned']);
|
|
24
24
|
const FORM_LETTER = { landed: 'k', dropped: 'd', sent_back: 'x', abandoned: 'a' };
|
|
25
|
-
const PLAY_WORD = { took: 'took', landed: 'landed', dropped: 'dropped', sent_back: 'sent back', abandoned: 'abandoned', not_needed: 'not needed', held: 'held' };
|
|
25
|
+
const PLAY_WORD = { took: 'took', landed: 'landed', dropped: 'dropped', sent_back: 'sent back', abandoned: 'abandoned', not_needed: 'not needed', held: 'held', could_not_start: 'unstarted' };
|
|
26
26
|
// A person has this long after a drop to say the job did not need doing.
|
|
27
27
|
const NOT_NEEDED_WINDOW_MS = 7 * DAY_MS;
|
|
28
28
|
|
|
29
29
|
// An outcome is one play:
|
|
30
30
|
// { event_id, task_id, member, title, kind, at, claimed_at, landed_by }
|
|
31
|
-
// kind is took | landed | dropped | sent_back | abandoned | not_needed | held
|
|
31
|
+
// kind is took | landed | dropped | sent_back | abandoned | not_needed | held |
|
|
32
|
+
// could_not_start.
|
|
32
33
|
// abandoned is a claim that ended archived without landing; a job still in
|
|
33
34
|
// review is pending and not counted. not_needed is a drop or abandon that
|
|
34
35
|
// someone other than the member later confirmed the job never needed; it is
|
|
@@ -36,8 +37,14 @@ const NOT_NEEDED_WINDOW_MS = 7 * DAY_MS;
|
|
|
36
37
|
// because the work is done and its pull request waits for a person, but the
|
|
37
38
|
// proof that says so could not be saved; the release says so in a field
|
|
38
39
|
// (lib/release-kind.js), never in its words. It is not finished either, so it
|
|
39
|
-
// is neither a landing nor a miss.
|
|
40
|
-
//
|
|
40
|
+
// is neither a landing nor a miss. could_not_start is a claim a flight let go
|
|
41
|
+
// before the engine ran, because the build machine could not start the job; the
|
|
42
|
+
// release says so in a field the same way. The member never touched the work, so
|
|
43
|
+
// it is not finished and not a miss either. The field checks itself: a claim the
|
|
44
|
+
// flight marked started (a build_started event after its claim, see
|
|
45
|
+
// markBuildStarted in task-db.js) had its engine asked, so a release that says
|
|
46
|
+
// could_not_start on it is a drop like any other. at and claimed_at are epoch
|
|
47
|
+
// ms; claimed_at may be null. landed_by is autoland | you, only on landed.
|
|
41
48
|
|
|
42
49
|
function byTimeAsc(a, b) {
|
|
43
50
|
return Number(a.at || 0) - Number(b.at || 0);
|
|
@@ -50,7 +57,7 @@ function isFinished(outcome) {
|
|
|
50
57
|
// jobs = landed + dropped + sent back + abandoned in the window, the same
|
|
51
58
|
// count the rating divides by. Jobs still in review are pending.
|
|
52
59
|
function tally(outcomes) {
|
|
53
|
-
const out = { took: 0, landed: 0, dropped: 0, sent_back: 0, abandoned: 0, not_needed: 0, held: 0 };
|
|
60
|
+
const out = { took: 0, landed: 0, dropped: 0, sent_back: 0, abandoned: 0, not_needed: 0, held: 0, could_not_start: 0 };
|
|
54
61
|
for (const o of outcomes || []) {
|
|
55
62
|
if (Object.prototype.hasOwnProperty.call(out, o.kind)) out[o.kind] += 1;
|
|
56
63
|
}
|
|
@@ -309,6 +316,18 @@ function parseJson(text) {
|
|
|
309
316
|
}
|
|
310
317
|
}
|
|
311
318
|
|
|
319
|
+
// When a landing happened. A merge a person made on GitHub is written down later, when the sweep
|
|
320
|
+
// notices it, and its event says when the merge was (landed_at). The rating dates the landing by
|
|
321
|
+
// that, so a merge swept in weeks late does not count as news. It is never later than the event
|
|
322
|
+
// itself, so a time in the future cannot hold a landing inside the window; a time that cannot be
|
|
323
|
+
// read leaves the landing dated when it was written down.
|
|
324
|
+
function landedAt(row, payload) {
|
|
325
|
+
const written = Number(row.created_at);
|
|
326
|
+
const text = payload && typeof payload.landed_at === 'string' ? payload.landed_at.trim() : '';
|
|
327
|
+
const merged = text ? Date.parse(text) : NaN;
|
|
328
|
+
return Number.isFinite(merged) && Number.isFinite(written) ? Math.min(merged, written) : written;
|
|
329
|
+
}
|
|
330
|
+
|
|
312
331
|
function landedBy(row, payload) {
|
|
313
332
|
if (payload && (payload.auto_accepted === true || payload.self_landed === true)) return 'autoland';
|
|
314
333
|
return /auto/i.test(String(row.actor || '')) ? 'autoland' : 'you';
|
|
@@ -330,8 +349,13 @@ function outcomeFromEvent(row) {
|
|
|
330
349
|
member = row.actor || payload.claimed_by;
|
|
331
350
|
} else if (row.event_type === 'claim_released') {
|
|
332
351
|
// A release that carries the proof-not-saved fields is work waiting on a person,
|
|
333
|
-
// not a drop.
|
|
334
|
-
|
|
352
|
+
// not a drop. One that carries the could-not-start kind was let go before the
|
|
353
|
+
// engine ran, so it is not a drop either, unless its claim was marked started
|
|
354
|
+
// (engine_started): then the kind is false and it is a drop. Only the fields say
|
|
355
|
+
// so: the same words with no fields stay a drop.
|
|
356
|
+
kind = isProofNotSaved(payload) ? 'held'
|
|
357
|
+
: isCouldNotStart(payload) && Number(row.engine_started) !== 1 ? 'could_not_start'
|
|
358
|
+
: 'dropped';
|
|
335
359
|
member = payload.previous_claimed_by || payload.released_by || row.actor;
|
|
336
360
|
} else if (row.event_type === 'claim_expired') {
|
|
337
361
|
kind = 'dropped';
|
|
@@ -365,7 +389,7 @@ function outcomeFromEvent(row) {
|
|
|
365
389
|
member: String(member),
|
|
366
390
|
title: row.title || '',
|
|
367
391
|
kind,
|
|
368
|
-
at: row.event_type === 'claim_expired' && Number.isFinite(leaseUntil) ? leaseUntil : Number(row.created_at),
|
|
392
|
+
at: row.event_type === 'claim_expired' && Number.isFinite(leaseUntil) ? leaseUntil : kind === 'landed' ? landedAt(row, payload) : Number(row.created_at),
|
|
369
393
|
reason: kind !== 'took' && kind !== 'landed' ? (typeof payload.reason === 'string' && payload.reason.trim() || typeof payload.note === 'string' && payload.note.trim() || null) : null,
|
|
370
394
|
claimed_at: claimedAt,
|
|
371
395
|
landed_by: kind === 'landed' ? landedBy(row, payload) : null,
|
|
@@ -420,7 +444,11 @@ function outcomesFromEvents(rows, since = -Infinity) {
|
|
|
420
444
|
const ended = ['claim_released', 'claim_expired', 'completed', 'reopened', 'archived'].includes(row.event_type);
|
|
421
445
|
if (!play) { if (ended) closed.add(key); continue; }
|
|
422
446
|
if (play.kind === 'took') { plays.push(play); continue; }
|
|
423
|
-
|
|
447
|
+
// A release that another member's claim follows within two minutes reads as a hand-over, and is left out. That
|
|
448
|
+
// is a rule about old history, whose releases carry no mark, and it holds for them as it always did. A release
|
|
449
|
+
// on a claim the engine started (engine_started) is a real miss whoever claims next, and one that could not
|
|
450
|
+
// start has its own line: neither is a hand-over, so neither is erased.
|
|
451
|
+
if (row.event_type === 'claim_released' && Number(row.engine_started) !== 1 && play.kind !== 'could_not_start') {
|
|
424
452
|
let next = null;
|
|
425
453
|
for (let j = i + 1; j < ordered.length && ordered[j].task_id === row.task_id; j++) {
|
|
426
454
|
if (ordered[j].event_type === 'claimed') { next = ordered[j]; break; }
|
|
@@ -555,6 +583,12 @@ function playEventsSql(extraWhere = '') {
|
|
|
555
583
|
SELECT e.event_id, e.task_id, e.workspace_root, e.event_type, e.actor, e.payload, e.created_at, e.version,
|
|
556
584
|
t.title, t.claimed_by, t.claimed_at,
|
|
557
585
|
c.event_id AS claim_event_id, c.created_at AS claim_event_at, c.actor AS claim_actor,
|
|
586
|
+
CASE WHEN e.event_type = 'claim_released' AND c.event_id IS NOT NULL AND EXISTS (
|
|
587
|
+
SELECT 1 FROM task_events b
|
|
588
|
+
WHERE b.task_id = e.task_id AND b.version > c.version AND b.version < e.version
|
|
589
|
+
-- the flight marks a claim started right before it asks the assigned engine
|
|
590
|
+
AND b.event_type = 'build_started'
|
|
591
|
+
) THEN 1 ELSE 0 END AS engine_started,
|
|
558
592
|
CASE WHEN e.event_type = 'archived' AND c.event_id IS NOT NULL AND NOT EXISTS (
|
|
559
593
|
SELECT 1 FROM task_events x
|
|
560
594
|
WHERE x.task_id = e.task_id AND x.version > c.version AND x.version < e.version
|
|
@@ -643,6 +677,9 @@ function loadSeason(workspaceRoot, { now = Date.now(), days = SEASON_DAYS, dbPat
|
|
|
643
677
|
title: t.title,
|
|
644
678
|
member: String(t.claimed_by),
|
|
645
679
|
since: Number(t.proof_at || t.last_event_at || t.updated_at || 0),
|
|
680
|
+
// Done, and its pull request still open for a person to land. The rating never asks
|
|
681
|
+
// GitHub; `atris task sweep --held` does, and credits the builder when it was merged.
|
|
682
|
+
...(taskDb.heldPullRequestOf(t) ? { waiting_on_pull_request: true } : {}),
|
|
646
683
|
}));
|
|
647
684
|
season.claims = live
|
|
648
685
|
.filter(t => t.status === 'claimed' && t.claimed_by)
|