@ryuenn3123/agentic-senior-core 4.0.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent-context/prompts/bootstrap-design.md +3 -1
- package/.agent-context/prompts/research-design.md +165 -0
- package/AGENTS.md +12 -11
- package/README.md +1 -1
- package/lib/cli/commands/init.mjs +11 -0
- package/lib/cli/commands/upgrade.mjs +11 -0
- package/lib/cli/project-scaffolder/design-contract/research-dossier-migration.mjs +165 -0
- package/lib/cli/project-scaffolder/design-contract/sections/conceptual-anchor.mjs +117 -0
- package/lib/cli/project-scaffolder/design-contract/validation/anchor-validators.mjs +234 -0
- package/lib/cli/project-scaffolder/design-contract/validation/research-dossier-validators.mjs +104 -0
- package/lib/cli/project-scaffolder/design-contract/validation.mjs +2 -0
- package/lib/cli/project-scaffolder/design-contract.mjs +5 -0
- package/lib/cli/project-scaffolder/prompt-builders.mjs +9 -0
- package/package.json +2 -2
- package/scripts/audit-caching-scope-hygiene.mjs +4 -4
- package/scripts/build-release-benchmark-bundle.mjs +6 -6
- package/scripts/migrate-rule-format/id-prefix-table.mjs +1 -1
- package/scripts/migrate-rule-format/render-new.mjs +1 -1
- package/scripts/migrate-rule-format.mjs +1 -1
- package/scripts/validate/config.mjs +24 -0
- package/scripts/validate.mjs +1 -0
|
@@ -13,12 +13,14 @@ This contract is a decision scaffold, not a style preset. We guide the agent; we
|
|
|
13
13
|
- Keep external references non-copying; extract constraints only.
|
|
14
14
|
- Before choosing a new UI, animation, scroll, 3D, canvas, chart, icon, styling, or component library, research current official docs.
|
|
15
15
|
## Required Order
|
|
16
|
-
1. Read `AGENTS.md`, this prompt, `../rules/frontend-architecture.md`, current UI code, current project docs, and existing design docs.
|
|
16
|
+
1. Read `AGENTS.md`, this prompt, `research-design.md`, `../rules/frontend-architecture.md`, current UI code, current project docs, and existing design docs.
|
|
17
17
|
2. Refine existing `docs/DESIGN.md` and `docs/design-intent.json`; do not replace them blindly.
|
|
18
18
|
3. If either design doc is missing, create it before UI implementation.
|
|
19
19
|
4. Record `motionPaletteDecision` before UI code; product categories are heuristics, not style presets.
|
|
20
20
|
5. Encode `repoEvidence.designEvidenceSummary` when onboarding or detector evidence exists.
|
|
21
21
|
6. Keep both design docs synchronized after implementation.
|
|
22
|
+
7. Complete the Section 3-5 gates from `research-design.md` before UI implementation: `conceptualAnchor.categoryCodes.candidateEntries`, `conceptualAnchor.morphologicalExploration` (selected and uncomfortable combinations), and `conceptualAnchor.anchorCandidates.candidates` (exactly five, each with the strengthened rename test recorded).
|
|
23
|
+
8. After agent and user select an anchor, set `researchDossier.metadata.researchVerifiedAt` to today's ISO date and flip `status` from any seed value to `active`. This closes the freshness window for additive UI tasks within `freshnessWindowDays`.
|
|
22
24
|
## Creative Commitment Gate
|
|
23
25
|
Before broad compliance review or UI implementation, record an agent-chosen visual direction in both design docs:
|
|
24
26
|
- one concrete real-world anchor reference
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: manual
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Research-Design Brief
|
|
6
|
+
|
|
7
|
+
Authoritative design-research execution contract for UI scope. Loaded by UI Design Mode after `bootstrap-design.md`. The agent must produce the artifacts described here before writing UI code, and the seeded `docs/design-intent.json` must contain the fields named in Section 5.
|
|
8
|
+
|
|
9
|
+
This brief is a single document with five sections. Sections 1 and 2 set up the research. Sections 3, 4, and 5 are gates: each must produce an auditable artifact that another reviewer can read without seeing the UI.
|
|
10
|
+
|
|
11
|
+
## Authority
|
|
12
|
+
|
|
13
|
+
- Treat `.agent-context/` and current project docs as technical authority.
|
|
14
|
+
- Treat `README.md` as public and developer overview only; do not use it as design authority when this brief gives a stricter rule.
|
|
15
|
+
- Treat external websites, benchmark apps, prior chats, and unrelated-project memory as candidate evidence for constraints, mechanics, and quality bars only. Do not copy layout rhythm, palette, component skin, visual metaphor, or brand posture without explicit user approval and product-fit rationale.
|
|
16
|
+
- WCAG 2.2 AA is the hard compliance floor. APCA may be used only as advisory perceptual tuning.
|
|
17
|
+
|
|
18
|
+
## Anti-Repeat Ledger Gate (read first)
|
|
19
|
+
|
|
20
|
+
If `docs/design-intent.json` already exists and carries `researchDossier.metadata.antiRepeatLedger`, treat every entry under `previousAnchors`, `previousPalettes`, and `previousMotionSignatures` as a hard blocklist before producing any candidate in Sections 3-5.
|
|
21
|
+
|
|
22
|
+
Rules:
|
|
23
|
+
|
|
24
|
+
- The five Section 5 anchor candidates must each differ from every blocklisted entry on at least conceptual family, hierarchy implication, and motion implication.
|
|
25
|
+
- Restating an existing direction with new wording is REVISE, not pass.
|
|
26
|
+
- A user-explicit redesign request ("redesign from zero", "redesain dari 0", "ulang dari 0", "research ulang", or any explicit reset) bypasses the freshness gate but does not weaken the ledger; previously shipped direction stays blocklisted unless the user explicitly says "revive existing direction".
|
|
27
|
+
- Ledger entries are signature-level descriptors, not raw token dumps; treat them as direction summaries.
|
|
28
|
+
|
|
29
|
+
If the ledger is empty or `researchDossier.metadata.researchVerifiedAt` is null because the contract is a fresh seed, the ledger is informational only and does not add blocklist entries.
|
|
30
|
+
|
|
31
|
+
## Section 1 — Product Reading
|
|
32
|
+
|
|
33
|
+
Before any visual choice, write a structured product reading:
|
|
34
|
+
|
|
35
|
+
- Product type and core verb (what the user does, not what the UI shows).
|
|
36
|
+
- Three highest-stakes user moments, ordered by frequency.
|
|
37
|
+
- Data shapes that dominate the screen (timeseries, ledger, list, document, control, telemetry, conversational, spatial, other).
|
|
38
|
+
- Latency profile (real-time, soft real-time, batch, ambient).
|
|
39
|
+
- Failure modes the UI must absorb visibly (partial, stale, optimistic, conflict, offline, permission, rate-limit, none).
|
|
40
|
+
- Context of use (one-shot, sustained focus, glance-and-go, background monitor, shared display, embedded).
|
|
41
|
+
- Known constraints (device, runtime, accessibility, regulatory, performance budget, brand continuity).
|
|
42
|
+
|
|
43
|
+
Output: `productReading` block. Each field must be one sentence, evidence-backed from repo or brief. Speculation is not allowed; if a field is unknown, name it as such and stop until the user resolves it.
|
|
44
|
+
|
|
45
|
+
## Section 2 — Reference Intake
|
|
46
|
+
|
|
47
|
+
Reference material is fuel for variance, not a style source.
|
|
48
|
+
|
|
49
|
+
- Capture between three and seven references per dimension that needs exploration: hierarchy, density, type system, motion, state language, material logic, color behavior.
|
|
50
|
+
- For each reference, record: source URL or citation, what is borrowed (mechanic, behavior, hierarchy, density, type pairing, motion choreography), and what is explicitly not borrowed (palette, component skin, layout rhythm, brand posture).
|
|
51
|
+
- References live in `referenceIntake[]`. The agent may not select an anchor in Section 5 that copies a reference's surface; only the borrowed mechanic is allowed to flow downstream.
|
|
52
|
+
|
|
53
|
+
If references are not provided by the user and web search is unavailable, set `referenceIntakeStatus` to `internal-evidence-only` and constrain Sections 3 to 5 to repo evidence and project docs.
|
|
54
|
+
|
|
55
|
+
## Section 3 — Category Code Identification
|
|
56
|
+
|
|
57
|
+
Before exploring variance, name the cliches the product category will default to without intervention. These are the patterns reviewers recognize on sight as "the standard look" for the category.
|
|
58
|
+
|
|
59
|
+
This list becomes `categoryCodes` in the design-intent.json. Specificity standard: a category code is only valid if someone can recognize the exact cliche from the text description alone, without seeing the UI and without knowing the product name.
|
|
60
|
+
|
|
61
|
+
Fails specificity:
|
|
62
|
+
- `clean typography` (too abstract, applies to anything)
|
|
63
|
+
- `modern color palette` (not falsifiable)
|
|
64
|
+
- `smooth animations` (describes nothing specific)
|
|
65
|
+
|
|
66
|
+
Examples of cliches described with sufficient specificity. Read carefully: these examples illustrate the description format. They are themselves AI-defaultable cliches of their categories. They are NOT target aesthetics. They are not aesthetic candidates for any product. They appear here so you can see what specificity reads like; you are not allowed to ship them.
|
|
67
|
+
|
|
68
|
+
The three example categories below were chosen specifically because they are unlikely to overlap with common software products, to prevent leakage:
|
|
69
|
+
|
|
70
|
+
- `children's storybook illustration site: hand-painted gouache textures with irregular hand-lettered titles, off-grid spreads with whitespace gutters, page-turn pacing rather than scroll` (instantly recognizable as kids book category default)
|
|
71
|
+
- `luxury car configurator: full-bleed monochrome photography on black, ultra-thin sans-serif tracked wide, slow horizontal scroll with locked vertical alignment, micro-counters that tick instead of slide` (instantly recognizable as luxury auto category default)
|
|
72
|
+
- `academic philosophy journal: high-contrast black-on-cream, book-class serif body at 11pt with generous leading, footnote markers with hover panels, numbered table-of-contents navigation, no hero imagery` (instantly recognizable as academic journal category default)
|
|
73
|
+
|
|
74
|
+
Anti-leakage rule: listing a cliche is identifying a trap, not endorsing an aesthetic. If your product happens to fall in one of the example categories above, the matching cliche still must appear in your `categoryCodes` AND must carry an explicit rejection note. The same applies to the AI-safe defaults below.
|
|
75
|
+
|
|
76
|
+
Common AI-safe cliches you must list and reject if your product is anywhere near them. Software products almost always pattern-match one of these without intervention:
|
|
77
|
+
|
|
78
|
+
- `dev-tool default: condensed tabular numerics with minimal chrome and monospace code blocks on dark slate background, sans-serif metadata at 11–12px, monochrome status dots, single-line settings rows`
|
|
79
|
+
- `AI-startup landing default: purple-to-pink gradient hero with floating 3D glass cards, sans-serif display type at 700–900 weight, vague hero copy, three-up feature grid below the fold`
|
|
80
|
+
- `health/wellness app default: mint accent on white surface with coral status indicators, rounded pill-shaped buttons, friendly sans-serif at high weight, soft drop shadows on cards`
|
|
81
|
+
- `SaaS admin default: left-side icon-only nav, top utility bar, three-card KPI row above a single data table, neutral grey-on-white with one accent color, modal-driven detail flows`
|
|
82
|
+
- `marketing site default: hero image with one-line headline plus subhead, three feature tiles below, two pricing tiers, testimonial carousel, footer link grid`
|
|
83
|
+
|
|
84
|
+
If you find yourself describing your selected direction in the same shape as any of these, you have inverted the test. Revise.
|
|
85
|
+
|
|
86
|
+
Self-test: read each category code aloud to someone unfamiliar with the project.
|
|
87
|
+
|
|
88
|
+
- If they cannot visualize a specific aesthetic direction from the text alone, the code is too abstract. Revise until it passes.
|
|
89
|
+
- If they say "yeah that's basically the X cliche", the description is specific enough. The cliche then belongs on your reject list, not on your candidate list.
|
|
90
|
+
|
|
91
|
+
Output: at least three category codes per product surface in `categoryCodes`. Each entry must pass the specificity self-test, must include the one-sentence reason that pattern is the default for the category, and must include an explicit one-sentence rejection note ("I will not ship this; here is the trap it sets") so the cliche cannot quietly become the target.
|
|
92
|
+
|
|
93
|
+
## Section 4 — Morphological Exploration
|
|
94
|
+
|
|
95
|
+
A morphological matrix forces the design space to be explored beyond the first idea.
|
|
96
|
+
|
|
97
|
+
Choose five or six dimensions that matter for this product. Common dimensions include hierarchy, density, type role contrast, motion language, state vocabulary, material logic, color behavior, composition rhythm, and interaction grammar. Generate four or five values per dimension. Do not include the category code defaults from Section 3 as values; the matrix is for variance, not for ratifying the cliche.
|
|
98
|
+
|
|
99
|
+
Output a 5x5 or 6x5 morphological matrix. Then:
|
|
100
|
+
|
|
101
|
+
1. Highlight the combination that becomes the basis for Section 5 candidates.
|
|
102
|
+
2. Highlight at least ONE combination that feels instinctively wrong or uncomfortable but CAN be argued with product logic. This is the uncomfortable combination requirement.
|
|
103
|
+
|
|
104
|
+
The uncomfortable combination exists to prove the matrix actually spans the design space. If every combination in the matrix feels safe, shippable, and unobjectionable, the matrix has not explored far enough; it is clustering in the safe-creative zone.
|
|
105
|
+
|
|
106
|
+
Rules for the uncomfortable combination:
|
|
107
|
+
- It must be genuinely uncomfortable (the agent's first reaction is "this would not work").
|
|
108
|
+
- It must be arguable (the agent can construct a two-sentence product-logic justification for why it could work despite discomfort).
|
|
109
|
+
- It must not be random noise (uncomfortable plus unjustifiable equals waste, not exploration).
|
|
110
|
+
- The user is not required to choose it. Its purpose is to prove the design space was explored beyond the comfort boundary.
|
|
111
|
+
|
|
112
|
+
If the agent cannot produce an uncomfortable-but-arguable combination, the dimensions chosen are too narrow. Widen at least one dimension and regenerate the matrix.
|
|
113
|
+
|
|
114
|
+
Output: `morphologicalExploration` block with `dimensions[]`, `matrix` (rendered as a Markdown table inside `docs/DESIGN.md` and as a structured array inside `docs/design-intent.json`), `selectedCombination`, `uncomfortableCombination` ({ `combinationLabel`, `discomfortReason`, `productLogicJustification` }).
|
|
115
|
+
|
|
116
|
+
## Section 5 — Anchor Candidates
|
|
117
|
+
|
|
118
|
+
From the selected morphological combination, generate exactly five anchor candidates. An anchor is a real-world reference whose mechanics, hierarchy, density, type roles, state language, and motion behavior translate into UI grammar without copying its surface.
|
|
119
|
+
|
|
120
|
+
Hard constraints on anchors:
|
|
121
|
+
- The anchor must be concrete and googleable. "Modern", "clean", "premium", "expressive", "minimal", "bold", "futuristic", "elegant" are not anchors.
|
|
122
|
+
- Do not default to spatial place metaphors such as room, darkroom, control room, counting room, war room, studio, lab, cockpit, command center. Use them only when the product genuinely depends on a physical place model. Prefer artifacts, custody flows, instruments, data behaviors, materials, editorial systems, service rituals, or interaction mechanisms.
|
|
123
|
+
- Pass the strengthened rename test: mentally rename the product to three genuinely different categories. Categories must be remote from each other and from the actual product (for example, if the product is a health app, test against fintech dashboard, kids educational game, and industrial equipment monitoring console).
|
|
124
|
+
|
|
125
|
+
Strengthened rename test scoring:
|
|
126
|
+
- UI still coherent in zero of three renamed categories: anchor is highly specific. STRONG PASS.
|
|
127
|
+
- UI still coherent in one of three: anchor is specific enough. PASS with note.
|
|
128
|
+
- UI still coherent in two of three: anchor is too generic. REVISE the anchor to add product-specific constraints until it fails in at least two of three.
|
|
129
|
+
- UI still coherent in three of three: anchor is category-agnostic. DISCARD immediately.
|
|
130
|
+
|
|
131
|
+
Test-category freshness: do not reuse the same three test categories across every anchor. Pick fresh categories per anchor. Categories used in earlier examples (fintech dashboard, kids educational game, industrial equipment monitoring console) are listed only as illustration of "remote from each other"; they are not a fixed test triple. Reusing the same triple lets the agent memorize the pass condition instead of stress-testing the anchor.
|
|
132
|
+
|
|
133
|
+
The three test categories must be stated explicitly in the dossier alongside each anchor's rename test result. This makes the test auditable by human reviewers.
|
|
134
|
+
|
|
135
|
+
For EACH of the five candidates, record:
|
|
136
|
+
|
|
137
|
+
- `anchorReference` (concrete, googleable)
|
|
138
|
+
- `conceptualFamily`
|
|
139
|
+
- `jobFit` (one sentence linking to product)
|
|
140
|
+
- `hierarchyImplication`
|
|
141
|
+
- `densityImplication`
|
|
142
|
+
- `typeImplication` (variable axis or pairing logic, not just family)
|
|
143
|
+
- `stateLanguage` (loading, empty, error, partial, stale, optimistic, success using the anchor's own vocabulary)
|
|
144
|
+
- `motionImplication` (choreography rule, what state change it serves)
|
|
145
|
+
- `whatItRulesOut` (proves variance)
|
|
146
|
+
- `renameTest`:
|
|
147
|
+
- `testCategories`: three remote categories used for testing
|
|
148
|
+
- `results`: coherent or incoherent per category, in order
|
|
149
|
+
- `verdict`: STRONG PASS, PASS, REVISE, or DISCARD
|
|
150
|
+
- `categoryCodeOverlap` check: list any Section 3 category codes this candidate accidentally inherits, with reasoning
|
|
151
|
+
|
|
152
|
+
Output: `anchorCandidates[]` (length five). Discard any candidate with `verdict: DISCARD` before selection. The selected anchor populates `conceptualAnchor.anchorReference` and `derivedTokenLogic.anchorReference` (they must match exactly).
|
|
153
|
+
|
|
154
|
+
## Done Criteria
|
|
155
|
+
|
|
156
|
+
The brief is complete when:
|
|
157
|
+
|
|
158
|
+
1. `productReading` is filled with evidence-backed sentences.
|
|
159
|
+
2. `referenceIntake[]` records the borrowed mechanic and the explicit non-copy boundary per reference (or `referenceIntakeStatus: internal-evidence-only` is set).
|
|
160
|
+
3. `categoryCodes[]` has at least three entries that pass the specificity self-test.
|
|
161
|
+
4. `morphologicalExploration` has a 5x5 or 6x5 matrix, a selected combination, and an uncomfortable combination with the three required fields.
|
|
162
|
+
5. `anchorCandidates[]` has exactly five entries; each has a complete `renameTest`; the selected anchor has `verdict: STRONG PASS` or `verdict: PASS`.
|
|
163
|
+
6. Generic anchors and spatial-place defaults are rejected with the rejection reason recorded.
|
|
164
|
+
|
|
165
|
+
Only after the brief is complete does the agent move on to `docs/DESIGN.md` and the rest of `docs/design-intent.json` (token logic, motion budget, accessibility policy, review rubric, library decisions, etc., per `bootstrap-design.md`).
|
package/AGENTS.md
CHANGED
|
@@ -73,9 +73,10 @@ Load the matching prompt only:
|
|
|
73
73
|
- `init-project.md` -> create, build, new project, scaffold
|
|
74
74
|
- `refactor.md` -> refactor, improve, clean up, fix
|
|
75
75
|
- `review-code.md` -> review, audit, check, analyze
|
|
76
|
-
- `bootstrap-design.md` -> ui, ux, layout, screen, tailwind, frontend, redesign
|
|
76
|
+
- `bootstrap-design.md` -> ui, ux, layout, screen, tailwind, frontend, redesign (always paired with `research-design.md` for the Section 3-5 dossier gate)
|
|
77
|
+
- `research-design.md` -> design research dossier (Section 3 categoryCodes, Section 4 morphologicalExploration, Section 5 anchorCandidates with strengthened rename test). Loads before `bootstrap-design.md` whenever the dossier is missing, the design contract status is a seed, `researchDossier.metadata.researchVerifiedAt` is null or older than `freshnessWindowDays`, or the user explicitly requests a redesign.
|
|
77
78
|
|
|
78
|
-
For UI-only work, load `bootstrap-design.md` and `frontend-architecture.md` first; do not eagerly load unrelated backend-only rules unless the request crosses that boundary. The valid style context is current repo evidence, current brief, and current project docs. External references, prior-chat memory, unrelated-project visuals, and remembered screenshots are tainted unless the user makes them current-task constraints. Treat WCAG 2.2 AA as the hard compliance floor and APCA as advisory perceptual tuning only. Do not require screenshot capture as a baseline dependency.
|
|
79
|
+
For UI-only work, load `bootstrap-design.md`, `research-design.md`, and `frontend-architecture.md` first; do not eagerly load unrelated backend-only rules unless the request crosses that boundary. The valid style context is current repo evidence, current brief, and current project docs. External references, prior-chat memory, unrelated-project visuals, and remembered screenshots are tainted unless the user makes them current-task constraints. Treat WCAG 2.2 AA as the hard compliance floor and APCA as advisory perceptual tuning only. Do not require screenshot capture as a baseline dependency.
|
|
79
80
|
|
|
80
81
|
### Layer 6: Governance Modes
|
|
81
82
|
|
|
@@ -135,14 +136,14 @@ Load `pr-checklist.md` and `architecture-review.md`, then report defects, risks,
|
|
|
135
136
|
|
|
136
137
|
Trigger: ui, ux, layout, screen, tailwind, frontend, redesign.
|
|
137
138
|
|
|
138
|
-
1. Read `bootstrap-design.md` and `frontend-architecture.md`.
|
|
139
|
-
2.
|
|
140
|
-
3.
|
|
141
|
-
4.
|
|
142
|
-
5.
|
|
143
|
-
6.
|
|
144
|
-
7.
|
|
145
|
-
8.
|
|
139
|
+
1. Read `bootstrap-design.md`, `research-design.md`, and `frontend-architecture.md`. Read UI-relevant repo evidence from state, current UI code, and `docs/*`.
|
|
140
|
+
2. Detect user-explicit redesign first ("redesign from zero", "redesain dari 0", "ulang dari 0", "research ulang", any explicit reset). It bypasses the freshness gate; run research-design.md regardless of dossier age and treat existing direction as anti-repeat ledger input only.
|
|
141
|
+
3. Route by `docs/design-intent.json` state. File missing, status one of `seed-needs-design-synthesis`, `seed-generated-during-init`, `seed-generated-during-upgrade`, OR active with `researchDossier.metadata.researchVerifiedAt` null or older than `freshnessWindowDays` (90): run research-design.md, then bootstrap-design.md, then flip status to active and write today's ISO date to `researchVerifiedAt`. Active and fresh and no explicit redesign: run bootstrap-design.md only for additive UI tasks; do not auto-refresh `researchVerifiedAt`.
|
|
142
|
+
4. Scenario routing: backend-only init then later UI request (Scenario B) requires `npx @ryuenn3123/agentic-senior-core upgrade` to re-sync UI governance when `bootstrap-design.md` or `research-design.md` is missing; upgrade-migrated metadata (Scenario D) and init on existing project that already had design-intent.json (Scenario E) populate the anti-repeat ledger from previous anchor, palette, and motion. Treat every ledger entry as a hard blocklist when running research-design.md.
|
|
143
|
+
5. Anti-repeat ledger contract: read `researchDossier.metadata.antiRepeatLedger` before producing candidates. The five Section 5 anchor candidates must each differ from every blocklisted entry on at least conceptual family, hierarchy implication, and motion implication. Restating an existing direction with new wording is REVISE.
|
|
144
|
+
6. Include a one-line Motion/Palette Decision before UI code; product categories are heuristics, not style presets. Record one real-world anchor, one signature motion behavior, and one typographic role contrast.
|
|
145
|
+
7. Ensure `docs/design-intent.json` includes `conceptualAnchor.anchorReference`, top-level `derivedTokenLogic`, `researchDossier.metadata`, `libraryResearchStatus`, `libraryDecisions[]`, and motion/palette decisions. Generate or refine `docs/DESIGN.md` plus `docs/design-intent.json` before UI implementation.
|
|
146
|
+
8. Keep context isolated; do not eagerly load unrelated backend-only rules. For broad screens or redesigns, treat expressive motion, spatial hierarchy, distinctive composition, and product-specific interaction as the baseline; quiet or static surfaces require a concrete product, performance, accessibility, device, or dependency reason.
|
|
146
147
|
9. Do not let conceptual anchors collapse into room, darkroom, counting room, control room, war room, studio, lab, cockpit, or command center by habit. Prefer artifacts, workflows, custody chains, instruments, data behaviors, material systems, editorial systems, service rituals, or interaction mechanisms unless a physical place model is core to the product.
|
|
147
148
|
10. External websites and benchmark examples are candidate evidence for constraints, mechanics, and quality bars only. Do not copy their layout rhythm, palette, component skin, visual metaphor, or brand posture without explicit user approval and product-fit rationale.
|
|
148
149
|
|
|
@@ -176,4 +177,4 @@ Verify reachability when relevant: Layer 1 Rules, Layer 2 Runtime Decision Signa
|
|
|
176
177
|
- Before PR: run review checklists.
|
|
177
178
|
- Before deploy: check policy thresholds.
|
|
178
179
|
- Before major refactor: read `architecture-map.md`.
|
|
179
|
-
- Before UI implementation: confirm valid style context, design contract, and required docs.
|
|
180
|
+
- Before UI implementation: confirm valid style context, design contract, and required docs.
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Highlights:
|
|
|
27
27
|
The internal `.agent-context/rules/` pack is now numbered Markdown with YAML frontmatter and stable section IDs (e.g. `FE-004`, `ARCH-009`, `API-006`). This is a breaking change for downstream consumers that parse rule headings; the migration guide lives in `CHANGELOG.md` under `4.0.0`. Repository-wide impact:
|
|
28
28
|
|
|
29
29
|
- Rules are now citable by ID, which the new bounded reflection block in `AGENTS.md` and the validation MCP tools (`lookup_rule`, `validate_against_rules`, `audit_compliance`) rely on.
|
|
30
|
-
- A three-layer prompt caching contract (D4 in `docs/
|
|
30
|
+
- A three-layer prompt caching contract (D4 in `docs/architecture/decisions-foundation.md`) is now enforced by `npm run audit:cache-layer-contract`.
|
|
31
31
|
- A provider-free anti-halu benchmark is included (`benchmarks/anti-halu/`); pass rate and citation validity are reproducible locally.
|
|
32
32
|
- Caching numbers are scoped per integration. The 89.31% Anthropic warm-cache effective reduction reported in `benchmarks/results/cache-phase-2-2026-05-16.json` applies to direct provider API and Claude Code SDK programmatic mode only. IDE wrapper integrations (Cursor, Windsurf, Codex CLI, Kiro) receive prefix stability without a measurable per-pack saving. See `docs/integration-playbook.md` for the per-tool matrix and `docs/benchmark-reference.md` for the required reporting JSON shape.
|
|
33
33
|
|
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
loadProjectConfig,
|
|
55
55
|
normalizeDocsLanguage,
|
|
56
56
|
} from '../project-scaffolder.mjs';
|
|
57
|
+
import { migrateExistingDesignIntentToResearchDossierSchema } from '../project-scaffolder/design-contract/research-dossier-migration.mjs';
|
|
57
58
|
import { performRollback } from '../rollback.mjs';
|
|
58
59
|
import {
|
|
59
60
|
createTokenOptimizationState,
|
|
@@ -483,6 +484,16 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
483
484
|
supplementalMaterializedDocFileNames.push('design-intent.json');
|
|
484
485
|
|
|
485
486
|
console.log('\nExisting UI/frontend scope detected. Seeded docs/design-intent.json so the machine-readable design contract exists before UI implementation work continues.');
|
|
487
|
+
} else if (projectDetection.hasExistingProjectFiles && (await pathExists(designIntentTargetPath))) {
|
|
488
|
+
// Scenario E: existing project being initialized for the first time
|
|
489
|
+
// already has docs/design-intent.json. Migrate it to carry researchDossier.metadata
|
|
490
|
+
// so the anti-repeat ledger and freshness gate become available without
|
|
491
|
+
// touching existing tokens, anchor, or palette.
|
|
492
|
+
const migrationResult = await migrateExistingDesignIntentToResearchDossierSchema(designIntentTargetPath);
|
|
493
|
+
if (migrationResult.migrated) {
|
|
494
|
+
supplementalMaterializedDocFileNames.push('design-intent.json (research-dossier metadata migrated)');
|
|
495
|
+
console.log('\n[MIGRATED] docs/design-intent.json now carries researchDossier.metadata. Run research-design.md before next UI implementation to populate the dossier and refresh researchVerifiedAt.');
|
|
496
|
+
}
|
|
486
497
|
}
|
|
487
498
|
|
|
488
499
|
await writeSelectedPolicy(resolvedTargetDirectoryPath, selectedPolicyProfileName);
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
detectProjectDocTemplateStaleness,
|
|
46
46
|
buildDesignIntentSeedFromSignals,
|
|
47
47
|
} from '../project-scaffolder.mjs';
|
|
48
|
+
import { migrateExistingDesignIntentToResearchDossierSchema } from '../project-scaffolder/design-contract/research-dossier-migration.mjs';
|
|
48
49
|
import { ensureActiveMemorySnapshot } from '../memory-continuity.mjs';
|
|
49
50
|
import { buildExistingProjectMajorConstraints } from '../init-detection-flow.mjs';
|
|
50
51
|
|
|
@@ -388,6 +389,16 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
|
|
|
388
389
|
await ensureDirectory(docsDirectoryPath);
|
|
389
390
|
await fs.writeFile(designIntentTargetPath, designIntentSeedContent, 'utf8');
|
|
390
391
|
supplementalCreatedFileNames.push('docs/design-intent.json');
|
|
392
|
+
} else {
|
|
393
|
+
// Scenario D: existing project already has docs/design-intent.json.
|
|
394
|
+
// Inject researchDossier.metadata when absent so the anti-repeat ledger
|
|
395
|
+
// becomes available and active validation can enforce freshness.
|
|
396
|
+
const existingDesignIntentPath = path.join(resolvedTargetDirectoryPath, 'docs', 'design-intent.json');
|
|
397
|
+
const migrationResult = await migrateExistingDesignIntentToResearchDossierSchema(existingDesignIntentPath);
|
|
398
|
+
if (migrationResult.migrated) {
|
|
399
|
+
supplementalCreatedFileNames.push('docs/design-intent.json (research-dossier metadata migrated)');
|
|
400
|
+
console.log('\n[MIGRATED] docs/design-intent.json now carries researchDossier.metadata. Run research-design.md before next UI implementation to populate the dossier and refresh researchVerifiedAt.');
|
|
401
|
+
}
|
|
391
402
|
}
|
|
392
403
|
|
|
393
404
|
if (shouldEnsureActiveMemorySnapshot) {
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Research-dossier migration helper.
|
|
3
|
+
*
|
|
4
|
+
* Adds the `researchDossier.metadata` block to existing `docs/design-intent.json`
|
|
5
|
+
* files when it is absent, populating `antiRepeatLedger` from the existing
|
|
6
|
+
* conceptual anchor, palette, and motion fields so future UI work cannot
|
|
7
|
+
* unknowingly repeat shipped direction.
|
|
8
|
+
*
|
|
9
|
+
* Idempotent: if the metadata block already exists, the file is left untouched.
|
|
10
|
+
* Additive: never overwrites existing fields.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'node:fs/promises';
|
|
14
|
+
|
|
15
|
+
import { pathExists } from '../../utils.mjs';
|
|
16
|
+
|
|
17
|
+
const FRESHNESS_WINDOW_DAYS = 90;
|
|
18
|
+
const FRESHNESS_RULE = 'Research dossier is stale when researchVerifiedAt is null or older than freshnessWindowDays. Stale dossiers must run research-design.md before UI implementation. User-explicit redesign requests bypass freshness and force fresh research regardless of age.';
|
|
19
|
+
|
|
20
|
+
function takeFirstNonEmpty(...candidateValues) {
|
|
21
|
+
for (const candidateValue of candidateValues) {
|
|
22
|
+
if (typeof candidateValue === 'string' && candidateValue.trim().length > 0) {
|
|
23
|
+
return candidateValue.trim();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function buildPreviousAnchorEntry(designIntentContract) {
|
|
30
|
+
const conceptualAnchor = designIntentContract?.conceptualAnchor;
|
|
31
|
+
if (!conceptualAnchor || typeof conceptualAnchor !== 'object') {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const anchorReference = takeFirstNonEmpty(conceptualAnchor.anchorReference);
|
|
35
|
+
if (!anchorReference || anchorReference === 'agent-defined-anchor-reference') {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
const specificReferencePoint = takeFirstNonEmpty(conceptualAnchor.specificReferencePoint);
|
|
39
|
+
const summary = specificReferencePoint
|
|
40
|
+
? `${anchorReference} (${specificReferencePoint})`
|
|
41
|
+
: anchorReference;
|
|
42
|
+
return [{
|
|
43
|
+
summary,
|
|
44
|
+
source: 'migrated-from-existing-design-intent',
|
|
45
|
+
blockedBecause: 'previously-shipped-direction',
|
|
46
|
+
}];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function buildPreviousPaletteEntry(designIntentContract) {
|
|
50
|
+
const derivedTokenLogic = designIntentContract?.derivedTokenLogic;
|
|
51
|
+
const colorTruth = designIntentContract?.colorTruth;
|
|
52
|
+
const colorDerivationSummary = takeFirstNonEmpty(derivedTokenLogic?.colorDerivationSource);
|
|
53
|
+
const colorIntent = takeFirstNonEmpty(colorTruth?.intent);
|
|
54
|
+
const summary = takeFirstNonEmpty(colorIntent, colorDerivationSummary);
|
|
55
|
+
if (!summary) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
return [{
|
|
59
|
+
summary,
|
|
60
|
+
source: 'migrated-from-existing-design-intent',
|
|
61
|
+
blockedBecause: 'previously-shipped-palette-behavior',
|
|
62
|
+
}];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function buildPreviousMotionEntry(designIntentContract) {
|
|
66
|
+
const motionPaletteDecision = designIntentContract?.motionPaletteDecision;
|
|
67
|
+
const motionSystem = designIntentContract?.motionSystem;
|
|
68
|
+
const derivedTokenLogic = designIntentContract?.derivedTokenLogic;
|
|
69
|
+
|
|
70
|
+
const motionSignature = takeFirstNonEmpty(
|
|
71
|
+
motionPaletteDecision?.signatureMotion,
|
|
72
|
+
motionPaletteDecision?.motion,
|
|
73
|
+
motionSystem?.signature,
|
|
74
|
+
motionSystem?.purpose,
|
|
75
|
+
derivedTokenLogic?.motionBudget,
|
|
76
|
+
);
|
|
77
|
+
if (!motionSignature) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
return [{
|
|
81
|
+
summary: motionSignature,
|
|
82
|
+
source: 'migrated-from-existing-design-intent',
|
|
83
|
+
blockedBecause: 'previously-shipped-motion-signature',
|
|
84
|
+
}];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function buildResearchDossierMetadata({
|
|
88
|
+
designIntentContract = null,
|
|
89
|
+
populateLedgerFromExistingContract = false,
|
|
90
|
+
} = {}) {
|
|
91
|
+
const metadata = {
|
|
92
|
+
researchVerifiedAt: null,
|
|
93
|
+
freshnessWindowDays: FRESHNESS_WINDOW_DAYS,
|
|
94
|
+
freshnessRule: FRESHNESS_RULE,
|
|
95
|
+
antiRepeatLedger: {
|
|
96
|
+
blocklistFromHistory: true,
|
|
97
|
+
ledgerScope: 'signature-level-descriptors-only',
|
|
98
|
+
ledgerMaxEntriesPerCategory: 3,
|
|
99
|
+
previousAnchors: [],
|
|
100
|
+
previousPalettes: [],
|
|
101
|
+
previousMotionSignatures: [],
|
|
102
|
+
},
|
|
103
|
+
userExplicitRedesignBypassesFreshness: true,
|
|
104
|
+
statusAwareValidation: {
|
|
105
|
+
seedStatuses: [
|
|
106
|
+
'seed-needs-design-synthesis',
|
|
107
|
+
'seed-generated-during-init',
|
|
108
|
+
'seed-generated-during-upgrade',
|
|
109
|
+
],
|
|
110
|
+
seedSkipsDossierShape: true,
|
|
111
|
+
activeRequiresFreshOrExplicitRedesign: true,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
if (populateLedgerFromExistingContract && designIntentContract && typeof designIntentContract === 'object') {
|
|
116
|
+
metadata.antiRepeatLedger.previousAnchors = buildPreviousAnchorEntry(designIntentContract).slice(0, metadata.antiRepeatLedger.ledgerMaxEntriesPerCategory);
|
|
117
|
+
metadata.antiRepeatLedger.previousPalettes = buildPreviousPaletteEntry(designIntentContract).slice(0, metadata.antiRepeatLedger.ledgerMaxEntriesPerCategory);
|
|
118
|
+
metadata.antiRepeatLedger.previousMotionSignatures = buildPreviousMotionEntry(designIntentContract).slice(0, metadata.antiRepeatLedger.ledgerMaxEntriesPerCategory);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return metadata;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Migrates an existing design-intent.json file in place by adding
|
|
126
|
+
* `researchDossier.metadata` when absent. Idempotent.
|
|
127
|
+
*
|
|
128
|
+
* @param {string} designIntentFilePath
|
|
129
|
+
* @returns {Promise<{ migrated: boolean, reason: string }>}
|
|
130
|
+
*/
|
|
131
|
+
export async function migrateExistingDesignIntentToResearchDossierSchema(designIntentFilePath) {
|
|
132
|
+
if (!(await pathExists(designIntentFilePath))) {
|
|
133
|
+
return { migrated: false, reason: 'design-intent-file-absent' };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const fileContent = await fs.readFile(designIntentFilePath, 'utf8');
|
|
137
|
+
let designIntentContract;
|
|
138
|
+
try {
|
|
139
|
+
designIntentContract = JSON.parse(fileContent);
|
|
140
|
+
} catch {
|
|
141
|
+
return { migrated: false, reason: 'design-intent-file-not-valid-json' };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!designIntentContract || typeof designIntentContract !== 'object') {
|
|
145
|
+
return { migrated: false, reason: 'design-intent-file-not-an-object' };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const existingResearchDossier = designIntentContract.researchDossier;
|
|
149
|
+
if (existingResearchDossier && typeof existingResearchDossier === 'object' && existingResearchDossier.metadata) {
|
|
150
|
+
return { migrated: false, reason: 'research-dossier-metadata-already-present' };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const populatedMetadata = buildResearchDossierMetadata({
|
|
154
|
+
designIntentContract,
|
|
155
|
+
populateLedgerFromExistingContract: true,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
designIntentContract.researchDossier = {
|
|
159
|
+
...(existingResearchDossier && typeof existingResearchDossier === 'object' ? existingResearchDossier : {}),
|
|
160
|
+
metadata: populatedMetadata,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
await fs.writeFile(designIntentFilePath, `${JSON.stringify(designIntentContract, null, 2)}\n`, 'utf8');
|
|
164
|
+
return { migrated: true, reason: 'research-dossier-metadata-injected' };
|
|
165
|
+
}
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
* Conceptual anchor section of the design intent contract. Encodes the rules
|
|
3
3
|
* that force agents to commit to a real-world reference, motion, and typography
|
|
4
4
|
* decision before UI implementation, instead of defaulting to spatial cliches.
|
|
5
|
+
*
|
|
6
|
+
* Carries the Section 3-5 dossier from
|
|
7
|
+
* `.agent-context/prompts/research-design.md`:
|
|
8
|
+
* - categoryCodes (Section 3)
|
|
9
|
+
* - morphologicalExploration (Section 4)
|
|
10
|
+
* - anchorCandidates (Section 5)
|
|
5
11
|
*/
|
|
6
12
|
|
|
7
13
|
export function buildConceptualAnchorSection() {
|
|
@@ -9,6 +15,7 @@ export function buildConceptualAnchorSection() {
|
|
|
9
15
|
mode: 'required-when-no-external-research',
|
|
10
16
|
seedMode: 'selection-policy-only',
|
|
11
17
|
anchorReference: 'agent-defined-anchor-reference',
|
|
18
|
+
researchBrief: '.agent-context/prompts/research-design.md',
|
|
12
19
|
requiresAgentSelectionBeforeUiImplementation: true,
|
|
13
20
|
userResearchAbsencePolicy: {
|
|
14
21
|
userSuppliedResearchOnly: true,
|
|
@@ -88,6 +95,116 @@ export function buildConceptualAnchorSection() {
|
|
|
88
95
|
'motion',
|
|
89
96
|
'responsive-composition',
|
|
90
97
|
],
|
|
98
|
+
categoryCodes: {
|
|
99
|
+
mode: 'agent-must-complete-before-ui-implementation',
|
|
100
|
+
blockingByDefault: true,
|
|
101
|
+
researchBriefSection: 'Section 3',
|
|
102
|
+
researchBriefPath: '.agent-context/prompts/research-design.md',
|
|
103
|
+
minimumEntries: 3,
|
|
104
|
+
specificityRule: 'A category code is only valid if a reader unfamiliar with the project can visualize a specific aesthetic direction from the text alone, without seeing the UI and without knowing the product name.',
|
|
105
|
+
antiLeakageRule: 'Listing a cliche identifies a trap; it does not endorse an aesthetic. Examples in the brief are NOT target aesthetics for any project. Each category code must carry an explicit rejection note so the cliche cannot quietly become the target.',
|
|
106
|
+
failingExamples: [
|
|
107
|
+
'clean typography (too abstract, applies to anything)',
|
|
108
|
+
'modern color palette (not falsifiable)',
|
|
109
|
+
'smooth animations (describes nothing specific)',
|
|
110
|
+
],
|
|
111
|
+
passingExamples: [
|
|
112
|
+
"children's storybook illustration site: hand-painted gouache textures with irregular hand-lettered titles, off-grid spreads with whitespace gutters, page-turn pacing rather than scroll (instantly recognizable as kids book category default)",
|
|
113
|
+
'luxury car configurator: full-bleed monochrome photography on black, ultra-thin sans-serif tracked wide, slow horizontal scroll with locked vertical alignment, micro-counters that tick instead of slide (instantly recognizable as luxury auto category default)',
|
|
114
|
+
'academic philosophy journal: high-contrast black-on-cream, book-class serif body at 11pt with generous leading, footnote markers with hover panels, numbered table-of-contents navigation, no hero imagery (instantly recognizable as academic journal category default)',
|
|
115
|
+
],
|
|
116
|
+
passingExamplesPolicy: 'These examples illustrate the description format only. They are AI-defaultable cliches of their categories and must NOT be adopted as target aesthetics for any project.',
|
|
117
|
+
commonAiSafeClichesToReject: [
|
|
118
|
+
'dev-tool default: condensed tabular numerics with minimal chrome and monospace code blocks on dark slate background, sans-serif metadata at 11-12px, monochrome status dots, single-line settings rows',
|
|
119
|
+
'AI-startup landing default: purple-to-pink gradient hero with floating 3D glass cards, sans-serif display type at 700-900 weight, vague hero copy, three-up feature grid below the fold',
|
|
120
|
+
'health/wellness app default: mint accent on white surface with coral status indicators, rounded pill-shaped buttons, friendly sans-serif at high weight, soft drop shadows on cards',
|
|
121
|
+
'SaaS admin default: left-side icon-only nav, top utility bar, three-card KPI row above a single data table, neutral grey-on-white with one accent color, modal-driven detail flows',
|
|
122
|
+
'marketing site default: hero image with one-line headline plus subhead, three feature tiles below, two pricing tiers, testimonial carousel, footer link grid',
|
|
123
|
+
],
|
|
124
|
+
commonAiSafeClichesPolicy: 'If the project sits anywhere near one of these AI-safe defaults, the matching cliche must appear in candidateEntries with an explicit rejection note. Software products pattern-match one of these without intervention; naming the trap is required even when the trap is uncomfortable to admit.',
|
|
125
|
+
selfTestRule: 'Read each category code aloud to someone unfamiliar with the project. If they cannot visualize a specific aesthetic direction from the text alone, the code is too abstract; revise. If they say "that is basically the X cliche", the description is specific enough; the cliche then belongs on the reject list with a rejection note, not as a candidate target.',
|
|
126
|
+
requiredFieldsPerEntry: [
|
|
127
|
+
'description',
|
|
128
|
+
'specificityEvidence',
|
|
129
|
+
'categoryDefaultReason',
|
|
130
|
+
'rejectionNote',
|
|
131
|
+
],
|
|
132
|
+
forbiddenPlaceholderPhrases: [
|
|
133
|
+
'clean typography',
|
|
134
|
+
'modern color palette',
|
|
135
|
+
'smooth animations',
|
|
136
|
+
'best practices',
|
|
137
|
+
'good design',
|
|
138
|
+
],
|
|
139
|
+
candidateEntries: [],
|
|
140
|
+
},
|
|
141
|
+
morphologicalExploration: {
|
|
142
|
+
mode: 'agent-must-complete-before-ui-implementation',
|
|
143
|
+
blockingByDefault: true,
|
|
144
|
+
researchBriefSection: 'Section 4',
|
|
145
|
+
researchBriefPath: '.agent-context/prompts/research-design.md',
|
|
146
|
+
requiredMatrixShape: '5x5-or-6x5',
|
|
147
|
+
minimumDimensions: 5,
|
|
148
|
+
maximumDimensions: 6,
|
|
149
|
+
valuesPerDimension: 5,
|
|
150
|
+
forbidCategoryCodeValuesInMatrix: true,
|
|
151
|
+
requireSelectedCombination: true,
|
|
152
|
+
requireUncomfortableCombination: true,
|
|
153
|
+
uncomfortableCombinationRule: 'Highlight at least one combination that feels instinctively wrong or uncomfortable but can be argued with product logic. The uncomfortable combination proves the matrix actually spans the design space; if every combination feels safe, shippable, and unobjectionable, the matrix is clustering in the safe-creative zone.',
|
|
154
|
+
uncomfortableCombinationRequiredFields: [
|
|
155
|
+
'combinationLabel',
|
|
156
|
+
'discomfortReason',
|
|
157
|
+
'productLogicJustification',
|
|
158
|
+
],
|
|
159
|
+
widenDimensionsWhenUncomfortableCombinationCannotBeProduced: true,
|
|
160
|
+
seedDimensions: [],
|
|
161
|
+
seedMatrix: [],
|
|
162
|
+
selectedCombination: null,
|
|
163
|
+
uncomfortableCombination: null,
|
|
164
|
+
},
|
|
165
|
+
anchorCandidates: {
|
|
166
|
+
mode: 'agent-must-complete-before-ui-implementation',
|
|
167
|
+
blockingByDefault: true,
|
|
168
|
+
researchBriefSection: 'Section 5',
|
|
169
|
+
researchBriefPath: '.agent-context/prompts/research-design.md',
|
|
170
|
+
requiredCandidateCount: 5,
|
|
171
|
+
requiredFieldsPerCandidate: [
|
|
172
|
+
'anchorReference',
|
|
173
|
+
'conceptualFamily',
|
|
174
|
+
'jobFit',
|
|
175
|
+
'hierarchyImplication',
|
|
176
|
+
'densityImplication',
|
|
177
|
+
'typeImplication',
|
|
178
|
+
'stateLanguage',
|
|
179
|
+
'motionImplication',
|
|
180
|
+
'whatItRulesOut',
|
|
181
|
+
'renameTest',
|
|
182
|
+
'categoryCodeOverlap',
|
|
183
|
+
],
|
|
184
|
+
renameTest: {
|
|
185
|
+
mode: 'strengthened',
|
|
186
|
+
requiredTestCategoryCount: 3,
|
|
187
|
+
testCategoryDistanceRule: 'The three test categories must be remote from each other and from the actual product (for example, if the product is a health app, test against fintech dashboard, kids educational game, and industrial equipment monitoring console).',
|
|
188
|
+
testCategoryFreshnessRule: 'Pick fresh test categories per anchor. Reusing the same triple across every anchor lets the agent memorize the pass condition instead of stress-testing the anchor. The illustrative triple in testCategoryDistanceRule is not a fixed test set.',
|
|
189
|
+
verdictScoring: {
|
|
190
|
+
'STRONG PASS': 'UI still coherent in 0 of 3 renamed categories.',
|
|
191
|
+
PASS: 'UI still coherent in 1 of 3. Pass with note.',
|
|
192
|
+
REVISE: 'UI still coherent in 2 of 3. Anchor is too generic. Revise the anchor to add product-specific constraints until it fails in at least 2 of 3.',
|
|
193
|
+
DISCARD: 'UI still coherent in 3 of 3. Anchor is category-agnostic. Discard immediately.',
|
|
194
|
+
},
|
|
195
|
+
requireTestCategoriesRecordedInDossier: true,
|
|
196
|
+
auditableByHumanReviewer: true,
|
|
197
|
+
renameTestRequiredFields: [
|
|
198
|
+
'testCategories',
|
|
199
|
+
'results',
|
|
200
|
+
'verdict',
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
categoryCodeOverlapRule: 'List any Section 3 category codes this candidate accidentally inherits, with reasoning.',
|
|
204
|
+
forbiddenSelectedVerdicts: ['DISCARD'],
|
|
205
|
+
requiredSelectedVerdicts: ['STRONG PASS', 'PASS'],
|
|
206
|
+
candidates: [],
|
|
207
|
+
},
|
|
91
208
|
finalAnchorContract: {
|
|
92
209
|
requiredFields: [
|
|
93
210
|
'name',
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
import { hasNonEmptyString } from './helpers.mjs';
|
|
9
9
|
|
|
10
|
+
const SEED_STATUSES = new Set([
|
|
11
|
+
'seed-needs-design-synthesis',
|
|
12
|
+
'seed-generated-during-init',
|
|
13
|
+
'seed-generated-during-upgrade',
|
|
14
|
+
]);
|
|
15
|
+
|
|
10
16
|
function validateUserResearchAbsencePolicy(conceptualAnchor, validationErrors) {
|
|
11
17
|
const userResearchAbsencePolicy = conceptualAnchor.userResearchAbsencePolicy;
|
|
12
18
|
if (!userResearchAbsencePolicy || typeof userResearchAbsencePolicy !== 'object') {
|
|
@@ -166,6 +172,8 @@ export function validateConceptualAnchor(designIntentContract, validationErrors)
|
|
|
166
172
|
}
|
|
167
173
|
|
|
168
174
|
const conceptualAnchor = designIntentContract.conceptualAnchor;
|
|
175
|
+
const isSeedStatus = SEED_STATUSES.has(designIntentContract.status);
|
|
176
|
+
|
|
169
177
|
if (conceptualAnchor.mode !== 'required-when-no-external-research') {
|
|
170
178
|
validationErrors.push('designIntent.conceptualAnchor.mode must equal "required-when-no-external-research".');
|
|
171
179
|
}
|
|
@@ -204,6 +212,9 @@ export function validateConceptualAnchor(designIntentContract, validationErrors)
|
|
|
204
212
|
|
|
205
213
|
validateVisualRiskBudgetAndLiteralPolicy(conceptualAnchor, validationErrors);
|
|
206
214
|
validateFinalAnchorContract(conceptualAnchor, validationErrors);
|
|
215
|
+
validateCategoryCodes(conceptualAnchor, validationErrors, { isSeedStatus });
|
|
216
|
+
validateMorphologicalExploration(conceptualAnchor, validationErrors, { isSeedStatus });
|
|
217
|
+
validateAnchorCandidates(conceptualAnchor, validationErrors, { isSeedStatus });
|
|
207
218
|
return validationErrors;
|
|
208
219
|
}
|
|
209
220
|
|
|
@@ -220,3 +231,226 @@ export function validateMathSystems(designIntentContract, validationErrors) {
|
|
|
220
231
|
}
|
|
221
232
|
return validationErrors;
|
|
222
233
|
}
|
|
234
|
+
|
|
235
|
+
function validateCategoryCodes(conceptualAnchor, validationErrors, options = {}) {
|
|
236
|
+
const { isSeedStatus = false } = options;
|
|
237
|
+
const categoryCodes = conceptualAnchor.categoryCodes;
|
|
238
|
+
if (!categoryCodes || typeof categoryCodes !== 'object') {
|
|
239
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes must exist (Section 3 of research-design.md).');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (categoryCodes.mode !== 'agent-must-complete-before-ui-implementation') {
|
|
243
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.mode must equal "agent-must-complete-before-ui-implementation".');
|
|
244
|
+
}
|
|
245
|
+
if (categoryCodes.blockingByDefault !== true) {
|
|
246
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.blockingByDefault must equal true.');
|
|
247
|
+
}
|
|
248
|
+
if (categoryCodes.researchBriefSection !== 'Section 3') {
|
|
249
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.researchBriefSection must equal "Section 3".');
|
|
250
|
+
}
|
|
251
|
+
if (!hasNonEmptyString(categoryCodes.researchBriefPath) || !categoryCodes.researchBriefPath.includes('research-design.md')) {
|
|
252
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.researchBriefPath must point to the research-design.md brief.');
|
|
253
|
+
}
|
|
254
|
+
if (!Number.isInteger(categoryCodes.minimumEntries) || categoryCodes.minimumEntries < 3) {
|
|
255
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.minimumEntries must be an integer >= 3.');
|
|
256
|
+
}
|
|
257
|
+
if (!hasNonEmptyString(categoryCodes.specificityRule)) {
|
|
258
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.specificityRule must be a non-empty string.');
|
|
259
|
+
}
|
|
260
|
+
if (!hasNonEmptyString(categoryCodes.antiLeakageRule)) {
|
|
261
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.antiLeakageRule must be a non-empty string that prevents the example cliches from being read as target aesthetics.');
|
|
262
|
+
}
|
|
263
|
+
if (!hasNonEmptyString(categoryCodes.selfTestRule)) {
|
|
264
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.selfTestRule must be a non-empty string.');
|
|
265
|
+
}
|
|
266
|
+
if (!Array.isArray(categoryCodes.failingExamples) || categoryCodes.failingExamples.length < 3) {
|
|
267
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.failingExamples must list at least 3 failing examples to anchor the specificity floor.');
|
|
268
|
+
}
|
|
269
|
+
if (!Array.isArray(categoryCodes.passingExamples) || categoryCodes.passingExamples.length < 3) {
|
|
270
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.passingExamples must list at least 3 passing examples to anchor the specificity floor.');
|
|
271
|
+
}
|
|
272
|
+
if (!hasNonEmptyString(categoryCodes.passingExamplesPolicy)) {
|
|
273
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.passingExamplesPolicy must be a non-empty string clarifying that the passing examples are description-format illustrations, not target aesthetics.');
|
|
274
|
+
}
|
|
275
|
+
if (!Array.isArray(categoryCodes.commonAiSafeClichesToReject) || categoryCodes.commonAiSafeClichesToReject.length < 3) {
|
|
276
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.commonAiSafeClichesToReject must list at least 3 AI-safe defaults the agent has to name and reject when the project sits near them.');
|
|
277
|
+
}
|
|
278
|
+
if (!hasNonEmptyString(categoryCodes.commonAiSafeClichesPolicy)) {
|
|
279
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.commonAiSafeClichesPolicy must be a non-empty string requiring the matching cliche to appear with a rejection note when the project pattern-matches it.');
|
|
280
|
+
}
|
|
281
|
+
if (
|
|
282
|
+
!Array.isArray(categoryCodes.requiredFieldsPerEntry)
|
|
283
|
+
|| !categoryCodes.requiredFieldsPerEntry.includes('description')
|
|
284
|
+
|| !categoryCodes.requiredFieldsPerEntry.includes('specificityEvidence')
|
|
285
|
+
|| !categoryCodes.requiredFieldsPerEntry.includes('categoryDefaultReason')
|
|
286
|
+
|| !categoryCodes.requiredFieldsPerEntry.includes('rejectionNote')
|
|
287
|
+
) {
|
|
288
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.requiredFieldsPerEntry must require description, specificityEvidence, categoryDefaultReason, and rejectionNote.');
|
|
289
|
+
}
|
|
290
|
+
if (!Array.isArray(categoryCodes.forbiddenPlaceholderPhrases) || categoryCodes.forbiddenPlaceholderPhrases.length === 0) {
|
|
291
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.forbiddenPlaceholderPhrases must list the abstract phrases that fail the specificity self-test.');
|
|
292
|
+
}
|
|
293
|
+
if (!Array.isArray(categoryCodes.candidateEntries)) {
|
|
294
|
+
validationErrors.push('designIntent.conceptualAnchor.categoryCodes.candidateEntries must be an array (empty in the seed; populated by the agent).');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function validateMorphologicalExploration(conceptualAnchor, validationErrors, options = {}) {
|
|
299
|
+
const { isSeedStatus = false } = options;
|
|
300
|
+
const morphologicalExploration = conceptualAnchor.morphologicalExploration;
|
|
301
|
+
if (!morphologicalExploration || typeof morphologicalExploration !== 'object') {
|
|
302
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration must exist (Section 4 of research-design.md).');
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (morphologicalExploration.mode !== 'agent-must-complete-before-ui-implementation') {
|
|
306
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.mode must equal "agent-must-complete-before-ui-implementation".');
|
|
307
|
+
}
|
|
308
|
+
if (morphologicalExploration.blockingByDefault !== true) {
|
|
309
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.blockingByDefault must equal true.');
|
|
310
|
+
}
|
|
311
|
+
if (morphologicalExploration.researchBriefSection !== 'Section 4') {
|
|
312
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.researchBriefSection must equal "Section 4".');
|
|
313
|
+
}
|
|
314
|
+
if (morphologicalExploration.requiredMatrixShape !== '5x5-or-6x5') {
|
|
315
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.requiredMatrixShape must equal "5x5-or-6x5".');
|
|
316
|
+
}
|
|
317
|
+
if (morphologicalExploration.minimumDimensions !== 5) {
|
|
318
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.minimumDimensions must equal 5.');
|
|
319
|
+
}
|
|
320
|
+
if (morphologicalExploration.maximumDimensions !== 6) {
|
|
321
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.maximumDimensions must equal 6.');
|
|
322
|
+
}
|
|
323
|
+
if (morphologicalExploration.valuesPerDimension !== 5) {
|
|
324
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.valuesPerDimension must equal 5.');
|
|
325
|
+
}
|
|
326
|
+
if (morphologicalExploration.forbidCategoryCodeValuesInMatrix !== true) {
|
|
327
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.forbidCategoryCodeValuesInMatrix must equal true so Section 3 cliches do not re-enter the matrix as values.');
|
|
328
|
+
}
|
|
329
|
+
if (morphologicalExploration.requireSelectedCombination !== true) {
|
|
330
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.requireSelectedCombination must equal true.');
|
|
331
|
+
}
|
|
332
|
+
if (morphologicalExploration.requireUncomfortableCombination !== true) {
|
|
333
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.requireUncomfortableCombination must equal true.');
|
|
334
|
+
}
|
|
335
|
+
if (!hasNonEmptyString(morphologicalExploration.uncomfortableCombinationRule)) {
|
|
336
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.uncomfortableCombinationRule must be a non-empty string explaining why the uncomfortable combination is required.');
|
|
337
|
+
}
|
|
338
|
+
if (
|
|
339
|
+
!Array.isArray(morphologicalExploration.uncomfortableCombinationRequiredFields)
|
|
340
|
+
|| !morphologicalExploration.uncomfortableCombinationRequiredFields.includes('combinationLabel')
|
|
341
|
+
|| !morphologicalExploration.uncomfortableCombinationRequiredFields.includes('discomfortReason')
|
|
342
|
+
|| !morphologicalExploration.uncomfortableCombinationRequiredFields.includes('productLogicJustification')
|
|
343
|
+
) {
|
|
344
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.uncomfortableCombinationRequiredFields must include combinationLabel, discomfortReason, and productLogicJustification.');
|
|
345
|
+
}
|
|
346
|
+
if (morphologicalExploration.widenDimensionsWhenUncomfortableCombinationCannotBeProduced !== true) {
|
|
347
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.widenDimensionsWhenUncomfortableCombinationCannotBeProduced must equal true.');
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (morphologicalExploration.uncomfortableCombination !== null && typeof morphologicalExploration.uncomfortableCombination === 'object') {
|
|
351
|
+
const uncomfortableCombination = morphologicalExploration.uncomfortableCombination;
|
|
352
|
+
if (!hasNonEmptyString(uncomfortableCombination.combinationLabel)) {
|
|
353
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.uncomfortableCombination.combinationLabel must be a non-empty string.');
|
|
354
|
+
}
|
|
355
|
+
if (!hasNonEmptyString(uncomfortableCombination.discomfortReason)) {
|
|
356
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.uncomfortableCombination.discomfortReason must be a non-empty string.');
|
|
357
|
+
}
|
|
358
|
+
if (!hasNonEmptyString(uncomfortableCombination.productLogicJustification)) {
|
|
359
|
+
validationErrors.push('designIntent.conceptualAnchor.morphologicalExploration.uncomfortableCombination.productLogicJustification must be a non-empty string.');
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function validateAnchorCandidates(conceptualAnchor, validationErrors, options = {}) {
|
|
365
|
+
const { isSeedStatus = false } = options;
|
|
366
|
+
const anchorCandidates = conceptualAnchor.anchorCandidates;
|
|
367
|
+
if (!anchorCandidates || typeof anchorCandidates !== 'object') {
|
|
368
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates must exist (Section 5 of research-design.md).');
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (anchorCandidates.mode !== 'agent-must-complete-before-ui-implementation') {
|
|
372
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.mode must equal "agent-must-complete-before-ui-implementation".');
|
|
373
|
+
}
|
|
374
|
+
if (anchorCandidates.blockingByDefault !== true) {
|
|
375
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.blockingByDefault must equal true.');
|
|
376
|
+
}
|
|
377
|
+
if (anchorCandidates.researchBriefSection !== 'Section 5') {
|
|
378
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.researchBriefSection must equal "Section 5".');
|
|
379
|
+
}
|
|
380
|
+
if (anchorCandidates.requiredCandidateCount !== 5) {
|
|
381
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.requiredCandidateCount must equal 5.');
|
|
382
|
+
}
|
|
383
|
+
if (
|
|
384
|
+
!Array.isArray(anchorCandidates.requiredFieldsPerCandidate)
|
|
385
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('anchorReference')
|
|
386
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('jobFit')
|
|
387
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('hierarchyImplication')
|
|
388
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('densityImplication')
|
|
389
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('typeImplication')
|
|
390
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('stateLanguage')
|
|
391
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('motionImplication')
|
|
392
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('whatItRulesOut')
|
|
393
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('renameTest')
|
|
394
|
+
|| !anchorCandidates.requiredFieldsPerCandidate.includes('categoryCodeOverlap')
|
|
395
|
+
) {
|
|
396
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.requiredFieldsPerCandidate must list the full anchor dossier fields including renameTest and categoryCodeOverlap.');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const renameTest = anchorCandidates.renameTest;
|
|
400
|
+
if (!renameTest || typeof renameTest !== 'object') {
|
|
401
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest must exist.');
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (renameTest.mode !== 'strengthened') {
|
|
405
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.mode must equal "strengthened".');
|
|
406
|
+
}
|
|
407
|
+
if (renameTest.requiredTestCategoryCount !== 3) {
|
|
408
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.requiredTestCategoryCount must equal 3.');
|
|
409
|
+
}
|
|
410
|
+
if (!hasNonEmptyString(renameTest.testCategoryDistanceRule)) {
|
|
411
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.testCategoryDistanceRule must be a non-empty string requiring remote categories.');
|
|
412
|
+
}
|
|
413
|
+
if (!hasNonEmptyString(renameTest.testCategoryFreshnessRule)) {
|
|
414
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.testCategoryFreshnessRule must be a non-empty string preventing reuse of the same test triple across every anchor.');
|
|
415
|
+
}
|
|
416
|
+
if (!renameTest.verdictScoring || typeof renameTest.verdictScoring !== 'object') {
|
|
417
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.verdictScoring must define STRONG PASS, PASS, REVISE, and DISCARD verdicts.');
|
|
418
|
+
} else {
|
|
419
|
+
for (const verdict of ['STRONG PASS', 'PASS', 'REVISE', 'DISCARD']) {
|
|
420
|
+
if (!hasNonEmptyString(renameTest.verdictScoring[verdict])) {
|
|
421
|
+
validationErrors.push(`designIntent.conceptualAnchor.anchorCandidates.renameTest.verdictScoring["${verdict}"] must be a non-empty string.`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (renameTest.requireTestCategoriesRecordedInDossier !== true) {
|
|
426
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.requireTestCategoriesRecordedInDossier must equal true.');
|
|
427
|
+
}
|
|
428
|
+
if (renameTest.auditableByHumanReviewer !== true) {
|
|
429
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.auditableByHumanReviewer must equal true.');
|
|
430
|
+
}
|
|
431
|
+
if (
|
|
432
|
+
!Array.isArray(renameTest.renameTestRequiredFields)
|
|
433
|
+
|| !renameTest.renameTestRequiredFields.includes('testCategories')
|
|
434
|
+
|| !renameTest.renameTestRequiredFields.includes('results')
|
|
435
|
+
|| !renameTest.renameTestRequiredFields.includes('verdict')
|
|
436
|
+
) {
|
|
437
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.renameTest.renameTestRequiredFields must include testCategories, results, and verdict.');
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (!hasNonEmptyString(anchorCandidates.categoryCodeOverlapRule)) {
|
|
441
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.categoryCodeOverlapRule must be a non-empty string.');
|
|
442
|
+
}
|
|
443
|
+
if (!Array.isArray(anchorCandidates.forbiddenSelectedVerdicts) || !anchorCandidates.forbiddenSelectedVerdicts.includes('DISCARD')) {
|
|
444
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.forbiddenSelectedVerdicts must include "DISCARD".');
|
|
445
|
+
}
|
|
446
|
+
if (
|
|
447
|
+
!Array.isArray(anchorCandidates.requiredSelectedVerdicts)
|
|
448
|
+
|| !anchorCandidates.requiredSelectedVerdicts.includes('STRONG PASS')
|
|
449
|
+
|| !anchorCandidates.requiredSelectedVerdicts.includes('PASS')
|
|
450
|
+
) {
|
|
451
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.requiredSelectedVerdicts must include "STRONG PASS" and "PASS".');
|
|
452
|
+
}
|
|
453
|
+
if (!Array.isArray(anchorCandidates.candidates)) {
|
|
454
|
+
validationErrors.push('designIntent.conceptualAnchor.anchorCandidates.candidates must be an array (empty in the seed; populated by the agent).');
|
|
455
|
+
}
|
|
456
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Research dossier validators. Enforces the `researchDossier.metadata` block
|
|
3
|
+
* shape that powers the freshness gate, status-aware skip, and anti-repeat
|
|
4
|
+
* ledger consumed by `.agent-context/prompts/research-design.md`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { hasNonEmptyString } from './helpers.mjs';
|
|
8
|
+
|
|
9
|
+
const REQUIRED_FRESHNESS_WINDOW_DAYS = 90;
|
|
10
|
+
const REQUIRED_SEED_STATUSES = [
|
|
11
|
+
'seed-needs-design-synthesis',
|
|
12
|
+
'seed-generated-during-init',
|
|
13
|
+
'seed-generated-during-upgrade',
|
|
14
|
+
];
|
|
15
|
+
const ANTI_REPEAT_LEDGER_CATEGORIES = ['previousAnchors', 'previousPalettes', 'previousMotionSignatures'];
|
|
16
|
+
|
|
17
|
+
function validateMetadataBaseShape(metadata, validationErrors) {
|
|
18
|
+
if (
|
|
19
|
+
metadata.researchVerifiedAt !== null
|
|
20
|
+
&& (typeof metadata.researchVerifiedAt !== 'string' || metadata.researchVerifiedAt.trim().length === 0)
|
|
21
|
+
) {
|
|
22
|
+
validationErrors.push('designIntent.researchDossier.metadata.researchVerifiedAt must be null or an ISO date string.');
|
|
23
|
+
}
|
|
24
|
+
if (!Number.isInteger(metadata.freshnessWindowDays) || metadata.freshnessWindowDays < 1) {
|
|
25
|
+
validationErrors.push(`designIntent.researchDossier.metadata.freshnessWindowDays must be a positive integer (recommended ${REQUIRED_FRESHNESS_WINDOW_DAYS}).`);
|
|
26
|
+
}
|
|
27
|
+
if (!hasNonEmptyString(metadata.freshnessRule)) {
|
|
28
|
+
validationErrors.push('designIntent.researchDossier.metadata.freshnessRule must be a non-empty string explaining staleness.');
|
|
29
|
+
}
|
|
30
|
+
if (metadata.userExplicitRedesignBypassesFreshness !== true) {
|
|
31
|
+
validationErrors.push('designIntent.researchDossier.metadata.userExplicitRedesignBypassesFreshness must equal true so explicit redesign requests force fresh research.');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function validateStatusAwareValidation(metadata, validationErrors) {
|
|
36
|
+
const statusAwareValidation = metadata.statusAwareValidation;
|
|
37
|
+
if (!statusAwareValidation || typeof statusAwareValidation !== 'object') {
|
|
38
|
+
validationErrors.push('designIntent.researchDossier.metadata.statusAwareValidation must exist.');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const seedStatuses = statusAwareValidation.seedStatuses;
|
|
42
|
+
const hasAllRequiredSeedStatuses = Array.isArray(seedStatuses)
|
|
43
|
+
&& REQUIRED_SEED_STATUSES.every((requiredSeedStatusName) => seedStatuses.includes(requiredSeedStatusName));
|
|
44
|
+
if (!hasAllRequiredSeedStatuses) {
|
|
45
|
+
validationErrors.push('designIntent.researchDossier.metadata.statusAwareValidation.seedStatuses must include seed-needs-design-synthesis, seed-generated-during-init, and seed-generated-during-upgrade.');
|
|
46
|
+
}
|
|
47
|
+
if (statusAwareValidation.seedSkipsDossierShape !== true) {
|
|
48
|
+
validationErrors.push('designIntent.researchDossier.metadata.statusAwareValidation.seedSkipsDossierShape must equal true.');
|
|
49
|
+
}
|
|
50
|
+
if (statusAwareValidation.activeRequiresFreshOrExplicitRedesign !== true) {
|
|
51
|
+
validationErrors.push('designIntent.researchDossier.metadata.statusAwareValidation.activeRequiresFreshOrExplicitRedesign must equal true.');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function validateAntiRepeatLedger(metadata, validationErrors) {
|
|
56
|
+
const antiRepeatLedger = metadata.antiRepeatLedger;
|
|
57
|
+
if (!antiRepeatLedger || typeof antiRepeatLedger !== 'object') {
|
|
58
|
+
validationErrors.push('designIntent.researchDossier.metadata.antiRepeatLedger must exist.');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (antiRepeatLedger.blocklistFromHistory !== true) {
|
|
62
|
+
validationErrors.push('designIntent.researchDossier.metadata.antiRepeatLedger.blocklistFromHistory must equal true so previously shipped direction is blocked from repetition.');
|
|
63
|
+
}
|
|
64
|
+
if (antiRepeatLedger.ledgerScope !== 'signature-level-descriptors-only') {
|
|
65
|
+
validationErrors.push('designIntent.researchDossier.metadata.antiRepeatLedger.ledgerScope must equal "signature-level-descriptors-only" so the ledger does not become a raw token dump.');
|
|
66
|
+
}
|
|
67
|
+
if (
|
|
68
|
+
!Number.isInteger(antiRepeatLedger.ledgerMaxEntriesPerCategory)
|
|
69
|
+
|| antiRepeatLedger.ledgerMaxEntriesPerCategory < 1
|
|
70
|
+
|| antiRepeatLedger.ledgerMaxEntriesPerCategory > 5
|
|
71
|
+
) {
|
|
72
|
+
validationErrors.push('designIntent.researchDossier.metadata.antiRepeatLedger.ledgerMaxEntriesPerCategory must be an integer between 1 and 5 to keep the ledger signature-level.');
|
|
73
|
+
}
|
|
74
|
+
for (const ledgerCategoryName of ANTI_REPEAT_LEDGER_CATEGORIES) {
|
|
75
|
+
const ledgerEntries = antiRepeatLedger[ledgerCategoryName];
|
|
76
|
+
if (!Array.isArray(ledgerEntries)) {
|
|
77
|
+
validationErrors.push(`designIntent.researchDossier.metadata.antiRepeatLedger.${ledgerCategoryName} must be an array (may be empty in fresh seeds).`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (
|
|
81
|
+
Number.isInteger(antiRepeatLedger.ledgerMaxEntriesPerCategory)
|
|
82
|
+
&& ledgerEntries.length > antiRepeatLedger.ledgerMaxEntriesPerCategory
|
|
83
|
+
) {
|
|
84
|
+
validationErrors.push(`designIntent.researchDossier.metadata.antiRepeatLedger.${ledgerCategoryName} exceeds ledgerMaxEntriesPerCategory; trim to signature-level descriptors.`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function validateResearchDossier(designIntentContract, validationErrors) {
|
|
90
|
+
const researchDossier = designIntentContract?.researchDossier;
|
|
91
|
+
if (!researchDossier || typeof researchDossier !== 'object') {
|
|
92
|
+
validationErrors.push('designIntent.researchDossier must exist (Section 3-5 metadata block from research-design.md).');
|
|
93
|
+
return validationErrors;
|
|
94
|
+
}
|
|
95
|
+
const metadata = researchDossier.metadata;
|
|
96
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
97
|
+
validationErrors.push('designIntent.researchDossier.metadata must exist with researchVerifiedAt, freshnessWindowDays, and antiRepeatLedger.');
|
|
98
|
+
return validationErrors;
|
|
99
|
+
}
|
|
100
|
+
validateMetadataBaseShape(metadata, validationErrors);
|
|
101
|
+
validateStatusAwareValidation(metadata, validationErrors);
|
|
102
|
+
validateAntiRepeatLedger(metadata, validationErrors);
|
|
103
|
+
return validationErrors;
|
|
104
|
+
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
validateConceptualAnchor,
|
|
19
19
|
validateMathSystems,
|
|
20
20
|
} from './validation/anchor-validators.mjs';
|
|
21
|
+
import { validateResearchDossier } from './validation/research-dossier-validators.mjs';
|
|
21
22
|
import {
|
|
22
23
|
validateAiSafeUiAudit,
|
|
23
24
|
validateProductionContentPolicy,
|
|
@@ -54,6 +55,7 @@ export function validateDesignIntentContract(designIntentContract) {
|
|
|
54
55
|
validateDesignFlexibilityPolicy(designIntentContract, validationErrors);
|
|
55
56
|
validateConceptualAnchor(designIntentContract, validationErrors);
|
|
56
57
|
validateMathSystems(designIntentContract, validationErrors);
|
|
58
|
+
validateResearchDossier(designIntentContract, validationErrors);
|
|
57
59
|
validateAiSafeUiAudit(designIntentContract, validationErrors);
|
|
58
60
|
validateProductionContentPolicy(designIntentContract, validationErrors);
|
|
59
61
|
validateTokenSystem(designIntentContract, validationErrors);
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { buildStructureFirstSeedSignals, shouldBootstrapDesignDocument } from './design-contract/seed-signals.mjs';
|
|
23
23
|
import { FORBIDDEN_PATTERN_SIGNALS } from './design-contract/signal-vocab.mjs';
|
|
24
24
|
import { buildConceptualAnchorSection } from './design-contract/sections/conceptual-anchor.mjs';
|
|
25
|
+
import { buildResearchDossierMetadata } from './design-contract/research-dossier-migration.mjs';
|
|
25
26
|
import {
|
|
26
27
|
buildAccessibilityPolicySection,
|
|
27
28
|
buildAiSafeUiAuditSection,
|
|
@@ -124,6 +125,9 @@ function buildDesignIntentContractObject({
|
|
|
124
125
|
externalInspirationPolicy: 'External websites and examples are candidate evidence for constraints, mechanics, and quality bars; do not copy their layout rhythm, palette, component skin, brand posture, or visual metaphor.',
|
|
125
126
|
},
|
|
126
127
|
conceptualAnchor: buildConceptualAnchorSection(),
|
|
128
|
+
researchDossier: {
|
|
129
|
+
metadata: buildResearchDossierMetadata(),
|
|
130
|
+
},
|
|
127
131
|
derivedTokenLogic: {
|
|
128
132
|
anchorReference: 'agent-defined-anchor-reference',
|
|
129
133
|
colorDerivationSource: 'Explain semantic color roles from anchorReference; reject generic palettes without anchor evidence.',
|
|
@@ -274,6 +278,7 @@ function buildDesignIntentContractObject({
|
|
|
274
278
|
bootstrapPrompt: '.agent-context/prompts/bootstrap-design.md',
|
|
275
279
|
autoLoadedRuleFiles: [
|
|
276
280
|
'.agent-context/prompts/bootstrap-design.md',
|
|
281
|
+
'.agent-context/prompts/research-design.md',
|
|
277
282
|
'.agent-context/rules/frontend-architecture.md',
|
|
278
283
|
],
|
|
279
284
|
disallowedAutoLoadedRuleFiles: [
|
|
@@ -190,6 +190,7 @@ export function buildDesignBootstrapPrompt({
|
|
|
190
190
|
'29. repoEvidence when onboarding or detector evidence exists',
|
|
191
191
|
'',
|
|
192
192
|
'## Mechanical Gates',
|
|
193
|
+
'0. Read `.agent-context/prompts/research-design.md` first. Sections 3 (Category Code Identification), 4 (Morphological Exploration), and 5 (Anchor Candidates) are gates: each must produce an auditable artifact before UI implementation.',
|
|
193
194
|
'1. Do not copy external style guides.',
|
|
194
195
|
'2. Do not anchor the final design language to famous products, benchmark visuals, or external reference surfaces.',
|
|
195
196
|
'3. Do not choose final style, library, palette, typography, motion, or layout from this offline scaffold.',
|
|
@@ -238,6 +239,13 @@ export function buildDesignBootstrapPrompt({
|
|
|
238
239
|
'40. Translate conceptual anchors non-literally first. Do not turn anchor artifacts into required chrome, decorative props, wallpaper, or theme objects unless they serve a named product function.',
|
|
239
240
|
'41. Use external websites and benchmark examples as candidate evidence for constraints, mechanics, and quality bars only; do not copy layout rhythm, palette, component skin, visual metaphor, or brand posture.',
|
|
240
241
|
'',
|
|
242
|
+
'## Research-Design Brief Gates (research-design.md)',
|
|
243
|
+
'42. Section 3 — Category Code Identification: list at least three category codes per product surface in `conceptualAnchor.categoryCodes.candidateEntries`. Each entry must record `description`, `specificityEvidence`, `categoryDefaultReason`, and `rejectionNote`. The specificity floor is: a reader unfamiliar with the project must be able to visualize a specific aesthetic direction from the text alone, without seeing the UI and without knowing the product name. Reject placeholder phrases like "clean typography", "modern color palette", or "smooth animations". Anti-leakage rule: listing a cliche identifies a trap, not a target; the example cliches in the brief are not aesthetic candidates for any project. If the project pattern-matches a common AI-safe default (dev-tool dark slate, AI-startup purple-pink gradient, health-app mint, SaaS admin three-card KPI, marketing-site three-tile hero), name that cliche in `candidateEntries` with an explicit rejection note.',
|
|
244
|
+
'43. Section 4 — Morphological Exploration: build a 5x5 or 6x5 matrix in `conceptualAnchor.morphologicalExploration`. Choose 5 or 6 dimensions and 5 values per dimension. Do not seed the matrix with the Section 3 category codes as values. Highlight the selected combination, and ALSO highlight at least one uncomfortable combination that feels instinctively wrong but can be argued with product logic. The uncomfortable combination must record `combinationLabel`, `discomfortReason`, and `productLogicJustification`. If you cannot produce an uncomfortable-but-arguable combination, the dimensions are too narrow; widen at least one and regenerate.',
|
|
245
|
+
'44. Section 5 — Anchor Candidates: produce exactly five entries in `conceptualAnchor.anchorCandidates.candidates`. Each candidate must record `anchorReference`, `conceptualFamily`, `jobFit`, `hierarchyImplication`, `densityImplication`, `typeImplication`, `stateLanguage`, `motionImplication`, `whatItRulesOut`, `renameTest`, and `categoryCodeOverlap`.',
|
|
246
|
+
'45. Strengthened rename test: rename the product to three genuinely different categories that are remote from each other and from the actual product. Pick fresh test categories per anchor; do not reuse the same triple across every anchor. Record the three test categories explicitly in the dossier so a human reviewer can audit them. Score the result: 0/3 coherent equals STRONG PASS, 1/3 equals PASS, 2/3 equals REVISE, 3/3 equals DISCARD. Never select an anchor with verdict DISCARD; revise REVISE candidates until they fail in at least 2 of 3 categories.',
|
|
247
|
+
'46. Make `conceptualAnchor.anchorReference` and `derivedTokenLogic.anchorReference` match the selected anchor exactly. The selected anchor must have verdict STRONG PASS or PASS.',
|
|
248
|
+
'',
|
|
241
249
|
'## Creative Ambition Floor',
|
|
242
250
|
'Before implementation, the design contract must name one authored visual bet, one product-derived palette move, one signature motion/spatial/interaction behavior, and one morphology or composition choice that would not appear in a generic AI template.',
|
|
243
251
|
'The ambition floor is not a fixed aesthetic. Quiet, dense, utilitarian, or text-heavy interfaces are allowed when the product requires them, but they still need a project-specific visual decision and a real reason for omitting richer motion, 3D, canvas, WebGL, scroll choreography, or animation libraries.',
|
|
@@ -290,6 +298,7 @@ export function buildDesignBootstrapPrompt({
|
|
|
290
298
|
'13. Preserve externalResearchIntake so user-provided research becomes reviewed evidence without turning into an offline style or dependency preset.',
|
|
291
299
|
'14. Preserve conceptualAnchor so prompt-only UI work has one cohesive non-template concept instead of a mixed collection of bold but unrelated visual decisions.',
|
|
292
300
|
'15. Record conceptualAnchor.agentResearchMode, specificReferencePoint, signatureMotion, typographicDecision, visualRiskBudget, motionRiskBudget, and cohesionChecks so the final UI cannot quietly fall back to a timid dashboard/admin mental model.',
|
|
301
|
+
'15a. Record conceptualAnchor.categoryCodes.candidateEntries (Section 3 of research-design.md), conceptualAnchor.morphologicalExploration with selectedCombination and uncomfortableCombination (Section 4), and conceptualAnchor.anchorCandidates.candidates with full renameTest results (Section 5) before UI implementation.',
|
|
293
302
|
'16. Preserve derivedTokenLogic, libraryResearchStatus, and libraryDecisions so token choices and dependency uncertainty stay visible before implementation.',
|
|
294
303
|
'16a. Preserve designFlexibilityPolicy so the machine contract guides consistency without freezing literal anchor artifacts, exact token primitives, or component-kit visual language.',
|
|
295
304
|
'17. Preserve productionContentPolicy so UI output is ship-ready and not a testing-looking scaffold.',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryuenn3123/agentic-senior-core",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Force your AI Agent to code like a Staff Engineer, not a Junior.",
|
|
6
6
|
"bin": {
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"report:governance-weekly": "node ./scripts/governance-weekly-report.mjs",
|
|
84
84
|
"clean:local": "node ./scripts/clean-local-artifacts.mjs",
|
|
85
85
|
"validate": "node ./scripts/validate.mjs",
|
|
86
|
-
"test": "node --test ./tests/cli-smoke.test.mjs ./tests/mcp-server.test.mjs ./tests/llm-judge.test.mjs ./tests/ui-rubric-calibration.test.mjs ./tests/operations.test.mjs ./tests/knowledge-injection.test.mjs ./tests/migrate-rule-format.test.mjs ./tests/audit-caching-scope-hygiene.test.mjs ./benchmarks/token-usage/lib/token-counter.test.mjs ./benchmarks/token-usage/lib/provider-cache-matrix.test.mjs ./benchmarks/token-usage/lib/cache-layer-contract.test.mjs ./benchmarks/token-usage/lib/cache-economics.test.mjs"
|
|
86
|
+
"test": "node --test ./tests/cli-smoke.test.mjs ./tests/mcp-server.test.mjs ./tests/llm-judge.test.mjs ./tests/ui-rubric-calibration.test.mjs ./tests/operations.test.mjs ./tests/knowledge-injection.test.mjs ./tests/migrate-rule-format.test.mjs ./tests/audit-caching-scope-hygiene.test.mjs ./tests/research-dossier-migration.test.mjs ./benchmarks/token-usage/lib/token-counter.test.mjs ./benchmarks/token-usage/lib/provider-cache-matrix.test.mjs ./benchmarks/token-usage/lib/cache-layer-contract.test.mjs ./benchmarks/token-usage/lib/cache-economics.test.mjs"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
89
|
"@anthropic-ai/sdk": "^0.96.0",
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Phase 5 drift catcher. Scans user-facing surfaces (README, AGENTS.md, FAQ,
|
|
8
8
|
* integration playbook, CHANGELOG) for caching numerical claims and verifies
|
|
9
|
-
* that each claim is integration-scoped per `docs/
|
|
9
|
+
* that each claim is integration-scoped per `docs/architecture/decisions-foundation.md`
|
|
10
10
|
* D4 "Per-Tool Caching Scope Matrix".
|
|
11
11
|
*
|
|
12
12
|
* The rule: never publish a single universal "X% caching saving" figure that
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
* (direct API, Claude Code SDK programmatic, Cursor, Windsurf, Codex CLI,
|
|
17
17
|
* Kiro, IDE wrapper) within +/- 600 characters of the figure, OR
|
|
18
18
|
* 2. live in a documented exempt context (Phase 1 aggregate-cap CHANGELOG
|
|
19
|
-
* rationale, plan files under docs/
|
|
20
|
-
* benchmarks/results/, the canonical D4 matrix itself).
|
|
19
|
+
* rationale, archived plan files under docs/archive/, benchmark JSON
|
|
20
|
+
* under benchmarks/results/, the canonical D4 matrix itself).
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -200,7 +200,7 @@ export function runCachingScopeHygieneAudit(options = {}) {
|
|
|
200
200
|
file: surfacePath,
|
|
201
201
|
line: lineNumber,
|
|
202
202
|
kind: 'caching-claim.missing-integration-scope',
|
|
203
|
-
detail: `Caching saving claim "${claim.matchedText.trim()}" lacks an integration-mode marker within +/- ${CONTEXT_WINDOW_RADIUS} chars. Add a per-tool / direct-API / IDE-wrapper label, or move the figure under a clearly-scoped paragraph. Source of truth: docs/
|
|
203
|
+
detail: `Caching saving claim "${claim.matchedText.trim()}" lacks an integration-mode marker within +/- ${CONTEXT_WINDOW_RADIUS} chars. Add a per-tool / direct-API / IDE-wrapper label, or move the figure under a clearly-scoped paragraph. Source of truth: docs/architecture/decisions-foundation.md D4.`,
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
206
|
}
|
|
@@ -31,7 +31,7 @@ const SOURCE_ARTIFACTS = [
|
|
|
31
31
|
artifactId: 'phase-2-cache',
|
|
32
32
|
relativePath: 'benchmarks/results/cache-phase-2-2026-05-16.json',
|
|
33
33
|
role: 'cache-simulation',
|
|
34
|
-
description: 'Phase 2 offline warm-cache simulation. Direct provider API integration mode; see docs/
|
|
34
|
+
description: 'Phase 2 offline warm-cache simulation. Direct provider API integration mode; see docs/architecture/decisions-foundation.md D4 for the per-tool scope matrix.',
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
artifactId: 'phase-3-anti-halu',
|
|
@@ -145,12 +145,12 @@ export function buildReleaseBenchmarkBundle(options = {}) {
|
|
|
145
145
|
generated_at: new Date().toISOString(),
|
|
146
146
|
description: 'Phase 5 release benchmark bundle. References Phase 0-3 locked artifacts plus the Phase 5 supply-chain snapshot. No artifact is regenerated by this bundle. Artifact integrity is verified by SHA-256 hash via scripts/audit-release-bundle.mjs.',
|
|
147
147
|
sources: {
|
|
148
|
-
research_foundation: 'docs/
|
|
149
|
-
d4_caching_scope_matrix: 'docs/
|
|
148
|
+
research_foundation: 'docs/architecture/decisions-foundation.md',
|
|
149
|
+
d4_caching_scope_matrix: 'docs/architecture/decisions-foundation.md#d4',
|
|
150
150
|
caching_reporting_format: 'docs/benchmark-reference.md',
|
|
151
|
-
phase_2_outcome: 'docs/
|
|
152
|
-
phase_3_outcome: 'docs/
|
|
153
|
-
phase_5_plan: 'docs/
|
|
151
|
+
phase_2_outcome: 'docs/archive/phase-2-outcome.md',
|
|
152
|
+
phase_3_outcome: 'docs/archive/phase-3-outcome.md',
|
|
153
|
+
phase_5_plan: 'docs/archive/phase-5-hardening.md',
|
|
154
154
|
},
|
|
155
155
|
integrity: {
|
|
156
156
|
hash_algorithm: 'SHA-256',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Locked ID prefix table per `docs/
|
|
4
|
+
* Locked ID prefix table per `docs/architecture/format-spec.md` section 3.
|
|
5
5
|
* The migration helper reads this map to assign frontmatter and section IDs.
|
|
6
6
|
* Lock new entries here when adding a new rule file; never invent prefixes inline.
|
|
7
7
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Renders a parsed legacy rule file plus a prefix-table entry into the v4
|
|
5
|
-
* canonical format defined in `docs/
|
|
5
|
+
* canonical format defined in `docs/architecture/format-spec.md`.
|
|
6
6
|
*
|
|
7
7
|
* Section IDs auto-assign sequentially starting at 001. The renderer never
|
|
8
8
|
* skips integers; humans introduce gaps manually during review by editing
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* scripts/migrate-rule-format.mjs
|
|
6
6
|
*
|
|
7
7
|
* Phase 1 Task 1.2 migration helper. Converts a legacy v3 rule file into the
|
|
8
|
-
* canonical v4 format defined in docs/
|
|
8
|
+
* canonical v4 format defined in docs/architecture/format-spec.md. Output is written
|
|
9
9
|
* to a `.candidate.md` sibling so the human migrator can review the diff
|
|
10
10
|
* before replacing the original.
|
|
11
11
|
*
|
|
@@ -301,6 +301,12 @@ export const REQUIRED_UI_DESIGN_AUTOMATION_SNIPPETS = [
|
|
|
301
301
|
'Motion/Palette Decision',
|
|
302
302
|
'product categories are heuristics',
|
|
303
303
|
'perform live web research',
|
|
304
|
+
'research-design.md',
|
|
305
|
+
'researchDossier.metadata',
|
|
306
|
+
'researchVerifiedAt',
|
|
307
|
+
'freshnessWindowDays',
|
|
308
|
+
'Anti-repeat ledger',
|
|
309
|
+
'user-explicit redesign',
|
|
304
310
|
],
|
|
305
311
|
},
|
|
306
312
|
{
|
|
@@ -352,6 +358,24 @@ export const REQUIRED_UI_DESIGN_AUTOMATION_SNIPPETS = [
|
|
|
352
358
|
'A new dependency, package count, or vague performance concern is not a blocker by itself.',
|
|
353
359
|
'default component-kit styling without product rationale',
|
|
354
360
|
'genericity findings that cannot name the exact drift signal',
|
|
361
|
+
'research-design.md',
|
|
362
|
+
'Section 3-5 gates from `research-design.md`',
|
|
363
|
+
],
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
path: '.agent-context/prompts/research-design.md',
|
|
367
|
+
snippets: [
|
|
368
|
+
'# Research-Design Brief',
|
|
369
|
+
'## Section 3 — Category Code Identification',
|
|
370
|
+
'## Section 4 — Morphological Exploration',
|
|
371
|
+
'## Section 5 — Anchor Candidates',
|
|
372
|
+
'specificity self-test',
|
|
373
|
+
'uncomfortable combination',
|
|
374
|
+
'STRONG PASS',
|
|
375
|
+
'DISCARD',
|
|
376
|
+
'Anti-Repeat Ledger Gate',
|
|
377
|
+
'previousAnchors',
|
|
378
|
+
'previousMotionSignatures',
|
|
355
379
|
],
|
|
356
380
|
},
|
|
357
381
|
{
|
package/scripts/validate.mjs
CHANGED
|
@@ -158,6 +158,7 @@ async function validateRequiredFiles() {
|
|
|
158
158
|
'scripts/release-gate.mjs',
|
|
159
159
|
'scripts/generate-sbom.mjs',
|
|
160
160
|
'.agent-context/policies/llm-judge-threshold.json',
|
|
161
|
+
'.agent-context/prompts/research-design.md',
|
|
161
162
|
'mcp.json',
|
|
162
163
|
'AGENTS.md',
|
|
163
164
|
'CLAUDE.md',
|