Skip to content

M-L1-T3d: JSON.stringify body in fetch-ai httpClient (RED spec)#129

Merged
a3ka merged 3 commits into
devfrom
feature/M-L1-T3d-body-stringify
May 7, 2026
Merged

M-L1-T3d: JSON.stringify body in fetch-ai httpClient (RED spec)#129
a3ka merged 3 commits into
devfrom
feature/M-L1-T3d-body-stringify

Conversation

@a3ka
Copy link
Copy Markdown
Contributor

@a3ka a3ka commented May 3, 2026

Why

Production bug 2026-05-03 16:39 UTC after PR #126 (T-3c schema) merged. Fetch-ai crawl: processed:0, durationMs:90 — adapter runs but yields zero records.

Root cause: wiring's httpClient.fetch wrapper passes body as JS object to Node's native fetch. Node coerces non-string body via String() → `"[object Object]"` → Agentverse rejects → silent zero.

This is the 4th iteration on fetch-ai (PRs #120 adapter, #124 wiring inject, #125 POST method, #126 schema rewrite, this PR closes wire-format layer).

Changes

  • tests/wiring-rest-adapter-httpclient.test.ts — 1 new RED test asserting native fetch receives JSON string body
  • docs/sprints/M-L1-T3d-body-stringify.md — milestone + slim spec for backend-dev

Готово когда

  • 5/5 wiring tests GREEN
  • typecheck clean
  • baseline GREEN
  • production smoke: processed > 0, ~10K agents from Agentverse

Currently

RED 1/5 (T-3d test fails on expect(typeof capturedInit.body).toBe('string')). 1-line fix in 01-registry.cjs per slim spec.

🤖 Generated with Claude Code

…y object body

Production bug discovered 2026-05-03 16:39 UTC after PR #126 (T-3c schema)
merged: fetch-ai crawl returns processed:0 with durationMs:90 — adapter
runs but yields zero records.

Root cause: wiring's httpClient.fetch wrapper for fetch-ai passes body as
JS object directly to Node's native fetch:

  fetch: async ({ url, method, body, headers }) => {
    const r = await fetch(url, { method, body, headers });  // ← body is JS object
    ...
  }

Node fetch coerces non-string non-Buffer body via String() → "[object Object]"
→ Agentverse rejects with 400 (or returns empty parsed body) → adapter's
defensive try/catch swallows result silently → processed:0.

This is the 4th iteration on fetch-ai. PRs #120 (adapter) #124 (wiring inject)
#125 (POST method) #126 (schema rewrite) each fixed one layer. T-3d closes
the wire-format layer.

RED test: 1 new test in tests/wiring-rest-adapter-httpclient.test.ts asserts
that when adapter calls httpClient.fetch with object body, the underlying
native fetch receives a JSON string (not an object). Vitest output:
1 failed (T-3d), 4 passed (existing).

Fix is 1-line in wiring/01-registry.cjs — slim spec for backend-dev in
docs/sprints/M-L1-T3d-body-stringify.md.
@a3ka a3ka added dev-ready Phase 0 APPROVED, dev can start implementation (M-Q2) spec-ready Triggers spec-review.yml fast-CI gate (M-Q2) labels May 3, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

8 Skipped Deployments
Project Deployment Actions Updated (UTC)
paxio-docs Ignored Ignored Preview May 7, 2026 11:49am
paxio-fleet Ignored Ignored Preview May 7, 2026 11:49am
paxio-intel Ignored Ignored Preview May 7, 2026 11:49am
paxio-landing Ignored Ignored Preview May 7, 2026 11:49am
paxio-pay Ignored Ignored Preview May 7, 2026 11:49am
paxio-radar Ignored Ignored Preview May 7, 2026 11:49am
paxio-registry Ignored Ignored Preview May 7, 2026 11:49am
paxio-wallet Ignored Ignored Preview May 7, 2026 11:49am

Request Review

@a3ka
Copy link
Copy Markdown
Contributor Author

a3ka commented May 3, 2026

Phase 0 SPEC APPROVED (architect self-call sub-agent)

Verdict: SPEC APPROVED.

  • Coverage: 4/4 Готово-когда criteria
  • Vacuous-skip: T-3d fails for right reason ('expected object to be string') — production bug captured
  • Coding standards walk P0→P1→P2 clean
  • Slim spec: 2-file read budget, conditional JSON.stringify preserves string/Buffer pass-through
  • Push instructions explicit per TD-dev-push policy
  • Architectural enforcement preserved (mcp/paxio-curated regression guards)
  • Baseline: 1446 GREEN, 1 RED (expected T-3d), no other regressions

Backend-dev ready to pick up. Expected outcome: 5/5 wiring tests GREEN + production smoke processed>0.

🤖 Phase 0 reviewer (sub-agent self-call by architect)

backend-dev and others added 2 commits May 3, 2026 21:26
Production bug after PR #126: adapter passes object body to wiring's
httpClient.fetch; wrapper passed it as-is to native fetch; Node coerces
object via String() → "[object Object]" → Agentverse rejects → adapter
silent processed:0.

Fix: serialize body to JSON string when it is an object before native fetch.
String/Buffer bodies pass through unchanged.

5/5 wiring tests GREEN.
Type 1 task — unit tests cover full contract; this acceptance is thin
wrapper for quality-gate.sh step 6/6 (test-runner reported missing acceptance).

5 checks:
  1. wiring calls JSON.stringify(body)
  2. conditional typeof === 'object' (preserves string/Buffer)
  3. native fetch receives serializedBody, not raw object
  4. RED test still asserts JSON-string contract
  5. vitest 5/5 GREEN

Local run: PASS=5 FAIL=0.
@a3ka a3ka merged commit acd2376 into dev May 7, 2026
20 checks passed
a3ka pushed a commit that referenced this pull request May 7, 2026
Phase N review verdict: APPROVED. 3-commit chain on feature/M-L1-T3d-body-stringify
(architect RED + backend-dev cherry-pick fix + architect acceptance).

Closes 4th and final pipeline-layer bug on M-L1 fetch-ai crawler: wiring's
httpClient.fetch wrapper passed body as raw JS object to Node's native fetch,
which coerced via String(obj) → "[object Object]" → Agentverse 400 → silent
processed:0. Surgical 8-line conditional JSON.stringify only when typeof body
=== 'object', preserves string/Buffer pass-through, mcp + paxio-curated
branches unchanged.

Quality gate: 7/7 GREEN (typecheck + node-check + vitest 1436/1436 + acceptance
5/5 + governance). Scope clean per commit, identity matches scope-guard table,
TESTS SACRED honored (backend-dev didn't touch tests), conventional commits,
production smoke ready (next 6h cron tick should yield processed > 0 with
~10K agents). Zero P0/P1/P2 blockers, zero new TD entries.

Ready for architect gate-1 merge per workflow.md::feature/* → dev autonomous gate.
a3ka pushed a commit that referenced this pull request May 7, 2026
Production bug after PR #129 merge: fetch-ai crawl returned 9226
storageErrors / 0 upserted. Root cause: pg CHECK constraints on new
taxonomy columns reject NULL; adapter doesn't populate these
(Zod-optional); upsertParams used `?? undefined` → pg coerced to NULL
→ CHECK violation on every insert.

Fix: emit SQL-DEFAULT-matching enum values for CHECK-constrained columns:
framework='unknown', wallet_status='none', payment_facilitator='unknown',
security_badge_level='none'. Replace remaining `?? undefined` with `?? null`
for non-enum fields.

28/28 postgres-storage tests GREEN.
a3ka pushed a commit that referenced this pull request May 7, 2026
Phase N review — APPROVED, no must-fix, no TD entries.

5th iteration on M-L1 fetch-ai pipeline closes the storage CHECK-violation
layer. After PR #129 (body-stringify) restored adapter to ~9226 records,
all 9226 hit storage.upsert() and pg rejected each with CHECK violation
on framework/wallet_status/payment_facilitator/security_badge_level
columns (?? undefined → NULL → CHECK fail). Fix: emit SQL-DEFAULT-matching
enum strings ('unknown'/'none') at positions \$18/\$21/\$26/\$39 + replace
remaining `?? undefined` with `?? null` (pg 8.x deprecates undefined
params).

2-commit chain on clean base (b6768f8):
  - 2fa4b85 architect — RED spec (1 test + slim spec + acceptance)
  - 00ee0cc registry-dev — impl (1 file, 22-line edit)

Reviewer worktree validation (identity=reviewer):
  - pnpm typecheck: 0 errors
  - pnpm exec vitest run: 1454 passed | 16 skipped | 5 todo (full baseline)
  - postgres-storage.test.ts target: 28/28 GREEN
  - bash scripts/verify_M-L1-T3e-storage-defaults.sh: PASS=7 FAIL=0
  - quality-gate.sh per test-runner report: 7/7 GREEN
  - git status clean
  - per-commit identity matches scope-guard table
  - tests SACRED honored (registry-dev touched 0 test files)

Standards walk:
  - P0 clean (no multi-tenancy break, no SQL injection, no any/cast)
  - P1 clean (factory frozen, domain purity preserved, AppError untouched,
    conventional commits, tests sacred)
  - P2 clean (rationale comments, consistent return shape, no dead code)

Note: acceptance script step 2 has cosmetic glitch (echoes literal `??`
producing benign `line 26: ??: command not found` warning); does NOT
affect PASS counter; architect-owned P2 cosmetic.

→ architect merges PR #131 → dev per workflow.md gate-1 autonomous.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dev-ready Phase 0 APPROVED, dev can start implementation (M-Q2) spec-ready Triggers spec-review.yml fast-CI gate (M-Q2)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant