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

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 16, 2026 6:34am

Request Review

@github-actions github-actions Bot 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 main Jul 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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants