@valescoagency/runway 0.11.1 → 0.12.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/README.md +1 -1
- package/dist/dashboard/linear-sync.js +11 -1
- package/dist/dashboard/storage.js +19 -7
- package/dist/dashboard/views.js +13 -1
- package/dist/telemetry.js +10 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -541,7 +541,7 @@ These are tractable, just not v1.
|
|
|
541
541
|
|
|
542
542
|
## Status
|
|
543
543
|
|
|
544
|
-
0.
|
|
544
|
+
0.12.0 — production-shaped and dogfooded against live Linear queues.
|
|
545
545
|
The end-to-end pipeline (init → run → review → PR) is stable; surface
|
|
546
546
|
may still shift as the orchestrator's policy and iteration mechanics
|
|
547
547
|
mature. See [CHANGELOG.md](./CHANGELOG.md) for per-release detail.
|
|
@@ -15,12 +15,18 @@ export function createLinearAdapter(opts) {
|
|
|
15
15
|
const teamKey = opts.team ?? "VA";
|
|
16
16
|
const readyLabel = opts.readyLabel ?? "ready-for-agent";
|
|
17
17
|
async function snapshotFromIssue(raw) {
|
|
18
|
-
const [state, labels] = await Promise.all([
|
|
18
|
+
const [state, labels, project] = await Promise.all([
|
|
19
|
+
raw.state,
|
|
20
|
+
raw.labels(),
|
|
21
|
+
raw.project,
|
|
22
|
+
]);
|
|
19
23
|
return {
|
|
20
24
|
identifier: raw.identifier,
|
|
21
25
|
title: raw.title,
|
|
22
26
|
status: state?.name ?? "",
|
|
23
27
|
labels: labels.nodes.map((l) => l.name),
|
|
28
|
+
projectId: project?.id ?? null,
|
|
29
|
+
projectName: project?.name ?? null,
|
|
24
30
|
};
|
|
25
31
|
}
|
|
26
32
|
return {
|
|
@@ -113,6 +119,8 @@ export function startLinearSync(opts) {
|
|
|
113
119
|
title: q.title,
|
|
114
120
|
labels: q.labels,
|
|
115
121
|
queuePosition: i,
|
|
122
|
+
projectId: q.projectId,
|
|
123
|
+
projectName: q.projectName,
|
|
116
124
|
});
|
|
117
125
|
});
|
|
118
126
|
log("info", `[runway dashboard] linear sync: refreshed ready queue (${queue.length} issue${queue.length === 1 ? "" : "s"})`);
|
|
@@ -139,6 +147,8 @@ export function startLinearSync(opts) {
|
|
|
139
147
|
title: r.title,
|
|
140
148
|
labels: r.labels,
|
|
141
149
|
queuePosition: null,
|
|
150
|
+
projectId: r.projectId,
|
|
151
|
+
projectName: r.projectName,
|
|
142
152
|
});
|
|
143
153
|
}
|
|
144
154
|
log("info", `[runway dashboard] linear sync: refreshed status for ${refreshed.length} of ${recent.length} recent issue${recent.length === 1 ? "" : "s"}`);
|
|
@@ -99,7 +99,9 @@ const SCHEMA = `
|
|
|
99
99
|
status TEXT NOT NULL,
|
|
100
100
|
title TEXT NOT NULL,
|
|
101
101
|
labels_json TEXT,
|
|
102
|
-
queue_position INTEGER
|
|
102
|
+
queue_position INTEGER,
|
|
103
|
+
project_id TEXT,
|
|
104
|
+
project_name TEXT
|
|
103
105
|
);
|
|
104
106
|
|
|
105
107
|
CREATE INDEX IF NOT EXISTS idx_linear_snapshots_queue_position
|
|
@@ -289,6 +291,9 @@ export function createStorage(path, opts = {}) {
|
|
|
289
291
|
`ALTER TABLE issue_processes ADD COLUMN issue_labels TEXT`,
|
|
290
292
|
`ALTER TABLE issue_processes ADD COLUMN pr_url TEXT`,
|
|
291
293
|
`ALTER TABLE issue_processes ADD COLUMN hitl_reason TEXT`,
|
|
294
|
+
// VA-450: project Name + UUID for the Todo queue Project column.
|
|
295
|
+
`ALTER TABLE linear_snapshots ADD COLUMN project_id TEXT`,
|
|
296
|
+
`ALTER TABLE linear_snapshots ADD COLUMN project_name TEXT`,
|
|
292
297
|
]) {
|
|
293
298
|
try {
|
|
294
299
|
db.exec(sql);
|
|
@@ -403,24 +408,29 @@ export function createStorage(path, opts = {}) {
|
|
|
403
408
|
const selectAggregates = db.prepare(`SELECT * FROM evaluator_aggregates_v1`);
|
|
404
409
|
const upsertLinearSnapshot = db.prepare(`
|
|
405
410
|
INSERT INTO linear_snapshots (
|
|
406
|
-
issue_identifier, snapshot_at, status, title, labels_json, queue_position
|
|
407
|
-
|
|
411
|
+
issue_identifier, snapshot_at, status, title, labels_json, queue_position,
|
|
412
|
+
project_id, project_name
|
|
413
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
408
414
|
ON CONFLICT (issue_identifier) DO UPDATE SET
|
|
409
415
|
snapshot_at = excluded.snapshot_at,
|
|
410
416
|
status = excluded.status,
|
|
411
417
|
title = excluded.title,
|
|
412
418
|
labels_json = excluded.labels_json,
|
|
413
|
-
queue_position = excluded.queue_position
|
|
419
|
+
queue_position = excluded.queue_position,
|
|
420
|
+
project_id = excluded.project_id,
|
|
421
|
+
project_name = excluded.project_name
|
|
414
422
|
`);
|
|
415
423
|
const clearQueuePositionsStmt = db.prepare(`UPDATE linear_snapshots SET queue_position = NULL`);
|
|
416
424
|
const listTodoQueueStmt = db.prepare(`
|
|
417
|
-
SELECT issue_identifier, snapshot_at, status, title, labels_json, queue_position
|
|
425
|
+
SELECT issue_identifier, snapshot_at, status, title, labels_json, queue_position,
|
|
426
|
+
project_id, project_name
|
|
418
427
|
FROM linear_snapshots
|
|
419
428
|
WHERE queue_position IS NOT NULL
|
|
420
429
|
ORDER BY queue_position ASC
|
|
421
430
|
`);
|
|
422
431
|
const listAllSnapshotsStmt = db.prepare(`
|
|
423
|
-
SELECT issue_identifier, snapshot_at, status, title, labels_json, queue_position
|
|
432
|
+
SELECT issue_identifier, snapshot_at, status, title, labels_json, queue_position,
|
|
433
|
+
project_id, project_name
|
|
424
434
|
FROM linear_snapshots
|
|
425
435
|
`);
|
|
426
436
|
// VA-391: "active drain" = a trace_id with one or more
|
|
@@ -616,7 +626,7 @@ export function createStorage(path, opts = {}) {
|
|
|
616
626
|
};
|
|
617
627
|
const listAggregates = () => selectAggregates.all().map(rowToAggregate);
|
|
618
628
|
const saveLinearSnapshot = (s) => {
|
|
619
|
-
upsertLinearSnapshot.run(s.issueIdentifier, s.snapshotAt, s.status, s.title, s.labels.length === 0 ? null : JSON.stringify(s.labels), s.queuePosition);
|
|
629
|
+
upsertLinearSnapshot.run(s.issueIdentifier, s.snapshotAt, s.status, s.title, s.labels.length === 0 ? null : JSON.stringify(s.labels), s.queuePosition, s.projectId, s.projectName);
|
|
620
630
|
};
|
|
621
631
|
const clearLinearQueuePositions = () => {
|
|
622
632
|
clearQueuePositionsStmt.run();
|
|
@@ -860,6 +870,8 @@ function rowToLinearSnapshot(row) {
|
|
|
860
870
|
title: String(r.title ?? ""),
|
|
861
871
|
labels: parseLabels(r.labels_json),
|
|
862
872
|
queuePosition: nullableNum(r.queue_position),
|
|
873
|
+
projectId: r.project_id == null ? null : String(r.project_id),
|
|
874
|
+
projectName: r.project_name == null ? null : String(r.project_name),
|
|
863
875
|
};
|
|
864
876
|
}
|
|
865
877
|
/**
|
package/dist/dashboard/views.js
CHANGED
|
@@ -33,6 +33,7 @@ const SHARED_STYLE = `
|
|
|
33
33
|
.id { color: #93c5fd; }
|
|
34
34
|
.detail { color: #d4d4d8; }
|
|
35
35
|
.muted { color: #9ca3af; }
|
|
36
|
+
.project-uuid { color: #9ca3af; font-size: 11px; display: block; }
|
|
36
37
|
code { background: #1f2937; padding: 1px 6px; border-radius: 3px; }
|
|
37
38
|
.status-badge { display: inline-block; margin-left: 8px;
|
|
38
39
|
padding: 1px 6px; border-radius: 3px; font-size: 11px;
|
|
@@ -316,7 +317,7 @@ export function filterStateToQueryString(state) {
|
|
|
316
317
|
*/
|
|
317
318
|
function renderTodoQueue(queue) {
|
|
318
319
|
const body = queue.length === 0
|
|
319
|
-
? `<tr><td colspan="
|
|
320
|
+
? `<tr><td colspan="4" class="empty">Linear Todo queue is empty.</td></tr>`
|
|
320
321
|
: queue.map(renderQueueRow).join("");
|
|
321
322
|
return `<h2>Todo queue (next up)</h2>
|
|
322
323
|
<table>
|
|
@@ -325,6 +326,7 @@ function renderTodoQueue(queue) {
|
|
|
325
326
|
<th>Issue</th>
|
|
326
327
|
<th>Title</th>
|
|
327
328
|
<th>Labels</th>
|
|
329
|
+
<th>Project</th>
|
|
328
330
|
</tr>
|
|
329
331
|
</thead>
|
|
330
332
|
<tbody>${body}</tbody>
|
|
@@ -332,10 +334,20 @@ function renderTodoQueue(queue) {
|
|
|
332
334
|
}
|
|
333
335
|
function renderQueueRow(s) {
|
|
334
336
|
const labels = s.labels.length === 0 ? "" : s.labels.join(", ");
|
|
337
|
+
// VA-450: project cell shows the name on top with the UUID muted
|
|
338
|
+
// underneath in the same column. Unprojected issues degrade to an
|
|
339
|
+
// em-dash so the row still renders (the drain WILL pick them up).
|
|
340
|
+
const project = s.projectName == null && s.projectId == null
|
|
341
|
+
? `<span class="muted">—</span>`
|
|
342
|
+
: `<span>${escapeHtml(s.projectName ?? "")}</span>` +
|
|
343
|
+
(s.projectId == null
|
|
344
|
+
? ""
|
|
345
|
+
: `<span class="project-uuid">${escapeHtml(s.projectId)}</span>`);
|
|
335
346
|
return `<tr>
|
|
336
347
|
<td class="id">${escapeHtml(s.issueIdentifier)}</td>
|
|
337
348
|
<td class="detail">${escapeHtml(s.title)}</td>
|
|
338
349
|
<td class="muted">${escapeHtml(labels)}</td>
|
|
350
|
+
<td class="detail">${project}</td>
|
|
339
351
|
</tr>`;
|
|
340
352
|
}
|
|
341
353
|
function renderRow(r, snapshots) {
|
package/dist/telemetry.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NodeSdk } from "@effect/opentelemetry";
|
|
2
2
|
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
3
3
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
4
4
|
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
|
5
5
|
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
6
|
-
import { Layer } from "effect";
|
|
7
6
|
/**
|
|
8
7
|
* VA-358 + VA-388: OpenTelemetry telemetry layer for the orchestrator.
|
|
9
8
|
*
|
|
@@ -45,11 +44,14 @@ const liveLayer = NodeSdk.layer(() => ({
|
|
|
45
44
|
// paths.
|
|
46
45
|
logRecordProcessor: new BatchLogRecordProcessor(new OTLPLogExporter()),
|
|
47
46
|
}));
|
|
48
|
-
// VA-388:
|
|
49
|
-
//
|
|
50
|
-
// `
|
|
51
|
-
//
|
|
52
|
-
|
|
47
|
+
// VA-388: when `logRecordProcessor` is set above, `NodeSdk.layer`
|
|
48
|
+
// internally composes `Logger.layerLoggerAdd` against an internal
|
|
49
|
+
// `OtelLoggerProvider`, so `Effect.log` calls fan out to OTLP in
|
|
50
|
+
// addition to the default stderr logger. An earlier revision wrapped
|
|
51
|
+
// `Logger.layerLoggerReplace` around `liveLayer` to silence stderr,
|
|
52
|
+
// but `NodeSdk.layer` consumes `OtelLoggerProvider` via `Layer.provide`
|
|
53
|
+
// internally and does not re-export it — that composition raised
|
|
54
|
+
// "Service not found: OtelLoggerProvider" at startup.
|
|
53
55
|
export const TelemetryLive = isTelemetryEnabled()
|
|
54
|
-
?
|
|
56
|
+
? liveLayer
|
|
55
57
|
: NodeSdk.layerEmpty;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@valescoagency/runway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Linear-driven orchestrator + scaffolder for coding agents on Sandcastle. `runway init` scaffolds a target repo (sandcastle + varlock + 1Password); `runway run` drains a Linear queue against it; `runway doctor`, `runway upgrade`, `runway upgrade-repo` round out the lifecycle.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|