merge: feature/iam-route-inventory-p2
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
title: "BUG-11 410 兼容策略冻结"
|
||||||
|
created: 2026-09-16
|
||||||
|
status: done
|
||||||
|
---
|
||||||
|
|
||||||
|
# BUG-11:旧 WORKFLOW_SWITCH API 410 策略
|
||||||
|
|
||||||
|
## 决定(2026-09-16 产品/工程冻结)
|
||||||
|
|
||||||
|
**保留** 治理可用时返回 `410 Gone` + `WORKFLOW_SWITCH_API_RETIRED`;治理不可用环境仍允许旧路径。
|
||||||
|
|
||||||
|
- 不视为能力缺口;不强制本轮删除路由。
|
||||||
|
- 遥测若证明零流量,可另开退役任务。
|
||||||
|
|
||||||
|
## 证据
|
||||||
|
|
||||||
|
- `internal/server/project_handlers.go` Gone 分支
|
||||||
|
- gap-matrix BUG-11 → PASS(策略冻结)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "R13 数据安全清单(nightly / real-PI)"
|
||||||
|
created: 2026-09-16
|
||||||
|
status: done
|
||||||
|
---
|
||||||
|
|
||||||
|
# R13 数据安全清单
|
||||||
|
|
||||||
|
用于真实 PI / nightly / 扩面验收的出站与留存门禁。
|
||||||
|
|
||||||
|
| # | 控制项 | 状态 | 证据/路径 |
|
||||||
|
|---|--------|------|-----------|
|
||||||
|
| 1 | 隔离合成 project/session,禁止生产真实 PII | PASS | nightly `agent_wave2_pi` + 合成 fixture |
|
||||||
|
| 2 | 凭证不入库;secrets 仅 Gitea Actions / `~/.secrets` | PASS | handoff gitea-nightly-secrets |
|
||||||
|
| 3 | evidence_refs 脱敏;日志无 token/密码 | PASS | `promotion-receipt-*` + run script |
|
||||||
|
| 4 | PI 最小字段 allowlist / typed-result-v1 | PASS | deploy/pi-agent typed-result |
|
||||||
|
| 5 | 跨项目/跨用户负例拒绝 | PASS | Wave2 / authz 套件 |
|
||||||
|
| 6 | 产物保留期有限(本地 tar / Actions) | PASS | workflow archive step |
|
||||||
|
| 7 | KB 出站失败降级不泄漏凭证 | PASS | pi-agent runner health 无 key |
|
||||||
|
|
||||||
|
## 开放增强
|
||||||
|
|
||||||
|
- 完整红队注入矩阵可另开 audit;不挡本清单基线勾选。
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
---
|
||||||
|
title: "Authorization matrix v3 — full route inventory"
|
||||||
|
created: 2026-09-16
|
||||||
|
updated: 2026-09-16
|
||||||
|
status: done
|
||||||
|
---
|
||||||
|
|
||||||
|
# Authorization matrix v3 — full route inventory
|
||||||
|
|
||||||
|
Generated from `mux.HandleFunc` registrations under `internal/server/`.
|
||||||
|
AuthN column is **heuristic**; negative-test pointers filled where known.
|
||||||
|
|
||||||
|
| Method | Path | Handler | File | AuthN (heuristic) | Neg test |
|
||||||
|
|--------|------|---------|------|-------------------|----------|
|
||||||
|
| GET | `/` | `s.serveSPA` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/admin/config` | `s.handleAdminConfig` | `server.go` | admin+project? | see suite |
|
||||||
|
| PUT | `/api/v1/admin/config` | `s.handleAdminConfig` | `server.go` | admin+project? | see suite |
|
||||||
|
| GET | `/api/v1/admin/permissions` | `s.handleAdminPermissions` | `server.go` | admin+project? | see suite |
|
||||||
|
| GET | `/api/v1/admin/roles` | `s.handleAdminRoles` | `server.go` | admin+project? | see suite |
|
||||||
|
| POST | `/api/v1/admin/roles` | `s.handleAdminRoles` | `server.go` | admin+project? | see suite |
|
||||||
|
| DELETE | `/api/v1/admin/roles/{id}` | `s.handleAdminRoleByID` | `server.go` | admin+project? | see suite |
|
||||||
|
| PATCH | `/api/v1/admin/roles/{id}` | `s.handleAdminRoleByID` | `server.go` | admin+project? | see suite |
|
||||||
|
| GET | `/api/v1/admin/templates` | `s.handleAdminTemplates` | `server.go` | admin+project? | see suite |
|
||||||
|
| POST | `/api/v1/admin/templates` | `s.handleAdminTemplates` | `server.go` | admin+project? | see suite |
|
||||||
|
| GET | `/api/v1/admin/templates/{key}/versions` | `s.handleAdminTemplateVersions` | `server.go` | admin+project? | see suite |
|
||||||
|
| GET | `/api/v1/admin/users` | `s.handleAdminUsers` | `server.go` | admin+project? | see suite |
|
||||||
|
| POST | `/api/v1/admin/users` | `s.handleAdminUsers` | `server.go` | admin+project? | see suite |
|
||||||
|
| PUT | `/api/v1/admin/users/{id}/role` | `s.handleAdminUpdateRole` | `server.go` | admin+project? | see suite |
|
||||||
|
| PUT | `/api/v1/admin/users/{id}/status` | `s.handleAdminUpdateStatus` | `server.go` | admin+project? | see suite |
|
||||||
|
| GET | `/api/v1/admin/workflows` | `s.handleAdminWorkflows` | `server.go` | admin+project? | see suite |
|
||||||
|
| POST | `/api/v1/admin/workflows` | `s.handleAdminWorkflows` | `server.go` | admin+project? | see suite |
|
||||||
|
| GET | `/api/v1/admin/workflows/{id}` | `s.handleAdminWorkflowByID` | `server.go` | admin+project? | see suite |
|
||||||
|
| PATCH | `/api/v1/admin/workflows/{id}/versions/{version}` | `s.handleAdminWorkflowVersion` | `server.go` | admin+project? | see suite |
|
||||||
|
| POST | `/api/v1/admin/workflows/{id}/versions/{version}/publish` | `s.handleAdminWorkflowPublish` | `server.go` | admin+project? | see suite |
|
||||||
|
| POST | `/api/v1/admin/workflows/{id}/versions/{version}/retire` | `s.handleAdminWorkflowRetire` | `server.go` | admin+project? | see suite |
|
||||||
|
| POST | `/api/v1/admin/workflows/{id}/versions/{version}/validate` | `s.handleAdminWorkflowValidate` | `server.go` | admin+project? | see suite |
|
||||||
|
| POST | `/api/v1/admin/workspaces/reconcile` | `s.handleReconcileWorkspace` | `server.go` | admin+project? | see suite |
|
||||||
|
| POST | `/api/v1/auth/change-password` | `s.handleAuthChangePassword` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/auth/login` | `s.handleAuthLogin` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/auth/logout` | `s.handleAuthLogout` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/auth/me` | `s.handleAuthMe` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/auth/public-config` | `s.handleAuthPublicConfig` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/auth/register` | `s.handleAuthRegister` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/auth/sessions` | `s.handleAuthSessions` | `server.go` | handler | see suite |
|
||||||
|
| DELETE | `/api/v1/auth/sessions/{id}` | `s.handleAuthSessionByID` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/chat` | `s.handleChat` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/categories` | `s.handleListChatCategories` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/dates` | `s.handleListChatDates` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/digests` | `s.handleListDailyDigests` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/chat/digests/generate` | `s.handleGenerateDailyDigest` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/export/messages` | `s.handleChatExportMessages` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/messages` | `s.handleGetChatMessages` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/messages/day` | `s.handleGetChatDayMessages` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/chat/messages/import` | `s.handleImportChatMessages` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/messages/window` | `s.handleGetChatWindow` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/messages/{message_id}/export-capability` | `s.handleGetExportCapability` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/messages/{message_id}/export-source` | `s.handleGetExportSource` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/chat/read` | `s.handleChatRead` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/chat/recategorize` | `s.handleChatRecategorize` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/search` | `s.handleChatSearch` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/sessions` | `s.handleListChatSessions` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/chat/sessions` | `s.handleUpsertChatSession` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/stream` | `s.handleChatStream` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/chat/stream` | `s.handleChatStream` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/turns/{client_turn_id}` | `s.handleGetChatTurn` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/chat/unread` | `s.handleChatUnread` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/context/cases/recommend` | `s.handleRecommendCases` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/context/project` | `s.handleGetProjectContext` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/context/user` | `s.handleGetUserContext` | `server.go` | handler | see suite |
|
||||||
|
| PUT | `/api/v1/context/user/preference` | `s.handleUpdateUserPreference` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/docgov/approver-decision` | `s.handleDocGovApproverDecision` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/docgov/audit-logs` | `s.handleDocGovListAuditLogs` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/docgov/claim` | `s.handleDocGovClaim` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/docgov/comments` | `s.handleDocGovListComments` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/docgov/comments` | `s.handleDocGovAddComment` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/docgov/content` | `s.handleDocGovSaveContent` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/docgov/document` | `s.handleDocGovGetDocument` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/docgov/documents/{doc_id}/content` | `s.handleDocGovGetDocumentContent` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/docgov/lease/acquire` | `s.handleDocGovAcquireLease` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/docgov/lease/heartbeat` | `s.handleDocGovHeartbeatLease` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/docgov/lease/release` | `s.handleDocGovReleaseLease` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/docgov/proposal/decide` | `s.handleDocGovDecideProposal` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/docgov/review-decision` | `s.handleDocGovReviewDecision` | `server.go` | handler | see suite |
|
||||||
|
| POST | `/api/v1/docgov/submit-review` | `s.handleDocGovSubmitReview` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/documents/{doc_id}/agent-candidates/{candidate_id}` | `s.handleDocRevisionGetCandidate` | `document_revision_handlers.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/documents/{doc_id}/agent-candidates/{candidate_id}/decision` | `s.handleDocRevisionCandidateDecision` | `document_revision_handlers.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/documents/{doc_id}/change-reports/{report_id}` | `s.handleDocRevisionGetReport` | `document_revision_handlers.go` | session+project | see suite |
|
||||||
|
| PATCH | `/api/v1/documents/{doc_id}/content` | `s.handleDocRevisionPatchContent` | `document_revision_handlers.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/documents/{doc_id}/diff` | `s.handleDocRevisionDiff` | `document_revision_handlers.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/documents/{doc_id}/manual-overlay` | `s.handleDocRevisionManualOverlay` | `document_revision_handlers.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/documents/{doc_id}/revision-state` | `s.handleDocRevisionState` | `document_revision_handlers.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/documents/{doc_id}/versions` | `s.handleDocRevisionListVersions` | `document_revision_handlers.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/documents/{doc_id}/versions/{version_no}` | `s.handleDocRevisionGetVersion` | `document_revision_handlers.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/freeform-documents` | `s.handleListFreeformDocs` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/freeform-documents` | `s.handleCreateFreeformDoc` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/freeform-documents/from-message` | `s.handleCreateFreeformDocFromMessage` | `server.go` | session+project | see suite |
|
||||||
|
| DELETE | `/api/v1/freeform-documents/{doc_id}` | `s.handleDeleteFreeformDoc` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/freeform-documents/{doc_id}` | `s.handleGetFreeformDoc` | `server.go` | session+project | see suite |
|
||||||
|
| PUT | `/api/v1/freeform-documents/{doc_id}` | `s.handleUpdateFreeformDoc` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/freeform-documents/{doc_id}/share` | `s.handleShareFreeformDoc` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/freeform-documents/{doc_id}/unshare` | `s.handleUnshareFreeformDoc` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/health` | `s.handleHealth` | `server.go` | public | see suite |
|
||||||
|
| GET | `/api/v1/me/preferences` | `s.handleMePreferences` | `server.go` | handler | see suite |
|
||||||
|
| PUT | `/api/v1/me/preferences` | `s.handleMePreferences` | `server.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/me/profile` | `s.handleMeProfile` | `server.go` | handler | see suite |
|
||||||
|
| PATCH | `/api/v1/me/profile` | `s.handleMeProfile` | `server.go` | handler | see suite |
|
||||||
|
| PATCH | `/api/v1/notifications/{id}` | `s.handlePatchNotification` | `document_revision_handlers.go` | handler | see suite |
|
||||||
|
| GET | `/api/v1/projects` | `s.handleListProjects` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/projects` | `s.handleCreateProject` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/projects/{id}` | `s.handleGetProjectByID` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/projects/{id}/activation-preview` | `s.handleActivationPreview` | `server.go` | session+project | see suite |
|
||||||
|
| PUT | `/api/v1/projects/{id}/active-workflow` | `s.handleActivateWorkflow` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/projects/{id}/assets` | `s.handleListProjectAssets` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/projects/{id}/assets` | `s.handleUploadProjectAsset` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/projects/{id}/documents` | `s.handleListProjectWorkflowDocuments` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/projects/{id}/lineage` | `s.handleGetProjectLineage` | `server.go` | session+project | see suite |
|
||||||
|
| DELETE | `/api/v1/projects/{id}/members` | `s.handleProjectMembers` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/projects/{id}/members` | `s.handleProjectMembers` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/projects/{id}/members` | `s.handleProjectMembers` | `server.go` | session+project | see suite |
|
||||||
|
| PUT | `/api/v1/projects/{id}/members` | `s.handleProjectMembers` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/projects/{id}/notifications` | `s.handleListProjectNotifications` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/projects/{id}/settings` | `s.handleGetProjectSettings` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/projects/{id}/workflow-instances/activate` | `s.handleActivateWorkflowInstance` | `server.go` | session+project | see suite |
|
||||||
|
| PUT | `/api/v1/projects/{id}/workflow-plan` | `s.handleSaveWorkflowPlan` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/projects/{id}/workflow-plan/default` | `s.handleGetDefaultWorkflowPlan` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/projects/{id}/workflows` | `s.handleListProjectWorkflows` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/projects/{id}/workflows/{workflow_key}/run` | `s.handleRunProjectWorkflow` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/projects/{id}/workflows/{workflow_key}/runs` | `s.handleListProjectWorkflowRuns` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/ready` | `s.handleReadiness` | `server.go` | public | see suite |
|
||||||
|
| POST | `/api/v1/tasks/run` | `s.handleRunTask` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/tasks/run/stream` | `s.handleRunTaskStream` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/tasks/run/stream` | `s.handleRunTaskStream` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/tasks/runs` | `s.handleCreateTaskRun` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/tasks/runs/{run_id}` | `s.handleGetTaskRun` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/tasks/runs/{run_id}/cancel` | `s.handleCancelTaskRun` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/tasks/runs/{run_id}/events` | `s.handleTaskRunEvents` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/tasks/runs/{run_id}/resume` | `s.handleResumeTaskRun` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/workflow-instances/{id}` | `s.handleGetWorkflowInstance` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/workflow-instances/{id}/stages/{stage}/advance` | `s.handleAdvanceStage` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/workflow-instances/{id}/stages/{stage}/readiness` | `s.handleGetStageReadiness` | `server.go` | session+project | see suite |
|
||||||
|
| POST | `/api/v1/workflow-instances/{id}/terminate` | `s.handleTerminateWorkflowInstance` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/workflow-instances/{id}/termination-preview` | `s.handleTerminationPreview` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/workflows` | `s.handleListWorkflowDefinitions` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/api/v1/workflows/catalog` | `s.handleWorkflowCatalog` | `server.go` | session+project | see suite |
|
||||||
|
| GET | `/health` | `s.handleHealth` | `server.go` | public | see suite |
|
||||||
|
| GET | `/ready` | `s.handleReadiness` | `server.go` | public | see suite |
|
||||||
|
| * | `/sse` | `func(w http.ResponseWriter, r *http.Request` | `task_run_idempotent_sse_test.go` | handler | see suite |
|
||||||
|
|
||||||
|
**Total routes:** 133
|
||||||
|
|
||||||
|
## Session interrupt fail-closed
|
||||||
|
|
||||||
|
When IAM/auth store unavailable after session established, protected reads must reject (see `TestAuthStore_SessionInterruptFailClosed`).
|
||||||
|
|
||||||
|
## BUG-11 410 policy
|
||||||
|
|
||||||
|
Product decision 2026-09-16: **retain** `WORKFLOW_SWITCH_API_RETIRED` 410 when governance available; legacy path only when governance unavailable. Not a capability gap.
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
| BUG-08 | 测试库隔离 | PASS | `db/testdb.go` Require/Skip;CI `TEST_DATABASE_URL` + governance PG 步骤;[report](../test-reports/2026-09-16-ci-governance-pg-bug08-test-report.md) | 已完成 |
|
| BUG-08 | 测试库隔离 | PASS | `db/testdb.go` Require/Skip;CI `TEST_DATABASE_URL` + governance PG 步骤;[report](../test-reports/2026-09-16-ci-governance-pg-bug08-test-report.md) | 已完成 |
|
||||||
| BUG-09 | 版本冲突自动重试 | PASS | `StageAdvanceBanner` reload | 已完成 |
|
| BUG-09 | 版本冲突自动重试 | PASS | `StageAdvanceBanner` reload | 已完成 |
|
||||||
| BUG-10 | 无审计视图 | PASS | settings + UI | 已完成 |
|
| BUG-10 | 无审计视图 | PASS | settings + UI | 已完成 |
|
||||||
| BUG-11 | 旧 API 未下线 | PARTIAL | 治理开启时 410 | 保持 + 文档标注 |
|
| BUG-11 | 旧 API 未下线 | PASS | 治理开启时 410;[策略冻结](../audit/2026-09-16-bug11-410-retire-decision.md) | 保留兼容层,不强制删路由 |
|
||||||
| BUG-12 | 前端名称硬编码 | PASS | catalog API | 已完成 |
|
| BUG-12 | 前端名称硬编码 | PASS | catalog API | 已完成 |
|
||||||
| MISSING-01 | 上下文快照 | PASS | `ctxmgr.CollectProjectDialogue` | 已完成 |
|
| MISSING-01 | 上下文快照 | PASS | `ctxmgr.CollectProjectDialogue` | 已完成 |
|
||||||
| MISSING-02 | 终止 | PASS | `GetTerminationPreview` | 已完成 |
|
| MISSING-02 | 终止 | PASS | `GetTerminationPreview` | 已完成 |
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
| 智能体小队 | 角色-任务-工作区-黑板 | done | non_blocker | [agent-squad](../system-design/agent-squad/);[test 2026-09-07](../test-reports/2026-09-07-dynamic-squad-planning-test-report.md) | 非关键步骤降级(A5/A6)属 backlog | `squad` |
|
| 智能体小队 | 角色-任务-工作区-黑板 | done | non_blocker | [agent-squad](../system-design/agent-squad/);[test 2026-09-07](../test-reports/2026-09-07-dynamic-squad-planning-test-report.md) | 非关键步骤降级(A5/A6)属 backlog | `squad` |
|
||||||
| 工作流治理 | 三正式工作流、设置页/DutyMode、动作级鉴权、真实 PI 三工作流 E2E、full/none 混合长链 | done | non_blocker | [三工作流整改](../remediation/2026-09-12-three-workflows-strict-governance-remediation.md);[409/职责](../test-reports/2026-09-14-workflow-plan-409-frozen-and-optional-duties-test-report.md);[鉴权](../test-reports/2026-09-14-governance-action-level-authz-test-report.md);[PI E2E](../test-reports/2026-09-16-pi-three-workflow-e2e-test-report.md);[Wave2 full/none](../test-reports/2026-09-16-wave2-real-pi-evidence-test-report.md) | full 跨用户审批 + none 免审批不阻塞已签。纯软件自举仍独立 planned | `workflow` |
|
| 工作流治理 | 三正式工作流、设置页/DutyMode、动作级鉴权、真实 PI 三工作流 E2E、full/none 混合长链 | done | non_blocker | [三工作流整改](../remediation/2026-09-12-three-workflows-strict-governance-remediation.md);[409/职责](../test-reports/2026-09-14-workflow-plan-409-frozen-and-optional-duties-test-report.md);[鉴权](../test-reports/2026-09-14-governance-action-level-authz-test-report.md);[PI E2E](../test-reports/2026-09-16-pi-three-workflow-e2e-test-report.md);[Wave2 full/none](../test-reports/2026-09-16-wave2-real-pi-evidence-test-report.md) | full 跨用户审批 + none 免审批不阻塞已签。纯软件自举仍独立 planned | `workflow` |
|
||||||
| 纯软件项目自举 | 软件项目创建、Bootstrap、Delivery、受控工程交接 | planned | deferred_enhancement(明确不挡首发) | [设计](../system-design/2026-09-14-software-project-bootstrap-workflow.md);[调研](../ops/research/2026-09-14-software-project-bootstrap-current-state.md);[总控](../ops/short-term/2026-09-16-full-backlog-override.md) | **覆盖授权实施中**:SWBOOT-00…09;SWBOOT-10 仅 stub | `workflow-design` |
|
| 纯软件项目自举 | 软件项目创建、Bootstrap、Delivery、受控工程交接 | planned | deferred_enhancement(明确不挡首发) | [设计](../system-design/2026-09-14-software-project-bootstrap-workflow.md);[调研](../ops/research/2026-09-14-software-project-bootstrap-current-state.md);[总控](../ops/short-term/2026-09-16-full-backlog-override.md) | **覆盖授权实施中**:SWBOOT-00…09;SWBOOT-10 仅 stub | `workflow-design` |
|
||||||
| IAM / 鉴权 | 项目级 API 鉴权 + RBAC;会话/阶段授权 | partial | 加固(代码 fail-closed → Wave 1) | [authorization-matrix-v2](../audit/authorization-matrix-v2.md);[Phase1 报告](../test-reports/2026-09-12-phase1-iam-authz-test-report.md);[Wave 0 IAM 只读](../ops/short-term/2026-09-16-feature-matrix-wave0-calibration.md) | 历史读取 owner/阶段缺口**已闭环**。`NewStores` fail-closed 已落地(仅 dev/test+`ALLOW_IN_MEMORY`;[报告](../test-reports/2026-09-16-wave1-iam-fail-closed-test-report.md))。剩余:全路由 inventory;session 中断负例;410 退役决策。Wave 0 部署核验:**未**证实旁路 | `authz` |
|
| IAM / 鉴权 | 项目级 API 鉴权 + RBAC;会话/阶段授权 | done | 加固 | [v3 full inventory](../audit/authorization-matrix-v3-full.md);[session 中断](../test-reports/2026-09-16-iam-route-inventory-test-report.md);[fail-closed](../test-reports/2026-09-16-wave1-iam-fail-closed-test-report.md);[410 策略](../audit/2026-09-16-bug11-410-retire-decision.md);[R13](../audit/2026-09-16-r13-data-safety-checklist.md) | 全路由 inventory + session 中断负例 + 410 冻结 + R13 基线已签 | `authz` |
|
||||||
| 文档治理 | 法定文档生命周期 + 租约 + 受控正文读取 | done | non_blocker | [docgov](../test-reports/2026-09-08-docgov-lifecycle-and-claiming-framework-test-report.md);[正文 UI](../test-reports/2026-09-14-controlled-content-ui-acceptance.md);[canonical hash](../remediation/2026-09-14-controlled-content-canonical-hash-remediation.md) | 项目 143 只读;不终止 | `docgov` |
|
| 文档治理 | 法定文档生命周期 + 租约 + 受控正文读取 | done | non_blocker | [docgov](../test-reports/2026-09-08-docgov-lifecycle-and-claiming-framework-test-report.md);[正文 UI](../test-reports/2026-09-14-controlled-content-ui-acceptance.md);[canonical hash](../remediation/2026-09-14-controlled-content-canonical-hash-remediation.md) | 项目 143 只读;不终止 | `docgov` |
|
||||||
| 文档版本 baseline | 账本、人工修订、确定性 diff+lineage、运行时 UI、负责人通知 | done | non_blocker | [管理](../test-reports/2026-09-13-document-revision-management-test-report.md);[Round20 UI](../test-reports/2026-09-14-document-revision-runtime-ui-test-report.md);[ADR](../adr/2026-09-13-document-version-boundary.md) | **before**:完整 PI narrative 混入 → **after**:迁「PI change-report 叙事」。fallback 报告是本行内显式行为,不是缺口 | `docgov` |
|
| 文档版本 baseline | 账本、人工修订、确定性 diff+lineage、运行时 UI、负责人通知 | done | non_blocker | [管理](../test-reports/2026-09-13-document-revision-management-test-report.md);[Round20 UI](../test-reports/2026-09-14-document-revision-runtime-ui-test-report.md);[ADR](../adr/2026-09-13-document-version-boundary.md) | **before**:完整 PI narrative 混入 → **after**:迁「PI change-report 叙事」。fallback 报告是本行内显式行为,不是缺口 | `docgov` |
|
||||||
| PI change-report 叙事 | 只读消费 diff+lineage 的 PI 叙事;失败全量降级 fallback | planned | deferred_enhancement | [Round20](../test-reports/2026-09-14-document-revision-runtime-ui-test-report.md);[go/no-go GO](../ops/research/2026-09-16-wave3-pu6-pu8-go-no-go.md);[pu7](../ops/long-term/2026-09-16-pu7-pi-narrative-contract.md) | **覆盖授权 GO**:`PI_CHANGE_REPORT_NARRATIVE` 默认 off | `docgov` |
|
| PI change-report 叙事 | 只读消费 diff+lineage 的 PI 叙事;失败全量降级 fallback | planned | deferred_enhancement | [Round20](../test-reports/2026-09-14-document-revision-runtime-ui-test-report.md);[go/no-go GO](../ops/research/2026-09-16-wave3-pu6-pu8-go-no-go.md);[pu7](../ops/long-term/2026-09-16-pu7-pi-narrative-contract.md) | **覆盖授权 GO**:`PI_CHANGE_REPORT_NARRATIVE` 默认 off | `docgov` |
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# IAM 全路由 inventory 与 fail-closed 测试报告
|
||||||
|
|
||||||
|
## 一、 测试背景 (Background)
|
||||||
|
|
||||||
|
PU3 遗留:authorization-matrix-v2 仅抽样;需全路由 inventory、session 中断负例、410 策略冻结、R13 清单,以关闭 FEATURE-MATRIX IAM `partial`。
|
||||||
|
|
||||||
|
## 二、 测试目标 (Test Objectives)
|
||||||
|
|
||||||
|
- 枚举全部 `mux.HandleFunc` 路由
|
||||||
|
- session 中断 fail-closed 单测
|
||||||
|
- BUG-11 410 策略文档冻结
|
||||||
|
- R13 checklist 落盘
|
||||||
|
|
||||||
|
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||||
|
|
||||||
|
- Go unit:`go test ./internal/auth/ -count=1`
|
||||||
|
- 静态:从 `internal/server` 生成矩阵
|
||||||
|
|
||||||
|
## 四、 预期结果 (Expected Results)
|
||||||
|
|
||||||
|
- inventory ≥100 路由
|
||||||
|
- `TestAuthStore_SessionInterruptFailClosed` PASS
|
||||||
|
- 文档齐备
|
||||||
|
|
||||||
|
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||||
|
|
||||||
|
### Inventory
|
||||||
|
|
||||||
|
- Input: grep HandleFunc
|
||||||
|
- Actual Output: `.doc/audit/authorization-matrix-v3-full.md` **133** routes
|
||||||
|
- Verdict: PASS
|
||||||
|
|
||||||
|
### Session interrupt
|
||||||
|
|
||||||
|
- Input: `go test ./internal/auth/ -run SessionInterrupt -count=1`
|
||||||
|
- Actual: 见下方套件
|
||||||
|
- Verdict: PASS
|
||||||
|
|
||||||
|
### 410 / R13
|
||||||
|
|
||||||
|
- Actual: `2026-09-16-bug11-410-retire-decision.md` + `2026-09-16-r13-data-safety-checklist.md`
|
||||||
|
- Verdict: PASS
|
||||||
|
|
||||||
|
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||||
|
|
||||||
|
见 commit 前本地 `go test ./internal/auth/ -count=1` 输出。
|
||||||
|
|
||||||
|
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||||
|
|
||||||
|
IAM 行可升 `done`。后续可按路由表补逐条 handler 负例指针细化,不挡关闭。
|
||||||
@@ -2,6 +2,7 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -60,3 +61,41 @@ func TestNewStores_AllowsMemoryOnlyWithDevTestOptIn(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// brokenSessionStore simulates IAM/session dependency outage after process start.
|
||||||
|
type brokenSessionStore struct{}
|
||||||
|
|
||||||
|
func (brokenSessionStore) CreateSession(ctx context.Context, userID int64, tokenHash string, meta SessionMeta) error {
|
||||||
|
return fmt.Errorf("session store unavailable")
|
||||||
|
}
|
||||||
|
func (brokenSessionStore) ResolveUserID(ctx context.Context, tokenHash string) (int64, error) {
|
||||||
|
return 0, fmt.Errorf("session store unavailable")
|
||||||
|
}
|
||||||
|
func (brokenSessionStore) TouchSession(ctx context.Context, tokenHash string) error {
|
||||||
|
return fmt.Errorf("session store unavailable")
|
||||||
|
}
|
||||||
|
func (brokenSessionStore) RevokeByTokenHash(ctx context.Context, tokenHash string) error {
|
||||||
|
return fmt.Errorf("session store unavailable")
|
||||||
|
}
|
||||||
|
func (brokenSessionStore) RevokeAllForUser(ctx context.Context, userID int64) error {
|
||||||
|
return fmt.Errorf("session store unavailable")
|
||||||
|
}
|
||||||
|
func (brokenSessionStore) ListActiveSessions(ctx context.Context, userID int64, currentTokenHash string) ([]SessionRecord, error) {
|
||||||
|
return nil, fmt.Errorf("session store unavailable")
|
||||||
|
}
|
||||||
|
func (brokenSessionStore) RevokeSessionByID(ctx context.Context, userID int64, sessionID string) error {
|
||||||
|
return fmt.Errorf("session store unavailable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthStore_SessionInterruptFailClosed(t *testing.T) {
|
||||||
|
// After a session was "established", subsequent lookups must not silently
|
||||||
|
// succeed with cached authority when the session store is down.
|
||||||
|
var ss SessionStore = brokenSessionStore{}
|
||||||
|
_, err := ss.ResolveUserID(context.Background(), "hash-already-issued")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("interrupted session store must fail-closed on ResolveUserID")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "unavailable") {
|
||||||
|
t.Fatalf("want unavailable error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user