Backlog — GitHub → SRO via Adapters e Mappers¶
Primeira integração real do The Band: ingerir dados do GitHub e materializá-los no modelo da Scrum Reference Ontology.
Documento derivado de priv/knowledge_base/ontology/continuum/sro/ e
priv/knowledge_base/mappings/github/.
1. O problema honesto: GitHub não é Scrum¶
Antes de qualquer tarefa, a constatação que define o escopo:
A API do GitHub não expõe a maior parte do que a SRO descreve. Não há cerimônias, não há Product Owner, não há critério de aceitação estruturado, não há distinção entre tarefa planejada e executada. Fingir que há produz dado inventado — que é pior que dado ausente, porque ninguém desconfia dele.
Matriz de cobertura¶
| Conceito SRO | Observável no GitHub? | Origem | Confiança |
|---|---|---|---|
sro.scrum_project |
⚠️ por configuração | repositório ou GitHub Project v2 declarado como projeto Scrum pelo tenant | média |
sro.scrum_process |
⚠️ derivado | inferido da existência de sprints no projeto | baixa |
sro.sprint |
✅ | ProjectV2IterationField (iterations do Projects v2) |
alta |
sro.product_backlog |
⚠️ derivado | o conjunto de itens do Project sem iteração atribuída | média |
sro.sprint_backlog |
✅ | itens do Project atribuídos a uma iteration | alta |
sro.user_story |
⚠️ por regra | issue classificada por label/tipo, regra declarada por tenant | média |
sro.epic / sro.atomic_user_story |
✅ | sub-issues (parent/child) da API de issues | alta |
sro.acceptance_criterion |
❌ | texto livre no corpo da issue | — |
sro.intended_scrum_development_task |
⚠️ derivado | item de backlog no momento da entrada na iteration | baixa |
sro.performed_scrum_development_task |
⚠️ derivado | transição de status do item + issue fechada | média |
sro.deliverable |
⚠️ derivado | Pull Request mergeado vinculado à issue | média |
sro.accepted_deliverable |
❌ | não há avaliação contra critério de aceitação | — |
sro.ceremony (4 tipos) |
❌ | não existe na API | — |
sro.scrum_team |
⚠️ parcial | GitHub Team, se o tenant declarar equivalência | baixa |
sro.development_team |
⚠️ parcial | idem | baixa |
sro.product_owner / scrum_master |
❌ | não há papel Scrum no GitHub | — |
sro.developer |
⚠️ por regra | assignee de issue ou autor de PR — papel presumido, não declarado | média |
sro.scrum_project_deliverable |
⚠️ derivado | release | média |
Legenda. ✅ existe na API · ⚠️ exige regra de classificação declarada pelo tenant · ❌ não é derivável do GitHub.
O que fazer com o que não é observável¶
Três opções, e a escolha precisa ser explícita por conceito:
- Não materializar. Cerimônias, Product Owner e Scrum Master ficam ausentes. As perguntas de competência CQ04, CQ17, CQ18, CQ21 e CQ22 ficam sem resposta a partir do GitHub — e o sistema deve dizer isso, não retornar lista vazia como se fosse resposta.
- Materializar por configuração do tenant. Papéis Scrum vêm de um cadastro manual
que liga contas GitHub a papéis. Dado com proveniência
project_decision, nãogithub. - Materializar por regra declarada. User story, developer e task saem de regras por tenant (labels, tipos de issue, campos de Project). A regra é versionada como YAML e a proveniência aponta para ela.
Regra que não se quebra. Conceito derivado por inferência carrega
derivation_rule_ideconfidencena proveniência. Um número calculado sobre dados inferidos precisa poder dizer que foi inferido — senão vira fato por acidente.
2. Arquitetura: Portas, Adapters e Mappers¶
Três responsabilidades, três camadas, nenhuma sabendo da seguinte.
GitHub GraphQL/REST
│
▼
┌──────────────────────────────────────────────────────────┐
│ ADAPTER — TheBand.Integrations.GitHub │
│ Fala o protocolo: auth, GraphQL, paginação, rate limit, │
│ retry, cursor. Devolve payload bruto normalizado. │
│ NÃO conhece ontologia. NÃO conhece Ecto de domínio. │
└──────────────────────────────────────────────────────────┘
│ %RawEntity{type: "github.issue", payload: %{...}, provenance: %{...}}
▼
┌──────────────────────────────────────────────────────────┐
│ RAW + PROVENANCE — TheBand.RawData / Provenance │
│ Persiste o payload original, imutável, com proveniência. │
│ Nada é descartado aqui. │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ MAPPER — TheBand.SemanticIntegration.Mapper │
│ Lê o mapeamento YAML, aplica regras do tenant, produz │
│ comandos de domínio. Conhece ontologia. │
│ NÃO conhece HTTP. NÃO escreve no banco diretamente. │
└──────────────────────────────────────────────────────────┘
│ {:ok, [{TheBand.Ontology.Continuum.SRO, :register_user_story, [attrs]}]}
▼
┌──────────────────────────────────────────────────────────┐
│ API PÚBLICA DO MÓDULO ONTOLÓGICO │
│ TheBand.Ontology.Continuum.SRO.register_user_story/1 │
│ Única porta de escrita. Valida, persiste, versiona. │
└──────────────────────────────────────────────────────────┘
Contratos (behaviours)¶
defmodule TheBand.Integrations.SourceAdapter do
@moduledoc "Porta que toda fonte externa implementa."
@callback fetch(entity :: atom(), opts :: keyword()) ::
{:ok, %{entities: [map()], cursor: String.t() | nil, has_next: boolean()}}
| {:error, term()}
@callback entity_types() :: [atom()]
@callback rate_limit_status(opts :: keyword()) :: {:ok, map()} | {:error, term()}
end
defmodule TheBand.SemanticIntegration.Mapper do
@moduledoc "Porta que traduz payload bruto em comandos de domínio."
@callback map(raw_entity :: map(), mapping :: map(), context :: map()) ::
{:ok, [command :: {module(), atom(), list()}]}
| {:skip, reason :: String.t()}
| {:error, term()}
end
Por que separar Adapter de Mapper. Uma issue do GitHub alimenta SRO (user story), EO (autor) e SPO (atividade). Um Adapter por conceito significaria buscar a mesma issue três vezes. Um Mapper acoplado ao HTTP significaria não conseguir remapear dados já coletados quando a regra semântica mudar — e ela vai mudar.
Com a separação, remapear é reprocessar raw_entities, sem tocar na API do GitHub.
Isso é o que torna a correção de um mapeamento errado barata.
Onde vive cada coisa¶
lib/the_band/
├── integrations/
│ ├── source_adapter.ex behaviour
│ └── github/
│ ├── adapter.ex implementa SourceAdapter
│ ├── client.ex Req + auth + rate limit
│ ├── pagination.ex cursores do GraphQL
│ └── entities/ um módulo por entidade da API
├── semantic_integration/
│ ├── mapper.ex behaviour
│ ├── mapping_registry.ex carrega mappings/*.yaml
│ ├── transformer.ex aplica source_path, transforms
│ ├── derivation_rules.ex regras por tenant (labels → user story etc.)
│ └── mappers/
│ ├── github_issue_mapper.ex
│ ├── github_project_item_mapper.ex
│ └── ...
├── raw_data/ payload bruto imutável
├── provenance/ proveniência + confiança + regra de derivação
└── ontology/continuum/sro/ API pública, única porta de escrita
priv/connectors/github/
├── queries/*.graphql consultas versionadas
└── definitions/*.yaml paginação, checkpoint, retry, mapeamentos de saída
priv/knowledge_base/mappings/github/ semântica: equivalência, justificativa, limitações
3. Fatias verticais¶
O backlog é organizado em fatias que respondem perguntas de competência, não em camadas. Cada fatia termina com uma pergunta da SRO respondível com dado real.
Fatia 0 — Fundação da ingestão¶
Sem isso nada roda. Não responde CQ nenhuma; é o preço de entrada.
| # | Item | Entrega |
|---|---|---|
| G0.1 | Cadastro de fonte GitHub | sources + source_credentials (referência a secret manager, nunca o segredo) |
| G0.2 | Cliente HTTP com Req | auth por token/App, timeout, retry com backoff exponencial |
| G0.3 | Controle de rate limit | leitura de rateLimit do GraphQL, pausa antes do estouro, não depois |
| G0.4 | Behaviour SourceAdapter |
contrato + teste de contrato com Mox |
| G0.5 | Persistência de payload bruto | raw_entities imutável + provenance_records |
| G0.6 | Paginação por cursor | checkpoint persistido; retomada após falha sem reprocessar tudo |
| G0.7 | Job Oban de sincronização | idempotente, com tenant_id validado |
| G0.8 | Registry de mapeamentos | carrega mappings/github/*.yaml, valida contra schema, cacheia |
| G0.9 | Behaviour Mapper + transformer |
resolve source_path, aplica transform, monta comandos |
| G0.10 | Reprocessamento | remapear raw_entities sem nova chamada à API |
10 itens. Dependências: fundação multitenant (F1–F7 do backlog geral).
Fatia 1 — Pessoas e repositórios¶
Responde: nada de SRO ainda, mas é pré-requisito de tudo. Já entrega valor em CMPO.
| # | Item | Conceito alvo | Mapeamento | Status do YAML |
|---|---|---|---|---|
| G1.1 | Adapter: organization |
eo.organization |
github.organization.to.eo.organization |
✅ existe |
| G1.2 | Mapper: organização | eo.organization |
idem | ✅ |
| G1.3 | Adapter: user |
eo.person |
github.user.to.eo.person |
✅ existe |
| G1.4 | Mapper: pessoa | eo.person |
idem | ✅ |
| G1.5 | Classificação de bots | — | novo campo no mapeamento | ⚠️ ajustar |
| G1.6 | Adapter: repository |
cmpo.source_repository |
github.repository.to.cmpo.source_repository |
✅ existe |
| G1.7 | Mapper: repositório | cmpo.source_repository |
idem | ✅ |
| G1.8 | Adapter + Mapper: team |
eo.team, eo.team_membership |
criar github.team.to.eo.team |
❌ criar |
8 itens. G1.5 é obrigatório: conta Bot não é pessoa, e contá-la como tal distorce
toda métrica por pessoa.
Fatia 2 — Projeto Scrum e sprints¶
Responde CQ03 (quantos sprints foram executados) e CQ06–CQ11 (datas de início e fim de projeto, processo e atividades).
| # | Item | Conceito alvo | Notas |
|---|---|---|---|
| G2.1 | Declaração de projeto Scrum | sro.scrum_project |
cadastro por tenant: qual repositório ou GitHub Project é um projeto Scrum. Não é inferível. |
| G2.2 | Adapter: ProjectV2 |
— | consulta GraphQL de Projects v2 |
| G2.3 | Adapter: ProjectV2IterationField |
sro.sprint |
iterations com startDate e duration |
| G2.4 | Mapper: iteration → sprint | sro.sprint |
criar github.project_iteration.to.sro.sprint |
| G2.5 | Derivação do processo Scrum | sro.scrum_process |
derivado; proveniência com confidence: low |
| G2.6 | Adapter: ProjectV2Item |
— | itens do board com seus campos |
| G2.7 | Sprint backlog | sro.sprint_backlog, sro_sprint_backlog_items |
itens com iteration atribuída |
| G2.8 | Product backlog | sro.product_backlog |
itens sem iteration — derivado, confidence: medium |
8 itens. Mapeamentos novos: 2.
Iteration é o achado que viabiliza este backlog. Sem Projects v2 com campo de iteração, não há sprint no GitHub — milestone não serve: não tem duração, não tem ordem, e é usada para release tanto quanto para sprint. Se o tenant não usa iterations, a Fatia 2 não produz dado, e isso precisa aparecer como lacuna, não como zero.
Fatia 3 — Épicos, user stories e tarefas: roteamento por tipo de issue¶
Responde CQ23, CQ24, CQ25, CQ27 (user stories no product backlog, prioridade, decomposição, seleção para sprint).
Uma issue do GitHub não tem destino único. Conforme o issueType definido pela
organização, ela é épico, user story, tarefa ou defeito — e o roteamento está declarado
em priv/knowledge_base/rules/github_issue_type_routing.yaml.
issueType no GitHub |
→ Conceito | Mapeamento |
|---|---|---|
Epic |
sro.epic |
github.issue.epic.to.sro.epic |
User Story, Story, Feature |
sro.atomic_user_story |
github.issue.user_story.to.sro.atomic_user_story |
Task |
sro.intended_scrum_development_task |
github.issue.task.to.sro.intended_scrum_development_task |
Bug |
osdef.defect |
github.issue.bug.to.osdef.defect |
| nulo ou desconhecido | — | fallback: skip |
O tipo declara intenção; a estrutura declara o fato¶
Em SRO, épico não é um tipo — é uma consequência. sro.epic e
sro.atomic_user_story são ambos sro.user_story; a diferença é ter ou não partes.
Por isso a precedência é structure_over_declaration:
| Situação no GitHub | Resultado | Por quê |
|---|---|---|
tipo Epic, com sub-issues de user story |
sro.epic |
tipo e estrutura concordam |
tipo Epic, sem sub-issues |
sro.atomic_user_story + divergência registrada |
não existe épico sem partes — sro.rule05 |
tipo User Story, com sub-issues de user story |
sro.epic + divergência registrada |
a composição a torna épico, o rótulo não importa |
tipo User Story, com sub-issues do tipo Task |
sro.atomic_user_story |
tarefa atende user story, não a compõe |
A última linha é a armadilha do conjunto. Sub-issue do tipo Task parece decomposição,
mas é outra relação: sro.intended_task_planned_to_meet_user_story. Tratá-la como
composição promoveria a user story a épico e a tiraria do alcance das tarefas —
sro.rule07 exige que tarefa se ligue a user story atômica.
Épico dentro de épico¶
A relação sro.epic_composed_of_user_story tem alvo sro.user_story, e épico é uma
user story. Logo a decomposição é recursiva, sem profundidade fixa: um épico pode
compor outro épico, que compõe outro, até chegar às atômicas.
Três axiomas delimitam a recursão — sem eles, a relação admitiria um épico que é parte de si mesmo:
| Axioma | Garante |
|---|---|
sro.rule04.epic_hierarchy_is_acyclic |
nenhuma user story é parte de si mesma, direta ou transitivamente |
sro.rule05.epic_has_parts |
todo épico tem ao menos uma parte |
sro.rule06.decomposition_terminates_in_atomic |
toda folha da decomposição é uma user story atômica |
sro.rule07.task_never_meets_epic |
tarefa se liga a user story atômica, nunca a épico |
A verificação de ciclo é responsabilidade do comando de registro. O GitHub não impede ancestralidade circular via API, e uma constraint de banco não pega ciclo transitivo em auto-relacionamento — é preciso percorrer o caminho até a raiz antes de persistir.
Itens¶
| # | Item | Conceito alvo | Notas |
|---|---|---|---|
| G3.1 | Adapter: issue com issueType |
— | inclui parent, subIssues, subIssuesSummary |
| G3.2 | Regra de roteamento por tipo | — | ✅ github.issue_type_routing declarada; falta o motor que a executa |
| G3.3 | Sobrescrita por tenant | — | rules/tenants/<tenant>.yaml — nomes de tipo são texto livre da organização |
| G3.4 | Mapper: issue → épico | sro.epic |
✅ mapeamento existe |
| G3.5 | Mapper: issue → user story atômica | sro.atomic_user_story |
✅ mapeamento existe |
| G3.6 | Mapper: issue → tarefa pretendida | sro.intended_scrum_development_task |
✅ mapeamento existe |
| G3.7 | Mapper: issue → defeito | osdef.defect |
✅ mapeamento existe |
| G3.8 | Resolução de precedência | — | compara tipo declarado com estrutura; aplica structure_over_declaration |
| G3.9 | Registro de divergência | proveniência | grava declared_concept, derived_concept, reason |
| G3.10 | Verificação de ciclo | sro.rule04 |
percorre ancestrais antes de persistir |
| G3.11 | Importance e complexity | atributos | campos de Project (Priority, Estimate); ausência é nulo, nunca zero |
| G3.12 | Vínculo com sprint backlog | sro_sprint_backlog_items |
uma story pode reaparecer em sprints seguintes |
| G3.13 | Lacuna de issueType |
— | tenant sem tipos configurados: skip e lacuna reportada, não user story presumida |
13 itens. Mapeamentos já criados: 4. Regra de derivação já criada: 1.
G3.13 não é caso de borda. Tipos de issue são recurso relativamente recente e opcional. Um tenant que usa apenas labels cai inteiro no
fallback: skip— e precisa saber disso no cadastro da fonte, não depois de uma sincronização que não produziu nada.
Fatia 4 — Tarefas executadas¶
Responde CQ05, CQ19, CQ20, CQ29, CQ31 (tarefas executadas no sprint, quem foi responsável, quem participou).
| # | Item | Conceito alvo | Notas |
|---|---|---|---|
| G4.1 | Adapter: histórico de status | — | ProjectV2ItemFieldValue + timeline events |
| G4.2 | Mapper: tarefa executada | sro.performed_scrum_development_task |
derivado da transição de status/fechamento |
| G4.3 | Tarefa pretendida | sro.intended_scrum_development_task |
derivada da entrada do item na iteration |
| G4.4 | Causação pretendida → executada | relação was caused by |
FK; sem ela não há análise de aderência |
| G4.5 | Responsável pela tarefa | spo_participations (is in charge of) |
assignee da issue |
| G4.6 | Participantes | spo_participations (participates in) |
autores de commits e revisores vinculados |
| G4.7 | Papel Developer presumido | sro.developer via eo_team_memberships |
proveniência de inferência obrigatória |
7 itens. Mapeamentos novos: 2.
A distinção pretendida/executada não existe no GitHub — a mesma issue é as duas coisas em momentos diferentes. G4.3 e G4.4 a reconstroem a partir do histórico do item. É derivação, e a proveniência precisa dizer isso. Colapsar as duas em uma entidade só é o atalho que destrói CQ29.
Fatia 5 — Entregáveis¶
Responde CQ33, CQ34, CQ35 (entregáveis produzidos no sprint e no projeto, e quais user stories materializaram).
| # | Item | Conceito alvo | Notas |
|---|---|---|---|
| G5.1 | Adapter: PR com issues vinculadas | — | closingIssuesReferences do GraphQL |
| G5.2 | Mapper: PR mergeado → entregável | sro.deliverable |
criar github.pull_request.to.sro.deliverable |
| G5.3 | Materialização | sro_materializations |
entregável × user story |
| G5.4 | Entregável de sprint | sro.sprint_deliverable |
agregação dos entregáveis do sprint |
| G5.5 | Release → entregável do projeto | sro.scrum_project_deliverable |
criar mapeamento de release |
| G5.6 | Lacuna de aceitação | acceptance_status |
fica unknown — GitHub não avalia contra critério de aceitação |
6 itens. Mapeamentos novos: 2.
G5.6 é uma decisão, não um bug.
sro.accepted_deliverableexige conformidade com critérios de aceitação, que o GitHub não registra. Preencheracceptedporque o PR foi mergeado seria inventar a avaliação — e a medida de retrabalho nasceria falsa. O estado correto éunknown, e CQ36/CQ37 ficam sem resposta a partir desta fonte.
Fatia 6 — Times e papéis Scrum¶
Responde CQ13, CQ14, CQ15, CQ16 (times, papéis, membros e o papel de cada membro).
| # | Item | Conceito alvo | Notas |
|---|---|---|---|
| G6.1 | Declaração de time Scrum | sro.scrum_team, sro.development_team |
cadastro por tenant sobre GitHub Teams |
| G6.2 | Catálogo de papéis Scrum | eo_organizational_roles |
seed a partir da base de conhecimento |
| G6.3 | Alocação de papéis | eo_team_memberships |
cadastro manual — não é derivável do GitHub |
| G6.4 | Lacuna de PO e Scrum Master | — | ausentes sem cadastro; CQ17, CQ21, CQ22 sem resposta |
4 itens. Nenhum mapeamento novo — é cadastro, não ingestão.
4. Mapeamentos YAML¶
Já criados — 13¶
| Mapeamento | → Conceito | Equivalência |
|---|---|---|
github.user.to.eo.person |
eo.person |
partial |
github.organization.to.eo.organization |
eo.organization |
partial |
github.repository.to.cmpo.source_repository |
cmpo.source_repository |
partial |
github.ref.to.cmpo.branch |
cmpo.branch |
partial |
github.commit.to.cmpo.commit_artifact_copy |
cmpo.commit_artifact_copy |
partial |
github.pull_request.to.cmpo.change_request |
cmpo.change_request |
partial |
github.pull_request_review.to.qapo.artifact_evaluation |
qapo.artifact_evaluation |
partial |
github.workflow_run.to.ciro.continuous_integration_process |
ciro.continuous_integration_process |
partial |
github.deployment.to.cdro.deployment_activity |
cdro.deployment_activity |
partial |
github.issue.epic.to.sro.epic |
sro.epic |
derived |
github.issue.user_story.to.sro.atomic_user_story |
sro.atomic_user_story |
derived |
github.issue.task.to.sro.intended_scrum_development_task |
sro.intended_scrum_development_task |
derived |
github.issue.bug.to.osdef.defect |
osdef.defect |
partial |
Os quatro últimos substituem o antigo github.issue.to.sro.user_story, que tratava
toda issue como user story. Agora o destino é decidido por issueType e pela estrutura
de sub-issues.
Ainda a criar — 6¶
| Mapeamento | Fatia | → Conceito |
|---|---|---|
github.team.to.eo.team |
1 | eo.team + eo.team_membership |
github.project_iteration.to.sro.sprint |
2 | sro.sprint |
github.project_item.to.sro.sprint_backlog_item |
2 | vínculo story × sprint backlog |
github.project_item_status.to.sro.performed_task |
4 | sro.performed_scrum_development_task |
github.pull_request.to.sro.deliverable |
5 | sro.deliverable |
github.release.to.sro.scrum_project_deliverable |
5 | sro.scrum_project_deliverable |
A tarefa pretendida saiu desta lista: vem da issue do tipo
Task, já mapeada. O que falta é a tarefa executada, derivada do histórico de status do item no Project — são entidades distintas ligadas porsro.performed_task_caused_by_intended_task.
pull_requestaparece duas vezes no conjunto: já mapeia paracmpo.change_requeste passará a mapear parasro.deliverable. É o comportamento esperado — uma entidade externa alimenta várias ontologias. O PR é a solicitação de mudança e produz o entregável; são fatos distintos sobre o mesmo payload.
Extensão de schema — concluída¶
Os mapeamentos derivados exigiam campos que o schema não tinha. mapping.schema.yaml
ganhou dois blocos:
source:
selector: # roteia a mesma entidade para conceitos diferentes
field: issueType.name
rule_ref: github.issue_type_routing
values: ["Epic"]
structural_requirements:
has_children: true
children_of_concept: sro.user_story
derivation: # obrigatório quando equivalence é "derived"
rule_id: github.issue_type_routing
confidence: medium # high | medium | low
inputs: [issueType.name, subIssues]
precedence: structure_over_declaration
on_divergence: flag_and_follow_precedence
fallback: skip
scripts/validate_knowledge_base.py passou a exigir:
equivalence: derivedsem blocoderivation→ erro;rule_idourule_refapontando para regra inexistente emrules/→ erro;fallbackdiferente deskip→ erro;- conceito citado em
structural_requirementsque não existe → erro.
5. Regras de derivação por tenant¶
Novo tipo de artefato na base de conhecimento: priv/knowledge_base/rules/tenants/.
derivation_rule:
id: tenant.acme.user_story_classification
tenant: acme
target_concept: sro.user_story
confidence: medium
when:
any_label: ["user story", "feature", "story"]
issue_type: ["Feature"]
unless:
any_label: ["bug", "defect", "chore"]
fallback: skip # nunca "assume que é user story"
fallback: skip é o padrão obrigatório. Quando a regra não decide, o dado fica em
raw_entities sem promoção ao domínio — visível como lacuna, não como fato.
6. Ordem de execução¶
Fatia 0 fundação da ingestão 10 itens ← nada roda sem isso
↓
Fatia 1 pessoas e repositórios 8 itens ← já entrega valor em CMPO
↓
Fatia 2 projeto Scrum e sprints 8 itens ← primeira CQ de SRO respondida (CQ03)
↓
Fatia 3 épicos, stories e tarefas 13 itens ← CQ23, CQ24, CQ25, CQ27
↓
Fatia 4 tarefas executadas 7 itens ← CQ05, CQ19, CQ20, CQ29, CQ31
↓
Fatia 5 entregáveis 6 itens ← CQ33, CQ34, CQ35
↓
Fatia 6 times e papéis 4 itens ← CQ13–CQ16 (cadastro, não ingestão)
56 itens. Fatias 1 e 2 são o mínimo para o sistema dizer algo verdadeiro sobre um projeto real.
Cobertura final de SRO a partir do GitHub¶
| CQs | % | |
|---|---|---|
| Respondidas com dado observado | CQ03, CQ06–CQ11, CQ13, CQ15, CQ23, CQ25, CQ27, CQ35 | 12 de 37 |
| Respondidas com derivação declarada | CQ05, CQ12, CQ14, CQ16, CQ19, CQ20, CQ24, CQ29, CQ30, CQ31, CQ33, CQ34 | 12 de 37 |
| Sem resposta a partir do GitHub | CQ01, CQ02, CQ04, CQ17, CQ18, CQ21, CQ22, CQ26, CQ28, CQ32, CQ36, CQ37 | 13 de 37 |
65% da SRO fica respondível pelo GitHub. O terço restante depende de cerimônias, papéis e critérios de aceitação — que exigem outra fonte (Jira, Azure DevOps) ou cadastro manual.
Isso não é falha do modelo: é o modelo cumprindo sua função de mostrar o que a ferramenta não sabe. Um integrador sem ontologia simplesmente devolveria listas vazias e ninguém perceberia a diferença.
7. Riscos¶
Rate limit do GraphQL é por complexidade, não por requisição. Uma consulta de Projects
v2 com itens, campos e histórico pode custar centenas de pontos. O controle precisa ler
rateLimit.cost e remaining na própria resposta e pausar antes do estouro — descobrir
o limite por erro 403 já é tarde, e a retomada custa uma janela inteira.
Projects v2 é opcional. Se o tenant não usa Projects com iterations, as Fatias 2 a 5 não produzem nada. Isso precisa ser detectado no cadastro da fonte (G0.1) e informado, não descoberto depois de três dias de sincronização vazia.
Regras de derivação são configuração de domínio. Vivem versionadas na base de conhecimento e passam por revisão semântica. Se virarem campo de formulário editável sem histórico, a métrica muda de significado sem que ninguém saiba quando.
Reprocessamento precisa ser barato desde o dia um. As regras de classificação da Fatia 3 vão estar erradas na primeira tentativa. Se corrigir exigir nova coleta completa do GitHub, ninguém vai corrigir. G0.10 não é otimização — é o que torna o erro recuperável.
Sub-issues são API recente. Verificar disponibilidade na versão do GitHub do tenant (GitHub Enterprise Server costuma estar atrás). Sem sub-issues, a distinção epic vs atômica cai para heurística de task list em markdown — e aí é melhor não distinguir.
8. Próximo passo¶
/speckit-specify Ingestão de dados do GitHub para a Scrum Reference Ontology:
adapter GitHub com paginação por cursor, rate limit e persistência de payload
bruto com proveniência; registry de mapeamentos YAML; mapper de organização,
pessoa e repositório. Escopo das Fatias 0 e 1 do backlog GitHub → SRO.
Depois de aprovada, /speckit-plan precisa decidir explicitamente:
- biblioteca YAML (pesquisa de manutenção, segurança e compatibilidade);
- estratégia de cache dos mapeamentos (compile time, boot ou ETS);
- formato do checkpoint de paginação;
- se
raw_entitiesguarda payload completo ou normalizado — e o custo de armazenamento de cada opção.