Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Standardize Chronological Ordering for Session Message Ingestion and Timelines: Message Ingestion Synchronization Layers Not Assume

These rules are ALWAYS ACTIVE for all session message ingestion, timeline aggregation, sync hydration reducers, transcript exports, and history revert calculations across packages/app, packages/tui, packages/opencode, and packages/web.

### Rules

- **R-MSG-001** MUST_NOT: Message ingestion and synchronization layers MUST NOT assume or depend upon network transport arrival order for message sequencing.

### Verify

```bash
# Run test suite for session synchronization, timeline aggregation, and revert calculation
npx turbo test --filter=app --filter=tui --filter=opencode --filter=web
# Run linter and type-checker across packages
npx turbo check
```

**Accept when:**
- Sync reducers and timeline views sort messages strictly by creation timestamp even when supplied out-of-order test events.
- Transcript exports and revert boundary calculations produce deterministic outputs matching creation timestamp ordering across all packages.

<enforcement>
Claude Code MUST NOT skip or defer verification.
</enforcement>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Standardize Chronological Ordering for Session Message Ingestion and Timelines: Session History Compaction Routines Revert Boundary

These rules are ALWAYS ACTIVE for all files matching the configured scope.

### Rules

- **R-CHRON-001** MUST: Session history compaction routines and revert boundary calculations MUST determine message sequence and cutoffs strictly by persistent creation timestamps.

### Verify

```bash
# Discover and run the project's test suite for session synchronization, timeline aggregation, and revert calculation
# Discover and run the repository linter and type-checker across packages/app, packages/tui, packages/opencode, and packages/web.
```

**Accept when:**
- Sync reducers and timeline views sort messages strictly by creation timestamp even when supplied out-of-order test events.
- Transcript exports and revert boundary calculations produce deterministic outputs matching creation timestamp ordering across all packages.

<enforcement>
Claude Code MUST NOT skip or defer verification.
</enforcement>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Standardize Chronological Ordering for Session Message Ingestion and Timelines: Session Message Timelines Transcript Exports Sync

These rules are ALWAYS ACTIVE for all files matching the configured scope across packages/app, packages/tui, packages/opencode, and packages/web.

### Rules

- **R-ORD-001** MUST: Session message timelines, transcript exports, and sync hydration reducers MUST order messages deterministically by persistent creation timestamp rather than array insertion sequence.
- **R-ORD-002** MUST: Incorporate a secondary deterministic tie-breaker (such as unique message ID) when creation timestamps are equal to prevent non-deterministic sorting order across clients.

### Verify

```bash
# Run the project's test suite for session synchronization, timeline aggregation, and revert calculation
npx jest --testNamePattern="session|timeline|revert|sync"
# Run repository linter and type-checker across packages
npx turbo run lint typecheck
```

**Accept when:**
- Sync reducers and timeline views sort messages strictly by creation timestamp even when supplied out-of-order test events.
- Transcript exports and revert boundary calculations produce deterministic outputs matching creation timestamp ordering across all packages.

<enforcement>
Claude Code MUST NOT skip or defer verification. Automated unit and integration test suites validating out-of-order message hydration and timeline sorting must pass successfully.
</enforcement>
83 changes: 83 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,86 @@
<!-- actual-ai:adr-governance:start -->
# Project ADRs

This project's conventions are encoded as ADRs under `.actual/rules/`. **The ADRs ARE the pattern.** Follow them verbatim instead of reading existing implementations to figure out how to do something.

> **Note:** this directive is calibrated for one-shot tasks (a single discrete feature). For multi-task interactive sessions, consult the ADRs for each task transition rather than holding to the per-session caps below.

## Workflow (follow in order)

1. **Identify topic.** Match the files you'll edit against the path-glob table below. Pick the 1-3 topics that match. Do not pre-emptively pick "related" topics; pick only what the file paths actually match.

2. **Select ADRs by filename — the filename is the index.** Run `ls .actual/rules/`. Each filename is `<topic>-<aspect-slug>-<hash>.md`; the `<aspect-slug>` (middle segment) names the ADR's specific concern — e.g., `database-schema-defined`, `zod-input-validation`, `cache-key-format`.

**Scan ALL filenames first, then pick only the ones whose aspect-slug directly names a noun or verb in your task.** Select by filename; never read a body to decide relevance. **Hard cap: read at most 5 ADR files total.** If more than 5 look relevant, you are over-matching — keep the 5 most specific.

**If the path-glob table below is a single `**/*` → `cross-cutting-` row** (one big bucket, no per-area topics), this filename scan is your ONLY filter. Do **not** read the bucket exhaustively — treat the filenames as a menu, match aspect-slugs to your task, read ≤5, and ignore the rest. Reading every ADR in the bucket is the exact failure this directive exists to prevent.

3. **Locate insertion points (one read per file, max 3 files).** You may read source files ONLY to (a) find where to add code (which directory, which barrel export to update) or (b) look up an exact identifier you must import. **Do not read source files as pattern examples — the ADRs already encode the pattern.** If you find yourself reading a file because "I want to see how X is done elsewhere," stop. The ADR you already read tells you how.

