skills(ai): optimization flight — the closed agent surface cut to its retirement rows, the flagship defineSkill example made to resolve, open-edition MCP wiring and the tool registry taught (net −1,315 tokens) - #14463
Open
os-litant wants to merge 4 commits into
Conversation
…esolve, teach open-edition MCP Restructures skills/objectstack-ai/SKILL.md against the read-only audit: the package spent 26% of its budget teaching the agent surface it declares closed to its own readers, its flagship defineSkill example named four tools that resolve to nothing, and the one AI path that executes without a cloud licence (MCP) had zero coverage. Deletions (each one a construct the reader can still reach): - AI-B-02 agent Required/Optional/Example -> two retirement rows plus a pointer to references/_index.md. - AI-C-01 the "runtime is cloud/EE, open is MCP" fact, restated 8x -> one anchor blockquote defining a bare cloud marker used on the affected headings. - AI-D-03 Structured Output (an agent-only field the file itself calls "declared only"), AI-D-01 "Why Three Tiers?", AI-D-02 competitive positioning, AI-B-01 "When to Use This Skill", AI-C-02 the cloud ops callout (ai_usage_daily has no open-repo object), AI-D-04 the vendor model catalogue, AI-D-05 knowledge best-practices (merged into a hygiene column), AI-D-06 generic prompt-engineering pitfalls, AI-F-04 the defineTool section demoted to a not-the-default-path note. Rewrites: - AI-E-01 the flagship defineSkill example now names query_records, get_record and action_escalate_case, all three of which resolve on the ladder in validate-ai-tool-references.ts:148-171. - AI-A-02 the Model Selection / Temperature taste tables are replaced by the enforced contract (temperature min 0 max 2, agent.zod.ts:38). - Falsehood 1: agent.tools is a retiredKey() tombstone (parse error), not "supported but legacy". - Falsehood 2: the CEL example pointed at a tool availability condition that ToolSchema does not declare; the AI-domain CEL carrier is a model-registry promptTemplate. - AI-G-03 outputSchema is flagged experimental, matching how the file already flags every other declared-only field. Additions, each paid by a deletion in the same file: - Open-edition MCP wiring: MCPServerPlugin, POST /api/v1/mcp, and the tool names a client actually sees, read from packages/mcp/src. - The skill.tools[] resolution ladder and the 30-name platform tool registry. - The trigger-condition operator/value-shape column (a mismatch is a parse error, skill.zod.ts:37-51,139-202). - The KnowledgeServicePlugin wiring the RAG example could not be run without. AI-B-03 reorder: the first customer-authorable section is now first; the closed agent surface is a reference section at the bottom. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Generator output only (`pnpm --filter @objectstack/spec gen:skill-docs`) after the AI-A-01 frontmatter edit. No hand edit to skills/README.md prose. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…LL.md Mechanical: `node scripts/check-role-word.mjs --update`. The rewrite dropped the file's role-word count 5 -> 1, and the gate's ratchet-DOWN remedy is the author's own. Only that one line changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…deprecated alias Contract review round 1, FAIL 1. The MCP section taught `OS_MCP_SERVER_ENABLED=true` as the way to start the long-lived stdio transport. That is the LEGACY trigger: `resolveMcpStdioAutoStart()` (packages/types/src/env.ts:333-345) reads `OS_MCP_STDIO_ENABLED` first and returns it clean, while the old var returns `viaDeprecatedAlias: true`, on which `MCPServerPlugin.start()` (packages/mcp/src/plugin.ts:234-239) logs "Starting the stdio transport via OS_MCP_SERVER_ENABLED=true is DEPRECATED". An author copying the sentence shipped a boot-time warning. Now: `autoStart: true`, or `OS_MCP_STDIO_ENABLED=true`, with one clause noting the old var still starts it and warns. Also states that stdio defaults off, which the previous wording only implied. Paid in-file, not by re-wrap: the section's closing sentence restated the `instructions` row of the Skill Configuration table (that `@objectstack/mcp` projects `instructions` onto MCP prompts) and is deleted under AI-C-01's own rule. 5,481 -> 5,476 tokens. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
os-zhuang
approved these changes
Sep 2, 2026
os-zhuang
marked this pull request as ready for review
September 2, 2026 12:02
os-zhuang
enabled auto-merge
September 2, 2026 12:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #14305
Skills catalog optimization program #14292 (maintainer mandate 2026-09-02). Not a closing
reference: AI-H-01 (the
evals/README.mdstub) is deferred to #14296 item 2, so the cardstays open.
Transliteration key — the GitHub body sanitizer eats angle-bracket-shaped fragments, so
two are spelled without them below:
action_NAME= theaction_prefix plus anangle-bracketed
nameplaceholder (rung 3 of the tool-resolution ladder), andos-check-marker= the HTML-comment fence markercheck-skill-examples.tskeys on.Per-file token delta
skills/objectstack-ai/SKILL.mdskills/README.mdcontent/docs/ai/skills-reference.mdxscripts/role-word-baseline.jsonLines 592 → 419.
evals/README.md(315/315) and the generatedreferences/_index.mduntouched; no ceiling raised; no new file;
scripts/check-skills-token-ratchet.mjsuntouched.落点 | before | after
description:3-10packages/spec:475-491→ Model Configurationtemperatureismin(0).max(2), default0.7; outside 0–2 is a parse error (packages/spec/src/ai/agent.zod.ts:38):40-50"When to Use This Skill":145-215agent Required / Optional / 34-linedefineAgentexampleagent.tools,agent.knowledge) + a pointer toreferences/_index.md, inside the new bottom reference section## Skill Configurationis now the first section after the boundary anchor and the tier diagram; the closed agent surface is a reference section at the bottom. Free — reorder only:28-36, 83-86, 89-90, 126-131, 330-335, 339-342, 360, 364-365:138-141ops calloutAI_DAILY_USER_MESSAGES,ai_usage_daily,GET /api/v1/ai/status— cloud ops, andai_usage_dailyhas no object in the open repo:63-73"Why Three Tiers?":24-26:495-521"Structured Output"structuredOutputliteral, TS1109 in statement position) goes with it:461-473"Supported Providers":442-456"Knowledge Source Best Practices":526-529, 541-544pitfalls 1/2/4/5:362-381HITLIAIServicemethod names, every symbol cloud-closedenableActionApproval: trueholds, where it is triaged, and that the skip rule above is what decides whether your action is held:251-275flagshipdefineSkillexampletools: ['query_support_case','create_support_case','update_support_case','escalate_case']— all four unresolvabletools: ['query_records', 'get_record', 'action_escalate_case']with an inline comment naming the rungs. Measured belowMCPServerRuntime/MCPServerPlugin/list_actions/run_action//api/v1/mcpacross all 11 published packages## MCP — the open-edition AI pathsection. Paid by AI-D-03:386-391RAG wiringIKnowledgeService.registerSource()" and the package that provides it named nowhereKnowledgeServicePlugin({ sources })from@objectstack/service-knowledge, plus the two adapter packages theadapterids need. Paid by AI-D-05:277-286trigger table{ operator: 'in', value: 'admin' }is a parse error the package never warned about:289-335, :559defineTool:71-73vs:168:579-580promptTemplate.system/.user;ToolSchemacarries no expression field of any kind". See falsehood 2:297outputSchemaobjectName, silently reading as enforcedguardrails/memory/structuredOutputdefineAgentunder AI-B-02,defineToolunder AI-F-04). The corpus is 265 → 263 blocks and stays greenThe three funded additions — every claim read at source
1. Open-edition MCP wiring.
MCPServerPlugin/MCPServerRuntimeare exported frompackages/mcp/src/index.ts:13-16. The HTTP surface is default-on, served per-request bythe runtime dispatcher at
POST /api/v1/mcp(packages/mcp/src/plugin.ts:110-118;OS_MCP_SERVER_ENABLED=falseopts out), and stdio is a separate opt-in on its own switch —see Review round 1 below, which corrected which switch that is.
Tool names: the card's four are confirmed, and the list was incomplete. Read from the
registerToolcall sites inpackages/mcp/src/mcp-http-tools.ts:list_objects(:324),describe_object(:380),validate_expression(:406),query_records(:464),aggregate_records(:506, conditional on the bridge implementingaggregate),get_record(:580),
create_record(:605),update_record(:626),delete_record(:648), and fromregisterActionToolslist_actions(:708) andrun_action(:732). So:list_actions,run_action,query_records,describe_object— all four exist, andexamples/app-todo/test/mcp-actions.e2e.ts:110-113asserts the first three on a realJSON-RPC
tools/list.carries all eleven, grouped, with the scope gate on each family.
aggregate_records, notaggregate_data.aggregate_datais a different name in a different registry — aservice-aiplatformtool (
platform-tool-names.ts:44). Both spellings now appear in the file, each in its ownregistry, which is exactly the confusion the section prevents.
The MCP wiring fence is deliberately unmarked (PM assumption 4, confirmed): the
os-check-markergate compiles blocks against three surfaces —skills + docs (@objectstack/spec),spec source TSDoc,client SDK (@objectstack/client-react, @objectstack/client)— and@objectstack/mcpis in none of them, so the block could not bemarked without the gate refusing to resolve the import. Both remaining marked blocks compile
against
@objectstack/specdeclarations only.2. The
skill.tools[]ladder and the platform registry. The ladder isstack.tools[].name ∪ PLATFORM_PROVIDED_TOOL_NAMES ∪ action_NAME(
packages/lint/src/validate-ai-tool-references.ts:148-171,collectToolUniverse).Counted, as asked: exactly 30 names in
packages/spec/src/system/constants/platform-tool-names.ts— 6 underservice-ai(:43-50)and 24 under
service-ai-studio(:56-81); the const spans:38-82, so the card's:38-80was one line short of the closing brace. The 6 are enumerated in full; the 24 are given as a
representative sample plus the file pointer, deliberately, to keep the addition inside its
budget.
3. The trigger-condition operator ↔ value-shape column.
SKILL_TRIGGER_LIST_VALUE_OPERATORS=
['in','not_in'](packages/spec/src/ai/skill.zod.ts:37),SKILL_TRIGGER_SCALAR_VALUE_OPERATORS=['eq','neq'](:51), enforced bysuperRefine(checkSkillTriggerConditionValueShape)(:139-191) onSkillTriggerConditionSchema(:193-202).containsis deliberately in neither list andaccepts both. Driven against the real schema below.
The two falsehoods
1 —
agent.toolsis a tombstone, not "legacy". The file said "Direct tool assignment toagents is supported but considered legacy" at
:71-73and "REMOVED in protocol 17 … Aparse error now" at
:168— 145 lines apart. Implementation sides with:168:tools: retiredKey('...agent.tools was removed in @objectstack/spec 17 — use skills...')(
packages/spec/src/ai/agent.zod.ts:234). Rewritten: both retired keys now sit in one"tombstones, not legacy options — authoring either is a parse error carrying its migration"
table, with
agent.knowledge(:251) beside it. The "supported" sentence is gone.2 — no CEL carrier on
ToolSchema. The verify section cited "any CEL predicate (e.g. atool's availability condition)".
ToolSchemadeclares exactlyname,label,description,parameters,outputSchema,objectName(
packages/spec/src/ai/tool.zod.ts:132-168) — no condition or expression field of any kind.Rewritten to name the real AI-domain carrier, the model-registry
promptTemplate.system/.user, which is whatskills/objectstack-formula/SKILL.md:473already states correctly.Follow-up for
skills/README.md(not fixed here — the card forbids hand edits to thatfile's prose, and this flight touches it only as generator output):
skills/README.md:104-105routes "AI tool params" to objectstack-formula on the same false premise as falsehood 2.
There are no AI tool params carrying CEL. The routing row should either name the
model-registry
promptTemplatecarriers or be dropped. Owner: whoever holds the catalogrouting table.
premise_falseNone. Every finding's cited span was present and said what the audit reported.
git diff --stat a59f78d..d16df741 -- skills/objectstack-ai/is empty, so PM assumption 1 held and nospan had to be relocated by content.
The −2,108 gap, itemised (not smoothed)
The card asks for ≈ −2,300 net in-file. That figure is the audit's package net, and it
includes the deferred
evals/README.md−195. The audit's own in-file arithmetic is−2,108 (
−2,420deletions+312additions). Delivered: −1,315. The 793-token gapdecomposes, measured on the final file:
outputSchemacaveat (AI-G-03)The dominant term is structural, not slippage: the audit books funded addition 2 at +0
by charging it to AI-B-02's −600, while also counting that −600 in the deletion total. A
319-token addition cannot be free and also be a 600-token deletion. The second term is the
MCP section, where the +180 budget did not cover the eleven real tool names plus the
default-on/stdio distinction; I judged the names worth the overrun — they are the whole
point of the addition, and the file still lands with 1,330 tokens of headroom against a
ceiling it previously had 15.
The 106 of under-delivered deletion is spread across sections where the audit's estimate ran
ahead of what could go without losing a decision (chiefly Model Configuration and the
Actions/Tool split); no section was left uncut.
Ruled A by contract review round 1 — accept the net cut; no second pass.
Out-of-scope cards filed
skills/objectstack-aitells authorsmetadata_assistantis "not vocabulary" while the platform's own Studio app pinsdefaultAgent: 'metadata_assistant'#14461 —metadata_assistant: the skill says the legacy alias is "not vocabulary"while
packages/platform-objects/src/apps/studio.app.ts:50pinsdefaultAgent: 'metadata_assistant'— the onlyapp.defaultAgentusage in the repo. Threereadings, graded rather than patched.
objectstack-aigenerated reference index advertises 6 schemas the SKILL.md never teaches and omits the one it names —SKILL_MAPinbuild-skill-references.tsis unreconciled with the body #14462 — AI-B-04: the generatedreferences/_index.mdadvertises 6 schemas the bodynever teaches, and
SKILL_MAP(packages/spec/scripts/build-skill-references.ts:98-109)omits
ai/solution-blueprint.zod.ts, the schema behind thesolution_designskill thebody names.
MCPServerPlugin's own docblock still teaches the deprecated stdio trigger —plugin.ts:112-122disagrees withplugin.ts:234-239twelve lines below it #14473 — the staleMCPServerPlugindocblock that produced review round 1's FAIL (seebelow).
All unassigned,
findinglabel, no pm-state, no priority. Dedupe was a targetedsearch_issuesper card (repo-scoped REST answers 403 for this seat), each returning anon-empty result set, so the session's search is not in the silent-zero failure mode.
Gates — head
5dabfd2aFamily re-derived after regeneration, as the card requires:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands→ the.mdx+skills/**change set produced 36 commands; addingscripts/role-word-baseline.json(below) grew it to 42, and all 42 were re-run in full on this head after the round-1 patch
(the re-derived list came back byte-identical to the 42 already run). Exit codes captured
before any pipe, one log per command.
42 run · 41 green · 1 NOT MEASURED · 0 red.
node scripts/check-skills-token-ratchet.mjs—✓ 37 authored bundle file(s) within their ceilings;skills/objectstack-ai/SKILL.md is 5476 tokens (ceiling 6806; headroom 1330).pnpm --filter @objectstack/spec check:skill-examples—✅ 263 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them.@objectstack/specand the@objectstack/client-reactclosure were built first (this gate refuses a stale or missing dist rather than false-greening).pnpm check:skill-compatibility—✓ 11 SKILL.md file(s) reconciled against 78 workspace packages.pnpm check:skill-identifier-liveness—OK — Leg 1: 480 citation(s) over 47 published file(s) … Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).pnpm --filter @objectstack/spec check:skill-docs—✓ skills/README.md,✓ content/docs/ai/skills-reference.mdx,✅ Skill docs in sync.pnpm check:role-word—check-role-word: OK, no new occurrences of the reserved word.pnpm check:skill-frame-sync,check:doc-authoring,check:doc-anchors,check:docs-single-h1,check:published-readme-links,check:corpus-claim-drift, and the rest of the 42 — green.node scripts/check-test-completeness.mjs— exit 3, NOT MEASURED. Its own text: "this gate grades a savedturbo run testlog, and no log was named … running the family locally, record this gate as NOT MEASURED. ⛔ It is not a red." CI tees the log and passes the path.PM assumption 3, measured before the first deletion:
check-skill-identifier-liveness'sLeg 2
BINDINGStable registers 8 exhaustive sections across objectstack-api, objectstack-data(×3), objectstack-platform and objectstack-ui (×3). None is in
skills/objectstack-ai/SKILL.md, so no heading in this file was bound and the reorder anddeletions were free of Leg 2. Leg 1 citations dropped 490 → 480 with the gate still green.
One extra file, mechanically forced.
check:role-wordwent red at exit 1 with aratchet-DOWN:
role-word count improved 5 → 1 — run --update and commit the baseline.That gate's remedy for a downward ratchet is explicitly the author's own, so
node scripts/check-role-word.mjs --updatewas run; the diff is the single line"skills/objectstack-ai/SKILL.md": 5→1inscripts/role-word-baseline.json. No otherline changed, and no other file was touched to satisfy a gate. The round-1 patch did not
move that count again —
check:role-wordreturned exit 0 withOK, no new occurrences, sothe baseline is unchanged on this head.
Staleness, checked rather than assumed.
dispatch-gateswarned thatorigin/mainhadmoved past my base.
git diff d16df741..origin/main -- .github/workflows/ package.jsonshowsonly
release.yml, and it changes noskills/**,content/**ordocs/**path filter — sothe derived family is unaffected. The branch is deliberately still based on the dispatched
d16df741.Verification of the rewritten example and the new claims
AI-E-01 — the real rule, both directions.
validateAiToolReferences(the exportedai-skill-tool-unresolvedimplementation) was driven over the shipped example's tool listand the rewritten one, against a stack carrying one AI-exposed
escalate_caseAction andno
stack.toolsrecords (the ADR-0109 default path):Predicted direction before running: old red, new clean. Observed: exactly that, and the rule
volunteered the near-miss hint on
escalate_case— the same one-word slip the old exampletaught. The driver was written into the worktree, run, and deleted in the same step; the tree
is clean (
git statusempty).Funded addition 3 and AI-A-02 — driven against the real schemas, not transcribed:
(the two bound messages print the comparison symbols; spelled
lte/gtehere so thebody sanitizer cannot eat the fragment.)
Every row of the new trigger table and the
temperaturerow is a measurement, including thetwo the audit could only read off the source: the empty array is a real predicate, and
containsgenuinely takes both shapes.Review round 1 — one FAIL, fixed at
5dabfd2aContract review round 1 on
e0fdaecbpassed every claim at source except one span, and it wasright.
skills/objectstack-ai/SKILL.md:189-190read:That names only the deprecated trigger. Verified at source in my own worktree before
touching anything:
resolveMcpStdioAutoStart()(packages/types/src/env.ts:333-345) readsOS_MCP_STDIO_ENABLEDfirst and returns{ enabled: true, viaDeprecatedAlias: false };OS_MCP_SERVER_ENABLED=truefalls through to the legacy branch and returnsviaDeprecatedAlias: true, on whichMCPServerPlugin.start()(
packages/mcp/src/plugin.ts:234-239) logs "Starting the stdio transport viaOS_MCP_SERVER_ENABLED=true is DEPRECATED — that var now only gates the default-on HTTP
surface. Use OS_MCP_STDIO_ENABLED=true (or the plugin
autoStartoption)". An author copyingmy sentence shipped a boot-time warning and had no way to learn the live spelling.
Now:
That also states defaults off, which the old wording only implied.
Paid in-file, not by re-wrap. The section's closing sentence ("Your skills'
instructionsreach this surface too…") restated the
instructionsrow of the Skill Configuration table insubstance — the exact duplication AI-C-01 exists to remove — and is deleted. 5,481 → 5,476
tokens; the file went down, not up.
Where the error came from, filed as its own card: #14473. The sentence was written from
MCPServerPlugin's class docblock (packages/mcp/src/plugin.ts:112-122), which stilldescribes the pre-split behaviour — "explicit
trueadditionally auto-starts the stdiotransport" — and disagrees with its own code twelve lines below. That is a code comment
disagreeing with its code, not a spec
.describe(), so no spec-side twin is owed. Unassigned,findinglabel, dedupe search returned a non-empty result set with nothing open covering it.Union re-derived after the edit, not reused.
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandson thepatched tree returned a list byte-identical to the 42 from round 1, and all 42 were re-run on
5dabfd2a(with@objectstack/specand the@objectstack/client-reactclosure rebuilt firstin the same locked run, since the worktree was re-created from the pushed branch). Same
result: 41 green, 1 NOT MEASURED (
check-test-completeness, exit 3), 0 red.The open question is ruled A — accept the net cut. No second pass.
Labels
skip-changeset— this releases nothing. Checked against the gate's own enumeration(
scripts/check-empty-changeset.mjs:357: "It releases nothing (.github/, .claude/, skills/,docs/, content/, examples/, tests-only, and the like)"); the diff is
skills/**,content/docs/**and onescripts/ratchet baseline. No changeset file was written — thegate's route 2 is the label, and an empty changeset is a real input that stalls a release
silently.
needs:contract-reviewon this PR and on #14305: falsehood 1 (aretiredKey()tombstonereclassified from "supported"), falsehood 2 (a CEL carrier claim), the trigger-operator value
shapes, the
temperaturebound, the MCP tool names, and theskill.tools[]ladder are allcontract-semantics claims. Stays draft until that review lands.
🤖 Generated with Claude Code
https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1