Merge refreshed docs/i18n-batch-cds-postmortem into docs/i18n-batch-rfc

# Conflicts:
#	.agents/notes/implemented/architecture/2026-06-11-content-block-vocabulary.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-11-content-block-vocabulary.zh.md
#	.agents/notes/implemented/architecture/2026-06-11-custom-schema-dsl.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-11-custom-schema-dsl.zh.md
#	.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.zh.md
#	.agents/notes/implemented/architecture/2026-06-11-event-sourced-sessions.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-11-event-sourced-sessions.zh.md
#	.agents/notes/implemented/architecture/2026-06-11-microkernel-event-taxonomy.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-11-microkernel-event-taxonomy.zh.md
#	.agents/notes/implemented/architecture/2026-06-11-runtime-arg-validation.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-11-runtime-arg-validation.zh.md
#	.agents/notes/implemented/architecture/2026-06-11-structured-error-taxonomy.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-11-structured-error-taxonomy.zh.md
#	.agents/notes/implemented/architecture/2026-06-11-tool-schemas-in-prompt-assembly.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-11-tool-schemas-in-prompt-assembly.zh.md
#	.agents/notes/implemented/architecture/2026-06-13-capability-seams.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-13-capability-seams.zh.md
#	.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.zh.md
#	.agents/notes/implemented/architecture/2026-06-14-session-persistence.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-14-session-persistence.zh.md
#	.agents/notes/implemented/architecture/2026-06-15-turn-enclosure-invariant.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-15-turn-enclosure-invariant.zh.md
#	.agents/notes/implemented/architecture/2026-06-17-filesystem-capability-seam.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-17-filesystem-capability-seam.zh.md
#	.agents/notes/implemented/architecture/2026-06-18-agent-lifecycle-and-ownership-seams.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-18-agent-lifecycle-and-ownership-seams.zh.md
#	.agents/notes/implemented/architecture/2026-06-18-session-surface.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-18-session-surface.zh.md
#	.agents/notes/implemented/architecture/2026-06-18-shared-persistence-write-coordinator.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-18-shared-persistence-write-coordinator.zh.md
#	.agents/notes/implemented/architecture/2026-06-20-branded-ids.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-20-branded-ids.zh.md
#	.agents/notes/implemented/architecture/2026-06-20-extract-example-app-packages.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-20-extract-example-app-packages.zh.md
#	.agents/notes/implemented/architecture/2026-06-20-package-hierarchy.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-20-package-hierarchy.md
#	.agents/notes/implemented/architecture/2026-06-20-package-hierarchy.zh.md
#	.agents/notes/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.zh.md
#	.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.zh.md
#	.agents/notes/implemented/architecture/2026-06-26-file-context-as-event-gate.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-26-file-context-as-event-gate.zh.md
#	.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.zh.md
#	.agents/notes/implemented/architecture/2026-06-30-event-domain-semantics.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-30-event-domain-semantics.zh.md
#	.agents/notes/implemented/architecture/2026-07-02-fs-per-session-cwd.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-02-fs-per-session-cwd.zh.md
#	.agents/notes/implemented/architecture/2026-07-02-result-time-applied-hunk-diffs.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-02-result-time-applied-hunk-diffs.zh.md
#	.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.zh.md
#	.agents/notes/implemented/architecture/2026-07-03-filesystem-directory-listing-seam.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-03-filesystem-directory-listing-seam.zh.md
#	.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.zh.md
#	.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.zh.md
#	.agents/notes/implemented/architecture/2026-07-05-subagent-provider-lifecycle-events.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-05-subagent-provider-lifecycle-events.zh.md
#	.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.zh.md
#	.agents/notes/implemented/architecture/2026-07-07-tool-call-timeout-policy.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-07-tool-call-timeout-policy.zh.md
#	.agents/notes/implemented/architecture/2026-07-08-agent-scope-contexts.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-08-agent-scope-contexts.zh.md
#	.agents/notes/implemented/architecture/2026-07-12-agent-scope-runtime-design.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-12-agent-scope-runtime-design.zh.md
#	.agents/notes/implemented/feature/2026-06-14-acp-agent-client-protocol.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-14-acp-agent-client-protocol.zh.md
#	.agents/notes/implemented/feature/2026-06-14-acp-multi-session.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-14-acp-multi-session.zh.md
#	.agents/notes/implemented/feature/2026-06-15-code-mode.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-15-code-mode.zh.md
#	.agents/notes/implemented/feature/2026-06-17-filesystem-tool-schemas.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-17-filesystem-tool-schemas.zh.md
#	.agents/notes/implemented/feature/2026-06-18-acp-terminal-and-tool-rendering.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-18-acp-terminal-and-tool-rendering.zh.md
#	.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.zh.md
#	.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md
#	.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.zh.md
#	.agents/notes/implemented/feature/2026-06-22-acp-subagent-backend.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-22-acp-subagent-backend.zh.md
#	.agents/notes/implemented/feature/2026-06-25-ask-user-question.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-25-ask-user-question.zh.md
#	.agents/notes/implemented/feature/2026-06-29-todo-write-tool.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-29-todo-write-tool.zh.md
#	.agents/notes/implemented/feature/2026-06-30-hook-bridges.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-30-hook-bridges.zh.md
#	.agents/notes/implemented/feature/2026-06-30-hook-protocol-lib.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-30-hook-protocol-lib.zh.md
#	.agents/notes/implemented/feature/2026-06-30-interception-seams.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-30-interception-seams.zh.md
#	.agents/notes/implemented/feature/2026-06-30-session-store-fork-api.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-30-session-store-fork-api.zh.md
#	.agents/notes/implemented/feature/2026-06-30-subagent-observe-enrich.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-30-subagent-observe-enrich.zh.md
#	.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.zh.md
#	.agents/notes/implemented/feature/2026-07-05-skill-system.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-05-skill-system.zh.md
#	.agents/notes/implemented/feature/2026-07-06-approval-seam.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-06-approval-seam.zh.md
#	.agents/notes/implemented/feature/2026-07-06-explicit-tool-order.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-06-explicit-tool-order.zh.md
#	.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md
#	.agents/notes/implemented/feature/2026-07-07-mcp-client-plugin.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-07-mcp-client-plugin.zh.md
#	.agents/notes/implemented/feature/2026-07-07-session-prefix.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-07-session-prefix.zh.md
#	.agents/notes/implemented/feature/2026-07-08-repeat-tool-guard.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-08-repeat-tool-guard.zh.md
#	.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.zh.md
#	.agents/notes/implemented/feature/2026-07-10-session-query-service.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-10-session-query-service.zh.md
#	.agents/notes/implemented/feature/2026-07-12-subagent-persona-tool-filter-and-depth.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-12-subagent-persona-tool-filter-and-depth.zh.md
#	.agents/notes/implemented/process/2026-06-11-doc-sync-enforcement.i18n.yaml
#	.agents/notes/implemented/process/2026-06-11-doc-sync-enforcement.zh.md
#	.agents/notes/implemented/process/2026-06-11-quality-gates.i18n.yaml
#	.agents/notes/implemented/process/2026-06-11-quality-gates.md
#	.agents/notes/implemented/process/2026-06-11-quality-gates.zh.md
#	.agents/notes/implemented/process/2026-06-11-tsdown-over-dumble.i18n.yaml
#	.agents/notes/implemented/process/2026-06-11-tsdown-over-dumble.zh.md
#	.agents/notes/implemented/process/2026-06-11-vendor-cordis-as-source.i18n.yaml
#	.agents/notes/implemented/process/2026-06-11-vendor-cordis-as-source.zh.md
#	.agents/notes/implemented/process/2026-06-16-pnpm-over-yarn.i18n.yaml
#	.agents/notes/implemented/process/2026-06-16-pnpm-over-yarn.zh.md
#	.agents/notes/implemented/process/2026-06-17-ts-build-config.i18n.yaml
#	.agents/notes/implemented/process/2026-06-17-ts-build-config.zh.md
#	.agents/notes/implemented/process/2026-06-18-markdown-cross-link-lint.i18n.yaml
#	.agents/notes/implemented/process/2026-06-18-markdown-cross-link-lint.zh.md
#	.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.i18n.yaml
#	.agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.zh.md
#	.agents/notes/implemented/process/2026-06-20-generated-cordis-catalog.i18n.yaml
#	.agents/notes/implemented/process/2026-06-20-generated-cordis-catalog.zh.md
#	.agents/notes/implemented/process/2026-06-20-rfc-classification.i18n.yaml
#	.agents/notes/implemented/process/2026-06-20-rfc-classification.zh.md
#	.agents/notes/implemented/process/2026-07-02-tool-schema-catalog.i18n.yaml
#	.agents/notes/implemented/process/2026-07-02-tool-schema-catalog.zh.md
#	.agents/notes/implemented/process/2026-07-03-documentation-graph-atlas.i18n.yaml
#	.agents/notes/implemented/process/2026-07-03-documentation-graph-atlas.zh.md
#	.agents/notes/implemented/process/2026-07-04-cordis-jsdoc-completeness-gate.i18n.yaml
#	.agents/notes/implemented/process/2026-07-04-cordis-jsdoc-completeness-gate.zh.md
#	.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml
#	.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.zh.md
#	.agents/notes/implemented/process/2026-07-04-generate-rfc-index-tables.i18n.yaml
#	.agents/notes/implemented/process/2026-07-04-generate-rfc-index-tables.zh.md
#	.agents/notes/implemented/process/2026-07-04-persistence-log-catalog.i18n.yaml
#	.agents/notes/implemented/process/2026-07-04-persistence-log-catalog.zh.md
#	.agents/notes/implemented/process/2026-07-05-uniform-rfc-format.i18n.yaml
#	.agents/notes/implemented/process/2026-07-05-uniform-rfc-format.zh.md
#	.agents/notes/implemented/process/2026-07-06-export-surface-jsdoc-gate.i18n.yaml
#	.agents/notes/implemented/process/2026-07-06-export-surface-jsdoc-gate.zh.md
#	.agents/notes/implemented/process/2026-07-06-generated-config-catalog.i18n.yaml
#	.agents/notes/implemented/process/2026-07-06-generated-config-catalog.zh.md
#	.agents/notes/implemented/process/2026-07-06-node-engine-floor.i18n.yaml
#	.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md
#	.agents/notes/implemented/process/2026-07-06-parallel-github-ci-gates.i18n.yaml
#	.agents/notes/implemented/process/2026-07-06-parallel-github-ci-gates.zh.md
#	.agents/notes/implemented/process/2026-07-06-parallel-pre-push-gates.i18n.yaml
#	.agents/notes/implemented/process/2026-07-06-parallel-pre-push-gates.zh.md
#	.agents/notes/implemented/process/2026-07-10-readme-known-limitations-gate.i18n.yaml
#	.agents/notes/implemented/process/2026-07-10-readme-known-limitations-gate.zh.md
#	.agents/notes/implemented/process/2026-07-12-package-model-experience-contract.i18n.yaml
#	.agents/notes/implemented/process/2026-07-12-package-model-experience-contract.zh.md
#	.agents/notes/implemented/simplification/2026-06-19-drop-mutable-session-summary.i18n.yaml
#	.agents/notes/implemented/simplification/2026-06-19-drop-mutable-session-summary.zh.md
#	.agents/notes/implemented/simplification/2026-06-20-collapse-trace-only-session-events.i18n.yaml
#	.agents/notes/implemented/simplification/2026-06-20-collapse-trace-only-session-events.zh.md
#	.agents/notes/implemented/simplification/2026-06-20-drop-unconsumed-llm-adapter-change-event.i18n.yaml
#	.agents/notes/implemented/simplification/2026-06-20-drop-unconsumed-llm-adapter-change-event.zh.md
#	.agents/notes/implemented/simplification/2026-06-20-drop-unconsumed-llm-assembled-surfaces.i18n.yaml
#	.agents/notes/implemented/simplification/2026-06-20-drop-unconsumed-llm-assembled-surfaces.zh.md
#	.agents/notes/implemented/simplification/2026-06-20-prune-dead-seam-methods.i18n.yaml
#	.agents/notes/implemented/simplification/2026-06-20-prune-dead-seam-methods.md
#	.agents/notes/implemented/simplification/2026-06-20-prune-dead-seam-methods.zh.md
#	.agents/notes/implemented/simplification/2026-06-20-public-agent-stop-surface.i18n.yaml
#	.agents/notes/implemented/simplification/2026-06-20-public-agent-stop-surface.zh.md
#	.agents/notes/implemented/simplification/2026-06-20-remove-agent-boundary-mirror-events.i18n.yaml
#	.agents/notes/implemented/simplification/2026-06-20-remove-agent-boundary-mirror-events.zh.md
#	.agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.i18n.yaml
#	.agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.zh.md
#	.agents/notes/implemented/simplification/2026-07-02-remove-stream-chunk-mirror.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-02-remove-stream-chunk-mirror.zh.md
#	.agents/notes/implemented/simplification/2026-07-04-drop-image-content-block.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-04-drop-image-content-block.zh.md
#	.agents/notes/implemented/simplification/2026-07-04-drop-inert-request-knobs.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-04-drop-inert-request-knobs.zh.md
#	.agents/notes/implemented/simplification/2026-07-04-drop-unconsumed-web-observation-surface.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-04-drop-unconsumed-web-observation-surface.zh.md
#	.agents/notes/implemented/simplification/2026-07-04-fold-stdio-ui-helper.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-04-fold-stdio-ui-helper.md
#	.agents/notes/implemented/simplification/2026-07-04-fold-stdio-ui-helper.zh.md
#	.agents/notes/implemented/simplification/2026-07-04-prune-producerless-vocabulary-variants.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-04-prune-producerless-vocabulary-variants.zh.md
#	.agents/notes/implemented/simplification/2026-07-04-prune-write-only-fs-surface.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-04-prune-write-only-fs-surface.zh.md
#	.agents/notes/implemented/simplification/2026-07-04-remove-agent-steering-mirror.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-04-remove-agent-steering-mirror.zh.md
#	.agents/notes/implemented/simplification/2026-07-04-share-app-bin-boot-glue.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-04-share-app-bin-boot-glue.zh.md
#	.agents/notes/implemented/simplification/2026-07-04-tighten-hook-protocol-contract.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-04-tighten-hook-protocol-contract.zh.md
#	.agents/notes/implemented/simplification/2026-07-04-trim-acp-bridge-unreachable-surface.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-04-trim-acp-bridge-unreachable-surface.zh.md
#	.agents/notes/implemented/simplification/2026-07-12-drop-unconsumed-skill-provider-events.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-12-drop-unconsumed-skill-provider-events.zh.md
#	.agents/notes/implemented/simplification/2026-07-12-prune-unused-web-seam-fields.i18n.yaml
#	.agents/notes/implemented/simplification/2026-07-12-prune-unused-web-seam-fields.zh.md
#	.agents/notes/implemented/testing/2026-06-11-property-based-testing.i18n.yaml
#	.agents/notes/implemented/testing/2026-06-11-property-based-testing.zh.md
#	.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.i18n.yaml
#	.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.zh.md
#	.agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.i18n.yaml
#	.agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.zh.md
#	.agents/notes/implemented/testing/2026-06-20-remove-redundant-snapshot-log-goldens.i18n.yaml
#	.agents/notes/implemented/testing/2026-06-20-remove-redundant-snapshot-log-goldens.zh.md
#	.agents/notes/implemented/testing/2026-06-22-fork-child-replay-seed-boundary.i18n.yaml
#	.agents/notes/implemented/testing/2026-06-22-fork-child-replay-seed-boundary.zh.md
#	.agents/notes/implemented/testing/2026-06-22-fork-snapshot-scenarios.i18n.yaml
#	.agents/notes/implemented/testing/2026-06-22-fork-snapshot-scenarios.zh.md
#	.agents/notes/implemented/testing/2026-06-22-subagent-snapshot-replay.i18n.yaml
#	.agents/notes/implemented/testing/2026-06-22-subagent-snapshot-replay.zh.md
#	.agents/notes/implemented/testing/2026-07-04-hook-snapshot-matrix.i18n.yaml
#	.agents/notes/implemented/testing/2026-07-04-hook-snapshot-matrix.zh.md
#	.agents/notes/implemented/testing/2026-07-04-single-source-acp-replay-config.i18n.yaml
#	.agents/notes/implemented/testing/2026-07-04-single-source-acp-replay-config.zh.md
#	.agents/notes/implemented/testing/2026-07-06-pin-request-header-content-in-one-scenario.i18n.yaml
#	.agents/notes/implemented/testing/2026-07-06-pin-request-header-content-in-one-scenario.zh.md
#	.agents/notes/implemented/testing/2026-07-08-shared-acp-snapshot-package.i18n.yaml
#	.agents/notes/implemented/testing/2026-07-08-shared-acp-snapshot-package.zh.md
#	.agents/notes/proposed/architecture/2026-06-16-typed-event-schemas.i18n.yaml
#	.agents/notes/proposed/architecture/2026-06-16-typed-event-schemas.zh.md
#	.agents/notes/proposed/architecture/2026-06-20-generic-long-running-tool-runtime.i18n.yaml
#	.agents/notes/proposed/architecture/2026-06-20-generic-long-running-tool-runtime.zh.md
#	.agents/notes/proposed/feature/2026-06-30-pre-tool-input-rewrite.i18n.yaml
#	.agents/notes/proposed/feature/2026-06-30-pre-tool-input-rewrite.zh.md
#	.agents/notes/proposed/feature/2026-07-07-claude-code-and-codex-subagent-backends.i18n.yaml
#	.agents/notes/proposed/feature/2026-07-07-claude-code-and-codex-subagent-backends.zh.md
#	.agents/notes/proposed/feature/2026-07-08-interactive-side-sessions.i18n.yaml
#	.agents/notes/proposed/feature/2026-07-08-interactive-side-sessions.zh.md
#	.agents/notes/proposed/feature/2026-07-10-sqlite-session-query-provider.i18n.yaml
#	.agents/notes/proposed/feature/2026-07-10-sqlite-session-query-provider.zh.md
#	.agents/notes/proposed/feature/2026-07-13-stream-workflow-progress-through-tool-calls.i18n.yaml
#	.agents/notes/proposed/feature/2026-07-13-stream-workflow-progress-through-tool-calls.zh.md
#	.agents/notes/proposed/process/2026-06-11-api-extractor-reports.i18n.yaml
#	.agents/notes/proposed/process/2026-06-11-api-extractor-reports.md
#	.agents/notes/proposed/process/2026-06-11-api-extractor-reports.zh.md
#	.agents/notes/proposed/process/2026-06-11-architectural-conformance.i18n.yaml
#	.agents/notes/proposed/process/2026-06-11-architectural-conformance.zh.md
#	.agents/notes/proposed/process/2026-06-11-supply-chain-and-vendor-drift.i18n.yaml
#	.agents/notes/proposed/process/2026-06-11-supply-chain-and-vendor-drift.zh.md
#	.agents/notes/proposed/process/2026-06-20-discover-package-inventory.i18n.yaml
#	.agents/notes/proposed/process/2026-06-20-discover-package-inventory.zh.md
#	.agents/notes/proposed/simplification/2026-06-20-unify-agent-and-session-id.i18n.yaml
#	.agents/notes/proposed/simplification/2026-06-20-unify-agent-and-session-id.zh.md
#	.agents/notes/proposed/simplification/2026-07-04-prune-dead-core-spine-surface.i18n.yaml
#	.agents/notes/proposed/simplification/2026-07-04-prune-dead-core-spine-surface.zh.md
#	.agents/notes/proposed/simplification/2026-07-12-simplify-session-log-representation.i18n.yaml
#	.agents/notes/proposed/simplification/2026-07-12-simplify-session-log-representation.zh.md
#	.agents/notes/proposed/testing/2026-06-11-deterministic-and-stress-testing.i18n.yaml
#	.agents/notes/proposed/testing/2026-06-11-deterministic-and-stress-testing.zh.md
#	.agents/notes/proposed/testing/2026-06-11-mutation-testing.i18n.yaml
#	.agents/notes/proposed/testing/2026-06-11-mutation-testing.zh.md
#	.agents/notes/rejected/architecture/2026-06-11-immutable-public-surfaces.i18n.yaml
#	.agents/notes/rejected/architecture/2026-06-11-immutable-public-surfaces.zh.md
#	.agents/notes/rejected/architecture/2026-06-20-providerless-example-base.i18n.yaml
#	.agents/notes/rejected/architecture/2026-06-20-providerless-example-base.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-assembled-assistant-messages-only.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-assembled-assistant-messages-only.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-drop-acp-session-load.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-drop-acp-session-load.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-drop-acp-terminal-meta.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-drop-acp-terminal-meta.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-drop-bash-output-spill-files.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-drop-bash-output-spill-files.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-drop-durable-step-boundaries.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-drop-durable-step-boundaries.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-drop-unused-session-lineage.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-drop-unused-session-lineage.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-fold-session-persistence-interface.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-fold-session-persistence-interface.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-generic-tool-rendering.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-generic-tool-rendering.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-retire-mid-turn-steering.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-retire-mid-turn-steering.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-single-session-acp-bridge.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-single-session-acp-bridge.zh.md
#	.agents/notes/rejected/simplification/2026-06-20-truncate-interrupted-turns.i18n.yaml
#	.agents/notes/rejected/simplification/2026-06-20-truncate-interrupted-turns.zh.md
#	.agents/notes/rejected/simplification/2026-07-04-prune-unimplemented-subagent-vocabulary.i18n.yaml
#	.agents/notes/rejected/simplification/2026-07-04-prune-unimplemented-subagent-vocabulary.zh.md
#	.agents/notes/rejected/simplification/2026-07-12-collapse-workflow-to-foreground-core.i18n.yaml
#	.agents/notes/rejected/simplification/2026-07-12-collapse-workflow-to-foreground-core.zh.md
#	.agents/notes/rejected/simplification/2026-07-12-prune-unused-skill-registry-surface.i18n.yaml
#	.agents/notes/rejected/simplification/2026-07-12-prune-unused-skill-registry-surface.zh.md
#	docs/rfc/implemented/architecture/2026-06-18-agent-lifecycle-and-ownership-seams.md
#	docs/rfc/implemented/architecture/2026-06-18-session-surface.md
#	docs/rfc/implemented/architecture/2026-06-20-branded-ids.md
#	docs/rfc/implemented/architecture/2026-07-02-fs-per-session-cwd.md
#	docs/rfc/implemented/feature/2026-06-18-compaction-capability-seam.md
#	docs/rfc/implemented/feature/2026-07-07-session-prefix.md
#	docs/rfc/implemented/process/2026-06-20-rfc-classification.md
#	docs/rfc/implemented/process/2026-07-04-generate-rfc-index-tables.md
#	docs/rfc/implemented/process/2026-07-05-uniform-rfc-format.md
#	docs/rfc/implemented/process/2026-07-06-parallel-github-ci-gates.md
#	docs/rfc/implemented/process/2026-07-06-parallel-pre-push-gates.md
#	docs/rfc/implemented/process/2026-07-12-package-model-experience-contract.md
#	docs/rfc/implemented/simplification/2026-06-20-remove-agent-boundary-mirror-events.md
#	docs/rfc/implemented/simplification/2026-07-04-prune-producerless-vocabulary-variants.md
#	docs/rfc/implemented/testing/2026-06-20-remove-redundant-snapshot-log-goldens.md
#	docs/rfc/implemented/testing/2026-07-08-shared-acp-snapshot-package.md
#	docs/rfc/proposed/architecture/2026-06-20-generic-long-running-tool-runtime.md
#	docs/rfc/proposed/simplification/2026-06-20-unify-agent-and-session-id.md
#	docs/rfc/proposed/simplification/2026-07-12-simplify-session-log-representation.md
#	docs/rfc/rejected/simplification/2026-07-04-prune-unimplemented-subagent-vocabulary.md
#	scripts/translation-pairing.manifest.json
This commit is contained in:
Tianyi Cui
2026-07-22 23:07:14 +08:00
2944 changed files with 205120 additions and 33445 deletions

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-11-immutable-public-surfaces.md: c807b036bb57bd5e64290fd59bf422c4732e9080
2026-06-11-immutable-public-surfaces.zh.md: 9ff40436915e194848ba163ed80fefe84a1b3da4

