notion-starter 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. notion_starter-0.3.0/.editorconfig +15 -0
  2. notion_starter-0.3.0/.env.example +13 -0
  3. notion_starter-0.3.0/.github/workflows/ci.yml +27 -0
  4. notion_starter-0.3.0/.github/workflows/release.yml +68 -0
  5. notion_starter-0.3.0/.gitignore +14 -0
  6. notion_starter-0.3.0/AGENTS.md +33 -0
  7. notion_starter-0.3.0/CONTRIBUTING.md +95 -0
  8. notion_starter-0.3.0/IA.md +381 -0
  9. notion_starter-0.3.0/LICENSE +21 -0
  10. notion_starter-0.3.0/PKG-INFO +236 -0
  11. notion_starter-0.3.0/QUALIDADE.md +45 -0
  12. notion_starter-0.3.0/README.md +187 -0
  13. notion_starter-0.3.0/examples/check_schema.py +46 -0
  14. notion_starter-0.3.0/examples/coletar_mapa.py +82 -0
  15. notion_starter-0.3.0/examples/export_rows.py +68 -0
  16. notion_starter-0.3.0/examples/gerar_arvore_html.py +198 -0
  17. notion_starter-0.3.0/examples/gerenciar_tarefas.py +65 -0
  18. notion_starter-0.3.0/examples/listar_paginas.py +45 -0
  19. notion_starter-0.3.0/examples/relatorios_do_git.py +113 -0
  20. notion_starter-0.3.0/examples/sync_from_csv.py +103 -0
  21. notion_starter-0.3.0/pyproject.toml +54 -0
  22. notion_starter-0.3.0/src/notion_starter/__init__.py +104 -0
  23. notion_starter-0.3.0/src/notion_starter/client.py +1386 -0
  24. notion_starter-0.3.0/src/notion_starter/constants.py +43 -0
  25. notion_starter-0.3.0/src/notion_starter/content.py +766 -0
  26. notion_starter-0.3.0/src/notion_starter/exceptions.py +103 -0
  27. notion_starter-0.3.0/src/notion_starter/git_historico.py +209 -0
  28. notion_starter-0.3.0/src/notion_starter/github.py +421 -0
  29. notion_starter-0.3.0/src/notion_starter/inventory.py +319 -0
  30. notion_starter-0.3.0/src/notion_starter/logging.py +105 -0
  31. notion_starter-0.3.0/src/notion_starter/openrouter.py +294 -0
  32. notion_starter-0.3.0/src/notion_starter/properties.py +219 -0
  33. notion_starter-0.3.0/src/notion_starter/readers.py +181 -0
  34. notion_starter-0.3.0/src/notion_starter/schema.py +332 -0
  35. notion_starter-0.3.0/src/notion_starter/services/__init__.py +9 -0
  36. notion_starter-0.3.0/src/notion_starter/services/anexos.py +90 -0
  37. notion_starter-0.3.0/src/notion_starter/services/clonagem.py +306 -0
  38. notion_starter-0.3.0/src/notion_starter/services/conteudo.py +626 -0
  39. notion_starter-0.3.0/src/notion_starter/services/estrutura_projeto.py +341 -0
  40. notion_starter-0.3.0/src/notion_starter/services/exploracao.py +220 -0
  41. notion_starter-0.3.0/src/notion_starter/services/historico_repositorios.py +375 -0
  42. notion_starter-0.3.0/src/notion_starter/services/ia.py +203 -0
  43. notion_starter-0.3.0/src/notion_starter/services/importacao.py +125 -0
  44. notion_starter-0.3.0/src/notion_starter/services/ingestao.py +490 -0
  45. notion_starter-0.3.0/src/notion_starter/services/inventario_github.py +682 -0
  46. notion_starter-0.3.0/src/notion_starter/services/normalizacao.py +372 -0
  47. notion_starter-0.3.0/src/notion_starter/services/projetos.py +41 -0
  48. notion_starter-0.3.0/src/notion_starter/services/relacoes.py +234 -0
  49. notion_starter-0.3.0/src/notion_starter/services/relatorios_diarios.py +177 -0
  50. notion_starter-0.3.0/src/notion_starter/services/relatorios_docx.py +879 -0
  51. notion_starter-0.3.0/src/notion_starter/services/reordenacao.py +195 -0
  52. notion_starter-0.3.0/src/notion_starter/services/schema.py +108 -0
  53. notion_starter-0.3.0/src/notion_starter/services/sincronizar_github.py +168 -0
  54. notion_starter-0.3.0/src/notion_starter/services/tarefas.py +154 -0
  55. notion_starter-0.3.0/src/notion_starter/tasks.py +411 -0
  56. notion_starter-0.3.0/src/notion_starter/utils.py +112 -0
  57. notion_starter-0.3.0/src/notion_starter/valores_br.py +147 -0
  58. notion_starter-0.3.0/tests/conftest.py +11 -0
  59. notion_starter-0.3.0/tests/test_anexos.py +85 -0
  60. notion_starter-0.3.0/tests/test_client.py +514 -0
  61. notion_starter-0.3.0/tests/test_client_resiliencia.py +628 -0
  62. notion_starter-0.3.0/tests/test_content.py +576 -0
  63. notion_starter-0.3.0/tests/test_fonte_planilha.py +242 -0
  64. notion_starter-0.3.0/tests/test_git_historico.py +142 -0
  65. notion_starter-0.3.0/tests/test_importacao.py +91 -0
  66. notion_starter-0.3.0/tests/test_inventory.py +239 -0
  67. notion_starter-0.3.0/tests/test_properties.py +127 -0
  68. notion_starter-0.3.0/tests/test_readers.py +144 -0
  69. notion_starter-0.3.0/tests/test_schema.py +40 -0
  70. notion_starter-0.3.0/tests/test_schema_descricao.py +143 -0
  71. notion_starter-0.3.0/tests/test_services_conteudo_seguranca.py +241 -0
  72. notion_starter-0.3.0/tests/test_services_estrutura_projeto.py +163 -0
  73. notion_starter-0.3.0/tests/test_services_historico_repositorios.py +294 -0
  74. notion_starter-0.3.0/tests/test_services_relacoes.py +171 -0
  75. notion_starter-0.3.0/tests/test_services_relatorios_diarios.py +203 -0
  76. notion_starter-0.3.0/tests/test_services_relatorios_docx.py +360 -0
  77. notion_starter-0.3.0/tests/test_services_reordenacao.py +139 -0
  78. notion_starter-0.3.0/tests/test_services_schema.py +78 -0
  79. notion_starter-0.3.0/tests/test_tasks.py +489 -0
  80. notion_starter-0.3.0/tests/test_valores_br.py +75 -0
