From 086dfed9928d197173435162dd24b1eb8e78f055 Mon Sep 17 00:00:00 2001 From: NuklearRabbit <145918611+NuklearRabbit@users.noreply.github.com> Date: Wed, 5 Aug 2026 22:05:13 +0200 Subject: [PATCH] fix: publish RAGcore Procedure Sync and derive its evidence for real MCP_HUB_BASE_URL had the same wrong-hostname bug as RAGCORE_BASE_URL earlier this session (itworx-mcp-hub:8000 doesn't resolve; the real container is reachable at the host's own 192.168.10.150:1100) -- fixed live, resolving the Automation page showing "Operationeel" and "Hub Onbereikbaar" simultaneously. Went on to actually publish the "Fleet Ops -- RAGcore Procedure Sync" n8n workflow now that RAGcore is reachable: its own RAGcore Sync Token credential had gone stale from the same rotation as the earlier one, so minted a fresh, dedicated, minimally-scoped (sources:sync only) credential, verified a real manual run (33 synced, 0 failed, result registered) before publishing. That exposed a real, now-stale bug: derive_n8n_status() hardcoded this workflow's evidence to None with a comment explaining it was unpublished -- true when written, false now. The workflow's own result-report callback already writes a real n8n_procedures_synced audit event; wired that in as its evidence source, the same pattern the scheduled scan and error handler already use, instead of a value that could never update itself once the workflow went live. Co-Authored-By: Claude Sonnet 5 --- backend/app/services/integration_status.py | 15 ++++++++++++--- backend/tests/test_integration_status.py | 5 +++-- backend/tests/test_integrations.py | 7 +++++++ n8n/workflows/MANIFEST.md | 15 ++++++++++----- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/backend/app/services/integration_status.py b/backend/app/services/integration_status.py index 4566e4b..e778751 100644 --- a/backend/app/services/integration_status.py +++ b/backend/app/services/integration_status.py @@ -19,8 +19,7 @@ from app.schemas import ( settings = get_settings() # The 4 canonical Fleet Ops n8n workflows (see n8n/workflows/MANIFEST.md). All 4 are -# built (workflow 3, RAGcore Procedure Sync, has all 6 nodes saved); workflow 3 is not -# yet published/active, so it will show no *run* evidence until it is. +# built (all with their full node set saved). _CANONICAL_WORKFLOWS = ( "Fleet Ops — Vehicle Return Orchestration", "Fleet Ops — Scheduled Data Quality Scan", @@ -98,6 +97,16 @@ def derive_n8n_status(db: Session) -> N8nIntegrationStatus: ) ) + # RAGcore Procedure Sync evidence: result reports posted by the workflow itself once + # it finishes uploading procedures to RAGcore (app/api/routers/integrations.py:: + # procedures_sync_result), the same "the workflow's own callback is the evidence" + # pattern the scheduled scan and error handler already use below. + latest_procedure_sync_at = db.scalar( + select(func.max(AuditEvent.occurred_at)).where( + AuditEvent.action == "n8n_procedures_synced" + ) + ) + # Error handler evidence: registrations posted by the "Fleet Ops — Workflow Error # Handler" n8n workflow itself, which also doubles as proof that workflow is wired # up and firing correctly. @@ -123,7 +132,7 @@ def derive_n8n_status(db: Session) -> N8nIntegrationStatus: evidence_by_workflow = { "Fleet Ops — Vehicle Return Orchestration": latest_success_at, "Fleet Ops — Scheduled Data Quality Scan": latest_scan_at, - "Fleet Ops — RAGcore Procedure Sync": None, + "Fleet Ops — RAGcore Procedure Sync": latest_procedure_sync_at, "Fleet Ops — Workflow Error Handler": latest_handler_failure_at, } workflows = [ diff --git a/backend/tests/test_integration_status.py b/backend/tests/test_integration_status.py index 058effa..859ea94 100644 --- a/backend/tests/test_integration_status.py +++ b/backend/tests/test_integration_status.py @@ -119,8 +119,9 @@ def test_integration_status_lists_all_four_canonical_workflows(ops_client): "Fleet Ops — Workflow Error Handler", } ragcore_sync = next(w for w in body["workflows"] if "RAGcore" in w["name"]) - # All 4 canonical workflows are built (RAGcore Procedure Sync has all 6 nodes saved - # live); it has no *run* evidence yet since it is deliberately left unpublished. + # All 4 canonical workflows are built (all 6 nodes saved live). This fresh test run + # has reported no real sync result yet, so -- like the other three workflows before + # their own first real signal -- there is no run evidence yet either. assert ragcore_sync["built"] is True assert ragcore_sync["last_seen_at"] is None diff --git a/backend/tests/test_integrations.py b/backend/tests/test_integrations.py index 0ced5ae..cdd4189 100644 --- a/backend/tests/test_integrations.py +++ b/backend/tests/test_integrations.py @@ -257,3 +257,10 @@ def test_procedures_sync_result_registers_and_is_idempotent(client, ops_client): matching = [e for e in audit_events if e["metadata"]["execution_id"] == execution_id] assert len(matching) == 1 assert matching[0]["after"] == {"synced": 33, "failed": 1} + + # The workflow's own callback is its evidence -- same pattern the scheduled scan and + # error handler already use -- so this real report must now show up as run evidence + # in the integration status, not stay hardcoded to "no evidence yet". + status = ops_client.get("/api/v1/integrations/status").json()["n8n"] + ragcore_sync = next(w for w in status["workflows"] if "RAGcore" in w["name"]) + assert ragcore_sync["last_seen_at"] is not None diff --git a/n8n/workflows/MANIFEST.md b/n8n/workflows/MANIFEST.md index 500e076..d558606 100644 --- a/n8n/workflows/MANIFEST.md +++ b/n8n/workflows/MANIFEST.md @@ -38,10 +38,15 @@ credential values are never embedded; nodes reference named n8n credentials inst ## 3. Fleet Ops — RAGcore Procedure Sync Fully built and saved live (6 real nodes: Schedule Trigger → List procedures → Prepare -uploads → Upload to RAGcore → Summarize sync result → Report sync result to Fleet Ops), -but **deliberately not published/active** — the Schedule Trigger runs daily at midnight, -so activating it starts real, unattended runs against production RAGcore and Fleet Ops; -that is a separate, explicit go-live decision, not something to flip silently. +uploads → Upload to RAGcore → Summarize sync result → Report sync result to Fleet Ops). + +**Published/active as of 2026-08-05**, once RAGcore itself went live (see +`PROJECT_STATE.md`'s "RAGcore actually went live" entry): the workflow's `RAGcore Sync +Token` credential had also gone stale from the same credential rotation, so a fresh, +dedicated, minimally-scoped (`sources:sync` only) credential was minted before +publishing. Verified with a real manual execution first — 33 procedures synced, 0 +failed, Fleet Ops registered the result (`execution_id` 159) — before flipping it to +run unattended on its daily schedule. While validating this workflow (2026-08-05), found and fixed a real defect: the "Report sync result to Fleet Ops" node's three body-parameter expressions each had a stray @@ -62,7 +67,7 @@ the same way via the same CLI import path, re-verified. | Event contract | N/A — HTTP-triggered sync. Reads `GET /api/v1/integrations/n8n/procedures`, uploads via RAGcore's `POST /v1/uploads`, reports via `POST /api/v1/integrations/n8n/procedures-sync-result`. | | Required credentials | `Fleet Ops Service Token` (Header Auth, on the procedures-list and result-report calls); `RAGcore Sync Token` (Header Auth, `sources:sync` scope, on the upload call) | | Live workflow ID | `6wbkc4d1AouGpmWT` | -| Active status (as of 2026-08-05) | Built / Saved / **Inactive** (not published — daily unattended runs require a separate explicit go-live decision) | +| Active status (as of 2026-08-05) | **Active / Published** | | Error Workflow | `Fleet Ops — Workflow Error Handler` (wired) | | Checksum (sha256) | `643c0515a50fed3d35e28f0b8cb17841f6f8d91699944980b5c56e619d5bf2a6` |