Skip to content

Commit 2d9855f

Browse files
tjpinderclaude
andcommitted
v1.9.0: add CRIT-SV-CUSTOMER-ATTESTATION-VALIDATION-GATE — third C3 axis
Surfaced by the ADAComplianceDocs 2026-04-29 audit (audit 5 of 7). The product introduced a structurally new Layer 2 shape the framework hadn't previously modeled: a customer-attested status field whose publish path must reference a system-verified validation gate before the attestation can persist. Distinct from the two existing C3 axes: - Output-attestation provenance (v1.4.0+): "this customer-facing attestation references customer-submitted evidence" (FL's RateSnapshotJson on Platform_RateLetters) - Input-data provenance (v1.8.0): "this data is labeled as customer-attested vs system-derived" (HP's customer_attested enum on Hireposture_AuditFindings) The new third axis: "this customer-attested data cannot publish until system-verified evidence supports it" (ADA's checkConformanceGuard in src/lib/statements.ts which blocks Platform_Statements.conformanceStatus='full' publishes when critical/serious axe-core findings are open) Provenance markers say WHERE data came from. Validation gates say WHEN data may publish. Different verbs, different runtime semantics — genuinely new principle, not a calibration of v1.8.0. The new rule (CRIT-SV-CUSTOMER-ATTESTATION-VALIDATION-GATE) is a co-occurrence check: - Trigger: customer-attested status column declarations (conformanceStatus, complianceStatus, attestationStatus, certificationStatus, wcagConformance, etc.) - Required: any validation-gate function or typed-error pattern (checkConformance, conformanceGuard, validateBeforePublish, acknowledgeOpenFindings, ConformanceGuardBlockedError, etc.) - Vacuous-pass when trigger never fires (most products) Validated backwards-compatible across the portfolio: - FieldLedger: no trigger (no customer-attested status field) PASS - ClarityLift: no trigger PASS - IdeaLift: no trigger PASS - Hireposture: no trigger (customer_attested is enum on findings, not a status publish flow) PASS - ADAComplianceDocs: trigger fires, gate present PASS First-implementation note: trigger pattern list will need calibration as MA / PRAPI / future products surface edge cases. Closes framework #14. Case study: https://github.com/Startvest-LLC/adacompliancedocs/blob/main/audits/tif-compliance.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent dc68593 commit 2d9855f

2 files changed

Lines changed: 117 additions & 1 deletion

File tree

