create-ailk 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +21 -1
  2. package/component-catalog.json +184 -6
  3. package/component-catalog.md +20 -9
  4. package/dist/apply-design.d.ts +9 -0
  5. package/dist/apply-design.js +78 -31
  6. package/dist/design-home.d.ts +54 -11
  7. package/dist/design-home.js +160 -22
  8. package/dist/lib/design-checks.d.ts +6 -1
  9. package/dist/lib/design-checks.js +23 -2
  10. package/dist/programmatic.js +44 -20
  11. package/dist/site-roster.d.ts +4 -2
  12. package/dist/site-roster.js +7 -5
  13. package/dist/starter-grid.js +16 -0
  14. package/dist/surfaces.js +11 -0
  15. package/dist/sync-routes.d.ts +28 -0
  16. package/dist/sync-routes.js +123 -1
  17. package/dist/update.d.ts +6 -1
  18. package/dist/update.js +120 -18
  19. package/package.json +3 -3
  20. package/site-templates/_base/ailk-template.json +2 -2
  21. package/site-templates/_base/content/en/WorkingAgreementPage/working-agreement.mdx +78 -0
  22. package/site-templates/designs/minimalism/DESIGN.md +1 -1
  23. package/site-templates/designs/minimalism/contact/local-business-service.mdx +23 -0
  24. package/site-templates/designs/minimalism/contact/organization-product.mdx +23 -0
  25. package/site-templates/designs/minimalism/contact/organization-service.mdx +23 -0
  26. package/site-templates/designs/minimalism/contact/person-service.mdx +23 -0
  27. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +18 -1
  28. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +11 -0
  29. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +18 -1
  30. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +34 -23
  31. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +18 -1
  32. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +33 -23
  33. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +18 -1
  34. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +33 -23
  35. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +18 -1
  36. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +31 -21
  37. package/templates/README.md +26 -25
  38. package/templates/apps/api/CLAUDE.md +7 -3
  39. package/templates/apps/api/jest.config.cjs +1 -0
  40. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +26 -0
  41. package/templates/apps/api/src/services/project-listings.ts +6 -1
  42. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +69 -61
  43. package/templates/apps/mcp/jest.config.cjs +1 -0
  44. package/templates/apps/web/CLAUDE.md +26 -1
  45. package/templates/apps/web/app/[locale]/about/[slug]/page.tsx +46 -0
  46. package/templates/apps/web/app/[locale]/about/__tests__/about-routes.test.tsx +350 -0
  47. package/templates/apps/web/app/[locale]/about/page.tsx +83 -1
  48. package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +2 -2
  49. package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +2 -2
  50. package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +7 -0
  51. package/templates/apps/web/app/[locale]/contact/__tests__/contact-form-magnet.test.ts +138 -0
  52. package/templates/apps/web/app/[locale]/contact/__tests__/contact-form-text.test.ts +17 -0
  53. package/templates/apps/web/app/[locale]/contact/__tests__/contact-magnet-page.test.tsx +347 -0
  54. package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +73 -0
  55. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +5 -3
  56. package/templates/apps/web/app/[locale]/contact/contact-form-magnet.ts +77 -0
  57. package/templates/apps/web/app/[locale]/contact/contact-form-text.ts +2 -0
  58. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +35 -3
  59. package/templates/apps/web/app/[locale]/contact/page.tsx +81 -9
  60. package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +2 -2
  61. package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
  62. package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +2 -2
  63. package/templates/apps/web/app/[locale]/layout.tsx +23 -2
  64. package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +2 -2
  65. package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +2 -2
  66. package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +2 -2
  67. package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
  68. package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
  69. package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
  70. package/templates/apps/web/app/[locale]/working-agreement/__tests__/page.test.tsx +63 -0
  71. package/templates/apps/web/app/[locale]/working-agreement/page.tsx +41 -0
  72. package/templates/apps/web/jest.config.cjs +1 -0
  73. package/templates/apps/web/lib/__tests__/deck-page.test.ts +244 -0
  74. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +4 -2
  75. package/templates/apps/web/lib/deck-page.ts +186 -0
  76. package/templates/apps/web/messages/en.json +16 -5
  77. package/templates/database/CHANGELOG.md +9 -0
  78. package/templates/database/__tests__/seed.test.ts +39 -2
  79. package/templates/database/jest.config.cjs +1 -0
  80. package/templates/database/package.json +1 -1
  81. package/templates/database/seed.ts +29 -0
  82. package/templates/tools/jest/pin-timezone.cjs +34 -0
  83. package/versions.json +34 -34
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ailk",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Scaffolder for AI Launch Kit — `pnpm create ailk@latest <dir>` produces a starter repo consuming @working-theory/* from npm (per ADR 0007).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -28,8 +28,8 @@
28
28
  "tar": "^7.5.21"
29
29
  },
