backfill space member names - #1637
Open
liyaqing-l wants to merge 77 commits into
Open
Conversation
liyaqing-l
commented
Aug 28, 2026
Collaborator
- 员工工号不足 6 位时,仅在查询员工目录前补零,例如:
- 1234 → 001234
- 数据库仍保存原始 1234
## Problem Default SkillSet Skill and MCP removal was routed through the canonical control plane, where ordinary-membership immutability checks rejected the shared system Default with `SYSTEM_DEFAULT_IMMUTABLE`. BFF and OpenAPI Gateway callers therefore could not create the per-owner, per-Bot exclusions that preserve the published Default Set contract. ## Solution - Branch Default Skill removal before the ordinary-Set guard and persist `ac_default_skillset_skill_exclusion` under the resolved Bot owner. - Retire the Default-governed Skill installation on first exclusion, while preserving it when an active ordinary Set still provides the Skill. - Branch projected Default MCP removal to `ac_default_skillset_mcp_exclusion` without requiring an ordinary MCP membership row or deleting explicit MCP installations. - Include both exclusion collections in desired-state snapshots and compensating restore so runtime reconcile failures roll back atomically. - Keep Default Set add/update/delete/deactivate immutability unchanged. ## Validation - `uv run --project src/backend pytest -q src/backend/tests/community/repository/skill_center/test_skill_set_control_plane_uow.py src/backend/tests/community/core/skill_center/test_skill_set_control_plane_service.py src/backend/tests/community/contracts/gateway/test_rule15_skillsets.py` — 104 passed. - `uv run --project src/backend pytest -q src/backend/tests/community/endpoints/test_endpoint_runner.py -k "excludes_default_skill or excludes_dynamic_default_mcp"` — 2 passed. - `uv run --project src/backend pytest -q src/backend/tests/community/architecture/test_module_boundaries.py src/backend/tests/community/architecture/test_service_api_conformance.py src/backend/tests/community/core/skill_center src/backend/tests/community/repository/skill_center` — 1083 passed, 27 skipped. - `scripts/ci/python_sast_local.sh src/backend 1 --base origin/dev --head HEAD` — passed. - Changed-file Ruff checks — passed; the repository test file retains its pre-existing unused `sqlalchemy.text` import and was checked with F401 ignored rather than mixing unrelated cleanup. ## Compatibility and risk No HTTP schema or database schema changes. The change restores the existing Default exclusion semantics for both BFF and Gateway through their shared control plane. Ordinary SkillSet membership behavior is unchanged. On reconcile failure, exclusion and installation desired state are restored before runtime compensation.
BotModel.to_dict() does not include template_config — it is dynamically fetched from the template service (ac_templates.ext) and attached by BotService.get_bot(). RenderScreenService was using BotRepository.get_by_id() directly, which bypasses that enrichment, so resolve_render_screen_scope() could never see template_config.capabilities.member_management for dynamic template bots. Fix: inject TemplateService into RenderScreenService and enrich the bot dict with template_config in _require_bot(), so scope resolution can detect member-management capability for all bot types. Tests: update fixtures to provide mock_template_service and configure it to return template config for dynamic shared bot scenarios. Validation: uv run pytest on render_screen scope/service/router/handler, bot_collaborator, and architecture rules suites - 232 passed.
…gine_type check Replace the complex capability-service-based scope resolution with a simple rule: active_engine == 'claude_code' AND template_type is not a plain CC type (empty/None/'normal'/'normalCC'). This removes the need for TemplateService injection and cross-table template config lookups, making the scope decision purely based on ac_bots columns that are always available. - scope.py: replace MemberManagementCapabilityService dependency with a simple template_type check - render_screen_service.py: remove TemplateService injection and template_config enrichment - tests: simplify fixtures, remove mock_template_service, expand scope tests to cover all template_type variants Validation: uv run pytest on render_screen scope/service/router/handler, architecture rules, and bot_collaborator suites - 237 passed.
## Problem Caller-aware Bot conversations can target both a shared Caller Service runtime for `draft`, `verify`, or `online` and a caller-specific Expert Chat instance. The IAM refresh path needed to update every eligible runtime binding without mistakenly selecting a different user's dynamic instance or performing a separate Caller credential exchange for each target. ## Solution - Add explicit `CALLER_SERVICE` and `CALLER_INSTANCE` runtime-binding targets while preserving Session Files `AUTO` behavior. - Use the existing collaboration-lock snapshot to select an eligible Caller Service runtime; permit the owner when no lock exists. - Independently include the authenticated user's active Caller Instance. - Exchange one opaque Caller Token per IAM request and reuse it for every selected outbound-rule update. - Return IAM success when at least one target updates; preserve the existing outbound failure when no target can be updated. ## Validation - `uv run --project src/backend pytest -q src/backend/tests/community/core/runtime_binding src/backend/tests/community/core/caller_identity src/backend/tests/community/adapters/http/openapi_v1/engine_runtime/test_session_files.py src/backend/tests/community/endpoints/test_openapi_session_files.py src/backend/tests/community/di/test_community_router_bindings_resolve.py` — 79 passed. - `uv run --project src/backend ruff check ...` on the affected Python files — passed. - `uv run --project src/backend python -m compileall -q ...` on affected backend modules — passed. - `git diff --check` — passed. ## Compatibility and risk This is a server-side change. It does not accept a client-controlled binding id and leaves Agent Run locking, relay/WebSocket behavior, Session Files `AUTO` resolution, and BaaS append semantics unchanged. ## Spec `spec/pipeline/caller-iam-multi-target-refresh/001-spec-output.md`
## Problem
BCN stream conversion shared the broad `core-service` log and only
emitted
warnings for malformed interaction payloads. This made it difficult to
correlate the raw stream chunk received from the engine with the SSE
event
produced for BCN, especially when diagnosing duplicated tool lifecycle
events.
Secret-marked `ask_user` interactions were also dropped during
projection,
which could leave downstream consumers waiting for an interaction that
was
never delivered.
## Solution
- Route existing `DefaultStreamConverter` warnings to the dedicated
`bcn-converter` logger.
- Wrap the converter only at the BCN `chat.send` streaming boundary and
emit
one structured record per conversion containing the run ID, complete raw
`StreamChunk`, and complete `SseEvent` result or `null` when filtered.
- Record conversion exceptions with the raw input and structured error
details,
then re-raise them so the existing SSE error path remains authoritative.
- Isolate serialization and logger backend failures from the conversion
path.
- Treat `secret` and `isSecret` as unsupported extension fields: omit
the
markers from the BCN question projection but deliver the interaction as
a
standard `ask_user` event.
- Leave OpenAPI and Gateway conversion wiring unchanged.
- Register the new canonical logger name and add focused regression
coverage.
## Validation
- `uv run pytest tests/unit/core/service/sse/test_default_converter.py
tests/unit/adapters/web/open_api/test_bcn_router.py
tests/architecture/test_exception_handling.py
tests/architecture/test_logger_usage.py -q`
- 110 passed (the architecture exception audit also emitted its existing
non-blocking repository-wide warning)
- `uv run ruff format --check
src/secbaas/community/core/service/sse/_default_converter.py
src/secbaas/community/adapters/web/routers/bcn_downlink/bcn_router.py
tests/unit/core/service/sse/test_default_converter.py
tests/unit/adapters/web/open_api/test_bcn_router.py`
- All files formatted
- `uv run ruff check
src/secbaas/community/core/service/sse/_default_converter.py
src/secbaas/community/adapters/web/routers/bcn_downlink/bcn_router.py
tests/unit/core/service/sse/test_default_converter.py
tests/unit/adapters/web/open_api/test_bcn_router.py`
- All checks passed
## Compatibility and risk
- The BCN wire schema is unchanged: `secret` and `isSecret` are not
emitted.
- Secret-marked questions are no longer dropped; downstream receives
them as
ordinary plaintext `ask_user` questions so the interaction can complete.
- INFO records intentionally contain complete raw message content and
complete
converted output, including secret-marked interaction content. Access
and
retention must therefore follow the existing SecBaaS sensitive-log
policy.
- Deployment must add an AntLogs collector for
`/home/admin/logs/secbaas/bcn-converter.log`; the application will
create and
rotate the file through the existing logger plugin.
…1481) ## Problem Prepub `GET /openapi/v1/bots/all` responds at RT p50=622ms (p90=3.7s, max 17.4s; access.log, 6h sample). One request runs ~12-14 serial DB round trips. Two avoidable chunks dominate: - `DesktopBotService.list_user_bots` queried once per status — 5 serial trips (PENDING/ACTIVE/OFFLINE/RELEASING/FAILED) - Every 200-row page pulled by the inventory passed through `_attach_template_configs_to_bots`, although the inventory response provably never reads `template_config` (neither `BotInventoryItem` nor the router `_to_inventory_item` maps it) ## Solution - `search_bots` gains `bot_status_list` (status IN filter); `list_user_bots` collapses 5 serial queries into one (500/page with a fill loop, coverage ≥ the old 100×5; failure keeps the old swallow-and-log contract) - `list_bots_by_conditions` gains `attach_templates: bool = True` (default preserves the established get/list consistency contract); the inventory cloud pull passes `False` — one fewer batched read per pulled page, behavior unchanged - No API shape, response, or error behavior changes ## Validation - `ci_test.sh --base origin/REL20260826`: 14460 passed, case pass rate 100%, line coverage 87.08%, changed-line coverage 94.44% (≥80% gate) - `TestListUserBots` rewritten for single-query semantics (single-IN + pagination fill + swallowed failure); new opt-out (misc) and pass-through (inventory) coverage - All `search_bots` call sites verified keyword-only — the new parameter cannot shift positional mappings - Post-deploy check: prepub /bots/all RT p50/p90 tracked via antlogs (current baseline p50 ~600-800ms); P2 (query parallelization + SQL pagination pushdown) targets <150ms next
#1499) ## Problem task 模块 `TaskExecutor.form_coop_group` 在创建 state_machine 协作群(BCS create_group)时,把 `opening_message.params` 以**字符串化 JSON**(`json.dumps({...})`)发出。但 BCS 契约(`ocb-public/src/bcs/docs/custom-collaboration-opening-message-integration-guide.md` §4)要求 `opening_message.params` 是**JSON object**;字符串化会被真实 BCS 的 untagged enum `OpeningMessage` 判 `data did not match any variant of untagged enum OpeningMessage` → **422**,真实预发/生产才能触发。singlebox 的 BCS double 不校验 `opening_message`,本问题一直被本地测试掩盖。 另外缺少一条直连预发环境、端到端验证 `OpenApiBotAdapter` 单 bot 派发的 e2e(现有 `test_open_api_bot_adapter_live` 仅本地 singlebox 占位)。 ## Solution 1. **修复 `opening_message.params` 序列化**(`task_executor.py`):把 `"params": _json.dumps({...})` 改为直接传 dict(JSON object),并删除为此引入的局部 `import json as _json`;附 BCS 契约注释指向文档 §4。仅影响 state_machine 协作群建群(显式 `api_key_prefix` 不受影响)。 2. **新增回归断言**(`test_state_machine.py::test_form_coop_group_opening_message_params_is_object`):state_machine 建群带 `task_id` 时,断言 `opening_message.params` 为 `dict`(非字符串)且含关键字段(`taskId`/`apiBaseUrl`/`groupId`/`runId`/`businessScene`),防止该契约被字符串化回退。 3. **新增预发 e2e**(`test_open_api_bot_adapter_pre_e2e.py`):env-gated(`AVERNET_PRE_OPENAPI_API_KEY` + `AVERNET_PRE_OPENAPI_BOT_ID` 才启用,`BASE_URL` 默认预发 host,`COOKIE` 支持文件路径或原文并自动去 `Cookie:` 前缀,`PREFIX` 留空走 adapter 默认 `api_key[:8]`),直连预发 BaaS Open API,经 `send_and_wait`(ensure_grant → send_message → 轮询 get_run)到终态断言 `status ∈ {COMPLETED, FAILED}`;空缺即 `skipUnless` 跳过,CI 安全。 ## Validation - `py_compile` 通过;`task_executor.py` 与两个测试文件 SAST(block 规则集)exit 0。 - `test_state_machine.py` 全绿(8 既有 + 1 新增回归 = 9 passed);新回归按 TDD 验过红→绿(临时把源码回滚到字符串化版本则该断言失败、还原后通过),证明其能守住契约。 - 预发 e2e 默认 skip(env 未设即跳过,不破 CI);cookie 文件 / 原文 / 空三态解析与 import 已验。 - cherry-pick 到本分支后复验:`py_compile` OK + `test_state_machine` 9 passed + 预发 e2e 1 skipped;pre-push gate(lint-only SAST)passed。 ## Compatibility and risk - `opening_message.params` string→object 是修对、与 BCS 契约对齐;为 state_machine 协作群建群必经字段,prod 同样受益,无依赖“字符串 params”的合法消费方(BCS 本就不收),风险低。 - singlebox double 不校验 `opening_message`,是本问题漏到生产的根因;新增回归断言补上该盲区。 - 预发 e2e 仅在本地填入预发凭据时运行,默认 skip,对 CI 无影响、不携带凭据入库。 ## Spec - 契约依据:`ocb-public/src/bcs/docs/custom-collaboration-opening-message-integration-guide.md` §4(`opening_message.params` = 传给业务组件的 JSON object)。
## Problem BCN conversion logging lived in a router-level wrapper even though `DefaultStreamConverter` owns the event-type and payload-shape knowledge. This duplicated conversion-aware payload handling and logged complete high-frequency chat and thinking bodies, increasing log volume. ## Solution - Move opt-in input/output logging into `DefaultStreamConverter`, with logging disabled by default. - Register a BCN-specific converter configuration at the composition root and have BCN downlink select it, while OpenAPI and Gateway keep the silent default. - Keep one log record for every conversion, including `output=null` and conversion failures. - Limit only chat and thinking body fields to a 10-character preview with an original-length marker. - Preserve complete tool, interaction, lifecycle, identifier, and structured metadata fields. - Remove the router logging wrapper and isolate log serialization or backend failures from the conversion stream. ## Validation Local verification was not rerun after the code-only history rewrite, as requested. Before the rewrite, the unchanged code tree was validated with: - Focused BaaS unit and architecture suites: 150 tests passed, with one existing non-blocking repository-wide architecture warning. - Ruff formatting and lint checks for all changed Python files: passed. - `git diff --check`: passed. GitHub CI will provide final branch validation. ## Compatibility and risk - No BCN wire contract, SSE sequencing, or delivered payload behavior changes. - OpenAPI and Gateway conversion logging remains disabled. - Only log projections are shortened; delivered `StreamChunk` and `SseEvent` data remains complete. - The conversion log format remains `input=<json> output=<json|null>`, while chat and thinking text over 10 characters now contains a preview marker. - Rollback is limited to restoring the router wrapper and default converter selection.
Rel20260826 task qz bugfix
## Problem Catalog Search currently replaces the BCS `/bots/search` total with the number of Backend records that survive the exact `(bot_id, entity_id)` join. When the BCS page contains records that are temporarily unavailable in Backend, the public response no longer reflects the authoritative BCS pagination total. ## Solution - Preserve the validated non-negative integer `total` from the BCS catalog page in a transport-neutral page result. - Keep the existing exact composite-address join, BCS metadata projection, sensitive-field sanitization, and response item ordering unchanged. - Return the BCS total unchanged from Catalog Search; Backend no longer recomputes it from joined items. - Fail closed when the BCS total is missing or malformed, and add regression coverage for totals larger than the joined item count. ## Validation - `DEPLOY_PROFILE=test uv run pytest tests/community/adapters/http/openapi_v1/bot_public/test_bot_public_router.py tests/community/core/bot_public/test_bot_catalog_metadata_service.py tests/community/core/bot_public/test_bot_public_service.py -q` — 180 passed. - `DEPLOY_PROFILE=test uv run pytest tests/community/architecture/test_repository_contracts.py tests/community/architecture/test_http_adapter_layer_is_http_only.py tests/community/architecture/test_no_fastapi_in_core.py -q` — 15 passed. - Targeted `ruff check` — passed. - Gateway OpenAPI schema JSON parse — passed. - `git diff --check` against `REL20260826` — passed. ## Compatibility and risk The public route, query parameters, BCS path, exact join key, item projection, and error mapping remain unchanged. The only response behavior change is that `data.total` now follows BCS rather than the post-join Backend item count. ## Spec `spec/pipeline/catalog-search-bcs-total-rel20260826/001-spec-output.md` (local task artifact; intentionally not committed).
…piBotAdapter (#1504) PR desc ## Problem 预发/线上 `agentclaw-*` / `secbaas-*` 这类 ACE 网关后的真实 host 上,`OpenApiBotAdapter` 的 `send_message` / `get_run` / `ensure_grant` GET **只发 `Authorization: Bearer`**,既不随请求带登录 Cookie/Referer,也不校验响应业务 `code`。后果: - Bearer-only 被 ACE 当未登录 → 回 **HTTP 200 的 `USER_NOT_LOGIN` 登录门**(body 无 `data.message_id`); - adapter 只看 HTTP 状态、不校验 `code`,把“无 `message_id`”当成功 → `run_id=None`; - `send_and_wait` 接着 `get_run(None)` → 误导性 `404 "Message not found: None"`,真实根因(ACE 登录门)被掩盖。 对照工具 `send_bot_message.py`(带 Cookie + Referer)同入参可成功拿到 `message_id`,印证问题在 adapter 这一侧。 ## Solution `open_api_bot_adapter.py`: 1. 新增 `_headers()`:返回 `Bearer + 可选 Cookie/Referer`(key 有才加)。 2. `ensure_grant` GET / `send_message` / `get_run` 三处请求头由 Bearer-only 改为 `self._headers()`(`grant` POST 本就带 Cookie/Referer,未动)。 3. `send_message` 业务信封校验:HTTP 200 但 `code!=0` 或无 `message_id` → 直接 `raise OpenApiError(带 code/payload)`,不再吞成 `run_id=None`。 4. `get_run`:`code!=0` → `raise OpenApiError`。 5. 给 `ensure_grant` / `send_and_wait` / `send_and_wait_async` 入口加 3 行 debug `logger.info`,便于联调定位。 行为:`CorpApiKeyProvider` / singlebox 的 cookie/referer 为空 → 不加头、行为不变(prod 走 service-to-service、不经 ACE);带 cookie 的调用方(预发 e2e/联调)→ 过 ACE → 拿到真 `message_id`。 ## Validation - 单测 `test_open_api_bot_adapter.py`:新增 5 条(cookie/referer 透传 3 + 业务信封校验 2),TDD 红→绿;整文件 **17 passed**。 - 集成目录 `task_runner/integration`:**113 passed, 5 skipped**(预发 e2e 等 skip-by-default),无回归。 - SAST(block 规则集,源码 + 测试)exit 0。 - A/B 印证:同 key/url/bot/cookie 下 `send_bot_message.py` 成功拿到 `message_id`(改前 adapter 失败;改后 adapter 透传 cookie → 对齐工具)。 - pre-push gate(lint-only SAST)passed。 ## Compatibility and risk - 改动为**加法**:cookie/referer 非空才加头,空则行为不变,不影响 prod/singlebox。 - 业务 `code` 校验是新行为:此前 HTTP 200 + `code!=0` 会被当成功(返 `run_id=None`),现会抛 `OpenApiError`;旧行为本身即 bug,无合理依赖方。 - ⚠ `ensure_grant` 入口的 `logger.info("[task][openapi_bot] ensure_grant bot_id=%s")` 缺格式化参数(`%s` 无对应 arg);若该 logger 在 INFO 级启用,`"...%s" % ()` 会抛 `TypeError` 使 `ensure_grant` 入口崩溃。建议补成 `..., bot_id)` 或删除该行(另两行 `send_and_wait`/`send_and_wait_async` 的 log 已带参数,不受影响)。 ## Related issues - 预发 e2e `test_open_api_bot_adapter_pre_e2e.py` 此前报 `404 "Message not found: None"`,根因即本题;改后 adapter 透传 cookie/Referer 过 ACE,可跑通。 --------- Co-authored-by: Claude <noreply@anthropic.com>
The shared-scope list path enforced collaborator checks on reads, which broke group-chat rendering: members who added a coding bot as a friend are neither the owner nor collaborators, so GET /api/bot-render-screens answered 403 and panels failed with component-library-missing errors. CDN mappings (library name -> CDN URL) are non-sensitive render resources. Restore the original read-open semantics: list returns all records of a shared bot without identity checks, while create, update and delete keep the strict owner-or-collaborator authorization. - service: drop the collaborator gate from the shared-scope list branch - router: stop calling authorize_render_screen_bot on list - tests: flip the non-collaborator list case to assert read access, and assert the router no longer authorizes reads Validation: uv run pytest on render_screen scope/service/router/handler suites - 56 passed.
1. 完善 MCP、MCP 市场、本地注册相关接口和 Schema;
2. 更新 OpenAPI 文档及相关测试;
3. 创建团队空间时校验:
- 当前环境;
- 创建人;
- 归一化后的空间名称;
4. 同一用户不能重复创建同名团队空间,返回 409;
5. 不同用户仍允许创建同名空间;
6. 补充空间和 MCP 测试。
…rant model) (#1514) ## Problem `OpenApiBotAdapter.ensure_grant` 之前**默认总会先 GET `/api/v1/api-keys/{prefix}/allowed-bots`**(判断 bot 是否已授权 → 是否跳过 grant)。但该 admin 端点**只认 Human(登录)Cookie**(`create_api_key.py` 的 create/grant/list 都走 Cookie+Referer、不带 Bearer),而 corp/pre/prod 部署里 `OpenApiBotAdapter` 的 cookie/referer 为空(`CorpApiKeyProvider` 默认留空,prod 假定 service-to-service + OOB 预授权)。于是: - `ensure_grant` 的 GET 用 **Bearer-only** 打一个只认 Human Cookie 的 admin 端点 → BaaS 拿不到登录用户上下文 → **500 `INTERNAL_ERROR`**(线上实测:`<<< ensure_grant GET allowed-bots status=500 INTERNAL_ERROR`,一路上抛到 `on_execute` 500)。 - 即便 bot 已被外部 OOB grant 过也没用:`ensure_grant` 总在“判断是否已授权→跳过 grant”**之前**就 GET,GET 自己崩了,根本到不了 skip 分支。 这与 corp 一直声称的“api_key 已 OOB 预授权 bot,ensure_grant 是 no-op”假设矛盾——该假设依赖 GET 能跑通,而 GET 在无 cookie 的 prod/corp 上跑不通 → 真实 BaaS 上单 bot 派发直接断(prod 之前可能没真跑 single_bot 才没暴露)。 ## Solution `open_api_bot_adapter.py`: 1. `__init__` 新增 `ensure_grant: bool = False`(默认**跳过**)。 2. `ensure_grant` 顶部:`if not self._ensure_grant: 日志 + return` —— 默认不发 allowed-bots GET、不 grant,直进 `send_message`(dispatch 端点 `/openapi/v1/messages` 认 Bearer,正常)。 3. 顺带修 `ensure_grant` 入口 `logger.info("...bot_id=%s")` **缺 `bot_id` 参数**的 bug(opt-in 模式否则 `TypeError`)。 测试 `test_open_api_bot_adapter.py`: - 新增 `test_ensure_grant_skipped_by_default`:默认构造 → ensure_grant → 断言不发 allowed-bots GET。 - `_adapter` helper + prefix 回落用例显式 `ensure_grant=True`(保留对 GET/grant/prefix/cookie 透传的覆盖)。 行为:corp/prod/pre(`CorpTaskIntegrationModule` 构造 `OpenApiBotAdapter(keys)` 不传 flag)→ ensure_grant 跳过、直接派发;需要自查/grant 的(单测/联调)显式 `ensure_grant=True`。bot 由外部 OOB 预授权(prod 既定模型)。 ## Validation - TDD 红→绿:`test_ensure_grant_skipped_by_default` 先 RED(默认原先会 GET)、改源码后 GREEN。 - `test_open_api_bot_adapter.py`:**18 passed**(17 既有 + 1 新增),无回归(opt-in 用例继续覆盖 GET/grant/cookie 透传/业务 code 校验)。 - 集成目录 `task_runner/integration`:**114 passed, 5 skipped**,无回归。 - SAST(block 规则集,源码 + 测试)exit 0。 - 线上实证:改前 `ensure_grant GET allowed-bots status=500 INTERNAL_ERROR` → `on_execute` 500;改后默认跳过该 GET、直进 send_message,500 消失。 ## Compatibility and risk - **默认行为变更**:`ensure_grant` 由“默认执行 GET/grant”改为“默认跳过”。prod 假定 bot 已 OOB 预授权 → 跳过是正确姿势;**前提是部署侧确实已对目标 bot 做 OOB grant**(`create_api_key.py --action grant` 或 BaaS 控制台)。若某部署未预授权、仍想让 adapter 自查/grant,需显式 `ensure_grant=True` 且配 Human Cookie(admin 端点要 Cookie)。 - 不影响 singlebox(用 `SingleboxEngineAdapter`,非本 adapter)。 - 不影响 dispatch 端点(`/openapi/v1/messages`、`get_run`)——它们认 Bearer,与 ensure_grant 无关。 ## Related issues - 直接起因:线上 `OpenApiBotAdapter.ensure_grant` GET `/api/v1/api-keys/{prefix}/allowed-bots` 返回 500 `INTERNAL_ERROR`(Bearer-only 打只认 Human Cookie 的 admin 端点),即便 bot 已 OOB grant 也复现(卡在 GET,到不了 skip 分支)。 - 关联已合入的 `fix(task): forward cookie/referer and validate business code in OpenApiBotAdapter`:那条解决 dispatch 端点的 ACE/信封问题;本条解决 admin `ensure_grant` 在无 cookie 部署下的 500(改为默认不调)。
…1536) ## Problem REL20260826 基线上的 task_discovery 模块缺少 dev 分支已落地的多项能力,会阻碍后续基于 REL20260826 的发布线引入这些改进: - 多机 cron 并发时缺少 per-bot 分布式锁,导致同一 bot 可能被多台机器重复发现 - 缺少工单 NOTICE 通道,发现通知仅依赖外发卡片,单通道有漏达风险 - `DiscoveredTask` 字段未对齐执行层 `TaskSpec` 语义(`project_name`/`description`/`business_scenario`/`work_item_url` vs `title`/`instruction`/`background`/`objective`/`acceptances`),下游确认链路需要二次映射 - 缺少运行时 cron reschedule 与前端 URL 动态注入能力,调整触发时间或前端地址需重启 backend - 缺少 `/discovery/reschedule`、`/discovery/dingtalk-config` 两个 HTTP 端点 需要在 REL20260826 之上新建 `REL20260826_lz` 分支,将上述 task_discovery 相关变更以最小代价同步过来。 ## Solution 基于 `origin/REL20260826` 创建本地分支 `REL20260826_lz`,使用 `git checkout origin/dev -- <file>` 将 dev 上与 task_discovery 相关的源码/测试/文档一次性搬到该分支,整合为一个干净的 commit。 **同步范围(与 dev 完全一致)**: 1. task_discovery 核心:`discovery_service.py`、`lock_models.py`(新)、`models.py`、`protocols.py`、`scheduler.py`、`session_creator.py`、`session_initiator.py`、`task_reader.py` 2. DI 模块:`task_discovery_module.py`、`infrastructure/community/notify.py` 3. Repository 协议/实现:`protocols/task.py`、`implementations/task/discovery_lock.py`(新) 4. HTTP Router:`adapters/http/task/router.py`(新增 `/discovery/reschedule` 与 `/discovery/dingtalk-config`) 5. 通知插件:`plugins/community/notify_sender.py`(新增 `DingTalkCredentialHolder` + `DingTalkNotifySender`) 6. 测试:5 个测试文件,含新增的 `test_task_discovery_coverage.py` 与 `test_task_discovery_lock_repository.py` 7. Spec 文档:`specs/2026-08-25-task-discovery-taskspec-align-and-notify-channels/{design,spec,tasks}.md` **有意不同步的内容**(避免引入无关重构): - `core/work_orders/models.py`:仅手工添加 `TASK_DISCOVERED` 枚举值 + `EVENT_CATEGORIES` 条目;dev 中把 `SPACE_MEMBER_REMOVED` 移到末尾的纯位置重排未同步(无功能差异) - `core/repository/README.md`:仅添加 `TaskDiscoveryLockRepositoryProtocol` / `TaskDiscoveryLockRepository` 两行清单;dev 中 skill_center 重构相关条目未同步(与 task_discovery 无关) **拒绝的备选方案**: - 直接 merge dev:会带入 dev 比 REL20260826 多出的全部其他模块改动,超出"只同步 task_discovery"的范围 - cherry-pick dev 上的 commit:task_discovery 改动散落在多次 commit,与 skill_center 等其他重构交织,无法干净隔离 ## Validation `src/backend/` 下用 `uv run pytest` 跑相关测试: | 测试文件 | 结果 | | --- | --- | | `tests/community/core/task/test_task_discovery_unit.py` | 23 passed | | `tests/community/core/task/test_task_discovery_coverage.py` | 27 passed | | `tests/community/repository/task/test_task_discovery_lock_repository.py` | 11 passed | | `tests/community/endpoints/test_task_discovery_router.py` | 3 passed | | `tests/community/core/task/singlebox_e2e/test_task_discovery_e2e.py` | 2 skipped(需要 singlebox 全栈基础设施) | | **合计** | **64 passed, 2 skipped** | **Diff 一致性验证**(`git diff origin/dev -- <path>`): - task_discovery 核心、DI、Repository、Router、插件、Spec、测试 — 与 dev 完全一致(diff 为空) - `work_orders/models.py` 与 dev 仅有 `SPACE_MEMBER_REMOVED` 位置差异,task_discovery 相关条目已对齐 - `repository/README.md` 与 dev 仅有 skill_center 相关条目差异,task_discovery 条目已对齐 **未能运行的检查**: - e2e 测试依赖 baas / mosn / sofa-registry,本地环境不具备(按 `pytest.ini` 注释需 `RUN_ACCEPTANCE=1` + CI 容器) - `docs/arch/ci.enforce.md` 描述的完整 CI 门禁(依赖边界检查 / 配置 schema 校验 / 协议契约测试 / 红旗检测)依赖 CI 执行,本地未跑全 ## Compatibility and risk - **`DiscoveredTask` 字段重命名**:`project_name`/`description`/`business_scenario`/`work_item_url` → `title`/`instruction`/`background`(+`objective`+`acceptances`)。`task_reader.py` 通过 `DROP+CREATE` 重建 `discovered_tasks` 表,**旧库数据会被清空**;该表为 task_discovery 独立 SQLite,不影响其他持久化表 - **新增数据库表 `ac_task_discovery_lock`**:UNIQUE 约束 `(env, bot_id, discovery_date)` 即锁本体。需确认 REL20260826 部署环境上 `Base.metadata.create_all` 能正确建表,或已有对应 DDL 迁移 - **新增两个 HTTP 端点**:`POST /api/v1/collaboration/tasks/discovery/reschedule`、`POST /api/v1/collaboration/tasks/discovery/dingtalk-config`。无破坏性,鉴权沿用现有 task router - **回滚路径**:直接 revert 该 commit;无外部不可逆状态需要清理(除已落地的 lock 表行外,按日 TTL 自动失效) ## Spec - `src/backend/specs/2026-08-25-task-discovery-taskspec-align-and-notify-channels/design.md` — 技术设计:DiscoveredTask 对齐 TaskSpec + 发现流程双通知通道 - `src/backend/specs/2026-08-25-task-discovery-taskspec-align-and-notify-channels/spec.md` — 规范定义 - `src/backend/specs/2026-08-25-task-discovery-taskspec-align-and-notify-channels/tasks.md` — 任务跟踪 ## Related issues (optional) 无显式 issue 关联。本 PR 即把 `origin/dev` 上已落地的任务发现能力回填到 REL20260826 基线,便于发布线集成。
… in ORM truth and test anchors - TtlRenewalScheduleModel.__tablename__ + module docstring aligned to production DDL name (D-01) - contract TABLE constant + docstring renamed; has_table(TABLE) anchor untouched - 25 compiled-SQL assertions in repository unit tests renamed (D-02: strings only)
…name baas_bot_ttl_renewal_schedule
… retarget discovery-scan ttl projections
- ArcaCreationResult.ttl_expiration_time restored to str | None (fixed +08:00
'%Y-%m-%d %H:%M:%S'), new ttl_expiration_timestamp: int | None (ms epoch)
- time_utils gains CST=ZoneInfo('Asia/Shanghai') and format_ttl_expiration_time
- arca_paas_service emits both fields (numeric guard unchanged); facade passes
the pair through verbatim
- wrapper reads ttl_expiration_timestamp (ms) with never-raise posture intact
- find_unregistered both ttl projections retargeted to $.ttl_expiration_timestamp
(CR-GAP-01), label 'ttl' unchanged; compiled-SQL assertions pinned (mysql +
sqlite bare json_extract)
…anghai - time_utils: rename naive_utc_now/naive_utc_fromtimestamp to naive_cst_now/naive_cst_fromtimestamp with fixed Asia/Shanghai (+08:00, no DST) semantics; format_ttl_expiration_time delegates to naive_cst_fromtimestamp; module/function docstrings rewritten to the single-clock-domain CR-01 invariant - _deadline_renewal_task.py: 8 call sites + import block + CR-01 comment switched to naive_cst_* (arithmetic untouched) - _device_service_arca_ttl.py: register-path expiration conversion switched to naive_cst_fromtimestamp - tests: UTC expectation anchors in the scheduler and wrapper suites moved to fixed Asia/Shanghai; new tests/unit/core/utils/ test_time_utils.py pins all time_utils helpers (host-tz independent)
- time_utils.py line-wrap fix - test_device_service_arca_ttl.py spacing fixes - test_time_utils.py line-wrap fix Post-merge gate fix: the pre-merge tree passed targeted unit tests but failed tests/architecture/test_ruff_lint_rules.py::test_ruff_formatting_passes.
…gistered reader Regression gate found two TestFindUnregistered cases pinning the OLD reader contract (fixtures seeded only the ttl_expiration_time string key). Under the approved CR-GAP-01 retarget both projections now read $.ttl_expiration_timestamp: - _seed_hot_device gains a provider_device_props override parameter (mirroring _seed_hot_binding's device_props style) - found-row fixtures seed the dual-key scanner-format props - expected labels become the millisecond values (sqlite json_extract returns JSON integers as int) User-approved scope expansion (2026-08-25); file was outside the plan's declared file set.
…R-01/IN-01) - list_due_for_renewal docstring now states the naive_cst_now (+08:00) caller contract instead of the pre-refactor naive-UTC wording - naive_cst_fromtimestamp docstring drops the misleading '(or millis)' and states the divide-by-1000 requirement for millisecond epochs
…L pair chain WR-02 (code review): the creation extraction guard accepted 0 and the wrapper 'is None' check passed it through, so ttl_expiration_timestamp=0 became an epoch-0 (1970) renewal anchor while _renew_one treats 0 as missing - a due -> failure -> STOPPED oscillation loop. The creation chain now accepts only positive numbers (0 / negatives are not valid deadlines) and the wrapper treats falsy values as missing (skip register, warn, defer to discovery scan), matching _renew_one. WR-03 (code review): the creation chain silently dropped numeric-string ttl_timestamp values while the same pipeline's consumers (_renew_one, _get_info_sync) coerce them via int(float()). The creation chain now mirrors that coercion with an explicit TypeError/ValueError/OverflowError guard; non-numeric strings fall back to both fields None. Both are within the plan's declared file set; tests added for zero / numeric-string / non-numeric-string extraction cases and the wrapper zero-skip case.
Add rsync excludes for workspace/.repos and workspace/.prewarm/ready.json.
Problem
OCB corp-side DI modules reference baas_config.eval_template_uuid, but
the community BaasConfig dataclass lacks this field, causing
AttributeError: 'BaasConfig' object has no attribute
'eval_template_uuid' at runtime.
This field is required for the eval environment scenarios: when creating
persistent eval sandboxes (Scene 2) and ephemeral eval containers (Scene
3), the eval-specific ARCA template UUID must be passed to the BaaS
publish API. BaasConfig already has template_uuid (production) and
teclaw_template_uuid (TeClaw), but is missing the eval template field,
preventing corp-side code from routing to the correct eval template.
## Solution
Add eval_template_uuid: str = "" to the BaasConfig dataclass, positioned
between teclaw_template_uuid and personal_bot_template_uuid. Sync
ConfigModule.baas() provider to read eval_template_uuid from the YAML
config
block.
- Default value is an empty string (""), consistent with existing
teclaw_template_uuid / personal_bot_template_uuid.
- Non-breaking: pure field addition with a safe default. Community
deployments that don't configure this field get an empty string; corp
overlays inject the actual value.
- Rejected alternative: subclassing BaasConfig on the corp side —
violates Microkernel Architecture Rule 14 (all DI wiring must be
configuration-driven, no if is_local_mode() branches). Corp modules
(SingleboxDefaultEnvBotModule, CorpDefaultEnvBotModule) already
reference baas_config.eval_template_uuid directly, so the community
class must expose the attribute.
## Validation
- OCB singlebox end-to-end: after updating ocb-public pointer, POST
/api/service-bot/default-env/create no longer throws AttributeError;
switch validation and business logic return correctly.
- OCB corp DI unit tests: test_default_env_bot_di.py — 16/16 passed.
- Community CI: unaffected — pure field addition with default value.
## Compatibility and risk
- Backward-compatible: new field has a default value; existing configs
and deployments are unaffected.
- Config migration: corp deployments that need the eval environment must
add eval_template_uuid under user_config.baas (no change if already
present).
- Rollback: remove the config entry; the field defaults to empty,
causing eval template routing to fail gracefully rather than crash.
## Related issues (optional)
Required by OCB eval environment infrastructure (Scene 2: persistent
eval sandbox; Scene 3: ephemeral eval container).
---------
Co-authored-by: jiumu.zd <jiumu.zd@alipay.com>
Co-authored-by: Claude (GLM-5.1) <noreply@anthropic.com>
为不经过 Gateway 的普通 HTTP 调用链增加 JWT 身份认证能力,同时复用系统已有的 Principal 验签逻辑,不重新实现
JWT 安全机制。
调用方
↓ 携带 X-Avernet-Principal JWT
Backend 普通 HTTP 接口
↓
复用 resolve_caller()
↓
复用 verify_principal_token()
↓
校验签名、有效期、issuer、audience 和 Principal 结构
↓
获取 VerifiedCaller.user
---------
Co-authored-by: Weijia Sun <freddiesun95@gmail.com>
Co-authored-by: tianxun <tianxun.wr@antgroup.com>
Co-authored-by: Dylan <48973507+yhzhang35@users.noreply.github.com>
Co-authored-by: VINCE <12515529+vzvince@users.noreply.github.com>
Co-authored-by: lucas <rongzhi.xzp@antgroup.com>
Co-authored-by: jiangj0627 <jian.jiangj@antgroup.com>
Co-authored-by: sj_mei <shangjian.msj@antfin.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: WEN <luzhi.zw@antgroup.com>
Co-authored-by: pf_miles <yue.weny@antgroup.com>
Co-authored-by: pf_miles <miles.wy.1@gmail.com>
## Summary - refresh AICoding MCP scope/details on restart when `confirmed_template_update` is set - actively resync runtime skill symlinks after confirmed template restart - keep restart refresh logic inside AICoding strategy and avoid CLI grant changes - inject MCP sync into `BotService` from the DI composition root instead of exposing it through `DeviceService` ## Compatibility and risk - Scope is gated by `extra_configs["confirmed_template_update"]`; plain restart keeps existing behavior and does not resync MCP/skills. - Affected call sites are the restart paths that can reach a ready device: - replacement restart: `_allocate_device_async` calls `refresh_restart_authorization` after `apply_device` succeeds, so the refresh targets the new runtime instead of the old binding. - BaaS in-place restart: `restart_bot` calls `refresh_restart_authorization` after `_restart_bot_baas` succeeds. - pre-allocation restart extension handling only applies config; runtime refresh is intentionally deferred to the ready-device points above. - `refresh_restart_authorization` return value means "engine opted into async best-effort refresh"; it does not mean MCP/skill sync succeeded. - The previous restart Passport scope rewrite is intentionally not reused for this AICoding path: this change syncs latest MCP runtime configuration and refreshes `~/.claude/skills` symlinks, without modeling `mcporter` or `la ~/.claude/skills/` as CLI grants. - `caller_identity_repo` remains in `BotService` construction for release-branch DI/API compatibility, but is no longer used by this restart-runtime refresh path. ## Tests - `.venv/bin/python -m py_compile src/agentclaw/community/core/bot_management/engines/aicoding/strategy.py src/agentclaw/community/core/bot_management/engines/default.py src/agentclaw/community/core/bot_management/engines/provisioning.py src/agentclaw/community/core/bot_management/services/bot_service.py src/agentclaw/community/core/devices/services/device_service.py src/agentclaw/community/di/modules/bot_management_module.py tests/community/core/bot_management/services/test_restart_authorization_refresh.py tests/community/core/bot_management/services/test_bot_service_restart_idempotency.py` - `.venv/bin/python -m pytest tests/community/core/bot_management/services/test_restart_authorization_refresh.py tests/community/core/bot_management/services/test_bot_service_restart_idempotency.py::TestRestartAuthorizationResyncWiring tests/community/core/mcp/services/test_sync_service.py tests/community/core/mcp/test_defaults_per_engine.py -q` (`97 passed, 17 warnings`)
## Problem 任务派发前缺少基于 claim_on 关系的候选过滤,可能将任务派发给无权限的 Bot。协作群任务也缺少稳定的 driver Bot 身份和回调鉴权信息,导致任务执行结果 无法可靠回投和收敛。 ## Solution - 新增 claim_on JOIN 灰度开关和无状态授权转发能力。 - 搜推候选增加 claim 关系过滤,并保留未授权候选的诊断信息。 - 协作群创建时补充 driver Bot token,明确 Bot 调用身份。 - 支持按 group_kind 区分 chat、manager_worker 和 state_machine。 - 完善协作群事件订阅和任务回调链路,支持任务执行状态与结果回投。 - 增加 OpenAPI Bot 跨事件循环客户端处理,避免异步任务跨 loop 使用连接池。 - 增加 task-discovery 的 OpenAPI 会话发起能力及相关 DI 接线。 - 保留 stateless grant/revoke 能力,移除本地授权状态依赖。 - 补充 claim JOIN、Bot token、OpenAPI 会话、状态机和任务发现相关测试。 - 同步相关 Backend、BaaS 配置及 ACK Sandbox TTL 处理逻辑。 ## Validation - Pre-push SAST/lint gate:通过。 - 任务相关测试、OpenAPI Bot 适配器测试及 task-discovery 测试已补充。 - 完整 Backend CI 已执行,当前分支后续需以最新 CI 结果为准。 ## Compatibility and risk - 任务执行接口保持兼容,新增字段均通过可选配置或 DI 注入。 - 未配置 claim JOIN 或 Bot token provider 时,保留降级路径,不阻断基础任务派发。 - OpenAPI Bot 默认保持 OOB 预授权模式,不强制调用 grant 接口。 - 协作群回调失败时保留 poller 兜底收敛机制。 --------- Co-authored-by: WEN6Lev57q4 <luzhi.zw@antgroup.com> Co-authored-by: jian.jiangj <jian.jiangj@antgroup.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Cassius <langlang.cai@antgroup.com>
## Summary - refresh AICoding restart MCP scope only after confirmed template updates - retry runtime MCP detail sync while BaaS devices are not active yet - retry Claude skills symlink runtime sync in the same best-effort background flow ## Test - cd src/backend && .venv/bin/python -m pytest tests/community/core/bot_management/services/test_restart_authorization_refresh.py tests/community/core/bot_management/services/test_bot_service_restart_idempotency.py::TestRestartAuthorizationResyncWiring -q
仅使用 skill_id 定位 Skill; - 用户身份从已验证的 Principal 中解析,不再接收 user_id Query 参数; - Git Skill 直接从公共 Skill 仓库读取; - Local Skill 根据 Skill 记录反查 Bot,并校验访问权限; - 返回统一 OpenAPI Envelope,data 内保留普通接口的 content 字段; --------- Co-authored-by: Weijia Sun <freddiesun95@gmail.com> Co-authored-by: tianxun <tianxun.wr@antgroup.com> Co-authored-by: Dylan <48973507+yhzhang35@users.noreply.github.com> Co-authored-by: VINCE <12515529+vzvince@users.noreply.github.com> Co-authored-by: lucas <rongzhi.xzp@antgroup.com> Co-authored-by: jiangj0627 <jian.jiangj@antgroup.com> Co-authored-by: sj_mei <shangjian.msj@antfin.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: WEN <luzhi.zw@antgroup.com> Co-authored-by: pf_miles <yue.weny@antgroup.com> Co-authored-by: pf_miles <miles.wy.1@gmail.com>
## Problem `McpSkillSetControlPlaneCommands.remove_mcp()` duplicated a partial Default-SkillSet exclusion path even though `SkillSetManagementService.remove_mcp()` already routes Default Sets to `exclude_default_mcp()`. The duplicate branch did not validate platform-default policy, retire stale Installation rows, or return the changed MCP code for projection. This is the follow-up cleanup requested in the review of #1608. ## Solution - Keep Default/ordinary routing at the `SkillSetManagementService` seam, where engine/template default policy is available. - Make repository `remove_mcp()` ordinary-membership-only, matching `add_mcp()`, by enforcing `_ordinary(row)` under the Set row lock. - Remove the unreachable duplicate Default exclusion implementation and its unused ORM import. - Add a repository contract regression proving ordinary membership removal rejects a Default Set address without creating an exclusion. ## Validation - TDD red: focused regression failed with `DID NOT RAISE SkillSetControlPlaneConflictError`. - TDD green: focused regression passed (`1 passed`). - Repository UoW + `SkillSetManagementService`: `150 passed`. - Affected Skill Center core/repository suites: `1075 passed, 27 skipped`. - Backend architecture suite: `201 passed`. - Ruff lint on changed files: passed. - Backend local SAST block scan against `github/REL20260828...HEAD`: passed. - Full Backend suite was not run locally because this is an isolated repository refactor; GitHub CI remains the full-suite gate. - Whole-file `ruff format --check` reports existing formatting drift in both touched legacy files; they were not bulk-formatted to avoid an unrelated large diff. ## Compatibility and risk No HTTP, Service API, Plugin API, database schema, or runtime projection behavior changes for supported callers. Default MCP opt-out continues through `exclude_default_mcp()`; ordinary membership removal continues through `remove_mcp()`. Direct repository misuse of `remove_mcp()` with a Default Set now fails closed with `SYSTEM_DEFAULT_IMMUTABLE` instead of executing the incomplete duplicate path. ## Related issues Follow-up to #1608 and its Default MCP exclusion review comments.
## Summary - Persist AICoding confirmed restart resync intent with template config - Defer BaaS in-place restart MCP/CLI/skill runtime refresh until restart publish completes - Add AICoding BaaS publish listener and targeted tests ## Tests - uv run python -m py_compile src/agentclaw/community/core/bot_management/engines/aicoding/strategy.py src/agentclaw/community/core/bot_management/engines/aicoding/restart_authorization_listener.py src/agentclaw/community/core/bot_management/engines/provisioning.py src/agentclaw/community/core/bot_management/engines/default.py src/agentclaw/community/core/bot_management/services/bot_service.py src/agentclaw/community/di/modules/bot_management_module.py - uv run pytest tests/community/core/bot_management/services/test_restart_authorization_refresh.py tests/community/core/bot_management/services/test_restart_template_refresh.py tests/community/core/bot_management/services/test_bot_service_restart_idempotency.py::TestRestartAuthorizationResyncWiring -q
liyaqing-l
requested review from
FreddieSun,
carolynli,
cassiuscai,
jiangj0627,
liveandevil,
msjbear,
regrecall,
totalfrank,
vzvince,
xianmuyq,
xxxxpenny and
yuyiming
as code owners
August 28, 2026 06:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.