feat: track linked work items in status

This commit is contained in:
Affaan Mustafa 2026-05-11 11:51:45 -04:00 committed by Affaan Mustafa
parent 579284c9be
commit 8926ea925e
7 changed files with 396 additions and 7 deletions

View File

@ -94,7 +94,7 @@ This repo is the raw code only. The guides explain everything.
- **Media and launch tooling**`manim-video`, `remotion-video-creation`, and upgraded social publishing surfaces make technical explainers and launch content part of the same system. - **Media and launch tooling**`manim-video`, `remotion-video-creation`, and upgraded social publishing surfaces make technical explainers and launch content part of the same system.
- **Framework and product surface growth**`nestjs-patterns`, richer Codex/OpenCode install surfaces, and expanded cross-harness packaging keep the repo usable beyond Claude Code alone. - **Framework and product surface growth**`nestjs-patterns`, richer Codex/OpenCode install surfaces, and expanded cross-harness packaging keep the repo usable beyond Claude Code alone.
- **ECC 2.0 alpha is in-tree** — the Rust control-plane prototype in `ecc2/` now builds locally and exposes `dashboard`, `start`, `sessions`, `status`, `stop`, `resume`, and `daemon` commands. It is usable as an alpha, not yet a general release. - **ECC 2.0 alpha is in-tree** — the Rust control-plane prototype in `ecc2/` now builds locally and exposes `dashboard`, `start`, `sessions`, `status`, `stop`, `resume`, and `daemon` commands. It is usable as an alpha, not yet a general release.
- **Operator status snapshots**`ecc status --markdown --write status.md` turns the local state store into a portable handoff covering readiness, active sessions, skill-run health, install health, and pending governance events. - **Operator status snapshots**`ecc status --markdown --write status.md` turns the local state store into a portable handoff covering readiness, active sessions, skill-run health, install health, pending governance events, and linked work items from Linear/GitHub/handoffs.
- **Ecosystem hardening** — AgentShield, ECC Tools cost controls, billing portal work, and website refreshes continue to ship around the core plugin instead of drifting into separate silos. - **Ecosystem hardening** — AgentShield, ECC Tools cost controls, billing portal work, and website refreshes continue to ship around the core plugin instead of drifting into separate silos.
### v1.9.0 — Selective Install & Language Expansion (Mar 2026) ### v1.9.0 — Selective Install & Language Expansion (Mar 2026)

View File

@ -40,6 +40,12 @@
"items": { "items": {
"$ref": "#/$defs/governanceEvent" "$ref": "#/$defs/governanceEvent"
} }
},
"workItems": {
"type": "array",
"items": {
"$ref": "#/$defs/workItem"
}
} }
}, },
"$defs": { "$defs": {
@ -311,6 +317,66 @@
"$ref": "#/$defs/nonEmptyString" "$ref": "#/$defs/nonEmptyString"
} }
} }
},
"workItem": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"source",
"sourceId",
"title",
"status",
"priority",
"url",
"owner",
"repoRoot",
"sessionId",
"metadata",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"$ref": "#/$defs/nonEmptyString"
},
"source": {
"$ref": "#/$defs/nonEmptyString"
},
"sourceId": {
"$ref": "#/$defs/nullableString"
},
"title": {
"$ref": "#/$defs/nonEmptyString"
},
"status": {
"$ref": "#/$defs/nonEmptyString"
},
"priority": {
"$ref": "#/$defs/nullableString"
},
"url": {
"$ref": "#/$defs/nullableString"
},
"owner": {
"$ref": "#/$defs/nullableString"
},
"repoRoot": {
"$ref": "#/$defs/nullableString"
},
"sessionId": {
"$ref": "#/$defs/nullableString"
},
"metadata": {
"$ref": "#/$defs/jsonValue"
},
"createdAt": {
"$ref": "#/$defs/nonEmptyString"
},
"updatedAt": {
"$ref": "#/$defs/nonEmptyString"
}
}
} }
} }
} }

View File

