@ai.all/ds7 0.0.0-stage → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/01-architecture/ATOMIC-DESIGN.md +68 -0
- package/01-architecture/COMPONENT-ANATOMY.md +101 -0
- package/01-architecture/MODIFIER-SYSTEM.md +90 -0
- package/02-design-system/BRANDBOOK-SANDBOX.md +52 -0
- package/02-design-system/NAMING-CONVENTIONS.md +45 -0
- package/02-design-system/TOKENS-SPEC.md +102 -0
- package/03-performance-web-vitals/ASSETS-PIPELINE.md +62 -0
- package/03-performance-web-vitals/CRITICAL-CSS.md +39 -0
- package/03-performance-web-vitals/FONTS-ZERO-CLS.md +59 -0
- package/03-performance-web-vitals/HYDRATION-STRATEGY.md +68 -0
- package/04-seo-ai-optimization/AEO-GEO-AI-SEARCH.md +53 -0
- package/04-seo-ai-optimization/GOOGLE-LOCAL-EEAT.md +36 -0
- package/04-seo-ai-optimization/STRUCTURED-DATA.md +95 -0
- package/05-internationalization/I18N-ARCHITECTURE.md +59 -0
- package/05-internationalization/LOCALE-TEMPLATE.json +33 -0
- package/06-business-conversion/B2C-B2B-MODELS.md +37 -0
- package/06-business-conversion/CONTEXTUAL-LEADS.md +45 -0
- package/06-business-conversion/EV-SOLAR-ECOSYSTEM.md +38 -0
- package/07-testing/TESTING-STRATEGY.md +95 -0
- package/08-token-optimization/TOKEN-STRATEGY.md +100 -0
- package/ALL.md +232 -0
- package/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +118 -2
- package/SPEC.md +102 -0
- package/bin/ds7.js +224 -0
- package/package.json +61 -4
- package/templates/component.vue +116 -0
- package/templates/nuxt.config.ts +64 -0
- package/templates/theme.css +55 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# 04 · AEO and GEO: writing for AI search engines
|
|
2
|
+
|
|
3
|
+
Answer Engine Optimization (AEO) and Generative Engine Optimization (GEO) make a site easy to quote in AI answers: ChatGPT search, Perplexity, Gemini overviews and similar systems.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. What changed
|
|
8
|
+
|
|
9
|
+
Classic search matched words. AI search extracts **claims and facts** that answer a question, then cites the source. Pages that state a question, answer it in a few precise sentences, and back it with structured data are quoted more often.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 2. Direct answer block
|
|
14
|
+
|
|
15
|
+
Each informational section begins with its question as an `h2` and a short factual answer of 40–50 words:
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<h2 id="cost-4kw">How much does a 4 kW home solar system cost?</h2>
|
|
19
|
+
|
|
20
|
+
<p class="direct-answer">
|
|
21
|
+
A standard 4 kW system costs <strong>[range from your price list]</strong> before grants.
|
|
22
|
+
Replace the placeholder with a figure you can source, and state the date and the region.
|
|
23
|
+
</p>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Rules for the answer:
|
|
27
|
+
- No introduction ("Great question…"). The first words answer the question.
|
|
28
|
+
- Numbers come with units, a date and a region.
|
|
29
|
+
- Every figure has a source in the page or in linked official documents.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 3. Tables for comparisons
|
|
34
|
+
|
|
35
|
+
Assistants parse tables faster than prose. Use real `<table>` markup with `<thead>`:
|
|
36
|
+
|
|
37
|
+
| Model | Power | Efficiency | Warranty |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| [example] | [value] | [value] | [value] |
|
|
40
|
+
|
|
41
|
+
Fill in values from your product data, not from this example.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 4. Sources
|
|
46
|
+
|
|
47
|
+
Name primary sources: the law, the official gazette, the grant programme page. Link them. A claim with a link to the regulation is quoted more often than one without.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 5. Measurement
|
|
52
|
+
|
|
53
|
+
Track which pages appear in AI answers with a manual check each month, and record it in the project's `docs/METRICS.md`. Do not record a figure you did not observe.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# 04 · Local search, E-E-A-T and behavioural signals
|
|
2
|
+
|
|
3
|
+
For businesses that serve a region, most new customers find them through a map listing. This document lists what the site controls.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Local pack
|
|
8
|
+
|
|
9
|
+
Google's local results use the business profile and the website together.
|
|
10
|
+
|
|
11
|
+
1. **NAP consistency.** Name, address and phone are identical on the website, in `LocalBusiness` structured data and in the business profile. Same spelling, same format.
|
|
12
|
+
2. **Reviews.** Real customer reviews, requested after each job. Do not write reviews for customers and do not ask for reviews only from happy customers.
|
|
13
|
+
3. **Service areas.** The towns or regions served are listed in the page content and in the structured data (`areaServed`).
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. E-E-A-T
|
|
18
|
+
|
|
19
|
+
Google rates the trustworthiness of pages that affect money or safety more strictly.
|
|
20
|
+
|
|
21
|
+
- **Legal identity:** full legal name, company registration number, registered address. Shown in the footer and on a legal page.
|
|
22
|
+
- **Qualifications:** the licences and certifications the staff hold, with the issuing body named.
|
|
23
|
+
- **Partnerships:** only partner logos the business is authorised to use, with a link to the partner's confirmation where one exists.
|
|
24
|
+
- **Named authors** for guides, with their role.
|
|
25
|
+
|
|
26
|
+
Placeholders in this standard (`[Legal name]`, `[Registration number]`) must be replaced with real data by the business.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 3. Behavioural signals
|
|
31
|
+
|
|
32
|
+
Google observes what visitors do after a result.
|
|
33
|
+
|
|
34
|
+
- **Fast first content.** A visitor who sees content at once stays. See `03-performance-web-vitals/`.
|
|
35
|
+
- **Useful interaction.** A calculator or configurator keeps a visitor on the page to finish a task.
|
|
36
|
+
- **Completed intent.** A visitor who reaches the contact action or downloads what they came for does not need to search again. Design the contact action to be the obvious next step.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# 04 · Structured data (Schema.org JSON-LD)
|
|
2
|
+
|
|
3
|
+
Structured data is a machine-readable description of the business, its products and its pages. Search engines and AI assistants use it to identify entities and to show rich results.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. One graph per site
|
|
8
|
+
|
|
9
|
+
Describe all entities in a single `@graph` that links them by `@id`, instead of scattered fragments:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"@context": "https://schema.org",
|
|
14
|
+
"@graph": [
|
|
15
|
+
{
|
|
16
|
+
"@type": "Organization",
|
|
17
|
+
"@id": "https://example.com/#organization",
|
|
18
|
+
"name": "[Brand name]",
|
|
19
|
+
"legalName": "[Legal name]",
|
|
20
|
+
"url": "https://example.com",
|
|
21
|
+
"logo": "https://example.com/brand/logo.svg",
|
|
22
|
+
"email": "[contact email]",
|
|
23
|
+
"address": {
|
|
24
|
+
"@type": "PostalAddress",
|
|
25
|
+
"addressLocality": "[City]",
|
|
26
|
+
"addressRegion": "[Region]",
|
|
27
|
+
"addressCountry": "[ISO country code]"
|
|
28
|
+
},
|
|
29
|
+
"areaServed": ["[Region 1]", "[Region 2]"]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"@type": "WebSite",
|
|
33
|
+
"@id": "https://example.com/#website",
|
|
34
|
+
"url": "https://example.com",
|
|
35
|
+
"name": "[Brand name]",
|
|
36
|
+
"publisher": { "@id": "https://example.com/#organization" },
|
|
37
|
+
"inLanguage": ["es", "en", "uk"]
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Name, address and phone must match the business profile exactly (see `GOOGLE-LOCAL-EEAT.md`).
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. Products and offers
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"@type": "Product",
|
|
52
|
+
"name": "[Product name]",
|
|
53
|
+
"image": "https://example.com/images/p/product.webp",
|
|
54
|
+
"description": "[One factual sentence]",
|
|
55
|
+
"brand": { "@type": "Brand", "name": "[Brand]" },
|
|
56
|
+
"offers": {
|
|
57
|
+
"@type": "Offer",
|
|
58
|
+
"price": "[price]",
|
|
59
|
+
"priceCurrency": "EUR",
|
|
60
|
+
"priceValidUntil": "[date the price is valid until]",
|
|
61
|
+
"availability": "https://schema.org/InStock",
|
|
62
|
+
"seller": { "@id": "https://example.com/#organization" }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Only publish a price that is current on the page. Update `priceValidUntil` when you update the price.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 3. FAQ
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"@type": "FAQPage",
|
|
76
|
+
"mainEntity": [
|
|
77
|
+
{
|
|
78
|
+
"@type": "Question",
|
|
79
|
+
"name": "[Question as the visitor asks it]",
|
|
80
|
+
"acceptedAnswer": {
|
|
81
|
+
"@type": "Answer",
|
|
82
|
+
"text": "[The same answer as on the page]"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The FAQ text in structured data must match the visible text on the page.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 4. Validation
|
|
94
|
+
|
|
95
|
+
Check every page with a structured-data validator before release. A validator error counts as a failed gate.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# 05 · Internationalisation: one hub
|
|
2
|
+
|
|
3
|
+
All interface text lives in one place. Adding a language is one file and one configuration entry.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Principle
|
|
8
|
+
|
|
9
|
+
Do not keep text in several places: partly in `i18n/`, partly in inline `{ es, en }` objects inside components, partly typed into templates.
|
|
10
|
+
|
|
11
|
+
Structure:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
i18n/
|
|
15
|
+
└── locales/
|
|
16
|
+
├── es.ts # base language
|
|
17
|
+
├── en.ts
|
|
18
|
+
├── uk.ts
|
|
19
|
+
└── [code].ts # any new language
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 2. Adding a language
|
|
25
|
+
|
|
26
|
+
1. Copy the base locale file to `[code].ts` and translate every value. Keys stay the same.
|
|
27
|
+
2. Register the language:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// nuxt.config.ts
|
|
31
|
+
i18n: {
|
|
32
|
+
strategy: 'prefix_except_default',
|
|
33
|
+
defaultLocale: 'es',
|
|
34
|
+
locales: [
|
|
35
|
+
{ code: 'es', language: 'es-ES', name: 'Español', file: 'es.ts' },
|
|
36
|
+
{ code: 'en', language: 'en-GB', name: 'English', file: 'en.ts' },
|
|
37
|
+
{ code: 'uk', language: 'uk-UA', name: 'Українська', file: 'uk.ts' },
|
|
38
|
+
{ code: '[code]', language: '[bcp47]', name: '[name]', file: '[code].ts' },
|
|
39
|
+
],
|
|
40
|
+
langDir: '../i18n/locales',
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Routes, language switcher, `hreflang` and sitemap entries for the new language are generated from this list.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 3. Keys
|
|
49
|
+
|
|
50
|
+
- Grouped by component or section: `businessScreens.night.title`.
|
|
51
|
+
- Named by meaning, never by appearance (`hero.title`, not `big.red.title`).
|
|
52
|
+
- The same key set in every locale. The parity check fails the build when a key is missing.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 4. UI strings versus business data
|
|
57
|
+
|
|
58
|
+
- `i18n/locales/*.ts`: interface text only. Button labels, form labels, navigation, error messages.
|
|
59
|
+
- `app/data/*.ts`: business data. Products, specifications, service areas, articles. Typed. Text fields that differ by language use an object keyed by locale, validated by the schema, so a missing translation breaks the build.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Locale template. Copy to i18n/locales/<code>.ts in your project and translate every value. Keys must match the base locale exactly.",
|
|
3
|
+
"nav": {
|
|
4
|
+
"home": "Home",
|
|
5
|
+
"residential": "Residential",
|
|
6
|
+
"business": "Business",
|
|
7
|
+
"mobility": "Mobility",
|
|
8
|
+
"equipment": "Equipment",
|
|
9
|
+
"service": "Service",
|
|
10
|
+
"contact": "Contact"
|
|
11
|
+
},
|
|
12
|
+
"common": {
|
|
13
|
+
"viewProduct": "View product",
|
|
14
|
+
"requestQuote": "Request a quote",
|
|
15
|
+
"calculate": "Calculate savings",
|
|
16
|
+
"fromPrice": "from",
|
|
17
|
+
"close": "Close",
|
|
18
|
+
"send": "Send",
|
|
19
|
+
"success": "Your request was sent"
|
|
20
|
+
},
|
|
21
|
+
"calc": {
|
|
22
|
+
"title": "Calculate your solar savings",
|
|
23
|
+
"billLabel": "How much do you pay for electricity per month?",
|
|
24
|
+
"yearlySavings": "Estimated yearly savings",
|
|
25
|
+
"recommendedPower": "Recommended capacity",
|
|
26
|
+
"paybackTime": "Estimated payback period"
|
|
27
|
+
},
|
|
28
|
+
"badges": {
|
|
29
|
+
"top": "Best seller",
|
|
30
|
+
"new": "New",
|
|
31
|
+
"sale": "Offer"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# 06 · Commercial models for energy projects (example)
|
|
2
|
+
|
|
3
|
+
> Example document from a Spanish solar company, 2025–2026. Market rules, subsidies and prices change. Verify every figure against current official sources before use.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Residential (B2C)
|
|
8
|
+
|
|
9
|
+
**Virtual battery.** In sunny months a home system produces more than the household uses. The surplus goes to the grid and is credited on the customer's account. In winter the credit offsets night-time consumption.
|
|
10
|
+
|
|
11
|
+
**Result for the customer:** a bill close to zero over the year, when credits match consumption.
|
|
12
|
+
|
|
13
|
+
**Energy communities.** Spanish rules (RD 244/2019) allow shared self-consumption between nearby homes. Offer it to terraced houses and communities as a shared installation with a per-home discount.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. Business (B2B)
|
|
18
|
+
|
|
19
|
+
**Power purchase agreement (PPA).** A company that does not want to spend its own capital can have a third party install the system on its roof. The company buys the energy at a fixed rate, below the grid tariff.
|
|
20
|
+
|
|
21
|
+
Explain the contract length, the rate, the escalation clause and who owns the system at the end. Do not present the rate as a saving without the tariff it is compared to.
|
|
22
|
+
|
|
23
|
+
**Solar carports.** Panels over car parks for hotels, warehouses and shops. Shade for customers and staff, plus charging points for fleet vehicles.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 3. Community and public buildings (B2A)
|
|
28
|
+
|
|
29
|
+
Public programmes can fund a large share of installations for shared buildings (lifts, garages, stairwell lighting). The share and the eligibility change by programme and year. State the programme name, the year and the source.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 4. Rules for any offer
|
|
34
|
+
|
|
35
|
+
- Name the programme, the year and the official source.
|
|
36
|
+
- Show the price before and after any grant as two separate figures.
|
|
37
|
+
- Do not make a promise about a grant that is not yet approved.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 06 · Contextual leads
|
|
2
|
+
|
|
3
|
+
A visitor who has already used a calculator knows what they want. Contact actions should carry that context, so the first message is useful to both sides.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Contact link with context
|
|
8
|
+
|
|
9
|
+
A plain `wa.me/<number>` link leaves the visitor to write the first message, and many leave. Build the message from the visitor's last action:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const contactLink = (context: {
|
|
13
|
+
page: string
|
|
14
|
+
monthlyBill?: number
|
|
15
|
+
powerKw?: number
|
|
16
|
+
productId?: string
|
|
17
|
+
}) => {
|
|
18
|
+
const phone = '[international number, digits only]'
|
|
19
|
+
let text = 'Hello, I would like information. '
|
|
20
|
+
if (context.monthlyBill) {
|
|
21
|
+
text += `My monthly bill is about ${context.monthlyBill} €; the estimate was ${context.powerKw} kW. `
|
|
22
|
+
}
|
|
23
|
+
if (context.productId) {
|
|
24
|
+
text += `I am interested in: ${context.productId}. `
|
|
25
|
+
}
|
|
26
|
+
text += 'Could you confirm availability and the grants that apply?'
|
|
27
|
+
return `https://wa.me/${phone}?text=${encodeURIComponent(text)}`
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The visitor only has to press send. The message is in the visitor's language (from the i18n layer, not hard-coded).
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 2. Summary document
|
|
36
|
+
|
|
37
|
+
After a calculation, offer a summary the visitor can keep: the inputs, the result, the equipment and the contact. Generate it in the browser. Ask for a phone number or email before the download, and say why.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 3. Offline queue
|
|
42
|
+
|
|
43
|
+
If the connection drops during a form submission, keep the data in `IndexedDB` (or `localStorage` for very small forms), show a neutral "saved, will be sent" message, and send it when the connection returns. A lead must not be lost because of a dropped connection.
|
|
44
|
+
|
|
45
|
+
Remove stored leads after the server confirms receipt.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 06 · Electric vehicles and solar (example)
|
|
2
|
+
|
|
3
|
+
> Example from a Spanish solar company. Product names, subsidy programmes and amounts are examples. Verify them before use.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Charging from surplus solar
|
|
8
|
+
|
|
9
|
+
A home charger connected to the inverter over the local network can direct surplus solar power to the car. A household that uses 1 kW while the panels make 5 kW sends the remaining 4 kW to the car.
|
|
10
|
+
|
|
11
|
+
**Benefit for the customer:** the car charges from solar energy that would otherwise go to the grid.
|
|
12
|
+
|
|
13
|
+
Check the compatible inverter brands before you promise this.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. Show the benefit in terms people use
|
|
18
|
+
|
|
19
|
+
Technical units (kW, amperes, volts) are hard to compare. Show the result in distance or money:
|
|
20
|
+
|
|
21
|
+
- "Six panels on the roof give about 45 km of driving per day."
|
|
22
|
+
- "Compared with petrol, the monthly saving can exceed [amount calculated for this customer]."
|
|
23
|
+
|
|
24
|
+
State the assumptions next to the figure.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 3. Vehicle-to-home (V2H)
|
|
29
|
+
|
|
30
|
+
A typical home battery stores about 5 kWh. A modern electric vehicle battery stores several times more. With V2H, the car supplies the home in the evening peak or during a grid outage, if the charger and the car support it.
|
|
31
|
+
|
|
32
|
+
Say which models support V2H. Do not offer it to a customer whose hardware does not.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 4. Subsidies
|
|
37
|
+
|
|
38
|
+
Public programmes can cover part of the charger and installation cost. In the example company's catalogue, each product shows two prices: the equipment price and the price after the programme's share. Show both, with the programme name and the source.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# 07 · Testing strategy
|
|
2
|
+
|
|
3
|
+
Run the smallest test that proves the change. Run the full suite once before a commit that finishes a task.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Layers, fastest first
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
1. Unit (domain logic) ~10 ms per test run when logic changes
|
|
11
|
+
2. Composable (Vue reactivity) ~50 ms per test run when a composable changes
|
|
12
|
+
3. Component (Vue Test Utils) ~200 ms per test run when a component changes
|
|
13
|
+
4. End-to-end (Playwright) ~5 s per flow key paths, before release
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Accessibility checks (axe) run on every page at 390, 820 and 1440 px as part of the quality gates.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. Tools
|
|
21
|
+
|
|
22
|
+
| Tool | Use |
|
|
23
|
+
|---|---|
|
|
24
|
+
| Vitest | unit, composable and component tests |
|
|
25
|
+
| `@vue/test-utils` | mounting components without a browser |
|
|
26
|
+
| Playwright | flows (calculator, contact, 404) and screenshots for review |
|
|
27
|
+
| axe-core | accessibility rules, through Playwright |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 3. Commands
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx vitest run path/to/file.test.ts # one file
|
|
35
|
+
npx vitest run path/to/folder/ # one folder
|
|
36
|
+
npx vitest run -t "savings" # by test name
|
|
37
|
+
npx vitest run --reporter=dot # short output
|
|
38
|
+
npx vitest run # full suite, before a finishing commit
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 4. Naming
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// good: what is tested and what is expected
|
|
47
|
+
it('calculates savings with 30% self-consumption', () => { … })
|
|
48
|
+
|
|
49
|
+
// poor: says nothing about what breaks
|
|
50
|
+
it('works', () => { … })
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 5. Test file shape
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { describe, it, expect } from 'vitest'
|
|
59
|
+
import { calcSavings } from './residential'
|
|
60
|
+
|
|
61
|
+
describe('calcSavings', () => {
|
|
62
|
+
it('returns zero savings for zero consumption', () => {
|
|
63
|
+
expect(calcSavings({ kwhMonth: 0, panelKw: 5, tariff: 0.18 })).toBe(0)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('caps savings at the full bill', () => {
|
|
67
|
+
const r = calcSavings({ kwhMonth: 100, panelKw: 20, tariff: 0.18 })
|
|
68
|
+
expect(r.savingsPct).toBeLessThanOrEqual(100)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Each test file runs alone, without a global setup that the test depends on.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 6. Coverage expectations
|
|
78
|
+
|
|
79
|
+
| Layer | Expectation |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `domain/` | high; every branch of business rules |
|
|
82
|
+
| `composables/` | main paths and error paths |
|
|
83
|
+
| `components/ui/` | critical interactions only |
|
|
84
|
+
| `pages/` | smoke test: renders, has one `h1`, no console errors |
|
|
85
|
+
|
|
86
|
+
Do not unit-test static data, copy or CSS. Copy is checked by the locale parity gate; CSS by the visual review in the sandbox.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 7. Token-efficient testing for agents
|
|
91
|
+
|
|
92
|
+
1. Before editing, know which test covers the file.
|
|
93
|
+
2. After editing, run that test with `--reporter=dot`.
|
|
94
|
+
3. Widen the scope only if shared logic changed.
|
|
95
|
+
4. Run the full suite once at the end of the task.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# 08 · Token and context optimisation
|
|
2
|
+
|
|
3
|
+
Goal: an agent reads what the task needs and nothing else, and does not repeat work.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Map before reading
|
|
8
|
+
|
|
9
|
+
Find the layer of the task first, then read only the files on its call chain:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
task → layer → files of the layer → direct dependencies only
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Reading order, from general to specific:
|
|
16
|
+
|
|
17
|
+
1. `ds7/ALL.md` (standard rules), then the project section of the root `ALL.md`, below the AI-ALL packs block (project rules and decisions).
|
|
18
|
+
3. `docs/ARCHITECTURE.md` (layer map).
|
|
19
|
+
4. `app/data/<relevant>` if the task concerns data.
|
|
20
|
+
5. `app/domain/<module>` if the task changes business logic.
|
|
21
|
+
6. `app/composables/<name>` if a composable is involved.
|
|
22
|
+
7. `app/components/<name>` if a component is changed.
|
|
23
|
+
8. `app/pages/<name>` if a page is changed.
|
|
24
|
+
|
|
25
|
+
Do not read all of `app/components/` or all of `app/data/`.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 2. Project graph
|
|
30
|
+
|
|
31
|
+
Keep a short graph in `docs/ARCHITECTURE.md`, one line per dependency, so the agent can find callers without searching:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
pages/index.vue → Hero, Systems, Equipment, Faq
|
|
35
|
+
composables/usePageSeo → useHead, all pages
|
|
36
|
+
data/catalog.ts → ProductCard, Equipment
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Update it when a dependency changes.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 3. Working rules
|
|
44
|
+
|
|
45
|
+
**Before the task**
|
|
46
|
+
1. Read `ds7/ALL.md` and the project section of the root `ALL.md`.
|
|
47
|
+
2. Find the layer in `docs/ARCHITECTURE.md`.
|
|
48
|
+
3. Read only the files on the call chain.
|
|
49
|
+
|
|
50
|
+
**During the task**
|
|
51
|
+
4. After each change, run one targeted test.
|
|
52
|
+
5. Do not start background processes unless needed.
|
|
53
|
+
6. Use `--reporter=dot` for short test output.
|
|
54
|
+
|
|
55
|
+
**At the end**
|
|
56
|
+
7. Run the full suite once.
|
|
57
|
+
8. Record decisions in `docs/DECISIONS.md`, not in the chat.
|
|
58
|
+
9. Commit only what is verified.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 4. File summaries
|
|
63
|
+
|
|
64
|
+
Each non-trivial file starts with one or two lines describing it. An agent can read the first line to decide whether to open the file:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// Residential savings calculator. Input: monthly kWh, panel kW, tariff €/kWh. Output: SavingsResult.
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```vue
|
|
71
|
+
<!--
|
|
72
|
+
UiProductCard — product card molecule.
|
|
73
|
+
Modifiers: -top (highlight), -new (ribbon), -compact (slim).
|
|
74
|
+
-->
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 5. Budget guide
|
|
80
|
+
|
|
81
|
+
| File | Reading strategy |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `ds7/ALL.md` | whole file, always |
|
|
84
|
+
| Project section of root `ALL.md` | whole section, always |
|
|
85
|
+
| `docs/ARCHITECTURE.md` | the section that matches the layer |
|
|
86
|
+
| `app/data/*.ts` | only when the task concerns data |
|
|
87
|
+
| `app/domain/*.ts` | only when business logic changes |
|
|
88
|
+
| `app/components/ui/*.vue` | only the file being changed |
|
|
89
|
+
| `i18n/locales/*.ts` | only when keys are added |
|
|
90
|
+
| `ds7/**/*.md` | on demand, one section |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 6. Useful commands
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
grep -rl "useModal" app/ --include="*.vue" # who uses a composable
|
|
98
|
+
npx vitest run path/to/file.test.ts --reporter=dot # one test, minimal output
|
|
99
|
+
git diff --name-only HEAD # what changed
|
|
100
|
+
```
|