4. **Implement.** Write the code following the rule statements verbatim. If two ADRs seem to conflict, follow the more specific one (longer topic prefix wins).

5. **Verify after implementing.** Only after the code is written, re-read the `verify_commands` or `accept_criteria` sections of the ADRs you applied and check your work against them. Run the verify commands if any.

## Anti-patterns to avoid

- Reading the first N rules alphabetically because they're cheap. Filter by aspect-slug first, then read only the relevant ones.
- Reading >5 ADR files for a single feature. If you're tempted, you're over-scoping the topic match.
- Reading the entire `cross-cutting-` bucket because "every rule is always active." Selection is by filename (step 2); you apply the ≤5 you selected, not all of them.
- Reading existing similar features to "see the pattern" — the ADRs encode the pattern. Trust them.
- Re-reading the same ADR multiple times. Cache it mentally.
- Continuing to browse the codebase after step 3. By step 4 you should be writing, not reading.

Each rule file at `.actual/rules/<topic>-<aspect>-<hash>.md` contains the full ADR with rule statements, verify commands, and accept criteria.

## Verification Protocol

These rules are ALWAYS ACTIVE. Apply every rule **from the ADRs you selected in step 2** that governs the files you touch — to all code generation, modification, and review. "Always active" does **not** mean read every ADR: you apply the handful you selected by filename, within the read cap above.

Every rule follows a **Verify → Fix → Repeat** loop. After generating or modifying code for any rule you MUST:

1. **RUN** the rule's `### Verify` command(s).
2. **CAPTURE** the full output (stdout + stderr).
3. **EVALUATE** the output against the rule's **Accept when** criteria.
4. **IF FAILING:** diagnose the root cause, apply a fix, and re-run from step 1.
5. **IF PASSING:** keep the passing output as evidence before moving on.
6. **MAX ITERATIONS:** 5 attempts per rule. If still failing after 5 attempts, STOP and report the failure with all captured output.

Compliance is not optional. Do not skip verification, assume correctness, or defer it to a later task. Every change to a governed area must be accompanied by a passing verification run.

## Mandatory Dependency Grounding

Before writing or modifying implementation code that uses an external dependency:

1. Identify every affected external dependency.
2. Locate the repository's manifest and lock or resolution artifact.
3. Determine the exact repository-resolved version from the lock artifact.
4. Verify that the active environment matches that version.
5. Verify each API being introduced or changed against evidence applicable to that exact version (official documentation or public API reference).
6. Produce a dependency-grounding record.
7. Stop if any version, environment, or API cannot be verified.

Do not begin implementation until dependency grounding has passed.

The repository lock or resolution artifact is authoritative. A manifest range or model recollection is not sufficient.

### Integrated workflow

1. Stabilize the workspace.
2. Inspect the task and relevant architecture decisions.
3. Identify affected dependencies.
4. Run dependency grounding.
5. Produce the implementation plan.
6. Implement the change.
7. Validate grounding coverage, build, types, lint, and tests.
8. Report evidence, assumptions, and blockers.

## Path glob → topic

| You're editing | Topic prefix |
|---|---|
| `packages/**/*` | `cross-cutting-` _(3 ADRs)_ |
<!-- actual-ai:adr-governance:end -->

- To regenerate the legacy JavaScript SDK, run `./packages/sdk/js/script/build.ts`.
- After changing the public Protocol or Server `HttpApi`, run `bun run generate` from `packages/client`. Do not edit `src/generated` or `src/generated-effect` directly.
- Keep runtime dependencies directed from Schema to Core and Protocol, then from Core and Protocol to Server. Client runtime code may depend on Schema and Protocol but never Core or Server; `sdk-next` composes Client, Core, and Server.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Standardize Chronological Ordering for Session Message Ingestion and Timelines: Session History Compaction Routines Revert Boundary

Status: proposed
Date: 2026-10-01
Deciders: AI (signal conversion)

## Context

- Session message delivery and state synchronization across client applications (including packages/app, packages/tui, packages/opencode, and packages/web) occur over asynchronous transport channels such as WebSocket and SSE connections.
- Previously, timeline aggregation, sync hydration reducers, transcript exports, and history revert calculations relied on array ingestion order (the sequence in which messages arrived at the client or were inserted into arrays).
- Out-of-order network arrival caused message sequence corruption, desynchronized transcripts across frontend surfaces, and broken session revert points when updates arrived non-sequentially.

## Problem Statement

Relying on implicit arrival sequence and array insertion order for session messages leads to transcript desynchronization, corrupt timelines, and broken history revert boundaries during out-of-order network delivery.

## Decision

1. MUST: Session history compaction routines and revert boundary calculations MUST determine message sequence and cutoffs strictly by persistent creation timestamps.

## Policy Block

- MUST Session history compaction routines and revert boundary calculations MUST determine message sequence and cutoffs strictly by persistent creation timestamps.

