@gradial/aci 0.1.53-rc.1 → 0.1.53-rc.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ### Added
6
+
7
+ - Page `metadata.robots` accepts boolean `index` and `follow` directives.
8
+ Next and Astro emit the corresponding HTML tag. Publish excludes pages with
9
+ `index: false` from the release sitemap.
10
+
11
+ ### Migration required
12
+
13
+ - Existing Astro sites must install `@gradial/aci/astro/Head` and pass resolved
14
+ metadata as one object through their route and layout. Next sites must upgrade
15
+ the SDK and rebuild. See [page metadata migration](./migrations/page-metadata-head.md).
16
+ Packed starter HTML checks in `make test-frontend-contract` verify the tags.
17
+ Sitemap exclusion can precede an SDK upgrade. Inspect the rendered preview
18
+ before telling an author that `noindex` is present. No platform test currently
19
+ enforces that check on older site builds.
20
+
5
21
  ### Changed
6
22
 
7
23
  - Immutable code builds (`ACI_CODE_ARTIFACT_BUILD=1`) no longer read or compile
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Agentic Content Infrastructure SDK
2
2
 
3
- `@gradial/aci` `0.1.53-rc.1` is a pre-1.0 public-surface reset for the ACI
3
+ `@gradial/aci` `0.1.53-rc.2` is a pre-1.0 public-surface reset for the ACI
4
4
  client SDK. Breaking changes from the old `0.1.x` accidental surface are
5
5
  intentional: the root import is framework-neutral, framework runtime behavior is
6
6
  behind explicit subpaths, and the first required customer path is React/Next.
@@ -8,7 +8,7 @@ behind explicit subpaths, and the first required customer path is React/Next.
8
8
  ## Install
9
9
 
10
10
  ```bash
11
- npm install @gradial/aci@0.1.53-rc.1 zod@^4
11
+ npm install @gradial/aci@0.1.53-rc.2 zod@^4
12
12
  ```
13
13
 
14
14
  ## Root Import
@@ -137,13 +137,13 @@ host bundles select and serve immutable release artifacts.
137
137
  The first RC scaffold path is Next:
138
138
 
139
139
  ```bash
140
- npx @gradial/aci@0.1.53-rc.1 init my-site --template next-starter
140
+ npx @gradial/aci@0.1.53-rc.2 init my-site --template next-starter
141
141
  ```
142
142
 
143
143
  List available templates:
144
144
 
145
145
  ```bash
146
- npx @gradial/aci@0.1.53-rc.1 init --list
146
+ npx @gradial/aci@0.1.53-rc.2 init --list
147
147
  ```
148
148
 
149
149
  ## Personalization: The One-Tag Install
@@ -503,6 +503,14 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
503
503
  title: { type: 'string', minLength: 1 },
504
504
  description: { type: 'string' },
505
505
  canonical: { type: 'string' },
506
+ robots: {
507
+ type: 'object',
508
+ additionalProperties: false,
509
+ properties: {
510
+ index: { type: 'boolean' },
511
+ follow: { type: 'boolean' },
512
+ },
513
+ },
506
514
  },
507
515
  },
