From cab2daa35ee0130ccdb6c6873b4c83fc367da8c6 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Wed, 16 Sep 2026 10:50:15 -0300 Subject: [PATCH] =?UTF-8?q?chore(OPS123):=20escopo=20de=20dispatch=20do=20?= =?UTF-8?q?designer=20frontier=20=E2=80=94=20s=C3=B3=20design=20de=20fato?= =?UTF-8?q?=20(Closes=20#1091)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O frontier designer (openai/gpt-5.6-sol, OAuth ChatGPT Plus) só roda Criar/Criticar o artefato de item que muda UI. A regra vive na doutrina ui-design-html.md §Escopo de dispatch (gate de 3 perguntas + lista fechada); plan-issue e execution-pipeline apontam para o dono; o agente recusa fora do escopo e o smoke do guard vai para o designer-degraded. --- .agents/skills/plan-issue/SKILL.md | 6 +- .agents/skills/plan-issue/ui-design-html.md | 22 +++++++ .../skills/work-issue/execution-pipeline.md | 8 +++ .opencode/agent/designer-campanha-solla.md | 1 + .opencode/agent/designer-degraded.md | 1 + .opencode/agent/designer.md | 1 + docs/AGENT-OPS.md | 2 +- docs/changelog/2026-09-16-ops123.md | 1 + tests/unit/opencodeAgents.unit.spec.ts | 58 +++++++++++++++++++ 9 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 docs/changelog/2026-09-16-ops123.md diff --git a/.agents/skills/plan-issue/SKILL.md b/.agents/skills/plan-issue/SKILL.md index 51598e04..d0e33292 100644 --- a/.agents/skills/plan-issue/SKILL.md +++ b/.agents/skills/plan-issue/SKILL.md @@ -23,7 +23,7 @@ parse (main agent) 1. **Nada no tracker antes do gate.** Antes da confirmação: proibido `pnpm agent:register`, criar Issue/PR. 2. **Register com `--plan` nasce `blocked`.** Promote só depois do plano em `main`. 3. **Planos de Issues `in-progress`/`done`/`in-prod` são imutáveis.** -4. **Design UI (obrigatório se muda UI):** o sub-agente `designer` produz o design hi-fi; o orquestrador grava `docs/plans/-ui-design.html` + `docs/plans/-ui-design-assets/*.svg` (doutrina: `ui-design-html.md`). Classe A/sem UI → sem design. +4. **Design UI (obrigatório se muda UI):** o sub-agente `designer` produz o design hi-fi; o orquestrador grava `docs/plans/-ui-design.html` + `docs/plans/-ui-design-assets/*.svg` (doutrina: `ui-design-html.md`). Classe A/sem UI → sem design. **O `designer` frontier só roda design de fato** (doutrina §Escopo de dispatch): smoke/validação de `permission`/config, visão, exploração e escrita de plano **nunca** no frontier. ## Decomposição em sub-agentes @@ -52,7 +52,7 @@ Cada fase pesada é delegada a um sub-agente com contexto mínimo. O agente prin ### Sub-agente: Designer -**Quando:** Passo 3d, paralelo (1 por ideia UI). +**Quando:** Passo 3d, **somente** ideia que muda UI (Impeccable B/C/D). Escopo e gate de dispatch são os de `ui-design-html.md` §Escopo de dispatch — o frontier não roda smoke/validação de `permission`, visão, exploração nem escrita de plano. Prefira **uma** sessão para N itens UI a N sessões frias. **Input:** plano de intenção aprovado (B/C/D) + `ui-design-html.md` (doutrina) + `.opencode/agent/designer.md` (papel) **Task:** Produzir o design hi-fi do item — tokens/brand reais, cenas 390/1280, estados críticos. **Não escrever código de app nem nomear componente final.** **Output:** o design hi-fi — `docs/plans/-ui-design.html` + `docs/plans/-ui-design-assets/*.svg`; o orquestrador garante a gravação no disco. @@ -115,7 +115,7 @@ O agente principal: 1. Valida cada plano contra `shaping.md` (self-score ≥4) 2. Aplica melhorias se necessário 3. Cria os arquivos `docs/plans/.md` no disco -4. Se UI: dispatcha o sub-agente `designer` (paralelo, 1 por ideia UI) para produzir `docs/plans/-ui-design.html` + `docs/plans/-ui-design-assets/*.svg`; o orquestrador grava os arquivos no disco e preenche/valida o campo `Design UI:` + a seção do plano +4. Se UI: dispatcha o sub-agente `designer` **só para ideia que muda UI** — prefira **uma** sessão para os N itens UI a N sessões frias (`ui-design-html.md` §Escopo de dispatch) — para produzir `docs/plans/-ui-design.html` + `docs/plans/-ui-design-assets/*.svg`; o orquestrador grava os arquivos no disco e preenche/valida o campo `Design UI:` + a seção do plano ## Passo 4 — GATE diff --git a/.agents/skills/plan-issue/ui-design-html.md b/.agents/skills/plan-issue/ui-design-html.md index a88f3b9c..bd3620de 100644 --- a/.agents/skills/plan-issue/ui-design-html.md +++ b/.agents/skills/plan-issue/ui-design-html.md @@ -21,6 +21,28 @@ Mesma doutrina para os dois papéis (fonte única — os prompts dos agentes apo - **Tier degradado:** o `designer-degraded` cria/estende/critica, mas **marca todo output `DEGRADED`** e **nunca certifica** — exige sign-off humano explícito. `DEGRADED` não é design aprovado. - **Escrita só no artefato (file tools + bash):** os dois agentes têm `permission` fail-closed — `edit`/`write`/`patch` negados fora de `docs/plans/-ui-design*` e o **bash** nega os vetores de escrita enumerados (redirecionamento, `sed -i`, `tee`, shells/interpretadores) fora do artefato; o resto do shell fica em `ask`. Não tente contornar por shell (o guard cobre os vetores enumerados, não é sandbox de SO) — leia/inspecione à vontade (`cat`, `sed -n`, `git diff`) e grave o artefato pelas file tools. +## Escopo de dispatch — o `designer` frontier só roda design de fato + +O tier frontier (`designer` com pin `openai/gpt-5.6-sol`) é **recurso escasso e caro**: ele é despachado **apenas** nos dois modos de design — **Criar** ou **Criticar** o artefato — e **apenas** para item que **muda UI**. Todo trabalho que não é isso roda no **modelo padrão da sessão**, nunca no frontier. + +**Lista fechada do que NUNCA vai para o `designer` frontier:** + +- smoke/validação de `permission`, guard de escrita, frontmatter, `model:` ou config de agente — **a semântica de `permission` é do frontmatter, não do modelo**: valide estaticamente, ou rode o smoke com o `designer-degraded` (barato); o resultado do guard é o mesmo; +- teste de visão/sanidade de imagem ("qual a cor dominante") — use o modelo padrão da sessão, que tem visão; +- exploração de código (`@explore`), review geral (`@general`), `@solla-comunicacao`; +- escrever/revisar plano de intenção, impl plan, PR, changelog ou doc; +- qualquer coisa cujo output não seja `docs/plans/-ui-design*`. + +**Gate antes de disparar (as três têm de ser "sim"):** + +1. O item **muda UI** (Impeccable B/C/D)? 2. É **Criar** ou **Criticar** o artefato hi-fi? 3. O output é `docs/plans/-ui-design*`? + +Qualquer "não" ⇒ **não despache** o `designer` frontier: segue com o implementador ou com o modelo padrão da sessão. + +**Fan-out:** prefira **uma** sessão `designer` para N itens UI do mesmo lote (cache reusado; o system prompt frio de ~25K tokens é pago uma vez) a N sessões frias em paralelo. Paralelismo maximiza velocidade **e consumo** — sob quota, agrupe. + +**Agente de design sem `model:` herda a sessão.** `designer-campanha-solla` não tem pin: ele roda no modelo da sessão-mãe. Dispare essa sessão em modelo barato e só troque para o frontier quando a task for de fato criar/criticar design. + ## O que o design DEVE ter (fidelidade mínima) 1. **Layout real** das superfícies tocadas: zonas (lista, detalhe, formulário, mapa, empty, erro), hierarquia de atenção, CTA primário vs secundário. diff --git a/.agents/skills/work-issue/execution-pipeline.md b/.agents/skills/work-issue/execution-pipeline.md index 62e6b8c7..4dfab7b1 100644 --- a/.agents/skills/work-issue/execution-pipeline.md +++ b/.agents/skills/work-issue/execution-pipeline.md @@ -84,6 +84,14 @@ dados/lógica no markup aprovado, hooks/rotas/queries, copy, port mecânico de seção aprovada, bug que restaura o design aprovado, reuso de tokens/componentes já especificados. O default é **não despachar**. +**Fora do `designer` frontier de vez (proibição, não non-trigger):** smoke/ +validação de `permission`/guard/frontmatter do próprio agente, teste de visão/ +sanidade de imagem, exploração (`@explore`), review geral (`@general`), escrita +de plano/PR/changelog. Esses workers rodam no **modelo padrão da sessão** (ou no +`designer-degraded`, barato, quando o alvo é o guard dos agentes de design) — +nunca no pin frontier. Gate de dispatch e lista fechada: `ui-design-html.md` +§Escopo de dispatch. + **Crítica final (fail-closed).** Sem tier primário **não há certificação**. A ladder e a regra do `DEGRADED` são as de `ui-design-html.md` — a indisponibilidade **desce o tier** e registra `Design tier: ` no PR; diff --git a/.opencode/agent/designer-campanha-solla.md b/.opencode/agent/designer-campanha-solla.md index 3ed06d57..418c8abb 100644 --- a/.opencode/agent/designer-campanha-solla.md +++ b/.opencode/agent/designer-campanha-solla.md @@ -69,6 +69,7 @@ Você é o diretor de design e conversão do site de campanha do Deputado Federa ## Regras inegociáveis +- **Só design de fato.** Você não tem `model:` pin — herda o modelo da sessão. Dispare sua sessão em modelo barato e só troque para o frontier (Sol/Astra) quando a task for de fato desenhar/criticar a tela. Nunca gaste o frontier em smoke/validação de `permission`/config, teste de visão, exploração, review ou escrita de plano (doutrina `ui-design-html.md` §Escopo de dispatch). - **Sem fonte, não publica**: número, obra, citação e depoimento só com fonte verificável; aspas só reais; nada de números desatualizados de terceiros sem checagem na Câmara/site oficial. - **Um CTA por página**; nunca dividir a atenção acima da dobra. - **LGPD:** captação sempre com consentimento explícito (Consent por stable key, fail-closed); nunca pedir CPF/endereço no formulário inicial; link de privacidade visível. diff --git a/.opencode/agent/designer-degraded.md b/.opencode/agent/designer-degraded.md index fd3366db..74384638 100644 --- a/.opencode/agent/designer-degraded.md +++ b/.opencode/agent/designer-degraded.md @@ -62,6 +62,7 @@ Leia screenshots e referências **direto com a tool Read** — nunca peça ao hu ## Inegociáveis +- **Você é o tier certo para o smoke do guard dos agentes de design.** A semântica de `permission` é do **frontmatter, não do modelo**: validar guard de escrita / vetores enumerados pode rodar aqui (barato) em vez de queimar o `designer` frontier. O output continua `DEGRADED`. - **Nunca escreva fora de `docs/plans/*-ui-design*`** (o `.html` do item e a pasta de assets): a `permission` barra o resto — `src/`, `.agents/`, `.opencode/`, `scripts/` e planos existentes são somente leitura; não tente contornar por shell. - **Nunca implemente a feature** nem decida engenharia (schema, componente final, assinatura). - **Nunca certifique:** design degradado é insumo para o humano decidir. diff --git a/.opencode/agent/designer.md b/.opencode/agent/designer.md index 25d17f75..07d562f1 100644 --- a/.opencode/agent/designer.md +++ b/.opencode/agent/designer.md @@ -60,6 +60,7 @@ Leia screenshots, prints e referências **direto com a tool Read** — nunca pe ## Inegociáveis +- **Só design de fato:** você é despachado **apenas** para **Criar** ou **Criticar** o artefato `docs/plans/-ui-design*` de item que **muda UI**. Se a task não for isso — smoke/validação de `permission`/guard/frontmatter, teste de visão/sanidade de imagem, exploração (`@explore`), review geral (`@general`), escrever plano/PR/changelog, ou qualquer output fora do artefato — **recuse e devolva ao orquestrador**: é desperdício do tier frontier (doutrina `ui-design-html.md` §Escopo de dispatch). Manter o escopo é parte do seu papel. - **Nunca escreva fora de `docs/plans/*-ui-design*`** (o `.html` do item e a pasta de assets): a `permission` barra o resto — `src/`, `.agents/`, `.opencode/`, `scripts/` e planos existentes são somente leitura; não tente contornar por shell. - **Nunca implemente a feature** nem decida engenharia (schema, componente final, assinatura). - **Nunca certifique sozinho:** o design final é decisão humana no gate; mudança material volta ao PR. diff --git a/docs/AGENT-OPS.md b/docs/AGENT-OPS.md index 1bf56bdc..1fd11a0b 100644 --- a/docs/AGENT-OPS.md +++ b/docs/AGENT-OPS.md @@ -21,7 +21,7 @@ main → push dispara deploy.yml sozinho (ou workflow_dispatch manual) → prefl **Skills:** `plan-issue` (intenção + Issues) → `work-issue` (humano: Issue já claimada → impl plan → confirmação → execução) ou `agent-work-issue` (pool: já claimada → impl plan → execução sem pausa) → `/simplify` → `capture-review-debts` → PR `--base main` (GitHub) → `project-status`. `docs/roadmap.md` = legado congelado; fonte canônica = Issues do GitHub (`github.com/fsolla/teqo/issues`). -**Design hi-fi (OPS113):** item que muda UI passa pelo agente `designer` (`model: openai/gpt-5.6-sol`; crítica contestada/pixel-critical em `openai/gpt-6-astra`, mesmo agente via `/models`); a doutrina `.agents/skills/plan-issue/ui-design-html.md` é a fonte única do artefato `docs/plans/-ui-design.html` (+ `-ui-design-assets/*.svg`), que é a fonte de verdade do port — o implementador porta classe-a-classe. Indisponibilidade/quota desce a ladder — `opencode-go/deepseek-v4.1-flash` (agente `designer-degraded`) → `deepseek/deepseek-flash` (inline pelo orquestrador) — sempre gravando o tier no PR (`Design tier:`) — ou na Issue, no fluxo autônomo sem tier primário, que não abre PR e flipa para `blocked`; tier degradado marca `DEGRADED`, não certifica e para em sign-off humano (triggers no `work-issue`/`agent-work-issue`: OPS115). Ambos têm `permission` fail-closed: `edit` negado fora de `docs/plans/*-ui-design*` e `bash` com os **vetores de escrita enumerados** negados (redirecionamento, `sed -i`, `tee`, shells/interpretadores — OPS117), preservando leitura e o `prettier --write` do artefato; o resto do shell fica em `ask` (o guard cobre os vetores, não é sandbox de SO). +**Design hi-fi (OPS113):** item que muda UI passa pelo agente `designer` (`model: openai/gpt-5.6-sol`; crítica contestada/pixel-critical em `openai/gpt-6-astra`, mesmo agente via `/models`); a doutrina `.agents/skills/plan-issue/ui-design-html.md` é a fonte única do artefato `docs/plans/-ui-design.html` (+ `-ui-design-assets/*.svg`), que é a fonte de verdade do port — o implementador porta classe-a-classe. Indisponibilidade/quota desce a ladder — `opencode-go/deepseek-v4.1-flash` (agente `designer-degraded`) → `deepseek/deepseek-flash` (inline pelo orquestrador) — sempre gravando o tier no PR (`Design tier:`) — ou na Issue, no fluxo autônomo sem tier primário, que não abre PR e flipa para `blocked`; tier degradado marca `DEGRADED`, não certifica e para em sign-off humano (triggers no `work-issue`/`agent-work-issue`: OPS115). Ambos têm `permission` fail-closed: `edit` negado fora de `docs/plans/*-ui-design*` e `bash` com os **vetores de escrita enumerados** negados (redirecionamento, `sed -i`, `tee`, shells/interpretadores — OPS117), preservando leitura e o `prettier --write` do artefato; o resto do shell fica em `ask` (o guard cobre os vetores, não é sandbox de SO). **Escopo de dispatch fechado** (`ui-design-html.md` §Escopo de dispatch): o tier frontier só roda **Criar/Criticar** o artefato de item que muda UI — smoke/validação de `permission`/config, teste de visão, exploração e escrita de plano vão para o **modelo padrão da sessão** (o smoke do guard dos agentes de design roda no `designer-degraded`, barato, pois a semântica de `permission` é do frontmatter, não do modelo); prefira **uma** sessão para N itens UI a N sessões frias; agente de design sem pin (`designer-campanha-solla`) herda o modelo da sessão — dispare a sessão barata e só troque para frontier no design. - **Agente faz sozinho:** claim (pool-supervisor no pool; humano via `pnpm agent:claim` fora da sessão — OPS33: `worktree next` claima) → implementa → **e2e local afetado** (OPS72, discricionário: roda os e2e que criou + os da mesma superfície — `pnpm test:e2e:affected`; ver skill) → **`pnpm push`** (origin = GitHub) → PR via `node scripts/github-pr.mjs` **Ready** (nunca draft) com `Closes #N` → o safety net `agent-pr-ready-automerge.yml` arma o **auto-merge nativo do GitHub** (`enablePullRequestAutoMerge`, rebase) — o servidor mergea quando o required check `CI (PR) / checks` fica verde. Regra always-on: `.agents/rules/agent-pr-workflow.mdc`. Em Cursor Cloud: `ManagePullRequest` com `draft: false` (o default draft da tool **não** vale neste repo) — o merge continua pelo safety net. - **Só humano:** aprovar o environment `production` no run (o único gate de publicação — OPS104 nunca auto-aprova; OPS107: aprove só o run escolhido e rejeite os demais — runs não aprovados não publicam nada), envs de produção no homeserver (`~/stack/teqo-1313.env`, fora do repo — o deploy as lê via BuildKit secrets), `GITHUB_TOKEN` local (PAT) para os scripts GitHub, secrets no repo GitHub, `pnpm configure:branch-protection` (idempotente; reaplicar se o drift voltar), runbook manual/rollback (`docs/ops/teqo-1313-deploy.md`). diff --git a/docs/changelog/2026-09-16-ops123.md b/docs/changelog/2026-09-16-ops123.md new file mode 100644 index 00000000..43e75953 --- /dev/null +++ b/docs/changelog/2026-09-16-ops123.md @@ -0,0 +1 @@ +**OPS123 — escopo de dispatch do designer frontier: só design de fato (2026-09-16):** fecha o vazamento de cota do agente `designer` (`openai/gpt-5.6-sol`, OAuth ChatGPT Plus) que queimou a cota semanal do plano numa manhã — 12 das 14 sessões `openai` do dia 2026-09-16 foram smoke/validação não-design (guard de escrita OPS117, smoke OPS113, teste de visão) rodadas no frontier, além de 5 subagentes `designer` frios em paralelo para o lote do plan-issue-36. A regra de escopo passa a ter **um dono**: a doutrina `.agents/skills/plan-issue/ui-design-html.md` ganha a seção **§Escopo de dispatch** — o tier frontier só roda **Criar**/**Criticar** o artefato `docs/plans/-ui-design*` de item que muda UI; **lista fechada** do que nunca vai ao frontier (smoke/validação de `permission`/guard/frontmatter/`model:`/config de agente — a semântica de `permission` é do frontmatter, não do modelo; teste de visão/sanidade de imagem; `@explore`/`@general`/`@solla-comunicacao`; escrita/revisão de plano, impl plan, PR, changelog ou doc) e um **gate de 3 perguntas** antes de disparar (muda UI? Criar/Criticar? output é o artefato?). Os dispatchers **apontam para o dono** (sem twinning): `plan-issue/SKILL.md` (regra dura #4, `Quando` do sub-agente Designer e passo 3d) e `work-issue/execution-pipeline.md §Design` (bloco "Fora do `designer` frontier de vez" — proibição, não non-trigger). O próprio `.opencode/agent/designer.md` ganha o inegociável **"Só design de fato"** (recusa e devolve ao orquestrador fora do escopo); `designer-degraded.md` declara-se o tier certo para o smoke do guard dos agentes de design (mesmo ruleset de `permission`, barato); `designer-campanha-solla.md` (sem `model:` pin) avisa que herda o modelo da sessão — dispare a sessão barata e só troque para o frontier no design. **Anti fan-out:** prefira **uma** sessão `designer` para N itens UI do mesmo lote (cache reusado; o system prompt frio de ~25K tokens é pago uma vez) a N sessões frias em paralelo. Frontmatter/`permission` intactos (o guard OPS117 segue verde). `docs/AGENT-OPS.md` atualizado. Testes: `tests/unit/opencodeAgents.unit.spec.ts` ganha 6 asserções pinando o contrato (seção da doutrina, gate, recusa do agente, escopo do `designer-degraded`, referência nos dois skills). Sem migration, sem schema, sem UI, sem acesso/Consent. Issue #1091. diff --git a/tests/unit/opencodeAgents.unit.spec.ts b/tests/unit/opencodeAgents.unit.spec.ts index 54e6d717..784e5f4f 100644 --- a/tests/unit/opencodeAgents.unit.spec.ts +++ b/tests/unit/opencodeAgents.unit.spec.ts @@ -62,6 +62,11 @@ const frontmatter = (agent: string) => { 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}:`) @@ -90,3 +95,56 @@ describe('design agents share the OPS117 bash write guard', () => { expect(block(agent, 'edit')).toEqual([`'*': deny`, `'docs/plans/*-ui-design*': allow`]) }) }) + +// 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', + ) + }) +})