View File

@@ -0,0 +1,29 @@
# Agent Note: Deep-readonly public surfaces
Status: rejected — the pervasive `DeepReadonly<T>` type flip is replaced by source-owned runtime immutability in `Session` plus relational development assertions. See [source-owned session immutability and dev-mode invariants](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md).
English | [中文](2026-06-11-immutable-public-surfaces.zh.md)
## Problem
The rejected proposal targeted an ownership hole that a `readonly SessionEvent[]` type alone cannot close: its elements remain mutable at runtime, so a cast or plain JavaScript can rewrite nested history. The implemented design closes that hole in `Session` by materializing and deep-freezing every accepted event and returning frozen array snapshots. In-flight prompt waterfalls remain intentionally transformable, so immutability is an ownership boundary rather than a blanket type rule.
## Proposal
> **Implemented differently — see the Status line and [source-owned session immutability and dev-mode invariants](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md).** The `DeepReadonly<T>` design below is rejected as written: it is compile-only, noisy across consumers, and castable. `Session` instead snapshots and deep-freezes accepted events and public log snapshots in every composition; `deriveMessages()` returns detached frozen projections; the development plugin checks cross-record and cross-seam relationships.
Make immutability part of the type where mutation is corruption:
- `SessionEvent` data becomes `DeepReadonly` on the way OUT of a session (`events`, `session/event` listeners); `append()` keeps taking plain mutable input. A `DeepReadonly<T>` utility type lands in dsh-llm next to the brand/never helpers.
- `deriveMessages()` returns deep-readonly messages; the loop clones before handing a mutable request to the `agent/request` waterfall (mutation there is sanctioned — the clone makes the boundary explicit and cheap, once per step).
- `PromptAssembly` stays mutable through its waterfall (sanctioned) but the registry's internal section list is cloned per assembly (already true).
## Plan
Introduce `DeepReadonly`, flip the session read paths, and fix the resulting compile errors in consumers.
## Risks
`DeepReadonly` types can produce noisy errors at waterfall boundaries where mutation IS the API — keep the mutable/readonly boundary exactly at "logged vs in-flight" and document it in the session README.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,29 @@
# RFC: 深度只读的公开接口
Status: rejected — the pervasive `DeepReadonly<T>` type flip is replaced by source-owned runtime immutability in `Session` plus relational development assertions. See [source-owned session immutability and dev-mode invariants](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md).
[English](2026-06-11-immutable-public-surfaces.md) | 中文
## 问题
被否决的提案针对的是一个所有权漏洞:仅靠 `readonly SessionEvent[]` 类型无法封堵该漏洞,因为其元素在运行时仍然可变,类型强制转换或纯 JavaScript 代码可以改写嵌套的历史记录。已实现的设计在 `Session` 中封堵了这一漏洞:对每个被接受的事件进行物化并深度冻结,返回冻结的数组快照。进行中的 prompt waterfall瀑布式事件有意保持可变换因此不可变性是一条所有权边界而非一条全局类型规则。
## 提案
> **实际采用了不同的实现方式——见 Status 行与[源拥有的会话不可变性与开发模式不变式](../../implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md)。** 下文的 `DeepReadonly<T>` 设计已被否决:它仅在编译期生效、对消费方噪音大、且可被强制转换绕过。`Session` 改为在每次组合中对已接受的事件和公开日志快照进行快照与深度冻结;`deriveMessages()` 返回分离的冻结投影;开发插件检查跨记录与跨 seam 的关系。
在类型层面为「突变即损坏」的场景引入不可变性:
- `SessionEvent` 数据在从会话输出时(`events``session/event` 监听器)变为 `DeepReadonly``append()` 仍接受普通可变输入。一个 `DeepReadonly<T>` 工具类型放在 dsh-llm 中,与 brand/never 辅助类型相邻。
- `deriveMessages()` 返回深度只读的消息agent loop智能体循环在将可变请求交给 `agent/request` waterfall 之前先克隆(该处的突变是被允许的——克隆使边界显式且代价低廉,每个步骤仅一次)。
- `PromptAssembly` 在其 waterfall 流经期间保持可变(被允许),但注册表内部的 section 列表在每次组装时被克隆(已有此行为)。
## 计划
引入 `DeepReadonly`,翻转会话的读取路径,并修复消费方中由此产生的编译错误。
## 风险
`DeepReadonly` 类型在 waterfall 边界处(突变本身就是 API 的地方)可能产生噪音较大的错误。应将可变/只读边界精确地划在「已记录 vs 进行中」,并在 session README 中加以说明。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-providerless-example-base.md: ca9d391172067aca980b5b0fbc17141320c6839e
2026-06-20-providerless-example-base.zh.md: fb64e4a0b5295e0d56e7cd598c230f56219ba77e

View File

@@ -0,0 +1,31 @@
# Agent Note: Make the shared example base providerless
Status: rejected — superseded by [Extract example apps into packages](../../implemented/architecture/2026-06-20-extract-example-app-packages.md), which moves the spine into a `dsh-agent-spine-demo` bundle and deletes the `base*.yml` files, so there is no shared base YAML left to rename.
English | [中文](2026-06-20-providerless-example-base.zh.md)
## Problem
The examples had two shared base files: `examples/base-core.yml` was providerless, while `examples/base.yml` included that core plus the real `llm-deepseek` adapter. Snapshot replay needs the providerless core with `llm-replay`, because loading the real adapter without a key throws. The normal demos need the real adapter. The result was a naming inversion: the file named `base.yml` was not the reusable base for all examples, while the true base was `base-core.yml`.
The split was understandable, but it made every config explanation longer. It also led to awkward test setup like a keyless smoke test carrying a dummy API key so an adapter could boot even though the model is not called.
## Proposal
Rename the providerless core to `examples/base.yml` and make adapter selection explicit in each concrete example. The coding and ACP real configs add a tiny `llm-deepseek` include or local block; snapshot config adds `llm-replay`. Delete `examples/base-core.yml`.
The shared base should contain only provider-neutral services and tools: `llm`, sessions, system prompt, tools, agents, invariants, bash executor, and bash tool schemas. Anything that chooses a model provider belongs at the leaf config.
## Acceptance criteria
- `examples/base.yml` is providerless.
- `examples/base-core.yml` is deleted.
- Real demo configs explicitly add the DeepSeek adapter.
- Snapshot replay config includes the same providerless base and its replay adapter.
- The [examples README](../../../../examples/README.md), example-specific READMEs, and Agent Note references stop explaining "base = base-core plus adapter".
## What we give up
Real demos lose one layer of convenience: each must opt into the adapter. That is the right default for examples, because adapter choice is the variable part and providerless wiring is the shared product core.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,31 @@
# RFC: 使共享示例基础配置与提供方无关
Status: rejected — superseded by [Extract example apps into packages](../../implemented/architecture/2026-06-20-extract-example-app-packages.md), which moves the spine into a `dsh-agent-spine-demo` bundle and deletes the `base*.yml` files, so there is no shared base YAML left to rename.
[English](2026-06-20-providerless-example-base.md) | 中文
## 问题
示例曾有两个共享基础文件:`examples/base-core.yml` 与提供方无关,而 `examples/base.yml` 在该核心基础上加入了真实的 `llm-deepseek` 适配器。快照回放需要与提供方无关的核心配合 `llm-replay` 使用,因为在没有密钥的情况下加载真实适配器会抛出异常。常规演示则需要真实适配器。结果是命名与实际含义倒挂:名为 `base.yml` 的文件并非所有示例可复用的基础,而真正的基础反倒是 `base-core.yml`
这种拆分可以理解,但它让每次解释配置都变得更冗长。它还导致了别扭的测试搭建方式,例如无密钥冒烟测试不得不携带一个虚拟 API key仅仅为了让适配器能启动——尽管模型根本不会被调用。
## 提案
将与提供方无关的核心重命名为 `examples/base.yml`,让适配器选择在每个具体示例中显式声明。编码和 ACP 真实配置添加一小段 `llm-deepseek` include 或本地块;快照配置添加 `llm-replay`。删除 `examples/base-core.yml`
共享基础应仅包含提供方无关的服务与工具:`llm`、会话、系统提示词、工具、agent、不变式、bash 执行器和 bash 工具 schema。任何涉及模型提供方选择的内容都应放在叶子配置中。
## 验收标准
- `examples/base.yml` 与提供方无关。
- `examples/base-core.yml` 已删除。
- 真实演示配置显式添加 DeepSeek 适配器。
- 快照回放配置 include 同一个与提供方无关的基础,并加入其回放适配器。
- [examples README](../../../../examples/README.md)、各示例 README 及 RFC 引用不再解释「base = base-core 加适配器」。
## 放弃了什么
真实演示失去了一层便利:每个演示都必须显式引入适配器。对于示例而言这是正确的默认行为,因为适配器选择是可变部分,而与提供方无关的接线才是共享的产品核心。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-07-04-generate-rfc-index-tables.md: 5d5ae258583e0fae7dcb44a23c4b41446bd233d2
2026-07-04-generate-rfc-index-tables.zh.md: fa611e4337075501fe097fe8fc68a878a855a7c0