In scope:
- packages/app message timelines, layout helpers, server session context, and sync reducers
- packages/tui session sync context, session routes, and transcript utilities
- packages/opencode session message models, prompt compaction, revert logic, and session state
- packages/web shared session components

Out of scope:
- Ephemeral real-time byte chunk streaming prior to boundary message creation
- Non-message system event logs that do not participate in session revert or transcript export

## Rationale

- Persistent creation timestamps provide an immutable, deterministic ordering baseline that is independent of transport latency or packet reordering.
- Enforcing timestamp-based sorting across all frontends and transcript storage layers ensures timeline consistency across packages/app, packages/tui, packages/opencode, and packages/web.
- Calculating session revert boundaries based on creation timestamps ensures that rollback operations target the true chronological history rather than an arbitrary arrival sequence.

## Consequences

Positive:
- Eliminates message timeline sequence corruption caused by out-of-order network delivery over SSE and WebSocket connections.
- Ensures consistent transcript exports and synchronization state across web, app, and TUI clients.
- Provides deterministic and accurate session history revert operations.

Negative:
- Introduces explicit sorting overhead at sync hydration and timeline rendering boundaries.
- Requires consistent timestamp generation precision across all message producer boundaries.

## Alternatives

- Implicit array appending and ingestion sequence for session message order and history revert boundaries (rejected)
Rejected because: Relying on implicit arrival order caused message sequence corruption, transcript desynchronization, and broken session revert points when updates arrived out of order over WebSocket or SSE connections across multiple client surfaces.
- Server-assigned monotonic sequence numbers for timeline ordering (deferred)
When valid: May be evaluated if sub-millisecond timestamp collisions occur across distributed message producers.

## Risks

- Messages generated with identical creation timestamps may experience non-deterministic sorting order across clients.
Mitigation: Incorporate a secondary deterministic tie-breaker (such as unique message ID) when creation timestamps are equal.
Owner: Core Architecture Team

## Implementation Notes

- Sorting logic should be implemented at hydration and reducer ingestion boundaries to prevent propagation of unordered arrays.
- Refactoring applies across packages/app (context/global-sync, context/server-session, pages/session/timeline), packages/tui (context/sync, routes/session, util/transcript), packages/opencode (session/message-v2, session/revert, session/session), and packages/web (components/Share).

## Continuation Context


Verify commands:
- Discover and run the project's test suite for session synchronization, timeline aggregation, and revert calculation to ensure chronological ordering under out-of-order input payloads.
- Discover and run the repository linter and type-checker across packages/app, packages/tui, packages/opencode, and packages/web.

Accept when:
- Sync reducers and timeline views sort messages strictly by creation timestamp even when supplied out-of-order test events.
- Transcript exports and revert boundary calculations produce deterministic outputs matching creation timestamp ordering across all packages.

## Enforcement

- Verified by: Automated unit and integration test suites validating out-of-order message hydration and timeline sorting.
- Verified by: Code review of sync hydration reducers, transcript generators, and timeline model components.
- Violation handling: Pull requests introducing array-append ordering or unsorted ingestion boundaries will fail automated tests or code review.
- Violation handling: Discrepancies in timeline synchronization between packages will be logged as sequencing regressions.
- Exception process: Exceptions for ephemeral, non-persisted streaming event buffers must be reviewed and approved by the Core Architecture Team.

## References

- file:packages/app/src/context/server-session.ts
- file:packages/app/src/context/global-sync/event-reducer.ts
- file:packages/app/src/context/sync.tsx
- file:packages/app/src/utils/session-message.ts
- file:packages/app/src/pages/session/timeline/message-timeline.tsx
- file:packages/app/src/pages/session/timeline/model.ts
- file:packages/app/src/pages/session/timeline/rows.ts
- file:packages/app/src/pages/layout/helpers.ts
- file:packages/tui/src/context/sync.tsx
- file:packages/tui/src/routes/session/index.tsx
- file:packages/tui/src/util/transcript.ts
- file:packages/opencode/src/session/message-v2.ts
- file:packages/opencode/src/session/prompt.ts
- file:packages/opencode/src/session/revert.ts
- file:packages/opencode/src/session/session.ts
- file:packages/web/src/components/Share.tsx
- commit:5aa5cb35235509c7bcb206179cf29ee11627276e
- commit:91132551141aeb93ca3053a64295a294312c78a7
- commit:23cc677108069e4a7e5ae914d508fde9671b9431
- commit:28bcc0e4f4d4679946542e05412cb96d737a0428
- commit:20750c332e75dd68a10e88f9a0c6b1ca9ac41213
- commit:db581e47a3a6f4900a6289ad7fddec60fec44e1c
- commit:a54a693af242108b0b5c9db6ae498c10b2d8843b
- pr:#40990
- pr:#40991
- pr:#40994
- pr:#40995
- pr:#41000
- pr:#41001
- pr:#41006
Loading
Loading