mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:01:09 +02:00
refactor(projections): wrap streak-tracker writes in system actor
Derived-state writes should be attributed to the projection subsystem,
not to whoever triggered the upstream event. `_streakState` is local-
only today so no cross-device user-visible effect, but once any derived
table joins sync this is the only correct model.
- `markActive` and `ensureSeeded` now run under
`runAsAsync({ kind: 'system', source: 'projection' }, …)`
- Sets the pattern for future projections (DaySnapshot, correlations, …)
to follow verbatim when they start writing persistently
Closes one of the Step-1 follow-ups tracked in
COMPANION_BRAIN_ARCHITECTURE §20. Remaining:
- mana-sync Go + Postgres migration for the `actor` field
- rule-engine to wrap its future writes the same way (no writes today)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2fe9522953
commit
90e6d4dcc6
1 changed files with 21 additions and 12 deletions
|
|
@ -17,9 +17,12 @@
|
|||
import { db } from '../database';
|
||||
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
|
||||
import { eventBus } from '../events/event-bus';
|
||||
import { runAsAsync } from '../events/actor';
|
||||
import type { DomainEvent } from '../events/types';
|
||||
import type { StreakInfo } from './types';
|
||||
|
||||
const PROJECTION_ACTOR = { kind: 'system', source: 'projection' } as const;
|
||||
|
||||
// ── Persistent State ────────────────────────────────
|
||||
|
||||
interface StreakState {
|
||||
|
|
@ -152,7 +155,10 @@ export function startStreakTracker(): void {
|
|||
for (const def of STREAK_DEFS) {
|
||||
if (!def.triggerEvents.includes(event.type)) continue;
|
||||
if (def.filter && !def.filter(event.payload as Record<string, unknown>)) continue;
|
||||
markActive(def.id);
|
||||
// Derived write — attribute to the projection subsystem, not to
|
||||
// whoever triggered the upstream event. Matters the moment
|
||||
// `_streakState` (or any future derived table) joins sync.
|
||||
void runAsAsync(PROJECTION_ACTOR, () => markActive(def.id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -167,17 +173,20 @@ export function stopStreakTracker(): void {
|
|||
async function ensureSeeded(): Promise<void> {
|
||||
const count = await db.table(TABLE).count();
|
||||
if (count > 0) return;
|
||||
// Seed empty states so useStreaks() returns all definitions
|
||||
for (const def of STREAK_DEFS) {
|
||||
await db.table(TABLE).add({
|
||||
id: def.id,
|
||||
label: def.label,
|
||||
moduleId: def.moduleId,
|
||||
currentStreak: 0,
|
||||
longestStreak: 0,
|
||||
lastActiveDate: '',
|
||||
});
|
||||
}
|
||||
// Seed empty states so useStreaks() returns all definitions. Same
|
||||
// attribution reasoning as markActive — this is a subsystem write.
|
||||
await runAsAsync(PROJECTION_ACTOR, async () => {
|
||||
for (const def of STREAK_DEFS) {
|
||||
await db.table(TABLE).add({
|
||||
id: def.id,
|
||||
label: def.label,
|
||||
moduleId: def.moduleId,
|
||||
currentStreak: 0,
|
||||
longestStreak: 0,
|
||||
lastActiveDate: '',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ── Read API ────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue