Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions docs/architecture/deepchat-tape-policy-provenance/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# DeepChat Tape Policy Provenance - Plan

## Architecture Decision

Use the existing `TapeViewAssemblerResult.policyId` and `policyVersion` as the source of truth for
initial chat and resume manifests. Keep request-level tool-loop and context-pressure recovery
manifests as shadow policies because they are post-assembly request transformations.

## Flow

```text
TapeViewAssembler.buildTapeChatView()
-> result.policyId = legacy_context_v1
-> runStreamForMessage(viewContext.policy = result.policyId)
-> ViewManifest.policy = legacy_context_v1
-> ViewManifest.policyVersion = 1

Tool loop / context pressure recovery
-> request-level ViewManifest
-> shadow policy label
-> policyVersion = null
```

## Module Changes

| Module | Change |
| --- | --- |
| `src/shared/types/tape-view-manifest.ts` | Add `legacy_context_v1` and `policyVersion`. |
| `src/main/presenter/agentRuntimePresenter/tapeViewManifest.ts` | Persist policy version in manifest. |
| `src/main/presenter/agentRuntimePresenter/index.ts` | Pass assembler policy id/version into view context. |
| `src/main/presenter/agentRuntimePresenter/tapeService.ts` | Store policy version in event metadata. |
| `src/renderer/src/components/trace/TraceDialog.vue` | Show policy version when present. |
| Tests | Update manifest, service, and trace expectations. |

## Compatibility

- Existing shadow policy strings remain accepted by shared types and UI.
- Old manifests without `policyVersion` continue to render; the field is treated as absent/null.
- Replay slice export keeps its current lookup behavior.

## Verification

```bash
pnpm vitest run test/main/presenter/agentRuntimePresenter/tapeViewManifest.test.ts
pnpm vitest run test/main/presenter/agentRuntimePresenter/tapeService.test.ts
pnpm vitest run test/renderer/components/trace/TraceDialog.test.ts
pnpm run format
pnpm run i18n
pnpm run lint
pnpm run typecheck:node
pnpm run typecheck:web
```
56 changes: 56 additions & 0 deletions docs/architecture/deepchat-tape-policy-provenance/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# DeepChat Tape Policy Provenance - Spec

Status: implemented SDD. This goal records the active Tape view policy in every ViewManifest.

## Problem

`TapeViewAssembler` returns `policyId` and `policyVersion`, but `ViewManifest.policy` still stores
the older shadow labels such as `legacy_context_shadow` and `resume_shadow`. This weakens the Tape
audit trail because the persisted manifest does not identify the actual `TapeViewPolicy` that
selected the initial chat or resume context.

## Goals

1. Store the active `TapeViewPolicy` id in `ViewManifest.policy` for initial chat and resume
requests.
2. Store the active policy version in `ViewManifest.policyVersion`.
3. Preserve existing `tool_loop_shadow`, `context_pressure_recovery_shadow`, and legacy shadow
policy labels for compatibility.
4. Show policy version in TraceDialog when present.
5. Keep replay export and manifest lookup compatible with old manifest records.

## Non-Goals

- Changing the default policy away from `legacy_context_v1`.
- Adding user-facing policy selection.
- Changing token-budget selection.
- Rewriting tool-loop or context-pressure recovery selection.

## Acceptance Criteria

1. Normal chat manifests use `policy = "legacy_context_v1"` and `policyVersion = 1`.
2. Resume manifests use `policy = "legacy_context_v1"` and `policyVersion = 1`.
3. Tool-loop manifests keep `policy = "tool_loop_shadow"` and `policyVersion = null`.
4. Context-pressure recovery manifests keep `policy = "context_pressure_recovery_shadow"` and
`policyVersion = null`.
5. Existing shadow policy values remain valid manifest values.
6. TraceDialog displays policy version when the manifest includes one.
7. Manifest, runtime, trace UI, replay, lint, typecheck, and targeted tests pass.

## Contract