30
30
  "devDependencies": {
31
- "@working-theory/schema": "0.41.0",
32
- "@working-theory/templates": "0.41.0"
31
+ "@working-theory/schema": "0.42.0",
32
+ "@working-theory/templates": "0.42.0"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "_base",
3
3
  "requires": [],
4
- "$comment": "The BASE LAYER (#5618 D1) — not a template. Every declared template reaches this tree by `extends: \"_base\"`; scaffold() installs it through installBundleTree before any module bundle and before the template's own tree, only when --template is named. It carries the seed content every template brings itself and no module owns: /pricing, /privacy and /terms (spec 5462 D16). No installMap: every file here is unmapped and lands at its own path under the scaffold root's content/, so a template overrides a base page by shipping the same path. Never published as a registry bundle; the leading underscore is the skip signal for the bundle producer and the site-templates/ directory scans.",
5
- "postInstall": "Base layer installed: seed copy for /pricing, /privacy and /terms is in content/en/ - replace the placeholder copy before you launch."
4
+ "$comment": "The BASE LAYER (#5618 D1) — not a template. Every declared template reaches this tree by `extends: \"_base\"`; scaffold() installs it through installBundleTree before any module bundle and before the template's own tree, only when --template is named. It carries the seed content every template brings itself and no module owns: /pricing, /privacy, /terms (spec 5462 D16) and /working-agreement (#8661 D10). No installMap: every file here is unmapped and lands at its own path under the scaffold root's content/, so a template overrides a base page by shipping the same path. Never published as a registry bundle; the leading underscore is the skip signal for the bundle producer and the site-templates/ directory scans.",
5
+ "postInstall": "Base layer installed: seed copy for /pricing, /privacy, /terms and /working-agreement is in content/en/ - replace the placeholder copy before you launch."
6
6
  }
@@ -0,0 +1,78 @@
1
+ ---
2
+ type: "WorkingAgreementPage"
3
+ slug: "working-agreement"
4
+ title: "Working Agreement"
5
+ description: "Placeholder working agreement for Your Company. Replace every section with how you actually work with a client before you launch."
6
+ sections:
7
+ [
8
+ {
9
+ "id": "working-agreement-document",
10
+ "type": "document-chassis",
11
+ "data":
12
+ {
13
+ "headline": "Working Agreement",
14
+ "layout": "article",
15
+ "updated": "Last updated {Month Day, Year}",
16
+ "navGroupLabel": "Contents",
17
+ "sections":
18
+ [
19
+ {
20
+ "id": "placeholder-notice",
21
+ "heading": "This is placeholder text",
22
+ "blocks":
23
+ [
24
+ {
25
+ "kind": "paragraph",
26
+ "text": "This page is starter content that shipped with your site. It is not a contract, it binds nobody, and it is not legal advice. Replace every section below with how Your Company actually works with a client, and have someone qualified read it before you publish.",
27
+ },
28
+ {
29
+ "kind": "paragraph",
30
+ "text": "Until you do, leave this notice in place so nobody mistakes the page for a finished agreement.",
31
+ },
32
+ ],
33
+ },
34
+ {
35
+ "id": "what-to-write-here",
36
+ "heading": "What belongs on this page",
37
+ "blocks":
38
+ [
39
+ {
40
+ "kind": "paragraph",
41
+ "text": "A working agreement sets out how an engagement runs day to day. Write each part about the work you actually do, and keep the sentences short enough that a client can follow them.",
42
+ },
43
+ {
44
+ "kind": "list",
45
+ "listStyle": "number",
46
+ "items":
47
+ [
48
+ "How you work with a client, from the first conversation to the last delivery.",
49
+ "What each side provides, and when.",
50
+ "Timelines, and how and how often you communicate.",
51
+ "How changes and cancellations are handled.",
52
+ "How the agreement ends, and what happens to the work when it does.",
53
+ ],
54
+ },
55
+ ],
56
+ },
57
+ {
58
+ "id": "contact",
59
+ "heading": "Questions about this agreement",
60
+ "blocks":
61
+ [
62
+ {
63
+ "kind": "paragraph",
64
+ "text": "Replace this with the address you actually monitor — for example hello@example.com — so a client with a question about the agreement has somewhere to send it.",
65
+ },
66
+ {
67
+ "kind": "paragraph",
68
+ "text": "Replace this with the date this agreement last changed, and say what happens when it changes again.",
69
+ },
70
+ ],
71
+ },
72
+ ],
73
+ },
74
+ },
75
+ ]
76
+ ---
77
+
78
+ {/* Rendered from frontmatter `sections[]`. Placeholder seed content — this is not a working agreement and not legal advice. Replace every section before you launch. */}
@@ -183,7 +183,7 @@ spacing:
183
183
 
184
184
  The minimalism design is the standard foundation — the same black-on-white wireframe, the same type and the same components — presented as a deck. The home page is a deck: each home section is one slide, snapping one slide per scroll, and the landing hero carries the brand, so the site reads like a presentation before it reads like a website; the rest of the site is quiet and document-like. Its character comes from structure, not a palette: it carries no theme of its own.
185
185
 