508
516
  regions: {
@@ -1,4 +1,4 @@
1
- import type { KernelPage, KernelRouteMetadata, KernelSiteConfig, RenderInput } from './types.js';
1
+ import type { KernelPage, KernelPageRobots, KernelRouteMetadata, KernelSiteConfig, RenderInput } from './types.js';
2
2
  export interface RouteEntry {
3
3
  slug: string;
4
4
  path: string;
@@ -78,3 +78,9 @@ export interface RenderInputOptions {
78
78
  }
79
79
  export declare function loadRenderInput(provider: ContentProvider, route: string | undefined, options: RenderInputOptions): Promise<RenderInput>;
80
80
  export declare function routeMetadataForContent(siteConfig: KernelSiteConfig, page: KernelPage | null): KernelRouteMetadata;
81
+ /**
82
+ * Serializes page robots directives to a `<meta name="robots">` content
83
+ * value, matching what Next.js emits for the same object. Returns '' when
84
+ * no directive is set, so templates can skip the tag.
85
+ */
86
+ export declare function robotsMetaContent(robots: KernelPageRobots | undefined): string;
@@ -53,6 +53,33 @@ export function routeMetadataForContent(siteConfig, page) {
53
53
  title,
54
54
  description,
55
55
  canonical: page?.metadata?.canonical,
56
+ robots: pageRobots(page?.metadata?.robots),
56
57
  siteName: siteConfig.seo?.siteName || siteConfig.title
57
58
  };
58
59
  }
60
+ function pageRobots(value) {
61
+ if (!value || typeof value !== 'object' || Array.isArray(value))
62
+ return undefined;
63
+ const { index, follow } = value;
64
+ const robots = {};
65
+ if (typeof index === 'boolean')
66
+ robots.index = index;
67
+ if (typeof follow === 'boolean')
68
+ robots.follow = follow;
69
+ return robots.index === undefined && robots.follow === undefined ? undefined : robots;
70
+ }
71
+ /**
72
+ * Serializes page robots directives to a `<meta name="robots">` content
73
+ * value, matching what Next.js emits for the same object. Returns '' when
74
+ * no directive is set, so templates can skip the tag.
75
+ */
76
+ export function robotsMetaContent(robots) {
77
+ if (!robots)
78
+ return '';
79
+ const directives = [];
80
+ if (robots.index !== undefined)
81
+ directives.push(robots.index ? 'index' : 'noindex');
82
+ if (robots.follow !== undefined)
83
+ directives.push(robots.follow ? 'follow' : 'nofollow');
84
+ return directives.join(', ');
85
+ }
@@ -37,10 +37,20 @@ export interface SiteFooter {
37
37
  }
38
38
  export type KernelFooterGroup = FooterGroup;
39
39
  export type KernelFooterColumn = FooterColumn;
40
+ /**
41
+ * Page-level search engine directives. `index: false` renders `noindex` and
42
+ * drops the page from the release sitemap; `follow: false` renders
43
+ * `nofollow`. Absent keys emit nothing (crawler default: index, follow).
44
+ */
45
+ export interface KernelPageRobots {
46
+ index?: boolean;
47
+ follow?: boolean;
48
+ }
40
49
  export interface KernelPageSeo {
41
50
  title?: string;
42
51
  description?: string;
43
52
  canonical?: string;
53
+ robots?: KernelPageRobots;
44
54
  }
45
55
  export interface KernelPageBlock {
46
56
  $type: 'component';
@@ -108,6 +118,7 @@ export interface KernelRouteMetadata {
108
118
  title: string;
109
119
  description: string;
110
120
  canonical?: string;
121
+ robots?: KernelPageRobots;
111
122
  siteName: string;
112
123
  }
113
124
  export interface RenderInput<TPage extends KernelPage = KernelPage, TSiteConfig extends KernelSiteConfig = KernelSiteConfig> {
package/dist/next/page.js CHANGED
@@ -57,6 +57,7 @@ export function createPage(registry, options) {
57
57
  title: meta.title,
58
58
  description: meta.description,
59
59
  alternates: meta.canonical ? { canonical: meta.canonical } : undefined,
60
+ robots: meta.robots,
60
61
  openGraph: {
61
62
  title: meta.title,
62
63
  description: meta.description,
@@ -0,0 +1,38 @@
1
+ # Page metadata and crawler directives
2
+
3
+ Upgrade and rebuild each site before using `metadata.robots`. Backend sitemap
4
+ exclusion does not prove that a site emits the HTML directive.
5
+
6
+ ## Astro integration
7
+
8
+ Install the SDK-owned head component once. Future metadata fields then follow
9
+ SDK upgrades through the same object.
10
+
11
+ 1. Import `Head` from `@gradial/aci/astro/Head` in the site layout.
12
+ 2. Replace individual metadata props with `meta: KernelRouteMetadata` from `@gradial/aci/content`.
13
+ 3. Render `<Head meta={meta} />` inside `<head>`.
14
+ 4. Remove the layout's title, description, canonical, robots, and Open Graph tags that `Head` now owns.
15
+ 5. Pass `meta={data.meta}` from the catch-all route.
16
+ 6. Pass `meta={errorPage.meta}` from the 404 and 500 seats.
17
+ 7. Keep site-owned charset, viewport, icons, scripts, and custom head tags.
18
+
19
+ Use `packages/sdk/templates/astro/template/src/` as the integration reference.
20
+ `getPageData` returns the complete resolved metadata object. No separate robots
21
+ prop or serialized robots string is needed.
22
+
23
+ ## Verify the rendered output
24
+
25
+ Verify the directive on the site build that will serve the content. This is a
26
+ release check, not a claim that a search engine has removed a URL.
27
+
28
+ 1. Author `"metadata": { "robots": { "index": false, "follow": false } }` on a test page.
29
+ 2. Build the site and publish the test content.
30
+ 3. Check that the rendered preview has one robots tag containing `noindex, nofollow`.
31
+ 4. Check that the release sitemap excludes that page and retains an indexable control page.
32
+ 5. Check that robots.txt permits fetching the test page so crawlers can read its tag.
33
+ 6. Repeat against the active release after activation.
34
+
35
+ `make test-frontend-contract` checks rendered HTML from packed Next and Astro
36
+ starters. It does not certify a customer's older SDK or copied layout.
37
+ Authoring integrations must inspect rendered preview HTML before claiming the
38
+ directive is present. The companion Grady change must preserve this requirement.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradial/aci",
3
- "version": "0.1.53-rc.1",
3
+ "version": "0.1.53-rc.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/gradial/bare-metal-go.git",
@@ -103,6 +103,9 @@
103
103
  "./astro/ConsentBanner": {
104
104
  "import": "./src/astro/ConsentBanner.astro"
105
105
  },
106
+ "./astro/Head": {
107
+ "import": "./src/astro/Head.astro"
108
+ },
106
109
  "./consent-banner": {
107
110
  "types": "./dist/consent-banner/index.d.ts",
108
111
  "import": "./dist/consent-banner/index.js"
@@ -160,11 +163,11 @@
160
163
  "zod": "^4.0.0"
161
164
  },
162
165
  "optionalDependencies": {
163
- "@gradial/aci-cli-darwin-arm64": "0.1.53-rc.1",
164
- "@gradial/aci-cli-darwin-x64": "0.1.53-rc.1",
165
- "@gradial/aci-cli-linux-arm64": "0.1.53-rc.1",
166
- "@gradial/aci-cli-linux-x64": "0.1.53-rc.1",
167
- "@gradial/aci-cli-win32-arm64": "0.1.53-rc.1",
168
- "@gradial/aci-cli-win32-x64": "0.1.53-rc.1"
166
+ "@gradial/aci-cli-darwin-arm64": "0.1.53-rc.2",
167
+ "@gradial/aci-cli-darwin-x64": "0.1.53-rc.2",
168
+ "@gradial/aci-cli-linux-arm64": "0.1.53-rc.2",
169
+ "@gradial/aci-cli-linux-x64": "0.1.53-rc.2",
170
+ "@gradial/aci-cli-win32-arm64": "0.1.53-rc.2",
171
+ "@gradial/aci-cli-win32-x64": "0.1.53-rc.2"
169
172
  }
170
173
  }
@@ -0,0 +1,18 @@
1
+ ---
2
+ import { robotsMetaContent, type KernelRouteMetadata } from '@gradial/aci/content';
3
+
4
+ interface Props {
5
+ meta: KernelRouteMetadata;
6
+ }
7
+
8
+ const { meta } = Astro.props;
9
+ const robots = robotsMetaContent(meta.robots);
10
+ ---
11
+
12
+ <title>{meta.title}</title>
13
+ <meta name="description" content={meta.description} />
14
+ {meta.canonical ? <link rel="canonical" href={meta.canonical} /> : null}
15
+ {robots ? <meta name="robots" content={robots} /> : null}
16
+ <meta property="og:title" content={meta.title} />
17
+ <meta property="og:description" content={meta.description} />
18
+ <meta property="og:site_name" content={meta.siteName} />
@@ -496,6 +496,14 @@ async function generatePageSchema(schemaDir, componentEntries, outDir) {
496
496
  title: { type: 'string', minLength: 1 },
497
497
  description: { type: 'string' },
498
498
  canonical: { type: 'string' },
499
+ robots: {
500
+ type: 'object',
501
+ additionalProperties: false,
502
+ properties: {
503
+ index: { type: 'boolean' },
504
+ follow: { type: 'boolean' },
505
+ },
506
+ },
499
507
  },
500
508
  },
501
509
  regions: {
@@ -1,11 +1,10 @@
1
1
  ---
2
2
  import '../design-system/styles.css';
3
+ import Head from '@gradial/aci/astro/Head';
4
+ import type { KernelRouteMetadata } from '@gradial/aci/content';
3
5
 
4
6
  interface Props {
5
- title: string;
6
- description: string;
7
- canonical?: string;
8
- siteName?: string;
7
+ meta: KernelRouteMetadata;
9
8
  /**
10
9
  * Pre-rendered Gradial collect script tag (collectScriptTagHTML from
11
10
  * '@gradial/aci/astro'). Empty string / undefined = personalization
@@ -17,7 +16,7 @@ interface Props {
17
16
  assignmentsTag?: string;
18
17
  }
19
18
 
20
- const { title, description, canonical, siteName = 'Gradial', collectTag, assignmentsTag } = Astro.props;
19
+ const { meta, collectTag, assignmentsTag } = Astro.props;
21
20
  ---
22
21
 
23
22
  <!doctype html>
@@ -25,13 +24,8 @@ const { title, description, canonical, siteName = 'Gradial', collectTag, assignm
25
24
  <head>
26
25
  <meta charset="utf-8" />
27
26
  <meta name="viewport" content="width=device-width, initial-scale=1" />
28
- <title>{title}</title>
29
27
  <link rel="icon" type="image/png" href="/assets/bare-metal-logo.png" />
30
- <meta name="description" content={description} />
31
- {canonical ? <link rel="canonical" href={canonical} /> : null}
32
- <meta property="og:title" content={title} />
33
- <meta property="og:description" content={description} />
34
- <meta property="og:site_name" content={siteName} />
28
+ <Head meta={meta} />
35
29
  {collectTag ? <Fragment set:html={collectTag} /> : null}
36
30
  {assignmentsTag ? <Fragment set:html={assignmentsTag} /> : null}
37
31
  </head>
@@ -17,10 +17,7 @@ const renderChildren = createRenderChildren(registry);
17
17
  ---
18
18
 
19
19
  <SiteLayout
20
- title={errorPage.meta.title}
21
- description={errorPage.meta.description}
22
- canonical={errorPage.meta.canonical}
23
- siteName={errorPage.meta.siteName}
20
+ meta={errorPage.meta}
24
21
  >
25
22
  {data ? (
26
23
  data.layout.slots.map((slot) => {
@@ -16,10 +16,7 @@ const renderChildren = createRenderChildren(registry);
16
16
  ---
17
17
 
18
18
  <SiteLayout
19
- title={errorPage.meta.title}
20
- description={errorPage.meta.description}
21
- canonical={errorPage.meta.canonical}
22
- siteName={errorPage.meta.siteName}
19
+ meta={errorPage.meta}
23
20
  >
24
21
  {data ? (
25
22
  data.layout.slots.map((slot) => {
@@ -25,10 +25,7 @@ const renderChildren = createRenderChildren(registry);
25
25
  ---
26
26
 
27
27
  <SiteLayout
28
- title={data.meta.title}
29
- description={data.meta.description}
30
- canonical={data.meta.canonical}
31
- siteName={data.meta.siteName}
28
+ meta={data.meta}
32
29
  collectTag={data.collectTag}
33
30
  assignmentsTag={data.assignmentsTag}
34
31
  >
@@ -58,6 +58,36 @@ Base page fields are SDK-owned and included in the generated
58
58
  governs an interior page (see Page Templates); pages under a route a
59
59
  template covers omit it.
60
60
 
61
+ #### Search engine indexing
62
+
63
+ `metadata.robots` requests crawler indexing behavior. An upgraded site renders a
64
+ `<meta name="robots">` tag in the page head:
65
+
66
+ ```json
67
+ {
68
+ "metadata": {
69
+ "title": "Private Campaign Page",
70
+ "robots": { "index": false, "follow": false }
71
+ }
72
+ }
73
+ ```
74
+
75
+ - **`index: false`** — renders `noindex` and excludes the page from `sitemap.xml`.
76
+ - **`follow: false`** — renders `nofollow`. Crawlers do not follow the page's
77
+ links.
78
+ - **Omit a key** — no directive for it. Crawlers default to index, follow.
79
+
80
+ Verify the rendered preview HTML before claiming the directive is present.
81
+ Next sites need an SDK upgrade and rebuild. Existing Astro sites also need
82
+ the SDK-owned `@gradial/aci/astro/Head` integration. Sitemap exclusion alone
83
+ does not prove that the page emits `noindex`. Search removal depends on a
84
+ later crawl and is not immediate.
85
+
86
+ Use `index: false` for temporary campaign pages, event landing pages, and
87
+ thank-you pages. Do not also block the path in `robots.txt`: a crawler that
88
+ cannot fetch the page never sees the `noindex` tag, and the URL can stay in
89
+ search results.
90
+
61
91
  #### Sitemap visibility
62
92
 
63
93
  `metadata.sitemap.visible` controls whether the page appears in the generated
@@ -77,10 +107,10 @@ template covers omit it.
77
107
  }
78
108
  ```
79
109
 
80
- Use `false` for pages that should publish but stay out of search engine
81
- discovery: temporary campaign pages, event-specific landing pages, staging
82
- pages, or pages already blocked by `robots.txt` (setting it explicitly makes
83
- the intent clear in content).
110
+ Use `false` only for pages that may appear in search results but should not
111
+ be listed in the sitemap, or for pages already blocked by `robots.txt`. To keep
112
+ a page out of search results, use `metadata.robots` instead — it excludes the
113
+ page from the sitemap too.
84
114
 
85
115
  This does **not** add a `noindex` meta tag — it only controls sitemap inclusion.
86
116
 
@@ -143,6 +143,7 @@ const renderChildren = createRenderChildren(registry);
143
143
  title={data.meta.title}
144
144
  description={data.meta.description}
145
145
  canonical={data.meta.canonical}
146
+ robots={data.robotsContent}
146
147
  siteName={data.meta.siteName}
147
148
  collectTag={data.collectTag}
148
149
  >