Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions packages/dogfood/test/validation-conformance.ledger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
//
// ADR-0060 P2 — Validation-Rule Conformance ledger (the third instance of the
// reusable pattern). One row per `validations` union rule type, each pinned to
// its runtime enforcement site and a proof. ADR-0020 wired this whole union into
// the write path (engine insert/update → rule-validator.ts), turning every rule
// type from declaration into enforcement; this ledger makes that a CHECKED fact
// and — via the ratchet — makes a NEW rule type with no enforcement a build break
// (the exact pre-ADR-0020 `state_machine` disease).

import type { ConformanceRow } from '@objectstack/verify';

const RULE_VALIDATOR = 'packages/objectql/src/validation/rule-validator.ts';
const PROOF = 'packages/objectql/src/validation/rule-validator.test.ts';

export const VALIDATION_SURFACE: ConformanceRow[] = [
{
id: 'rule-state-machine',
summary: 'state_machine — legal status-transition guard (ADR-0020 D3)',
surface: 'validation.zod.ts:state_machine',
state: 'enforced',
enforcement: `${RULE_VALIDATOR} checkStateMachine — engine insert/update path rejects an illegal from→to transition`,
covers: ['state_machine'],
proof: PROOF,
},
{
id: 'rule-script',
summary: 'script — arbitrary CEL failure predicate',
surface: 'validation.zod.ts:script',
state: 'enforced',
enforcement: `${RULE_VALIDATOR} checkPredicate — CEL predicate; TRUE = violation`,
covers: ['script'],
proof: PROOF,
},
{
id: 'rule-cross-field',
summary: 'cross_field — multi-field CEL invariant',
surface: 'validation.zod.ts:cross_field',
state: 'enforced',
enforcement: `${RULE_VALIDATOR} checkPredicate — evaluated against the merged record (prior ∪ change)`,
covers: ['cross_field'],
proof: PROOF,
},
{
id: 'rule-format',
summary: 'format — value-shape rule (regex/builtin)',
surface: 'validation.zod.ts:format',
state: 'enforced',
enforcement: `${RULE_VALIDATOR} checkFormat`,
covers: ['format'],
proof: PROOF,
},
{
id: 'rule-json-schema',
summary: 'json_schema — structural validation of a JSON field',
surface: 'validation.zod.ts:json_schema',
state: 'enforced',
enforcement: `${RULE_VALIDATOR} checkJsonSchema`,
covers: ['json_schema'],
proof: PROOF,
},
{
id: 'rule-conditional',
summary: 'conditional — when/then predicate rule',
surface: 'validation.zod.ts:conditional',
state: 'enforced',
enforcement: `${RULE_VALIDATOR} checkConditional`,
covers: ['conditional'],
proof: PROOF,
},
];
46 changes: 46 additions & 0 deletions packages/dogfood/test/validation-conformance.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
//
// ADR-0060 P2 — the validation-rule conformance ledger is a CHECKED artifact,
// built on the SAME reusable checkLedger helper as the authz and expression
// ledgers (proving the pattern composes: a new surface is a filled-in table, not
// a re-derived discipline). The ratchet re-discovers every `validations` union
// rule type from spec source and fails the build if any is unclassified — so a
// new validation rule type added without runtime enforcement (the pre-ADR-0020
// `state_machine` failure) can't ship silently.

import { describe, expect, it } from 'vitest';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { checkLedger } from '@objectstack/verify';
import { VALIDATION_SURFACE } from './validation-conformance.ledger.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = join(HERE, '../../..');
const VALIDATION_ZOD = join(REPO_ROOT, 'packages/spec/src/data/validation.zod.ts');

/** Re-discover every `validations` union rule type from its discriminator. */
function discoverRuleTypes(): Set<string> {
const src = readFileSync(VALIDATION_ZOD, 'utf8');
const found = new Set<string>();
for (const m of src.matchAll(/type:\s*z\.literal\('([a-z_]+)'\)/g)) found.add(m[1]);
return found;
}

describe('ADR-0060 P2 — validation-rule conformance ledger', () => {
it('is a sound conformance ledger + ratchet, every rule type enforced + proven', () => {
const problems = checkLedger(VALIDATION_SURFACE, {
proofRoot: REPO_ROOT,
discover: discoverRuleTypes,
proofRequiredForEnforced: true, // a write-path guard must carry a runtime proof
});
expect(problems, problems.join('\n')).toEqual([]);
});

it('sanity: discovery finds the known rule types', () => {
const found = discoverRuleTypes();
for (const t of ['state_machine', 'script', 'cross_field', 'format', 'json_schema', 'conditional']) {
expect(found.has(t), `rule type '${t}' not discovered`).toBe(true);
}
});
});
Loading