Skip to content

Instantly share code, notes, and snippets.

@litnimax
Created August 4, 2026 21:07
Show Gist options
  • Select an option

  • Save litnimax/c7e479d1afeff7185158a9dc13716232 to your computer and use it in GitHub Desktop.

Select an option

Save litnimax/c7e479d1afeff7185158a9dc13716232 to your computer and use it in GitHub Desktop.
odoo2angee-engine system overview (2026-08-04)

odoo2angee-engine — System Overview

Version: 0.1.0.dev0
Date: 2026-08-04
Python: ≥ 3.14
Repository: https://github.com/litnimax/odoo2angee-engine
Status: first vertical slices implemented; M13 engine-side complete, Paseo v3 UI pending

This document describes the current system as implemented: ownership, architecture, data flow, commands, artifacts, trust boundaries, and how universal vs pilot-specific the pieces are. It is a portable overview for humans and agents; detailed contracts live in the linked docs and schemas.


1. Mission

odoo2angee-engine is a migration-evidence engine for planning Odoo → Angee migrations.

It:

  • scans local Odoo addons without starting Odoo, importing addon Python, accessing a database, or writing into Odoo/Angee source trees;
  • preserves source facts as an immutable intermediate representation (IR);
  • prepares deterministic work items for external agents (Paseo);
  • validates untrusted agent submissions and publishes normalized reports;
  • optionally captures a bounded runtime oracle for sale.order draft behavior;
  • generates one Angee model from an explicit accepted decision.

It does not:

  • own IDE UI, Git, worktrees, terminals, or agent execution (Paseo owns those);
  • run an LLM inside the engine;
  • turn Angee into a migration control plane;
  • silently drop unsupported source constructs;
  • widen generation beyond the one-model primitive-field slice.

2. Ownership boundaries

Concern Owner
UI, agents, Git, worktrees, terminals, files, diff Paseo
Odoo inventory, IR, migration units, decisions, validation, reports odoo2angee-engine
Odoo source semantics Odoo sources + engine static parsers
Target framework/runtime Angee (read as capability catalog; never a control plane)
Target application examples arpee-angee (reference only)
Runtime characterization of sale draft Engine capture-sale-oracle only

Rule: reference repositories (Odoo, Angee, arpee, Paseo) are read-only. Portable artifacts never embed absolute host paths, timestamps, mtimes, or random IDs.


3. Architecture

Dependencies point inward:

CLI → application use cases → domain IR
                ↑                 ↑
         source adapters    validation / storage / reports

Package layers

Package Role
odoo2angee.cli command dispatch, human/JSON/events presentation
odoo2angee.doctor read-only environment checks
odoo2angee.inventory filesystem, manifest, Python, XML, CSV, JS adapters
odoo2angee.ir immutable Odoo IR + accepted Angee target IR
odoo2angee.analysis work-item prep, untrusted ingest, migration units
odoo2angee.target_capabilities pinned Angee capability catalog loader
odoo2angee.oracle explicit Docker sale characterization
odoo2angee.generation one-model validation, render, ownership, drift
odoo2angee.project_summary read-only target state summary
odoo2angee.events / progress / cancellation Paseo subprocess contract
odoo2angee.validation / storage invariants, canonical JSON, atomic writes

Domain code must not import Odoo, Django, Angee, Paseo, Git, or terminal UI.


4. End-to-end data flow

Odoo source files
  → static inventory adapters (ast / safe XML / CSV / JS inventory)
  → immutable Odoo IR
  → structural validation
  → 13 canonical inventory JSON + shared publication marker

immutable Odoo IR / published inventory
  → addon-analysis work item v2
  → external Paseo agent (structured output)
  → untrusted submission validation
  → normalized analysis + findings reports

published inventory (marker-complete)
  → model-behavior migration unit (roots → same-addon closure)
  → migration-analysis work item v3
      (+ target capability catalog
       + optional sale oracle digest
       + clean Angee Git HEAD)
  → external Paseo agent
  → untrusted ingest
  → analysis-only report (no decision, no code gen)

explicit accepted decision (.odoo2angee/decisions/)
  → Angee one-model target IR
  → deterministic owned target files + generation report

capture-sale-oracle (only Docker path)
  → clean revision-pinned Odoo worktree
  → packaged fixture × 2 fresh DBs
  → byte-identical canonical observations
  → .odoo2angee/oracles/odoo.sale.order-draft.v1.json

