M56: make RAGcore sync fail closed
MobilityOps acceptance / backend (push) Failing after 19s
MobilityOps acceptance / frontend (push) Successful in 26s
MobilityOps acceptance / e2e (push) Skipped

This commit is contained in:
NuklearRabbit
2026-08-24 03:58:57 +02:00
parent 444e61253b
commit 81de78bd8b
4 changed files with 60 additions and 3 deletions
+21
View File
@@ -1,5 +1,26 @@
# Project state
## M56 — make RAGcore procedure sync fail closed (2026-08-24)
- M55 restored private connectivity and RAGcore became reachable/ready from both Fleet Ops
API replicas. The repeated live canary then returned honest `insufficient` answers because
source-filtered search found no documents, while unfiltered search proved the procedure
text itself was indexed under unrelated legacy/upload identities.
- The active n8n workflow had drifted onto `Fleet Ops Service Token` for the RAGcore upload
node instead of the existing `RAGcore Sync Token`. Its summary counted every body without
an `error` property as synced, so RAGcore Problem responses were falsely reported as 33
successes. No unsafe relaxation of Fleet Ops citation validation was made.
- The committed workflow now sends the documented stable identity tuple (`source_id`,
`external_id`, `locale`), uses the dedicated sync credential by name and counts success
only when RAGcore returns a real `AcceptedJob` (`job_id` plus `status_url`). The contract
checker enforces all three invariants to prevent recurrence.
- Validation: workflow JSON parses with the expected credential/fields, `git diff --check`
is clean and the OpenAPI/event/MCP/n8n synchronization gate passes against the current
source tree.
- Exact next action: commit/push M56, import the exact workflow while binding the existing
RAGcore credential ID without exposing it, publish/execute it, verify all 33 stable source
documents through RAGcore and rerun the four live Chromium/Firefox acceptance checks.
## M55 — restore private cross-project RAGcore routing (2026-08-24)
- M54 promoted successfully as `81e3fd63bdbcb2e9c4ae1d709ea46f40537b6f62`, with two
+2 -2
View File
@@ -72,8 +72,8 @@ the same way via the same CLI import path, re-verified.
| Live workflow ID | `6wbkc4d1AouGpmWT` |
| Active status (as of 2026-08-05) | **Active / Published** |
| Error Workflow | `Fleet Ops — Workflow Error Handler` (wired) |
| Execution telemetry | Successful syncs report both the bounded sync result and the canonical workflow heartbeat. |
| Checksum (sha256) | `4afd46b6ef57b7e0a4705611732506d7fcc2a37567266847991b85050e1d37c3` |
| Execution telemetry | Only a valid RAGcore `AcceptedJob` response counts as synced; HTTP problem bodies count as failed. Fleet Ops separately verifies exact active documents before reporting index health. |
| Checksum (sha256) | `7e9e4beb02a9f9de69efe9f74626e44b6f34f2e0f905229b4605b8cd452b7128` |
## 4. Fleet Ops — Workflow Error Handler
@@ -121,6 +121,14 @@
"name": "source_id",
"value": "={{ $json.id }}"
},
{
"name": "external_id",
"value": "={{ $json.document_id + '.md' }}"
},
{
"name": "locale",
"value": "={{ $json.language }}"
},
{
"parameterType": "formBinaryData",
"name": "file",
@@ -150,7 +158,7 @@
},
{
"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 } }];"
"jsCode": "const items = $input.all();\nlet synced = 0;\nlet failed = 0;\nfor (const item of items) {\n const body = item && item.json;\n const accepted = body && typeof body.job_id === 'string' && typeof body.status_url === 'string' && body.status_url.length > 0;\n if (accepted) {\n synced += 1;\n } else {\n failed += 1;\n }\n}\nreturn [{ json: { execution_id: String($execution.id), synced, failed } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
+28
View File
@@ -109,6 +109,34 @@ def check_workflows(failures: list[str]) -> None:
fail(f"{filename} contains a cleartext Fleet Ops callback", failures)
if filename == "fleet-ops-vehicle-return.json":
check_return_workflow_timing(definition, failures)
if filename == "fleet-ops-ragcore-procedure-sync.json":
check_ragcore_sync_workflow(definition, failures)
def check_ragcore_sync_workflow(definition: dict, failures: list[str]) -> None:
"""Keep the sync identity stable and reject HTTP error bodies as successful syncs."""
nodes_by_name = {node.get("name"): node for node in definition.get("nodes", [])}
upload = nodes_by_name.get("Upload to RAGcore", {})
credential = upload.get("credentials", {}).get("httpHeaderAuth", {})
if credential.get("name") != "RAGcore Sync Token":
fail("RAGcore upload must use the dedicated sync credential", failures)
parameters = upload.get("parameters", {}).get("bodyParameters", {}).get("parameters", [])
field_names = {
item.get("name") for item in parameters if isinstance(item, dict)
}
required = {"space_id", "source_id", "external_id", "locale", "file"}
if not required.issubset(field_names):
fail(
"RAGcore upload must preserve source_id, external_id and locale provenance",
failures,
)
summary_code = (
nodes_by_name.get("Summarize sync result", {}).get("parameters", {}).get("jsCode", "")
)
if not all(token in summary_code for token in ("job_id", "status_url", "failed += 1")):
fail("RAGcore sync summary must accept only a real AcceptedJob response", failures)
def check_return_workflow_timing(definition: dict, failures: list[str]) -> None: