Add tenant apply resource checklist

Refine tenant-apply dry runs with per-resource status entries so databases, worker jails, and datasets are reported as explicit future-create candidates instead of only appearing inside summary sections.

---
Build: pass | Tests: pass — 31 passed (1 file)
This commit is contained in:
Mevy Assistant 2026-04-24 09:39:45 +02:00
parent d3ae1f3ade
commit daf29fa332
5 changed files with 79 additions and 0 deletions

View file

@ -298,3 +298,9 @@ Preflight for a future live apply should at minimum verify:
- tenant resources are still disjoint from shared platform resources
- tenant resources are still disjoint from other tenants
- no blocked manual step is being silently skipped
The dry-run apply surface should also report a per-resource checklist:
- `would-create` for tenant-owned resources a future live apply could create
- `exists-in-contract` for resources already satisfied declaratively
- `blocked` for anything a future live apply must not touch yet

View file

@ -202,6 +202,8 @@ Also updated:
declarative model and what still blocks any automatic apply
- explicit policy buckets for future automatic candidates, manual-only
steps, and permanent out-of-scope actions
- a per-resource checklist showing `would-create`, `exists-in-contract`, or
`blocked` status for databases, datasets, and worker jails
## Recommended first code tasks

View file

@ -167,6 +167,16 @@ function printApplyPlan(
console.log(`Allowed databases: ${plan.allowedResources.databases.join(', ')}`);
console.log(`Allowed worker jails: ${plan.allowedResources.workerJails.join(', ')}`);
console.log(`Allowed datasets: ${plan.allowedResources.datasets.join(', ') || '(none)'}`);
console.log('Resource checklist:');
for (const entry of plan.resourceChecklist.databases) {
console.log(`- [${entry.status}] database ${entry.name}: ${entry.detail}`);
}
for (const entry of plan.resourceChecklist.workerJails) {
console.log(`- [${entry.status}] worker-jail ${entry.name}: ${entry.detail}`);
}
for (const entry of plan.resourceChecklist.datasets) {
console.log(`- [${entry.status}] dataset ${entry.name}: ${entry.detail}`);
}
console.log('Action policy:');
for (const action of plan.actionPolicy.automaticCandidates) {
console.log(`- [future-auto] ${action.name}: ${action.resources.join(', ') || '(none)'}`);

View file

@ -115,6 +115,30 @@ describe('tenant-registry', () => {
expect.objectContaining({ name: 'Shared platform resources' }),
]),
);
expect(plan.resourceChecklist.databases).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'mevy_brain',
status: 'would-create',
}),
]),
);
expect(plan.resourceChecklist.workerJails).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'mevy_ctrl_worker',
status: 'would-create',
}),
]),
);
expect(plan.resourceChecklist.datasets).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'zroot/mevy-ai',
status: 'would-create',
}),
]),
);
expect(plan.manualSteps).toContain(
'No live resources are created by this workflow today.',
);

View file

@ -139,6 +139,23 @@ export interface TenantApplyPlan {
tenant: TenantRecord;
registryPath: string;
readyForAutomaticApply: boolean;
resourceChecklist: {
databases: Array<{
name: string;
status: 'would-create' | 'exists-in-contract' | 'blocked';
detail: string;
}>;
workerJails: Array<{
name: string;
status: 'would-create' | 'exists-in-contract' | 'blocked';
detail: string;
}>;
datasets: Array<{
name: string;
status: 'would-create' | 'exists-in-contract' | 'blocked';
detail: string;
}>;
};
actionPolicy: {
automaticCandidates: Array<{
name: string;
@ -697,6 +714,26 @@ export function planTenantApply(
tenant,
registryPath,
readyForAutomaticApply: false,
resourceChecklist: {
databases: Object.values(tenant.databases).map((name) => ({
name,
status: 'would-create',
detail:
'Future live apply could create this tenant-owned database, but no host mutation exists yet.',
})),
workerJails: tenant.workerJails.map((name) => ({
name,
status: 'would-create',
detail:
'Future live apply could provision this tenant-owned worker jail, subject to host-level review.',
})),
datasets: tenant.datasets.map((name) => ({
name,
status: 'would-create',
detail:
'Future live apply could create this tenant-owned dataset without touching shared platform datasets.',
})),
},
actionPolicy: {
automaticCandidates: [
{