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
25 changes: 24 additions & 1 deletion .orgii/skills/dual-instance-verification/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,24 @@ actions in the background, invisible to every existing cell.
a local source DB mid-run (hollow read), block the identity endpoint
(lookup failure), kill the app mid-transfer (partial persist). The guard
under test must defer/refuse — any destructive act under injected fault is
a failure.
a failure. When the change ADDS a fault point (a new read, probe, or IPC
call), inject THAT fault — the rotation list only covers yesterday's
failure modes.
- **Upgrade cell: persisted state must cross the version boundary.** Any
change that reads durable state written by earlier builds (push cursors,
cache metadata, parser output, settings) gets one cell where the OLD build
writes the state and the NEW build operates on it: run a pre-change binary
(a dated `org2-main.exe` or a develop build) through the flow first, then
swap binaries over the SAME homes and continue. Assert the new build rides
the ordinary incremental path — no epoch rewrite, no refuse, no silent
re-derive — and that a second cycle (new build writes, new build reads)
is idempotent. A fresh-anchor test with only the new binary proves nothing
about migration: PR #692's costliest bug (every legacy flat cursor forced
an O(total) epoch rewrite) was invisible to every run that built its state
with the new code. Second-order cycles count too: state the new build
STAMPS must survive the new build's own next scan/rescan before the
invariant is real (PR #693's lineage stamp was erased by the very next
rescan's metadata rewrite).
- **Unexplained delta becomes a cell.** The first ledger delta, log line,
resource pattern, or store-vs-UI discrepancy without a mechanism-level
explanation is promoted to a scenario in the CURRENT run — not noted for
Expand Down Expand Up @@ -228,6 +245,12 @@ rollover or the window silently truncates.
boot, and positional chunk ids turned the shuffle into a fresh hash chain
each time. Within one app lifetime everything looked stable; only
boot-vs-boot comparison of push decisions could see it. (#608 root cause.)
- **Fresh-state runs cannot see migration bugs**: every cell that builds its
own state with the binary under test samples only the post-change state
space. Bugs that live in the TRANSITION — legacy cursor meets new hash
mode, old parser rows meet new election, stamped metadata meets the next
rescan — need the upgrade cell above. The tell is a verification report
whose every artifact was created during the run itself.
- **"Pre-existing" used as a verdict**: a symptom reproduces on baseline, is
correctly cleared of THIS PR's authorship, and is then silently cleared of
being a bug at all — because the run's attention is scoped to the PR, and
Expand Down
68 changes: 57 additions & 11 deletions .orgii/skills/org2-performance-guard/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: org2-performance-guard
description: Prevent CPU, RAM, I/O, and background-work regressions in ORG2. Use when adding or reviewing polling, timers, Realtime subscriptions, event listeners, workers, streaming paths, caches, pagination, external-history scans, cloud sync, source-control loading, per-session state, or multi-instance behavior; also use before delivering a performance refactor or any feature that stays alive while the UI is idle or hidden.
description: Prevent CPU, RAM, I/O, background-work, and false-green lifecycle regressions in ORG2. Use when adding or reviewing polling, timers, Realtime subscriptions, event listeners, workers, streaming paths, caches, pagination, provider-owned transcript ingestion or identity/dedupe, external-history scans, cloud sync, source-control loading, per-session state, true-machine verification, or multi-provider/multi-instance behavior; also use before delivering a performance refactor or any feature that stays alive while the UI is idle or hidden.
---

# ORG2 Performance Guard
Expand All @@ -25,6 +25,8 @@ Require all applicable invariants before delivery:
- Keep blocking filesystem, database, git, and process work off async executor threads and render-critical paths.
- Load large histories, request rounds, diffs, and replay segments on demand; do not eagerly materialize invisible data.
- Isolate secondary Tauri identities completely: data home, external-history home, ports, cookies/auth, and app-lifetime caches.
- Treat provider ingestion, local identity/listability, UI hydration, cloud transport, and remote rendering as separate verification boundaries. Passing A-to-B sync does not prove the upstream local lifecycle.
- Test every provider and raw source transition claimed by the change. Do not infer Claude Code compaction coverage from Codex append coverage, or vice versa.
- Keep rendered E2E strict. Missing UI must fail with diagnostics; never turn a regression into `console.warn`, catch-and-continue, or a debug-helper bypass.

## Required workflow
Expand Down Expand Up @@ -63,10 +65,36 @@ For each resource, record the required behavior in these states:
| Scope | personal org, cloud org, removed org, revoked share |
| Session | unopened, active, inactive, deleted, forked |
| Instance | primary, direct-launched secondary, launcher-created secondary |
| Source | discover, append, large append, compact/rewrite, rotate, delete |
| UI | clean load, old row active/open/pinned during refresh, restart |
| Transport | local ingest, upload, remote download, reconnect |

Flag any resource whose owner or terminal state is ambiguous.

### 3. Choose the correct pattern
### 3. Separate provider lifecycle from machine topology

For provider history, session identity, dedupe, or sync work, build a coverage matrix before testing:

| Axis | Minimum relevant states |
| -------------- | -------------------------------------------------------------------------------- |
| Provider | every changed provider plus every provider explicitly claimed as working |
| Raw transition | create, append, large append, compact/rewrite, rotate, fork/subagent, delete |
| App timing | cold start, source changes while ORG2 is open, rescan, restart |
| UI state | clean roster, previous row active/open/pinned, search/filter/load-more as needed |
| Topology | local ingest, isolated secondary, A upload, B download/reconnect |

Apply these validity rules:

- Treat each matrix cell as independent evidence. Two machines exercise topology; they do not create provider compaction, rotation, or lineage transitions automatically.
- Exercise the raw provider artifact or a faithful before/after fixture. Do not seed only normalized cache/database rows when the parser, watermark, identity, lineage, or dedupe contract is under test.
- Derive identity markers from the raw artifact. Do not fabricate identical group keys that merely restate the implementation assumption.
- Include an assumption-breaking fixture for identity logic: for example, a rewritten transcript head with a changed first-message UUID but a preserved ancestry marker.
- Observe local ingest and listability before enabling or asserting cloud upload. Then verify upload cursor/payload and remote rendering separately.
- Keep the previous session active, open, or pinned while applying the source transition when exact-id hydration or force-reveal paths exist.
- Repeat rescan/restart once to prove idempotence and stable row/resource counts.
- Name every unexecuted provider or transition. Never summarize partial coverage as “multi-provider,” “dual-machine,” or “full lifecycle.”

### 4. Choose the correct pattern

Apply the smallest applicable pattern:

Expand All @@ -80,7 +108,7 @@ Apply the smallest applicable pattern:
- **Demand-driven loading:** paginate or fetch details only after expansion/selection; retain only the visible or recently used window.
- **Generation guard:** discard late async results after stop, restart, account switch, endpoint switch, or a newer request.

### 4. Sweep equivalent paths
### 5. Sweep equivalent paths

After finding one issue, search for every semantic peer. A fix is incomplete if another surface still owns a parallel implementation.

Expand All @@ -96,7 +124,7 @@ Typical ORG2 sweeps:

Unify duplicate resource ownership before tuning individual call sites.

### 5. Protect correctness and privacy
### 6. Protect correctness and privacy

Performance changes must not weaken:

Expand All @@ -110,7 +138,7 @@ Performance changes must not weaken:

Capture identity and generation at request start. Before committing a result, confirm the current identity/generation still matches. Do not display a previous identity's cached rows while refreshing.

### 6. Verify proportionally
### 7. Verify proportionally

Always run:

Expand All @@ -121,11 +149,16 @@ Always run:

For rendered/background changes, also run the real Tauri surface when available:

1. Observe primary and secondary instances separately.
2. Measure visible idle, hidden idle, active streaming, and post-close/post-delete behavior.
3. Exercise account switch, endpoint switch, and direct secondary launch when relevant.
4. Confirm request/subscription/timer counts stabilize rather than grow after repeated open/close cycles.
5. Confirm strict rendered E2E uses user-visible actions for the behavior under assertion.
1. Isolate primary and secondary data homes, provider roots, auth, ports, and processes.
2. Capture a baseline: raw files, cache rows/listability, active/open/pinned row, cursor/epoch, payload count, process count, CPU, and RSS as applicable.
3. Apply the raw source transition while ORG2 is already open. For compaction/rewrite/rotation, stage or produce the actual before/after artifact instead of pre-populating the final database state.
4. Assert local parsing, identity/lineage, exact row count, listability, timestamp, and sidebar behavior before cloud transport can hide the owning-boundary failure.
5. Keep an old row active/open/pinned, rescan, and assert that hydration does not resurrect a superseded sibling or hide the active row entirely.
6. Verify A upload and B download/reconnect separately, including cursor/epoch and exact appended payload counts when incremental behavior is claimed.
7. Rescan and restart once; confirm data and request/subscription/timer/process counts remain stable.
8. Measure visible idle, hidden idle, active work, and post-close/post-delete behavior.
9. Exercise account switch, endpoint switch, and direct secondary launch when relevant.
10. Confirm strict rendered E2E uses user-visible actions for the behavior under assertion.

Do not claim a performance improvement from code shape alone. State the evidence actually collected and any environment blocker.

Expand All @@ -142,6 +175,11 @@ Reject or revise a change when any applicable answer is unknown or false:
- Does one session's update wake unrelated session views?
- Does a growing transcript/history/diff require full eager materialization?
- Does a direct secondary launch inherit primary external history or auth state?
- Which provider and raw source transition produced the evidence for each compatibility claim?
- Did the test inspect local ingest and identity before testing cloud transport?
- Did it keep the previous row active/open/pinned across rescan or only test a clean roster?
- Were family/identity keys parsed from raw artifacts, or fabricated to match the implementation?
- Did “dual-machine” testing merely replicate an already-normalized final state?
- Can a missing rendered element be skipped while the E2E still passes?

## Required delivery output
Expand All @@ -155,10 +193,18 @@ Report findings and evidence in this compact form:
| Scope/isolation | fix / keep | cache/request key | identity/generation guard | switch/revocation test |
| Rendering/hot path | fix / keep | subscription/allocation trace | narrowing/coalescing | render or unit evidence |

For provider ingestion, session identity, or sync work, also report:

| Provider | Raw transition | App/UI state | Topology/boundary | Expected invariant | Observed evidence |
| -------------- | ----------------- | ---------------------------- | --------------------------- | ----------------------------------------- | ---------------------------- |
| exact provider | actual transition | cold/live/active-row/restart | local/A-to-cloud/cloud-to-B | exact rows, identity, cursor, payload, UI | measured result or `not run` |

Use one row per materially distinct matrix cell. A shared implementation permits shared unit coverage only at the shared boundary; each provider adapter still needs representative raw input before claiming compatibility.

End with:

- `Performance verdict: pass` only when every applicable invariant is evidenced.
- `Performance verdict: blocked` when required real measurement or compilation cannot run; name the blocker.
- `Performance verdict: blocked` when required real measurement, provider transition, or compilation cannot run; name the blocker and the uncovered matrix cells.
- `Performance verdict: fail` when an unbounded, duplicate, hidden-active, stale-write, or cross-identity path remains.

Never promise that a skill can make regressions impossible. Enforce the gates, expose unknowns, and refuse an unsupported green verdict.
4 changes: 2 additions & 2 deletions .orgii/skills/org2-performance-guard/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
interface:
display_name: "ORG2 Performance Guard"
short_description: "Prevent CPU/RAM regressions in ORG2 changes"
default_prompt: "Use $org2-performance-guard to audit this ORG2 change for CPU, RAM, polling, cache, and lifecycle regressions."
short_description: "Guard ORG2 performance and lifecycle coverage"
default_prompt: "Use $org2-performance-guard to audit this ORG2 change across performance, provider lifecycle, and true-machine coverage."
35 changes: 32 additions & 3 deletions src-tauri/crates/orgtrack-core/src/sources/claude_code/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! converts them into ORGII's canonical `ActivityChunk` shape for read-only
//! replay.

use std::collections::{BTreeSet, HashMap, HashSet};
use std::collections::{BTreeSet, HashMap, HashSet, VecDeque};
use std::fs;
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -38,7 +38,10 @@ const CLAUDE_CODE_PROVIDER_SLUG: &str = "claudecode";
// v10: harness-injected user lines (isMeta, task-notification origin) no
// longer open rounds or feed the first-prompt title; user image blocks
// surface as data-URL attachments on the user bubble.
const CLAUDE_CODE_METADATA_PARSER_VERSION: i64 = 10;
// v11: capture compact-boundary ancestry markers so continuation families
// survive Claude Code rewriting the first user message during compaction.
const CLAUDE_CODE_METADATA_PARSER_VERSION: i64 = 11;
const MAX_COMPACT_BOUNDARY_MARKERS: usize = imported_cache::MAX_CONTINUATION_MARKERS - 1;

pub type ClaudeCodeHistorySessionRow = ImportedHistorySessionRow;
pub type ClaudeCodeHistorySessionPage = ImportedHistorySessionPage;
Expand Down Expand Up @@ -74,6 +77,9 @@ struct ClaudeCodeHistoryMeta {
/// field, but message uuids are preserved — so this is a stable group key
/// uniting a conversation's continuation siblings for dedupe.
first_user_uuid: Option<String>,
/// Compact-boundary uuids retained by continuation rewrites. Together
/// with `first_user_uuid` these form a bounded ancestry marker set.
continuation_markers: Vec<String>,
}

#[derive(Debug, Deserialize)]
Expand All @@ -82,6 +88,8 @@ struct ClaudeJsonlLine {
#[serde(default)]
r#type: String,
#[serde(default)]
subtype: String,
#[serde(default)]
summary: String,
/// `ai-title` records: the auto-generated title shown in the Claude Code app.
#[serde(default)]
Expand Down Expand Up @@ -878,6 +886,9 @@ struct ClaudeSessionMetaState {
// same way Codex does, instead of listing it as a top-level session.
parent_source_session_id: Option<String>,
first_user_uuid: Option<String>,
/// Keep the newest compact boundaries; the first-user marker consumes the
/// remaining slot in the 64-marker cache metadata budget.
compact_boundary_uuids: VecDeque<String>,
}

impl ClaudeSessionMetaState {
Expand Down Expand Up @@ -955,6 +966,22 @@ impl ClaudeSessionMetaState {
{
self.first_user_uuid = Some(parsed.uuid.trim().to_string());
}
if parsed.r#type == "system"
&& parsed.subtype == "compact_boundary"
&& !parsed.uuid.trim().is_empty()
{
let marker = parsed.uuid.trim();
if !self
.compact_boundary_uuids
.iter()
.any(|existing| existing == marker)
{
if self.compact_boundary_uuids.len() >= MAX_COMPACT_BOUNDARY_MARKERS {
self.compact_boundary_uuids.pop_front();
}
self.compact_boundary_uuids.push_back(marker.to_string());
}
}
let harness_injected = is_harness_injected_user_line(&parsed);
if let Some(message) = parsed.message {
if self.first_prompt.is_empty() && parsed.r#type == "user" && !harness_injected {
Expand Down Expand Up @@ -1096,6 +1123,7 @@ impl ClaudeSessionMetaState {
.parent_source_session_id
.map(|uuid| format!("{CLAUDE_CODE_SESSION_PREFIX}{uuid}")),
first_user_uuid: self.first_user_uuid,
continuation_markers: self.compact_boundary_uuids.into_iter().collect(),
})
}
}
Expand Down Expand Up @@ -1209,8 +1237,9 @@ fn session_meta_to_cache_input(meta: ClaudeCodeHistoryMeta) -> ImportedHistoryCa
branch: meta.branch,
impact: meta.impact,
listable: true,
source_metadata_json: imported_cache::continuation_group_metadata_json(
source_metadata_json: imported_cache::continuation_metadata_json(
meta.first_user_uuid.as_deref(),
&meta.continuation_markers,
),
parent_session_id: meta.parent_session_id,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,7 @@ fn captures_first_user_uuid_as_continuation_group_key() {
// contribute a key.
let content = r#"{"type":"custom-title","customTitle":"My convo","sessionId":"d0641111-1111-1111-1111-111111111111"}
{"type":"user","uuid":"b7b5ae5f-0000-0000-0000-000000000001","sessionId":"d0641111-1111-1111-1111-111111111111","cwd":"/tmp/project","gitBranch":"main","timestamp":"2026-07-17T10:00:00.000Z","message":{"role":"user","content":"first message"}}
{"type":"system","subtype":"compact_boundary","uuid":"eeb66522-0000-0000-0000-000000000001","sessionId":"d0641111-1111-1111-1111-111111111111","timestamp":"2026-07-17T10:00:30.000Z"}
{"type":"user","uuid":"b7b5ae5f-0000-0000-0000-000000000002","sessionId":"d0641111-1111-1111-1111-111111111111","cwd":"/tmp/project","gitBranch":"main","timestamp":"2026-07-17T10:01:00.000Z","message":{"role":"user","content":"second message"}}
"#;
std::fs::write(&path, content).expect("write fixture");
Expand All @@ -879,6 +880,10 @@ fn captures_first_user_uuid_as_continuation_group_key() {
meta.first_user_uuid.as_deref(),
Some("b7b5ae5f-0000-0000-0000-000000000001")
);
assert_eq!(
meta.continuation_markers,
vec!["eeb66522-0000-0000-0000-000000000001"]
);

let cache_input = session_meta_to_cache_input(meta);
let metadata_json = cache_input.source_metadata_json.expect("metadata json");
Expand All @@ -889,6 +894,19 @@ fn captures_first_user_uuid_as_continuation_group_key() {
.and_then(|value| value.as_str()),
Some("b7b5ae5f-0000-0000-0000-000000000001")
);
assert_eq!(
parsed
.get(imported_cache::CONTINUATION_MARKERS_FIELD)
.and_then(Value::as_array)
.expect("continuation markers")
.iter()
.filter_map(Value::as_str)
.collect::<Vec<_>>(),
vec![
"b7b5ae5f-0000-0000-0000-000000000001",
"eeb66522-0000-0000-0000-000000000001"
]
);

std::fs::remove_file(&path).expect("remove fixture");
std::fs::remove_dir(&temp_dir).expect("remove temp dir");
Expand Down
Loading
Loading