```ts
export type DeepChatTapeViewPolicy =
| 'legacy_context_v1'
| 'legacy_context_shadow'
| 'resume_shadow'
| 'tool_loop_shadow'
| 'context_pressure_recovery_shadow'

export interface DeepChatTapeViewManifest {
policy: DeepChatTapeViewPolicy
policyVersion: number | null
}
```

`legacy_context_shadow` and `resume_shadow` are accepted for older persisted manifests only.
11 changes: 11 additions & 0 deletions docs/architecture/deepchat-tape-policy-provenance/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# DeepChat Tape Policy Provenance - Tasks

## Implementation Tasks

- [x] T1: Add `legacy_context_v1` and `policyVersion` to the ViewManifest contract.
- [x] T2: Pass assembler policy id/version into chat and resume ViewManifest creation.
- [x] T3: Keep tool-loop and context-pressure recovery manifests on shadow policy labels with null
policy version.
- [x] T4: Show policy version in TraceDialog when available.
- [x] T5: Update baseline docs and tests.
- [x] T6: Run focused tests, format, i18n, lint, and typecheck.
50 changes: 50 additions & 0 deletions docs/architecture/deepchat-tape-policy-selector/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# DeepChat Tape Policy Selector - Plan

## Architecture Decision

Keep selector logic inside `src/main/presenter/agentRuntimePresenter/tapeViewPolicy.ts`. This avoids
adding a service and keeps the policy boundary local to the assembler.

## Flow

```text
TapeViewAssembler.buildTapeChatView()
-> resolveTapeViewPolicy()
-> policy.buildChat()
-> return messages + policy id/version + selection reason

TapeViewAssembler.buildTapeResumeView()
-> resolveTapeViewPolicy()
-> policy.buildResume()
-> return messages + policy id/version + selection reason
```

## Module Changes

| Module | Change |
| --- | --- |
| `src/main/presenter/agentRuntimePresenter/tapeViewPolicy.ts` | Add registry, lookup, list, and resolver helpers. |
| `src/main/presenter/agentRuntimePresenter/tapeViewAssembler.ts` | Resolve default policy through selector. |
| `test/main/presenter/agentRuntimePresenter/tapeViewPolicy.test.ts` | Cover registry and selector behavior. |
| `test/main/presenter/agentRuntimePresenter/tapeViewAssembler.test.ts` | Assert selection reason and injected policy behavior. |
| `docs/architecture/deepchat_tape_spec_v1.md` | Record the selector boundary. |

## Compatibility

- The default resolved policy remains `legacy_context_v1`.
- Existing injected policy tests continue to work.
- ViewManifest policy provenance remains `legacy_context_v1@1` for chat and resume.

## Verification

```bash
pnpm vitest run test/main/presenter/agentRuntimePresenter/tapeViewPolicy.test.ts
pnpm vitest run test/main/presenter/agentRuntimePresenter/tapeViewAssembler.test.ts
pnpm vitest run test/main/presenter/agentRuntimePresenter/tapeViewManifest.test.ts
pnpm run format
pnpm run i18n
pnpm run lint
pnpm run typecheck:node
pnpm run typecheck:web
pnpm vitest run --reporter=dot
```
54 changes: 54 additions & 0 deletions docs/architecture/deepchat-tape-policy-selector/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# DeepChat Tape Policy Selector - Spec

Status: implemented SDD. This goal adds a policy registry and default selector for Tape view
assembly.

## Problem

`TapeViewAssembler` can accept an injected policy, but production assembly still selects the legacy
policy inline. The architecture needs a small selector boundary so future policy expansion can add
new policies without changing chat or resume assembly call sites.

## Goals

1. Add a `TapeViewPolicy` registry in the existing policy module.
2. Resolve the active policy through a selector for chat and resume assembly.
3. Keep `legacy_context_v1` as the default policy for all sessions.
4. Preserve current provider-bound message output.
5. Return policy selection reason in assembler metadata for audit/debugging.

## Non-Goals

- Introducing a new context-selection algorithm.
- Adding user-facing policy settings.
- Changing compaction, preflight, or context-pressure recovery.
- Adding a separate policy service or persistence table.

