You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Setup: AI "Test embedder" and Knowledge "Test connection" have no server handler — the handlers exist but are not registered #24769
Filing gate ③ a task the maintainer set: objectstack-ai/hotcrm#2188, the maintainer's browser pass of 2026-10-20. Each defect gets its own card, by the maintainer's ruling 「按照按照具体的issue单独提交卡片,不受数量限制。」 (recorded on objectstack-ai/hotcrm#2188, 6283467378). This is finding F-19 of track 2 (Setup as the system administrator), by the cloud tester session_01VuXyJvhGKJy8qeUvhKyxzv. It was measured on HotCRM mainf22dfa52 with @objectstack/* 18.0.0-next.5, in Chromium at 1440×900 on a locally booted, seeded install. The evidence is on the branch claude/qa-setup-pass-2026-10-20 at ed4f40e42, which is never merged. Filed by the repo:hotcrm seat 2 (os-support-ai, session session_01KqkTQNLcxVc588eZpJmp1r). ⛔ Not a claim. Grade: triage's.
Reader (who acts): objectstack triage routes it.
The finding, as measured
Actual:POST /api/settings/ai/test_embedder → 400 "No handler registered for action 'test_embedder' in 'ai'."; POST /api/settings/knowledge/test → 400 "No handler registered for action 'test' in 'knowledge'."
Dedupe words: no handler registered for action, test embedder, knowledge test connection.
Seat review
Landing (seat review):settings-service-plugin.ts:124-129 registers test handlers for mail, sms, storage and ai.test only. This is still the case on origin/main.
Dedupe
Method: semantic search_issues on objectstack-ai/objectstack and objectstack-ai/objectui, open and closed, using the finding's own dedupe words and a second phrasing; plus a grep of every issue title (objectstack #1–#24751, objectui #7–#13153) listed through the repo-scoped issues?state=all endpoint, because the semantic search can miss title words (objectstack#14743).
Claim: PM loop round 1 · 2026-10-20T07:17Z
Session: session_01UmqzD97e14iws6hmVhgZ5y
Account: os-support-ai (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-24769-settings-test-handlers
Worktree: objectstack-issue-24769
Domain: domain:services
Seat: domain:services#2 (seat post #21118)
File surface: packages/services/service-settings/src/settings-service-plugin.ts (the default actionHandlers map), src/manifests/ai.manifest.ts and src/manifests/knowledge.manifest.ts (their test handlers, only if the measured fix needs them), their tests, one new .changeset/24769-*.md. ⛔ Not settings-service.ts / settings-service.types.ts (PR #24681 and the epic's C8), ⛔ not auth.manifest.ts or the translation files (PR #24795), ⛔ not packages/spec/** (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: opus (TIER_DEFAULT; dispatch-gates --tier on this surface: "no path-derived mandate · floor sonnet · default opus · ceiling fable"; the default tier because whether a validation-only handler may answer a "Test connection" button is a judgement)
Clause-②: no
Responsibility: service-settings' SettingsServicePlugin default actionHandlers map registers test handlers for mail, sms, storage and ai.test only | aiTestEmbedderActionHandler and knowledgeTestActionHandler are already exported beside their manifests | every admin pressing AI "Test embedder" or Knowledge "Test connection"; measured on a HotCRM install 2026-10-20 (card)
Thread-read: none
Serial constraints cleared: no open PR touches settings-service-plugin.ts, ai.manifest.ts or knowledge.manifest.ts (the file lists of all 18 open PRs read at this claim). PR #24681 (epic) edits settings-service.ts, settings-service.types.ts and two service-settings tests; PR #24795 (#24739, seat 1) edits auth.manifest.ts and three translation files. Neither is on this surface. Queued service-settings cards #24766, #24767 and #24776 are not dispatched with this card; #24776 (auth.manifest.ts) is serial behind PR #24795.
{
"issue": 24769,
"status": "done",
"branch": "claude/issue-24769-settings-test-handlers",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24830",
"session": "session_01UmqzD97e14iws6hmVhgZ5y — this run's harness-stamped id (mode:subagent, shared with the dispatching PM seat; identity is the branch)",
"premise_still_valid": true,
"summary": "Premise confirmed on origin/main a43405d59. SettingsServicePlugin's default actionHandlers map held only mail.test, sms.test, storage.test and ai.test. A new class pin run against the unchanged plugin named exactly [\"ai/test_embedder\",\"knowledge/test\"], and both HTTP door cases answered 400. M2 measured: no package in this repo registers either action. git grep \"registerAction(\" finds settings-namespace registrations only in plugin-email, service-sms and service-storage; service-knowledge, knowledge-memory, knowledge-ragflow and embedder-openai never reference settings; service-ai is cloud-edition (NOT MEASURED). M3 measured and kept: opts.actionHandlers ?? DEFAULTS replaces the whole map, and registerAction is a Map.set override. Fix (landing settings-service-plugin.ts, as expected): the default map now carries ai.test_embedder and knowledge.test, and the option docblock now names the default set. Per M2, two stub answers were corrected in the same files. They promised 'Mount @objectstack/embedder-openai + a knowledge adapter / @objectstack/knowledge-ragflow to exercise live calls', which is false because neither package overrides the action. Every passing answer on a remote backend now says 'This checks the form only — no embedding request was sent / no connection was attempted'. The two docblocks that claimed a live-probe override in service-knowledge were rewritten to state what was measured. Two contrast-control tests that pinned the removed sentences were replaced. Both buttons stay network-free form checks, answering ok:true with severity info when complete. That matches the existing storage and ai.test convention; I took that as the route's intent rather than a public-contract decision. The PR body lists what each button checks. Five acceptance notes are in the PR body; none was filed and none has a carrier.",
"tests": "RED before, test commit a66a74166 against the unchanged plugin at base a43405d59: `vitest run src/settings-default-action-handlers.pin.test.ts` gave 'Tests 3 failed | 2 passed (5)': 'expected [ Array(2) ] to deeply equal []' with '+ \"ai/test_embedder\", + \"knowledge/test\"', and twice 'expected 400 to be 200' at the HTTP door. GREEN after, at c8dbf1395: the three touched test files gave 'Test Files 3 passed (3) · Tests 58 passed (58)'. Final head 62b892d56 (origin/main 307ce4c4b merged in, closure rebuilt): `pnpm --filter @objectstack/service-settings typecheck` exit 0, and `pnpm --filter @objectstack/service-settings exec vitest run --maxWorkers=2` gave 'Test Files 54 passed (54) · Tests 899 passed (899)'. Lock VERDICT command-exit 0, with the two commands joined by &&. tsc --listFiles includes all 3 touched test files. ABLATIONS at c8dbf1395, through scripts/ablation-replace.mjs in WRAP mode inside a trap'd script. Each anchor hit x1 and the blob changed; each restore was proven blob==HEAD with git diff HEAD empty. No dist is involved: the subjects are in-package relative imports, and @objectstack/* resolves through the vitest source aliases. All three went red, the expected direction. (A) Removing `knowledge: { test: knowledgeTestActionHandler },` gave 'Tests 2 failed | 3 passed (5)': \"expected [ 'knowledge/test' ] to deeply equal []\" and 'expected 400 to be 200'; restored blob 716f8a5cda1d. (B) Appending the old embedder mount sentence gave 'Tests 1 failed | 27 passed (28)': \"expected 'openai embedder settings are complete…' not to match /to exercise live calls/\"; restored blob bb1ad034d9ad. (C) Appending the old ragflow mount sentence gave 'Tests 1 failed | 24 passed (25)': \"ragflow: expected 'RAGFlow adapter configured (http://lo…' not to match /to exercise live calls/\"; restored blob 396195582b05. B and C keep the form-only sentence, so the negative pin fires on its own. test:repo was not run locally; it is CI's (Test Core).",
"gates": "Local subset at final head 62b892d56. The ① dependency closure was built with `turbo run build --filter='@objectstack/service-settings...' --concurrency=2` (16 tasks, exit 0). `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 67 commands from the 7 changed paths against merge base 307ce4c4b; the PM's lead had 49 commands at a43405d59, and the changeset path plus the merged main account for the difference. Each command ran with its exit code captured before any pipe. 65 exited 0 on the first run. `node scripts/check-plugin-teardown-shape.mjs --self-test` first exited 3 (the shallow clone could not reach pinned fixture 621a4876); once that object was present it re-ran exit 0, '48 cases pass'. `pnpm check:dual-build-cjs-loads` exited 3, PREREQUISITE NOT MET: it needs every package's dist/ and only the closure is built, so it is NOT MEASURED locally and left to CI. `--ran` printed '✓ dispatch-gates --ran: 67 derived famil(ies) accounted for — 66 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)', 0 UNRUN, exit 0. check:platform-checklist is n/a (no export renamed or removed); check:dev-prereqs and check:skill-frame-freshness are n/a (no packages/spec build or frame files touched). CI on PR #24830, read once at head 62b892d56, all in_progress or queued: Lint · ESLint, Lint · docs & code guards, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · scoped ×3, Type Check · workspace / source gates / consumer gates / debt ledger, Test Core (1-6/6), Build Core, Governed Surface Queue Guard, Check Changeset, Check PR Size, card/branch claim checks. Skipped by selector: Dogfood Regression Gate (matrix), Build Docs, Console Pin Gate, Dogfood Verify CLI, Packed-tarball smoke, 18.x upgrade harness. No conclusion is claimed.",
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "2 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write as objectstack-fleet[bot]. (1) pr_create (run 39394877475): POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos/objectstack-ai/objectstack/issues/24830/assignees ['os-support-ai']; read-back 11351 bytes sent, 11351 stored, identical; no labels (skip-changeset does not apply, the package publishes). (2) this os-dev-report: POST /repos/objectstack-ai/objectstack/issues/24769/comments via scripts/pm/post-stamped.mjs. git push (five pushes to the branch) is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [],
"line_budget": "n/a — no skills/** or line-ratcheted ledger touched. The diff vs merge base 307ce4c4b is 7 files, +309 / -44 (353 changed lines, under the 3000 human-merge threshold).",
"deviations": [
"Scope widened beyond registration on the PM's M2 instruction, inside the claim's file surface. The answer text and docblocks in ai.manifest.ts and knowledge.manifest.ts were corrected, and two existing contrast-control tests that pinned the removed 'Mount … to exercise live calls' sentences were replaced. The reason is in the PR body.",
"origin/main (307ce4c4b, including the landed 18.x epic PR that touched service-settings) was merged BEFORE opening the PR, per AGENTS.md §10. The closure was then rebuilt and typecheck and package tests re-run on the merged head 62b892d56.",
"check:dual-build-cjs-loads is NOT MEASURED locally (PREREQUISITE NOT MET, needs all-package dist); declared to CI. test:repo is not run locally; declared to CI.",
"Attribution: a harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. AGENTS.md takes precedence, so commits carry the model-free pair (Claude-Session + Co-authored-by: Claude) and the PR body ends with the AGENTS.md session-URL footer.",
"The merge commit 62b892d56 carries git's default merge message and no trailer pair."
],
"files_changed": [
".changeset/24769-settings-test-handlers.md",
"packages/services/service-settings/src/settings-service-plugin.ts",
"packages/services/service-settings/src/settings-default-action-handlers.pin.test.ts",
"packages/services/service-settings/src/manifests/ai.manifest.ts",
"packages/services/service-settings/src/manifests/ai.manifest.test.ts",
"packages/services/service-settings/src/manifests/knowledge.manifest.ts",
"packages/services/service-settings/src/manifests/knowledge.manifest.test.ts"
]
}
Activity
objectstack-fleet commentedon Oct 20, 2026
Claim: PM loop round 1 · 2026-10-20T07:17Z
Session:
session_01UmqzD97e14iws6hmVhgZ5yAccount:
os-support-ai(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24769-settings-test-handlersWorktree:
objectstack-issue-24769Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface:
packages/services/service-settings/src/settings-service-plugin.ts(the defaultactionHandlersmap),src/manifests/ai.manifest.tsandsrc/manifests/knowledge.manifest.ts(their test handlers, only if the measured fix needs them), their tests, one new.changeset/24769-*.md. ⛔ Notsettings-service.ts/settings-service.types.ts(PR #24681 and the epic's C8), ⛔ notauth.manifest.tsor the translation files (PR #24795), ⛔ notpackages/spec/**(stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus(TIER_DEFAULT;dispatch-gates --tieron this surface: "no path-derived mandate · floor sonnet · default opus · ceiling fable"; the default tier because whether a validation-only handler may answer a "Test connection" button is a judgement)Clause-②: no
Responsibility:
service-settings' SettingsServicePlugin default actionHandlers map registers test handlers for mail, sms, storage and ai.test only | aiTestEmbedderActionHandler and knowledgeTestActionHandler are already exported beside their manifests | every admin pressing AI "Test embedder" or Knowledge "Test connection"; measured on a HotCRM install 2026-10-20 (card)Thread-read: none
Serial constraints cleared: no open PR touches
settings-service-plugin.ts,ai.manifest.tsorknowledge.manifest.ts(the file lists of all 18 open PRs read at this claim). PR #24681 (epic) editssettings-service.ts,settings-service.types.tsand twoservice-settingstests; PR #24795 (#24739, seat 1) editsauth.manifest.tsand three translation files. Neither is on this surface. Queuedservice-settingscards #24766, #24767 and #24776 are not dispatched with this card; #24776 (auth.manifest.ts) is serial behind PR #24795.Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
os-dev-report
{ "issue": 24769, "status": "done", "branch": "claude/issue-24769-settings-test-handlers", "pr": "https://github.com/objectstack-ai/objectstack/pull/24830", "session": "session_01UmqzD97e14iws6hmVhgZ5y — this run's harness-stamped id (mode:subagent, shared with the dispatching PM seat; identity is the branch)", "premise_still_valid": true, "summary": "Premise confirmed on origin/main a43405d59. SettingsServicePlugin's default actionHandlers map held only mail.test, sms.test, storage.test and ai.test. A new class pin run against the unchanged plugin named exactly [\"ai/test_embedder\",\"knowledge/test\"], and both HTTP door cases answered 400. M2 measured: no package in this repo registers either action. git grep \"registerAction(\" finds settings-namespace registrations only in plugin-email, service-sms and service-storage; service-knowledge, knowledge-memory, knowledge-ragflow and embedder-openai never reference settings; service-ai is cloud-edition (NOT MEASURED). M3 measured and kept: opts.actionHandlers ?? DEFAULTS replaces the whole map, and registerAction is a Map.set override. Fix (landing settings-service-plugin.ts, as expected): the default map now carries ai.test_embedder and knowledge.test, and the option docblock now names the default set. Per M2, two stub answers were corrected in the same files. They promised 'Mount @objectstack/embedder-openai + a knowledge adapter / @objectstack/knowledge-ragflow to exercise live calls', which is false because neither package overrides the action. Every passing answer on a remote backend now says 'This checks the form only — no embedding request was sent / no connection was attempted'. The two docblocks that claimed a live-probe override in service-knowledge were rewritten to state what was measured. Two contrast-control tests that pinned the removed sentences were replaced. Both buttons stay network-free form checks, answering ok:true with severity info when complete. That matches the existing storage and ai.test convention; I took that as the route's intent rather than a public-contract decision. The PR body lists what each button checks. Five acceptance notes are in the PR body; none was filed and none has a carrier.", "tests": "RED before, test commit a66a74166 against the unchanged plugin at base a43405d59: `vitest run src/settings-default-action-handlers.pin.test.ts` gave 'Tests 3 failed | 2 passed (5)': 'expected [ Array(2) ] to deeply equal []' with '+ \"ai/test_embedder\", + \"knowledge/test\"', and twice 'expected 400 to be 200' at the HTTP door. GREEN after, at c8dbf1395: the three touched test files gave 'Test Files 3 passed (3) · Tests 58 passed (58)'. Final head 62b892d56 (origin/main 307ce4c4b merged in, closure rebuilt): `pnpm --filter @objectstack/service-settings typecheck` exit 0, and `pnpm --filter @objectstack/service-settings exec vitest run --maxWorkers=2` gave 'Test Files 54 passed (54) · Tests 899 passed (899)'. Lock VERDICT command-exit 0, with the two commands joined by &&. tsc --listFiles includes all 3 touched test files. ABLATIONS at c8dbf1395, through scripts/ablation-replace.mjs in WRAP mode inside a trap'd script. Each anchor hit x1 and the blob changed; each restore was proven blob==HEAD with git diff HEAD empty. No dist is involved: the subjects are in-package relative imports, and @objectstack/* resolves through the vitest source aliases. All three went red, the expected direction. (A) Removing `knowledge: { test: knowledgeTestActionHandler },` gave 'Tests 2 failed | 3 passed (5)': \"expected [ 'knowledge/test' ] to deeply equal []\" and 'expected 400 to be 200'; restored blob 716f8a5cda1d. (B) Appending the old embedder mount sentence gave 'Tests 1 failed | 27 passed (28)': \"expected 'openai embedder settings are complete…' not to match /to exercise live calls/\"; restored blob bb1ad034d9ad. (C) Appending the old ragflow mount sentence gave 'Tests 1 failed | 24 passed (25)': \"ragflow: expected 'RAGFlow adapter configured (http://lo…' not to match /to exercise live calls/\"; restored blob 396195582b05. B and C keep the form-only sentence, so the negative pin fires on its own. test:repo was not run locally; it is CI's (Test Core).", "gates": "Local subset at final head 62b892d56. The ① dependency closure was built with `turbo run build --filter='@objectstack/service-settings...' --concurrency=2` (16 tasks, exit 0). `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 67 commands from the 7 changed paths against merge base 307ce4c4b; the PM's lead had 49 commands at a43405d59, and the changeset path plus the merged main account for the difference. Each command ran with its exit code captured before any pipe. 65 exited 0 on the first run. `node scripts/check-plugin-teardown-shape.mjs --self-test` first exited 3 (the shallow clone could not reach pinned fixture 621a4876); once that object was present it re-ran exit 0, '48 cases pass'. `pnpm check:dual-build-cjs-loads` exited 3, PREREQUISITE NOT MET: it needs every package's dist/ and only the closure is built, so it is NOT MEASURED locally and left to CI. `--ran` printed '✓ dispatch-gates --ran: 67 derived famil(ies) accounted for — 66 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)', 0 UNRUN, exit 0. check:platform-checklist is n/a (no export renamed or removed); check:dev-prereqs and check:skill-frame-freshness are n/a (no packages/spec build or frame files touched). CI on PR #24830, read once at head 62b892d56, all in_progress or queued: Lint · ESLint, Lint · docs & code guards, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · scoped ×3, Type Check · workspace / source gates / consumer gates / debt ledger, Test Core (1-6/6), Build Core, Governed Surface Queue Guard, Check Changeset, Check PR Size, card/branch claim checks. Skipped by selector: Dogfood Regression Gate (matrix), Build Docs, Console Pin Gate, Dogfood Verify CLI, Packed-tarball smoke, 18.x upgrade harness. No conclusion is claimed.", "mcp_calls": "0 — no MCP GitHub tool was called", "api_writes": "2 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write as objectstack-fleet[bot]. (1) pr_create (run 39394877475): POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos/objectstack-ai/objectstack/issues/24830/assignees ['os-support-ai']; read-back 11351 bytes sent, 11351 stored, identical; no labels (skip-changeset does not apply, the package publishes). (2) this os-dev-report: POST /repos/objectstack-ai/objectstack/issues/24769/comments via scripts/pm/post-stamped.mjs. git push (five pushes to the branch) is not a REST write.", "open_questions": [], "out_of_scope_findings": [], "line_budget": "n/a — no skills/** or line-ratcheted ledger touched. The diff vs merge base 307ce4c4b is 7 files, +309 / -44 (353 changed lines, under the 3000 human-merge threshold).", "deviations": [ "Scope widened beyond registration on the PM's M2 instruction, inside the claim's file surface. The answer text and docblocks in ai.manifest.ts and knowledge.manifest.ts were corrected, and two existing contrast-control tests that pinned the removed 'Mount … to exercise live calls' sentences were replaced. The reason is in the PR body.", "origin/main (307ce4c4b, including the landed 18.x epic PR that touched service-settings) was merged BEFORE opening the PR, per AGENTS.md §10. The closure was then rebuilt and typecheck and package tests re-run on the merged head 62b892d56.", "check:dual-build-cjs-loads is NOT MEASURED locally (PREREQUISITE NOT MET, needs all-package dist); declared to CI. test:repo is not run locally; declared to CI.", "Attribution: a harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. AGENTS.md takes precedence, so commits carry the model-free pair (Claude-Session + Co-authored-by: Claude) and the PR body ends with the AGENTS.md session-URL footer.", "The merge commit 62b892d56 carries git's default merge message and no trailer pair." ], "files_changed": [ ".changeset/24769-settings-test-handlers.md", "packages/services/service-settings/src/settings-service-plugin.ts", "packages/services/service-settings/src/settings-default-action-handlers.pin.test.ts", "packages/services/service-settings/src/manifests/ai.manifest.ts", "packages/services/service-settings/src/manifests/ai.manifest.test.ts", "packages/services/service-settings/src/manifests/knowledge.manifest.ts", "packages/services/service-settings/src/manifests/knowledge.manifest.test.ts" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
ACCEPT → PR #24830 comment
6287086728·domain:servicesseat 2 ·session_01UmqzD97e14iws6hmVhgZ5y· 2026-10-20T08:46ZGenerated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Landed ·
domain:servicesseat 2 ·os-support-ai·session_01UmqzD97e14iws6hmVhgZ5y· 2026-10-20T09:40Zadded_to_merge_queue2026-10-20T09:07Z →merged2026-10-20T09:38Zorigin/main7697ddcf94is an ancestor (git merge-base --is-ancestor, exit 0; control leg onorigin/main~30, exit 0)origin/mainsettings-service-plugin.tsregisterstest_embedder: aiTestEmbedderActionHandler(1 hit)completedby the PR'sFixes #24769;pm:dispatchedremoved in this actACCEPT
6287086728is on the PR.Generated by Claude Code