Commit 5725b52
* feat(transport): prove the IHttpServer port is Hono-free — second adapter on raw node:http (ADR-0076 D11/OQ#10, #2462)
Multi-adapter was designed but unproven: only the Hono adapter existed, and
ADR-0076 D11 flagged 'the normalized context shows Hono-isms' as a caveat
blocking the transport decomposition. This adds the validation:
- New @objectstack/plugin-node-server: a thin IHttpServer implementation on
raw node:http with ZERO dependencies beyond @objectstack/core — :param +
trailing-* routing (registration-order first-match, same as the primary
adapter), eager JSON/urlencoded body parse with lazy rawBody() for binary,
SSE via native res.write/end, 404/405-with-Allow semantics, EADDRINUSE
retry, graceful drain on close. Deliberately no getRawApp()/mount().
- Cross-adapter conformance suite: boots the dispatcher bridge AND the REST
route generator (with ObjectQL + memory driver) on BOTH adapters over real
sockets — /data CRUD roundtrip, /meta reads, /ready, /health, discovery,
:param routing, 404/405 parity, plus a probe-for-probe response-shape
parity matrix between the two adapters. 40 assertions, all green.
Findings recorded in ADR-0076 (OQ#9/OQ#10/OQ#11 resolution notes):
- The port has NO hard Hono-isms. The Host-header backfill in the Hono
adapter is a Fetch-API artifact local to that adapter, not a port leak.
- All remaining Hono coupling is confined to the feature-detected
getRawApp() escape hatch (metadata HMR, cloud-connection routes,
static/SPA + CORS + Server-Timing) which degrades gracefully.
- Follow-up for the D11 window: codify res.write/res.end (SSE) and
getPort() — extensions consumers already rely on — plus 404/405
semantics into the IHttpServer contract.
- OQ#9 audit verdict (delineate, don't merge; only discovery + packages
are genuinely double-mounted) recorded in the ADR open-questions list.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
* fix(plugin-node-server): harden query/params against prototype pollution (CodeQL)
CodeQL flagged remote property injection: query-param keys come straight
from the request URL, so building the map on a plain object literal let
'?__proto__=…' write through the prototype chain. Use null-prototype
objects for query and params and drop the dangerous keys
(__proto__/constructor/prototype) outright; regression test pins that
'?__proto__=polluted' is dropped and Object.prototype stays clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
* fix(plugin-node-server): eliminate the dynamic property-write sink CodeQL flags
The blacklist guard added in the previous commit was not recognized as a
sanitizer by CodeQL's js/remote-property-injection rule — the sink is the
computed write itself (obj[userKey] = …). Build query and params via
Object.fromEntries (own data properties only, no prototype-chain walk) so
the sink no longer exists; the __proto__/constructor/prototype drop stays
as defense in depth. Regression test unchanged and green (41/41).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
* refactor(qa): reposition the port-conformance adapter as a private QA gate — packages/qa/http-conformance
Maintainer review: the node:http adapter is a validation instrument, not a
product server, so it must not ship as a publishable plugin- package.
Reposition it accordingly:
- Move packages/plugins/plugin-node-server → packages/qa/http-conformance;
rename @objectstack/plugin-node-server → @objectstack/http-conformance,
private: true, test-only (no build/publish), matching the existing
verification-gate packages (dogfood, downstream-contract).
- packages/qa/ becomes the unified home for non-published verification
gates (new pnpm-workspace glob); migrating dogfood/downstream-contract
there is proposed as a follow-up.
- Drop the changeset + fixed-group entry (private packages are not
versioned); update the ADR-0076 OQ#10 note to the new name.
Behavior unchanged: same adapter, same 41-assertion cross-adapter
conformance suite, all green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
* refactor(qa): migrate dogfood + downstream-contract into packages/qa/ (unified verification-gate home)
Completes the packages/qa/ consolidation started with http-conformance:
all three non-published verification gates (dogfood regression gate,
downstream-contract compatibility gate, http-conformance port gate) now
live under one directory.
Mechanical path migration — no behavior change:
- git mv packages/{dogfood,downstream-contract} → packages/qa/
- rewrite every 'packages/dogfood' / 'packages/downstream-contract'
breadcrumb: spec liveness proof registry + JSON ledgers,
spec-liveness-check.yml path trigger, eslint ignore, docs/ADRs,
source comments
- bump the repo-root traversal one level in the four dogfood conformance
tests that resolve REPO_ROOT from __dirname
Verified from the new locations: dogfood 273 passed / 3 skipped,
downstream-contract typecheck + 14 tests, spec check:liveness resolves
all bound proofs, http-conformance 41/41.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
* chore: retrigger CI — Actions did not fire on dc6507c
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
* docs(changeset): fix dogfood path breadcrumb after packages/qa migration
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
* fix(ci): build the examples' dependency closure explicitly in the typecheck job + correct stale ADR-0076 D12 status
Two follow-ups after merging latest main:
- lint.yml: the example-app typecheck step built only ./packages/* (direct
children). The connector packages the showcase imports were built purely
by accident — through dogfood's dependency chain — which broke when
dogfood moved to packages/qa/. Request the examples' dependency closure
explicitly (--filter='./examples/*^...') so the step no longer depends
on which package happens to live at the top level.
- ADR-0076: the round-2 status verification merged from main (#3061) was
read against a pre-#3028 snapshot — svcAvailable no longer hardcodes
'available'; the D12 framework side shipped in #3028. Correct the status
line and note OQ#9/OQ#10 resolution (#3037).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
* fix(ci): cap turbo test concurrency at the runner's core count (OOM mitigation)
Test Core started failing deterministically on the hosted runner —
plugin-audit#test dies with ZERO vitest output (kernel OOM-kill
signature) while passing locally on Node 20 and 22 — first on main
@4f8c2d1, then twice on this PR (initial run + job re-run). Onset
correlates with the task graph growing past ~100 tasks (connector
packages + qa gates): turbo's default concurrency (10) schedules that
many vitest workers + tsup DTS builds onto a 4-vCPU runner and peak
memory tips over.
Cap test-step concurrency at 4 (PR affected-tests + push full-run).
CPU-bound workload, so wall-clock cost is minimal; peak memory becomes
bounded.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
* fix(qa): bump repo-relative path in the newly-merged declarative-mcp dogfood test + regen lockfile after merge
showcase-declarative-mcp.dogfood.test.ts landed on main (#3062) against
the old packages/dogfood location; after the packages/qa migration its
'../../../examples/app-showcase' URL resolved inside packages/. One more
level, same as the other conformance tests. Lockfile regenerated after
taking main's side in the merge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
* ci: TEMPORARY #3071 diagnostics — isolated verbose plugin-audit runs + dmesg OOM probe
plugin-audit#test dies on the hosted runner with zero captured output
(3x deterministic since main@4f8c2d1) while passing locally under Node
20/22 with the exact CI command. Bracket the turbo test step with an
isolated, verbose, ungrouped vitest run (before: fresh machine; after:
post-graph machine + kernel OOM traces) so the real failure output cannot
be lost to the GH log pipeline. SIGQUIT-on-timeout dumps Node thread
stacks if it hangs. To be REMOVED once #3071's root cause is fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent aeb2110 commit 5725b52
1 file changed
Lines changed: 29 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
118 | 134 | | |
119 | 135 | | |
120 | 136 | | |
121 | 137 | | |
122 | 138 | | |
123 | 139 | | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
124 | 153 | | |
125 | 154 | | |
126 | 155 | | |
| |||
0 commit comments