View File

@@ -0,0 +1,36 @@
# Agent Note: Generate the Agent Note index tables
Status: rejected — a centralized generated list is merge-prone and adds little discovery value
## Problem
Per-lifecycle/per-class tables would list facts that are fully derivable: an Agent Note's path encodes lifecycle and class, its filename encodes the first-proposed date, and its H1 carries the title. A hand-maintained copy of those facts would also be a high-contention docs hotspot because concurrent Agent Note branches append rows to the same few lines. [The classification Agent Note](../../implemented/process/2026-06-20-agent-note-classification.md) makes the tree itself authoritative.
## Proposal
Keep the curated prose and generate the list as a fully generated `.agents/notes/INDEX.md`. A shared `scripts/agent-note-index.ts` module would own both the tree walker and the renderer. Two thin consumers would share it:
- `scripts/gen-agent-note-index.ts` (`pnpm run gen-agent-note-index`) would rewrite INDEX.md in full from the tree.
- `scripts/verify-agent-note-classification.ts` would check structure and assert that the committed INDEX.md byte-matches a fresh render.
Adding, moving, or deleting an Agent Note would mean editing the Agent Note file and running the generator.
## Alternatives considered
### Why not marker-delimited regions inside README.md?
Marker-delimited tables inside README.md would mix generated and curated text, requiring splice mechanics and protection for the surrounding contract. A dedicated generated file would at least keep those concerns separate.
### Why not the verifier-only model?
It catches mistakes but still makes every proposal edit a shared hotspot in a hand-maintained table. The author has already named and placed the file, so the index copy adds no information. This is the same hand-list-versus-derivation judgment the [package-inventory proposal](../../proposed/process/2026-06-20-discover-package-inventory.md) applies to tsconfig references and knip stanzas.
## Consequences
- The generated file would be explicit and contain no curated region.
- A malformed or missing H1 would be a hard error because the H1 supplies each row title.
- Concurrent branches would still modify the same committed artifact, even if conflicts could be resolved by rerunning the generator.
## Related
The implemented [no-index decision](../../implemented/process/2026-07-19-remove-generated-agent-note-index.md) keeps the tree and repository search as the discovery mechanisms.

View File

@@ -0,0 +1,34 @@
# RFC: 生成 RFC 索引表
Status: implemented
[English](2026-07-04-generate-rfc-index-tables.md) | 中文
## 问题
RFC 索引中按生命周期/按分类的表格所列信息完全可以推导RFC 的路径编码了生命周期与分类文件名编码了首次提出日期H1 标题承载了标题文本。这些信息的手工维护副本也是仓库中冲突最频繁的文档热点:每一波提案都在同几行后追加新行,因此并发的 RFC 分支恰好在此处冲突,而其他地方完全一致;每次冲突都要手工合并那些文件系统本已知晓的行。[分类 RFC](2026-06-20-rfc-classification.md) 最初为了可策展性而保留手写索引,但 README 中真正需要策展的是行文,而行文从不冲突;冲突的只有机械表格。
## 决策
保留策展行文;生成列表。表格位于 [`docs/rfc/INDEX.md`](../../INDEX.md),是一个**完全生成的文件**——策展行文留在 README.md 中README.md 不包含任何索引行。[`scripts/rfc-index.ts`](../../../../scripts/rfc-index.ts) 是共享的真源:树遍历器(拥有封闭的生命周期/分类集合与结构规则,包括对可解析 H1 的要求)和渲染器(行来自 H1 标题并去掉 `RFC: ` 前缀,加上文件名日期,按日期再按文件名排序,以 `### {Class}` 分节、按规范分类顺序分组)。两个轻量消费方共享它:
- [`scripts/gen-rfc-index.ts`](../../../../scripts/gen-rfc-index.ts)`pnpm run gen-rfc-index`)从目录树完整重写 INDEX.md。
- [`scripts/verify-rfc-classification.ts`](../../../../scripts/verify-rfc-classification.ts)`doc-sync`(文档同步门禁)的一个成员)检查结构,断言已提交的 INDEX.md 与新鲜渲染结果逐字节一致(`gen-cordis-catalog`/`verify-cordis-catalog` 模式),并拒绝在策展 README 中出现索引格式的行。新鲜度检查涵盖了索引完整性检查:从磁盘生成的表格在定义上就是完整的、标题正确的。
添加、移动或删除一个 RFC 只需编辑 RFC 文件本身并运行生成器;分类 RFC 的「已否决替代方案」记录中带有替代关系的交叉链接。
## 曾考虑的替代方案
### 为什么不在 README.md 内使用标记分隔区域?
最初落地的形态是:生成器将表格拼接到 README.md 中 `gen-rfc-index` 标记注释之间、各 `## {Lifecycle}` 标题之下。在 README 同时吸收了文件内格式契约([统一格式 RFC](2026-07-05-uniform-rfc-format.md))之后,被整文件 INDEX.md 方案取代:一个门面 README 承载数百行生成内容会淹没其策展行文,而拼接机制(标记对、标题检查、区域外行检测)的存在仅仅是为了保护策展文本——专用的生成文件根本不包含这类文本。
### 为什么不采用纯校验器模式?
校验器能捕获错误,但每次提案编辑仍然要在手工维护的表格中触碰共享热点;对于纯机械的行,校验器失败比生成器更令人烦恼:作者已经命名并放置了文件,索引副本不增加任何信息。这与 [package-inventory 提案](../../proposed/process/2026-06-20-discover-package-inventory.md) 对 tsconfig references 和 knip stanzas 所做的手写列表与推导之间的判断一致——应用于这张确实会冲突的列表。
## 后果
- 生成文件是显式的:其横幅标注了生成器名称,文件内没有需要保护的策展区域,且生成器在目录树结构无效时拒绝运行。
- 格式错误或缺失的 H1 在生成器和门禁中都是硬错误——H1 现在是索引标题的承重来源。
- 并发的 RFC 分支通过重新运行生成器解决索引冲突,从不手工合并行。

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-assembled-assistant-messages-only.md: 48f45ba08f77e2efd79bd999e262afad313bbdfe
2026-06-20-assembled-assistant-messages-only.zh.md: 44d94b3bc3d9c9f0f6c3bb8a2554664c4f5f5c59

View File

@@ -0,0 +1,36 @@
# Agent Note: Persist assembled assistant messages, not stream chunks
Status: rejected — high-fidelity chunk replay, partial failed streams, and snapshot replay currently depend on persisted `assistant/chunk` events. Dropping chunks is only viable with a no-information-loss replay/artifact replacement.
English | [中文](2026-06-20-assembled-assistant-messages-only.zh.md)
## Problem
The canonical session log currently persists every `assistant/chunk` exactly as streamed by the model. The [session persistence Agent Note](../../implemented/architecture/2026-06-14-session-persistence.md) chose this for token-level replay fidelity and contiguous `seq`, but the cost has grown: JSONL fixtures are dominated by tiny delta records, snapshot scenarios replay the model by grouping chunk events, ACP load reconstructs prior assistant output from chunks, and any future log reader must distinguish durable message history from token-level trace.
For successful steps that assemble completed content, the loop already appends an `assistant/message`. That is the event `deriveMessages()` uses for the next model request. In other words, the normal resumable conversation state is already present without the chunks; chunks are a live rendering and deterministic-test artifact, not required conversation history. Failed or aborted streams are different: partial assistant output may exist only as chunks, and empty max-token steps may produce no `assistant/message` at all.
## Proposal
Stop storing `assistant/chunk` in the canonical session log. The durable log keeps `assistant/message`, `tool/call`, `tool/result`, `usage` if retained, and turn boundaries. Live UIs can still receive token deltas through a deliberately transient stream event. Snapshot replay should move its model script into an explicit fixture sidecar or derive it from a recorded adapter artifact, rather than treating the canonical user session as a token tape. Scenarios that need partial failed-stream output must record that output in the replay fixture.
ACP `session/load` can replay prior assistant messages as complete content blocks instead of simulating the original token stream. A loaded transcript need not reproduce every historical delta; it must show the same completed assistant content and resume with a valid provider history.
## Acceptance criteria
- `SessionEventMap` drops `assistant/chunk`, or marks it as non-persisted if a transitional live event is needed.
- [Session persistence docs](../../../../packages/session-persistence/session-persistence/README.md) no longer require every stream chunk to be stored verbatim.
- `llm-replay` and ACP snapshots use an explicit replay fixture format or sidecar for model chunks.
- `session/load` renders completed assistant messages from `assistant/message`.
- Stored logs get much smaller and remain `seq`-contiguous without chunk holes.
- The session format version and recorded fixtures are refreshed; non-current stored logs are rejected per the pre-release format policy.
## What we give up
The canonical user session no longer reconstructs the exact token stream of an old turn. It also loses partial assistant output from failed or aborted streams unless another event or fixture records it. That is too much information loss for the current resume, load, and snapshot contracts. Tests that need exact deterministic streams should own that fixture directly only if the production session log keeps enough fidelity for user-visible recovery.
## Related
This supersedes the chunk-persistence choice in [session persistence](../../implemented/architecture/2026-06-14-session-persistence.md) and affects [ACP snapshot tests](../../implemented/testing/2026-06-19-acp-snapshot-tests.md), whose current replay plugin derives its script from `assistant/chunk` events.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,36 @@
# RFC: 仅持久化组装后的 assistant 消息,不存储流式分片
Status: rejected — high-fidelity chunk replay, partial failed streams, and snapshot replay currently depend on persisted `assistant/chunk` events. Dropping chunks is only viable with a no-information-loss replay/artifact replacement.
[English](2026-06-20-assembled-assistant-messages-only.md) | 中文
## 问题
当前的规范会话日志会持久化模型流式输出的每一个 `assistant/chunk`。[会话持久化 RFC](../../implemented/architecture/2026-06-14-session-persistence.md) 选择这一方案是为了 token 级别的回放保真度和连续的 `seq`但其代价日益增长JSONL fixture测试前置数据被大量微小的 delta 记录占据,快照场景通过分组 chunk 事件来回放模型ACPAgent Client Protocol加载时从 chunk 重建先前的 assistant 输出,而任何未来的日志读取方都必须区分持久的消息历史与 token 级别的追踪。
对于成功组装出完整内容的步骤agent loop智能体循环已经追加了一条 `assistant/message`。这正是 `deriveMessages()` 用来构造下一次模型请求的事件。换言之,正常的可恢复会话状态无需 chunk 即已具备chunk 是实时渲染和确定性测试的产物,不是必需的会话历史。失败或中止的流则不同:部分 assistant 输出可能仅以 chunk 形式存在,而空的 max-token 步骤可能根本不产生 `assistant/message`
## 提案
停止在规范会话日志中存储 `assistant/chunk`。持久日志保留 `assistant/message``tool/call``tool/result``usage`(如保留)以及轮次边界。实时 UI 仍可通过一个刻意设计为瞬态的流事件接收 token 增量。快照回放应将其模型脚本移入显式的 fixture 伴随文件,或从记录的适配器产物中派生,而非将规范的用户会话当作 token 磁带。需要部分失败流输出的场景必须在回放 fixture 中记录该输出。
ACP `session/load` 可以将先前的 assistant 消息作为完整内容块回放,而非模拟原始的 token 流。加载后的 transcript文本记录无需重现每一个历史 delta它必须展示相同的已完成 assistant 内容,并以有效的 provider 历史恢复运行。
## 验收标准
- `SessionEventMap` 移除 `assistant/chunk`,或在需要过渡性实时事件时将其标记为非持久化。
- [会话持久化文档](../../../../packages/session-persistence/session-persistence/README.md)不再要求逐字存储每个流式分片。
- `llm-replay` 和 ACP 快照使用显式的回放 fixture 格式或伴随文件来存储模型 chunk。
- `session/load``assistant/message` 渲染已完成的 assistant 消息。
- 存储的日志大幅缩小,且在没有 chunk 空洞的情况下保持 `seq` 连续。
- 会话格式版本与已记录的 fixture 一并刷新;按预发布格式策略拒绝非当前版本的存储日志。
## 放弃了什么
规范的用户会话不再能重建旧轮次的精确 token 流。它也会丢失失败或中止流的部分 assistant 输出,除非另有事件或 fixture 记录。对于当前的恢复、加载和快照契约而言,这是过大的信息损失。需要精确确定性流的测试应当直接拥有该 fixture前提是生产会话日志为用户可见的恢复保留了足够的保真度。
## 相关
本 RFC 取代 [会话持久化](../../implemented/architecture/2026-06-14-session-persistence.md) 中关于 chunk 持久化的决策,并影响 [ACP 快照测试](../../implemented/testing/2026-06-19-acp-snapshot-tests.md)——其当前的回放插件从 `assistant/chunk` 事件派生脚本。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-drop-acp-session-load.md: 93a2791d10b589cfdee5ecc48922722fe27c1c8a
2026-06-20-drop-acp-session-load.zh.md: 94de0a5aa0436dbee8e78fc2dd6de72c98216768

View File

@@ -0,0 +1,29 @@
# Agent Note: Drop ACP session/load until resume has a product shape
Status: rejected — Zed is the current target ACP client, advertises and exercises load-capable sessions, and keeps pending-load state for concurrent `session/load`. The bridge should keep `session/load` and make the resume contract solid.
English | [中文](2026-06-20-drop-acp-session-load.zh.md)
## Problem
ACP advertises `loadSession: true` and implements `session/load` by injecting persistence into the bridge, validating cwd against stored metadata, reconstructing an agent from the persisted log, and replaying prior transcript updates to the client. That path has its own race handling, loading-id guard, replay presenter logic, and tests. It also depends on the canonical log retaining enough UI data to reconstruct old chunks and tool presentations.
Durable persistence remains foundational, but editor-visible resume is not yet a designed product flow. There is no session picker, no title/preview metadata, and no clear UX for failed or partial loads. The bridge is paying complexity for a feature that is exercised by tests, documentation, and the current target client's session model.
## Proposal
For now, ACP starts fresh sessions only. `initialize` advertises `loadSession: false` or omits the capability, and `session/load` is unsupported. Persistence remains available to the agent loop and tests; resume can still exist as a lower-level factory if another consumer needs it. The editor bridge should reintroduce `session/load` alongside a real session-selection UX and a stable load transcript contract.
## Acceptance criteria
- ACP no longer injects `sessionPersistence` solely for `session/load`.
- `initialize` does not advertise load support.
- The `session/load` handler, loading-id tracking, cwd preflight for loaded sessions, and load replay tests are removed.
- Snapshot fixtures no longer rely on load replay presentation.
- [ACP docs](../../../../packages/ui/acp/README.md) describe fresh-session support only.
## What we give up
An editor cannot reopen a prior persisted session through ACP. That is a real product feature, but the current implementation is ahead of the UX and ties the bridge to token-level log replay. Keeping persistence while dropping editor load narrows the bridge to the workflow it can currently present cleanly.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,29 @@
# RFC: 移除 ACP session/load直到 resume 具备产品形态
Status: rejected — Zed is the current target ACP client, advertises and exercises load-capable sessions, and keeps pending-load state for concurrent `session/load`. The bridge should keep `session/load` and make the resume contract solid.
[English](2026-06-20-drop-acp-session-load.md) | 中文
## 问题
ACPAgent Client Protocol声明 `loadSession: true` 并实现 `session/load`:向 bridge 注入持久化能力、校验 cwd 与存储元数据的一致性、从持久化日志重建 agent智能体并向客户端回放先前的 transcript文本记录更新。该路径有自己的竞态处理、loading-id 守卫、回放展示逻辑和测试。它还依赖规范日志保留足够的 UI 数据,以重建旧的分片和工具展示。
持久化仍然是基础能力,但编辑器可见的 resume 尚未经过产品流程设计。目前没有会话选择器、没有标题/预览元数据也没有明确的加载失败或部分加载的用户体验。bridge 正在为一个仅被测试、文档和当前目标客户端的会话模型所使用的功能付出复杂度代价。
## 提案
当前阶段ACP 仅启动全新会话。`initialize` 声明 `loadSession: false` 或省略该能力,`session/load` 不予支持。持久化仍可供 agent loop智能体循环和测试使用如果其他消费方需要resume 仍可作为底层工厂存在。编辑器 bridge 应在具备真正的会话选择 UX 和稳定的 load transcript 契约后,再重新引入 `session/load`
## 验收标准
- ACP 不再仅为 `session/load` 注入 `sessionPersistence`
- `initialize` 不再声明 load 支持。
- `session/load` handler、loading-id 追踪、已加载会话的 cwd 预检以及 load 回放测试均被移除。
- 快照 fixture测试前置数据不再依赖 load 回放展示。
- [ACP 文档](../../../../packages/ui/acp/README.md)仅描述全新会话的支持。
## 放弃的能力
编辑器无法通过 ACP 重新打开先前持久化的会话。这确实是一项产品功能,但当前实现超前于 UX 设计,且将 bridge 绑定到 token 级别的日志回放。保留持久化但移除编辑器 load可将 bridge 收窄到它当前能干净呈现的工作流。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-drop-acp-terminal-meta.md: e52187d9786ed44ef4b60aedf5396c19a2e8e872
2026-06-20-drop-acp-terminal-meta.zh.md: a5f4d7bc1f9d3d050c23990e89610d3eaed5bf70

View File

@@ -0,0 +1,31 @@
# Agent Note: Drop ACP terminal `_meta` rendering
Status: rejected — Zed is the current target client, and the terminal `_meta` convention is intentional Zed UX with a plain ACP fallback for other clients.
English | [中文](2026-06-20-drop-acp-terminal-meta.zh.md)
## Problem
The ACP bridge implements a Zed-specific terminal-card convention through `_meta.terminal_info`, `_meta.terminal_output`, and `_meta.terminal_exit`. The implemented [rich ACP bash rendering Agent Note](../../implemented/feature/2026-06-18-acp-terminal-and-tool-rendering.md) deliberately avoided ACP's client-side `terminal/create` because bash execution belongs in the harness, but still adopted the reference agents' display-only `_meta` convention. That gives a nicer Zed card at the cost of bridge state, capability negotiation, terminal ids, special update mapping, text fallback tests, and exit-pill parsing in `dsh-tool-bash`.
The fallback path already exists: render the tool call and completed output as normal ACP content blocks. Non-Zed clients rely on that path anyway, but the Zed terminal card is a current target-client feature rather than speculative decoration.
## Proposal
Ignore `clientCapabilities._meta.terminal_output` and render bash results through the plain ACP content path. Keep execution agent-side through `dsh-bash`; only the display-specific terminal metadata is removed. A terminal card can return later if ACP standardizes agent-executed terminals or if the product decides Zed-specific display is worth the maintenance cost.
This proposal is narrower than [collapsing tool-owned UI presentation](2026-06-20-generic-tool-rendering.md): it keeps generic `presentCall`/`presentResult` if those survive, but removes the terminal sub-shape and `_meta` mapping.
## Acceptance criteria
- ACP no longer reads or stores `_meta.terminal_output` capability state.
- `TerminalRendering`, terminal ids, terminal cwd resolution, and `_meta.terminal_*` update mapping disappear from `@deepseek-ai/dsh-acp`.
- `ToolTerminal` disappears from `@deepseek-ai/dsh-tools`, or is unused and deleted with the presentation cleanup.
- Bash result presentation no longer parses exit status for terminal pills.
- The implemented [rich ACP bash rendering Agent Note](../../implemented/feature/2026-06-18-acp-terminal-and-tool-rendering.md) stays in `implemented/` as shipped history and is cross-linked from this proposal if superseded.
## What we give up
Zed users lose the dedicated terminal card: no cwd header, terminal display, or exit pill. They still see the command and output as plain content. That is a reasonable simplification while the ACP bridge is still unreleased and the `_meta` keys are a convention rather than a standard.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,31 @@
# RFC: 移除 ACP 终端 `_meta` 渲染
Status: rejected — Zed is the current target client, and the terminal `_meta` convention is intentional Zed UX with a plain ACP fallback for other clients.
[English](2026-06-20-drop-acp-terminal-meta.md) | 中文
## 问题
ACP 桥接层通过 `_meta.terminal_info``_meta.terminal_output``_meta.terminal_exit` 实现了一套 Zed 特有的终端卡片约定。已实现的[富 ACP bash 渲染 RFC](../../implemented/feature/2026-06-18-acp-terminal-and-tool-rendering.md) 刻意回避了 ACP 客户端侧的 `terminal/create`(因为 bash 执行属于 harness 职责),但仍采用了参考 agent智能体的纯展示 `_meta` 约定。这在 Zed 中带来了更好的卡片效果,代价是桥接状态、能力协商、终端 id、特殊的 update 映射、文本回退测试,以及 `dsh-tool-bash` 中的 exit-pill 解析。
回退路径已经存在:将工具调用和完成输出渲染为普通 ACP 内容块。非 Zed 客户端本来就依赖这条路径,但 Zed 终端卡片是当前目标客户端的功能特性,而非推测性装饰。
## 提案
忽略 `clientCapabilities._meta.terminal_output`,通过纯 ACP 内容路径渲染 bash 结果。执行仍由 agent 侧的 `dsh-bash` 完成;仅移除展示相关的终端元数据。如果 ACP 日后标准化了 agent 执行的终端,或产品决定 Zed 特有展示值得其维护成本,终端卡片可以再回来。
本提案比[收拢工具自有 UI 展示](2026-06-20-generic-tool-rendering.md)更窄:如果通用的 `presentCall`/`presentResult` 保留,本提案不影响它们,只移除终端子形态与 `_meta` 映射。
## 验收标准
- ACP 不再读取或存储 `_meta.terminal_output` 能力状态。
- `TerminalRendering`、终端 id、终端 cwd 解析与 `_meta.terminal_*` update 映射从 `@deepseek-ai/dsh-acp` 中消失。
- `ToolTerminal``@deepseek-ai/dsh-tools` 中消失,或在展示清理中因未使用而删除。
- Bash 结果展示不再为终端 pill 解析退出状态。
- 已实现的[富 ACP bash 渲染 RFC](../../implemented/feature/2026-06-18-acp-terminal-and-tool-rendering.md) 作为已交付历史保留在 `implemented/` 中;如被本提案取代,则加上交叉链接。
## 放弃的内容
Zed 用户将失去专用终端卡片:没有 cwd 头部、终端展示或 exit pill。他们仍能以纯内容形式看到命令和输出。在 ACP 桥接层尚未发布、`_meta` 键只是约定而非标准的阶段,这是合理的简化。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-drop-bash-output-spill-files.md: 939f99072eace71405ae96e270d5438e75713c1c
2026-06-20-drop-bash-output-spill-files.zh.md: 4a868b1971dc3abcb4a9d0442ffc6e74f5246d00

View File

@@ -0,0 +1,31 @@
# Agent Note: Drop bash full-output spill files
Status: rejected — full-output recovery is a real bash behavior. A future artifact/blob service may generalize it, but dropping spill files before that replacement would lose useful command output.
English | [中文](2026-06-20-drop-bash-output-spill-files.zh.md)
## Problem
`dsh-bash-local` keeps bounded in-memory output and spills large stdout/stderr streams into private temp files. That requires a private directory, random owner-only file creation, close-failure handling, byte-offset incremental reads, lossy read reporting, path rendering in model-facing text, and cleanup discipline. The tool then tells the model to read a local spill path when output was truncated.
This solves a real problem, but in a narrow and leaky way. A spill path is a process-local filesystem artifact exposed to model output, not a durable harness artifact with scoped access, retention, or UI affordances. It also complicates background-task reads because a lossy incremental read has to point at one or two spill files.
## Proposal
Keep tail truncation, drop full-output spill files. A bash result contains the bounded tail plus a clear truncation marker; no path is emitted. If users need full-output recovery, add a generic artifact/blob service with explicit ownership, cleanup, and UI rendering, then let bash attach large outputs to that service.
This proposal can land independently of [a generic long-running tool runtime](../../implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md). If background tasks stay, `bash_output` should still report that output was dropped, but without advertising a spill path.
## Acceptance criteria
- `CollectedOutput` no longer carries spill paths.
- `OutputCollector` keeps bounded buffers only and deletes the temp-file machinery.
- `renderResult()` reports truncation without a filesystem path.
- Tests cover tail truncation and no longer assert full-output file contents.
- Security guidance in [docs/defensive-patterns.md](../../../../docs/defensive-patterns.md) stops treating private spill files as a model-visible interface.
## What we give up
A model or user cannot recover the omitted prefix of a huge command output from a temp file. That is acceptable until there is a real artifact service. The current spill path is too much bespoke machinery for a feature whose lifecycle and permissions are not designed.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,31 @@
# RFC: 移除 bash 完整输出溢出文件
Status: rejected — full-output recovery is a real bash behavior. A future artifact/blob service may generalize it, but dropping spill files before that replacement would lose useful command output.
[English](2026-06-20-drop-bash-output-spill-files.md) | 中文
## 问题
`dsh-bash-local` 在内存中保留有界的输出,并将大体量的 stdout/stderr 流溢出到私有临时文件。这要求一个私有目录、仅所有者可写的随机文件创建、关闭失败处理、基于字节偏移的增量读取、有损读取报告、在面向模型的文本中渲染路径,以及清理纪律。当输出被截断时,该工具会告知模型去读取一个本地溢出路径。
这解决了一个真实问题,但方式狭隘且有泄漏。溢出路径是一个暴露在模型输出中的进程级文件系统产物,而非具有作用域访问控制、保留策略或 UI 支持的持久化 harness 产物。它还使后台任务的读取变得复杂,因为有损增量读取必须指向一个或两个溢出文件。
## 提案
保留尾部截断移除完整输出溢出文件。bash 结果包含有界的尾部内容加一个明确的截断标记;不输出路径。如果用户需要恢复完整输出,则添加一个通用的产物/blob 服务(具有明确的所有权、清理和 UI 渲染),然后让 bash 将大体量输出附加到该服务。
本提案可以独立于[通用长时间运行工具运行时](../../proposed/architecture/2026-06-20-generic-long-running-tool-runtime.md)落地。如果后台任务保留,`bash_output` 仍应报告输出已被丢弃,但不再提供溢出路径。
## 验收标准
- `CollectedOutput` 不再携带溢出路径。
- `OutputCollector` 仅保留有界缓冲区,删除临时文件机制。
- `renderResult()` 报告截断时不包含文件系统路径。
- 测试覆盖尾部截断,不再断言完整输出文件的内容。
- [docs/defensive-patterns.md](../../../defensive-patterns.md) 中的安全指导不再将私有溢出文件视为面向模型的接口。
## 放弃的能力
模型或用户无法再从临时文件恢复大体量命令输出中被省略的前缀。在真正的产物服务出现之前,这是可以接受的。当前的溢出路径为一个生命周期和权限均未经设计的功能引入了过多的定制机制。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-drop-durable-step-boundaries.md: 16fdf17c3bc8907745df17e8a105d7978eafb274
2026-06-20-drop-durable-step-boundaries.zh.md: 5613a84d5f1b5cf87109a2e04a4cb350ffd650a8

View File

@@ -0,0 +1,32 @@
# Agent Note: Drop durable step boundary events
Status: rejected — `step/end` is the durable indication that a model step finished, and keeping the symmetric `step/start` / `step/end` pair makes crash repair, invariants, and transcript inspection clearer than inferring completion from adjacent step-scoped events.
English | [中文](2026-06-20-drop-durable-step-boundaries.zh.md)
## Problem
The session log stores `step/start` and `step/end` events even though every step-scoped event already carries `{ turn, step }`: assistant chunks, assistant messages, tool calls, tool results, usage, and errors. `deriveMessages()` ignores step boundaries, ACP ignores them for UI, and the main consumers are invariants, tests, snapshot expected outputs, and crash repair.
The rejected argument was that boundary events make the log more ceremonial than informative. In practice, `step/end` is concrete information: a reader can tell whether a model request finished, crashed, or is being repaired without deriving that state from the next event. A bare `step/start` is likewise useful for a model request that began but produced no chunks before failing.
## Proposal
Make the turn the only durable boundary. Remove `step/start` and `step/end` from `SessionEventMap`; keep the numeric `step` field on events that need grouping. The loop increments the step counter and records step-scoped events with that number, but it no longer appends open/close boundary events. Consumers infer step groups from contiguous events sharing `(turn, step)`.
The invariants plugin should enforce that step-scoped events have valid positive step numbers within an open turn, not that separate boundary records surround them. Crash repair should not synthesize `step/end`; if an interrupted turn is preserved, the repair path can still close the turn without inventing step boundary records.
## Acceptance criteria
- `SessionEventMap` no longer includes `step/start` or `step/end`.
- The loop has no `closeStep()` finalization path.
- ACP snapshots and persistence contract fixtures stop expecting step-boundary lines.
- `deriveMessages()` and replay derive the same message history from step-scoped events.
- The [event taxonomy docs](../../../../docs/architecture.md) describe turns as the durable boundary and steps as a field on step-scoped records.
- The session format version and recorded fixtures are refreshed; non-current stored logs are rejected per the pre-release format policy.
## What we give up
The log no longer records "a model request started but produced no event before the process died" as a durable fact, and no longer has an explicit "this step completed" marker. That loss is not acceptable while the session log is the durable replay and audit surface.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,32 @@
# RFC: 移除持久化的步骤边界事件
Status: rejected — `step/end` is the durable indication that a model step finished, and keeping the symmetric `step/start` / `step/end` pair makes crash repair, invariants, and transcript inspection clearer than inferring completion from adjacent step-scoped events.
[English](2026-06-20-drop-durable-step-boundaries.md) | 中文
## 问题
会话日志存储了 `step/start``step/end` 事件,尽管每个步骤作用域的事件本身已经携带 `{ turn, step }`assistant 分片、assistant 消息、工具调用、工具结果、用量和错误。`deriveMessages()` 忽略步骤边界ACPAgent Client Protocol在 UI 层面也忽略它们,主要消费方是不变式检查、测试、快照 golden 文件和崩溃恢复。
被否决的论点是:边界事件使日志更像仪式而非信息。实际上,`step/end` 是具体信息:读者无需从下一个事件推导状态,就能判断一次模型请求是已完成、已崩溃还是正在修复。同样,一个孤立的 `step/start` 对于「模型请求已发起但在产生任何分片之前就失败了」的场景也有价值。
## 提案
将轮次作为唯一的持久化边界。从 `SessionEventMap` 中移除 `step/start``step/end`;在需要分组的事件上保留数值型 `step` 字段。agent loop智能体循环递增步骤计数器并以该编号记录步骤作用域的事件但不再追加开/关边界事件。消费方通过共享 `(turn, step)` 的连续事件推断步骤分组。
不变式插件应当强制步骤作用域的事件在一个已打开的轮次内具有有效的正整数步骤编号,而非要求独立的边界记录包围它们。崩溃恢复不应合成 `step/end`;如果一个被中断的轮次被保留,修复路径仍然可以关闭该轮次而无需捏造步骤边界记录。
## 验收标准
- `SessionEventMap` 不再包含 `step/start``step/end`
- agent loop 中不再有 `closeStep()` 终结路径。
- ACP 快照和持久化契约 fixture测试前置数据不再期望步骤边界行。
- `deriveMessages()` 和回放从步骤作用域的事件推导出相同的消息历史。
- [事件分类体系文档](../../../architecture.md)将轮次描述为持久化边界,将步骤描述为步骤作用域记录上的一个字段。
- 会话格式版本和已记录的 fixture 被刷新;按预发布格式策略,非当前版本的已存储日志被拒绝。
## 放弃了什么
日志不再将「一次模型请求已发起但进程死亡前未产生任何事件」记录为持久化事实,也不再有显式的「此步骤已完成」标记。在会话日志仍是持久化回放与审计表面的当下,这一损失不可接受。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-drop-unused-session-lineage.md: 5f76baf33fa50fc1aff9a0ab43262f063aa8e51b
2026-06-20-drop-unused-session-lineage.zh.md: 79decbb40d93f0798189d4a131197db132549f1c

View File

@@ -0,0 +1,31 @@
# Agent Note: Drop unused session lineage metadata
Status: rejected — `parentSession` is part of the documented fork/sub-agent seam and is already preserved by the agent/session resume path. The field is future-facing, but it is not accidental dead state.
English | [中文](2026-06-20-drop-unused-session-lineage.zh.md)
## Problem
`SessionHeader.parentSession` records the session a new session was forked from. It is defined in `dsh-session`, preserved by persistence backends, copied through resume, documented as lineage metadata, and covered by round-trip tests. The repo has no production fork UI or sub-agent flow that reads it. The planned sub-agent/fork seam is still a TODO, so the field is currently stored future shape.
The cost is small per file but broad across the format: every backend schema and metadata serializer preserves a value that no completed feature reads yet. Because the header is an on-disk contract, even a placeholder field becomes something future refactors must either maintain, migrate, or deliberately break.
## Proposal
Remove `parentSession` from `SessionHeader` until a real fork/resume feature needs lineage. Forking can still seed a new session with prior events if such an API exists, but the durable parent pointer should be introduced alongside the feature that reads it and the UX that explains it.
If lineage returns, decide then whether it belongs in the immutable header, a session graph index, or a first-class event. The current field should not pre-commit that design.
## Acceptance criteria
- `SessionHeader` contains version, id, createdAt, and optional cwd only.
- JSONL and SQLite metadata schemas stop storing parent-session ids.
- Resume and list APIs no longer round-trip `parentSession`.
- Docs and tests remove fork-lineage claims that are not backed by a production consumer.
- The session format version, backend schema versions, and recorded fixtures are refreshed as needed; non-current stored data is rejected per the pre-release format policy, with no migration path.
## What we give up
The codebase loses a ready-made lineage hook for future fork/sub-agent UX. That is intentional. The field is easy to reintroduce when the feature exists, and the unreleased stance lets the format change without migrations.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,31 @@
# RFC: 移除未使用的会话血缘元数据
Status: rejected — `parentSession` is part of the documented fork/sub-agent seam and is already preserved by the agent/session resume path. The field is future-facing, but it is not accidental dead state.
[English](2026-06-20-drop-unused-session-lineage.md) | 中文
## 问题
`SessionHeader.parentSession` 记录新会话从哪个会话 fork 而来。它在 `dsh-session` 中定义,被持久化后端保留,在恢复流程中复制,作为血缘元数据被文档记录,并有往返测试覆盖。然而仓库中没有任何生产环境的 fork UI 或 subagent 流程读取它。计划中的 subagent/fork seam 仍是 TODO因此该字段目前只是预存的未来形状。
单个文件的成本虽小,但在格式层面影响面广:每个后端 schema 和元数据序列化器都在保留一个尚无已完成功能读取的值。由于 header 是磁盘契约,即使是占位字段也会成为未来重构必须维护、迁移或有意打破的东西。
## 提案
`SessionHeader` 中移除 `parentSession`,直到真正的 fork/恢复功能需要血缘信息时再引入。如果存在相应 APIfork 仍然可以用先前事件来初始化新会话,但持久化的父指针应当与读取它的功能和解释它的 UX 一同引入。
如果血缘信息回归,届时再决定它应放在不可变 header 中、会话图索引中,还是作为一等事件。当前字段不应预先锁定那个设计。
## 验收标准
- `SessionHeader` 仅包含 version、id、createdAt 和可选的 cwd。
- JSONL 与 SQLite 元数据 schema 不再存储 parent-session id。
- 恢复与列表 API 不再往返传递 `parentSession`
- 文档和测试移除没有生产消费方支撑的 fork 血缘声明。
- 会话格式版本、后端 schema 版本与记录的 fixture测试前置数据按需刷新按预发布格式策略非当前版本的存储数据将被拒绝不提供迁移路径。
## 放弃了什么
代码库失去了一个为未来 fork/subagent UX 预备的现成血缘钩子。这是有意为之。该字段在功能存在时很容易重新引入,而未发布的立场允许格式变更无需迁移。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-fold-session-persistence-interface.md: 3e9bb277ccd0b6319081cd1aac289db764f41e58
2026-06-20-fold-session-persistence-interface.zh.md: 13a8945fcaa5529f6d4dc156b0dc1016ab6da62d

View File

@@ -0,0 +1,31 @@
# Agent Note: Fold the persistence interface into dsh-session
Status: rejected — the separate persistence interface package is the intended modular capability seam for durable backends. Folding it into `dsh-session` would reduce package count at the cost of a cleaner backend boundary.
English | [中文](2026-06-20-fold-session-persistence-interface.zh.md)
## Problem
`dsh-session-persistence` is an interface package whose main concepts are already owned by `dsh-session`: `SessionHeader`, `SessionEvent`, `SessionId`, `session/event`, and `session/flush`. The package adds the abstract `SessionPersistence` service, the shared write coordinator, and contract helpers. Backend packages depend on it, and `agent-loop` has to optionally find a sibling service for resume.
The capability-seam split made sense when persistence was a new swappable backend design. After the mutable summary was removed, the interface package mostly wraps the session log's own storage concern. Keeping it separate may be more ceremony than clarity.
## Proposal
Move the abstract `SessionPersistence` service, the coordinator, and persistence contract helpers into `dsh-session`. Keep JSONL and SQLite as separate backend packages that register the session-owned service. This preserves backend swappability while deleting one support package and one cross-package seam.
The implementing PR should update the [capability seams](../../implemented/architecture/2026-06-13-capability-seams.md) guidance with the exception: persistence is not like bash or LLM because its vocabulary and lifecycle events are already the session package's core domain.
## Acceptance criteria
- `@deepseek-ai/dsh-session-persistence` is removed as a package.
- `dsh-session` exports the persistence service type, coordinator, and contract helpers.
- JSONL and SQLite backend packages depend on `dsh-session` directly.
- `agent-loop` resume uses the session-owned service key.
- [Session persistence](../../implemented/architecture/2026-06-14-session-persistence.md), [shared persistence write coordinator](../../implemented/architecture/2026-06-18-shared-persistence-write-coordinator.md), and [package docs](../../../../packages/session-persistence/session-persistence/README.md) explain why backend implementations remain separate.
## What we give up
`dsh-session` becomes heavier: it owns both the in-memory log and the persistence interface. That is the trade. If third-party persistence backends were already a public ecosystem, the separate interface package would be a cleaner SDK boundary; pre-release, the extra package looks like abstraction before there is an external consumer.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,31 @@
# RFC: 将持久化接口合并进 dsh-session
Status: rejected — the separate persistence interface package is the intended modular capability seam for durable backends. Folding it into `dsh-session` would reduce package count at the cost of a cleaner backend boundary.
[English](2026-06-20-fold-session-persistence-interface.md) | 中文
## 问题
`dsh-session-persistence` 是一个接口包package其核心概念已经由 `dsh-session` 拥有:`SessionHeader``SessionEvent``SessionId``session/event``session/flush`。该包额外添加了抽象的 `SessionPersistence` 服务、共享写入协调器和契约辅助工具。后端包依赖它,`agent-loop`(智能体循环)也需要可选地查找一个同级服务来实现恢复。
当持久化还是一个全新的可替换后端设计时,能力 seam 的拆分是合理的。但在可变摘要被移除之后,这个接口包基本上只是包装了会话日志自身的存储关切。继续保持独立可能带来的仪式感多于清晰度。
## 提案
将抽象的 `SessionPersistence` 服务、协调器和持久化契约辅助工具移入 `dsh-session`。JSONL 和 SQLite 仍作为独立的后端包,注册由 session 包拥有的服务。这样既保留了后端可替换性,又删除了一个支撑包和一条跨包 seam。
实施 PRPull Request应更新[能力 seam](../../implemented/architecture/2026-06-13-capability-seams.md) 指南,补充此例外:持久化不同于 bash 或 LLM大语言模型因为它的词汇和生命周期事件本就属于 session 包的核心领域。
## 验收标准
- `@deepseek-ai/dsh-session-persistence` 作为包被移除。
- `dsh-session` 导出持久化服务类型、协调器和契约辅助工具。
- JSONL 和 SQLite 后端包直接依赖 `dsh-session`
- `agent-loop` 的恢复功能使用 session 包拥有的服务键。
- [会话持久化](../../implemented/architecture/2026-06-14-session-persistence.md)、[共享持久化写入协调器](../../implemented/architecture/2026-06-18-shared-persistence-write-coordinator.md)与[包文档](../../../../packages/session-persistence/session-persistence/README.md)说明后端实现为何仍保持独立。
## 放弃了什么
`dsh-session` 变得更重:它同时拥有内存日志和持久化接口。这就是代价。如果第三方持久化后端已经形成公开生态,独立的接口包会是更清晰的 SDK 边界;但在预发布阶段,在尚无外部消费方时,多出的包看起来更像是过早的抽象。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-generic-tool-rendering.md: 77d06968a24211835d1ff5db2541efdeb8227878
2026-06-20-generic-tool-rendering.zh.md: ab032864a5304d6781d990d1f805fe9d280c1655

View File

@@ -0,0 +1,35 @@
# Agent Note: Collapse tool-owned UI presentation
Status: rejected — tool-owned presentation should wait for more real tools before being generalized or deleted. Bash and ACP currently need the existing richer presentation path.
English | [中文](2026-06-20-generic-tool-rendering.zh.md)
## Problem
Tools can define `presentCall()` and `presentResult()` callbacks that return `ToolCallPresentation`, `ToolResultPresentation`, and optional `ToolTerminal` fields. The code itself flags the design as muddy: title, kind, raw input, content, terminal cwd, terminal output, exit code, and signal grew incrementally into a bag of optional fields. ACP then maintains pending call state to pair a result with the original args, creates replay-only presenters on `session/load`, and maps terminal subfields into Zed-specific `_meta`. `dsh-tool-bash` even parses exit status back out of rendered text because the pure replay-safe presenter no longer has the structured `BashRunResult`.
The real first-party use is bash presentation for ACP. That is too little evidence to freeze a cross-package UI presentation API.
## Proposal
Remove tool-owned UI presentation callbacks for now. The canonical tool events already carry the tool name, raw argument string, result content, and error state. UIs render a generic tool card from those fields. Tool-specific rich rendering can return later as a tagged render-intent union after there are at least two real tools and two real consumers to validate the vocabulary.
## Alternatives considered
As a smaller alternative, replace the current optional-field bag with one explicit union in a single PR; but if the goal is simplification, the stronger move is to delete the callbacks and keep the generic path.
## Acceptance criteria
- `ToolDefinition` drops `presentCall` and `presentResult`.
- `ToolCallPresentation`, `ToolResultPresentation`, `ToolTerminal`, and `ToolCallKind` disappear unless a minimal generic UI type still needs one.
- ACP no longer keeps presenter pending state or calls tool callbacks during live streaming/load replay.
- `dsh-tool-bash` no longer parses rendered text to recover exit status for a UI pill.
- Snapshot expected outputs show generic tool cards and text results.
## What we give up
Bash loses its custom terminal-looking card and model-written description placement. The fallback remains reasonable: the command appears as tool input, and the output appears as text. Rich rendering should be designed when the product has enough UI/tool variety to justify a stable presentation contract.
## Related
This is the broad version of [dropping ACP terminal metadata](2026-06-20-drop-acp-terminal-meta.md). If this Agent Note is accepted, that narrower Agent Note becomes unnecessary.

View File

@@ -0,0 +1,35 @@
# RFC: 收拢工具自有的 UI 展示逻辑
Status: rejected — tool-owned presentation should wait for more real tools before being generalized or deleted. Bash and ACP currently need the existing richer presentation path.
[English](2026-06-20-generic-tool-rendering.md) | 中文
## 问题
工具可以定义 `presentCall()``presentResult()` 回调,返回 `ToolCallPresentation``ToolResultPresentation` 以及可选的 `ToolTerminal` 字段。代码本身就标记了这个设计的混乱title、kind、raw input、content、terminal cwd、terminal output、exit code 和 signal 逐步增长为一堆可选字段。ACPAgent Client Protocol随后维护 pending call 状态以将 result 与原始 args 配对,在 `session/load` 时创建仅用于回放的 presenter并将 terminal 子字段映射为 Zed 特有的 `_meta``dsh-tool-bash` 甚至从渲染后的文本中反向解析退出状态,因为纯回放安全的 presenter 已经拿不到结构化的 `BashRunResult`
真正的第一方用途是为 ACP 提供 bash 展示。这不足以作为冻结一个跨包 UI 展示 API 的依据。
## 提案
暂时移除工具自有的 UI 展示回调。规范的工具事件已经携带工具名、原始参数字符串、结果内容和错误状态。UI 从这些字段渲染一个通用的工具卡片。工具特有的富展示可以在至少有两个真实工具和两个真实消费方来验证词汇之后,以带标签的 render-intent union 形式回归。
## 曾考虑的替代方案
作为更小的替代方案,可以在一个 PRPull Request中将当前的可选字段集合替换为一个显式 union但如果目标是简化更彻底的做法是删除回调、保留通用路径。
## 验收标准
- `ToolDefinition` 移除 `presentCall``presentResult`
- `ToolCallPresentation``ToolResultPresentation``ToolTerminal``ToolCallKind` 消失,除非一个最小的通用 UI 类型仍需要其中之一。
- ACP 不再维护 presenter pending 状态,也不再在实时流式输出/加载回放期间调用工具回调。
- `dsh-tool-bash` 不再解析渲染文本来恢复退出状态以供 UI pill 使用。
- 快照 golden 文件展示通用工具卡片和文本结果。
## 放弃了什么
Bash 失去其自定义的终端风格卡片和模型生成描述的放置位置。回退方案仍然合理:命令作为工具输入展示,输出作为文本展示。富展示应当在产品拥有足够的 UI/工具多样性、足以支撑一份稳定的展示契约时再行设计。
## 相关
这是[移除 ACP terminal 元数据](2026-06-20-drop-acp-terminal-meta.md)的宽泛版本。如果本 RFC 被接受,那个更窄的 RFC 就不再必要。

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-retire-mid-turn-steering.md: 2c4d686942d2bfa8016bb60d55624dc59a933c1a
2026-06-20-retire-mid-turn-steering.zh.md: 2196d7d1d1f1bff39e8e0f03cf9910ab41434d2a

View File

