M8: GUI polish, n8n workflow-3 fixes, RAGcore retrieval root-cause and fix

GUI: dashboard Attention Queue presents a curated severity mix instead of pure
severity-sort (grouped Now/Today/Later headers); Today's Movements seed data
curated so a fresh reset shows a credible day (2+ departures, 2+ returns), with
a new seed-integrity test; About Demo restructured into a compact grid with
progressive disclosure for technical sections; Duplicate Merge shows match/conflict
counts, hides matching fields by default, and previews the final merged record
before confirmation.

Repo hygiene: removed a stray empty `backend;C` directory and an untracked 31MB
zip export; `.gitignore` now excludes future archive exports.

n8n: fixed invalid JSON (a missing `},` between two node objects) in the committed
`fleet-ops-vehicle-return.json` -- the file could not be parsed. Live-validated
workflow 3 (RAGcore Procedure Sync): found and fixed a real defect (three body
parameters had a stray trailing `}}`) and a missing Error Workflow wiring, both
via the safe `n8n import:workflow` CLI path; exported the corrected, still-
inactive workflow as the new source of truth and updated MANIFEST.md/check_drift.py.
Publishing it (starts real daily unattended runs) remains a separate decision.

RAGcore: root-caused and fixed (live, approved) the "zero retrieval candidates"
bug -- a filesystem permission bug (`embedding_profiles.json` unreadable by the
app's own runtime user) that broke every retrieval call before it reached Qdrant.
Every other suspect (grants, scope resolution, Qdrant filters, embeddings) was
verified healthy first. Found a second, deeper gap: the reranker adapter calls
an Ollama HTTP route that does not exist on the deployed Ollama version, so
`/v1/answers` still returns `not_answerable`. `KNOWLEDGE_PROVIDER` stays `demo`
until that is resolved on the RAGcore side. Evidence-based MCP Hub integration
status (real tool-call audit history, not just a boolean flag) replaces the old
`configured`/`not_configured` guess. Full findings in
`docs/final-integrations/current-state-audit.md`.

Backend: 172 tests passing, ruff clean, mypy clean (50 files). Frontend: tsc
clean, production build clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
NuklearRabbit
2026-08-05 13:05:02 +02:00
co-authored by Claude Sonnet 5
parent 3ebca9e9b7
commit 34df66d28c
25 changed files with 669 additions and 100 deletions
+28 -22
View File
@@ -18,7 +18,7 @@ credential values are never embedded; nodes reference named n8n credentials inst
| Active status (as of 2026-08-04) | Active / Published |
| Error Workflow | `Fleet Ops — Workflow Error Handler` (wired) |
| Timeouts / bounded retries | `Record follow-up` HTTP node: 15s timeout, retry on fail (3 tries, 1000ms wait) |
| Checksum (sha256) | `a6f399dd77a7203dec7c0ac95e8540abf55f2703da519e06f1c37f2e1220f609` |
| Checksum (sha256) | `e5b6ba02a7824867620ceaf214224521d52de76337604b947f26f1b0b5432358` (updated 2026-08-05 — the committed file had invalid JSON, a missing `},` between two node objects; fixed, no live workflow change) |
## 2. Fleet Ops — Scheduled Data Quality Scan
@@ -37,33 +37,39 @@ credential values are never embedded; nodes reference named n8n credentials inst
## 3. Fleet Ops — RAGcore Procedure Sync
Not yet built, but no longer blocked. Credential issuance for the `fleet-ops` application
previously failed 100% of the time with an opaque rejection ("authoritative service-account
state rejected issuance" via the raw API; a generic error via the admin UI). Root-caused to
a genuine bug in RAGcore itself — a cross-transaction race in
`src/ragcore/api/v1/control/dependencies.py` where `get_control_application` and
`get_credential_service` each opened their own independent database transaction, so a
freshly-created service account was invisible to the immediately-following credential-issue
read. Fixed in RAGcore (with explicit owner approval) by sharing one request-scoped
transaction between both dependencies; verified against RAGcore's own test suite (64
passing) and deployed to the live instance. A working credential now exists: n8n credential
**"RAGcore Sync Token"** (Header Auth, `Authorization: Bearer <token>`), scope
`sources:sync` for `fleet-ops`. Will build
`n8n/workflows/fleet-ops-ragcore-procedure-sync.json` against the real RAGcore contract
(`POST /v1/uploads`, `GET /v1/knowledge-spaces`, etc. — see
`contracts/ragcore-contract-assumptions.md` and the live inspection notes in
`docs/live-ai-integration/n8n-current-state.md`) next.
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.
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
trailing `}}` (e.g. `={{ $json.execution_id }} }}` instead of `={{ $json.execution_id
}}`), which would have sent malformed values on every real run. Fixed via `n8n
import:workflow` (the safe CLI path — not the REST API, which caused a prior wipe
incident in this environment) against the same live workflow ID, keeping it inactive;
re-exported and verified the fix applied with no other change (still 6 real nodes,
`active: false`). Also found and fixed a second gap: `settings.errorWorkflow` was unset
(workflows 1-2 wire `"errorWorkflow": "Xppn2rAEqUuyiCJF"`, workflow 3 did not) — wired it
the same way via the same CLI import path, re-verified.
| Field | Value |
|---|---|
| File | `fleet-ops-ragcore-procedure-sync.json` (not yet created) |
| Live workflow ID | — |
| Active status | Not built |
| File | `fleet-ops-ragcore-procedure-sync.json` |
| Purpose | Sync the trilingual procedure documents from Fleet Ops into RAGcore as source documents, keeping stable per-document IDs and skipping unchanged content. |
| Trigger | Schedule Trigger (daily at midnight) |
| 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) |
| Error Workflow | `Fleet Ops — Workflow Error Handler` (wired) |
| Checksum (sha256) | `643c0515a50fed3d35e28f0b8cb17841f6f8d91699944980b5c56e619d5bf2a6` |
## 4. Fleet Ops — Workflow Error Handler
Central technical workflow attached to workflows 1-2 via n8n's per-workflow "Error
Workflow" setting (workflow 3 will be wired the same way once it exists). Receives n8n's
Central technical workflow attached to all three other Fleet Ops workflows (1, 2, 3) via
n8n's per-workflow "Error Workflow" setting. Receives n8n's
standard Error Trigger payload, derives a bounded/secret-free failure report (safe error
category, truncated summary, no stack trace, no headers/tokens), and POSTs it to Fleet
Ops, which registers an audit event idempotently keyed on `execution_id`.
+1
View File
@@ -33,6 +33,7 @@ KNOWN_WORKFLOWS = [
("fleet-ops-vehicle-return.json", "mobilityops-return-processing"),
("fleet-ops-data-quality-scan.json", "mobilityops-scheduled-quality-scan"),
("fleet-ops-error-handler.json", "Xppn2rAEqUuyiCJF"),
("fleet-ops-ragcore-procedure-sync.json", "6wbkc4d1AouGpmWT"),
]
# Fields that legitimately differ between a committed definition and the live instance
@@ -0,0 +1,246 @@
{
"id": "6wbkc4d1AouGpmWT",
"name": "Fleet Ops — RAGcore Procedure Sync",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
128,
-144
],
"id": "08f41c46-d5a9-4cb9-beec-0f074a9eb7bc",
"name": "Sticky Note"
},
{
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-144,
-144
],
"id": "6edcd359-08eb-4d88-98e9-5ca0625047cc",
"name": "Schedule Trigger"
},
{
"parameters": {
"url": "http://192.168.10.150:1236/api/v1/integrations/n8n/procedures",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"options": {
"timeout": 15000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
80,
-144
],
"id": "b66d3ee4-6264-48ab-89fc-aeb4d1597435",
"name": "List procedures",
"retryOnFail": true,
"credentials": {
"httpHeaderAuth": {
"name": "Fleet Ops Service Token"
}
}
},
{
"parameters": {
"jsCode": "const documents = $input.first().json.documents;\nconst results = [];\nfor (const doc of documents) {\n const binary = await this.helpers.prepareBinaryData(Buffer.from(doc.content, 'utf-8'), doc.document_id + '.md', 'text/markdown');\n results.push({json: {id: doc.id, language: doc.language, document_id: doc.document_id, title: doc.title, version: doc.version, content_hash: doc.content_hash}, binary: {file: binary}});\n}\nreturn results;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
304,
-144
],
"id": "e9699a7a-87ff-4603-b89b-553dc7f2f29d",
"name": "Prepare uploads"
},
{
"parameters": {
"method": "POST",
"url": "https://rag.itworx.tech/v1/uploads",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Idempotency-Key",
"value": "={{ $json.id + '-' + $json.content_hash }}"
}
]
},
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"name": "space_id",
"value": "f4c91e49-5cf9-48ba-b3d6-e0e9854ebccc"
},
{
"name": "source_id",
"value": "={{ $json.id }}"
},
{
"parameterType": "formBinaryData",
"name": "file",
"inputDataFieldName": "file"
}
]
},
"options": {
"timeout": 20000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
528,
-144
],
"id": "6d38f228-ed82-44a7-bc59-c785767d4f35",
"name": "Upload to RAGcore",
"retryOnFail": true,
"credentials": {
"httpHeaderAuth": {
"name": "RAGcore Sync Token"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "const items = $input.all();\nlet synced = 0;\nlet failed = 0;\nfor (const item of items) {\n if (item.json && item.json.error) {\n failed += 1;\n } else {\n synced += 1;\n }\n}\nreturn [{ json: { execution_id: String($execution.id), synced, failed } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
640,
-144
],
"id": "4b90056e-29f8-4df0-b7b4-695f4b9e0004",
"name": "Summarize sync result"
},
{
"parameters": {
"method": "POST",
"url": "http://192.168.10.150:1236/api/v1/integrations/n8n/procedures-sync-result",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "execution_id",
"value": "={{ $json.execution_id }}"
},
{
"name": "synced",
"value": "={{ $json.synced }}"
},
{
"name": "failed",
"value": "={{ $json.failed }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
848,
-144
],
"id": "953cd587-3d3e-44b5-907c-92b8355b00e3",
"name": "Report sync result to Fleet Ops",
"credentials": {
"httpHeaderAuth": {
"name": "Fleet Ops Service Token"
}
}
}
],
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "List procedures",
"type": "main",
"index": 0
}
]
]
},
"List procedures": {
"main": [
[
{
"node": "Prepare uploads",
"type": "main",
"index": 0
}
]
]
},
"Prepare uploads": {
"main": [
[
{
"node": "Upload to RAGcore",
"type": "main",
"index": 0
}
]
]
},
"Upload to RAGcore": {
"main": [
[
{
"node": "Summarize sync result",
"type": "main",
"index": 0
}
]
]
},
"Summarize sync result": {
"main": [
[
{
"node": "Report sync result to Fleet Ops",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": false,
"errorWorkflow": "Xppn2rAEqUuyiCJF",
"callerPolicy": "workflowsFromSameOwner"
},
"active": false,
"meta": {
"templateCredsSetupCompleted": false
},
"tags": []
}
@@ -68,6 +68,7 @@
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 1000
},
{
"parameters": {
"respondWith": "json",