From 038758eb81434c3d6a40146e292a81a47736dc0e Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Sun, 23 Aug 2026 15:11:58 -0300 Subject: [PATCH] =?UTF-8?q?docs(OPS47):=20alinha=20plano=20do=20agent-pool?= =?UTF-8?q?=20=C3=A0=20pol=C3=ADtica=20main-based=20p=C3=B3s-OPS71?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - agent-pool-orchestrator.md: PR --base stage + issue-done-on-stage-merge.yml + promote stage→main viram --base main + issue-done-on-main-merge.yml + sem promote; migration-lock → countOpenSchemaPrs; model-selection → agent-pool-models.mjs; agent-github.mjs → agent-forgejo.mjs; workflow marcado removido OPS65 (caminho real .forgejo/workflows/agent-pool.yml) - agent-pool/SKILL.md: L79 sem 'Antes do promote'; L105 agent-pool-github→forgejo - comentário/label: agent-pool-state.mjs 'merge in stage'→main; agentPoolState.unit.spec.ts label 'on stage merge'→main - changelog 2026-08-23-ops47.md (Closes #735) --- .agents/skills/agent-pool/SKILL.md | 4 +- docs/CHANGELOG-AGENTS.md | 2 + docs/changelog/2026-08-23-ops47.md | 1 + docs/plans/agent-pool-orchestrator.md | 148 ++++++++++-------- ...aponta-para-plano-com-politica-pre-impl.md | 75 +++++++++ scripts/agent-pool.mjs | 6 +- scripts/lib/agent-pool-state.mjs | 2 +- tests/unit/agentPoolState.unit.spec.ts | 2 +- 8 files changed, 164 insertions(+), 76 deletions(-) create mode 100644 docs/changelog/2026-08-23-ops47.md create mode 100644 docs/plans/agent-pool-skill-md-aponta-para-plano-com-politica-pre-impl.md diff --git a/.agents/skills/agent-pool/SKILL.md b/.agents/skills/agent-pool/SKILL.md index c4f5fc96..5ed492bf 100644 --- a/.agents/skills/agent-pool/SKILL.md +++ b/.agents/skills/agent-pool/SKILL.md @@ -76,7 +76,7 @@ Pergunte de forma explícita, por exemplo: ## Pré-requisitos (uma vez) 1. **Repo secret `CURSOR_API_KEY`** (Settings → Secrets and variables → Actions) — chave de cursor.com/dashboard/api. Sem ela o tick live morre antes de spawnar (fail-closed). -2. Workflow mergeado em `main` — `schedule` e o trigger de merge em `main` **só rodam da default branch**. Antes do promote, use dispatch na branch (`--ref`). +2. Workflow mergeado em `main` — `schedule` e o trigger de merge em `main` **só rodam da default branch**. (Histórico: não há mais promote `stage→main` — removido em 2026-08-01 — nem dispatch no workflow, que foi removido no OPS65.) 3. Fila com Issues `ready` elegíveis (`pnpm agent:pool -- status` / `pnpm agent:status`) e nenhum audit solitário em curso (se audit: `pause`). 4. Validar o ambiente: `pnpm agent:pool -- doctor` (gh auth, repo variables, chave Cursor, tabela de modelos `/v1/models`). @@ -102,7 +102,7 @@ Issues nascidas de `plan-issue` com `--plan` **não** entram como `ready`: ficam - **Falha terminal** (run ERROR/CANCELLED/EXPIRED ou fim sem PR): tick comenta, move a Issue para `blocked` e arquiva o agente. **Triage humana**: ler o run em cursor.com/agents, decidir — re-`ready` manual se transitório (o circuit breaker recusa a 3ª tentativa automática), corrigir a spec se sistêmico. - **Worker travado**: archive em cursor.com/agents → o próximo tick reconcilia como falha documentada. - **Duplicata**: impossível em condições normais (alocador único + lock otimista + `agentId` idempotente); o tick cancela runs extras se alguém spawnar manualmente. -- **`needs:migration`/`serializes:[migrations]`**: o tick não spawna enquanto houver PR aberto tocando schema (predicado `countOpenSchemaPrs` em `agent-pool-github.mjs`) — re-avalia a cada tick. +- **`needs:migration`/`serializes:[migrations]`**: o tick não spawna enquanto houver PR aberto tocando schema (predicado `countOpenSchemaPrs` em `agent-pool-forgejo.mjs`) — re-avalia a cada tick. - **Audit solitário**: `pause` antes, `resume` depois (a skill `engineering-audit` lembra). ## Smoke remoto (aceite — rodar na primeira ativação) diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index 099ae789..3b2538cf 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -6,6 +6,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente **OPS46-S1 — hook ESM mapeia next/cache incondicionalmente (2026-08-23):** débito da revisão estrutural do OPS46 (issue #789): `tests/helpers/e2eEsmResolve.mjs` remapeava `next/cache` → `next/cache.js` sem tentar a resolução bare — quando o next publicar um `exports` map, o remap podia mascarar erro de resolução real (fail-loud vira fail-silent). Fix no branch `next/cache`: `await nextResolve(specifier, context)` primeiro; só em `ERR_MODULE_NOT_FOUND` remapeia para `next/cache.js`; qualquer outro erro propaga. Branch `react-server` intocado (injeção de conditions não mascara erro). Spec unit novo `tests/unit/e2eEsmResolve.unit.spec.ts` (3 casos com `nextResolve` mockado: bare-ok sem remap, `ERR_MODULE_NOT_FOUND` remapeia com `context` intacto, erro de outro código propaga). Verificação: `gate:fast` verde (unit 2432), sanity OPS72: `playwright test --list` coleta 202 testes com o hook carregado. Gatilho de reavaliação: bump major do next (fallback vira código morto e pode ser removido). Impl plan: `docs/plans/ops46-s1-hook-esm-bare-first-impl.md`. +**OPS47 — agent-pool/SKILL.md aponta para plano com política pré-main obsoleta (--base stage + auto-merge) (2026-08-23):** docs-only, zero mudança de comportamento. O `.agents/skills/agent-pool/SKILL.md:18` cita `docs/plans/agent-pool-orchestrator.md` como referência viva de arquitetura ("Arquitetura e elegibilidade"), mas o plano (escrito 2026-07-30, era pré-main) ainda descrevia a política obsoleta: PR `--base stage`, `issue-done-on-stage-merge.yml`, promote `stage→main`, `refresh-stage` — realidade removida em 2026-08-01 (promote/refresh) e 2026-08-12 (migration-lock). **Edição linha-a-linha do plano (dono do concern), frame histórico do pool dormente (OPS65) preservado:** overview e resumo executivo passam a "workers PR direto para main" com nota do promote removido; diagrama mermaid `pull_request closed em stage`→`em main` e `merge em main → issue-done-on-main-merge.yml`; contrato de prompt do worker `gh pr create --base stage`→`--base main` (reflete o vivo: `POOL_BASE_REF='main'` em `scripts/agent-pool.mjs:70` e prompt `--base main` em `scripts/lib/agent-pool-prompt.mjs:40-41`); gargalo de migrations reescrito de "job `migration-lock`" para `countOpenSchemaPrs` (`scripts/lib/agent-pool-forgejo.mjs:83`, com nota da remoção do job em 2026-08-12); referências à skill deletada `model-selection` → `scripts/lib/agent-pool-models.mjs`; `scripts/lib/agent-github.mjs` (inexistente) → `agent-forgejo.mjs`; `.github/workflows/agent-pool.yml` e `gh workflow run agent-pool.yml` marcados como removidos no OPS65 (dispatch falha 404); `startingRef: 'stage'`→`POOL_BASE_REF` (=`'main'`); D7/D8/D1 e rabbit holes alinhados (sem `refresh-stage`, sem "slot ocupado até merge em stage"). **SKILL.md:** resíduo L79 "Antes do promote, use dispatch na branch" → nota histórica (promote removido 2026-08-01, workflow removido OPS65). **Touch-ups de comentário/label (sem mudança de lógica):** `tests/unit/agentPoolState.unit.spec.ts:103` "CI flipped on stage merge"→"on main merge"; `scripts/lib/agent-pool-state.mjs:131` "merge in stage"→"merge in main". Fora de escopo (órfão/histórico): `docs/plans/paradigma-agentes-paralelos.md`; código do pool e workflows (dormentes, não tocados). Impl plan: `docs/plans/agent-pool-skill-md-aponta-para-plano-com-politica-pre-impl.md`. + **OPS46 — infra de teste e2e: coleta local consertada (next/cache) + allocator de município sem colisão no wrap (2026-08-23):** entrega de dois débitos do OPS45 (capture-review-debts). **Fase 1 — e2e local não coleta:** `pnpm exec playwright test --list` falhava em todo spec com `Cannot find module 'node_modules/next/cache'` (e o diagnóstico correto do CI-vs-máquina: não há delta — o CI só roda pelo script `pnpm test:e2e`, que carrega `--conditions=react-server --import=tsx/esm` no `NODE_OPTIONS`; comando bare não tinha nada). Causa: `next@15` não declara `exports` → ESM nativo não resolve `next/cache` (o arquivo é `cache.js`), e `server-only` precisa da condição `react-server`. Fix no runner, zero mudança em produção: hook de resolução ESM vitest-free `tests/helpers/e2eEsmResolve.mjs` (mapeia `next/cache`→`next/cache.js` e injeta `react-server` nas conditions) registrada no processo main via `module.register` e propagada aos workers via `NODE_OPTIONS=--import=e2eEsmLoader.mjs` no `playwright.config.ts` — só quando os flags do script já não estão presentes. Aceite: bare `playwright test --list` coleta 198 testes em 41 arquivos (antes: 0); runs reais bare admin 5/5 e campaign 18/18; `pnpm test:e2e` intocado. **Fase 2 — allocator de município com wrap destrutivo:** a sequência persistente `campaign_fixture_municipality_alloc` nunca é resetada e um ciclo completo de suíte claima ~505 do catálogo de 435 (medido: 432 no int + 73 no e2e) → o wrap acontece em todo ciclo em DB persistente de worktree, e dois runs concorrentes podiam receber o mesmo índice e purgar as linhas vivas um do outro (purge-on-claim). Fix: registry de claims em módulo compartilhado vitest-free `tests/helpers/campaignMunicipalityAllocator.ts` — tabela `campaign_fixture_municipality_claims` (`index` PK, `run_id`, `claimed_at`), claim via `INSERT ON CONFLICT DO NOTHING RETURNING` (conflito = slot vivo → pula; claim stale >2h de run crashado → rouba; tudo ocupado → erro claro de exaustão), release no `cleanup()` dos dois fixtures (int + e2e, só quando claimaram). Guard estático novo no `testMunicipalityAllocatorConventions` (proíbe `nextval`/sequence fora do módulo) + spec int de stress `campaignMunicipalityAllocator.int.spec.ts` (exclusividade viva por 3 wraps, exaustão+recuperação, steal, release). Verificação: tsc/lint/format/cycles verdes (knip: limitação P3 pré-existente de não carregar payload.config, igual no HEAD), unit 2420 + int 819 (92 arquivos) verdes com tabela de claims zerada ao final, e2e local afetado (admin 5/5 + campaign 18/18 bare) verdes; importMap.js restaurado com envs `S3_*` dummy após o `--list` bare regenerá-lo sem elas (classe OPS69 — o guard do #87 cobre no CI). Impl plan: `docs/plans/ops46-infra-teste-e2e-local-allocator-impl.md`. **E1 — extração do hook compartilhado de busca assíncrona com debounce (2026-08-23):** a 3ª cópia do efeito de busca async (requestId + `setTimeout(250)` + loading/failed) — introduzida pela C143 em `DemandResponsibleMultiSelect` (demand), e presente em `ResponsibleMultiSelect` (C90) e `AsyncSearchCombobox` — vira o hook `useAsyncSearchOptions({ open, query, search, isQueryReady? }) → { options, loading, failed }` (`src/hooks/useAsyncSearchOptions.ts`, novo; constante `ASYNC_SEARCH_DEBOUNCE_MS` exportada). Os 3 componentes consomem o hook e mantêm só o que é seu (grupos por typeLabel, avatares, headings de sugestões do município, `pinnedOptions`, filtro de selecionados, mensagens de vazio); semântica de query vazia preservada por consumidor (Demand busca sempre → sugestões; ResponsibleMultiSelect reseta via `isContactSearchQueryReady`; combobox com gate configurável — o efeito sync `selected←value` fica no componente). Fix pontual: server actions intocadas (continuam props — hook client-safe por injeção), sem schema/migration/access/Consent. Testes: spec unit novo `tests/unit/useAsyncSearchOptions.unit.spec.ts` (renderHook + fake timers, 9 casos: debounce/trim, coalesce, gate reset, closed, failed + retry, in-flight loading, out-of-order, quirk gate→not-ready com resposta stale pinado — comportamento herdado dos 3 originais, registrado na decisão D3 do impl plan). Impl plan `docs/plans/extrair-hook-busca-assincrona-debounce-impl.md`. Verificação: unit 2428 + int 815 verdes, e2e activity/demand (campaignActivity, campaignRegisterDemand, campaignDemandVisibility) verdes em re-run (1 flake de cold-compile pré-existente isolado), tsc/lint/format/cycles/build verdes; knip com o erro de load pré-existente (exit 0); fora de escopo: `WizardMunicipalitySearchStep` (4ª variante, AbortController — defer com gatilho no plano). diff --git a/docs/changelog/2026-08-23-ops47.md b/docs/changelog/2026-08-23-ops47.md new file mode 100644 index 00000000..1e73cd74 --- /dev/null +++ b/docs/changelog/2026-08-23-ops47.md @@ -0,0 +1 @@ +**OPS47 — agent-pool/SKILL.md aponta para plano com política pré-main obsoleta (--base stage + auto-merge) (2026-08-23):** docs-only, zero mudança de comportamento. O `.agents/skills/agent-pool/SKILL.md:18` cita `docs/plans/agent-pool-orchestrator.md` como referência viva de arquitetura ("Arquitetura e elegibilidade"), mas o plano (escrito 2026-07-30, era pré-main) ainda descrevia a política obsoleta: PR `--base stage`, `issue-done-on-stage-merge.yml`, promote `stage→main`, `refresh-stage` — realidade removida em 2026-08-01 (promote/refresh) e 2026-08-12 (migration-lock). **Edição linha-a-linha do plano (dono do concern), frame histórico do pool dormente (OPS65) preservado:** overview e resumo executivo passam a "workers PR direto para main" com nota do promote removido; diagrama mermaid `pull_request closed em stage`→`em main` e `merge em main → issue-done-on-main-merge.yml`; contrato de prompt do worker `gh pr create --base stage`→`--base main` (reflete o vivo: `POOL_BASE_REF='main'` em `scripts/agent-pool.mjs:70` e prompt `--base main` em `scripts/lib/agent-pool-prompt.mjs:40-41`); gargalo de migrations reescrito de "job `migration-lock`" para `countOpenSchemaPrs` (`scripts/lib/agent-pool-forgejo.mjs:83`, com nota da remoção do job em 2026-08-12); referências à skill deletada `model-selection` → `scripts/lib/agent-pool-models.mjs`; `scripts/lib/agent-github.mjs` (inexistente) → `agent-forgejo.mjs`; `.github/workflows/agent-pool.yml` e `gh workflow run agent-pool.yml` marcados como removidos no OPS65 (dispatch falha 404); `startingRef: 'stage'`→`POOL_BASE_REF` (=`'main'`); D7/D8/D1 e rabbit holes alinhados (sem `refresh-stage`, sem "slot ocupado até merge em stage"). **SKILL.md:** resíduo L79 "Antes do promote, use dispatch na branch" → nota histórica (promote removido 2026-08-01, workflow removido OPS65). **Touch-ups de comentário/label (sem mudança de lógica):** `tests/unit/agentPoolState.unit.spec.ts:103` "CI flipped on stage merge"→"on main merge"; `scripts/lib/agent-pool-state.mjs:131` "merge in stage"→"merge in main". Fora de escopo (órfão/histórico): `docs/plans/paradigma-agentes-paralelos.md`; código do pool e workflows (dormentes, não tocados). Impl plan: `docs/plans/agent-pool-skill-md-aponta-para-plano-com-politica-pre-impl.md`. diff --git a/docs/plans/agent-pool-orchestrator.md b/docs/plans/agent-pool-orchestrator.md index ed40027a..6661e933 100644 --- a/docs/plans/agent-pool-orchestrator.md +++ b/docs/plans/agent-pool-orchestrator.md @@ -1,6 +1,6 @@ --- name: Agent pool orchestrator -overview: 'Supervisor remoto stateless (GitHub Actions tick) que mantém até 5 Cursor Cloud Agents rodando work-issue sobre Issues ready; claim coordenado pelo supervisor, modelo da Issue no spawn, estado em repo variables + derivação; promote stage→main continua humano.' +overview: 'Supervisor remoto stateless (GitHub Actions tick) que mantém até 5 Cursor Cloud Agents rodando work-issue sobre Issues ready; claim coordenado pelo supervisor, modelo da Issue no spawn, estado em repo variables + derivação; workers PR direto para main (o promote stage→main foi removido em 2026-08-01).' todos: - id: fase0-spike content: 'Spike API Cursor: GET /v1/models, spawn com model explícito, agentId idempotente, boot real' @@ -26,8 +26,9 @@ isProject: false Um **supervisor remoto stateless** mantém até **5 Cursor Cloud Agents** em paralelo, cada um executando `work-issue` sobre uma GitHub Issue `ready` e elegível para autonomia. O supervisor -é um **workflow do GitHub Actions** (`.github/workflows/agent-pool.yml`) que roda -`node scripts/agent-pool.mjs tick` a cada 10 minutos, a cada merge em `stage`, e sob +é um **workflow do GitHub Actions** (na prática `.forgejo/workflows/agent-pool.yml`, removido no +OPS65; descrito aqui como histórico "O que foi") que roda +`node scripts/agent-pool.mjs tick` a cada 10 minutos, a cada merge em `main`, e sob `workflow_dispatch` — nenhum processo local, nenhum daemon, laptop fechado não importa. A cada tick, um script Node determinístico (testável, sem deps npm): lê o estado do pool @@ -36,7 +37,8 @@ calcula `gap = maxSlots - ativos`, **claima ele mesmo** as próximas Issues eleg `ready→in-progress` com lock otimista — alocador único, sem race) e spawna workers via `POST https://api.cursor.com/v1/agents` com o `model:` do frontmatter da Issue (fallback `composer-2.5`). Para quando o humano mandar (`action=stop`) ou quando a fila drenar. -`agent:promote` nunca é chamado — promote `stage→main` continua humano. +Workers abrem PR `--base main` com `Closes #N` (o promote `stage→main` não existe mais — +removido em 2026-08-01; o merge acontece direto em `main`). Decisão central: o supervisor é **GitHub Actions**, não um Cursor Automation — o tick é lógica determinística com testes unitários, e cada tick de Automation queimaria um slot de agente (de 8 @@ -49,11 +51,11 @@ do plano Pro) + tokens a cada 10 min. Automation fica documentado como plano B. ```mermaid flowchart TB subgraph humano [Humano] - Cmd["gh workflow run agent-pool.yml -f action=start|stop|status"] + Cmd["gh workflow run agent-pool.yml -f action=start|stop|status (workflow removido OPS65 — falha 404)"] end subgraph sup [Supervisor remoto — GitHub Actions] - Trig["Triggers: schedule */10min · pull_request closed em stage · workflow_dispatch"] + Trig["Triggers: schedule */10min · pull_request closed em main · workflow_dispatch"] Tick["pnpm agent:pool tick — scripts/agent-pool.mjs"] Vars["Estado: repo variables POOL_*"] Derive["Ativos derivados: Cursor API + labels GitHub"] @@ -64,13 +66,13 @@ flowchart TB subgraph workers [Workers — Cursor Cloud Agents, cap 5] Spawn["POST /v1/agents — prompt work-issue + model da Issue"] - W["worker × N: claim pré-feito → implementa → PR base stage → watch CI até merge"] + W["worker × N: claim pré-feito → implementa → PR base main → watch CI até merge"] Spawn --> W end Cmd -->|"workflow_dispatch (único canal)"| Trig Tick -->|"claim coordenado + spawn até gap"| Spawn - W -->|"PR + auto-merge CI green"| Merge["merge em stage → issue-done-on-stage-merge.yml flip done"] + W -->|"PR + auto-merge CI green"| Merge["merge em main → issue-done-on-main-merge.yml flip done"] Merge -->|"trigger pull_request closed"| Trig ``` @@ -92,10 +94,11 @@ Cada tick é um job curto (< 2 min) e idempotente que executa, nesta ordem: (sai da fila, humano triage), archive do agente, circuit breaker (≥2 falhas na mesma Issue → nunca re-enfileira; conta via comentários do pool). - run não-terminal → slot ocupado. -4. **Gargalo de migrations** — mesma query do job `migration-lock` - (`gh pr list --state open --json files`): se ≥1 PR aberto toca `src/migrations/` ou - `payload-types.ts`, Issues `needs:migration` / `serializes:[migrations]` saem da fila **deste - tick** (re-avaliadas no próximo). +4. **Gargalo de migrations** — `countOpenSchemaPrs` (`scripts/lib/agent-pool-forgejo.mjs`): + se ≥1 PR aberto toca `src/migrations/` ou `payload-types.ts`, Issues `needs:migration` / + `serializes:[migrations]` saem da fila **deste tick** (re-avaliadas no próximo). O job CI + `migration-lock` foi removido em 2026-08-12; a guarda sobrevive neste limite de PRs de schema + abertos. 5. **Fila elegível** — `isAutonomousClaimable` (seção 3) ordenada por prio → mais antiga (paridade com `agent:claim`). 6. **Spawn** — `gap = maxSlots - ativos`; claima e spawna até `gap` workers (seção 4 e 5). @@ -119,16 +122,19 @@ cron, e não há nada no loop que um tick de 30 s não faça. | --------------------------------------------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `enabled`, `maxSlots`, `paused`, `startedAt`, `startedBy` | **Repo variables** (`gh variable get/set POOL_*`) | Escalares de config; API própria; legíveis por humano no browser (Settings → Variables) | | `activeRuns: [{runId, issueNumber, claimedAt}]` | **Derivado, não persistido** | Estado persistido dessincroniza da realidade (crash entre spawn e write). As duas fontes de verdade — status do run na Cursor API e labels/comentários no GitHub — bastam; o comentário de claim na Issue (com a URL do agente `bc-…`) é o vínculo durável worker↔Issue | -| `lastTickAt`, histórico de ticks, último erro | **GHA runs + job summary** (`gh run list --workflow agent-pool.yml`) | O próprio run é o registro; não duplica | +| `lastTickAt`, histórico de ticks, último erro | **GHA runs + job summary** (`gh run list --workflow agent-pool.yml`) | O próprio run é o registro; não duplica (workflow removido no OPS65 — pool dormente) | | Reserva worker→Issue | **Comentário de claim na Issue** (marcador `pool-worker bc- `) | Visível a humanos, auditável, self-healing | O step "set/get variables" usa `gh variable` com `GITHUB_TOKEN` e `permissions: actions: write` (REST de Actions variables). Localmente, `pnpm agent:pool -- status` usa o `gh` auth do usuário. +(O pool está dormente desde o OPS65 — o workflow `agent-pool.yml` foi removido e o dispatch via +CLI falha 404; este estado era persistido enquanto o pool operava.) ### Canal canônico: **`workflow_dispatch`** (um só) ```bash # start (qualquer máquina/phone com gh auth; também via browser Actions → Run workflow) +# — workflow REMOVIDO no OPS65: dispatch falha 404 (pool dormente) gh workflow run agent-pool.yml -f action=start # maxSlots default 5 gh workflow run agent-pool.yml -f action=start -f maxSlots=3 @@ -155,17 +161,17 @@ Função pura em `scripts/lib/agent-pool-eligibility.mjs`, testada por tabela. E (labels, body/frontmatter, state), `doneIds`, `migrationBusy` (bool do tick), `now`. Saída: `{ ok: true } | { ok: false, reason }`. -| Condição | Decisão | Motivo | -| -------------------------------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------- | -| `state !== 'OPEN'` ou sem label `ready` | exclui (`not-ready`) | contrato básico do claim | -| label `in-progress` / `blocked` / `done` / `in-prod` | exclui (`state-label`) | estado é exclusivo | -| label `requirements-changed` | exclui (`needs-human`) | escopo mudou; gate humano pendente | -| label `needs:consent` | exclui (`needs-human`) | gate legal/produto — nunca autônomo | -| `depends:` com dep não satisfeita (não `done`/`in-prod`/fechada) | exclui (`blocked-by-deps`) | mesma regra de `agent-claim.mjs` (dep sem Issue = roadmap entregue, satisfeita) | -| `needs:migration` ou `serializes:[migrations]` **e** `migrationBusy` | exclui **neste tick** (`migration-busy`) | respeita o `migration-lock` (≤1 PR de schema); transitório, volta no próximo tick | -| sem link `docs/plans/` no body | **warn, não bloqueia** (v1) | preferir Issues com plano; loga no sumário | -| `POOL_PAUSED` (audit solitário) | pausa **global** no passo 2 do tick | engineering-audit é modo solitário (AGENT-OPS) | -| `model:` ausente/inválido | **não exclui** | fallback `composer-2.5` (seção 5) | +| Condição | Decisão | Motivo | +| -------------------------------------------------------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `state !== 'OPEN'` ou sem label `ready` | exclui (`not-ready`) | contrato básico do claim | +| label `in-progress` / `blocked` / `done` / `in-prod` | exclui (`state-label`) | estado é exclusivo | +| label `requirements-changed` | exclui (`needs-human`) | escopo mudou; gate humano pendente | +| label `needs:consent` | exclui (`needs-human`) | gate legal/produto — nunca autônomo | +| `depends:` com dep não satisfeita (não `done`/`in-prod`/fechada) | exclui (`blocked-by-deps`) | mesma regra de `agent-claim.mjs` (dep sem Issue = roadmap entregue, satisfeita) | +| `needs:migration` ou `serializes:[migrations]` **e** `migrationBusy` | exclui **neste tick** (`migration-busy`) | serializa migrations (≤1 PR de schema aberto via `countOpenSchemaPrs` — o job `migration-lock` do CI foi removido 2026-08-12); transitório, volta no próximo tick | +| sem link `docs/plans/` no body | **warn, não bloqueia** (v1) | preferir Issues com plano; loga no sumário | +| `POOL_PAUSED` (audit solitário) | pausa **global** no passo 2 do tick | engineering-audit é modo solitário (AGENT-OPS) | +| `model:` ausente/inválido | **não exclui** | fallback `composer-2.5` (seção 5) | Issues que exigem gate de `plan-issue` (wireframes, confirmação de produto) já chegam `blocked` ou sem `ready` — o predicado as exclui por construção, e isso é pinado em teste. @@ -175,7 +181,7 @@ contrato; uma label a mais é um passo manual a mais no `plan-issue` sem ganho d (as exclusões já são fail-closed). A fila do pool é uma **extensão** da fila do `agent-claim` (mesma ordenação, predicados mais -estritos). O builder da fila é extraído para `scripts/lib/agent-github.mjs` +estritos). O builder da fila é extraído para `scripts/lib/agent-forgejo.mjs` (`buildClaimQueue(openReady, byId)`) e compartilhado — `agent-claim.mjs` continua com comportamento idêntico (humano pode claimar `needs:consent` conscientemente; o pool, não). @@ -214,7 +220,8 @@ Parâmetros (só relevantes ao backoff de retry de tick): `spawnRetryBackoff = p ## 5. Seleção de modelo - Fonte: frontmatter `model:` da Issue (escrito por `agent:register --model`, tabela de slugs - em `.agents/skills/model-selection/SKILL.md`). + e regras em `scripts/lib/agent-pool-models.mjs` — a skill `model-selection` foi removida; as + regras de modelo migraram para o módulo). - Spawn: `model: { id: , params: [...] }` no body do `POST /v1/agents`. A API valida contra `GET /v1/models` (ids + aliases + params, ex.: `composer-2.5` com param `fast`). - **Namespace:** os slugs do repo (Task tool) e os da Cloud API são tabelas irmãs, não idênticas. @@ -226,7 +233,7 @@ Parâmetros (só relevantes ao backoff de retry de tick): `spawnRetryBackoff = p Omitir `fast` faz a API cair no default da variante (`fast=true` → usage `composer-2.5-fast`); o resolver pina `fast=false` sempre que o modelo anuncia o param. - **Fallback:** `model:` ausente, desconhecido ou rejeitado pela API → **`composer-2.5` + `fast=false`** - (pool "Cursor Models", custo incluído — regra 1 da model-selection). Warn no sumário do tick. + (pool "Cursor Models", custo incluído — regra 1 do mapeamento em `agent-pool-models.mjs`). Warn no sumário do tick. - Validação: o tick consulta `GET /v1/models` uma vez por tick (cache em memória do processo) e nunca spawna com slug fora da lista — erro de validação não pode derrubar o tick. @@ -237,14 +244,14 @@ mas com a dependência transitiva habitual de `scripts/lib/cli.mjs` → `dotenv` deps como os workflows irmãos; corrigido em 2026-07-31 após o ERR_MODULE_NOT_FOUND da primeira ativação): -| Comando | Faz | -| ----------------------- | ------------------------------------------------------------------------------------------------------ | -| `start [--max-slots N]` | Wrapper: `gh workflow run agent-pool.yml -f action=start [-f maxSlots=N]` | -| `stop` | Idem `action=stop` (para de spawnar; ativos drenam) | -| `pause` / `resume` | Idem — modo audit solitário | -| `status` | Lê variables + Cursor API + Issues; imprime slots, Issues em voo, PRs, fila elegível, último tick/erro | -| `tick` | Executa a reconciliação (usado pelo workflow; local exige `--dry-run`) | -| `tick --dry-run` | Reconcilia **sem** claim/spawn/flip — imprime o plano do tick (default local) | +| Comando | Faz | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| `start [--max-slots N]` | Wrapper: `gh workflow run agent-pool.yml -f action=start [-f maxSlots=N]` — **falha 404 desde o OPS65** (workflow removido, pool dormente) | +| `stop` | Idem `action=stop` (para de spawnar; ativos drenam) | +| `pause` / `resume` | Idem — modo audit solitário | +| `status` | Lê variables + Cursor API + Issues; imprime slots, Issues em voo, PRs, fila elegível, último tick/erro | +| `tick` | Executa a reconciliação (usado pelo workflow; local exige `--dry-run`) | +| `tick --dry-run` | Reconcilia **sem** claim/spawn/flip — imprime o plano do tick (default local) | Flags de ambiente: `CURSOR_API_KEY` (obrigatória para spawn/archive/cancel; `status` degrada para "Cursor API indisponível" sem ela), `GH_TOKEN`/`gh auth` para GitHub. Logs: stdout @@ -257,26 +264,26 @@ rotação documentada no runbook). `GITHUB_TOKEN` com `issues: write` (flip/come ## 7. Mudanças no repo (lista de arquivos previstos) -| Arquivo | Ação | Fase | -| ---------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------- | -| `scripts/lib/agent-pool-eligibility.mjs` | novo — `isAutonomousClaimable` + fila do pool | 1 | -| `scripts/lib/agent-pool-state.mjs` | novo — variables get/set + derivação de ativos | 1 | -| `scripts/agent-pool.mjs` | novo — CLI da seção 6 | 1 (status/tick dry) → 2/3 (spawn/ciclo) | -| `scripts/lib/agent-github.mjs` | extrai `buildClaimQueue()` (mesma lógica, hoje inline em agent-claim) | 1 | -| `scripts/agent-claim.mjs` | consome `buildClaimQueue()` — **comportamento idêntico** | 1 | -| `scripts/lib/agent-pool-cursor.mjs` | novo — client fino da API v1 (models/agents/runs/cancel/archive) | 2 | -| `scripts/lib/agent-pool-prompt.mjs` | novo — template do prompt do worker | 2 | -| `scripts/lib/agent-pool-models.mjs` | novo — mapeamento slug repo → `model.id` Cloud + fallback | 2 | -| `.github/workflows/agent-pool.yml` | novo — dispatch (fase 2) + schedule + `pull_request` closed (fase 3) | 2→3 | -| `package.json` | script `agent:pool` | 1 | -| `tests/unit/agentPoolEligibility.unit.spec.ts` | novo — tabela do predicado + ordenação + migration cap | 1 | -| `tests/unit/agentPoolState.unit.spec.ts` | novo — derivação de ativos, failure path, circuit breaker, gap | 1→3 | -| `tests/unit/agentPoolModels.unit.spec.ts` | novo — mapping/fallback/validação | 2 | -| `tests/unit/agentPoolPrompt.unit.spec.ts` | novo — contrato do prompt (issue N, sem claim, PR base stage) | 2 | -| `docs/plans/agent-pool-orchestrator.md` | este plano + resultados do spike | 0 | -| `.agents/skills/agent-pool/SKILL.md` | novo — runbook humano (start/stop/status/triage) | 4 | -| `docs/AGENT-OPS.md` | linha na tabela de comandos + seção curta de secrets | 4 | -| `docs/CHANGELOG-AGENTS.md` | uma entrada curta | 4 | +| Arquivo | Ação | Fase | +| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | --------------------------------------- | +| `scripts/lib/agent-pool-eligibility.mjs` | novo — `isAutonomousClaimable` + fila do pool | 1 | +| `scripts/lib/agent-pool-state.mjs` | novo — variables get/set + derivação de ativos | 1 | +| `scripts/agent-pool.mjs` | novo — CLI da seção 6 | 1 (status/tick dry) → 2/3 (spawn/ciclo) | +| `scripts/lib/agent-forgejo.mjs` | extrai `buildClaimQueue()` (mesma lógica, hoje inline em agent-claim) | 1 | +| `scripts/agent-claim.mjs` | consome `buildClaimQueue()` — **comportamento idêntico** | 1 | +| `scripts/lib/agent-pool-cursor.mjs` | novo — client fino da API v1 (models/agents/runs/cancel/archive) | 2 | +| `scripts/lib/agent-pool-prompt.mjs` | novo — template do prompt do worker | 2 | +| `scripts/lib/agent-pool-models.mjs` | novo — mapeamento slug repo → `model.id` Cloud + fallback | 2 | +| `.forgejo/workflows/agent-pool.yml` | novo — dispatch (fase 2) + schedule + `pull_request` closed (fase 3) — **removido no OPS65** (pool dormente) | 2→3 | +| `package.json` | script `agent:pool` | 1 | +| `tests/unit/agentPoolEligibility.unit.spec.ts` | novo — tabela do predicado + ordenação + migration cap | 1 | +| `tests/unit/agentPoolState.unit.spec.ts` | novo — derivação de ativos, failure path, circuit breaker, gap | 1→3 | +| `tests/unit/agentPoolModels.unit.spec.ts` | novo — mapping/fallback/validação | 2 | +| `tests/unit/agentPoolPrompt.unit.spec.ts` | novo — contrato do prompt (issue N, sem claim, PR base main) | 2 | +| `docs/plans/agent-pool-orchestrator.md` | este plano + resultados do spike | 0 | +| `.agents/skills/agent-pool/SKILL.md` | novo — runbook humano (start/stop/status/triage) | 4 | +| `docs/AGENT-OPS.md` | linha na tabela de comandos + seção curta de secrets | 4 | +| `docs/CHANGELOG-AGENTS.md` | uma entrada curta | 4 | Nada em `src/` — sem migrations, sem gates de app (`tsc`/lint cobrem scripts; `knip`/`cycles` não são afetados por `scripts/`, verificar no gate). `pnpm gate:fast` antes de cada push, como @@ -290,15 +297,15 @@ sempre. (Settings → Secrets → Actions). 2. Fila com Issues `ready` (ver `pnpm agent:status`); nenhum audit em curso. 3. Workflow mergeado em `main` — **schedule e trigger de PR só rodam da default branch** - (antes do promote, tudo funciona via `workflow_dispatch` na branch do PR B/C). + (antes do merge, tudo funciona via `workflow_dispatch` na branch do PR B/C). **Start remoto:** `gh workflow run agent-pool.yml -f action=start` (ou browser). Em ≤1 tick (≤10 min; dispatch dispara na hora) os primeiros `min(maxSlots, fila)` workers aparecem em cursor.com/agents com nome `pool-i-…`. **Monitorar:** `pnpm agent:pool status` local, ou Actions → último run → job summary, ou -cursor.com/agents. Workers abrem PR `--base stage` com `Closes #N`; auto-merge no CI green; -`issue-done-on-stage-merge.yml` flipa `done`; o tick seguinte repõe o slot. +cursor.com/agents. Workers abrem PR `--base main` com `Closes #N`; auto-merge no CI green; +`issue-done-on-main-merge.yml` flipa `done`; o tick seguinte repõe o slot. **Stop/drain:** `action=stop` — próximo tick já não spawna; ativos seguem até o merge e o pool marca `POOL_ENABLED=false` ao drenar. Matar worker travado: cursor.com/agents → archive (o tick @@ -324,7 +331,7 @@ ou deixar bloqueada. - Cálculo de `gap` e cap hard (nunca spawna além de `maxSlots` mesmo com fila longa). - Model mapping: slug válido → id Cloud (+ effort/reasoning) **+ `fast=false` quando o param existe**; `-fast` **proibido** (strip + warn + pin `fast=false`); ausente/inválido → `composer-2.5` + `fast=false`; slug fora do `GET /v1/models` → fallback + warn. -- Prompt: contém `#N`, instrução de não-claim, `gh pr create --base stage`, `Closes #N`. +- Prompt: contém `#N`, instrução de não-claim, `gh pr create --base main`, `Closes #N`. - State: parse/serialize de variables; defaults. **Smoke manual remoto (critérios de aceite):** @@ -360,12 +367,14 @@ ou deixar bloqueada. - **Fase 4 — docs/ops (~1–2h):** PR D — skill `agent-pool`, AGENT-OPS, CHANGELOG, linha de pause na skill engineering-audit, smoke remoto completo (seção 9) executado e registrado. -Cada PR vai `--base stage` com CI green (fluxo normal de PR, mas **sem** `agent:register`/Issue -de spec — ops infra). **Schedule só ativa após o promote humano a `main`.** +Cada PR vai `--base main` com CI green (fluxo normal de PR, mas **sem** `agent:register`/Issue +de spec — ops infra). **Schedule só ativa após o merge em `main`.** (O promote `stage→main` foi +removido em 2026-08-01 — o merge acontece direto em `main`.) ## 11. Fora de escopo -- `pnpm agent:promote` (promote `stage→main` é humano — o pool nunca chama). +- `pnpm agent:promote` — **não existe mais** (script removido 2026-08-01): workers PR direto para + `main` com auto-merge nativo; o pool nunca teve promote. - Qualquer `DATABASE_URL` de stage/prod no supervisor ou nos workers (Cloud usa `.cursor/cloud-setup.sh` + seed mínimo; o workflow não recebe secrets de banco). - Editar Issues `in-progress` de outros agentes/humanos (o pool só toca as que ele claimou). @@ -386,7 +395,7 @@ task exige `isAutonomousClaimable` testável); custa zero slots de agente e zero concorrentes — 5 workers + supervisor Automation deixariam 2 para humanos e queimariam LLM a cada 10 min, ~144 ticks/dia); triggers nativos cobrem cron e merge; `workflow_dispatch` é remoto por definição. Mantém o norte "só GitHub + Cursor" (Actions é GitHub) e a família de scripts -`agent:*`/`refresh-stage` (precedente direto de ops via dispatch). +`agent:*` (precedente direto de ops via dispatch; `refresh-stage` foi removido em 2026-08-01). Rejeitadas: Automation cron (plano B documentado — o mesmo `pnpm agent:pool tick` pode ser embrulhado numa Automation depois, pois o script é agnóstico de superfície); supervisor 24/7 (timeout não documentado, slot permanente, LLM fazendo cron); VPS (fora do norte). @@ -421,11 +430,11 @@ validação (namespaces diferem; falha de spawn por slug inválido derrubaria o fail-closed; label extra é passo manual sem ganho. **D7 — PR do worker.** `autoCreatePR: false` no spawn — o worker segue a skill (`gh pr create ---base stage` + `Closes #N` + `gh pr merge --auto --merge` + watch). O auto-PR da API abriria -contra a base errada e sem o corpo do contrato. `startingRef: 'stage'` no spawn (diff mínimo -contra a base do PR). +--base main` + `Closes #N` + auto-merge nativo por rebase + watch). O auto-PR da API abriria +contra a base errada e sem o corpo do contrato. `startingRef: POOL_BASE_REF` (= `'main'`) no +spawn (diff mínimo contra a base do PR). -**D8 — Slot ocupado até o merge.** Conforme a spec: ativo = do claim ao merge em `stage` (ou +**D8 — Slot ocupado até o merge.** Conforme a spec: ativo = do claim ao merge em `main` (ou falha documentada). Efeito: o throughput do pool inclui a espera do CI (~dezenas de min por PR); com auto-merge isso é hands-off e correto por construção. Derivação: Issue `in-progress` com marcador do pool = ocupado. @@ -439,10 +448,11 @@ marcador do pool = ocupado. - **Custo 5× Cloud:** agents são cobrados a preço de API por modelo; default `composer-2.5` (pool incluído) e `maxSlots` configurável são as rédeas; spend limit no dashboard Cursor é pré-requisito operacional; `GET /v1/agents/{id}/usage` no `status` fica como follow-up. -- **Flake de CI com 5 PRs simultâneos:** `migration-lock` já serializa schema; e2e/unit por diff +- **Flake de CI com 5 PRs simultâneos:** o cap de schema (`countOpenSchemaPrs`) serializa + migrations; e2e/unit por diff limitam carga; risco real é fila de runners GHA — visível no job summary, fora do controle do pool (documentado, não mitigado em v1). -- **Migration-lock starvation:** humano abre PR de schema fora do pool → pool respeita o cap e +- **Migration starvation:** humano abre PR de schema fora do pool → pool respeita o cap e para de spawnar migrations; o inverso (pool segurando o único slot de migration enquanto o worker demora) é resolvido pelo drain natural + triage humano da Issue `blocked`. - **Timeout GHA 6h vs cron:** irrelevante no modelo stateless (tick < 2 min). Schedules do diff --git a/docs/plans/agent-pool-skill-md-aponta-para-plano-com-politica-pre-impl.md b/docs/plans/agent-pool-skill-md-aponta-para-plano-com-politica-pre-impl.md new file mode 100644 index 00000000..b1c83942 --- /dev/null +++ b/docs/plans/agent-pool-skill-md-aponta-para-plano-com-politica-pre-impl.md @@ -0,0 +1,75 @@ +# Impl: OPS47 — agent-pool/SKILL.md aponta para plano com política pré-main obsoleta + +Status: aprovado +Atualizado em: 2026-08-23 +Issue: #735 +Intenção: sem plano (body é spec — título) +Appetite restante: — + +## Leitura da intenção + +- **Outcome:** Alinhar `docs/plans/agent-pool-orchestrator.md` e os resíduos stage-era citados por `agent-pool/SKILL.md` à política main-based atual (PR `--base main` no GitHub, auto-merge nativo, flip `issue-done-on-main-merge.yml`, sem promote), de modo que a referência viva (SKILL.md:18) aponte para um plano factualmente correto — mantendo o frame histórico do pool dormente (OPS65). +- **O que NÃO negociar:** Frame histórico do pool dormente (L14-16 do SKILL.md e o tom "O que foi" do plano são legítimos — keep); docs-only, zero mudanças em `src/`/workflows/código do pool; não criar docs novos nem arquivar o plano inteiro; não tocar em `paradigma-agentes-paralelos.md` (órfão, fora de escopo); nunca editar `CHANGELOG-AGENTS.md` na mão (insert-only via `pnpm changelog:build`). +- **O que reavaliar:** Escopo dos resíduos — os comentários de label em `tests/unit/agentPoolState.unit.spec.ts:103` e `scripts/lib/agent-pool-state.mjs:131` são touch-ups opcionais (labels/comentários, não comportamento); decidir incluir ou deixar registrado como opcional. Conteúdo já corrente (SKILL.md L98 `--base main` + auto-merge, L100 promote dual OPS17/18, `startingRef` L425/467/484) fica intacto. + +## Abordagem recomendada + +**Opções consideradas:** A — editar o dono linha-a-linha; B — reescrever/arquivar o plano inteiro ou criar doc novo. +**Recomendação:** A — editar `docs/plans/agent-pool-orchestrator.md` linha-a-linha para a política main atual, preservando o frame histórico do pool dormente; corrigir o resíduo de SKILL.md:79. +**Rejeitadas:** B — violaria "edit the owner, don't twin" (AGENTS): o plano já é o dono do concern; reescrever do zero ou criar twin apagaria a história do paradigma e geraria dois donos. + +### Componentes / mudanças + +**1. `docs/plans/agent-pool-orchestrator.md`** (517 linhas) — edições por grupo: + +- **L3** — "promote stage→main continua humano": trocar por nota de que o promote foi removido (2026-08-01) e o merge agora acontece direto em `main`. +- **L30 / L56 / L428** — "merge em `stage`" / "pull_request closed em stage": atualizar para merge em `main` / `pull_request` closed em `main`. +- **L39** — "`agent:promote` nunca é chamado": ajustar para "não existe mais (script removido 2026-08-01)". +- **L67 / L275 / L300 / L327 / L363 / L424** — "PR base stage" / "`gh pr create --base stage`" → `--base main` (reflete o vivo: `POOL_BASE_REF='main'` em `scripts/agent-pool.mjs:70` e prompt `--base main` em `scripts/lib/agent-pool-prompt.mjs:40-41`). +- **L73** — "merge em stage → `issue-done-on-stage-merge.yml` flip done" → nome do workflow vivo `.github/workflows/issue-done-on-main-merge.yml`. +- **L95 / L165 / L442** — job `migration-lock` → substituir por `countOpenSchemaPrs` (`scripts/lib/agent-pool-forgejo.mjs:83`), com nota da remoção do job (2026-08-12) e que a guarda sobrevive nesse limite de PRs de schema abertos. +- **L217 / L229** — referências a `.agents/skills/model-selection/SKILL.md` → `scripts/lib/agent-pool-models.mjs` (skill deletada; regras de modelo migraram para o módulo). +- **L29 / L52 / L122 / L132-144 / L242 / L270** — `.github/workflows/agent-pool.yml` / `gh workflow run agent-pool.yml` → marcar como workflow removido no OPS65 (pool dormente); manter a descrição como histórico "O que foi", não como instrução operacional viva. +- **L293 / L364** — "promote humano a main" / **L368** "`pnpm agent:promote`": reescrever como descrição histórica — promote deletado 2026-08-01, o fluxo atual é PR → `main` com auto-merge nativo (rebase) e flip pós-merge. +- **L425** — `startingRef: 'stage'` no spawn → `startingRef: POOL_BASE_REF` (= `'main'`, `scripts/agent-pool.mjs:254`); o mecanismo em L467/484 já é real com base `main` → não é política stale, só alinhar o valor citado. + +**2. `.agents/skills/agent-pool/SKILL.md`** — fix do resíduo: + +- **L79** — "Antes do promote, use dispatch na branch (`--ref`)": remover/reformular — não há promote nem dispatch (404), o fluxo é PR `--base main` + auto-merge nativo. +- Manter intacto o frame histórico (L5 Forgejo Actions, L14-16 dormência, L94 "workflow removido OPS65 — falha 404") e o que já está corrente (L98 `--base main` + auto-merge; L100 promote dual OPS17/18). + +**3. Touch-ups opcionais (comentário/label, sem mudança de comportamento):** + +- `tests/unit/agentPoolState.unit.spec.ts:103` — label "CI flipped on stage merge" → "on main merge". +- `scripts/lib/agent-pool-state.mjs:131` — comentário "merge in stage" → "merge in main". + +**4. Changelog:** + +- Criar `docs/changelog/2026-08-23-ops47.md` (uma linha longa, formato `**OPS47 — (<date>):** <prose>`) e rodar `pnpm changelog:build` (nunca editar `CHANGELOG-AGENTS.md` na mão). + +## Fases verificáveis + +1. **Plano + SKILL.md** — aplicar as edições do grupo 1 e 2; validar tom histórico mantido e nenhum resíduo stage-era vivo. +2. **Changelog** — escrever `docs/changelog/2026-08-23-ops47.md` e rodar `pnpm changelog:build`. +3. **Gates** — docs-only → CI roda só os guards de docs; `pnpm changelog:check`; grep de saída (abaixo); push via `pnpm push`. + +## Rabbit holes / Não escopo + +- `docs/plans/paradigma-agentes-paralelos.md` — órfão/histórico, fora de escopo. +- CI workflows, `.github/workflows/*`, `scripts/agent-pool*.mjs` e demais código do pool — dormentes (OPS65), não tocar. +- `tests/unit/agentPoolState.unit.spec.ts` e `scripts/lib/agent-pool-state.mjs` — só o comentário do label se os touch-ups opcionais forem incluídos; nenhuma lógica muda. +- Não regenerar/não criar migração, não tocar em dados. + +## Riscos e mitigação + +- **Edição linha-a-linha pode apagar o tom histórico do plano** → mitigação: manter o frame "O que foi"/dormente em cada grupo editado (OPS65, OPS71/OPS76 como marcos), nunca reescrever parágrafos inteiros sem preservar a narrativa. +- **`CHANGELOG-AGENTS.md` é insert-only** → nunca editar na mão; única via é `pnpm changelog:build` (OPS44). +- **Resíduo stage esquecido em linha não mapeada** → mitigação: grep de saída como gate antes do push. +- **Referência cruzada quebrada ao renomear skill → módulo** → verificar que `scripts/lib/agent-pool-models.mjs` existe e citar o caminho real (sem inventar). + +## Aceite de engenharia + +- [ ] Aceite da intenção coberto (plano + SKILL.md alinhados à política main, frame dormente preservado) +- [ ] Invariantes AGENTS (docs-only, sem mudanças em `src/`/workflows; changelog via `changelog:build`; `CHANGELOG-AGENTS.md` intocado) +- [ ] Grep de saída: zero ocorrências vivas de `--base stage`, `issue-done-on-stage-merge`, `stage→main` (ou "merge em stage") em docs ativos — resíduos só como descrição histórica datada +- [ ] `pnpm changelog:check` verde; CI docs-guards verde; PR via `pnpm push` diff --git a/scripts/agent-pool.mjs b/scripts/agent-pool.mjs index 82db00d5..f1240e5f 100644 --- a/scripts/agent-pool.mjs +++ b/scripts/agent-pool.mjs @@ -4,9 +4,9 @@ * * The pool keeps up to POOL_MAX_SLOTS (default 5) Cursor Cloud Agents running * agent-work-issue over `ready` autonomous-eligible issues. The supervisor is - * REMOTE: a stateless GitHub Actions tick (`.github/workflows/agent-pool.yml`) - * runs `workflow --action …` / `tick --live`; humans drive it via - * `gh workflow run` (canonical channel). Local usage is inspection + + * REMOTE: a stateless GitHub Actions tick (`.forgejo/workflows/agent-pool.yml`, + * removed OPS65 — pool dormant) runs `workflow --action …` / `tick --live`; humans + * drive it via `gh workflow run` (canonical channel). Local usage is inspection + * dispatch: * * pnpm agent:pool -- status # config, slots, fila elegível diff --git a/scripts/lib/agent-pool-state.mjs b/scripts/lib/agent-pool-state.mjs index c0b07113..abd1f3c5 100644 --- a/scripts/lib/agent-pool-state.mjs +++ b/scripts/lib/agent-pool-state.mjs @@ -128,7 +128,7 @@ export const POOL_SPAWN_GRACE_MS = 15 * 60 * 1000 * by the pool (no claim marker) — those belong to humans/manual agents and the * pool never touches them. * - * Occupied means "from the claim to the merge in stage (or a documented + * Occupied means "from the claim to the merge in main (or a documented * terminal failure)" — a terminal run with an open PR is still occupied * (auto-merge finishes the job hands-off), per the plan's slot definition. * diff --git a/tests/unit/agentPoolState.unit.spec.ts b/tests/unit/agentPoolState.unit.spec.ts index c3a4d4d1..5da7e50d 100644 --- a/tests/unit/agentPoolState.unit.spec.ts +++ b/tests/unit/agentPoolState.unit.spec.ts @@ -100,7 +100,7 @@ describe('classifyPoolClaim', () => { expect(classifyPoolClaim({ events: [], now })).toBeNull() }) - it('frees the slot once the issue is done (CI flipped on stage merge)', () => { + it('frees the slot once the issue is done (CI flipped on main merge)', () => { expect(classifyPoolClaim({ events: [claimEvent], issueDone: true, now })).toEqual({ class: POOL_CLAIM_CLASS.freed, })