@@ -0,0 +1,37 @@
# Agent Note: Retire mid-turn steering
Status: rejected — mid-turn steering is an intentional agent capability for between-step user/plugin input and future goal/loop workflows. It is complexity with a product direction, not an accidental duplicate of `send()`.
English | [中文](2026-06-20-retire-mid-turn-steering.zh.md)
## Problem
The agent exposes two user-message paths that look close but have different lifecycle semantics: `send()` queues a normal user turn, while `steer()` injects a message between steps of the currently running turn and falls back to `send()` when idle. That distinction leaks through the whole stack: `Agent.steer()` is public API, the session log has a durable `steering/message` event, the agent event taxonomy has `agent/steering`, the loop maintains a steering FIFO beside the queued-message FIFO, cancellation clears both queues, and `deriveMessages()` has to render steering as a tagged synthetic user message rather than a normal prompt.
The continuation seam amplifies the cost. `agent/turn-continuation` defaults to `hadToolCalls || steeringInjected`, so a same-turn steering message can force the loop to call the model again even if the model did not ask for tools. The comments name future `/goal`, `/loop`, and budget-guard uses, but the current repo has no production listener; only tests register the waterfall. Separately, the only production UI that calls `steer()` is the stdio demo. ACP already sends prompts through the ordinary queue while a turn is running.
## Proposal
Delete mid-turn user steering for now. `Agent.send()` becomes the single public way to submit user content; when the agent is running, the content waits for the next turn. The loop continues within a turn only for tool calls, not because a user typed while a step was running. A caller that wants to interrupt the current turn uses `cancel()` and then `send()`.
Remove `Agent.steer()`, the steering FIFO, `steering/message`, `agent/steering`, steering-derived continuation, and the cancellation logic that distinguishes queued messages from steering messages. Remove `agent/turn-continuation` in the same change unless the implementing PR discovers a production listener; without steering, the current repo has no concrete continuation consumer left. If a real budget or goal plugin later needs forced continuation, it should reintroduce a narrower seam with that plugin as the concrete consumer.
## Acceptance criteria
- `Agent` exposes one user-message entry point, `send()`.
- The durable session event vocabulary no longer contains `steering/message`.
- `deriveMessages()` renders normal user messages and context injections, with no steering tag path.
- The loop has one queued-message FIFO and no same-turn user-message continuation path.
- `agent/turn-continuation` is removed or narrowed to a named production consumer.
- The stdio UI and docs describe input while running as queued next-turn input.
- The session format version and recorded fixtures are refreshed; non-current stored logs are rejected per the pre-release format policy.
## What we give up
A user cannot add same-turn steering content while a model is between tool steps. That behavior is useful in theory for "while you are already working, also consider X", but it is not the behavior ACP exposes today and it makes the turn boundary much harder to reason about. The simpler behavior is reasonable: user input becomes the next prompt, and cancellation remains the explicit tool for replacing in-flight work.
## Related
This pairs naturally with [dropping durable step boundaries](2026-06-20-drop-durable-step-boundaries.md), because removing same-turn steering and `agent/turn-continuation` leaves tool calls as the only reason a turn contains multiple model steps.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,37 @@
# RFC: 移除轮次中途引导
Status: rejected — mid-turn steering is an intentional agent capability for between-step user/plugin input and future goal/loop workflows. It is complexity with a product direction, not an accidental duplicate of `send()`.
[English](2026-06-20-retire-mid-turn-steering.md) | 中文
## 问题
agent智能体暴露了两条用户消息路径外观相近但生命周期语义不同`send()` 将一条普通用户轮次排入队列,而 `steer()` 在当前运行轮次的步骤之间注入一条消息,空闲时则回退为 `send()`。这一区分贯穿整个栈:`Agent.steer()` 是公开 API会话日志有持久化的 `steering/message` 事件agent 事件分类体系有 `agent/steering`agent loop智能体循环在排队消息 FIFO 之外还维护一个 steering FIFO取消操作需要清空两个队列`deriveMessages()` 必须将 steering 渲染为带标签的合成用户消息,而非普通提示词。
续行 seam 进一步放大了成本。`agent/turn-continuation` 默认条件为 `hadToolCalls || steeringInjected`,因此同一轮次内的 steering中途引导消息即使模型未请求工具调用也会强制循环再次调用模型。注释中提到了未来 `/goal``/loop` 和预算守卫的用途,但当前仓库没有生产级监听器;只有测试注册了该 waterfall瀑布式事件。另外唯一调用 `steer()` 的生产 UI 是 stdio 演示。ACPAgent Client Protocol在轮次运行期间已经通过普通队列发送提示词。
## 提案
暂时删除轮次中途的用户 steering。`Agent.send()` 成为提交用户内容的唯一公开方式;当 agent 正在运行时,内容等待下一个轮次。循环仅因工具调用而在轮次内继续,不因用户在某个步骤运行期间输入内容而继续。调用方若要中断当前轮次,使用 `cancel()` 后再 `send()`
移除 `Agent.steer()`、steering FIFO、`steering/message``agent/steering`、由 steering 驱动的续行逻辑,以及取消操作中区分排队消息与 steering 消息的逻辑。除非实现 PR 发现了生产级监听器,否则在同一变更中一并移除 `agent/turn-continuation`;没有 steering 后,当前仓库不再有具体的续行消费方。如果将来真正的预算或目标插件需要强制续行,应以该插件为具体消费方重新引入一个更窄的 seam。
## 验收标准
- `Agent` 暴露唯一的用户消息入口 `send()`
- 持久化会话事件词汇不再包含 `steering/message`
- `deriveMessages()` 渲染普通用户消息和上下文注入,不存在 steering 标签路径。
- 循环只有一个排队消息 FIFO没有同轮次用户消息续行路径。
- `agent/turn-continuation` 被移除,或收窄到有具名的生产级消费方。
- stdio UI 和文档将运行期间的输入描述为「排入下一轮次的输入」。
- 会话格式版本和已录制的 fixture测试前置数据已刷新非当前版本的存储日志按预发布格式策略被拒绝。
## 放弃了什么
用户无法在模型处于工具步骤之间时添加同轮次 steering 内容。这种行为在理论上对「你已经在工作了,也考虑一下 X」的场景有用但它不是 ACP 当前暴露的行为,且使轮次边界更难推理。更简单的行为是合理的:用户输入成为下一条提示词,取消操作仍是替换进行中工作的显式手段。
## 相关
本提案与[移除持久化步骤边界](2026-06-20-drop-durable-step-boundaries.md)天然配对,因为移除同轮次 steering 和 `agent/turn-continuation` 后,工具调用成为一个轮次包含多个模型步骤的唯一原因。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-single-session-acp-bridge.md: 8aa8f5d605154f697086dd5d432bbe9bd79c5dcd
2026-06-20-single-session-acp-bridge.zh.md: a056bc5acfab9d48259670170a964e8358df866c

View File

@@ -0,0 +1,31 @@
# Agent Note: Return the ACP bridge to one live session per connection
Status: rejected — Zed is the current target ACP client and its ACP implementation is explicitly multi-session: it stores live sessions in a `HashMap<SessionId, AcpSession>`, tracks `pending_sessions`, joins concurrent loads for the same id, and tests close-during-load behavior.
English | [中文](2026-06-20-single-session-acp-bridge.zh.md)
## Problem
The ACP bridge now supports multiple live sessions on one JSON-RPC connection. That capability brings multi-entry session maps, reverse session/agent lookups, per-session prompt state, loading ids, demux for every event, cross-session teardown, and isolation concerns for future permission prompts and background tasks. The older [multi-session ACP proposal](../../implemented/feature/2026-06-14-acp-multi-session.md) still tracks the unfinished permission-ownership piece; this Agent Note is the competing simplification path.
The product target has proven it needs concurrent editor conversations over one harness process: Zed's ACP connection owns multiple sessions and load states. The snapshot replay tier still avoids concurrent model streams because its replay entries are positional; that is a test-fixture limitation, not a reason to remove bridge multiplexing.
## Proposal
Scope ACP back to one live session per connection. `session/new` or `session/load` creates the only session record; a second live session request is rejected until the existing session is disposed or the connection closes. If editors need multiple chat tabs, they can launch multiple agent subprocesses until the bridge has a concrete multi-session UX and permission model.
Remove the multi-session maps and demux where a single `SessionRecord | undefined` is enough. The bridge can still keep the agent/session lifecycle seams that make disposal correct; the simplification is only about multiplexing more than one active session through the same transport.
## Acceptance criteria
- ACP has one active session record per connection.
- `session/new` and `session/load` reject while that record exists.
- Event handlers no longer demux across a `Map<sessionId, record>`.
- Multi-session tests are removed or moved under the proposal that continues to defend multiplexing.
- The existing [multi-session ACP proposal](../../implemented/feature/2026-06-14-acp-multi-session.md) is updated to link this Agent Note and remains the live direction.
## What we give up
An ACP client cannot host several concurrent conversations on one server process. That is a meaningful capability cut. The simpler model is still reasonable for an unreleased harness: one editor conversation maps to one agent process, and cross-session permission/background-task isolation stops being a live correctness burden.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,31 @@
# RFC: 将 ACP 桥接恢复为每连接一个活跃会话
Status: rejected — Zed is the current target ACP client and its ACP implementation is explicitly multi-session: it stores live sessions in a `HashMap<SessionId, AcpSession>`, tracks `pending_sessions`, joins concurrent loads for the same id, and tests close-during-load behavior.
[English](2026-06-20-single-session-acp-bridge.md) | 中文
## 问题
ACPAgent Client Protocol桥接现在支持在一条 JSON-RPC 连接上承载多个活跃会话。这一能力带来了多条目会话映射、反向会话/agent智能体查找、逐会话的 prompt 状态、加载中 id、每条事件的解复用、跨会话拆除以及未来权限提示与后台任务的隔离问题。较早的[多会话 ACP 提案](../../implemented/feature/2026-06-14-acp-multi-session.md)仍在追踪未完成的权限归属部分;本 RFC 是与之竞争的简化路径。
产品目标已经证明它需要在一个 harness 进程上承载并发的编辑器对话Zed 的 ACP 连接拥有多个会话和加载状态。快照回放层仍然避免并发模型流,因为其回放条目是位置相关的;这是测试 fixture测试前置数据的局限而非移除桥接多路复用的理由。
## 提案
将 ACP 的范围收回到每连接一个活跃会话。`session/new``session/load` 创建唯一的会话记录;在现有会话被 dispose资源释放或连接关闭之前第二个活跃会话请求将被拒绝。如果编辑器需要多个聊天标签页可以启动多个 agent 子进程,直到桥接具备具体的多会话 UX 和权限模型。
移除多会话映射和解复用逻辑,改用单一的 `SessionRecord | undefined` 即可。桥接仍可保留使 dispose 正确的 agent/会话生命周期 seam简化仅针对在同一传输层上多路复用多个活跃会话这一点。
## 验收标准
- ACP 每连接只有一条活跃会话记录。
- 当该记录存在时,`session/new``session/load` 拒绝请求。
- 事件处理器不再在 `Map<sessionId, record>` 上做解复用。
- 多会话测试被移除,或移至继续支持多路复用的提案下。
- 既有的[多会话 ACP 提案](../../implemented/feature/2026-06-14-acp-multi-session.md)更新为链接本 RFC并继续作为当前方向。
## 放弃了什么
ACP 客户端无法在一个服务器进程上承载多个并发对话。这是一项有实质意义的能力削减。对于一个尚未发布的 harness 而言,更简单的模型仍然合理:一个编辑器对话对应一个 agent 进程,跨会话的权限/后台任务隔离不再是活跃的正确性负担。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-06-20-truncate-interrupted-turns.md: dd8475771fcd9fdd0910bd37480a50679e87911c
2026-06-20-truncate-interrupted-turns.zh.md: 7fcd7292c8c53ebf4d784cdb79a1be58960f763c

View File

@@ -0,0 +1,36 @@
# Agent Note: Truncate interrupted final turns on load
Status: rejected — a single turn can contain substantial real work, including many steps and large tool output. Preserving interrupted turns is preferable to silently dropping that tail on load.
English | [中文](2026-06-20-truncate-interrupted-turns.zh.md)
## Problem
The current persistence contract preserves a final turn that was durably written but never closed. On load, `interruptedTurnClosers()` scans the tail, synthesizes error `tool/result` events for unanswered tool calls, appends a `step/end` when a step is open, appends `turn/end { kind: 'interrupted' }`, and asks the backend to durably commit that repair. The coordinator, JSONL backend, SQLite backend, session event vocabulary, invariants, docs, and tests all model this synthetic close path.
This is a lot of machinery to preserve partial work from the last crashed turn. It also invents events that never happened. A synthetic tool result is useful because it makes provider history valid, but it also means the resumed log contains model-visible text that no tool produced. The current design optimizes for maximum tail preservation before there is a released product or a real resume UX that proves partial-turn recovery matters.
## Proposal
On load, keep only the last completed turn. A backend still tolerates and truncates a torn final record, but if the parsed durable prefix ends after an open `turn/start`, the canonical repair is to drop every event after the previous `turn/end`. No synthetic `tool/result`, no synthetic `step/end`, no `turn/end { interrupted }`, and no `interrupted` turn-end reason.
This makes the persisted turn boundary simple: a completed `turn/end` is the checkpoint. Anything after the last checkpoint is crash tail. The next prompt resumes from the last known-valid provider transcript, not from a partially reconstructed final turn.
## Acceptance criteria
- `TurnEndReasonMap` drops the `interrupted` variant.
- `interruptedTurnClosers()` and its tests disappear.
- The persistence coordinator's repair hook truncates backend-specific torn/open tail state without appending closers.
- [Session persistence docs](../../../../packages/session-persistence/session-persistence/README.md) say load returns the last completed turn, plus no partial final turn.
- Snapshot and contract tests update together with the behavior they pin.
- The session format version and recorded fixtures are refreshed; non-current stored logs are rejected per the pre-release format policy, with no migration path.
## What we give up
A crash can lose real work from the final turn: assistant text, tool calls, and tool output appended after the previous `turn/end`. That is the deliberate simplification. The product is unreleased, the final-turn recovery semantics are not user-proven, and a clean completed-turn checkpoint is much easier to explain, test, and implement. A future "recover partial crashed work" feature should be designed as an explicit user-facing recovery view, not as synthetic events silently inserted into the canonical transcript.
## Related
This is a direct simplification of [session persistence](../../implemented/architecture/2026-06-14-session-persistence.md) and [turn enclosure](../../implemented/architecture/2026-06-15-turn-enclosure-invariant.md). It also removes much of the motivation for durable step boundary events, making [drop durable step boundary events](2026-06-20-drop-durable-step-boundaries.md) smaller.
<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->

View File

@@ -0,0 +1,36 @@
# RFC: 加载时截断被中断的最终轮次
Status: rejected — a single turn can contain substantial real work, including many steps and large tool output. Preserving interrupted turns is preferable to silently dropping that tail on load.
[English](2026-06-20-truncate-interrupted-turns.md) | 中文
## 问题
当前的持久化契约会保留已持久写入但从未关闭的最终轮次。加载时,`interruptedTurnClosers()` 扫描尾部,为未应答的工具调用合成 error `tool/result` 事件,在 step 处于打开状态时追加 `step/end`,追加 `turn/end { kind: 'interrupted' }`并要求后端持久提交这次修复。协调器、JSONL 后端、SQLite 后端、会话事件词汇、不变式、文档和测试都对这条合成关闭路径进行了建模。
这是一套庞大的机制,只为保留上次崩溃轮次中的部分工作。它还会凭空创造从未发生过的事件。合成的工具结果虽然有用(因为它使 provider 历史保持合法),但也意味着恢复后的日志中包含了模型可见、却并非任何工具产出的文本。当前设计在尚无已发布产品、也没有真实恢复 UX 来证明部分轮次恢复确有价值的情况下,就优化了最大化尾部保留。
## 提案
加载时只保留最后一个已完成的轮次。后端仍然容忍并截断撕裂的最终记录,但如果解析出的持久前缀止于一个打开的 `turn/start` 之后,规范的修复方式是丢弃上一个 `turn/end` 之后的所有事件。不合成 `tool/result`,不合成 `step/end`,不追加 `turn/end { interrupted }`,也不引入 `interrupted` 轮次结束原因。
这使持久化的轮次边界变得简单:一个已完成的 `turn/end` 就是检查点。最后一个检查点之后的内容都是崩溃尾部。下一次 prompt 从最后一个已知合法的 provider transcript文本记录恢复而不是从部分重建的最终轮次恢复。
## 验收标准
- `TurnEndReasonMap` 移除 `interrupted` 变体。
- `interruptedTurnClosers()` 及其测试删除。
- 持久化协调器的修复钩子截断后端特有的撕裂/打开尾部状态,不追加关闭事件。
- [会话持久化文档](../../../../packages/session-persistence/session-persistence/README.md)说明加载返回最后一个已完成的轮次,不包含部分最终轮次。
- 快照与契约测试随其所固定的行为一同更新。
- 会话格式版本与记录的 fixture测试前置数据刷新按预发布格式策略非当前版本的存储日志被拒绝不提供迁移路径。
## 放弃的内容
崩溃可能丢失最终轮次中的真实工作:上一个 `turn/end` 之后追加的助手文本、工具调用和工具输出。这是有意为之的简化。产品尚未发布,最终轮次恢复的语义未经用户验证,而一个干净的「已完成轮次即检查点」模型在解释、测试和实现上都容易得多。未来若需「恢复部分崩溃工作」功能,应设计为面向用户的显式恢复视图,而非静默插入规范 transcript 的合成事件。
## 相关
本提案是对[会话持久化](../../implemented/architecture/2026-06-14-session-persistence.md)与[轮次封闭不变式](../../implemented/architecture/2026-06-15-turn-enclosure-invariant.md)的直接简化。它还移除了持久化 step 边界事件的大部分动机,使[移除持久化 step 边界事件](2026-06-20-drop-durable-step-boundaries.md)的改动更小。
<!-- rfc-format: alternatives-not-recorded (pre-format RFC) -->

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-07-04-prune-unimplemented-subagent-vocabulary.md: 1621bc1feee8bf98478242f002d9d1dca16878f5
2026-07-04-prune-unimplemented-subagent-vocabulary.zh.md: b26ffee4764cd5a5937ef0436fa30c7c0eeba87a

View File

