@ryuenn3123/agentic-senior-core 3.0.16 → 3.0.19
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 +31 -4
- package/.agent-context/rules/frontend-architecture.md +26 -0
- package/.agent-context/state/memory-continuity-benchmark.json +1 -1
- package/.cursorrules +1 -1
- package/.gemini/instructions.md +7 -1
- package/.github/copilot-instructions.md +7 -1
- package/.instructions.md +3 -0
- package/.windsurfrules +1 -1
- package/AGENTS.md +13 -1
- package/lib/cli/commands/init.mjs +2 -2
- package/lib/cli/memory-continuity.mjs +2 -1
- package/lib/cli/project-scaffolder/constants.mjs +1 -0
- package/lib/cli/project-scaffolder/design-contract.mjs +523 -171
- package/lib/cli/project-scaffolder/prompt-builders.mjs +38 -15
- package/lib/cli/project-scaffolder/storage.mjs +0 -2
- package/package.json +2 -2
- package/scripts/documentation-boundary-audit.mjs +5 -2
- package/scripts/frontend-usability-audit.mjs +34 -0
- package/scripts/mcp-server/constants.mjs +60 -0
- package/scripts/mcp-server/tool-registry.mjs +149 -0
- package/scripts/mcp-server/tools.mjs +446 -0
- package/scripts/mcp-server.mjs +23 -661
- package/scripts/release-gate/audit-checks.mjs +426 -0
- package/scripts/release-gate/constants.mjs +53 -0
- package/scripts/release-gate/runtime.mjs +63 -0
- package/scripts/release-gate/static-checks.mjs +182 -0
- package/scripts/release-gate.mjs +12 -771
- package/scripts/sync-thin-adapters.mjs +24 -0
- package/scripts/ui-design-judge/constants.mjs +24 -0
- package/scripts/ui-design-judge/design-execution-summary.mjs +233 -0
- package/scripts/ui-design-judge/git-input.mjs +131 -0
- package/scripts/ui-design-judge/prompting.mjs +73 -0
- package/scripts/ui-design-judge/providers.mjs +102 -0
- package/scripts/ui-design-judge/reporting.mjs +181 -0
- package/scripts/ui-design-judge/rubric-calibration.mjs +211 -0
- package/scripts/ui-design-judge/rubric-goldset.json +188 -0
- package/scripts/ui-design-judge.mjs +130 -441
- package/scripts/ui-rubric-calibration.mjs +35 -0
- package/scripts/validate/config.mjs +98 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { detectCiProvider } from './git-input.mjs';
|
|
4
|
+
|
|
5
|
+
function normalizeSeverity(rawSeverityValue) {
|
|
6
|
+
const normalizedSeverityValue = String(rawSeverityValue || '').trim().toLowerCase();
|
|
7
|
+
|
|
8
|
+
if (['critical', 'high', 'medium', 'low'].includes(normalizedSeverityValue)) {
|
|
9
|
+
return normalizedSeverityValue;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (normalizedSeverityValue === 'major') {
|
|
13
|
+
return 'high';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (normalizedSeverityValue === 'minor' || normalizedSeverityValue === 'info') {
|
|
17
|
+
return 'low';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return 'low';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function extractVerdictObject(rawResponseText) {
|
|
24
|
+
const verdictMatch = rawResponseText.match(/JSON_VERDICT:\s*(\{[\s\S]*\})/i);
|
|
25
|
+
if (!verdictMatch) {
|
|
26
|
+
return { verdict: null, malformed: true };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
return {
|
|
31
|
+
verdict: JSON.parse(verdictMatch[1]),
|
|
32
|
+
malformed: false,
|
|
33
|
+
};
|
|
34
|
+
} catch {
|
|
35
|
+
return {
|
|
36
|
+
verdict: null,
|
|
37
|
+
malformed: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function normalizeFindings(rawFindings) {
|
|
43
|
+
if (!Array.isArray(rawFindings)) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return rawFindings.map((rawFinding) => ({
|
|
48
|
+
area: String(rawFinding?.area || 'general'),
|
|
49
|
+
severity: normalizeSeverity(rawFinding?.severity),
|
|
50
|
+
problem: String(rawFinding?.problem || 'No problem description provided.'),
|
|
51
|
+
evidence: String(rawFinding?.evidence || 'No evidence provided.'),
|
|
52
|
+
recommendation: String(rawFinding?.recommendation || 'No recommendation provided.'),
|
|
53
|
+
blockingRecommended: rawFinding?.blockingRecommended === true,
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function normalizeRubricVerdict(rawVerdictValue) {
|
|
58
|
+
const normalizedVerdictValue = String(rawVerdictValue || '').trim().toLowerCase();
|
|
59
|
+
if (['strong', 'acceptable', 'weak', 'unclear'].includes(normalizedVerdictValue)) {
|
|
60
|
+
return normalizedVerdictValue;
|
|
61
|
+
}
|
|
62
|
+
return 'unclear';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function normalizeRubricBreakdown(rawRubricBreakdown, expectedDimensions = []) {
|
|
66
|
+
if (!Array.isArray(rawRubricBreakdown)) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const expectedDimensionNames = Array.isArray(expectedDimensions)
|
|
71
|
+
? expectedDimensions.map((dimension) => String(dimension || '').trim()).filter(Boolean)
|
|
72
|
+
: [];
|
|
73
|
+
|
|
74
|
+
return rawRubricBreakdown
|
|
75
|
+
.map((rawDimensionEntry) => ({
|
|
76
|
+
dimension: String(rawDimensionEntry?.dimension || '').trim(),
|
|
77
|
+
score: typeof rawDimensionEntry?.score === 'number' && Number.isFinite(rawDimensionEntry.score)
|
|
78
|
+
? rawDimensionEntry.score
|
|
79
|
+
: null,
|
|
80
|
+
verdict: normalizeRubricVerdict(rawDimensionEntry?.verdict),
|
|
81
|
+
reason: String(rawDimensionEntry?.reason || 'No rubric reason provided.'),
|
|
82
|
+
blocking: rawDimensionEntry?.blocking === true,
|
|
83
|
+
}))
|
|
84
|
+
.filter((dimensionEntry) => {
|
|
85
|
+
if (!dimensionEntry.dimension) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return expectedDimensionNames.length === 0 || expectedDimensionNames.includes(dimensionEntry.dimension);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function normalizeGenericityAssessment(rawGenericityAssessment) {
|
|
93
|
+
const normalizedStatus = String(rawGenericityAssessment?.status || '').trim().toLowerCase();
|
|
94
|
+
return {
|
|
95
|
+
status: ['distinctive', 'mixed', 'generic', 'unclear'].includes(normalizedStatus)
|
|
96
|
+
? normalizedStatus
|
|
97
|
+
: 'unclear',
|
|
98
|
+
reason: String(rawGenericityAssessment?.reason || 'No genericity assessment provided.'),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function buildReport(partialReport) {
|
|
103
|
+
return {
|
|
104
|
+
generatedAt: new Date().toISOString(),
|
|
105
|
+
auditName: 'ui-design-judge',
|
|
106
|
+
schemaVersion: '1.2',
|
|
107
|
+
mode: 'advisory',
|
|
108
|
+
advisoryOnly: true,
|
|
109
|
+
passed: true,
|
|
110
|
+
skipped: false,
|
|
111
|
+
skipReason: null,
|
|
112
|
+
provider: 'none',
|
|
113
|
+
ciProvider: detectCiProvider(),
|
|
114
|
+
contractPresent: false,
|
|
115
|
+
summary: {
|
|
116
|
+
changedUiFileCount: 0,
|
|
117
|
+
alignmentScore: null,
|
|
118
|
+
driftCount: 0,
|
|
119
|
+
blockingCandidateCount: 0,
|
|
120
|
+
designExecutionSignalCount: 0,
|
|
121
|
+
genericityStatus: 'unclear',
|
|
122
|
+
},
|
|
123
|
+
designExecution: {
|
|
124
|
+
policyPresent: false,
|
|
125
|
+
representationStrategy: null,
|
|
126
|
+
contractReady: false,
|
|
127
|
+
screenshotDependencyForbidden: false,
|
|
128
|
+
repoEvidenceAvailable: false,
|
|
129
|
+
handoffPresent: false,
|
|
130
|
+
handoffVersion: null,
|
|
131
|
+
handoffReady: false,
|
|
132
|
+
handoffArtifactCount: 0,
|
|
133
|
+
presentHandoffArtifacts: [],
|
|
134
|
+
missingHandoffArtifacts: [],
|
|
135
|
+
repoEvidenceSummaryVersion: null,
|
|
136
|
+
requiredCapabilities: [],
|
|
137
|
+
enabledCapabilities: [],
|
|
138
|
+
missingCapabilities: [],
|
|
139
|
+
semanticReviewFocus: [],
|
|
140
|
+
notes: [],
|
|
141
|
+
},
|
|
142
|
+
rubric: {
|
|
143
|
+
expectedDimensions: [],
|
|
144
|
+
breakdown: [],
|
|
145
|
+
genericityAssessment: {
|
|
146
|
+
status: 'unclear',
|
|
147
|
+
reason: 'No genericity assessment provided.',
|
|
148
|
+
},
|
|
149
|
+
tasteVsFailureSeparated: null,
|
|
150
|
+
calibration: {
|
|
151
|
+
version: 'ui-rubric-calibration-v1',
|
|
152
|
+
providerStatus: 'unclear',
|
|
153
|
+
calibratedStatus: 'unclear',
|
|
154
|
+
statusChanged: false,
|
|
155
|
+
namedGenericityRequired: false,
|
|
156
|
+
matchedGenericitySignals: [],
|
|
157
|
+
matchedValidBoldSignals: [],
|
|
158
|
+
blockingFindingCount: 0,
|
|
159
|
+
contractFidelityWeak: false,
|
|
160
|
+
contractDriftDetected: false,
|
|
161
|
+
tasteVsFailureSeparated: null,
|
|
162
|
+
evidenceTextCount: 0,
|
|
163
|
+
notes: ['No rubric calibration was performed.'],
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
semanticJudge: {
|
|
167
|
+
attempted: false,
|
|
168
|
+
skipped: false,
|
|
169
|
+
skipReason: null,
|
|
170
|
+
},
|
|
171
|
+
malformedVerdict: false,
|
|
172
|
+
providerError: false,
|
|
173
|
+
findings: [],
|
|
174
|
+
notes: [],
|
|
175
|
+
...partialReport,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function emitMachineReadableReport(machineReportPayload) {
|
|
180
|
+
console.log(JSON.stringify(machineReportPayload, null, 2));
|
|
181
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
function normalizeForSignalMatch(rawValue) {
|
|
4
|
+
return String(rawValue || '')
|
|
5
|
+
.toLowerCase()
|
|
6
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
7
|
+
.trim()
|
|
8
|
+
.replace(/\s+/g, ' ');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function collectEvidenceTexts({
|
|
12
|
+
genericityAssessment,
|
|
13
|
+
rubricBreakdown,
|
|
14
|
+
findings,
|
|
15
|
+
notes,
|
|
16
|
+
}) {
|
|
17
|
+
const textParts = [
|
|
18
|
+
genericityAssessment?.reason,
|
|
19
|
+
...(Array.isArray(rubricBreakdown)
|
|
20
|
+
? rubricBreakdown.flatMap((dimensionEntry) => [dimensionEntry?.dimension, dimensionEntry?.reason])
|
|
21
|
+
: []),
|
|
22
|
+
...(Array.isArray(findings)
|
|
23
|
+
? findings.flatMap((finding) => [
|
|
24
|
+
finding?.area,
|
|
25
|
+
finding?.problem,
|
|
26
|
+
finding?.evidence,
|
|
27
|
+
finding?.recommendation,
|
|
28
|
+
])
|
|
29
|
+
: []),
|
|
30
|
+
...(Array.isArray(notes) ? notes : []),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
return textParts
|
|
34
|
+
.map((textValue) => normalizeForSignalMatch(textValue))
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function collectMatchedSignals(signalList, evidenceTexts) {
|
|
39
|
+
const normalizedEvidenceText = Array.isArray(evidenceTexts)
|
|
40
|
+
? evidenceTexts.join(' ')
|
|
41
|
+
: normalizeForSignalMatch(evidenceTexts);
|
|
42
|
+
|
|
43
|
+
if (!normalizedEvidenceText) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (Array.isArray(signalList) ? signalList : [])
|
|
48
|
+
.map((signalValue) => ({
|
|
49
|
+
raw: String(signalValue || '').trim(),
|
|
50
|
+
normalized: normalizeForSignalMatch(signalValue),
|
|
51
|
+
}))
|
|
52
|
+
.filter((signalEntry) => signalEntry.raw && signalEntry.normalized)
|
|
53
|
+
.filter((signalEntry) => normalizedEvidenceText.includes(signalEntry.normalized))
|
|
54
|
+
.map((signalEntry) => signalEntry.raw);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function detectContractDrift(rubricBreakdown, findings, designExecutionSummary) {
|
|
58
|
+
const contractFidelityEntry = Array.isArray(rubricBreakdown)
|
|
59
|
+
? rubricBreakdown.find((dimensionEntry) => dimensionEntry?.dimension === 'contractFidelity')
|
|
60
|
+
: null;
|
|
61
|
+
const blockingFindingCount = Array.isArray(findings)
|
|
62
|
+
? findings.filter((finding) => finding?.blockingRecommended === true || ['critical', 'high'].includes(String(finding?.severity || '').toLowerCase())).length
|
|
63
|
+
: 0;
|
|
64
|
+
const contractFidelityWeak = contractFidelityEntry
|
|
65
|
+
? contractFidelityEntry.verdict === 'weak'
|
|
66
|
+
|| contractFidelityEntry.verdict === 'unclear'
|
|
67
|
+
|| (typeof contractFidelityEntry.score === 'number' && contractFidelityEntry.score < 70)
|
|
68
|
+
: false;
|
|
69
|
+
const contractReady = designExecutionSummary?.contractReady === true;
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
blockingFindingCount,
|
|
73
|
+
contractFidelityWeak,
|
|
74
|
+
contractDriftDetected: !contractReady || contractFidelityWeak || blockingFindingCount > 0,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function calibrateGenericityAssessment({
|
|
79
|
+
reviewRubricSummary,
|
|
80
|
+
designExecutionSummary,
|
|
81
|
+
genericityAssessment,
|
|
82
|
+
rubricBreakdown,
|
|
83
|
+
findings,
|
|
84
|
+
notes,
|
|
85
|
+
tasteVsFailureSeparated,
|
|
86
|
+
}) {
|
|
87
|
+
const providerStatus = String(genericityAssessment?.status || 'unclear').trim().toLowerCase() || 'unclear';
|
|
88
|
+
const evidenceTexts = collectEvidenceTexts({
|
|
89
|
+
genericityAssessment,
|
|
90
|
+
rubricBreakdown,
|
|
91
|
+
findings,
|
|
92
|
+
notes,
|
|
93
|
+
});
|
|
94
|
+
const matchedGenericitySignals = collectMatchedSignals(reviewRubricSummary?.genericitySignals, evidenceTexts);
|
|
95
|
+
const matchedValidBoldSignals = collectMatchedSignals(reviewRubricSummary?.validBoldSignals, evidenceTexts);
|
|
96
|
+
const { blockingFindingCount, contractFidelityWeak, contractDriftDetected } = detectContractDrift(
|
|
97
|
+
rubricBreakdown,
|
|
98
|
+
findings,
|
|
99
|
+
designExecutionSummary
|
|
100
|
+
);
|
|
101
|
+
const namedGenericityRequired = reviewRubricSummary?.reportingRules?.mustExplainGenericity === true;
|
|
102
|
+
const calibrationNotes = [];
|
|
103
|
+
let calibratedStatus = providerStatus;
|
|
104
|
+
|
|
105
|
+
if (
|
|
106
|
+
namedGenericityRequired
|
|
107
|
+
&& ['generic', 'mixed'].includes(providerStatus)
|
|
108
|
+
&& matchedGenericitySignals.length === 0
|
|
109
|
+
) {
|
|
110
|
+
calibratedStatus = 'unclear';
|
|
111
|
+
calibrationNotes.push('Genericity claim was not backed by any named drift signal.');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (matchedGenericitySignals.length > 0 && matchedValidBoldSignals.length === 0) {
|
|
115
|
+
calibratedStatus = contractDriftDetected || matchedGenericitySignals.length >= 2
|
|
116
|
+
? 'generic'
|
|
117
|
+
: 'mixed';
|
|
118
|
+
calibrationNotes.push('Named genericity drift signals dominate the review evidence.');
|
|
119
|
+
} else if (matchedValidBoldSignals.length > 0 && matchedGenericitySignals.length === 0) {
|
|
120
|
+
if (contractDriftDetected) {
|
|
121
|
+
calibratedStatus = 'mixed';
|
|
122
|
+
calibrationNotes.push('Authored signals are present, but contract drift prevents a distinctive verdict.');
|
|
123
|
+
} else if (matchedValidBoldSignals.length >= 2) {
|
|
124
|
+
calibratedStatus = 'distinctive';
|
|
125
|
+
calibrationNotes.push('Multiple valid bold signals were named without generic drift evidence.');
|
|
126
|
+
} else if (providerStatus === 'unclear') {
|
|
127
|
+
calibratedStatus = 'mixed';
|
|
128
|
+
calibrationNotes.push('One valid bold signal was named, but evidence is not strong enough for a distinctive verdict.');
|
|
129
|
+
}
|
|
130
|
+
} else if (matchedGenericitySignals.length > 0 && matchedValidBoldSignals.length > 0) {
|
|
131
|
+
calibratedStatus = contractDriftDetected ? 'mixed' : 'mixed';
|
|
132
|
+
calibrationNotes.push('The evidence contains both generic drift and legitimate authored moves.');
|
|
133
|
+
} else if (providerStatus === 'distinctive' && contractDriftDetected) {
|
|
134
|
+
calibratedStatus = 'mixed';
|
|
135
|
+
calibrationNotes.push('Distinctive tone does not override contract drift or blocking findings.');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (tasteVsFailureSeparated === false && calibratedStatus === 'distinctive') {
|
|
139
|
+
calibratedStatus = 'mixed';
|
|
140
|
+
calibrationNotes.push('The review did not separate taste preference from real failure conditions.');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (calibrationNotes.length === 0) {
|
|
144
|
+
calibrationNotes.push('Provider verdict stayed intact after rubric calibration.');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
version: 'ui-rubric-calibration-v1',
|
|
149
|
+
providerStatus,
|
|
150
|
+
calibratedStatus,
|
|
151
|
+
statusChanged: calibratedStatus !== providerStatus,
|
|
152
|
+
namedGenericityRequired,
|
|
153
|
+
matchedGenericitySignals,
|
|
154
|
+
matchedValidBoldSignals,
|
|
155
|
+
blockingFindingCount,
|
|
156
|
+
contractFidelityWeak,
|
|
157
|
+
contractDriftDetected,
|
|
158
|
+
tasteVsFailureSeparated,
|
|
159
|
+
evidenceTextCount: evidenceTexts.length,
|
|
160
|
+
notes: calibrationNotes,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function buildRubricCalibrationReport({
|
|
165
|
+
cases,
|
|
166
|
+
reviewRubricSummary,
|
|
167
|
+
}) {
|
|
168
|
+
const normalizedCases = Array.isArray(cases) ? cases : [];
|
|
169
|
+
const results = normalizedCases.map((caseEntry) => {
|
|
170
|
+
const calibration = calibrateGenericityAssessment({
|
|
171
|
+
reviewRubricSummary,
|
|
172
|
+
designExecutionSummary: caseEntry.designExecutionSummary,
|
|
173
|
+
genericityAssessment: caseEntry.genericityAssessment,
|
|
174
|
+
rubricBreakdown: caseEntry.rubricBreakdown,
|
|
175
|
+
findings: caseEntry.findings,
|
|
176
|
+
notes: caseEntry.notes,
|
|
177
|
+
tasteVsFailureSeparated: caseEntry.tasteVsFailureSeparated,
|
|
178
|
+
});
|
|
179
|
+
const expected = caseEntry.expected && typeof caseEntry.expected === 'object'
|
|
180
|
+
? caseEntry.expected
|
|
181
|
+
: {};
|
|
182
|
+
|
|
183
|
+
const statusMatches = String(expected.calibratedStatus || '') === calibration.calibratedStatus;
|
|
184
|
+
const contractDriftMatches = typeof expected.contractDriftDetected === 'boolean'
|
|
185
|
+
? expected.contractDriftDetected === calibration.contractDriftDetected
|
|
186
|
+
: true;
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
id: String(caseEntry.id || 'unknown-case'),
|
|
190
|
+
label: String(caseEntry.label || ''),
|
|
191
|
+
passed: statusMatches && contractDriftMatches,
|
|
192
|
+
expected,
|
|
193
|
+
calibration,
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const passedCaseCount = results.filter((resultEntry) => resultEntry.passed).length;
|
|
198
|
+
const totalCases = results.length;
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
generatedAt: new Date().toISOString(),
|
|
202
|
+
reportName: 'ui-rubric-calibration',
|
|
203
|
+
schemaVersion: '1.0',
|
|
204
|
+
passed: passedCaseCount === totalCases,
|
|
205
|
+
failureCount: totalCases - passedCaseCount,
|
|
206
|
+
totalCases,
|
|
207
|
+
passedCaseCount,
|
|
208
|
+
accuracyPercent: totalCases === 0 ? 0 : Number(((passedCaseCount / totalCases) * 100).toFixed(1)),
|
|
209
|
+
results,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "ui-rubric-goldset-v1",
|
|
3
|
+
"reviewRubric": {
|
|
4
|
+
"version": "ui-rubric-v1",
|
|
5
|
+
"genericitySignals": [
|
|
6
|
+
"safe-centered-hero-without-product-rationale",
|
|
7
|
+
"balanced-card-grid-without-priority-shift",
|
|
8
|
+
"default-framework-button-and-input-treatment",
|
|
9
|
+
"trend-gradient-without-structural-role",
|
|
10
|
+
"interchangeable-dashboard-chrome"
|
|
11
|
+
],
|
|
12
|
+
"validBoldSignals": [
|
|
13
|
+
"one-clear-signature-move",
|
|
14
|
+
"project-specific-layout-tension",
|
|
15
|
+
"purposeful-motion-as-identity",
|
|
16
|
+
"distinct-typographic-hierarchy",
|
|
17
|
+
"non-template-task-priority"
|
|
18
|
+
],
|
|
19
|
+
"reportingRules": {
|
|
20
|
+
"mustExplainGenericity": true,
|
|
21
|
+
"mustSeparateTasteFromFailure": true,
|
|
22
|
+
"contractFidelityOverridesPersonalTaste": true
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"cases": [
|
|
26
|
+
{
|
|
27
|
+
"id": "distinctive-authored-valid",
|
|
28
|
+
"label": "Authored UI with clear valid bold signals and no contract drift",
|
|
29
|
+
"designExecutionSummary": {
|
|
30
|
+
"contractReady": true
|
|
31
|
+
},
|
|
32
|
+
"genericityAssessment": {
|
|
33
|
+
"status": "distinctive",
|
|
34
|
+
"reason": "The UI lands one clear signature move with project specific layout tension and non template task priority."
|
|
35
|
+
},
|
|
36
|
+
"rubricBreakdown": [
|
|
37
|
+
{
|
|
38
|
+
"dimension": "contractFidelity",
|
|
39
|
+
"score": 88,
|
|
40
|
+
"verdict": "strong",
|
|
41
|
+
"reason": "Contract fidelity stays intact while the layout uses non template task priority.",
|
|
42
|
+
"blocking": true
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"findings": [],
|
|
46
|
+
"notes": [
|
|
47
|
+
"The composition keeps one clear signature move instead of collapsing into default chrome."
|
|
48
|
+
],
|
|
49
|
+
"tasteVsFailureSeparated": true,
|
|
50
|
+
"expected": {
|
|
51
|
+
"calibratedStatus": "distinctive",
|
|
52
|
+
"contractDriftDetected": false
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "bold-but-contract-drift",
|
|
57
|
+
"label": "Bold ideas exist but contract drift keeps the result mixed",
|
|
58
|
+
"designExecutionSummary": {
|
|
59
|
+
"contractReady": true
|
|
60
|
+
},
|
|
61
|
+
"genericityAssessment": {
|
|
62
|
+
"status": "distinctive",
|
|
63
|
+
"reason": "There is project specific layout tension and one clear signature move, but the mobile hierarchy drifted."
|
|
64
|
+
},
|
|
65
|
+
"rubricBreakdown": [
|
|
66
|
+
{
|
|
67
|
+
"dimension": "contractFidelity",
|
|
68
|
+
"score": 54,
|
|
69
|
+
"verdict": "weak",
|
|
70
|
+
"reason": "Contract fidelity weakened when the primary CTA moved below secondary content.",
|
|
71
|
+
"blocking": true
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
"findings": [
|
|
75
|
+
{
|
|
76
|
+
"area": "responsive",
|
|
77
|
+
"severity": "high",
|
|
78
|
+
"problem": "Mobile layout no longer follows the contract.",
|
|
79
|
+
"evidence": "The primary action now drops below supporting proof.",
|
|
80
|
+
"recommendation": "Restore the intended task order.",
|
|
81
|
+
"blockingRecommended": true
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"notes": [
|
|
85
|
+
"The distinctive direction is real, but contract fidelity still drifted."
|
|
86
|
+
],
|
|
87
|
+
"tasteVsFailureSeparated": true,
|
|
88
|
+
"expected": {
|
|
89
|
+
"calibratedStatus": "mixed",
|
|
90
|
+
"contractDriftDetected": true
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "generic-template-drift",
|
|
95
|
+
"label": "Generic template drift is named clearly and stays generic",
|
|
96
|
+
"designExecutionSummary": {
|
|
97
|
+
"contractReady": false
|
|
98
|
+
},
|
|
99
|
+
"genericityAssessment": {
|
|
100
|
+
"status": "mixed",
|
|
101
|
+
"reason": "The redesign fell back to balanced card grid without priority shift plus default framework button and input treatment."
|
|
102
|
+
},
|
|
103
|
+
"rubricBreakdown": [
|
|
104
|
+
{
|
|
105
|
+
"dimension": "contractFidelity",
|
|
106
|
+
"score": 49,
|
|
107
|
+
"verdict": "weak",
|
|
108
|
+
"reason": "The hierarchy collapsed into interchangeable dashboard chrome.",
|
|
109
|
+
"blocking": true
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
"findings": [
|
|
113
|
+
{
|
|
114
|
+
"area": "layout",
|
|
115
|
+
"severity": "high",
|
|
116
|
+
"problem": "The page uses balanced card grid without priority shift.",
|
|
117
|
+
"evidence": "Every block carries equal weight and the CTA no longer leads.",
|
|
118
|
+
"recommendation": "Rebuild the page around one dominant task surface.",
|
|
119
|
+
"blockingRecommended": true
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
"notes": [
|
|
123
|
+
"Interchangeable dashboard chrome now dominates the flow."
|
|
124
|
+
],
|
|
125
|
+
"tasteVsFailureSeparated": true,
|
|
126
|
+
"expected": {
|
|
127
|
+
"calibratedStatus": "generic",
|
|
128
|
+
"contractDriftDetected": true
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"id": "provider-overcalls-generic",
|
|
133
|
+
"label": "Provider called the work generic, but the evidence points to valid authored moves",
|
|
134
|
+
"designExecutionSummary": {
|
|
135
|
+
"contractReady": true
|
|
136
|
+
},
|
|
137
|
+
"genericityAssessment": {
|
|
138
|
+
"status": "generic",
|
|
139
|
+
"reason": "The work feels generic, although it carries one clear signature move and distinct typographic hierarchy."
|
|
140
|
+
},
|
|
141
|
+
"rubricBreakdown": [
|
|
142
|
+
{
|
|
143
|
+
"dimension": "contractFidelity",
|
|
144
|
+
"score": 82,
|
|
145
|
+
"verdict": "strong",
|
|
146
|
+
"reason": "Contract fidelity is strong and the task order remains deliberate.",
|
|
147
|
+
"blocking": true
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"findings": [],
|
|
151
|
+
"notes": [
|
|
152
|
+
"The surface uses one clear signature move with distinct typographic hierarchy and non template task priority."
|
|
153
|
+
],
|
|
154
|
+
"tasteVsFailureSeparated": true,
|
|
155
|
+
"expected": {
|
|
156
|
+
"calibratedStatus": "distinctive",
|
|
157
|
+
"contractDriftDetected": false
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"id": "unnamed-genericity-claim",
|
|
162
|
+
"label": "Genericity claim without named drift signal drops to unclear",
|
|
163
|
+
"designExecutionSummary": {
|
|
164
|
+
"contractReady": true
|
|
165
|
+
},
|
|
166
|
+
"genericityAssessment": {
|
|
167
|
+
"status": "generic",
|
|
168
|
+
"reason": "The layout still feels a little too safe and familiar."
|
|
169
|
+
},
|
|
170
|
+
"rubricBreakdown": [
|
|
171
|
+
{
|
|
172
|
+
"dimension": "contractFidelity",
|
|
173
|
+
"score": 78,
|
|
174
|
+
"verdict": "acceptable",
|
|
175
|
+
"reason": "The contract is mostly intact.",
|
|
176
|
+
"blocking": true
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"findings": [],
|
|
180
|
+
"notes": [],
|
|
181
|
+
"tasteVsFailureSeparated": true,
|
|
182
|
+
"expected": {
|
|
183
|
+
"calibratedStatus": "unclear",
|
|
184
|
+
"contractDriftDetected": false
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
]
|
|
188
|
+
}
|