@ -107,12 +107,43 @@ CREATE INDEX IF NOT EXISTS idx_governance_events_session_id_created_at
ON governance_events (session_id, created_at DESC); ON governance_events (session_id, created_at DESC);
`; `;
const WORK_ITEMS_SQL = `
CREATE TABLE IF NOT EXISTS work_items (
id TEXT PRIMARY KEY,
source TEXT NOT NULL,
source_id TEXT,
title TEXT NOT NULL,
status TEXT NOT NULL,
priority TEXT,
url TEXT,
owner TEXT,
repo_root TEXT,
session_id TEXT,
metadata TEXT NOT NULL CHECK (json_valid(metadata)),
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY (session_id) REFERENCES sessions (id) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS idx_work_items_status_updated_at
ON work_items (status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_work_items_source_source_id
ON work_items (source, source_id);
CREATE INDEX IF NOT EXISTS idx_work_items_session_id_updated_at
ON work_items (session_id, updated_at DESC);
`;
const MIGRATIONS = [ const MIGRATIONS = [
{ {
version: 1, version: 1,
name: '001_initial_state_store', name: '001_initial_state_store',
sql: INITIAL_SCHEMA_SQL, sql: INITIAL_SCHEMA_SQL,
}, },
{
version: 2,
name: '002_work_items',
sql: WORK_ITEMS_SQL,
},
]; ];
function ensureMigrationTable(db) { function ensureMigrationTable(db) {

View File

@ -5,6 +5,8 @@ const { assertValidEntity } = require('./schema');
const ACTIVE_SESSION_STATES = ['active', 'running', 'idle']; const ACTIVE_SESSION_STATES = ['active', 'running', 'idle'];
const SUCCESS_OUTCOMES = new Set(['success', 'succeeded', 'passed']); const SUCCESS_OUTCOMES = new Set(['success', 'succeeded', 'passed']);
const FAILURE_OUTCOMES = new Set(['failure', 'failed', 'error']); const FAILURE_OUTCOMES = new Set(['failure', 'failed', 'error']);
const CLOSED_WORK_ITEM_STATUSES = new Set(['done', 'closed', 'resolved', 'merged', 'cancelled']);
const ATTENTION_WORK_ITEM_STATUSES = new Set(['blocked', 'needs-review', 'failed', 'stalled']);
function normalizeLimit(value, fallback) { function normalizeLimit(value, fallback) {
if (value === undefined || value === null) { if (value === undefined || value === null) {
@ -121,6 +123,24 @@ function mapGovernanceEventRow(row) {
}; };
} }
function mapWorkItemRow(row) {
return {
id: row.id,
source: row.source,
sourceId: row.source_id,
title: row.title,
status: row.status,
priority: row.priority,
url: row.url,
owner: row.owner,
repoRoot: row.repo_root,
sessionId: row.session_id,
metadata: parseJsonColumn(row.metadata, null),
createdAt: row.created_at,
updatedAt: row.updated_at,
};
}
function classifyOutcome(outcome) { function classifyOutcome(outcome) {
const normalized = String(outcome || '').toLowerCase(); const normalized = String(outcome || '').toLowerCase();
if (SUCCESS_OUTCOMES.has(normalized)) { if (SUCCESS_OUTCOMES.has(normalized)) {
@ -134,6 +154,19 @@ function classifyOutcome(outcome) {
return 'unknown'; return 'unknown';
} }
function classifyWorkItemStatus(status) {
const normalized = String(status || '').toLowerCase();
if (CLOSED_WORK_ITEM_STATUSES.has(normalized)) {
return 'closed';
}
if (ATTENTION_WORK_ITEM_STATUSES.has(normalized)) {
return 'attention';
}
return 'open';
}
function toPercent(numerator, denominator) { function toPercent(numerator, denominator) {
if (denominator === 0) { if (denominator === 0) {
return null; return null;
@ -202,11 +235,36 @@ function summarizeInstallHealth(installations) {
}; };
} }
function summarizeReadiness({ activeSessionCount, skillRuns, installHealth, pendingGovernanceCount }) { function summarizeWorkItems(workItems) {
const summary = {
totalCount: workItems.length,
openCount: 0,
blockedCount: 0,
closedCount: 0,
items: workItems,
};
for (const workItem of workItems) {
const classification = classifyWorkItemStatus(workItem.status);
if (classification === 'closed') {
summary.closedCount += 1;
} else if (classification === 'attention') {
summary.openCount += 1;
summary.blockedCount += 1;
} else {
summary.openCount += 1;
}
}
return summary;
}
function summarizeReadiness({ activeSessionCount, skillRuns, installHealth, pendingGovernanceCount, workItems }) {
const failedSkillRuns = skillRuns.summary.failureCount; const failedSkillRuns = skillRuns.summary.failureCount;
const warningInstallations = installHealth.warningCount; const warningInstallations = installHealth.warningCount;
const pendingGovernanceEvents = pendingGovernanceCount; const pendingGovernanceEvents = pendingGovernanceCount;
const attentionCount = failedSkillRuns + warningInstallations + pendingGovernanceEvents; const blockedWorkItems = workItems.blockedCount;
const attentionCount = failedSkillRuns + warningInstallations + pendingGovernanceEvents + blockedWorkItems;
return { return {
status: attentionCount > 0 ? 'attention' : 'ok', status: attentionCount > 0 ? 'attention' : 'ok',
@ -215,6 +273,7 @@ function summarizeReadiness({ activeSessionCount, skillRuns, installHealth, pend
failedSkillRuns, failedSkillRuns,
warningInstallations, warningInstallations,
pendingGovernanceEvents, pendingGovernanceEvents,
blockedWorkItems,
}; };
} }
@ -301,6 +360,25 @@ function normalizeGovernanceEventInput(governanceEvent) {
}; };
} }
function normalizeWorkItemInput(workItem) {
const now = new Date().toISOString();
return {
id: workItem.id,
source: workItem.source,
sourceId: workItem.sourceId ?? null,
title: workItem.title,
status: workItem.status,
priority: workItem.priority ?? null,
url: workItem.url ?? null,
owner: workItem.owner ?? null,
repoRoot: workItem.repoRoot ?? null,
sessionId: workItem.sessionId ?? null,
metadata: workItem.metadata ?? null,
createdAt: workItem.createdAt || now,
updatedAt: workItem.updatedAt || now,
};
}
function createQueryApi(db) { function createQueryApi(db) {
const listRecentSessionsStatement = db.prepare(` const listRecentSessionsStatement = db.prepare(`
SELECT * SELECT *
@ -366,6 +444,22 @@ function createQueryApi(db) {
ORDER BY created_at DESC, id DESC ORDER BY created_at DESC, id DESC
LIMIT ? LIMIT ?
`); `);
const listWorkItemsStatement = db.prepare(`
SELECT *
FROM work_items
ORDER BY updated_at DESC, id DESC
LIMIT ?
`);
const listAllWorkItemsStatement = db.prepare(`
SELECT *
FROM work_items
ORDER BY updated_at DESC, id DESC
`);
const getWorkItemStatement = db.prepare(`
SELECT *
FROM work_items
WHERE id = ?
`);
const getSkillVersionStatement = db.prepare(` const getSkillVersionStatement = db.prepare(`
SELECT * SELECT *
FROM skill_versions FROM skill_versions
@ -547,6 +641,50 @@ function createQueryApi(db) {
created_at = excluded.created_at created_at = excluded.created_at
`); `);
const upsertWorkItemStatement = db.prepare(`
INSERT INTO work_items (
id,
source,
source_id,
title,
status,
priority,
url,
owner,
repo_root,
session_id,
metadata,
created_at,
updated_at
) VALUES (
@id,
@source,
@source_id,
@title,
@status,
@priority,
@url,
@owner,
@repo_root,
@session_id,
@metadata,
@created_at,
@updated_at
)
ON CONFLICT(id) DO UPDATE SET
source = excluded.source,
source_id = excluded.source_id,
title = excluded.title,
status = excluded.status,
priority = excluded.priority,
url = excluded.url,
owner = excluded.owner,
repo_root = excluded.repo_root,
session_id = excluded.session_id,
metadata = excluded.metadata,
updated_at = excluded.updated_at
`);
function getSessionById(id) { function getSessionById(id) {
const row = getSessionStatement.get(id); const row = getSessionStatement.get(id);
return row ? mapSessionRow(row) : null; return row ? mapSessionRow(row) : null;
@ -582,12 +720,15 @@ function createQueryApi(db) {
const activeLimit = normalizeLimit(options.activeLimit, 5); const activeLimit = normalizeLimit(options.activeLimit, 5);
const recentSkillRunLimit = normalizeLimit(options.recentSkillRunLimit, 20); const recentSkillRunLimit = normalizeLimit(options.recentSkillRunLimit, 20);
const pendingLimit = normalizeLimit(options.pendingLimit, 5); const pendingLimit = normalizeLimit(options.pendingLimit, 5);
const workItemLimit = normalizeLimit(options.workItemLimit, 10);
const activeSessions = listActiveSessionsStatement.all(activeLimit).map(mapSessionRow); const activeSessions = listActiveSessionsStatement.all(activeLimit).map(mapSessionRow);
const activeSessionCount = countActiveSessionsStatement.get().total_count; const activeSessionCount = countActiveSessionsStatement.get().total_count;
const recentSkillRuns = listRecentSkillRunsStatement.all(recentSkillRunLimit).map(mapSkillRunRow); const recentSkillRuns = listRecentSkillRunsStatement.all(recentSkillRunLimit).map(mapSkillRunRow);
const installations = listInstallStateStatement.all().map(mapInstallStateRow); const installations = listInstallStateStatement.all().map(mapInstallStateRow);
const pendingGovernanceEvents = listPendingGovernanceStatement.all(pendingLimit).map(mapGovernanceEventRow); const pendingGovernanceEvents = listPendingGovernanceStatement.all(pendingLimit).map(mapGovernanceEventRow);
const workItems = summarizeWorkItems(listAllWorkItemsStatement.all().map(mapWorkItemRow));
workItems.items = listWorkItemsStatement.all(workItemLimit).map(mapWorkItemRow);
const skillRuns = { const skillRuns = {
windowSize: recentSkillRunLimit, windowSize: recentSkillRunLimit,
summary: summarizeSkillRuns(recentSkillRuns), summary: summarizeSkillRuns(recentSkillRuns),
@ -603,6 +744,7 @@ function createQueryApi(db) {
skillRuns, skillRuns,
installHealth, installHealth,
pendingGovernanceCount, pendingGovernanceCount,
workItems,
}), }),
activeSessions: { activeSessions: {
activeCount: activeSessionCount, activeCount: activeSessionCount,
@ -614,6 +756,7 @@ function createQueryApi(db) {
pendingCount: pendingGovernanceCount, pendingCount: pendingGovernanceCount,
events: pendingGovernanceEvents, events: pendingGovernanceEvents,
}, },
workItems,
}; };
} }
@ -683,6 +826,27 @@ function createQueryApi(db) {
}); });
return normalized; return normalized;
}, },
upsertWorkItem(workItem) {
const normalized = normalizeWorkItemInput(workItem);
assertValidEntity('workItem', normalized);
upsertWorkItemStatement.run({
id: normalized.id,
source: normalized.source,
source_id: normalized.sourceId,
title: normalized.title,
status: normalized.status,
priority: normalized.priority,
url: normalized.url,
owner: normalized.owner,
repo_root: normalized.repoRoot,
session_id: normalized.sessionId,
metadata: stringifyJson(normalized.metadata, 'workItem.metadata'),
created_at: normalized.createdAt,
updated_at: normalized.updatedAt,
});
const row = getWorkItemStatement.get(normalized.id);
return row ? mapWorkItemRow(row) : null;
},
upsertSession(session) { upsertSession(session) {
const normalized = normalizeSessionInput(session); const normalized = normalizeSessionInput(session);
assertValidEntity('session', normalized); assertValidEntity('session', normalized);

View File

@ -13,6 +13,7 @@ const ENTITY_DEFINITIONS = {
decision: 'decision', decision: 'decision',
installState: 'installState', installState: 'installState',
governanceEvent: 'governanceEvent', governanceEvent: 'governanceEvent',
workItem: 'workItem',
}; };
let cachedSchema = null; let cachedSchema = null;

View File

@ -11,7 +11,7 @@ function showHelp(exitCode = 0) {
Usage: node scripts/status.js [--db <path>] [--json|--markdown] [--write <path>] [--limit <n>] Usage: node scripts/status.js [--db <path>] [--json|--markdown] [--write <path>] [--limit <n>]
Query the ECC SQLite state store for active sessions, recent skill runs, Query the ECC SQLite state store for active sessions, recent skill runs,
install health, and pending governance events. install health, pending governance events, and linked work items.
`); `);
process.exit(exitCode); process.exit(exitCode);
} }
@ -142,6 +142,24 @@ function printGovernance(section) {
} }
} }
function printWorkItems(section) {
console.log(`Work items: ${section.openCount} open, ${section.blockedCount} blocked, ${section.closedCount} closed`);
if (section.items.length === 0) {
console.log(' - none');
return;
}
for (const item of section.items.slice(0, 10)) {
const sourceId = item.sourceId ? `#${item.sourceId}` : item.id;
console.log(` - ${item.source}/${sourceId} ${item.status}: ${item.title}`);
console.log(` Owner: ${item.owner || '(unassigned)'}`);
console.log(` Updated: ${item.updatedAt}`);
if (item.url) {
console.log(` URL: ${item.url}`);
}
}
}
function printReadiness(section) { function printReadiness(section) {
console.log(`Readiness: ${section.status}`); console.log(`Readiness: ${section.status}`);
console.log(` Attention items: ${section.attentionCount}`); console.log(` Attention items: ${section.attentionCount}`);
@ -149,6 +167,7 @@ function printReadiness(section) {
console.log(` Failed skill runs: ${section.failedSkillRuns}`); console.log(` Failed skill runs: ${section.failedSkillRuns}`);
console.log(` Warning installs: ${section.warningInstallations}`); console.log(` Warning installs: ${section.warningInstallations}`);
console.log(` Pending governance: ${section.pendingGovernanceEvents}`); console.log(` Pending governance: ${section.pendingGovernanceEvents}`);
console.log(` Blocked work items: ${section.blockedWorkItems}`);
} }
function printHuman(payload) { function printHuman(payload) {
@ -163,6 +182,8 @@ function printHuman(payload) {
printInstallHealth(payload.installHealth); printInstallHealth(payload.installHealth);
console.log(); console.log();
printGovernance(payload.governance); printGovernance(payload.governance);
console.log();
printWorkItems(payload.workItems);
} }
function formatPercent(value) { function formatPercent(value) {
@ -188,6 +209,7 @@ function renderMarkdown(payload) {
`Failed skill runs: ${payload.readiness.failedSkillRuns}`, `Failed skill runs: ${payload.readiness.failedSkillRuns}`,
`Warning installs: ${payload.readiness.warningInstallations}`, `Warning installs: ${payload.readiness.warningInstallations}`,
`Pending governance: ${payload.readiness.pendingGovernanceEvents}`, `Pending governance: ${payload.readiness.pendingGovernanceEvents}`,
`Blocked work items: ${payload.readiness.blockedWorkItems}`,
'', '',
'## Active Sessions', '## Active Sessions',
'', '',
@ -267,6 +289,30 @@ function renderMarkdown(payload) {
} }
} }
lines.push(
'',
'## Work Items',
'',
`Open: ${payload.workItems.openCount}`,
`Blocked: ${payload.workItems.blockedCount}`,
`Closed: ${payload.workItems.closedCount}`
);
if (payload.workItems.items.length === 0) {
lines.push('', '- none');
} else {
lines.push('', 'Recent work items:');
for (const item of payload.workItems.items.slice(0, 10)) {
const sourceId = item.sourceId ? `#${item.sourceId}` : item.id;
lines.push(`- ${formatCode(item.source)} ${formatCode(sourceId)} ${item.status}: ${item.title}`);
lines.push(` - Owner: ${item.owner || '(unassigned)'}`);
lines.push(` - Updated: ${item.updatedAt}`);
if (item.url) {
lines.push(` - URL: ${item.url}`);
}
}
}
return `${lines.join('\n')}\n`; return `${lines.join('\n')}\n`;
} }
@ -296,6 +342,7 @@ async function main() {
activeLimit: options.limit, activeLimit: options.limit,
recentSkillRunLimit: 20, recentSkillRunLimit: 20,
pendingLimit: options.limit, pendingLimit: options.limit,
workItemLimit: options.limit,
}), }),
}; };

