Repository navigation
Description
Filing unassigned — recorded as an out-of-scope finding from #7848 / PR #7982 (Prime Directive #10), not started.
What is true after #7848
HttpTestAdapter (packages/core/src/qa/http-adapter.ts) no longer hard-codes /api/data. Its record action types (create_record, read_record, update_record, delete_record, query_records) build their URLs from the two schemas RestServer itself resolves from:
RestApiConfigSchema→apiPath ?? ${basePath}/${version}(the expressionRestServer.getApiBasePath()evaluates)CrudEndpointsConfigSchema.dataPrefix
which is what closed the 404s. But it reads their defaults, because the adapter is handed an origin and nothing else — it never sees the deployment's config.
The gap
Both pieces are configurable, and a deployment that moves the mount is invisible to the adapter:
| config | server serves | adapter asks | result |
|---|---|---|---|
| stock | /api/v1/data/:object |
/api/v1/data/:object |
✅ |
api.apiPath: '/api/2026-01' |
/api/2026-01/data/:object |
/api/v1/data/:object |
❌ 404 |
crud.dataPrefix: '/objects' |
/api/v1/objects/:object |
/api/v1/data/:object |
❌ 404 |
The failure reads exactly like the one #7848 fixed — a 404 that looks like the suite author's own URL mistake rather than a platform limitation. Today's workaround is to write those steps as api_call, which takes the path it is given; that is what the shipped examples/app-showcase/qa/platform-smoke.test.json does throughout.
The shape a fix probably takes
@objectstack/client already answers this question and is the precedent worth copying rather than re-inventing (packages/client/src/index.ts, getRoute): prefer the server's own routes.data from GET {base}/discovery, fall back to the convention when discovery is unavailable or the key is absent. That makes the adapter correct against a re-prefixed host without adding a second declaration of the mount.
Two things to decide rather than assume:
- Bootstrap. Discovery itself is found at a conventional path, so the fallback problem does not disappear, it moves one level up — the client has the same property. Is that acceptable here, or should
os testgrow a flag (--api-path) so an operator can state it? - Cost and failure mode. One memoised probe per run is cheap, but "discovery is disabled on this host" must degrade loudly (Route & surface ownership §3), not silently produce the same 404 with a longer path to the diagnosis.
Not urgent, and why
Every fixture boot in the repo — showcase, CRM, the platform-checklist runs — is stock, so nothing in-repo hits this. It is filed so the limit is a tracked decision rather than a surprise for the first deployment that re-prefixes its API. Recorded in docs/qa/platform-checklist/areas/cli.json (cli.qa-suite-execution, fixtures.knownGaps) and in content/docs/deployment/cli.mdx, so it is stated where an author would look.
Generated by Claude Code
Activity
hotlong commentedon Aug 12, 2026
Triage:
finding(held),domain:cliconfirmed (packages/core/src/qa/http-adapter.tsis qa/cli-lane surface), type Bug.fixtures.knownGaps+deployment/cli.mdx), and the fix carries two genuine open choices (discovery-probe bootstrap vs an--api-pathflag; loud-degradation contract). Those choices deserve a promotion-time decision, not a hold-time guess.HttpTestAdapter/os testconfig plumbing (fold this in, following the@objectstack/clientgetRoutediscovery precedent the card names).本评论来自分诊座位(scheduled session
session_0199Rq2oEnNNRmdhmWwqUwvQ),不构成认领。Generated by Claude Code
hotlong commentedon Aug 13, 2026
State-machine migration, maintainer-authorized (2026-08-13, live triage session: 「我授权你先迁标签」; semantics on #8449): this card is a graded hold — verdict and restart conditions are in the grading comment(s) above — so
finding→pm:on-hold. Verdict, restart conditions, domain unchanged.Generated by Claude Code
os-zhuang commentedon Aug 16, 2026
Requeued
pm:on-hold→pm:queueunder the maintainer's Bug-branch ruling for the on-hold stock reclassification (#9043 scope addendum): the close-default 「有些暂时不准备开发的应该直接关闭。」 does not apply totype:Bugholds — this is a reproducible, user-reachable defect (any deployment settingapi.apiPathorcrud.dataPrefixgets 404s fromHttpTestAdapter), and a held bug is a hidden bug because the selection order's Bug priority only works on queued cards. Relayed by the skills-seat sweep, sessionsession_017TNzEetykdh7ceZGwuAPLq.Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
Claim: PM dispatch seat, round 8
Session:
session_012WKSnqAaoqtW3QX7SSf1VkBranch:
claude/issue-7983-http-adapter-mount-resolutionWorktree:
objectstack-issue-7983Domain:
domain:cliFile surface:
packages/core/src/qa/http-adapter.ts(+ its tests),docs/qa/platform-checklist/areas/cli.json(theknownGapsentry this card wrote),content/docs/deployment/cli.mdx(same),.changeset/**(stop on breach; explain in the report)Container & model: M,
mode:subagent,model: opus—node scripts/pm/dispatch-gates.mjsthis round reports no path-derived mandate (floor sonnet · default opus · ceiling fable). Clause ② judged from card CONTENT and does not fire, because of the Zone 1 ruling below: the discovery-probe route adds no public surface and widens no accepted set. The alternative that would have engaged the manual floor — a newos test --api-pathflag — is refused here for exactly that reason.Serial constraints cleared:
packages/core/src/qa/http-adapter.ts: free. One touch since the card was filed (445ae4deb, an unrelated auth-locale change); no open PR in this repo writespackages/core.docs/qa/platform-checklist/**: free for this file. PR docs(qa,pm): qa-run joins the sweep exclusion list; run records owe defect extraction at close-out #9366 is open on that tree but touches onlyREADME.md,RUNNER.md,runs/README.md— notareas/cli.json. Verified by reading its file list, not by package prefix.packages/client/src/**is READ-ONLY for this card. automation: the run-resume route still answers HTTP 200 wrapping an inner {success:false} — the route #3962's status-code unification left behind #8684 is writing that package this round. ThegetRouteprecedent this card names is to be copied from, never edited.packages/runtime+packages/services/service-automation+packages/client; Report the business identifiers already minted twice by the tenancy split — an operator-facing inventory, on installs the #8686 backfill cannot repair #8928 →packages/cli/src/commands/**; RestServer.probeMcpServeable re-derives the request's environment instead of using the shared entry point — /discovery'smcpadvertisement can follow the wrong kernel #9120 (PR fix(rest): route probeMcpServeable through the shared environment entry point so /discovery's mcp advertisement follows the request's kernel #9291) →packages/rest/src/**. All disjoint from this surface.The promotion-time decision this card was waiting on
Triage graded this a hold in part because "the fix carries two genuine open choices … those choices deserve a promotion-time decision, not a hold-time guess." The 2026-08-16 requeue moved it
pm:on-hold→pm:queueunder the Bug-branch ruling without making that decision, so it is queued with its shape still open. Making it now, before dispatch, rather than letting a dev guess in a worktree:Ruled: the discovery probe. ⛔ Not an
--api-pathflag.Choice 1 is not evenly balanced once the manual floor is applied. A new
os test --api-pathflag widens the public CLI surface — that is aFeature, it sits on the maintainer-only floor, and it is not mine to authorize or a dev's to build. The discovery probe adds no public surface and re-uses a shipped, precedented pattern (@objectstack/client'sgetRoute+ memoiseddiscoveryInfo,packages/client/src/index.ts:492,524-563). So the route is decided by which option this seat is allowed to decide, not by preference.Ruled: loud degradation is a requirement, not a choice.
Choice 2 was never really open — Route & surface ownership §3 already governs it, and the card states the failure mode precisely: "discovery is disabled on this host" must degrade loudly, naming the mount it resolved and why, not reproduce the same 404 with a longer path to the diagnosis. One memoised probe per run.
The part I expect to be wrong, handed over as an assumption
@objectstack/clienthardcodes${baseUrl}/api/v1/discoveryand falls back to.well-known— so a host that movesapi.apiPathmoves discovery itself out from under the probe. My reading is therefore thatcrud.dataPrefix(which moves the data path under a findable api base) is recoverable, whileapi.apiPath(which moves the base the probe needs to find) may not be. A fix that honestly closes only one of the two rows in the card's table is an acceptable outcome — but then theknownGapsline is narrowed, not deleted, and the PR body states which row survives. This is the dev's to measure, not to inherit from me.Full brief, including the read-coupling with #9292's
/discoveryhandler (pin against whateverroutes.datasays, never against a literal/api/v1/data, so the pins survive that card), went to the agent.Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
Dev process died in a container restart — re-dispatched as a recovery run. The claim is unchanged and this card was never unattended. PM
domain:cliseat (#6024), sessionsession_012WKSnqAaoqtW3QX7SSf1Vk.Recording this so the gap in the timeline is a known cause rather than a mystery, and so nobody reads the empty remote branch as an abandoned claim.
Cause is known and is not the dev's: the container hosting this session restarted at ~21:5xZ and took the running
os-devprocess with it. That is not a maintainer abort (no signal, no instruction) and not the self-death shape that triggers stale-claim reclaim — and the reclaim test does not fire here anyway, since the claim is this same seat's and only hours old.What survived, verified before re-dispatching (read-only inspection — this seat writes no code):
951627219, carrying the whole fix:packages/core/src/qa/http-adapter.tsand its test file.docs/qa/platform-checklist/areas/cli.json,content/docs/deployment/cli.mdx).origin/main, so nothing had been pushed and no PR exists. That is the only reason this needed a dispatch rather than a nudge.The measured result, which I am recording now because it answers the question this card was held on for months — and which the recovery agent has been told to re-verify rather than inherit:
crud.dataPrefix: '/objects'HTTP Error 404api.apiPath: '/api/2026-01'HTTP Error 404HTTP Error 404, now naming the mountThat is exactly the split the dispatch predicted as its expected falsification:
dataPrefixis recoverable,apiPathis not, becauseapiPathmoves the base that/discoveryitself sits under. So theknownGapsentry gets narrowed, not deleted — which is what the brief said the honest outcome would look like.The dev also established a control worth keeping on the record:
/.well-known/objectstackadvertises the dispatcher's${prefix}/data, measured as/api/v1/dataunder all three configs above, so probing it would attach a false provenance ("discovery told us") to the same 404. It is deliberately not probed.The recovery dispatch's one added instruction: state explicitly which of the surviving work was re-verified and which was taken as-is. A test run that passed before the restart is not evidence now.
Generated by Claude Code
fix(qa): HttpTestAdapter resolves the data mount from discovery, and …
docs(qa): narrow the HttpTestAdapter mount gap to the apiPath row (#7983
os-zhuang commentedon Aug 17, 2026
{ "issue": 7983, "status": "done", "branch": "claude/issue-7983-http-adapter-mount-resolution", "pr": "https://github.com/objectstack-ai/objectstack/pull/9385", "premise_still_valid": true, "summary": "RECOVERY run. The predecessor's commit 951627219 (http-adapter.ts + http-adapter.test.ts) and its three uncommitted files survived intact; I re-verified rather than trusted them, committed the remainder as 99592d0ab, pushed, and opened draft PR #9385. HttpTestAdapter now resolves the Data Protocol mount from one memoised GET {apiBase}/discovery per run, addressing whatever routes.data advertises, falling back to the RestApiConfigSchema + CrudEndpointsConfigSchema convention with a loud diagnostic that names the mount, the failed probe and the remedy; every 404/405 from a record action carries the same provenance, while 400/422 stays undecorated. Zone 1 honoured: no --api-path flag, packages/client untouched, no content/docs change beyond deployment/cli.mdx. RE-VERIFIED BY ME: the full before/after table (re-derived independently, see tests), the knownGaps and cli.mdx narrowing (both narrow the apiPath row rather than delete it), the whole test suite, the gate union, the type-check ratchet, absence of control bytes, and that the built dist carries the new code. TAKEN AS-IS: only the predecessor's claim that its original table came from an actually booted three-config stack — I could not re-observe that boot, so I re-derived every row by a different route instead, and all three reproduce.", "tests": "All on final commit 99592d0ab (git rev-parse --short HEAD), clean tree, gate union run AFTER that commit. (1) pnpm --filter @objectstack/core test => 'Test Files 35 passed (35) / Tests 857 passed (857)', exit 0. (2) TYPECHECK NOTE, not a template value: @objectstack/core has NO typecheck script, so pnpm --filter @objectstack/core typecheck would match zero scripts and exit 0 without checking anything — I did not report that as a pass. The real measurement is the ratchet: pnpm check:type-check-debt (workspace closure built first via turbo run build --filter=./packages/* --filter=./packages/*/*, 70/70 cached FULL TURBO) => '33 ledger entr(ies) re-measured in 214.2s, 1926 raw tsc error(s) total, none above its recorded number. surplus: none — every entry sits exactly at its measurement, so any new error is red.' MEASURED, green. (3) Gate union re-derived on my ACTUAL changed paths (node scripts/pm/dispatch-gates.mjs) and all run green: check:changeset-gate-self-tests, check:cross-package-test-inputs, check:docs-audit-scope, check:docs-redirects, check:role-word, check:kernel-hook-pairs, check:test-source-alias, check:objectui-changeset, the four @objectstack/spec liveness checks (empty-state, liveness, strictness-ledger, variant-docs — each echoed its script name, so none was a zero-match silent green), scripts/check-adr-0087-registration.mjs, check-changeset-no-major.mjs, check-cross-package-test-inputs.mjs, check-empty-changeset.mjs, docs-audit/check-affected-docs.mjs. The derivation ALSO named a convention-triggered family the earlier list did not (new test file): check:query-options-erasure (ratchet holds, none new), check:engine-double-contract (OK, 317 pinned), check:where-matcher (253 matchers, 0 silently-wrong, none new), check:type-check-coverage. All green; nothing weakened, no threshold lowered, no baseline rebaselined, no test skipped. (4) TABLE RE-VERIFICATION, by a route independent of the predecessor's boot: constructed RestServer under the three configs and read BOTH bases back off the server's own normalized config (constructor is (server, protocol, config) — config is the THIRD argument; my first harness passed it first and produced a false 'row 3 does not reproduce', which was my own bug, corrected before drawing any conclusion). Result reproduces every row: stock serves /api/v1/data (before HIT, after HIT); crud.dataPrefix '/objects' serves /api/v1/objects (before /api/v1/data MISS 404, after /api/v1/objects HIT — row CLOSED); api.apiPath '/api/2026-01' serves /api/2026-01/data with discovery itself moved to /api/2026-01/discovery so the probe 404s (before MISS, after MISS, announced — row NOT closed). Mechanisms confirmed at source: rest-server.ts mounts discovery at ${basePath}/discovery, and its handler sets discovery.routes.data = ${realBase}${crud.dataPrefix}. The control is now STRONGER than three samples: /.well-known/objectstack is the runtime dispatcher's (dispatcher-plugin.ts serves it from getDiscoveryInfo(prefix)) and http-dispatcher.ts composes data: ${prefix}/data from the dispatcher's own prefix, never reading crud.dataPrefix or the REST apiPath — so it is structurally independent of both configs, which is why probing it would attach a false provenance to the same 404. Deliberately not probed, and pinned as such. (5) Corroboration: packages/rest direct-mount-base-follows-apipath.test.ts 6/6 passed, including 'advertises the moved bases in {apiPath}/discovery'. (6) Hygiene: grep -naP over all five changed files found no control bytes; built dist confirmed to carry the new probe code before any judgement was drawn; PR body audited so the only closing keyword pairs with #7983 and no bare '<'+letter can truncate it.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
ACCEPT — PM
domain:cliseat (#6024), sessionsession_012WKSnqAaoqtW3QX7SSf1VkPR #9385 (recovery run, final commit
99592d0ab). Enqueued; all six required checks are running clean, nothing red.The PM's promotion-time ruling held, and its predicted falsification landed exactly
The dispatch ruled discovery probe, ⛔ no
os test --api-pathflag — decided by which option this seat is allowed to decide, since a new CLI flag widens the public surface onto the maintainer-only floor. Verified on the branch: zero occurrences of--api-pathanywhere underpackages/, with a control query (apiPathin the adapter, 7 hits) proving the search works.packages/clientuntouched, as required while #8684 writes it.The handover also predicted the fix would close only one of the two rows, and it does —
crud.dataPrefixclosed,api.apiPathnot, becauseapiPathmoves the discovery document itself out from under the probe. So theknownGapsentry is narrowed, not deleted, which is exactly the honest outcome the brief described. I checked that specifically rather than accepting the claim: the row now states which config is reached and which is not, plus arevision: 3history entry carrying the measured before/after.Three things this run did that are worth more than the fix
configas the first constructor argument whenRestServertakes it third, which produced a false "row 3 does not reproduce". It caught and corrected that rather than reporting a falsification — the difference between a real finding and a self-inflicted one.@objectstack/corehas notypecheckscript, sopnpm --filter @objectstack/core typecheckmatches zero scripts and exits 0 without checking anything. It explicitly declined to report that as a pass and measured the real thing instead —check:type-check-debt, on a built closure: "33 ledger entries re-measured, 1926 raw tsc errors, none above its recorded number. surplus: none." A zero-match command that exits 0 is the purest form of the aggregate-reads-green failure, and it named it./.well-knowncontrol from empirical to structural. The earlier run had three samples showing/.well-known/objectstackreporting/api/v1/dataregardless of config. This run established why: the dispatcher serves it fromgetDiscoveryInfo(prefix)and composesdata: ${prefix}/datafrom its own prefix, never readingcrud.dataPrefixor the RESTapiPath. That makes it structurally independent of both configs — so probing it would attach a false provenance ("discovery told us") to the same 404. Deliberately not probed, and pinned as such.Recovery discipline
The dispatch's one added instruction was to state which surviving work was re-verified and which was taken as-is. It answered precisely: everything re-verified except the predecessor's claim that its table came from a booted three-config stack — which it could not re-observe, so it re-derived every row by an independent route instead, and all three reproduce. That is the right way to handle inherited evidence.
Gate union re-derived on the actual changed paths and green throughout, including a convention-triggered family the original dispatch list did not name (new test file ⇒
check:query-options-erasure,check:engine-double-contract,check:where-matcher,check:type-check-coverage). Nothing weakened, no baseline rebaselined, no test skipped.Generated by Claude Code
fix(qa): HttpTestAdapter resolves the Data Protocol mount from discov…