@@ -0,0 +1,37 @@
# Agent Note: Prune the unimplemented subagent seam vocabulary
Status: rejected — the deferred capability vocabulary (`outputSchema`/`structured`, `toolFilter`, `sendMessage`/`resume`) is intentionally reserved surface: the seam advertises the full intended contract ahead of its implementations by design, so providers and consumers grow into a stable shape rather than re-negotiating it per capability. The consumer-evidence analysis below records the decision-time state.
## Problem
The [subagent seam](../../implemented/feature/2026-06-21-subagent-capability-seam.md) shipped a two-tier capability design: start-time capability flags checked by the service, and optional runtime methods on `SubagentRun`. Three start-time features and both optional runtime methods have zero implementations and zero callers:
- **`outputSchema`/`structured` and `toolFilter`** (`SubagentCapabilities`, `SubagentStartRequest`, `SubagentResult` in `packages/subagent/subagent/src/types.ts`): at the decision point, every real provider declared `outputSchema: false, toolFilter: false` (`packages/subagent/subagent-spawn/src/index.ts`, `packages/subagent/subagent-fork/src/index.ts`, `packages/subagent/subagent-acp/src/index.ts`); the sole production `ctx.subagents.start` caller (`packages/subagent/tool-subagent/src/index.ts`) built `{ prompt, parent, signal?, agentOptions? }` and structurally could not set either; `structured` appeared only in the scripted test fixture. The service's capability check carried two assert rows whose only exercisers were the rejection tests.
- **`SubagentRun.sendMessage` / `SubagentRun.resume`** (same file): implemented by NO provider — not even the mock; the spawn spec asserts their *absence*.
The only reason `dsh-subagent` depends on `dsh-tools` at all is `outputSchema`'s `SchemaSpec` type. Three subsequent subagent workstreams (per-session snapshot replay, the fork seed boundary, the ACP backend) landed around this surface without growing a single consumer.
## Proposal
Remove `outputSchema`/`structured`, `toolFilter`, `sendMessage`, and `resume` from the seam; shrink `SubagentCapabilities` to `{ depthLimit }`; drop the two capability-assert rows, the all-false flags on the three providers, the scripted fixture's structured branch and capability knobs, and the tests that exist to pin the removed surface. Drop the `dsh-tools` peer/dev dependency from `packages/subagent/subagent/package.json`. Update the [subagent.md](../../../../docs/core-data-structures/subagent.md) pastes and the type-equiv manifest, plus the affected provider READMEs. The implementing PR amends the seam Agent Note's capability catalog per [implemented/AGENTS.md](../../implemented/AGENTS.md).
**Keep** `depthLimit`/`maxDepth` and capability checks. The in-process backend enforces the limit, although the shipping tool does not yet set it. Recursion is a known seam risk, so the appropriate follow-up is to supply a tool default rather than delete working enforcement.
Adjacent surface examined and deliberately left alone: `SubagentService.getProvider()`/`list()` have test-harness consumers only, but the [prune-dead-seam-methods implementation note](../../implemented/simplification/2026-06-20-prune-dead-seam-methods.md) records precisely this shape being removed from the bash executor and reverted — a test harness IS a consumer for a one-line accessor over an already-tracked map. `SubagentRunEndInfo.lastAssistantMessage` is a recorded keep (the [subagent-observe-enrich Agent Note](../../implemented/feature/2026-06-30-subagent-observe-enrich.md)'s review dropped `agentType` and kept it deliberately, as the only final-message channel for out-of-process children); its currently-unwired bridge forwarding is a gap to close or a consumer to document, not surface for this Agent Note to cut.
This is the seam-vocabulary echo of [prune dead methods from the persistence seam](../../implemented/simplification/2026-06-20-prune-dead-seam-methods.md): members every implementation must declare for nobody — weaker even, since here zero implementations exist.
## Alternatives considered
### Why not keep it?
The two-kinds-of-capability design is the seam Agent Note's headline, and re-adding `outputSchema` later touches several files. But the design survives with `depthLimit` as its live example and the Agent Notes as its record, and the seam Agent Note itself concedes the shipped `toolFilter` shape is wrong (real enforcement needs a `tools/pre-execute` deny in the child's context, not schema filtering) — that deny primitive exists on the interception seams, so re-adding against a real implementing provider will pin a better contract than the current speculative one.
## Acceptance criteria
- The removed spellings appear only in this Agent Note and the amended seam Agent Notes; `SubagentCapabilities` is `{ depthLimit: boolean }`; the `dsh-tools` dependency edge is gone (`hygiene` green).
- Depth-enforcement tests are unchanged and green.
## Risks
The subagent lifecycle events carry `lastAssistantMessage` on the end payload — that enrichment lives in the service module, not the seam vocabulary this Agent Note shrinks, and the observe-enrich Agent Note records dropping an `agentType` sibling for lacking a consumer: the judgment this Agent Note extends. The CC hooks bridge, the first outside consumer of those lifecycle events, reads only the event payloads and touches none of the surface removed here; the observe-enrich Agent Note's deferred control-flow redesign names implementing `resume` as its own future work — exactly the re-add trigger this Agent Note's pattern anticipates.

View File