View File

@ -269,15 +269,16 @@ async function runTests() {
const firstMigrations = firstStore.getAppliedMigrations(); const firstMigrations = firstStore.getAppliedMigrations();
firstStore.close(); firstStore.close();
assert.strictEqual(firstMigrations.length, 1); assert.strictEqual(firstMigrations.length, 2);
assert.strictEqual(firstMigrations[0].version, 1); assert.strictEqual(firstMigrations[0].version, 1);
assert.strictEqual(firstMigrations[1].version, 2);
assert.ok(fs.existsSync(expectedPath)); assert.ok(fs.existsSync(expectedPath));
const secondStore = await createStateStore({ homeDir }); const secondStore = await createStateStore({ homeDir });
const secondMigrations = secondStore.getAppliedMigrations(); const secondMigrations = secondStore.getAppliedMigrations();
secondStore.close(); secondStore.close();
assert.strictEqual(secondMigrations.length, 1); assert.strictEqual(secondMigrations.length, 2);
assert.strictEqual(secondMigrations[0].version, 1); assert.strictEqual(secondMigrations[0].version, 1);
} finally { } finally {
cleanupTempDir(homeDir); cleanupTempDir(homeDir);
@ -294,7 +295,7 @@ async function runTests() {
const store = await createStateStore({ dbPath: ':memory:' }); const store = await createStateStore({ dbPath: ':memory:' });
assert.strictEqual(store.dbPath, ':memory:'); assert.strictEqual(store.dbPath, ':memory:');
assert.strictEqual(store.getAppliedMigrations().length, 1); assert.strictEqual(store.getAppliedMigrations().length, 2);
store.close(); store.close();
assert.ok(!fs.existsSync(path.join(tempDir, ':memory:'))); assert.ok(!fs.existsSync(path.join(tempDir, ':memory:')));
@ -345,6 +346,7 @@ async function runTests() {
assert.strictEqual(status.readiness.failedSkillRuns, 1); assert.strictEqual(status.readiness.failedSkillRuns, 1);
assert.strictEqual(status.readiness.warningInstallations, 0); assert.strictEqual(status.readiness.warningInstallations, 0);
assert.strictEqual(status.readiness.pendingGovernanceEvents, 1); assert.strictEqual(status.readiness.pendingGovernanceEvents, 1);
assert.strictEqual(status.readiness.blockedWorkItems, 0);
assert.strictEqual(status.activeSessions.activeCount, 1); assert.strictEqual(status.activeSessions.activeCount, 1);
assert.strictEqual(status.activeSessions.sessions[0].id, 'session-active'); assert.strictEqual(status.activeSessions.sessions[0].id, 'session-active');
assert.strictEqual(status.skillRuns.summary.totalCount, 4); assert.strictEqual(status.skillRuns.summary.totalCount, 4);
@ -355,6 +357,7 @@ async function runTests() {
assert.strictEqual(status.installHealth.totalCount, 1); assert.strictEqual(status.installHealth.totalCount, 1);
assert.strictEqual(status.governance.pendingCount, 1); assert.strictEqual(status.governance.pendingCount, 1);
assert.strictEqual(status.governance.events[0].id, 'gov-1'); assert.strictEqual(status.governance.events[0].id, 'gov-1');
assert.strictEqual(status.workItems.openCount, 0);
} finally { } finally {
cleanupTempDir(testDir); cleanupTempDir(testDir);
} }
@ -385,6 +388,80 @@ async function runTests() {
assert.deepStrictEqual(status.installHealth.installations, []); assert.deepStrictEqual(status.installHealth.installations, []);
assert.strictEqual(status.governance.pendingCount, 0); assert.strictEqual(status.governance.pendingCount, 0);
assert.deepStrictEqual(status.governance.events, []); assert.deepStrictEqual(status.governance.events, []);
assert.strictEqual(status.workItems.totalCount, 0);
assert.deepStrictEqual(status.workItems.items, []);
} finally {
cleanupTempDir(testDir);
}
})) passed += 1; else failed += 1;
if (await test('tracks linked work items for Linear, GitHub, and handoff progress', async () => {
const testDir = createTempDir('ecc-state-work-items-');
const dbPath = path.join(testDir, 'state.db');
try {
await seedStore(dbPath);
const store = await createStateStore({ dbPath });
const linearItem = store.upsertWorkItem({
id: 'linear-ecc-20-control-plane',
source: 'linear',
sourceId: 'ECC-20',
title: 'Define harness-neutral session/worktree contract',
status: 'in-progress',
priority: 'high',
url: 'https://linear.app/ecctools/issue/ECC-20',
owner: 'control-plane',
repoRoot: '/tmp/ecc-repo',
sessionId: 'session-active',
metadata: {
project: 'ECC 2.0: Control Plane',
},
createdAt: '2026-03-15T08:12:00.000Z',
updatedAt: '2026-03-15T08:15:00.000Z',
});
store.upsertWorkItem({
id: 'handoff-release-gate',
source: 'handoff',
sourceId: 'ecc-rc1-release-decision-20260511.md',
title: 'Rerun rc.1 release gate before tag',
status: 'blocked',
priority: 'high',
owner: 'release',
repoRoot: '/tmp/ecc-repo',
metadata: {
blocker: 'tag decision pending',
},
createdAt: '2026-03-15T08:13:00.000Z',
updatedAt: '2026-03-15T08:16:00.000Z',
});
store.upsertWorkItem({
id: 'github-pr-1738',
source: 'github',
sourceId: '1738',
title: 'Add Qwen install target',
status: 'merged',
priority: 'normal',
url: 'https://github.com/affaan-m/everything-claude-code/pull/1738',
owner: 'maintainer',
createdAt: '2026-03-15T08:14:00.000Z',
updatedAt: '2026-03-15T08:17:00.000Z',
});
const status = store.getStatus();
store.close();
assert.strictEqual(linearItem.id, 'linear-ecc-20-control-plane');
assert.strictEqual(linearItem.metadata.project, 'ECC 2.0: Control Plane');
assert.strictEqual(status.workItems.totalCount, 3);
assert.strictEqual(status.workItems.openCount, 2);
assert.strictEqual(status.workItems.blockedCount, 1);
assert.strictEqual(status.workItems.closedCount, 1);
assert.strictEqual(status.readiness.blockedWorkItems, 1);
assert.strictEqual(status.readiness.attentionCount, 3);
assert.strictEqual(status.workItems.items[0].id, 'github-pr-1738');
} finally { } finally {
cleanupTempDir(testDir); cleanupTempDir(testDir);
} }
@ -608,10 +685,13 @@ async function runTests() {
assert.match(written, /## Readiness/); assert.match(written, /## Readiness/);
assert.match(written, /Status: attention/); assert.match(written, /Status: attention/);
assert.match(written, /Attention items: 2/); assert.match(written, /Attention items: 2/);
assert.match(written, /Blocked work items: 0/);
assert.match(written, /- `session-active` \[claude\/dmux-tmux\] active/); assert.match(written, /- `session-active` \[claude\/dmux-tmux\] active/);
assert.match(written, /Success rate: 66\.7%/); assert.match(written, /Success rate: 66\.7%/);
assert.match(written, /Install health: healthy/); assert.match(written, /Install health: healthy/);
assert.match(written, /Pending governance events: 1/); assert.match(written, /Pending governance events: 1/);
assert.match(written, /## Work Items/);
assert.match(written, /Open: 0/);
} finally { } finally {
cleanupTempDir(testDir); cleanupTempDir(testDir);
} }