Spaces:
Sleeping
Sleeping
Commit ·
af09dd7
1
Parent(s): e48c905
document S4.5 D1 regression root cause
Browse files
docs/evidence/THOUGHT_ENGINE_NODE_D1_REGRESSION_V1.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# THOUGHT ENGINE NODE D1 REGRESSION V1
|
| 2 |
+
|
| 3 |
+
Date: 2026-08-18
|
| 4 |
+
Stage: S4.5C Node -> D1 root-cause continuation
|
| 5 |
+
|
| 6 |
+
## Symptom
|
| 7 |
+
|
| 8 |
+
The deployed Thought Engine Node remained healthy at `/health`, but D1-backed reads took about 10 seconds and returned healthy-looking empty state (`/sessions` => `{"total":0,"sessions":[]}`). Session creation through the Bridge also failed downstream.
|
| 9 |
+
|
| 10 |
+
## Cloudflare control
|
| 11 |
+
|
| 12 |
+
Authoritative D1 database: `thought-vault`
|
| 13 |
+
|
| 14 |
+
Database UUID: `ed9b375d-bf00-477b-942e-379828b70e81`
|
| 15 |
+
|
| 16 |
+
Authoritative pre-smoke row counts from direct remote Wrangler control:
|
| 17 |
+
|
| 18 |
+
- sessions: 7
|
| 19 |
+
- thought_units: 60
|
| 20 |
+
- thought_edges: 53
|
| 21 |
+
- thought_reviews: 4
|
| 22 |
+
- witness_events: 60
|
| 23 |
+
- pattern_registry: 0
|
| 24 |
+
- version_history: 0
|
| 25 |
+
|
| 26 |
+
No data loss is supported by the evidence. Historical rows are present.
|
| 27 |
+
|
| 28 |
+
Schema control completed from `C:\Users\kryst` context:
|
| 29 |
+
|
| 30 |
+
- `pattern_registry` (7 columns)
|
| 31 |
+
- `version_history` (6)
|
| 32 |
+
- `witness_events` (7)
|
| 33 |
+
- `thought_reviews` (7)
|
| 34 |
+
- `thought_edges` (6)
|
| 35 |
+
- `thought_units` (9)
|
| 36 |
+
- `sessions` (9)
|
| 37 |
+
- Cloudflare internal `_cf_KV`
|
| 38 |
+
|
| 39 |
+
`sessions` columns:
|
| 40 |
+
|
| 41 |
+
1. session_id TEXT primary key
|
| 42 |
+
2. title TEXT
|
| 43 |
+
3. created_by TEXT NOT NULL default 'system'
|
| 44 |
+
4. created_at TEXT NOT NULL default datetime('now')
|
| 45 |
+
5. updated_at TEXT NOT NULL default datetime('now')
|
| 46 |
+
6. status TEXT NOT NULL default 'active'
|
| 47 |
+
7. root_thought_id TEXT
|
| 48 |
+
8. active_thought_id TEXT
|
| 49 |
+
9. metadata TEXT default '{}'
|
| 50 |
+
|
| 51 |
+
Both schema queries completed successfully against the intended UUID in about 0.14-0.21 ms SQL execution time.
|
| 52 |
+
|
| 53 |
+
### Wrangler context anomaly
|
| 54 |
+
|
| 55 |
+
Launching Wrangler from the production Orchestrator working directory (`C:\Users\kryst\Infrastructure\mcp-servers\mcp-orchestrator`) caused Wrangler to attempt account `ae406365ceacd48b5b3bfa299f2644be` and return Cloudflare authentication error code 10000, while Wrangler's authentication fallback displayed account `8543b906ec5121d56ff7bf7ede12525d`.
|
| 56 |
+
|
| 57 |
+
The Desktop Commander process environment did not contain `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_D1_DATABASE_ID`, or `CLOUDFLARE_D1_API_TOKEN`. Re-running the schema commands from `C:\Users\kryst` immediately targeted the correct `thought-vault` UUID and succeeded. This is treated as a local Wrangler working-context anomaly, not evidence about the deployed Node.
|
| 58 |
+
|
| 59 |
+
## Node source and deployment identity
|
| 60 |
+
|
| 61 |
+
Original local / remote / deployed revision: `1fce18e542306fd929363902119b0a496d16ef79`
|
| 62 |
+
|
| 63 |
+
Diagnostic deployed revision: `e48c9052b54c9e1eb221a80027a0a2e66569fbaa`
|
| 64 |
+
|
| 65 |
+
Rollback revision: `1fce18e542306fd929363902119b0a496d16ef79`
|
| 66 |
+
|
| 67 |
+
HF Space: `Kode-Animator/thought-engine-node`
|
| 68 |
+
|
| 69 |
+
HF runtime reached `RUNNING` on diagnostic revision `e48c9052...`.
|
| 70 |
+
|
| 71 |
+
Pre-existing untracked `test_smoke.py` remained untouched and was not committed.
|
| 72 |
+
|
| 73 |
+
## D1 client implementation
|
| 74 |
+
|
| 75 |
+
`d1_client.py` reads:
|
| 76 |
+
|
| 77 |
+
- `CLOUDFLARE_ACCOUNT_ID`
|
| 78 |
+
- `CLOUDFLARE_D1_DATABASE_ID`
|
| 79 |
+
- `CLOUDFLARE_D1_API_TOKEN`
|
| 80 |
+
|
| 81 |
+
Request construction:
|
| 82 |
+
|
| 83 |
+
`https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/d1/database/{DATABASE_ID}/query`
|
| 84 |
+
|
| 85 |
+
Headers:
|
| 86 |
+
|
| 87 |
+
- `Authorization: Bearer <token>`
|
| 88 |
+
- `Content-Type: application/json`
|
| 89 |
+
|
| 90 |
+
Body:
|
| 91 |
+
|
| 92 |
+
- `{"sql": <sql>}`
|
| 93 |
+
- optional `params`
|
| 94 |
+
|
| 95 |
+
Client behavior:
|
| 96 |
+
|
| 97 |
+
- `httpx.AsyncClient(timeout=10.0)`
|
| 98 |
+
- no retry loop
|
| 99 |
+
- `raise_for_status()` on HTTP response
|
| 100 |
+
- broad exception catch returned `[]`
|
| 101 |
+
|
| 102 |
+
HF metadata confirmed all three Cloudflare values exist as Space Secrets. Hugging Face intentionally hides secret values after creation, so the configured account ID and database UUID could not be compared directly without changing runtime observability or manually replacing secrets. Token value was never exposed.
|
| 103 |
+
|
| 104 |
+
## API compatibility control
|
| 105 |
+
|
| 106 |
+
Current Cloudflare D1 documentation still specifies:
|
| 107 |
+
|
| 108 |
+
`POST /accounts/{account_id}/d1/database/{database_id}/query`
|
| 109 |
+
|
| 110 |
+
with Bearer API token authorization and `sql` / optional `params` JSON body. The Node request shape matches the current documented API. No API-path compatibility regression is supported.
|
| 111 |
+
|
| 112 |
+
## HF -> Cloudflare boundary
|
| 113 |
+
|
| 114 |
+
Existing HF logs before instrumentation repeatedly contained only:
|
| 115 |
+
|
| 116 |
+
`D1 Connection Error:`
|
| 117 |
+
|
| 118 |
+
because the broad exception handler logged only `str(e)`.
|
| 119 |
+
|
| 120 |
+
A one-line diagnostic logging change was developed with TDD so the existing log also records `type(e).__name__`.
|
| 121 |
+
|
| 122 |
+
Test: `test_d1_client.py`
|
| 123 |
+
|
| 124 |
+
RED proof: `ReadTimeout` was absent from the original blank log.
|
| 125 |
+
|
| 126 |
+
GREEN proof: after the logging-only change, the test passed and `git diff --check` was clean.
|
| 127 |
+
|
| 128 |
+
After deploying `e48c9052...`, a fresh read-only call to `/sessions` returned HTTP 200 with `{"total":0,"sessions":[]}` after about 10.6 seconds.
|
| 129 |
+
|
| 130 |
+
The corresponding HF runtime log recorded:
|
| 131 |
+
|
| 132 |
+
`D1 Connection Error [ConnectTimeout]:`
|
| 133 |
+
|
| 134 |
+
Therefore the Node does not reach an HTTP response from Cloudflare. Authentication acceptance/rejection and D1 query acceptance/rejection are downstream of the first observed failure.
|
| 135 |
+
|
| 136 |
+
Boundary classification:
|
| 137 |
+
|
| 138 |
+
- DNS: not independently isolated
|
| 139 |
+
- TCP connect: not independently isolated from TLS setup by current `httpx.ConnectTimeout` evidence
|
| 140 |
+
- TLS handshake: not independently isolated
|
| 141 |
+
- HTTP response: NOT REACHED
|
| 142 |
+
- Cloudflare auth: NOT REACHED / NOT TESTED
|
| 143 |
+
- D1 request acceptance: NOT REACHED / NOT TESTED
|
| 144 |
+
- observed timeout class: `httpx.ConnectTimeout`
|
| 145 |
+
|
| 146 |
+
## Root cause
|
| 147 |
+
|
| 148 |
+
Classification: D - HF runtime cannot successfully establish the Cloudflare API connection.
|
| 149 |
+
|
| 150 |
+
First broken boundary: deployed HF Thought Engine Node -> `api.cloudflare.com:443` connection establishment, before HTTP response.
|
| 151 |
+
|
| 152 |
+
Evidence:
|
| 153 |
+
|
| 154 |
+
1. Direct remote Wrangler queries to the intended D1 database succeed and historical data is present.
|
| 155 |
+
2. Node source uses the current documented Cloudflare D1 REST path.
|
| 156 |
+
3. Deployed Node `/sessions` consistently consumes roughly the 10-second httpx timeout and returns false empty state.
|
| 157 |
+
4. Typed runtime instrumentation proves the thrown exception is `httpx.ConnectTimeout`.
|
| 158 |
+
5. No Cloudflare HTTP status is observed, so credential/schema/API response errors cannot be the first failure.
|
| 159 |
+
|
| 160 |
+
Confidence: HIGH for classification D and the first broken boundary; insufficient evidence to attribute the connect timeout specifically to DNS, TCP routing, TLS handshake, provider egress policy, or an upstream network incident.
|
| 161 |
+
|
| 162 |
+
## Fix status
|
| 163 |
+
|
| 164 |
+
Primary repair: NOT PERFORMED.
|
| 165 |
+
|
| 166 |
+
Reason: no evidence-backed minimal Node-side fix has yet been established for the HF -> Cloudflare connection-establishment timeout. Changing credentials, database identifiers, API path, timeout duration, retries, DNS behavior, IPv4/IPv6 preference, or provider settings without further evidence would violate the no-fix-without-root-cause rule.
|
| 167 |
+
|
| 168 |
+
Database mutation: NONE.
|
| 169 |
+
|
| 170 |
+
Smoke session: NOT CREATED because database connectivity is not restored.
|
| 171 |
+
|
| 172 |
+
Historical Node session restoration: NOT ACHIEVED.
|
| 173 |
+
|
| 174 |
+
Bridge historical list proof: NOT RUN because the downstream Node path remains broken.
|
| 175 |
+
|
| 176 |
+
## Secondary defect
|
| 177 |
+
|
| 178 |
+
Independently confirmed: any D1 exception is swallowed into `[]`, allowing unavailable D1 state to masquerade as zero records.
|
| 179 |
+
|
| 180 |
+
The diagnostic commit improves log observability only. It does not yet change API error semantics. Per S4.5 ordering, the primary connectivity failure should be resolved before the secondary empty-state masquerade is repaired.
|
| 181 |
+
|
| 182 |
+
## Untouched systems
|
| 183 |
+
|
| 184 |
+
Bridge revision: `b17615cc0ab007b2dce33ebee9f5f258934c3e96` (unchanged)
|
| 185 |
+
|
| 186 |
+
Candidate changes: NONE
|
| 187 |
+
|
| 188 |
+
Federation changes: NONE
|
| 189 |
+
|
| 190 |
+
Production Orchestrator changes: NONE
|
| 191 |
+
|
| 192 |
+
D1 schema/data mutation: NONE
|
| 193 |
+
|
| 194 |
+
## Next boundary
|
| 195 |
+
|
| 196 |
+
S5 remains blocked.
|
| 197 |
+
|
| 198 |
+
The next investigation must refine `ConnectTimeout` into DNS vs TCP vs TLS/provider-egress evidence, or establish a provider-side incident/configuration fact, before selecting a repair.
|