Skip to content

ADR-0076 剩余工作的 framework 侧切片:D12 诚实能力 + A1.5 消费方收窄 + D7 指标追踪 (#2462) - #3028

Merged
os-zhuang merged 3 commits into
mainfrom
claude/objectql-protocol-layering-s2sn67
Jul 16, 2026
Merged

ADR-0076 剩余工作的 framework 侧切片:D12 诚实能力 + A1.5 消费方收窄 + D7 指标追踪 (#2462)#3028
os-zhuang merged 3 commits into
mainfrom
claude/objectql-protocol-layering-s2sn67

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

落地 #2462不依赖跨仓库窗口、可立即执行的 framework 侧切片。跨仓库项(D10、D11、Step 2,以及 console 侧消费方更新)仍留在协调窗口内,本 PR 不触碰。

D12 — 诚实能力(framework 侧,含 issue 评论中的 /realtime 具体实例)

根因:两个 discovery 构建器(HttpDispatcher.getDiscoveryInfo 与 metadata-protocol shim 的 getDiscovery)对任何已注册服务一律硬编码 { status: 'available', handlerReady: true },stub / dev 假服务 / fallback 全部谎报为真——AI agent 与 console 因此被误导。

  • spec:标准化自描述标记(解决 OQ#11)——SERVICE_SELF_INFO_KEY(__serviceInfo)、ServiceSelfInfoSchemareadServiceSelfInfo();plugin-dev 既有的 _dev: true 被规范化为 { status: 'stub', handlerReady: false },无需改动 plugin-dev。discovery schema 早已含 stub 状态值,无 schema 变更。
  • runtime + metadata-protocol:两个 discovery 构建器都改为尊重该标记——stub 报 stub、fallback 报 degraded,绝不报 available
  • objectql:轻量 analytics fallback 自标 degraded(继续服务,无 /analytics 404 回归;service-analytics 通过 replaceService 替换后无标记,如实报 available)。
  • realtime 实例:discovery 不再广告 /realtime 路由与 websockets: true——service-realtime 是进程内 pub/sub,任何地方都没有挂载 /realtime HTTP 面,广告的路由必然 404。已注册的服务如实报 degraded + handlerReady: false;provider 由不存在的 plugin-realtime 更正为 service-realtime。客户端 SDK 不受影响(getRoute 回落到约定路径,行为与之前完全一致)。
  • docs:realtime-protocol.mdx / http-protocol.mdx 中的 discovery 示例同步为诚实输出。

console 侧的"只信 handlerReady: true / available"消费方更新仍按 issue 约定留在跨仓库窗口——本切片刻意只选了不会让 console 回归的子集:dev stub 仅存在于 dev 环境;analytics fallback 报 degradedenabled/handlerReady 均为 true(与生产环境中 services.metadata 长期报 degraded 的既有先例一致);realtime 路由本来就 404。

A1.5 — 按 D9 子接口收窄消费方(issue 中标注"任意时间、低风险")

全仓扫描后,真正可收窄的类型标注只有 REST 层(其余引用要么是实现类、要么是注释)。新增 RestProtocol = DataProtocol & MetadataProtocol 并贯穿 RestServer / rest-api-plugin(服务端扩展方法本就走运行时特性探测的 cast,不约束该类型)。纯类型层变更。

D7 — 引擎拆库触发指标进 CI

scripts/check-engine-split-ratio.mjs + 每周 workflow(报告模式,写入 step summary;OQ#5 定阈值后可加 --threshold 门禁)。当前读数:近 30 天 100% 跨包——D7 继续搁置,且现在有了持续数据。

验证

  • 全 monorepo pnpm test:126/126 任务通过(其中一次运行 plugin-dev 偶发失败,纯 mock 合同测试、与本 diff 无关,单独及后续全量运行均通过)。
  • check:api-surface:5 项新增、0 破坏,快照已重新生成。
  • 端到端:app-crm --fresh 实机启动验证——--preset minimal 下 analytics 如实报 degraded + 诚实提示;默认 preset(真 service-analytics)报 available;两种情况下 routes.realtime 均不出现,services.realtime 的提示已指向 service-realtime
  • 新增测试:spec 8 个(marker 读取语义)、dispatcher 5 个、protocol shim 3 个;并修正了钉死旧谎报行为的断言。

Closes 无(#2462 仍为跟踪 issue;跨仓库项未完成)。

🤖 Generated with Claude Code

https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu


Generated by Claude Code

claude added 3 commits July 16, 2026 06:29
… MetadataProtocol (ADR-0076 D9/A1.5, #2462)
The REST layer was typed against the full ObjectStackProtocol god-union
while actually calling only the data CRUD + metadata control-plane slices
(server-only extensions are feature-detected via casts). Introduce
RestProtocol = DataProtocol & MetadataProtocol, thread it through
RestServer / rest-api-plugin, and export it from the package barrel, per
the D9 incremental narrowing guidance. Type-level only.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
…e route honesty (ADR-0076 D12 framework slice, #2462)
Root cause (D12): both discovery builders hardcoded {status:'available',
handlerReady:true} for every registered service, so stubs, dev fakes and
fallbacks reported as fully real — agents and the console were misled.
- spec: standardize the self-description marker (resolves OQ#11):
SERVICE_SELF_INFO_KEY ('__serviceInfo') + ServiceSelfInfoSchema +
readServiceSelfInfo(), which also normalizes plugin-dev's legacy
_dev:true to {status:'stub', handlerReady:false}.
- runtime + metadata-protocol: getDiscoveryInfo()/getDiscovery() honor the
marker — stubs report 'stub', fallbacks 'degraded', never 'available'.
- objectql: the deliberate lightweight analytics fallback self-identifies
as degraded (it keeps serving — no /analytics 404; service-analytics
replaces it via replaceService and reports 'available' untouched).
- realtime (concrete D12 instance from #2462): discovery no longer
advertises a /realtime route or websockets:true — service-realtime is an
in-process pub/sub bus, nothing mounts /realtime, so the advertised
route always 404'd. The registered service reports degraded with
handlerReady:false; provider corrected from the nonexistent
'plugin-realtime' to 'service-realtime'. The client SDK is unaffected
(it falls back to the conventional path, which 404s exactly as before).
- docs: update the discovery examples in realtime-protocol.mdx /
http-protocol.mdx to the honest output.
Verified end-to-end: fresh app-crm boot with --preset minimal reports
analytics degraded with the honest message; default preset (real
service-analytics) reports available; no routes.realtime in either.
The console-side consumer update (trust only handlerReady/available)
stays in the coordinated cross-repo window per the issue.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
D7 (extracting the ObjectQL engine into its own repo) is gated on the
cross-package commit ratio of engine.ts/registry.ts falling from ~88% to
a low, stable level — but nothing measured it. Add
scripts/check-engine-split-ratio.mjs (report-only by default; optional
--threshold gate once OQ#5 is decided) and a weekly workflow that writes
the ratio to the run's step summary. Current reading: 100% over the last
30 days — D7 stays firmly deferred.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1BtxNeUaGmvUYr8FwtUNu
@vercel

vercelBot commented Jul 16, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 16, 2026 6:34am

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation ci/cd tests tooling size/l labels Jul 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 6 package(s): @objectstack/metadata-protocol, @objectstack/objectql, @objectstack/plugin-hono-server, @objectstack/rest, @objectstack/runtime, @objectstack/spec.

107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/rest, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/rest, @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/rest, @objectstack/runtime, @objectstack/spec)
  • content/docs/api/wire-format.mdx(via @objectstack/runtime)
  • content/docs/automation/approvals.mdx(via packages/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via @objectstack/runtime, packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via @objectstack/metadata-protocol, @objectstack/objectql, packages/spec)
  • content/docs/concepts/north-star.mdx(via packages/runtime, packages/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via packages/objectql, @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/index.mdx(via @objectstack/runtime)
  • content/docs/deployment/migration-from-objectql.mdx(via @objectstack/objectql)
  • content/docs/deployment/production-readiness.mdx(via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx(via @objectstack/runtime)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/vercel.mdx(via @objectstack/objectql, @objectstack/runtime)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/cli.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/plugin-hono-server, @objectstack/runtime, @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via packages/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via packages/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx(via @objectstack/objectql, @objectstack/plugin-hono-server, @objectstack/runtime)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/objectql, @objectstack/plugin-hono-server, @objectstack/rest, @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/objectql, @objectstack/plugin-hono-server, @objectstack/rest, @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/runtime)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/objectql, @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx(via @objectstack/objectql, @objectstack/plugin-hono-server, @objectstack/rest, @objectstack/runtime, @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/rest, @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/objectql, @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 16, 2026 07:14
@os-zhuang
os-zhuang merged commit 9ea3c93 into mainJul 16, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the claude/objectql-protocol-layering-s2sn67 branch July 16, 2026 07:14
os-zhuang pushed a commit that referenced this pull request Jul 16, 2026
…echeck 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
os-zhuang added a commit that referenced this pull request Jul 16, 2026
…结论 (#2462) (#3037)
* 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
---------
Co-authored-by: Claude <noreply@anthropic.com>
os-zhuang added a commit that referenced this pull request Jul 16, 2026
* 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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cddocumentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude