Skip to content

datasources: POST /datasources/NAME/test answers an unknown name 200 {ok:false} where every sibling NAME route answers 404 (the #25486 family) #27510

Closed

Description

@objectstack-fleet

domain:devx#1 · sunhaolin · session_017xRBcSwDXZrZg7nGjP6P3U

Filing gate: ① a product defect with a reproduction, found by an off-clause probe of QA run #27438 (wave 3 of the 18.3.0 sweep, anchor #27377). reach: measured at the public datasource admin doors.
Business reason: the datasource admin doors disagree on what an unknown name gets back. POST …/NAME/test answers 200 {ok:false} while every sibling route answers 404. An admin tool or script that tests a datasource by name cannot tell "this datasource is down" from "this datasource does not exist".
Reader who acts: the triage seat, then the implementer for packages/services/service-datasource (the admin REST routes).
Dedupe (open AND closed): the query "datasources test unknown name returns 200 ok false instead of 404; datasource test draft creates sqlite file" returned 34 hits. The nearest is #25486 (closed): "three doors answer an unknown resource as empty or bad input … where their siblings answer 404". That is the same family; this door is a fourth instance it did not cover. #27100 (closed) changed this door's message after a failed connect, not the unknown-name status.

Batch-of: 16 · QA run #27438

Symptom

POST /api/v1/datasources/NAME/test answers an unknown name with 200 {ok:false} where its sibling routes answer 404.

Reproduction (subject c4c515a, scratch copy of the showcase, admin)

  1. POST /api/v1/datasources/nope/test → 200 {ok:false, error:"[ObjectQL] Datasource 'nope' has no registered driver to introspect."}
  2. GET /api/v1/datasources/nope, PATCH …/nope and the other :name routes → 404 RESOURCE_NOT_FOUND.

Expected: 404 RESOURCE_NOT_FOUND for an unknown saved name, the same as the siblings. {ok:false} should be kept for a datasource that exists and fails its probe.

QA-source: #27438 · integration-system.datasource-admin-lifecycle · observed beside the clauses


Generated by Claude Code

Activity

  1. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Oct 29, 2026
  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 可对外的 API | integration-system.datasource-admin-lifecycle | P②

    Triage: p2 · domain:server · area:api · 2026-10-29T11:16Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw

    Claim: PM loop round 3 · in-seat subagent · p2 · 2026-10-29T12:01Z
    Session: session_01L6bhgXcN9HaZYSEUdJWEjw
    Account: os-sales (the seat's linked user; the card's assignee)
    Branch: claude/issue-27510-datasource-test-unknown-404
    Worktree: objectstack-issue-27510
    Domain: domain:server
    Seat: domain:server#1
    Ruling-ref: triage 6495190501: resolve the saved name with the lookup the sibling routes use and answer RESOURCE_NOT_FOUND; keep {ok:false} for a datasource that exists and fails its probe.
    File surface: packages/services/service-datasource/src/admin-routes.ts and/or external-datasource-service.ts, a test beside the existing datasource-test suites, datasource-route-ledger.ts only if its :name/test note states what the route answers (with its conformance test), one changeset (stop on breach; explain in the report)
    Container & model: S, mode:subagent (general-purpose subagent loading .claude/agents/os-dev.md as its role file), model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: POST /datasources/NAME/test answers an unknown name 200 {ok:false} where GET/PATCH/DELETE /datasources/NAME answer 404 | a caller or the console reads a typo as "connection failed" | any change to the admin datasource routes
    Thread-read: 6495190501
    Premise: holds (read at origin/main aef631b80b: admin-routes.ts:756-766 calls testConnection(name) with no existence check; testConnection catches every error into {ok:false}; GET /:name answers 404 at admin-routes.ts:749). Two cautions carried into the order: the primary datasource (default) may have no metadata record (#24772) and must still be testable; remote-tables and object-draft answer 400 for an unknown name, a separate discrepancy, out of scope here.
    Serial constraints cleared: no open PR touches the service-datasource admin routes or service (all 24 open PRs' file lists read).

  4. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw
    os-dev-report

    {
      "issue": 27510,
      "status": "done",
      "branch": "claude/issue-27510-datasource-test-unknown-404",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27574",
      "session": "session_01L6bhgXcN9HaZYSEUdJWEjw",
      "premise_still_valid": true,
      "summary": "The premise holds at base d4ff1a025f: the Test door called testConnection(name) without checking that the name exists, and testConnection caught every error into ok:false. ExternalDatasourceService.testConnection now reads the stored record first through getDatasource. That is the same metadata.get('datasource', name) read that the sibling doors' getDatasourceRecord makes. When nothing is stored, it throws a 404 RESOURCE_NOT_FOUND error in GET's words, and the existing badRequest relay in admin-routes.ts answers it as GET /datasources/NAME does, so the route handler is unchanged. The primary datasource (default) is exempt by name and is still probed by its live driver with or without a stored record. A saved datasource that fails its probe keeps 200 {ok:false}. FILE-SURFACE EXTENSION (the claim names no existing test files): five existing pins in primary-datasource-test-and-sync.test.ts and saved-datasource-test-after-failed-connect.test.ts tested 'a saved datasource with no live driver' under a name with no stored record. Two of them hit /ghost/test over HTTP and so pinned this exact defect, which means any fix turns them red. Their assertions are unchanged; each fixture now stores that datasource's record. The seat should amend the claim's file surface to cover these two files. Not touched: datasource-route-ledger.ts, because its :name/test note does not state what the route answers. The worktree at /home/claude/objectstack-issue-27510 is left in place, clean and pushed, for the seat to remove.",
      "tests": "Red first at base d4ff1a025f: the new src/__tests__/saved-datasource-test-unknown-name.test.ts (real HTTP door over the real composition) gave 'Tests 2 failed | 3 passed (5)'; the two unknown-name cases received status 200, success true. After the fix, on the final head 1ae90cf405 (origin/main merged), under the os-verify-lock: VERDICT command-exit 0 for 'pnpm --filter @objectstack/metadata-protocol --filter @objectstack/service-datasource build && pnpm --filter @objectstack/service-datasource test && ... typecheck', with 'Test Files 59 passed (59)' and 'Tests 989 passed (989)'. tsc --noEmit exited 0 and covers all three touched test files (--listFiles count 3/3). eslint --no-inline-config packages/services/service-datasource exited 0. Ablation via scripts/ablation-replace.mjs in wrap mode (the subject is imported from src by a relative path, so no dist is involved): A1 replaced the guard with 'if (false ...)' and gave '2 failed | 3 passed (5)', both unknown-name cases. A2 dropped the default exemption and gave '1 failed | 4 passed (5)', the primary-with-no-record control. Each leg showed anchor 1 to 0 and a changed blob, then restored to blob 4750f4f6e7fb == HEAD with an empty git diff HEAD. The tree was clean afterwards.",
      "gates": "Local: dispatch-gates --commands --repo objectstack-ai/objectstack at 1ae90cf405 derived 62 families. All ran with exit codes captured before any pipe. The --ran reconciliation with ':: exit N' records reads '62 derived, 61 run, 1 NOT-MEASURED, 0 UNRUN'. The 61 exited 0. NOT MEASURED: pnpm check:dual-build-cjs-loads, exit 3, PREREQUISITE NOT MET (needs a full-repo build; CI owns it). The diff touches no build config or exports. eslint on the package exited 0. Overlap: 0 of 30 open PRs touch any file in the diff, so no merge-tree was needed. origin/main was merged once before pr_create. CI on PR #27574 head 1ae90cf405, read once with no polling: in_progress for Check Changeset, Check PR Size, Governed Surface Queue Guard, Lint (ESLint, PM dispatch-gates, repo and tooling gates, docs and code guards, CI/release/engine gates, scoped x3), PR-body guards, and Type Check (workspace, source gates, consumer gates, debt ledger). The opt-in jobs were skipped.",
      "dogfood_battery": "n/a: the PR title has no '!' and Clause-2 carries no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2: pr_create through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, which executed POST /repos/objectstack-ai/objectstack/pulls; read-back 9416 bytes sent and 9416 stored, identical; draft, no assignees, no reviewers, no labels), and this os-dev-report comment through post-stamped (POST /repos/objectstack-ai/objectstack/issues/27510/comments). The git pushes are not REST writes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "GET /datasources/NAME/remote-tables and POST /datasources/NAME/object-draft answer an unknown name 400 EXTERNAL_DATASOURCE_ERROR, where the by-name siblings answer 404. Code path: assertFederationSource calls getDatasource, gets undefined, isFederationSource(undefined) is false, and the door throws notAFederationSourceError. Not measured over HTTP in this run. Dedupe words: datasource remote-tables object-draft unknown name 400 instead of 404 · carrier: domain:server#1 seat (named in the claim's Premise as a separate discrepancy) · noted, not filed"
      ],
      "deviations": [
        "File surface: two existing test files were edited beyond the claim, as a forced fixture triage (see summary).",
        "No Skills-docs line in the PR body or changeset: the seat terms list one, but os-dev wins (its Definition of done adds Skills-docs only when the kind is not none or the (narrowing) arm is present, and the claim carries none).",
        "Commit trailers use AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude) instead of the harness reminder's model-named trailer (seat terms rule 7; the pre-push hook confirmed it)."
      ]
    }
  5. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw

    Claim amendment: declaration and file surface · 2026-10-29T13:35Z
    Clause-②: no (narrowing). The claim's no was the seat's error. POST /datasources/NAME/test answered an unknown name 200 {ok:false} and now refuses it with 404 RESOURCE_NOT_FOUND. A request that was answered 200 is now refused, which is a narrowing of a REST door. The precedent is exact: #26801 (6279efef6f, security explain refuses a userId with no sys_user record with 404) declared no (narrowing) with a ! title. Owed now: a ! title, the changeset's **BREAKING** banner and ADR-0087 marker (level stays patch, since Metadata-protocol: none), the dogfood battery, a Skills-docs: line, and one CONTRACT_REVIEW_TIER review run by the seat before the queue.
    File surface (added): packages/services/service-datasource/src/__tests__/primary-datasource-test-and-sync.test.ts and saved-datasource-test-after-failed-connect.test.ts, fixture records only. Two of their pins drove /ghost/test over HTTP and so pinned this defect; their assertions are unchanged.
    The rest of the claim stands.

  6. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw
    os-dev-report

    {
      "issue": 27510,
      "status": "done",
      "branch": "claude/issue-27510-datasource-test-unknown-404",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27574",
      "session": "session_01L6bhgXcN9HaZYSEUdJWEjw",
      "premise_still_valid": true,
      "summary": "Patch round after claim amendment 6498154139. The new head is b09ea62c76 (pushed), and the only commit since 1ae90cf405 is the changeset; no code changed. The changeset is modelled on 6279efef6f (#26801). Its title line has '!'. It now declares 'Clause-②: no (narrowing)' and carries the ADR-0087 marker 'not-required (no-migration-prescription)' and a **BREAKING** banner with Before / Now / Unchanged / Who sees it / The remedy sections. The level stays @objectstack/service-datasource patch, with Metadata-protocol: none. The exact Clause-② line for the PR body: 'Clause-②: no (narrowing) — `POST /api/v1/datasources/:name/test` and the in-process `ExternalDatasourceService.testConnection(name)` refuse a name that resolves no saved datasource with `404 RESOURCE_NOT_FOUND`, which they answered `200 { ok: false }` before. Nothing is widened.' The Skills-docs line: 'Skills-docs: none — no content/docs page or skills/** file states what `POST /api/v1/datasources/:name/test` answers (grep for the route, `test_connection` and `testConnection` over content/docs and skills/ found none; data-modeling/external-datasources.mdx names only `GET /api/v1/datasources`); the `RESOURCE_NOT_FOUND` example in protocol/kernel/error-handling.mdx is `GET /api/v1/datasources/:name`, whose answer this door now matches'. The PR title and body were not edited; the seat patches them. The worktree is kept.",
      "tests": "No code changed in this round; the package readings from 1ae90cf405 stand (59/59 files, 989/989 tests, typecheck 0, eslint 0, ablations A1 and A2 red and restored). Dogfood battery: see dogfood_battery.",
      "gates": "Changeset gates on committed head b09ea62c76: node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition … [BREAKING+bang+clause-②-narrowing] not-required (no-migration-prescription)'); --self-test :: exit 0 (705 assertions); node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 ('no major bump'); node scripts/check-empty-changeset.mjs --base origin/main :: exit 0; pnpm check:changeset-gate-self-tests, check:nul-bytes, check:doc-authoring, check:issue-citations, check:pm-changeset-deadline-census, node scripts/check-closing-keyword-parity.mjs and node scripts/docs-audit/check-affected-docs.mjs :: exit 0 each. One reading to note: run against the UNCOMMITTED changeset, check-adr-0087-registration reported 'no declared-breaking changeset (1 non-breaking seen)' and exited 0. It reads committed changesets, so only the post-commit reading above counts. Re-deriving dispatch-gates --commands at the new head gave the same 62 families as before, and the earlier --ran reading (61 exit 0, 1 NOT MEASURED: check:dual-build-cjs-loads, exit 3, needs a full-repo build) stands. CI on b09ea62c76 was not read in this round.",
      "dogfood_battery": "b09ea62c76 · os-verify-lock: VERDICT command-exit 0, held the lock 3167s, for pnpm --filter '@objectstack/dogfood^...' build && pnpm --filter @objectstack/dogfood test, with no OS_TEST_SHARD and no path · Test Files 350 passed | 3 skipped (353) · Tests 2817 passed | 20 skipped (2837) · no failures",
      "mcp_calls": "0",
      "api_writes": "1 in this round: this os-dev-report comment through post-stamped (POST /repos/objectstack-ai/objectstack/issues/27510/comments). The git push is not a REST write. The previous round's 2 writes are unchanged.",
      "open_questions": [],
      "out_of_scope_findings": [
        "Unchanged from the first report: GET /datasources/NAME/remote-tables and POST /datasources/NAME/object-draft answer an unknown name 400 EXTERNAL_DATASOURCE_ERROR (from the code path read; not measured over HTTP) · carrier: domain:server#1 seat · noted, not filed"
      ]
    }
  7. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw

    Landed — #27574 · 2026-10-29T15:15Z

    Probe Reading
    PR #27574 merged 2026-10-29T15:15Z · merge commit e270dab6f9f9ae6f2ba2b49f3c651acdc33cabbd · head b09ea62c764f4d63a128b14220f87b00f4254be3
    #27510 closed (completed) by the PR's Fixes line
    Follow-up the federation doors (remote-tables, object-draft) answering an unknown name 400 are #27618; not part of this card

    State change: pm:dispatched removed, assignee os-sales cleared. Nothing remains on this card.

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:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions