# Firebird Discovery Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Executar discovery reproduzível e estritamente read-only do banco Firebird disponível, documentando ambiente, schema, produto, tributação, imagens, incrementalidade, riscos, mapping e fingerprint. **Architecture:** Um coletor local terá uma camada de validação que aceita somente consultas `SELECT`/metadados e um adaptador de conexão que não expõe operações de escrita. O relatório será gerado a partir de resultados do MCP Firebird e, quando possível, do coletor local; nenhuma tabela auxiliar, migration ou alteração será feita no banco. **Tech Stack:** Python 3, driver Firebird disponível no ambiente somente se já instalado, `pytest`, SQL de `RDB$`/`INFORMATION_SCHEMA` compatível com Firebird. ## Global Constraints - O banco deve ser tratado como read-only. - São proibidos `INSERT`, `UPDATE`, `DELETE`, `ALTER`, `CREATE`, `DROP`, `TRUNCATE`, migrations, triggers, procedures, alteração de índices/constraints, criação de usuário e modificação de permissões. - Não executar testes destrutivos contra o banco real. - Não armazenar senhas ou secrets em arquivos, logs ou documentação. - Não iniciar API fiscal, API de imagens, serviço Windows, atualização de produtos ou scheduler de produção. - Achados devem ser classificados como `CONFIRMADO`, `INFERIDO` ou `NÃO CONFIRMADO`. --- ### Task 1: Preparar base e contrato read-only **Files:** - Create: `src/gti_discovery/__init__.py` - Create: `src/gti_discovery/read_only.py` - Create: `tests/test_read_only.py` - Create: `pyproject.toml` - Create: `.env.example` - Create: `CLAUDE.md` **Interfaces:** - Produces `validate_read_only(sql: str) -> str`, que retorna SQL normalizado ou lança `ValueError`. - Produces `ReadOnlyConnection`, com método `execute(sql: str, params: tuple = ())` e sem métodos de escrita. - [ ] **Step 1: Write the failing tests** ```python import pytest from gti_discovery.read_only import validate_read_only def test_accepts_select_and_with_queries(): assert validate_read_only(" SELECT * FROM RDB$DATABASE ") == "SELECT * FROM RDB$DATABASE" assert validate_read_only("WITH x AS (SELECT 1 AS n FROM RDB$DATABASE) SELECT n FROM x") @pytest.mark.parametrize("sql", ["INSERT INTO X VALUES (1)", "UPDATE X SET A=1", "DELETE FROM X", "CREATE TABLE X(A INT)", "ALTER TABLE X ADD A INT", "DROP TABLE X", "TRUNCATE TABLE X", "EXECUTE PROCEDURE P"]) def test_rejects_write_or_side_effect_sql(sql): with pytest.raises(ValueError): validate_read_only(sql) ``` - [ ] **Step 2: Run test to verify it fails** Run: `python -m pytest tests/test_read_only.py -q` Expected: FAIL because the package and validator do not exist. - [ ] **Step 3: Implement the minimal guard** Normalize whitespace, remove one trailing semicolon, reject multiple statements, comments and any first keyword other than `SELECT` or `WITH`. Reject side-effect keywords anywhere in the statement, including procedure execution. `ReadOnlyConnection.execute` must call the validator before delegating to the driver connection. - [ ] **Step 4: Run test to verify it passes** Run: `python -m pytest tests/test_read_only.py -q` Expected: PASS. - [ ] **Step 5: Commit** ```bash git add src tests pyproject.toml .env.example CLAUDE.md git commit -m "feat: add read-only discovery foundation" ``` If Git is still unavailable, record the exact blocker and do not initialize or commit without explicit authorization. ### Task 2: Coletar metadados e fingerprint **Files:** - Create: `src/gti_discovery/collector.py` - Create: `src/gti_discovery/fingerprint.py` - Create: `tests/test_fingerprint.py` **Interfaces:** - `collect_metadata(connection) -> dict` executes only predefined read-only queries. - `build_fingerprint(metadata: dict) -> str` returns a stable SHA-256 digest over sorted relevant metadata. - [ ] **Step 1: Write tests for stable fingerprints and changes** ```python from gti_discovery.fingerprint import build_fingerprint def test_fingerprint_is_order_independent(): a = {"tables": [{"name": "A", "columns": ["ID", "NAME"]}]} b = {"tables": [{"columns": ["ID", "NAME"], "name": "A"}]} assert build_fingerprint(a) == build_fingerprint(b) def test_relevant_schema_change_changes_fingerprint(): a = {"tables": [{"name": "A", "columns": ["ID"]}]} b = {"tables": [{"name": "A", "columns": ["ID", "NCM"]}]} assert build_fingerprint(a) != build_fingerprint(b) ``` - [ ] **Step 2: Run tests and verify failure** Run: `python -m pytest tests/test_fingerprint.py -q` Expected: FAIL because fingerprint functions do not exist. - [ ] **Step 3: Implement collection** Use Firebird system metadata for database info, relations, fields, keys, indexes, foreign keys, triggers, procedures/packages/events where exposed. Include table counts and approximate row counts only when safely available. Redact values and credentials from serialized output. Query a minimal masked sample only for candidate product tables. - [ ] **Step 4: Implement canonical fingerprinting** Canonicalize JSON recursively with sorted object keys and deterministic list ordering for tables, columns, keys, indexes, relations and relevant engine/type metadata; hash UTF-8 canonical JSON with SHA-256. Expose classification helper returning `SCHEMA_SUPPORTED`, `SCHEMA_CHANGED` or `SCHEMA_UNKNOWN` based on an allowlisted expected fingerprint and missing/ambiguous product mapping. - [ ] **Step 5: Run tests** Run: `python -m pytest tests -q` Expected: PASS. - [ ] **Step 6: Commit** ```bash git add src/gti_discovery/collector.py src/gti_discovery/fingerprint.py tests git commit -m "feat: collect Firebird metadata and fingerprints" ``` ### Task 3: Execute discovery against the available database **Files:** - Create: `artifacts/` output files only if explicitly safe and non-sensitive - Modify: `docs/DATABASE_DISCOVERY.md` - Modify: `docs/SCHEMA_MAPPING.md` **Interfaces:** - Uses the Firebird MCP read-only tools and collector contracts from Tasks 1-2. - [ ] **Step 1: Capture environment metadata** Collect Firebird version, database identity without secrets, charset/collation, timezone when detectable, approximate size, relation count, engines, and relevant configuration. - [ ] **Step 2: Enumerate schema relationships** Inspect all candidate tables/columns for product, stock, barcode, fiscal, company/branch, image/file/attachment and audit concepts; document PKs, FKs, indexes, nullability, defaults and apparent relationships. - [ ] **Step 3: Confirm product and fiscal semantics with minimal samples** Select only masked, non-sensitive columns and a very small sample. Never select credentials, customer data, free-form secrets or full BLOB values. Mark names without direct evidence as `INFERIDO` or `NÃO CONFIRMADO`. - [ ] **Step 4: Inspect operational objects** List and describe relevant triggers, procedures, packages and events. Check replication/cache indicators only where readable; explicitly document unavailable evidence. - [ ] **Step 5: Write the two discovery documents** Record actual findings and uncertainties, including no-write evidence, images alternatives A/B/C, incremental processing options, risks and future decisions. Do not propose fiscal automation yet. - [ ] **Step 6: Run offline safety tests and review outputs** Run: `python -m pytest tests -q` Expected: PASS; manually verify no password, connection string secret, raw BLOB or unnecessary personal data appears in docs/artifacts. ### Task 4: Update project state and final verification **Files:** - Create: `docs/AI_PROJECT_STATE.md` - Modify: `CLAUDE.md` if needed - [ ] **Step 1: Record current state** State that this workspace contains the Firebird discovery target, that no ERP writes were performed, which artifacts exist, and what remains blocked or undecided. Keep it current rather than diary-like. - [ ] **Step 2: Verify prohibited SQL is absent from executable discovery paths** Run: `rg -n --glob '!docs/superpowers/plans/**' '\\b(INSERT|UPDATE|DELETE|ALTER|CREATE|DROP|TRUNCATE)\\b' src tests` Expected: only intentional rejection tests/guard literals, with no executable write path. - [ ] **Step 3: Run final tests and inspect repository status** Run: `python -m pytest tests -q`; then `git status --short --branch`, `git diff --check`, and `git log -5 --oneline` when Git is available. - [ ] **Step 4: Create one final commit** Stage only mission files and create one clear final commit if the repository exists. If no Git repository exists, report `commit: não criado, diretório sem .git` and do not claim a clean Git status.