@ryuenn3123/agentic-senior-core 3.0.6 → 3.0.7

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.
@@ -1,22 +1,48 @@
1
1
 
2
- # Bootstrap DESIGN.md (UI/UX Art Direction)
2
+ # Bootstrap Dynamic Design Contract
3
3
 
4
- When a user requests frontend design or redesign, the agent should automatically synthesize a complete DESIGN.md covering:
5
- - Visual identity (color palette, typography, spacing, iconography)
6
- - Layout and navigation principles
7
- - Component library and atomic design system
8
- - Accessibility and responsive guidelines
9
- - User journey and interaction flows
10
- - Design tokens and handoff notes for developers
4
+ When a user requests frontend design or redesign, the agent should automatically synthesize a dynamic design contract made of:
5
+ - `docs/DESIGN.md` for human-readable design direction and implementation rationale
6
+ - `docs/design-intent.json` for machine-readable intent, anti-generic constraints, and validation hints
7
+
8
+ This contract is a structure and reasoning system, not a fixed visual template. It must adapt to product context, user needs, platform constraints, and current design signals.
11
9
 
12
10
  The agent must:
13
11
  1. Read [AGENTS.md](../../AGENTS.md) for project context and team roles.
14
12
  2. Scan all files in [.agent-context/rules/](../rules/) for UI/UX and accessibility standards.
15
13
  3. Reference [docs/deep-dive.md](../../docs/deep-dive.md) and [docs/faq.md](../../docs/faq.md) for architecture and product background.
16
- 4. If [docs/DESIGN.md](../../docs/DESIGN.md) exists, check for drift and propose improvements instead of rewriting from scratch.
17
- 5. All references to docs or rules must be clickable markdown links.
14
+ 4. If [docs/DESIGN.md](../../docs/DESIGN.md) or `docs/design-intent.json` already exists, check for drift and improve them instead of rewriting blindly.
15
+ 5. Treat any example structure or stylistic inspiration as non-normative. Use it only to judge depth and clarity, never to copy a visual language directly.
16
+ 6. All references to docs or rules must be clickable markdown links.
17
+
18
+ Required `docs/DESIGN.md` sections:
19
+ 1. Design Intent and Product Personality
20
+ 2. Audience and Use-Context Signals
21
+ 3. Visual Direction and Distinctive Moves
22
+ 4. Color System and Semantic Roles
23
+ 5. Typography System and Hierarchy
24
+ 6. Spacing, Layout Rhythm, and Density Strategy
25
+ 7. Interaction, Motion, and Feedback Rules
26
+ 8. Component Language and Shared Patterns
27
+ 9. Accessibility Non-Negotiables
28
+ 10. Responsive Strategy
29
+ 11. Anti-Patterns to Avoid
30
+ 12. Implementation Notes for Future UI Tasks
31
+
32
+ Required `docs/design-intent.json` fields:
33
+ - `mode`
34
+ - `status`
35
+ - `project`
36
+ - `brandAdjectives`
37
+ - `antiAdjectives`
38
+ - `visualDirection`
39
+ - `experiencePrinciples`
40
+ - `forbiddenPatterns`
41
+ - `requiredDesignSections`
42
+ - `implementation`
18
43
 
19
44
  Output:
20
- - Write a full DESIGN.md in markdown, with clear sections and clickable links to referenced docs/rules.
21
- - Use practical, modern, and accessible design language.
45
+ - Create or update both `docs/DESIGN.md` and `docs/design-intent.json`.
46
+ - Keep both files synchronized: the markdown explains the why, the JSON captures the contract in machine-readable form.
47
+ - Use practical, modern, accessible language grounded in the project, not generic SaaS defaults.
22
48
  - Wait for user approval before generating Figma or code assets.
@@ -35,7 +35,7 @@ If the user specifies a framework/blueprint, the agent should:
35
35
  See [docs/roadmap.md](../../docs/roadmap.md) and [docs/deep-dive.md](../../docs/deep-dive.md) for the latest stack and blueprint list.
36
36
 
37
37
  ## UI/UX Bootstrap
38
- When a user requests frontend or UI/UX design, the agent should automatically execute the [bootstrap-design.md](./bootstrap-design.md) prompt to synthesize DESIGN.md.
38
+ When a user requests frontend or UI/UX design, the agent should automatically execute the [bootstrap-design.md](./bootstrap-design.md) prompt to synthesize a dynamic design contract (`docs/DESIGN.md` + `docs/design-intent.json`).
39
39
 
40
40
  ---
41
41
 
