Classify tenant apply policy actions
Refine tenant-apply planning so future automatic candidates, manual-only steps, and permanent out-of-scope actions are reported explicitly instead of being implied by generic prose. --- Build: pass | Tests: pass — 31 passed (1 file)
This commit is contained in:
parent
0f81c69d28
commit
253cdcecb6
5 changed files with 111 additions and 0 deletions
|
|
@ -282,6 +282,16 @@ Any future live `tenant-apply` must satisfy these rules:
|
||||||
- require explicit operator review before host mutation
|
- require explicit operator review before host mutation
|
||||||
- keep per-tenant hostd, Unix users, and repo workspaces out of scope
|
- keep per-tenant hostd, Unix users, and repo workspaces out of scope
|
||||||
|
|
||||||
|
The intended policy buckets are:
|
||||||
|
|
||||||
|
- future automatic candidates:
|
||||||
|
tenant-owned databases, tenant-owned worker jails, tenant-owned datasets
|
||||||
|
- manual-only:
|
||||||
|
operator approval and any host-level execution step
|
||||||
|
- out of scope:
|
||||||
|
per-tenant hostd, per-tenant Unix users, per-tenant repo workspaces, any
|
||||||
|
shared platform resource mutation
|
||||||
|
|
||||||
Preflight for a future live apply should at minimum verify:
|
Preflight for a future live apply should at minimum verify:
|
||||||
|
|
||||||
- tenant exists in the registry
|
- tenant exists in the registry
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,8 @@ Also updated:
|
||||||
- what remains manual or explicitly out of scope
|
- what remains manual or explicitly out of scope
|
||||||
- a structured preflight checklist showing what already passes in the
|
- a structured preflight checklist showing what already passes in the
|
||||||
declarative model and what still blocks any automatic apply
|
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
|
||||||
|
|
||||||
## Recommended first code tasks
|
## Recommended first code tasks
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,17 @@ function printApplyPlan(
|
||||||
console.log(`Allowed databases: ${plan.allowedResources.databases.join(', ')}`);
|
console.log(`Allowed databases: ${plan.allowedResources.databases.join(', ')}`);
|
||||||
console.log(`Allowed worker jails: ${plan.allowedResources.workerJails.join(', ')}`);
|
console.log(`Allowed worker jails: ${plan.allowedResources.workerJails.join(', ')}`);
|
||||||
console.log(`Allowed datasets: ${plan.allowedResources.datasets.join(', ') || '(none)'}`);
|
console.log(`Allowed datasets: ${plan.allowedResources.datasets.join(', ') || '(none)'}`);
|
||||||
|
console.log('Action policy:');
|
||||||
|
for (const action of plan.actionPolicy.automaticCandidates) {
|
||||||
|
console.log(`- [future-auto] ${action.name}: ${action.resources.join(', ') || '(none)'}`);
|
||||||
|
console.log(` ${action.detail}`);
|
||||||
|
}
|
||||||
|
for (const action of plan.actionPolicy.manualOnly) {
|
||||||
|
console.log(`- [manual] ${action.name}: ${action.detail}`);
|
||||||
|
}
|
||||||
|
for (const action of plan.actionPolicy.outOfScope) {
|
||||||
|
console.log(`- [out-of-scope] ${action.name}: ${action.detail}`);
|
||||||
|
}
|
||||||
if (plan.blockers.length > 0) {
|
if (plan.blockers.length > 0) {
|
||||||
console.log('Blockers:');
|
console.log('Blockers:');
|
||||||
for (const blocker of plan.blockers) {
|
for (const blocker of plan.blockers) {
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,24 @@ describe('tenant-registry', () => {
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
expect(plan.actionPolicy.automaticCandidates).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
name: 'Tenant databases',
|
||||||
|
resources: expect.arrayContaining(['mevy_brain']),
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
name: 'Tenant worker jails',
|
||||||
|
resources: expect.arrayContaining(['mevy_ctrl_worker']),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
expect(plan.actionPolicy.outOfScope).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ name: 'Per-tenant hostd' }),
|
||||||
|
expect.objectContaining({ name: 'Shared platform resources' }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
expect(plan.manualSteps).toContain(
|
expect(plan.manualSteps).toContain(
|
||||||
'No live resources are created by this workflow today.',
|
'No live resources are created by this workflow today.',
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,21 @@ export interface TenantApplyPlan {
|
||||||
tenant: TenantRecord;
|
tenant: TenantRecord;
|
||||||
registryPath: string;
|
registryPath: string;
|
||||||
readyForAutomaticApply: boolean;
|
readyForAutomaticApply: boolean;
|
||||||
|
actionPolicy: {
|
||||||
|
automaticCandidates: Array<{
|
||||||
|
name: string;
|
||||||
|
resources: string[];
|
||||||
|
detail: string;
|
||||||
|
}>;
|
||||||
|
manualOnly: Array<{
|
||||||
|
name: string;
|
||||||
|
detail: string;
|
||||||
|
}>;
|
||||||
|
outOfScope: Array<{
|
||||||
|
name: string;
|
||||||
|
detail: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
allowedResources: {
|
allowedResources: {
|
||||||
databases: string[];
|
databases: string[];
|
||||||
workerJails: string[];
|
workerJails: string[];
|
||||||
|
|
@ -682,6 +697,61 @@ export function planTenantApply(
|
||||||
tenant,
|
tenant,
|
||||||
registryPath,
|
registryPath,
|
||||||
readyForAutomaticApply: false,
|
readyForAutomaticApply: false,
|
||||||
|
actionPolicy: {
|
||||||
|
automaticCandidates: [
|
||||||
|
{
|
||||||
|
name: 'Tenant databases',
|
||||||
|
resources: Object.values(tenant.databases),
|
||||||
|
detail:
|
||||||
|
'A future live apply may create tenant-owned databases only, with no shared DB mutation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tenant worker jails',
|
||||||
|
resources: tenant.workerJails,
|
||||||
|
detail:
|
||||||
|
'A future live apply may provision tenant-owned worker jails only, subject to host-level review.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tenant datasets',
|
||||||
|
resources: tenant.datasets,
|
||||||
|
detail:
|
||||||
|
'A future live apply may create tenant-owned datasets only, without touching shared platform datasets.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
manualOnly: [
|
||||||
|
{
|
||||||
|
name: 'Operator approval',
|
||||||
|
detail:
|
||||||
|
'Any future live apply must require explicit operator confirmation before host mutation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Host-level execution',
|
||||||
|
detail:
|
||||||
|
'Any future jail, dataset, or database creation must run as an explicit host-level step.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
outOfScope: [
|
||||||
|
{
|
||||||
|
name: 'Per-tenant hostd',
|
||||||
|
detail: 'Tenant apply must never create a separate hostd service.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Per-tenant Unix user',
|
||||||
|
detail:
|
||||||
|
'Tenant apply must not create a tenant-scoped Unix user or home contract.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Per-tenant repo workspace',
|
||||||
|
detail:
|
||||||
|
'Tenant apply must not create a tenant-specific repo checkout or runtime workspace.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Shared platform resources',
|
||||||
|
detail:
|
||||||
|
'Tenant apply must never create, mutate, or delete shared platform services, datasets, or jails.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
allowedResources: {
|
allowedResources: {
|
||||||
databases: Object.values(tenant.databases),
|
databases: Object.values(tenant.databases),
|
||||||
workerJails: tenant.workerJails,
|
workerJails: tenant.workerJails,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue