@volter/supercode-orchestrator 0.4.153 → 0.4.154
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/board/ci.mjs +15 -2
- package/board/workflow.mjs +21 -8
- package/package.json +1 -1
package/board/ci.mjs
CHANGED
|
@@ -126,6 +126,20 @@ function registeredBranches(board, id) {
|
|
|
126
126
|
return out;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
/** Whether `head` is a branch named for card `id`: `wt/<id>` (or `wt/t-…`), alone or with a `-…`/`/…` suffix. */
|
|
130
|
+
export const namedWorkBranch = (id, head) => [`wt/${id}`, `wt/${id.replace(/^t_/, 't-')}`].some((prefix) => head === prefix || head.startsWith(`${prefix}-`) || head.startsWith(`${prefix}/`));
|
|
131
|
+
|
|
132
|
+
/** The branch names an arc declares as its work: its own and its subtasks' `--branch`, and each branch its latest
|
|
133
|
+
* review request covered. With `namedWorkBranch`, these are the branches its PRs are opened from. */
|
|
134
|
+
export function declaredWorkBranches(db, id) {
|
|
135
|
+
const declared = db.prepare(`SELECT t.branch_name FROM tasks t LEFT JOIN supercode_cards c ON c.task_id=t.id
|
|
136
|
+
WHERE (t.id=? OR c.subtask_of=?) AND t.status != 'cancelled' AND t.branch_name IS NOT NULL`).all(id, id).map((r) => r.branch_name);
|
|
137
|
+
const row = db.prepare("SELECT payload FROM task_events WHERE task_id=? AND kind='review_requested' ORDER BY id DESC LIMIT 1").get(id);
|
|
138
|
+
let reviewed = [];
|
|
139
|
+
try { reviewed = candidatePullRequests(row && JSON.parse(row.payload)?.ci).map((p) => p.branch); } catch { /* an unreadable payload names none */ }
|
|
140
|
+
return [...new Set([...declared, ...reviewed].filter(Boolean))];
|
|
141
|
+
}
|
|
142
|
+
|
|
129
143
|
/** Every opened PR of the arc, each proposed to its repository's default branch: an open PR whose head is the card's
|
|
130
144
|
* work branch in the card's own repository, a `wt/<card id>` branch in any repository this machine keeps a checkout
|
|
131
145
|
* of, or a sibling checkout the arc registered (`registeredBranches`). The repository's default branch, declared or
|
|
@@ -140,8 +154,7 @@ export function openReviewPullRequests(board, id) {
|
|
|
140
154
|
const current = task?.branch_name ? null : run('git', ['branch', '--show-current'], own);
|
|
141
155
|
let branch = task?.branch_name ?? (current || null);
|
|
142
156
|
const registered = registeredBranches(board, id);
|
|
143
|
-
const
|
|
144
|
-
const mine = (repository, head) => (repository === ownRepository && head === branch) || named(head)
|
|
157
|
+
const mine = (repository, head) => (repository === ownRepository && head === branch) || namedWorkBranch(id, head)
|
|
145
158
|
|| registered.some((r) => r.repository === repository && r.branch === head);
|
|
146
159
|
const prs = [];
|
|
147
160
|
for (const repository of new Set([ownRepository, ...registered.map((r) => r.repository), ...knownRepositories()])) {
|
package/board/workflow.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { resolve, relative, isAbsolute } from 'node:path';
|
|
|
11
11
|
import { randomBytes } from 'node:crypto';
|
|
12
12
|
import { actorOf, appendRunObservation, event as record, json, now } from './store.mjs';
|
|
13
13
|
import { evaluate, holds, render } from './expr.mjs';
|
|
14
|
-
import { completionEvidence, reviewCandidate } from './ci.mjs';
|
|
14
|
+
import { completionEvidence, declaredWorkBranches, namedWorkBranch, reviewCandidate } from './ci.mjs';
|
|
15
15
|
import { isWorkspacePath } from './workspace.mjs';
|
|
16
16
|
import { identifyNotice } from './notices.mjs';
|
|
17
17
|
|
|
@@ -103,17 +103,29 @@ function pattern(p) {
|
|
|
103
103
|
return text.startsWith('(?i)') ? new RegExp(text.slice(4), 'i') : new RegExp(text);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
// the time of the latest commit that names `id` in its message
|
|
107
|
-
//
|
|
108
|
-
//
|
|
106
|
+
// the time of the latest commit that names `id` in its message on the card's work in the repository at `path`, or null
|
|
107
|
+
// (asked only when an expression does). Its work is the branches its PRs are opened from (the checkout's own branch,
|
|
108
|
+
// a declared or reviewed one, `wt/<id>…`), local or remote-tracking (a worktree's pushes land on the shared refs),
|
|
109
|
+
// and the default branch they merge into. Another ref naming the card (an evidence branch, a later card's work) is not
|
|
110
|
+
// a change to its reviewed work. Merge commits are not the card's work: merging its reviewed PR, or main into its
|
|
111
|
+
// branch, is not a newer change to review.
|
|
109
112
|
const commitTimes = new Map();
|
|
110
|
-
function observeLastCommit(path, id) {
|
|
113
|
+
function observeLastCommit(path, id, branches = []) {
|
|
111
114
|
if (!path) return null;
|
|
112
115
|
const key = `${path}:${id}`;
|
|
113
116
|
const cached = commitTimes.get(key);
|
|
114
117
|
if (cached && now() - cached.at < 15) return cached.value;
|
|
118
|
+
const git = (repo, args) => spawnSync('git', ['-C', repo, ...args], { encoding: 'utf8', timeout: 10_000 });
|
|
115
119
|
const readCommit = (repo) => {
|
|
116
|
-
const
|
|
120
|
+
const listed = git(repo, ['for-each-ref', '--format=%(refname)', 'refs/heads', 'refs/remotes']);
|
|
121
|
+
if (listed.status !== 0) return { repo: false, value: null };
|
|
122
|
+
const current = git(repo, ['branch', '--show-current']).stdout?.trim();
|
|
123
|
+
const fallback = git(repo, ['symbolic-ref', '--quiet', 'refs/remotes/origin/HEAD']).stdout?.trim().replace(/^refs\/remotes\/origin\//, '');
|
|
124
|
+
const names = new Set([...branches, current, fallback].filter(Boolean));
|
|
125
|
+
const short = (ref) => ref.startsWith('refs/heads/') ? ref.slice(11) : ref.split('/').slice(3).join('/');
|
|
126
|
+
const refs = listed.stdout.split('\n').filter((ref) => ref && !ref.endsWith('/HEAD') && (names.has(short(ref)) || namedWorkBranch(id, short(ref))));
|
|
127
|
+
if (!refs.length) return { repo: true, value: null };
|
|
128
|
+
const r = git(repo, ['log', ...refs, '--no-merges', '-1', '--format=%ct', '--fixed-strings', `--grep=${id}`, '--']);
|
|
117
129
|
const value = Number(r.stdout?.trim());
|
|
118
130
|
return { repo: r.status === 0, value: Number.isFinite(value) && value > 0 ? value : null };
|
|
119
131
|
};
|
|
@@ -145,7 +157,7 @@ export function refreshCardInputs(board, id, { commits = false } = {}) {
|
|
|
145
157
|
const parent=!own && task.subtask_of?board.db.prepare('SELECT workspace_path FROM tasks WHERE id=?').get(task.subtask_of)?.workspace_path:null;
|
|
146
158
|
const path=own??parent??null;
|
|
147
159
|
if(path)commitTimes.delete(`${path}:${id}`);
|
|
148
|
-
observeLastCommit(path,id);
|
|
160
|
+
observeLastCommit(path,id,declaredWorkBranches(board.db,id));
|
|
149
161
|
}
|
|
150
162
|
}
|
|
151
163
|
|
|
@@ -307,7 +319,8 @@ export function scopeOf(db, board, task, { params, event = {}, extra = {} } = {}
|
|
|
307
319
|
last_events: Object.fromEntries(all(db, 'SELECT e.kind, e.created_at, e.payload FROM task_events e JOIN (SELECT kind, MAX(id) AS id FROM task_events WHERE task_id = ? GROUP BY kind) l ON l.id = e.id', task.id)
|
|
308
320
|
.map((r) => [r.kind, { created_at: r.created_at, payload: json(r.payload, {}) ?? {} }])),
|
|
309
321
|
comments_since: (t) => all(db, 'SELECT author, body, created_at FROM task_comments WHERE task_id = ? AND created_at >= ?', task.id, t),
|
|
310
|
-
// the latest commit naming this card, in its workspace's repository (a
|
|
322
|
+
// the latest commit naming this card on its work branches and default branch, in its workspace's repository (a
|
|
323
|
+
// subtask's: its arc's), as epoch seconds
|
|
311
324
|
last_commit_at: () => {
|
|
312
325
|
const own = task.workspace_kind !== 'scratch' ? task.workspace_path : null;
|
|
313
326
|
const arcWs = !own && task.subtask_of ? one(db, "SELECT workspace_path FROM tasks WHERE id = ? AND workspace_kind != 'scratch'", task.subtask_of)?.workspace_path : null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.154",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The orchestrator runtime over the Volter Harness ontology: one typed operational model whose folder is its serialization, read and written through the harness orchestration doors (docs/ORCHESTRATOR-IR.md)",
|
|
6
6
|
"exports": {
|