manifests/base-v1.json

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
{
22
"$schema": "../docs/manifest-schema.md",
33
"name": "Startvest Integrity Framework — base manifest",
4-
"version": "1.8.0",
4+
"version": "1.9.0",
55
"changelog": [
6+
{
7+
"version": "1.9.0",
8+
"date": "2026-04-29",
9+
"changes": [
10+
"Added CRIT-SV-CUSTOMER-ATTESTATION-VALIDATION-GATE — third C3 axis. Triggered by ADAComplianceDocs 2026-04-29 audit which surfaced a structurally new Layer 2 shape: customer-attestation publish paths must reference a system-verified validation gate before persisting. Distinct from the two existing C3 axes — output-attestation provenance (v1.4.0+: 'this customer-facing attestation references customer-submitted evidence', e.g. FL's RateSnapshotJson) and input-data provenance (v1.8.0: 'this data is labeled as customer-attested vs system-derived', e.g. HP's customer_attested enum). The new third axis: 'this customer-attested data cannot publish until system-verified evidence supports it', e.g. ADA's checkConformanceGuard at src/lib/statements.ts which blocks Platform_Statements.conformanceStatus='full' publishes when critical/serious axe-core findings are open. Provenance markers say WHERE data came from; validation gates say WHEN data may publish. Different verbs, different runtime semantics — genuinely new principle, not a calibration of v1.8.0. Co-occurrence rule: trigger on customer-attested status column declarations (conformanceStatus, complianceStatus, attestationStatus, certificationStatus, wcagConformance, etc.); require any validation-gate function pattern (checkConformance, conformanceGuard, validateBeforePublish, acknowledgeOpenFindings, ConformanceGuardBlockedError, etc.) somewhere in the corpus. Vacuous-pass when trigger never fires (most products). Validated backwards-compatible against FL (no trigger fires), CL (no trigger), IL (no trigger), HP (no trigger — HP's customer_attested is provenance enum on findings, not a status publish flow), ADA (trigger fires + gate present + passes). First-implementation note: trigger pattern list will need calibration as MA / PRAPI / future products surface edge cases. Closes framework #14. Case study: https://github.com/Startvest-LLC/adacompliancedocs/blob/main/audits/tif-compliance.md#headline-finding-customer-attestation-validation-gate-v190-calibration"
11+
]
12+
},
613
{
714
"version": "1.8.0",
815
"date": "2026-04-29",
@@ -305,6 +312,53 @@
305312
}
306313
}
307314
},
315+
{
316+
"id": "CRIT-SV-CUSTOMER-ATTESTATION-VALIDATION-GATE",
317+
"severity": "CRITICAL",
318+
"title": "Customer-attestation publish paths must reference a system-verified validation gate",
319+
"why": "The third Layer 2 Constraint 3 axis (added v1.9.0 after the ADAComplianceDocs audit). Provenance markers (v1.4.0+ output, v1.8.0 input) say WHERE customer-attested data came from. This rule says WHEN it may publish. Specifically: when a product persists a customer-attested status field — a column on a persisted entity that the customer sets to declare a compliance, conformance, certification, or attestation outcome — the publish path must reference a system-verified validation gate (a function that checks the attestation against the available system-verified evidence and refuses or audit-logs override). Without this gate, the framework cannot prevent the overlay-vendor failure pattern: a customer publishes 'fully compliant' while system-verified findings remain open. Canonical example: ADAComplianceDocs's Platform_Statements.conformanceStatus='full' is gated by checkConformanceGuard in src/lib/statements.ts which queries open critical/serious axe-core findings before allowing the publish. Override permitted via acknowledgeOpenFindings=true but audit-logged. Vacuous-pass when the product has no customer-attested status field — most products don't.",
320+
"fix": "If a persisted entity carries a customer-attested status column (conformanceStatus, complianceStatus, attestationStatus, certificationStatus, wcagConformance, etc.), the publish path must call a validation gate (checkConformance, conformanceGuard, validateBeforePublish, requireOpenFindingsResolved, etc.) before persisting. The gate may permit override via an explicit acknowledgement parameter — but the override must be audit-logged. Guard signatures matching ConformanceGuardBlockedError or similar typed-error patterns satisfy the rule; so do guard-function names that include 'validate', 'check', 'guard', 'gate', or 'require' as a verb prefix.",
321+
"researchCitation": "Startvest Integrity Framework — Constraint 3 (Self-Attestation Isolation), third axis. Triggered by ADAComplianceDocs 2026-04-29 audit. Closes framework #14.",
322+
"check": {
323+
"kind": "co-occurrence",
324+
"globs": [
325+
"src/**/*.{ts,tsx,js,mjs}",
326+
"app/**/*.{ts,tsx,js,mjs}",
327+
"services/**/*.{ts,tsx,js,mjs}",
328+
"lib/**/*.{ts,tsx,js,mjs}",
329+
"apps/**/*.{ts,tsx,js,mjs}",
330+
"packages/**/*.{ts,tsx,js,mjs}",
331+
"sql/**/*.sql",
332+
"scripts/**/*.sql"
333+
],
334+
"trigger": {
335+
"patterns": [
336+
"\\b(?:conformance|compliance|attestation|certification|wcag|accessibility)Status\\s*:\\s*z\\.enum\\(",
337+
"\\b(?:conformance|compliance|attestation|certification|wcag|accessibility)Status\\s*:\\s*\\{",
338+
"\\b(?:conformance|compliance|attestation|certification|wcag|accessibility)Status\\s*:\\s*['\"](?:full|partial|compliant|non-conformant|certified|attested)",
339+
"\\b(?:conformance|compliance|attestation|certification|wcag|accessibility)_status\\s+(?:nvarchar|varchar|nchar|enum)",
340+
"\\bConformanceLevel\\s*:\\s*(?:z\\.enum|\\{)",
341+
"\\bComplianceLevel\\s*:\\s*(?:z\\.enum|\\{)",
342+
"tableName\\s*:\\s*['\"][^'\"]*Statement(?:s|Schema|Entity)?['\"]",
343+
"tableName\\s*:\\s*['\"][^'\"]*Conformance(?:s|Schema|Entity)?['\"]",
344+
"tableName\\s*:\\s*['\"][^'\"]*ComplianceClaim(?:s|Schema|Entity)?['\"]"
345+
]
346+
},
347+
"required": {
348+
"patterns": [
349+
"\\b(?:check|validate|guard|require|enforce)(?:Conformance|Compliance|Attestation|BeforePublish|PrePublish|OpenFinding|PublishGate)\\b",
350+
"\\b(?:conformance|compliance|attestation|publish|validation)\\s*[Gg]ate\\b",
351+
"\\backnowledgeOpenFindings\\b",
352+
"\\b(?:Conformance|Compliance|Attestation|Publish|Validation)Guard(?:Blocked)?Error\\b",
353+
"checkConformanceGuard",
354+
"validateBeforePublish",
355+
"requireOpenFindingsResolved",
356+
"publishGuard",
357+
"attestationGuard"
358+
]
359+
}
360+
}
361+
},
308362
{
309363
"id": "HIGH-SV-INTEGRITY-MD-CLAIMS-VERIFIABLE",
310364
"severity": "HIGH",

test/runner.test.mjs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,67 @@ async function testIntegrityMdClaimsStructuralLint() {
360360
}
361361
}
362362

