@ryuenn3123/agentic-senior-core 3.0.49 → 4.0.0

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.
Files changed (84) hide show
  1. package/.agent-context/prompts/bootstrap-design.md +2 -1
  2. package/.agent-context/review-checklists/pr-checklist.md +1 -0
  3. package/.agent-context/rules/api-docs.md +63 -45
  4. package/.agent-context/rules/architecture.md +133 -118
  5. package/.agent-context/rules/database-design.md +36 -16
  6. package/.agent-context/rules/docker-runtime.md +66 -43
  7. package/.agent-context/rules/efficiency-vs-hype.md +38 -17
  8. package/.agent-context/rules/error-handling.md +35 -14
  9. package/.agent-context/rules/event-driven.md +35 -18
  10. package/.agent-context/rules/frontend-architecture.md +103 -74
  11. package/.agent-context/rules/git-workflow.md +81 -197
  12. package/.agent-context/rules/microservices.md +42 -41
  13. package/.agent-context/rules/naming-conv.md +27 -6
  14. package/.agent-context/rules/performance.md +32 -10
  15. package/.agent-context/rules/realtime.md +26 -9
  16. package/.agent-context/rules/security.md +39 -19
  17. package/.agent-context/rules/testing.md +36 -15
  18. package/AGENTS.md +9 -9
  19. package/README.md +10 -1
  20. package/lib/cli/commands/init.mjs +1 -0
  21. package/lib/cli/compiler.mjs +1 -0
  22. package/lib/cli/detector/constants.mjs +135 -0
  23. package/lib/cli/detector/design-evidence/collector.mjs +256 -0
  24. package/lib/cli/detector/design-evidence/constants.mjs +39 -0
  25. package/lib/cli/detector/design-evidence/file-traversal.mjs +83 -0
  26. package/lib/cli/detector/design-evidence/structured-attribute-evidence.mjs +117 -0
  27. package/lib/cli/detector/design-evidence/summary.mjs +109 -0
  28. package/lib/cli/detector/design-evidence/utility-helpers.mjs +122 -0
  29. package/lib/cli/detector/design-evidence.mjs +25 -610
  30. package/lib/cli/detector/stack-detection.mjs +243 -0
  31. package/lib/cli/detector/ui-signals.mjs +150 -0
  32. package/lib/cli/detector/workspace-scan.mjs +177 -0
  33. package/lib/cli/detector.mjs +20 -688
  34. package/lib/cli/memory-continuity.mjs +1 -0
  35. package/lib/cli/project-scaffolder/design-contract/sections/audits.mjs +96 -0
  36. package/lib/cli/project-scaffolder/design-contract/sections/conceptual-anchor.mjs +116 -0
  37. package/lib/cli/project-scaffolder/design-contract/sections/execution-handoff.mjs +211 -0
  38. package/lib/cli/project-scaffolder/design-contract/seed-signals.mjs +79 -0
  39. package/lib/cli/project-scaffolder/design-contract/signal-vocab.mjs +64 -0
  40. package/lib/cli/project-scaffolder/design-contract/validation/anchor-validators.mjs +222 -0
  41. package/lib/cli/project-scaffolder/design-contract/validation/audit-validators.mjs +117 -0
  42. package/lib/cli/project-scaffolder/design-contract/validation/completeness.mjs +83 -0
  43. package/lib/cli/project-scaffolder/design-contract/validation/execution-validators.mjs +328 -0
  44. package/lib/cli/project-scaffolder/design-contract/validation/helpers.mjs +8 -0
  45. package/lib/cli/project-scaffolder/design-contract/validation/structural-validators.mjs +79 -0
  46. package/lib/cli/project-scaffolder/design-contract/validation/system-validators.mjs +256 -0
  47. package/lib/cli/project-scaffolder/design-contract/validation.mjs +59 -896
  48. package/lib/cli/project-scaffolder/design-contract.mjs +147 -557
  49. package/mcp.json +30 -9
  50. package/package.json +17 -2
  51. package/scripts/audit-cache-layer-contract.mjs +258 -0
  52. package/scripts/audit-caching-scope-hygiene.mjs +263 -0
  53. package/scripts/audit-file-size.mjs +219 -0
  54. package/scripts/audit-reflection-citations.mjs +163 -0
  55. package/scripts/audit-release-bundle.mjs +170 -0
  56. package/scripts/audit-rule-id-uniqueness.mjs +313 -0
  57. package/scripts/benchmark-evidence-bundle.mjs +1 -0
  58. package/scripts/build-release-benchmark-bundle.mjs +204 -0
  59. package/scripts/context-triggered-audit.mjs +1 -0
  60. package/scripts/documentation-boundary-audit.mjs +1 -0
  61. package/scripts/explain-on-demand-audit.mjs +2 -1
  62. package/scripts/frontend-usability-audit.mjs +10 -10
  63. package/scripts/llm-judge/checklist-loader.mjs +45 -0
  64. package/scripts/llm-judge/constants.mjs +66 -0
  65. package/scripts/llm-judge/diff-collection.mjs +74 -0
  66. package/scripts/llm-judge/prompting.mjs +78 -0
  67. package/scripts/llm-judge/providers.mjs +111 -0
  68. package/scripts/llm-judge/verdict.mjs +134 -0
  69. package/scripts/llm-judge.mjs +21 -482
  70. package/scripts/mcp-server/tool-registry.mjs +55 -0
  71. package/scripts/mcp-server/tools.mjs +137 -1
  72. package/scripts/migrate-rule-format/id-prefix-table.mjs +37 -0
  73. package/scripts/migrate-rule-format/parse-legacy.mjs +180 -0
  74. package/scripts/migrate-rule-format/render-new.mjs +169 -0
  75. package/scripts/migrate-rule-format/roundtrip-validate.mjs +89 -0
  76. package/scripts/migrate-rule-format.mjs +192 -0
  77. package/scripts/release-gate/constants.mjs +1 -1
  78. package/scripts/release-gate/static-checks.mjs +1 -1
  79. package/scripts/rules-guardian-audit.mjs +5 -2
  80. package/scripts/single-source-lazy-loading-audit.mjs +2 -1
  81. package/scripts/ui-design-judge/git-input.mjs +3 -0
  82. package/scripts/validate/config.mjs +3 -2
  83. package/scripts/validate/coverage-checks.mjs +1 -1
  84. package/scripts/validate.mjs +93 -1
