docs: write final n8n + RAGcore integration evidence summary
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.
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user