mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 23:53:05 +00:00
docs(OPS47): alinha plano do agent-pool à política main-based pós-OPS71
- 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)
This commit is contained in:
parent
b00c69c9d8
commit
685218d188
8 changed files with 164 additions and 76 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
1
docs/changelog/2026-08-23-ops47.md
Normal file
1
docs/changelog/2026-08-23-ops47.md
Normal file
|
|
@ -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`.
|
||||
|
|
@ -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-<id> <agentUrl>`) | 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: <slug>, 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<N>-…`.
|
||||
|
||||
**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
|
||||
|
|
|
|||
|
|
@ -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 — <title> (<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`
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue