shortcut registry keyed (96 -> 73)

shortcuts.ts is data with no component around it, so it carries catalog keys and
ShortcutsModal resolves them -- same shape as the settings sections.

gen-platform-skill.ts also reads this registry to generate the agent-facing SKILL.md,
which docs:check validates. That doc stays in one language, so the generator resolves
against en-GB directly and throws if a key is missing rather than emitting a dot-path
into a file an agent will quote at somebody. The regenerated skill is byte-identical,
which is the check that keying the registry changed nothing an agent reads.
This commit is contained in:
2026-08-23 05:26:43 +00:00
parent 5e032898f8
commit 2e846933e9
5 changed files with 113 additions and 36 deletions
+16 -4
View File
@@ -38,6 +38,16 @@ const KIND_MEANING: Record<string, string> = {
export async function renderPlatformSkill(): Promise<string> {
const { PERMISSIONS } = await import('../src/servers/permissions/registry');
const { SHORTCUT_SECTIONS } = await import('../src/apps/officer-web/state/shortcuts');
const enCatalog = (await import('../src/apps/officer-web/locales/en-GB.json')).default as Record<string, unknown>;
/** Resolve a dot-path against the English catalog. This doc is for agents and stays in one language. */
const enString = (key: string): string => {
const value = key.split('.').reduce<unknown>((node, part) => {
if (node && typeof node === 'object') return (node as Record<string, unknown>)[part];
return undefined;
}, enCatalog);
if (typeof value !== 'string') throw new Error(`gen-platform-skill: no English string for "${key}"`);
return value;
};
const byKind = new Map<string, typeof PERMISSIONS>();
for (const p of PERMISSIONS) {
@@ -112,14 +122,16 @@ export async function renderPlatformSkill(): Promise<string> {
out('Derived from `src/apps/officer-web/state/shortcuts.ts`. These have moved several times; quote this');
out('list rather than remembering one.');
out();
// The registry holds catalog keys since the shell was localized; this doc is agent-facing and stays
// English, so it resolves against en-GB rather than the reader's locale.
for (const section of SHORTCUT_SECTIONS) {
out(`## ${section.title}`);
if (section.note) {
out(`## ${enString(section.titleKey)}`);
if (section.noteKey) {
out();
out(section.note);
out(enString(section.noteKey));
}
out();
for (const s of section.shortcuts) out(`- \`${s.keys}\` — ${s.description}`);
for (const s of section.shortcuts) out(`- \`${s.keys}\` — ${enString(s.descriptionKey)}`);
out();
}