@@ -97,6 +97,6 @@ See [docs/roadmap.md](../../docs/roadmap.md) and [docs/deep-dive.md](../../docs/
97
97
 
98
98
  ---
99
99
 
100
- ## Bootstrap UI/UX (DESIGN.md)
100
+ ## Bootstrap UI/UX (Dynamic Design Contract)
101
101
 
102
- To start UI/UX design from scratch, use the [bootstrap-design.md](./bootstrap-design.md) prompt to synthesize `docs/DESIGN.md`.
102
+ To start UI/UX design from scratch, use the [bootstrap-design.md](./bootstrap-design.md) prompt to synthesize `docs/DESIGN.md` and `docs/design-intent.json`.
@@ -15,7 +15,7 @@ Before making changes:
15
15
  3. Read .agent-context/rules/error-handling.md — fix error handling patterns.
16
16
  4. Resolve active language guidance from dynamic stack signals (TypeScript in this repository).
17
17
  5. Enforce backend universal principles: no clever hacks, no premature abstraction, readability over brevity.
18
- 6. Enforce Universal SOP hard gate: stop implementation if `docs/architecture-decision-record.md` is missing, and for UI scope stop if `docs/DESIGN.md` is missing.
18
+ 6. Enforce Universal SOP hard gate: stop implementation if `docs/architecture-decision-record.md` is missing, and for UI scope stop if `docs/DESIGN.md` or `docs/design-intent.json` is missing.
19
19
 
20
20
  For every change you make, provide a Reasoning Chain:
21
21
  - What was wrong (rule reference)
@@ -20,7 +20,7 @@ Use these checklists:
20
20
  7. Enforce cross-session consistency guardian: session handoff must include active architecture contract summary, drift detection must warn before direction changes, and direction changes require explicit user confirmation.
21
21
  8. Enforce explain-on-demand state visibility: default responses must avoid unnecessary state-file internals, state internals are exposed only on explicit request, and diagnostic mode must explain relevant state decisions when needed.
22
22
  9. Enforce single-source and lazy-loading policy: canonical rule source must be explicitly enforced, language-specific guidance must load lazily based on detected scope, and conflicting duplicate rule instructions must not appear during normal flow.
23
- 10. Enforce Universal SOP hard gate: block coding flow when required project docs are missing (`docs/architecture-decision-record.md`, and for UI scope `docs/DESIGN.md`).
23
+ 10. Enforce Universal SOP hard gate: block coding flow when required project docs are missing (`docs/architecture-decision-record.md`, and for UI scope `docs/DESIGN.md` plus `docs/design-intent.json`).
24
24
 
25
25
  For EVERY violation found:
26
26
  - State the exact file and line
@@ -135,4 +135,4 @@ VERDICT: PASS / FAIL (X/Y items passed)
135
135
  - [ ] Backend and frontend mindset checks are both applied when scope spans API and UI boundaries
136
136
  - [ ] Security and testing requirements remain mandatory after static template purge
137
137
  - [ ] Coding flow is blocked if `docs/architecture-decision-record.md` (or `docs/Architecture-Decision-Record.md`) is missing
138
- - [ ] UI implementation flow is blocked if `docs/DESIGN.md` is missing
138
+ - [ ] UI implementation flow is blocked if `docs/DESIGN.md` or `docs/design-intent.json` is missing
@@ -21,7 +21,7 @@ The `.agent-context/rules/` directory is the default guidance source for impleme
21
21
  - Security and testing are non-negotiable baseline requirements.
22
22
  - Hard block before coding:
23
23
  - `docs/architecture-decision-record.md` (alias: `docs/Architecture-Decision-Record.md`) must exist.
24
- - For UI scope, `docs/DESIGN.md` must exist.
24
+ - For UI scope, `docs/DESIGN.md` and `docs/design-intent.json` must exist.
25
25
  - If required project context docs are missing, stop implementation and bootstrap docs before writing application code.
26
26
 
27
27
  ## Rules as Guardian (Cross-Session Consistency)
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-04-19T03:52:25.678Z",
2
+ "generatedAt": "2026-04-20T00:30:26.121Z",
3
3
  "reportName": "memory-continuity-benchmark",
4
4
  "schemaVersion": "1.0.0",
5
5
  "passed": true,
package/.cursorrules CHANGED
@@ -1,6 +1,6 @@
1
1
  # AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
2
2
 
3
- Generated by Agentic-Senior-Core CLI v3.0.6
3
+ Generated by Agentic-Senior-Core CLI v3.0.7
4
4
  Timestamp: 2026-04-18T00:00:00.000Z
5
5
  Selected profile: beginner
6
6
  Selected policy file: .agent-context/policies/llm-judge-threshold.json
@@ -46,4 +46,4 @@ Do not claim done before checklist pass.
46
46
 
47
47
  ## UNIVERSAL SOP HARD GATES
48
48
  - Stop coding if docs/architecture-decision-record.md (or docs/Architecture-Decision-Record.md) is missing.
49
- - For UI scope, stop coding if docs/DESIGN.md is missing.
49
+ - For UI scope, stop coding if docs/DESIGN.md or docs/design-intent.json is missing.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Adapter Mode: thin
4
4
  Adapter Source: .instructions.md
5
- Canonical Snapshot SHA256: 354edbbfaedec284ddcc6d4a5bb5393a7f5385650d1a6ae84a2d9633b5a6d10d
5
+ Canonical Snapshot SHA256: 08c326d79f2c0f4bd7ef106da3542ab0f78f4d02bc205d63598db34cf3f40731
6
6
 
7
7
  Canonical policy source: [.instructions.md](../.instructions.md).
8
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Adapter Mode: thin
4
4
  Adapter Source: .instructions.md
5
- Canonical Snapshot SHA256: 354edbbfaedec284ddcc6d4a5bb5393a7f5385650d1a6ae84a2d9633b5a6d10d
5
+ Canonical Snapshot SHA256: 08c326d79f2c0f4bd7ef106da3542ab0f78f4d02bc205d63598db34cf3f40731
6
6
 
7
7
  The canonical policy source for this repository is [.instructions.md](../.instructions.md).
8
8
 
package/.windsurfrules CHANGED
@@ -1,6 +1,6 @@
1
1
  # AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
2
2
 
3
- Generated by Agentic-Senior-Core CLI v3.0.6
3
+ Generated by Agentic-Senior-Core CLI v3.0.7
4
4
  Timestamp: 2026-04-18T00:00:00.000Z
5
5
  Selected profile: beginner
6
6
  Selected policy file: .agent-context/policies/llm-judge-threshold.json
@@ -46,4 +46,4 @@ Do not claim done before checklist pass.
46
46
 
47
47
  ## UNIVERSAL SOP HARD GATES
48
48
  - Stop coding if docs/architecture-decision-record.md (or docs/Architecture-Decision-Record.md) is missing.
49
- - For UI scope, stop coding if docs/DESIGN.md is missing.
49
+ - For UI scope, stop coding if docs/DESIGN.md or docs/design-intent.json is missing.
package/AGENTS.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Adapter Mode: thin
4
4
  Adapter Source: .instructions.md
5
- Canonical Snapshot SHA256: 354edbbfaedec284ddcc6d4a5bb5393a7f5385650d1a6ae84a2d9633b5a6d10d
5
+ Canonical Snapshot SHA256: 08c326d79f2c0f4bd7ef106da3542ab0f78f4d02bc205d63598db34cf3f40731
6
6
 
7
7
  This file is an adapter entrypoint for agent discovery.
8
8
  The canonical policy source is [.instructions.md](.instructions.md).
package/README.md CHANGED
@@ -10,12 +10,12 @@
10
10
  **Production-grade Rules Engine (Governance Engine) for AI coding agents.**
11
11
  Works with Cursor, Windsurf, GitHub Copilot, Claude Code, Gemini, and other LLM-powered IDE workflows.
12
12
 
13
- Latest release: 3.0.6 (2026-04-19).
13
+ Latest release: 3.0.7 (2026-04-20).
14
14
 
15
- Highlights in 3.0.0:
16
- - Universal IDE adapter surface is completed and synchronized through thin adapters.
17
- - Scoped auto-docs sync rollout is active with explicit phase-1 boundaries and precision/recall evidence.
18
- - Init and scaffolding flow is slimmer, template-free on active path, and validated by deterministic release gates.
15
+ Highlights in 3.0.7:
16
+ - Dynamic UI design contract bootstrap now ships paired markdown and machine-readable intent.
17
+ - Upgrade warns when existing UI repositories are missing required design contract files.
18
+ - Init discovery stays slimmer by reusing architecture context and removing redundant wizard branches.
19
19
 
20
20
  </div>
21
21
 
@@ -399,6 +399,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
399
399
  )?.label || 'Both (frontend + backend)';
400
400
 
401
401
  let architectureRecommendation = null;
402
+ let architectureProjectDescription = '';
402
403
  let architectPreferenceState = await readArchitectPreferenceState();
403
404
  let architectPreferenceUpdated = false;
404
405
 
@@ -457,6 +458,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
457
458
  selectedProjectScopeLabel = architectureSelection.selectedProjectScopeLabel;
458
459
  selectedManualStackFileName = architectureSelection.selectedManualStackFileName;
459
460
  selectedManualBlueprintFileName = architectureSelection.selectedManualBlueprintFileName;
461
+ architectureProjectDescription = architectureSelection.architectureProjectDescription;
460
462
  architectureRecommendation = architectureSelection.architectureRecommendation;
461
463
  architectPreferenceState = architectureSelection.architectPreferenceState;
462
464
  architectPreferenceUpdated = architectureSelection.architectPreferenceUpdated;
@@ -607,6 +609,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
607
609
  } else {
608
610
  discoveryAnswers = await runProjectDiscovery(userInterface, {
609
611
  defaultProjectName: path.basename(resolvedTargetDirectoryPath),
612
+ defaultProjectDescription: architectureProjectDescription,
610
613
  });
611
614
  }
612
615
 
@@ -645,6 +648,9 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
645
648
  for (const generatedPromptFileName of scaffoldingResult.generatedPromptFileNames || []) {
646
649
  console.log(` - .agent-context/prompts/${generatedPromptFileName}`);
647
650
  }
651
+ for (const materializedFileName of scaffoldingResult.materializedFileNames || []) {
652
+ console.log(` - docs/${materializedFileName}`);
653
+ }
648
654
  console.log('Project docs will be authored dynamically by your IDE assistant from these prompts.');
