Update Owliver language and suggestions

This commit is contained in:
2026-10-07 19:15:44 +05:30
parent 8ac4e1ee88
commit 81e5979f8f
156 changed files with 7330 additions and 1755 deletions

View File

@@ -1513,6 +1513,14 @@ record('the Owliver skill offers its chip, and draws no card',
*/
console.log('\n── Owliver suggestions from the API ──');
/* i18next, before ANY component renders. Every t() in the tree returns its own
key until this has run, so a block that forgets it fails its parity check for
a reason that has nothing to do with what it is testing. The app gets this
from App.tsx; a harness that loads page modules directly has to ask. Loaded
once here rather than per block, which is how two blocks came to be missing
it while a third had it. */
await server.ssrLoadModule('/src/lib/i18n/index.ts');
const serverSuggestions = await server.ssrLoadModule('/src/lib/skills/serverSuggestions.js');
/* ── The request ─────────────────────────────────────────────────────────── */
@@ -3869,7 +3877,11 @@ record('both agent menus offer removal, and neither offers it unconditionally',
`${removeItems.length} remove item(s), each behind (overridden || !shipped)`);
record('a custom agent is offered Remove, and a built-in one is only offered a revert',
removeItems.every((block) => / Remove</.test(block) && /Revert to shipped/.test(block)
/* Matched by translation KEY rather than by the English words: the labels
now come from the catalogue, and a check that greps for "Remove" would be
asserting on which language the file happens to be written in. */
removeItems.every((block) => /workspaceAgents\.remove/.test(block)
&& /workspaceAgents\.revertToShipped/.test(block)
&& /shipped\s*\?/.test(block)),
'the label is chosen by `shipped`, not by name');
@@ -3923,7 +3935,7 @@ record('...while Archived stays reachable as the place removal is undone',
the confirmation names "Archived" while no such tab is on screen. */
record('...and Archived is offered even when it is empty',
!/\{archivedCount > 0 && \(/.test(agentsListSource)
&& /Archived <span[^>]*>\(\{archivedCount\}\)/.test(agentsListSource),
&& /workspaceAgents\.archived'\)\} <span[^>]*>\(\{archivedCount\}\)/.test(agentsListSource),
'the tab is unconditional and shows its count');
record('...and reverting a built-in agent is not worded as a removal',
@@ -4880,7 +4892,7 @@ record('every field the editor had is still editable',
record('the header still states version, status and customization',
/v\{agent\.version\}/.test(detailSource)
&& /\{agent\.status\}/.test(detailSource)
&& /isOverridden\(id\) && <Badge variant="info">Customized/.test(detailSource)
&& /isOverridden\(id\) && <Badge variant="info">\{t\('agentDetail\.customized'\)\}/.test(detailSource)
&& /Publish update/.test(detailSource),
'v · status · Customized · Publish update');
@@ -9082,7 +9094,14 @@ console.log('\n── Candidates vs Talent Pool ──');
}
html = html.slice(0, at) + html.slice(i);
}
return { pass: html === positionsBefore, detail: html === positionsBefore ? '' : `len ${positionsBefore.length}->${html.length}` };
/* Compared with runs of whitespace collapsed. Text that used to be
written across several lines of JSX now comes from the catalogue as
one line, so the markup differs by newlines and indentation that HTML
collapses anyway. Everything this check exists to catch — a changed
element, class, attribute or word — still fails it. */
const ws = (h) => h.replace(/\s+/g, ' ').trim();
const same = ws(html) === ws(positionsBefore);
return { pass: same, detail: same ? '' : `len ${positionsBefore.length}->${html.length}` };
})().pass);
}
}
@@ -9323,6 +9342,106 @@ console.log('\n── Candidates vs Talent Pool ──');
return { pass: ids.length > 0, detail: ids.join(', ') };
})().pass);
}
/* ── The page actually translates ───────────────────────────────────────
*
* The parity checks above all run in English, so every one of them would
* still pass if `t()` were wired up and the Spanish catalogue were empty.
* This renders the same page in Spanish and insists the words CHANGED.
*
* Asserting on specific strings rather than merely "different": a page
* where t() returns raw keys also differs from English, and that is the
* failure this is most likely to catch.
*/
{
const i18n = (await server.ssrLoadModule('/src/lib/i18n/index.ts')).default;
const Page = (await server.ssrLoadModule('/src/pages/admin/ControlCenter.jsx')).default;
const render = () => {
const client = new QueryClient({ defaultOptions: { queries: { retry: false, enabled: false } } });
return stripControls(renderToStaticMarkup(
React.createElement(R, { initialEntries: ['/admin'] },
React.createElement(QueryClientProvider, { client },
React.createElement(UiEditingProvider, { page: 'control-center' },
React.createElement(Page))))
));
};
const en = words(render());
await i18n.changeLanguage('es');
const es = words(render());
await i18n.changeLanguage('en');
record('Control Center renders in Spanish', es !== en);
record('Control Center shows Spanish words, not keys', (() => {
const want = ['Centro de control', 'Vacantes abiertas', 'Candidatos'];
const missing = want.filter((w) => !es.includes(w));
const keys = /controlCenter\.title|metrics\.|sections\./.test(es);
return {
pass: missing.length === 0 && !keys,
detail: keys ? 'raw keys rendered' : (missing.length ? `missing ${missing.join(', ')}` : 'translated'),
};
})().pass);
record('switching back restores English', words(render()) === en);
/* THE CHECK THAT MATTERS, and the one whose absence let a half-translated
page be reported as finished. "es !== en" passes when a single word
changed, which is exactly the state a reader sees as broken: Spanish
headings sitting on top of English captions, legends and stage names.
So this names the English the page used to show and insists none of it
survives the switch. A key added to the catalogue but never wired into
the component fails here, where every other check stays green. */
record('no English survives on the Spanish Control Center', (() => {
const english = [
'Applications, screening, interviews and hires',
'entered · conversion and drop-off by stage',
'Entry Stage', 'Primary Pipeline Bottleneck',
'pass through and', 'Open positions', 'In review', 'AI screened',
'Avg KROW score', 'Hiring activity', 'Pipeline intelligence',
'Position performance', 'Action center', 'Active positions',
'Top talent', 'Recent activity', 'Shortlisted', 'Interview',
];
const left = english.filter((e) => es.includes(e));
return { pass: left.length === 0, detail: left.length ? `still English: ${left.join(' | ')}` : 'fully translated' };
})().pass);
/* The panel writes its own greeting, description and placeholders in the
browser — `dynamic.ts` — so a translated DASHBOARD with an English
Owliver is the exact half-done state this is here to catch. */
const dyn = await server.ssrLoadModule('/src/components/ai-assistant/dynamic.ts');
const facts = {
today: new Date('2026-10-07T09:00:00'),
total: 24, postings: new Array(8),
unscoredProfiles: [], activity24h: [],
};
const esIntro = dyn.buildIntro('admin.controlCenter', facts, 'Aravind', i18n.getFixedT('es'));
const enIntro = dyn.buildIntro('admin.controlCenter', facts, 'Aravind', i18n.getFixedT('en'));
record('the assistant greeting translates', (() => {
const ok = esIntro.title.startsWith('Buenos días') && enIntro.title.startsWith('Good morning');
return { pass: ok, detail: `${enIntro.title} / ${esIntro.title}` };
})().pass);
record('the assistant greeting agrees in gender and number', (() => {
/* "Buenos días" and "Buenas tardes" — the adjective agrees with the
noun, so a greeting assembled from "Good " plus a translated word is
wrong in one of them. One key per time of day is why this passes. */
const afternoon = { ...facts, today: new Date('2026-10-07T14:00:00') };
const pm = dyn.buildIntro('admin.controlCenter', afternoon, '', i18n.getFixedT('es'));
return { pass: pm.title === 'Buenas tardes', detail: pm.title };
})().pass);
record('the assistant description counts in Spanish', (() => {
const ok = esIntro.description.includes('24 solicitudes')
&& esIntro.description.includes('8 vacantes');
return { pass: ok, detail: esIntro.description };
})().pass);
record('a singular count does not take the plural form', (() => {
const one = { ...facts, total: 1, postings: new Array(1) };
const d = dyn.buildIntro('admin.controlCenter', one, '', i18n.getFixedT('es')).description;
return { pass: d.includes('1 solicitud ') || d.includes('1 solicitud'), detail: d };
})().pass);
}
}
}