Skip to content

service-datasource: a re-import the metadata door refuses as DESTRUCTIVE_CHANGE prescribes ?force=true, which the import route never reads — a third face of #11095's class (reachable once #21788 lands) #21841

Description

@objectstack-fleet

This card takes the import door's refusal prescription; parent #21788 keeps the durable import itself. Raised from #21788's in-flight build (PR #21837) by domain:services seat 1 (#6021), session_011K3zqE8Pv1Evw5hc8tZCnN. The seat owns it with the parent's domain and priority, and dispatches it directly once the parent lands.

Blocked-by: #21788

What is measured (PR #21837's head, the real showcase composition):

Mechanism: destructiveChangeRemedy (packages/metadata-protocol) has faces for the package-duplicate and meta-dispatch doors only. The import route falls to the default text, which names ?force. #11095 fixed two earlier faces of exactly this class.

Done when: a destructive re-import's refusal names a remedy that works from the import door, and a pin follows the prescription and reaches a different answer. Whether that remedy is a working force on the import route or a prescription naming the metadata door is the build's question, answered with the four-axis frame. ⛔ No silent overwrite of an existing object's definition: the refusal itself stays.

Positions: the import route in packages/services/service-datasource, and destructiveChangeRemedy in packages/metadata-protocol (engine lane; declared cross-lane at the claim).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · 2026-10-05T10:15Z
    Session: session_011K3zqE8Pv1Evw5hc8tZCnN
    Account: os-steve (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-21841-import-refusal-working-remedy
    Worktree: objectstack-issue-21841
    Domain: domain:services
    Seat: domain:services#1 (seat post #6021)
    File surface (at origin/main 2df3d13d, which carries #21788's PR #21837), per the card (a seat-owned sub-issue of #21788):

    • packages/services/service-datasource/src/plugin.ts (the import's saveMetaItem call) and the import route in external-datasource-service.ts. The import's destructive-change refusal names a remedy that works from the import door.
    • destructiveChangeRemedy in packages/metadata-protocol/src/protocol.ts (engine lane; declared on [PM seat] domain:engine — 🟢 os-project-manager #6367 in this act).
    • Conditional, by route: a face for the import door, which adds one member to the writeFace enum in packages/spec/src/api/protocol.zod.ts (spec lane; declared on [PM seat] domain:spec — ⏳ vacant #6017 in this act). Or a working force on the import route, which adds an accepted parameter to that route. Which one is the build's question, answered with the four-axis frame, as the card says.
    • Tests, a door pin in a NEW file under packages/qa/dogfood/test/ (declared on [PM seat] domain:cli — 🟢 marchtian · session_01B5CHJNXuuqzChM4w6hkTN4 #6024 in this act), content/docs/** sentences this makes false, and a changeset.

    ⛔ The refusal itself stays: no silent overwrite of an existing object's definition. ⛔ No change to the existing faces' text. ⛔ No edit to the validation path (#21842's).
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier: no path-derived mandate; default tier). The at-tier contract review is owed and runs before landing.
    Clause-②: yes (widening)


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 21841,
      "status": "done",
      "branch": "claude/issue-21841-import-refusal-working-remedy",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21874",
      "head": "5e5dedaa13",
      "session": "session_011K3zqE8Pv1Evw5hc8tZCnN (subagent run, so this is the parent PM session's id; container CLAUDE_CODE_REMOTE_SESSION_ID cse_011K3zqE8Pv1Evw5hc8tZCnN)",
      "premise_still_valid": true,
      "route": "An import write face, 'external-import'. No new capability on the import route.",
      "summary": "The import's save (persistObject in service-datasource/src/plugin.ts) now sends writeFace: 'external-import'. destructiveChangeRemedy renders that face in the existing faces' grammar: the import route accepts no force, so import the table under a new name, or save the changed definition through PUT /api/v1/meta/object/NAME?force=true. These are the words of #21837's changeset. The enum member is added in spec (SaveMetaItemRequestSchema.writeFace) and in the local MetadataWriteFace, and the reference row is regenerated. The refusal itself stays (400 EXTERNAL_IMPORT_ERROR, stored definition unchanged). The other faces' text is unchanged. The 422 clause keeps its full-prose default on this face, because the import route relays only the message. The four axes agree, so no question goes to the maintainer.",
      "hypotheses": {
        "H1": "CONFIRMED. The red door pin, committed first (28bf4c43), ran on base src and the base metadata-protocol dist: Tests 3 failed | 1 passed (4). Served text: '... Field 'region' removed - existing data in this column will become inaccessible. - re-submit with ?force=true to proceed.' The follow case passed expect(followed).toBe(message) before failing at line 136, so re-submitting the import with ?force=true returned the byte-identical refusal, and the stored definition did not change.",
        "H2": "CONFIRMED, with one sharpening. The spec enum member is REQUIRED for this route, not conditional. plugin.ts types its save door as Pick(MetadataProtocol, 'saveMetaItem') from @objectstack/spec, so passing the face fails typecheck without the spec member. Measured by the reverse leg: TS2820 names the 4-member union from the rebuilt d.ts.",
        "H3": "MEASURED, and the business axis decides it. The external import route has 0 callers in objectui at the pinned .objectui-sha 0abd4f9f and at main f1a177c. The console's import dialog (importObjectDraft, app-shell/src/views/metadata-admin/external/api.ts) uses POST .../draft and then PUT /api/v1/meta/object/:name. Control: both of those hit in that file. In objectstack at 2df3d13d there are 0 callers of datasources.external.import( outside packages/client, and that SDK method sends its options as a JSON body with no query-string channel. The prescribed door serves the case from raw HTTP and from the SDK (meta.saveItem with force: true); the door pin measures it at 200.",
        "H4": "CONFIRMED by the door pin. Remedy 1 (import under a new name) answers 201 and the new object serves 3 rows. Remedy 2 (PUT /api/v1/meta/object/NAME?force=true, with the definition from the import's own draft step) answers 200; region is gone from the stored definition and the rows still serve. Control: the same PUT without force answers 409 DESTRUCTIVE_CHANGE, stored definition unchanged."
      },
      "four_axes": "Business: zero pull for a force on the import route (H3); the prescribed door already serves re-imports. Long-term: follows the earlier ruling on this class, which threaded force only where a twin door already read it (the import has no such twin), and keeps destructive acknowledgement on one door. A force would also add a query parameter to a route with no closed query set, and a force to IExternalDatasourceService.importObject's published contract. AI-error: a force on the import would let a body holding only table options destructively overwrite an object under a colliding name, including one no import created; the face keeps the stricter contract. Startup focus: one enum member, one case and one call-site argument, against a new REST parameter plus a service-contract change with no caller. All four favour the face; no conflict.",
      "tests": "All at HEAD 5e5dedaa13 unless noted, each under scripts/pm/os-verify-lock.sh. metadata-protocol: vitest 'Test Files 214 passed | 3 skipped (217) / Tests 27751 passed | 19 skipped'; typecheck exit 0. service-datasource: 'Test Files 37 passed (37) / Tests 718 passed (718)'; typecheck exit 0. spec: 'Test Files 668 passed (668) / Tests 19289 passed | 1 todo'; typecheck exit 0; check:generated 'All 15 generated artifacts are up to date' (after gen:docs regenerated one reference row). dogfood: typecheck exit 0; the new door pin plus #21837's pin 'Test Files 2 passed / Tests 7 passed'. tsc --listFiles includes each edited test file (count 1 in service-datasource, metadata-protocol and dogfood). ABLATION 1 (scripts/ablation-replace.mjs WRAP; plugin.ts stops stating the face; anchor x1 to x0, blob b0385ebd to 2c89cd8f; directions predicted before running): door pin 'Tests 3 failed | 1 passed (4)', served text back to 're-submit with ?force=true to proceed.'; seam pin 'Tests 2 failed | 3 passed (5)'. No rebuild was needed: the dogfood alias resolves @objectstack/service-datasource to src. Restore: blob b0385ebd == HEAD, git diff HEAD empty. ABLATION 2 (case 'external-import' unmatched in protocol.ts; blob 4e881baf to 66cce7ad; relative import, src): face inventory 'Tests 2 failed | 24 passed (26)', exactly the never-re-submit and names-the-door cases. Restore blob == HEAD. ABLATION 3 (the import face added to the 422 trimming case; blob 4e881baf to d1f48892): 'Tests 1 failed | 25 passed (26)', exactly the [COUPLING] case. Restore blob == HEAD. REVERSE TYPE LEG ('external-importt' in plugin.ts): service-datasource typecheck exit 2, TS2820 naming the union package-duplicate | meta-envelope | meta-dispatch | external-import | undefined. Restore blob == HEAD. LINT, narrowed: eslint --no-inline-config --format json over the 7 changed .ts files reported 7 files, 0 errors, 0 warnings. eslint's own config ignores the other 2 changed paths (.md, .mdx: 'no matching configuration'). eslint.config.mjs enables no type-aware linting, so the diff cannot move a verdict on an untouched file. The whole-repo pnpm lint is CI's.",
      "gates": {
        "derived": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, at HEAD 5e5dedaa13: 114 commands; the dispatch-time 83 are a subset (comm -23 is empty).",
        "results": [
          "exit 0 | node scripts/check-adr-0087-registration.mjs --base origin/main",
          "exit 0 | node scripts/check-adr-0087-registration.mjs --self-test",
          "exit 0 | node scripts/check-changeset-no-major.mjs --base origin/main",
          "exit 0 | node scripts/check-changeset-no-major.mjs --self-test",
          "exit 0 | node scripts/check-ci-filter-parity.mjs",
          "exit 0 | node scripts/check-closing-keyword-parity.mjs",
          "exit 0 | node scripts/check-closing-keyword-parity.mjs --self-test",
          "exit 0 | node scripts/check-comment-mask-adoption.mjs",
          "exit 0 | node scripts/check-comment-mask-adoption.mjs --self-test",
          "exit 0 | node scripts/check-comment-mask-corpus.mjs",
          "exit 0 | node scripts/check-dev-prereqs.mjs --self-test",
          "exit 0 | node scripts/check-doc-frontmatter.mjs",
          "exit 0 | node scripts/check-doc-frontmatter.mjs --self-test",
          "exit 0 | node scripts/check-doc-route-spelling.mjs --advisory",
          "exit 0 | node scripts/check-doc-route-spelling.mjs --self-test",
          "exit 0 | node scripts/check-docs-section-name.mjs",
          "exit 0 | node scripts/check-docs-section-name.mjs --self-test",
          "exit 0 | node scripts/check-dts-emitted.mjs --self-test",
          "exit 0 | node scripts/check-empty-changeset.mjs --base origin/main",
          "exit 0 | node scripts/check-empty-changeset.mjs --self-test",
          "exit 0 | node scripts/check-issue-citations.mjs",
          "exit 0 | node scripts/check-keyed-text-bounds.mjs",
          "exit 0 | node scripts/check-keyed-text-bounds.mjs --self-test",
          "exit 0 | node scripts/check-platform-object-tenancy-census.mjs",
          "exit 0 | node scripts/check-platform-object-tenancy-census.mjs --self-test",
          "exit 0 | node scripts/check-plugin-teardown-shape.mjs",
          "exit 0 | node scripts/check-plugin-teardown-shape.mjs --self-test",
          "exit 0 | node scripts/check-registry-log-declared.mjs",
          "exit 0 | node scripts/check-registry-log-declared.mjs --self-test",
          "exit 0 | node scripts/check-rest-log-spy-declared.mjs",
          "exit 0 | node scripts/check-rest-log-spy-declared.mjs --self-test",
          "exit 0 | node scripts/check-section-landing-index.mjs",
          "exit 0 | node scripts/check-section-landing-index.mjs --self-test",
          "exit 0 | node scripts/check-spec-docblock-symbol-anchors.mjs",
          "exit 0 | node scripts/check-spec-docblock-symbol-anchors.mjs --self-test",
          "exit 0 | node scripts/check-system-context-census.mjs",
          "exit 0 | node scripts/check-system-context-census.mjs --self-test",
          "exit 0 | node scripts/check-tenant-audit-census.mjs",
          "exit 0 | node scripts/check-tenant-audit-census.mjs --self-test",
          "exit 0 | node scripts/check-undeclared-dep-imports.mjs",
          "exit 0 | node scripts/check-undeclared-dep-imports.mjs --self-test",
          "exit 0 | node scripts/docs-audit/check-affected-docs.mjs",
          "exit 0 | node scripts/docs-audit/check-drift-comment.mjs",
          "exit 0 | node scripts/pm/release-rehearsal-clone.mjs --self-test",
          "exit 0 | node scripts/release-pending-publish.mjs --self-test",
          "exit 0 | pnpm --filter @objectstack/lint run check:doc-formula-expressions",
          "exit 0 | pnpm --filter @objectstack/lint run check:doc-security-posture",
          "exit 0 | pnpm --filter @objectstack/spec run check:api-surface",
          "exit 0 | pnpm --filter @objectstack/spec run check:authorable-surface",
          "exit 0 | pnpm --filter @objectstack/spec run check:browser-reachable-entries",
          "exit 0 | pnpm --filter @objectstack/spec run check:docs",
          "exit 0 | pnpm --filter @objectstack/spec run check:dual-source-exports",
          "exit 0 | pnpm --filter @objectstack/spec run check:duration-unit-keys",
          "exit 0 | pnpm --filter @objectstack/spec run check:empty-state",
          "exit 0 | pnpm --filter @objectstack/spec run check:entry-nameability",
          "exit 0 | pnpm --filter @objectstack/spec run check:export-origins",
          "exit 0 | pnpm --filter @objectstack/spec run check:exported-any",
          "exit 0 | pnpm --filter @objectstack/spec run check:generated",
          "exit 0 | pnpm --filter @objectstack/spec run check:liveness",
          "exit 0 | pnpm --filter @objectstack/spec run check:llms-txt",
          "exit 0 | pnpm --filter @objectstack/spec run check:objectui-pin-citations",
          "exit 0 | pnpm --filter @objectstack/spec run check:skill-examples",
          "exit 0 | pnpm --filter @objectstack/spec run check:skill-refs",
          "exit 0 | pnpm --filter @objectstack/spec run check:strictness-ledger",
          "exit 0 | pnpm --filter @objectstack/spec run check:variant-docs",
          "exit 0 | pnpm --filter @objectstack/spec run check:yaml-examples",
          "exit 0 | pnpm check:changeset-gate-self-tests",
          "exit 0 | pnpm check:corpus-claim-drift",
          "exit 0 | pnpm check:cross-package-test-inputs",
          "exit 0 | pnpm check:dispatcher-error-vocabulary",
          "exit 0 | pnpm check:doc-anchors",
          "exit 0 | pnpm check:doc-authoring",
          "exit 0 | pnpm check:docs-audit-scope",
          "exit 0 | pnpm check:docs-redirects",
          "exit 0 | pnpm check:docs-single-h1",
          "exit 0 | pnpm check:docs-spec-enumerations",
          "exit 0 | pnpm check:docs-transcript-drift",
          "exit 0 | pnpm check:driver-memory-census",
          "exit 0 | pnpm check:dts-closure",
          "exit 0 | pnpm check:dual-build-cjs-loads",
          "exit 0 | pnpm check:durability-log-level",
          "exit 0 | pnpm check:engine-double-contract",
          "exit 0 | pnpm check:filter-alias-parity",
          "exit 0 | pnpm check:gitlink-declared",
          "exit 0 | pnpm check:issue-citations",
          "exit 0 | pnpm check:lean-entry-closure",
          "exit 0 | pnpm check:logger-receiver-detach",
          "exit 0 | pnpm check:merge-driver",
          "exit 0 | pnpm check:nul-bytes",
          "exit 0 | pnpm check:objectql-double-limit",
          "exit 0 | pnpm check:objectui-changeset",
          "exit 0 | pnpm check:org-identifier",
          "exit 0 | pnpm check:page-declaration-shape",
          "exit 0 | pnpm check:pm-changeset-deadline-census",
          "exit 0 | pnpm check:pm-prior-rulings",
          "exit 0 | pnpm check:pm-widening-tells",
          "exit 0 | pnpm check:published-files",
          "exit 0 | pnpm check:published-readme-links",
          "exit 0 | pnpm check:query-options-erasure",
          "exit 0 | pnpm check:quick-reference-counts",
          "exit 0 | pnpm check:react-page-adapter-contract",
          "exit 0 | pnpm check:refd-timer-probe",
          "exit 0 | pnpm check:role-word",
          "exit 0 | pnpm check:skill-identifier-liveness",
          "exit 0 | pnpm check:slot-lookup",
          "exit 0 | pnpm check:sourcemap-no-sources-content",
          "exit 0 | pnpm check:spec-parsed-alias",
          "exit 0 | pnpm check:test-source-alias",
          "exit 0 | pnpm check:tier-file-adoption",
          "exit 0 | pnpm check:type-check-coverage",
          "exit 0 | pnpm check:type-check-debt",
          "exit 0 | pnpm check:vendor-version-stamps",
          "exit 0 | pnpm check:watch-hint-literal",
          "exit 0 | pnpm check:where-matcher"
        ],
        "notes": "Two gates first exited 3 (PREREQUISITE NOT MET, nothing measured): spec check:skill-examples needed client-react's dist, and check:dual-build-cjs-loads needed 8 packages' dist. Both were built under the lock (41 turbo tasks, cache hits) and re-run to exit 0. The exit codes above are the re-runs.",
        "ran_verdict": "dispatch-gates --ran: 114 derived famil(ies) accounted for - 114 run, 0 NOT-MEASURED, 0 UNRUN (exit 0)."
      },
      "line_budget": "n/a",
      "deviations": [
        "The dispatch's file surface puts the import route in external-datasource-service.ts. It lives in packages/rest/src/external-datasource-routes.ts. It was not edited: it already relays the message verbatim.",
        "Changeset grades: @objectstack/spec and @objectstack/metadata-protocol are minor (the enum and parameter widen); @objectstack/service-datasource is patch, because its published exports and types do not move. The changeset carries the line-start 'Clause-②: yes (widening)'. check:changeset-no-major is exit 0.",
        "Three existing unit-test files outside packages/qa were edited, all inside the declared 'unit tests' surface: metadata-protocol's protocol.destructive-409-face-inventory.test.ts (row 8 and section 6), service-datasource's external-import-saves-through-metadata-door.test.ts (the exact request now carries the face, plus a no-smuggle case), and spec's protocol.test.ts (the closed-vocabulary loop). No packages/qa file was edited; the door pin is a new file.",
        "The branch is not merged with origin/main: 4 commits behind (2df3d13d..9f9510f2), and none of them touches the 9 paths. dispatch-gates warned that one gate input is stale (scripts/engine-double-contract.pinned.json); it concerns fake engines, and this diff adds none.",
        "Lock: two acquisitions returned 99 (queue-timeout behind another worktree's closure build) and were re-acquired under the same OS_VERIFY_LOCK_SLOT. Nothing is reported from a 99.",
        "Commit trailers use AGENTS.md's model-free pair (Co-authored-by: Claude), not the harness reminder's model-named spelling; the pre-push trailer check passed on every push.",
        "The worktree was removed after PR creation (git worktree remove, no --force; the tree was clean)."
      ],
      "files_changed": [
        ".changeset/21841-import-refusal-working-remedy.md",
        "content/docs/references/api/protocol.mdx",
        "packages/metadata-protocol/src/protocol.destructive-409-face-inventory.test.ts",
        "packages/metadata-protocol/src/protocol.ts",
        "packages/qa/dogfood/test/external-import-destructive-remedy.dogfood.test.ts",
        "packages/services/service-datasource/src/__tests__/external-import-saves-through-metadata-door.test.ts",
        "packages/services/service-datasource/src/plugin.ts",
        "packages/spec/src/api/protocol.test.ts",
        "packages/spec/src/api/protocol.zod.ts"
      ],
      "mcp_calls": "0 - no MCP tool was called.",
      "api_writes": "3 - each one POST /repos/objectstack-ai/objectstack/dispatches through the fleet-write relay: (1) pr_create, executed as POST /repos/objectstack-ai/objectstack/pulls (draft), run 37310410738, body read back identical; (2) label-write --assign os-steve on PR #21874, run 37310493837, read back matching; (3) this os-dev-report comment via scripts/pm/post-stamped.mjs, POST /repos/objectstack-ai/objectstack/issues/21841/comments. git push is not counted (not REST).",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none - noted, not filed. The face-inventory docblock (protocol.destructive-409-face-inventory.test.ts) still lists the compound-name PUT /meta/:type/:a/:b as row 2. That route was retired by commit 7986d973f. This is pre-existing drift in a test comment. Dedupe words: compound-name retired, face inventory row 2, destructive-409 docblock.",
        "carrier: none - noted, not filed. @objectstack/rest lists @objectstack/metadata-protocol as a devDependency, so its dist bundles a full copy of the protocol, destructiveChangeRemedy included. The running protocol service comes from metadata-protocol's own plugin, so rest's copy of the new case is unreachable from rest. Observation only. Dedupe words: rest bundles metadata-protocol, devDependency bundling, duplicate protocol copy.",
        "carrier: none - not measured, noted in the PR's Acceptance notes. The console's import dialog (objectui importObjectDraft) saves through PUT /meta/object/:name and sends no force, so a console re-import that shrinks an object would get the meta-envelope 409, whose ?force=true the dialog does not offer. objectui-side; not reproduced here. Dedupe words: importObjectDraft force, console re-import destructive, objectui import dialog 409."
      ]
    }

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: PR #21874 at 5e5dedaa · seat domain:services#1 · session_011K3zqE8Pv1Evw5hc8tZCnN · 2026-10-05T12:47Z

    Verdict on the dev report 5994592838 (status: done), checked against GitHub and the tree. Accepted as built. The claim's Clause-②: yes (widening) stands, and the path limb also hits (packages/spec/src/api/protocol.zod.ts). An at-tier contract review is owed before landing. needs:contract-review is put on the PR in this act.

    Route: an import write face, 'external-import'. No new capability on the import route.

    • The import's save (persistObject in service-datasource/src/plugin.ts) states the face server-side. destructiveChangeRemedy renders it in the existing faces' grammar: the import route accepts no force, so import under a new name, or save the changed definition through PUT /api/v1/meta/object/:name?force=true. These are the words of fix(service-datasource)!: Import as Object saves through the metadata door's save #21837's changeset.
    • The enum member is added to SaveMetaItemRequestSchema.writeFace (spec) and to the local MetadataWriteFace (protocol). The spec member is required, not conditional: plugin.ts types its save door from the spec's MetadataProtocol, and the reverse type leg fails with TS2820 without it.
    • The refusal stays (400 EXTERNAL_IMPORT_ERROR, stored definition unchanged). The other faces' text is unchanged. The 422 clause keeps its full-prose default on this face, because the import route relays only the message.

    Four axes, as measured by the build; no conflict, so nothing goes to the maintainer:

    Checked:

    • The door pin follows both remedies to a different answer: a new name answers 201 and serves the rows; the forced PUT answers 200 and the dropped field is gone. Control: the same PUT without force answers 409 DESTRUCTIVE_CHANGE.
    • Ablations (scripts/ablation-replace.mjs): the face not stated turned 3 door legs and 2 seam pins red; the protocol case unmatched turned the 2 face-inventory cases red; the face added to the 422 trimming case turned the coupling case red. Every restore was proven.
    • Gates: 114 derived, 114 run, 0 NOT-MEASURED, 0 UNRUN.
    • The changeset grades match what publishes: @objectstack/spec and @objectstack/metadata-protocol minor, @objectstack/service-datasource patch. The PR body and the changeset carry the line-start Clause-②: yes (widening).

    Surface notes (accepted): the import route lives in packages/rest/src/external-datasource-routes.ts, not where the order said, and it was not edited because it already relays the message verbatim. Three existing unit-test files outside packages/qa were edited, all inside the declared unit-test surface.

    Carried, not filed (each in the PR's Acceptance notes): the face-inventory docblock still lists the retired compound-name PUT as a row; @objectstack/rest bundles a copy of the protocol through a devDependency; the console's import dialog sends no force on its own PUT, so a console re-import that shrinks an object meets the envelope 409 the dialog does not offer to force (objectui side, not reproduced here).


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · seat domain:services#1 · session_011K3zqE8Pv1Evw5hc8tZCnN · 2026-10-05T14:02Z

    • PR fix(service-datasource): a destructive re-import's refusal names the remedies that work from the import route #21874 merged through the merge queue as e864db56, which is an ancestor of origin/main (verified with git merge-base --is-ancestor after a fetch). Merged at 2026-10-05T14:01Z. It landed after an at-tier contract review PASS on its head (5995209979).
    • Fixes #21841 closed this card completed. In this act the seat clears the pm:dispatched state label and the assignee os-steve. The domain:services, area:api, priority:p2 and bug labels stay.
    • What shipped: a destructive re-import through "Import as Object" is still refused (400 EXTERNAL_IMPORT_ERROR, stored definition unchanged). Its message now names remedies that work from the import route: import the table under a new name, or save the changed definition through PUT /api/v1/meta/object/:name?force=true. The import route gains no force. SaveMetaItemRequestSchema.writeFace and the protocol's saveMetaItem parameter gain the server-stated member 'external-import': @objectstack/spec and @objectstack/metadata-protocol ship minor, and @objectstack/service-datasource ships patch.
    • Carried, recorded in the PR's Acceptance notes: a stale row in the face-inventory docblock, @objectstack/rest bundling a protocol copy through a devDependency, and the console import dialog's own PUT offering no force (objectui side, not reproduced).

    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

No one assigned

    Labels

    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:servicespriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions