feat(content): migrate interactiveCopy into six typed collections

Move the six remaining bilingual datasets from app.js:160 into Astro
content collections so GuideSelector has data to drive. Mechanical copy
of 178 strings (160 localized pairs + 18 worker array strings) plus
the non-localized fields (status, path, command, score, icon, title,
number). Source field on commonSkills embedded from skillSources to
match the GuideSelectorData contract; if a URL changes, both this
entry and skillSources/<id>.json must be updated. Status kept on trees
even though GuideSelectorData omits it — index.html renders it.

Did not delete interactiveCopy from app.js: legacy page still consumes
it, and verify.mjs still asserts against it. Did not migrate the
labels (data.labels.*) — those live inside render functions in app.js
and belong to 15d.
This commit is contained in:
Marcos Paulo
2026-09-05 18:48:07 +00:00
parent 99ccc8e249
commit fc063f606a
28 changed files with 507 additions and 0 deletions
+103
View File
@@ -153,6 +153,103 @@ const chapters = defineCollection({
}),
});
// Workers from `interactiveCopy.workers` in app.js: the three sub-agent
// cards on /full-guide/. Each entry holds a 3-element array per locale
// (`[label, detail, result]`), not a localized object — the renderer
// unwraps it directly: `worker[language()][0..2]`.
const workers = defineCollection({
type: 'data',
schema: z.object({
id: z.string(),
en: z.array(z.string()),
pt: z.array(z.string()),
}),
});
// Trees from `interactiveCopy.trees` in app.js: the four worktree nodes
// (main / ui / tests / docs). `owner` and `note` are localized; `path`
// and `command` are single shell strings (the git command shown to the
// user). `status` is rendered by index.html, not the GuideSelector island,
// but it ships in the data and the schema keeps it so nothing is dropped.
const trees = defineCollection({
type: 'data',
schema: z.object({
id: z.string(),
status: z.string(),
owner: localized,
path: z.string(),
command: z.string(),
note: localized,
}),
});
// Routes from `interactiveCopy.routes` in app.js: plan / build / explore /
// review. `score` is the numeric reasoning-load value (rendered into
// `style="--score:N%"`), `label` and `why` are localized.
const routes = defineCollection({
type: 'data',
schema: z.object({
id: z.string(),
score: z.number(),
label: localized,
why: localized,
}),
});
// Skill files from `interactiveCopy.skillFiles` in app.js: SKILL.md /
// references/ / scripts/ / assets/. `icon` and `title` are unlocalized
// labels; `en` and `pt` are separate top-level strings, not a localized
// object — matching how `renderSkillFile` reads them.
const skillFiles = defineCollection({
type: 'data',
schema: z.object({
id: z.string(),
icon: z.string(),
title: z.string(),
en: z.string(),
pt: z.string(),
}),
});
// Skill workflow from `interactiveCopy.skillWorkflow` in app.js: the five
// steps of building a skill (observe / trigger / scaffold / write /
// validate). `number` is unlocalized; `title` / `question` / `action` /
// `output` / `proof` are all localized.
const skillWorkflow = defineCollection({
type: 'data',
schema: z.object({
id: z.string(),
number: z.string(),
title: localized,
question: localized,
action: localized,
output: localized,
proof: localized,
}),
});
// Common skills from `interactiveCopy.commonSkills` in app.js: the seven
// shared skill cards on /full-guide/. `number` and `title` are unlocalized;
// `kind` / `rule` / `use` / `example` / `caution` are localized. `source`
// is the GitHub URL — in app.js it is read from `skillSources[id]` at
// render time; here it is embedded to match the GuideSelectorData
// contract. If the URL changes, both this entry and the matching
// `skillSources/<id>.json` must be updated.
const commonSkills = defineCollection({
type: 'data',
schema: z.object({
id: z.string(),
number: z.string(),
kind: localized,
title: z.string(),
rule: localized,
use: localized,
example: localized,
caution: localized,
source: z.string().url(),
}),
});
// Review desk entries from skills-review/catalog.js +
// skills-review/submitted-catalog.js: 24 in total. Each entry is a Markdown
// file with frontmatter for the metadata and a body that holds the
@@ -189,5 +286,11 @@ export const collections = {
handsOnPrompts,
skillInstallPrompts,
chapters,
workers,
trees,
routes,
skillFiles,
skillWorkflow,
commonSkills,
reviews,
};