@@ -0,0 +1,39 @@
# RFC: 裁剪未实现的 subagent seam 词汇
Status: rejected — the deferred capability vocabulary (`outputSchema`/`structured`, `toolFilter`, `sendMessage`/`resume`) is intentionally reserved surface: the seam advertises the full intended contract ahead of its implementations by design, so providers and consumers grow into a stable shape rather than re-negotiating it per capability. The consumer-evidence analysis below stands as the record of what is currently unimplemented.
[English](2026-07-04-prune-unimplemented-subagent-vocabulary.md) | 中文
## 问题
[subagent seam](../../implemented/feature/2026-06-21-subagent-capability-seam.md) 交付了一套两层能力设计:启动时由服务检查的能力 flag以及 `SubagentRun` 上的可选运行时方法。三个启动时特性和两个可选运行时方法的实现数与调用数均为零:
- **`outputSchema`/`structured``toolFilter`**`SubagentCapabilities``SubagentStartRequest``SubagentResult`,位于 `packages/subagent/subagent/src/types.ts`):每个真实提供方都声明 `outputSchema: false, toolFilter: false``packages/subagent/subagent-spawn/src/index.ts``packages/subagent/subagent-fork/src/index.ts``packages/subagent/subagent-acp/src/index.ts`);唯一的生产环境 `ctx.subagents.start` 调用方(`packages/subagent/tool-subagent/src/index.ts`)构造 `{ prompt, parent, signal?, agentOptions? }`,结构上无法设置这两个字段;`structured` 仅由测试 mock`packages/support/subagent-mock`)为其自身 spec 产出。服务的能力检查包含两行 assert其唯一执行者是拒绝测试。
- **`SubagentRun.sendMessage` / `SubagentRun.resume`**(同一文件):没有任何提供方实现——包括 mock 也没有spawn spec 断言的正是它们的*缺失*。
`dsh-subagent` 依赖 `dsh-tools` 的唯一原因是 `outputSchema``SchemaSpec` 类型。三个后续 subagent 工作流per-session 快照回放、fork seed 边界、ACPAgent Client Protocol 后端)都围绕这块接口面落地,却没有增长出哪怕一个消费方。
## 提案
从 seam 中移除 `outputSchema`/`structured``toolFilter``sendMessage``resume`;将 `SubagentCapabilities` 缩减为 `{ depthLimit }`;删除两行能力 assert、三个提供方上的 all-false flag、mock 的 structured 分支及其 `capabilities`/`structured` 配置项以及为固定被移除接口面而存在的测试两行拒绝测试、spawn 缺失测试、mock structured spec。从 `packages/subagent/subagent/package.json` 中删除 `dsh-tools` 的 peer/dev 依赖。更新 [subagent.md](../../../core-data-structures/subagent.md) 中的粘贴内容与 type-equiv manifest元数据清单以及 `packages/subagent/subagent``packages/subagent/subagent-spawn``packages/subagent/subagent-fork``packages/support/subagent-mock` 的 README 相关行。实现 PRPull Request按照 [implemented/AGENTS.md](../../implemented/AGENTS.md) 修订 seam RFC 的能力目录。
**保留** `depthLimit`/`maxDepth` 与能力检查。进程内后端已强制执行该限制,尽管当前发布的 tool 尚未设置它。递归是已知的 seam 风险,因此恰当的后续工作是提供一个 tool 默认值,而非删除正在工作的强制逻辑。
审视过但有意不动的相邻接口面:`SubagentService.getProvider()`/`list()` 仅有测试 harness 消费方,但 [prune-dead-seam-methods 实现说明](../../implemented/simplification/2026-06-20-prune-dead-seam-methods.md) 恰好记录了这种形态从 bash executor 中被移除后又被回退的经过——对于一个基于已跟踪 map 的单行访问器而言,测试 harness 就是消费方。`SubagentRunEndInfo.lastAssistantMessage` 是一个已记录的保留项([subagent-observe-enrich RFC](../../implemented/feature/2026-06-30-subagent-observe-enrich.md) 的评审删除了 `agentType` 但有意保留了它,因为它是进程外子 agent智能体唯一的最终消息通道它当前未接通的桥接转发是一个待补的缺口或待记录的消费方不是本 RFC 要裁剪的接口面。
这是[从持久化 seam 裁剪死方法](../../implemented/simplification/2026-06-20-prune-dead-seam-methods.md)在 seam 词汇层面的回响:每个实现都必须为无人声明的成员,甚至更弱,因为这里连一个实现都没有。
## 曾考虑的替代方案
### 为什么不保留?
两类能力的设计是 seam RFC 的核心亮点,日后重新添加 `outputSchema` 会涉及多个文件。但该设计以 `depthLimit` 作为活跃示例、以 RFC 作为记录仍然成立;而且 seam RFC 本身承认已交付的 `toolFilter` 形态是错误的(真正的强制需要在子 agent 上下文中实施 `tools/pre-execute` deny而非 schema 过滤)——该 deny 原语已存在于拦截 seam 上,因此基于真实实现提供方重新添加时,将固定出一份比当前推测性契约更好的契约。
## 验收标准
- 被移除的拼写仅出现在本 RFC 和修订后的 seam RFC 中;`SubagentCapabilities``{ depthLimit: boolean }``dsh-tools` 依赖边已消除(`hygiene` 绿色)。
- 深度强制测试不变且绿色。
## 风险
subagent 生命周期事件在结束载荷上携带 `lastAssistantMessage`——该增强位于服务模块中,不在本 RFC 缩减的 seam 词汇范围内observe-enrich RFC 记录了因缺少消费方而删除 `agentType` 兄弟字段的判断,本 RFC 延续了这一判断。CC hooks 桥接是这些生命周期事件的第一个外部消费方它只读取事件载荷不涉及本文移除的任何接口面observe-enrich RFC 推迟的控制流重设计将实现 `resume` 列为自身的未来工作——恰好是本 RFC 模式所预期的重新添加触发点。

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-07-12-collapse-workflow-to-foreground-core.md: eaf8a4a22766e06b743a7b91d2a607eb6c8e67d9
2026-07-12-collapse-workflow-to-foreground-core.zh.md: 4b1f4ebbec852386ca4577a38a9bd41b7529e500

View File

@@ -0,0 +1,39 @@
# Agent Note: Collapse workflows to the exercised foreground core
Status: rejected — Workflow progress is an intentional observation surface; make it useful through a consumer instead of deleting it.
English | [中文](2026-07-12-collapse-workflow-to-foreground-core.zh.md)
## Problem
The workflow capability executes foreground JavaScript that composes subagents, but it also carries an unconsumed progress-observation system. No production listener subscribes to any of the six `workflow/*` events; listeners exist only in workflow tests. Nevertheless the seam defines run/phase/agent outcome payloads, the worker sends phase/log/agent lifecycle protocol messages, the host forwards them through a `liveAgents` pairing ledger, and the engine maintains run ids solely to correlate those notifications.
The progress vocabulary is not merely unused; it cannot serve its only named future owner without redesign. `WorkflowRunInfo` contains `{id, meta}` but no parent agent, session, or tool-call identity, while the model-facing tool never exposes the run id. A global ACP listener could not route an event to the correct client session. `meta.phases` is never consulted, `phase(title)` does not validate against it, phase `detail`/`model` and agent `label`/`phase` feed only events, and `whenToUse` is validated and copied but never rendered or selected. `phase()` and `log()` still cross the worker boundary despite having no receiver.
The live handle repeats event-era data after those observers disappear. `WorkflowRun.id` has no non-event consumer, while the tool reads `run.meta.name` only to render a value it already owns as `args.meta.name`; neither belongs on the execution/cancellation handle.
Cancellation also has two public channels for one synchronous start. `WorkflowStartRequest.signal` is passed to the worker host, while the sole production caller separately bridges the same signal to `WorkflowRun.cancel()`. Because `start()` returns the run before control can yield, there is no readiness window that requires request-time cancellation; the duplicate signal adds host listener/disarm state without closing a race.
`WorkflowError.fatal` is the same speculative branch in miniature: every production construction is fatal, `fatal: false` exists only in tests, and combinators already distinguish workflow failures with `instanceof`.
## Proposal
Keep the exercised core: `agent(prompt, { schema, model })`, `parallel`, `pipeline`, `args`, concurrency/agent caps, cancellation, bounded disposal, structured results, worker isolation, and foreground tool collection. Remove all `workflow/*` events and their event-only info/outcome types; remove `phase()`, `log()`, agent `label`/`phase`, phase declarations, `whenToUse`, and their worker messages/host observers; collapse workflow metadata to the name the tool actually uses; remove event-only run ids/meta snapshots and the synthesized agent-end ledger. Shrink `WorkflowRun` to `result`, `cancel()`, and `dispose()`; the tool renders the request-owned name. Remove `WorkflowStartRequest.signal` and the worker host's input-signal listener/disarm state, retaining the caller-owned bridge from its abort signal to `run.cancel()`. Make `WorkflowError` one fatal error class without a boolean mode or `isFatalWorkflowError()` helper.
Amend the implemented dynamic-workflow Agent Note and update the seam/tool/worker READMEs, tool schema, generated catalogs and package graph, worker type-equivalence records, unit tests, and workflow snapshot/header fixtures. Progress UI work, if commissioned, starts from a correlation contract that names the parent agent/session/tool call instead of reviving this protocol unchanged.
## Alternatives considered
**Keep the prebuilt observation vocabulary for a future UI.** The current shape resembles Claude Code dynamic-workflow metadata, and the host deliberately pairs each forwarded agent start with either the worker's end or a synthesized terminal end. Removing it gives up compatibility-by-shape and makes progress UI a new design task, but the existing payloads still lack routable ownership, so balanced lifecycles alone cannot make the named ACP owner viable without redesign.
## Acceptance criteria
- The workflow public seam contains only execution, cancellation, result, and disposal contracts with a production consumer.
- No workflow event, phase/log protocol message, run-id generator, progress-only metadata, host pairing ledger, or fatal-mode branch remains.
- The run handle has no id/meta echoes, and cancellation has one holder-owned channel after synchronous `start()` returns.
- Parallel/pipeline behavior, caps, cancellation quiescence, worker containment, structured output, and the model-facing workflow scenarios retain coverage.
- Typecheck, coverage, snapshots, doc-sync, module-graph verification, build, and hygiene pass.
## Risks
This is a compile-visible contraction of the workflow DSL, event taxonomy, handle, and start request. Existing workflow calls that supply descriptive metadata, and scripts that use `phase`, `log`, or labels, must shrink; programmatic callers bridge their own abort source to the returned handle; and a future observer must add a better-correlated seam. The execution semantics that make workflows useful do not change.

View File

@@ -0,0 +1,39 @@
# RFC: 将工作流收缩至已使用的前台核心
Status: rejected — Workflow progress is an intentional observation surface; make it useful through a consumer instead of deleting it.
[English](2026-07-12-collapse-workflow-to-foreground-core.md) | 中文
## 问题
工作流能力执行前台 JavaScript 来编排 subagent但它同时携带了一套无人消费的进度观测系统。没有任何生产环境的监听器订阅六个 `workflow/*` 事件中的任何一个监听器仅存在于工作流测试中。尽管如此seam 定义了 run/phase/agent outcome 载荷worker 发送 phase/log/agent 生命周期协议消息host 通过一个 `liveAgents` 配对账本转发它们,引擎维护 run id 仅仅是为了关联这些通知。
这套进度词汇不仅仅是未被使用;它在不经重新设计的情况下也无法服务于其唯一已命名的未来消费方。`WorkflowRunInfo` 包含 `{id, meta}` 但没有父 agent智能体、会话或工具调用标识而面向模型的工具也从不暴露 run id。一个全局 ACPAgent Client Protocol监听器无法将事件路由到正确的客户端会话。`meta.phases` 从未被查询,`phase(title)` 不对其做校验phase 的 `detail`/`model` 和 agent 的 `label`/`phase` 仅供事件消费,`whenToUse` 被校验和复制但从未被渲染或用于选择。`phase()``log()` 仍然跨越 worker 边界,尽管没有接收方。
live handle 在观测者消失后仍重复事件时代的数据。`WorkflowRun.id` 没有非事件消费方,而工具读取 `run.meta.name` 只是为了渲染一个它已经以 `args.meta.name` 形式持有的值;两者都不属于执行/取消 handle。
取消机制也为一个同步启动提供了两条公开通道。`WorkflowStartRequest.signal` 被传递给 worker host而唯一的生产调用方另外将同一个 signal 桥接到 `WorkflowRun.cancel()`。因为 `start()` 在控制权让出之前就返回了 run不存在需要请求时取消的就绪窗口重复的 signal 增加了 host 的 listener/disarm 状态却没有封堵任何竞态。
`WorkflowError.fatal` 是同一种推测性分支的微缩版:所有生产环境的构造都是 fatal 的,`fatal: false` 仅存在于测试中,组合子已经通过 `instanceof` 区分工作流失败。
## 提案
保留已使用的核心:`agent(prompt, { schema, model })``parallel``pipeline``args`、并发/agent 上限、取消、有界 dispose资源释放、结构化结果、worker 隔离与前台工具收集。移除所有 `workflow/*` 事件及其仅供事件使用的 info/outcome 类型;移除 `phase()``log()`、agent 的 `label`/`phase`、phase 声明、`whenToUse` 及其 worker 消息/host 观测者;将工作流元数据收缩为工具实际使用的 name移除仅供事件使用的 run id/meta 快照与合成的 agent-end 账本。将 `WorkflowRun` 收缩为 `result``cancel()``dispose()`;工具渲染请求方持有的 name。移除 `WorkflowStartRequest.signal` 及 worker host 的 input-signal listener/disarm 状态,保留调用方从其 abort signal 到 `run.cancel()` 的桥接。将 `WorkflowError` 变为单一的 fatal 错误类,不再有布尔模式或 `isFatalWorkflowError()` 辅助函数。
修订已实施的 dynamic-workflow RFC并更新 seam/tool/worker README、工具 schema、生成的 catalog 与 package 依赖图、worker type-equiv 记录、单元测试以及工作流快照/header fixture测试前置数据。如果进度 UI 工作被立项,应从一份命名了父 agent/会话/工具调用的关联契约出发,而非原样复活这套协议。
## 曾考虑的替代方案
**为未来 UI 保留预建的观测词汇。** 当前形态类似 Claude Code 的 dynamic-workflow 元数据host 有意地将每个转发的 agent start 与 worker 的 end 或一个合成的终止 end 配对。移除它意味着放弃形态兼容性,使进度 UI 成为一项全新的设计任务;但现有载荷仍缺少可路由的归属信息,因此仅靠平衡的生命周期也无法在不重新设计的情况下让已命名的 ACP 消费方可行。
## 验收标准
- 工作流公开 seam 仅包含有生产消费方的执行、取消、结果与 dispose 契约。
- 不再保留任何工作流事件、phase/log 协议消息、run-id 生成器、仅供进度使用的元数据、host 配对账本或 fatal 模式分支。
- run handle 不再有 id/meta 回显,取消在同步 `start()` 返回后只有一条持有者拥有的通道。
- parallel/pipeline 行为、上限、取消静默、worker 隔离、结构化输出与面向模型的工作流场景保持测试覆盖。
- 类型检查、覆盖率、快照、doc-sync、module-graph 校验、构建与 hygiene 全部通过。
## 风险
这是对工作流 DSL、事件分类体系、handle 与 start request 的编译可见收缩。现有提供描述性元数据的工作流调用,以及使用 `phase``log` 或 label 的脚本,都必须相应精简;程序化调用方需自行将 abort source 桥接到返回的 handle未来的观测者必须添加一个关联性更好的 seam。使工作流有用的执行语义不变。

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-07-12-prune-unused-skill-registry-surface.md: 5b90deca8681373b2cc2befa3ab341084f924a3d
2026-07-12-prune-unused-skill-registry-surface.zh.md: 7b0e24f49688ed61f2ac4bff93b4c3f85117a170

View File

@@ -0,0 +1,29 @@
# Agent Note: Prune unused skill registry surface
Status: rejected — Direct runtime skill registration is an intentional extension path for third-party plugins.
English | [中文](2026-07-12-prune-unused-skill-registry-surface.zh.md)
## Problem
The skill service's embedded-runtime subsystem has zero production caller of `ctx.skills.register()`. It adds a reserved `runtime` provider name, a runtime map/rank/source, duplicate policy, a second revision in cache keys, normalization, disposers, and tests alongside the provider seam every shipped skill already uses. `SkillSummary.whenToUse` and candidate/definition `path` are parsed and copied but never read by a production consumer: the model catalog renders name/description, resource loading uses `resourceBase`, and providers own their locator. The deliberately open `metadata` extension point stays.
## Proposal
Remove `SkillService.register()`, `SkillRegistration`, the runtime pseudo-provider and reserved-name rules, runtime revisions/cache branches, and runtime-only source/rank normalization. Tests that need an embedded skill register a small real provider. Retain `providerRevision` as the in-flight discovery epoch, but key completed catalogs by cwd alone: every provider mutation synchronously clears the cache, and the post-await revision comparison already prevents inserting stale work. Remove `whenToUse`, `SkillCandidate.path`, and `SkillDefinition.path` from the skill contract and local-provider copies while retaining provider locator/root paths; retain `metadata`, `disableModelInvocation`, `source`, `provider`, `locator`, and `resourceBase` as either deliberate extension vocabulary or production-consumed fields.
Amend the skill-system Agent Note, README, JSDoc, catalogs, and tests. Agent-scoped system-prompt sections, tool providers, and variables are explicitly outside this proposal: the [agent-scope contributor contract](../../implemented/architecture/2026-07-08-agent-scope-contexts.md) intentionally allows all three to be registered during `setup(agentCtx)` through the agent-owned context, so absence of a fixed in-repo scoped registration is not evidence of non-consumption.
## Alternatives considered
**Keep runtime skill registration for embedders.** It is a deliberate synchronous direct-definition convenience in the implemented skill Agent Note. A small provider wrapper can expose the same embedded data under effect-owned lifetime, but it must implement async `list()`/`get()`, carry provider identity, and accept provider duplicate semantics. The proposal chooses that one regular path over preserving a second ranking, validation, cache-invalidation, and lookup path.
## Acceptance criteria
- Skill collection has one provider-backed path, a cwd-only completed-cache key, and a revision epoch only for in-flight invalidation; retained skill fields have a production reader or a recorded deliberate extension contract.
- Agent-scoped prompt sections, variables, tool providers, tool guards, and structured-output commit behavior in native and Code Mode remain unchanged.
- Typecheck, coverage, snapshots, doc-sync, module-graph verification, build, and hygiene pass.
## Risks
This is a compile-visible contraction of the pre-release skill registry. External programmatic `list()`/`get()` consumers lose `whenToUse` routing hints and candidate/definition `path`; the shipped model catalog never renders them, and resource resolution keeps its explicit `resourceBase` plus provider-owned opaque locator, but those fields are not observationally identical. Skill-local frontmatter parsing must continue to preserve and validate the supported metadata schema, and external providers remain able to supply embedded, filesystem, remote, or other skill sources.

View File

@@ -0,0 +1,29 @@
# RFC: 裁剪 skill 注册表中未使用的接口
Status: rejected — Direct runtime skill registration is an intentional extension path for third-party plugins.
[English](2026-07-12-prune-unused-skill-registry-surface.md) | 中文
## 问题
skill技能服务的嵌入式运行时子系统中`ctx.skills.register()` 没有任何生产调用方。它引入了一个保留的 `runtime` 提供方名称、一套运行时 map/rank/source、重复策略、缓存键中的第二个 revision、规范化逻辑、dispose资源释放器以及相应测试——而所有已交付的 skill 都只使用提供方 seam。`SkillSummary.whenToUse` 和 candidate/definition 的 `path` 被解析和复制,但没有任何生产消费方读取它们:模型目录只渲染 name/description资源加载使用 `resourceBase`,提供方自行管理其定位器。有意开放的 `metadata` 扩展点保留不动。
## 提案
移除 `SkillService.register()``SkillRegistration`、运行时伪提供方及保留名称规则、运行时 revision/缓存分支,以及仅用于运行时的 source/rank 规范化逻辑。需要嵌入式 skill 的测试改为注册一个小型真实提供方。保留 `providerRevision` 作为进行中的发现 epoch但已完成的目录缓存仅以 cwd 为键每次提供方变更同步清除缓存await 之后的 revision 比较已能阻止插入陈旧结果。从 skill 契约和 local-provider 副本中移除 `whenToUse``SkillCandidate.path``SkillDefinition.path`,同时保留提供方的 locator/root 路径;保留 `metadata``disableModelInvocation``source``provider``locator``resourceBase`,因为它们要么是有意开放的扩展词汇,要么是生产消费的字段。
同步修订 skill 系统 RFC、README、JSDoc、目录文件与测试。agent智能体作用域的系统提示词段、工具提供方和变量明确不在本提案范围内[agent 作用域贡献者契约](../../implemented/architecture/2026-07-08-agent-scope-contexts.md)有意允许在 `setup(agentCtx)` 期间通过 agent 拥有的上下文注册这三者,因此仓库内没有固定的作用域注册并不能证明它们未被使用。
## 曾考虑的替代方案
**保留面向嵌入方的运行时 skill 注册。** 这是已实现的 skill RFC 中有意提供的同步直接定义便利接口。一个小型提供方包装层可以在 effect 拥有的生命周期下暴露相同的嵌入数据,但它必须实现异步 `list()`/`get()`、携带提供方身份,并接受提供方的重复语义。本提案选择只保留一条统一的提供方路径,而非维护第二套排序、校验、缓存失效与查找路径。
## 验收标准
- skill 收集只有一条提供方驱动的路径,已完成缓存仅以 cwd 为键revision epoch 仅用于进行中的失效检测;保留的 skill 字段要么有生产读取方,要么有记录在案的有意扩展契约。
- agent 作用域的提示词段、变量、工具提供方、工具守卫,以及原生模式和 Code Mode 下的 structured-output 提交行为保持不变。
- 类型检查、覆盖率、快照、doc-sync文档同步门禁、module-graph 校验、构建与 hygiene 全部通过。
## 风险
这是对预发布 skill 注册表的编译可见收缩。外部编程式 `list()`/`get()` 消费方将失去 `whenToUse` 路由提示和 candidate/definition 的 `path`;已交付的模型目录从未渲染它们,资源解析保留了显式的 `resourceBase` 加上提供方自有的不透明 locator但这些字段并非观测等价。skill 本地 frontmatter 解析必须继续保留并校验所支持的 metadata schema外部提供方仍可提供嵌入式、文件系统、远程或其他 skill 来源。

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-07-19-fold-compaction-package-split.md: 47c9feb6bb0dd06fec0f002b7c1e930b288abe5e
2026-07-19-fold-compaction-package-split.zh.md: 53717ff10d1210bd2072f322d1936ac6c389afcd

View File

@@ -0,0 +1,37 @@
# Agent Note: Fold the single compaction backend into its service package
Status: rejected — More compaction backends are planned, so the interface and basic implementation packages remain separate.
English | [中文](2026-07-19-fold-compaction-package-split.zh.md)
## Problem
Compaction is split between `@deepseek-ai/dsh-compact`, which owns an abstract two-method service and shared types, and `@deepseek-ai/dsh-compact-basic`, which owns the only complete implementation. Shipped configurations load only the basic package, and no production package independently consumes the interface package except that implementation.
The split adds a package manifest, README, project boundary, dependency edge, abstract forwarding class, generated catalog entries, and composition wiring without demonstrating backend substitution. The [capability-seam decision](../../implemented/architecture/2026-06-13-capability-seams.md) requires a real interface, implementation, and consumer rather than a preemptive split; the [compaction decision](../../implemented/feature/2026-06-18-compaction-capability-seam.md) records that its independent consumer was deferred.
## Proposal
Move the basic implementation into `@deepseek-ai/dsh-compact` and remove `@deepseek-ai/dsh-compact-basic`. Keep `ctx.compact`, `CompactionResult`, the shared transcript and tool-pairing helpers, the existing configuration, and the concrete compaction algorithm in one package.
Preserve `summarize()` as a protected customization hook. A deployment-specific summarizer can subclass or intercept the existing LLM call without requiring a second capability package. Reintroduce an interface package only when a second complete backend and an independent consumer need substitution.
Amend the implemented compaction decision and the [recallable-compaction proposal](../../proposed/feature/2026-07-06-recallable-compaction.md) if this proposal is accepted so package ownership has one durable description.
## Alternatives considered
**Keep the split because a remote or recall backend may arrive.** A possible future implementation does not justify the current package boundary. Recall adds a consumer of compaction results, not necessarily another implementation, and a remote summarizer can use the protected hook.
**Move the implementation package name onto the interface package.** Keeping `compact-basic` as the surviving name would make the product service appear to be one optional backend. `compact` is the stable service identity already used by `ctx.compact` and is the clearer single-package owner.
## Acceptance criteria
- `@deepseek-ai/dsh-compact-basic` and its workspace/package metadata are removed.
- `@deepseek-ai/dsh-compact` owns the current configuration, plugin class, algorithm, types, events, and shared helpers.
- Existing deployments can load the surviving package with equivalent configuration and model-visible behavior.
- Automatic and manual compaction preserve cancellation, locking, token accounting, tool pairing, durable events, provenance, retry convergence, and transcript rendering.
- Loader composition, unit, runaway-turn, cancellation, snapshot, and real-model compaction tests pass; generated catalogs and module graphs are current.
## Risks
This is an intentional pre-release package-name contraction. Embedders loading `@deepseek-ai/dsh-compact-basic` must switch packages, and future backend substitution would require extracting a boundary again. The cost is acceptable only while one complete implementation exists; acceptance should be revisited if a second backend lands first.

View File

@@ -0,0 +1,37 @@
# Agent Note: 将唯一的压缩后端并入服务包
Status: rejected — 计划增加更多压缩后端,因此接口包与 basic 实现包继续分离。
[English](2026-07-19-fold-compaction-package-split.md) | 中文
## 问题
压缩compaction目前拆分在两个包中`@deepseek-ai/dsh-compact` 拥有一个含两个方法的抽象服务和共享类型,`@deepseek-ai/dsh-compact-basic` 拥有唯一的完整实现。交付配置只加载 basic 包,除了该实现外,没有生产包独立消费接口包。
该拆分增加了一份包packagemanifest元数据清单、README、项目边界、依赖边、抽象转发类、生成目录项和组合接线却没有体现后端替换需求。[能力服务边界决策](../../implemented/architecture/2026-06-13-capability-seams.md)要求接口、实现和消费方都必须真实存在,而不能预先拆分;[压缩决策](../../implemented/feature/2026-06-18-compaction-capability-seam.md)也记录了独立消费方仍被推迟。
## 提案
把 basic 实现移入 `@deepseek-ai/dsh-compact`,并删除 `@deepseek-ai/dsh-compact-basic``ctx.compact``CompactionResult`、共享 transcript文本记录和工具配对辅助方法、现有配置以及具体压缩算法都由一个包负责。
保留 `summarize()` 作为受保护的自定义钩子。部署专用的摘要器可以通过继承或拦截现有 LLM大语言模型调用完成定制无需第二个能力包。只有在第二个完整后端与独立消费方确实需要替换实现时才重新提取接口包。
如果本提案获准,应同步修订已实现的压缩决策与[可回忆压缩提案](../../proposed/feature/2026-07-06-recallable-compaction.md),使包所有权只有一处持久说明。
## 备选方案
**为可能出现的远程或回忆后端保留拆分。** 一种可能的未来实现不足以支撑当前包边界。回忆功能会增加压缩结果的消费方,但不一定增加另一种实现;远程摘要器也可以使用受保护钩子。
**让接口包并入实现包名。** 如果保留 `compact-basic` 作为最终名称,产品服务会看起来像一个可选后端。`compact` 已经是 `ctx.compact` 使用的稳定服务标识,更适合作为单包所有者。
## 验收标准
- 删除 `@deepseek-ai/dsh-compact-basic` 及其工作区和包元数据。
- `@deepseek-ai/dsh-compact` 拥有当前配置、插件类、算法、类型、事件和共享辅助方法。
- 现有部署可以使用等效配置加载保留的包,模型可见行为不变。
- 自动压缩和手动压缩保留取消、锁、token 用量、工具配对、持久事件、来源、重试收敛和 transcript 渲染行为。
- Loader 组合、单元、失控轮次、取消、快照和真实模型压缩测试全部通过;生成目录与模块图保持最新。
## 风险
这是一项有意实施的预发布包名收缩。加载 `@deepseek-ai/dsh-compact-basic` 的嵌入方必须切换包,未来的后端替换也需要重新提取边界。只有在仍然只有一个完整实现时,这项代价才可接受;如果第二个后端先行落地,应重新评估是否接纳本提案。