269 lines
21 KiB
JavaScript
269 lines
21 KiB
JavaScript
/**
|
||
* Adversarial case corpus for Phase 4D parser conformance.
|
||
*
|
||
* Each case is raw bytes as an author could actually produce them. Nothing here
|
||
* is normalized on the way in: the point is what the two parsers do with the
|
||
* awkward form, so the awkward form is what is stored.
|
||
*/
|
||
|
||
const SKILL = [
|
||
'---',
|
||
'id: sample-skill',
|
||
'name: Sample Skill',
|
||
'description: A sample skill.',
|
||
'pages:',
|
||
' - candidates',
|
||
'status: active',
|
||
'actions:',
|
||
' - navigate_to_candidates',
|
||
'---',
|
||
'',
|
||
'# Sample Skill',
|
||
'',
|
||
'## Purpose',
|
||
'',
|
||
'- Read the pipeline.',
|
||
'',
|
||
'## Capabilities',
|
||
'',
|
||
'- Summarize candidates.',
|
||
'',
|
||
].join('\n');
|
||
|
||
const AGENT = [
|
||
'---',
|
||
'id: sample-agent',
|
||
'name: Sample Agent',
|
||
'description: A sample agent.',
|
||
'icon: users',
|
||
'status: published',
|
||
'version: 2',
|
||
'reasoning: balanced',
|
||
'trigger: Use on candidates.',
|
||
'pages:',
|
||
' - candidates',
|
||
'skills:',
|
||
' - candidate-search',
|
||
'starters:',
|
||
' - label: Who is waiting?',
|
||
' prompt: Who is waiting on a decision?',
|
||
'permissions:',
|
||
' owner: demo@krow.app',
|
||
' access: all',
|
||
'---',
|
||
'',
|
||
'# Sample Agent',
|
||
'',
|
||
'## Instructions',
|
||
'',
|
||
'Answer about candidates.',
|
||
'',
|
||
'## Purpose',
|
||
'',
|
||
'- Report the pipeline.',
|
||
'',
|
||
].join('\n');
|
||
|
||
/** A skill body, with the frontmatter lines replaced wholesale. */
|
||
const skillWith = (fmLines) => ['---', ...fmLines, '---', '', '# Sample Skill', '', '## Purpose', '', '- Read the pipeline.', ''].join('\n');
|
||
const agentWith = (fmLines) => ['---', ...fmLines, '---', '', '# Sample Agent', '', '## Instructions', '', 'Answer.', ''].join('\n');
|
||
|
||
const BASE_SKILL_FM = [
|
||
'id: sample-skill',
|
||
'name: Sample Skill',
|
||
'description: A sample skill.',
|
||
'pages:',
|
||
' - candidates',
|
||
];
|
||
|
||
const BASE_AGENT_FM = [
|
||
'id: sample-agent',
|
||
'name: Sample Agent',
|
||
'description: A sample agent.',
|
||
'pages:',
|
||
' - candidates',
|
||
];
|
||
|
||
/** BASE_SKILL_FM with one line appended. */
|
||
const skillPlus = (...extra) => skillWith([...BASE_SKILL_FM, ...extra]);
|
||
const agentPlus = (...extra) => agentWith([...BASE_AGENT_FM, ...extra]);
|
||
|
||
export const CASES = [
|
||
/* ── Baselines ────────────────────────────────────────────────────────── */
|
||
{ name: 'baseline-skill', kind: 'skill', raw: SKILL },
|
||
{ name: 'baseline-agent', kind: 'agent', raw: AGENT },
|
||
|
||
/* ── Byte-level shape ─────────────────────────────────────────────────── */
|
||
{ name: 'utf8-bom', kind: 'skill', raw: '' + SKILL },
|
||
{ name: 'utf8-bom-agent', kind: 'agent', raw: '' + AGENT },
|
||
{ name: 'crlf', kind: 'skill', raw: SKILL.replace(/\n/g, '\r\n') },
|
||
{ name: 'crlf-agent', kind: 'agent', raw: AGENT.replace(/\n/g, '\r\n') },
|
||
{ name: 'cr-only', kind: 'skill', raw: SKILL.replace(/\n/g, '\r') },
|
||
{ name: 'bom-crlf-blankline', kind: 'skill', raw: '\r\n' + SKILL.replace(/\n/g, '\r\n') },
|
||
{ name: 'leading-blank-line', kind: 'skill', raw: '\n' + SKILL },
|
||
{ name: 'multiple-leading-blank-lines', kind: 'skill', raw: '\n\n\n' + SKILL },
|
||
{ name: 'leading-spaces-then-blank-lines', kind: 'skill', raw: ' \n \n' + SKILL },
|
||
{ name: 'no-trailing-newline', kind: 'skill', raw: SKILL.replace(/\n+$/, '') },
|
||
{ name: 'many-trailing-newlines', kind: 'skill', raw: SKILL + '\n\n\n' },
|
||
{ name: 'trailing-spaces-on-values', kind: 'skill', raw: skillWith(BASE_SKILL_FM.map((l) => l + ' ')) },
|
||
{ name: 'trailing-ws-after-open-fence', kind: 'skill', raw: SKILL.replace(/^---/, '--- ') },
|
||
{ name: 'trailing-tab-after-close-fence', kind: 'skill', raw: SKILL.replace(/\n---\n/, '\n---\t\n') },
|
||
{ name: 'frontmatter-only-no-body', kind: 'skill', raw: '---\n' + BASE_SKILL_FM.join('\n') + '\n---' },
|
||
{ name: 'frontmatter-only-trailing-newline', kind: 'skill', raw: '---\n' + BASE_SKILL_FM.join('\n') + '\n---\n' },
|
||
|
||
/* ── Scalars ──────────────────────────────────────────────────────────── */
|
||
{ name: 'double-quoted-scalar', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: "Sample Skill"', 'description: "A sample."', 'pages:', ' - candidates']) },
|
||
{ name: 'single-quoted-scalar', kind: 'skill', raw: skillWith(["id: sample-skill", "name: 'Sample Skill'", "description: 'A sample.'", 'pages:', ' - candidates']) },
|
||
{ name: 'colon-in-quoted-string', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: "Sample: Skill"', 'description: "Note: read this."', 'pages:', ' - candidates']) },
|
||
{ name: 'colon-in-unquoted-string', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: Note: read this.', 'pages:', ' - candidates']) },
|
||
{ name: 'hash-in-quoted-string', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: "Shift #1"', 'description: "Tag #ops"', 'pages:', ' - candidates']) },
|
||
{ name: 'hash-unquoted-trailing-comment', kind: 'skill', raw: skillPlus('category: ops # a trailing comment') },
|
||
{ name: 'hash-unquoted-midword', kind: 'skill', raw: skillPlus('category: ops#1') },
|
||
{ name: 'doubled-quote-escape', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: "She said ""go"""', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'full-line-comment', kind: 'skill', raw: skillWith(['# a comment line', ...BASE_SKILL_FM]) },
|
||
{ name: 'empty-scalar', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description:', 'pages:', ' - candidates']) },
|
||
{ name: 'tilde-scalar', kind: 'skill', raw: skillPlus('category: ~') },
|
||
{ name: 'null-scalar', kind: 'skill', raw: skillPlus('category: null') },
|
||
{ name: 'boolean-scalar', kind: 'agent', raw: agentPlus('webSearch: true') },
|
||
{ name: 'integer-scalar', kind: 'agent', raw: agentPlus('version: 3') },
|
||
{ name: 'float-scalar', kind: 'agent', raw: agentPlus('version: 1.5') },
|
||
{ name: 'negative-integer', kind: 'agent', raw: agentPlus('version: -2') },
|
||
|
||
/* ── Collections ──────────────────────────────────────────────────────── */
|
||
{ name: 'empty-array', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates', 'actions:']) },
|
||
{ name: 'inline-flow-array', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages: [candidates]']) },
|
||
{ name: 'inline-flow-map', kind: 'agent', raw: agentPlus('permissions: {owner: a, access: all}') },
|
||
{ name: 'sequence-of-mappings', kind: 'agent', raw: AGENT },
|
||
{ name: 'nested-mapping', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'permissions:', ' owner: demo@krow.app', ' access: specific', ' people:', ' - user: a@b.c', ' role: editor']) },
|
||
{ name: 'dash-alone-nested-block', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'starters:', ' -', ' label: Hello', ' prompt: Hello there']) },
|
||
{ name: 'tab-indented-sequence', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages:', '\t- candidates']) },
|
||
{ name: 'four-space-indent', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'ragged-indent', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates', ' - positions']) },
|
||
|
||
/* ── Multiline / unsupported YAML ─────────────────────────────────────── */
|
||
{ name: 'block-scalar-literal', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: |', ' Line one.', ' Line two.', 'pages:', ' - candidates']) },
|
||
{ name: 'block-scalar-folded', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: >', ' Line one.', 'pages:', ' - candidates']) },
|
||
{ name: 'anchor-and-alias', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: &n Sample Skill', 'description: *n', 'pages:', ' - candidates']) },
|
||
{ name: 'multi-document', kind: 'skill', raw: skillWith([...BASE_SKILL_FM, '---', 'id: second']) },
|
||
|
||
/* ── Keys ─────────────────────────────────────────────────────────────── */
|
||
{ name: 'duplicate-key', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: First Name', 'name: Second Name', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'duplicate-key-array', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates', 'pages:', ' - positions']) },
|
||
{ name: 'unsupported-frontmatter-field', kind: 'skill', raw: skillPlus('unknownField: whatever') },
|
||
{ name: 'unsupported-field-agent', kind: 'agent', raw: agentPlus('nonsense: 1') },
|
||
{ name: 'key-with-space', kind: 'skill', raw: skillWith(['id: sample-skill', 'my key: value', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'uppercase-key', kind: 'skill', raw: skillPlus('Category: Ops') },
|
||
|
||
/* ── Malformed ────────────────────────────────────────────────────────── */
|
||
{ name: 'malformed-yaml-bare-line', kind: 'skill', raw: skillWith(['id: sample-skill', 'this is not a pair', 'name: Sample Skill', 'pages:', ' - candidates']) },
|
||
{ name: 'malformed-open-fence-two-dashes', kind: 'skill', raw: SKILL.replace(/^---/, '--') },
|
||
{ name: 'malformed-open-fence-four-dashes', kind: 'skill', raw: SKILL.replace(/^---/, '----') },
|
||
{ name: 'malformed-open-fence-indented', kind: 'skill', raw: ' ' + SKILL },
|
||
{ name: 'malformed-open-fence-text-after', kind: 'skill', raw: SKILL.replace(/^---/, '---yaml') },
|
||
{ name: 'malformed-close-fence-two-dashes', kind: 'skill', raw: '---\n' + BASE_SKILL_FM.join('\n') + '\n--\n\n# Body\n' },
|
||
{ name: 'malformed-close-fence-missing', kind: 'skill', raw: '---\n' + BASE_SKILL_FM.join('\n') + '\n\n# Body\n' },
|
||
{ name: 'malformed-close-fence-four-dashes', kind: 'skill', raw: '---\n' + BASE_SKILL_FM.join('\n') + '\n----\n\n# Body\n' },
|
||
{ name: 'empty-fence-pair', kind: 'skill', raw: '---\n---\n\n# Body\n' },
|
||
{ name: 'empty-fence-with-blank', kind: 'skill', raw: '---\n\n---\n\n# Body\n' },
|
||
{ name: 'missing-frontmatter', kind: 'skill', raw: '# Sample Skill\n\n## Purpose\n\n- Read the pipeline.\n' },
|
||
{ name: 'empty-markdown', kind: 'skill', raw: '' },
|
||
{ name: 'whitespace-only-markdown', kind: 'skill', raw: ' \n\n\t\n' },
|
||
{ name: 'frontmatter-is-a-sequence', kind: 'skill', raw: '---\n- one\n- two\n---\n\n# Body\n' },
|
||
|
||
/* ── Field-level validity ─────────────────────────────────────────────── */
|
||
{ name: 'invalid-definition-id-uppercase', kind: 'skill', raw: skillWith(['id: Sample_Skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'invalid-definition-id-leading-dash', kind: 'skill', raw: skillWith(['id: -sample', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'invalid-definition-id-underscore', kind: 'skill', raw: skillWith(['id: sample_skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'missing-id-derives-from-name', kind: 'skill', raw: skillWith(['name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'missing-name', kind: 'skill', raw: skillWith(['id: sample-skill', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'missing-pages', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.']) },
|
||
{ name: 'unknown-page', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - nowhere']) },
|
||
{ name: 'page-alias-uppercase', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - Candidates']) },
|
||
{ name: 'page-alias-underscore', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - talent_pool']) },
|
||
{ name: 'invalid-status-skill', kind: 'skill', raw: skillPlus('status: bogus') },
|
||
{ name: 'inactive-status-skill', kind: 'skill', raw: skillPlus('status: inactive') },
|
||
{ name: 'invalid-status-agent', kind: 'agent', raw: agentPlus('status: bogus') },
|
||
{ name: 'invalid-reasoning-agent', kind: 'agent', raw: agentPlus('reasoning: turbo') },
|
||
{ name: 'invalid-icon-agent', kind: 'agent', raw: agentPlus('icon: rocket') },
|
||
{ name: 'invalid-version-agent', kind: 'agent', raw: agentPlus('version: zero') },
|
||
{ name: 'invalid-field-type-pages-scalar', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages: candidates']) },
|
||
{ name: 'invalid-field-type-pages-scalar-agent', kind: 'agent', raw: agentWith(['id: sample-agent', 'name: Sample Agent', 'description: A sample.', 'pages: candidates']) },
|
||
{ name: 'invalid-field-type-name-list', kind: 'skill', raw: skillWith(['id: sample-skill', 'name:', ' - a', ' - b', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'invalid-permissions-access', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'permissions:', ' access: nobody']) },
|
||
{ name: 'self-subagent', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'subagents:', ' - sample-agent']) },
|
||
|
||
/* ── Size ─────────────────────────────────────────────────────────────── */
|
||
{ name: 'oversized-markdown', kind: 'skill', raw: skillPlus() + '\n' + 'x'.repeat(65600) },
|
||
{ name: 'at-size-bound', kind: 'skill', raw: (() => { const b = skillPlus(); return b + 'y'.repeat(65536 - b.length); })() },
|
||
|
||
/* ── Gaps closed after the first oracle run ───────────────────────────── */
|
||
{ name: 'missing-name-agent', kind: 'agent', raw: agentWith(['id: sample-agent', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'missing-pages-agent', kind: 'agent', raw: agentWith(['id: sample-agent', 'name: Sample Agent', 'description: A sample.']) },
|
||
{ name: 'unknown-page-agent', kind: 'agent', raw: agentWith(['id: sample-agent', 'name: Sample Agent', 'description: A sample.', 'pages:', ' - nowhere']) },
|
||
{ name: 'oversized-agent', kind: 'agent', raw: agentPlus() + '\n' + 'x'.repeat(65600) },
|
||
{ name: 'visibility-field-personal', kind: 'skill', raw: skillPlus('visibility: personal') },
|
||
{ name: 'visibility-field-invalid', kind: 'skill', raw: skillPlus('visibility: nobody') },
|
||
{ name: 'blank-page-entry-agent', kind: 'agent', raw: agentWith(['id: sample-agent', 'name: Sample Agent', 'description: A sample.', 'pages:', ' - candidates', ' - ""']) },
|
||
{ name: 'duplicate-page-entry-agent', kind: 'agent', raw: agentWith(['id: sample-agent', 'name: Sample Agent', 'description: A sample.', 'pages:', ' - candidates', ' - candidates']) },
|
||
{ name: 'id-with-trailing-space', kind: 'skill', raw: skillWith(['id: sample-skill ', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'id-quoted', kind: 'skill', raw: skillWith(['id: "sample-skill"', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'starters-plain-strings-agent', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'starters:', ' - Who is waiting?', ' - Where are the gaps?']) },
|
||
{ name: 'starter-missing-label-agent', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'starters:', ' - prompt: Only a prompt']) },
|
||
{ name: 'knowledge-note-agent', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'knowledge:', ' - label: A note', ' body: The body of the note.']) },
|
||
{ name: 'knowledge-bad-kind-agent', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'knowledge:', ' - label: A note', ' kind: rumour', ' body: x']) },
|
||
{ name: 'knowledge-link-without-url-agent', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'knowledge:', ' - label: A link', ' kind: link']) },
|
||
{ name: 'skills-scalar-coerced-agent', kind: 'agent', raw: agentPlus('skills: candidate-search') },
|
||
{ name: 'skills-blank-entry-agent', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'skills:', ' - candidate-search', ' - ""']) },
|
||
{ name: 'web-search-snake-case-agent', kind: 'agent', raw: agentPlus('web_search: true') },
|
||
{ name: 'web-search-string-true-agent', kind: 'agent', raw: agentPlus('webSearch: "true"') },
|
||
{ name: 'version-quoted-integer-agent', kind: 'agent', raw: agentPlus('version: "3"') },
|
||
{ name: 'version-zero-agent', kind: 'agent', raw: agentPlus('version: 0') },
|
||
{ name: 'version-empty-agent', kind: 'agent', raw: agentPlus('version:') },
|
||
{ name: 'crlf-inside-frontmatter-only', kind: 'skill', raw: (() => { const i = SKILL.indexOf('\n---\n', 3); return SKILL.slice(0, i).replace(/\n/g, '\r\n') + SKILL.slice(i); })() },
|
||
{ name: 'body-whitespace-only-after-fence', kind: 'skill', raw: '---\n' + BASE_SKILL_FM.join('\n') + '\n---\n \n\t\n' },
|
||
{ name: 'description-whitespace-only', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: " "', 'pages:', ' - candidates']) },
|
||
{ name: 'permissions-null-agent', kind: 'agent', raw: agentPlus('permissions:') },
|
||
{ name: 'permissions-people-bad-role-agent', kind: 'agent', raw: agentWith([...BASE_AGENT_FM, 'permissions:', ' access: specific', ' people:', ' - user: a@b.c', ' role: overlord']) },
|
||
|
||
/* ── Gaps closed after the second oracle run ──────────────────────────────
|
||
*
|
||
* Two rules had no case that could tell a correct implementation from a
|
||
* plausible wrong one, so both were being asserted by accident:
|
||
*
|
||
* The id fallback chain. Every earlier case either declares `id:` or
|
||
* declares a `name:` to slug, so the THIRD link — the default path, which
|
||
* is the literal `custom` on both sides — was never reached by a case that
|
||
* is otherwise valid. `id-omitted-unnamed` is that case: the frontend
|
||
* accepts it as the skill `custom`, and a backend deriving no id would
|
||
* refuse it on save.
|
||
*
|
||
* Text coercion. `name`, `description`, `category`, `trigger` and the
|
||
* entries of `pages` are text columns in migration 000005, and a definition
|
||
* may write any of them as a list, a number or a boolean. The frontend
|
||
* keeps the raw value on the record and stringifies it at each use; the
|
||
* backend has to choose the string at the boundary. These cases pin which
|
||
* string it chooses.
|
||
*/
|
||
{ name: 'id-omitted-unnamed', kind: 'skill', raw: skillWith(['description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'id-omitted-named', kind: 'skill', raw: skillWith(['name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'id-omitted-unnamed-agent', kind: 'agent', raw: agentWith(['description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'id-omitted-named-agent', kind: 'agent', raw: agentWith(['name: Sample Agent', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'id-omitted-name-unsluggable', kind: 'skill', raw: skillWith(['name: "!!!"', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'name-numeric', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: 42', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'name-boolean', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: true', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'description-list', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description:', ' - one', ' - two', 'pages:', ' - candidates']) },
|
||
{ name: 'description-numeric', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: 7', 'pages:', ' - candidates']) },
|
||
{ name: 'pages-numeric-entry', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - candidates', ' - 5']) },
|
||
{ name: 'pages-mapping-entry', kind: 'skill', raw: skillWith(['id: sample-skill', 'name: Sample Skill', 'description: A sample.', 'pages:', ' - page: candidates']) },
|
||
{ name: 'trigger-list-agent', kind: 'agent', raw: agentPlus('trigger:', ' - one', ' - two') },
|
||
{ name: 'name-list-agent', kind: 'agent', raw: agentWith(['id: sample-agent', 'name:', ' - a', ' - b', 'description: A sample.', 'pages:', ' - candidates']) },
|
||
{ name: 'category-numeric', kind: 'skill', raw: skillPlus('category: 3') },
|
||
|
||
/* The other backend-only bound. agent_definitions.version is a PostgreSQL
|
||
`integer`; the frontend accepts any whole number of 1 or more, so a version
|
||
above 2^31-1 is one the editor takes and the database cannot store. It had
|
||
no case at all, which left the rule asserted by nobody. */
|
||
{ name: 'version-above-int32-agent', kind: 'agent', raw: agentPlus('version: 3000000000') },
|
||
{ name: 'version-at-int32-agent', kind: 'agent', raw: agentPlus('version: 2147483647') },
|
||
];
|