186
- The deck presents slide by slide (`layout.deckPresentation: true`). Each slide fits the screen, and a slide with more to say scrolls inside itself, with its down chevron held in view at its foot. The pinned mark takes the ground of the slide under it, so it shows its dark variant over a dark slide. Each slide has its own link, `#slide-<name>`, which the address bar follows as the reader moves without adding a history entry. Slides fade in as they arrive, unless the reader has asked for reduced motion. Every slide's header is centred, and feature cards stack in one column on phones.
186
+ The deck presents slide by slide (`layout.deckPresentation: true`). Each slide fits the screen, and a slide with more to say scrolls inside itself, with its down chevron after its last line. The pinned mark takes the ground of the slide under it, so it shows its dark variant over a dark slide. Each slide has its own link, `#slide-<name>`, which the address bar follows as the reader moves without adding a history entry. Slides fade in as they arrive, unless the reader has asked for reduced motion. Every slide's header is centred, and feature cards stack in one column on phones.
187
187
 
188
188
  Every page is composed from the AI Launch Kit component library (`@working-theory/ui`). Nothing is hand-built: a page is a list of registered sections, each with a bounded set of variants.
189
189
 
@@ -0,0 +1,23 @@
1
+ ---
2
+ type: "ContactPage"
3
+ slug: "contact"
4
+ title: "Contact {{SITE_NAME}}"
5
+ # The minimalism design's Contact page for the local-business-service shape — one heading, one line and the contact form. The heading takes the site's name when the design is applied.
6
+ description: "Get in touch with {{SITE_NAME}}: a question, a partnership or just to say hello."
7
+ sections:
8
+ [
9
+ {
10
+ "id": "contact",
11
+ "type": "contact-form",
12
+ "data":
13
+ {
14
+ "variant": "column",
15
+ "headline": "Contact {{SITE_NAME}}",
16
+ "description": "Question, partnership, a podcast or just want to say hello? Tell us what's on your mind.",
17
+ "fields": [],
18
+ },
19
+ },
20
+ ]
21
+ ---
22
+
23
+ The minimalism design seeded this page. Its form is the site's Contact form, which the site owner edits in Admin.
@@ -0,0 +1,23 @@
1
+ ---
2
+ type: "ContactPage"
3
+ slug: "contact"
4
+ title: "Contact {{SITE_NAME}}"
5
+ # The minimalism design's Contact page for the organization-product shape — one heading, one line and the contact form. The heading takes the site's name when the design is applied.
6
+ description: "Get in touch with {{SITE_NAME}}: a question, a partnership or just to say hello."
7
+ sections:
8
+ [
9
+ {
10
+ "id": "contact",
11
+ "type": "contact-form",
12
+ "data":
13
+ {
14
+ "variant": "column",
15
+ "headline": "Contact {{SITE_NAME}}",
16
+ "description": "Question, partnership, a podcast or just want to say hello? Tell us what's on your mind.",
17
+ "fields": [],
18
+ },
19
+ },
20
+ ]
21
+ ---
22
+
23
+ The minimalism design seeded this page. Its form is the site's Contact form, which the site owner edits in Admin.
@@ -0,0 +1,23 @@
1
+ ---
2
+ type: "ContactPage"
3
+ slug: "contact"
4
+ title: "Contact {{SITE_NAME}}"
5
+ # The minimalism design's Contact page for the organization-service shape — one heading, one line and the contact form. The heading takes the site's name when the design is applied.
6
+ description: "Get in touch with {{SITE_NAME}}: a question, a partnership or just to say hello."
7
+ sections:
8
+ [
9
+ {
10
+ "id": "contact",
11
+ "type": "contact-form",
12
+ "data":
13
+ {
14
+ "variant": "column",
15
+ "headline": "Contact {{SITE_NAME}}",
16
+ "description": "Question, partnership, a podcast or just want to say hello? Tell us what's on your mind.",
17
+ "fields": [],
18
+ },
19
+ },
20
+ ]
21
+ ---
22
+
23
+ The minimalism design seeded this page. Its form is the site's Contact form, which the site owner edits in Admin.
@@ -0,0 +1,23 @@
1
+ ---
2
+ type: "ContactPage"
3
+ slug: "contact"
4
+ title: "Contact {{SITE_NAME}}"
5
+ # The minimalism design's Contact page for the person-service shape — one heading, one line and the contact form. The heading takes the site's name when the design is applied.
6
+ description: "Get in touch with {{SITE_NAME}}: a question, a partnership or just to say hello."
7
+ sections:
8
+ [
9
+ {
10
+ "id": "contact",
11
+ "type": "contact-form",
12
+ "data":
13
+ {
14
+ "variant": "column",
15
+ "headline": "Contact {{SITE_NAME}}",
16
+ "description": "Question, partnership, a podcast or just want to say hello? Tell me what's on your mind.",
17
+ "fields": [],
18
+ },
19
+ },
20
+ ]
21
+ ---
22
+
23
+ The minimalism design seeded this page. Its form is the site's Contact form, which the site owner edits in Admin.
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "shape": "organization-product",
19
19
  "design": "standard",
20
- "generatedHash": "971a929b8fe9feb35845c69fc7af93fb01ff9b1e408b8a6abedb5fd5ddbdf26f",
20
+ "generatedHash": "20f84bf7cdd2f51960084e6a91f2cbdc35d86fe87230619572124ca977609f5b",
21
21
  "layout": {
22
22
  "nav": "full",
23
23
  "footer": "columns",
@@ -437,6 +437,23 @@
437
437
  }
438
438
  ]
439
439
  },
