@ryuenn3123/agentic-senior-core 3.0.50 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent-context/prompts/bootstrap-design.md +3 -1
- package/.agent-context/prompts/research-design.md +165 -0
- package/.agent-context/review-checklists/pr-checklist.md +1 -0
- package/.agent-context/rules/api-docs.md +63 -47
- package/.agent-context/rules/architecture.md +133 -120
- package/.agent-context/rules/database-design.md +36 -18
- package/.agent-context/rules/docker-runtime.md +66 -43
- package/.agent-context/rules/efficiency-vs-hype.md +38 -17
- package/.agent-context/rules/error-handling.md +35 -16
- package/.agent-context/rules/event-driven.md +35 -18
- package/.agent-context/rules/frontend-architecture.md +103 -76
- package/.agent-context/rules/git-workflow.md +81 -197
- package/.agent-context/rules/microservices.md +42 -41
- package/.agent-context/rules/naming-conv.md +27 -8
- package/.agent-context/rules/performance.md +32 -12
- package/.agent-context/rules/realtime.md +26 -9
- package/.agent-context/rules/security.md +39 -20
- package/.agent-context/rules/testing.md +36 -16
- package/AGENTS.md +21 -20
- package/README.md +10 -1
- package/lib/cli/commands/init.mjs +12 -0
- package/lib/cli/commands/upgrade.mjs +11 -0
- package/lib/cli/compiler.mjs +1 -0
- package/lib/cli/detector/constants.mjs +135 -0
- package/lib/cli/detector/design-evidence/collector.mjs +256 -0
- package/lib/cli/detector/design-evidence/constants.mjs +39 -0
- package/lib/cli/detector/design-evidence/file-traversal.mjs +83 -0
- package/lib/cli/detector/design-evidence/structured-attribute-evidence.mjs +117 -0
- package/lib/cli/detector/design-evidence/summary.mjs +109 -0
- package/lib/cli/detector/design-evidence/utility-helpers.mjs +122 -0
- package/lib/cli/detector/design-evidence.mjs +25 -610
- package/lib/cli/detector/stack-detection.mjs +243 -0
- package/lib/cli/detector/ui-signals.mjs +150 -0
- package/lib/cli/detector/workspace-scan.mjs +177 -0
- package/lib/cli/detector.mjs +20 -688
- package/lib/cli/memory-continuity.mjs +1 -0
- package/lib/cli/project-scaffolder/design-contract/research-dossier-migration.mjs +165 -0
- package/lib/cli/project-scaffolder/design-contract/sections/audits.mjs +96 -0
- package/lib/cli/project-scaffolder/design-contract/sections/conceptual-anchor.mjs +233 -0
- package/lib/cli/project-scaffolder/design-contract/sections/execution-handoff.mjs +211 -0
- package/lib/cli/project-scaffolder/design-contract/seed-signals.mjs +79 -0
- package/lib/cli/project-scaffolder/design-contract/signal-vocab.mjs +64 -0
- package/lib/cli/project-scaffolder/design-contract/validation/anchor-validators.mjs +456 -0
- package/lib/cli/project-scaffolder/design-contract/validation/audit-validators.mjs +117 -0
- package/lib/cli/project-scaffolder/design-contract/validation/completeness.mjs +83 -0
- package/lib/cli/project-scaffolder/design-contract/validation/execution-validators.mjs +328 -0
- package/lib/cli/project-scaffolder/design-contract/validation/helpers.mjs +8 -0
- package/lib/cli/project-scaffolder/design-contract/validation/research-dossier-validators.mjs +104 -0
- package/lib/cli/project-scaffolder/design-contract/validation/structural-validators.mjs +79 -0
- package/lib/cli/project-scaffolder/design-contract/validation/system-validators.mjs +256 -0
- package/lib/cli/project-scaffolder/design-contract/validation.mjs +61 -896
- package/lib/cli/project-scaffolder/design-contract.mjs +151 -556
- package/lib/cli/project-scaffolder/prompt-builders.mjs +9 -0
- package/mcp.json +30 -9
- package/package.json +17 -2
- package/scripts/audit-cache-layer-contract.mjs +258 -0
- package/scripts/audit-caching-scope-hygiene.mjs +263 -0
- package/scripts/audit-file-size.mjs +219 -0
- package/scripts/audit-reflection-citations.mjs +163 -0
- package/scripts/audit-release-bundle.mjs +170 -0
- package/scripts/audit-rule-id-uniqueness.mjs +313 -0
- package/scripts/benchmark-evidence-bundle.mjs +1 -0
- package/scripts/build-release-benchmark-bundle.mjs +204 -0
- package/scripts/context-triggered-audit.mjs +1 -0
- package/scripts/documentation-boundary-audit.mjs +1 -0
- package/scripts/explain-on-demand-audit.mjs +2 -1
- package/scripts/frontend-usability-audit.mjs +10 -10
- package/scripts/llm-judge/checklist-loader.mjs +45 -0
- package/scripts/llm-judge/constants.mjs +66 -0
- package/scripts/llm-judge/diff-collection.mjs +74 -0
- package/scripts/llm-judge/prompting.mjs +78 -0
- package/scripts/llm-judge/providers.mjs +111 -0
- package/scripts/llm-judge/verdict.mjs +134 -0
- package/scripts/llm-judge.mjs +21 -482
- package/scripts/mcp-server/tool-registry.mjs +55 -0
- package/scripts/mcp-server/tools.mjs +137 -1
- package/scripts/migrate-rule-format/id-prefix-table.mjs +37 -0
- package/scripts/migrate-rule-format/parse-legacy.mjs +180 -0
- package/scripts/migrate-rule-format/render-new.mjs +169 -0
- package/scripts/migrate-rule-format/roundtrip-validate.mjs +89 -0
- package/scripts/migrate-rule-format.mjs +192 -0
- package/scripts/release-gate/constants.mjs +1 -1
- package/scripts/release-gate/static-checks.mjs +1 -1
- package/scripts/rules-guardian-audit.mjs +5 -2
- package/scripts/single-source-lazy-loading-audit.mjs +2 -1
- package/scripts/ui-design-judge/git-input.mjs +3 -0
- package/scripts/validate/config.mjs +27 -2
- package/scripts/validate/coverage-checks.mjs +1 -1
- package/scripts/validate.mjs +94 -1
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conceptual anchor and math systems validators: enforces the anti-generic
|
|
3
|
+
* anchor selection contract, source domains, visual risk budget, literal
|
|
4
|
+
* translation policy, derived axes, final anchor contract, and the math
|
|
5
|
+
* systems block.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { hasNonEmptyString } from './helpers.mjs';
|
|
9
|
+
|
|
10
|
+
const SEED_STATUSES = new Set([
|
|
11
|
+
'seed-needs-design-synthesis',
|
|
12
|
+
'seed-generated-during-init',
|
|
13
|
+
'seed-generated-during-upgrade',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
function validateUserResearchAbsencePolicy(conceptualAnchor, validationErrors) {
|
|
17
|
+
const userResearchAbsencePolicy = conceptualAnchor.userResearchAbsencePolicy;
|
|
18
|
+
if (!userResearchAbsencePolicy || typeof userResearchAbsencePolicy !== 'object') {
|
|
19
|
+
validationErrors.push('designIntent.conceptualAnchor.userResearchAbsencePolicy must exist.');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (userResearchAbsencePolicy.userSuppliedResearchOnly !== true) {
|
|
23
|
+
validationErrors.push('designIntent.conceptualAnchor.userResearchAbsencePolicy.userSuppliedResearchOnly must equal true.');
|
|
24
|
+
}
|
|
25
|
+
if (userResearchAbsencePolicy.scaffoldSeedDoesNotCountAsResearch !== true) {
|
|
26
|
+
validationErrors.push('designIntent.conceptualAnchor.userResearchAbsencePolicy.scaffoldSeedDoesNotCountAsResearch must equal true.');
|
|
27
|
+
}
|
|
28
|
+
if (userResearchAbsencePolicy.priorUiDoesNotCountAsResearch !== true) {
|
|
29
|
+
validationErrors.push('designIntent.conceptualAnchor.userResearchAbsencePolicy.priorUiDoesNotCountAsResearch must equal true.');
|
|
30
|
+
}
|
|
31
|
+
if (userResearchAbsencePolicy.requireAgentLedResearchWhenAvailable !== true) {
|
|
32
|
+
validationErrors.push('designIntent.conceptualAnchor.userResearchAbsencePolicy.requireAgentLedResearchWhenAvailable must equal true.');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function validateCandidateSelectionPolicy(conceptualAnchor, validationErrors) {
|
|
37
|
+
const candidateSelectionPolicy = conceptualAnchor.candidateSelectionPolicy;
|
|
38
|
+
if (!candidateSelectionPolicy || typeof candidateSelectionPolicy !== 'object') {
|
|
39
|
+
validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy must exist.');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (candidateSelectionPolicy.considerAtLeast < 3) {
|
|
43
|
+
validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.considerAtLeast must be at least 3.');
|
|
44
|
+
}
|
|
45
|
+
if (candidateSelectionPolicy.discardObviousCandidateCount < 2) {
|
|
46
|
+
validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.discardObviousCandidateCount must be at least 2.');
|
|
47
|
+
}
|
|
48
|
+
if (candidateSelectionPolicy.minimumCandidateDistance !== 'high') {
|
|
49
|
+
validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.minimumCandidateDistance must equal "high".');
|
|
50
|
+
}
|
|
51
|
+
if (candidateSelectionPolicy.discardPredictableCandidates !== true) {
|
|
52
|
+
validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.discardPredictableCandidates must equal true.');
|
|
53
|
+
}
|
|
54
|
+
if (candidateSelectionPolicy.preferDistinctiveOverSafe !== true) {
|
|
55
|
+
validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.preferDistinctiveOverSafe must equal true.');
|
|
56
|
+
}
|
|
57
|
+
if (candidateSelectionPolicy.doNotRevealHiddenCandidateList !== true) {
|
|
58
|
+
validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.doNotRevealHiddenCandidateList must equal true.');
|
|
59
|
+
}
|
|
60
|
+
if (candidateSelectionPolicy.outputOnlyChosenAnchor !== true) {
|
|
61
|
+
validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.outputOnlyChosenAnchor must equal true.');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function validateCreativeCommitmentPolicy(conceptualAnchor, validationErrors) {
|
|
66
|
+
const creativeCommitmentPolicy = conceptualAnchor.creativeCommitmentPolicy;
|
|
67
|
+
if (!creativeCommitmentPolicy || typeof creativeCommitmentPolicy !== 'object') {
|
|
68
|
+
validationErrors.push('designIntent.conceptualAnchor.creativeCommitmentPolicy must exist.');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (creativeCommitmentPolicy.requiredBeforeComplianceReview !== true) {
|
|
72
|
+
validationErrors.push('designIntent.conceptualAnchor.creativeCommitmentPolicy.requiredBeforeComplianceReview must equal true.');
|
|
73
|
+
}
|
|
74
|
+
if (creativeCommitmentPolicy.recordInDesignDocs !== true) {
|
|
75
|
+
validationErrors.push('designIntent.conceptualAnchor.creativeCommitmentPolicy.recordInDesignDocs must equal true.');
|
|
76
|
+
}
|
|
77
|
+
if (
|
|
78
|
+
!Array.isArray(creativeCommitmentPolicy.requiredCommitmentFields)
|
|
79
|
+
|| !creativeCommitmentPolicy.requiredCommitmentFields.includes('specificReferencePoint')
|
|
80
|
+
|| !creativeCommitmentPolicy.requiredCommitmentFields.includes('signatureMotion')
|
|
81
|
+
|| !creativeCommitmentPolicy.requiredCommitmentFields.includes('typographicDecision')
|
|
82
|
+
) {
|
|
83
|
+
validationErrors.push('designIntent.conceptualAnchor.creativeCommitmentPolicy.requiredCommitmentFields must include specificReferencePoint, signatureMotion, and typographicDecision.');
|
|
84
|
+
}
|
|
85
|
+
if (creativeCommitmentPolicy.rejectGenericQualityWordsOnly !== true) {
|
|
86
|
+
validationErrors.push('designIntent.conceptualAnchor.creativeCommitmentPolicy.rejectGenericQualityWordsOnly must equal true.');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function validateVisualRiskBudgetAndLiteralPolicy(conceptualAnchor, validationErrors) {
|
|
91
|
+
const visualRiskBudget = conceptualAnchor.visualRiskBudget;
|
|
92
|
+
if (!visualRiskBudget || typeof visualRiskBudget !== 'object') {
|
|
93
|
+
validationErrors.push('designIntent.conceptualAnchor.visualRiskBudget must exist.');
|
|
94
|
+
} else {
|
|
95
|
+
if (visualRiskBudget.mode !== 'high-distinctiveness-with-accessibility-and-performance-guardrails') {
|
|
96
|
+
validationErrors.push('designIntent.conceptualAnchor.visualRiskBudget.mode must preserve high-distinctiveness guardrails.');
|
|
97
|
+
}
|
|
98
|
+
if (visualRiskBudget.allowRichMotionAndMicroInteraction !== true) {
|
|
99
|
+
validationErrors.push('designIntent.conceptualAnchor.visualRiskBudget.allowRichMotionAndMicroInteraction must equal true.');
|
|
100
|
+
}
|
|
101
|
+
if (visualRiskBudget.rejectTimidDefaultWhenAnchorSupportsExpressiveUi !== true) {
|
|
102
|
+
validationErrors.push('designIntent.conceptualAnchor.visualRiskBudget.rejectTimidDefaultWhenAnchorSupportsExpressiveUi must equal true.');
|
|
103
|
+
}
|
|
104
|
+
if (visualRiskBudget.requireReducedMotionFallback !== true) {
|
|
105
|
+
validationErrors.push('designIntent.conceptualAnchor.visualRiskBudget.requireReducedMotionFallback must equal true.');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const literalTranslationPolicy = conceptualAnchor.literalTranslationPolicy;
|
|
110
|
+
if (!literalTranslationPolicy || typeof literalTranslationPolicy !== 'object') {
|
|
111
|
+
validationErrors.push('designIntent.conceptualAnchor.literalTranslationPolicy must exist.');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (literalTranslationPolicy.preferNonLiteralTranslation !== true) {
|
|
115
|
+
validationErrors.push('designIntent.conceptualAnchor.literalTranslationPolicy.preferNonLiteralTranslation must equal true.');
|
|
116
|
+
}
|
|
117
|
+
if (!hasNonEmptyString(literalTranslationPolicy.allowedLiteralUse)) {
|
|
118
|
+
validationErrors.push('designIntent.conceptualAnchor.literalTranslationPolicy.allowedLiteralUse must be a non-empty string.');
|
|
119
|
+
}
|
|
120
|
+
if (!String(literalTranslationPolicy.forbiddenLiteralUse || '').includes('decorative wallpaper')) {
|
|
121
|
+
validationErrors.push('designIntent.conceptualAnchor.literalTranslationPolicy.forbiddenLiteralUse must reject decorative wallpaper.');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function validateFinalAnchorContract(conceptualAnchor, validationErrors) {
|
|
126
|
+
if (
|
|
127
|
+
!Array.isArray(conceptualAnchor.requiredDerivedAxes)
|
|
128
|
+
|| !conceptualAnchor.requiredDerivedAxes.includes('typography')
|
|
129
|
+
|| !conceptualAnchor.requiredDerivedAxes.includes('responsive-composition')
|
|
130
|
+
) {
|
|
131
|
+
validationErrors.push('designIntent.conceptualAnchor.requiredDerivedAxes must include typography and responsive-composition.');
|
|
132
|
+
}
|
|
133
|
+
const finalAnchorContract = conceptualAnchor.finalAnchorContract;
|
|
134
|
+
if (!finalAnchorContract || typeof finalAnchorContract !== 'object') {
|
|
135
|
+
validationErrors.push('designIntent.conceptualAnchor.finalAnchorContract must exist.');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (
|
|
139
|
+
!Array.isArray(finalAnchorContract.requiredFields)
|
|
140
|
+
|| !finalAnchorContract.requiredFields.includes('anchorReference')
|
|
141
|
+
|| !finalAnchorContract.requiredFields.includes('agentResearchMode')
|
|
142
|
+
|| !finalAnchorContract.requiredFields.includes('specificReferencePoint')
|
|
143
|
+
|| !finalAnchorContract.requiredFields.includes('signatureMotion')
|
|
144
|
+
|| !finalAnchorContract.requiredFields.includes('typographicDecision')
|
|
145
|
+
|| !finalAnchorContract.requiredFields.includes('derivedTokenLogic')
|
|
146
|
+
|| !finalAnchorContract.requiredFields.includes('visualRiskBudget')
|
|
147
|
+
|| !finalAnchorContract.requiredFields.includes('motionRiskBudget')
|
|
148
|
+
|| !finalAnchorContract.requiredFields.includes('cohesionChecks')
|
|
149
|
+
) {
|
|
150
|
+
validationErrors.push('designIntent.conceptualAnchor.finalAnchorContract.requiredFields must require anchorReference, agentResearchMode, specificReferencePoint, signatureMotion, typographicDecision, derivedTokenLogic, visualRiskBudget, motionRiskBudget, and cohesionChecks.');
|
|
151
|
+
}
|
|
152
|
+
if (
|
|
153
|
+
!Array.isArray(finalAnchorContract.derivedTokenLogicAxes)
|
|
154
|
+
|| !finalAnchorContract.derivedTokenLogicAxes.includes('motion')
|
|
155
|
+
|| !finalAnchorContract.derivedTokenLogicAxes.includes('morphology')
|
|
156
|
+
) {
|
|
157
|
+
validationErrors.push('designIntent.conceptualAnchor.finalAnchorContract.derivedTokenLogicAxes must include motion and morphology.');
|
|
158
|
+
}
|
|
159
|
+
if (
|
|
160
|
+
!Array.isArray(finalAnchorContract.cohesionChecks)
|
|
161
|
+
|| !finalAnchorContract.cohesionChecks.includes('no-dashboard-mental-model')
|
|
162
|
+
|| !finalAnchorContract.cohesionChecks.includes('motion-derived-from-anchor')
|
|
163
|
+
) {
|
|
164
|
+
validationErrors.push('designIntent.conceptualAnchor.finalAnchorContract.cohesionChecks must reject dashboard mental models and require motion derivation.');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function validateConceptualAnchor(designIntentContract, validationErrors) {
|
|
169
|
+
if (!designIntentContract.conceptualAnchor || typeof designIntentContract.conceptualAnchor !== 'object') {
|
|
170
|
+
validationErrors.push('designIntent.conceptualAnchor must exist.');
|
|
171
|
+
return validationErrors;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const conceptualAnchor = designIntentContract.conceptualAnchor;
|
|
175
|
+
const isSeedStatus = SEED_STATUSES.has(designIntentContract.status);
|
|
176
|
+
|
|
177
|
+
if (conceptualAnchor.mode !== 'required-when-no-external-research') {
|
|
178
|
+
validationErrors.push('designIntent.conceptualAnchor.mode must equal "required-when-no-external-research".');
|
|
179
|
+
}
|
|
180
|
+
if (conceptualAnchor.seedMode !== 'selection-policy-only') {
|
|
181
|
+
validationErrors.push('designIntent.conceptualAnchor.seedMode must equal "selection-policy-only".');
|
|
182
|
+
}
|
|
183
|
+
if (conceptualAnchor.requiresAgentSelectionBeforeUiImplementation !== true) {
|
|
184
|
+
validationErrors.push('designIntent.conceptualAnchor.requiresAgentSelectionBeforeUiImplementation must equal true.');
|
|
185
|
+
}
|
|
186
|
+
if (!hasNonEmptyString(conceptualAnchor.anchorReference)) {
|
|
187
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorReference must be a stable non-empty ID.');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
validateUserResearchAbsencePolicy(conceptualAnchor, validationErrors);
|
|
191
|
+
validateCandidateSelectionPolicy(conceptualAnchor, validationErrors);
|
|
192
|
+
validateCreativeCommitmentPolicy(conceptualAnchor, validationErrors);
|
|
193
|
+
|
|
194
|
+
if (
|
|
195
|
+
!Array.isArray(conceptualAnchor.forbiddenFinalAnchorTerms)
|
|
196
|
+
|| !conceptualAnchor.forbiddenFinalAnchorTerms.includes('dashboard')
|
|
197
|
+
|| !conceptualAnchor.forbiddenFinalAnchorTerms.includes('cards')
|
|
198
|
+
|| !conceptualAnchor.forbiddenFinalAnchorTerms.includes('safe-admin-layout')
|
|
199
|
+
) {
|
|
200
|
+
validationErrors.push('designIntent.conceptualAnchor.forbiddenFinalAnchorTerms must reject basic UI labels.');
|
|
201
|
+
}
|
|
202
|
+
if (
|
|
203
|
+
!Array.isArray(conceptualAnchor.sourceDomains)
|
|
204
|
+
|| conceptualAnchor.sourceDomains.length < 4
|
|
205
|
+
|| !conceptualAnchor.sourceDomains.includes('complex-physical-engineering')
|
|
206
|
+
|| !conceptualAnchor.sourceDomains.includes('cinematic-behavior-and-transition-systems')
|
|
207
|
+
|| !conceptualAnchor.sourceDomains.includes('workflow-and-custody-systems')
|
|
208
|
+
|| !conceptualAnchor.sourceDomains.includes('premium-interactive-web-experiences')
|
|
209
|
+
) {
|
|
210
|
+
validationErrors.push('designIntent.conceptualAnchor.sourceDomains must list broad non-template anchor domains.');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
validateVisualRiskBudgetAndLiteralPolicy(conceptualAnchor, validationErrors);
|
|
214
|
+
validateFinalAnchorContract(conceptualAnchor, validationErrors);
|
|
215
|
+
validateCategoryCodes(conceptualAnchor, validationErrors, { isSeedStatus });
|
|
216
|
+
validateMorphologicalExploration(conceptualAnchor, validationErrors, { isSeedStatus });
|
|
217
|
+
validateAnchorCandidates(conceptualAnchor, validationErrors, { isSeedStatus });
|
|
218
|
+
return validationErrors;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function validateMathSystems(designIntentContract, validationErrors) {
|
|
222
|
+
if (!designIntentContract.mathSystems || typeof designIntentContract.mathSystems !== 'object') {
|
|
223
|
+
validationErrors.push('designIntent.mathSystems must exist.');
|
|
224
|
+
return validationErrors;
|
|
225
|
+
}
|
|
226
|
+
if (!String(designIntentContract.mathSystems.typographyScaleRatio || '').trim()) {
|
|
227
|
+
validationErrors.push('designIntent.mathSystems.typographyScaleRatio must describe the chosen or pending type scale decision.');
|
|
228
|
+
}
|
|
229
|
+
if (!String(designIntentContract.mathSystems.baseGridUnit || '').trim()) {
|
|
230
|
+
validationErrors.push('designIntent.mathSystems.baseGridUnit must describe the chosen or pending spacing grid decision.');
|
|
231
|
+
}
|
|
232
|
+
return validationErrors;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function validateCategoryCodes(conceptualAnchor, validationErrors, options = {}) {
|
|
236
|
+
const { isSeedStatus = false } = options;
|
|
237
|
+
const categoryCodes = conceptualAnchor.categoryCodes;
|
|
238
|
+
if (!categoryCodes || typeof categoryCodes !== 'object') {
|
|
239
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes must exist (Section 3 of research-design.md).');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (categoryCodes.mode !== 'agent-must-complete-before-ui-implementation') {
|
|
243
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.mode must equal "agent-must-complete-before-ui-implementation".');
|
|
244
|
+
}
|
|
245
|
+
if (categoryCodes.blockingByDefault !== true) {
|
|
246
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.blockingByDefault must equal true.');
|
|
247
|
+
}
|
|
248
|
+
if (categoryCodes.researchBriefSection !== 'Section 3') {
|
|
249
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.researchBriefSection must equal "Section 3".');
|
|
250
|
+
}
|
|
251
|
+
if (!hasNonEmptyString(categoryCodes.researchBriefPath) || !categoryCodes.researchBriefPath.includes('research-design.md')) {
|
|
252
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.researchBriefPath must point to the research-design.md brief.');
|
|
253
|
+
}
|
|
254
|
+
if (!Number.isInteger(categoryCodes.minimumEntries) || categoryCodes.minimumEntries < 3) {
|
|
255
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.minimumEntries must be an integer >= 3.');
|
|
256
|
+
}
|
|
257
|
+
if (!hasNonEmptyString(categoryCodes.specificityRule)) {
|
|
258
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.specificityRule must be a non-empty string.');
|
|
259
|
+
}
|
|
260
|
+
if (!hasNonEmptyString(categoryCodes.antiLeakageRule)) {
|
|
261
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.antiLeakageRule must be a non-empty string that prevents the example cliches from being read as target aesthetics.');
|
|
262
|
+
}
|
|
263
|
+
if (!hasNonEmptyString(categoryCodes.selfTestRule)) {
|
|
264
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.selfTestRule must be a non-empty string.');
|
|
265
|
+
}
|
|
266
|
+
if (!Array.isArray(categoryCodes.failingExamples) || categoryCodes.failingExamples.length < 3) {
|
|
267
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.failingExamples must list at least 3 failing examples to anchor the specificity floor.');
|
|
268
|
+
}
|
|
269
|
+
if (!Array.isArray(categoryCodes.passingExamples) || categoryCodes.passingExamples.length < 3) {
|
|
270
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.passingExamples must list at least 3 passing examples to anchor the specificity floor.');
|
|
271
|
+
}
|
|
272
|
+
if (!hasNonEmptyString(categoryCodes.passingExamplesPolicy)) {
|
|
273
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.passingExamplesPolicy must be a non-empty string clarifying that the passing examples are description-format illustrations, not target aesthetics.');
|
|
274
|
+
}
|
|
275
|
+
if (!Array.isArray(categoryCodes.commonAiSafeClichesToReject) || categoryCodes.commonAiSafeClichesToReject.length < 3) {
|
|
276
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.commonAiSafeClichesToReject must list at least 3 AI-safe defaults the agent has to name and reject when the project sits near them.');
|
|
277
|
+
}
|
|
278
|
+
if (!hasNonEmptyString(categoryCodes.commonAiSafeClichesPolicy)) {
|
|
279
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.commonAiSafeClichesPolicy must be a non-empty string requiring the matching cliche to appear with a rejection note when the project pattern-matches it.');
|
|
280
|
+
}
|
|
281
|
+
if (
|
|
282
|
+
!Array.isArray(categoryCodes.requiredFieldsPerEntry)
|
|
283
|
+
|| !categoryCodes.requiredFieldsPerEntry.includes('description')
|
|
284
|
+
|| !categoryCodes.requiredFieldsPerEntry.includes('specificityEvidence')
|
|
285
|
+
|| !categoryCodes.requiredFieldsPerEntry.includes('categoryDefaultReason')
|
|
286
|
+
|| !categoryCodes.requiredFieldsPerEntry.includes('rejectionNote')
|
|
287
|
+
) {
|
|
288
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.requiredFieldsPerEntry must require description, specificityEvidence, categoryDefaultReason, and rejectionNote.');
|
|
289
|
+
}
|
|
290
|
+
if (!Array.isArray(categoryCodes.forbiddenPlaceholderPhrases) || categoryCodes.forbiddenPlaceholderPhrases.length === 0) {
|
|
291
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.forbiddenPlaceholderPhrases must list the abstract phrases that fail the specificity self-test.');
|
|
292
|
+
}
|
|
293
|
+
if (!Array.isArray(categoryCodes.candidateEntries)) {
|
|
294
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.candidateEntries must be an array (empty in the seed; populated by the agent).');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function validateMorphologicalExploration(conceptualAnchor, validationErrors, options = {}) {
|
|
299
|
+
const { isSeedStatus = false } = options;
|
|
300
|
+
const morphologicalExploration = conceptualAnchor.morphologicalExploration;
|
|
301
|
+
if (!morphologicalExploration || typeof morphologicalExploration !== 'object') {
|
|
302
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration must exist (Section 4 of research-design.md).');
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (morphologicalExploration.mode !== 'agent-must-complete-before-ui-implementation') {
|
|
306
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.mode must equal "agent-must-complete-before-ui-implementation".');
|
|
307
|
+
}
|
|
308
|
+
if (morphologicalExploration.blockingByDefault !== true) {
|
|
309
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.blockingByDefault must equal true.');
|
|
310
|
+
}
|
|
311
|
+
if (morphologicalExploration.researchBriefSection !== 'Section 4') {
|
|
312
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.researchBriefSection must equal "Section 4".');
|
|
313
|
+
}
|
|
314
|
+
if (morphologicalExploration.requiredMatrixShape !== '5x5-or-6x5') {
|
|
315
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.requiredMatrixShape must equal "5x5-or-6x5".');
|
|
316
|
+
}
|
|
317
|
+
if (morphologicalExploration.minimumDimensions !== 5) {
|
|
318
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.minimumDimensions must equal 5.');
|
|
319
|
+
}
|
|
320
|
+
if (morphologicalExploration.maximumDimensions !== 6) {
|
|
321
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.maximumDimensions must equal 6.');
|
|
322
|
+
}
|
|
323
|
+
if (morphologicalExploration.valuesPerDimension !== 5) {
|
|
324
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.valuesPerDimension must equal 5.');
|
|
325
|
+
}
|
|
326
|
+
if (morphologicalExploration.forbidCategoryCodeValuesInMatrix !== true) {
|
|
327
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.forbidCategoryCodeValuesInMatrix must equal true so Section 3 cliches do not re-enter the matrix as values.');
|
|
328
|
+
}
|
|
329
|
+
if (morphologicalExploration.requireSelectedCombination !== true) {
|
|
330
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.requireSelectedCombination must equal true.');
|
|
331
|
+
}
|
|
332
|
+
if (morphologicalExploration.requireUncomfortableCombination !== true) {
|
|
333
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.requireUncomfortableCombination must equal true.');
|
|
334
|
+
}
|
|
335
|
+
if (!hasNonEmptyString(morphologicalExploration.uncomfortableCombinationRule)) {
|
|
336
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.uncomfortableCombinationRule must be a non-empty string explaining why the uncomfortable combination is required.');
|
|
337
|
+
}
|
|
338
|
+
if (
|
|
339
|
+
!Array.isArray(morphologicalExploration.uncomfortableCombinationRequiredFields)
|
|
340
|
+
|| !morphologicalExploration.uncomfortableCombinationRequiredFields.includes('combinationLabel')
|
|
341
|
+
|| !morphologicalExploration.uncomfortableCombinationRequiredFields.includes('discomfortReason')
|
|
342
|
+
|| !morphologicalExploration.uncomfortableCombinationRequiredFields.includes('productLogicJustification')
|
|
343
|
+
) {
|
|
344
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.uncomfortableCombinationRequiredFields must include combinationLabel, discomfortReason, and productLogicJustification.');
|
|
345
|
+
}
|
|
346
|
+
if (morphologicalExploration.widenDimensionsWhenUncomfortableCombinationCannotBeProduced !== true) {
|
|
347
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.widenDimensionsWhenUncomfortableCombinationCannotBeProduced must equal true.');
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (morphologicalExploration.uncomfortableCombination !== null && typeof morphologicalExploration.uncomfortableCombination === 'object') {
|
|
351
|
+
const uncomfortableCombination = morphologicalExploration.uncomfortableCombination;
|
|
352
|
+
if (!hasNonEmptyString(uncomfortableCombination.combinationLabel)) {
|
|
353
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.uncomfortableCombination.combinationLabel must be a non-empty string.');
|
|
354
|
+
}
|
|
355
|
+
if (!hasNonEmptyString(uncomfortableCombination.discomfortReason)) {
|
|
356
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.uncomfortableCombination.discomfortReason must be a non-empty string.');
|
|
357
|
+
}
|
|
358
|
+
if (!hasNonEmptyString(uncomfortableCombination.productLogicJustification)) {
|
|
359
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.uncomfortableCombination.productLogicJustification must be a non-empty string.');
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function validateAnchorCandidates(conceptualAnchor, validationErrors, options = {}) {
|
|
365
|
+
const { isSeedStatus = false } = options;
|
|
366
|
+
const anchorCandidates = conceptualAnchor.anchorCandidates;
|
|
367
|
+
if (!anchorCandidates || typeof anchorCandidates !== 'object') {
|
|
368
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates must exist (Section 5 of research-design.md).');
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (anchorCandidates.mode !== 'agent-must-complete-before-ui-implementation') {
|
|
372
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.mode must equal "agent-must-complete-before-ui-implementation".');
|
|
373
|
+
}
|
|
374
|
+
if (anchorCandidates.blockingByDefault !== true) {
|
|
375
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.blockingByDefault must equal true.');
|
|
376
|
+
}
|
|
377
|
+
if (anchorCandidates.researchBriefSection !== 'Section 5') {
|
|
378
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.researchBriefSection must equal "Section 5".');
|
|
379
|
+
}
|
|
380
|
+
if (anchorCandidates.requiredCandidateCount !== 5) {
|
|
381
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.requiredCandidateCount must equal 5.');
|
|
382
|
+
}
|
|
383
|
+
if (
|
|
384
|
+
!Array.isArray(anchorCandidates.requiredFieldsPerCandidate)
|
|
385
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('anchorReference')
|
|
386
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('jobFit')
|
|
387
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('hierarchyImplication')
|
|
388
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('densityImplication')
|
|
389
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('typeImplication')
|
|
390
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('stateLanguage')
|
|
391
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('motionImplication')
|
|
392
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('whatItRulesOut')
|
|
393
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('renameTest')
|
|
394
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('categoryCodeOverlap')
|
|
395
|
+
) {
|
|
396
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.requiredFieldsPerCandidate must list the full anchor dossier fields including renameTest and categoryCodeOverlap.');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const renameTest = anchorCandidates.renameTest;
|
|
400
|
+
if (!renameTest || typeof renameTest !== 'object') {
|
|
401
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest must exist.');
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (renameTest.mode !== 'strengthened') {
|
|
405
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.mode must equal "strengthened".');
|
|
406
|
+
}
|
|
407
|
+
if (renameTest.requiredTestCategoryCount !== 3) {
|
|
408
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.requiredTestCategoryCount must equal 3.');
|
|
409
|
+
}
|
|
410
|
+
if (!hasNonEmptyString(renameTest.testCategoryDistanceRule)) {
|
|
411
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.testCategoryDistanceRule must be a non-empty string requiring remote categories.');
|
|
412
|
+
}
|
|
413
|
+
if (!hasNonEmptyString(renameTest.testCategoryFreshnessRule)) {
|
|
414
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.testCategoryFreshnessRule must be a non-empty string preventing reuse of the same test triple across every anchor.');
|
|
415
|
+
}
|
|
416
|
+
if (!renameTest.verdictScoring || typeof renameTest.verdictScoring !== 'object') {
|
|
417
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.verdictScoring must define STRONG PASS, PASS, REVISE, and DISCARD verdicts.');
|
|
418
|
+
} else {
|
|
419
|
+
for (const verdict of ['STRONG PASS', 'PASS', 'REVISE', 'DISCARD']) {
|
|
420
|
+
if (!hasNonEmptyString(renameTest.verdictScoring[verdict])) {
|
|
421
|
+
validationErrors.push(`designIntent.conceptualAnchor.anchorCandidates.renameTest.verdictScoring["${verdict}"] must be a non-empty string.`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (renameTest.requireTestCategoriesRecordedInDossier !== true) {
|
|
426
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.requireTestCategoriesRecordedInDossier must equal true.');
|
|
427
|
+
}
|
|
428
|
+
if (renameTest.auditableByHumanReviewer !== true) {
|
|
429
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.auditableByHumanReviewer must equal true.');
|
|
430
|
+
}
|
|
431
|
+
if (
|
|
432
|
+
!Array.isArray(renameTest.renameTestRequiredFields)
|
|
433
|
+
|| !renameTest.renameTestRequiredFields.includes('testCategories')
|
|
434
|
+
|| !renameTest.renameTestRequiredFields.includes('results')
|
|
435
|
+
|| !renameTest.renameTestRequiredFields.includes('verdict')
|
|
436
|
+
) {
|
|
437
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.renameTestRequiredFields must include testCategories, results, and verdict.');
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (!hasNonEmptyString(anchorCandidates.categoryCodeOverlapRule)) {
|
|
441
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.categoryCodeOverlapRule must be a non-empty string.');
|
|
442
|
+
}
|
|
443
|
+
if (!Array.isArray(anchorCandidates.forbiddenSelectedVerdicts) || !anchorCandidates.forbiddenSelectedVerdicts.includes('DISCARD')) {
|
|
444
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.forbiddenSelectedVerdicts must include "DISCARD".');
|
|
445
|
+
}
|
|
446
|
+
if (
|
|
447
|
+
!Array.isArray(anchorCandidates.requiredSelectedVerdicts)
|
|
448
|
+
|| !anchorCandidates.requiredSelectedVerdicts.includes('STRONG PASS')
|
|
449
|
+
|| !anchorCandidates.requiredSelectedVerdicts.includes('PASS')
|
|
450
|
+
) {
|
|
451
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.requiredSelectedVerdicts must include "STRONG PASS" and "PASS".');
|
|
452
|
+
}
|
|
453
|
+
if (!Array.isArray(anchorCandidates.candidates)) {
|
|
454
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.candidates must be an array (empty in the seed; populated by the agent).');
|
|
455
|
+
}
|
|
456
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validators for the AI-safe UI audit (with nested AI color and motion-spatial
|
|
3
|
+
* courage audits) and the production content policy. These are the gates that
|
|
4
|
+
* keep template-flavored UI from shipping by default.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function validateAiColorAudit(aiSafeUiAudit, validationErrors) {
|
|
8
|
+
const aiColorAudit = aiSafeUiAudit.aiColorAudit;
|
|
9
|
+
if (!aiColorAudit || typeof aiColorAudit !== 'object') {
|
|
10
|
+
validationErrors.push('designIntent.aiSafeUiAudit.aiColorAudit must exist.');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (aiColorAudit.status !== 'agent-must-complete-before-ui-implementation') {
|
|
14
|
+
validationErrors.push('designIntent.aiSafeUiAudit.aiColorAudit.status must require completion before UI implementation.');
|
|
15
|
+
}
|
|
16
|
+
if (!String(aiColorAudit.failureDefinition || '').includes('AI color')) {
|
|
17
|
+
validationErrors.push('designIntent.aiSafeUiAudit.aiColorAudit.failureDefinition must define AI color drift.');
|
|
18
|
+
}
|
|
19
|
+
if (!Array.isArray(aiColorAudit.autopilotRisks) || aiColorAudit.autopilotRisks.length < 4) {
|
|
20
|
+
validationErrors.push('designIntent.aiSafeUiAudit.aiColorAudit.autopilotRisks must list common autopilot palettes.');
|
|
21
|
+
}
|
|
22
|
+
if (!Array.isArray(aiColorAudit.requiredEvidence) || aiColorAudit.requiredEvidence.length < 3) {
|
|
23
|
+
validationErrors.push('designIntent.aiSafeUiAudit.aiColorAudit.requiredEvidence must list color evidence requirements.');
|
|
24
|
+
}
|
|
25
|
+
if (!String(aiColorAudit.reviewQuestion || '').trim()) {
|
|
26
|
+
validationErrors.push('designIntent.aiSafeUiAudit.aiColorAudit.reviewQuestion must be a non-empty string.');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function validateMotionSpatialCourageAudit(aiSafeUiAudit, validationErrors) {
|
|
31
|
+
const motionSpatialCourageAudit = aiSafeUiAudit.motionSpatialCourageAudit;
|
|
32
|
+
if (!motionSpatialCourageAudit || typeof motionSpatialCourageAudit !== 'object') {
|
|
33
|
+
validationErrors.push('designIntent.aiSafeUiAudit.motionSpatialCourageAudit must exist.');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (motionSpatialCourageAudit.status !== 'agent-must-complete-before-ui-implementation') {
|
|
37
|
+
validationErrors.push('designIntent.aiSafeUiAudit.motionSpatialCourageAudit.status must require completion before UI implementation.');
|
|
38
|
+
}
|
|
39
|
+
if (!String(motionSpatialCourageAudit.defaultStance || '').includes('first-class options')) {
|
|
40
|
+
validationErrors.push('designIntent.aiSafeUiAudit.motionSpatialCourageAudit.defaultStance must treat motion and spatial UI as first-class options.');
|
|
41
|
+
}
|
|
42
|
+
if (!Array.isArray(motionSpatialCourageAudit.requiredDecisionFields) || motionSpatialCourageAudit.requiredDecisionFields.length < 3) {
|
|
43
|
+
validationErrors.push('designIntent.aiSafeUiAudit.motionSpatialCourageAudit.requiredDecisionFields must list required motion/spatial decisions.');
|
|
44
|
+
}
|
|
45
|
+
if (!String(motionSpatialCourageAudit.rejectionRule || '').includes('product reason')) {
|
|
46
|
+
validationErrors.push('designIntent.aiSafeUiAudit.motionSpatialCourageAudit.rejectionRule must require a product reason before omitting spatial UI.');
|
|
47
|
+
}
|
|
48
|
+
if (!String(motionSpatialCourageAudit.reviewQuestion || '').trim()) {
|
|
49
|
+
validationErrors.push('designIntent.aiSafeUiAudit.motionSpatialCourageAudit.reviewQuestion must be a non-empty string.');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function validateAiSafeUiAudit(designIntentContract, validationErrors) {
|
|
54
|
+
if (!designIntentContract.aiSafeUiAudit || typeof designIntentContract.aiSafeUiAudit !== 'object') {
|
|
55
|
+
validationErrors.push('designIntent.aiSafeUiAudit must exist.');
|
|
56
|
+
return validationErrors;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const aiSafeUiAudit = designIntentContract.aiSafeUiAudit;
|
|
60
|
+
if (aiSafeUiAudit.status !== 'agent-must-complete-before-ui-implementation') {
|
|
61
|
+
validationErrors.push('designIntent.aiSafeUiAudit.status must require completion before UI implementation.');
|
|
62
|
+
}
|
|
63
|
+
if (!String(aiSafeUiAudit.failureDefinition || '').includes('AI-safe')) {
|
|
64
|
+
validationErrors.push('designIntent.aiSafeUiAudit.failureDefinition must define AI-safe UI drift.');
|
|
65
|
+
}
|
|
66
|
+
if (!String(aiSafeUiAudit.failureDefinition || '').includes('placeholder copy')) {
|
|
67
|
+
validationErrors.push('designIntent.aiSafeUiAudit.failureDefinition must reject test/demo/placeholder UI copy.');
|
|
68
|
+
}
|
|
69
|
+
if (!String(aiSafeUiAudit.interchangeabilityTest || '').includes('renamed')) {
|
|
70
|
+
validationErrors.push('designIntent.aiSafeUiAudit.interchangeabilityTest must include the rename/interchangeability test.');
|
|
71
|
+
}
|
|
72
|
+
if (!Array.isArray(aiSafeUiAudit.requiredProductSpecificSignals) || aiSafeUiAudit.requiredProductSpecificSignals.length < 3) {
|
|
73
|
+
validationErrors.push('designIntent.aiSafeUiAudit.requiredProductSpecificSignals must list at least three product-specific signals.');
|
|
74
|
+
}
|
|
75
|
+
if (!String(aiSafeUiAudit.paletteExplorationRule || '').trim()) {
|
|
76
|
+
validationErrors.push('designIntent.aiSafeUiAudit.paletteExplorationRule must be a non-empty string.');
|
|
77
|
+
}
|
|
78
|
+
if (!String(aiSafeUiAudit.backgroundPatternRule || '').trim()) {
|
|
79
|
+
validationErrors.push('designIntent.aiSafeUiAudit.backgroundPatternRule must be a non-empty string.');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
validateAiColorAudit(aiSafeUiAudit, validationErrors);
|
|
83
|
+
validateMotionSpatialCourageAudit(aiSafeUiAudit, validationErrors);
|
|
84
|
+
|
|
85
|
+
if (!String(aiSafeUiAudit.reviewQuestion || '').trim()) {
|
|
86
|
+
validationErrors.push('designIntent.aiSafeUiAudit.reviewQuestion must be a non-empty string.');
|
|
87
|
+
}
|
|
88
|
+
if (aiSafeUiAudit.blockingByDefault !== true) {
|
|
89
|
+
validationErrors.push('designIntent.aiSafeUiAudit.blockingByDefault must equal true.');
|
|
90
|
+
}
|
|
91
|
+
return validationErrors;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function validateProductionContentPolicy(designIntentContract, validationErrors) {
|
|
95
|
+
if (!designIntentContract.productionContentPolicy || typeof designIntentContract.productionContentPolicy !== 'object') {
|
|
96
|
+
validationErrors.push('designIntent.productionContentPolicy must exist.');
|
|
97
|
+
return validationErrors;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const productionContentPolicy = designIntentContract.productionContentPolicy;
|
|
101
|
+
if (productionContentPolicy.status !== 'agent-must-complete-before-ui-implementation') {
|
|
102
|
+
validationErrors.push('designIntent.productionContentPolicy.status must require completion before UI implementation.');
|
|
103
|
+
}
|
|
104
|
+
if (!String(productionContentPolicy.userFacingCopyRule || '').includes('testing')) {
|
|
105
|
+
validationErrors.push('designIntent.productionContentPolicy.userFacingCopyRule must reject testing/demo/placeholder UI copy.');
|
|
106
|
+
}
|
|
107
|
+
if (!String(productionContentPolicy.terminalDependencyRule || '').includes('Terminal commands')) {
|
|
108
|
+
validationErrors.push('designIntent.productionContentPolicy.terminalDependencyRule must keep terminal commands out of core UI flows.');
|
|
109
|
+
}
|
|
110
|
+
if (!Array.isArray(productionContentPolicy.allowedExceptions) || productionContentPolicy.allowedExceptions.length < 3) {
|
|
111
|
+
validationErrors.push('designIntent.productionContentPolicy.allowedExceptions must list limited exceptions.');
|
|
112
|
+
}
|
|
113
|
+
if (productionContentPolicy.blockingByDefault !== true) {
|
|
114
|
+
validationErrors.push('designIntent.productionContentPolicy.blockingByDefault must equal true.');
|
|
115
|
+
}
|
|
116
|
+
return validationErrors;
|
|
117
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completeness check for the design intent contract: validates the conceptual
|
|
3
|
+
* anchor reference, derived token logic shape, library research status, and
|
|
4
|
+
* library decision entries. Used both standalone (for incremental seeds) and
|
|
5
|
+
* as the first stage of `validateDesignIntentContract`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { hasNonEmptyString } from './helpers.mjs';
|
|
9
|
+
|
|
10
|
+
export function validateDesignContractCompleteness(designIntentContract) {
|
|
11
|
+
const validationIssues = [];
|
|
12
|
+
const conceptualAnchor = designIntentContract?.conceptualAnchor;
|
|
13
|
+
const derivedTokenLogic = designIntentContract?.derivedTokenLogic;
|
|
14
|
+
const libraryDecisions = designIntentContract?.libraryDecisions;
|
|
15
|
+
|
|
16
|
+
const anchorReference = conceptualAnchor?.anchorReference || derivedTokenLogic?.anchorReference;
|
|
17
|
+
if (!hasNonEmptyString(anchorReference)) {
|
|
18
|
+
validationIssues.push('designIntent.conceptualAnchor.anchorReference must be a stable non-empty ID for deterministic validation.');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!derivedTokenLogic || typeof derivedTokenLogic !== 'object') {
|
|
22
|
+
validationIssues.push('designIntent.derivedTokenLogic must exist.');
|
|
23
|
+
} else {
|
|
24
|
+
if (derivedTokenLogic.anchorReference !== anchorReference) {
|
|
25
|
+
validationIssues.push('designIntent.derivedTokenLogic.anchorReference must exactly match designIntent.conceptualAnchor.anchorReference.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
for (const requiredFieldName of [
|
|
29
|
+
'colorDerivationSource',
|
|
30
|
+
'spacingDerivationSource',
|
|
31
|
+
'typographyDerivationSource',
|
|
32
|
+
'motionDerivationSource',
|
|
33
|
+
'colorSpace',
|
|
34
|
+
'spatialBaseUnit',
|
|
35
|
+
'typeScaleMethod',
|
|
36
|
+
'motionBudget',
|
|
37
|
+
'validationRule',
|
|
38
|
+
]) {
|
|
39
|
+
if (!hasNonEmptyString(derivedTokenLogic[requiredFieldName])) {
|
|
40
|
+
validationIssues.push(`designIntent.derivedTokenLogic.${requiredFieldName} must be a non-empty string.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
hasNonEmptyString(derivedTokenLogic.validationRule)
|
|
46
|
+
&& !derivedTokenLogic.validationRule.includes('anchorReference')
|
|
47
|
+
) {
|
|
48
|
+
validationIssues.push('designIntent.derivedTokenLogic.validationRule must require traceability to anchorReference.');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!['verified', 'pending-verification', 'no-external-library-needed'].includes(designIntentContract?.libraryResearchStatus)) {
|
|
53
|
+
validationIssues.push('designIntent.libraryResearchStatus must be verified, pending-verification, or no-external-library-needed.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!Array.isArray(libraryDecisions)) {
|
|
57
|
+
validationIssues.push('designIntent.libraryDecisions must be an array.');
|
|
58
|
+
} else {
|
|
59
|
+
for (const [libraryIndex, libraryDecision] of libraryDecisions.entries()) {
|
|
60
|
+
if (!libraryDecision || typeof libraryDecision !== 'object') {
|
|
61
|
+
validationIssues.push(`designIntent.libraryDecisions[${libraryIndex}] must be an object.`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!hasNonEmptyString(libraryDecision.library)) {
|
|
66
|
+
validationIssues.push(`designIntent.libraryDecisions[${libraryIndex}].library must be a non-empty string.`);
|
|
67
|
+
}
|
|
68
|
+
if (!hasNonEmptyString(libraryDecision.purpose)) {
|
|
69
|
+
validationIssues.push(`designIntent.libraryDecisions[${libraryIndex}].purpose must be a non-empty string.`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const hasVerification = hasNonEmptyString(libraryDecision.verifiedAt)
|
|
73
|
+
&& hasNonEmptyString(libraryDecision.sourceUrl);
|
|
74
|
+
const hasFallback = hasNonEmptyString(libraryDecision.fallbackIfUnavailable);
|
|
75
|
+
|
|
76
|
+
if (!hasVerification && !hasFallback) {
|
|
77
|
+
validationIssues.push(`designIntent.libraryDecisions[${libraryIndex}] must either record verification source or provide fallbackIfUnavailable.`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return validationIssues;
|
|
83
|
+
}
|