Consolidates this effort's outcome across all four canonical workflows: WF1/WF2 hardened and live, WF3 blocked on a RAGcore-side credential rejection (with trace IDs for the operator to investigate), WF4 built and live-validated with one open non-blocking follow-up. No credential values or secrets included.
208 lines
12 KiB
Markdown
208 lines
12 KiB
Markdown
# Live n8n + RAGcore integration — final evidence
|
|
|
|
No credential values, tokens, or secrets appear anywhere in this document. Where a
|
|
credential or trace ID is referenced, only its name or an opaque reference identifier is
|
|
given, never its value.
|
|
|
|
## Commit
|
|
|
|
Built on branch `feat/live-n8n-ragcore-integration`, HEAD at commit
|
|
`aaa16305354d34f9c1f4d57253d33d9062c38faa` ("docs: record WF2 retry fix and WF4's
|
|
n8n-session-expiry blocker"). Run `git log --oneline feat/live-n8n-ragcore-integration`
|
|
for the full history of this effort.
|
|
|
|
## Scope
|
|
|
|
The brief required treating n8n (`https://n8n.itworx.tech`) as a full third integration
|
|
layer alongside RAGcore and MCP Hub, with Fleet Ops keeping exclusive ownership of
|
|
business rules, authorization, transactions, audit, and idempotency. Four canonical n8n
|
|
workflows were required. The repository (`n8n/workflows/*.json` + `MANIFEST.md` +
|
|
`n8n/workflows/check_drift.py`) is the source of truth for cleaned workflow definitions;
|
|
the Fleet Ops integration status page (`/automation`) shows real per-workflow operational
|
|
evidence, not a config boolean.
|
|
|
|
## Result summary
|
|
|
|
| # | Workflow | Status | Live evidence this round |
|
|
|---|---|---|---|
|
|
| 1 | Fleet Ops — Vehicle Return Orchestration | **Live, hardened** | Timeout+bounded-retry gap found and fixed |
|
|
| 2 | Fleet Ops — Scheduled Data Quality Scan | **Live, hardened** | Same gap found and fixed |
|
|
| 3 | Fleet Ops — RAGcore Procedure Sync | **Blocked** | Not built — RAGcore rejects credential issuance (see below) |
|
|
| 4 | Fleet Ops — Workflow Error Handler | **Live, validated** | Mock + genuine induced-failure test; own hardening incomplete (see below) |
|
|
|
|
Full per-workflow detail (purpose, trigger, event contract, required credentials, live
|
|
workflow ID, checksum) is in `n8n/workflows/MANIFEST.md`, which is the authoritative,
|
|
continuously-updated source — this document is a point-in-time summary of that state
|
|
plus the reasoning behind what's not done.
|
|
|
|
## Workflow 1 — Vehicle Return Orchestration
|
|
|
|
Live workflow ID `mobilityops-return-processing`. Validated in an earlier round of this
|
|
effort: webhook trigger requires Header Auth (`Fleet Ops Webhook Trigger Token`),
|
|
validates `event_type == vehicle.returned.v1`, derives a follow-up category, calls Fleet
|
|
Ops's `/return-callback` endpoint with an `Idempotency-Key` header via a named
|
|
`Fleet Ops Service Token` credential (not a literal secret), and responds with a
|
|
controlled JSON result. Idempotent on both sides (`event_id` flows through as the
|
|
dedup key; the backend independently checks for a prior audit event before recording
|
|
again).
|
|
|
|
**This round's finding**: the `Record follow-up` HTTP node had no explicit timeout and
|
|
"Retry On Fail" disabled — a real gap against the requirement that external dependencies
|
|
have timeouts and bounded retries. Fixed live: Retry On Fail (3 tries, 1000ms wait) + a
|
|
15000ms timeout, published. Safe to retry because the callback is idempotent. Repo
|
|
definition and manifest checksum synced (commit `0562893`).
|
|
|
|
Attached to workflow 4 as its Error Workflow.
|
|
|
|
## Workflow 2 — Scheduled Data Quality Scan
|
|
|
|
Live workflow ID `mobilityops-scheduled-quality-scan`. Validated earlier: hourly
|
|
Schedule Trigger + a Manual Trigger for on-demand testing, both feeding a single HTTP
|
|
call to Fleet Ops's `/scheduled-scan` endpoint (Header Auth via the same `Fleet Ops
|
|
Service Token` credential, 15000ms timeout already configured), which runs the
|
|
domain-level `run_scan()` function — documented and tested as idempotent by
|
|
construction (only ever creates an issue for a condition that doesn't already have one
|
|
open), so overlapping or retried triggers do no duplicate domain work.
|
|
|
|
**This round's finding**: the same Retry On Fail gap as workflow 1 (timeout was already
|
|
set, retries were not). Fixed live the same way (3 tries, 1000ms wait), published. Repo
|
|
definition and manifest checksum synced (commit `167bf49`).
|
|
|
|
Attached to workflow 4 as its Error Workflow.
|
|
|
|
## Workflow 3 — RAGcore Procedure Sync — blocked
|
|
|
|
**Not built.** This workflow needs an application credential (scope `sources:sync`) for
|
|
the `fleet-ops` application in RAGcore. Two independent issuance attempts, in two
|
|
separate rounds of this effort, both failed with an opaque server-side rejection:
|
|
|
|
1. **Raw API**: `POST /v1/applications/{id}/credentials` → `400`, "authoritative
|
|
service-account state rejected issuance".
|
|
2. **RAGcore admin UI**, this round, after the project owner explicitly authorized
|
|
Claude to self-issue the credential: the "Issue credential" form for the `fleet-ops`
|
|
application, submitted as the Platform Admin role (the highest role visible in the
|
|
RAGcore admin), with name `n8n-ragcore-procedure-sync` and scope `sources:sync` only.
|
|
Result: "Something went wrong. The credential could not be issued with those
|
|
values.", trace reference `1955c6a8968c4941a22a1faef39e17a7`.
|
|
|
|
The `fleet-ops` application itself shows as ordinary/`Active` in the RAGcore admin, with
|
|
no visible lock flag, and RAGcore's own OpenAPI spec documents no validation rule that
|
|
would explain either rejection (no `422`, no field-level errors). Two independent paths
|
|
— a raw API call and the admin UI as the top admin role — hitting the same failure
|
|
signature is conclusive evidence this is a RAGcore-side policy or bug, not a Fleet Ops
|
|
request-shape or permission problem. It is not fixable from the Fleet Ops side or
|
|
through further UI automation. Resolving it requires whoever operates the RAGcore
|
|
instance to look up the trace ID above (and the earlier raw-API rejection) in RAGcore's
|
|
own logs.
|
|
|
|
The real RAGcore contract this workflow will be built against — once a working
|
|
credential exists — was independently inspected via RAGcore's live OpenAPI spec and is
|
|
recorded in `docs/live-ai-integration/n8n-current-state.md` and
|
|
`contracts/ragcore-contract-assumptions.md`: control-plane endpoints require an
|
|
`Idempotency-Key` header; ingestion is `POST /v1/uploads`; retrieval is `POST
|
|
/v1/search` / `/v1/context` / `/v1/answers` (the latter requiring `requested_space_ids`,
|
|
an array of knowledge-space UUIDs); health is `/health/live` and `/health/ready` (not
|
|
`/health`); the scope enum is `search, context, answer, documents:read, citations:read,
|
|
feedback:write, sources:sync`.
|
|
|
|
**`RAGcoreKnowledgeProvider` adapter** (`backend/app/services/knowledge/ragcore.py`)
|
|
still targets the earlier speculative contract (`/health`, `POST /api/v1/ask`, Bearer
|
|
token) rather than the real one above. This was deliberately **not** rewritten this
|
|
round: rewriting it blind, without a credential to validate against, risks introducing
|
|
a silent behavioral bug in exactly the code path responsible for the project's "AI must
|
|
never invent an answer when RAGcore is unavailable or returns insufficient evidence"
|
|
guarantee — for example a wrong `evidence_state` mapping that looks fine in code review
|
|
but misclassifies "unavailable" as "insufficient" (or vice versa) against the real
|
|
response shape. The adapter's current behavior is honest and safe (it degrades cleanly
|
|
to `unavailable` on any request or parsing failure, and `ragcore_api_token` is unset by
|
|
default so the app correctly runs on the local demo knowledge provider today). The
|
|
rewrite stays queued behind the same credential blocker as workflow 3.
|
|
|
|
## Workflow 4 — Workflow Error Handler
|
|
|
|
Live workflow ID `Xppn2rAEqUuyiCJF`. Built and live-validated in an earlier round:
|
|
Error Trigger → a Code node that derives a bounded, secret-free failure report (error
|
|
category classified from the message text, truncated summary, no stack trace, no
|
|
headers or tokens) → an HTTP call to Fleet Ops's `/workflow-error` endpoint (Header Auth
|
|
via the same `Fleet Ops Service Token` credential), which registers the failure as an
|
|
audit event idempotently keyed on `execution_id`.
|
|
|
|
Set as the Error Workflow on both workflow 1 and workflow 2. Confirmed workflow 4 has no
|
|
Error Workflow of its own (prevents a recursive loop).
|
|
|
|
**Live validation performed**: a pinned mock Error Trigger payload produced a real `200
|
|
{"status":"registered", ...}` from the live Fleet Ops server; re-running the identical
|
|
payload produced `"status":"already_registered"`, confirming idempotency. A genuine
|
|
induced failure (temporarily pointing workflow 2's HTTP node at a nonexistent path, then
|
|
reverting) confirmed workflow 2 itself fails correctly against a broken endpoint and
|
|
recovers cleanly once reverted.
|
|
|
|
**Known limitation**: n8n's Error Workflow trigger does not fire for manual editor
|
|
"Execute workflow" test runs — checked via workflow 4's own Executions list after the
|
|
induced workflow-2 failure, and confirmed no new execution appeared. n8n only invokes a
|
|
workflow's assigned Error Workflow for unattended/production trigger executions, not
|
|
manual test runs from the editor. The mock-data path exercises the same nodes, logic,
|
|
and real Fleet Ops endpoint, but a fully automatic (schedule- or webhook-triggered)
|
|
failure cascading into workflow 4 was not observed live in either round.
|
|
|
|
**Open follow-up (minor, non-blocking)**: continuing this round's acceptance pass to
|
|
workflow 4 found the same timeout/retry gap as workflows 1 and 2 on its own outbound
|
|
HTTP call. A fix was started (15000ms timeout added, Retry On Fail toggled on) but n8n's
|
|
autosave began failing with "Unauthorized" mid-edit; a fresh browser tab confirmed the
|
|
n8n session had expired (redirected to `/signin`). Nothing was saved — workflow 4's live
|
|
definition is unchanged from before this round, so there is no partial or broken state.
|
|
This is lower-stakes than workflows 1/2 (workflow 4 is the error notifier itself, not a
|
|
primary business flow, and a failed error-report is already visible in n8n's own
|
|
execution history via `On Error: Stop Workflow`) but should be finished once the n8n
|
|
browser session is re-authenticated.
|
|
|
|
## Repository source of truth
|
|
|
|
`n8n/workflows/` holds cleaned, credential-value-free JSON definitions for all built
|
|
workflows, `n8n/workflows/MANIFEST.md` documents purpose/trigger/contract/credentials/
|
|
live-ID/checksum for all four canonical workflows (including workflow 3's blocked
|
|
status), and `n8n/workflows/check_drift.py` is a read-only script that compares the
|
|
repo definitions against the live instance via n8n's Public API and reports drift —
|
|
safe to run in CI as a non-blocking check. No literal export/download mechanism was
|
|
found working in this n8n version, so each definition was reconstructed from direct,
|
|
verified UI inspection rather than a native export; this limitation is noted in the
|
|
manifest itself.
|
|
|
|
## Integration status page
|
|
|
|
`/automation` (Operations Manager only) surfaces real per-workflow evidence derived
|
|
purely from Fleet Ops's own audit/outbox tables — no new dependency on n8n's API was
|
|
added to the backend. Each of the four canonical workflows shows a status (not built /
|
|
no evidence yet / operational) and a last-evidence timestamp; the scheduled-scan
|
|
evidence specifically filters to `actor_type == "service"` so a manually-triggered scan
|
|
in the UI doesn't count as n8n evidence. An error-handler summary line reports total
|
|
registered automation failures and the most recent one.
|
|
|
|
Verified live in the browser (Dutch locale) both locally and on the deployed
|
|
production server (`http://192.168.10.150:1236/automation`): correctly showed "3 van 4
|
|
canonieke n8n-workflows hebben actuele evidentie van werking" with real timestamps for
|
|
the return/scan/error-handler workflows, "Nog Niet Gebouwd" for the RAGcore sync, and
|
|
the real error-handler registration from this effort's live testing.
|
|
|
|
## Deployments performed (all explicitly user-approved)
|
|
|
|
1. Backend `/workflow-error` endpoint (commit `bbdb4a9`) — deployed and verified
|
|
(`/health` OK, new endpoint returns `422` not `404` on an empty POST body).
|
|
2. Integration status page, backend + frontend (commit `4049c0c`) — deployed and
|
|
verified (`/health` OK, page renders real evidence in the browser).
|
|
|
|
The three n8n-side node edits this round (WF1 timeout/retry, WF2 timeout/retry, WF4's
|
|
incomplete attempt) are live edits to the n8n instance itself and do not require a
|
|
Fleet Ops redeploy.
|
|
|
|
## What's left
|
|
|
|
1. **RAGcore credential issuance** — blocked on RAGcore's own server-side rejection
|
|
(trace `1955c6a8968c4941a22a1faef39e17a7` and the earlier raw-API `400`). Needs
|
|
RAGcore's operator to investigate. Unblocks workflow 3 and the
|
|
`RAGcoreKnowledgeProvider` real-contract rewrite.
|
|
2. **Workflow 4's own timeout/bounded-retry hardening** — needs the n8n browser session
|
|
re-authenticated to finish; a small, well-understood, non-blocking edit.
|
|
3. **Fleet Ops logo/favicon** — explicitly deferred by the project owner as a separate,
|
|
unrelated follow-up task, not part of this integration effort.
|