-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathledger.ts
More file actions
138 lines (120 loc) · 4.93 KB
/
Copy pathledger.ts
File metadata and controls
138 lines (120 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
export type GeneOrigin = 'inward' | 'outward-speculative' | 'adopted' | 'preexisting'
export type GeneState = 'active' | 'muted'
export type EventType = 'born' | 'muted' | 'unmuted' | 'removed-proposed' | 'removed'
export type GeneEvent = {
at: string
type: EventType
reason: string
}
export type Gene = {
origin: GeneOrigin
born: string
seasonal: boolean
core: boolean
muteThresholdWeeks: number
state: GeneState
events: GeneEvent[]
invocations: Record<string, number>
}
export type HarnessModel = {
harness: string
model: string
}
export type Cycles = {
lastRun: string | null
lastOutwardScan: string | null
lastPublish: string | null
count: number
reportOnlyPruning: boolean
}
export type Ledger = {
genes: Record<string, Gene>
harnessModels: HarnessModel[]
cycles: Cycles
}
export const DEFAULT_MUTE_THRESHOLD_WEEKS = 8
export function emptyLedger(): Ledger {
return {
genes: {},
harnessModels: [],
cycles: { lastRun: null, lastOutwardScan: null, lastPublish: null, count: 0, reportOnlyPruning: true },
}
}
export function registerGene(
ledger: Ledger,
key: string,
origin: GeneOrigin,
atISO: string,
bornDate: string,
opts: { core?: boolean } = {},
): Ledger {
if (ledger.genes[key]) return ledger
const gene: Gene = {
origin,
// `born` is the caller's own "today" date string, not a UTC slice of
// atISO — atISO is only the born event's full-precision timestamp.
// Deriving born from atISO independently let it drift a day away from
// the date the caller used for a same-cycle invocation at UTC/local
// boundaries (see M3 in the 2026-08-14 final-review-fix-brief).
born: bornDate,
seasonal: false,
core: opts.core ?? false,
muteThresholdWeeks: DEFAULT_MUTE_THRESHOLD_WEEKS,
state: 'active',
events: [{ at: atISO, type: 'born', reason: `origin=${origin}` }],
invocations: {},
}
return { ...ledger, genes: { ...ledger.genes, [key]: gene } }
}
// A parsed invocation key is normally already plugin-qualified (`plugin:skill`)
// or an existing gene. omp records skill activation as a bare `skill://<name>`
// read with no plugin namespace, so a bare name must be resolved to its
// plugin-qualified gene — otherwise every cycle would register a spurious
// unqualified gene alongside the real one. Resolution is a unique suffix match
// against existing genes; unresolved or ambiguous names stay bare (registered
// as-is) so a skill is never silently attributed to the wrong plugin.
export function resolveGeneKey(ledger: Ledger, raw: string): string {
if (raw.includes(':') || ledger.genes[raw]) return raw
const matches = Object.keys(ledger.genes).filter((k) => k.endsWith(`:${raw}`))
return matches.length === 1 ? matches[0] : raw
}
export function recordInvocation(ledger: Ledger, key: string, dateISO: string, count: number): Ledger {
const gene = ledger.genes[key]
if (!gene) throw new Error(`unknown gene: ${key}`)
const invocations = { ...gene.invocations, [dateISO]: (gene.invocations[dateISO] ?? 0) + count }
return { ...ledger, genes: { ...ledger.genes, [key]: { ...gene, invocations } } }
}
export function applyEvent(ledger: Ledger, key: string, atISO: string, type: EventType, reason: string): Ledger {
const gene = ledger.genes[key]
if (!gene) throw new Error(`unknown gene: ${key}`)
const state: GeneState = type === 'muted' ? 'muted' : type === 'unmuted' ? 'active' : gene.state
const events = [...gene.events, { at: atISO, type, reason }]
return { ...ledger, genes: { ...ledger.genes, [key]: { ...gene, state, events } } }
}
export function markSeasonal(ledger: Ledger, key: string): Ledger {
const gene = ledger.genes[key]
if (!gene) throw new Error(`unknown gene: ${key}`)
return { ...ledger, genes: { ...ledger.genes, [key]: { ...gene, seasonal: true } } }
}
export function setCore(ledger: Ledger, key: string): Ledger {
const gene = ledger.genes[key]
if (!gene) throw new Error(`unknown gene: ${key}`)
return { ...ledger, genes: { ...ledger.genes, [key]: { ...gene, core: true } } }
}
export function recordCycleRun(ledger: Ledger, dateISO: string): Ledger {
return { ...ledger, cycles: { ...ledger.cycles, lastRun: dateISO, count: ledger.cycles.count + 1 } }
}
export function recordOutwardScan(ledger: Ledger, dateISO: string): Ledger {
return { ...ledger, cycles: { ...ledger.cycles, lastOutwardScan: dateISO } }
}
export function setReportOnlyPruning(ledger: Ledger, value: boolean): Ledger {
return { ...ledger, cycles: { ...ledger.cycles, reportOnlyPruning: value } }
}
export function recordHarnessModel(ledger: Ledger, harness: string, model: string): Ledger {
const exists = ledger.harnessModels.some(hm => hm.harness === harness && hm.model === model)
if (exists) return ledger
return { ...ledger, harnessModels: [...ledger.harnessModels, { harness, model }] }
}
export function recordPublish(ledger: Ledger, dateISO: string): Ledger {
return { ...ledger, cycles: { ...ledger.cycles, lastPublish: dateISO } }
}