Skip to content

Commit 0da7cd2

Browse files
os-trumpclaude
andauthored
refactor(spec)!: strip internal tracker ids from the three customer-facing text populations, and widen check-doc-authoring Rule 3 to hold them (#13019)
* chore(gates): widen check-doc-authoring Rule 3 to the three adjacent spec text populations Rule 3 held the zod `message:` population alone and named three neighbouring ones as deliberately out of scope pending a ruling. That ruling arrived as ruling-inheritance rather than a new decision: the founding rationale is "the ban follows the audience, not the file type", and it transfers unchanged to - strictObject unknown-key error-map options (surface/history/aliases/ guidance/guidanceSets/retiredForms) — printed verbatim at the refusing author, same audience and same moment as a refusal message; - retiredKey() tombstone prescriptions — the prescription IS the parse error; - .describe() prose — projects into content/docs/references/** and the generated skill artifacts. ADR ids and migration commands are KEPT: AGENTS.md positively requires a tombstone to carry a durable reference, and an ADR id is customer-resolvable where #NNNN is not. No exemption mechanism is added; the rule stays exemption-free by design, exactly as its own header prescribed. Positions alone were not enough. The guidance maps — and a share of the refusal messages — are hoisted into named consts and referenced from the sink, so a matcher reading only a literal's own position stops at the VariableDeclaration. collectTextSinkConsts() closes that with a per-file sink-alias fixed point. It is not a hypothetical about the new buckets: the hoisted spelling was hiding five ids from the `message:` rule itself, which had reported that population clean since the day it landed. The `seen` floor is now PER BUCKET rather than on the total, for the same reason #4932's floor is per-ROOT: .describe() alone would hold a total positive forever while the guidance matcher rotted unseen. Measured on this base: green before the widening (535 message strings, 0 violations), red after it with 584 violations — strictObject 262, describe 187, tombstone 130, message 5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * refactor(spec)!: strip internal tracker ids from customer-facing spec text — mechanical pass 479 of the 584 literals the widened Rule 3 reports, across 84 files. Driven by the gate's own violation list, so the edited ranges are exactly the ruled population; every edit is inside a string literal, none touches code. The rules, in the order they fire: 197 a parenthetical whose whole content is ids — it goes with them 166 a parenthetical carrying ids beside customer-resolvable content — the ids go, an ADR id / protocol version / phrase stays, and a fragment left holding an open connective ("widened by") is dropped whole rather than stranded 110 the `history:` idiom — "Until #N [批 NN] closed this shape" states the closure as a fact instead of citing the work that performed it 15 bare-prose idioms, each deleting the governing preposition with the id; a stranded `since` is a worse sentence than the citation was ADR ids, protocol versions, error codes and migration commands are kept throughout — they are the durable references AGENTS.md requires, and they are customer-resolvable where #NNNN is not. 485 insertions / 485 deletions: the line count is unchanged because nothing but literal text moved. The remaining 105 need sentence repair rather than excision and are hand-edited in the following commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * refactor(spec)!: strip the remaining tracker ids — hand-repaired sentences The 105 sites the mechanical rules could not take without rewriting the sentence around them, across 37 files. Two further mechanical clusters were folded into the rule set first (a parenthetical whose ids sit at the end of one concatenated operand while its content continues in the next; a `.describe()` opening with a bracketed `[#N]` tag), leaving 50 read and repaired one by one. Each repair states in words what the id used to point at — "the gap this key exists to close", "the console release that restores this flag", "the same silent-drop bug one level down" — rather than deleting the clause and leaving a sentence that no longer says anything. ADR ids, protocol versions, error codes and `os migrate` commands are untouched throughout. Verified beyond the gate, because an excision from mid-parenthetical can take the closing bracket with it and still parse: every concatenated string expression in all 88 touched files was compared against the branch base for bracket balance — 11,065 expressions, balance unchanged on every one. That check caught one real defect (a dropped `)` in `protocol.zod.ts`) that reads almost right and compiles fine. `check:doc-authoring` is now GREEN on the widened rule: 13,306 customer-facing strings across 670 spec sources, 0 violations, with every bucket populated (message 778 · strictObject 3282 · tombstone 751 · describe 8495) so the per-bucket blindness floor is satisfied rather than merely unbreached. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * test(spec): move the sentence pins with the strings they pin, and add negative pins 12 spec test files asserted on wording this PR reworded. Each pin now asserts the NEW sentence rather than being deleted — `toContain('#3810')` becomes `toContain('match-everything write')`, `toContain('#5058')` becomes `toContain('open gap')` — so the pin still holds the fact it was written to hold. Nine were moved mechanically and verified against the post-strip message text (the old fragment must be gone from the corpus and the proposed new one must actually occur in it); the remaining pins asserted only "the message carries id #N", which has no replacement substring, and were re-pointed by hand at the surviving clause. Where a pin existed to hold a tracker id, it is replaced by the NEGATIVE pin the gate's own failure text prescribes: the message must not match `#\d{3,5}`. That keeps the assertion load-bearing instead of trivially true — a pin merely deleted would let the id return. Also adds the changeset: `@objectstack/spec` patch, FROM -> TO at bucket level. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * chore(spec): regenerate content/docs/references after the .describe() strip The `.describe()` bucket projects into the generated reference docs, so the strip lands there too: 64 files, 772 insertions / 772 deletions, line for line. Regenerated with `check:generated --fix` on the committed merge (never in MERGE state, where `gen:schema` would roll the authorable-surface anchor back to the old fork point) and narrowed to the one artifact the check proved stale. Verified: every regenerated path is under `content/docs/references/`, `content/docs/releases/` is untouched, and no `+` line in the diff carries a tracker id. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * test(spec): repoint the remaining assertion pins off the stripped ids The first pin pass moved the pins whose expected text still existed in the post-strip sources. This one covers the rest, found by running the suite rather than by pattern-matching: 92 tests across 40 files were asserting on wording this PR reworded. Scope was held narrow in two directions, deliberately: - only ids this branch actually REMOVED from non-test spec sources, read out of the diff rather than guessed — a pin on an id that still exists must keep working, and silently weakening it would be worse than leaving it; - only literals in an ASSERTION ARGUMENT. Test TITLES and COMMENTS keep their ids on purpose: the gate excludes test bodies because that reader HAS the tracker, and the ban follows the audience. An earlier line-based attempt would have stripped 482 literals across 134 files, most of them titles. Three shapes of repair: - an id used as a DISCRIMINATOR between sibling refusals — `driver-credential- refusal.test.ts` filtered issues with `message.includes('#9041')` vs `('#9147')` — now discriminates on the remedy clause that actually differs ("the URL's own userinfo" vs "add `username` to `config`"). That helper is why ten tests there read as "the refusal did not fire": it fired, and the filter could not see it. - an id that was one term of a larger expected string or regex — the id term is dropped, the rest of the pin stands. - an id that was the WHOLE expected value — repointed at a distinctive surviving phrase of the same message, or, where a sibling assertion already covers that, replaced by the negative pin the gate's own failure text prescribes (`.not.toMatch(/#\d{3,5}/)`), so the pin stays load-bearing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * test(spec): finish moving the pins, and undo the ones that were never stale Iterated against the suite rather than against a pattern: 92 -> 46 -> 13 -> 4 -> 0. Two corrections worth recording, because both produced confidently wrong states that still read as progress. **Reverted the pins that were never stale.** A bulk phrase swap repointed pins whose ids are still in the sources — in populations OUTSIDE the four ruled buckets: conversion-registry summaries, migration entries, TSDoc, and refusal prose built inside `error: () =>` callbacks (filed as a separate finding). Those tests were passing; the swap broke them. Ten sites are restored to the id they legitimately still assert. The rule the reverts encode: a pin may only lose an id this branch actually removed FROM THAT MESSAGE, which is narrower than "an id this branch removed somewhere". **Restored a regex the repair had eaten.** The regex cleaner stripped `\(\)` to tidy emptied groups and took the literal `` `find\(\)` `` in `driver.test.ts` with it. That failure was briefly, and wrongly, diagnosed as pre-existing on main — the phrase really was absent from every revision, because the mangled regex was what was being compared. The evidence that settled it was `git diff` against the branch base, not a grep for the phrase. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * test(spec): pin the area-gate refusal on its enforcement clause The last stale pin. `#4722` on the `areas[].requiredPermissions` tombstone was first repointed at a phrase from the SIBLING `visible` guidance, which is a different message on the same surface — it matched neither. It now pins "re-checked in the shell", the clause that states how the gate is actually enforced, which is what the citation stood in for. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * chore(spec): regenerate references after merging main (discharges the merge deferral) `origin/main` moved 21 commits during the run. One conflict, in `packages/spec/src/ui/view.zod.ts` — the cross-wave file declared at claim time: main rewrote the `visibleWhen` describe to document `current_user` resolution, this branch had stripped two ids from the older sentence. Resolved by taking main's newer prose whole and applying this branch's strip to it, so neither the new content nor the convention is lost. Asserted mechanically before staging: no tracker id survives in the resolved line. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * fix(spec): repo-wide consumer-pin sweep + repair the strip's cross-operand scars CI was red on Test Core with 8 failures in @objectstack/metadata. Cause: the pin iteration ran against the spec suite only, and packages OUTSIDE spec pin spec messages too. This round sweeps every tracked test file in every package and repairs two defect classes the spec-only pass could not see. **Stale consumer pins (4 sites, 4 packages).** Same narrow rule as before — a pin may only lose an id this branch removed FROM THAT MESSAGE: - metadata/plugin-artifact-forward-conversion: `…17 (#12497, ADR-0049)` -> `…17 (ADR-0049)`; the surviving ADR id carries the pin. - metadata/stored-envelope: `toContain('#5309')` had no replacement fragment, so it becomes the negative pin the gate prescribes, beside the existing `storage bookkeeping` assertion which stays the positive half. - objectql/engine-unknown-option: two pins — a regex term `#8057, ADR-0049` trimmed to `ADR-0049`, and `/#4286, ADR-0049/` likewise. - service-automation/screen-nodes: `/#4343/` repointed at `` /`script.config.actionType` was removed/ `` plus a negative id pin. **Cross-operand scars in the prose itself (13 sites, 10 spec sources).** An excision that spans two concatenated operands leaves a scar neither a per-literal tidy nor `git diff` review can see, because each literal is individually well-formed — it appears only once the `+` chain is joined, which is exactly what the customer reads: '… sees a body ' + '(#5309), so …' -> '… sees a body ' + ', so …' reads as "a body , so" 12 were space-before-punctuation / double-space at an operand boundary, found by a joined-chain scan and repaired by trimming the left operand. The 13th was a stranded possessive: `RECORD_NOT_FOUND (#7867's not-found gate)` had become `( 's not-found gate)`, now `(the by-id not-found gate)`. Verified by running suites, since substring probes proved unreliable in BOTH directions here — they missed objectql/#8057 and service-automation/#4343 (the id survives elsewhere in spec) and wrongly cleared metadata/#5309 (it survives elsewhere in that package). Ten packages, 27,704 tests, all green: spec 11675 · objectql 4268 · runtime 2898 · metadata-protocol 2010 · lint 2300 · plugin-auth 1695 · service-automation 1091 · core 1015 · metadata 674 · plugin-dev 58. All 14 spec artifacts regenerated and current. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 96c796c commit 0da7cd2

208 files changed

Lines changed: 2103 additions & 1605 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/wild-poems-repeat.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
Strip internal issue-tracker ids from the customer-facing text in `packages/spec`
6+
7+
Refusal prose, unknown-key guidance, tombstone prescriptions and `.describe()`
8+
docs no longer carry `#NNNN` references. A customer reading a rejected-metadata
9+
error — or the generated reference docs — has no access to this repo's tracker,
10+
so an id there is a citation-shaped token that resolves to nothing in the one
11+
place the sentence most needs to be actionable.
12+
13+
**FROM → TO, at bucket level** (584 strings across 88 spec sources):
14+
15+
| bucket | strings | FROM | TO |
16+
|---|---:|---|---|
17+
| `strictObject` guidance / history / aliases | 262 | ``Until #4001 closed this shape these were dropped silently`` | ``Until this shape was closed these were dropped silently`` |
18+
| `retiredKey()` tombstones + `*_RETIRED_KEY_GUIDANCE` | 130 | ``removed in @objectstack/spec 17 (#3894) — use `skills`.`` | ``removed in @objectstack/spec 17 — use `skills`.`` |
19+
| `.describe()` docs prose | 187 | ``Parsed but no runtime consumer yet (liveness #1878/#1893).`` | ``Parsed but no runtime consumer yet.`` |
20+
| zod `message:` (hoisted spelling) | 5 | ``a pair that cannot work as written (#9041).`` | ``a pair that cannot work as written.`` |
21+
22+
**Kept, deliberately:** ADR ids, protocol and package versions, error codes, and
23+
the `os migrate meta --from <N>` migration commands. AGENTS.md requires a
24+
tombstone prescription to carry a durable reference, and those are the forms a
25+
customer can actually resolve — the issue id riding beside them was the
26+
strippable half.
27+
28+
No behaviour changes: no schema accepts or rejects anything it did not before,
29+
and no key, default or error `code` moved. This is the wording of messages and
30+
generated docs only.

content/docs/references/ai/agent.mdx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,18 @@ const result = AIModelConfigSchema.parse(data);
5050
| **role** | `string` || The persona/role (e.g. "Senior Support Engineer") |
5151
| **instructions** | `string` || System Prompt / Prime Directives |
5252
| **model** | `{ provider: Enum<'openai' \| 'azure_openai' \| 'anthropic' \| 'local'>; model: string; temperature: number; maxTokens?: number; … }` | optional | |
53-
| **lifecycle** | `{ id: string; description?: string; contextSchema?: Record<string, any>; initial: string; … }` | optional | [EXPERIMENTAL — not enforced] State machine defining the agent conversation flow and constraints. Parsed but no runtime consumer yet (liveness #1878/#1893). |
53+
| **lifecycle** | `{ id: string; description?: string; contextSchema?: Record<string, any>; initial: string; … }` | optional | [EXPERIMENTAL — not enforced] State machine defining the agent conversation flow and constraints. Parsed but no runtime consumer yet. |
5454
| **surface** | `Enum<'ask' \| 'build'>` | optional (default: `"ask"`) | Product surface this agent binds ('ask' \| 'build') — ADR-0063 §1 |
5555
| **skills** | `string[]` | optional | Skill names to attach (Agent→Skill→Tool architecture) |
56-
| **tools** | `never` | optional | [REMOVED] `agent.tools` was removed in @objectstack/spec 17 (#3894) — use `skills`. An agent reaches exactly the tools its surface-compatible skills declare (ADR-0064), so move each reference into a skill: a platform tool by its registered name, or `action_<name>` for one of your own AI-exposed Actions. This is NOT a rename — there is no key the value moves to: the migration DELETES the key and emits a notice naming each tool that was listed, and you re-declare each one in a skill by hand. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
57-
| **knowledge** | `never` | optional | [REMOVED] `agent.knowledge` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — declaring knowledge sources/indexes on an agent never scoped retrieval: the `search_knowledge` tool takes `sourceIds` from the LLM's tool-call arguments, not from the agent record. Delete the block. Restrict retrieval at the knowledge-service / source level (per-source permissions), and describe intended grounding in `instructions` so the model asks for the right sources. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
56+
| **tools** | `never` | optional | [REMOVED] `agent.tools` was removed in @objectstack/spec 17 — use `skills`. An agent reaches exactly the tools its surface-compatible skills declare (ADR-0064), so move each reference into a skill: a platform tool by its registered name, or `action_<name>` for one of your own AI-exposed Actions. This is NOT a rename — there is no key the value moves to: the migration DELETES the key and emits a notice naming each tool that was listed, and you re-declare each one in a skill by hand. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
57+
| **knowledge** | `never` | optional | [REMOVED] `agent.knowledge` was removed in @objectstack/spec 17.0.0 (audit close-out) — declaring knowledge sources/indexes on an agent never scoped retrieval: the `search_knowledge` tool takes `sourceIds` from the LLM's tool-call arguments, not from the agent record. Delete the block. Restrict retrieval at the knowledge-service / source level (per-source permissions), and describe intended grounding in `instructions` so the model asks for the right sources. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
5858
| **active** | `boolean` | optional (default: `true`) | |
5959
| **access** | `string[]` | optional | Who can chat with this agent |
6060
| **permissions** | `string[]` | optional | Required permission-set capabilities |
6161
| **planning** | `{ maxIterations: integer }` | optional | Autonomous reasoning and planning configuration |
62-
| **memory** | `{ longTerm?: object; reflectionInterval?: integer }` | optional | [EXPERIMENTAL — not enforced] Agent memory management. Parsed but no runtime consumer yet (liveness #1878/#1893). |
63-
| **guardrails** | `{ maxTokensPerInvocation?: integer; maxExecutionTimeSec?: integer; blockedTopics?: string[] }` | optional | [EXPERIMENTAL — not enforced] Safety guardrails for the agent. Parsed but not enforced — real limits come from the quota service (liveness #1878/#1893). |
64-
| **structuredOutput** | `{ format: Enum<'json_object' \| 'json_schema' \| 'regex' \| 'grammar' \| 'xml'>; schema?: Record<string, any>; strict: boolean; retryOnValidationFailure: boolean; … }` | optional | [EXPERIMENTAL — not enforced] Structured output format and validation configuration. Parsed but no runtime consumer yet (liveness #1878/#1893). |
62+
| **memory** | `{ longTerm?: object; reflectionInterval?: integer }` | optional | [EXPERIMENTAL — not enforced] Agent memory management. Parsed but no runtime consumer yet. |
63+
| **guardrails** | `{ maxTokensPerInvocation?: integer; maxExecutionTimeSec?: integer; blockedTopics?: string[] }` | optional | [EXPERIMENTAL — not enforced] Safety guardrails for the agent. Parsed but not enforced — real limits come from the quota service. |
64+
| **structuredOutput** | `{ format: Enum<'json_object' \| 'json_schema' \| 'regex' \| 'grammar' \| 'xml'>; schema?: Record<string, any>; strict: boolean; retryOnValidationFailure: boolean; … }` | optional | [EXPERIMENTAL — not enforced] Structured output format and validation configuration. Parsed but no runtime consumer yet. |
6565
| **protection** | `{ lock: Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>; reason: string; docsUrl?: string }` | optional | Package author protection block — lock policy for this agent. |
6666
| **_lock** | `Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>` | optional | Item-level lock — controls overlay & delete (ADR-0010). |
6767
| **_lockReason** | `string` | optional | Human-readable reason shown when a write is refused by _lock. |

content/docs/references/ai/skill.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ const result = SkillSchema.parse(data);
3636
| **label** | `string` || Skill display name |
3737
| **description** | `string` | optional | Skill description |
3838
| **surface** | `Enum<'ask' \| 'build' \| 'both'>` | optional (default: `"ask"`) | Agent surface this skill binds to ('ask' \| 'build' \| 'both') — ADR-0063 §3; read by the cloud agent runtime only |
39-
| **instructions** | `string` | optional | LLM instructions when skill is active — also served as an MCP prompt (#3905) |
39+
| **instructions** | `string` | optional | LLM instructions when skill is active — also served as an MCP prompt |
4040
| **tools** | `string[]` || Tool names belonging to this skill (supports trailing wildcard, e.g. `action_*`) — bound by the cloud agent runtime only |
41-
| **triggerPhrases** | `never` | optional | [REMOVED] `skill.triggerPhrases` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — phrases were never matched against the user's message; skill activation is `triggerConditions` (AND of context field/operator/value) intersected with the agent's `skills[]`, plus explicit /skill-name pinning. Delete the key. Put routing intent in `triggerConditions`; describe intent in `description`/`instructions` for the LLM. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
41+
| **triggerPhrases** | `never` | optional | [REMOVED] `skill.triggerPhrases` was removed in @objectstack/spec 17.0.0 (audit close-out) — phrases were never matched against the user's message; skill activation is `triggerConditions` (AND of context field/operator/value) intersected with the agent's `skills[]`, plus explicit /skill-name pinning. Delete the key. Put routing intent in `triggerConditions`; describe intent in `description`/`instructions` for the LLM. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
4242
| **triggerConditions** | `{ field: string; operator: Enum<'eq' \| 'neq' \| 'in' \| 'not_in' \| 'contains'>; value: string \| string[] }[]` | optional | Programmatic activation conditions — evaluated by the cloud agent runtime only |
4343
| **active** | `boolean` | optional (default: `true`) | Whether the skill is enabled |
4444
| **protection** | `{ lock: Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>; reason: string; docsUrl?: string }` | optional | Package author protection block — lock policy for this skill. |

content/docs/references/ai/tool.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const result = ToolSchema.parse(data);
2323

2424
## Tool
2525

26-
AI tool definition. [READ-ONLY PROJECTION — not an execution entry point] Authoring a tool as metadata does NOT make it runnable: this schema has no `implementation`/`handler` field and no framework executor loads a metadata-authored tool. The runtime executes a separately-registered `AIToolDefinition` (cloud `@objectstack/service-ai`); tool metadata is a one-way projection for Studio/discovery. Do not expect a hand-authored tool to run in the open edition (liveness audit #1878/#1892).
26+
AI tool definition. [READ-ONLY PROJECTION — not an execution entry point] Authoring a tool as metadata does NOT make it runnable: this schema has no `implementation`/`handler` field and no framework executor loads a metadata-authored tool. The runtime executes a separately-registered `AIToolDefinition` (cloud `@objectstack/service-ai`); tool metadata is a one-way projection for Studio/discovery. Do not expect a hand-authored tool to run in the open edition.
2727

2828
### Properties
2929

@@ -33,7 +33,7 @@ AI tool definition. [READ-ONLY PROJECTION — not an execution entry point] Auth
3333
| **label** | `string` || Tool display name |
3434
| **description** | `string` || Tool description for LLM function calling |
3535
| **parameters** | `Record<string, any>` || JSON Schema for tool parameters |
36-
| **outputSchema** | `Record<string, any>` | optional | [EXPERIMENTAL — not enforced] JSON Schema for tool output. Keys are folded into the tool description only; outputs are not validated (liveness #1878/#1893). |
36+
| **outputSchema** | `Record<string, any>` | optional | [EXPERIMENTAL — not enforced] JSON Schema for tool output. Keys are folded into the tool description only; outputs are not validated. |
3737
| **objectName** | `string` | optional | Target object name (snake_case) |
3838
| **protection** | `{ lock: Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>; reason: string; docsUrl?: string }` | optional | Package author protection block — lock policy for this tool. |
3939
| **_lock** | `Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>` | optional | Item-level lock — controls overlay & delete (ADR-0010). |

content/docs/references/api/analytics.mdx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ const result = AnalyticsEndpoint.parse(data);
5353
| Property | Type | Required | Description |
5454
| :--- | :--- | :--- | :--- |
5555
| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| …>` || Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) |
56-
| **declaredCode** | `string` | optional | The producer-declared code, verbatim, when it is not a member of the closed `code` vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112, #9106) |
56+
| **declaredCode** | `string` | optional | The producer-declared code, verbatim, when it is not a member of the closed `code` vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112) |
5757
| **message** | `string` || Readable error message |
58-
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim (#9934). Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution (#3821) for anything unmarked. Status-agnostic; never replaces `message`. |
58+
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces `message`. |
5959
| **category** | `string` | optional | Error category (e.g. validation, authorization) |
6060
| **httpStatus** | `integer` | optional | HTTP status of the response carrying this error |
6161
| **details** | `any` | optional | Additional error context (e.g. field validation errors) |
@@ -88,8 +88,8 @@ const result = AnalyticsEndpoint.parse(data);
8888
| **limit** | `number` | optional | |
8989
| **offset** | `number` | optional | |
9090
| **timezone** | `string` | optional | |
91-
| **query** | `never` | optional | [REMOVED] `query` was removed from AnalyticsQueryRequest in @objectstack/spec 17.0.0 (#3878). The `{ cube, query: {...} }` envelope was the dialect of the retired degraded analytics shim (#3891) — the real engine never understood it. Move the query.* fields to the body top level: `{ cube, measures, dimensions?, where?, timeDimensions?, order?, limit?, offset?, timezone? }`. |
92-
| **format** | `never` | optional | [REMOVED] `format` was removed from AnalyticsQueryRequest in @objectstack/spec 17.0.0 (#3878). It was never implemented — every response is the JSON envelope. Delete the key; for CSV/XLSX use the export surface instead. |
91+
| **query** | `never` | optional | [REMOVED] `query` was removed from AnalyticsQueryRequest in @objectstack/spec 17.0.0. The `{ cube, query: {...} }` envelope was the dialect of the retired degraded analytics shim — the real engine never understood it. Move the query.* fields to the body top level: `{ cube, measures, dimensions?, where?, timeDimensions?, order?, limit?, offset?, timezone? }`. |
92+
| **format** | `never` | optional | [REMOVED] `format` was removed from AnalyticsQueryRequest in @objectstack/spec 17.0.0. It was never implemented — every response is the JSON envelope. Delete the key; for CSV/XLSX use the export surface instead. |
9393

9494

9595
---
@@ -110,9 +110,9 @@ const result = AnalyticsEndpoint.parse(data);
110110
| Property | Type | Required | Description |
111111
| :--- | :--- | :--- | :--- |
112112
| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| …>` || Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) |
113-
| **declaredCode** | `string` | optional | The producer-declared code, verbatim, when it is not a member of the closed `code` vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112, #9106) |
113+
| **declaredCode** | `string` | optional | The producer-declared code, verbatim, when it is not a member of the closed `code` vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112) |
114114
| **message** | `string` || Readable error message |
115-
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim (#9934). Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution (#3821) for anything unmarked. Status-agnostic; never replaces `message`. |
115+
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces `message`. |
116116
| **category** | `string` | optional | Error category (e.g. validation, authorization) |
117117
| **httpStatus** | `integer` | optional | HTTP status of the response carrying this error |
118118
| **details** | `any` | optional | Additional error context (e.g. field validation errors) |
@@ -145,9 +145,9 @@ const result = AnalyticsEndpoint.parse(data);
145145
| Property | Type | Required | Description |
146146
| :--- | :--- | :--- | :--- |
147147
| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| …>` || Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) |
148-
| **declaredCode** | `string` | optional | The producer-declared code, verbatim, when it is not a member of the closed `code` vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112, #9106) |
148+
| **declaredCode** | `string` | optional | The producer-declared code, verbatim, when it is not a member of the closed `code` vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112) |
149149
| **message** | `string` || Readable error message |
150-
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim (#9934). Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution (#3821) for anything unmarked. Status-agnostic; never replaces `message`. |
150+
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces `message`. |
151151
| **category** | `string` | optional | Error category (e.g. validation, authorization) |
152152
| **httpStatus** | `integer` | optional | HTTP status of the response carrying this error |
153153
| **details** | `any` | optional | Additional error context (e.g. field validation errors) |

0 commit comments

Comments
 (0)