Repository navigation
Implement AI Leadership & Intelligence protocols (Model Registry, RAG Pipeline, NLQ) - #60
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI Protocol Protocol Status Test Coverage Notes model.zod.ts (High Priority) - Effort: 3-4 days LLM configuration (OpenAI, Anthropic, Ollama, local) Vector DB configuration (Pinecone, Weaviate, ChromaDB) Natural language to ObjectQL AST transformation rules |
@copilot |
…sive tests Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
…l AI tests passing) Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
…n, Sales) Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
…feedback Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
|
This PR is very large. Consider breaking it into smaller PRs for easier review. |
|
fix ci build test lint |
@copilot |
|
This PR is very large. Consider breaking it into smaller PRs for easier review. |
…uces — retire the orchestrator, declare PluginStartupResult once (objectstack-ai#16059) (objectstack-ai#18303) Fixes objectstack-ai#16059 Executes the maintainer ruling (director seat, decision batch objectstack-ai#60, 2026-09-06, maintainer reply 「同意」): the spec keeps a startup-result contract and it describes what the kernel actually produces. The never-landed orchestrator is retired under ADR-0049 enforce-or-remove; the one shape that is real is re-declared against what `@objectstack/core` ships and core now imports it instead of declaring a twin. ## What is retired `IStartupOrchestrator` (`orchestrateStartup` / `rollback` / `checkHealth` / `startWithTimeout`) plus the three schemas it tied together — 3 emitted defs, 8 exported names across `./kernel` and `./contracts` (11 api-surface rows). | retired | why | |:--|:--| | `IStartupOrchestrator` | nothing in any repository implements it or calls it | | `StartupOptionsSchema` / `StartupOptions` / `StartupOptionsParsed` | only ever the `options` argument of `orchestrateStartup`; the kernel reads `PluginMetadata.startupTimeout` and `KernelConfig.rollbackOnFailure` | | `HealthStatusSchema` / `HealthStatus` | the return vocabulary of `checkHealth` and the value of `PluginStartupResult.health` — no probe system has ever existed behind either | | `StartupOrchestrationResultSchema` / `StartupOrchestrationResult` | the aggregate `orchestrateStartup` returned; no implementation, so no aggregate was ever built | Route 3 of the `spec-property-retirement` playbook: no authored document carried any of the three defs, so there is no seam for a D2 conversion and no author to hand a tombstone to. `RETIRED_DEFS_BY_MAJOR[18]` plus the D3 semantic entry `startup-orchestrator-retired` are the declaration, and the three `json-schema.manifest/kernel.json` keys with their 16 `authorable-surface/kernel.json` lines are deleted deliberately in this change. The build printed all three proofs: ``` ℹ️ 3 schema(s) left the published set since 827cacb, each declared (objectstack-ai#4725): - json-schema/kernel/HealthStatus.json — RETIRED_DEFS_BY_MAJOR, major 18. - json-schema/kernel/StartupOptions.json — RETIRED_DEFS_BY_MAJOR, major 18. - json-schema/kernel/StartupOrchestrationResult.json — RETIRED_DEFS_BY_MAJOR, major 18. ℹ️ 3 baseline deletion(s) since 827cacb carry their own proof (objectstack-ai#4650): - kernel/HealthStatus:* (5 line(s)) — def no longer emitted by this build; … - kernel/StartupOptions:* (6 line(s)) — … - kernel/StartupOrchestrationResult:* (5 line(s)) — … ``` ## What survives, re-declared `PluginStartupResultSchema` / `PluginStartupResult` on both entries, rewritten to the shape `ObjectKernel.startPluginWithTimeout()` has always returned: `pluginName`, `success`, optional `durationMs`, `timedOut`, and `error` as the serializable projection. `packages/core/src/plugin-loader.ts` no longer declares an interface — it re-exports the spec type, so the two cannot drift again. Three keys of the surviving def take the tombstone route rather than a bare deletion, registered in `RETIRED_KEYS_BY_MAJOR[18]`, because this def keeps emitting and its type is now imported by `@objectstack/core` — the `tsc` channel makes the prescription receivable even though nothing parses the schema: - `plugin` (a nested plugin object the kernel never built) — write `pluginName` - `health` (a `HealthStatus` nothing ever filled) — delete it - `startTime` — see below ## The one member the ruling left open, and what decided it The ruling did not settle whether core's deprecated `startTime` is mirrored or tombstoned, and neither does the playbook: `startTime` was never on the spec surface, so "retirement" was not its frame. **A repo gate decided it.** Mirroring it reds `check:duration-unit-keys` (maintainer ruling B on objectstack-ai#14478, no baseline, no allowlist): ``` [unit-in-prose-not-in-name] packages/spec/src/kernel/startup-orchestrator.zod.ts:136 `startTime` — describe names ms but the key name carries no unit. ``` Neither of that rule's two schema-declared exemptions fits: it is not an `EpochMs` instant (it holds an elapsed duration, which is exactly the confusion the rule separates) and it mirrors no external standard. The remaining options were to rename it to `startTimeMs` — minting a spelling nothing has ever produced, for a member core's own TSDoc calls "slated for removal" — or to end the ADR-0087 L1 window here. This PR ends it: the spec tombstones `startTime`, and `ObjectKernel.startPluginWithTimeout()` stops setting it.⚠️ **This is the only observable behaviour change in the diff** and the only judgement call in it. Everything else removes surfaces nothing read. If the maintainer wants the alias to live one release longer, the cheap reversal is a `startTimeMs` rename in both packages; say so and it is a one-commit follow-up. ## Evidence **Consumers, re-measured on this card** (the ruling's assertion was written 2026-09-06; the prerequisite was to re-verify it): - objectstack at `827cacbf4b`: zero files outside `packages/spec/src` name `StartupOptionsSchema`, `HealthStatusSchema` or `StartupOrchestrationResultSchema`. `IStartupOrchestrator` and `orchestrateStartup` hit 3 and 5 files — every one a released `CHANGELOG.md` or a generated artifact (`api-surface/contracts.json`, `export-origins/contracts.json`). Same-corpus controls on the same query lit up: `defineStack` 347 files, `ManifestSchema` 62, `PluginStartupResult` 15. - objectui at the pinned `.objectui-sha` `53ded82bf7`: all six symbols zero, controls `defineStack` 27 files and `ManifestSchema` 6. - `cloud` is **NOT MEASURED** — not attached to this session, so no reading exists for it. **Reverse verification** (this is a cross-package type change, so the point is proving core reads the rebuilt `.d.ts` rather than a cache). From the committed state, `health: { healthy: true, checkedAt: 1 }` was inserted into the success-path literal in `packages/core/src/kernel.ts`, proven on disk by occurrence count and by `git hash-object` differing from the HEAD blob: ``` --- marker occurrences BEFORE: 0 / AFTER MUTATION: 1 --- MUTATED LEG EXIT=1 src/kernel.ts(795,17): error TS2322: Type '{ healthy: true; checkedAt: number; }' is not assignable to type 'undefined'. --- restored blob 55b3458… == HEAD blob 55b3458… --- RESTORED LEG EXIT=0 ``` The tombstone's `never` crossed the package boundary and the restore reproduced the HEAD blob byte for byte (`git diff HEAD` empty). **Same-major bookkeeping.** The `timeout` / `timestamp` / `totalDuration` rename registrations that objectstack-ai#15678 and objectstack-ai#15676 landed in this same unreleased major 18 are KEPT, not absorbed. The playbook's absorption clause is motivated by the conversion table's fixture-disjointness contract, and those three entries record "No D2 conversion" themselves, so that mechanism does not exist here. The tree's own answer agrees: 14 `RETIRED_KEYS_BY_MAJOR[18]` entries already name keys whose def is retired in the same major 18 (the objectstack-ai#15513 incident-response / training / change-management family). Gate (b2) states this steady state explicitly — "an entry naming a key the build no longer emits at all is NOT an error". ## Acceptance notes - The generated page keeps the frontmatter `title: Startup Orchestrator` / `description: Startup Orchestrator protocol schemas`. Those two strings are derived from the module FILENAME by `build-docs.ts`; the page BODY is rewritten and now describes the result contract. Renaming `startup-orchestrator.zod.ts` would move the docs route, the `export-origins` paths and the pin file's module slot, and the ruling names the file to edit rather than to move — so it is noted here, not done. - `content/docs/references/index.mdx` moves 1525 → 1522 schemas and the Kernel row 162 → 159 — the whole-def-removal reading the playbook's ratchet-visibility table predicts, and the counter-evidence to a zero-delta run. - `packages/spec/src/type-alias-convention.pin.test.ts` drops `Iso467` / `Iso469` with their schemas (785 → 783 pins); `Iso468` stays, still isomorphic — every new member is `.optional()` with no default and no transform. --- _Generated by [Claude Code](https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…e the 27 signature hashes (objectstack-ai#18971) Fixes objectstack-ai#16045 Clause-②: yes (widening) Ruled at `5560224701` (director batch objectstack-ai#60, 2026-09-06, maintainer verbatim 「同意」), re-affirmed by triage at `5724532096`: option A, a readable declaration-text snapshot, ⛔ not a hash. The card body's three mutually exclusive routes predate that ruling and were not re-litigated here. `@objectstack/spec` pinned its public surface on one axis. `api-surface/` records each export as `name (kind)`, and a signature change, a renamed interface field and a dropped union member move **none** of those rows. The only shape pin was `api-surface-signatures.json`: 27 rows, and reference-level even there. This adds `api-surface-declarations/`, the declaration text the packed build actually emits for every export of every published entry point, and retires the 27 hashes it subsumes. ## The counts, re-derived on this head before the first generation The ruling asks for this by name; the card's own numbers were self-declared unverified and 12 days old. | Number | Card | This head (`b33898f5d`) | Unit, and what would make it something else | |---|---|---|---| | entry points | 17 | **17** | type entry points in the `exports` map — those whose `require.types` ends in `.d.ts`. Adding or removing one such subpath. | | `exports` map entries | (not stated) | **19** | every key in the map. The extra two are `./openapi.json` and `./package.json` — asset subpaths with no declaration at all, filtered out by the same `.d.ts` test `build-api-surface.ts` has always applied. ⇒ premise 1 resolved: **17 is right and the map did not grow**; 19 counts two things that were never entry points. | | pinned rows | 5309 | **5336** | `name (kind)` rows summed over the 17 `api-surface/` shards. +27 since the card. Ratio unmoved: 27/5336 = 0.51%, so the headline 99.5% stands. | | distinct exported names | (not stated) | **5200** | (entry, name) pairs. The gap to 5336 is dual-declared names, which are two rows by design. | | signature hashes | 27 | **27** | top-level keys of `api-surface-signatures.json`. Bright control: the first value really is a `sha256:` string, so this counts signature entries and not empty objects. | Premise 3 also holds: all 17 packed `.d.ts` files exist and resolve through the map (3,215,437 bytes for the root entry down to 13,081 for `./integration`). No entry point lacks a packed declaration, so the gap the dispatch reserved for itself did not open. ## What the artefact costs — premise 4, which nobody had costed | | | |---|---| | shards | 17, one per entry point | | declaration blocks | 5336 | | bytes | **12,661,943 (12.08 MiB)** | | lines | **237,706** | | gzipped | **1,071,825 (1.02 MiB)** — against this package's ~17.57 MiB compressed `dist`, so about **+5.8%** of tarball | | largest shard | `system.txt`, 3,592,701 bytes / 73,283 lines | | median declaration | **81 bytes** | | skew | the 20 largest declarations hold **~65%** of all bytes; four exceed 20,000 lines each (`EnvironmentArtifactSchema` 21,868, `ObjectStackDefinitionSchema` and `ObjectStackSchema` 21,851, `ChangeSetSchema` 20,395) | Stated plainly, as the dispatch asks, and ⛔ not as a veto: the packed `.d.ts` is a tsup dts rollup, so a Zod schema's declaration is its **fully expanded** structural type. That expansion is exactly what makes an inner field rename visible — and it is also why a single schema can produce a 21,000-line diff. The ruling's stated reason for choosing text over a hash is that the contract-review seat reads the diff; that reasoning holds per declaration and is worth a second look at the top twenty. One reading, for whoever wants it: 31% of declarations hold 97.7% of the bytes, so nothing cheap is available by trimming the tail. ## Both instruments, measured on one tree at one commit The card's thesis is that the old pin cannot fail on a shape change. Not argued — ablated, with the mutation proven on disk by blob hash and the mutation proven to have reached `dist/` before any verdict was read. **A. the source-level control — a renamed interface field, the card's own class.** `JobRunOutcome.reason?` renamed to `degradationReason?` in `packages/spec/src/contracts/job-service.ts` (blob `363443e2` to `d4b1520c`), spec rebuilt, `ablation-dist-preflight` exit 0 confirming the marker reached the built artefact: ``` check:api-surface exit=0 "public API surface unchanged" [BLIND] check:api-surface-declarations exit=1 "~ JobRunOutcome (interface)" [SEES IT] ``` Restore leg: blob back to `363443e2`, rebuilt, `ablation-dist-preflight --absent` exit 0 (marker gone from all 214 built files), `git diff HEAD` clean, gate back to exit 0. **B. the gate can fail on its own artefact.** One field renamed inside `qa.txt` by hand (blob `3f5efb04` to `5b86fec2`, injected occurrences 1, deleted text 0): exit **1**, attributed to `TestSuiteSchema (const)`, failure text naming the regenerate command. Restored to the HEAD blob, `git diff HEAD` empty: exit **0**. ## The retirement, and the coverage proof the ruling demands All **27** signature names resolve to a declaration block in `api-surface-declarations/root.txt`, **0 missing** — enumerated from `defineAction` through `defineWebhook`, each as `(function)`. One honest qualification, because the subsumption is not uniform. For those 27 factory declarations the text is `declare function defineAction(config: z.input of ActionSchema): ActionParsed;` — a type **reference**, exactly as blind to an inner-key narrowing as `typeToString` was. What is gained is not sharper text on the 27; it is the **5309 other declarations**, including `ActionSchema` itself, whose own expanded block is where such a narrowing shows up. So the retirement is a strict superset of pinned declarations, not an equal trade. Nothing published read the retired file — it was never in this package's `files[]`. ## Where it lands, and why there - Generator: `packages/spec/scripts/build-api-surface-declarations.ts`, beside the eight sibling artefact generators, reading the same input through the same `collectEntries` logic. The ruling says "one generator script under `scripts/`"; this reads that as the directory the whole family lives in, because the artefact reads the **built dist** and only the lane that builds spec can run its gate. - Artefact: `packages/spec/api-surface-declarations/ENTRY.txt`, a sibling **directory** of `api-surface/`. Not inside it: `listShardNames` throws on any file in that directory that is not a `NAME.json` shard, so `api-surface/` is closed by construction. No existing `api-surface/*.json` is regenerated by this PR (`check:api-surface` green throughout), which keeps it clear of PR objectstack-ai#18688 and PR objectstack-ai#18319. - Gate: `check:api-surface-declarations`, a step in lint.yml's `Type Check · consumer gates` lane after the two build steps, with `check:api-surface` and the other dist-reading gates. **No new required context** — a step in an existing lane. Registered in the `check:generated` ledger, in `REGEN_ARTIFACTS`, and in `.gitattributes` as `merge=os-regen`. - Sharded per entry point from day one, for the reason its neighbour is: the merge queue rebuilds server-side where no custom driver runs, so two PRs sharing one generated file evict the second. Pit 1 from `5715457322` is answered by the layout rather than by an assumption — and `check:merge-driver`, which reconciles `.gitattributes` against `REGEN_ARTIFACTS` in both directions, is green over the swap. - Published, with the reason the gate demands. `check:published-files` refuses a `files[]` entry that carries none; the registered line says what a consumer does with it — read two published tarballs and see *which declared shape* moved between releases, the question `api-surface` cannot answer. If 1.02 MiB of tarball is judged too much, one line of `files[]` removes it without touching anything else. Three registries had to learn about the new gate, each because it discovered the gate on its own rather than because a list named it: - `check:published-files` — demanded the reason above. - `scripts/pm/dispatch-gates.mjs` — its live manifest edge gave the new gate a population before anything listed it, which is the eighth member of a class whose seventh was recorded the same way. Declared as `CLASS_EIGHTH`, with a case asserting the edge really reaches it. - `scripts/pm/check-widening-tells.mjs` — `PUBLISHED_SURFACES` is derived from `REGEN_ARTIFACTS`, so retiring the signatures row dropped it off that surface and reddened two self-test cases. Both are retargeted to state the retirement as a counterfactual (the surface follows the table, not a literal); ⛔ the new artefact is **not** added to that surface, because the ruling assigns "is a snapshot diff a Clause-② signal" to the skills seat by name and out of this card's scope. Both directions are now pinned, so the boundary is declared rather than forgotten. 483 cases pass, up from 481. ## Verification - **Gate families**: derived with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` from the merge base, 120 commands, every exit code redirected to a file and read back. **All 120 green.** Four returned exit **3** PREREQUISITE NOT MET on first pass (`check:doc-formula-expressions`, `check:dual-build-cjs-loads`, `check:lean-entry-closure`, `check:type-check-debt`); each names a build, each was built and re-run green, and none is recorded as a finding. Reconciled with `--ran`. - **Tests**: `@objectstack/spec` local project **488 files / 14,182 tests passed**; the tooling suites that name the edited scripts, both projects, **10 files / 220 tests passed** (`sharded-artifacts`, `check-generated-ledger`, `dist-freshness`, `dist-freshness-adoption`, `api-surface-dual-kind-rows.pin`, `build-schemas-check-mode`, `def-key-collisions`, `root-index`, `export-list`, `docs-import-surface`). `pnpm --filter @objectstack/spec typecheck` green. - **eslint, the union rather than a narrowing**: `eslint . --no-inline-config --format json` at `b33898f5d` examined **6856 files**, **0 errors, 0 warnings**, exit 0. The population is eslint's own config resolution and the count is read from its JSON output; type-aware linting is not enabled in `eslint.config.mjs` (no `parserOptions.project`, no typed rules), so this diff cannot move an untouched file's verdict either way. - **Control bytes**: `check:nul-bytes` green over 8906 files, plus a direct scan of all 31 changed paths for the wider control-byte class — no matches. - `scripts/check-single-claim-paths.mjs` in the diffstat is **not mine**: it arrived with the one-commit `origin/main` merge (`16cb493d5`) this PR carries. ## Acceptance notes - `.claude/skills/spec-property-retirement/SKILL.md` line 124 lists `api-surface-signatures` as an instance of a retirement shape, and that row goes stale with this landing. ⛔ Left untouched on purpose: `.claude/**` is a governed surface, so editing it would make this whole PR maintainer-landed for a one-word prose nit. Noted, not filed. - `packages/spec/scripts/build-schemas.ts` line 830 carries the same stale mention. Left untouched because PR objectstack-ai#18952 holds that file; noted, not filed, with the later lander as the natural carrier. - Three files in this diff are held by open PRs and were edited anyway because the retirement forces it, not by choice: `scripts/pm/check-widening-tells.mjs` (PR objectstack-ai#18948), `scripts/pm/dispatch-gates.mjs` (PR objectstack-ai#18903) and `.github/workflows/lint.yml` (PRs objectstack-ai#18946, objectstack-ai#18889, objectstack-ai#18414). All are hand-written files where a text conflict is visible rather than silent, and all three of my hunks are small and far from theirs. Whoever lands second resolves. - The top-20 skew above is a reading, not a finding: no gate is wrong and nothing is unenforced. It is recorded here because the ruling's own justification for text over hash is per-declaration readability, and at 21,000 lines a declaration that argument thins out. ## 维护者速读(草稿) **改了什么。** `@objectstack/spec` 从今天起为它的**每一个**公开导出留一份"形状快照" —— 不是哈希,而是打包后 `.d.ts` 里那段声明原文,按入口点分成 17 个文件签入仓库,并配一道 CI 闸门:重新生成后对不上就红,失败信息里直接给出重新生成的命令。同时退休了旧的 27 条签名哈希文件。 **为什么改。** 原来的 pin 只记"某个名字还在不在",5336 行里只有 27 行能看出"形状变没变"。也就是说:把一个接口字段改名、砍掉一个联合成员、改一个函数签名 —— 这些都是会让客户升级后编译失败的破坏性改动 —— 全部一路绿灯。本次 PR 里有实测:改了 `JobRunOutcome` 的一个字段名之后,旧闸门 `check:api-surface` 退出码 **0**(看不见),新闸门退出码 **1**(点名了那个 interface)。路线是 2026-09-06 决策批次 objectstack-ai#60 里您逐字「同意」的那一条。 **风险与代价(含回滚)。** 代价是体积:12.08 MiB 文本、23.7 万行,压缩后 1.02 MiB,相当于 npm 包增长约 5.8%。更值得注意的是分布极不均匀 —— 最大的 4 个 schema 各自超过 2 万行声明文本,一旦它们变动,复核席位面对的是一份 2 万行的 diff;而裁决选"文本不选哈希"的理由恰恰是"diff 可读"。这一点我按实测如实报告,未自行改动路线。回滚成本很低:从 `files[]` 去掉一行即可停止随包发布;整道闸门回滚就是撤销本 PR,不留任何数据迁移。 **席位意见。** **你要做的。** 只有一件事需要您判断:12 MiB / 23.7 万行这个量级,以及最大 4 个 schema 的 diff 可读性,是否仍符合当初选 A 方案时的预期。若认为需要收窄,那是裁决层面的一次增补,不是本 PR 的返工。其余部分已按裁决落地并自证。 --- _Generated by [Claude Code](https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Q2 2026: AI Leadership & Intelligence - COMPLETE ✅
Phase 1: Core AI Protocols ✅ COMPLETE
AI Model Registry Protocol (
src/ai/model-registry.zod.ts)RAG Pipeline Protocol (
src/ai/rag-pipeline.zod.ts)Natural Language Query Protocol (
src/ai/nlq.zod.ts)Updated exports in
src/index.tsTotal: 102 AI tests passing (20 Agent + 25 Model Registry + 32 RAG + 25 NLQ)
All 766 tests passing across entire spec package
Phase 2: AI-Powered Example Apps ✅ COMPLETE (4/4)
1. AI Support Assistant (
examples/ai-support) ⭐Complete RAG-powered customer support system
Features:
2. AI Data Analyst (
examples/ai-analyst) ⭐Natural language business intelligence
Features:
3. AI Code Generator (
examples/ai-codegen) ⭐Generate ObjectStack apps from natural language
Features:
4. AI Sales Assistant (
examples/ai-sales) ⭐Intelligent sales automation
Features:
Phase 3: Documentation & Integration Guides ✅ COMPLETE
docs/AI_INTEGRATION_GUIDE.md)CI/Build Fixes ✅ COMPLETE
FieldMappingSchematoNLQFieldMappingSchemain NLQ protocol.optional().default())Success Criteria Validation ✅ ALL MET
ai-codegenexample with GPT-4 Turboai-supportwith Pinecone vector DB, MMR retrieval, rerankingai-analystexampleSecurity Summary ✅
✅ No vulnerabilities found (CodeQL scan completed)
Technical Highlights
Protocol Statistics:
Supported Technologies:
Code Quality
✅ All code review feedback addressed
✅ All tests passing: 766/766
✅ Build successful
✅ Type checking passing
✅ No breaking changes - All additions are opt-in
Migration Guide
No migration needed - All new protocols are additive.
To use the new AI features:
Deliverables: 5/5 Complete
Q2 2026 Milestone: COMPLETE 🎉
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.