## Acceptance Criteria

1. `TapeViewAssembler` uses `resolveTapeViewPolicy()` for default policy selection.
2. `resolveTapeViewPolicy()` returns `legacy_context_v1` with reason `default` when no policy is
requested.
3. Unknown requested policy ids fall back to `legacy_context_v1` with reason `fallback_default`.
4. Injected test policies remain supported and report reason `injected`.
5. Assembler output remains provider-message equivalent with the previous implementation.
6. Policy registry tests cover list, lookup, default selection, and fallback selection.
7. Focused Tape tests, format, i18n, lint, typecheck, and full Vitest pass.

## Contract

```ts
export type TapeViewPolicySelectionReason = 'default' | 'requested' | 'fallback_default' | 'injected'

export interface TapeViewPolicySelection {
policy: TapeViewPolicy
requestedPolicyId: string | null
reason: TapeViewPolicySelectionReason
}

export function resolveTapeViewPolicy(input?: {
requestedPolicyId?: string | null
}): TapeViewPolicySelection
```

The first registry contains only `legacy_context_v1`.
10 changes: 10 additions & 0 deletions docs/architecture/deepchat-tape-policy-selector/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# DeepChat Tape Policy Selector - Tasks

## Implementation Tasks

- [x] T1: Add registry, lookup, list, and resolver helpers to `tapeViewPolicy.ts`.
- [x] T2: Route `TapeViewAssembler` default selection through the resolver.
- [x] T3: Add selection reason to `TapeViewAssemblerResult`.
- [x] T4: Update policy and assembler tests.
- [x] T5: Update baseline Tape architecture docs.
- [x] T6: Run focused tests, format, i18n, lint, typecheck, and full tests.
58 changes: 58 additions & 0 deletions docs/architecture/deepchat-tape-replay-contract/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# DeepChat Tape Replay Contract - Plan

## Architecture Decision

Add a replay-slice export on top of `DeepChatTapeService`. The service already owns manifest lookup
and has access to the SQLite presenter, so it remains the single Tape boundary.

## Flow

```text
renderer SessionClient.exportMessageTapeReplaySlice(messageId, options)
-> sessions.exportMessageTapeReplaySlice route
-> AgentSessionPresenter.exportMessageTapeReplaySlice(messageId, options)
-> AgentRuntimePresenter.exportMessageTapeReplaySlice(sessionId, messageId, options)
-> DeepChatTapeService.exportReplaySlice(sessionId, messageId, options)
-> select manifest by requestSeq or latest
-> find matching message trace by requestSeq
-> collect manifest/included/excluded/anchor tape entries
-> return deterministic replay slice
```

## Module Changes

| Module | Change |
| --- | --- |
| `src/shared/types/tape-replay.ts` | Add replay slice, trace snapshot, entry snapshot, and options types. |
| `src/main/presenter/agentRuntimePresenter/tapeService.ts` | Add `exportReplaySlice()`. |
| `src/main/presenter/agentRuntimePresenter/index.ts` | Expose agent-level replay export method. |
| `src/main/presenter/agentSessionPresenter/index.ts` | Resolve `messageId -> sessionId -> agent`. |
| `src/shared/contracts/routes/sessions.routes.ts` | Add typed replay export route. |
| `src/main/routes/index.ts` | Wire the route. |
| `src/renderer/api/SessionClient.ts` | Add replay export client method. |
| `test/main/presenter/agentRuntimePresenter/tapeService.test.ts` | Cover replay export behavior. |

## Hashing

- `entry.payloadHash`: hash of raw `payload_json`.
- `entry.metaHash`: hash of raw `meta_json`.
- `trace.headersHash`: hash of raw `headers_json`.
- `trace.bodyHash`: hash of raw `body_json`.
- `sliceHash`: deterministic hash of the returned slice with `sliceHash` empty.

## Compatibility

- Missing tape table returns `null`.
- Missing manifest returns `null`.
- Missing matching trace returns a manifest-only slice.
- Old traces keep working through existing trace APIs.