440
+ {
441
+ "route": "/working-agreement",
442
+ "kind": "page",
443
+ "pageType": "WorkingAgreementPage",
444
+ "schema": "WorkingAgreementPage",
445
+ "shell": "MarketingShell",
446
+ "chrome": "on",
447
+ "sections": [
448
+ {
449
+ "type": "document-chassis",
450
+ "component": "DocumentChassis",
451
+ "options": {
452
+ "layout": "article"
453
+ }
454
+ }
455
+ ]
456
+ },
440
457
  {
441
458
  "route": "/purchase-policy",
442
459
  "kind": "page",
@@ -239,6 +239,16 @@ _Code route — no content, no sections._
239
239
 
240
240
  <!-- generated:end -->
241
241
 
242
+ `/working-agreement`:
243
+
244
+ <!-- generated:begin page=/working-agreement -->
245
+
246
+ | # | Section | Component | Copy (first line) |
247
+ | --- | ----------------------------------- | ----------------- | ----------------- |
248
+ | 1 | `document-chassis` (layout=article) | `DocumentChassis` | Working Agreement |
249
+
250
+ <!-- generated:end -->
251
+
242
252
  `/purchase-policy`:
243
253
 
244
254
  <!-- generated:begin page=/purchase-policy -->
@@ -511,6 +521,7 @@ marketing-site:
511
521
  - { route: "/sign-up", kind: route }
512
522
  - { route: "/privacy", kind: page, content: ["content/en/PrivacyPage/privacy.mdx"] }
513
523
  - { route: "/terms", kind: page, content: ["content/en/TermsPage/terms.mdx"] }
524
+ - { route: "/working-agreement", kind: page, content: ["content/en/WorkingAgreementPage/working-agreement.mdx"] }
514
525
  - { route: "/purchase-policy", kind: page, content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"] }
515
526
  - { route: "/projects", kind: page, content: ["content/en/ProjectsList/projects.mdx"] }
516
527
  - { route: "/projects/[slug]", kind: page, contentDir: "content/en/Project" }
@@ -4,7 +4,7 @@
4
4
  "categories": ["accounting", "legal", "technology"],
5
5
  "shape": "organization-product",
6
6
  "design": "standard",
7
- "generatedHash": "518cd610a8b86cf4d520c8691a52dc5a0f53a07bf5b44a8515ef4fb1ec84aa92",
7
+ "generatedHash": "08391280820bcc6edd5c7062b75c2f33852b02c37ab518d1302bb7cb43c44714",
8
8
  "layout": {
9
9
  "nav": "full",
10
10
  "footer": "columns",
@@ -427,6 +427,23 @@
427
427
  }
428
428
  ]
429
429
  },