5. Non-negotiable scanner rules

  1. Never import or execute an Odoo addon, its manifest, or its Python modules.
  2. Parse manifests and Python with ast; parse XML with external entity/network access disabled.
  3. Never write below the scanned addon directory.
  4. Preserve unsupported or dynamic constructs as diagnostics/findings — never silently drop source facts.
  5. Canonical artifacts are deterministic and path-portable.
  6. Keep source facts separate from target mappings and user decisions.
  7. odoo2angee.ir.odoo is source-only: no Angee/Django types, no target choices.

6. CLI commands

All commands support human output (default), --json, or --events-jsonl --invocation-id <id>.

Command Purpose Writes Docker
doctor check Python/Node/Git, Odoo/Angee roots, isolation no no
scan-addon <addon> static inventory → 13 JSON target inventory no
project-summary summarize published artifacts no no
prepare-addon-analysis <addon> publish model-only v2 work item work-items no
ingest-addon-analysis <addon> <file> validate draft → analysis reports reports no
prepare-migration-analysis sale publish v3 migration work item migration-analysis no
ingest-migration-analysis sale <file> validate draft → v3 report migration-analysis no
capture-sale-oracle runtime sale draft oracle (2×) oracles yes
generate-angee-model <decision.json> one-model Angee generation target addon no

--check on ingest/generate compares expected bytes without writing (where supported).

Typical pilot sequence

uv run odoo2angee doctor
uv run odoo2angee scan-addon sale
uv run odoo2angee capture-sale-oracle --json          # optional, once per revision
uv run odoo2angee prepare-migration-analysis sale --json
# Paseo agent → submission JSON
uv run odoo2angee ingest-migration-analysis sale agent-output.json --json
# later, after human-accepted decision:
uv run odoo2angee generate-angee-model path/to/decision.json

7. Artifacts (under angee.target/.odoo2angee/)

7.1 Inventory (13 files)

inventory/<addon>/
  addon.json
  models.json
  fields.json
  inheritance.json
  security.json
  views.json
  actions.json
  menus.json
  data.json
  cron.json
  controllers.json
  frontend.json
  findings.json
  • schema_version = 1
  • addon-relative paths only
  • shared SHA-256 publication marker over marker-neutral payloads (required by M13 snapshot loader; optional in legacy inventory schema)
  • identical content is not rewritten on rescan

7.2 Addon analysis v1/v2

