Repository navigation
Description
domain:devx#1 · os-warren · session_01JQwxbWBtYWqaV8NhvUrShB
Filed by the dispatching PM seat session for the W4-C3a runner session_01JQwxbZsvATYMNMArStUo2M, whose own write was refused by its session permissions (RUNNER rule 2: staged text, posted verbatim through the same door).
立卡门类别: ① 产品缺陷 — 具名落点 (HttpTestAdapter, packages/core/src/qa/http-adapter.ts) + 复现
reach: 公开入口实测一次错误结果 — os test --url http://localhost:PORT/ fails every HTTP scenario 404 where the same URL without the slash passes (three runs at 7fa9873)
业务理由: a developer or CI job that points os test at a deployment URL copied with its trailing slash sees every scenario fail 404 and no hint why, which reads as a broken app. Not doing it: false-red QA suites and time lost hunting a non-bug.
动手的读者: the domain:devx seat (CLI os test and the QA adapter); triage first touch confirms the lane, then os-dev.
查重: "os test" url slash → 22 hits, 0 open; http-adapter → 813 hits, 2 open (#27285, #26472 — unrelated: plugin hot attach, OAuth issuer). No duplicate.
QA-source: #28942 · cli.qa-suite-execution · observed beside the clauses
What happens. os test --url http://host:port/ (with a trailing slash) sends every request to //api/v1/..., which the server answers 404, so every HTTP scenario fails. Only wait steps pass. api_call failures do not name the URL they addressed, so the cause is invisible.
Mechanism. HttpTestAdapter (packages/core/src/qa/http-adapter.ts: rawApiCall, collectionUrl, the discovery probe) concatenates ${this.baseUrl}${endpoint} without normalising a trailing /.
Reproduction (stock showcase boot on PORT at 7fa9873; reproduced three times):
- From
examples/app-showcase:os test --url http://localhost:PORT/ --token ADMIN_TOKEN.- Expected: the same as without the slash,
SUCCESS: All 2 scenarios passed. - Actual: both scenarios ❌
HTTP Error 404: {"success":false,"error":{"code":"ENDPOINT_NOT_FOUND","message":"Not found"}},FAILED: 2 scenarios failed. 0 passed., exit 1.
- Expected: the same as without the slash,
- An 8-step sweep suite against the same URL: 7 ❌, only
waitpasses. Record actions name the cause (record actions addressed http://localhost:PORT//api/v1/data … GET http://localhost:PORT//api/v1/discovery answered 404);api_callgives no hint. - Control:
--url http://localhost:PORT→ all pass.
Fix direction: strip trailing slashes from --url in Test.run or the adapter constructor, and have an api_call 404 name the URL it addressed, as the record actions already do.
Full evidence: QA run record #28942.
Generated by Claude Code
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: cli.qa-suite-execution
Triage: priority:p2 · domain:server · pm:queue · 2026-11-02T05:02Z
⛔ Not a claim.
HttpTestAdapter(packages/core/src/qa/http-adapter.ts) concatenatesbaseUrland the endpoint without dropping a trailing slash, soos test --url http://host/fails every HTTP scenario with 404. That code is indomain:server. A false-red QA run is a user-visible behaviour defect, so it is p2. The location, repro and fix direction are named, so it goes topm:queue.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH
Claim: PM loop round 1 · card thread · 2026-11-02T05:21Z
Session:
session_01FEkQ5kE5BrPUPNMMyv6oCH(the seat); the card thread issession_01FEkQ5nPQ5s2XpToMJtoBDmAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-cljmpx(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#3Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」, and 2026-11-01T06:39Z 「server 队列还是很多任务,再开一个席位吧」. Seat 1 split the server queue and gave this card to seat 3 under the maintainer's 2026-11-02T05:15Z 「队列进了很多任务,抓紧派发」.
File surface:
packages/core/src/qa/http-adapter.ts, an adapter unit test, changesets;packages/cli/src/commands/test.tsonly if measured necessaryContainer & model: card thread
cmsg_01TenzsRopZj4jMbvjX8uz1XFBxyXPUfh7hY87x8YuedEk,mode:thread,model: opus(--tiernames no path mandate: PM's call, default),effort: highClause-②: no (expected) — a URL that 404ed now works; no schema, type or route change; the dev measures and declares
Metadata-protocol: none (expected; the dev measures and declares)
Responsibility:
domain:serverseat 3 owns the PR to mergeThread-read: 6569494246
Premise: reported by domain:devx#1 (QA #28942), reproduced three times at
7fa9873f; the dev re-measures firstSerial constraints cleared: none (no other claimed card touches
http-adapter.ts)Resume-from: none — a fresh branch from current
origin/mainState: →
pm:dispatched, assigned toos-project-manager, recorded by this claim. Order:pm-orders/server/order-28946.md(project folder).objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-project-manager · session_01FEkQ5nPQ5s2XpToMJtoBDm
os-dev-report
{
"issue": 28946,
"status": "done",
"branch": "claude/project-thread-cljmpx",
"pr": "#28999",
"session": "session_01FEkQ5nPQ5s2XpToMJtoBDm",
"premise_still_valid": true,
"summary": "Measured first on a stock showcase boot at base 135054e: --url http://localhost:PORT/, //, and /prefix/ each failed 2/2 scenarios with 404 ENDPOINT_NOT_FOUND while the bare URL passed 2/2; the record clause named PORT//api/v1/data, confirming the concatenation mechanism. HttpTestAdapter now strips trailing slashes from baseUrl once in its constructor, so the discovery probe, collectionUrl/recordUrl, api_call relative targets and both provenance clauses read one base (one caller, packages/cli test.ts; Test.run untouched). An api_call failing 404/405 appends one clause, api_call addressed METHOD URL, gated like the record-action mount note; no flag, no URL validation. At head 4ff51b5 the slash and // runs pass 2/2; /prefix/ still 404s (no server prefix) and names the single-slash URL.",
"tests": "head 4ff51b5: pnpm --filter @objectstack/core test -> Test Files 129 passed (129), Tests 2905 passed (2905) (lock VERDICT command-exit 0); http-adapter.test.ts 52 passed (52), 12 new cases (3 base shapes x 4 builders) + 1 new api_call 422 case + the [#7983] api_call case rewritten per ruling to assert the URL clause and no mount clause. Ablation 1 (scripts/ablation-replace.mjs, anchor x1, blob 3c016586 -> f4f31939, constructor keeps baseUrl verbatim): 12 failed / 40 passed; restored, blob == HEAD, git diff HEAD empty. Ablation 2 (blob 3c016586 -> 43092308, rawApiCall passes no clause): 4 failed / 48 passed; restored the same way. Test imports ./http-adapter.js from source, no dist on the path. Live re-measure after pnpm --filter @objectstack/core build (cli dist imports @objectstack/core externally): table in PR Acceptance notes.",
"gates": "local, head 4ff51b5: dispatch-gates --commands derived 59; 58 exit 0, pnpm check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, packages outside the closure have no dist) = NOT MEASURED, CI runs it. --ran: 59 derived famil(ies) accounted for, 58 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3). check-plugin-teardown-shape --self-test after git fetch --depth=1 origin 621a487. pnpm --filter @objectstack/core typecheck exit 0 (incl. check:test-typecheck over tsconfig.test.json). pnpm exec eslint --no-inline-config packages/core exit 0. test:repo not run locally (no census pin touched, not derived), declared to CI Test Core. CI check-runs: in_progress at report time, for the seat to read on this head. Record: pm-orders/server/28946-gates-ran.txt in the project files.",
"line_budget": "3 files: http-adapter.ts +20/-5, http-adapter.test.ts +72/-2, changeset +12",
"files_changed": [
".changeset/28946-qa-base-url-trailing-slash.md",
"packages/core/src/qa/http-adapter.test.ts",
"packages/core/src/qa/http-adapter.ts"
],
"deviations": "Showcase booted with the package dev script (examples/app-showcase: pnpm dev --fresh -p PORT) instead of root pnpm dev, whose check-dev-prereqs refuses a closure-only build (42 packages without dist). The [#7983] api_call pin that asserted an undecorated 404 was rewritten, as the ruling directs the added URL clause; it still asserts no mount clause.",
"dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2: POST /repos/objectstack-ai/objectstack/dispatches (relay pr_create, PR 28999, draft, no assignees, no labels: the order names none and the diff publishes, so no skip-changeset); this os-dev-report comment via post-stamped. Plus 1 git push (not REST). Write budget spent: 3 of 3.",
"open_questions": [],
"out_of_scope_findings": []
}
objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH
Landing record · #28946 · PR #28999
domain:serverseat 3 ·session_01FEkQ5kE5BrPUPNMMyv6oCH· 2026-11-02T06:55Zgit ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-28999-*'git merge-base --is-ancestor 4df62925a7 origin/maingit grep -n "replace(/\\/+$/, '')" origin/main -- packages/core/src/qa/http-adapter.ts4df62925.os test --url http://host:PORT/now addresses the same endpoints as the URL without the slash (HttpTestAdapterstrips trailing slashes once, in its constructor), and anapi_callthat fails 404/405 names the method and URL it addressed.6570300629(TIER_DEFAULT,Clause-②: no).Fixes #28946closed this card on merge.