@@ -0,0 +1,222 @@
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
+ function validateUserResearchAbsencePolicy(conceptualAnchor, validationErrors) {
11
+ const userResearchAbsencePolicy = conceptualAnchor.userResearchAbsencePolicy;
12
+ if (!userResearchAbsencePolicy || typeof userResearchAbsencePolicy !== 'object') {
13
+ validationErrors.push('designIntent.conceptualAnchor.userResearchAbsencePolicy must exist.');
14
+ return;
15
+ }
16
+ if (userResearchAbsencePolicy.userSuppliedResearchOnly !== true) {
17
+ validationErrors.push('designIntent.conceptualAnchor.userResearchAbsencePolicy.userSuppliedResearchOnly must equal true.');
18
+ }
19
+ if (userResearchAbsencePolicy.scaffoldSeedDoesNotCountAsResearch !== true) {
20
+ validationErrors.push('designIntent.conceptualAnchor.userResearchAbsencePolicy.scaffoldSeedDoesNotCountAsResearch must equal true.');
21
+ }
22
+ if (userResearchAbsencePolicy.priorUiDoesNotCountAsResearch !== true) {
23
+ validationErrors.push('designIntent.conceptualAnchor.userResearchAbsencePolicy.priorUiDoesNotCountAsResearch must equal true.');
24
+ }
25
+ if (userResearchAbsencePolicy.requireAgentLedResearchWhenAvailable !== true) {
26
+ validationErrors.push('designIntent.conceptualAnchor.userResearchAbsencePolicy.requireAgentLedResearchWhenAvailable must equal true.');
27
+ }
28
+ }
29
+
30
+ function validateCandidateSelectionPolicy(conceptualAnchor, validationErrors) {
31
+ const candidateSelectionPolicy = conceptualAnchor.candidateSelectionPolicy;
32
+ if (!candidateSelectionPolicy || typeof candidateSelectionPolicy !== 'object') {
33
+ validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy must exist.');
34
+ return;
35
+ }
36
+ if (candidateSelectionPolicy.considerAtLeast < 3) {
37
+ validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.considerAtLeast must be at least 3.');
38
+ }
39
+ if (candidateSelectionPolicy.discardObviousCandidateCount < 2) {
40
+ validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.discardObviousCandidateCount must be at least 2.');
41
+ }
42
+ if (candidateSelectionPolicy.minimumCandidateDistance !== 'high') {
43
+ validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.minimumCandidateDistance must equal "high".');
44
+ }
45
+ if (candidateSelectionPolicy.discardPredictableCandidates !== true) {
46
+ validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.discardPredictableCandidates must equal true.');
47
+ }
48
+ if (candidateSelectionPolicy.preferDistinctiveOverSafe !== true) {
49
+ validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.preferDistinctiveOverSafe must equal true.');
50
+ }
51
+ if (candidateSelectionPolicy.doNotRevealHiddenCandidateList !== true) {
52
+ validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.doNotRevealHiddenCandidateList must equal true.');
53
+ }
54
+ if (candidateSelectionPolicy.outputOnlyChosenAnchor !== true) {
55
+ validationErrors.push('designIntent.conceptualAnchor.candidateSelectionPolicy.outputOnlyChosenAnchor must equal true.');
56
+ }
57
+ }
58
+
59
+ function validateCreativeCommitmentPolicy(conceptualAnchor, validationErrors) {
60
+ const creativeCommitmentPolicy = conceptualAnchor.creativeCommitmentPolicy;
61
+ if (!creativeCommitmentPolicy || typeof creativeCommitmentPolicy !== 'object') {
62
+ validationErrors.push('designIntent.conceptualAnchor.creativeCommitmentPolicy must exist.');
63
+ return;
64
+ }
65
+ if (creativeCommitmentPolicy.requiredBeforeComplianceReview !== true) {
66
+ validationErrors.push('designIntent.conceptualAnchor.creativeCommitmentPolicy.requiredBeforeComplianceReview must equal true.');
67
+ }
68
+ if (creativeCommitmentPolicy.recordInDesignDocs !== true) {
69
+ validationErrors.push('designIntent.conceptualAnchor.creativeCommitmentPolicy.recordInDesignDocs must equal true.');
70
+ }
71
+ if (
72
+ !Array.isArray(creativeCommitmentPolicy.requiredCommitmentFields)
73
+ || !creativeCommitmentPolicy.requiredCommitmentFields.includes('specificReferencePoint')
74
+ || !creativeCommitmentPolicy.requiredCommitmentFields.includes('signatureMotion')
75
+ || !creativeCommitmentPolicy.requiredCommitmentFields.includes('typographicDecision')
76
+ ) {
77
+ validationErrors.push('designIntent.conceptualAnchor.creativeCommitmentPolicy.requiredCommitmentFields must include specificReferencePoint, signatureMotion, and typographicDecision.');
78
+ }
79
+ if (creativeCommitmentPolicy.rejectGenericQualityWordsOnly !== true) {
80
+ validationErrors.push('designIntent.conceptualAnchor.creativeCommitmentPolicy.rejectGenericQualityWordsOnly must equal true.');
81
+ }
82
+ }
83
+
84
+ function validateVisualRiskBudgetAndLiteralPolicy(conceptualAnchor, validationErrors) {
85
+ const visualRiskBudget = conceptualAnchor.visualRiskBudget;
86
+ if (!visualRiskBudget || typeof visualRiskBudget !== 'object') {
87
+ validationErrors.push('designIntent.conceptualAnchor.visualRiskBudget must exist.');
88
+ } else {
89
+ if (visualRiskBudget.mode !== 'high-distinctiveness-with-accessibility-and-performance-guardrails') {
90
+ validationErrors.push('designIntent.conceptualAnchor.visualRiskBudget.mode must preserve high-distinctiveness guardrails.');
91
+ }
92
+ if (visualRiskBudget.allowRichMotionAndMicroInteraction !== true) {
93
+ validationErrors.push('designIntent.conceptualAnchor.visualRiskBudget.allowRichMotionAndMicroInteraction must equal true.');
94
+ }
95
+ if (visualRiskBudget.rejectTimidDefaultWhenAnchorSupportsExpressiveUi !== true) {
96
+ validationErrors.push('designIntent.conceptualAnchor.visualRiskBudget.rejectTimidDefaultWhenAnchorSupportsExpressiveUi must equal true.');
97
+ }
98
+ if (visualRiskBudget.requireReducedMotionFallback !== true) {
99
+ validationErrors.push('designIntent.conceptualAnchor.visualRiskBudget.requireReducedMotionFallback must equal true.');
100
+ }
101
+ }
102
+
103
+ const literalTranslationPolicy = conceptualAnchor.literalTranslationPolicy;
104
+ if (!literalTranslationPolicy || typeof literalTranslationPolicy !== 'object') {
105
+ validationErrors.push('designIntent.conceptualAnchor.literalTranslationPolicy must exist.');
106
+ return;
107
+ }
108
+ if (literalTranslationPolicy.preferNonLiteralTranslation !== true) {
109
+ validationErrors.push('designIntent.conceptualAnchor.literalTranslationPolicy.preferNonLiteralTranslation must equal true.');
110
+ }
111
+ if (!hasNonEmptyString(literalTranslationPolicy.allowedLiteralUse)) {
112
+ validationErrors.push('designIntent.conceptualAnchor.literalTranslationPolicy.allowedLiteralUse must be a non-empty string.');
113
+ }
114
+ if (!String(literalTranslationPolicy.forbiddenLiteralUse || '').includes('decorative wallpaper')) {
115
+ validationErrors.push('designIntent.conceptualAnchor.literalTranslationPolicy.forbiddenLiteralUse must reject decorative wallpaper.');
116
+ }
117
+ }
118
+
119
+ function validateFinalAnchorContract(conceptualAnchor, validationErrors) {
120
+ if (
121
+ !Array.isArray(conceptualAnchor.requiredDerivedAxes)
122
+ || !conceptualAnchor.requiredDerivedAxes.includes('typography')
123
+ || !conceptualAnchor.requiredDerivedAxes.includes('responsive-composition')
124
+ ) {
125
+ validationErrors.push('designIntent.conceptualAnchor.requiredDerivedAxes must include typography and responsive-composition.');
126
+ }
127
+ const finalAnchorContract = conceptualAnchor.finalAnchorContract;
128
+ if (!finalAnchorContract || typeof finalAnchorContract !== 'object') {
129
+ validationErrors.push('designIntent.conceptualAnchor.finalAnchorContract must exist.');
130
+ return;
131
+ }
132
+ if (
133
+ !Array.isArray(finalAnchorContract.requiredFields)
134
+ || !finalAnchorContract.requiredFields.includes('anchorReference')
135
+ || !finalAnchorContract.requiredFields.includes('agentResearchMode')
136
+ || !finalAnchorContract.requiredFields.includes('specificReferencePoint')
137
+ || !finalAnchorContract.requiredFields.includes('signatureMotion')
138
+ || !finalAnchorContract.requiredFields.includes('typographicDecision')
139
+ || !finalAnchorContract.requiredFields.includes('derivedTokenLogic')
140
+ || !finalAnchorContract.requiredFields.includes('visualRiskBudget')
141
+ || !finalAnchorContract.requiredFields.includes('motionRiskBudget')
142
+ || !finalAnchorContract.requiredFields.includes('cohesionChecks')
143
+ ) {
144
+ validationErrors.push('designIntent.conceptualAnchor.finalAnchorContract.requiredFields must require anchorReference, agentResearchMode, specificReferencePoint, signatureMotion, typographicDecision, derivedTokenLogic, visualRiskBudget, motionRiskBudget, and cohesionChecks.');
145
+ }
146
+ if (
147
+ !Array.isArray(finalAnchorContract.derivedTokenLogicAxes)
148
+ || !finalAnchorContract.derivedTokenLogicAxes.includes('motion')
149
+ || !finalAnchorContract.derivedTokenLogicAxes.includes('morphology')
150
+ ) {
151
+ validationErrors.push('designIntent.conceptualAnchor.finalAnchorContract.derivedTokenLogicAxes must include motion and morphology.');
152
+ }
153
+ if (
154
+ !Array.isArray(finalAnchorContract.cohesionChecks)
155
+ || !finalAnchorContract.cohesionChecks.includes('no-dashboard-mental-model')
156
+ || !finalAnchorContract.cohesionChecks.includes('motion-derived-from-anchor')
157
+ ) {
158
+ validationErrors.push('designIntent.conceptualAnchor.finalAnchorContract.cohesionChecks must reject dashboard mental models and require motion derivation.');
159
+ }
160
+ }
161
+
162
+ export function validateConceptualAnchor(designIntentContract, validationErrors) {
163
+ if (!designIntentContract.conceptualAnchor || typeof designIntentContract.conceptualAnchor !== 'object') {
164
+ validationErrors.push('designIntent.conceptualAnchor must exist.');
165
+ return validationErrors;
166
+ }
167
+
168
+ const conceptualAnchor = designIntentContract.conceptualAnchor;
169
+ if (conceptualAnchor.mode !== 'required-when-no-external-research') {
170
+ validationErrors.push('designIntent.conceptualAnchor.mode must equal "required-when-no-external-research".');
171
+ }
172
+ if (conceptualAnchor.seedMode !== 'selection-policy-only') {
173
+ validationErrors.push('designIntent.conceptualAnchor.seedMode must equal "selection-policy-only".');
174
+ }
175
+ if (conceptualAnchor.requiresAgentSelectionBeforeUiImplementation !== true) {
176
+ validationErrors.push('designIntent.conceptualAnchor.requiresAgentSelectionBeforeUiImplementation must equal true.');
177
+ }
178
+ if (!hasNonEmptyString(conceptualAnchor.anchorReference)) {
179
+ validationErrors.push('designIntent.conceptualAnchor.anchorReference must be a stable non-empty ID.');
180
+ }
181
+
182
+ validateUserResearchAbsencePolicy(conceptualAnchor, validationErrors);
183
+ validateCandidateSelectionPolicy(conceptualAnchor, validationErrors);
184
+ validateCreativeCommitmentPolicy(conceptualAnchor, validationErrors);
185
+
186
+ if (
187
+ !Array.isArray(conceptualAnchor.forbiddenFinalAnchorTerms)
188
+ || !conceptualAnchor.forbiddenFinalAnchorTerms.includes('dashboard')
189
+ || !conceptualAnchor.forbiddenFinalAnchorTerms.includes('cards')
190
+ || !conceptualAnchor.forbiddenFinalAnchorTerms.includes('safe-admin-layout')
191
+ ) {
192
+ validationErrors.push('designIntent.conceptualAnchor.forbiddenFinalAnchorTerms must reject basic UI labels.');
193
+ }
194
+ if (
195
+ !Array.isArray(conceptualAnchor.sourceDomains)
196
+ || conceptualAnchor.sourceDomains.length < 4
197
+ || !conceptualAnchor.sourceDomains.includes('complex-physical-engineering')
198
+ || !conceptualAnchor.sourceDomains.includes('cinematic-behavior-and-transition-systems')
199
+ || !conceptualAnchor.sourceDomains.includes('workflow-and-custody-systems')
200
+ || !conceptualAnchor.sourceDomains.includes('premium-interactive-web-experiences')
201
+ ) {
202
+ validationErrors.push('designIntent.conceptualAnchor.sourceDomains must list broad non-template anchor domains.');
203
+ }
204
+
205
+ validateVisualRiskBudgetAndLiteralPolicy(conceptualAnchor, validationErrors);
206
+ validateFinalAnchorContract(conceptualAnchor, validationErrors);
207
+ return validationErrors;
208
+ }
209
+
210
+ export function validateMathSystems(designIntentContract, validationErrors) {
211
+ if (!designIntentContract.mathSystems || typeof designIntentContract.mathSystems !== 'object') {
212
+ validationErrors.push('designIntent.mathSystems must exist.');
213
+ return validationErrors;
214
+ }
215
+ if (!String(designIntentContract.mathSystems.typographyScaleRatio || '').trim()) {
216
+ validationErrors.push('designIntent.mathSystems.typographyScaleRatio must describe the chosen or pending type scale decision.');
217
+ }
218
+ if (!String(designIntentContract.mathSystems.baseGridUnit || '').trim()) {
219
+ validationErrors.push('designIntent.mathSystems.baseGridUnit must describe the chosen or pending spacing grid decision.');
220
+ }
221
+ return validationErrors;
222
+ }
@@ -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
+ }