work-items/<addon>/...
submissions/<addon>/*.json          # Paseo-written, untrusted
reports/<addon>/analysis.json
reports/<addon>/findings.json

v2 work item is self-contained: 13 inventory paths, trusted SourceRef catalog, per-model analysis identity, instructions, exact draft-07 output schema. Engine owns canonical IDs; agent uses submission-local keys only.

7.3 Migration analysis v3 (separate namespace)

migration-analysis/
  work-items/<addon>/...
  submissions/<addon>/*.json
  reports/<addon>/...

Report is explicitly:

  • accepted_decision: false
  • implementation_authorized: false
  • code_generation: false

project-summary exposes migration_analyses with states: prepared | published | stale | incomplete | invalid.

7.4 Oracle

oracles/odoo.sale.order-draft.v1.json

7.5 Decisions / generation

decisions/<addon>/*.json            # human-accepted model slice
# generated files live in the target Angee addon with ownership marker

8. Odoo IR v1

Public aggregate: odoo2angee.ir.OdooAddon (from scan_addon(...).ir).

Frozen entities include:

OdooModel, OdooField, OdooInheritance, OdooConstraint, OdooSecurityGroup, OdooAccessRule, OdooRecordRule, OdooView, OdooAction, OdooMenu, OdooDataRecord, OdooScheduledAction, OdooController, OdooFrontendAsset.

Mapping boundary: IR holds only Odoo evidence. Future mappings consume it as immutable input; they must not reach back into parser-specific dicts.


9. Migration unit foundation

Policy: odoo.model-behavior-closure.v1
Schema: schemas/odoo-migration-unit-v1.schema.json

Generic API

build_model_behavior_migration_unit(snapshot, roots)
build_sale_order_migration_unit(snapshot)  # roots = sale.order + sale.order.line

What the closure includes

  • all same-addon fragments for selected root models
  • their fields and constraints
  • framework callbacks: depends, depends_context, onchange, constrains, ondelete, autovacuum
  • lifecycle methods: create, write, unlink
  • recursively reachable direct self.method() calls
  • method semantics (extractor revision pinned)

What it deliberately does not do

  • follow relations (relation_expansion: false)
  • scan dependency-addon closure
  • include views/security/menus as unit members
  • authorize writes or target choices

Related-recordset calls, super, env/context/privilege/persistence, inheritance parents outside selection, and relation targets become typed external requirements or obligations.

Unit identity binds: full artifact-set digests, semantics schema/extractor revision, selected semantics digest, policy, and unit content.


10. Agent handoffs (trust boundary)

The engine never runs an agent. Pattern:

  1. Prepare — trusted context → work item + exact output schema
  2. Paseo — structured generation; agent is read-only; one JSON draft
  3. Ingest — rebuild evidence, fail-closed membership checks, own IDs, atomic publish

v2 — addon / model-only analysis

  • every current model → ready_for_design | blocked | deferred
  • rejects decisions, mappings, extra top-level fields
  • v1 submissions still accepted for compatibility

v3 — migration-unit analysis (sale pilot surface)

  • binds complete inventory publication, canonical unit, packaged capability catalog + clean Angee HEAD, optional oracle digest or explicit absence
  • agent classifies every obligation, external requirement, and available oracle stage as:
    • supported
    • partially_supported
    • capability_missing
    • manual_design_required
  • work-item schema $id embeds context digest (no schema substitution)
  • ingest returns report_sha256 (content digest) separately from semantic report_id

Filesystem hardening (hostile namespace)

Configured target and host-owned ancestors are trusted configuration. All .odoo2angee descendants are treated as hostile:

  • descriptor-relative traversal
  • O_NOFOLLOW / O_DIRECTORY
  • bounded reads, fstat on opened leaves
  • v3 single-file publisher: temp file + fsync + final evidence check + rename through directory descriptors

JSON size caps: 16 MiB (v1/v2 inputs), 32 MiB (v3).


11. Sale behavioral oracle

ID: odoo.sale.order-draft.v1
Kind: sale-behavior-observation
Command: capture-sale-oracle
Only runtime/Docker boundary in the product.

Why it exists

Static inventory knows structure and method AST facts. It cannot faithfully execute Odoo ORM, compute graphs, view-driven Form onchange ordering, or draft-vs-persist behavior. The oracle is executable evidence for the bounded sale.order / sale.order.line aggregate.

Scenario stages (fixed)

  1. draft.defaults
  2. draft.partner-selected
  3. draft.line-alpha-added / draft.line-beta-added
  4. draft.line-alpha-updated (qty/price/discount)
  5. draft.tax-removed / draft.tax-restored
  6. draft.line-beta-removed
  7. draft.before-save
  8. persisted.after-reload
  9. rollback.before / rollback.after

Observations use stable fixture keys (not DB ids). Money/qty are canonicalized decimals. Two runs on fresh DBs must be byte-identical.

Capture guarantees

  • requires oracle.source_revision (full 40-char Git SHA)
  • clean shared worktree for Odoo core + sale at that revision
  • read-only source mounts, disposable network/tmpfs DB
  • revalidate clean revision after each run and before commit
  • atomic publish; unchanged bytes not rewritten
  • cooperative cancellation; labeled Docker cleanup
  • mutable image tags are not future provenance (prefer digests)

project-summary lists published oracles without starting Docker. Analysis binds oracle by digest or records absence; source and oracle revisions are independent claims (same_source_revision may be unproven).

Known claim precision

Draft “no DB writes” is proven for sale.order / sale.order.line row counts, not absolute database non-mutation. Prefer that wording in consumers.


12. Target capability catalog

  • Packaged canonical JSON (catalogs/angee-target-capabilities-v1.json)
  • Schema: target-capability-catalog-v1.schema.json
  • Curated from a named Angee Git revision
  • Granular status, repository-relative evidence, constraints, notes
  • Loader is fail-closed; never imports Angee or probes runtime
  • Handwritten app workarounds do not promote a capability to supported

Example families: same-row derived values, cross-row dependency graphs, server draft/prefill calculation, invariant-preserving writes, editable relation drafts, dynamic form modifiers, company-scoped records.


13. One-model Angee generation

Input: accepted decision under .odoo2angee/decisions/ conforming to angee-model-slice-v1.schema.json.

Produces (bounded slice):

  • exactly one new Odoo-shaped Angee model
  • explicit primitive field subset
  • one admin-only permissions.zed
  • one GraphQL resource
  • one standard ResourceList form/list
  • ownership marker + generation report (omitted source fields named)

Does not:

  • convert Odoo record rules, XML views, related fields
  • close addon dependency graphs
  • infer target semantics from the scanner alone

Target project must already exist and compose the addon in settings.yaml. --check compares expected bytes without writing.

Pilot gates after generation (in the target project):

uv run manage.py angee build
uv run manage.py makemigrations sale
uv run manage.py makemigrations --check --dry-run sale
uv run manage.py schema
uv run manage.py schema --check
uv run manage.py test pilot.sale.tests

14. Paseo subprocess contract (summary)

Full text: docs/paseo-contract.md
Schema: schemas/paseo-events-v1.schema.json

  • Engine is a short-lived subprocess; no stdin
  • Paseo serializes writers to one angee.target
  • Output modes: human | --json | --events-jsonl --invocation-id
  • Event envelope v1: invocation.started → phases/progress/artifacts → exactly one terminal completed | failed | cancelled
  • SIGINT/SIGTERM → exit 130/143 with graceful cancel where checkpoints allow
  • stderr: human one-liners with stable prefixes; behavior from exit codes + stdout
  • Forward compatibility: ignore unknown fields/types/phases

15. Configuration

odoo2angee.yaml (project-local source of truth):

odoo:
  core: /path/to/odoo
  addons:
    - /path/to/addons

angee:
  framework: /path/to/angee-django
  target: /path/to/pilot-target

reference:
  arpee: /path/to/arpee-angee

runtime:
  workspaces: /path/to/workspaces

oracle:
  source_revision: <40-char-sha>
  # docker_binary: docker
  # odoo_image: odoo:19.0
  # postgres_image: postgres:17

Paseo should always pass --config /absolute/path.


16. Universality vs pilot scope

Honest readiness for “extract any model”:

Layer Generic? Notes
Scanner + inventory Yes any addon with __manifest__.py
Odoo IR Yes all inventoried entities
Method semantics Yes any method
Addon analysis v2 Yes model-only status per addon
Migration unit builder Yes build_model_behavior_migration_unit(snapshot, roots)
Migration unit policy Partial same-addon, no relations — intentional v1
Migration analysis v3 surface No CLI/schemas gated to sale + fixed roots
Oracle No one sale draft scenario
Capability catalog Yes (IDs) content driven by current Angee audit
Generation Partial renderer generic; slice = 1 model + primitives
Multi-addon dependency closure No deferred
Security / XML view auto-port No by design

Interpretation: the engine is a generic evidence platform with a sale.order aggregate pilot end-to-end. Source facts for any model are available now; full migration-analysis productization and runtime oracles for arbitrary models are not.

Natural next productization steps

  1. prepare-migration-analysis <addon> --roots m1,m2 (drop sale hardcode; oracle optional)
  2. Oracle scenario registry (sale draft = first registered scenario)
  3. Multi-addon / _inherit policy v2 (opt-in)
  4. Relation expansion policy (allowlisted / depth-limited)
  5. Paseo durable v3 agent/review workflow
  6. Generation scope expansion only after accepted evidence — not before

17. Implementation status (checklist)

Capability Status
Read-only addon inventory scanner implemented
Immutable Odoo IR implemented
Method semantics extractor implemented
Publication marker / snapshot loader implemented
Model-behavior migration unit foundation implemented
Pinned Angee capability catalog implemented
Paseo events/cancellation contract implemented
Addon analysis handoff v2 (+ v1 compat) implemented
Sale oracle capture implemented
Migration analysis handoff v3 (engine) implemented
Paseo agent/review UI for v3 pending
One-model Angee generation implemented
Multi-model / multi-addon generation not in scope yet
Implicit Docker anywhere else forbidden

18. Verification

uv run python -m unittest discover -s tests -v
uv run python -m compileall -q src tests
uv run ruff check src tests
uv run odoo2angee doctor
uv run odoo2angee scan-addon contacts
uv build

Tests must continue to prove: non-execution of addon code, path containment, deterministic output, diagnostic completeness, source non-mutation, and black-box Paseo contract behavior.


19. Document index (in-repo)

Path Topic
README.md user-facing commands and guarantees
AGENTS.md contributor rules and local path snapshot
docs/architecture.md layers, trust, data flow
docs/implementation-plan.md milestones and first vertical slice
docs/odoo-ir.md source IR contract
docs/paseo-contract.md subprocess/events/artifacts/exit codes
docs/m13-sale-behavior-evidence.md sale unit + oracle + capabilities
docs/system-overview.md this document
schemas/*.schema.json versioned portable contracts
catalogs/angee-target-capabilities-v1.json pinned target capabilities
skills/migrate-odoo-addon-analysis packaged agent skill

20. One-paragraph summary

odoo2angee-engine statically inventories Odoo addons into deterministic artifacts and an immutable IR; builds bounded source-behavior migration units; hands trusted, schema-exact analysis work to Paseo-owned agents; validates untrusted drafts without running LLMs; optionally characterizes sale.order draft runtime behavior via an explicit double-run Docker oracle; and generates only an explicit one-model Angee slice from human-accepted decisions—keeping source facts, target capabilities, agent prose, and code generation in strictly separated trust domains.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment