teqo-backup/tests/unit/opencodeAgents.unit.spec.ts
Francisco Solla 3ccb1aea9b test(DESIGN1): alinha o spec do gate de arquivos ao DESIGN.md do front/designer
O DESIGN1 liberou só o frontier para evoluir as regras visuais vivas
(`DESIGN.md` §7) e manteve o tier degradado propondo — o spec pinava a lista
antiga de `permission.edit` e derrubou o `verify` da main (deploy run
36247187381), o que bloqueava qualquer PR. A expectativa vira por agente.
2026-09-26 11:59:21 -03:00

157 lines
6.1 KiB
TypeScript

import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
// OPS117 guard: the two design agents must carry the SAME `permission.bash`
// ruleset. They are two files by design (the degraded agent is a distinct
// `mode: subagent` identity), so nothing but this spec stops one of them from
// drifting while the other keeps the guard — the exact anti-goal (`designer`
// guarded, `designer-degraded` leaking a shell write).
//
// The expected array is pinned on purpose: comparing the two files only would
// let a rule disappear from BOTH at once. Here the contract is the literal
// ruleset, so eroding the guard fails the build too.
//
// The spec parses the YAML frontmatter line-by-line (no YAML dependency): a
// block is the 4-space-indented run under ` <key>:`. Order matters — bash
// rules resolve last-match-wins, so the artifact allow sits at the end.
const repoRoot = process.cwd()
const agents = ['designer', 'designer-degraded'] as const
// Keep in sync with `.opencode/agent/designer.md` and `designer-degraded.md`.
const expectedBash = [
`'*': ask`,
`'*>*': deny`,
`'sed *-i*': deny`,
`'tee *': deny`,
`'cp *': deny`,
`'mv *': deny`,
`'rm *': deny`,
`'mkdir *': deny`,
`'touch *': deny`,
`'truncate *': deny`,
`'dd *': deny`,
`'install *': deny`,
`'ln *': deny`,
`'chmod *': deny`,
`'chown *': deny`,
`'bash *': deny`,
`'sh *': deny`,
`'zsh *': deny`,
`'python*': deny`,
`'node *': deny`,
`'bun *': deny`,
`'deno *': deny`,
`'perl *': deny`,
`'ruby *': deny`,
`'find *-exec*': deny`,
`'find *-delete*': deny`,
`'xargs *': deny`,
`'git checkout*': deny`,
`'git restore*': deny`,
`'git apply*': deny`,
`'* > docs/plans/*-ui-design*': allow`,
`'prettier --write docs/plans/*-ui-design*': allow`,
]
const frontmatter = (agent: string) => {
const content = readFileSync(resolve(repoRoot, `.opencode/agent/${agent}.md`), 'utf8')
const match = content.match(/^---\n([\s\S]*?)\n---/)
expect(match, `${agent}: frontmatter must be delimited by ---`).not.toBeNull()
return match![1]
}
const bodyOf = (agent: string) => {
const content = readFileSync(resolve(repoRoot, `.opencode/agent/${agent}.md`), 'utf8')
return content.replace(/^---\n[\s\S]*?\n---\n/, '')
}
const block = (agent: string, key: 'bash' | 'edit') => {
const lines = frontmatter(agent).split('\n')
const start = lines.findIndex((line) => line === ` ${key}:`)
expect(start, `${agent}: permission.${key} block must exist`).toBeGreaterThanOrEqual(0)
const rules: string[] = []
for (const line of lines.slice(start + 1)) {
if (line.trim() === '') continue
if (!line.startsWith(' ')) break
rules.push(line.trim())
}
return rules
}
describe('design agents share the OPS117 bash write guard', () => {
it.each(agents)('%s declares the pinned bash ruleset (default ask, writes denied)', (agent) => {
const rules = block(agent, 'bash')
// Default is `ask` (never silent), the artifact is the last match, and the
// enumerated write vectors — redirect, in-place sed, tee, mutators, nested
// shells/interpreters, find -exec/-delete, xargs, destructive git — deny.
expect(rules).toEqual(expectedBash)
})
// DESIGN1: only the frontier designer evolves the living visual rules
// (`DESIGN.md` §7); the degraded tier proposes and hands the decision back.
const expectedEdit = {
designer: [`'*': deny`, `'docs/plans/*-ui-design*': allow`, `'DESIGN.md': allow`],
'designer-degraded': [`'*': deny`, `'docs/plans/*-ui-design*': allow`],
} as const
it.each(agents)('%s keeps the file-tool gate fail-closed (OPS113 intact)', (agent) => {
expect(block(agent, 'edit')).toEqual(expectedEdit[agent])
})
})
// Quota guard: the frontier designer must only run real design work. The waste
// this pins against — burning the OpenAI Plus weekly quota on permission/
// frontmatter smoke tests, vision sanity checks and config probes via the
// frontier pin — is exactly what the closed dispatch scope forbids. The rule's
// owner is the doctrine; the agents and the work-issue pipeline point at it.
describe('frontier designer is scoped to real design work (quota guard)', () => {
const doctrine = () =>
readFileSync(resolve(repoRoot, '.agents/skills/plan-issue/ui-design-html.md'), 'utf8')
it('the doctrine owns a closed dispatch scope with a pre-dispatch gate', () => {
const content = doctrine()
expect(content, 'doctrine must name the dispatch-scope section').toContain('Escopo de dispatch')
expect(content, 'doctrine must forbid the frontier for non-design work').toContain(
'NUNCA vai para o `designer` frontier',
)
expect(content, 'doctrine must pin the three-question gate').toContain('muda UI')
expect(content, 'doctrine must call out the permission smoke in particular').toContain(
'permission',
)
})
it('the designer agent refuses work outside the design modes', () => {
const content = bodyOf('designer')
expect(content, 'designer must declare the scope rule').toContain('Só design de fato')
expect(content, 'designer must refuse and hand back, not comply').toContain('recuse')
expect(content, 'designer must point at the doctrine').toContain('ui-design-html.md')
})
it('the degraded agent is the sanctioned tier for the design-guard smoke', () => {
const content = bodyOf('designer-degraded')
expect(content, 'degraded agent must accept the guard smoke').toContain(
'smoke do guard dos agentes de design',
)
expect(content, 'degraded smoke must stay DEGRADED').toContain('DEGRADED')
})
it('plan-issue and the work-issue pipeline point at the closed scope', () => {
const planIssue = readFileSync(resolve(repoRoot, '.agents/skills/plan-issue/SKILL.md'), 'utf8')
const pipeline = readFileSync(
resolve(repoRoot, '.agents/skills/work-issue/execution-pipeline.md'),
'utf8',
)
expect(planIssue, 'plan-issue must reference the scope').toContain('§Escopo de dispatch')
expect(pipeline, 'pipeline must forbid the frontier for the guard smoke').toContain(
'Fora do `designer` frontier de vez',
)
})
})