[
 {
  "chave": "SIGLA-2",
  "territorio": "infra",
  "titulo": "O chão: o Capacita tem o SEU banco, num contentor só dele (regra 42)",
  "escopo_paths": [
   "docker/",
   "scripts/"
  ],
  "aceites": [
   "test -f docker/docker-compose.yml && docker compose -f docker/docker-compose.yml config -q",
   "test -f docker/docker-compose.yml && ! grep -qiE '1433|sqlserver|mais[-_ ]?saude' docker/docker-compose.yml",
   "bash scripts/banco-subir.sh && docker exec capacitasalvador-postgres pg_isready -U capacita -d capacita",
   "bash scripts/banco-subir.sh && bash scripts/banco-subir.sh && docker exec capacitasalvador-postgres pg_isready -U capacita -d capacita"
  ],
  "depende_de": []
 },
 {
  "chave": "SIGLA-3",
  "territorio": "backend",
  "titulo": "Alicerce do backend: Nest + Prisma, `npm run build` -> dist/main e /api/v1/health",
  "escopo_paths": [
   "backend/package.json",
   "backend/package-lock.json",
   "backend/tsconfig.json",
   "backend/tsconfig.build.json",
   "backend/nest-cli.json",
   "backend/jest.config.js",
   "backend/.env.example",
   "backend/prisma/",
   "backend/src/main.ts",
   "backend/src/app.module.ts",
   "backend/src/comum/",
   "backend/test/health.e2e-spec.ts"
  ],
  "aceites": [
   "cd backend && npm install --no-audit --no-fund && npm run build && test -f dist/main.js",
   "cd backend && npx prisma migrate deploy && npx prisma migrate status",
   "rm -f /tmp/fab-SIGLA-3-jest.json && cd backend && npx jest --runInBand --ci --json --outputFile=/tmp/fab-SIGLA-3-jest.json test/health.e2e-spec.ts && node -e \"const r=require('/tmp/fab-SIGLA-3-jest.json');if(!(r.success&&r.numFailedTests===0&&r.numTotalTests>=3&&r.numTotalTests===r.numPassedTests)){console.error('bateria do card: '+JSON.stringify({s:r.success,t:r.numTotalTests,p:r.numPassedTests}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-2"
  ]
 },
 {
  "chave": "SIGLA-4",
  "territorio": "backend",
  "titulo": "Identidade: os QUATRO papéis, entrada e sessão; o médico tem estado pendente|ativo (regras 46, 55)",
  "escopo_paths": [
   "backend/prisma/",
   "backend/src/modules/identidade/",
   "backend/src/app.module.ts",
   "backend/test/identidade.e2e-spec.ts"
  ],
  "aceites": [
   "cd backend && npx prisma migrate deploy && npx prisma db seed && npx prisma migrate status",
   "rm -f /tmp/fab-SIGLA-4-jest.json && cd backend && npx jest --runInBand --ci --json --outputFile=/tmp/fab-SIGLA-4-jest.json test/identidade.e2e-spec.ts && node -e \"const r=require('/tmp/fab-SIGLA-4-jest.json');if(!(r.success&&r.numFailedTests===0&&r.numTotalTests>=8&&r.numTotalTests===r.numPassedTests)){console.error('bateria do card: '+JSON.stringify({s:r.success,t:r.numTotalTests,p:r.numPassedTests}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-3"
  ]
 },
 {
  "chave": "SIGLA-5",
  "territorio": "backend",
  "titulo": "A lista de UPAs é da SECRETARIA, e cada UPA tem o seu coordenador e o seu link (regra 44)",
  "escopo_paths": [
   "backend/prisma/",
   "backend/src/modules/upa/",
   "backend/src/app.module.ts",
   "backend/test/upa.e2e-spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-5-jest.json && cd backend && npx jest --runInBand --ci --json --outputFile=/tmp/fab-SIGLA-5-jest.json test/upa.e2e-spec.ts && node -e \"const r=require('/tmp/fab-SIGLA-5-jest.json');if(!(r.success&&r.numFailedTests===0&&r.numTotalTests>=8&&r.numTotalTests===r.numPassedTests)){console.error('bateria do card: '+JSON.stringify({s:r.success,t:r.numTotalTests,p:r.numPassedTests}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-4"
  ]
 },
 {
  "chave": "SIGLA-6",
  "territorio": "backend",
  "titulo": "Cadastro pelo link do coordenador: CRM, várias UPAs, e entra PENDENTE (regras 44, 46)",
  "escopo_paths": [
   "backend/prisma/",
   "backend/src/modules/cadastro/",
   "backend/src/app.module.ts",
   "backend/test/cadastro.e2e-spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-6-jest.json && cd backend && npx jest --runInBand --ci --json --outputFile=/tmp/fab-SIGLA-6-jest.json test/cadastro.e2e-spec.ts && node -e \"const r=require('/tmp/fab-SIGLA-6-jest.json');if(!(r.success&&r.numFailedTests===0&&r.numTotalTests>=8&&r.numTotalTests===r.numPassedTests)){console.error('bateria do card: '+JSON.stringify({s:r.success,t:r.numTotalTests,p:r.numPassedTests}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-5"
  ]
 },
 {
  "chave": "SIGLA-7",
  "territorio": "backend",
  "titulo": "Só o coordenador DA UPA QUE O MÉDICO ESCOLHEU o aceita ou o tira (regras 44, 46)",
  "escopo_paths": [
   "backend/prisma/",
   "backend/src/modules/vinculo/",
   "backend/src/app.module.ts",
   "backend/test/vinculo.e2e-spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-7-jest.json && cd backend && npx jest --runInBand --ci --json --outputFile=/tmp/fab-SIGLA-7-jest.json test/vinculo.e2e-spec.ts && node -e \"const r=require('/tmp/fab-SIGLA-7-jest.json');if(!(r.success&&r.numFailedTests===0&&r.numTotalTests>=8&&r.numTotalTests===r.numPassedTests)){console.error('bateria do card: '+JSON.stringify({s:r.success,t:r.numTotalTests,p:r.numPassedTests}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-6"
  ]
 },
 {
  "chave": "SIGLA-8",
  "territorio": "backend",
  "titulo": "Aula ao vivo: a secretaria cadastra e indica o especialista (TROCÁVEL) + calendário que o pendente não vê (regras 55, 57)",
  "escopo_paths": [
   "backend/prisma/",
   "backend/src/modules/aula/",
   "backend/src/app.module.ts",
   "backend/test/aula.e2e-spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-8-jest.json && cd backend && npx jest --runInBand --ci --json --outputFile=/tmp/fab-SIGLA-8-jest.json test/aula.e2e-spec.ts && node -e \"const r=require('/tmp/fab-SIGLA-8-jest.json');if(!(r.success&&r.numFailedTests===0&&r.numTotalTests>=10&&r.numTotalTests===r.numPassedTests)){console.error('bateria do card: '+JSON.stringify({s:r.success,t:r.numTotalTests,p:r.numPassedTests}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-4"
  ]
 },
 {
  "chave": "SIGLA-9",
  "territorio": "backend",
  "titulo": "As questões pertencem À AULA: o responsável escreve, uma a cada 20 min, e a troca não as apaga (regras 56, 57)",
  "escopo_paths": [
   "backend/prisma/",
   "backend/src/modules/questao/",
   "backend/src/app.module.ts",
   "backend/test/questao.e2e-spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-9-jest.json && cd backend && npx jest --runInBand --ci --json --outputFile=/tmp/fab-SIGLA-9-jest.json test/questao.e2e-spec.ts && node -e \"const r=require('/tmp/fab-SIGLA-9-jest.json');if(!(r.success&&r.numFailedTests===0&&r.numTotalTests>=8&&r.numTotalTests===r.numPassedTests)){console.error('bateria do card: '+JSON.stringify({s:r.success,t:r.numTotalTests,p:r.numPassedTests}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-8"
  ]
 },
 {
  "chave": "SIGLA-10",
  "territorio": "backend",
  "titulo": "Parâmetros da secretaria: X, Y, dias da gravação e multiplicador — tabela de pontos é PARÂMETRO, não código",
  "escopo_paths": [
   "backend/prisma/",
   "backend/src/modules/parametro/",
   "backend/src/app.module.ts",
   "backend/test/parametro.e2e-spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-10-jest.json && cd backend && npx jest --runInBand --ci --json --outputFile=/tmp/fab-SIGLA-10-jest.json test/parametro.e2e-spec.ts && node -e \"const r=require('/tmp/fab-SIGLA-10-jest.json');if(!(r.success&&r.numFailedTests===0&&r.numTotalTests>=6&&r.numTotalTests===r.numPassedTests)){console.error('bateria do card: '+JSON.stringify({s:r.success,t:r.numTotalTests,p:r.numPassedTests}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-4"
  ]
 },
 {
  "chave": "SIGLA-11",
  "territorio": "backend",
  "titulo": "Presença e ponto no ao vivo: o bloco de 20 min valida-se a responder; X por responder, X+Y por acertar, errar não tira (regras 45, 48, 56)",
  "escopo_paths": [
   "backend/prisma/",
   "backend/src/modules/participacao/",
   "backend/src/app.module.ts",
   "backend/test/participacao.e2e-spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-11-jest.json && cd backend && npx jest --runInBand --ci --json --outputFile=/tmp/fab-SIGLA-11-jest.json test/participacao.e2e-spec.ts && node -e \"const r=require('/tmp/fab-SIGLA-11-jest.json');if(!(r.success&&r.numFailedTests===0&&r.numTotalTests>=12&&r.numTotalTests===r.numPassedTests)){console.error('bateria do card: '+JSON.stringify({s:r.success,t:r.numTotalTests,p:r.numPassedTests}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-9",
   "SIGLA-10"
  ]
 },
 {
  "chave": "SIGLA-12",
  "territorio": "backend",
  "titulo": "A gravação do ao vivo abre por x dias — parâmetro POR AULA com omissão global (regra 49)",
  "escopo_paths": [
   "backend/prisma/",
   "backend/src/modules/gravacao/",
   "backend/src/app.module.ts",
   "backend/test/gravacao.e2e-spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-12-jest.json && cd backend && npx jest --runInBand --ci --json --outputFile=/tmp/fab-SIGLA-12-jest.json test/gravacao.e2e-spec.ts && node -e \"const r=require('/tmp/fab-SIGLA-12-jest.json');if(!(r.success&&r.numFailedTests===0&&r.numTotalTests>=8&&r.numTotalTests===r.numPassedTests)){console.error('bateria do card: '+JSON.stringify({s:r.success,t:r.numTotalTests,p:r.numPassedTests}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-8",
   "SIGLA-10"
  ]
 },
 {
  "chave": "SIGLA-13",
  "territorio": "frontend",
  "titulo": "Alicerce do frontend: Next, entrada por papel e o início de cada um dos quatro",
  "escopo_paths": [
   "frontend/package.json",
   "frontend/package-lock.json",
   "frontend/next.config.js",
   "frontend/tsconfig.json",
   "frontend/playwright.config.ts",
   "frontend/src/app/layout.tsx",
   "frontend/src/app/page.tsx",
   "frontend/src/app/entrar/",
   "frontend/src/componentes/",
   "frontend/src/lib/",
   "frontend/e2e/entrada.spec.ts"
  ],
  "aceites": [
   "cd frontend && npm install --no-audit --no-fund && npm run build",
   "rm -f /tmp/fab-SIGLA-13-pw.json && cd frontend && npx playwright test e2e/entrada.spec.ts --reporter=json > /tmp/fab-SIGLA-13-pw.json && node -e \"const r=require('/tmp/fab-SIGLA-13-pw.json');const t=(r.suites||[]).flatMap(function f(s){return (s.specs||[]).concat((s.suites||[]).flatMap(f))});const n=t.length, ok=t.filter(x=>x.ok).length;if(!(n>=4&&n===ok)){console.error('jornada do card: '+JSON.stringify({n:n,ok:ok}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-4"
  ]
 },
 {
  "chave": "SIGLA-14",
  "territorio": "frontend",
  "titulo": "Ecrã da secretaria: a lista de UPAs e o coordenador de cada uma (regra 44)",
  "escopo_paths": [
   "frontend/src/app/secretaria/upas/",
   "frontend/e2e/upas.spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-14-pw.json && cd frontend && npx playwright test e2e/upas.spec.ts --reporter=json > /tmp/fab-SIGLA-14-pw.json && node -e \"const r=require('/tmp/fab-SIGLA-14-pw.json');const t=(r.suites||[]).flatMap(function f(s){return (s.specs||[]).concat((s.suites||[]).flatMap(f))});const n=t.length, ok=t.filter(x=>x.ok).length;if(!(n>=3&&n===ok)){console.error('jornada do card: '+JSON.stringify({n:n,ok:ok}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-5",
   "SIGLA-13"
  ]
 },
 {
  "chave": "SIGLA-15",
  "territorio": "frontend",
  "titulo": "O ecrã público do link: o médico declara o CRM, escolhe as suas UPAs e fica PENDENTE (regras 44, 46)",
  "escopo_paths": [
   "frontend/src/app/cadastro/",
   "frontend/e2e/cadastro.spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-15-pw.json && cd frontend && npx playwright test e2e/cadastro.spec.ts --reporter=json > /tmp/fab-SIGLA-15-pw.json && node -e \"const r=require('/tmp/fab-SIGLA-15-pw.json');const t=(r.suites||[]).flatMap(function f(s){return (s.specs||[]).concat((s.suites||[]).flatMap(f))});const n=t.length, ok=t.filter(x=>x.ok).length;if(!(n>=3&&n===ok)){console.error('jornada do card: '+JSON.stringify({n:n,ok:ok}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-6",
   "SIGLA-13"
  ]
 },
 {
  "chave": "SIGLA-16",
  "territorio": "frontend",
  "titulo": "Ecrã do coordenador: aceitar ou tirar o médico, e SÓ na sua UPA (regras 44, 46)",
  "escopo_paths": [
   "frontend/src/app/coordenador/",
   "frontend/e2e/coordenador.spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-16-pw.json && cd frontend && npx playwright test e2e/coordenador.spec.ts --reporter=json > /tmp/fab-SIGLA-16-pw.json && node -e \"const r=require('/tmp/fab-SIGLA-16-pw.json');const t=(r.suites||[]).flatMap(function f(s){return (s.specs||[]).concat((s.suites||[]).flatMap(f))});const n=t.length, ok=t.filter(x=>x.ok).length;if(!(n>=3&&n===ok)){console.error('jornada do card: '+JSON.stringify({n:n,ok:ok}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-7",
   "SIGLA-13"
  ]
 },
 {
  "chave": "SIGLA-17",
  "territorio": "frontend",
  "titulo": "Calendário das aulas: o médico aceite vê; o PENDENTE vê zero (regras 46, 57)",
  "escopo_paths": [
   "frontend/src/app/calendario/",
   "frontend/e2e/calendario.spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-17-pw.json && cd frontend && npx playwright test e2e/calendario.spec.ts --reporter=json > /tmp/fab-SIGLA-17-pw.json && node -e \"const r=require('/tmp/fab-SIGLA-17-pw.json');const t=(r.suites||[]).flatMap(function f(s){return (s.specs||[]).concat((s.suites||[]).flatMap(f))});const n=t.length, ok=t.filter(x=>x.ok).length;if(!(n>=3&&n===ok)){console.error('jornada do card: '+JSON.stringify({n:n,ok:ok}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-8",
   "SIGLA-13"
  ]
 },
 {
  "chave": "SIGLA-18",
  "territorio": "frontend",
  "titulo": "Ecrã da secretaria: cadastrar a aula ao vivo e TROCAR o especialista responsável (regra 57)",
  "escopo_paths": [
   "frontend/src/app/secretaria/aulas/",
   "frontend/e2e/aulas.spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-18-pw.json && cd frontend && npx playwright test e2e/aulas.spec.ts --reporter=json > /tmp/fab-SIGLA-18-pw.json && node -e \"const r=require('/tmp/fab-SIGLA-18-pw.json');const t=(r.suites||[]).flatMap(function f(s){return (s.specs||[]).concat((s.suites||[]).flatMap(f))});const n=t.length, ok=t.filter(x=>x.ok).length;if(!(n>=3&&n===ok)){console.error('jornada do card: '+JSON.stringify({n:n,ok:ok}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-8",
   "SIGLA-13"
  ]
 },
 {
  "chave": "SIGLA-19",
  "territorio": "frontend",
  "titulo": "Ecrã do especialista: escrever as questões da SUA aula, e continuarem lá depois da troca (regras 56, 57)",
  "escopo_paths": [
   "frontend/src/app/especialista/questoes/",
   "frontend/e2e/questoes.spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-19-pw.json && cd frontend && npx playwright test e2e/questoes.spec.ts --reporter=json > /tmp/fab-SIGLA-19-pw.json && node -e \"const r=require('/tmp/fab-SIGLA-19-pw.json');const t=(r.suites||[]).flatMap(function f(s){return (s.specs||[]).concat((s.suites||[]).flatMap(f))});const n=t.length, ok=t.filter(x=>x.ok).length;if(!(n>=3&&n===ok)){console.error('jornada do card: '+JSON.stringify({n:n,ok:ok}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-9",
   "SIGLA-13"
  ]
 },
 {
  "chave": "SIGLA-20",
  "territorio": "frontend",
  "titulo": "A sala do ao vivo: a pergunta de 20 em 20 min e os minutos validados à vista (regras 45, 48, 56)",
  "escopo_paths": [
   "frontend/src/app/ao-vivo/",
   "frontend/e2e/ao-vivo.spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-20-pw.json && cd frontend && npx playwright test e2e/ao-vivo.spec.ts --reporter=json > /tmp/fab-SIGLA-20-pw.json && node -e \"const r=require('/tmp/fab-SIGLA-20-pw.json');const t=(r.suites||[]).flatMap(function f(s){return (s.specs||[]).concat((s.suites||[]).flatMap(f))});const n=t.length, ok=t.filter(x=>x.ok).length;if(!(n>=4&&n===ok)){console.error('jornada do card: '+JSON.stringify({n:n,ok:ok}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-11",
   "SIGLA-17"
  ]
 },
 {
  "chave": "SIGLA-21",
  "territorio": "frontend",
  "titulo": "O leitor da gravação: a barra não avança e o relógio pára quando a aba perde o foco (regra 49)",
  "escopo_paths": [
   "frontend/src/app/gravacao/",
   "frontend/src/componentes/leitor/",
   "frontend/e2e/gravacao.spec.ts"
  ],
  "aceites": [
   "rm -f /tmp/fab-SIGLA-21-pw.json && cd frontend && npx playwright test e2e/gravacao.spec.ts --reporter=json > /tmp/fab-SIGLA-21-pw.json && node -e \"const r=require('/tmp/fab-SIGLA-21-pw.json');const t=(r.suites||[]).flatMap(function f(s){return (s.specs||[]).concat((s.suites||[]).flatMap(f))});const n=t.length, ok=t.filter(x=>x.ok).length;if(!(n>=4&&n===ok)){console.error('jornada do card: '+JSON.stringify({n:n,ok:ok}));process.exit(1)}\""
  ],
  "depende_de": [
   "SIGLA-12",
   "SIGLA-17"
  ]
 }
]