430
+ {
431
+ "route": "/working-agreement",
432
+ "kind": "page",
433
+ "pageType": "WorkingAgreementPage",
434
+ "schema": "WorkingAgreementPage",
435
+ "shell": "MarketingShell",
436
+ "chrome": "on",
437
+ "sections": [
438
+ {
439
+ "type": "document-chassis",
440
+ "component": "DocumentChassis",
441
+ "options": {
442
+ "layout": "article"
443
+ }
444
+ }
445
+ ]
446
+ },
430
447
  {
431
448
  "route": "/purchase-policy",
432
449
  "kind": "page",
@@ -319,6 +319,16 @@ Reference: `screenshots/legal--privacy-policy.png`, `copy/legal--*.txt`. Content
319
319
 
320
320
  <!-- generated:end -->
321
321
 
322
+ `/working-agreement`:
323
+
324
+ <!-- generated:begin page=/working-agreement -->
325
+
326
+ | # | Section | Component | Copy (first line) |
327
+ | --- | ----------------------------------- | ----------------- | ----------------- |
328
+ | 1 | `document-chassis` (layout=article) | `DocumentChassis` | Working Agreement |
329
+
330
+ <!-- generated:end -->
331
+
322
332
  ### `/purchase-policy` — `PurchasePolicyPage` (product shape) · chrome on
323
333
 
324
334
  One document-chassis page with the shape's policy sections — returns, shipping and refunds (#6646). Its Returns section makes the JSON-LD a `MerchantReturnPolicy` at `#return-policy`.
@@ -421,29 +431,30 @@ marketing-site-free:
421
431
  shape: organization-product
422
432
  design: standard
423
433
  roster:
424
- - { route: "/", kind: page, content: ["content/en/HomePage/home.mdx"] }
425
- - { route: "/features", kind: page, content: ["content/en/FeaturesList/features.mdx"] }
426
- - { route: "/features/[slug]", kind: page, contentDir: "content/en/Feature" }
427
- - { route: "/for", kind: page, content: ["content/en/UseCasesList/for.mdx"] }
428
- - { route: "/for/[slug]", kind: page, contentDir: "content/en/UseCase" }
429
- - { route: "/pricing", kind: page, content: ["content/en/Pricing/pricing.mdx"] }
430
- - { route: "/about", kind: page, content: ["content/en/AboutPage/about.mdx"] }
431
- - { route: "/faq", kind: page, content: ["content/en/FAQPage/faq.mdx"] }
432
- - { route: "/contact", kind: page, content: ["content/en/ContactPage/contact.mdx"] }
433
- - { route: "/glossary", kind: page, content: ["content/en/GlossaryList/glossary.mdx"] }
434
- - { route: "/newsletter", kind: page, content: ["content/en/NewsletterSignup/newsletter.mdx"] }
435
- - { route: "/sign-in", kind: route }
436
- - { route: "/sign-up", kind: route }
437
- - { route: "/privacy", kind: page, content: ["content/en/PrivacyPage/privacy.mdx"] }
438
- - { route: "/terms", kind: page, content: ["content/en/TermsPage/terms.mdx"] }
439
- - { route: "/purchase-policy", kind: page, content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"] }
440
- - { route: "/blog", kind: page, content: ["content/en/BlogList/blog.mdx"] }
441
- - { route: "/meet", kind: page, content: ["content/en/LandingPage/meet.mdx"] }
442
- - { route: "/meet/[slug]", kind: page, content: ["content/en/BookCall/book-a-call.mdx", "content/en/BookCall/onsite-estimate.mdx", "content/en/BookCall/start-project.mdx", "content/en/BookCall/onsite-service.mdx"] }
443
- - { route: "/portfolio", kind: page, content: ["content/en/PortfolioList/portfolio.mdx"] }
444
- - { route: "/portfolio/[slug]", kind: page, contentDir: "content/en/PortfolioItem" }
445
- - { route: "/team", kind: page, content: ["content/en/TeamList/team.mdx"] }
446
- - { route: "/team/[slug]", kind: page, contentDir: "content/en/TeamMember" }
434
+ - { route: "/", kind: page, content: ["content/en/HomePage/home.mdx"] }
435
+ - { route: "/features", kind: page, content: ["content/en/FeaturesList/features.mdx"] }
436
+ - { route: "/features/[slug]", kind: page, contentDir: "content/en/Feature" }
437
+ - { route: "/for", kind: page, content: ["content/en/UseCasesList/for.mdx"] }
438
+ - { route: "/for/[slug]", kind: page, contentDir: "content/en/UseCase" }
439
+ - { route: "/pricing", kind: page, content: ["content/en/Pricing/pricing.mdx"] }
440
+ - { route: "/about", kind: page, content: ["content/en/AboutPage/about.mdx"] }
441
+ - { route: "/faq", kind: page, content: ["content/en/FAQPage/faq.mdx"] }
442
+ - { route: "/contact", kind: page, content: ["content/en/ContactPage/contact.mdx"] }
443
+ - { route: "/glossary", kind: page, content: ["content/en/GlossaryList/glossary.mdx"] }
444
+ - { route: "/newsletter", kind: page, content: ["content/en/NewsletterSignup/newsletter.mdx"] }
445
+ - { route: "/sign-in", kind: route }
446
+ - { route: "/sign-up", kind: route }
447
+ - { route: "/privacy", kind: page, content: ["content/en/PrivacyPage/privacy.mdx"] }
448
+ - { route: "/terms", kind: page, content: ["content/en/TermsPage/terms.mdx"] }
449
+ - { route: "/working-agreement", kind: page, content: ["content/en/WorkingAgreementPage/working-agreement.mdx"] }
450
+ - { route: "/purchase-policy", kind: page, content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"] }
451
+ - { route: "/blog", kind: page, content: ["content/en/BlogList/blog.mdx"] }
452
+ - { route: "/meet", kind: page, content: ["content/en/LandingPage/meet.mdx"] }
453
+ - { route: "/meet/[slug]", kind: page, content: ["content/en/BookCall/book-a-call.mdx", "content/en/BookCall/onsite-estimate.mdx", "content/en/BookCall/start-project.mdx", "content/en/BookCall/onsite-service.mdx"] }
454
+ - { route: "/portfolio", kind: page, content: ["content/en/PortfolioList/portfolio.mdx"] }
455
+ - { route: "/portfolio/[slug]", kind: page, contentDir: "content/en/PortfolioItem" }
456
+ - { route: "/team", kind: page, content: ["content/en/TeamList/team.mdx"] }
457
+ - { route: "/team/[slug]", kind: page, contentDir: "content/en/TeamMember" }
447
458
  ```
448
459
 
449
460
  <!-- generated:end -->
@@ -11,7 +11,7 @@
11
11
  ],
12
12
  "shape": "local-business-service",
13
13
  "design": "standard",
14
- "generatedHash": "2884b9e5535e253b067f60bef990f66a97651c366f0484fa1699c0cd54cd4f75",
14
+ "generatedHash": "b8ee2da3100d51dd191ed148dc6c9884394585064da8a27605414ea38dbe3fb0",
15
15
  "layout": {
16
16
  "nav": "full",
17
17
  "footer": "columns",
@@ -338,6 +338,23 @@
338
338
  }
339
339
  ]
340
340
  },
341
+ {
342
+ "route": "/working-agreement",
343
+ "kind": "page",
344
+ "pageType": "WorkingAgreementPage",
345
+ "schema": "WorkingAgreementPage",
346
+ "shell": "MarketingShell",
347
+ "chrome": "on",
348
+ "sections": [
349
+ {
350
+ "type": "document-chassis",
351
+ "component": "DocumentChassis",
352
+ "options": {
353
+ "layout": "article"
354
+ }
355
+ }
356
+ ]
357
+ },
341
358
  {
342
359
  "route": "/blog",
343
360
  "kind": "page",
@@ -231,6 +231,14 @@ _Code route — no content, no sections._
231
231
 
232
232
  <!-- generated:end -->
233
233
 
234
+ <!-- generated:begin page=/working-agreement -->
235
+
236
+ | # | Section | Component | Copy (first line) |
237
+ | --- | ----------------------------------- | ----------------- | ----------------- |
238
+ | 1 | `document-chassis` (layout=article) | `DocumentChassis` | Working Agreement |
239
+
240
+ <!-- generated:end -->
241
+
234
242
  `/blog` (the Free teaser row, § 1.8 — served by the generic `[slug]` route over the always-copied overlay's `BlogList/blog.mdx`):
235
243
 
236
244
  <!-- generated:begin page=/blog -->
@@ -307,8 +315,9 @@ The standard page set for this shape (`derive-roster.py --shape local-business-s
307
315
  | `PortfolioList` · `PortfolioItem` | the portfolio pair belongs to every shape's standard roster (operator, 2026-09-25; roadmap § 4) |
308
316
  | `BlogList` | every Free scaffold's nav links `/blog` unconditionally (`renderNavConfig`, operator ruling 2026-09-23); the Free teaser serves it, as on organization-product (§ 1.8) |
309
317
  | `LandingPage` | the Free `/meet` meeting-types hub is a LandingPage seed, the same generic `[slug]` route serves it through (#6897 D3); the four `/meet/[slug]` BookCall detail seeds ride the `BookCall` type already in the standard roster |
318
+ | `WorkingAgreementPage` | the Contact form's terms checkbox links `/working-agreement` beside `/terms` and `/privacy`, so every starter ships the page from `_base` (#8661 D10) |
310
319
 
311
- `|S − D| = 15` and `|R| = 20`.
320
+ `|S − D| = 15` and `|R| = 21`.
312
321
 
313
322
  `/sign-in` and `/sign-up` are also rostered (#6797) — code routes (`kind: route`) with no page type, so they sit outside this set equation: the locale layout's header links both, and the roster of record lists every page the scaffold serves.
314
323
 
@@ -325,28 +334,29 @@ local-business-service:
325
334
  shape: local-business-service
326
335
  design: standard
327
336
  roster:
328
- - { route: "/", kind: page, content: ["content/en/HomePage/home.mdx"] }
329
- - { route: "/services", kind: page, content: ["content/en/ServicesList/services.mdx"] }
330
- - { route: "/services/[slug]", kind: page, contentDir: "content/en/Service" }
331
- - { route: "/for", kind: page, content: ["content/en/UseCasesList/for.mdx"] }
332
- - { route: "/for/[slug]", kind: page, contentDir: "content/en/UseCase" }
333
- - { route: "/about", kind: page, content: ["content/en/AboutPage/about.mdx"] }
334
- - { route: "/contact", kind: page, content: ["content/en/ContactPage/contact.mdx"] }
335
- - { route: "/faq", kind: page, content: ["content/en/FAQPage/faq.mdx"] }
336
- - { route: "/glossary", kind: page, content: ["content/en/GlossaryList/glossary.mdx"] }
337
- - { route: "/newsletter", kind: page, content: ["content/en/NewsletterSignup/newsletter.mdx"] }
338
- - { route: "/sign-in", kind: route }
339
- - { route: "/sign-up", kind: route }
340
- - { route: "/purchase-policy", kind: page, content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"] }
341
- - { route: "/privacy", kind: page, content: ["content/en/PrivacyPage/privacy.mdx"] }
342
- - { route: "/terms", kind: page, content: ["content/en/TermsPage/terms.mdx"] }
343
- - { route: "/blog", kind: page, content: ["content/en/BlogList/blog.mdx"] }
344
- - { route: "/meet", kind: page, content: ["content/en/LandingPage/meet.mdx"] }
345
- - { route: "/meet/[slug]", kind: page, content: ["content/en/BookCall/book-a-call.mdx", "content/en/BookCall/onsite-estimate.mdx", "content/en/BookCall/start-project.mdx", "content/en/BookCall/onsite-service.mdx"] }
346
- - { route: "/portfolio", kind: page, content: ["content/en/PortfolioList/portfolio.mdx"] }
347
- - { route: "/portfolio/[slug]", kind: page, contentDir: "content/en/PortfolioItem" }
348
- - { route: "/team", kind: page, content: ["content/en/TeamList/team.mdx"] }
349
- - { route: "/team/[slug]", kind: page, contentDir: "content/en/TeamMember" }
337
+ - { route: "/", kind: page, content: ["content/en/HomePage/home.mdx"] }
338
+ - { route: "/services", kind: page, content: ["content/en/ServicesList/services.mdx"] }
339
+ - { route: "/services/[slug]", kind: page, contentDir: "content/en/Service" }
340
+ - { route: "/for", kind: page, content: ["content/en/UseCasesList/for.mdx"] }
341
+ - { route: "/for/[slug]", kind: page, contentDir: "content/en/UseCase" }
342
+ - { route: "/about", kind: page, content: ["content/en/AboutPage/about.mdx"] }
343
+ - { route: "/contact", kind: page, content: ["content/en/ContactPage/contact.mdx"] }
344
+ - { route: "/faq", kind: page, content: ["content/en/FAQPage/faq.mdx"] }
345
+ - { route: "/glossary", kind: page, content: ["content/en/GlossaryList/glossary.mdx"] }
346
+ - { route: "/newsletter", kind: page, content: ["content/en/NewsletterSignup/newsletter.mdx"] }
347
+ - { route: "/sign-in", kind: route }
348
+ - { route: "/sign-up", kind: route }
349
+ - { route: "/purchase-policy", kind: page, content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"] }
350
+ - { route: "/privacy", kind: page, content: ["content/en/PrivacyPage/privacy.mdx"] }
351
+ - { route: "/terms", kind: page, content: ["content/en/TermsPage/terms.mdx"] }
352
+ - { route: "/working-agreement", kind: page, content: ["content/en/WorkingAgreementPage/working-agreement.mdx"] }
353
+ - { route: "/blog", kind: page, content: ["content/en/BlogList/blog.mdx"] }
354
+ - { route: "/meet", kind: page, content: ["content/en/LandingPage/meet.mdx"] }
355
+ - { route: "/meet/[slug]", kind: page, content: ["content/en/BookCall/book-a-call.mdx", "content/en/BookCall/onsite-estimate.mdx", "content/en/BookCall/start-project.mdx", "content/en/BookCall/onsite-service.mdx"] }
356
+ - { route: "/portfolio", kind: page, content: ["content/en/PortfolioList/portfolio.mdx"] }
357
+ - { route: "/portfolio/[slug]", kind: page, contentDir: "content/en/PortfolioItem" }
358
+ - { route: "/team", kind: page, content: ["content/en/TeamList/team.mdx"] }
359
+ - { route: "/team/[slug]", kind: page, contentDir: "content/en/TeamMember" }
350
360
  ```
351
361
 
352
362
  <!-- generated:end -->
@@ -4,7 +4,7 @@
4
4
  "categories": ["accounting", "legal", "real-estate"],
5
5
  "shape": "organization-service",
6
6
  "design": "standard",
7
- "generatedHash": "dc5f942d7fbe9eaeb85606cb73145354af9ca9e780b450824a00973771a41960",
7
+ "generatedHash": "a6330a3da9b734cd384ee07ecb35c2e171d26effe3286caae36bf3b13f8955e4",
8
8
  "layout": {
9
9
  "nav": "full",
10
10
  "footer": "columns",
@@ -314,6 +314,23 @@
314
314
  }
315
315
  ]
316
316
  },
317
+ {
318
+ "route": "/working-agreement",
319
+ "kind": "page",
320
+ "pageType": "WorkingAgreementPage",
321
+ "schema": "WorkingAgreementPage",
322
+ "shell": "MarketingShell",
323
+ "chrome": "on",
324
+ "sections": [
325
+ {
326
+ "type": "document-chassis",
327
+ "component": "DocumentChassis",
328
+ "options": {
329
+ "layout": "article"
330
+ }
331
+ }
332
+ ]
333
+ },
317
334
  {
318
335
  "route": "/purchase-policy",
319
336
  "kind": "page",
@@ -220,6 +220,14 @@ _Code route — no content, no sections._
220
220
 
221
221
  <!-- generated:end -->
222
222
 
223
+ <!-- generated:begin page=/working-agreement -->
224
+
225
+ | # | Section | Component | Copy (first line) |
226
+ | --- | ----------------------------------- | ----------------- | ----------------- |
227
+ | 1 | `document-chassis` (layout=article) | `DocumentChassis` | Working Agreement |
228
+
229
+ <!-- generated:end -->
230
+
223
231
  `/purchase-policy` (this tree's own service-shape override — cancellation and refunds, D4):
224
232
 
225
233
  <!-- generated:begin page=/purchase-policy -->
@@ -305,8 +313,9 @@ The standard page set for this shape (`derive-roster.py --shape organization-ser
305
313
  | `PortfolioList` · `PortfolioItem` | the portfolio pair belongs to every shape's standard roster (operator, 2026-09-25; roadmap § 4) |
306
314
  | `BlogList` | every Free scaffold's nav links `/blog` unconditionally (`renderNavConfig`, operator ruling 2026-09-23); the Free teaser serves it, as on organization-product (§ 1.8) |
307
315
  | `LandingPage` | the Free `/meet` meeting-types hub is a LandingPage seed, the same generic `[slug]` route serves it through (#6897 D3); the four `/meet/[slug]` BookCall detail seeds ride the `BookCall` type already in the standard roster |
316
+ | `WorkingAgreementPage` | the Contact form's terms checkbox links `/working-agreement` beside `/terms` and `/privacy`, so every starter ships the page from `_base` (#8661 D10) |
308
317
 
309
- `|S − D| = 15` and `|R| = 20`.
318
+ `|S − D| = 15` and `|R| = 21`.
310
319
 
311
320
  `/sign-in` and `/sign-up` are also rostered (#6797) — code routes (`kind: route`) with no page type, so they sit outside this set equation: the locale layout's header links both, and the roster of record lists every page the scaffold serves.
312
321
 
@@ -323,28 +332,29 @@ organization-service:
323
332
  shape: organization-service
324
333
  design: standard
325
334
  roster:
326
- - { route: "/", kind: page, content: ["content/en/HomePage/home.mdx"] }
327
- - { route: "/about", kind: page, content: ["content/en/AboutPage/about.mdx"] }
328
- - { route: "/services", kind: page, content: ["content/en/ServicesList/services.mdx"] }
329
- - { route: "/services/[slug]", kind: page, contentDir: "content/en/Service" }
330
- - { route: "/for", kind: page, content: ["content/en/UseCasesList/for.mdx"] }
331
- - { route: "/for/[slug]", kind: page, contentDir: "content/en/UseCase" }
332
- - { route: "/contact", kind: page, content: ["content/en/ContactPage/contact.mdx"] }
333
- - { route: "/faq", kind: page, content: ["content/en/FAQPage/faq.mdx"] }
334
- - { route: "/glossary", kind: page, content: ["content/en/GlossaryList/glossary.mdx"] }
335
- - { route: "/newsletter", kind: page, content: ["content/en/NewsletterSignup/newsletter.mdx"] }
336
- - { route: "/sign-in", kind: route }
337
- - { route: "/sign-up", kind: route }
338
- - { route: "/privacy", kind: page, content: ["content/en/PrivacyPage/privacy.mdx"] }
339
- - { route: "/terms", kind: page, content: ["content/en/TermsPage/terms.mdx"] }
340
- - { route: "/purchase-policy", kind: page, content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"] }
341
- - { route: "/blog", kind: page, content: ["content/en/BlogList/blog.mdx"] }
342
- - { route: "/meet", kind: page, content: ["content/en/LandingPage/meet.mdx"] }
343
- - { route: "/meet/[slug]", kind: page, content: ["content/en/BookCall/book-a-call.mdx", "content/en/BookCall/onsite-estimate.mdx", "content/en/BookCall/start-project.mdx", "content/en/BookCall/onsite-service.mdx"] }
344
- - { route: "/portfolio", kind: page, content: ["content/en/PortfolioList/portfolio.mdx"] }
345
- - { route: "/portfolio/[slug]", kind: page, contentDir: "content/en/PortfolioItem" }
346
- - { route: "/team", kind: page, content: ["content/en/TeamList/team.mdx"] }
347
- - { route: "/team/[slug]", kind: page, contentDir: "content/en/TeamMember" }
335
+ - { route: "/", kind: page, content: ["content/en/HomePage/home.mdx"] }
336
+ - { route: "/about", kind: page, content: ["content/en/AboutPage/about.mdx"] }
337
+ - { route: "/services", kind: page, content: ["content/en/ServicesList/services.mdx"] }
338
+ - { route: "/services/[slug]", kind: page, contentDir: "content/en/Service" }
339
+ - { route: "/for", kind: page, content: ["content/en/UseCasesList/for.mdx"] }
340
+ - { route: "/for/[slug]", kind: page, contentDir: "content/en/UseCase" }
341
+ - { route: "/contact", kind: page, content: ["content/en/ContactPage/contact.mdx"] }
342
+ - { route: "/faq", kind: page, content: ["content/en/FAQPage/faq.mdx"] }
343
+ - { route: "/glossary", kind: page, content: ["content/en/GlossaryList/glossary.mdx"] }
344
+ - { route: "/newsletter", kind: page, content: ["content/en/NewsletterSignup/newsletter.mdx"] }
345
+ - { route: "/sign-in", kind: route }
346
+ - { route: "/sign-up", kind: route }
347
+ - { route: "/privacy", kind: page, content: ["content/en/PrivacyPage/privacy.mdx"] }
348
+ - { route: "/terms", kind: page, content: ["content/en/TermsPage/terms.mdx"] }
349
+ - { route: "/working-agreement", kind: page, content: ["content/en/WorkingAgreementPage/working-agreement.mdx"] }
350
+ - { route: "/purchase-policy", kind: page, content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"] }
351
+ - { route: "/blog", kind: page, content: ["content/en/BlogList/blog.mdx"] }
352
+ - { route: "/meet", kind: page, content: ["content/en/LandingPage/meet.mdx"] }
353
+ - { route: "/meet/[slug]", kind: page, content: ["content/en/BookCall/book-a-call.mdx", "content/en/BookCall/onsite-estimate.mdx", "content/en/BookCall/start-project.mdx", "content/en/BookCall/onsite-service.mdx"] }
354
+ - { route: "/portfolio", kind: page, content: ["content/en/PortfolioList/portfolio.mdx"] }
355
+ - { route: "/portfolio/[slug]", kind: page, contentDir: "content/en/PortfolioItem" }
356
+ - { route: "/team", kind: page, content: ["content/en/TeamList/team.mdx"] }
357
+ - { route: "/team/[slug]", kind: page, contentDir: "content/en/TeamMember" }
348
358
  ```
349
359
 
350
360
  <!-- generated:end -->