Skip to content

feat(spec)!: retire connector.errorMapping — eleven inert authorable keys, one spelled like the live userMessage channel (#14676, ADR-0049) - #15299

Merged
os-justin merged 5 commits into
mainfrom
claude/issue-14676-retire-connector-error-mapping
Sep 4, 2026
Merged

os-justin merged 5 commits into
mainfrom
claude/issue-14676-retire-connector-error-mapping

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #14676

Retires ConnectorSchema.errorMapping under ADR-0049 enforce-or-remove, executing the triage ruling (comment 5516704842) and the seat claim (5537492862): the eleven authorable keys of ErrorMappingConfig (4) and ErrorMappingRule (7) had zero consumers, and one of them was spelled userMessage — the name of the live API-error channel — so a connector rule validated, published, and showed nobody anything. Removal settles the name collision by deletion; the live channel (ApiErrorSchema / EnhancedApiErrorSchema) is untouched, and #14674 is not addressed here.

Triage ruling — operative sentences (quoted from 5516704842)

Route ruled: removal, via the spec-property-retirement playbook … Take the removal route: ADR-0087 conversion, ledger discipline per the playbook, and the generated baselines / authorable-surface artifacts it names.

⛔ Split condition, and it is the one thing that flips this back to a decision. If the implementer finds a downstream consumer — hotcrm, objectui, or a customer stack authoring errorMapping — then removal is a breaking change to a published surface and this becomes needs-user-decision rather than a retirement. … Check that first, before touching anything, and stop and report if you find one.

⛔ Do not touch #14674.

Split condition — downstream consumer measurement (checked before the first edit)

realm reading result
objectui origin/main 0d8fd7c (fetched 2026-09-04, read-only sibling checkout) git grep -n -E 'errorMapping|ErrorMapping' origin/main no output, git exit 1 — zero hits
this repo at base 460134af the seven identifiers and the errorMapping key across packages/**, examples/**, skills/**, non-generated content/docs/**, *.form.ts, the qa/dogfood conformance ledger zero outside connector.zod.ts, its unit test, and the Iso382 type-identity pin
hotcrm — NOT MEASURED — not reachable from this container. Recorded as the confidence gap for the reviewer, never as a zero. The D2 conversion + prescription is the migration path for any out-of-repo stack.

No downstream consumer found ⇒ the split condition is not met and the ruling's removal route stands (no needs_decision).

Route — playbook §2, tombstone row

ConnectorSchema is a non-strict z.object, so errorMapping is a retiredKey() tombstone (ERROR_MAPPING_RETIRED), never a bare deletion (zod would strip it silently — the shape the tombstones exist to prevent). DeclarativeConnectorEntrySchema is ConnectorSchema.superRefine(...), so one tombstone covers both carriers: the refusal reaches stack.connectors[] (stack.zod.ts:679) and the PUT /api/v1/meta/connector/:name door (kernel/metadata-type-schemas.ts:254). §2's third row ("nobody parses it") therefore does not apply, and a D2 conversion is wired (playbook §3). The three defs leave whole (ErrorMappingConfig, ErrorMappingRule, and — see deviations — the orphaned ConnectorErrorCategory enum) with retired-defs entries under major 18.

PM mechanism assumptions — which held

  • (a) held. Tombstone route; one tombstone, two registered keys (integration/Connector:errorMapping, integration/DeclarativeConnectorEntry:errorMapping); the def schemas and their type aliases deleted, with retired-defs/18.* entries spelled like their neighbours.
  • (b) held. stack.zod.ts:679 parses connectors: z.array(DeclarativeConnectorEntrySchema); metadata-type-schemas.ts:254 binds it to /meta. The conversion is wired, not omitted.
  • (c) held. connector-error-mapping-removed (stripKeys inside mapCollection(stack, 'connectors', …)), toMajor: 18, retiredFromLoadPath: true, fixture expectedNotices: 1 — one notice per connectors[] entry carrying the block; the eleven nested keys leave with it. Disjoint from every other conversion (the only other connectors[] conversions touch rateLimitConfig and fieldMappings[].transform; the fixture carries neither). MIGRATIONS_BY_MAJOR[18].conversionIds and its rationale extended; gen:migration-registry then check:migration-registry green (150 semantic, 88 retired-key, 97 retired-def). origin/main (where A parallel branch inside a loop body overloads the step record's iteration with the branch index — the enclosing loop iteration is lost, so a branch step cannot be attributed to its row #14414 / PR feat(spec)!: ExecutionStepLog.iteration is single-valued (the enclosing loop iteration); the parallel branch index moves to a new optional branch key (#14414) #15227 regenerated registry.ts) merged via scripts/pm/os-regen-merge.sh — merge committed first, regenerated after; both sides verified present and A parallel branch inside a loop body overloads the step record's iteration with the branch index — the enclosing loop iteration is lost, so a branch step cannot be attributed to its row #14414's implementation body byte-identical to origin/main.
  • (d) held, with the count corrected. api-surface/integration.json loses seven exports, not five (the orphan enum, below). The gates fired in the order the playbook predicts and their output is the route evidence: first the manifest ratchet — ❌ 3 previously published schema(s) disappeared from this build (ConnectorErrorCategory / ErrorMappingConfig / ErrorMappingRule) — answered by deleting the three json-schema.manifest/integration.json rows; then gate (a) — ❌ 11 authorable key(s) disappeared from the contract — answered by deleting the eleven authorable-surface/integration.json rows; then check (c) accepted both: 3 schema(s) left the published set since 4dd5041bd0be, each declared (#4725) and 2 baseline deletion(s) since 4dd5041bd0be carry their own proof (#4650). The tombstoned key reads integration/Connector:errorMapping [RETIRED] and integration/DeclarativeConnectorEntry:errorMapping [RETIRED]; authorable-defaults/integration.json lost its two ErrorMappingConfig default rows; declaration-map/, export-origins/, content/docs/references/** regenerated by check:generated --fix, then a clean second run: ✓ All 15 generated artifacts are up to date. authorable-surface.base.json was never hand-edited; the criterion, check:authorable-surface, is green.
  • (e) held. The pin file's header says it is hand-maintained; Iso382 (and Iso381, the enum) leave, and the file's own self-count moved 828 → 826 with a ledger line in its house form. check:spec-parsed-alias: 1516 bare z.input aliases, 826 pinned isomorphic, 690 paired with an XParsed. OK.
  • (f) held. connector has no liveness ledger; check:liveness and check:strictness-ledger green with no edit.
  • (g) held. spec-changes.json and the upgrade guide project nothing for the unreleased major 18 (the siblings' 18-era entries are absent too — handlerStatus, allowRestore: 0 hits); check:spec-changes / check:upgrade-guide green, no diff.
  • (h) held. .changeset/connector-error-mapping-retired.md: @objectstack/spec minor, BREAKING banner, FROM → TO block, ADR-0087 marker — check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition … registered connector-error-mapping-removed (new here); check-changeset-no-major: ✓ This diff introduces no major bump.

Deviations from the claim surface

  1. ConnectorErrorCategorySchema / ConnectorErrorCategory removed as well (same file; declared in the dev claim comment 5537594722 before the first edit). Its only consumers were ErrorMappingRule.targetCategory and ErrorMappingConfig.defaultCategory; outside the declaring file it was referenced only by a value round-trip in connector.test.ts and the Iso381 pin. Playbook §4's orphan-value-schema row (refactor(spec)!: remove the plugin sandboxing / integrity / approval config that never existed (#3896 follow-up) #3950; the api/HandlerStatus enum went the same way in the most recent retirement). retired-defs/18.integration__ConnectorErrorCategory.ts. api/ErrorCategory (the HTTP-response vocabulary, ADR-0112 D9a) is unaffected.
  2. type-alias-convention.pin.test.ts: Iso381 leaves with Iso382; the self-count statements (header, it title, toHaveLength) moved with a ledger line — the file's own convention, asserted by its runtime companion.
  3. json-schema.manifest/integration.json (−3) and authorable-surface/integration.json (−11 rows, +2 [RETIRED] rows) carry hand-deletions the gates demanded; authorable-defaults/integration.json (−2) is regenerated. Generated-artifact paths, listed for completeness.
  4. connector.test.ts also carries an idempotence pin at the spec seam (a second replay over the converted snapshot emits zero notices and returns the input by reference), because the CLI migrate-meta e2e could not run here (see Measurements).

Not touched: #14674's surface (ApiErrorSchema / EnhancedApiErrorSchema), plugin-auth's withValidationErrorMapping, any SKILL.md, content/docs/releases/, docs/adr/** (ADR-0122 appendix D lists the retired aliases as a measured-corpus record of its time; governed, left as history).

Reverse verification

  • Parse channel (pins in connector.test.ts, [#14676]): an authored errorMapping is refused on ConnectorSchema, DeclarativeConnectorEntrySchema, the registry-resolved connector schema and ObjectStackSchema.connectors[] — issue code: 'invalid_type', path: ['errorMapping'] / ['connectors', 0, 'errorMapping'], and the message is the prescription (matched on the key, "was removed", "nothing ever read it", "Delete the key", ApiError.userMessage, "no error-mapping engine exists", the house os migrate meta --from 17 sentence as the last sentence, ADR-0049, and no issue-id token). Positive control: the same stack minus the key parses.
  • tsc channel: a typed Connector literal carrying the key needs @ts-expect-error (input type never); check:test-typecheck compiles the test layer, so an unused directive would red it — check:test-typecheck: OK — 54 file(s) / 261 error(s) / 145 pinned signature(s).
  • Conversion: one attributed notice (conversionId: 'connector-error-mapping-removed', toMajor: 18, path: 'connectors[0].errorMapping'); the output parses through the authoring schema; a second replay yields zero notices and the same reference. The chain-replay fixture test passes for the new id (conversions.test.ts) — i.e. the conversion is wired (playbook §3 reading).
  • Ablation (after committing the fix): replaced the tombstone line in connector.zod.ts with a bare deletion — the silent-strip regression the pins guard. Mutation proved on disk before measuring (tombstone lines before=1 after=0 marker=1). No rebuild was needed and none was done: connector.test.ts resolves ./connector.zod and ../stack.zod from source under vitest (no dist, no alias), so the mutation is measured where it lands. Direction observed: 4 red / 59 green — the three refusal pins and the tsc-agreement pin went red; the no-materialize pin stayed green (absence stays absence either way, as predicted). Restore: git checkout HEAD -- ABSOLUTE_PATH inside an EXIT INT TERM trap; proof: git hash-object on disk 988ef8cbeb0e3072a7f9d413a65283708e203da0 == HEAD blob 988ef8cbeb0e3072a7f9d413a65283708e203da0, git diff HEAD empty for the path, marker count 0, tombstone count 1.

Measurements — head ea77fa6ef (every exit captured before any pipe; verdict lines are the gates' own)

measurement exit verdict line
pnpm --filter @objectstack/spec build (under os-verify-lock.sh) 0 os-verify-lock: VERDICT command-exit 0 · held the lock 146s
vitest src/integration/connector.test.ts src/conversions src/migrations src/shared/retired-key-migrate-sentence.test.ts src/type-alias-convention.pin.test.ts (--maxWorkers=2, locked) 0 Test Files 9 passed (9) / Tests 438 passed (438)
pnpm --filter @objectstack/spec typecheck (locked) 0 check:test-typecheck: OK … / os-verify-lock: VERDICT command-exit 0
check:generated (clean run after --fix) 0 ✓ All 15 generated artifacts are up to date.
check:authorable-surface, check:api-surface, check:docs, check:export-origins, check:declaration-map, check:spec-changes, check:upgrade-guide, check:migration-registry, check:liveness, check:strictness-ledger, check:exported-any, check:dual-source-exports, check:entry-nameability, check:browser-reachable-entries 0 each each gate's own ✓ line (all rows in the os-dev-report on #14676)
check:spec-parsed-alias 0 ADR-0122 type-alias convention: 1516 bare z.input aliases, 826 pinned isomorphic, 690 paired with an XParsed. OK
node scripts/check-adr-0087-registration.mjs --base origin/main 0 ✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.
node scripts/check-changeset-no-major.mjs --base origin/main 0 ✓ This diff introduces no major bump.
pnpm check:doc-authoring 0 ✓ doc authoring guard: 14736 customer-facing string(s) across 734 spec sources clean
pnpm check:nul-bytes 0 check-nul-bytes: OK (scanned 8286 text file(s) …)
node scripts/check-system-context-census.mjs 0 check-system-context-census: OK — 106 elevation read sites in 20 packages across 45 files, all anchored
pnpm lint (whole repo, eslint . --no-inline-config, 89s) 0 eslint prints nothing on success — exit 0 is the verdict
derived union — node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no path args; 20 paths vs merge base 4dd5041bd) — 90 commands 85 × exit 0 all green except the five below
NOT MEASURED (prerequisite not met — the gate's own text) 3 / 1 / 3 / 3 @objectstack/lint check:doc-formula-expressions (PREREQUISITE NOT MET — the workspace package @objectstack/formula is not built), check:doc-security-posture (… @objectstack/lint is not built), spec check:skill-examples (packages/client-react/dist holds no .d.ts declarations — the package is not built), pnpm check:dual-build-cjs-loads (Run pnpm build first. ⛔ This is NOT a pass: nothing was measured.), pnpm check:type-check-debt (exit 3 — its own could-not-measure code). All need the whole-repo build, which exceeds this container's foreground cap; CI's Lint & Repo Gates / TypeScript Type Check own them.
NOT MEASURED — packages/cli/test/migrate-meta.e2e.test.ts — spawns bin/run-dev.js, whose ~50 workspace dependencies have no dist in this worktree; building that closure exceeds the foreground cap. Idempotence is pinned at the spec seam instead (deviation 4), and stripKeys is idempotent by construction (playbook §3). CI's Test Core runs the e2e.
Narrowed — downstream consumer sweep — a monorepo-wide pnpm --filter '...@objectstack/spec' typecheck (the tombstone's tsc sweep of every authoring site) is not runnable within the cap; replaced by the grep census above (zero hits for the seven identifiers and the key across packages/**, examples/**, skills/**, docs). CI's TypeScript Type Check is the sweep.

Changeset

.changeset/connector-error-mapping-retired.md — @objectstack/spec: minor (launch-window convention for a post-17.0.0 accept-set narrowing; the no-major gate is green), BREAKING banner naming the retired key, the seven exports that leave, what stays byte-identical, FROM → TO, and the retirement kit. ADR-0087 marker: registered connector-error-mapping-removed.

Review

Clause ②: needs:contract-review on this PR and on #14676 — the published accept set narrows (errorMapping goes from accepted to refused/stripped). Draft on purpose; the seat flips ready after its review.

🤖 Generated with Claude Code

https://claude.ai/code/session_01H2oQebDDxYKfWZusyd8GXk


Generated by Claude Code

… conversion, entries, pins, changeset

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2oQebDDxYKfWZusyd8GXk
…spec artifacts on the new dist

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2oQebDDxYKfWZusyd8GXk
…keys, one spelled like the live userMessage channel (#14676, ADR-0049)

retiredKey() tombstone on ConnectorSchema.errorMapping (inherited by
DeclarativeConnectorEntrySchema); ErrorMappingConfig / ErrorMappingRule /
ConnectorErrorCategory leave whole; D2 conversion
connector-error-mapping-removed in the step-18 chain; RETIRED_KEYS /
RETIRED_DEFS entries under 18; pins; generated artifacts; changeset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2oQebDDxYKfWZusyd8GXk
@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 27 documentable anchor(s). ⚠️ 9 changed file(s) yielded no anchor (packages/spec/api-surface/integration.json, packages/spec/authorable-defaults/integration.json, packages/spec/authorable-surface/integration.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

7 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via not_found (literal, a string literal in ConnectorErrorCategorySchema), rate_limit (literal, a string literal in ConnectorErrorCategorySchema; a string literal in fixture))
  • content/docs/api/error-catalog.mdx (via not_found (literal, a string literal in ConnectorErrorCategorySchema), rate_limit (literal, a string literal in ConnectorErrorCategorySchema; a string literal in fixture))
  • content/docs/api/error-handling-client.mdx (via server_error (literal, a string literal in ConnectorErrorCategorySchema))
  • content/docs/api/index.mdx (via not_found (literal, a string literal in ConnectorErrorCategorySchema), rate_limit (literal, a string literal in ConnectorErrorCategorySchema; a string literal in fixture))
  • content/docs/kernel/cluster.mdx (via RETIRED_DEFS_BY_MAJOR (symbol, a top-level const object))
  • content/docs/protocol/kernel/realtime-protocol.mdx (via RATE_LIMITED (literal, a string literal in fixture))
  • content/docs/protocol/objectui/concept.mdx (via rate_limit (literal, a string literal in ConnectorErrorCategorySchema; a string literal in fixture))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v15.mdx (via ConnectorSchema (symbol, a top-level const))
  • content/docs/releases/v17.mdx (via ConnectorSchema (symbol, a top-level const), not_found (literal, a string literal in ConnectorErrorCategorySchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 9 changed file(s) yielded no anchor (packages/spec/api-surface/integration.json, packages/spec/authorable-defaults/integration.json, packages/spec/authorable-surface/integration.json, …) — pages documenting those are invisible to this run
  • 5 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 128 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 2ed6be649d3c45af2397fa731265706845107705 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 98c48de347d8f38f47144b581032b67e51e3c06b — the merge of head bc695037078206a6dbbd3e5b1893878b6630486e into base 2ed6be649d3c45af2397fa731265706845107705, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 98c48de347d8f38f47144b581032b67e51e3c06b && git checkout 98c48de347d8f38f47144b581032b67e51e3c06b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2ed6be649d3c45af2397fa731265706845107705 bc695037078206a6dbbd3e5b1893878b6630486e && git checkout -B drift-repro 2ed6be649d3c45af2397fa731265706845107705 && git merge --no-ff bc695037078206a6dbbd3e5b1893878b6630486e

node scripts/docs-audit/affected-docs.mjs --json 2ed6be649d3c45af2397fa731265706845107705

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 2ed6be649d3c45af2397fa731265706845107705 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Sep 4, 2026
@os-justin
os-justin marked this pull request as ready for review September 4, 2026 12:40

Copy link
Copy Markdown
Collaborator Author

Provenance (seat landing stroke, 2026-09-04T12:41Z): Clause ② PASS · ACCEPT recorded on #14676 (comment 5539828597) by the domain:spec seat at CONTRACT_REVIEW_TIER; needs:contract-review cleared on both carriers in that stroke (2026-08-31 ruling); registry resynced on #15289's merge (head bc695037, comment 5540221142); all 34 check runs success on this head (Lint & Repo Gates 12:26Z, Test Core ×6, all Type Check jobs, Governed Surface Queue Guard, Spec property liveness, Check Changeset); migrations/registry.ts unchanged on main since 8f404a51. Not a governed surface. Flipped ready and auto-merge (squash) armed by the seat — the merge queue lands it. hotcrm consumers of the retired errorMapping surface remain NOT MEASURED (recorded on the card and in the body).


Generated by Claude Code

@os-justin
os-justin deleted the claude/issue-14676-retire-connector-error-mapping branch September 4, 2026 13:08
os-justin added a commit that referenced this pull request Sep 4, 2026
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 9, 2026
…zation is the one read face for the user's language (objectstack-ai#14788) (objectstack-ai#15386)

* wip: retire SessionUser.language; /auth/me/localization user-column-first locale (objectstack-ai#14788)

* feat(spec,hono-server): retire SessionUser.language; /auth/me/localization locale = sys_user.locale → Accept-Language → deployment default (objectstack-ai#14788)

* fix(spec): tombstone prescription cites the ADR, not an issue id (check:doc-authoring)

* chore(spec): regenerate the migration registry and references after merging main (objectstack-ai#15299 + this card)

---------

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

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants