@onlooker-community/ecosystem 0.31.2 → 0.32.2

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecosystem",
3
- "version": "0.31.2",
3
+ "version": "0.32.2",
4
4
  "description": "Observability substrate for Claude Code. Provides the shared $ONLOOKER_DIR storage root (default $HOME/.onlooker), canonical schema-validated event emission, session and tool tracking hooks, and prompt rules. Required by all other Onlooker plugins.",
5
5
  "author": {
6
6
  "name": "Onlooker Community",
@@ -1,19 +1,19 @@
1
1
  {
2
- ".": "0.31.2",
2
+ ".": "0.32.2",
3
3
  "plugins/archivist": "0.3.1",
4
4
  "plugins/tribunal": "1.1.1",
5
5
  "plugins/echo": "0.3.1",
6
6
  "plugins/cartographer": "0.3.1",
7
- "plugins/governor": "0.3.1",
7
+ "plugins/governor": "0.3.2",
8
8
  "plugins/compass": "0.4.1",
9
9
  "plugins/scribe": "0.4.1",
10
10
  "plugins/counsel": "0.5.1",
11
11
  "plugins/warden": "0.3.1",
12
- "plugins/librarian": "0.4.1",
12
+ "plugins/librarian": "0.6.0",
13
13
  "plugins/curator": "0.2.1",
14
14
  "plugins/historian": "0.3.2",
15
15
  "plugins/assayer": "1.1.1",
16
- "plugins/bursar": "0.2.1",
16
+ "plugins/bursar": "0.2.2",
17
17
  "plugins/lineage": "0.2.1",
18
18
  "plugins/inspector": "0.3.1"
19
19
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,94 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.32.2](https://github.com/onlooker-community/ecosystem/compare/ecosystem-v0.32.1...ecosystem-v0.32.2) (2026-08-02)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * librarian + bursar + governor improvements ([#119](https://github.com/onlooker-community/ecosystem/issues/119)) ([d7cdab5](https://github.com/onlooker-community/ecosystem/commit/d7cdab528ea5181ea35da68fbc5ad2e5b064abea))
9
+
10
+ ## [0.32.1](https://github.com/onlooker-community/ecosystem/compare/ecosystem-v0.32.0...ecosystem-v0.32.1) (2026-08-02)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * librarian conflict detection + bursar rollup lock timeout ([#117](https://github.com/onlooker-community/ecosystem/issues/117)) ([16180a2](https://github.com/onlooker-community/ecosystem/commit/16180a210a6f0561c09b77a2b102849f63f1e730))
16
+
17
+ ## [0.32.0](https://github.com/onlooker-community/ecosystem/compare/ecosystem-v0.31.2...ecosystem-v0.32.0) (2026-08-02)
18
+
19
+
20
+ ### Features
21
+
22
+ * add configuration and hooks for agent spawn tracking ([3ef4590](https://github.com/onlooker-community/ecosystem/commit/3ef459006bbbda246604bdd1ffaf9af0a59f9740))
23
+ * add settings.json for plugin configuration ([67fbdfe](https://github.com/onlooker-community/ecosystem/commit/67fbdfe37f067a45801e7d0355c4a533b687f6b2))
24
+ * **archivist:** introduce structured session memory plugin :rocket: ([378fff3](https://github.com/onlooker-community/ecosystem/commit/378fff3c14b40644af45b1a2335992e7b0428160))
25
+ * **assayer:** introduce claim-verification plugin ([#70](https://github.com/onlooker-community/ecosystem/issues/70)) ([1d0500b](https://github.com/onlooker-community/ecosystem/commit/1d0500b64f8cd670d1cfa1ac070182d72696bdfd))
26
+ * **bursar:** introduce multi-session budget rollup plugin ([#81](https://github.com/onlooker-community/ecosystem/issues/81)) ([b11e687](https://github.com/onlooker-community/ecosystem/commit/b11e687744bab70a94025c46c4aaa58fb7ea97f4))
27
+ * **cartographer:** add proactive instruction-file audit plugin :mag: ([#35](https://github.com/onlooker-community/ecosystem/issues/35)) ([387d00a](https://github.com/onlooker-community/ecosystem/commit/387d00ad04da5aae91048254ad0526bb674ed498))
28
+ * **compass:** pre-write intent clarity gate plugin :compass: ([#47](https://github.com/onlooker-community/ecosystem/issues/47)) ([144c2ef](https://github.com/onlooker-community/ecosystem/commit/144c2ef44d28bab3dcec14a9eace7ec76470d090))
29
+ * **counsel:** add /counsel on-demand weekly-review command :rocket: ([#76](https://github.com/onlooker-community/ecosystem/issues/76)) ([8ce951c](https://github.com/onlooker-community/ecosystem/commit/8ce951cd5cb7b173f194f86c2960a31fb0d6889d))
30
+ * **counsel:** weekly observability synthesis and coaching brief :robot: ([#51](https://github.com/onlooker-community/ecosystem/issues/51)) ([6364586](https://github.com/onlooker-community/ecosystem/commit/63645863cf3a1d7bbf0353aacb9b71e4f977dd56))
31
+ * **coverage:** report node + bash coverage on every PR :sparkles: ([cb5d122](https://github.com/onlooker-community/ecosystem/commit/cb5d1221ad20e6257d66b507897dae14549a870f))
32
+ * **curator:** introduce plugin with cheap-tier checks :microscope: ([#57](https://github.com/onlooker-community/ecosystem/issues/57)) ([7f9fa18](https://github.com/onlooker-community/ecosystem/commit/7f9fa18bbde29c8b5bd1eaad185bd4c5595a3762))
33
+ * **echo:** add prompt regression detection plugin ([#32](https://github.com/onlooker-community/ecosystem/issues/32)) ([65274d4](https://github.com/onlooker-community/ecosystem/commit/65274d4d8326950d6c998ca292fed13b1b8c493b))
34
+ * **ecosystem:** emit memory.recalled at SessionStart :link: ([#62](https://github.com/onlooker-community/ecosystem/issues/62)) ([d5876f9](https://github.com/onlooker-community/ecosystem/commit/d5876f9f819165cc07d691d733662b549863b7f5))
35
+ * **governor:** resource governance and budget enforcement plugin :rocket: ([#43](https://github.com/onlooker-community/ecosystem/issues/43)) ([04e6d70](https://github.com/onlooker-community/ecosystem/commit/04e6d7051f27db752bb121d389d65b4d8ade04ad))
36
+ * **historian:** introduce SessionEnd indexing :spiral_notepad: ([#59](https://github.com/onlooker-community/ecosystem/issues/59)) ([dd6c7f6](https://github.com/onlooker-community/ecosystem/commit/dd6c7f6ea872437cab6b16de50838dfc72750c7b))
37
+ * **historian:** retrieval pipeline + ollama embedder :telescope: ([#61](https://github.com/onlooker-community/ecosystem/issues/61)) ([7eae752](https://github.com/onlooker-community/ecosystem/commit/7eae752a288c4678ab093042469f2e65d428f0d9))
38
+ * **hooks:** add PreCompact and PostCompact context compaction trackers ([#15](https://github.com/onlooker-community/ecosystem/issues/15)) ([1ec5632](https://github.com/onlooker-community/ecosystem/commit/1ec5632404676ed8b35d324b79ad71a2e9093505))
39
+ * **hooks:** add SessionStart and SessionEnd session trackers ([#10](https://github.com/onlooker-community/ecosystem/issues/10)) ([a48d680](https://github.com/onlooker-community/ecosystem/commit/a48d680dd24c98e79ef1c0401b07483ecebf9e8b))
40
+ * **hooks:** add TaskCreated and TaskCompleted task lifecycle trackers ([#21](https://github.com/onlooker-community/ecosystem/issues/21)) ([986ffa8](https://github.com/onlooker-community/ecosystem/commit/986ffa84bdd857a464ca0d556671628190ed27bc))
41
+ * **hooks:** add UserPromptSubmit turn and session duration trackers ([#12](https://github.com/onlooker-community/ecosystem/issues/12)) ([cbb7657](https://github.com/onlooker-community/ecosystem/commit/cbb7657979ed144efce506e6b487e037679b9462))
42
+ * **hooks:** add WorktreeCreate and WorktreeRemove lifecycle trackers ([#24](https://github.com/onlooker-community/ecosystem/issues/24)) ([ff55e39](https://github.com/onlooker-community/ecosystem/commit/ff55e397a0c0adc3e76f66aba12c6b237149ad17))
43
+ * **hooks:** emit canonical schema events for tool history :sparkles: ([1e49a24](https://github.com/onlooker-community/ecosystem/commit/1e49a24bfb930942fa477b594395ef352618f574))
44
+ * **hooks:** enrich tool.file.read with read chunking observability ([#25](https://github.com/onlooker-community/ecosystem/issues/25)) ([8eb23c8](https://github.com/onlooker-community/ecosystem/commit/8eb23c8f4f03dfbeb701a30de1fa50c1c8ee48ac))
45
+ * **hooks:** track skill usage via skill.invoked events ([23fff0f](https://github.com/onlooker-community/ecosystem/commit/23fff0f0bfad8ab91788d8c45a0457d099d2e870))
46
+ * **hooks:** track tool call sequence on every PreToolUse :sparkles: ([0ad9546](https://github.com/onlooker-community/ecosystem/commit/0ad95465cc22a237e26115a67814a6e7b2951b1d))
47
+ * **inspector:** ship the per-edit lint/typecheck plugin ([#88](https://github.com/onlooker-community/ecosystem/issues/88)) ([2018243](https://github.com/onlooker-community/ecosystem/commit/201824384abd6a4fc5f4395266924aa413a2ffd1))
48
+ * **librarian:** /librarian review skill closes promotion loop :tada: ([#68](https://github.com/onlooker-community/ecosystem/issues/68)) ([8f3e3db](https://github.com/onlooker-community/ecosystem/commit/8f3e3dbdf6f08dceb0cf61d46281936a4f9954de))
49
+ * **librarian:** implement conflict detection for memory promotion scanning ([#115](https://github.com/onlooker-community/ecosystem/issues/115)) ([8699577](https://github.com/onlooker-community/ecosystem/commit/86995773cb44127d915b5aea53aed66c771d8bfb))
50
+ * **librarian:** land plugin end-to-end with memory layer designs :seedling: ([#55](https://github.com/onlooker-community/ecosystem/issues/55)) ([d4821ef](https://github.com/onlooker-community/ecosystem/commit/d4821efabfeb587e460e898d7db8f92fcc3f2c61))
51
+ * **lineage:** introduce per-change provenance plugin ([#83](https://github.com/onlooker-community/ecosystem/issues/83)) ([86b00d3](https://github.com/onlooker-community/ecosystem/commit/86b00d3d7393e2b63c5b04d60692fc89f202bf6c))
52
+ * **lint:** add marketplace cross-reference linter :nail_care: ([0f48817](https://github.com/onlooker-community/ecosystem/commit/0f488170326659ef1d0b8bd7ae4d207c78a43694))
53
+ * **lint:** add plugin manifest validator :nail_care: ([e12615f](https://github.com/onlooker-community/ecosystem/commit/e12615ff99d43caf59d5e215d882c0acb3352c01))
54
+ * **plugins:** installation enables plugins — remove per-plugin 'enabled' config key ([#108](https://github.com/onlooker-community/ecosystem/issues/108)) ([45e4e6b](https://github.com/onlooker-community/ecosystem/commit/45e4e6bd29a0a7545dbd5007bf3f09600e1be391))
55
+ * **plugins:** persist structured JSON and emit onlooker.artifact.ready :outbox_tray: ([#103](https://github.com/onlooker-community/ecosystem/issues/103)) ([9b689a4](https://github.com/onlooker-community/ecosystem/commit/9b689a41aa4bdb481fef93b484e6446da731e8f1))
56
+ * **prompt-rules:** deterministic regex-triggered guidance injection :relieved: ([#28](https://github.com/onlooker-community/ecosystem/issues/28)) ([662c811](https://github.com/onlooker-community/ecosystem/commit/662c8119657cebc350900f859c43dbaca97d6703))
57
+ * **scribe:** intent documentation from agent activity :pencil2: ([#50](https://github.com/onlooker-community/ecosystem/issues/50)) ([f0a95d1](https://github.com/onlooker-community/ecosystem/commit/f0a95d1058e36d1bb5f0f645964d9e88e8f98b66))
58
+ * **tribunal:** add multi-agent code review plugin :sparkles: ([#30](https://github.com/onlooker-community/ecosystem/issues/30)) ([893f24a](https://github.com/onlooker-community/ecosystem/commit/893f24a8876fdd6ccb5c7dcf2636a7c902e88949))
59
+ * **warden:** untrusted-content gate enforcing the Agents Rule of Two :shield: ([#53](https://github.com/onlooker-community/ecosystem/issues/53)) ([210aa51](https://github.com/onlooker-community/ecosystem/commit/210aa51bff66226a0eec1f17292a2af4ea4ef56a))
60
+
61
+
62
+ ### Bug Fixes
63
+
64
+ * **cartographer:** correct typo in release-please bump-patch key :face_with_spiral_eyes: ([#91](https://github.com/onlooker-community/ecosystem/issues/91)) ([dfab160](https://github.com/onlooker-community/ecosystem/commit/dfab1602afda2b6255a72b0975ebab9289d75b8e))
65
+ * **ci:** apply release-please extra-files for Claude plugin manifests ([#17](https://github.com/onlooker-community/ecosystem/issues/17)) ([da9913c](https://github.com/onlooker-community/ecosystem/commit/da9913ca4f7497280edc34f8c64baa903c1e6754))
66
+ * **ci:** checkout release tag before npm publish :relieved: ([bc7bbdc](https://github.com/onlooker-community/ecosystem/commit/bc7bbdc7a886a55ba8f04fe09bfa60043648c766))
67
+ * **ci:** grant id-token write for npm provenance on publish ([c78c9f0](https://github.com/onlooker-community/ecosystem/commit/c78c9f054c1d48ca8a83d0d26b76ce991fffe51b))
68
+ * **ci:** parse release-please paths_released JSON for npm publish ([749e1a0](https://github.com/onlooker-community/ecosystem/commit/749e1a02b563f37f81a8da21fc3f6e10e179314a))
69
+ * **ci:** stop upgrading npm globally before publish ([a7c7a0e](https://github.com/onlooker-community/ecosystem/commit/a7c7a0e1f25aee1bbb75bdd2af130dbc276480a6))
70
+ * **ci:** use HTTPS repository URL for npm provenance ([a7e8927](https://github.com/onlooker-community/ecosystem/commit/a7e89275c5a025a8afee009853265b717091f6ca))
71
+ * **ci:** use paths_released to gate npm publish :rage: ([#37](https://github.com/onlooker-community/ecosystem/issues/37)) ([c62b17f](https://github.com/onlooker-community/ecosystem/commit/c62b17f7e1352cfe260a23c8f48be30f72edbbed))
72
+ * **compass:** resolve MultiEdit file path from the top-level field ([#85](https://github.com/onlooker-community/ecosystem/issues/85)) ([468abaa](https://github.com/onlooker-community/ecosystem/commit/468abaaad4bef59fe4308bd8887dfcf6d633921a))
73
+ * **counsel:** drop unsupported --max-tokens flag from claude synthesis call :relieved: ([#79](https://github.com/onlooker-community/ecosystem/issues/79)) ([ade85ce](https://github.com/onlooker-community/ecosystem/commit/ade85cecb3243781f47e14fea4990ce31e69e8f4))
74
+ * **counsel:** stop pipefail from discarding all events on large logs :relieved: ([#78](https://github.com/onlooker-community/ecosystem/issues/78)) ([638347d](https://github.com/onlooker-community/ecosystem/commit/638347dec3b9df740b7a85c3e475fa2ffe5d054b))
75
+ * **emitter:** make emission dependency-free and fail-open :relieved: ([#99](https://github.com/onlooker-community/ecosystem/issues/99)) ([0dda7f8](https://github.com/onlooker-community/ecosystem/commit/0dda7f803ed2dfa28561bd8d9e4193b2b18e5bbf))
76
+ * **historian:** export CLAUDE_PLUGIN_ROOT so config loads correctly :bug: ([#112](https://github.com/onlooker-community/ecosystem/issues/112)) ([f24b0b4](https://github.com/onlooker-community/ecosystem/commit/f24b0b4375c64385fefd329cd07d8874785002e9))
77
+ * **historian:** repair CI broken by embedder check + document PR workflow :nail_care: ([#106](https://github.com/onlooker-community/ecosystem/issues/106)) ([02062a2](https://github.com/onlooker-community/ecosystem/commit/02062a21163e084705dae9cf421719b6e5a7b306))
78
+ * **hooks:** replace flock with portable mkdir mutex :bug: ([3dffa6f](https://github.com/onlooker-community/ecosystem/commit/3dffa6f5e43ef9f3c117f2406ddd03ce485df1cd))
79
+ * **package:** update repository URL format in package.json ([591ce9f](https://github.com/onlooker-community/ecosystem/commit/591ce9f54dd605ec04ceb77b9dcca40b3e08621e))
80
+ * **plugins:** glob-discover ecosystem root so sub-plugins work from cache :relieved: ([#110](https://github.com/onlooker-community/ecosystem/issues/110)) ([3639240](https://github.com/onlooker-community/ecosystem/commit/3639240383e1b820e5d8ea42639e0b863ef0d90e))
81
+ * **release:** sync marketplace.json out-of-band :relieved: ([0d2a0a3](https://github.com/onlooker-community/ecosystem/commit/0d2a0a38c0c4ee0e400b9a143a4be7904ea3f70a))
82
+ * **scribe:** mark hook scripts executable :relieved: ([#64](https://github.com/onlooker-community/ecosystem/issues/64)) ([05603e5](https://github.com/onlooker-community/ecosystem/commit/05603e56895c009c1435d1712592adbbc4c15e61))
83
+ * **tribunal:** persist all artifacts on every iteration including retries :relieved: ([#41](https://github.com/onlooker-community/ecosystem/issues/41)) ([1636105](https://github.com/onlooker-community/ecosystem/commit/163610535a4ce0fa73c8fb82dc5c6296d2d1065a))
84
+ * vendor portable-lock.sh into cartographer and governor ([#73](https://github.com/onlooker-community/ecosystem/issues/73)) ([ab2c354](https://github.com/onlooker-community/ecosystem/commit/ab2c354b131c26cc642ebb51e84a043dc43cbaa1))
85
+
86
+
87
+ ### Performance Improvements
88
+
89
+ * **bursar:** collapse process forks in SessionEnd hot path :relieved: ([#101](https://github.com/onlooker-community/ecosystem/issues/101)) ([7a426fe](https://github.com/onlooker-community/ecosystem/commit/7a426fe359785eca35ea1ad61523b05fda79e0da))
90
+ * **historian:** fix O(n²) chunk loop and embedder false-positive :relieved: ([4ef130a](https://github.com/onlooker-community/ecosystem/commit/4ef130a4c01ec378da5b4d8baeeb7b4fb6059272))
91
+
3
92
  ## [0.31.2](https://github.com/onlooker-community/ecosystem/compare/ecosystem-v0.31.1...ecosystem-v0.31.2) (2026-08-01)
4
93
 
5
94
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlooker-community/ecosystem",
3
- "version": "0.31.2",
3
+ "version": "0.32.2",
4
4
  "description": "Agents, skills, hooks, commands, rules, and MCP configurations that power [Onlooker](https://onlooker.dev)",
5
5
  "author": {
6
6
  "name": "Onlooker Community",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bursar",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Multi-session, per-project budget accounting for the Onlooker ecosystem. Rolls each session's spend into a per-project ledger on SessionEnd and surfaces \"this project burned $X this week\" at SessionStart. Where governor regulates a single session, bursar is the cross-session rollup: it reads governor.session.complete off the shared event bus and emits bursar.* events for audit. Named for the officer who keeps the accounts. Builds on the Onlooker ecosystem plugin.",
5
5
  "author": {
6
6
  "name": "Onlooker Community",
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.2](https://github.com/onlooker-community/ecosystem/compare/bursar-v0.2.1...bursar-v0.2.2) (2026-08-02)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * librarian conflict detection + bursar rollup lock timeout ([#117](https://github.com/onlooker-community/ecosystem/issues/117)) ([16180a2](https://github.com/onlooker-community/ecosystem/commit/16180a210a6f0561c09b77a2b102849f63f1e730))
9
+
3
10
  ## [0.2.1](https://github.com/onlooker-community/ecosystem/compare/bursar-v0.2.0...bursar-v0.2.1) (2026-08-01)
4
11
 
5
12
 
@@ -35,6 +35,11 @@ INPUT=$(cat)
35
35
 
36
36
  _done() { exit 0; }
37
37
 
38
+ # Emit diagnostic errors to stderr when ledger recording fails.
39
+ _log_error() {
40
+ printf 'bursar-session-end: %s\n' "$1" >&2
41
+ }
42
+
38
43
  # Parse session_id and cwd in a single jq pass (one fork, not two).
39
44
  { IFS= read -r SESSION_ID; IFS= read -r CWD; } < <(printf '%s' "$INPUT" | jq -r '.session_id // "", .cwd // ""' 2>/dev/null)
40
45
 
@@ -142,10 +147,18 @@ MODEL=""
142
147
  # ledger upsert actually succeeds. A failed write (lock timeout, mv failure)
143
148
  # must keep the breadcrumb so the session→project attribution survives for a
144
149
  # later attempt rather than being lost behind a false "recorded" event.
145
- if [[ -n "$RECORD" ]] && bursar_ledger_record "$PROJECT_KEY" "$RECORD"; then
146
- [[ -n "$EV" ]] && bursar_emit_event "bursar.session.recorded" "$EV" "$SESSION_ID" || true
147
-
148
- rm -f "$BREADCRUMB" 2>/dev/null || true
150
+ #
151
+ # Use a longer lock timeout (60s) to handle high concurrency: under typical
152
+ # usage, SessionEnd hooks from concurrent sessions compete for the per-project
153
+ # ledger lock. The default 5s timeout is too aggressive with 100+ concurrent
154
+ # sessions, leading to silent recording failures and breadcrumb accumulation.
155
+ if [[ -n "$RECORD" ]]; then
156
+ if bursar_ledger_record "$PROJECT_KEY" "$RECORD" 60; then
157
+ [[ -n "$EV" ]] && bursar_emit_event "bursar.session.recorded" "$EV" "$SESSION_ID" || true
158
+ rm -f "$BREADCRUMB" 2>/dev/null || true
159
+ else
160
+ _log_error "ledger_record failed for session $SESSION_ID project $PROJECT_KEY (lock timeout after 60s?). Breadcrumb retained for retry."
161
+ fi
149
162
  fi
150
163
 
151
164
  _done
@@ -29,23 +29,41 @@
29
29
  # Avoid associative arrays so bash 3.2 (macOS default) keeps working.
30
30
 
31
31
  # Acquire an exclusive lock at LOCKPATH. Returns 0 on success, 1 on timeout.
32
+ # Uses exponential backoff under high contention to reduce cache-line thrashing.
32
33
  lock_acquire() {
33
34
  local lockpath="${1:-}"
34
35
  local timeout="${2:-5}"
35
36
  [[ -z "$lockpath" ]] && return 1
36
37
 
37
38
  local lockdir="${lockpath}.d"
38
- local waited=0
39
- # Poll at 10 Hz so a 5s timeout = 50 attempts.
40
- local max_iter=$((timeout * 10))
39
+ local start_time
40
+ start_time=$(date +%s 2>/dev/null) || start_time=0
41
+
42
+ local backoff=10 # Start at 10ms (1/100s)
41
43
  while ! mkdir "$lockdir" 2>/dev/null; do
42
- if ((waited >= max_iter)); then
43
- return 1
44
+ # Check timeout using epoch comparison (works on all systems).
45
+ if ((start_time > 0)); then
46
+ local now
47
+ now=$(date +%s 2>/dev/null) || now=0
48
+ if (( (now - start_time) >= timeout )); then
49
+ return 1
50
+ fi
51
+ fi
52
+
53
+ # Exponential backoff: 10ms → 20ms → 40ms, capped at 100ms to avoid
54
+ # sleeping too long under sustained high contention. Reduces per-second
55
+ # mkdir() attempts from 10/sec (10Hz polling) to ~10-30/sec under load.
56
+ local sleep_ms=$((backoff))
57
+ if ((sleep_ms > 100)); then
58
+ sleep_ms=100
44
59
  fi
45
- # `sleep 0.1` works on Linux + macOS; the `|| sleep 1` is a paranoid
46
- # fallback for embedded shells that only accept integer seconds.
47
- sleep 0.1 2>/dev/null || sleep 1
48
- waited=$((waited + 1))
60
+
61
+ # Convert to seconds: `sleep 0.01` is 10ms. Fallback for shells
62
+ # that only accept integer seconds.
63
+ awk -v ms="$sleep_ms" 'BEGIN { printf "%.2f\n", ms/1000 }' | \
64
+ xargs -I {} sleep {} 2>/dev/null || sleep 1
65
+
66
+ backoff=$((backoff * 2))
49
67
  done
50
68
  return 0
51
69
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "governor",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Resource governance and budget enforcement for the Onlooker ecosystem. Tracks per-session token and cost spend, gates Task spawns before they exceed a configurable budget ceiling, and emits governor.* events for audit. Named for the steam-engine governor — a device that regulates output. Builds on the Onlooker ecosystem plugin.",
5
5
  "author": {
6
6
  "name": "Onlooker Community",
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.2](https://github.com/onlooker-community/ecosystem/compare/governor-v0.3.1...governor-v0.3.2) (2026-08-02)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * librarian + bursar + governor improvements ([#119](https://github.com/onlooker-community/ecosystem/issues/119)) ([d7cdab5](https://github.com/onlooker-community/ecosystem/commit/d7cdab528ea5181ea35da68fbc5ad2e5b064abea))
9
+
3
10
  ## [0.3.1](https://github.com/onlooker-community/ecosystem/compare/governor-v0.3.0...governor-v0.3.1) (2026-08-01)
4
11
 
5
12
 
@@ -83,19 +83,21 @@ ESTIMATED_COST=$(governor_estimate_cost "$ESTIMATED_TOKENS")
83
83
  ESTIMATION_METHOD=$(governor_estimate_method)
84
84
 
85
85
  # Build the completion ledger record.
86
- # estimated_tokens is negated to cancel the reservation written by PreToolUse.
87
- # actual_tokens (when present) complete the two-phase accounting so the running
88
- # total converges to real spend: N_est + (-N_est) + N_act = N_act.
86
+ # Keep positive estimated_tokens (do not negate) so session totals reflect
87
+ # projected spend. Reservations are recorded with record_type: "reservation";
88
+ # completions are recorded with record_type: "completion". Ledger totals sum
89
+ # only completion records, so each spawn contributes its estimate once.
90
+ # actual_tokens (when present) will eventually replace estimates when API
91
+ # usage metrics become available.
89
92
  AGENT_TYPE="${TOOL_NAME:-Task}"
90
93
  TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null) || TS="1970-01-01T00:00:00Z"
91
- NEG_ESTIMATED=$(( -ESTIMATED_TOKENS ))
92
94
 
93
95
  RECORD=$(jq -n \
94
96
  --arg ts "$TS" \
95
97
  --arg sid "$SESSION_ID" \
96
98
  --arg aid "${CLAUDE_SESSION_ID:-unknown}" \
97
99
  --arg at "$AGENT_TYPE" \
98
- --argjson est "$NEG_ESTIMATED" \
100
+ --argjson est "$ESTIMATED_TOKENS" \
99
101
  --argjson cost "$ESTIMATED_COST" \
100
102
  --argjson dur "$DURATION_MS" \
101
103
  '{
@@ -105,7 +107,8 @@ RECORD=$(jq -n \
105
107
  agent_type: $at,
106
108
  estimated_tokens: $est,
107
109
  cost_usd_estimated: $cost,
108
- duration_ms: $dur
110
+ duration_ms: $dur,
111
+ record_type: "completion"
109
112
  }' 2>/dev/null) || RECORD="{}"
110
113
 
111
114
  # Compute actual total once; used for both the ledger record and the event payload.
@@ -126,12 +126,10 @@ _governor_ledger_poison() {
126
126
 
127
127
  # Running total of tokens for a session.
128
128
  #
129
- # Uses the two-phase model: each record contributes
130
- # .estimated_tokens + (.actual_tokens // 0)
131
- #
132
- # In-flight reservations: estimated_tokens > 0, no actual_tokens → counts N_est
133
- # Completed Task records: estimated_tokens = -N_est, actual_tokens = N_act → counts N_act
134
- # Net: in-flight estimates + completed actuals.
129
+ # Sums all ledger records:
130
+ # - reservation records (in-flight projected spend)
131
+ # - completion records (which may cancel reservations and add actual_tokens)
132
+ # - legacy records with no record_type
135
133
  #
136
134
  # Usage: tokens=$(governor_ledger_total_tokens "$session_id")
137
135
  governor_ledger_total_tokens() {
@@ -145,7 +143,7 @@ governor_ledger_total_tokens() {
145
143
  "$ledger_path" 2>/dev/null || printf '0'
146
144
  }
147
145
 
148
- # Running total of cost for a session (same two-phase logic as tokens).
146
+ # Running total of cost for a session (same logic as tokens: all records).
149
147
  # Usage: cost=$(governor_ledger_total_cost "$session_id")
150
148
  governor_ledger_total_cost() {
151
149
  local session_id="${1:-}"
@@ -158,7 +156,7 @@ governor_ledger_total_cost() {
158
156
  "$ledger_path" 2>/dev/null || printf '0'
159
157
  }
160
158
 
161
- # Count completed Task calls (excludes reservation records).
159
+ # Count Task calls, excluding reservation records.
162
160
  # Usage: calls=$(governor_ledger_call_count "$session_id")
163
161
  governor_ledger_call_count() {
164
162
  local session_id="${1:-}"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "librarian",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "Consolidation layer between archivist's per-session artifacts and the user's durable typed memory store. Detects which session decisions, dead-ends, and open questions deserve to live across sessions, classifies them into the user/feedback/project/reference types, and queues them as proposals for explicit confirmation. Auto-promotion is opt-in. Builds on the Onlooker ecosystem plugin.",
5
5
  "author": {
6
6
  "name": "Onlooker Community",
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.0](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.5.0...librarian-v0.6.0) (2026-08-02)
4
+
5
+
6
+ ### Features
7
+
8
+ * **librarian:** implement conflict detection for memory promotion scanning ([#115](https://github.com/onlooker-community/ecosystem/issues/115)) ([8699577](https://github.com/onlooker-community/ecosystem/commit/86995773cb44127d915b5aea53aed66c771d8bfb))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * librarian conflict detection + bursar rollup lock timeout ([#117](https://github.com/onlooker-community/ecosystem/issues/117)) ([16180a2](https://github.com/onlooker-community/ecosystem/commit/16180a210a6f0561c09b77a2b102849f63f1e730))
14
+
15
+ ## [0.5.0](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.4.1...librarian-v0.5.0) (2026-08-02)
16
+
17
+
18
+ ### Features
19
+
20
+ * **librarian:** implement conflict detection for memory promotion scanning ([#115](https://github.com/onlooker-community/ecosystem/issues/115)) ([8699577](https://github.com/onlooker-community/ecosystem/commit/86995773cb44127d915b5aea53aed66c771d8bfb))
21
+
3
22
  ## [0.4.1](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.4.0...librarian-v0.4.1) (2026-08-01)
4
23
 
5
24
 
@@ -18,23 +18,41 @@
18
18
  # Avoid associative arrays so bash 3.2 (macOS default) keeps working.
19
19
 
20
20
  # Acquire an exclusive lock at LOCKPATH. Returns 0 on success, 1 on timeout.
21
+ # Uses exponential backoff under high contention to reduce cache-line thrashing.
21
22
  lock_acquire() {
22
23
  local lockpath="${1:-}"
23
24
  local timeout="${2:-5}"
24
25
  [[ -z "$lockpath" ]] && return 1
25
26
 
26
27
  local lockdir="${lockpath}.d"
27
- local waited=0
28
- # Poll at 10 Hz so a 5s timeout = 50 attempts.
29
- local max_iter=$((timeout * 10))
28
+ local start_time
29
+ start_time=$(date +%s 2>/dev/null) || start_time=0
30
+
31
+ local backoff=10 # Start at 10ms (1/100s)
30
32
  while ! mkdir "$lockdir" 2>/dev/null; do
31
- if ((waited >= max_iter)); then
32
- return 1
33
+ # Check timeout using epoch comparison (works on all systems).
34
+ if ((start_time > 0)); then
35
+ local now
36
+ now=$(date +%s 2>/dev/null) || now=0
37
+ if (( (now - start_time) >= timeout )); then
38
+ return 1
39
+ fi
40
+ fi
41
+
42
+ # Exponential backoff: 10ms → 20ms → 40ms, capped at 100ms to avoid
43
+ # sleeping too long under sustained high contention. Reduces per-second
44
+ # mkdir() attempts from 10/sec (10Hz polling) to ~10-30/sec under load.
45
+ local sleep_ms=$((backoff))
46
+ if ((sleep_ms > 100)); then
47
+ sleep_ms=100
33
48
  fi
34
- # `sleep 0.1` works on Linux + macOS; the `|| sleep 1` is a paranoid
35
- # fallback for embedded shells that only accept integer seconds.
36
- sleep 0.1 2>/dev/null || sleep 1
37
- waited=$((waited + 1))
49
+
50
+ # Convert to seconds: `sleep 0.01` is 10ms. Fallback for shells
51
+ # that only accept integer seconds.
52
+ awk -v ms="$sleep_ms" 'BEGIN { printf "%.2f\n", ms/1000 }' | \
53
+ xargs -I {} sleep {} 2>/dev/null || sleep 1
54
+
55
+ backoff=$((backoff * 2))
38
56
  done
39
57
  return 0
40
58
  }