Files
dingtalk-workspace-cli/docs/batch-auth-prd.json
T

506 lines
37 KiB
JSON

{
"title": "DWS Batch Authorization and Login Recommend Flow",
"version": "2026-06-16",
"owners": {
"cli": "dingtalk-workspace-cli",
"gateway": "lippi-open-mcpserver-gw",
"patCore": "lippi-pat-core-wukong-capability",
"bff": "lippi-open-app-dev",
"frontend": "lippi-open-dev-front"
},
"context": {
"problem": "DWS login --recommend and product-line batch authorization need a user-visible authorization page instead of silent bulk grants or local --yes-only blocking.",
"reference": {
"larkCli": {
"loginRecommend": "lark-cli auth login --recommend",
"domainAll": "lark-cli auth login --domain all",
"noWait": "lark-cli auth login --no-wait --json",
"deviceCode": "lark-cli auth login --device-code <code>",
"exclude": "lark-cli auth login --exclude <scope>",
"scopeCatalog": "lark-cli auth scopes",
"scopeCheck": "lark-cli auth check --scope <scope...>",
"qrCode": "lark-cli auth qrcode <url>",
"yesFlag": "lark-cli auth login has no --yes flag; lark-cli auth login --yes --no-wait --json returns Error: unknown flag: --yes. Therefore --yes must not be treated as an auth-login popup bypass."
},
"ralphCli": {
"model": "PRD-driven long-running development with explicit task ledger, progress notes, verification gates, and failure analysis.",
"evidence": "ralph v0.16.0 help and /Users/xuan/knowledge/META-ralph-reference.md",
"mapping": "This PRD keeps a single SSOT for goals, contracts, tasks, security gates, acceptance criteria, and blocked evidence so implementation can continue across multiple repositories without redefining done."
},
"existingDingTalkPage": "/src/app/personalAuthorization/index.jsx",
"targetPage": "Reuse the personalAuthorization route and render multiple scopes grouped by product."
},
"accessNotes": [
"The frontend GitLab repo was cloned through SSH to /Users/xuan/lippi-open-dev-front and implemented on branch codex/batch-auth-page-flow.",
"The DWS PAT BFF lives in /Users/xuan/lippi-open-app-dev and needs to be part of the chain because it owns /dingtalk-workspace-cli/oauth/device/action.",
"Acceptance is not complete after local build. It is complete only after prepub package/build succeeds for every changed repo and prepub functional verification passes.",
"The referenced DingTalk minutes URL could not be fetched because local dws is not logged in."
]
},
"goals": [
"After dws auth login --recommend succeeds, DWS starts a PAT recommended batch plan and opens the authorization page.",
"For PAT batch authorization commands through dws pat chmod without --yes, including multi-scope, product-line, and recommend modes, DWS opens the same authorization page instead of returning a local validation error.",
"The server, not the CLI, selects grantable scopes. CLI must grant only selectedScopes from pat.batch_plan.",
"Recommended login scope selection is controlled by prepub Diamond: open-source visible scopes are the enabled template set, recommendation is the low-risk subset, and the 22 medium-risk scopes are excluded from default login recommend.",
"High-risk scopes remain pending and must not be silently granted."
],
"nonGoals": [
"Do not trust CLI-provided orgId or uid.",
"Do not add a second authorization UI path.",
"Do not make medium-risk or high-risk destructive scopes part of default login recommend."
],
"userFlows": [
{
"name": "loginRecommend",
"steps": [
"User runs dws auth login --recommend.",
"CLI completes OAuth loopback or device login and saves token.",
"CLI calls pat.batch_plan with recommend=true, grantType=permanent, caller=auth_login_recommend, dryRun=true.",
"In an interactive terminal, CLI shows a Feishu-like TUI product-domain selector with all product lines selected by default; user can toggle, filter, or confirm.",
"CLI re-plans with the selected productCodes before starting the grant flow.",
"PAT core loads the Diamond-driven recommended set, excludes non-recommended medium-risk scopes from the default plan, and returns selectedScopes plus warnings/skipped scopes.",
"CLI calls pat.batch_grant with scopes=selectedScopes, startFlow=true, noWait=true, caller=auth_login_recommend.",
"PAT core creates a single batch Device Code Flow and returns PAT_BATCH_AUTH_PENDING with flowId/userCode/uri.",
"CLI opens uri, polls flow status, and retries the original grant after approval."
]
},
{
"name": "batchChmodWithoutYes",
"steps": [
"User runs dws pat chmod --recommend --product <product> without --yes.",
"CLI plans first, then starts a batch Device Code Flow instead of direct grant.",
"The authorization page displays all grantable selected scopes grouped by product.",
"Approve writes selected grants; reject leaves all grants unchanged."
]
},
{
"name": "batchChmodWithYes",
"steps": [
"User runs the same command with --yes.",
"CLI continues the existing non-interactive grant path.",
"PAT core writes grantable low/medium-risk scopes directly and returns high-risk scopes as pending."
]
}
],
"larkCliParity": [
{
"lark": "auth login --recommend",
"dws": "dws auth login --recommend",
"serverContract": "pat.batch_plan recommend=true caller=auth_login_recommend, then pat.batch_grant selectedScopes startFlow=true noWait=true",
"status": "implemented locally in CLI, gateway, PAT core, BFF, and frontend; prepub package and verification pending"
},
{
"lark": "auth login --domain <domain>|all",
"dws": "dws pat chmod --product <productCode[,productCode]> or --products <...>",
"serverContract": "pat.batch_plan productCodes=[...] resolves service-owned product scope templates",
"status": "implemented for PAT chmod product-line flow"
},
{
"lark": "auth login --scope <scopes>",
"dws": "dws pat chmod <scope...>",
"serverContract": "one scope preserves direct grant behavior; multiple scopes enter batch plan/flow when --yes is absent",
"status": "implemented for multi-scope PAT chmod"
},
{
"lark": "auth login --exclude <scope>",
"dws": "batch authorization page selectedScopes",
"serverContract": "frontend sends selectedScopes; PAT core rejects any selected scope outside flow scopes and treats unchecked flow scopes as rejectedScopes",
"status": "implemented locally in PAT core, BFF selectedScopes passthrough, and frontend grouped selection; prepub verification pending"
},
{
"lark": "auth login --no-wait --json and later --device-code",
"dws": "PAT_BATCH_AUTH_PENDING JSON with uri/authUrl/flowId/userCode plus polling retry",
"serverContract": "host-owned JSON mode returns structured pending and does not block; local interactive mode opens browser and polls",
"status": "implemented in CLI retry path and PAT pending response"
},
{
"lark": "auth login --yes",
"dws": "not supported for dws auth login; --yes belongs only to explicit batch grant commands such as dws pat chmod",
"serverContract": "auth login --recommend must still open or return the authorization page. For chmod-style batch commands, --yes may skip local CLI confirmation and attempt direct grant, but server-side pending/high-risk/page-required responses still win.",
"status": "verified on 2026-06-16 with local lark-cli: unknown flag --yes"
},
{
"lark": "auth scopes and auth check",
"dws": "pat.batch_plan dryRun and existing PAT auth check result",
"serverContract": "plan exposes selected/skipped/pending/already-granted scope sets; check/auth retry carries missing scope and grant options",
"status": "partially aligned; no new public scope-catalog command added in this slice"
}
],
"ralphExecutionPlan": {
"project": "dws-batch-authorization-page",
"tasks": [
{
"id": "1",
"title": "Build PRD and command parity map",
"description": "Use product-design style requirements plus Ralph-style SSOT to map DWS batch authorization against lark-cli.",
"steps": [
"Define user flows for login recommend, product-line grant, multi-scope grant, yes direct grant, and JSON no-wait mode.",
"Record frontend access and DingTalk minutes access blockers explicitly.",
"Keep acceptance criteria tied to command outputs and service contracts."
],
"done": true
},
{
"id": "2",
"title": "Implement DWS CLI batch authorization entry points",
"description": "dws auth login --recommend and dws pat chmod batch/product/recommend without --yes start the page-based flow.",
"steps": [
"Add --recommend to auth login and run PAT recommended plan after login.",
"Route product-line, recommend, and multi-scope chmod without --yes to pat.batch_grant startFlow.",
"Keep --yes as the explicit direct-write path."
],
"done": true
},
{
"id": "3",
"title": "Harden gateway identity and caller allowlist",
"description": "The gateway must derive orgId/uid from trusted identity and allow intended DWS batch callers.",
"steps": [
"Keep CLI-provided orgId/uid untrusted.",
"Allow dws-cli alongside openclaw for intended batch write callers.",
"Preserve caller/source checks for default grant writes."
],
"done": true
},
{
"id": "4",
"title": "Implement PAT core recommended filtering and batch DCF",
"description": "PAT core owns selectedScopes, Diamond-driven recommendation, batch Device Code Flow, selected approval, and idempotent writes.",
"steps": [
"Keep recommended scope selection config-driven: preauth Diamond recommends the low-risk subset and excludes the 22 medium-risk scopes.",
"Create/reuse one batch flow for selected scopes.",
"Approve only selectedScopes and reject out-of-flow selections.",
"Fail closed on critical storage failures and leave flow retryable."
],
"done": true
},
{
"id": "5",
"title": "Implement lippi-open-dev-front batch authorization UI",
"description": "Reuse /src/app/personalAuthorization/index.jsx and render grouped product permissions with selectable scopes.",
"steps": [
"Load flow detail and detect multiple scopes.",
"Default-check all server-selected flow scopes.",
"Allow product and individual-scope deselection.",
"Submit selectedScopes on approve and no orgId/uid.",
"Handle PAT_BATCH_PARTIAL_FAILURE by keeping the page retryable."
],
"done": true
},
{
"id": "6",
"title": "Submit all repos to prepub and verify package plus behavior",
"description": "Acceptance only passes after prepub package/build succeeds and prepub functional verification is OK.",
"steps": [
"Push or submit branches for CLI, gateway, PAT core, BFF, and frontend.",
"Trigger prepub package/build for every changed repo.",
"Verify prepub deployed versions include the current change set.",
"Run login recommend and batch chmod functional checks on prepub.",
"Open the prepub authorization page and verify layout and approve/reject callbacks."
],
"done": false
}
]
},
"prepubReleaseGate": {
"overallStatus": "not_passed_until_gateway_pat_current_heads_redeployed_and_bff_integration_test_resolved",
"changedRepos": [
{
"repo": "dingtalk-workspace-cli",
"localPath": "/Users/xuan/dingtalk-workspace-cli",
"branch": "codex/dws-auth-login-recommend",
"commit": "70b9f39715566a8306f8d437cc62ad2f12934449",
"prepubPackage": "pushed to GitHub branch and rebuilt local default /usr/local/bin/dws -> /Users/xuan/bin/dws from current source; local go test ./internal/app ./internal/pat passed. Still blocked_no_aone_app_for_current_github_repo_lippi_dws_release_uses_gitlab_release_repo for formal prepub package evidence.",
"prepubFunctionalVerification": "local_cli_tui_contract_ok: interactive default dws auth login --recommend uses human TUI mode with all product domains selected before grant; explicit --format json and non-interactive shells keep structured JSON. current_head_e2e_pending_server_redeploy"
},
{
"repo": "lippi-open-mcpserver-gw",
"localPath": "/Users/xuan/lippi-open-mcpserver-gw",
"branch": "codex/pat-batch-auth-service-gate-20260616",
"commit": "a0873a6a98b96eaa86a1e8e9f75726a3f8dc16a9",
"aoneAppId": 296391,
"aoneCrId": 34656146,
"aoneCrStatus": "INTG",
"aoneCrStatusLabel": "发布中",
"prepubPipelineId": 66,
"prepubPipelineInstanceId": 3094877547,
"prepubPackage": "Aone CR 34656146 pipeline 66 instance 3094877547 had build/deploy/integration success and deploy orders 155284562/155284566 succeeded, but that deploy completed before current head a0873a6 was committed; current head is not proven deployed. Final task 3390336920 is WAITING: CR34656146,CR34302328,CR34364191,CR34598300 待验证.",
"prepubFunctionalVerification": "pending_current_head_redeploy_or_equivalent_receipt"
},
{
"repo": "lippi-pat-core-wukong-capability",
"localPath": "/Users/xuan/lippi-pat-core-wukong-capability",
"branch": "codex/batch-auth-page-flow",
"commit": "79a8fa5a9e5a4cc20f14efe567680a4b2ab897e3",
"aoneAppId": 312546,
"aoneCrId": 34656143,
"aoneCrStatus": "INTG",
"aoneCrStatusLabel": "发布中",
"aoneBranchRevision": "b38e29e9e9c0f55dc71762c37d261180cb30692b",
"aoneNewestRevision": "79a8fa5a9e5a4cc20f14efe567680a4b2ab897e3",
"prepubPipelineId": 66,
"prepubPipelineInstanceId": 3094928748,
"prepubPackage": "Aone CR 34656143 latest pipeline 66 instance 3094948452 had build/deploy/integration success and deploy order 155318893 succeeded, but that deploy completed before current head 79a8fa5 was committed; current head is not proven deployed. Final task 3390679518 is WAITING: CR34656143 待验证.",
"prepubFunctionalVerification": "pending_current_head_redeploy"
},
{
"repo": "lippi-open-app-dev",
"localPath": "/Users/xuan/lippi-open-app-dev",
"branch": "codex/batch-auth-bff-selected-scopes",
"commit": "9ac576c77d142b5f4ec318c8d7b8ecf472ea6341",
"aoneAppId": 154450,
"aoneCrId": 34656145,
"aoneCrStatus": "INTG",
"aoneCrStatusLabel": "发布中",
"aoneBranchRevision": "f4222f3b2acba1899446d338c4b3788b8a58b29f",
"aoneNewestRevision": "9ac576c77d142b5f4ec318c8d7b8ecf472ea6341",
"prepubPipelineId": 66,
"prepubPipelineInstanceId": 3094929764,
"prepubPackage": "Latest Aone pipeline 66 instance 3095006435 built and deployed current head 9ac576c to prepub/non-prod; deploy orders 155345552 and 155345553 succeeded. Pipeline final status remains FAIL because integration test task 3390939831 failed; validation task 3390939832 remains INIT.",
"prepubFunctionalVerification": "pending_bff_integration_test_resolution_or_accepted_exception"
},
{
"repo": "lippi-open-dev-front",
"localPath": "/Users/xuan/lippi-open-dev-front",
"branch": "codex/batch-auth-page-flow",
"commit": "f33e349b2c29c7ba139e48fdfbdd70759ce3a4c4",
"aoneAppId": null,
"aoneCrId": null,
"prepubPackage": "O2 assets app still does not support pub-env=pre. Non-production daily assets build/deploy succeeded on task 149432767 for commit f33e349b2c29c7ba139e48fdfbdd70759ce3a4c4, build id 53073038, asset count 203, published to dev.g.alicdn.com/dingding/lippi-open-dev-front/5.219.0/. Local npm run build passed with existing bundle-size warnings only.",
"prepubFunctionalVerification": "frontend_daily_cdn_copy_and_account_avatar_priority_ok; full_e2e_pending_server_current_heads",
"o2AppId": 8780,
"o2IterationId": 16804012,
"o2TaskId": 149432767,
"o2FlowId": 114592657,
"o2ReleaseVersion": "5.219.0",
"o2PublishEnv": "daily"
}
],
"requiredEvidence": [
"Every changed repo has a pushed branch or prepub release submission tied to the current change set.",
"Prepub package/build succeeds for CLI, gateway, PAT core, BFF, and frontend.",
"Prepub deployed versions include the selectedScopes frontend-to-BFF-to-PAT core chain.",
"Prepub functional verification covers login recommend, chmod without --yes page flow, chmod with --yes direct flow, selectedScopes subset approval, reject, Diamond-driven low-risk recommendation, high-risk pending, and JSON no-wait pending response.",
"Prepub UI screenshot matches visualDesignTarget.",
"Frontend O2 assets app uses daily as the available non-production channel because prepub is unsupported for assets pub type."
],
"latestPrepubVerification": {
"verifiedAt": "2026-06-16T17:22:12+08:00",
"recommendationPolicy": {
"sourceOfTruth": "prepub Diamond plus product_scope_template DB",
"openWhitelistDataId": "com.dingtalk.pat.opensource-scope-whitelist",
"openWhitelistScopeCount": 173,
"preauthDataId": "com.dingtalk.pat.wukong-preauth-config",
"preauthVersion": "2026-06-16-dws-low-risk-151-preauth-v1",
"recommendedLowRiskScopeCount": 151,
"excludedMediumRiskScopeCount": 22,
"slsReceipt": "2026-06-16 17:19:26 prehost logs: OpenSourceScopeWhitelistConfigManager scopeCount=173; WukongPreAuthConfigManager recommendAllScopes=false scopeCount=151"
},
"dryRun": {
"command": "go run ./cmd/main.go pat chmod --recommend --dry-run --agentCode dingmbw5n9ktkkbbjv3g --format json",
"result": "items=151, selected=43, skipped=108, risk_counts={LOW:151}, skipped_reasons={already granted:108}",
"localReceipt": "/tmp/dws-pat-chmod-recommend-dryrun-after-diamond.json"
},
"loginRecommendApproval": {
"command": "go run ./cmd/main.go auth login --recommend --format json",
"flowId": "a4ef35755fb04c248b0d4319ce73651c",
"userCode": "E9CL-YJCV",
"authorizationUrl": "https://pre-open-dev.dingtalk.com/fe/old?hash=%23%2FpersonalAuthorization%3FflowId%3Da4ef35755fb04c248b0d4319ce73651c%26userCode%3DE9CL-YJCV#/personalAuthorization?flowId=a4ef35755fb04c248b0d4319ce73651c&userCode=E9CL-YJCV",
"pageObservedText": "DingTalk-Workspace请求你的授权; 可授权的权限(已选43/43项); 同意并授权; 授权成功",
"slsReceipt": "DeviceCodeFlowService.grantApprovedScopesBatch approved permanent via batch insert, flowId=a4ef35755fb04c248b0d4319ce73651c, orgId=439446171, uid=2010955235, agentCode=dingmbw5n9ktkkbbjv3g, scopeCount=43"
},
"databaseEvidence": {
"dmsGroup": "lippi_pat_core_product_logic",
"db": "product",
"agentCode": "dingmbw5n9ktkkbbjv3g",
"orgId": 439446171,
"uid": 2010955235,
"lowRiskRecommendedGranted": "151 rows, status=1, grant_scope=3",
"selectedIncrementGranted": "43 rows",
"activeMediumRiskGrants": "18 rows existed as historical grants; they were not part of this recommend plan because dry-run risk_counts had no MEDIUM rows",
"resultFiles": [
"/Users/xuan/dms-alibaba/db-groups/lippi_pat_core_product_logic/sql/quick_product/_results/2026-06-16/172211_product.json",
"/Users/xuan/dms-alibaba/db-groups/lippi_pat_core_product_logic/sql/quick_product/_results/2026-06-16/172212_product.json"
]
}
},
"latestDiagnostics": [
"PAT core Aone compile error at PatAuthCheckService.java:517 was caused by a two-argument tryConsumeOnceTokenFromTair call after the method signature changed to require effective risk level. Current feature and release-merge worktrees use RiskLevelEnum.MEDIUM.getCode() for the high-risk-to-medium DCF bypass path and compile locally.",
"The Maven warning about requested profile lippi-pat-core is an Aone/Maven parameter warning: the root POM has no profile with id lippi-pat-core. It is not the cause of the Java compilation failure and does not justify a POM change in this slice.",
"PAT core API version is explicitly aligned: root property lippi-pat-core-api.version is 1.0.5-SNAPSHOT and lippi-pat-core-api/pom.xml project.version evaluates to 1.0.5-SNAPSHOT. Verification passed with Java 11 Maven help:evaluate and lippi-pat-core-api compile.",
"PAT core root POM now defines lippi-enterprise-soc-api.version as 1.0.30-SNAPSHOT, matching local reference repos /Users/xuan/lippi-pat-core and /Users/xuan/lippi-pat-core-prepub-merge-34465221. Verification passed with Java 11 Maven lippi-pat-core-service compile.",
"PAT core Aone build task 3390400102 failed because release branch commit 3555c3aa resolved lippi-open-app-dev-api as 1.0.103, which lacks PatConnectorQueryService and PatConnectorQueryResult. Feature branch commit 78c1c311e91e2c2d204622b6219b8c1a179b6932 changes lippi-open-app-dev.version to 1.2.0-SNAPSHOT, matching /Users/xuan/lippi-pat-core and release merge worktree. Local Java 11 lippi-pat-core-service compile passed and the 1.2.0-SNAPSHOT jar contains both missing classes.",
"PAT core prepub runtime failure for flowId 8b35d5b6373c4cb9a7a67b339516fff2 was TDDL-4603 ERR_ACCROSS_DB_TRANSACTION. The failing path approved a batch flow by looping single-scope INSERT INTO agent_pat in one transaction, crossing sharding groups. Commit 0b805ff changes permanent/session batch approval and direct batch grant to PatAuthGrantService#grantScopesBatch, which uses AgentPatDAO.batchInsert and PatAuthRecordDAO.batchInsert where applicable, while DeviceCodeFlowService#handleFlowAction runs with Propagation.NOT_SUPPORTED to avoid opening a strict cross-shard transaction around the flow action.",
"PAT core local regression passed on 2026-06-16 with Java 11: mvn -pl lippi-pat-core-service -Dtest=PatAuthGrantServiceBatchTest,PatBatchGrantIdempotencyTest,DeviceCodeFlowServiceTransactionTest,DeviceCodeFlowServiceBatchSelectionTest test -s /Users/xuan/Downloads/settings.xml and full mvn -pl lippi-pat-core-service test -s /Users/xuan/Downloads/settings.xml.",
"PAT core Aone pipeline 66 instance 3094916508 reached SUCCESS for code merge, build, prepub deploy, and prepub integration test; prepub validation remained RUNNING when checked.",
"BFF now depends on com.dingtalk.pat:lippi-pat-core-api:1.0.5-SNAPSHOT and uses HandleFlowActionRequest#setSelectedScopes directly. The old reflection fallback has been removed.",
"BFF local verification passed with Java 11: mvn -pl lippi-open-app-dev-start -am -DskipTests compile -s /Users/xuan/Downloads/settings.xml.",
"BFF latest branch head is 9ac576c77d142b5f4ec318c8d7b8ecf472ea6341 after merging latest master. Aone CR 34656145 is INTG and pipeline 66 instance 3094919448 had code merge and build SUCCESS, with prepub deploy RUNNING at 2026-06-16 16:28:13+08:00.",
"Gateway latest branch head is a0873a6a98b96eaa86a1e8e9f75726a3f8dc16a9. It rejects pat.batch_grant calls that carry plan-only recommend/productCodes fields, normalizes non-empty selected scopes before forwarding to PAT core, and has local verification uv run python -m pytest test/test_pat_batch_bridge.py -q: 26 passed.",
"Frontend latest branch head is 356dcbab5d02c9f0225baabbd80a98b28d6f92b3. It only renders the batch page when the server explicitly marks batchFlow/isBatchFlow/flowType=BATCH, and reject success falls back to FLOW_REJECTED so the terminal page shows 已拒绝授权 instead of 授权失败. Verification: npm run build passed with existing bundle-size warnings.",
"lark-cli parity check on 2026-06-16: lark-cli auth login --help does not list --yes, and lark-cli auth login --yes --no-wait --json exits with Error: unknown flag: --yes. Lark recommended login therefore still requires Device Flow/browser authorization and does not provide a --yes bypass.",
"Non-PAT side verification on 2026-06-16 passed: CLI go test ./internal/app ./internal/pat ./internal/errors, gateway py_compile plus diff check, and frontend npm run build with existing bundle-size warnings only.",
"2026-06-16 23:59+08:00 frontend review follow-up: src/app/personalAuthorization/index.jsx now matches requested batch copy: title 可授权的权限, product group count as N 项, footer link 个人数据授权管理. Commit 065178a7011174c14952c89992022968a8a49a7b pushed to codex/batch-auth-page-flow.",
"Frontend O2 release route confirmed: app lippi-open-dev-front id=8780, iteration 16804012 named pat页面授权, branch codex/batch-auth-page-flow. O2 iteration id 86573 from the provided Dbase URL maps to an old pmod iteration in a1 o2, so 16804012 is the correct O2 release target discovered from app 8780.",
"Frontend O2 non-production task 149432667 succeeded for commit 065178a7011174c14952c89992022968a8a49a7b, flow 114592128, version 5.219.0, pub_env daily. Attempting pub_env pre returned: assets does not support prepub.",
"Independent cross-repo review found no P0/P1 contract break in CLI -> gateway -> BFF -> PAT -> frontend for selectedScopes. The important contract is that gateway pat.batch_grant takes scopes, while CLI/BFF use selectedScopes only at their own API boundaries before converting/passing to PAT.",
"CLI recommend login TUI default-selects all product domains before plan/grant, matching the Lark-like pre-authorization selector requirement. Local verification passed with go test ./internal/pat ./internal/app.",
"Frontend CDN verification after O2 task 149432667: https://dev.g.alicdn.com/dingding/lippi-open-dev-front/5.219.0/bundle.js contains 个人数据授权管理 and 可授权的权限, and no longer contains 应用授权管理 or 可授权的权限(已选.",
"2026-06-17 CLI fix: Bacon review found plain dws auth login --recommend skipped the Lark-like product selector because global --format defaults to json. Commit 9ee8ea552406d1804f062f2f9165a031b1f87ab3 changes auth login recommend selector gating so default json still shows the TUI in an interactive terminal, while explicit --format json and non-interactive shells skip it. Verification: go test ./internal/app ./internal/pat passed.",
"2026-06-17 local default dws binary refreshed: /usr/local/bin/dws -> /Users/xuan/bin/dws was rebuilt from dingtalk-workspace-cli commit 9ee8ea552406d1804f062f2f9165a031b1f87ab3; dws version --format json reports version dev, edition open.",
"2026-06-17 CLI follow-up: commit 70b9f39715566a8306f8d437cc62ad2f12934449 changes interactive dws auth login --recommend to use a human recommend flow when --format is not explicitly set. This prevents the PAT authorization wait path from falling back to machine JSON after the TUI. Default interactive behavior is now: login success summary, all-selected product-domain TUI, server re-plan, complete authorization URL print/open, browser approval polling. Explicit --format json and non-interactive shells remain structured/no-block. Verification: go test ./internal/app ./internal/pat passed, and /usr/local/bin/dws -> /Users/xuan/bin/dws was rebuilt from this commit.",
"2026-06-17 frontend account-row fix: commit f33e349b2c29c7ba139e48fdfbdd70759ce3a4c4 makes the authorization account row prefer userAvatar/userName before orgLogo/orgName to avoid mixed org icon + user name rendering. Verification: npm run build passed with existing bundle-size warnings only.",
"Frontend O2 non-production task 149432767 succeeded for commit f33e349b2c29c7ba139e48fdfbdd70759ce3a4c4, flow 114592657, version 5.219.0, pub_env daily, build id 53073038, assetCount 203. CDN bundle contains userAvatar|| and no longer contains the old orgLogo|| account-row priority; old title 可授权的权限(已选 remains absent.",
"2026-06-17 server release audit: gateway current head a0873a6 and PAT current head 79a8fa5 are not proven deployed because their successful prepub deploy receipts predate the current commits. BFF current head 9ac576c is deployed, but Aone integration-test task 3390939831 is FAIL. Full prepub e2e remains blocked on gateway/PAT current-head redeploy plus BFF integration-test resolution or accepted exception."
]
},
"serviceContract": {
"pat.batch_plan": {
"request": {
"scopes": "optional string[]",
"productCodes": "optional string[]",
"recommend": "boolean",
"grantType": "once|session|permanent",
"dryRun": "boolean",
"caller": "auth_login_recommend|batch_chmod_interactive|..."
},
"response": {
"selectedScopes": "server-selected grantable scopes",
"skippedScopes": "not grantable or filtered scopes",
"pendingScopes": "high-risk scopes requiring separate confirmation",
"warnings": "recommendation filter and partial selection warnings"
}
},
"pat.batch_grant": {
"request": {
"scopes": "required string[], must be selectedScopes from plan",
"grantType": "once|session|permanent",
"startFlow": "boolean; true opens authorization page",
"noWait": "boolean; true returns URL immediately",
"clientRequestId": "idempotency key",
"caller": "auth_login_recommend|batch_chmod_interactive|..."
},
"pendingResponse": {
"success": false,
"code": "PAT_BATCH_AUTH_PENDING",
"data": {
"flowId": "batch device flow id",
"userCode": "device user code",
"uri": "opaque authorization URL",
"authUrl": "same URL for compatibility",
"pollIntervalSeconds": 3,
"expiresInSeconds": "PatSwitch.deviceCodeFlowTtlSeconds"
}
}
},
"deviceFlowDetail": {
"route": "personalAuthorization",
"fields": [
"agentCode",
"appName",
"appIcon",
"userName",
"userAvatar",
"status",
"batchFlow",
"defaultGrantType",
"scopes[]"
],
"scopeFields": [
"scope",
"displayName",
"productCode",
"productName",
"riskLevel",
"effectiveRiskLevel",
"riskDesc",
"operationSummary"
]
},
"handleFlowAction": {
"request": {
"flowId": "required string",
"userCode": "required string",
"action": "approve|reject",
"grantType": "optional once|session|permanent; batch page omits it so PAT core uses defaultGrantType from the flow",
"selectedScopes": "required for the new batch page when action=approve; must be a non-empty subset of flow.scopes"
},
"compatibility": {
"selectedScopesNull": "legacy clients approve all flow-stored scopes",
"selectedScopesEmpty": "INVALID_PARAMETER",
"selectedScopesOutsideFlow": "INVALID_PARAMETER"
},
"response": {
"status": "APPROVED|REJECTED",
"approvedScopes": "scopes written by this approve action",
"rejectedScopes": "flow scopes not approved by this action"
},
"errorBehavior": {
"approveStorageFailure": "PAT_BATCH_PARTIAL_FAILURE; flow remains PENDING and the page can retry. Once tokens written by the current flow are cleaned by flowId ownership; permanent/session grants rely on transaction rollback and grant idempotency."
}
}
},
"frontendRequirements": [
"Reuse personalAuthorization page and API detail/action callbacks.",
"When scopes length is greater than one, render a grouped batch authorization surface.",
"Group by productCode/productName with product-level checkbox and expandable scope details.",
"Default checked state is all safe selected scopes.",
"Users can uncheck a product group or individual scopes; approving with zero selected scopes is disabled.",
"Approve must send selectedScopes matching the current checked scope list.",
"Reject rejects the whole flow.",
"Do not expose orgId or uid as trusted client input."
],
"visualDesignTarget": {
"sourceImage": "/var/folders/hz/2sm0ql493cv6jngtjk7xsby00000gp/T/codex-clipboard-2078e5f6-b389-4435-8ea5-0f0a94363462.png",
"mgdoneReference": "https://mgdone.alibaba-inc.com/file/187681481694834?file=187681481694834&page_id=74%3A36608&shareId=3b3db511-38e1-4f3d-a859-ab891ed85617",
"page": "A white authorization card centered on a light #f5f6fa page, with the footer links below the card.",
"card": "Single white 480x680 desktop card with rgba(131,137,143,0.16) border, no shadow, and 12px radius.",
"header": "Top centered dynamic app icon only, 56x56 with 8px radius. Do not render the right-side target app icon or any blue/red debug frame. Title is DingtalkWorkSpace, subtitle is 请求以下钉钉账号进行授权.",
"accountBlock": "Light #fafafa rounded 424x60 account row below the header, with real org/user avatar at left and the current account name next to it.",
"permissionBlock": "Light #fafafa rounded 424x300 permission panel titled 可授权的权限. Product rows use a blue checked checkbox, bold product name, gray count such as 12 项, gray description text, and a right chevron for expandable details.",
"selectionBehavior": "All recommended groups are checked by default. Product checkbox toggles the entire group; expanding a product reveals individual scope checkboxes.",
"manageTip": "Under the permission panel, show the existing 个人数据授权管理 link text for later review or revoke.",
"buttons": "Bottom two-button layout: left outlined white 拒绝 button, right primary blue 同意并授权 button, same height and rounded pill style.",
"mobile": "On narrow screens the same hierarchy must remain visible without text overlap; buttons share the available width."
},
"terminalTuiTarget": {
"sourceImages": [
"/var/folders/hz/2sm0ql493cv6jngtjk7xsby00000gp/T/codex-clipboard-16c11a9e-7ae7-4703-aa66-4251f7b59f16.png",
"/var/folders/hz/2sm0ql493cv6jngtjk7xsby00000gp/T/codex-clipboard-5aff04a6-9a45-4ba5-8741-b7abcd4f2303.png"
],
"loginRecommendSelector": "After login, interactive dws auth login --recommend presents 选择要授权的业务域 with all product domains selected by default.",
"controls": "TUI must support space toggle, up/down navigation, slash filter, enter confirm, and ctrl+a select all through the underlying selector component.",
"nextStep": "After confirmation, CLI prints or opens the complete encoded authorization URL returned by the server and waits for browser approval."
},
"securityRules": [
"Gateway derives orgId and uid from trusted token/header only.",
"Gateway allowlist must include openclaw and dws-cli for batch grant callers that are intended to write.",
"PAT core consumes the Diamond recommendation set for recommend=true and caller=auth_login_recommend; prepub Diamond currently recommends 151 low-risk scopes and excludes the 22 medium-risk scopes.",
"PAT core must use flow-stored scopes as the allowlist and reject any selectedScopes value outside the flow.",
"PAT core must only clear pending keys owned by the current flow and leave the flow pending if approve write fails after precheck, so the same authorization page can retry safely.",
"Critical approve/reject storage writes must fail closed with PAT_BATCH_PARTIAL_FAILURE instead of reporting success on partial persistence.",
"Batch flow approval must be idempotent; repeated approval or retry must not duplicate grants."
],
"acceptanceCriteria": [
"Prepub package/build succeeds for every changed repo: dingtalk-workspace-cli, lippi-open-mcpserver-gw, lippi-pat-core-wukong-capability, lippi-open-app-dev, and lippi-open-dev-front.",
"Prepub deployed versions include the current branch commits for CLI, gateway, PAT core, BFF, and frontend.",
"On prepub, dws auth login --recommend opens the batch authorization URL after successful login.",
"On prepub, interactive dws auth login --recommend displays a default-all-selected product-domain TUI before opening the authorization URL.",
"On prepub, dws pat chmod --recommend --product calendar without --yes opens the authorization URL.",
"On prepub, dws pat chmod --recommend --product calendar --yes preserves the existing direct grant behavior.",
"On prepub, a recommended config containing a delete/recall/remove/destroy/drop scope returns warnings/skipped scopes and never grants that scope.",
"On prepub, approving a batch flow grants only selectedScopes and rejects scopes outside the flow.",
"On prepub, rejecting a batch flow writes no new grants.",
"On prepub, JSON mode returns structured PAT_BATCH_AUTH_PENDING with uri/openBrowser fields and does not block.",
"On prepub, the authorization page visual result matches visualDesignTarget and the provided screenshot style."
],
"reviewPlan": {
"implementationCommander": "Check end-to-end CLI, gateway, PAT core integration contracts.",
"aiNativeUiArchitect": "Requested by goal but no local skill was available; use Product Design and existing frontend constraints as fallback.",
"qinzePuaReviewer": "Review security, idempotency, tests, and failure modes.",
"muskChiefOperator": "Review the product flow for speed, minimum surface area, and user clarity.",
"computerUse": "Verify prepub/front-end page only after frontend repo and build credentials are available."
},
"updatedAt": "2026-06-17T00:58:00+08:00"
}