## Verification

```bash
pnpm vitest run test/main/presenter/agentRuntimePresenter/tapeService.test.ts
pnpm run format
pnpm run i18n
pnpm run lint
pnpm run typecheck
```
86 changes: 86 additions & 0 deletions docs/architecture/deepchat-tape-replay-contract/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# DeepChat Tape Replay Contract - Spec

Status: implemented SDD. This goal extends the completed ViewManifest shadow-mode increment with a
stable replay/export contract.

## Problem

ViewManifest records which context facts participated in each provider request. The next Tape
architecture layer needs a typed export shape that joins a manifest, matching trace metadata, and
referenced tape entries into one deterministic slice. Without this slice, Inspector debugging and
future eval/replay tools still need to reimplement lookup rules.

## Goals

1. Export a `DeepChatTapeReplaySlice` for a message request sequence.
2. Reuse `DeepChatTapeService` and existing `deepchat_tape_entries` / `deepchat_message_traces`
storage.
3. Keep metadata-only export as the default.
4. Allow explicit inclusion of existing tape payloads and trace payloads for developer replay.
5. Produce stable hashes for slice, tape entry payloads, tape entry metadata, and trace payloads.
6. Return `null` when no manifest exists for the requested message or request sequence.

## Non-Goals

- Running live LLM replay.
- Replacing `buildContext()` or request preflight behavior.
- Adding a dedicated replay table.
- Adding cross-session memory retrieval.

## User Stories

- As a runtime debugger, I can export one request's manifest, trace metadata, and referenced tape
entries with one typed call.
- As an eval author, I can identify the exact manifest hash and request hash for a historical
request.
- As a privacy-conscious maintainer, I can inspect replay structure without duplicating raw prompt
or message content by default.

## Acceptance Criteria

1. `DeepChatTapeService` can export the latest replay slice for a message.
2. `DeepChatTapeService` can export a replay slice for an explicit `requestSeq`.
3. The slice includes the manifest record, matching trace metadata when present, referenced tape
entry snapshots, anchor refs, and stable hashes.
4. The default export omits tape `payload` / `meta` and trace `headersJson` / `bodyJson`.
5. Explicit options can include tape payloads and trace payloads from their existing storage paths.
6. A typed route and renderer client method expose the export by `messageId`.
7. Tests cover default privacy behavior, explicit payload inclusion, missing manifests, and
request-sequence selection.

## Contract

```ts
export interface DeepChatTapeReplaySlice {
schemaVersion: 1
sliceId: string
sessionId: string
messageId: string
requestSeq: number
mode: 'manifest_only' | 'trace_bound'
manifestRecord: DeepChatTapeViewManifestRecord
trace: DeepChatTapeReplayTraceSnapshot | null
entries: DeepChatTapeReplayEntrySnapshot[]
refs: {
manifestEntryId: number
includedEntryIds: number[]
excludedEntryIds: number[]
anchorEntryIds: number[]
}
hashes: {
manifestHash: string
sliceHash: string
}
createdAt: number
}
```

## Privacy

Default export is metadata-only. It contains IDs, timestamps, names, source refs, and hashes.
Payload inclusion is opt-in and reads from existing storage:

- tape entry payload/meta from `deepchat_tape_entries`
- trace headers/body from `deepchat_message_traces`

No new raw-content storage path is introduced.
10 changes: 10 additions & 0 deletions docs/architecture/deepchat-tape-replay-contract/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# DeepChat Tape Replay Contract - Tasks

## Implementation Tasks

- [x] T1: Add shared `DeepChatTapeReplaySlice` contract types.
- [x] T2: Add `DeepChatTapeService.exportReplaySlice()` with metadata-only defaults.
- [x] T3: Add route, presenter, and renderer client method.
- [x] T4: Add tests for latest selection, explicit `requestSeq`, missing manifest, default privacy,
and explicit payload inclusion.
- [x] T5: Run format, i18n, lint, typecheck, and focused tests.
Loading