mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
* feat(analytics): add per-project project_turn_index run counter turn_index counts runs session-wide across all projects (sessionStorage, resets each browser session). Add a project-scoped sibling project_turn_index (localStorage keyed by project id, project-lifetime per device) so we can analyse "within THIS project, which prompt / follow-up is this run?" without the session-global counter conflating separate projects. - identity.ts: claimProjectTurnIndex(projectId) - contracts: projectTurnIndex hint + project_turn_index run prop - ProjectView: claim alongside the session turn at the create-run dispatch - daemon: stamp project_turn_index (mirrors the turn_index passthrough) Existing turn_index is unchanged. New unit test pins per-project isolation, cross-session persistence, and independence from the session counter. * feat(analytics): stamp session/project turn index on the BYOK-OpenCode run path Per review of the tracking-acceptance doc: the BYOK-OpenCode send path previously carried neither turn_index nor project_turn_index. Claim both (plus is_first_run / has_existing_artifact) there too, so run_created / run_finished report the session-global and project-scoped run sequence uniformly across every runtime (local cli / amr / byok). * chore: retrigger ci --------- Co-authored-by: free666799 <293857035+free666799@users.noreply.github.com> Co-authored-by: Amy <1184569493@qq.com>
59 lines
2.4 KiB
TypeScript
59 lines
2.4 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
import {
|
|
claimProjectTurnIndex,
|
|
claimRunTurnIndex,
|
|
} from '../../src/analytics/identity';
|
|
|
|
// `claimRunTurnIndex` is the session-wide run counter (sessionStorage, spans
|
|
// all projects, resets each browser session). `claimProjectTurnIndex` is the
|
|
// per-project counter (localStorage, project-lifetime per device). These tests
|
|
// pin the semantic difference between the two.
|
|
describe('run turn index: session-wide vs per-project', () => {
|
|
beforeEach(() => {
|
|
window.localStorage.clear();
|
|
window.sessionStorage.clear();
|
|
});
|
|
|
|
it('per-project index starts at 0 and increments within a project', () => {
|
|
expect(claimProjectTurnIndex('proj-a')).toEqual({ projectTurnIndex: 0 });
|
|
expect(claimProjectTurnIndex('proj-a')).toEqual({ projectTurnIndex: 1 });
|
|
expect(claimProjectTurnIndex('proj-a')).toEqual({ projectTurnIndex: 2 });
|
|
});
|
|
|
|
it('keeps an independent counter per project id', () => {
|
|
claimProjectTurnIndex('proj-a'); // 0
|
|
claimProjectTurnIndex('proj-a'); // 1
|
|
expect(claimProjectTurnIndex('proj-b')).toEqual({ projectTurnIndex: 0 });
|
|
expect(claimProjectTurnIndex('proj-a')).toEqual({ projectTurnIndex: 2 });
|
|
});
|
|
|
|
it('persists across browser sessions (survives a sessionStorage reset)', () => {
|
|
claimProjectTurnIndex('proj-a'); // 0
|
|
claimProjectTurnIndex('proj-a'); // 1
|
|
// A new browser session clears sessionStorage but NOT localStorage.
|
|
window.sessionStorage.clear();
|
|
expect(claimProjectTurnIndex('proj-a')).toEqual({ projectTurnIndex: 2 });
|
|
});
|
|
|
|
it('returns null for an empty project id', () => {
|
|
expect(claimProjectTurnIndex('')).toBeNull();
|
|
});
|
|
|
|
it('session counter resets on new session while project counters persist', () => {
|
|
expect(claimRunTurnIndex()).toEqual({ turnIndex: 0, isFirstRun: true });
|
|
claimRunTurnIndex(); // turnIndex 1
|
|
claimProjectTurnIndex('proj-a'); // 0
|
|
claimProjectTurnIndex('proj-b'); // 0
|
|
|
|
window.sessionStorage.clear(); // simulate a new browser session
|
|
|
|
// Session-wide counter starts over…
|
|
expect(claimRunTurnIndex()).toEqual({ turnIndex: 0, isFirstRun: true });
|
|
// …but each project keeps its own place.
|
|
expect(claimProjectTurnIndex('proj-a')).toEqual({ projectTurnIndex: 1 });
|
|
expect(claimProjectTurnIndex('proj-b')).toEqual({ projectTurnIndex: 1 });
|
|
});
|
|
});
|