Skip to content

Commit 5eb6508

Browse files
Merge branch 'dev'
# Conflicts: # almanac/README.md # almanac/architecture/providers.md # almanac/getting-started.md
2 parents 9cba275 + 35fb944 commit 5eb6508

6 files changed

Lines changed: 186 additions & 1 deletion

File tree

almanac/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Begin with [Getting started](getting-started). It gives the shortest reading pat
3232

3333
The core idea is the [local repo wiki](concepts/local-repo-wiki): a browseable Markdown wiki committed with the code, plus derived local state for search and runs. That concept explains why page identity comes from paths, why `README.md` files are landing pages, and why file evidence belongs in `sources:`.
3434

35-
For implementation work, read [Service boundaries](architecture/service-boundaries). It explains how the CLI, app composition root, workflows, services, stores, ports, and integrations divide responsibility. For command behavior, use [CLI public command surface](reference/cli/public-command-surface).
35+
For implementation work, read [Service boundaries](architecture/service-boundaries). It explains how the CLI, app composition root, workflows, services, stores, ports, and integrations divide responsibility. For command behavior, use [CLI public command surface](reference/cli/public-command-surface). For config defaults and precedence, use [Config keys](reference/config-keys).
3636

3737
## Main Clusters
3838

almanac/getting-started.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Before editing pages, read the relevant manual files under `src/codealmanac/manu
5454
Use these routes when you already know the kind of work:
5555

5656
- For command behavior, read [CLI public command surface](reference/cli/public-command-surface).
57+
- For config defaults, model choices, and precedence, read [Config keys](reference/config-keys).
5758
- For page identity, routes, and `README.md` landing pages, read [Page identity](architecture/wiki/page-identity).
5859
- For page evidence and frontmatter, read [Frontmatter and sources](reference/page-format/frontmatter-and-sources).
5960
- For lifecycle run state, logs, and attach behavior, read [Run states and events](reference/runs/run-states-and-events).
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Hide Future Provider Text Deltas Implementation Plan
2+
3+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4+
5+
**Goal:** Stop future CodeAlmanac jobs from persisting token-by-token assistant text deltas in run logs.
6+
7+
**Architecture:** Codex and Claude may continue to emit `TEXT_DELTA` harness events because those are real provider stream events. The operation runner decides which harness events become durable product history. `TEXT_DELTA` is live-only stream material, so the operation runner skips it when recording run events.
8+
9+
**Tech Stack:** Python 3.12, Pydantic models, SQLite run event store, pytest, Ruff.
10+
11+
---
12+
13+
## Product Decision
14+
15+
`TEXT_DELTA` is not durable product history.
16+
17+
Persist:
18+
19+
- `TEXT`
20+
- `TOOL_USE`
21+
- `TOOL_RESULT`
22+
- `TOOL_SUMMARY`
23+
- `PROVIDER_SESSION`
24+
- `CONTEXT_USAGE`
25+
- `WARNING`
26+
- `ERROR`
27+
- `DONE`
28+
- agent trace events
29+
- run status events
30+
31+
Do not persist:
32+
33+
- `TEXT_DELTA`
34+
35+
This is provider-neutral. It applies to Codex and Claude.
36+
37+
## Scope
38+
39+
This affects future jobs created by:
40+
41+
- `codealmanac init`
42+
- `codealmanac ingest`
43+
- `codealmanac garden`
44+
- ingest jobs created by `codealmanac sync`
45+
- scheduled sync ingest jobs
46+
- scheduled garden jobs
47+
48+
This does not affect:
49+
50+
- old jobs already stored in the database
51+
- provider transcripts
52+
- Codex/Claude adapters
53+
- viewer/server read paths
54+
- read-only commands such as `search`, `show`, `topics`, `health`, `validate`,
55+
`serve`, `config`, `automation`, `update`, `doctor`, and `list`
56+
57+
## Out Of Scope
58+
59+
- No old-log filtering.
60+
- No database migration.
61+
- No live typing renderer.
62+
- No `--raw` mode.
63+
- No delta coalescing.
64+
- No provider adapter changes.
65+
66+
Old jobs that already persisted `TEXT_DELTA` rows can remain noisy because the
67+
product is not in public use yet.
68+
69+
## Implementation
70+
71+
### Step 1: Add A Predicate
72+
73+
Modify:
74+
75+
```text
76+
src/codealmanac/workflows/operations/harness.py
77+
```
78+
79+
Add:
80+
81+
```python
82+
def should_record_harness_event(event: HarnessEvent) -> bool:
83+
return event.kind != HarnessEventKind.TEXT_DELTA
84+
```
85+
86+
This function answers:
87+
88+
```text
89+
Should this provider event become durable job history?
90+
```
91+
92+
### Step 2: Use It At The Operation Boundary
93+
94+
Modify:
95+
96+
```text
97+
src/codealmanac/workflows/operations/service.py
98+
```
99+
100+
Change:
101+
102+
```python
103+
for event in harness_events(harness):
104+
self.record(...)
105+
```
106+
107+
to:
108+
109+
```python
110+
for event in harness_events(harness):
111+
if not should_record_harness_event(event):
112+
continue
113+
self.record(...)
114+
```
115+
116+
### Step 3: Add Regression Coverage
117+
118+
Modify:
119+
120+
```text
121+
tests/test_ingest_workflow.py
122+
```
123+
124+
Add `TEXT_DELTA` events before the existing completed `TEXT` event in
125+
`EventfulHarnessAdapter`.
126+
127+
Assert the run log still contains the completed assistant message and no
128+
`TEXT_DELTA` harness events.
129+
130+
## Verification
131+
132+
Targeted:
133+
134+
```bash
135+
uv run pytest tests/test_ingest_workflow.py::test_ingest_workflow_records_normalized_harness_events -q
136+
uv run pytest tests/test_ingest_workflow.py tests/test_codex_app_server_adapter.py tests/test_claude_adapter.py -q
137+
```
138+
139+
Full:
140+
141+
```bash
142+
uv run ruff check .
143+
uv run pytest -q
144+
uv run codealmanac validate
145+
```
146+
147+
Expected:
148+
149+
- Future runs no longer persist `TEXT_DELTA`.
150+
- Full assistant `TEXT` messages still persist.
151+
- Tool/status/error/usage events still persist.
152+
- Existing noisy jobs remain unchanged.

src/codealmanac/workflows/operations/harness.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ def harness_events(result: HarnessRunResult) -> tuple[HarnessEvent, ...]:
2525
return (terminal_harness_event(result.kind, result.status, result.output_text),)
2626

2727

28+
def should_record_harness_event(event: HarnessEvent) -> bool:
29+
return event.kind != HarnessEventKind.TEXT_DELTA
30+
31+
2832
def harness_run_event_kind(event: HarnessEvent) -> RunEventKind:
2933
if event.kind == HarnessEventKind.ERROR:
3034
return RunEventKind.ERROR

src/codealmanac/workflows/operations/service.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
first_line,
2222
harness_events,
2323
harness_run_event_kind,
24+
should_record_harness_event,
2425
validate_harness_result,
2526
)
2627
from codealmanac.workflows.operations.models import OperationContext, OperationResult
@@ -169,6 +170,8 @@ def record_harness_events(
169170
harness: HarnessRunResult,
170171
) -> None:
171172
for event in harness_events(harness):
173+
if not should_record_harness_event(event):
174+
continue
172175
self.record(
173176
RecordOperationEventRequest(
174177
context=context,

tests/test_ingest_workflow.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,26 @@ def run(self, request: RunHarnessRequest) -> HarnessRunResult:
104104
return result.model_copy(
105105
update={
106106
"events": (
107+
HarnessEvent(
108+
kind=HarnessEventKind.TEXT_DELTA,
109+
message="agent ",
110+
actor=HarnessRunActor(
111+
thread_id="root-thread",
112+
role=HarnessActorRole.ROOT,
113+
confidence=HarnessActorConfidence.PROVIDER,
114+
label="Main",
115+
),
116+
),
117+
HarnessEvent(
118+
kind=HarnessEventKind.TEXT_DELTA,
119+
message="read ",
120+
actor=HarnessRunActor(
121+
thread_id="root-thread",
122+
role=HarnessActorRole.ROOT,
123+
confidence=HarnessActorConfidence.PROVIDER,
124+
label="Main",
125+
),
126+
),
107127
HarnessEvent(
108128
kind=HarnessEventKind.TEXT,
109129
message="agent read source note",
@@ -423,6 +443,11 @@ def test_ingest_workflow_records_normalized_harness_events(
423443
assert log[-3].harness_event.agent_trace.child_thread_id == "helper-thread"
424444
assert log[-2].harness_event is not None
425445
assert log[-2].harness_event.provider_session_id == "root-thread"
446+
assert all(
447+
entry.harness_event is None
448+
or entry.harness_event.kind != HarnessEventKind.TEXT_DELTA
449+
for entry in log
450+
)
426451

427452

428453
def test_ingest_prompt_includes_git_source_runtime(

0 commit comments

Comments
 (0)