@arcforge/cognet 2.0.101 → 2.0.103

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcforge/cognet",
3
- "version": "2.0.101",
3
+ "version": "2.0.103",
4
4
  "description": "A cognitive engine",
5
5
  "type": "module",
6
6
  "private": false,
@@ -18,8 +18,8 @@
18
18
  "deploy": "echo \"This package is published ONLY by apps/tui/scripts/release.ts, which pins workspace:* deps to concrete versions first. Publishing it directly ships an unresolvable dependency.\" && exit 1"
19
19
  },
20
20
  "dependencies": {
21
- "@arcforge/err": "2.0.101",
22
- "@arcforge/types": "2.0.101",
21
+ "@arcforge/err": "2.0.103",
22
+ "@arcforge/types": "2.0.103",
23
23
  "hookable": "^5.5.3"
24
24
  },
25
25
  "devDependencies": {
package/src/clock.ts CHANGED
@@ -57,10 +57,11 @@ export function Clock(opts: ClockOpts) {
57
57
  async runTick<T>(fn: () => Promise<T>): Promise<T> {
58
58
  tick += 1
59
59
  const current = tick
60
+ const started = Date.now()
60
61
  void emit("cognet:tick:start", { tick: current })
61
62
  try {
62
63
  const result = await fn()
63
- void emit("cognet:tick:complete", { tick: current })
64
+ void emit("cognet:tick:complete", { tick: current, durationMs: Date.now() - started })
64
65
  return result
65
66
  } catch (cause) {
66
67
  if (signal?.aborted) {
@@ -68,7 +69,7 @@ export function Clock(opts: ClockOpts) {
68
69
  throw cause
69
70
  }
70
71
  const failure = err(cause)
71
- void emit("cognet:tick:failed", { tick: current, error: failure })
72
+ void emit("cognet:tick:failed", { tick: current, error: failure, durationMs: Date.now() - started })
72
73
  throw failure
73
74
  }
74
75
  },
@@ -76,10 +77,11 @@ export function Clock(opts: ClockOpts) {
76
77
  /** A named stage within a tick. Sets the current phase for its duration. */
77
78
  async runPhase<T>(name: string, fn: () => Promise<T>): Promise<T> {
78
79
  phase = name
80
+ const started = Date.now()
79
81
  void emit("cognet:phase:start", { tick, phase: name })
80
82
  try {
81
83
  const result = await fn()
82
- void emit("cognet:phase:complete", { tick, phase: name })
84
+ void emit("cognet:phase:complete", { tick, phase: name, durationMs: Date.now() - started })
83
85
  return result
84
86
  } catch (cause) {
85
87
  if (signal?.aborted) {
@@ -87,14 +89,14 @@ export function Clock(opts: ClockOpts) {
87
89
  throw cause
88
90
  }
89
91
  const failure = err(cause)
90
- void emit("cognet:phase:failed", { tick, phase: name, error: failure })
92
+ void emit("cognet:phase:failed", { tick, phase: name, error: failure, durationMs: Date.now() - started })
91
93
  throw failure
92
94
  } finally {
93
95
  phase = null
94
96
  }
95
97
  },
96
98
 
97
- /** A unit of work within a phase. Timed for the flame graph. */
99
+ /** A unit of work within a phase the innermost bracket. */
98
100
  async runSystem<T>(name: string, fn: () => Promise<T>): Promise<T> {
99
101
  const started = Date.now()
100
102
  void emit("cognet:system:start", { ...stamp(), system: name })
@@ -108,7 +110,7 @@ export function Clock(opts: ClockOpts) {
108
110
  throw cause
109
111
  }
110
112
  const failure = err(cause)
111
- void emit("cognet:system:failed", { ...stamp(), system: name, error: failure })
113
+ void emit("cognet:system:failed", { ...stamp(), system: name, error: failure, durationMs: Date.now() - started })
112
114
  throw failure
113
115
  }
114
116
  },
@@ -9,12 +9,26 @@ type ComponentOpts = {
9
9
 
10
10
  /**
11
11
  * Component — the single write path for component data.
12
- * Every write emits kernel telemetry and fires watchers; Entity() delegates
12
+ * Every write emits cognet telemetry and fires watchers; Entity() delegates
13
13
  * here so there is exactly one place a component can change.
14
14
  *
15
- * Telemetry goes to the runtime bus (→ tracing pipeline), never the session
16
- * log. Bus emit never rejects (handler errors are re-emitted as
17
- * axon:bus:error), so firing without await keeps writes synchronous.
15
+ * DURABILITY. These events go through abi.emit, which commits to the
16
+ * session log like every other cognet:* event and forwards to the bus after
17
+ * the append lands. They are not bus-only a brain whose world mutations
18
+ * vanish on restart cannot be debugged after the fact, which is the whole
19
+ * point of the log.
20
+ *
21
+ * The cost is real and known: a continuous-mode cognet ticking fast writes
22
+ * one durable line per component change, which is the highest-volume event
23
+ * source in the system by construction. That is acceptable while the ECS is
24
+ * opt-in and unwired (no cognet constructs Ecs() today). Whoever wires the
25
+ * first continuous world should measure it and, if it bites, gate at the
26
+ * write — sample, batch, or make durability a per-component declaration.
27
+ * Do NOT "fix" it by quietly routing these to the bus alone; that trades a
28
+ * measurable cost for an invisible hole.
29
+ *
30
+ * emit() is fire-and-forget by design (sync, void) so a world write is never
31
+ * an await point.
18
32
  */
19
33
  export function Component(opts: ComponentOpts) {
20
34
  const { state, emit } = opts