GitHub-Odoo Sync
1. What? — Definition and context
Section titled “1. What? — Definition and context”The GitHub-Odoo Sync workflow turns every GitHub event (issue, PR, commit) into a matching Odoo operation: project task created, label applied, milestone synced, commit historised. Since the #274 refactoring, the architecture moved from a 138-node monolithic workflow to a 13-node hub that dispatches to 7 specialised sub-workflows.
Hub-spoke architecture
Section titled “Hub-spoke architecture”| Workflow | ID | Nodes | Role |
|---|---|---|---|
| GitHub Project Sync (parent) | JR8ESduxKNSQ7iVs | 13 | Trigger + mapping lookup + dispatch by event type |
| SW-1 Issue Lifecycle | 8yU07chLmZuogA3j | 23 | opened / closed / reopened + sub-issues parent |
| SW-2 Issue Labels | OMeuYvh0B9XA2S1H | 11 | labeled / unlabeled |
| SW-3 Issue Properties | 0BoSdwR4d8S9Fq0C | 32 | assigned / edited / pinned / renamed / deleted |
| SW-4 Issue Milestone | EZiZqgZNpxEzof7F | 13 | milestoned / demilestoned |
| SW-5 PR Handler | 3o7IzU2brzJeedB7 | 18 | pull_request opened / merged / closed |
| SW-6 Git Events | O2jirkOJOpOIy5ez | 18 | push (commits) + create (branch stage transition) |
| SW-7 Repo Entity | WVWY01qmw9uYzSl8 | 28 | Label + Milestone CRUD at the repo level |
Shared dependency
Section titled “Shared dependency”A utility sub-workflow odoo-get-or-create-tag (j7e2EzEtIJ42T2S4) is called by SW-1, SW-2, and SW-7 to create or fetch Odoo tags matching GitHub labels.
Architecture diagram
Section titled “Architecture diagram”Supported events
Section titled “Supported events”Every GitHub event useful to a project workflow is synchronised:
| Category | Events | Sub-workflow |
|---|---|---|
| Issue lifecycle | opened, closed, reopened | SW-1 |
| Sub-issues | parent_added, parent_removed | SW-1 |
| Issue labels | labeled, unlabeled | SW-2 |
| Issue properties | assigned, edited, pinned, renamed, deleted | SW-3 |
| Issue milestone | milestoned, demilestoned | SW-4 |
| Pull requests | opened, closed, merged | SW-5 |
| Push & branches | push (commits), create (branch) | SW-6 |
| Repo-level | label/milestone CRUD | SW-7 |
2. Why? — Stakes and motivations
Section titled “2. Why? — Stakes and motivations”The problem without sync
Section titled “The problem without sync”| Without sync | Consequence |
|---|---|
| Business visibility | Issues stay invisible from Odoo |
| Time tracking | Impossible to tie a timesheet hour to a specific issue |
| Commit history | Scattered across GitHub, no aggregated view per Odoo task |
| Double entry | Manual Odoo task creation for every issue |
Why hub-spoke rather than a single workflow?
Section titled “Why hub-spoke rather than a single workflow?”The original workflow (138 nodes) had grown along with each new event handled. Three symptoms triggered the #274 refactoring:
| Symptom | Cause | Consequence |
|---|---|---|
| Slow editing | N8N UI struggling with 138 nodes | Ergonomically expensive edits |
| Tedious tests | Touching one sub-flow → re-test the whole | Silent regressions |
| Coupling | AI tag, Odoo task, milestone code shared | Local change broke distant branches |
The hub architecture with 7 sub-workflows resolves those tensions: each SW has a narrow scope, can be edited and tested in isolation, and signs its I/O contract via Execute Workflow Trigger.
Why GitHub → Odoo and not the other way?
Section titled “Why GitHub → Odoo and not the other way?”| Criterion | Choice |
|---|---|
| Source of truth for code | GitHub (where commits, PRs, CI live) |
| Source of truth for business | Odoo (timesheets, invoices, customer projects) |
| Trigger direction | GitHub webhooks (real-time, sub-second) |
| Enrichment | Odoo adds business context (project, tags, complexity, estimate) |
The reverse direction (closing an issue from Odoo) is technically feasible but introduces a loop risk. For now, GitHub stays the source; Odoo is sink + dashboard.
3. How? — Technical implementation
Section titled “3. How? — Technical implementation”An event’s journey
Section titled “An event’s journey”1. GitHub webhook — The N8N trigger receives the event with its X-Hub-Signature-256 HMAC signature (verified by N8N).
2. Mapping lookup — The hub queries the github_project_mapping Data Table to find the Odoo project tied to the repo (owner/repo → odoo_project_id).
3. Switch event type — The hub routes to the appropriate sub-workflow based on headers.x-github-event and body.action.
4. Sub-workflow execution — Each SW receives {body, headers, query, mapping} in passthrough and applies its business logic.
5. Odoo XML-RPC calls — All SWs converge on the same Odoo endpoint: http://odoo:8069/xmlrpc/2/object, database guig_db.
Odoo project_github_sync module
Section titled “Odoo project_github_sync module”The custom addon project_github_sync (v18.0.5.6.0) adds the fields needed by the sync. Every custom field uses the x_ prefix to avoid collision with Odoo natives:
Fields on project.task
Section titled “Fields on project.task”| Family | Field | Type | Source |
|---|---|---|---|
| GitHub | x_github_issue_id | Integer (indexed) | issue/PR number |
| GitHub | x_github_url | Char(500) | GitHub URL |
| GitHub | x_github_repo | Char (indexed) | owner/repo |
| GitHub | x_github_milestone_id | Integer | milestone number |
| GitHub | x_github_parent_issue_id | Integer | parent (sub-issues) |
| GitHub | x_github_commit_ids | One2many | linked commits |
| Effort (#188) | x_estimated_hours | Float(10,2) | estimate at creation |
| Effort (#188) | x_complexity | Selection | trivial / simple / moderate / complex |
| AI triage (#296) | x_ai_project_triaged_at | Datetime | last project analysis (TTL 7d) |
| AI triage (#296) | x_ai_personal_triaged_at | Datetime | last personal analysis (TTL 7d) |
| Telemetry | x_claude_time_total | Float(10,2) | active Claude Code time |
| Telemetry | x_claude_cost_total | Float(10,4) | API cost USD |
| Telemetry | x_claude_token_total | Integer | cumulative tokens |
| Telemetry | x_claude_sessions | Integer | session count |
| Telemetry | x_claude_lines_added/removed | Integer | LOC delta |
| Telemetry | x_claude_session_ids | One2many | session history |
| Telemetry | x_claude_category | Selection | auto category for generic tasks |
Added models
Section titled “Added models”| Model | Role |
|---|---|
project.task.github.commit | One record per commit linked to a task (sha, message, author, date) |
project.task.claude.session | One record per Claude Code session (id, duration, cost, tldr, category) |
Issue Lifecycle (SW-1) detailed
Section titled “Issue Lifecycle (SW-1) detailed”The busiest sub-workflow is SW-1 Issue Lifecycle, which handles 5 distinct actions:
Effort estimation at creation (#188)
Section titled “Effort estimation at creation (#188)”When a new issue is created, SW-1 derives two fields automatically from the title + body:
| Field | Calculation |
|---|---|
x_complexity | Heuristic on body length, keywords (refactor, migration, multi-step) |
x_estimated_hours | Deterministic model based on the complexity cohort (see Effort Estimator V1) |
The initial estimate is a lower bound used as a reference. The effective_hours field (aggregated timesheets) then measures the gap between estimate and reality, feeding a weekly coverage report.
Daily GitHub Triage + AI double-triage
Section titled “Daily GitHub Triage + AI double-triage”In parallel with event-driven sync, a daily scheduled workflow (61uBaaFQssxT1HJ0, 25 nodes) performs catch-up and AI triage:
| Step | Action |
|---|---|
| 1. Catch-up sync | Fetches GitHub issues modified in the last 24h, updates Odoo |
| 2. Stage promotion | If an issue is in progress on GitHub but Backlog on Odoo → promoted to In Progress |
| 3. AI Issue Triage (#296) | Sub-workflow AwqOyyAC3sUg9tSm (12n) — codex-yolo analyses the issue and proposes project stage + personal horizon |
| 4. TTL 7d cache | The x_ai_*_triaged_at fields prevent re-triaging an issue analysed recently |
The AI triage is also exposed in interactive mode via the triage-interactive-api consumed by Claude Code during an open session (/triage command).
Passed data format
Section titled “Passed data format”The hub passes a uniform object to every sub-workflow:
{ "body": { /* GitHub webhook payload */ }, "headers": { "x-github-event": "issues", "x-github-delivery": "..." }, "query": {}, "mapping": { "github_repo": "owner/repo", "odoo_project_id": 5, "default_stage_id": 21, "sync_enabled": true }}The input contract is strict (Execute Workflow Trigger in passthrough mode disabled for SW-3 to SW-7, which use typed workflowInputs).
Initial Sync (#50)
Section titled “Initial Sync (#50)”To onboard an existing repo with hundreds of issues, the Initial Sync workflow (#50) allows a one-shot import: GitHub API pagination + sequential Odoo task creation + tag setup. Use it only at setup time; afterwards the event-driven sync is sufficient.
4. What if? — Outlook and limits
Section titled “4. What if? — Outlook and limits”Current limits
Section titled “Current limits”| Limit | Impact | Mitigation |
|---|---|---|
| Unidirectional | Closing on Odoo does not close the GitHub issue | Reverse sync to design with anti-loop flag |
| Manual mapping | Each repo must be added to the Data Table | Auto-discovery per organisation later |
| Initial estimate = lower bound | Complex issues underestimated | Weekly recalibration planned via cohort matching |
| AI triage 24h max lag | Re-triage the next day if issue ignored | Acceptable, triage is an assistant not an oracle |
Evolution scenarios
Section titled “Evolution scenarios”If bidirectional sync is needed:
automation.actionOdoo webhook when a task moves to stageDone- The webhook posts a GitHub comment
Closed via Odoo task #N synced_from_odooflag in the mapping table to block back-propagation
If many repos to manage:
- Auto-discovery of repos from a GitHub organisation
- Mapping templates by name convention (
*-backend→ Backend project) - Dedicated Odoo UI to configure mappings without touching the Data Table
If AI triage becomes critical:
- Enable a high-confidence mode (auto-set the stage if
confidence > 0.9) - Log decisions in
claude_code_active_sessionsfor audit - Allow a manual override via
/triage <issue> <stage>
Related pages
Section titled “Related pages”Infrastructure
Section titled “Infrastructure”- Odoo 18 on Docker —
project_github_syncmodule, x_* fields - Why Odoo — Choices and architecture
- N8N Queue Mode — Backend running the sub-workflows
Workflows
Section titled “Workflows”- Claude Code Telemetry — Feeds the x_claude_* fields
- Monitoring Digests — Effort Estimator V1, Daily Todo Digest, Daily GitHub Triage
- Error Handler — Captures the 7 sub-workflow errors
Reference
Section titled “Reference”- Glossary — XML-RPC, Webhook, HMAC, Sub-workflow