From ab5f409d65e61c90ccf6504667ef7214077e7fd2 Mon Sep 17 00:00:00 2001 From: kina0630 Date: Tue, 19 May 2026 04:21:40 +0800 Subject: [PATCH] test(runtime): close durable storage and tracing audit --- .../tasks/durable-runtime-g03-g08-audit.md | 57 +++++++++++++++++++ .../docs/durable_runtime/GOAL_PROGRESS.md | 22 ++++++- .../tests/agentkit/runtime/test_pipeline.py | 20 ++++++- 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 coding-agent/.opencode/prompts/tasks/durable-runtime-g03-g08-audit.md diff --git a/coding-agent/.opencode/prompts/tasks/durable-runtime-g03-g08-audit.md b/coding-agent/.opencode/prompts/tasks/durable-runtime-g03-g08-audit.md new file mode 100644 index 000000000..f2c0cca07 --- /dev/null +++ b/coding-agent/.opencode/prompts/tasks/durable-runtime-g03-g08-audit.md @@ -0,0 +1,57 @@ +Goal: +Audit and close active durable runtime G03 and G08 progress gaps. + +Scope: +- Confirm G03 storage/plugin/composition wiring preserves JSONL defaults and + only enables PostgreSQL runtime storage when `storage.runtime_backend = "pg"`. +- Confirm G08 observability correlation propagates allowed runtime identity + keys and rejects unsafe raw prompt/content/message/result/secret/text-style + attributes. +- Add focused test coverage only where the audit evidence is too implicit. +- Update `docs/durable_runtime/GOAL_PROGRESS.md`. + +Out of scope: +- Do not change production storage behavior. +- Do not change OTLP/Langfuse exporter behavior. +- Do not introduce new runtime stores, endpoints, or migrations. +- Do not work on active G09 tape debug APIs or active G11 smoke docs/tests in + this slice. + +Context: +- ADRs: + - `docs/adr/0029-durable-runtime-identity.md` + - `docs/adr/0030-postgresql-durable-runtime-store.md` + - `docs/adr/0032-durable-runtime-lifecycle-statuses.md` +- Postmortem patterns consulted: + - `postmortem/patterns/PM-0001-address-code-review-issues.md` + - `postmortem/patterns/PM-0006-add-usage-event-fields-and-fix-tool-name-kwarg-in-pipeline.md` + - `postmortem/patterns/PM-0009-preserve-neutral-bare-anchor-semantics.md` + - `postmortem/patterns/PM-0010-route-incremental-context-append-through-tapeview.md` +- Relevant files: + - `src/coding_agent/ui/session_manager.py` + - `src/coding_agent/observability.py` + - `src/agentkit/runtime/pipeline.py` + - `tests/ui/test_session_manager_public_api.py` + - `tests/agentkit/runtime/test_pipeline.py` + - `tests/coding_agent/test_observability.py` + - `docs/durable_runtime/GOAL_PROGRESS.md` + +Target tests: +- `uv run pytest tests/ui/test_session_manager_public_api.py -k "storage_config or runtime_store or pg_backends or pg_pool or runtime_backend" -v` +- `uv run pytest tests/agentkit/runtime/test_pipeline.py -k "span" -v` +- `uv run pytest tests/coding_agent/test_observability.py -v` +- `uv run ruff check tests/agentkit/runtime/test_pipeline.py` +- `uv run ruff format --check tests/agentkit/runtime/test_pipeline.py` + +Loop policy: +- Engineer implements the smallest evidence/coverage change and runs target + tests. +- Reviewer reviews only the resulting diff and affected tests. +- Reviewer reports only P1/P2 findings. +- Engineer fixes only accepted P1/P2 findings and reruns the same target tests. +- Verifier reruns the exact target tests and reports pass/fail only. + +Stop conditions: +- Stop with a blocker if G03 requires production storage changes. +- Stop with a blocker if G08 requires weakening the observability denylist. +- Stop with a blocker if this audit expands into G09 tape debug work. diff --git a/coding-agent/docs/durable_runtime/GOAL_PROGRESS.md b/coding-agent/docs/durable_runtime/GOAL_PROGRESS.md index 068ce9ad4..820d71c08 100644 --- a/coding-agent/docs/durable_runtime/GOAL_PROGRESS.md +++ b/coding-agent/docs/durable_runtime/GOAL_PROGRESS.md @@ -13,12 +13,12 @@ match the active objective map. | G00 | Complete | `docs/durable_runtime/CURRENT_STATE.md` documents SessionManager, PipelineAdapter, storage plugins, PG storage, observability, approval flow, event flow, and tape flow. | | G01 | Complete | `docs/adr/0029-durable-runtime-identity.md` defines session, run, turn compatibility, tape, event, interaction, checkpoint, and Langfuse/OTLP correlation identity. | | G02 | Complete | `src/coding_agent/runtime_store.py` adds the PostgreSQL durable runtime store, with unit coverage in `tests/coding_agent/test_pg_runtime_store.py` and ADR-0030. | -| G03 | Needs audit | `storage.runtime_backend = "pg"` opt-in wiring exists, but the storage/plugin/composition boundary still needs final audit against the active objective wording. | +| G03 | Complete | Storage configuration preserves JSONL/no-runtime defaults and opt-in PG composition wires tape, checkpoint, HTTP session, and runtime stores through tests in `tests/ui/test_session_manager_public_api.py`. | | G04 | Complete | ADR-0032 and `SessionManager` now persist `queued -> running -> completed/failed/cancelled/interrupted` root run lifecycle rows while preserving `current_turn_id` compatibility. | | G05 | Complete | `SessionManager` persists runtime wire events and `{run_id}:latest` compacted message snapshots when a runtime store is configured. | | G06 | Complete | HTTP replay APIs expose runtime run records, latest message snapshots, and runtime events with `last_event_id` filtering. | | G07 | Complete | `SessionManager` persists durable approval interaction records for pending requests, applied decisions, session auto-approvals, and approval timeouts when a runtime store is configured. | -| G08 | Needs audit | AgentKit and HTTP root run correlation were added, but final audit must confirm the active objective's full safe attribute set is populated where required. | +| G08 | Complete | AgentKit stage/LLM spans include safe runtime correlation keys (`turn_id`, `tape_id`, `tool_call_id`, `interaction_id`, `event_id`, `checkpoint_id`), HTTP root runs bind `turn_id`/`tape_id`, and OTLP privacy tests reject raw prompt/message/result/secret/text attributes. | | G09 | Pending | `tape.info` and `tape.search` for PostgreSQL tape storage are still missing. | | G10 | Complete | ADR-0032 changes startup orphan recovery to mark active-owner `running` rows `interrupted` with `reclaimable: true`, without adding a scheduler. | | G11 | Pending | End-to-end smoke tests/docs for normal run, failed run, approval run, replay, Langfuse/OTLP correlation, and tape debug remain to be added. | @@ -48,6 +48,24 @@ match the active objective map. - `postmortem/patterns/PM-0022-revalidate-event-stream-ownership-after-queue-attach.md` - `postmortem/patterns/PM-0023-make-event-stream-cleanup-and-teardown-idempotent.md` +## Active G03/G08 Audit Verification + +- Added `.opencode/prompts/tasks/durable-runtime-g03-g08-audit.md`. +- Updated `tests/agentkit/runtime/test_pipeline.py` to assert all allowed + runtime correlation metadata keys are propagated while unsafe/non-scalar + metadata is dropped. +- Updated this progress table to move active G03 and G08 from `Needs audit` to + `Complete`. +- Baseline and target tests: + - `uv run pytest tests/ui/test_session_manager_public_api.py -k "storage_config or runtime_store or pg_backends or pg_pool or runtime_backend" -v` + - `uv run pytest tests/agentkit/runtime/test_pipeline.py -k "span" -v` + - `uv run pytest tests/coding_agent/test_observability.py -v` +- Postmortem patterns consulted: + - `postmortem/patterns/PM-0001-address-code-review-issues.md` + - `postmortem/patterns/PM-0006-add-usage-event-fields-and-fix-tool-name-kwarg-in-pipeline.md` + - `postmortem/patterns/PM-0009-preserve-neutral-bare-anchor-semantics.md` + - `postmortem/patterns/PM-0010-route-incremental-context-append-through-tapeview.md` + ## Historical Slice Log ## G00 Verification diff --git a/coding-agent/tests/agentkit/runtime/test_pipeline.py b/coding-agent/tests/agentkit/runtime/test_pipeline.py index 31b5c70f3..8b207e0df 100644 --- a/coding-agent/tests/agentkit/runtime/test_pipeline.py +++ b/coding-agent/tests/agentkit/runtime/test_pipeline.py @@ -386,8 +386,16 @@ async def mock_stream(messages, tools=None, **kwargs): context_budget=ContextBudget(), trace_metadata={ "turn_id": "run-1", + "tool_call_id": "tool-call-1", "interaction_id": "interaction-1", + "event_id": "event-1", + "checkpoint_id": "checkpoint-1", "prompt": "do-not-record", + "message": "do-not-record", + "result": "do-not-record", + "secret": "do-not-record", + "text": "do-not-record", + "ignored_object": {"nested": "do-not-record"}, }, ), config={"observation_sink": sink}, @@ -416,11 +424,21 @@ async def mock_stream(messages, tools=None, **kwargs): "agent_id": "root", "tape_id": "tape-1", "turn_id": "run-1", + "tool_call_id": "tool-call-1", "interaction_id": "interaction-1", + "event_id": "event-1", + "checkpoint_id": "checkpoint-1", "entry_count_before": 0, "entry_count_after": 0, } - assert "prompt" not in build_context_span.attributes + assert { + "prompt", + "message", + "result", + "secret", + "text", + "ignored_object", + }.isdisjoint(build_context_span.attributes) model_span = stage_spans[3] assert model_span.attributes["entry_count_before"] == 0 assert model_span.attributes["entry_count_after"] == 1