mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-11-03 19:34:24 +00:00
Complete Fleet Console operations view
This commit is contained in:
1 parent
31170989b6
commit
2efccb9af9
9 files changed
+151
-13
No files matched your search
@@ -58,7 +58,10 @@ and the first Claude reference-fleet path:
|
||||
branch/worktree mappings, checks, resources, schedules, release boundaries,
|
||||
and backlog inventory while refusing to invent missing ownership boundaries.
|
||||
- a redesigned public product site and read-only local Fleet Console backed by
|
||||
the same repository and runtime state rather than a separate dashboard model.
|
||||
the same repository and runtime state rather than a separate dashboard model;
|
||||
the Console covers ownership, domain relationships, worktree drift,
|
||||
recoverability, acknowledgements, checks, integration, delivery, resources,
|
||||
schedules, decisions, provider health, and context evidence.
|
||||
- identity-bound context-usage telemetry that separates measurements from
|
||||
estimates and relates cost/cache behavior to verified work;
|
||||
- validated system/session schedules with shell-free actions, authority,
|
||||
|
||||
@@ -1089,8 +1089,11 @@ commands require explicit invocation and normal authority checks.
|
||||
|
||||
Implementation checkpoint (2026-09-27): a read-only observation service and
|
||||
`torch console snapshot` now expose project, health, identity, message,
|
||||
backlog, check, resource, integration, schedule, and audit state without
|
||||
initializing a missing database. Context-locality output explicitly reports
|
||||
backlog, check, resource, integration, delivery, schedule, and audit state
|
||||
without initializing a missing database. The same snapshot exposes approved
|
||||
ownership and neighbour relationships, managed worktree branch drift,
|
||||
recoverability, acknowledgement state, tracked decisions, configured runtime
|
||||
and delivery providers, and forge health. Context-locality output explicitly reports
|
||||
`unavailable` until runtime usage samples exist, preventing estimates from
|
||||
masquerading as measurements.
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ test_integrity_note:
|
||||
- id: SCN-console-readonly
|
||||
given: an installed Fleet without an initialized control-plane database
|
||||
when: the observation API and local HTTP console are requested
|
||||
then: fixed assets and structured state are served with restrictive headers while Git and machine-local state remain unchanged
|
||||
then: ownership, worktree drift, recoverability, decisions, providers, delivery, and operational state are served with restrictive headers while Git and machine-local state remain unchanged
|
||||
- id: SCN-context-locality
|
||||
given: measured provider usage and a separate heuristic estimate for one persistent Fleet identity
|
||||
when: CLI and identity-bound MCP record and aggregate the samples
|
||||
@@ -247,6 +247,7 @@ test_integrity_note:
|
||||
- compatibility report counts and unchanged COMBATRIG Git status
|
||||
- HTTP status, content-security policy, fixed routes, and traversal refusal
|
||||
- unchanged Git status and absent database before and after console observation
|
||||
- approved ownership graph, managed worktree drift, recoverability grade, decisions, delivery, and provider status in one read-only snapshot
|
||||
- measured and estimated context groups, cache-read ratio, and cost per verified item
|
||||
- schedule plan blockers, subprocess shell flag, retry count, and durable failure message
|
||||
- preservation of project source and modified owner configuration
|
||||
|
||||
@@ -25,11 +25,18 @@
|
||||
<p class="console-error" id="console-error" hidden></p>
|
||||
<section class="console-grid">
|
||||
<article class="console-panel agents-panel"><div class="panel-heading"><h1>Fleet</h1><span id="agent-count">0 identities</span></div><div id="agent-list" class="agent-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Ownership</h2><span>Domain graph</span></div><div id="ownership-list" class="console-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Worktrees</h2><span id="recoverability-state">Recoverability</span></div><div id="worktree-list" class="console-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Backlog</h2><span id="task-count">0 active</span></div><div id="task-list" class="console-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Fleet changes</h2><span id="change-count">0 pending</span></div><div id="change-list" class="console-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Integration</h2><span>Exact commits</span></div><div id="integration-list" class="console-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Delivery</h2><span>Release is not deploy</span></div><div id="delivery-list" class="console-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Checks</h2><span>Exact receipts</span></div><div id="check-list" class="console-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Resources</h2><span>FIFO leases</span></div><div id="resource-list" class="console-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Schedules</h2><span>Declared lifetimes</span></div><div id="schedule-list" class="console-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Providers</h2><span>Capability health</span></div><div id="provider-list" class="console-list"></div></article>
|
||||
<article class="console-panel messages-panel"><div class="panel-heading"><h2>Messages</h2><span id="message-count">0 queued</span></div><div id="message-list" class="console-list"></div></article>
|
||||
<article class="console-panel"><div class="panel-heading"><h2>Decisions</h2><span>Tracked truth</span></div><div id="decision-list" class="console-list"></div></article>
|
||||
<article class="console-panel context-panel"><div class="panel-heading"><h2>Context locality</h2><span>Measured, never assumed</span></div><div id="context-detail" class="context-detail"></div></article>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
+43
-1
@@ -24,6 +24,18 @@ function render(snapshot) {
|
||||
<div class="agent-row"><span class="presence ${safe(agent.state)}"></span><div><strong>${safe(agent.title ?? agent.areaId)}</strong><small>${safe(agent.summary ?? `${agent.runtime ?? 'runtime'} · ${agent.state}`)}</small></div><code>${safe(agent.areaId)}</code></div>
|
||||
`).join('') : empty('Install an approved fleet to see persistent identities.');
|
||||
|
||||
const domains = snapshot.organization?.domains ?? [];
|
||||
$('#ownership-list').innerHTML = domains.length ? domains.map((domain) => `
|
||||
<div class="list-row"><div><strong>${safe(domain.title ?? domain.id)}</strong><small>${safe((domain.owned_paths ?? []).join(', ') || 'No paths assigned')}</small></div><span>${safe((domain.neighbours ?? []).join(', ') || 'isolated')}</span></div>
|
||||
`).join('') : empty('No approved domain ownership graph.');
|
||||
|
||||
const worktrees = snapshot.worktrees ?? [];
|
||||
$('#recoverability-state').textContent = snapshot.mode === 'installed'
|
||||
? (snapshot.recoverability?.level ?? 'Unknown') : 'Not installed';
|
||||
$('#worktree-list').innerHTML = worktrees.length ? worktrees.map((worktree) => `
|
||||
<div class="list-row"><div><strong>${safe(worktree.area)}</strong><small>${safe(worktree.branch)} · ${safe(worktree.path)}</small></div><span>${safe(`${worktree.ahead ?? 0} ahead / ${worktree.behind ?? 0} behind`)}</span></div>
|
||||
`).join('') : empty('No managed worktrees have been created.');
|
||||
|
||||
const tasks = (snapshot.backlog ?? []).filter((task) => !['completed', 'cancelled'].includes(task.state));
|
||||
$('#task-count').textContent = `${tasks.length} active`;
|
||||
$('#task-list').innerHTML = tasks.length ? tasks.slice(0, 12).map((task) => `
|
||||
@@ -42,6 +54,16 @@ function render(snapshot) {
|
||||
<div class="list-row"><div><strong>${safe(item.sourceArea)}</strong><small>${safe(item.sourceCommit?.slice(0, 9))}</small></div><span>${safe(item.state)}</span></div>
|
||||
`).join('') : empty('No commits waiting to land.');
|
||||
|
||||
const deliveries = snapshot.deliveries ?? [];
|
||||
$('#delivery-list').innerHTML = deliveries.length ? deliveries.map((item) => `
|
||||
<div class="list-row"><div><strong>${safe(item.label)}</strong><small>${safe(item.sourceArea)} · ${safe(item.commit?.slice(0, 9))}</small></div><span>${safe(item.state)}</span></div>
|
||||
`).join('') : empty('No delivery records. Integration does not imply deployment.');
|
||||
|
||||
const receipts = snapshot.checks?.receipts ?? [];
|
||||
$('#check-list').innerHTML = receipts.length ? receipts.map((receipt) => `
|
||||
<div class="list-row"><div><strong>${safe(receipt.checkId)}</strong><small>${safe(receipt.areaId)} · ${safe(receipt.commit?.slice(0, 9))}</small></div><span>${safe(receipt.result)}</span></div>
|
||||
`).join('') : empty('No exact-commit check receipts.');
|
||||
|
||||
const holders = snapshot.resources?.holders ?? [];
|
||||
const waiters = snapshot.resources?.waiters ?? [];
|
||||
$('#resource-list').innerHTML = holders.length || waiters.length
|
||||
@@ -49,12 +71,32 @@ function render(snapshot) {
|
||||
...waiters.map((item) => `<div class="list-row"><strong>${safe(item.resourceId)}</strong><span>${safe(item.areaId)} waits</span></div>`)].join('')
|
||||
: empty('All configured resources are available.');
|
||||
|
||||
const schedules = snapshot.schedules ?? [];
|
||||
$('#schedule-list').innerHTML = schedules.length ? schedules.map((schedule) => `
|
||||
<div class="list-row"><div><strong>${safe(schedule.id)}</strong><small>${safe(schedule.owner)} · ${safe(schedule.lifetime)}</small></div><span>${safe(schedule.behavior)}</span></div>
|
||||
`).join('') : empty('No schedules configured.');
|
||||
|
||||
const providers = snapshot.providers ?? {};
|
||||
const runtimeProviders = providers.runtimes ?? [];
|
||||
const deliveryProviders = providers.delivery ?? {};
|
||||
const configured = snapshot.mode === 'installed';
|
||||
$('#provider-list').innerHTML = [
|
||||
`<div class="list-row"><strong>Forge</strong><span>${safe(configured ? `${providers.forge?.provider ?? 'none'} · ${providers.forge?.status ?? 'unknown'}` : 'not configured')}</span></div>`,
|
||||
...runtimeProviders.map((runtime) => `<div class="list-row"><strong>${safe(runtime.name)}</strong><span>runtime configured</span></div>`),
|
||||
`<div class="list-row"><strong>Release</strong><span>${safe(configured ? (deliveryProviders.release?.provider ?? 'none') : 'not configured')}</span></div>`,
|
||||
`<div class="list-row"><strong>Deployment</strong><span>${safe(configured ? (deliveryProviders.deployment?.provider ?? 'none') : 'not configured')}</span></div>`,
|
||||
].join('');
|
||||
|
||||
const messages = snapshot.messages?.recent ?? [];
|
||||
$('#message-count').textContent = `${snapshot.messages?.unacknowledged ?? 0} unacknowledged`;
|
||||
$('#message-list').innerHTML = messages.length ? messages.map((message) => `
|
||||
<div class="message-row"><p><strong>${safe(message.sender)}</strong> to ${safe(message.recipient)}</p><span>${safe(message.body)}</span></div>
|
||||
<div class="message-row"><p><strong>${safe(message.sender)}</strong> to ${safe(message.recipient)}<small>${safe((message.acknowledgedBy ?? []).length ? `Acknowledged by ${(message.acknowledgedBy ?? []).join(', ')}` : 'Unacknowledged')}</small></p><span>${safe(message.body)}</span></div>
|
||||
`).join('') : empty('No durable messages yet.');
|
||||
|
||||
const decisions = snapshot.decisions?.content ?? '';
|
||||
$('#decision-list').innerHTML = decisions.trim()
|
||||
? `<p class="decision-copy">${safe(decisions)}</p>` : empty('No tracked decisions.');
|
||||
|
||||
const locality = snapshot.contextLocality;
|
||||
$('#context-detail').innerHTML = locality?.measured
|
||||
? locality.samples.map((sample) => `<div class="locality-row"><strong>${safe(sample.areaId)}</strong><span>${safe(String(sample.cacheRead ?? 0))} cached tokens</span></div>`).join('')
|
||||
|
||||
+8
-4
@@ -113,9 +113,10 @@ footer div { display: flex; gap: 1.5rem; }
|
||||
.console-summary p { margin: 0 0 .3rem; color: var(--muted); font-size: .78rem; }
|
||||
.console-summary strong { font-family: var(--display); font-size: 1.12rem; }
|
||||
.console-error { background: #ffe4da; border-left: 4px solid var(--signal); padding: 1rem; }
|
||||
.console-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; margin-top: 1rem; }
|
||||
.console-panel { min-height: 280px; padding: 1.2rem; border: 1px solid var(--line); background: var(--paper); }
|
||||
.agents-panel, .messages-panel { grid-column: span 2; }
|
||||
.console-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-top: 1rem; }
|
||||
.console-panel { min-height: 210px; padding: 1.2rem; border: 1px solid var(--line); background: var(--paper); }
|
||||
.agents-panel { grid-column: span 3; }
|
||||
.messages-panel { grid-column: span 2; }
|
||||
.panel-heading { display: flex; justify-content: space-between; align-items: baseline; border-bottom: 1px solid var(--line); padding-bottom: .8rem; }
|
||||
.panel-heading h1, .panel-heading h2 { font-size: 1.45rem; letter-spacing: -.02em; }
|
||||
.panel-heading span { color: var(--muted); font-size: .78rem; }
|
||||
@@ -127,7 +128,8 @@ footer div { display: flex; gap: 1.5rem; }
|
||||
.presence.working, .presence.online { background: var(--field); }
|
||||
.presence.blocked { background: var(--signal); }
|
||||
.list-row, .message-row, .locality-row { display: flex; justify-content: space-between; gap: 1rem; padding: .72rem 0; border-bottom: 1px solid #d7e1e6; }
|
||||
.list-row small { display: block; color: var(--muted); }
|
||||
.list-row small, .message-row small { display: block; color: var(--muted); margin-top: .2rem; }
|
||||
.decision-copy { white-space: pre-wrap; color: var(--muted); line-height: 1.55; }
|
||||
.list-row > span { color: var(--route); font-size: .8rem; }
|
||||
.message-row { display: block; }
|
||||
.message-row p { margin: 0; font-size: .8rem; color: var(--muted); }
|
||||
@@ -146,6 +148,8 @@ footer div { display: flex; gap: 1.5rem; }
|
||||
.blueprint article { min-height: auto; border-right: 0; border-bottom: 1px solid var(--line); }
|
||||
.blueprint-index { margin-bottom: 2rem; }
|
||||
.console-summary { grid-template-columns: repeat(2, 1fr); }
|
||||
.console-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.agents-panel { grid-column: span 2; }
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
|
||||
@@ -17,6 +17,11 @@ export function inspectManagedWorktree(root, entry, mainBranch) {
|
||||
const status = git(entry.path, ['status', '--porcelain=v1', '--untracked-files=all']);
|
||||
if (status) problems.push('worktree-dirty');
|
||||
const ahead = Number(git(root, ['rev-list', '--count', `${mainBranch}..${entry.branch}`]) || 0);
|
||||
const behind = Number(git(root, ['rev-list', '--count', `${entry.branch}..${mainBranch}`]) || 0);
|
||||
if (ahead > 0) problems.push(`unique-commits:${ahead}`);
|
||||
return { ...entry, actualBranch, ahead, safeToRemove: problems.length === 0, problems };
|
||||
return {
|
||||
...entry, actualBranch, ahead, behind,
|
||||
drift: ahead === 0 && behind === 0 ? 'current' : 'diverged',
|
||||
safeToRemove: problems.length === 0, problems,
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import { inspectRepository } from '../kernel/git.mjs';
|
||||
import { readInstallManifest } from '../kernel/install.mjs';
|
||||
import { projectStatePath } from '../kernel/paths.mjs';
|
||||
import { loadProjectConfig } from '../kernel/config.mjs';
|
||||
import { inspectManagedWorktree } from '../kernel/worktree-state.mjs';
|
||||
import { classifyRecoverability } from '../canonical/local.mjs';
|
||||
|
||||
function json(path) {
|
||||
return JSON.parse(readFileSync(path, 'utf8'));
|
||||
@@ -59,8 +61,25 @@ export function observeProject({ repositoryRoot, env = process.env, now = () =>
|
||||
const config = loadProjectConfig(repository.root);
|
||||
const roster = json(join(repository.root, '.torch', 'roster.yaml'));
|
||||
const manifest = readInstallManifest(repository.root);
|
||||
const doctor = diagnoseProject({ repository, env });
|
||||
const stateRoot = projectStatePath(manifest.projectId, env);
|
||||
const databasePath = join(stateRoot, 'state.db');
|
||||
const areas = roster.areas ?? [];
|
||||
const edges = [];
|
||||
const seenEdges = new Set();
|
||||
for (const area of areas) {
|
||||
for (const neighbour of area.neighbours ?? []) {
|
||||
if (neighbour === 'all') continue;
|
||||
const key = [area.id, neighbour].sort().join('\0');
|
||||
if (!seenEdges.has(key)) {
|
||||
seenEdges.add(key);
|
||||
edges.push({ from: area.id, to: neighbour });
|
||||
}
|
||||
}
|
||||
}
|
||||
const forgeFinding = doctor.findings.find((finding) => [
|
||||
'FORGE_UNAVAILABLE', 'FORGE_SYNCHRONIZATION_PENDING', 'FORGE_STATUS_INVALID',
|
||||
].includes(finding.code));
|
||||
const result = {
|
||||
schema: 'torch.dev/observation/v1alpha1', generatedAt: now().toISOString(),
|
||||
mode: 'installed', mutationPerformed: false,
|
||||
@@ -69,8 +88,13 @@ export function observeProject({ repositoryRoot, env = process.env, now = () =>
|
||||
root: repository.root, branch: repository.branch, head: repository.head,
|
||||
dirty: repository.dirtyEntries.length > 0,
|
||||
},
|
||||
doctor: diagnoseProject({ repository, env }),
|
||||
agents: (roster.areas ?? []).map((area) => ({
|
||||
doctor,
|
||||
organization: {
|
||||
sessionManager: areas.find((area) => area.id === 'session-manager') ?? null,
|
||||
domains: areas.filter((area) => area.id !== 'session-manager'),
|
||||
edges,
|
||||
},
|
||||
agents: areas.map((area) => ({
|
||||
areaId: area.id, title: area.title, state: 'offline', runtime: area.runtime ?? null,
|
||||
runtimeSessionId: null, summary: null, task: null, heartbeatAt: null,
|
||||
})),
|
||||
@@ -82,6 +106,29 @@ export function observeProject({ repositoryRoot, env = process.env, now = () =>
|
||||
deliveries: [],
|
||||
fleetChanges: [],
|
||||
schedules: config.schedules ?? [],
|
||||
worktrees: (manifest.external ?? [])
|
||||
.filter((entry) => entry.type === 'worktree')
|
||||
.map((entry) => inspectManagedWorktree(repository.root, entry, config.project.main_branch)),
|
||||
recoverability: classifyRecoverability({ repositoryRoot: repository.root }),
|
||||
decisions: {
|
||||
path: '.torch/decisions.md',
|
||||
content: readFileSync(join(repository.root, '.torch', 'decisions.md'), 'utf8'),
|
||||
},
|
||||
providers: {
|
||||
runtimes: Object.entries(config.runtimes)
|
||||
.filter(([name]) => name !== 'default')
|
||||
.map(([name, policy]) => ({ name, configured: true, policy })),
|
||||
forge: config.forge.provider === 'none'
|
||||
? { provider: 'none', status: 'local-only', remote: null }
|
||||
: {
|
||||
provider: config.forge.provider, remote: config.forge.remote,
|
||||
status: forgeFinding?.code === 'FORGE_UNAVAILABLE' ? 'degraded'
|
||||
: (forgeFinding?.code === 'FORGE_SYNCHRONIZATION_PENDING' ? 'pending-sync'
|
||||
: (forgeFinding ? 'invalid' : 'available')),
|
||||
finding: forgeFinding ?? null,
|
||||
},
|
||||
delivery: config.delivery.adapters,
|
||||
},
|
||||
audit: [],
|
||||
contextLocality: {
|
||||
measured: false, status: 'unavailable',
|
||||
@@ -112,7 +159,13 @@ export function observeProject({ repositoryRoot, env = process.env, now = () =>
|
||||
result.messages.recent = database.prepare(`
|
||||
SELECT id, sender_id AS sender, recipient_id AS recipient, kind, body, created_at AS createdAt
|
||||
FROM messages ORDER BY created_at DESC LIMIT 20
|
||||
`).all();
|
||||
`).all().map((message) => ({
|
||||
...message,
|
||||
acknowledgedBy: hasTable(database, 'message_acks')
|
||||
? database.prepare('SELECT area_id FROM message_acks WHERE message_id = ? ORDER BY area_id')
|
||||
.all(message.id).map((row) => row.area_id)
|
||||
: [],
|
||||
}));
|
||||
if (hasTable(database, 'message_acks') && hasTable(database, 'identities')) {
|
||||
result.messages.unacknowledged = database.prepare(`
|
||||
SELECT COUNT(*) AS count FROM messages m
|
||||
|
||||
@@ -9,6 +9,7 @@ import { analyzeRepository } from '../../src/kernel/analyze.mjs';
|
||||
import { proposeDomains } from '../../src/kernel/domains.mjs';
|
||||
import { inspectRepository } from '../../src/kernel/git.mjs';
|
||||
import { installProject } from '../../src/kernel/install.mjs';
|
||||
import { createWorktrees } from '../../src/kernel/worktrees.mjs';
|
||||
import { observeProject } from '../../src/observability/snapshot.mjs';
|
||||
|
||||
function fixture() {
|
||||
@@ -35,6 +36,9 @@ test('SCN-product-site: public surface explains the portable fleet and separates
|
||||
assert.doesNotMatch(html, /nostr|relay coordination|task lock/i);
|
||||
assert.match(consoleHtml, /Local Fleet Console/);
|
||||
assert.match(consoleHtml, /noindex,nofollow/);
|
||||
for (const label of ['Ownership', 'Worktrees', 'Delivery', 'Checks', 'Schedules', 'Providers', 'Decisions']) {
|
||||
assert.match(consoleHtml, new RegExp(`>${label}<`));
|
||||
}
|
||||
});
|
||||
|
||||
test('SCN-architecture-decisions: every specification decision has an explicit accepted or gated boundary', () => {
|
||||
@@ -57,6 +61,14 @@ test('SCN-console-readonly: HTTP console serves fixed assets and observes an ins
|
||||
status: 'approved', reviewedAt: '2026-09-27T00:00:00Z', reviewedBy: 'fixture-owner', notes: [],
|
||||
};
|
||||
installProject({ repository, proposal, env, projectId: 'console-fixture' });
|
||||
execFileSync('git', ['-C', root, 'add', '.torch']);
|
||||
execFileSync('git', ['-C', root, 'commit', '-m', 'install torch']);
|
||||
createWorktrees({
|
||||
repository: inspectRepository(root),
|
||||
parentOverride: mkdtempSync(join(tmpdir(), 'torch-console-worktrees-')),
|
||||
});
|
||||
execFileSync('git', ['-C', root, 'add', '.torch/install-manifest.json']);
|
||||
execFileSync('git', ['-C', root, 'commit', '-m', 'record worktrees']);
|
||||
const databasePath = join(env.XDG_DATA_HOME, 'torch', 'projects', 'console-fixture', 'state.db');
|
||||
assert.equal(existsSync(databasePath), false);
|
||||
const before = execFileSync('git', ['-C', root, 'status', '--porcelain'], { encoding: 'utf8' });
|
||||
@@ -64,6 +76,14 @@ test('SCN-console-readonly: HTTP console serves fixed assets and observes an ins
|
||||
assert.equal(observed.mode, 'installed');
|
||||
assert.equal(observed.mutationPerformed, false);
|
||||
assert.equal(observed.agents.length, proposal.domains.length + 1);
|
||||
assert.equal(observed.organization.domains.length, proposal.domains.length);
|
||||
assert.equal(observed.worktrees.length, proposal.domains.length + 1);
|
||||
assert.equal(observed.worktrees.every((worktree) => Number.isInteger(worktree.behind)), true);
|
||||
assert.equal(observed.recoverability.level, 'ONE-DISK');
|
||||
assert.match(observed.decisions.content, /TORCH decisions/);
|
||||
assert.equal(observed.providers.forge.status, 'local-only');
|
||||
assert.equal(observed.providers.delivery.release.provider, 'none');
|
||||
assert.deepEqual(observed.deliveries, []);
|
||||
assert.equal(observed.contextLocality.measured, false);
|
||||
assert.deepEqual(observed.fleetChanges, []);
|
||||
assert.equal(existsSync(databasePath), false);
|
||||
|
||||
Reference in new issue
Block a user