363+
async function testCustomerAttestationValidationGate() {
364+
// ADA-shape positive test: trigger fires (conformanceStatus on a Statement
365+
// table) and validation gate is present in corpus → PASS.
366+
// Negative test: trigger fires + gate missing → FAIL.
367+
// Vacuous test: no trigger fires → PASS-vacuous.
368+
const root = await makeRepo();
369+
try {
370+
await mkdir(join(root, 'src', 'lib'), { recursive: true });
371+
372+
const rule = {
373+
id: 'TEST-VALIDATION-GATE',
374+
severity: 'CRITICAL',
375+
title: 'validation gate test',
376+
why: 'C3 third axis',
377+
fix: '-',
378+
researchCitation: 'test',
379+
check: {
380+
kind: 'co-occurrence',
381+
globs: ['src/**/*.{ts,tsx,js,mjs}'],
382+
trigger: {
383+
patterns: [
384+
'\\b(?:conformance|compliance|attestation)Status\\b',
385+
'tableName\\s*:\\s*[\'"][^\'"]*Statement',
386+
],
387+
},
388+
required: {
389+
patterns: [
390+
'\\b(?:check|validate|guard)(?:Conformance|Compliance|BeforePublish)\\b',
391+
'\\backnowledgeOpenFindings\\b',
392+
'checkConformanceGuard',
393+
],
394+
},
395+
},
396+
};
397+
398+
// Negative: schema declares conformanceStatus, no gate present.
399+
await writeFile(
400+
join(root, 'src', 'lib', 'schema.ts'),
401+
`export const StatementSchema = { tableName: 'Platform_Statements', columns: { conformanceStatus: { type: 'enum' } } };\n`,
402+
);
403+
let results = await runRules([rule], root);
404+
assert.equal(results[0].passed, false, 'should FAIL when status column declared but no gate present');
405+
406+
// Positive: add the gate to a different file in the corpus.
407+
await writeFile(
408+
join(root, 'src', 'lib', 'statements.ts'),
409+
`export function checkConformanceGuard(orgId: string) { /* refuses 'full' if open findings */ }\n`,
410+
);
411+
results = await runRules([rule], root);
412+
assert.equal(results[0].passed, true, 'should PASS when gate present somewhere in corpus');
413+
414+
// Vacuous: remove the trigger entirely.
415+
await rm(join(root, 'src', 'lib', 'schema.ts'));
416+
results = await runRules([rule], root);
417+
assert.equal(results[0].passed, true, 'should PASS vacuously when no trigger fires');
418+
assert.equal(results[0].vacuous, true);
419+
} finally {
420+
await rm(root, { recursive: true, force: true });
421+
}
422+
}
423+
363424
async function testIntegrityMdClaimsCatchesReverseDrift() {
364425
// Reproduces IdeaLift's 2026-04-29 reverse-drift case. INTEGRITY.md
365426
// Outstanding Risks claims a Trust Principles link is missing; the
@@ -691,6 +752,7 @@ const tests = [
691752
['forbidden-regex', testForbiddenRegex],
692753
['required-regex (vacuous + matchAll)', testRequiredRegexVacuous],
693754
['co-occurrence (trigger + required + vacuous)', testCoOccurrence],
755+
['CRIT-SV-CUSTOMER-ATTESTATION-VALIDATION-GATE: ADA-shape', testCustomerAttestationValidationGate],
694756
['integrity-md-claims: catches CL-style drift', testIntegrityMdClaimsCatchesClarityLiftDrift],
695757
['integrity-md-claims: passes after fix', testIntegrityMdClaimsPassesAfterFix],
696758
['integrity-md-claims: structural lint + strikethrough', testIntegrityMdClaimsStructuralLint],

0 commit comments

Comments
 (0)