Merge authoritative main: take the newer free-port test helpers
ci / svc-realcombo (pull_request) Successful in 2m13s
ci / test (pull_request) Failing after 5m38s
ci / test-pg (pull_request) Successful in 11m1s

This commit is contained in:
2026-10-22 01:22:10 +00:00
72 changed files with 8523 additions and 228 deletions
@@ -0,0 +1,409 @@
---
title: feat: Clarify project access and lifecycle management
date: 2026-10-19
type: feat
artifact_contract: ce-unified-plan/v1
product_contract_source: ce-brainstorm
execution: code
---
## Goal Capsule
- **Objective:** Make project access understandable and enforceable: administrator authority stays outside job roles; the sole role permission is project access, which permits project creation; project creators with that permission manage their own project; members and project states determine whether work can continue; all fixed company templates are available to projects.
- **Means:** Keep Backend authoritative for authenticated identity and job-role permission, and RDM authoritative for canonical project ownership, membership, project state, and template catalog. Recheck authorization at every read/write and asynchronous execution boundary.
- **Authority:** The user's request in this conversation. Preserve the existing distinction between system administrator and RDM domain administrator. Product rules below govern when existing implementation or older proposals differ.
- **Execution profile:** Split implementation into bounded Backend, RDM, and Frontend units, with host-owned integration, verification, review, and delivery under the repository's agent-per-unit rules.
- **Stop conditions:** Stop when role permissions show only project access, project creator and state gates work for synchronous and in-flight operations, the complete fixed-template catalog is available, and focused verification passes. Do not implement document-duty assignment, production data changes, deployment, or unrelated permission redesign.
- **Delivery owner:** Host owns cross-repository planning, scope review, authoritative verification, review, PR/CI, merge scheduling, and safe closure. Component work begins only after fresh source and branch checks.
## Product Contract
### Summary
The system's role editor exposes job-level system settings and user-management actions alongside project access, which makes role permission sound broader than the intended project boundary. In this product, administrator and super-administrator authority governs system settings, accounts, roles, and platform permissions. A job role has one product permission: project access. Users with effective project access may create projects, and only the creator with effective project access may administer a project. Project membership and project/member state separately govern conversation work. Projects expose the complete fixed company-template library; assignment of document authors, reviewers, and approvers is outside this increment.
### Problem Frame
Current code has a broader action catalog, Backend role-permission records, RDM membership rows, creator/owner checks, project archive/close fields, and a 44-item published DHF catalog. These mechanisms do not yet express one product rule: system administration is distinct from role permission; member removal is currently represented by deleting membership; project-wide and per-member work suspension are not the same as archival; and a complete template catalog is not the same as project-level template access. The result is confusing role configuration and inconsistent expectations about who can create or continue work.
### Actors
- **Super-administrator:** Holds the highest protected system authority according to the existing super-administrator contract.
- **System administrator:** Manages the system actions explicitly assigned by the existing administrator contract. System administrator status alone does not grant project content access or project management.
- **Project creator:** The immutable `createdBy` identity for one canonical RDM project. It can manage that project only while its effective project-access permission and account are valid.
- **Active project member:** Can discover the project and create/use personal conversations while both project and member states permit work.
- **Read-only former or suspended member:** Can read the historical material allowed by the applicable read boundary, but cannot create or continue work.
### Requirements
- **R1. Separate system authority from job roles.** Role configuration must not expose or grant system settings, account/user management, role administration, administrator appointment, or platform-permission management. Those actions remain in protected administrator/super-administrator authorization paths. Preserve the existing distinction between administrator levels; do not infer authority from an RDM domain administrator, project creator, or project member.
- **R2. Reduce role permissions to project access.** Each role has one boolean project-access setting, whose meaning is permission to create a project. Unknown, disabled, unassigned, or denied roles cannot create projects. System administrator status by itself does not substitute for project access.
- **R3. Enforce creator-scoped project management.** Project management requires a valid account, effective project access, and the exact authenticated actor matching the canonical project's immutable creator. These two project conditions are sufficient for the in-scope management actions: project settings, membership, and project/member work states. Existing per-action member/project overrides must not separately grant these management actions to another actor or block the eligible creator. A member with role `owner`, a system administrator, a caller-supplied owner field, or a host-project owner does not bypass the gate.
- **R4. Preserve active-member work.** An active project member can see the project and create personal conversations. Existing conversations remain private according to the read boundary; membership does not itself publish another user's private uploads or drafts.
- **R5. Make removal revoke work but retain allowed history.** Removing a member revokes project work immediately. The former member cannot create conversations, send messages, resume/retry work, approve an execution that would continue work, upload or mutate project data, or continue an existing conversation. The former member retains read-only access to historical material within the agreed historical visibility boundary. Preserve the revocation record so history remains auditable and re-adding does not erase prior removal.
- **R6. Add project-wide work state.** An active project permits work only for members whose member state is active. A closed project permits no member to create or continue conversation work; existing readable conversations and documents remain available. The creator can still use project-management actions needed to reopen or administer the project.
- **R7. Add per-member work state.** The creator can suspend or reactivate an individual member without removing membership. A suspended member remains discoverable/read-only within the same historical boundary but cannot create or continue conversation work. Project-wide closed state overrides an individually active state.
- **R8. Enforce gates at every execution boundary.** Authorization must be rechecked before writes and before queued, resumed, confirmed, or delayed work causes side effects. A permission or state change that wins the race prevents the later operation. Read-only access must not accidentally resume a stream, run, or HITL action.
- **R9. Integrate the complete fixed-template library.** Inventory and expose every fixed company template used in R&D and business phases, including the existing 44-item DHF source batch and any additional fixed-template source found during inventory. Preserve source identity, version/hash, phase, applicability, and format. A template can be selected and instantiated into a project where generation is supported; a project is not prepopulated with every template automatically.
- **R10. Defer document-duty assignment.** Do not add, connect, or change assignment of document author/reviewer/approver duties for these templates in this increment. Do not alter existing duty history or make a template unavailable solely because new duty UI is deferred. Keep any existing behavior outside the changed paths intact.
- **R11. Migrate without privilege expansion.** Existing administrator grants remain protected administrator authority. Existing role assignments that grant system/user actions cease to be effective through roles; they must not silently promote users to administrators. Existing valid project-access assignments are preserved. Any ambiguous legacy owner, membership, or history visibility remains explicit and fails closed rather than being guessed.
- **R12. Keep project data and lifecycle history.** Closing a project or suspending a member is reversible and does not delete projects, conversations, documents, or membership history. Removal is not archive or account deletion.
- **R13. Let authorized role managers configure a new role's project permission.** An actor allowed to manage roles can edit the role's single project-access permission immediately after creation. The role editor must not require the separate `system.permissions.manage` authority to set that one job-role permission. Actors without role-management authority remain read-only, and direct API writes remain protected. System grants and administrator-owned platform limits continue to require `system.permissions.manage`.
### Scope Boundaries
**In scope:** role editor and server role validation; system/admin action sources; project creation and creator authorization; creator-only project settings, membership, and work-state management; project membership active/suspended/removed states; project active/closed state; historical read-only boundary after suspension/removal; all fixed-template inventory and project availability; API, UI, migrations, audit, and tests required for these behaviors.
**Out of scope:** implementing or newly integrating document-duty assignment; redesigning document author/review/approval workflow; exposing private content to system administrators; changing archive/restore behavior; project transfer to another creator; project deletion; automatic creation of every template instance; changing global project-name uniqueness; production migration, release/tag publication, deployment, and live-data repair.
**Deferred to Follow-Up Work:** assigning author/reviewer/approver responsibilities per fixed template; transfer of project creator ownership; richer per-member project action matrices beyond active/suspended work state; template fidelity remediation beyond current supported generation contracts.
### Key Flows
- **F1. Create a project:** An enabled user with effective project access creates a canonical project. The server records the authenticated creator; the client cannot choose or replace the creator.
- **F2. Manage a project:** The creator with effective project access changes settings, membership, or state. A non-creator, a creator whose project access has been removed, or an administrator without project access is denied.
- **F3. Add a member:** The creator adds an enabled account. The member sees the project and can create personal conversations only when the project and member are active.
- **F4. Suspend a member:** The creator suspends the member. Reads remain within the historical boundary; every new or continued work action is denied. Re-activation restores the active-member path without erasing history.
- **F5. Remove a member:** The creator removes the member. The work gate closes and removal is retained as history; the former member can only read permitted historical material.
- **F6. Close or reopen a project:** The creator closes the project; all members become read-only for project work. Reopening restores work to members whose own state is active.
- **F7. Use a fixed template:** A project member browses the complete company-template catalog, selects a template, and creates an instance only through supported generation. Duty assignment is not part of this flow.
### Acceptance Examples
- **AE1.** A normal user without project access receives a denial when creating a project; a user with project access succeeds and is recorded as creator.
- **AE2.** A user with system-admin rights but without project access cannot create or administer a project. The same user can perform only the system actions allowed by the administrator contract.
- **AE3.** A member with project access but who did not create project A cannot manage A or project B. Project A's creator cannot manage B.
- **AE4.** An active member sees the shared project and creates a personal conversation. Other members do not gain access to that conversation merely through membership.
- **AE5.** After removal or suspension, a member can read allowed existing history but every new or continued work action is rejected, including retry, resume, queued continuation, and confirmation that would cause execution.
- **AE6.** Closing project A blocks work for all members and preserves read access; reopening A restores work only for active members.
- **AE7.** Suspending one member blocks only that member's work; other active members continue while the project is active. Closing the project blocks all members regardless of individual state.
- **AE8.** The project catalog accounts for every fixed company template from the approved source inventory, including all 44 DHF assets. Duplicate bytes with distinct source identities remain distinct catalog entries. No project is silently seeded with 44 document instances.
- **AE9.** Role configuration cannot set a system setting, user-management, administrator, or platform-permission action. These actions remain available only through their protected admin paths.
- **AE10.** SQLite and PostgreSQL produce equivalent authorization, concurrency, and migration outcomes; cross-project and post-revocation negatives remain denied.
- **AE11.** An authorized role manager creates a role and can immediately enable or disable its single project-access permission. A role manager who lacks `system.permissions.manage` still succeeds; an actor without `system.roles.manage` cannot edit the permission through the UI or API. System-grant and platform-limit writes remain denied without `system.permissions.manage`.
### Success Criteria
- Role configuration has one understandable project-access choice and no configurable system/user actions.
- A role manager can create a role and configure its project-access choice without a separate platform-permission-management grant.
- Project creation and management follow the exact effective-project-access plus creator rule.
- Project/member state transitions have predictable, reversible read-only behavior, including for in-flight and queued work.
- Every fixed company template is discoverable with traceable source/version/phase metadata; supported selection creates a project instance without introducing duty assignment.
- Existing private-content, project-isolation, system-admin, and super-admin boundaries remain intact.
## Planning Contract
### Key Technical Decisions
- **KTD1. Keep admin authority outside roles.** Keep super-administrator and system-administrator checks in the existing trusted system-authority path. Role permission APIs and UI expose only the project-access capability. Reuse current administrator-level distinctions rather than creating role-based substitutes.
- **KTD2. Define one effective project-access capability.** A single role boolean determines whether an enabled account with an enabled assigned role can create projects. The legacy `allowed`/`capAllowed` pair is only a storage compatibility shape for this action: normalize old records from their prior effective result (`allowed && capAllowed`), and write both fields from the new single boolean. Do not show or authorize an independent role cap for project creation. Administrator status alone is not an implicit project grant. The same effective capability and exact creator identity are the complete authorization rule for in-scope project management; existing per-action project overrides cannot add another manager or impose a second management gate. Preserve explicit deny/fail-closed behavior when role/provider data is missing or stale.
- **KTD3. Separate project membership, member work state, and project work state.** Retain canonical RDM ownership/membership as domain authority. Represent `active`, `suspended`, and `removed` member history separately from `active`/`closed` project work state. Effective work requires an enabled account, active membership, active member state, and active project state.
- **KTD4. Preserve read and work authorization as different checks.** Membership revocation/suspension and project closure block all work paths immediately while leaving only the explicitly allowed read paths. Recheck current state at execution/side-effect boundaries rather than relying on a UI state or a conversation's creation-time authorization.
- **KTD5. Use the published company-template catalog as the source of truth.** Include all inventoried fixed company templates in the project catalog. Keep source assets and template instances distinct. Instantiate on explicit selection, not by bulk-seeding every project. Preserve unsupported fidelity/generation states as visible limitations; catalog presence alone does not claim full-fidelity rendering.
- **KTD6. Use conservative historical visibility.** Unless the user chooses a broader boundary during this planning conversation, retain only the history the member could already read before suspension/removal; do not make another member's private conversation or upload newly visible. Capture the chosen boundary in the final plan and tests.
- **KTD7. Keep this increment reversible and local.** No deletion or destructive data migration is needed. Preserve audit/history, run local verification, and keep deployment and production data repair outside this plan.
### High-Level Technical Design
```mermaid
flowchart LR
A[Authenticated actor] --> B[Backend admin authority]
A --> C[Backend job role: project access]
C -->|create permitted| D[RDM canonical project]
C -->|plus exact createdBy| E[Project management]
D --> F[Member state: active / suspended / removed]
D --> G[Project state: active / closed]
F --> H{Work gate}
G --> H
H -->|allowed| I[Create or continue conversation work]
H -->|denied| J[Historical read-only projection]
D --> K[Complete fixed-template catalog]
K -->|explicit selection| L[Supported project document instance]
```
Project-management authorization and project-work authorization are separate decisions. A closed project may still be administered by its eligible creator, while its members remain read-only. A suspended or removed member never gains creator-management capability through historical read access. Archival remains a separate existing lifecycle and is not changed by this plan.
### Implementation Notes
- Backend owns authenticated actor identity, role assignment, administrator actions, and effective project-access resolution. RDM owns canonical project creator, membership history, project/member work states, and template catalog consumption. Frontend is a projection, not an authorization source.
- Existing project and conversation data may live in different stores. Any write gate crossing Backend and RDM must use the established public extension contracts and preserve account-write fencing; do not assume one transaction spans both stores.
- RDM currently stores `rdm_members` rows by `(project_id, owner)` and removal deletes the row. It has creator checks, owner/member roles, archive/closed fields, and project action caps. The new contract must not mistake project owner role, archived state, or RDM domain-admin state for the requested rule.
- The RDM checkout inspected for this plan is 15 commits behind its remote-tracking `main`; the Backend RDM pin and fixed-template inventory must be refreshed before implementation. Current local source is planning evidence, not a delivery candidate.
### Sources & Research
- `modules/backend/internal/authz/catalog/catalog.go`, `modules/backend/internal/authz/service.go`, and `modules/backend/internal/server/server.go`: current catalog separates protected system actions from the special `project.create` role decision; authenticated system administrators currently bypass that decision, which this plan changes.
- `modules/backend/internal/store/role_policy.go` and `modules/frontend/src/components/RolePermissionsPanel.tsx`: current role policy storage and role-permission editor to be narrowed to project access.
- `modules/backend/internal/server/roles.go` and `modules/frontend/src/components/RolePermissionsPanel.tsx`: role creation/listing use `system.roles.manage` / `system.roles.view`, but role permission catalog and writes plus checkbox enablement currently require `system.permissions.view` and `system.permissions.manage`. This lets a delegated role manager create a role while leaving every permission checkbox disabled. This source path grounds R13; live account grants were not inspected.
- `modules/documents/docs/proposals/2026-10-13-system-project-membership.md`: prior approved boundary for RDM project identity, Backend projections, member-created private conversations, and project/resource privacy. Its privacy rules remain unless the user explicitly changes the historical scope in this plan.
- `modules/documents/docs/proposals/2026-10-15-iam-focused-role-permission-spec.md`: current administrator, role, and project permission contracts; the present role model is broader than the user's requested product model.
- `modules/documents/docs/records/2026-10-14-rdm-two-trees-latest-templates/rdm-two-trees-requirements-r1.md` and `modules/documents/docs/proposals/2026-10-14-rdm-two-trees-latest-templates.md`: 44-source DHF inventory and current known distinction between catalog coverage and rendering fidelity.
- `/home/weichen/projects/Agent-System-RDM/internal/members/members.go`, `internal/rdmapp/projects_api.go`, `internal/rdmapp/system_projects.go`, and `internal/rdmapp/templates/dhf/catalog.json`: inspected RDM membership deletion, creator management, active work checks, and template catalog. This checkout is behind remote `main` and must be refreshed before implementation.
- No external research was used; existing Backend and RDM authorization and template contracts provide direct local patterns.
### Alternatives Considered
- **Keep all existing system actions in the role matrix but lock them:** Rejected because it preserves the misleading product model and makes role configuration look capable of controlling admin authority.
- **Use project membership alone as management authority:** Rejected because a member or delegated project-owner role must not acquire the creator's project-management right.
- **Delete membership and infer history from conversation ownership:** Rejected because it loses explicit revocation history and cannot represent per-member suspension cleanly.
- **Close a project by archiving it:** Rejected because closure must be reversible and read-only while preserving a distinct archival lifecycle.
- **Instantiate every template at project creation:** Rejected because it creates unused documents and duties/approval expectations the user explicitly deferred.
### Deferred Planning Questions
- The user's answer on removed-member historical visibility is pending. The plan currently assumes only pre-revocation visible history remains readable. If the user wants all project conversations/documents visible after removal, update the privacy requirements and acceptance tests before implementation.
- Exact fixed-template source inventory beyond the 44-item DHF batch must be verified against the current RDM source and company template directory when component baselines are refreshed. Missing, duplicate, unsupported, or unreviewed entries must be listed rather than silently omitted.
- Implementation must verify how active runs, delayed commands, streamed events, uploads, and HITL confirmations map onto the current Backend/RDM public contracts; the required outcome is specified, but exact handler names are implementation-time discovery.
## Implementation Units
### U1. Restrict role permissions to project access
**Goal:** Remove system settings, account/user, administrator, and platform-permission actions from role configuration while preserving protected administrator behavior.
**Requirements:** R1, R2, R9, R11, R13
**Dependencies:** None
**Files:**
- `Agent-System-Dev/modules/backend/internal/authz/catalog/catalog.go`
- `Agent-System-Dev/modules/backend/internal/authz/service.go`
- `Agent-System-Dev/modules/backend/internal/store/role_policy.go`
- `Agent-System-Dev/modules/backend/internal/store/migrate.go`
- `Agent-System-Dev/modules/backend/internal/server/roles.go`
- `Agent-System-Dev/modules/backend/internal/server/role_authorization_test.go`
- `Agent-System-Dev/modules/frontend/src/components/RolePermissionsPanel.tsx`
- `Agent-System-Dev/modules/frontend/src/rolePermissions.ts`
- Related Backend and Frontend permission tests
**Approach:**
1. Keep protected system actions in admin/super-admin authorization. Restrict job-role permission read/write/catalog views to one project-access boolean and label it by its actual meaning. The UI has one checkbox, not separate default and cap controls.
2. Authorize this role field using role-view/manage authority. Do not require `system.permissions.manage` to edit a job-role definition. Keep system-grant and platform-limit writes under `system.permissions.manage`.
3. Enforce the same split in the Backend API: role managers can read the project-access descriptor and replace that role value; handcrafted writes to protected system/user actions remain rejected. Map the boolean to both legacy storage columns and authorize from the canonical boolean result.
4. Ensure system administrator status does not bypass the effective project-access check for project creation. Keep role/provider errors, disabled role/account, and unknown actions denied.
5. Migrate old role permission rows so system/user actions are ignored for role authorization and not shown as role capabilities. Do not convert them into administrator grants. Preserve the prior effective project-access result when normalizing `allowed` and `capAllowed`; retain explicit administrator grants unchanged.
**Test scenarios:**
- A role containing old system settings/user action rows cannot authorize those actions and the role API/UI omits them.
- An administrator without project access cannot create a project; an eligible role can.
- A system setting/user action grant through a role-permission write is rejected atomically.
- A user with `system.roles.manage` but without `system.permissions.manage` creates a role, enables its project access, saves successfully, reloads the role, and sees the saved value.
- A user with `system.roles.view` but without `system.roles.manage` can read the project-access value but sees it read-only; direct role-permission writes are rejected.
- A user without `system.permissions.manage` still cannot change system grants or platform limits.
- A legacy role with protected rows migrates without promoting its members or losing valid project access.
- An existing project role with `allowed=true, capAllowed=false` remains effectively denied after normalization; one with both fields true remains allowed.
- A missing/incompatible role provider denies project creation.
**Verification:** Backend role-policy/catalog/authorization migration tests and Frontend role editor tests prove the separated role-management and platform-permission gates; current admin and super-admin tests pass.
### U2. Enforce project creator management authority
**Goal:** Require effective project access and exact canonical creator identity for every project-management action.
**Requirements:** R2, R3, R11
**Dependencies:** U1
**Files:**
- `Agent-System-RDM/internal/rdmapp/projects_api.go`
- `Agent-System-RDM/internal/rdmapp/system_projects.go`
- `Agent-System-RDM/internal/perms/role_provider.go`
- `Agent-System-RDM/internal/perms/perms.go`
- Related project creator and permission tests
**Approach:**
1. Centralize a management check using authenticated actor, canonical project ID, immutable `createdBy`, current account/role project access, and project existence.
2. Apply that complete check to project settings, member management, and project/member work-state management. Existing per-action overrides cannot grant management to non-creators or further restrict the eligible creator. Do not change archive/restore, which remains a separate lifecycle.
3. Preserve separate project-resource authorization for ordinary member reads and conversation work. Do not grant management because a member has an `owner` membership label or an action override.
4. Keep account disable and project-access revocation race-safe using existing write fences or equivalent transactional rechecks.
**Test scenarios:**
- The creator with project access manages their own project.
- The creator after losing project access is denied management.
- A non-creator member, project `owner` role holder, system administrator, and RDM domain administrator are each denied unless they independently meet the exact creator rule.
- The eligible creator can manage in-scope project settings, membership, and state even when a legacy per-action override denies them; a non-creator cannot manage when an override allows them.
- Cross-project IDs, host/RDM ID confusion, and caller-supplied owner substitutions are denied.
- Concurrent account disable or permission revocation prevents a later management write from committing.
**Verification:** Focused RDM project authorization tests pass on SQLite and PostgreSQL; existing project privacy and host-projection tests remain green.
### U3. Preserve membership history and read-only removal
**Goal:** Make member removal and suspension revoke all project work immediately while retaining only authorized historical reads.
**Requirements:** R4, R5, R8, R12
**Dependencies:** U2. The historical visibility scope is a blocking product decision before U3 implementation; the current plan records KTD6 as the conservative default.
**Files:**
- `Agent-System-RDM/internal/members/members.go`
- `Agent-System-RDM/internal/rdmapp/projects_api.go`
- `Agent-System-RDM/internal/rdmapp/system_projects.go`
- `Agent-System-RDM/internal/rdmapp/private_doc_views.go`
- `Agent-System-RDM/internal/rdmapp/iam_i1_privacy_test.go`
- `Agent-System-Dev/modules/backend/internal/server/server.go`
- Relevant conversation read/write, resource authorization, and async-work tests
**Approach:**
1. Replace hard deletion as the authorization source with retained membership/revocation state and timestamps while preserving current member-list semantics for active members.
2. Implement the settled historical read boundary independently from work authorization. Do not expose another user's private conversation/upload unless the user explicitly chooses that scope.
3. Recheck current membership state for conversation creation, message writes, run/retry/resume, upload/mutation, HITL continuation, queued work, and streamed side effects.
4. On removal or suspension, invalidate or stop pending operations where they would otherwise continue producing side effects; reads must remain non-mutating.
5. Re-adding a member creates a new active period or reactivates with an auditable transition; preserve previous removal history.
**Test scenarios:**
- Removed/suspended actor can read allowed historical items but cannot create a conversation or mutate a prior conversation.
- Removed/suspended actor cannot retry, resume, upload, send, approve-to-execute, or cause a queued run to continue.
- An operation authorized before revocation but attempting its side effect after revocation is rejected.
- Another active member's private conversation and original upload remain hidden under the conservative visibility choice.
- Re-adding restores active work without deleting or rewriting prior membership history.
**Verification:** Backend and RDM read/write boundary tests cover the exact revocation race and preserve private-content negatives.
### U4. Add project-wide and per-member work states
**Goal:** Support reversible project closure and per-member suspension with consistent read-only behavior.
**Requirements:** R3, R6, R7, R8, R12
**Dependencies:** U2, U3
**Files:**
- `Agent-System-RDM/internal/projects/projects.go`
- `Agent-System-RDM/internal/members/members.go`
- `Agent-System-RDM/internal/rdmapp/projects_api.go`
- `Agent-System-RDM/internal/rdmapp/system_projects.go`
- `Agent-System-RDM/internal/perms/perms.go`
- Related project/member migration and state-transition tests
- Related Backend conversation/async execution authorization tests
**Approach:**
1. Add explicit reversible project work state `active|closed` and member work state `active|suspended`, leaving archival and historical removal distinct.
2. Define the work predicate as enabled account AND active member AND active member state AND active project state. Historical read access is evaluated separately.
3. Permit the eligible creator to reopen/manage a closed project while denying all member work. Per-member suspension affects only that member; project closure overrides every member state.
4. Enforce current state before every work side effect and on in-flight/queued continuations. Persist version/audit data so stale UI requests and delayed responses cannot restore old state.
5. Keep transitions idempotent and reversible. Do not delete or rewrite conversations, project data, or membership history.
**Test scenarios:**
- Closing a project blocks new and existing conversation work for every member while preserving existing reads.
- Reopening restores work only for active members.
- Suspending one member blocks only that member; other active members continue normally.
- Closing a project blocks a member even when their individual state is active.
- State changes concurrent with message send, queued execution, retry, and confirmation prevent post-close side effects.
- Creator management remains available on a closed project but is denied when creator permission/account is invalid.
**Verification:** RDM state and migration tests pass on SQLite/PostgreSQL; Backend integration tests prove the canonical RDM state gates all conversation work paths.
### U5. Expose the complete fixed-template library to projects
**Goal:** Ensure every fixed company template across business and R&D phases is cataloged and available for explicit project use without adding document-duty assignment.
**Requirements:** R9, R10
**Dependencies:** None for inventory; project access read boundary from U3/U4 for project exposure
**Files:**
- `Agent-System-RDM/internal/domain/template_asset.go`
- `Agent-System-RDM/internal/rdmapp/dhf_catalog.go`
- `Agent-System-RDM/internal/rdmapp/templates/dhf/catalog.json`
- `Agent-System-RDM/internal/rdmapp/dhf_template_instance.go`
- `Agent-System-RDM/internal/rdmapp/dhf_catalog_test.go`
- `Agent-System-RDM/internal/rdmapp/dhf_template_instance_test.go`
- Source inventory and generated catalog tests
**Approach:**
1. Refresh source inventory and verify the fixed-template set from company sources; retain all 44 DHF source identities and include other fixed templates if present.
2. Reconcile source path/hash, template ID/version, business/R&D phase, applicability, format, publication/readiness, and supported generation status. Do not replace templates with the 69 legacy skeleton types or infer missing mappings from similar names.
3. Connect the complete catalog to project-level browse/select behavior. Instantiate only a selected template and preserve exact source/version provenance.
4. Surface blocked or unsupported fidelity/generation status honestly. Catalog inclusion does not claim that every `.doc` or render profile is fully faithful.
5. Do not add or invoke assignment of author/reviewer/approver duties, approval flows, or new lifecycle transitions.
**Test scenarios:**
- The catalog has one traceable entry for every inventoried source, including duplicate-byte files with distinct source identities.
- All fixed business and R&D phase templates appear in the project template browser with source/version/phase metadata.
- An explicitly selected supported template creates one instance with exact source/version provenance.
- Unsupported/blocked templates are visible with their real state and cannot be represented as successfully generated.
- No project creation path automatically creates all template instances or duty assignments.
**Verification:** Template source-inventory, catalog, supported-generation, and project integration tests prove completeness and provenance for the refreshed source snapshot.
### U6. Present role, member, and project-state controls
**Goal:** Make the permission and read-only states clear in the existing admin, role, project, and member interfaces.
**Requirements:** R1-R10, R13
**Dependencies:** U1-U5 API contracts
**Files:**
- `Agent-System-Dev/modules/frontend/src/components/RolePermissionsPanel.tsx`
- `Agent-System-Dev/modules/frontend/src/components/AccountUsersPanel.tsx`
- `Agent-System-Dev/modules/frontend/src/components/TaskSidebar.tsx`
- Existing project settings/member components
- `Agent-System-Dev/modules/frontend/test/role-permissions*.test.ts`
- Relevant project access and browser tests
- `Agent-System-Dev/DESIGN.md`
**Approach:**
1. Role UI shows one project-access checkbox to authorized role managers. A newly created role is selected with an editable project-access checkbox when the actor has `system.roles.manage`; unrelated `system.permissions.manage` state does not disable it. Read-only users receive a clear reason.
2. System/admin actions appear only in the protected system-administration area and reflect the current super-admin/admin contract.
3. Project management controls are visible and accepted only for the creator with project access. The UI uses service-provided state; server authorization remains authoritative.
4. Show project closed, member suspended, and removed/read-only states distinctly, with write controls disabled or absent while read access remains.
5. Surface the fixed-template catalog and explicit selection state. Do not add document-duty assignment UI.
6. Follow the existing UI design tokens, responsive behavior, accessibility, and async sequence ownership in `DESIGN.md`.
**Test scenarios:**
- Role users see only the project-access control; admins see only their authorized admin actions.
- A role manager without `system.permissions.manage` sees an editable project-access checkbox after creating a role and can save/reload its value; a read-only role viewer gets a clear explanation and cannot submit a direct write.
- The role editor does not grant the same actor access to system-grant or platform-limit changes.
- A non-creator cannot discover active project-management controls, and a direct API request remains denied.
- Active project/member affordances work; suspended/closed/removed states show read-only behavior with all work actions blocked.
- A late response cannot restore active controls after account switch, state transition, or membership revocation.
- Template browsing shows all catalog entries while no duty-assignment control is introduced.
**Verification:** Frontend typecheck/unit tests and focused Playwright flows pass at desktop and narrow viewport sizes; design guard remains green.
## System-Wide Impact
- **Users:** Role editing becomes smaller; project creation and management become predictable; removed/suspended members retain read-only history but cannot resume work.
- **Administrators:** System/user duties remain in protected admin surfaces, separate from job-role permissions. System administrators do not gain project contents by virtue of their status.
- **RDM:** Remains authoritative for canonical projects, membership/state, template catalog, and domain content. Member deletion, project closure, and creator management checks require coordinated domain/API updates.
- **Backend:** Continues to own identity and role authority and must fence conversation writes and continuations using current authorization, not UI claims.
- **Compatibility:** Existing role data, active membership, project archive/close fields, host-project projections, conversation privacy, and template identities require explicit migration/compatibility tests. Dev's RDM pin must be aligned only through the normal reviewed cross-repository process.
## Verification Contract
| Scope | Verification | Proves |
|---|---|---|
| Backend role authority | Focused `go test` for `internal/authz`, `internal/store`, and `internal/server` role/authz tests | Protected system actions remain admin-only; role project access gates creation; migrations do not elevate users |
| RDM authorization and state | Focused tests for `internal/perms`, `internal/members`, `internal/projects`, and `internal/rdmapp` | Creator identity, member/project states, removal history, privacy and execution gates |
| Database portability | Existing isolated PostgreSQL test harness for affected Backend/RDM migration and concurrency tests | SQLite/PostgreSQL parity, conflict handling, and transaction fencing |
| Template inventory | Catalog/source/hash checks plus supported generation tests | Complete fixed-template coverage with exact source provenance and honest fidelity state |
| Frontend | Focused Vitest/typecheck and browser tests for role, project, member, status, and template flows | Correct affordances and read-only behavior across real UI transitions |
| Integrated candidate | `make test-worktree`, `scripts/check-pins.sh`, and the applicable Dev composition suite once on the final fixed candidate | Exact cross-repository composition is buildable and matches reviewed component pins |
| Delivery | Independent review, exact-head required CI, PR base/target checks and task evidence under `policy/delivery.md` and `policy/governance.md` | Reviewed candidate is deliverable; local SELF results are not presented as CI or runtime acceptance |
No deployment, production database migration, live permission rewrite, or business-account acceptance is included. Runtime behavior remains `NOT_RUN` until separately authorized and validated against the exact deployed composition.
## Definition of Done
- Role permission API and UI offer only project access; system settings, user/account, role, and platform-permission actions remain admin/super-admin controlled.
- Project creation requires effective project access; project management additionally requires exact canonical creator identity and current enabled state.
- Active members can see projects and create personal conversations. Suspended/removed members and closed projects reject all new and continued work while preserving the agreed read-only history.
- Authorization is rechecked for synchronous, queued, resumed, retried, streamed, and confirmation-triggered side effects.
- Every fixed company template is inventoried and available to projects with traceable identity, phase, version, and supported-generation status; specific template instances are created only on selection.
- No document-duty assignment functionality is added or newly connected; existing private data and duty history remain unchanged.
- Focused Backend, RDM SQLite/PostgreSQL, Frontend/browser, and exact Dev composition checks pass for the final candidate; independent review and required exact-head CI pass before merge.
- No deployment, production data change, release/tag publication, or live-user permission adjustment is performed under this plan.
@@ -0,0 +1,73 @@
# 快慢通道 O2 后续:A01 检查点证据合规 实施与测试计划
- 状态:**计划稿,待人审**(与[方案](../specs/2026-10-20-fast-slow-o2-a01-evidence-design.md)同批评审)。人审通过前不派 worker、不改产品代码。
- 日期:2026-10-20;作者:host;来源等级:SELF。
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md);worker 不提交、不推送、不开 PR;host 负责范围审阅、权威验证、PR/CI/合入。
- 付费与真实模型:I1/I2 零付费;P2 付费复验按资源提案单独批准。
## 1. 交付切分与顺序
| 增量 | 仓 | 内容 | 依赖 | 合入方式 |
|---|---|---|---|---|
| I1 | Backend | 说明/提示收紧(方案 §3.1)+ 评测允许修正一次与新标签(§3.2) | 无 | 组件仓 PR,当前 HEAD CI 通过后合入 |
| I2 | Dev | `modules/backend` gitlink 前移到 I1 合入提交 | I1 | Dev PR,`make test` 组合验证 + 合入窗口 |
| P2 | 证据目录 | 一次 A01 付费复验 | I2 + 用户批准资源提案 | 不入仓;结论写回后续任务卡 |
I1 是一个可独立实施的单元:文案与评测两处改动都在 Backend 且共享同一验证命令,拆开只会增加一次派遣与一次 CI。
## 2. I1 Backend(单元 `o2a01e-i1-backend-evidence-guidance`)
**允许路径**(前缀):
- `internal/runtime/work_checkpoint.go`、`internal/runtime/work_checkpoint_test.go`
- `internal/runmgr/material.go`
- `internal/runmgr/material_sources_test.go`、`internal/runmgr/material_first_checkpoint_test.go`(提示断言所在文件)
- `internal/runmgr/material_evidence_test.go`(宿主归一断言,只加测试,不改 `material_evidence.go`)
- `internal/server/work_checkpoint_eval_test.go`、`internal/server/work_checkpoint_eval_budget_test.go`
- `eval/live/work_checkpoints_test.go`
**改动**:
1. `checkpointDecl()`:按方案 §3.1 第 1、2 条重写 `Description`,并只给现有 schema 字段补 `description`;类型、枚举、`required`、`additionalProperties`、`minItems/maxItems` 与嵌套结构逐字不变。
2. `material.go`:按方案 §3.1 第 3 条调整材料任务提示的三句;其余提示不动。
3. `evalRejectedCheckpointPersisted` 改为返回被拒检查点去重计数;评测流按方案 §3.2 计算 `max(库内计数, 本次输入计数)`,达 2 才停,原因 `repeated rejected work checkpoint`,停止状态保持且判断优先于终态异常;库读失败仍 `checkpoint stop state unavailable`。
4. 新标签:`evalA01Budget` 增加 `approved-o2-a01-evidence`(30,000、护栏开);`checkpointEvalOutputName("A01", "approved-o2-a01-evidence")` → `candidate-o2-a01-evidence`。评测调用记录增加本样本被拒检查点计数字段。
**测试**(全部离线、零付费):
| ID | 测试 | 断言 |
|---|---|---|
| T1 | 说明示例可校验 | 从 `Description` 抽出的示例 JSON(`material_record` 无 evidence、`material_record`+`direct_relation`、`difference`、`unresolved_relation`)套入最小合法检查点后 `core.ValidateWorkCheckpoint` 通过 |
| T2 | 反例仍被拒 | `direct_relation` 带 result、`unresolved_relation` 带 result 均被核心校验拒绝(锁定说明与校验一致) |
| T3 | Schema 结构不变 | 现有 `work_checkpoint_test.go` schema 断言保持通过;新增断言:新旧 schema **都**去掉全部 `description` 键后一致(旧 schema 的 `quote` 已带 description,不能直接与改动前原文比) |
| T4 | 一次被拒可继续 | 改写 `TestEvaluationStopsPaidRequestsAfterRejectedCheckpoint`:先修正该测试 SSE 夹具缺失的结尾 `}`(否则真实调用会触发终态异常);输入含 1 次被拒 → 允许 1 次 provider 调用且无错误;输入含 2 次被拒 → 不调用、原因 `repeated rejected work checkpoint`;其后再发一次无被拒输入的请求仍被拒(停止状态保持) |
| T5 | 跨 run 累计 | 改写 `TestEvaluationStopsAcrossRunsAfterPersistedCheckpointRejection`(同样先修正其 SSE 夹具缺失的结尾 `}`;因 `terminalFault` 在同一评测流内保持,各子用例各用新的评测流与数据目录):库内 1 次被拒 → 允许;2 次(不同 callId)→ 停;同一 callId 重复事件只计 1;库内 1 次 + 输入中同一次 → 仍只计 1 |
| T6 | fail closed 不变 | `TestEvaluationFailsClosedWhenCheckpointEventsUnavailable` 保持通过 |
| T7 | 新标签 | `evalA01Budget` 与 `checkpointEvalOutputName` 表驱动用例覆盖新标签;未知标签仍拒绝 |
| T8 | 宿主降级与说明一致 | `material_evidence_test.go`:`analysis`+`direct_relation` 经 `normalizeMaterialEvidence` 转 `needs_user`;`material_record` 无 evidence 保持 `material_record` |
| T9 | 提示约定 | 提示断言文件中断言材料任务提示含“原样记载用 material_record,只附 sources,不附 evidence”的约定 |
**单元验证命令**(worker 自检与 host 权威验证相同):
1. `go test ./internal/runtime/ ./internal/runmgr/ ./internal/server/ -count=1`
2. `go test -tags live -run '^TestFollowupA01KeepsItsOwnOneShotResult$' ./eval/live/ -count=1` 与 `go vet -tags live ./eval/live/`:`eval/live` 全部文件带 `//go:build live`,默认 `go test ./...` 与 Backend CI **不覆盖**,须单独运行;该用例为表驱动、不联网(2026-10-20 在 Backend 74e5396 本地实测通过,SELF)。不设置 `WORK_CHECKPOINT_SERVER_MODE`。
3. host 另在 Backend 根目录运行 `go test ./... -count=1` 与 `go vet ./...` 各一次;PR CI 为准(不含第 2 条,由 host 经 `make evidence` 记录)。
**越界即停**:需要改 core、schema 结构、生产停止逻辑或其他文件时,worker 以 `scope-expansion` 终止,由 host 请求授权。
## 3. I2 Dev 组合
- 前移 `modules/backend` 到 I1 合入提交;`make test` + `make build` 由 PR CI 验证;按合入窗口流程合入。
- 不改 `pins/` 与其他组件;`task-pr-check` 通过后合入。
## 4. P2 付费复验(不在本计划授权内)
- 依据方案 §5、§6 与源码外资源提案执行一次 A01;标签 `approved-o2-a01-evidence`,数据目录名含 `fast-slow-u2-u3-`(如 `fast-slow-u2-u3-o2-a01e-model-data`)。
- 候选二进制从 I2 合入后的 Dev 精确提交构建,记录 Dev/Backend SHA。
- 结束后删除路由文件,判定按方案 §5 表格,结论与花费写入后续任务卡。
## 5. 完成与停止
- 本任务(设计)完成标准:方案、计划与源码外资源提案经 PR 合入 Dev,用户收到审阅请求。
- I1/I2 在用户批准方案与计划后启动;P2 在用户单独批准资源提案后启动。
- 任一单元 fail-stop 终态(`failed-unknown`、`scope-expansion`、`blocked` 等)立即停止并报告,不自动重试。
@@ -0,0 +1,311 @@
---
title: Agent Stack Audit and Remediation - Plan
type: fix
date: 2026-10-21
artifact_contract: ce-unified-plan/v1
product_contract_source: ce-plan-bootstrap
execution: code
---
# Agent Stack Audit and Remediation - Plan
## Goal Capsule
对 Agent-Native、Agent-Squad、Agent-Intent、Agent-Command 及其 Backend 接入做源码审查和外部协议核对,先修复现有能力的契约缺陷,再补充影响实际使用的功能。
本次交付是待用户审阅的方案;业务实现、迁移、部署和发布尚未授权或执行。
建议保留现有四库分层,分批兑现可验证结果,不重写 Agent 平台、不迁移到新的编排框架。
- 第一批:执行边界、失败结果、权限缓存、意图校验、命令回执、用户/项目配置。
- 第二批:持久化适配、队列限额、过程事件。
- 后续增量:多轮意图闭环、恢复诊断、目录召回、外部互通。
- 停止条件:批准批次的回归和适用组合验证通过,完成对应 Review、PR/CI 与收口;未批准增量不顺带实施。
## Product Contract
### Scope and Evidence
审查日期为 2026-10-21。以下 SHA 是本次实际检出;Dev 启动检查确认与当次远端 main 一致、组件无漂移,不能据此声称各组件远端没有更新。
| 对象 | 路径 | 审查 SHA |
|---|---|---|
| Dev | 仓库根 | `d9e9c0560a2767539f4415b579adbeff86a22ba6` |
| Native | `deps/agent-native` | `2fc66be65201232ea38ba64d457efad5565d869d` |
| Squad | `deps/agent-squad` | `c7121bda2a681dc46078350f7929e56e142db57f` |
| Intent | `deps/agent-intent` | `651a2e1e03c4326888ef9f742ce592140ae30ab8` |
| Command | `deps/agent-command` | `4f583ca3eeeb07a56a714c5086de84dcd6ad2980` |
| Backend | `modules/backend` | `f084d10aa5ea2e4ad2ab4e6eb1268d3e83c6e991` |
发现以当前源代码及现有测试的检查为依据,均给出触发条件;不是线上事故结论,也不是穷尽性安全认证。
研究子任务报告了 Native、Squad 的本地测试和仓外探针;主线程已核对关键源代码,但未重放这些执行,也未将其导入正式 evidence,故本方案不以这些报告作为通过凭证。
修复回归、组合测试、GATE、独立 REPLAY、生产验收均为 `NOT_RUN`。
旧 N1/N2/N3、Q1/Q2/Q3 和 I1/I2 提案只用于理解历史意图,不把旧“已完成”或“待实现”表述当作当前事实。
### Existing Responsibilities
| 模块 | 当前已有能力 | 应保持的边界 |
|---|---|---|
| Agent-Intent | 规则/守卫/预期、槽位验证、语义裁决、生成兜底、目录与诊断 | 识别和提出动作;不授予执行权限 |
| Agent-Command | 命令资产/注册表、危险分级、执行四态、审计信封 | 保持无 I/O;实际授权、执行、幂等持久化由宿主负责 |
| Agent-Native | 模型流、Agent 循环、工具/MCP、预算/取消、会话、checkpoint、外部适配 | 执行一次受约束任务;路径授权不冒充 OS 沙箱 |
| Agent-Squad | 规划、图调度、leader、HITL、黑板/邮箱/移交/任务板、存储与队列适配 | 协作与步骤推进;依赖注入执行器,不复制单 Agent 内核 |
这些库并非一条固定串行流水线:普通对话可以直接执行 Native,系统命令由宿主消费 Intent 并经 Command 受理,多步任务才进入 Squad。
真正的问题集中在边界传递和失败路径,而不是缺少更多拓扑或工具数量。
### Findings
严重级别按触发后的影响排序,生产暴露面另列。P1 不表示已观察到线上损害。
下列行号是本次基线定位,实施时还须按符号及新基线复核。
| ID | 优先级与触发条件 | 源码证据 | 后果与影响边界 |
|---|---|---|---|
| F01 | P1:reasoning 请求的 Targets 含 `../` 或指向根外的符号链接 | Native `adapters/native/backend.go:324-333` 先直接写文件,后续才做交付校验 | 可先写出工作区再返回失败;应在任何副作用前拒绝非法目标。上层自行校验可降低暴露,不能代替库契约 |
| F02 | P1:同一 WorkDir 被不同 ContractRequest.SessionScope 复用;或设置 Workspace.Writable | Native `adapters/native/contract.go:42-51` 未映射这些约束;`backend.go:260-273` 使用固定 transcript,仅记录字符串 scope | 跨 scope 上下文串读及写集约束失效。当前 Backend 未发现 Contract() 消费,不据此断言产品跨租户泄露 |
| F03 | P1:ExecConfig 已启用解释器但 Env 为 nil | Native `runtime/tools/exec.go:25-26,139` | nil 被交给 os/exec,继承宿主环境,与完整白名单合同不符。Backend 当前解释器装配显式给出 env,该具体入口已有缓解 |
| F04 | P1:崩溃留下半截 JSONL 尾行,随后续写 | Native `runtime/session/tree.go:155-181,194-207` | Load 只在内存忽略尾行,Append 紧接损坏字节;下一次读取变为中段 JSON 错误。完整 JSON 无换行也需处理 |
| F05 | P1:Executor 成功仅含 patches、失败带 patches,或 running 步骤崩溃后自动恢复 | Squad `squad/workflow.go:403-418,455-464`、`squad/engine.go:1055-1109` | 正常路径成功未物化而失败、失败可被提升为成功;恢复路径文件齐全即判 completed,缺文件则直接重跑,未核对可信终态和外部副作用。Native 自行落盘只能掩盖其中的成功物化问题 |
| F06 | P1:同名 role/model/backend 的权限或并发配置更新 | Squad `squad/backend.go:38-48,86-123`;`adapters/native/register.go:104-106` | 缓存键忽略 BashPermission/其他配置,Register 也不清缓存;allow→deny 后可能继续复用旧执行器。产品每次新建 resolver 可缓解,长期服务/公共库使用仍受影响 |
| F07 | P1:调用方修改注册输入或公开返回的嵌套对象 | Intent `intent/fold.go:37-57`、`registry.go:289,494,531,557`;Command `registry.go:60,98,107` | Intent.Action/Examples 与部分目录数据、Command.ParamsSchema/ExposeAsTool 共享引用;可在 revision/锁之外改变已验证定义。这是同进程 API 合同缺陷,不等于任意远端用户已能修改 |
| F08 | P1:启用意图生成兜底,并返回缺槽/非法槽值或异名 Action | Intent `intent/recognizer.go:333-414`、`catalog.go` | 生成路径未共用完整槽位校验,Top 丢风险/确认/动作元数据,handler 槽位查表键不一致;显式 Action 的合法异名目标又会被拒。Backend 开关 `runtime.intentGenerator` 默认 false |
| F09 | P1:规则识别到非法槽位,宿主收到 Start + Clarify | Backend `internal/intentgate/gate.go:167-178`、`internal/route/route.go:66-72`、`internal/server/server.go:1206-1230` | InvalidSlots 被丢弃,Router 只取 Start。命令入口还只传 text,未传已识别槽位;可能误路由或被后续 schema 拒绝。不能据此断言任何高危命令都能绕过 cmdbus 确认 |
| F10 | P1:handler 已有副作用后报错,或成功后审计存储失败 | Command `result.go:41-43`;Backend `internal/cmdbus/cmdbus.go:433-438,539-559` | 任意 handler error 被归为“无副作用 rejected”;审计失败改 unknown 却丢已生成 op ID,且库禁止 unknown 携带 ID,影响对账及安全重试 |
| F11 | P1/P2:使用 SQLiteStore 保存协作状态,或在 async/on_exit 模式退出时 checkpoint 失败 | Squad `adapters/sqlite/runstore.go:77-119`;`squad/workflow.go:41-53` | Mailbox/Handoffs/Board 未持久化;相同 JSON 文本作 map key 会覆盖解码目标;flush 错误被吞。Checkpoint 失败不等于已完成外部动作回滚,应单独报告持久化失败 |
| F12 | P2:同一 runtime 下多个 agent 同时或连续领取任务 | Squad `adapters/sqlite/queue.go:226-239` | 只数 agent 的在途任务,取两种 cap 的最小值不能限制 runtime 总并发;当前产品是否启用该独立队列须在实施时核验 |
| F13 | P1:当前用户/项目有独立模型路由配置,进入 Squad | Backend `internal/app/app.go:340-364`、`internal/runtime/squad_runner.go:83-95`、`internal/runtime/llm.go:604-608` | 小队从默认账户/空项目读取 base/key 等,入口只覆盖 model,可能把用户模型名与默认端点/凭据混用;ReasoningSplit、RouteContract 也与 direct 路径不一致 |
| F14 | P2:小队同名工具多次调用,或工具返回错误 | Native `runtime/loop.go:43-45,777-836`、`adapters/native/backend.go:405-418`;Squad `squad/port.go:66`;Backend `internal/runtime/squad_runner.go:49-54` | CallID 在旧过程契约中丢失,宿主用工具名代替;结束事件实际文本在 Text,宿主读 Detail 并恒写 ok。结果归属、摘要与成功状态失真 |
| F15 | P2:执行工具输出大量数据;或 Contract 调用要求纯文本 | Native `runtime/tools/exec.go:149,175`、`adapters/native/contract.go:25,67-77` | CombinedOutput 先全量缓冲再裁剪,64KiB 只是展示上限;声明 CapText 却不填 Text。分别并入 U1、U2 的合同修复 |
| F16 | P2:使用 A2A adapter 接入符合 1.0 的外部服务 | Squad `adapters/a2a/card.go:24-46`、`executor.go:19-25,135,171`、`conformance_test.go` | 声称 1.0,却仍用旧 `message/send`、`tasks/get`、小写状态与旧 Card;测试假服务复制了同一套自定义结构。当前 Backend 无该依赖,不阻塞本地核心修复,但不得声称 1.0 互通已验证 |
### Requirements
**执行与权限**
- R1. 不合法路径/写集/会话身份必须在模型或工具副作用发生前拒绝;支持空约束的既有兼容语义。
- R2. 后续运行采用新的权限配置;不能因旧缓存或错误配置作用域扩大能力或混用用户凭据。
- R3. 失败、取消、不确定、已完成保持真实语义;文件存在不是成功证据,未知不能无条件重试。
**输入、恢复与可核对性**
- R4. 规则与生成路径共用权威动作定义和槽位验证;缺失/非法/歧义必须保留到宿主。
- R5. 状态快照和注册表对外隔离可变数据,读者不能绕过更新协议改变定义。
- R6. 恢复后保留协作状态,保存失败可见;runtime/agent 并发上限独立生效。
- R7. 回执保留可信关联标识;事件保留工具调用身份、真实错误和实际输出;用户可分辨完成、等待与待核对。
**功能增量**
- R8. 在已批准修复收口后,单独选择下一批功能;不把提案中的全部建议自动视为实施范围。
---
## Planning Contract
### Technical Decisions
- KTD1. 保留四库边界和现有 Go 模块;通过当前接口增量修复。现有功能覆盖足够广,整体重写会扩大兼容性与验证成本。
- KTD2. 识别输出只作为动作建议。执行时按当前 actor/project 授权、命令 schema、确认凭证再判断;目录可见不代表允许执行。MCP 对工具 annotations 的不可信要求也支持这一边界,见 S1。
- KTD3. 以宿主当前运行的有效配置构造 model/base/key/协议及完成性规则,作为同一配置快照传入;不随意开通新的 provider 协议。工具权限可在执行前进一步收紧,不能由旧构造缓存恢复。
- KTD4. 先验证执行结果,再物化候选文件,再做交付检查;失败候选可以作为诊断保留,但不得发布为成功。结果 Unknown 与可重试性分别判断,不把 HTTP/网络错误等同于“尚未执行”。
- KTD5. Native 会话和 Squad 协作状态分别在原有存储上修复。检查点、工具副作用和产品聊天历史是不同记录;不新增统一分布式执行平台,不承诺跨外部系统 exactly-once。恢复设计参考 S2 的线程状态/跨线程存储区分。
- KTD6. Command 保持无 I/O。扩展可表达副作用不确定性的 handler 结果,并复用宿主已有 operation/outbox 存储;unknown 可携带已可信生成的 ID。可查询操作记录与权限检查必须真实存在,不能只改枚举注释。
- KTD7. 两个四态合同不强行合并:Command 描述动作受理/生效,Native 描述 Agent 执行终态;Squad 通过适配保留差异。命令 accepted 不是 Agent succeeded,创建确认卡也不是业务动作完成。
- KTD8. A2A 先明确支持版本并以官方独立 fixture 验证;需要兼容旧协议时显式分版本,不把旧协议重新标成 1.0。是否投入完整互通是后续增量决策,见 S3。
### Component Flow
```mermaid
flowchart TB
U[用户输入] --> H[Backend 身份与项目上下文]
H --> I[Intent 识别与槽位验证]
I --> C[Command 宿主授权与确认]
I --> W[工作流或 Squad 编排]
I --> N[Native 单 Agent 执行]
W --> N
N --> T[经授权工具与外部能力]
C --> O[操作记录与回执]
T --> E[真实结果与过程事件]
W --> S[协作状态与 checkpoint]
E --> H
O --> H
S --> H
```
这张图表达边界关系,不要求所有输入都逐一经过四个库。
### Outcome Flow
```mermaid
flowchart TB
R[请求] --> V{授权与参数有效}
V -->|否| X[Rejected: 未开始动作]
V -->|是| P{需要确认或异步处理}
P -->|是| A[Accepted: 保留可查询 ID]
P -->|否| E[执行动作]
A -->|授权确认或继续| E
E --> F{结果及记录是否确定}
F -->|已完成并可核对| C[Committed 或执行 Succeeded]
F -->|明确失败| D[Failed 或有依据的 Rejected]
F -->|无法确认| Q[Unknown: 保留 ID 与待核对原因]
Q --> Z[查询与对账: 禁止盲目重放]
```
取消请求同样需要确认执行侧状态;不能只因为本地 context 结束就声称远端动作已停止。
### Delivery Order
第一批建议 U1-U7、U10:其中 U1-U4 先修 Native/Squad 执行与恢复基础,U5-U7 处理输入/命令合同;U10 的当前用户/项目配置问题与它们并行处理,不排在可选存储适配之后。
第二批 U8、U9、U11:恢复适配、队列和事件一致性。
每个批次在组件兼容提交落地后更新 Backend 嵌套 gitlink 和 Dev 组合指针,完成一次适用组合验证。
同批只读研究可以并行;每个仓库写入单元按 `AGENT-EXECUTION.md` 新 session、受控 transport/admit 进入候选,由 host 验证与交付。
实施前重新确认当前代码、在途任务与远端分流;本文不会覆盖后续已修复问题,也不会复用过期 CI 结论。
迁移脚本可以在隔离测试库验证,真实数据迁移、部署和发布另走授权边界。
---
## Implementation Units
### U1. Native Workspace and Process Boundaries
目标:修复 F01/F03 及 F15 的输出缓冲问题,落实 R1。
文件:`deps/agent-native/adapters/native/backend.go`、`runtime/tools/exec.go`、现有 `runtime/tools/jail.go`/`atomic.go`;测试放 `adapters/native/backend_test.go`、`runtime/tools/exec_tools_test.go`。
方案:复用已有工作区路径解析,目标在执行前预检,实际访问仍校验根内与 symlink;Env nil 归一为空白名单;子进程持续 drain 到有界头尾缓冲,不在结束后才限制内存。
验收:合法目标正常产出;`../`、绝对路径和 symlink 逃逸在零外部写入前拒绝;nil/空/显式 env 分别验证;大输出与取消后子进程退出且缓冲有界。
不把工作区路径检查描述为完整 OS 沙箱;并发敌对文件系统场景优先复用可提供根约束的文件 API。
### U2. Native Session and Contract Fidelity
目标:修复 F02/F04 及 F15 的 CapText,落实 R1/R3/R6;依赖 U1 的文件访问边界。
文件:`deps/agent-native/adapters/native/contract.go`、`backend.go`、`runtime/session/tree.go`、`exec/contract_spec.go`;测试放 `runtime/session/tree_test.go`、`adapters/native/contract_regression_test.go`(新增)及 `conformance/`。
方案:将非空 SessionScope 绑定存储身份并在恢复前检查,Writable 限制贯穿可约束的文件工具;对于进程/MCP/宿主工具等无法兑现该写集的配置,本批在 preflight 明确拒绝非空 Writable,不假定根路径检查能限制任意进程。独占追加时修复半截尾记录/补齐合法无换行记录;中段损坏仍报错。保留真实文本输出,纯文本任务不强制隐式文件交付。
验收:同 root 不同 tenant/project/session 不串上下文;已有匹配 scope 可继续;身份不明的旧文件不得自动归给新身份;只允许写指定路径;半截尾行恢复后可再读再写;纯文本成功的 Text 非空。
兼容:空 scope 保持既有一次性/旧门面语义,不进行全库存量会话迁移;历史缺身份记录只在明确兼容路径读取。
### U3. Squad Execution Results
目标:修复 F05,落实 R3;无先行依赖。
文件:`deps/agent-squad/squad/workflow.go`、`leader.go`、`engine.go` 的 recoverInterrupted、`port.go`、步骤状态持久化及 `step_retry.go`、`adapters/native/register.go`;必要的 `deps/agent-native/exec/backend.go` 与适配器映射;测试 `squad/step_retry_test.go`、`squad/recovery_test.go`、`squad/attempt_test.go`、`squad/executor_result_contract_test.go`(新增)。
方案:按 KTD4 统一普通步、IntentRunner 和 leader 委派的结果处理;显式处理 `(nil,nil)` 与物化错误。已有文件只能帮助诊断,不能覆盖执行器终态。
当前旧 ExecResult/exec.Result 只有 Success/Retryable,不能承接 Unknown/SideEffects。本单元先以兼容新增的可选字段或等价的类型化错误保留这些信息,再让 retry 层消费;缺少副作用证明的失败默认禁止自动重放,不能继续只按错误文本推断可重试。已有明确未派发或纯读取失败仍可有界重试;无需在本批整体替换所有 Executor 接口。
恢复入口同样消费持久化的终态与副作用证据。running 崩溃后,只有可信完成记录才能恢复为 completed,只有明确未执行或安全重放证据才能转 pending;其他情况保留待核对原因,禁止自动推进,沿现有中断恢复/HITL 入口处理。无交互模式明确返回需核对状态,不无限等待,也不伪称动作已失败。
验收:成功 patches-only 正常完成;失败带完整 patches 仍失败;空结果稳定失败而不 panic;部分/非法 patches 不成功;取消/Unknown 不自动变成可重试失败。外部动作完成、终态未保存时崩溃,分别测试目标文件齐全/缺失,两种情况均不伪成功或自动重复动作。检查重复尝试仍复用 ExecutionID 和截止时间。
### U4. Squad Resolver Configuration
目标:修复 F06,落实 R2;无先行依赖。
文件:`deps/agent-squad/squad/backend.go`、`adapters/native/register.go`;测试 `squad/backend_test.go`、`adapters/native/register_test.go`。
方案:后端工厂和角色执行配置有明确版本/摘要,更新时使相关缓存失效;限流身份与执行器配置身份分开,不能换版本便绕过同一角色并发上限。
验收:allow→deny 收紧、deny→allow 显式放开、工厂替换、工具配置变化和限额变化正确;并行首次 Resolve 不重复构造;在途旧执行不会因新版本开启而突破总限额。
在途任务已开始动作不承诺回滚;需要即时撤权的宿主继续在每次工具执行前检查。
### U5. Intent Snapshot and Generated Actions
目标:修复 F07 的 Intent 部分与 F08,落实 R4/R5;无先行依赖。
文件:`deps/agent-intent/intent/fold.go`、`registry.go`、`catalog.go`、`recognizer.go`、`slots.go`;测试 `intent/registry_test.go`、`intent/slots_catalog_test.go`、`intent/i2_test.go`。
方案:补齐现有 clone 的输入/输出边界;目录保留权威 ActionRef;生成命令先解析回唯一 spec,再复用槽位校验和风险元数据;不得用 handler 字符串索引 spec-ID 槽位表。分类器输入同样隔离,拒绝非有限/越界分数并重检阈值。
验收:修改原始 spec、SpecByID/Catalog/Options 不影响生效快照;规则与生成同动作同槽位得到等价结果;必填缺失、非法枚举/格式、未知槽位及 handler 重名均稳定处理;Action 指向不同命令 ID 仍可正确路由;低置信度不转 matched。
拆成独立实施时沿“快照隔离”和“生成校验”两个有界 worker 包执行,不合成通用 schema 平台。
### U6. Backend Intent Admission
目标:修复 F09,落实 R4;依赖 U5 的权威结果结构。
文件:`modules/backend/internal/intentgate/gate.go`、`internal/route/route.go`、`internal/server/server.go`、必要的 `internal/workflow/` 消费点;测试 `internal/intentgate/gate_test.go`、`internal/route/route_test.go`、`internal/server/intent_admission_test.go`(新增)。
方案:传递 missing/invalid 及澄清信息;区分“可开始澄清的流程草稿”和“可执行副作用的完整动作”。命令槽位按已声明 schema 映射,不能无类型地复制所有字符串;已有 text handler 保持兼容。高危动作仍由 cmdbus 确认。
验收:缺槽和非法槽都不会直接执行业务动作;允许澄清的流程可挂起;校验通过后的槽位正确进入 handler;显式取消/纠正尚未实现时诚实回退,不能声明已支持。
### U7. Command Isolation and Receipts
目标:修复 F07 的 Command 部分及 F10,落实 R3/R5/R7;与 U6 可独立。
文件:`deps/agent-command/registry.go`、`result.go`、`conformance/`;`modules/backend/internal/cmdbus/cmdbus.go` 及已有 operation/outbox 的最小存储接口;测试 `deps/agent-command/registry_test.go`、`result_test.go`(不存在时新增)、`modules/backend/internal/cmdbus/cmdbus_test.go`、`account_write_fence_test.go`。
方案:复制 ParamsSchema 与 ExposeAsTool;unknown 允许保留可信 OperationID;handler 区分明确未执行与执行后未知。优先按现有持久化记录提供经 owner/project 授权的查询;若所选命令没有操作记录,则把最小持久化与查询作为该命令适配的组成部分,不能宣布“有 ID 就可查询”。
验收:外部修改返回 spec 不改注册表;成功后 audit 失败返回 unknown 且保留 ID;明确校验拒绝为 rejected;副作用后报错不伪称未执行;未知操作只核对、不自动重放;重复确认/跨用户查询不会重复执行或泄露结果。
跨仓先落兼容库变更,再更新 Backend。完整通用幂等平台不在本批;新幂等键只在选定命令已有明确重复执行风险时设计。
### U8. Squad Durable State
目标:修复 F11,落实 R6/R7;结果解释依赖 U3。
文件:`deps/agent-squad/adapters/sqlite/runstore.go`、迁移入口、`squad/workflow.go` 与现有 checkpoint writer;测试 `adapters/sqlite/runstore_test.go`、`squad/checkpoint_test.go`。
方案:SQLite 完整保存 Mailbox/Handoffs/Board;用明确字段到目标的序列解码,不用内容作 map key。终态保存、manifest 和 checkpoint flush 均检查错误,并在 EvRunCompleted/成功返回之前收敛;不能只在 defer 改 error 而此前已经发出完成事件。保存错误与业务终态分别可见,不能因此重复执行已完成业务动作。
验收:FileStore/SQLite 同一合同 round-trip;恢复后继续收信/移交/认领;相同 clarifications/steering 都保存;sync/async/on_exit 的失败注入均可见;终态保存/manifest 失败不发伪成功事件;旧 schema 升级保留原数据。真实库迁移另行授权。
### U9. Squad Queue Capacity
目标:修复 F12,落实 R6;可独立。
文件:`deps/agent-squad/adapters/sqlite/queue.go`;测试 `adapters/sqlite/queue_test.go`。
方案:一次原子 Claim 同时验证 agent 在途数和 runtime 总在途数,读取/计数错误拒绝认领;保留原租约和重领规则。
验收:runtime cap=1 下两个不同 agent 只能领取一个;同 agent 跨 runtime 仍受 agent cap;并发 Claim 不超限;释放后可继续领取。未接产品的队列模块不因此新接入产品。
### U10. Backend Scoped Runtime Configuration
目标:修复 F13,落实 R2;无需等全部库修复。
文件:`modules/backend/internal/app/app.go`、`internal/runtime/model.go`、`llm.go`、`internal/squadadapter/entry.go`;测试 `internal/app/squad_config_test.go`(新增)、`internal/runtime/model_test.go`、`route_contract_eof_test.go`。
方案:从当前 rc.Settings、run owner、profile 解析完整路由,direct/squad 复用现有配置规则;必要时增加窄的工厂函数。保持项目来源观察/授权限制,不能为接线一致性重新开启被明确禁用的直连工具。
验收:两个用户不同配置与密钥来源不串用;项目覆盖生效;profile model 覆盖使用对应路由规则;MiniMax reasoning 分离和受限 EOF contract 一致;默认配置仍兼容;测试仅用合成密钥和本地 fake server。
### U11. Tool Event Fidelity
目标:修复 F14,落实 R7;跨 Native→Squad→Backend。
文件:`deps/agent-native/runtime/loop.go`、`exec/backend.go`、`adapters/native/backend.go`;`deps/agent-squad/squad/port.go`、`adapters/native/register.go`;`modules/backend/internal/runtime/squad_runner.go`;测试各层现有 host-tools/adapter/runner 测试。
方案:向后兼容新增调用 ID 和真实错误/状态信息,按原语义传递 Text/Detail;宿主事件与最终 message parts 一致。并行回调使用受控串行聚合或局部锁,必要时修复共享 artifactList 的并发访问。
验收:同名工具两次调用各自配对;失败/拒绝/取消不显示 ok;摘要来自实际结果;并行事件/工件无丢失、无 race;旧适配器字段缺省不得被推断为已确认成功。
仅修事件合同,本单元不修改界面布局;若后续需要 UI 展示,先按 DESIGN.md 单列范围。
---
## Functional Follow-Ups
以下是功能建议,不计入前述修复的默认实施范围。推荐顺序为 A1、A2、A3,A4 按真实外部对接需求启动。
| ID | 建议 | 用户收益 | 最小交付与验收 |
|---|---|---|---|
| A1 | 多轮意图闭环 | “把日期改到周五”“取消刚才的任务”能作用于明确对象 | 消费已有 set_slot/change_slot/cancel/expectation;绑定 owner/project/conversation/action/revision;歧义先澄清,过期回答不影响新任务;先覆盖一类真实业务动作 |
| A2 | 执行与恢复诊断 | 看清卡在哪里、能否恢复、是否还要等人 | 在现有 run/operation 查询上提供当前责任角色、等待原因、最后稳定 checkpoint、已知副作用、下一步;先做授权读 API,不先造大型控制台 |
| A3 | 能力目录召回与离线评测 | 不因目录固定 top-8 而遗漏低优先级能力,减少误启动 | 先建立脱敏评测集:否定/引用指令、多意图、错误槽位、过期确认、生成规则一致性;比较准确匹配、误执行、澄清率与成本,再决定是否需要检索或模型调整 |
| A4 | 明确版本的 A2A 互通 | 外部 Agent 任务可查询、可取消并诚实报告状态 | 修 F16;采用官方 fixture/独立服务验证 Card、方法、状态与身份;持久化远端 task ID,处理 auth-required、未知态、超时和取消回执;不自动重发 message/send |
现有 Makefile 已显式覆盖若干 Squad 嵌套 go.mod,但未列 A2A 子模块。
A4 启动时须纳入自己的验证入口,不能假设根 `go test ./...` 自动覆盖独立模块。
不建议本轮新增更多拓扑、动态角色市场、全自动自我改进或向量平台;先用评测证明新能力确有收益。
---
## Verification Contract
实施先用各 F-ID 的具体触发条件形成最小回归,禁止只补“函数能调用”的测试。
不调用真实付费模型,不使用生产数据库,不将已设置的真实凭据输出到日志。
| 验证层 | 范围 | 通过判据 |
|---|---|---|
| 直接回归 | U1-U11 各自文件/包 | 原触发条件被阻断,正常使用不退化 |
| 合同验收 | 现有 conformance 与 adapter 边界 | Native 文本/文件/隔离合同,Squad 成功/失败/未知处理,Intent 规则/生成一致,Command 四态可对账 |
| 定向并发 | 注册表/缓存/队列/事件聚合 | 相关 `-race` 与原子认领用例通过,不机械扩大到所有包 |
| 故障恢复 | 截断 JSONL、存储错误、执行后响应丢失 | 恢复不丢协作状态,不伪称完成,不重复外部副作用 |
| 宿主集成 | 两个用户、项目覆盖、缺槽、确认、direct/squad | 权限与配置作用域一致,确认后只执行一次,结果与事件一致 |
| 组合 | 固定组件及 Backend 嵌套 gitlink | 每批最终候选执行项目规定的 `make test` 和必要构建,按当前门禁保存 SELF;当前 HEAD 必需 CI 为 GATE |
库级测试成功不证明产品装配成功;fake 协议服务成功不证明外部协议兼容;启动 READY 不证明业务功能通过。
对于缺少真实后端的可选路径,清楚标注 NOT_RUN,不阻断已批准且不依赖它的本地修复交付。
## Definition of Done
- 已批准批次的 F-ID 均有对应修复、反例与正常路径验证;被新基线消除的项记录来源,不重复实现。
- 变化的公共合同向后兼容或有明确迁移路径;错误语义和状态持久化可解释。
- 组件 SHA、Backend 嵌套引用与 Dev 组合一致;当前候选通过适用验证、独立 Review 和必需 CI 后按项目规范合入。
- 无未授权部署/真实数据迁移/发布;可选功能保持独立,用户业务验收不被工程自检替代。
## Appendix
### Primary Research
- S1. MCP Tools specification,核对版本 `2025-11-25`,研究日 2026-10-21:工具定义、结构化输入/输出与 annotations 信任边界。`https://modelcontextprotocol.io/specification/2025-11-25/server/tools`。影响 KTD2;未要求升级协议或引入 SDK。
- S2. LangGraph Persistence 官方文档,研究日 2026-10-21:checkpointer 的线程状态与 store 的跨线程数据各有作用域。`https://docs.langchain.com/oss/python/langgraph/persistence`。影响 KTD5;仅借鉴状态边界,不引入 LangGraph。
- S3. A2A `v1.0.1` 官方 specification 与 v1 变更说明,研究日 2026-10-21:协议方法、Agent Card 的 supportedInterfaces 和任务状态表示。`https://a2a-protocol.org/v1.0.1/specification/`、`https://a2a-protocol.org/v1.0.1/whats-new-v1/`。影响 F16/KTD8/A4;不混用开发中 PR 版本。
外部研究用于校验契约和改进方案,不能单凭行业实践宣判本项目缺陷。
进一步获取 Go os/exec 与 LangGraph interrupts 网页时检索工具连接失败;本方案未把未获取内容列为外部证据。
### Remaining Decisions
建议批准第一批 U1-U7、U10 后连续实施到各自可验收的集成结果,再进入第二批 U8、U9、U11。
也可一次批准两批,但不因此纳入 A1-A4。
需要用户确定的是实施批次与后续功能优先级;具体函数、补充字段和回归组织由实施者在上述合同内确定,不再逐文件请求许可。
当前没有证据支持宣称线上已被利用;部署状态、真实模型行为和外部 A2A 互通仍待相应阶段验证。
@@ -0,0 +1,93 @@
# 快慢通道 O2 后续:A01 检查点证据合规方案
- 状态:**方案稿,待人审。** 本文不授权产品代码改动、付费请求、A01 复验、实例配置写入或部署;人审通过后才按配套计划派 worker 实施零付费部分,付费复验按[资源提案](#6-付费复验资源提案摘要)单独批准。
- 日期:2026-10-20;作者:host(只读调查与文档撰写);来源等级:SELF。
- 决策来源:用户 2026-10-20 选定“立项:出设计和资源提案,方向是收紧说明并让评测允许修正一次(推荐,先零付费)”。
- 前序:[O2 方案](2026-10-19-fast-slow-o2-route-contract-eof-design.md);P1 结论见归档卡 `.task/archive/feature-fast-slow-o2-p1.md`(限路由契约在真实路由上有效;A01 停在应用层证据合规)。
- 配套计划:[实施与测试计划](../plans/2026-10-20-fast-slow-o2-a01-evidence-plan.md)。
## 1. 目标与非目标
**目标**:让 A01 在已生效的限路由契约下,第一批检查点按工具约定发布成功;偶发一次不合规时,评测允许模型按工具结果修正一次,而不是立即判失败。
**非目标**:
- 不改 `agent-system-core` 校验器(`validateWorkFindingEvidence`)与宿主证据归一(`normalizeMaterialEvidence`)语义;“不合规证据软降级为待确认”另行讨论(§5)。
- 不改工具 JSON Schema 结构(不引入 `oneOf`/条件 schema);不改路由契约、传输层或 `[DONE]` 判定。
- 不放宽评测质量判据(仍要求至少 2 个 running 检查点落库并经独立评估);不改预算上限的数值口径。
- 不写实例系统层配置、不部署。
## 2. 现状与根因(零付费调查)
**A01 事实**(P1 第二段,收据 `a01-run-receipt.json` sha256 前缀 `7e3583a8`):第一批检查点被核心校验拒绝:`checkpoint.evidence: direct relation cannot have result`。离线重放(同一参数、`agent-system-core` 当前版本)得到:finding 0 为 `kind=material_record`、`mode=direct_relation`,却带 `total=2`、`phase=1` 两个前提与 `result=8`;去掉 evidence 后同一 finding 校验通过。评测随即按“检查点被拒即停”停止全部付费请求,样本终止。
| 位置 | 现行行为 | 问题 |
|---|---|---|
| Backend `internal/runtime/work_checkpoint.go` `checkpointDecl()` | 工具说明用一句话并列三种 mode;schema 为单一扁平对象:`mode` 枚举、`premises[].role` 四值枚举、`result` 可选 | schema 允许任意 mode 搭配任意 role/value/result;各 mode 的必填与禁填只在核心校验器里,模型看不到 |
| Backend `internal/runmgr/material.go`(材料任务提示) | “分析判断必须写 assumptions,并说明单位、算式或前提”;“数字或分期归属用 evidence 给依据” | 未说明 `material_record` 不需要 evidence;“算式或前提”推动模型给原样记载也补数字前提 |
| core `task_snapshot.go` `validateWorkFindingEvidence` | `direct_relation`:恰 1 个 `direct_relation` 前提、无 value、无 result;`difference`:total、phase 带值且有 result;`unresolved_relation`:total、phase、dependent 带值、无 result | 一条 finding 不合规即整次检查点被拒(工具说明称“宿主会改为待确认”,与此不一致) |
| Backend `runmgr/material_evidence.go` `normalizeMaterialEvidence` | 无 evidence 的 `material_record` 由宿主扩展为整句并按原句展示(原句语境不唯一则转待确认);无 evidence 的非审慎分析转待确认;`direct_relation` **仅当** kind 为 `material_record`、单一来源、短引即完整原句且原句明确表达关系时保留,其余一律转待确认;`difference` 要求 `result == total − phase`,否则转待确认 | 正常路径本已支持“原样记载不附 evidence”;分析类使用 `direct_relation` 必然被降级 |
| Backend `internal/server/work_checkpoint_eval_test.go` 评测流 | 输入中出现被拒的 `publish_work_checkpoint` 工具结果,或库内已落一次被拒结果 → 停止后续全部付费请求 | 比生产严格:生产(`runtime/sub_loop.go`)把错误结果回给模型并继续 |
**根因**:模型看不到“按 mode 的必填/禁填”,提示又鼓励给记载补前提;而评测对首个不合规零容忍。前者是主因,后者把一次可修正的偏差放大为整样本失败。
## 3. 方案
### 3.1 收紧工具说明与任务提示(生产可见,零付费)
1. **工具说明**(`checkpointDecl().Description`)改为分条写清:
- `material_record` 默认只引用原句,**不附 evidence**(宿主扩展为整句并按原句展示)。
- 唯一例外:原句本身明确表达关系(如“第二期包含3项依赖外部接口的需求”)时,`material_record` 可附 `direct_relation`:单一来源、短引即完整原句、恰 1 个前提 `{role:"direct_relation", sourceIndex:0}`,**不写 value、不写 result**;不满足这些条件宿主会转待确认。
- `analysis`/`provisional`/`needs_user` 里的数字或分期归属用以下两种 mode 之一(每条 finding 只选一种;不要用 `direct_relation`,宿主会转待确认):
- `difference`:前提 `total`、`phase` 各 1 个且都写 value;`result` 必须等于 total − phase。
- `unresolved_relation`:前提 `total`、`phase`、`dependent` 各 1 个且都写 value;**不写 result**。
- 各给一个最小 JSON 示例(字段名与 schema 一致)。
- 删除“宿主会把不能证明的判断改为待确认”中与校验器不一致的承诺,改为如实描述两类结果:(a) evidence 结构不符(如 `direct_relation` 带 result)会**整次被拒**并在工具结果给出原因,须按原因修正后重发;(b) 结构合规但宿主核不上的——前提数字与短引不符、`direct_relation` 不满足上述条件、原句语境不唯一、无 evidence 的非审慎分析、无 evidence 的 `needs_user`(原文会被替换为通用待确认文案)——该条转为待确认并在工具结果说明。
2. **Schema 字段说明**:仅在现有字段上补 `description`(`evidence`、`mode`、`premises`、`role`、`value`、`result`),写明上面的按 mode 约束;**不改类型、枚举、必填和嵌套结构**,不引入 `oneOf`(部分 OpenAI 兼容服务对复合 schema 支持不确定)。
3. **材料任务提示**(`material.go`):把“分析判断必须写 assumptions,并说明单位、算式或前提”限定为分析类;补一句“原样记载用 material_record,只附 sources,不附 evidence”;保留现有 12/8/3 反例。
**测试锚点**:
- 说明中的每个 JSON 示例在测试里抽出并经 `core.ValidateWorkCheckpoint`(套入最小合法检查点)校验通过;反例(`direct_relation` 带 result)断言被拒。
- 宿主归一层(`normalizeMaterialEvidence`)补断言:`analysis` + `direct_relation` 转 `needs_user`;`material_record` 无 evidence 保持原样记载——锁定说明所述的降级行为与宿主一致。
- 材料任务提示补断言:含“原样记载不附 evidence”的约定。
- 不以整段文案做快照断言,避免文案调整引起脆弱失败。
### 3.2 评测允许修正一次(仅评测代码)
- 现行“首个被拒即停”改为:**同一评测数据目录内,被拒的 `publish_work_checkpoint` 结果累计达 2 次即停**,原因串改为 `repeated rejected work checkpoint`;第 1 次被拒后允许继续请求,让模型按工具结果修正。
- 计数口径:库内按 `(conversation, run, callId)` 去重的被拒检查点数,与当前请求输入中被拒检查点工具消息数取较大值(同一次被拒同时出现在库与输入中只计 1);库不可读仍 fail closed(原因不变)。停止状态一旦触发即保持,后续请求一律拒绝;被拒判断优先于终态异常等其他原因。
- 已知边界:生产写 `tool.result` 事件时忽略写入错误(`runtime/sub_loop.go`),若事件未落库且两次被拒分属不同 run,可能少计一次、多放行一次请求;该请求仍受预算与单 run 调用上限约束,不阻塞本方案。
- 其余停止条件(预算、单 run 8 次调用、6 个 run、终态异常、用量不可得)全部不变;修正请求同样计入这些上限。
- 评测输出新增已批准标签(见 §6),不复用 `approved-r04-20261014`;该标签沿用 A01 30,000 token 画像上限与护栏。
- 评测调用记录写入每次调用前的被拒计数;因最后一次被拒可能发生在最后一次调用之后,**判定以收尾时库内去重计数为准**,供独立评估区分“一次修正后通过”与“首发即通过”。
### 3.3 不做的替代方案
| 方案 | 不选原因 |
|---|---|
| Schema 改为按 mode 的 `oneOf` | 模型侧约束最强,但 provider 对复合 schema 的支持未核实,可能整体拒绝工具或静默忽略;收益不确定且扩大验证面 |
| 核心校验器对不合规 evidence 软降级为待确认 | 改变核心契约与跨仓语义,影响生产展示;作为二次失败后的候选(§5) |
| 只改评测不改说明 | 修正一次能掩盖说明缺陷,但每个样本多花一轮,且生产用户仍会看到被拒结果 |
## 4. 影响面与风险
- 生产:仅改模型可见文案与 schema 字段说明;校验与归一逻辑不变。风险是模型更少附 evidence,结果更多显示为“待确认”——这是当前设计允许的保守方向。
- 评测:只放宽“被拒即停”的次数,预算与调用上限不变,最坏额外消耗受同一上限约束。
- 跨仓:仅 Backend 一个组件仓 PR + Dev gitlink 组合 PR;不触 core、Agent-Native、Frontend/UI。
## 5. 付费复验判定
| 结果 | 判定 | 后续 |
|---|---|---|
| 首发检查点即合规,≥2 个 running 检查点落库,最终正文完整且独立评估通过 | PASS | O2 收口;是否写实例契约配置另行决策 |
| 第 1 次被拒、修正后合规,其余同上 | PASS(附注:说明仍有缺口) | 记录被拒原因,评估是否需进一步收紧 |
| 被拒 2 次 | FAIL(证据合规) | 提出“核心软降级”方案另行评审,不追加付费 |
| 传输终态、预算、用量等其他原因停止 | 按原因归类,不计入本方案结论 | 按原因另行处理,不自动重跑 |
## 6. 付费复验资源提案(摘要)
- 范围:在本方案零付费部分合入 Dev 后,一次 A01(同一 MiniMax-M3 路由 + 已验证的限路由契约),不重跑、不扩样本。
- 上限:报告 token ≤ 60,000(`WORK_CHECKPOINT_REMAINING_TOKENS=60000`);单 run ≤ 8 次调用;被拒检查点达 2 次即停。
- 前置:用户重新放置路由文件(P1 结束后已删除);host 只核验权限、哈希、模型、主机、key 是否存在及三元组摘要,不读取或回显 key。
- 详细提案(逐项命令、收据路径、停止规则)放在源码外证据目录,人审时一并提供;本提案不随本文获批,须用户单独批准。
@@ -0,0 +1,50 @@
# O2 A01 P2 复验 C8 零付费复核
日期:2026-10-21;性质:只读复核(零付费、不改代码);对象:Backend `dfb31da20ab0276ea8e167cf26675f7ac95a1b26`、agent-native `2fc66be65201232ea38ba64d457efad5565d869d`、P2 收据 `ec02e80753db2f85…`、provider-records `add19bd1500127bb…`。
## 结论
P2 第 3 次请求的 C8 是**工具名未声明**,不是参数 schema 不符。第 3 次请求被宿主切到 final-only,从工具声明里移除了 `publish_work_checkpoint`;模型按任务指令(reading → crosscheck)仍去调用它。final-only 由两个条件同时触发:已保存 2 个检查点;30000 预算在 4000 输出上限下只够再留一次最终答复。即使只存了 1 个检查点,第 3 次也会是 final-only。根因是:A01 的预算只够两轮检查点请求加一次最终答复,任务指令却期望至少两个阶段再加最终答复;同时模型无视了 final-only 系统提醒。
所以「schema 不符时不丢弃整轮、交给工具层返回可修正错误」这一改法**不适用于本次失败**,即使实施也不会让 P2 通过。
## 依据
| # | 事实 | 来源 |
|---|---|---|
| E1 | 物料子 Agent 在 `SavedCheckpoints >= 2` 或 `remaining <= 2*(LastPrompt+OutputCap+margin)` 时进入 final-only | Backend `internal/runtime/material_final_reserve.go:21-30` |
| E2 | final-only 请求从 `requestDecls` 中删除 `publish_work_checkpoint`,并插入系统提醒「不再发布检查点」 | Backend `internal/runtime/sub_loop.go:125-148` |
| E3 | P2 在第 3 次请求前已保存 2 个检查点(均 ok、0 拒绝),且 `phaseId` 只出现过 `reading` | P2 数据库 events |
| E4 | 第 3 次请求的预算核算:已用 12317、上次 prompt 5926、outputCap 4000、limit 30000。剩余 17683 > 5926+1481,不触发 stop;17683 <= 2×(5926+4000+1481)=22814,且已存 2 个检查点,两个条件都触发 final-only | provider-records 第 1、2 次 usage 与 outputCap,按 E1 公式计算 |
| E5 | 第 3 次 prompt_tokens 为 5160,比第 2 次的 5926 少。历史变长、prompt 反而变短,与「删去大段检查点工具声明」一致。cache_read 从 5503 跌到 128,由删声明与在首条 system 后插入提醒共同造成 | provider-records 第 2、3 次;`sub_loop.go:137-148` |
| E6 | 第 3 次思考末句为「Now crosscheck phase.」,随后发出一个 1000 字节、可解析的工具调用;任务指令要求 crosscheck 阶段调用 `publish_work_checkpoint` | P2 数据库;Backend `internal/runmgr/material.go:306` |
| E7 | C8 = `callsDeclared` 失败,即工具名不在本次请求声明中,或顶层 `argschema.Validate` 失败 | agent-native `runtime/llm/route_contract.go` `check`/`callsDeclared` |
E1、E2、E4 是确定性代码路径,可以确定第 3 次请求未声明 `publish_work_checkpoint`。E5、E6 佐证模型调用的就是它。原始参数按设计不落盘,所以「调用名是 `publish_work_checkpoint`」属于高置信推断,未经原文核实。
## 对「schema 不符交给工具层」的只读评估
- **不解决本次问题**:本次是名称未声明,不经过 schema 分支。
- **schema 分支本身风险有限,但收益也有限**:`argschema.Validate` 只校验顶层,即必填项非空、顶层原始类型,以及在声明了 `properties` 时拒绝未声明的顶层键;不递归 `findings`。证据结构等深层错误本来就由工具层返回可修正结果。
- **改契约语义需要设计评审**:C8 是 O2 已批准契约的一项。放宽后,一次顶层违规会从「终态失败」变成「多一轮请求」,影响付费上限的估算。
- **即使没有契约也是终态**:final-only 阶段调用检查点时,`runTool` 返回可修正错误,但同时置位 `finalOnlyRejected`;下一次请求直接以 `checkpoint requested after final-only transition` 结束(`sub_loop.go:109-110`、`380-388`)。所以单改契约不能把它变成可恢复路径。
结论:不建议实施该改法。
## 可选修正(未实施,需单独授权)
| 选项 | 做法 | 影响 | 建议 |
|---|---|---|---|
| F1 | 让模型提前知道下一次是 final-only:宿主按 E1 的两个条件,判定下一次请求将是 final-only 时,在当次检查点工具结果里写明「下一步直接写最终结论,不再发布检查点」;任务指令把阶段要求改为与预算一致(例如第 2 次检查点即 crosscheck,或预算紧时允许跳过)。本次的 final-only 系统提醒已被无视,所以工具结果提示是否有效必须付费复验 | 只改 Backend 物料提示与工具结果文字,不动契约;零付费单测可以验证宿主侧行为 | **推荐** |
| F2 | final-only 阶段保留检查点声明,由工具层拒绝,并去掉 `finalOnlyRejected` 终止 | 避免 C8,但每次违规多一轮付费请求,可能击穿最终答复预留;缓存还受插入提醒影响,不会因此稳定 | 备选 |
| F3 | C8 对未声明名称改为可修正 | 削弱路由契约,与 O2 设计相悖 | 不建议 |
F1、F2 是否真能让模型遵守,只能靠付费复验判定;零付费单测只能证明宿主侧行为。
## P2 花费与结论(记录)
- 授权:用户批准一次 A01 付费复验(label `approved-o2-a01-evidence`,30000 tokens 守卫)。
- 花费:3 次付费请求,共 17955 tokens(5873 + 6444 + 5638),未超守卫。
- 结论:NOT_PASS。路由契约在第 3 次请求判 C8(`terminal_error`),子运行以 `tool call stream ended before [DONE]` 失败,outcome 为 partial。
- 证据合规目标已达成:2 个检查点,0 拒绝,`material_record` 均未附 evidence。
- 路由文件已在复验后删除;本次复核没有发起任何付费请求。
+6
View File
@@ -189,6 +189,12 @@ scripts/svc.sh <name> status # 单服务详情
- 显式给任何运行参数(或 `--config` 文件、`SVC_*` 变量)时**整体重建**参数;
- agent/static 换端口:`restart --port N`(进程与登记同步更新);
- postgres 换端口:`set --apply`(见 §7.4)或 `restart --port N` 同样有效。
- **超级管理员凭据文件(agent,`superadmin_envfile=`)**:在 deploy/start/restart 时传一次
`AGENT_SYSTEM_ENV_FILE=<文件>`,svc 先校验(普通文件、属当前用户、权限 0600 即无组/其他位,
不合格则在停旧服务前拒绝),再只把**绝对路径**记入 svc.env(从不读取、复制或打印文件内容);
此后裸 `restart`、`recover`/watchdog 拉起都复用该路径。本次环境显式设置的
`SUPER_ADMIN_OWNER`/`SUPER_ADMIN_PASSWORD`/`AGENT_SYSTEM_ENV_FILE` 优先于登记;
`AGENT_SYSTEM_ENV_FILE=`(空值)清除登记;登记文件已不存在时告警且不转发(serve 按缺凭据拒启)。
`status` 输出示例(postgres):