649
655
  } else {
650
656
  console.log(`\nProject documentation generated in docs/:`);
@@ -674,6 +680,10 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
674
680
  selectedProfileName,
675
681
  selectedProfilePack,
676
682
  selectedPreset: initOptions.preset || null,
683
+ projectScope: {
684
+ key: selectedProjectScopeKey,
685
+ label: selectedProjectScopeLabel,
686
+ },
677
687
  selectedStackFileName: selectedResolvedStackFileName,
678
688
  selectedAdditionalStackFileNames,
679
689
  selectedBlueprintFileName: selectedResolvedBlueprintFileName,
@@ -746,6 +756,9 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
746
756
  console.log('- Generated files: .agent-instructions.md, .cursorrules, .windsurfrules, .clauderc, and .agent-context/state/onboarding-report.json');
747
757
  if (scaffoldingResult?.bootstrapMode === 'ai-synthesis') {
748
758
  console.log(`- Bootstrap prompts: ${(scaffoldingResult.generatedPromptFileNames || []).length} files generated in .agent-context/prompts/`);
759
+ if ((scaffoldingResult.materializedFileNames || []).length > 0) {
760
+ console.log(`- Seed docs: ${(scaffoldingResult.materializedFileNames || []).length} files generated in docs/`);
761
+ }
749
762
  console.log(`- Bootstrap docs language: ${scaffoldingResult.docsLanguage}`);
750
763
  console.log(`- Expected project docs after synthesis: ${scaffoldingResult.generatedFileNames.length} files in docs/`);
751
764
  } else if (scaffoldingResult) {
@@ -779,7 +792,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
779
792
  console.log('\nPrompt starter examples (copy and adapt in your IDE):');
780
793
  console.log('- If docs/project-brief.md is missing, execute .agent-context/prompts/bootstrap-project-context.md now and create all required docs files.');
781
794
  if ((scaffoldingResult.generatedPromptFileNames || []).includes('bootstrap-design.md')) {
782
- console.log('- If docs/DESIGN.md is missing, execute .agent-context/prompts/bootstrap-design.md now before building UI components.');
795
+ console.log('- If docs/DESIGN.md or docs/design-intent.json is missing, execute .agent-context/prompts/bootstrap-design.md now before building UI components.');
783
796
  }
784
797
  console.log(`- Build an MVP for ${promptProjectName} using the newly synthesized docs as strict project context.`);
785
798
  console.log('- When scope changes, update docs/* in the same change so future prompts stay aligned.');
@@ -29,6 +29,7 @@ import {
29
29
  detectProjectContext,
30
30
  buildDetectionSummary,
31
31
  formatDetectionCandidates,
32
+ collectProjectMarkers,
32
33
  } from '../detector.mjs';
33
34
  import {
34
35
  buildCompiledRulesContent,
@@ -103,6 +104,102 @@ function buildExistingProjectMajorConstraints() {
103
104
  ];
104
105
  }
105
106
 
107
+ async function readPackageJsonIfExists(targetDirectoryPath) {
108
+ const packageJsonPath = path.join(targetDirectoryPath, 'package.json');
109
+ if (!(await pathExists(packageJsonPath))) {
110
+ return null;
111
+ }
112
+
113
+ try {
114
+ return JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
115
+ } catch {
116
+ return null;
117
+ }
118
+ }
119
+
120
+ async function detectUiScopeSignals({
121
+ targetDirectoryPath,
122
+ existingOnboardingReport,
123
+ selectedStackFileName,
124
+ selectedBlueprintFileName,
125
+ }) {
126
+ const signalReasons = [];
127
+ const markerNames = await collectProjectMarkers(targetDirectoryPath);
128
+ const packageManifest = await readPackageJsonIfExists(targetDirectoryPath);
129
+
130
+ const persistedProjectScopeKey = String(existingOnboardingReport?.projectScope?.key || '').trim().toLowerCase();
131
+ if (persistedProjectScopeKey === 'frontend-only' || persistedProjectScopeKey === 'both') {
132
+ signalReasons.push(`onboarding project scope: ${persistedProjectScopeKey}`);
133
+ }
134
+
135
+ const selectedStackKey = String(selectedStackFileName || '').trim().toLowerCase();
136
+ if (selectedStackKey === 'react-native.md' || selectedStackKey === 'flutter.md') {
137
+ signalReasons.push(`selected stack implies UI runtime: ${selectedStackKey}`);
138
+ }
139
+
140
+ const selectedBlueprintKey = String(selectedBlueprintFileName || '').trim().toLowerCase();
141
+ if (selectedBlueprintKey.includes('frontend') || selectedBlueprintKey.includes('landing') || selectedBlueprintKey.includes('mobile-app')) {
142
+ signalReasons.push(`selected blueprint implies UI scope: ${selectedBlueprintKey}`);
143
+ }
144
+
145
+ const directUiMarkerNames = [
146
+ 'next.config.js',
147
+ 'next.config.mjs',
148
+ 'next.config.ts',
149
+ 'tailwind.config.js',
150
+ 'tailwind.config.mjs',
151
+ 'tailwind.config.ts',
152
+ 'vite.config.js',
153
+ 'vite.config.mjs',
154
+ 'vite.config.ts',
155
+ 'react-native.config.js',
156
+ 'app',
157
+ 'pages',
158
+ 'components',
159
+ 'public',
160
+ 'styles',
161
+ 'android',
162
+ 'ios',
163
+ 'index.html',
164
+ ];
165
+
166
+ const detectedUiMarkers = directUiMarkerNames.filter((markerName) => markerNames.has(markerName));
167
+ if (detectedUiMarkers.length > 0) {
168
+ signalReasons.push(`ui markers: ${detectedUiMarkers.join(', ')}`);
169
+ }
170
+
171
+ const dependencyMap = {
172
+ next: 'next',
173
+ react: 'react',
174
+ reactDom: 'react-dom',
175
+ reactNative: 'react-native',
176
+ expo: 'expo',
177
+ tailwindcss: 'tailwindcss',
178
+ };
179
+ const dependencySource = {
180
+ ...(packageManifest?.dependencies || {}),
181
+ ...(packageManifest?.devDependencies || {}),
182
+ };
183
+ const detectedUiDependencies = Object.values(dependencyMap).filter((dependencyName) => dependencySource[dependencyName]);
184
+ if (detectedUiDependencies.length > 0) {
185
+ signalReasons.push(`ui dependencies: ${detectedUiDependencies.join(', ')}`);
186
+ }
187
+
188
+ const hasStrongUiMarker = detectedUiMarkers.some((markerName) => (
189
+ markerName.startsWith('next.config')
190
+ || markerName === 'react-native.config.js'
191
+ || markerName === 'android'
192
+ || markerName === 'ios'
193
+ ));
194
+ const hasUiDependencies = detectedUiDependencies.length > 0;
195
+ const hasStructuralUiMarkers = detectedUiMarkers.length >= 2;
196
+
197
+ return {
198
+ isUiScopeLikely: signalReasons.length > 0 && (hasStrongUiMarker || hasUiDependencies || hasStructuralUiMarkers || persistedProjectScopeKey.length > 0),
199
+ signalReasons,
200
+ };
201
+ }
202
+
106
203
  export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions = {}) {
107
204
  const resolvedTargetDirectoryPath = path.resolve(targetDirectoryArgument || '.');
108
205
 
@@ -167,6 +264,21 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
167
264
  const includeCiGuardrails = typeof existingOnboardingReport?.ciGuardrailsEnabled === 'boolean'
168
265
  ? existingOnboardingReport.ciGuardrailsEnabled
169
266
  : true;
267
+ const uiScopeSignals = await detectUiScopeSignals({
268
+ targetDirectoryPath: resolvedTargetDirectoryPath,
269
+ existingOnboardingReport,
270
+ selectedStackFileName,
271
+ selectedBlueprintFileName,
272
+ });
273
+ const designContractPaths = ['docs/DESIGN.md', 'docs/design-intent.json'];
274
+ const missingDesignContractPaths = [];
275
+
276
+ for (const designContractPath of designContractPaths) {
277
+ const absoluteDesignContractPath = path.join(resolvedTargetDirectoryPath, ...designContractPath.split('/'));
278
+ if (!(await pathExists(absoluteDesignContractPath))) {
279
+ missingDesignContractPaths.push(designContractPath);
280
+ }
281
+ }
170
282
 
171
283
  const detectionMajorConstraints = buildExistingProjectMajorConstraints();
172
284
  const detectionTransparency = {
@@ -288,6 +400,18 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
288
400
  console.log('Recommendation: regenerate docs with init --scaffold-docs or update the files manually to match the latest template version.');
289
401
  }
290
402
 
403
+ if (uiScopeSignals.isUiScopeLikely && missingDesignContractPaths.length > 0) {
404
+ console.log('\n[WARN] UI/frontend scope was detected, but the dynamic design contract is incomplete:');
405
+ for (const missingDesignContractPath of missingDesignContractPaths) {
406
+ console.log(`- Missing ${missingDesignContractPath}`);
407
+ }
408
+ if (uiScopeSignals.signalReasons.length > 0) {
409
+ console.log(`- Detection signals: ${uiScopeSignals.signalReasons.join('; ')}`);
410
+ }
411
+ console.log('Recommendation: create or refresh docs/DESIGN.md and docs/design-intent.json before allowing UI implementation work.');
412
+ console.log('Upgrade synchronizes governance assets, but it does not author project-specific design docs automatically.');
413
+ }
414
+
291
415
  if (upgradeOptions.dryRun) {
292
416
  console.log('\nDry run enabled. No files were modified.');
293
417
  return;
@@ -320,6 +444,7 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
320
444
  targetDirectoryPath: resolvedTargetDirectoryPath,
321
445
  selectedProfileName,
322
446
  selectedProfilePack: existingOnboardingReport?.selectedProfilePack || null,
447
+ projectScope: existingOnboardingReport?.projectScope || null,
323
448
  selectedStackFileName,
324
449
  selectedAdditionalStackFileNames,
325
450
  selectedBlueprintFileName,
@@ -37,6 +37,7 @@ export async function writeOnboardingReport({
37
37
  selectedProfileName,
38
38
  selectedProfilePack,
39
39
  selectedPreset,
40
+ projectScope = null,
40
41
  selectedStackFileName,
41
42
  selectedAdditionalStackFileNames = [],
42
43
  selectedBlueprintFileName,
@@ -70,6 +71,7 @@ export async function writeOnboardingReport({
70
71
  }
71
72
  : null,
72
73
  selectedPreset,
74
+ projectScope,
73
75
  selectedStack: selectedStackFileName,
74
76
  selectedAdditionalStacks: selectedAdditionalStackFileNames,
75
77
  selectedBlueprint: selectedBlueprintFileName,
@@ -351,6 +353,8 @@ export async function buildCompiledRulesContent({
351
353
  'database-schema.md',
352
354
  'api-contract.md',
353
355
  'flow-overview.md',
356
+ 'DESIGN.md',
357
+ 'design-intent.json',
354
358
  ];
355
359
 
356
360
  for (const candidateFileName of candidateDocFileNames) {
@@ -369,7 +373,7 @@ export async function buildCompiledRulesContent({
369
373
  '',
370
374
  'Universal SOP hard block policy:',
371
375
  '- Stop implementation if docs/architecture-decision-record.md (alias: docs/Architecture-Decision-Record.md) is missing.',
372
- '- For UI scope, stop implementation if docs/DESIGN.md is missing.',
376
+ '- For UI scope, stop implementation if docs/DESIGN.md or docs/design-intent.json is missing.',
373
377
  '- Materialize missing docs first, then continue coding.',
374
378
  '',
375
379
  'These docs were generated during project initialization and reflect the architecture,',
@@ -402,7 +406,7 @@ export async function buildCompiledRulesContent({
402
406
  '- Hard block: do not write application code until docs/project-brief.md and docs/architecture-decision-record.md exist.',
403
407
  '- If docs/project-brief.md is missing, execute bootstrap-project-context prompt immediately.',
404
408
  hasBootstrapDesignPrompt
405
- ? '- For UI scope: if docs/DESIGN.md is missing, execute bootstrap-design prompt before implementing UI surfaces.'
409
+ ? '- For UI scope: if docs/DESIGN.md or docs/design-intent.json is missing, execute bootstrap-design prompt before implementing UI surfaces.'
406
410
  : '- For UI scope: add a design bootstrap prompt before implementing UI surfaces.',
407
411
  '- Save generated docs under docs/ and keep them updated when feature scope changes.',
408
412
  'Latest user prompt defines current feature scope and product direction.',
@@ -62,6 +62,7 @@ export async function resolveArchitectureSelection({
62
62
  selectedProjectScopeLabel,
63
63
  selectedManualStackFileName,
64
64
  selectedManualBlueprintFileName,
65
+ architectureProjectDescription: '',
65
66
  architectureRecommendation,
66
67
  architectPreferenceState: nextArchitectPreferenceState,
67
68
  architectPreferenceUpdated,
@@ -224,6 +225,7 @@ export async function resolveArchitectureSelection({
224
225
  selectedProjectScopeLabel,
225
226
  selectedManualStackFileName,
226
227
  selectedManualBlueprintFileName,
228
+ architectureProjectDescription,
227
229
  architectureRecommendation,
228
230
  architectPreferenceState: nextArchitectPreferenceState,
229
231
  architectPreferenceUpdated,
@@ -6,7 +6,7 @@
6
6
  import fs from 'node:fs/promises';
7
7
  import path from 'node:path';
8
8
 
9
- import { ensureDirectory, askChoice, askYesNo, toTitleCase, pathExists } from './utils.mjs';
9
+ import { ensureDirectory, askChoice, toTitleCase, pathExists } from './utils.mjs';
10
10
 
11
11
  const SUPPORTED_DOC_LANGUAGES = new Set(['en', 'id']);
12
12
  const PROJECT_DOC_FILE_NAMES = [
@@ -16,6 +16,7 @@ const PROJECT_DOC_FILE_NAMES = [
16
16
  'api-contract.md',
17
17
  'flow-overview.md',
18
18
  ];
19
+ const UI_DESIGN_CONTRACT_FILE_NAMES = ['DESIGN.md', 'design-intent.json'];
19
20
 
20
21
  // Legacy project docs may still carry this version header; keep for upgrade staleness checks.
21
22
  export const PROJECT_DOC_TEMPLATE_VERSION = '1.2.0';
@@ -54,57 +55,6 @@ const DOCKER_STRATEGY_CHOICES = [
54
55
  'Docker for both development and production',
55
56
  ];
56
57
 
57
- const DISCOVERY_MODE_CHOICES = [
58
- 'Quick mode (mostly choices, fastest)',
59
- 'Detailed mode (type your own answers)',
60
- ];
61
-
62
- const DESCRIPTION_TEMPLATE_CHOICES = [
63
- 'Marketplace / commerce platform',
64
- 'Internal operations dashboard',
65
- 'SaaS workflow product',
66
- 'Developer platform / API product',
67
- 'Content and community platform',
68
- 'Other',
69
- ];
70
-
71
- const FEATURE_PRESET_CHOICES = [
72
- 'MVP foundation (auth, core CRUD, role-based access)',
73
- 'Commerce flow (catalog, cart, checkout)',
74
- 'Operations flow (dashboard, approvals, reporting)',
75
- 'API platform flow (keys, rate-limit, webhooks)',
76
- 'Content flow (publish, moderation, search)',
77
- 'Other',
78
- ];
79
-
80
- const FEATURE_PRESET_MAP = {
81
- 'MVP foundation (auth, core CRUD, role-based access)': [
82
- 'Authentication and user profiles',
83
- 'Core CRUD workflow for primary resources',
84
- 'Role-based access control',
85
- ],
86
- 'Commerce flow (catalog, cart, checkout)': [
87
- 'Product catalog and filtering',
88
- 'Shopping cart and wishlist',
89
- 'Checkout and payment processing flow',
90
- ],
91
- 'Operations flow (dashboard, approvals, reporting)': [
92
- 'Operational dashboard with KPIs',
93
- 'Approval workflow and audit trail',
94
- 'Reporting and export functionality',
95
- ],
96
- 'API platform flow (keys, rate-limit, webhooks)': [
97
- 'API key management',
98
- 'Rate limiting and usage quotas',
99
- 'Webhook delivery and retry handling',
100
- ],
101
- 'Content flow (publish, moderation, search)': [
102
- 'Draft and publish workflow',
103
- 'Moderation queue and policy checks',
104
- 'Search and discovery experience',
105
- ],
106
- };
107
-
108
58
  function parseBooleanLikeValue(rawValue) {
109
59
  const normalizedValue = String(rawValue || '').trim().toLowerCase();
110
60
  if (['true', 'yes', 'y', '1'].includes(normalizedValue)) {
@@ -161,44 +111,6 @@ function resolveDockerStrategy({ dockerStrategy, useDocker, useDockerDevelopment
161
111
  return DOCKER_STRATEGY_CHOICES[0];
162
112
  }
163
113
 
164
- function parseDockerStrategy(dockerStrategy) {
165
- const normalizedDockerStrategy = String(dockerStrategy || '').toLowerCase();
166
-
167
- if (normalizedDockerStrategy.includes('both development and production')) {
168
- return {
169
- dockerStrategy: DOCKER_STRATEGY_CHOICES[3],
170
- hasDocker: true,
171
- useDockerDevelopment: true,
172
- useDockerProduction: true,
173
- };
174
- }
175
-
176
- if (normalizedDockerStrategy.includes('development only')) {
177
- return {
178
- dockerStrategy: DOCKER_STRATEGY_CHOICES[1],
179
- hasDocker: true,
180
- useDockerDevelopment: true,
181
- useDockerProduction: false,
182
- };
183
- }
184
-
185
- if (normalizedDockerStrategy.includes('production only')) {
186
- return {
187
- dockerStrategy: DOCKER_STRATEGY_CHOICES[2],
188
- hasDocker: true,
189
- useDockerDevelopment: false,
190
- useDockerProduction: true,
191
- };
192
- }
193
-
194
- return {
195
- dockerStrategy: DOCKER_STRATEGY_CHOICES[0],
196
- hasDocker: false,
197
- useDockerDevelopment: false,
198
- useDockerProduction: false,
199
- };
200
- }
201
-
202
114
  async function askFeatureList(userInterface) {
203
115
  console.log('\nList your key features (one per line, press Enter to finish):');
204
116
 
@@ -231,67 +143,32 @@ export async function runProjectDiscovery(userInterface, options = {}) {
231
143
  console.log('You can answer in your own language.');
232
144
  console.log('CLI prompts stay in English, but non-English answers are fully supported.\n');
233
145
 
234
- const selectedDiscoveryMode = await askChoice(
235
- 'How do you want to answer project questions?',
236
- DISCOVERY_MODE_CHOICES,
237
- userInterface
238
- );
239
-
240
- const isQuickMode = selectedDiscoveryMode === DISCOVERY_MODE_CHOICES[0];
241
-
242
146
  const defaultProjectName = (options.defaultProjectName || '').trim();
147
+ const defaultProjectDescription = String(options.defaultProjectDescription || '').trim();
243
148
  let projectName = '';
244
149
 
245
- if (isQuickMode && defaultProjectName) {
246
- const projectNameSource = await askChoice(
247
- 'Project name source:',
248
- [
249
- `Use folder name (${defaultProjectName})`,
250
- 'Type custom project name',
251
- ],
252
- userInterface
253
- );
150
+ const projectNamePrompt = defaultProjectName
151
+ ? `Project name (press Enter to use folder name: ${defaultProjectName}): `
152
+ : 'Project name: ';
254
153
 
255
- if (projectNameSource.startsWith('Use folder name')) {
256
- projectName = defaultProjectName;
257
- }
258
- }
259
-
260
- if (!projectName) {
261
- const projectNamePrompt = defaultProjectName
262
- ? `Project name (press Enter to use folder name: ${defaultProjectName}): `
263
- : 'Project name: ';
154
+ projectName = (await userInterface.question(projectNamePrompt)).trim();
264
155
 
265
- projectName = (await userInterface.question(projectNamePrompt)).trim();
266
-
267
- if (!projectName && defaultProjectName) {
268
- projectName = defaultProjectName;
269
- }
156
+ if (!projectName && defaultProjectName) {
157
+ projectName = defaultProjectName;
270
158
  }
271
159
 
272
160
  if (!projectName) {
273
161
  throw new Error('Project name is required for documentation scaffolding.');
274
162
  }
275
163
 
276
- let projectDescription = '';
277
- if (isQuickMode) {
278
- const selectedDescriptionTemplate = await askChoice(
279
- 'Project description template:',
280
- DESCRIPTION_TEMPLATE_CHOICES,
281
- userInterface
282
- );
164
+ const projectDescriptionPrompt = defaultProjectDescription
165
+ ? `One-line description (press Enter to use: ${defaultProjectDescription}): `
166
+ : 'One-line description: ';
283
167
 
284
- if (selectedDescriptionTemplate === 'Other') {
285
- projectDescription = (await userInterface.question('One-line description: ')).trim();
286
- } else {
287
- projectDescription = `${selectedDescriptionTemplate} for ${projectName}.`;
288
- }
289
- } else {
290
- projectDescription = (await userInterface.question('One-line description: ')).trim();
291
- }
168
+ let projectDescription = (await userInterface.question(projectDescriptionPrompt)).trim();
292
169
 
293
170
  if (!projectDescription) {
294
- projectDescription = `A ${projectName} project.`;
171
+ projectDescription = defaultProjectDescription || `A ${projectName} project.`;
295
172
  }
296
173
 
297
174
  const domainSelection = await askChoice(
@@ -327,79 +204,20 @@ export async function runProjectDiscovery(userInterface, options = {}) {
327
204
  authStrategy = (await userInterface.question('Describe your auth setup: ')).trim() || 'Custom auth';
328
205
  }
329
206
 
330
- let dockerStrategy = DOCKER_STRATEGY_CHOICES[0];
331
- if (isQuickMode) {
332
- dockerStrategy = await askChoice(
333
- 'Containerization strategy:',
334
- DOCKER_STRATEGY_CHOICES,
335
- userInterface
336
- );
337
- } else {
338
- const useDocker = await askYesNo(
339
- 'Use Docker for this project (development and/or production)?',
340
- userInterface,
341
- false
342
- );
343
-
344
- if (useDocker) {
345
- const useDockerDevelopment = await askYesNo(
346
- 'Use Docker for development workflow?',
347
- userInterface,
348
- true
349
- );
350
- const useDockerProduction = await askYesNo(
351
- 'Use Docker for production runtime/build?',
352
- userInterface,
353
- true
354
- );
355
-
356
- dockerStrategy = resolveDockerStrategy({
357
- useDocker,
358
- useDockerDevelopment,
359
- useDockerProduction,
360
- });
361
- }
362
- }
363
-
364
- const parsedDockerStrategy = parseDockerStrategy(dockerStrategy);
365
-
366
- let features = [];
367
- if (isQuickMode) {
368
- const selectedFeaturePreset = await askChoice(
369
- 'Feature set:',
370
- FEATURE_PRESET_CHOICES,
371
- userInterface
372
- );
207
+ const dockerStrategy = await askChoice(
208
+ 'Containerization strategy:',
209
+ DOCKER_STRATEGY_CHOICES,
210
+ userInterface
211
+ );
373
212
 
374
- if (selectedFeaturePreset === 'Other') {
375
- features = await askFeatureList(userInterface);
376
- } else {
377
- features = FEATURE_PRESET_MAP[selectedFeaturePreset] || [];
378
- }
379
- } else {
380
- features = await askFeatureList(userInterface);
381
- }
213
+ let features = await askFeatureList(userInterface);
382
214
 
383
215
  if (features.length === 0) {
384
216
  features.push('Core functionality (define during development)');
385
217
  }
386
218
 
387
- let additionalContext = 'No additional context provided.';
388
- if (isQuickMode) {
389
- const wantsAdditionalContext = await askYesNo(
390
- 'Add additional context now?',
391
- userInterface,
392
- false
393
- );
394
-
395
- if (wantsAdditionalContext) {
396
- additionalContext = (await userInterface.question('Additional context: ')).trim()
397
- || 'No additional context provided.';
398
- }
399
- } else {
400
- additionalContext = (await userInterface.question('\nAdditional context (optional, press Enter to skip): ')).trim()
401
- || 'No additional context provided.';
402
- }
219
+ const additionalContext = (await userInterface.question('\nAdditional context (optional, press Enter to skip): ')).trim()
220
+ || 'No additional context provided.';
403
221
 
404
222
  return {
405
223
  projectName,
@@ -407,9 +225,7 @@ export async function runProjectDiscovery(userInterface, options = {}) {
407
225
  primaryDomain,
408
226
  databaseChoice,
409
227
  authStrategy,
410
- dockerStrategy: parsedDockerStrategy.dockerStrategy,
411
- useDockerDevelopment: parsedDockerStrategy.useDockerDevelopment,
412
- useDockerProduction: parsedDockerStrategy.useDockerProduction,
228
+ dockerStrategy,
413
229
  features,
414
230
  additionalContext,
415
231
  };
@@ -490,6 +306,134 @@ function shouldBootstrapDesignDocument(discoveryAnswers, initContext) {
490
306
  return false;
491
307
  }
492
308
 
309
+ function inferDesignKeywords(discoveryAnswers) {
310
+ const normalizedDescription = String(discoveryAnswers.projectDescription || '').toLowerCase();
311
+ const normalizedDomain = String(discoveryAnswers.primaryDomain || '').toLowerCase();
312
+ const normalizedFeatures = Array.isArray(discoveryAnswers.features)
313
+ ? discoveryAnswers.features.map((featureValue) => String(featureValue).toLowerCase()).join(' ')
314
+ : '';
315
+ const aggregateText = `${normalizedDescription} ${normalizedDomain} ${normalizedFeatures}`;
316
+
317
+ if (aggregateText.includes('commerce') || aggregateText.includes('catalog') || aggregateText.includes('checkout')) {
318
+ return {
319
+ brandAdjectives: ['clear', 'desirable', 'confident'],
320
+ antiAdjectives: ['cluttered', 'hesitant', 'coupon-noisy'],
321
+ distinctiveMoves: [
322
+ 'Use product hierarchy and buying cues without turning the interface into a discount template.',
323
+ 'Keep decision-critical information prominent while secondary merchandising stays quiet.',
324
+ 'Let imagery and spacing create premium perception before decorative effects do.',
325
+ ],
326
+ };
327
+ }
328
+
329
+ if (aggregateText.includes('dashboard') || aggregateText.includes('operations') || aggregateText.includes('report')) {
330
+ return {
331
+ brandAdjectives: ['calm', 'precise', 'trustworthy'],
332
+ antiAdjectives: ['chaotic', 'gimmicky', 'visually exhausting'],
333
+ distinctiveMoves: [
334
+ 'Prioritize scanning clarity and status recognition over decorative density.',
335
+ 'Use visual weight to separate signal from operational noise.',
336
+ 'Reserve strong accents for alerts, decisions, and state transitions only.',
337
+ ],
338
+ };
339
+ }
340
+
341
+ if (aggregateText.includes('developer') || aggregateText.includes('api') || aggregateText.includes('platform')) {
342
+ return {
343
+ brandAdjectives: ['precise', 'technical', 'transparent'],
344
+ antiAdjectives: ['vague', 'marketing-heavy', 'template-polished'],
345
+ distinctiveMoves: [
346
+ 'Make structure and feedback feel exact without becoming sterile.',
347
+ 'Use code-adjacent rhythm and hierarchy to build trust with technical users.',
348
+ 'Keep complexity legible through spacing, grouping, and explicit interaction states.',
349
+ ],
350
+ };
351
+ }
352
+
353
+ if (aggregateText.includes('content') || aggregateText.includes('community') || aggregateText.includes('publish')) {
354
+ return {
355
+ brandAdjectives: ['editorial', 'warm', 'expressive'],
356
+ antiAdjectives: ['flat', 'anonymous', 'feed-generic'],
357
+ distinctiveMoves: [
358
+ 'Build a strong reading rhythm so content feels curated rather than dumped into cards.',
359
+ 'Use contrast and spacing to guide attention between creation, moderation, and discovery.',
360
+ 'Give key interaction moments personality without sacrificing clarity.',
361
+ ],
362
+ };
363
+ }
364
+
365
+ return {
366
+ brandAdjectives: ['clear', 'human', 'distinct'],
367
+ antiAdjectives: ['generic', 'template-like', 'trend-chasing'],
368
+ distinctiveMoves: [
369
+ 'Create a visual direction with one memorable tension instead of stacking fashionable effects.',
370
+ 'Use rhythm, hierarchy, and motion intentionally so the interface feels authored.',
371
+ 'Keep the system flexible enough to evolve with product scope without losing identity.',
372
+ ],
373
+ };
374
+ }
375
+
376
+ function buildDesignIntentSeed({
377
+ discoveryAnswers,
378
+ initContext,
379
+ architectureRecommendation,
380
+ }) {
381
+ const inferredKeywords = inferDesignKeywords(discoveryAnswers);
382
+ const designSignals = architectureRecommendation?.designGuidance?.normalizedSignals || null;
383
+
384
+ return `${JSON.stringify({
385
+ mode: 'dynamic',
386
+ status: 'seed-needs-design-synthesis',
387
+ project: {
388
+ name: discoveryAnswers.projectName,
389
+ context: discoveryAnswers.projectDescription,
390
+ domain: discoveryAnswers.primaryDomain,
391
+ stack: toTitleCase(initContext.stackFileName),
392
+ blueprint: toTitleCase(initContext.blueprintFileName),
393
+ },
394
+ brandAdjectives: inferredKeywords.brandAdjectives,
395
+ antiAdjectives: inferredKeywords.antiAdjectives,
396
+ visualDirection: {
397
+ trendStance: 'trend-aware-not-trend-chasing',
398
+ distinctiveMoves: inferredKeywords.distinctiveMoves,
399
+ copiedReferenceAllowed: false,
400
+ },
401
+ experiencePrinciples: [
402
+ 'Design must feel project-specific, not interchangeable with generic SaaS templates.',
403
+ 'Major interface decisions must be explainable in product and user terms.',
404
+ 'Accessibility, responsiveness, and implementation realism are non-negotiable.',
405
+ ],
406
+ forbiddenPatterns: [
407
+ 'generic-saas-hero',
408
+ 'copycat-brand-system',
409
+ 'unjustified-default-gradients',
410
+ 'placeholder-design-language',
411
+ ],
412
+ requiredDesignSections: [
413
+ 'Design Intent and Product Personality',
414
+ 'Audience and Use-Context Signals',
415
+ 'Visual Direction and Distinctive Moves',
416
+ 'Color System and Semantic Roles',
417
+ 'Typography System and Hierarchy',
418
+ 'Spacing, Layout Rhythm, and Density Strategy',
419
+ 'Interaction, Motion, and Feedback Rules',
420
+ 'Component Language and Shared Patterns',
421
+ 'Accessibility Non-Negotiables',
422
+ 'Responsive Strategy',
423
+ 'Anti-Patterns to Avoid',
424
+ 'Implementation Notes for Future UI Tasks',
425
+ ],
426
+ implementation: {
427
+ requiredDeliverables: ['docs/DESIGN.md', 'docs/design-intent.json'],
428
+ requireDesignRationale: true,
429
+ requireDistinctVisualDirection: true,
430
+ requireMachineReadableContract: true,
431
+ bootstrapPrompt: '.agent-context/prompts/bootstrap-design.md',
432
+ },
433
+ architectSignals: designSignals,
434
+ }, null, 2)}\n`;
435
+ }
436
+
493
437
  function buildProjectContextBootstrapPrompt({
494
438
  discoveryAnswers,
495
439
  initContext,
@@ -576,35 +520,61 @@ function buildDesignBootstrapPrompt({
576
520
  }) {
577
521
  const designSignals = architectureRecommendation?.designGuidance?.normalizedSignals || null;
578
522
  const designSignalsJson = JSON.stringify(designSignals, null, 2);
523
+ const designIntentSeed = buildDesignIntentSeed({
524
+ discoveryAnswers,
525
+ initContext,
526
+ architectureRecommendation,
527
+ });
579
528
 
580
529
  return [
581
- '# Bootstrap Prompt: Dynamic DESIGN.md Synthesis',
530
+ '# Bootstrap Prompt: Dynamic Design Contract Synthesis',
582
531
  '',
583
532
  `Protocol version: ${PROJECT_DOC_SYNTHESIS_PROMPT_VERSION}`,
584
533
  '',
585
534
  'You are the Lead UI/UX Art Director for this project.',
586
- 'Write docs/DESIGN.md from zero. Do not use generic template prose.',
535
+ 'Create a dynamic design contract, not a fixed stylistic template.',
587
536
  '',
588
537
  '## Mission',
589
538
  `Author docs/DESIGN.md in ${docsLanguage.toUpperCase()} language with strong art direction and engineering-ready guidance.`,
539
+ 'Keep docs/design-intent.json synchronized as the machine-readable source of design intent.',
540
+ '',
541
+ '## Deliverables',
542
+ '1. docs/DESIGN.md',
543
+ '2. docs/design-intent.json',
590
544
  '',
591
- '## Required Sections',
545
+ '## Required DESIGN.md Sections',
592
546
  '1. Design Vision and Product Personality',
593
- '2. Theme and Atmosphere Direction',
594
- '3. Color System (tokens, semantic roles, accessibility rationale)',
595
- '4. Typography System (pairing, scale, usage rules)',
596
- '5. Spacing and Layout Rhythm',
597
- '6. Motion and Interaction Principles',
598
- '7. Component Language (cards, forms, nav, states)',
599
- '8. Do and Don\'t Rules',
547
+ '2. Audience and Use-Context Signals',
548
+ '3. Visual Direction and Distinctive Moves',
549
+ '4. Color System (tokens, semantic roles, accessibility rationale)',
550
+ '5. Typography System (pairing, scale, usage rules)',
551
+ '6. Spacing, Layout Rhythm, and Density Strategy',
552
+ '7. Motion and Interaction Principles',
553
+ '8. Component Language (cards, forms, nav, states)',
600
554
  '9. Accessibility Non-Negotiables',
601
- '10. Redesign Protocol (how to evolve without breaking design identity)',
555
+ '10. Responsive Strategy',
556
+ '11. Anti-Patterns to Avoid',
557
+ '12. Implementation Notes for Future UI Tasks',
558
+ '',
559
+ '## Required design-intent.json Fields',
560
+ '1. mode',
561
+ '2. status',
562
+ '3. project',
563
+ '4. brandAdjectives',
564
+ '5. antiAdjectives',
565
+ '6. visualDirection',
566
+ '7. experiencePrinciples',
567
+ '8. forbiddenPatterns',
568
+ '9. requiredDesignSections',
569
+ '10. implementation',
602
570
  '',
603
571
  '## Hard Rules',
604
572
  '1. No copy-paste from external style guides.',
605
573
  '2. Every major decision must include psychological/product rationale.',
606
574
  '3. Keep implementation feasible for the selected stack and blueprint.',
607
575
  '4. Keep tone decisive like an art director, not generic AI boilerplate.',
576
+ '5. Do not anchor the final design language to a famous brand reference. Translate inspiration into original project-specific principles.',
577
+ '6. Reject interchangeable hero layouts, generic SaaS gradients, and trend-chasing decoration unless the project context explicitly justifies them.',
608
578
  '',
609
579
  '## Project Inputs',
610
580
  `- Project name: ${discoveryAnswers.projectName}`,
@@ -619,10 +589,17 @@ function buildDesignBootstrapPrompt({
619
589
  designSignalsJson || 'null',
620
590
  '```',
621
591
  '',
592
+ '## Seed Machine Contract',
593
+ 'Refine this seed instead of discarding it. Keep the final JSON aligned with the markdown design system.',
594
+ '```json',
595
+ designIntentSeed.trim(),
596
+ '```',
597
+ '',
622
598
  '## Required Execution',
623
- '1. Create docs/DESIGN.md with complete content.',
624
- '2. Ensure rules are practical for implementation and review.',
625
- '3. After DESIGN.md exists, use it as first-class source for future UI tasks.',
599
+ '1. Create or update docs/DESIGN.md with complete content.',
600
+ '2. Create or update docs/design-intent.json with machine-readable design intent.',
601
+ '3. Ensure both files stay project-specific, dynamic, and practical for implementation and review.',
602
+ '4. After the contract exists, use it as a first-class source for future UI tasks.',
626
603
  '',
627
604
  ].join('\n');
628
605
  }
@@ -643,12 +620,14 @@ export async function generateProjectDocumentation(
643
620
 
644
621
  const docsDirectoryPath = path.join(targetDirectoryPath, 'docs');
645
622
  const promptsDirectoryPath = path.join(targetDirectoryPath, '.agent-context', 'prompts');
623
+ await ensureDirectory(docsDirectoryPath);
646
624
  await ensureDirectory(promptsDirectoryPath);
647
625
 
648
626
  const synthesisContext = buildSynthesisContext(discoveryAnswers, initContext);
649
627
  const { requiredDocFileNames } = resolveProjectDocTargets(discoveryAnswers);
650
628
  const expectedDocFileNames = [...requiredDocFileNames];
651
629
  const generatedPromptFileNames = [];
630
+ const materializedFileNames = [];
652
631
 
653
632
  const projectContextPromptFileName = 'bootstrap-project-context.md';
654
633
  const architectureRecommendation = initContext.architectureRecommendation || null;
@@ -677,8 +656,19 @@ export async function generateProjectDocumentation(
677
656
  await fs.writeFile(path.join(promptsDirectoryPath, designPromptFileName), designPromptContent, 'utf8');
678
657
  generatedPromptFileNames.push(designPromptFileName);
679
658
 
680
- if (!expectedDocFileNames.includes('DESIGN.md')) {
681
- expectedDocFileNames.push('DESIGN.md');
659
+ const designIntentSeedFileName = 'design-intent.json';
660
+ const designIntentSeedContent = buildDesignIntentSeed({
661
+ discoveryAnswers,
662
+ initContext: synthesisContext,
663
+ architectureRecommendation,
664
+ });
665
+ await fs.writeFile(path.join(docsDirectoryPath, designIntentSeedFileName), designIntentSeedContent, 'utf8');
666
+ materializedFileNames.push(designIntentSeedFileName);
667
+
668
+ for (const designContractFileName of UI_DESIGN_CONTRACT_FILE_NAMES) {
669
+ if (!expectedDocFileNames.includes(designContractFileName)) {
670
+ expectedDocFileNames.push(designContractFileName);
671
+ }
682
672
  }
683
673
  }
684
674
 
@@ -686,6 +676,7 @@ export async function generateProjectDocumentation(
686
676
  docsDirectoryPath,
687
677
  generatedFileNames: expectedDocFileNames,
688
678
  generatedPromptFileNames,
679
+ materializedFileNames,
689
680
  bootstrapMode: 'ai-synthesis',
690
681
  synthesisPromptVersion: PROJECT_DOC_SYNTHESIS_PROMPT_VERSION,
691
682
  templateVersion: PROJECT_DOC_TEMPLATE_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuenn3123/agentic-senior-core",
3
- "version": "3.0.6",
3
+ "version": "3.0.7",
4
4
  "type": "module",
5
5
  "description": "Force your AI Agent to code like a Staff Engineer, not a Junior.",
6
6
  "bin": {
@@ -211,19 +211,19 @@ const REQUIRED_UNIVERSAL_SOP_SNIPPETS = [
211
211
  snippets: [
212
212
  '### 15. Universal SOP Consolidation',
213
213
  'Coding flow is blocked if `docs/architecture-decision-record.md` (or `docs/Architecture-Decision-Record.md`) is missing',
214
- 'UI implementation flow is blocked if `docs/DESIGN.md` is missing',
214
+ 'UI implementation flow is blocked if `docs/DESIGN.md` or `docs/design-intent.json` is missing',
215
215
  ],
216
216
  },
217
217
  {
218
218
  path: '.agent-context/prompts/review-code.md',
219
219
  snippets: [
220
- 'Enforce Universal SOP hard gate: block coding flow when required project docs are missing (`docs/architecture-decision-record.md`, and for UI scope `docs/DESIGN.md`).',
220
+ 'Enforce Universal SOP hard gate: block coding flow when required project docs are missing (`docs/architecture-decision-record.md`, and for UI scope `docs/DESIGN.md` plus `docs/design-intent.json`).',
221
221
  ],
222
222
  },
223
223
  {
224
224
  path: '.agent-context/prompts/refactor.md',
225
225
  snippets: [
226
- '6. Enforce Universal SOP hard gate: stop implementation if `docs/architecture-decision-record.md` is missing, and for UI scope stop if `docs/DESIGN.md` is missing.',
226
+ '6. Enforce Universal SOP hard gate: stop implementation if `docs/architecture-decision-record.md` is missing, and for UI scope stop if `docs/DESIGN.md` or `docs/design-intent.json` is missing.',
227
227
  ],
228
228
  },
229
229
  {
@@ -231,7 +231,7 @@ const REQUIRED_UNIVERSAL_SOP_SNIPPETS = [
231
231
  snippets: [
232
232
  'Universal SOP hard block policy:',
233
233
  'Hard block: do not write application code until docs/project-brief.md and docs/architecture-decision-record.md exist.',
234
- 'For UI scope: if docs/DESIGN.md is missing, execute bootstrap-design prompt before implementing UI surfaces.',
234
+ 'For UI scope: if docs/DESIGN.md or docs/design-intent.json is missing, execute bootstrap-design prompt before implementing UI surfaces.',
235
235
  ],
236
236
  },
237
237
  ];
@@ -250,10 +250,22 @@ const REQUIRED_TEMPLATE_FREE_BOOTSTRAP_SNIPPETS = [
250
250
  snippets: [
251
251
  'Project docs will be authored dynamically by your IDE assistant from these prompts.',
252
252
  'bootstrap-project-context.md',
253
+ 'Seed docs:',
253
254
  'I prepared dynamic synthesis bootstrap prompts',
254
255
  ],
255
256
  },
256
257
  ];
258
+ const REQUIRED_UPGRADE_UI_CONTRACT_WARNING_SNIPPETS = [
259
+ {
260
+ path: 'lib/cli/commands/upgrade.mjs',
261
+ snippets: [
262
+ 'UI/frontend scope was detected, but the dynamic design contract is incomplete:',
263
+ 'docs/design-intent.json',
264
+ 'Upgrade synchronizes governance assets, but it does not author project-specific design docs automatically.',
265
+ 'collectProjectMarkers',
266
+ ],
267
+ },
268
+ ];
257
269
  const FORBIDDEN_TEMPLATE_BOOTSTRAP_SNIPPETS = [
258
270
  {
259
271
  path: 'lib/cli/project-scaffolder.mjs',
@@ -1021,6 +1033,28 @@ async function validateTemplateFreeBootstrapCoverage() {
1021
1033
  }
1022
1034
  }
1023
1035
 
1036
+ async function validateUpgradeUiContractWarningCoverage() {
1037
+ console.log('\nChecking upgrade UI contract warning coverage...');
1038
+
1039
+ for (const coverageRule of REQUIRED_UPGRADE_UI_CONTRACT_WARNING_SNIPPETS) {
1040
+ const absoluteCoveragePath = join(ROOT_DIR, coverageRule.path);
1041
+
1042
+ if (!(await fileExists(absoluteCoveragePath))) {
1043
+ fail(`Missing upgrade UI contract warning source: ${coverageRule.path}`);
1044
+ continue;
1045
+ }
1046
+
1047
+ const coverageContent = await readTextFile(absoluteCoveragePath);
1048
+ for (const requiredSnippet of coverageRule.snippets) {
1049
+ if (coverageContent.includes(requiredSnippet)) {
1050
+ pass(`${coverageRule.path} includes upgrade UI contract warning snippet: ${requiredSnippet}`);
1051
+ } else {
1052
+ fail(`${coverageRule.path} is missing upgrade UI contract warning snippet: ${requiredSnippet}`);
1053
+ }
1054
+ }
1055
+ }
1056
+ }
1057
+
1024
1058
  async function validateDeterministicBoundaryEnforcementCoverage() {
1025
1059
  console.log('\nChecking deterministic boundary enforcement coverage...');
1026
1060
 
@@ -1289,6 +1323,7 @@ async function main() {
1289
1323
  await validateStackResearchEngineCoverage();
1290
1324
  await validateUniversalSopConsolidationCoverage();
1291
1325
  await validateTemplateFreeBootstrapCoverage();
1326
+ await validateUpgradeUiContractWarningCoverage();
1292
1327
  await validateDeterministicBoundaryEnforcementCoverage();
1293
1328
  await validateStackResearchSnapshotState();
1294
1329
  await validateMcpConfiguration();