@@ -0,0 +1,15 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ indent_style = space
8
+ indent_size = 2
9
+ trim_trailing_whitespace = true
10
+
11
+ [*.py]
12
+ indent_size = 4
13
+
14
+ [*.md]
15
+ trim_trailing_whitespace = false
@@ -0,0 +1,13 @@
1
+ # Token de integração do Notion — usado pelos exemplos em examples/.
2
+ # Crie uma integração em https://www.notion.so/my-integrations,
3
+ # compartilhe o database/página alvo com ela e cole o token aqui.
4
+ # O token deve começar com "ntn_".
5
+ NOTION_TOKEN=ntn_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
6
+
7
+ # ID do database de tarefas padrão (opcional — pode ser passado por chamada).
8
+ # NOTION_DATABASE_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
9
+
10
+ # Token do GitHub (opcional; necessário só para repositórios privados nos
11
+ # serviços de inventário/sincronização GitHub).
12
+ # Prefira token fine-grained com acesso somente de leitura.
13
+ # GITHUB_TOKEN=
@@ -0,0 +1,27 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ python:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - name: Install
21
+ run: |
22
+ python -m pip install --upgrade pip
23
+ pip install -e ".[dev]"
24
+ - name: Lint
25
+ run: ruff check .
26
+ - name: Test
27
+ run: pytest -q
@@ -0,0 +1,68 @@
1
+ name: Release Python package
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: "3.13"
19
+ - name: Build and validate metadata
20
+ run: |
21
+ python -m pip install --upgrade build twine
22
+ python -m build
23
+ python -m twine check dist/*
24
+ - uses: actions/upload-artifact@v4
25
+ with:
26
+ name: notion-starter-dist
27
+ path: dist/*
28
+
29
+ smoke:
30
+ needs: build
31
+ strategy:
32
+ fail-fast: false
33
+ matrix:
34
+ os: [ubuntu-latest, windows-latest, macos-latest]
35
+ python-version: ["3.10", "3.13"]
36
+ runs-on: ${{ matrix.os }}
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+ - uses: actions/download-artifact@v4
40
+ with:
41
+ name: notion-starter-dist
42
+ path: dist
43
+ - uses: actions/setup-python@v5
44
+ with:
45
+ python-version: ${{ matrix.python-version }}
46
+ - name: Install wheel and smoke
47
+ shell: bash
48
+ run: |
49
+ python -m pip install --upgrade pip
50
+ python -m pip install dist/*.whl
51
+ python -c "from notion_starter import NotionClient; print(NotionClient.__name__)"
52
+
53
+ publish:
54
+ needs: [build, smoke]
55
+ if: startsWith(github.ref, 'refs/tags/v')
56
+ runs-on: ubuntu-latest
57
+ environment: pypi
58
+ permissions:
59
+ id-token: write
60
+ steps:
61
+ - uses: actions/download-artifact@v4
62
+ with:
63
+ name: notion-starter-dist
64
+ path: dist
65
+ - name: Publish to PyPI with Trusted Publishing
66
+ uses: pypa/gh-action-pypi-publish@release/v1
67
+ with:
68
+ packages-dir: dist/
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .env
4
+ .venv/
5
+ venv/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ *.sqlite3
9
+ dist/
10
+ build/
11
+ *.egg-info/
12
+ node_modules/
13
+ staticfiles/
14
+ .notion-backups/
@@ -0,0 +1,33 @@
1
+ # AGENTS.md — notion-starter
2
+
3
+ Biblioteca Python **base** do ecossistema [Automações do Notion](https://github.com/Felipe-Alcantara/Automa-es-do-Notion) — o hub tem o roteamento completo entre módulos; leia-o se a tarefa envolver o CLI ou o app.
4
+
5
+ ## O que vive aqui
6
+
7
+ | Arquivo | Responsabilidade |
8
+ | --- | --- |
9
+ | `src/notion_starter/client.py` | Cliente HTTP resiliente (retries por semântica, rate limit, erros tipados); `obter_pagina`/`atualizar_pagina` leem e editam propriedades |
10
+ | `src/notion_starter/schema.py` | Leitura/comparação de schema de databases |
11
+ | `src/notion_starter/tasks.py` | `Tarefa`, `TaskList`, `CamposTarefa` |
12
+ | `src/notion_starter/content.py` + `properties.py` + `readers.py` | Conversão Markdown ↔ blocos (lógica pura); `properties.title`/`rich_text` fatiam texto >2000 (via `utils.fatiar_utf16`) |
13
+ | `src/notion_starter/inventory.py` | Mapeamento do workspace (lógica pura, sem rede) |
14
+ | `src/notion_starter/git_historico.py` | Histórico de um repositório git agrupado por dia (lógica pura, sem rede/Notion) |
15
+ | `src/notion_starter/github.py`, `openrouter.py` | Adaptadores externos compartilhados por CLI e app |
16
+ | `src/notion_starter/services/` | Casos de uso compartilhados entre CLI e app; defaults de ambiente continuam nos consumidores via `integrations.notion` |
17
+ | `src/notion_starter/utils.py` | Saneamento de texto/JSON (surrogates inválidos); `fatiar_utf16` (fatia por unidades UTF-16, teto em `constants.MAX_RICH_TEXT`) |
18
+ | `examples/` | Scripts de uso direto da lib |
19
+
20
+ ## Regras
21
+
22
+ - Regra de negócio compartilhada entre CLI e app pode viver em `notion_starter.services`.
23
+ Bordas e configuração de ambiente continuam fora daqui (`cli/`, `api/`, `mcp_server`,
24
+ `integrations.notion` dos consumidores).
25
+ - Só o `NotionClient` fala com a API do Notion; módulos de lógica são puros.
26
+ - Código e mensagens em português; exceções derivam de `NotionSyncError`; Conventional Commits.
27
+ - **Mudança de API pública quebra dois repos consumidores** — mantenha compatibilidade ou avise nos dois.
28
+
29
+ ## Testar
30
+
31
+ ```bash
32
+ python -m pytest # não precisa instalar; tests/conftest.py adiciona src/ ao path
33
+ ```
@@ -0,0 +1,95 @@
1
+ # 🤝 Contribuindo com o notion-starter
2
+
3
+ Obrigado por querer contribuir! Este repositório é a biblioteca Python base do
4
+ ecossistema [Automações do Notion](https://github.com/Felipe-Alcantara/Automa-es-do-Notion):
5
+ cliente resiliente para a API do Notion, schema, tarefas, conteúdo, inventário e a
6
+ camada compartilhada `notion_starter.services`. Issues, correções de documentação,
7
+ novos helpers, exemplos, testes e melhorias de resiliência são bem-vindos.
8
+
9
+ > Contribuições devem preservar os contratos existentes, a documentação viva e o
10
+ > gate de qualidade descrito abaixo.
11
+
12
+ ---
13
+
14
+ ## 🚀 Como Contribuir
15
+
16
+ 1. **Faça um fork** do repositório.
17
+ 2. **Crie uma branch** descritiva (`fix/...`, `feat/...`, `docs/...`) para mudanças
18
+ grandes; correções pequenas podem ir direto no `main` de quem mantém.
19
+ 3. **Faça suas mudanças** seguindo os padrões abaixo.
20
+ 4. **Rode os testes e o lint** antes de abrir o PR.
21
+ 5. **Abra um Pull Request** explicando o que mudou e por quê.
22
+
23
+ Não tem certeza por onde começar? Abra uma issue descrevendo a ideia — a gente
24
+ conversa antes de você investir tempo no código.
25
+
26
+ ---
27
+
28
+ ## 🛠️ Ambiente de Desenvolvimento
29
+
30
+ ```bash
31
+ git clone https://github.com/Felipe-Alcantara/notion-starter.git
32
+ cd notion-starter
33
+ python -m pip install -e ".[dev]"
34
+
35
+ # Gate de qualidade (HTTP é mockado — não precisa de token nem rede)
36
+ ruff check .
37
+ python -m pytest
38
+ ```
39
+
40
+ Requer Python 3.10+. Copie `.env.example` para `.env` apenas se for rodar os
41
+ exemplos de `examples/` contra um workspace real do Notion — nunca versione o `.env`.
42
+
43
+ ---
44
+
45
+ ## ✅ Padrões de Qualidade
46
+
47
+ - **Entenda o padrão existente antes de alterar.** Módulos coesos por
48
+ responsabilidade: `client` (único que fala HTTP com o Notion), `schema`,
49
+ `properties`/`readers`, `content`, `tasks`, `inventory`, `services` (casos de uso,
50
+ sem HTTP próprio). Preserve essas fronteiras.
51
+ - **Prefira a solução mais simples** que resolva o problema real. A biblioteca tem
52
+ poucas dependências de runtime de propósito.
53
+ - **Preserve contratos.** Assinaturas públicas, objetos (`Tarefa`, `RepoInfo`, ...) e
54
+ exceções (`NotionSyncError` e derivadas) devem permanecer estáveis; mudança
55
+ quebradora precisa ser explícita e documentada.
56
+ - **Tipos e validação.** `TypedDict` para payloads, `dataclass` para resultados;
57
+ valide entradas externas.
58
+ - **Não exponha segredos.** Nada de tokens, IDs reais ou URLs privadas no código,
59
+ nos testes ou na documentação.
60
+ - **Teste o comportamento.** Bugs corrigidos viram caso de regressão; HTTP é sempre
61
+ mockado com `responses`.
62
+ - **Código, docstrings e mensagens de erro em português.**
63
+ - **Atualize a documentação viva** (`README.md` e `IA.md`) no mesmo passo quando a
64
+ mudança alterar comportamento, estrutura ou comandos.
65
+
66
+ ---
67
+
68
+ ## ✍️ Padrões de Linguagem (Documentação e Logs)
69
+
70
+ - **Escreva para qualquer leitor** — linguagem geral e acessível, sem jargão interno.
71
+ - **Sem valores hardcoded** — use placeholders genéricos em vez de caminhos, tokens
72
+ ou IDs reais.
73
+ - **Enquadre o trabalho futuro como convite à contribuição** em vez de uma lista de
74
+ tarefas interna.
75
+
76
+ ---
77
+
78
+ ## 🔄 Fluxo de Pull Request
79
+
80
+ Um bom PR responde claramente:
81
+
82
+ - **O que mudou?**
83
+ - **Por que mudou?**
84
+ - **Como foi validado?** (ex.: `ruff check .` + `python -m pytest`)
85
+ - **Qual risco sobrou?**
86
+
87
+ Mantenha o PR focado: evite misturar refatoração ampla com novas funcionalidades.
88
+ Use commits pequenos no formato `tipo: descrição` (`feat`/`fix`/`docs`/`refactor`/`chore`).
89
+
90
+ ---
91
+
92
+ ## 💬 Código de Conduta
93
+
94
+ Seja respeitoso e acolhedor. Este é um espaço para aprender e construir juntos —
95
+ contribuições de pessoas de todos os níveis de experiência são bem-vindas.
@@ -0,0 +1,381 @@
1
+ # 🤖 IA.md — Contexto operacional do notion-starter
2
+
3
+ > **O que é**: Memória técnica deste repositório para retomada de contexto por IA ou
4
+ > por um novo mantenedor, sem reler todo o código. Baseado no template de contexto do
5
+ > Felixo System Design.
6
+ >
7
+ > **Histórico anterior**: este módulo nasceu da separação do monorepo
8
+ > [Automações do Notion](https://github.com/Felipe-Alcantara/Automa-es-do-Notion)
9
+ > em 2026-07-02. Toda a linha do tempo anterior (decisões de arquitetura, bugs e
10
+ > validações do core) permanece registrada no `IA.md` do hub — este arquivo cobre a
11
+ > vida do módulo a partir da separação.
12
+
13
+ ---
14
+
15
+ ## 📊 ESTADO ATUAL (RESUMO VIVO)
16
+
17
+ Última atualização: [2026-09-04]
18
+
19
+ - Fase: biblioteca base estável, preparada como pacote `notion-starter` para a
20
+ distribuição da CLI única.
21
+ - Qualidade: 355 testes verdes e `ruff` limpo; CI cobre Python 3.10–3.13.
22
+ - Documentação: README alinhado ao Felixo System Design e contrato de qualidade
23
+ centralizado em `QUALIDADE.md`.
24
+ - Próximos passos abertos: confirmação do contrato de publicação no PyPI e mais
25
+ tipos de propriedade/bloco e escrita em data sources.
26
+ - Risco conhecido: consumidores devem fixar suas próprias resoluções de
27
+ dependências quando precisarem de builds reproduzíveis.
28
+
29
+ ---
30
+
31
+ ## 🎯 OBJETIVO DO PROJETO
32
+
33
+ [2026-07-02] `notion-starter` é a biblioteca Python base do ecossistema: cliente
34
+ resiliente para a API do Notion (retry/backoff, cache de schema), helpers de
35
+ propriedade e leitura, conversor Markdown ↔ blocos, camada de tarefas (`TaskList`),
36
+ inventário do workspace e a camada compartilhada `notion_starter.services`
37
+ (clonagem, conteúdo, ingestão, sincronização GitHub, exportação DOCX). É consumida
38
+ pelo `notion-tasks-cli` e pelo `notion-workspace-app`.
39
+
40
+ ---
41
+
42
+ ## 📐 DECISÕES DE ARQUITETURA
43
+
44
+ - [2026-07-02] Fronteiras herdadas do monorepo (registradas no hub): só o
45
+ `NotionClient` fala HTTP com o Notion; `services` orquestra casos de uso sem
46
+ conhecer HTTP de borda; conversões e leituras (`content`, `properties`,
47
+ `readers`, `inventory`) são lógica pura testável sem rede.
48
+ - [2026-07-02] Exceções continuam derivando de `NotionSyncError` (compatibilidade).
49
+ - [2026-07-08] O repositório **não tem `start_app.py`**: é uma biblioteca importável,
50
+ não um programa. A porta de entrada interativa do ecossistema vive no hub
51
+ (`Automa-es-do-Notion/start_app.py`) e no `notion-workspace-app`. Os exemplos de
52
+ `examples/` são executados diretamente (`python examples/<nome>.py`) e documentados
53
+ no README. Exceção registrada conforme o padrão de qualidade.
54
+
55
+ ---
56
+
57
+ ## 🛠️ STACK & DEPENDÊNCIAS
58
+
59
+ - Python 3.10+ (CI: 3.10–3.13). Runtime: `requests`, `python-docx` (exportação DOCX),
60
+ `typing_extensions` só em Python < 3.11.
61
+ - Dev: `pytest`, `responses` (HTTP mockado), `ruff`.
62
+
63
+ ---
64
+
65
+ ## 🧪 TESTES & GATE
66
+
67
+ - Gate: `ruff check .` + `python -m pytest` (193 testes em 2026-07-13, sem rede).
68
+ - CI: GitHub Actions (`.github/workflows/ci.yml`) com matriz Python 3.10–3.13.
69
+
70
+ ---
71
+
72
+ ## 🧠 LINHA DO TEMPO
73
+
74
+ - [2026-07-02] ✅ Módulo extraído do monorepo. Recebeu depois a consolidação da
75
+ camada compartilhada: `integrations` (GitHub/OpenRouter) e `services` comuns dos
76
+ consumidores viraram shims apontando para cá.
77
+ - [2026-07-08] ✅ Alinhamento ao padrão de qualidade Felixo: adicionados
78
+ `CONTRIBUTING.md`, `IA.md`, `.env.example` e CI GitHub Actions. Decisão registrada:
79
+ sem `start_app.py` por ser biblioteca. Validação: `ruff check .` limpo e 183 testes
80
+ verdes.
81
+ - [2026-07-13] ✅ Merge do PR #1 (contribuição externa): `mover_pagina`,
82
+ `mover_database` (re-parent, versão 2025-09-03 por chamada) e `enviar_arquivo`
83
+ (File Upload API) + `properties.arquivo_enviado`. Em seguida, hardening no
84
+ `main`: o passo multipart do upload passou a usar retry/backoff próprio
85
+ (`_enviar_multipart`, espelhando a política do `_request_json` — antes era um
86
+ `requests.post` cru, sem resiliência) e `enviar_arquivo` valida o limite de
87
+ 20 MB (`NOTION_UPLOAD_MAX_BYTES`) antes de tocar a API. Motivo: migrações com
88
+ dezenas de uploads quebravam no primeiro 429 do `/send`. Validação:
89
+ `ruff check .` limpo e 193 testes verdes (3 novos: limite, retry em 429 e
90
+ retry em falha de rede).
91
+ - [2026-07-13] ✅ Fechadas as melhorias propostas no relatório de 10/07 (5.2):
92
+ `criar_database` estendido (is_inline, icone, descricao, prefixo_id/unique_id),
93
+ `valores_br` (números e datas BR), `FontePlanilha` (.xlsx via extra
94
+ `planilha`, .csv via stdlib) com `ItemColetado.propriedades` tipadas,
95
+ `services/importacao` (import em lote retomável por estado local),
96
+ `properties.schema_propriedade` e `services/anexos.anexar_arquivo` (upload +
97
+ propriedade files preservando anexos). Validação: 230 testes verdes, ruff
98
+ limpo, CI verde.
99
+ - [2026-07-18] ✅ Documentação alinhada ao Felixo System Design: README passou a
100
+ ter badges, índice, árvore real, guia de uso e rodapé open source;
101
+ `QUALIDADE.md` centralizou o gate e registrou a exceção motivada de versões
102
+ mínimas para uma biblioteca instalável. Motivo: tornar setup, manutenção e
103
+ critérios de pronto verificáveis sem quebrar a resolução dos consumidores.
104
+ Validação: 235 testes verdes e `ruff` limpo.
105
+
106
+ - [2026-07-23] ✅ `services/inventario_github.atualizar_repos`/`exportar_repos`
107
+ passam a aceitar um **repositório específico** em `contas` (`owner/repo` ou
108
+ URL completa do repo), além de contas inteiras. Nova função privada
109
+ `_repo_completo_da_entrada` distingue os dois formatos (via `_PADRAO_URL_REPO`
110
+ e checagem de `/` fora de URL de perfil) e `_listar_repos_da_entrada` chama
111
+ `GitHubClient.detalhar_repo` nesse caso, em vez de `listar_repos`. Motivo:
112
+ inventariar um projeto pontual de terceiros (ex.: um repo de outra conta)
113
+ sem trazer o resto dos repositórios dela para a database. Validação: 6 novos
114
+ testes em `notion-tasks-cli/tests/test_services_inventario_github.py`
115
+ (reconhecimento de owner/repo, URL de repo, URL de perfil sem repo, coleta
116
+ sem duplicar quando o mesmo repo aparece via conta e via entrada avulsa);
117
+ 235 testes do notion-starter e 132 do notion-tasks-cli seguem verdes, ruff
118
+ limpo em ambos.
119
+
120
+ - [2026-07-23] ✅ Novo módulo `services/estrutura_projeto.py` com três casos de
121
+ uso para a moldura fixa de projeto do workspace (README + `## Acompanhamento`
122
+ com 4 subpáginas + `## Planejamento e documentação` com 2 databases, ver
123
+ `DESIGN-WORKSPACE-NOTION.md` no hub): `inspecionar_estrutura` (lê
124
+ recursivamente subpáginas/databases de uma página de referência, read-only),
125
+ `clonar_estrutura_projeto` (recria a forma — títulos de subpágina + schema de
126
+ databases via `clonar_database` — em outra página, sem herdar conteúdo) e
127
+ `montar_estrutura_projeto` (aplica o padrão do zero). Também
128
+ `services/conteudo.criar_subpagina`, que expõe `client.criar_subpagina` como
129
+ caso de uso reutilizável (antes só usado internamente pelo README do
130
+ GitHub). Motivo: montar essa estrutura manualmente (como feito para o
131
+ projeto Audiofy) exigiu inspecionar várias páginas de exemplo bloco a bloco,
132
+ sem nenhuma ferramenta reutilizável — o padrão documentado não tinha
133
+ automação correspondente. Validação: 9 novos testes em
134
+ `notion-tasks-cli/tests/test_services_estrutura_projeto.py`; 244 testes do
135
+ notion-starter e 137 do notion-tasks-cli seguem verdes, ruff limpo em ambos.
136
+
137
+ - [2026-07-23] ✅ `NotionClient.anexar_blocos` ganhou o parâmetro opcional
138
+ `apos_bloco_id`, usando `position: after_block` da API do Notion (confirmado
139
+ na doc oficial, `developers.notion.com/reference/patch-block-children` —
140
+ substitui o antigo `after` no nível raiz, hoje legado) para inserir blocos
141
+ novos depois de um irmão específico, não só no final. Novo
142
+ `services/reordenacao.py` com `reordenar_bloco`: como a API do Notion não
143
+ tem endpoint para mover um bloco existente, a implementação apaga e recria
144
+ na posição pedida — sempre grava um backup em JSON do bloco original antes
145
+ de apagar. **Risco documentado e ativamente bloqueado por padrão**: para
146
+ `child_page`/`child_database`, apagar e recriar gera um **ID novo**,
147
+ quebrando links/backlinks/referências externas salvas para o ID antigo; a
148
+ função levanta `BlocoArriscadoError` nesses tipos a menos que o chamador
149
+ passe `forcar_tipos_arriscados=True` explicitamente. Motivo: precisei
150
+ reordenar um database solto numa página de projeto (Audiofy) e não havia
151
+ ferramenta alguma para isso — o único caminho seria apagar manualmente e
152
+ reimportar dados. Validação: 7 novos testes em
153
+ `tests/test_services_reordenacao.py`, mais os testes existentes de
154
+ `anexar_blocos`; 251 testes do notion-starter e 140 do notion-tasks-cli
155
+ seguem verdes, ruff limpo em ambos.
156
+
157
+ - [2026-07-23] ✅ Correção: `montar_estrutura_projeto` criava "Próximos passos"
158
+ e "Documentações" com um schema mínimo genérico (título + Observações), que
159
+ não batia com o schema real observado nas páginas de projeto existentes do
160
+ workspace ("Próximos passos": Tarefa/Status/Prioridade/Concluída/
161
+ Observações; "Documentações": Documento/Tipo/Status/Criado em/Atualizado
162
+ em/URL/Observações — com `select` de opções coerentes, não `rich_text`
163
+ solto). `DATABASES_PLANEJAMENTO` virou um dict `título -> schema` em vez de
164
+ uma tupla de títulos. Motivo: ao aplicar a ferramenta na página real do
165
+ projeto Audiofy, os databases criados ficaram visivelmente fora do padrão
166
+ das páginas de referência lidas anteriormente. Validação: teste ajustado
167
+ para checar as colunas específicas de cada database (não mais um schema
168
+ genérico); 251 testes do notion-starter e 140 do notion-tasks-cli seguem
169
+ verdes, ruff limpo.
170
+
171
+ - [2026-07-23] ✅ Correção crítica em `services/reordenacao.reordenar_bloco`:
172
+ `child_database` foi movido de `_TIPOS_ARRISCADOS` (bloqueável com
173
+ `forcar_tipos_arriscados=True`) para uma nova categoria `_TIPOS_IMPOSSIVEIS`,
174
+ recusada **sempre**, sem flag de escape (`BlocoImpossivelError`, distinta de
175
+ `BlocoArriscadoError`). Motivo: confirmado em produção (workspace real da
176
+ Flávia) que apagar+recriar um `child_database` via `anexar_blocos` **nunca
177
+ funciona** — a API do Notion só cria databases por `POST /databases`, não
178
+ por `PATCH /blocks/.../children`; a implementação anterior prometia essa
179
+ capacidade com a flag de força e, ao ser usada, apagava o database original
180
+ (com linhas) e falhava ao recriá-lo, deixando-o arquivado até restauração
181
+ manual. `child_page` continua suportado com a flag (recriação real funciona,
182
+ só o ID muda). Validação: novo teste
183
+ `test_reordenar_bloco_rejeita_child_database_mesmo_com_forcar`; 252 testes
184
+ do notion-starter e 141 do notion-tasks-cli verdes, ruff limpo.
185
+
186
+ - [2026-07-24] ✅ Novo `services/schema.garantir_coluna`: adiciona uma coluna a
187
+ um database **já existente**, sem apagar nada. Generaliza o padrão que só
188
+ existia hardcoded em
189
+ `inventario_github.garantir_coluna_hash` (que só cuidava da coluna de hash
190
+ do README) para qualquer nome/tipo de coluna. Usa *data source* quando
191
+ disponível, cai para o endpoint clássico de `database` caso contrário —
192
+ mesma estratégia do original. Motivo: nenhuma ferramenta do ecossistema
193
+ evoluía o schema de um database depois de criado (`criar-database` só
194
+ define na criação; `editar-linha`/`importar-planilha` só escrevem em
195
+ colunas existentes) — faltou ao tentar adicionar uma coluna real (Idioma)
196
+ a um database do workspace da Flávia. TDD: testes escritos e confirmados
197
+ falhando antes da implementação existir, só então o módulo foi criado.
198
+ Validação: 4 novos testes em `test_services_schema.py`; 256 testes do
199
+ notion-starter seguem verdes, ruff limpo.
200
+ - [2026-07-27] ✅ Relatórios diários a partir do git: `git_historico.py` (módulo
201
+ puro — executa `git` e agrupa commits por dia, sem conhecer Notion nem rede) e
202
+ `services/relatorios_diarios.py` (upsert **por data**: se o dia já tem página,
203
+ o corpo é anexado em vez de duplicar a linha). A separação existe porque o
204
+ histórico serve a outros destinos além do Notion, e o upsert serve a conteúdo
205
+ de qualquer origem, não só git. Decisão: página existente **preserva** suas
206
+ propriedades por padrão (`atualizar_propriedades_existentes=False`) — um mesmo
207
+ dia costuma acumular trabalho de projetos diferentes, e sobrescrever o resumo
208
+ apagaria o registro do outro projeto. Nasceu de um script pontual que publicou
209
+ 9 dias de trabalho de um repositório na database de relatórios. TDD: testes
210
+ antes da implementação, e um deles pegou um defeito real — mensagem de commit
211
+ contendo o byte separador fazia o registro ser descartado silenciosamente
212
+ (corrigido limitando as divisões do `split`). Exemplo executável em
213
+ `examples/relatorios_do_git.py`, com `--simular`. Validação: 30 testes novos,
214
+ 286 do notion-starter verdes, ruff limpo, e execução real contra um
215
+ repositório de 9 dias.
216
+
217
+ - [2026-08-13] ✅ `DiaDeTrabalho` ganhou `duracao_minutos` e
218
+ `duracao_por_extenso()` (ex.: `"5h30"`, `"35 min"`, vazio com um único
219
+ commit), e `resumo_markdown()` passou a incluir a duração entre o primeiro
220
+ e o último commit do dia na linha de resumo, além da hora que já existia
221
+ (`"2 commits, das 09:00 às 14:30 (duração: 5h30)."`). Motivo: alguns
222
+ agentes já registravam hora e duração manualmente ao publicar relatórios
223
+ no Notion (ex.: "Commit automático do Fetch All das 08:36") — o padrão foi
224
+ formalizado em `docs/PADRAO-RELATORIOS.md` no hub, e este módulo passou a
225
+ gerar esse dado automaticamente em vez de depender de alguém lembrar.
226
+ Validação: 5 novos testes em `test_git_historico.py`; 295 testes do
227
+ notion-starter verdes, ruff limpo.
228
+
229
+ ---
230
+
231
+ Ideias abertas à contribuição: cobertura de mais tipos de propriedade do Notion,
232
+ mais tipos de bloco no conversor Markdown, escrita de linhas em data sources.
233
+
234
+ ---
235
+
236
+ ## [2026-08-17] Operar Notion às cegas era o gargalo real — três defesas na biblioteca
237
+
238
+ **Contexto.** Uma sessão longa de trabalho real no workspace (criar 17 tarefas
239
+ ricas, reescrever 16 e montar 48 ligações entre elas) expôs o mesmo padrão em
240
+ todas as fricções: **a biblioteca não respondia perguntas que antecedem a
241
+ escrita**, e o custo aparecia depois, já gravado no Notion.
242
+
243
+ ### 1. `descrever_database` — ler o schema sem chamar a API na mão
244
+
245
+ `schema.py` só sabia **comparar** um database com um schema esperado. Para
246
+ descobrir o nome exato de uma coluna, os valores aceitos por um select ou como
247
+ uma relação está configurada, era preciso chamar `/v1/databases/<id>` cru e ler
248
+ JSON — passo que se pula com pressa.
249
+
250
+ Entram `DescricaoDatabase`, `Coluna` e `Relacao` (funções puras, sem rede):
251
+ colunas ordenadas com o título primeiro, opções de select/status/multi_select,
252
+ `editavel` marcando os tipos que o Notion calcula e recusa em PATCH
253
+ (`TIPOS_SOMENTE_LEITURA`), e a configuração de cada relação com `auto_referente`
254
+ comparando IDs sem hífen.
255
+
256
+ ### 2. `services/relacoes.py` — o tipo da relação não prevê o comportamento
257
+
258
+ Hipótese inicial: `single_property` = mão única, então uma ligação simétrica
259
+ exige gravar as duas pontas. **Medido no workspace real e a hipótese caiu
260
+ parcialmente.** Experimento: duas linhas novas no database `30296e2d…`, coluna
261
+ `Subtarefas relacionadas`, reportada como `single_property` pelas versões de API
262
+ `2022-06-28` **e** `2025-09-03`. PATCH só na ponta C→D; releitura de D **já
263
+ trazia C**. O Notion espelhou sem segunda escrita.
264
+
265
+ Conclusão registrada: **não dá para deduzir do tipo declarado se o espelho
266
+ acontece.** Assumir "espelha" deixa metade da malha faltando; assumir "não
267
+ espelha" gasta requisição e pode duplicar. `relacionar()` resolve conferindo —
268
+ escreve uma ponta, **relê a outra** e grava só o que faltar. Sai simétrico nos
269
+ dois mundos, é idempotente e o retorno diz quais páginas precisaram de escrita.
270
+ Relação para outro database e de mão única não tem coluna de volta: escreve só A.
271
+
272
+ ### 3. Reescrita que não custa o que não sabe repor
273
+
274
+ `limpar_conteudo` apagava **todos** os blocos de topo, e `escrever(substituir=True)`
275
+ chamava isso em silêncio. Consequência real: reescrever o texto de uma página
276
+ apagava a imagem dela (URL do Notion é assinada e expira — a lixeira não devolve
277
+ o arquivo) e, pior, apagava `child_database`, **levando o database inteiro** com
278
+ ID novo ao restaurar e todo link salvo quebrado.
279
+
280
+ Agora `TIPOS_NAO_RECRIAVEIS` (imagem, arquivo, vídeo, PDF, áudio, embed,
281
+ bookmark, link_preview, `child_page`, `child_database`, synced_block, table,
282
+ column_list) é **preservado por padrão**; apagar exige `incluir_nao_recriaveis=True`.
283
+ Os retornos viraram `ResultadoLimpeza`/`ResultadoEscrita`, que implementam
284
+ `__int__`/`__eq__` contra `int` — quem usava só a contagem antiga não quebrou.
285
+
286
+ ### 4. `EscritaAbaixoDeDatabaseError` — a regra que virou guarda
287
+
288
+ A "regra do link" (se o alvo é database, trabalhe nas linhas) existia só em
289
+ documentação, e modelos mais fracos a ignoravam: recebiam o link de uma página
290
+ que **contém** uma database e escreviam um parágrafo solto abaixo da tabela,
291
+ onde não vira linha nem aparece em view nenhuma.
292
+
293
+ `escrever_conteudo` agora chama `databases_da_pagina()` antes de qualquer escrita
294
+ e **recusa** com uma exceção que lista as databases encontradas com ID e traz os
295
+ comandos prontos (`linhas` → `conteudo` → `editar-linha`/`escrever <linha_id>`).
296
+ A checagem vive no serviço, não na borda, para valer também para MCP e scripts.
297
+ `mesmo_com_database=True` libera quando o bloco solto é mesmo a intenção.
298
+
299
+ **Validação real** (2026-08-17, workspace do usuário): `escrever` na página HOME
300
+ `1fc91f95…` foi recusado listando as três databases de dentro; uma database de
301
+ teste criada dentro de uma linha sobreviveu a `escrever --substituir`; as quatro
302
+ linhas de teste foram arquivadas ao fim. 329 testes verdes, `ruff` limpo.
303
+
304
+ ---
305
+
306
+ ## [2026-08-17] Histórico de vários repositórios, e a varredura que acha o dia esquecido
307
+
308
+ **Contexto.** `git_historico` já reconstruía o dia de trabalho de **um**
309
+ repositório. Mas um dia real quase nunca cabe num só — mexe-se na biblioteca, no
310
+ CLI que a consome e no app que a expõe — e o relatório precisa contar isso junto,
311
+ senão o mesmo dia vira três narrativas soltas que ninguém cruza depois.
312
+
313
+ `services/historico_repositorios.py` agrega os históricos e agrupa por data,
314
+ produzindo o texto no formato que os relatórios diários já usam: **hora e duração
315
+ por projeto**, nunca só a data. Acrescenta arquivos e linhas por commit, que
316
+ respondem a "foi ajuste ou reescrita?".
317
+
318
+ ### `descobrir_repositorios` — o motivo de existir
319
+
320
+ O pedido original era registrar o histórico dos projetos citados numa conversa.
321
+ A pergunta por trás dele era outra: *existe algum dia de trabalho que ficou sem
322
+ registro?* Listar repositórios à mão só encontra o que já se lembra — e o
323
+ esquecido, por definição, não está nessa lista.
324
+
325
+ Medido na máquina de origem: a lista de memória tinha **15 repositórios**; a
326
+ varredura encontrou **47**, e o histórico saltou de 125 para **201 dias** com
327
+ commit, de 2024-03-26 a 2026-08-17.
328
+
329
+ ### O corpo diz que é reconstruído
330
+
331
+ O texto gerado abre avisando que veio do git e que decisões e pendências sem
332
+ commit não aparecem ali. Inventar narrativa a partir de mensagem de commit é o
333
+ jeito mais fácil de povoar um relatório com ficção plausível; o aviso é o que
334
+ impede o leitor de tomar log por relato.
335
+
336
+ ### Decisões de robustez
337
+
338
+ - Repositório inacessível é **pulado**, não fatal: numa lista de quinze, um
339
+ caminho que mudou não pode custar o histórico dos outros catorze.
340
+ - `--shortstat` tem formato irregular (omite a metade que é zero, e merge não
341
+ gera linha). O parser vive numa função pura, testada com os três formatos.
342
+ - `relatorios_diarios` voltou a converter o retorno de `escrever_conteudo` para
343
+ inteiro com `int(...)` em vez de ler `.anexados` — converter mantém válido
344
+ qualquer double de teste que devolva só o número.
345
+
346
+ **Validação:** 354 testes verdes, `ruff` limpo; publicação real de 201 dias no
347
+ workspace do usuário (115 páginas criadas, 86 complementadas).
348
+
349
+ ---
350
+
351
+ ## [2026-08-25] `TaskList.criar` descobre o título do database
352
+
353
+ **Sintoma.** A criação de uma linha em `Relatórios diários` falhava porque o
354
+ payload sempre enviava a propriedade `Tarefa`, embora o título real se chamasse
355
+ `Relatório`. A API aceitava criação direta com o schema correto; o defeito estava
356
+ no modelo compartilhado.
357
+
358
+ **Decisão.** `TaskList.criar` lê o schema antes do POST, encontra a única coluna
359
+ `title` com `descrever_database` e usa seu nome real. Os atalhos do modelo de
360
+ tarefas (`Etapa`, `Prazo`, `Esforço`, `Áreas da vida`) só entram quando a coluna
361
+ existe com tipo compatível. A assinatura e o retorno `Tarefa` foram preservados;
362
+ somente a criação ficou genérica, sem fingir que listar/editar databases
363
+ arbitrários também são operações de tarefas.
364
+
365
+ **Validação.** 355 testes verdes e `ruff` limpo. No workspace real, foram
366
+ criadas com sucesso uma linha em `Relatórios diários` (título `Relatório`) e
367
+ outra em `Tasks` (título `Tarefa`, com `Etapa`/`Esforço`); ambas foram arquivadas
368
+ ao fim.
369
+
370
+ ## [2026-09-04] Pacote base preparado para a distribuição sem clone
371
+
372
+ O `pyproject.toml` foi alinhado ao contrato de distribuição da CLI única: a
373
+ versão candidata do `notion-starter` passou a ser `0.3.0`, com build Hatchling,
374
+ wheel e sdist. Nenhum código de domínio foi alterado nesta etapa; o pacote segue
375
+ sendo a fonte compartilhada para o CLI e o app, sem depender de uma URL Git nos
376
+ consumidores.
377
+
378
+ **Validação:** `ruff check .` limpo, **355 testes verdes**, `twine check` aprovado
379
+ para wheel e sdist e import validado em ambiente limpo. A publicação efetiva no
380
+ PyPI não foi executada: nome final, ownership e metadados legais ainda precisam
381
+ de confirmação explícita.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Felipe Alcantara
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.