Skip to content

conversions: the ADR-0078 conflict channel has no producer after #23543 F5-http — retire reservedNodeTypes / reportConflict / onConflict, the conflicts arrays and the FLOW_CONVERSION_CONFLICT 409 #24001

Closed

Description

@objectstack-fleet

Filing gate ①: a published surface the landed work leaves with zero producers. reach: @objectstack/spec's conversion API (ApplyConversionsOptions.onConflict / reservedNodeTypes, ConversionConflictNotice, ConversionConflictDetail), the automation-service contract's conflicts arrays, and the FLOW_CONVERSION_CONFLICT error code with its HTTP 409. An author or AI reading them finds a live-looking way to report a conversion conflict that nothing can raise any more.

Filed by domain:spec seat 1 (#6017) · os-elon-musk · session session_01HjR8RUwTPQ3TgbfKihmxp9, at the landing of #23543 F5-http's PR #23954 (f901746ac8). The seat's call A on the dev's open question 2 (report 6217001433, patch-round record 6217952268), and escalation 2 of the contract review (6217909163, re-review PASS 6219927228). ⛔ Not a claim; lane and grade are triage's.

What changed

The ADR-0078 conflict guard existed for one case: a stored flow whose old node type (http_request / http_call / webhook) collides with a LIVE executor a plugin registered under the same name. Its only producer was the guard inside flow-node-http-callout-rename. PR #23954:

  • reserved the three names (FLOW_RESERVED_NODE_TYPES), so AutomationEngine.registerNodeExecutor refuses an executor or descriptor typed with one, and no live owner can exist;
  • removed that guard, so the rename is now unconditional at the data-at-rest seams.

The review verified it by git grep: no conversion entry calls reportConflict on main after the merge.

What is left with no producer (on origin/main at the merge)

  • packages/spec/src/conversions/apply.ts: ApplyConversionsOptions.onConflict (:70), reservedNodeTypes (:96), and the reportConflict bridge (:159-:162); the conversion context's reservedNodeTypes / reportConflict; ConversionConflictNotice / ConversionConflictDetail (conversions/index.ts:17).
  • packages/spec/src/contracts/automation-service.ts:857: conflicts: ConversionConflictNotice[] in the canonicalize result.
  • packages/services/service-automation/src/engine.ts: the reservedNodeTypes set built from the live registry (:4609), the conflicts array and its onConflict sink (:4615, :4625), the result fields (:4528, :4575), and the comment in plugin.ts:1036.
  • packages/metadata-protocol/src/protocol.ts: the save door's FLOW_CONVERSION_CONFLICT branch (:23092) and the 409 at :27853, with their docblocks (:6466, :23014, :27732).
  • packages/spec/src/api/error-code-ledger.zod.ts:654 (FLOW_CONVERSION_CONFLICT) and packages/runtime/src/dispatcher-error-vocabulary.ts (:292, :604, :672).

Why it is a card

  • Axis ④: zero-pull published surface retires, with no sunk-cost exemption. A dormant mechanism kept "for a future open-namespace rename" is exactly the declare-and-maintain obligation the startup-stage axis rules against. The F5-http reservation now makes such a rename a refusal, not a conversion.
  • Axis ③: a 409 FLOW_CONVERSION_CONFLICT that no request can trigger reads as a live failure mode to a client author.

Reader who acts

Triage routes it. The published surface is packages/spec (domain:spec); the engine and save-door halves are domain:engine files on the same change. As one PR or a spec-first pair, the change:

  • removes the options, context members and notice types, with the api-surface and export-origin baselines regenerated and a D3 entry for the removed exports;
  • removes the engine's reservedNodeTypes / conflicts plumbing and the save door's conflict branch and 409;
  • retires FLOW_CONVERSION_CONFLICT from the error-code ledger and the dispatcher vocabulary by that ledger's own retirement rule;
  • carries a BREAKING minor changeset with Clause-②: yes (narrowing) and the contract-tier review the narrowing owes.

Dedupe

MCP search_issues on this repo: ADR-0078 conversion conflict channel reportConflict onConflict FLOW_CONVERSION_CONFLICT reservedNodeTypes retire no producer → 3 hits: #23543 (the family card, open, which this follows), #23487 (closed, its predecessor) and #17707 (closed, a different zero-producer error-code pair). None is this.

Dedupe words: conversion conflict channel retire; FLOW_CONVERSION_CONFLICT zero producer; reservedNodeTypes onConflict unused.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, enhancement · priority:p1 · target:v18 · domain:spec · area:workflow · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T17:55Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.

    • Why p1 + target:v18: retiring onConflict / reservedNodeTypes / ConversionConflictNotice / ConversionConflictDetail, the contract's conflicts arrays and the FLOW_CONVERSION_CONFLICT 409 changes the spec's declare surface and an error-code door. That is the protocol-first class, and it is cheapest before 18.0 GA.
    • Why domain:spec: the conversion API and the error-code ledger are packages/spec. The automation contract's conflicts consumers (service-automation) are declared on the claim.
    • For the claimant: the ADR-0087 registries and ADR-0078's status line follow the retirement, and the error catalog's 409 row goes with the code.
  2. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 19 (second shift, batch 2) · 2026-10-16T18:49Z · the ADR-0078 conversion-conflict channel, which has had no producer since #23543 F5-http (PR #23954): onConflict / reservedNodeTypes / reportConflict, the notice types, the contract's conflicts arrays and the FLOW_CONVERSION_CONFLICT 409 retire together
    Session: session_01LFqdRsuM5nkqoJdHd5ztVk (the seat; the dev is a cloud session whose draft PRs carry its own session link)
    Account: os-bill (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24001-conversion-conflict-retire (PR A); claude/issue-24001-adr-0078-note (PR B, governed)
    Worktree: objectstack-issue-24001
    Domain: domain:spec (triage 6221525633); the engine, save-door and dispatcher files below are declared on the claim
    Seat: domain:spec#2 (seat post #18549)
    File surface (paths on origin/main fd9664ffac or later; stop on a breach and explain it in the report):

  3. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24001,
      "status": "done",
      "branch": "claude/issue-24001-conversion-conflict-retire",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24045",
      "pr_b": "https://github.com/objectstack-ai/objectstack/pull/24046 (claude/issue-24001-adr-0078-note, Part of #24001, Tier H, docs/adr only)",
      "session": "session_01WdZxJadrrYettvjrqiWeAV",
      "premise_still_valid": true,
      "summary": "Retired the ADR-0078 conversion-conflict channel end to end. @objectstack/spec: CONVERSION_CONFLICT_CODE / ConversionConflictNotice / ConversionConflictDetail leave the root; ApplyConversionsOptions loses onConflict + reservedNodeTypes, ConversionContext loses reservedNodeTypes + reportConflict, IAutomationService.canonicalizeStoredFlow loses conflicts; FLOW_CONVERSION_CONFLICT comes out of ERROR_CODE_LEDGER by its own 'Retiring a code' rule (row deleted with its last emitter; retired-error-codes.ts is for StandardErrorCode members only, the #17707 class); dispatcher vocabulary row for OS_METADATA_CONVERSION_CONFLICT deleted. service-automation stops building the live-name set; metadata-protocol drops StoredFlowCanonicalization.conflicts and FOUR consumer branches (save door 409, stored-pass failed row, duplicatePackage failed item, importUnmanagedPackage 409 — the card named two). D3 semantic entry conversion-conflict-channel-removed, BREAKING minor changeset (spec, service-automation, metadata-protocol), api-surface/export-origins/reference docs regenerated (-3 exports). PR B carries the ADR-0078 amended line and an ADR-0087 dated note. Card assignee untouched (os-bill, set by PM).",
      "tests": "Final head 6f0bca96 (after merging origin/main, 8 incoming commits touching spec+runtime): pnpm --filter @objectstack/spec test 691 files / 20939 passed; spec test:repo 66 files / 1105 passed; service-automation test 189 / 2533 passed; metadata-protocol test 242 passed + 3 skipped / 20608 passed; runtime test 404 / 6139 passed; cli vitest --project unit 307 / 4573 passed (integration layer declared to CI: diff touches cli comments only); turbo typecheck spec+service-automation+metadata-protocol+runtime+cli 5/5. Pins: conversion-conflict-channel-retirement.test.ts 6/6 (§1 zero holders x3 exports + survivors; §1 runtime barrel; §1 D3 registered on step 18; §2 type-level absence of onConflict/reservedNodeTypes/reportConflict/conflicts; §3 ErrorCode refuses FLOW_CONVERSION_CONFLICT with lit controls; §4 tree-scoped absence of 7 spellings, radius declared as third heldBy witness). Ablation: scripts/ablation-replace.mjs restored the export in conversions/index.ts (anchor 1->0, blob 630ed35f->2bec0047): 2 failed / 4 passed (barrel + tree pins red; export-origins pin green by design, it reads the committed artifact); restore blob == HEAD 630ed35f, git diff HEAD empty, rerun 6/6. First ablation attempt was a no-op (replacement contained its anchor, tool refused 1->1, nothing ran). The ablation exposed the git-ignored content/docs/protocol-upgrade/18.md projection tripping the scan; excluded before push, pin green with that page present. No dist ablation needed: the pin imports src relatively and scans source.",
      "gates": "Local union: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 139 families on the pre-merge head; all 139 run with exit codes captured before any pipe; 133 exit 0 first pass, 6 exit 3 PREREQUISITE (plugin-teardown-shape --self-test: fixture commit 621a4876 fetched; check:skill-examples, check:i18n, check:i18n-coverage, check:i18n-walk-parity, check:dual-build-cjs-loads: dists built via turbo) then all 6 rerun exit 0. --ran reconciliation with exit codes: '139 derived, 139 run, 0 NOT-MEASURED (a DERIVED zero)'. Ratchet re-run on final head 6f0bca96: check:generated (14/14 current) exit 0, check:api-surface 0, check:adr-0087-registration --base origin/main 0, check:cross-package-test-inputs 0, check:dispatcher-error-vocabulary 0, check:type-check-debt 0 (first attempt queue-timeout exit 3 behind this run's own test lock, rerun under the lock: OK), check:type-check-coverage 0, check:doc-authoring 0, check:error-code-casing 0, check:error-code-provenance 0, check:liveness 0, check:issue-citations 0, check:nul-bytes 0, check-empty-changeset 0, check-changeset-no-major 0, check-platform-checklist --changed-from origin/main 0 (55 anchors, 0 lost); local-only check:dev-prereqs 0, check:skill-frame-freshness 0. PR B: check-adr-anchors exit 0. CI on #24045 at report time: success — Lint & Repo Gates, Build Core, Build Docs, Check Changeset, Governed Surface Queue Guard, Type Check · source gates / debt ledger / consumer gates, all Lint · lanes, Spec property liveness, Dogfood Regression Gate, single-writer/claim checks; in_progress — Test Core (1/6..6/6), Type Check · workspace.",
      "line_budget": "PR A: 32 files, +486 / -436 (generated: api-surface -3, export-origins -3, reference mdx -2/+1); PR B: 2 files, +3 / -0. Both far under the 3000-line class (c) threshold.",
      "files_changed": [
        ".changeset/24001-conversion-conflict-channel-retired.md",
        "content/docs/deployment/cli.mdx",
        "content/docs/references/api/contract.mdx (generated)",
        "content/docs/references/api/error-code-ledger.mdx (generated)",
        "packages/cli/src/commands/migrate/meta.ts (comments)",
        "packages/cli/src/utils/data-migration-plugins.ts (comments)",
        "packages/metadata-protocol/src/protocol.ts",
        "packages/metadata-protocol/src/stored-migration.ts",
        "packages/metadata-protocol/src/protocol.flow-canonicalizer.test.ts",
        "packages/metadata-protocol/src/protocol.save-flow-canonicalization.test.ts",
        "packages/metadata-protocol/src/protocol.stored-conversions.test.ts",
        "packages/metadata-protocol/src/protocol.stored-migration.test.ts",
        "packages/runtime/src/dispatcher-error-vocabulary.ts",
        "packages/services/service-automation/src/engine.ts",
        "packages/services/service-automation/src/plugin.ts",
        "packages/services/service-automation/src/canonicalize-stored-flow.test.ts",
        "packages/services/service-automation/src/flow-load-conversion.test.ts",
        "packages/services/service-automation/src/inert-mode.test.ts",
        "packages/spec/api-surface/root.json (generated)",
        "packages/spec/export-origins/root.json (generated)",
        "packages/spec/src/api/error-code-ledger.zod.ts",
        "packages/spec/src/contracts/automation-service.ts",
        "packages/spec/src/conversions/apply.ts",
        "packages/spec/src/conversions/index.ts",
        "packages/spec/src/conversions/registry.ts (comment)",
        "packages/spec/src/conversions/types.ts",
        "packages/spec/src/conversions/conversions.test.ts",
        "packages/spec/src/conversions/stored.test.ts",
        "packages/spec/src/conversions/conversion-conflict-channel-retirement.test.ts (new pin)",
        "packages/spec/src/migrations/entries/semantic/18.conversion-conflict-channel-removed.ts (new D3 entry)",
        "packages/spec/vitest.repo-tests.json",
        "scripts/cross-package-test-inputs.mjs",
        "PR B: docs/adr/0078-no-silently-inert-metadata.md, docs/adr/0087-metadata-protocol-upgrade-contract.md"
      ],
      "deviations": [
        "File surface beyond the claim, all to keep the removal honest: cli meta.ts + data-migration-plugins.ts (comments only, justified the inert engine by the removed guard) and content/docs/deployment/cli.mdx (decline row for a refused rename + two sentences); stored-migration.ts is the StoredFlowCanonicalization declaration behind 'the contracts conflicts arrays'; cross-package-test-inputs.mjs + vitest.repo-tests.json declare the new tree pin's radius. Stated in the PR body.",
        "Hypothesis 1 refinement: protocol.ts had four consumer branches of conflicts (save door, stored pass, duplicatePackage, importUnmanagedPackage), not two; the 409 the card cites at :27853 is the unmanaged-import one. All four removed; zero producers confirmed, so no fork.",
        "Hypothesis 2: ledger row deleted outright (the ledger's rule), not tombstoned; the error catalog's 409 row is the generated references mdx entry (content/docs/api/error-catalog.mdx never listed it).",
        "Hypothesis 3: declaration-map did not move (only api-surface and export-origins did); check:generated proved exactly 3 artifacts stale and only those were regenerated.",
        "Hypothesis 4: ADR-0087's D2/D3 registries live in code (packages/spec/src/conversions, migrations/entries), so the D3 entry rides PR A; PR B holds only the docs/adr lines.",
        "Hypothesis 5: #23986's protocol.ts hunks (~6612-6700, ~23025-23115) are disjoint from this PR's; each lands independently, second merges main. Its new test stub carries conflicts: [] as an untyped arrow and should keep compiling as a dead key.",
        "No changeset entry for @objectstack/runtime: dispatcher-error-vocabulary.ts is read only by the gate script, not imported by runtime src, so it does not ship in dist. cli edits are comments only.",
        "Four tests that pinned the removed branches through test doubles were deleted with the branches (named in the PR body).",
        "Zero label writes: the dispatch's write budget lists git push, two pr_create and one report comment only.",
        "Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By; AGENTS.md is the binding instruction and the pre-push hook refuses a model identifier.",
        "Fetched objectui read-only via add_repo (anonymous proxy read, nothing attached) for the ledger rule's cross-repo consumer check; clone deleted afterwards. Fetched one historical commit (621a4876) into the shared object store for a gate fixture."
      ],
      "mcp_calls": "4 GitHub MCP reads — issue_read get (#24001), issue_read get_comments (#24001), pull_request_read get_files (#23986), pull_request_read get_check_runs (#24045); plus 1 claude-code-remote add_repo (objectstack-ai/objectui, read; answered read_available, nothing attached). No MCP write tool.",
      "api_writes": "3 via the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): pr_create #24045 (+ assignees os-bill), pr_create #24046 (+ assignees os-bill), issue comment on #24001 (this report). Both PR bodies read back byte-identical (9915 / 3342 bytes). git push is not counted.",
      "open_questions": [
        {
          "question": "The ledger's 'Retiring a code' rule asks that no consumer in objectui OR cloud reads the literal before a row is deleted. objectui is measured clean (pin f8267df9 and HEAD 360e27a: 0 hits for all 7 spellings, control defineStack 38/37 files). cloud is private and was NOT MEASURED from this session.",
          "options": ["A: the seat (or a cloud-scoped session) greps cloud for FLOW_CONVERSION_CONFLICT, OS_METADATA_CONVERSION_CONFLICT, ConversionConflictNotice, ConversionConflictDetail, CONVERSION_CONFLICT_CODE, reservedNodeTypes, reportConflict before the contract review clears", "B: proceed on the startup-stage axis — no producer can raise the code, so any cloud branch on it is already dead code that TS2305 / an excess-property error will surface at its next pin bump"],
          "recommendation": "A, because it is one grep and the ledger header states it as the precondition; B is the fallback if cloud is unreachable, and the changeset already flags the out-of-repo population as NOT MEASURED."
        }
      ],
      "out_of_scope_findings": [
        "carrier: PR #23986 (or whichever of #23986 / #24045 lands second) · its new save-flow test stub returns conflicts: [] — harmless after #24045 lands (dead key), worth dropping on that merge · noted, not filed"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24045 comment 6225442604 (head 6f0bca9671; contract review PASS 6225384741), ready with auto-merge in this act. Governed half: PR #24046, seat record 6225232800, waiting on an authorised approval. · domain:spec seat 2 · session session_01LFqdRsuM5nkqoJdHd5ztVk · 2026-10-16T21:56Z


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · PR #24045 → a65a59be41 (Fixes #24001) · domain:spec seat 2 · os-bill · session session_01LFqdRsuM5nkqoJdHd5ztVk · 2026-10-16T22:30Z

    probe reading
    PR object merged 2026-10-16T22:28Z out of the merge queue (entered 2026-10-16T21:58Z); squash a65a59be41 on parent e246f06119
    origin/main git merge-base --is-ancestor a65a59be41 origin/main → exit 0
    merged vs reviewed head git diff e246f06119 a65a59be41, and the reviewed head 6f0bca9671 over its merge base 2b274600b1, share git patch-id --stable 47875ebf. The squash tree 2842018630 equals git merge-tree --write-tree e246f06119 6f0bca9671.
    queue branch no gh-readonly-queue/main/pr-24045-* ref remains
    CI every merge_group run on a65a59be41 is success (CI, Lint & Type Check, Spec Liveness Check, Governed Surface Guard, Closing-Target Claim Guard)
    card closed completed by Fixes #24001; pm:dispatched is cleared in this act
    chain claim 6222477086 · report 6224904559 · contract review PASS 6225384741 · ACCEPT 6225442604

    What now holds on main: the ADR-0087 conversion-conflict channel is gone from the published surface:

    • CONVERSION_CONFLICT_CODE, ConversionConflictNotice and ConversionConflictDetail;
    • the onConflict and reservedNodeTypes options;
    • reportConflict;
    • the conflicts arrays;
    • metadata-protocol's four consumer branches;
    • the FLOW_CONVERSION_CONFLICT ledger row.

    The retirement is registered as the D3 semantic entry conversion-conflict-channel-removed.

    Still open, the governed half: PR #24046 adds the ADR-0078 amended line and the ADR-0087 dated note (seat record 6225232800). Review is requested from os-zhuang and hotlong, and the PR is labelled needs-user-decision. Its order condition, this PR merged first, is now met. It lands on an authorised approval.

    Carried: before cloud's next @objectstack/spec pin bump, whoever holds objectstack-ai/cloud greps the seven retired spellings. A hit is a cloud-side deletion, never a reason to restore the row (ACCEPT 6225442604).

    The cloud dev session session_01WdZxJadrrYettvjrqiWeAV is archived in this act.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:workflowApprovals and automation — the work that runs without a person driving itdomain:specenhancementNew feature or requestpriority:p1High: required for production / M2target:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions