kijito-tools 0.2.0
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/LICENSE +201 -0
- package/NOTICE +6 -0
- package/README.md +220 -0
- package/bin/cli.js +34 -0
- package/install.sh +161 -0
- package/package.json +50 -0
- package/providers/_shared/wake-core.mjs +358 -0
- package/providers/claude/CLAUDE.md.snippet +31 -0
- package/providers/claude/install.sh +186 -0
- package/providers/claude/scripts/arm-session.sh +66 -0
- package/providers/claude/scripts/claude-armed.sh +101 -0
- package/providers/claude/scripts/heartbeat-watchdog.sh +209 -0
- package/providers/claude/scripts/inbox-selftest.sh +277 -0
- package/providers/claude/scripts/kijito-persona-lib.sh +94 -0
- package/providers/claude/scripts/kijito-qa-pass.sh +12 -0
- package/providers/claude/scripts/lifecycle-lib.sh +167 -0
- package/providers/claude/scripts/myctx.sh +46 -0
- package/providers/claude/scripts/self-clear.sh +134 -0
- package/providers/claude/scripts/session-autosend.sh +85 -0
- package/providers/claude/scripts/session-catchup-hint.sh +330 -0
- package/providers/claude/scripts/statusline-context.sh +67 -0
- package/providers/claude/skills/kijito-qa-memory/SKILL.md +110 -0
- package/providers/claude/skills/kijito-recall/SKILL.md +53 -0
- package/providers/claude/skills/kijito-start/SKILL.md +122 -0
- package/providers/claude/wiring/README.md +93 -0
- package/providers/claude/wiring/kijito-heartbeat@.service +21 -0
- package/providers/codex/README.md +75 -0
- package/providers/codex/codex-kijito-parity-plan.md +265 -0
- package/providers/codex/docs-codex-setup.md +64 -0
- package/providers/codex/install.mjs +184 -0
- package/providers/codex/n0-capability-probe-protocol.md +472 -0
- package/providers/codex/n0-harness/README.md +57 -0
- package/providers/codex/n0-harness/cli.mjs +58 -0
- package/providers/codex/n0-harness/evidence-manifest.mjs +77 -0
- package/providers/codex/n0-harness/fixture.mjs +279 -0
- package/providers/codex/n0-harness/lib.mjs +141 -0
- package/providers/codex/n0-harness/manifest.mjs +36 -0
- package/providers/codex/n0-harness/oracle.mjs +464 -0
- package/providers/codex/n0-harness/parser.mjs +200 -0
- package/providers/codex/n0-harness/prompt.mjs +43 -0
- package/providers/codex/n0-harness/snapshot.mjs +120 -0
- package/providers/codex/n0-harness/specimen.mjs +37 -0
- package/providers/codex/notify/kijito-notify-count.mjs +117 -0
- package/providers/codex/notify/kijito-notify-count.test.mjs +76 -0
- package/providers/codex/plans/gate4-battery-log.md +184 -0
- package/providers/codex/plans/gate4-battery-protocol.md +43 -0
- package/providers/codex/plans/hive-user-first-plan-QA.md +34 -0
- package/providers/codex/plans/hive-user-first-plan.md +247 -0
- package/providers/codex/release-manifest.json +53 -0
- package/providers/codex/same-chat-continuation-plan-gate.md +235 -0
- package/providers/codex/same-chat-continuation-plan.md +458 -0
- package/providers/codex/skills/kijito-qa-memory/SKILL.md +193 -0
- package/providers/codex/skills/kijito-qa-memory/agents/openai.yaml +4 -0
- package/providers/codex/skills/kijito-recall/SKILL.md +53 -0
- package/providers/codex/skills/kijito-start/SKILL.md +173 -0
- package/providers/codex/skills/kijito-start/agents/openai.yaml +4 -0
- package/providers/codex/test/n0-cli-refusal.test.mjs +31 -0
- package/providers/codex/test/n0-counterexample-matrix.test.mjs +118 -0
- package/providers/codex/test/n0-guard-census-core.mjs +1372 -0
- package/providers/codex/test/n0-guard-census.json +35479 -0
- package/providers/codex/test/n0-guard-census.mjs +31 -0
- package/providers/codex/test/n0-guard-census.test.mjs +374 -0
- package/providers/codex/test/n0-guard-counterexamples.mjs +1068 -0
- package/providers/codex/test/n0-guard-independent-projection.mjs +70 -0
- package/providers/codex/test/n0-guard-independent-projection.test.mjs +53 -0
- package/providers/codex/test/n0-guard-manifest-author.mjs +424 -0
- package/providers/codex/test/n0-guard-mutation-operators.test.mjs +244 -0
- package/providers/codex/test/n0-guard-mutation-runner.mjs +599 -0
- package/providers/codex/test/n0-guard-remainder-classes.test.mjs +314 -0
- package/providers/codex/test/n0-guard-sanitizer-pairs.test.mjs +68 -0
- package/providers/codex/test/n0-guard-shared-context-predicates.test.mjs +84 -0
- package/providers/codex/test/n0-harness.test.mjs +553 -0
- package/providers/codex/test/n0-notable-regressions.test.mjs +70 -0
- package/providers/codex/test/n0-subject-boundary.mjs +97 -0
- package/providers/codex/test/n0-subject-boundary.test.mjs +72 -0
- package/providers/codex/test/same-chat-plan-preflight.sh +167 -0
- package/providers/codex/tools/refresh-manifest.mjs +81 -0
- package/providers/codex/wake-helper/TRANSPORT-NOTES.md +25 -0
- package/providers/codex/wake-helper/integration.test.mjs +285 -0
- package/providers/codex/wake-helper/kijito-wake-helper.mjs +488 -0
- package/providers/codex/wake-helper/kijito-wake-helper.test.mjs +128 -0
- package/providers/codex/wake-helper/mock-daemon.mjs +64 -0
- package/providers/codex/wake-helper/status-probe.mjs +45 -0
- package/providers/codex/wake-helper/ws-uds.mjs +153 -0
- package/providers/monitor/.github/workflows/publish-npm.yml +38 -0
- package/providers/monitor/.github/workflows/publish-pypi.yml +30 -0
- package/providers/monitor/CHANGELOG.md +465 -0
- package/providers/monitor/IMPORT-PROVENANCE.md +68 -0
- package/providers/monitor/LICENSE +202 -0
- package/providers/monitor/NOTICE +7 -0
- package/providers/monitor/OPAQUE-OUTPUT-ENFORCEMENT.md +45 -0
- package/providers/monitor/README.md +571 -0
- package/providers/monitor/RELEASING.md +125 -0
- package/providers/monitor/arm-hive-monitor.sh +13 -0
- package/providers/monitor/bin/cli.js +43 -0
- package/providers/monitor/com.kijito.inbox-monitor.plist.template +59 -0
- package/providers/monitor/docs/DESIGN.md +984 -0
- package/providers/monitor/kijito-inbox-monitor@.service.template +95 -0
- package/providers/monitor/kijito_inbox_monitor.py +3666 -0
- package/providers/monitor/package.json +35 -0
- package/providers/monitor/pyproject.toml +42 -0
- package/providers/monitor/scripts/mutation-check.py +399 -0
- package/providers/monitor/scripts/prepublish-gate.sh +284 -0
- package/providers/monitor/test_kijito_monitor.py +5609 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to kijito-inbox-monitor are documented in this file.
|
|
4
|
+
The format is based on Keep a Changelog, and this project follows Semantic Versioning.
|
|
5
|
+
|
|
6
|
+
## [0.5.0] - 2026-08-15
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- **Darwin `boottime` re-sourced: `CLOCK_MONOTONIC_RAW`, not `CLOCK_MONOTONIC`.** Darwin's `CLOCK_MONOTONIC`
|
|
10
|
+
does include sleep, but it is calendar-derived - measured 2026-08-15 it read *exactly*
|
|
11
|
+
`wall − kern.boottime` to three decimals - so it absorbs NTP adjustments to the wall clock. On a
|
|
12
|
+
fresh-uptime Mac whose wall clock stepped back ~8.3 s after boot, it emitted
|
|
13
|
+
`boottime 4957.865 < monotonic 4966.194`, violating the definitional invariant `boottime ≥ monotonic`
|
|
14
|
+
that consumers difference against. Both semantics now come from raw clocks sharing one tick source
|
|
15
|
+
(`CLOCK_MONOTONIC_RAW` / `CLOCK_UPTIME_RAW`), so the invariant holds by construction.
|
|
16
|
+
- **Inverted-pair quarantine at the emit chokepoint.** If a platform ever hands the producer
|
|
17
|
+
`boottime < monotonic`, the `boottime` reading is omitted (never faked) and preserved under
|
|
18
|
+
`emitted.clock_defect` with the rejected value and its source constant, so a broken mapping reports
|
|
19
|
+
itself on the row instead of poisoning dwell measurements downstream. A positive-control test proves
|
|
20
|
+
the quarantine fires on a deliberately inverted pair - the prior invariant test passed vacuously on any
|
|
21
|
+
zero-sleep host, where an inverted mapping reads as an equal pair.
|
|
22
|
+
|
|
23
|
+
⚠️ **WHAT THE REVIEW OF THIS SET DOES AND DOES NOT COVER.** The seven alarm/liveness changes were reviewed
|
|
24
|
+
and approved by an independent engine-literate reviewer who re-ran the suite and the mutation harness rather
|
|
25
|
+
than accepting them. Two limits were disclosed rather than discovered: the reviewer did **not** run the
|
|
26
|
+
producer against a live account, so "verified live" claims here rest on the author's word; and the docs/gate
|
|
27
|
+
commit was out of that review's scope.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- **A wake `nonce` on every event** - 11 base62 chars, `base62(sha256(event_id))[:11]`. It lets a consumer join
|
|
31
|
+
a delivered wake back to the queue entry that carried it, and it is recomputable from the `event_id` in the
|
|
32
|
+
same row. **Derived rather than random on purpose:** this producer already defines identity (a `new` event
|
|
33
|
+
keeps the same id across a restart, a state-loss re-delivery, and two watchers of one inbox), and a
|
|
34
|
+
per-emission random nonce would have contradicted that - calling one re-delivered message two different wakes,
|
|
35
|
+
so a consumer finds no queue entry for the second, scores it lost, and alarms **on the recovery path the
|
|
36
|
+
producer exists to survive**. Deriving inherits the existing semantics for both identity families and changes
|
|
37
|
+
neither. Note it is a label and not a secret (deterministic, hence guessable from the `event_id`), and that it
|
|
38
|
+
identifies a *wake* rather than a *delivery* - two panes handed the same message share a nonce, so consumer
|
|
39
|
+
records must key on `(nonce, session_id)`.
|
|
40
|
+
- **Emission timestamps (`emitted`)** - `wall`, `monotonic`, `boottime` and `src`, all read at one instant.
|
|
41
|
+
Differencing wall against monotonic across two events measures the time the machine **was not executing**,
|
|
42
|
+
which is what separates "this sat in a queue for hours" from "the host was suspended" - indistinguishable in
|
|
43
|
+
wall time and completely different problems. Measured on a Parallels guest: 72.79 h of hypervisor freeze
|
|
44
|
+
looked like ordinary elapsed wall time, while `BOOTTIME - MONOTONIC` read `0.00 s` throughout, because a
|
|
45
|
+
hypervisor pause stops the guest's clocks together and the guest is not running to notice.
|
|
46
|
+
**The keys name SEMANTICS, not OS constants, because on macOS the two are inverted:** `monotonic` means "does
|
|
47
|
+
not tick while the machine is not executing" (Linux `CLOCK_MONOTONIC`, Darwin `CLOCK_UPTIME_RAW`) and
|
|
48
|
+
`boottime` means "does tick" (Linux `CLOCK_BOOTTIME`, Darwin `CLOCK_MONOTONIC`). macOS `CLOCK_MONOTONIC`
|
|
49
|
+
*includes* sleep and macOS has no `CLOCK_BOOTTIME`, so reading constants by name there yields the wrong
|
|
50
|
+
quantity under the right label with nothing complaining - measured on a real Mac at 408.19 h vs 389.99 h,
|
|
51
|
+
an 18.20 h gap that is the accumulated sleep. `src` names the constant behind each value so consumers can
|
|
52
|
+
audit the mapping from the row instead of assuming it. A key is **omitted rather than faked** where its
|
|
53
|
+
*semantic* is unavailable: a fabricated value is indistinguishable from a real zero-freeze reading, which is
|
|
54
|
+
the failure the field exists to prevent. `ts` is unchanged; `emitted.wall` is the coherent wall reading.
|
|
55
|
+
- **An alarm for escalated mail nobody is answering.** Fires only when a member holds mail a sender marked
|
|
56
|
+
URGENT *and* no activity from that member has been observed - both halves positive. Silence alone is never
|
|
57
|
+
the trigger, because idle-by-design and wedged are indistinguishable from outside; the urgent flag is a
|
|
58
|
+
sender declaring an expectation, which is what makes the silence mean something. Clears when either half
|
|
59
|
+
clears, with no ack.
|
|
60
|
+
- **An alarm for unread mail the inbox window did not show** (`unread_not_shown` above zero) - the case where
|
|
61
|
+
the endpoint tells you it is holding mail this response did not hand you.
|
|
62
|
+
- **An attributable liveness signal** (`--activity-file`) and a one-shot evaluation of it
|
|
63
|
+
(`--check-activity`), so an external supervisor can assert the producer is not merely running but working.
|
|
64
|
+
- **A producer-owned event id on every event**, so a consumer can deduplicate across restarts and rotations
|
|
65
|
+
without inferring identity from content.
|
|
66
|
+
- **`--no-urgent-alerts`** - see Changed.
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
- **`--no-stranded-alerts` no longer silences the urgent-unanswered alarm.** It gated both, while saying so
|
|
70
|
+
in only one of the three places an operator reads - and its own documented advice ("set this if you keep
|
|
71
|
+
deliberate test inboxes") therefore turned off a higher-severity alarm about real members as a side effect.
|
|
72
|
+
The two alarms now have separate flags; pass both to disable both. The coupling is pinned by tests through
|
|
73
|
+
the real run loop and by its own mutation, because nothing tested the mapping either way before.
|
|
74
|
+
(Found in review of the urgent-unanswered alarm, which is unreleased - so no released behaviour changes.)
|
|
75
|
+
- **Account-level alarms are routed by evidence of a consumer**, instead of to every directory persona -
|
|
76
|
+
long-dead test personas were receiving alerts into streams nobody reads.
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
- **One persona's hostile or un-tightenable `.lock` sidecar killed the whole producer.** `InsecureFile` is an
|
|
80
|
+
`OSError` by design, but every containment arm caught `FatalConfig` only, so the throw went past all of
|
|
81
|
+
them on the startup path and both late-add paths. Containment is now per-persona at four sites, and fails
|
|
82
|
+
**closed**: if every persona fails to initialise, the producer raises rather than staying up watching
|
|
83
|
+
nothing. The lesson worth keeping is that the earlier fix checked that the catch *existed* and never that
|
|
84
|
+
its type *covered the throw*.
|
|
85
|
+
- **`RELEASING.md` prescribed a path the package does not contain** - a clone could not run the gate the
|
|
86
|
+
document mandates. Restated as the property plus a self-contained check, with a third prepublish gate
|
|
87
|
+
(`path-escapes`) so the class cannot return.
|
|
88
|
+
|
|
89
|
+
## [0.4.0] - 2026-07-28
|
|
90
|
+
|
|
91
|
+
⚠️ **WHAT THIS RELEASE DOES AND DOES NOT ESTABLISH.** Two audit rounds swept a defect CLASS rather than
|
|
92
|
+
patching instances: *a safety check whose result nobody consumes, a safety state nothing clears, and - added
|
|
93
|
+
in the second round - a state committed as if an action succeeded before it did.* **It establishes that the
|
|
94
|
+
KNOWN INSTANCES ARE FIXED. It does NOT establish that no further shapes exist.** That distinction is not
|
|
95
|
+
boilerplate: widening the class immediately surfaced two more sites AND a blind spot in the detector itself
|
|
96
|
+
(bool-return detection was not transitive, so one function was invisible and another was caught only by the
|
|
97
|
+
accident of an unrelated `return True`). A class derived from N instances cannot be validated by
|
|
98
|
+
rediscovering those N.
|
|
99
|
+
⚠️ **PROVENANCE OF THE REVIEW.** The final verdict came from the author of the acceptance criteria, who was
|
|
100
|
+
also a party to the technical dispute it adjudicated. She disclosed both conflicts and held the
|
|
101
|
+
do-not-ship outcome as genuinely reachable - and returned it once before this release. The independent
|
|
102
|
+
reviewer originally assigned never read the request.
|
|
103
|
+
|
|
104
|
+
### Fixed (re-audit 11 - the alarm path)
|
|
105
|
+
- **An alarm could be recorded as raised when it was never delivered.** Every alarm committed its
|
|
106
|
+
"already alarmed" state BEFORE emitting and discarded the emit's answer, and three of the four had no
|
|
107
|
+
second channel. An undelivered alarm was never re-raised - not when the channel recovered, and not after
|
|
108
|
+
a restart, because `gap_alerted` is persisted. **Mail was never at risk** (the cursor holds correctly
|
|
109
|
+
throughout); it was the ALARMS that vanished, which matters because the headline promise is that a walk
|
|
110
|
+
which cannot complete pins *loudly* rather than in silence. `WatchTarget.lifecycle` now returns delivery,
|
|
111
|
+
and alarms go through `_alarm()`, which falls back to **stderr** - never a retry down the channel that
|
|
112
|
+
just failed. A pure announcement latch (`gap_alerted`) commits only on delivery; behavioural state
|
|
113
|
+
(`fsm_state`, `pin_evidence_intact`) commits regardless, because refusing to record evidence loss would
|
|
114
|
+
trade a lost alarm for a lost invariant. Lifecycle events remain unacknowledged and ungated (DESIGN.md
|
|
115
|
+
§170 unchanged); the guaranteed/informational split is now documented as §14.9.
|
|
116
|
+
- **The recovery edge failed the same way, facing the other way** - both `recovered` sites committed
|
|
117
|
+
`fsm_state = "UP"` and discarded the emit, leaving a consumer that saw the DOWN alert holding an alarm it
|
|
118
|
+
could never clear.
|
|
119
|
+
- **The dead-man's switch had no test at all.** Deleting the liveness DOWN alert outright left the entire
|
|
120
|
+
suite green - the one event the README sells as the headline feature was undefended. It now has tests and
|
|
121
|
+
its own mutation.
|
|
122
|
+
- **Two case-variant personas could refuse to start the whole producer.** `requested_personas()` deduped
|
|
123
|
+
exactly while `new_personas()` casefolds, so `--persona Loom --persona loom` resolved to one state path;
|
|
124
|
+
the second `flock` raised out of an uncaught list comprehension and killed startup for *every* persona,
|
|
125
|
+
blaming "another watcher" for a collision with itself.
|
|
126
|
+
|
|
127
|
+
### Fixed (documentation)
|
|
128
|
+
- `RELEASING.md` claimed the producer "does not run this package (yet)" and "executes the WORKING TREE
|
|
129
|
+
directly", four lines after the preceding section said the opposite. It had been stale since the producer
|
|
130
|
+
was pinned to a read-only artifact, and the stale half was the dangerous one: a reader who trusted it
|
|
131
|
+
would edit the tree, restart, and **deploy nothing while believing they deployed**.
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
### Security
|
|
135
|
+
- **The first fix for the permissions bug was itself a worse bug** (Loom re-audit 9, HIGH 1). The repair
|
|
136
|
+
introduced in the previous entry followed symlinks, validated neither owner nor file type, and - because
|
|
137
|
+
it was deliberately best-effort so that "a file we do not own cannot kill the watcher" - wrote the mail
|
|
138
|
+
anyway when the `chmod` failed. Measured: a pre-existing 0666 file stayed 0666 **and received mail**; a
|
|
139
|
+
symlink's target was chmod'ed and appended to; a *dangling* symlink caused its target to be created in
|
|
140
|
+
another directory. A passive disclosure had been turned into an active write primitive. Opens now use
|
|
141
|
+
`O_NOFOLLOW` and `O_NONBLOCK` (a FIFO at the path would otherwise block the writer forever - a hang,
|
|
142
|
+
which is worse than a crash because nothing reports it) and validate on the already-open descriptor that
|
|
143
|
+
the file is regular, owned by this user, and exactly 0600. Anything else is refused, and a refusal is a
|
|
144
|
+
**failed delivery**: the cursor holds and the mail is retried rather than written somewhere unsafe.
|
|
145
|
+
- **Only the file being opened was repaired** (Loom re-audit 9, HIGH 2). Pre-existing rotated archives and
|
|
146
|
+
an existing state file kept their old modes, and a 0700 file was left alone because the check tested
|
|
147
|
+
`mode & 0o077` rather than requiring exactly 0600. All persisted artifacts are now repaired, and
|
|
148
|
+
directories are 0700 at **every** level (`os.makedirs(mode=)` applies the mode to the leaf only, so
|
|
149
|
+
nested paths left their parents 0755). A directory writable by other users is reported.
|
|
150
|
+
- **The event stream was world-readable and it carries message bodies** (Loom re-audit 8, HIGH 1). Event
|
|
151
|
+
files and the state-file lock sidecar were created with a plain `open()`, which takes the process umask -
|
|
152
|
+
022 by default - so every `events.<persona>.ndjson` was mode 0644 and readable by any other local user,
|
|
153
|
+
with message content in it unless `--no-content` was set. The auth token (0600) and the state file (0600
|
|
154
|
+
via `mkstemp`) were already correct, which is what made the gap easy to miss: the one file nobody had
|
|
155
|
+
thought about is the one holding the plaintext. Both are now created 0600, directories this tool creates
|
|
156
|
+
are 0700, and an **existing** file that is more permissive is tightened on open and the change reported -
|
|
157
|
+
because the creation mode does nothing for files that already leaked. Rotated archives inherit 0600 from
|
|
158
|
+
the live file. If you have been running an earlier version, check the modes on your events files.
|
|
159
|
+
|
|
160
|
+
### Changed
|
|
161
|
+
- **Delivery is now ACKNOWLEDGED rather than assumed, and the delivery guarantee is stated honestly as
|
|
162
|
+
at-least-once, in order** (Loom re-audit 7, HIGH 1). The cursor *is* the acknowledgement: once it moves
|
|
163
|
+
past an id that message is never fetched again. It previously advanced on selection, so an `--exec` that
|
|
164
|
+
exited non-zero, timed out, or failed to spawn had its result discarded and the message was silently
|
|
165
|
+
dropped - on the one path whose entire purpose is waking an agent. It now advances only over messages the
|
|
166
|
+
emitter reports as delivered (`exec` exit 0, or a successful write), stopping at the first failure so a
|
|
167
|
+
consumer never sees message N+1 ahead of a retried N. **Make your consumer idempotent**; `KIJITOMON_ID` is
|
|
168
|
+
stable across re-deliveries. This also resolves a contradiction that already existed between the README
|
|
169
|
+
("exactly once across restarts") and DESIGN.md ("best-effort/at-most-once"); the docs now agree.
|
|
170
|
+
|
|
171
|
+
### Fixed
|
|
172
|
+
- **A window that withheld nothing while pointing at older mail was believed** (Loom re-audit 7, HIGH 4).
|
|
173
|
+
The server sets `next_before_id` *exactly* when rows were withheld, so "I hid nothing" and "there is more"
|
|
174
|
+
cannot both be true - and the gap check never looked at the continuation at all, so it took the first half
|
|
175
|
+
at its word and advanced over whatever the second half pointed at. Both directions of the contradiction now
|
|
176
|
+
pin. Verified against the live API across 14 pages, including the exactly-at-limit edge that could have made
|
|
177
|
+
the rule fire on healthy traffic (it does not).
|
|
178
|
+
- **A malformed pin field in the state file failed open** (Loom re-audit 7, HIGH 2). `pin_forced` was read
|
|
179
|
+
as `value is True`, so a JSON `1` normalised to false and silently *unpinned* the watermark, letting the
|
|
180
|
+
replay cap cross the very span the pin was protecting; `pin_evidence_intact: 0` had the mirror bug, and
|
|
181
|
+
booleans were accepted as message ids. Every persisted field is now read strictly, and anything
|
|
182
|
+
unrecognised is treated as a corrupt state file rather than a permissive default.
|
|
183
|
+
- **A persona respelled in a different case destroyed its own cursor** (Loom re-audit 7, HIGH 3). The state
|
|
184
|
+
*path* casefolds while the stored *identity* keeps the directory's spelling, so a file written as
|
|
185
|
+
`persona=Loom` was reloaded by a run that discovered `loom`, judged a mismatch, and re-baselined - skipping
|
|
186
|
+
every message since. A case-only difference now migrates the file and keeps the cursor. Deliberately
|
|
187
|
+
narrow: only the query *value* is compared case-insensitively.
|
|
188
|
+
- **The corruption-recovery pin could never clear** (Loom re-audit 7, HIGH 5). It parks the watermark one
|
|
189
|
+
below the window it re-emits, which made the ordinary release test unsatisfiable by that same window - so
|
|
190
|
+
the pin held forever, the cursor froze, and because delivered ids were recorded only while a *gap* was
|
|
191
|
+
pinned, the identical window was re-emitted on every poll and across every restart. The pin now carries a
|
|
192
|
+
persisted release floor, and every delivered id the watermark does not cover is remembered whatever left it
|
|
193
|
+
uncovered. (Repairing a fail-open into a permanent fail-closed is not a repair.)
|
|
194
|
+
- **A cursor write whose durability was unproven was reported to nobody** (Loom re-audit 9, MEDIUM). The
|
|
195
|
+
previous entry made `save()` *return* a durability status; the call site then discarded it - the same
|
|
196
|
+
defect one layer out. The watcher now consumes that answer and reports an unproven cursor once, clearing
|
|
197
|
+
when persistence recovers.
|
|
198
|
+
- **A sink that could not be opened safely crashed the poll loop or fell through to stdout** (Loom
|
|
199
|
+
re-audit 9, MEDIUM). A failed reopen after rotation raised out of `write()`, which under a supervisor is
|
|
200
|
+
a crash loop; and a refused per-persona sink returned `None`, which means "no sink configured, write to
|
|
201
|
+
stdout" - printing the very mail that had just been declined. Both are now failed deliveries, contained
|
|
202
|
+
to the affected persona, and a broken sink retries and recovers on its own.
|
|
203
|
+
- **The events file's DIRECTORY ENTRY was never made durable** (Loom re-audit 8, HIGH 2). `fsync` on the
|
|
204
|
+
file descriptor makes the *bytes* durable; the *name* lives in the directory. On create and on rotation
|
|
205
|
+
the directory was left unsynced, so the state directory could persist an advanced cursor while the event
|
|
206
|
+
pathname or a rotated archive was lost - and `--state-file` and `--events-file-template` may be in
|
|
207
|
+
*different* directories, so syncing one proves nothing about the other. The events directory is now
|
|
208
|
+
synced before the cursor that acknowledges those events is persisted, and a failure holds the cursor.
|
|
209
|
+
- **A failed state-directory `fsync` was reported as success** (Loom re-audit 8, HIGH 3). `save()` called
|
|
210
|
+
the sync and discarded its answer, so the cursor was written and its durability merely assumed, with no
|
|
211
|
+
diagnostic. `save()` now returns whether the write is durable and says so loudly when it is not. (The
|
|
212
|
+
failure direction is re-delivery rather than loss - a reverted state file replays mail - but a watcher
|
|
213
|
+
that cannot tell you it failed to persist will keep not telling you.)
|
|
214
|
+
- **A cursor could outlive the event it acknowledged** (Loom re-audit 7, MEDIUM). The state file's temp was
|
|
215
|
+
fsynced but the directory holding the rename was not, and the event sink was flushed but never fsynced.
|
|
216
|
+
Events are now fsynced *before* the cursor that acknowledges them is persisted, and the state directory is
|
|
217
|
+
fsynced after `os.replace`; a sink that cannot be synced retracts that poll's acknowledgements entirely.
|
|
218
|
+
- The single-writer lock file descriptor was never released - leaked on every refused lock, and the source of
|
|
219
|
+
the suite's two `ResourceWarning`s. `StateFile.unlock()` now exists and is called on shutdown.
|
|
220
|
+
|
|
221
|
+
### Added
|
|
222
|
+
- **Alarm routing now requires evidence of a consumer.** Account-level alarms went to every directory
|
|
223
|
+
persona, which meant long-dead test personas kept receiving alerts into streams nobody reads. Eligibility
|
|
224
|
+
is now positive - observed authorship, or memories the directory says they own - and it mirrors the
|
|
225
|
+
stranded-mail ownership test on purpose, since two predicates for one question drift apart and then
|
|
226
|
+
disagree. Authorship alone suffices so first contact is not broken, and an unreported memory count leaves
|
|
227
|
+
a persona eligible because no data is not evidence of absence.
|
|
228
|
+
|
|
229
|
+
It fails open: if the predicate would leave nobody, every directory watcher is used instead. An alarm
|
|
230
|
+
delivered to a dead stream costs a line; one delivered to nobody is the silent failure the tool exists to
|
|
231
|
+
prevent. Measured live, recipients fell from 25 to 18.
|
|
232
|
+
|
|
233
|
+
- **Urgent-unanswered alarm.** The watcher reports members holding mail a sender marked **urgent** while no
|
|
234
|
+
activity from them has been observed.
|
|
235
|
+
|
|
236
|
+
"Is this member stuck?" is normally unanswerable from outside: idle-by-design and wedged look identical,
|
|
237
|
+
so the obvious alarm fires on every quiet persona and rots into noise. The urgent flag breaks the tie
|
|
238
|
+
because it is a *sender* declaring an expectation - not the recipient declaring liveness - and silence
|
|
239
|
+
only means something once something was expected. A quiet member with no urgent mail never trips it, so
|
|
240
|
+
the alarm fires exactly where somebody escalated and nothing happened.
|
|
241
|
+
|
|
242
|
+
Both halves of the predicate must be positive: `unread_urgent > 0` and an explicit "no activity in a span
|
|
243
|
+
we covered". A NOT-OBSERVABLE answer means the watcher was not running then, and reporting that as silence
|
|
244
|
+
would be a fabrication. It costs no request - `unread_urgent` arrives on the same row as the unread count
|
|
245
|
+
the fast path already fetches every tick, and was previously parsed and discarded.
|
|
246
|
+
|
|
247
|
+
Kept disjoint from the stranded-mail alarm on purpose - that one is for inboxes nobody owns, this one for
|
|
248
|
+
real members who are not responding - because two alarms covering one inbox drift apart and then disagree.
|
|
249
|
+
Same honesty rules as the rest: an `alert` rather than a new event name, one summarising event per
|
|
250
|
+
watcher, the observation and never the diagnosis, self-clearing when either half clears, and no ack.
|
|
251
|
+
|
|
252
|
+
- **`--activity-file PATH`: publish who has been observed AUTHORING mail.** Refreshed each tick, it lets a
|
|
253
|
+
harness answer "has X been active since my message?" from data the watcher already collects.
|
|
254
|
+
|
|
255
|
+
Authorship was chosen over the two signals that look better and are both forgeable by accident. Inbox
|
|
256
|
+
read-state can be produced for any persona by any agent calling the inbox with the default
|
|
257
|
+
`mark_read=true`, and it fails the other way too, since a member consuming its own event stream reads its
|
|
258
|
+
mail without touching read-state. And a GET on the presence endpoint carrying a persona parameter beats
|
|
259
|
+
that persona into the active roster, so merely probing someone makes them look alive. Only B produces B's
|
|
260
|
+
outbound, and nobody else can manufacture or erase it.
|
|
261
|
+
|
|
262
|
+
It costs no request: all-personas mode already fetches every inbox each tick with `mark_read=false`, and
|
|
263
|
+
every row already carries its author. That matters, because the alternative - a client polling every
|
|
264
|
+
inbox on a timer to reconstruct this - puts a loop that reads everyone's mail on a schedule, where one
|
|
265
|
+
missing `mark_read=false` destroys read-state fleet-wide.
|
|
266
|
+
|
|
267
|
+
Both coverage limits are published, because a claim of silence is only as good as the watching.
|
|
268
|
+
`observed_since` bounds the process; `observation_floor_id` is the MAXIMUM of the per-inbox window floors,
|
|
269
|
+
deliberately not the minimum - a persona's mail lands in whichever inbox they wrote to, so a claim that
|
|
270
|
+
they authored nothing is only as strong as the worst-covered inbox. Queries below the floor answer NOT
|
|
271
|
+
OBSERVABLE rather than "silent". The rendered observation carries the wait count and last-evidence stamp
|
|
272
|
+
and is asserted by test to state no cause, since deadlocked, unreachable and still-working are
|
|
273
|
+
indistinguishable from this data and need opposite responses.
|
|
274
|
+
|
|
275
|
+
`--check-activity PERSONA --since-id N` evaluates a published report in one shot, with no token, network
|
|
276
|
+
or watch loop, so a shell heartbeat can call it. It exits 0 on evidence of activity, 1 on a silence in a
|
|
277
|
+
span the report actually covered (printing the observation), and 2 on NOT OBSERVABLE or an unreadable
|
|
278
|
+
report. 1 and 2 are distinct deliberately: collapsing them turns "I was not watching" into "they were
|
|
279
|
+
silent". The watcher and the one-shot share one implementation of the tri-state.
|
|
280
|
+
|
|
281
|
+
- **Every event now carries a producer-owned `event_id`**, so a consumer can dedupe without hashing our NDJSON
|
|
282
|
+
bytes. Byte-hashing works until it doesn't: it couples the consumer to our serialisation, so a change to key
|
|
283
|
+
order, spacing or `--content-chars` silently changes the dedupe key and re-delivers old events. Prompted by a
|
|
284
|
+
real consumer deduping ID-less events on `event+ts` - unique only while two events never land inside one clock
|
|
285
|
+
tick, and the timestamp is stamped at emit time.
|
|
286
|
+
|
|
287
|
+
Two identities, because messages and signals need opposite guarantees. `new` events carry the message's
|
|
288
|
+
identity (`<persona>:new:<message id>`), so the same message always yields the same id - across a restart, a
|
|
289
|
+
re-delivery after state loss, and two watchers of one inbox; dedupe on it for exactly-once processing. Every
|
|
290
|
+
other event is a signal and gets an id unique to that emission (`<persona>:<event>:<run>-<n>`), because a
|
|
291
|
+
recurrence is a genuinely different event and a second outage is a second thing worth seeing. Repeated
|
|
292
|
+
announcements of an unchanged condition are suppressed at the source instead, where suppression belongs.
|
|
293
|
+
|
|
294
|
+
`<run>` is random per process. A bare in-process counter is specifically ruled out: it restarts at 1 and hands
|
|
295
|
+
ids a consumer has already seen to brand-new events, so a correct consumer drops live mail - a worse failure
|
|
296
|
+
than the duplicate the id was introduced to prevent. Ids are stamped at the single emit chokepoint, so a future
|
|
297
|
+
event kind cannot forget one, and are exported to `--exec` consumers as `$KIJITOMON_EVENT_ID`.
|
|
298
|
+
|
|
299
|
+
- **Unread-mail-outside-the-window alarm.** The inbox endpoint reports `unread_not_shown` - unread messages
|
|
300
|
+
it holds that this response did not return. Above zero, the watcher raises an `alert` carrying the count,
|
|
301
|
+
the window floor, the cursor, and `above_watermark`. It is deliberately a cheap signal rather than a
|
|
302
|
+
coverage mechanism: the count has no cursor of its own, so it can say THAT mail is out of view but never
|
|
303
|
+
WHICH rows, and coverage still comes from the backward walk that terminates.
|
|
304
|
+
|
|
305
|
+
The event states an observation, not a diagnosis. The count includes unread mail anywhere in the inbox -
|
|
306
|
+
among it messages this watcher already delivered that the agent simply has not read - so it is not on its
|
|
307
|
+
own evidence that anything was missed. `above_watermark` is the fact that separates the two cases, and is
|
|
308
|
+
reported rather than resolved.
|
|
309
|
+
|
|
310
|
+
A zero is not self-justifying, and this is the trap the implementation is built around: the server computes
|
|
311
|
+
the field only when it withheld something, so it is `0` **by construction** on a page with nothing older.
|
|
312
|
+
The negative answer therefore requires positive evidence - the zero was genuinely computed, or the window
|
|
313
|
+
is structurally complete - and a field the server never sent is a third state that asserts nothing either
|
|
314
|
+
way. For the same reason the check runs on the newest-page poll only. Measured against a live inbox holding
|
|
315
|
+
four unread messages: the newest page reported `0` (correctly), a mid-walk page reported `4` (the whole
|
|
316
|
+
inbox's unread, not that window's), and the terminal page of the walk reported `0` while all four sat above
|
|
317
|
+
it - so feeding walk pages to the check would both invent alarms and clear real ones.
|
|
318
|
+
|
|
319
|
+
Routed like the stranded-mail alarm (an `alert` rather than a new event name, no ack, self-clearing) but
|
|
320
|
+
failing the opposite way when the persona directory is unknown: that alarm withholds because it would
|
|
321
|
+
otherwise flag every persona, while this one concerns the target's own inbox, where firing needlessly costs
|
|
322
|
+
a line in a stream nobody reads and withholding costs the silent wake gap this tool exists to prevent.
|
|
323
|
+
|
|
324
|
+
- **Stranded-mail alarm.** The watcher reports mail sitting in an inbox that nothing consumes. Such mail
|
|
325
|
+
is undeliverable and nothing else reports it: the sender gets a success and a message id, the recipient
|
|
326
|
+
gets no signal, and there is no bounce. Two real cases prompted it - a case-variant of a live persona,
|
|
327
|
+
whose reply sat unread for 14 days, and a group-looking name (`all`) with no broadcast semantics behind
|
|
328
|
+
it, which swallowed a fleet-wide announcement for 4 days.
|
|
329
|
+
|
|
330
|
+
An inbox is flagged when it holds mail and **either** the persona directory does not list it **or** it
|
|
331
|
+
owns zero memories - nothing has ever written as that persona, so nobody is working under it. The second
|
|
332
|
+
test matters because a directory built as a union of registered *recipients* lists every typo the moment
|
|
333
|
+
someone sends to it, which would leave the first test unable to fire. Ownership reads the top-level
|
|
334
|
+
`memory_count`, deliberately not a sum of `projects[].count`: project counts exclude global-scoped
|
|
335
|
+
memories, so a persona whose memories are all global sums to zero and looks unowned - measured against a
|
|
336
|
+
live account, that mistake would have flagged eight of nine active personas. Where a server reports no
|
|
337
|
+
memory counts the signal stays quiet rather than guessing.
|
|
338
|
+
|
|
339
|
+
Both signals come from endpoints already fetched, so the check costs no extra request. Reported once per
|
|
340
|
+
inbox per process, to stderr and as one summarising event per watcher; a case-variant is diagnosed as
|
|
341
|
+
such, naming its twin. Disable with `--no-stranded-alerts`.
|
|
342
|
+
|
|
343
|
+
Two routing rules are load-bearing and easy to get wrong: the alarm is an `alert` rather than a new event
|
|
344
|
+
name, so consumers already filtering `alert` surface it without being rearmed - a fresh name would have gone
|
|
345
|
+
unwatched on every armed consumer, because a running `grep` never re-reads its argv; and it is
|
|
346
|
+
routed only to watchers backed by a real directory persona, because a stranded inbox has mail and
|
|
347
|
+
therefore acquires a watch target and stream of its own - alerting every target would write the alarm
|
|
348
|
+
into the very stream nobody reads. Producing an event is not delivering it.
|
|
349
|
+
- `$KIJITOMON_STRANDED` exposes the affected inboxes to `exec-per-event` consumers, comma-separated.
|
|
350
|
+
|
|
351
|
+
### Fixed
|
|
352
|
+
- **A bounded inbox window could permanently skip mail** (reported by Loom). The inbox endpoint returns
|
|
353
|
+
the **newest** messages that fit a count limit *and* an aggregate content budget, and declares what it
|
|
354
|
+
left out via `truncated` / `size_truncated` / `size_dropped`. The watcher parsed only `result`, discarded
|
|
355
|
+
those fields, and advanced its cursor to the highest id it had seen - so any message the server omitted
|
|
356
|
+
while it sat *above* the cursor was never emitted and was stepped over permanently. The truncation was
|
|
357
|
+
never silent in the data, only in the handling of it.
|
|
358
|
+
|
|
359
|
+
The cursor is now a **confirmed-contiguous watermark**. When the window reaches back past it, every
|
|
360
|
+
omitted message is older than anything still owed and nothing changes - the steady state, since
|
|
361
|
+
long-polling keeps the backlog small. When the window starts *above* the watermark while the server
|
|
362
|
+
admits it withheld rows, the watcher **walks the span backward** with `before_id`, paging until it
|
|
363
|
+
reaches the watermark or the chain ends, and advances only then. A walk that fails, stalls, or exhausts
|
|
364
|
+
its page budget proves nothing, so the watermark **pins** and an `alert` names the cursor, the window
|
|
365
|
+
floor and the shortfall.
|
|
366
|
+
|
|
367
|
+
Coverage is established by **exhausting the chain, not by counting rows**. That distinction is what
|
|
368
|
+
makes an unquantified truncation resolvable at all: `truncated` states that rows were withheld without
|
|
369
|
+
saying how many, so no arithmetic can ever prove the span empty. It also reaches messages someone has
|
|
370
|
+
already **read** - precisely the rows most likely to be hiding in an old span, and the ones an
|
|
371
|
+
unread-only reconcile structurally cannot see.
|
|
372
|
+
|
|
373
|
+
Pinning is the point: advancing past an unresolved span makes the next poll see the window reaching back
|
|
374
|
+
past the cursor, declare itself safe, and bury the omission forever. The pin is persisted, so a restart
|
|
375
|
+
neither re-emits what was already delivered nor forgets the gap, and visible mail is still delivered
|
|
376
|
+
while pinned - failing closed costs no liveness. Pin tracking is bounded; on overflow the watcher says
|
|
377
|
+
plainly that it can no longer reason about the span rather than quietly dropping ids, and only an
|
|
378
|
+
authoritative walk can restore that ground truth.
|
|
379
|
+
|
|
380
|
+
Two accounting rules keep it honest. Mail arriving *between* the two reads is delivered but never counted
|
|
381
|
+
as recovery - new arrivals prove nothing about old omissions. And a lone oversized message
|
|
382
|
+
(`size_truncated` with `size_dropped: 0`) had its body clipped rather than being withheld, so it is not
|
|
383
|
+
an omission; count-limit truncation, size-budget drops and body clipping are accounted separately.
|
|
384
|
+
|
|
385
|
+
Corrupt pin state fails **closed**: a malformed record holds the pin with no tracking rather than
|
|
386
|
+
silently unpinning, because loading it as "nothing outstanding" would let the replay cap jump the cursor
|
|
387
|
+
over the very span the pin was protecting.
|
|
388
|
+
|
|
389
|
+
No mail was lost in practice before this: polling cadence kept every observed window reaching back past
|
|
390
|
+
the cursor. That was luck, not correctness - roughly eight typical messages in one gap exhausts the budget.
|
|
391
|
+
- **Case-variant personas no longer self-deadlock the watcher (silent wake gap).** A persona name was
|
|
392
|
+
mapped to its state file verbatim, but macOS (APFS) and Windows are case-**insensitive**, so
|
|
393
|
+
`Claude-chat` and `claude-chat` name the *same* file. Discovering a case-variant of an already-watched
|
|
394
|
+
persona made the watcher try to lock a state file it already held, so the variant was never adopted and
|
|
395
|
+
got **no event stream at all** - mail addressed to it woke nobody, and the failed adoption logged on
|
|
396
|
+
every tick (one observed 3-day run: 20,079 of 20,129 stderr lines from that single warning).
|
|
397
|
+
Persona matching is now case-insensitive throughout, and the persona's original case is preserved for the
|
|
398
|
+
API - case-insensitive match, case-preserving display. Note the deliberate asymmetry with the
|
|
399
|
+
stranded-mail check, which compares names **exactly**, because the server's inbox namespace *is*
|
|
400
|
+
case-sensitive and casefolding there would hide the very defect it detects.
|
|
401
|
+
- **Per-persona warnings are emitted once per process** instead of once per tick, so a condition that
|
|
402
|
+
cannot resolve itself can no longer grow stderr without bound.
|
|
403
|
+
|
|
404
|
+
## [0.3.0] - 2026-06-29
|
|
405
|
+
|
|
406
|
+
Near-instant wake via long-polling, with full self-heal.
|
|
407
|
+
|
|
408
|
+
### Added
|
|
409
|
+
- **Long-poll wake** (`--wait`, default 50s): the watcher holds a `/api/notify/pending?wait=&cursor=`
|
|
410
|
+
request that the server releases the instant new mail arrives, cutting wake latency from up to
|
|
411
|
+
`--poll-seconds` to near-instant **without raising the request rate** (one held connection per
|
|
412
|
+
account). Forward/backward compatible: against a server that doesn't support long-poll it
|
|
413
|
+
transparently falls back to interval polling and auto-upgrades once the server returns a cursor -
|
|
414
|
+
no redeploy. `--wait 0` disables it.
|
|
415
|
+
- **Instant new-persona pickup**: a newly created persona that receives mail is added as a watch
|
|
416
|
+
target within one tick (from the notify counts already fetched), instead of waiting for the
|
|
417
|
+
periodic `/api/personas` rescan.
|
|
418
|
+
|
|
419
|
+
### Reliability
|
|
420
|
+
- **Self-heal on connection loss** (wifi/NAT/Cloudflare/server-restart): a dropped or half-open hold
|
|
421
|
+
is detected by a client timeout above the server hold, then reconnected with exponential backoff,
|
|
422
|
+
resuming from the last opaque cursor so no wake is missed across the gap (lossless). The periodic
|
|
423
|
+
full per-persona inbox poll remains the by-message-id correctness backstop.
|
|
424
|
+
|
|
425
|
+
## [0.2.0] - 2026-06-29
|
|
426
|
+
|
|
427
|
+
Remote-only release. The monitor now watches your Kijito inbox at `api.kijito.ai` exclusively.
|
|
428
|
+
|
|
429
|
+
### Changed
|
|
430
|
+
- **Breaking:** the monitor targets the Kijito API at `https://api.kijito.ai` only. The `--url`
|
|
431
|
+
destination override and the `--allow-loopback` / `--allow-private` flags are removed.
|
|
432
|
+
- **Breaking:** a Kijito API token is now required. Provide it via `$KIJITOMON_TOKEN` or
|
|
433
|
+
`--token-file`; the process exits with a clear error if no token is set.
|
|
434
|
+
|
|
435
|
+
### Added
|
|
436
|
+
- A named `User-Agent` header on every request (required: the API is fronted by a WAF that
|
|
437
|
+
rejects the default Python-urllib agent).
|
|
438
|
+
|
|
439
|
+
### Fixed
|
|
440
|
+
- Persona discovery (`/api/personas`) now correctly targets the configured API host.
|
|
441
|
+
|
|
442
|
+
## [0.1.0] - 2026-06-24
|
|
443
|
+
|
|
444
|
+
First public release.
|
|
445
|
+
|
|
446
|
+
### Added
|
|
447
|
+
- Single, zero-dependency Python stdlib watcher for the Kijito inbox. It polls the inbox
|
|
448
|
+
and emits one event per new message, either as NDJSON on stdout or by running a command
|
|
449
|
+
per event, to keep a running agent's inbox live between tool calls.
|
|
450
|
+
- Multi-persona mode: one process watches every persona in the account via `/api/personas`, with
|
|
451
|
+
one `/api/notify/pending` fetch per tick fanned out in-process, per-persona cursors, and periodic
|
|
452
|
+
rediscovery of new personas.
|
|
453
|
+
- Per-persona owned, self-rotating event logs via `--events-file-template`, so each session
|
|
454
|
+
tails only its own `events.<persona>.ndjson`.
|
|
455
|
+
- Liveness alert state machine (`alert` after N consecutive failures, `recovered`, optional
|
|
456
|
+
`heartbeat`) for use as a dead-man's switch.
|
|
457
|
+
- SSRF-guarded `--url` override, peek-only inbox reads, monotonic-id cursor dedup, and
|
|
458
|
+
single-writer state files that resume cleanly under a supervisor.
|
|
459
|
+
- Console command `kijito-inbox-monitor`, installable with pipx, uv, or pip.
|
|
460
|
+
- An npm package that acts as a signpost to the PyPI tool (it delegates to `uvx`/`pipx`, or
|
|
461
|
+
prints install guidance), so the name is reserved on npm without a fragile Node installer.
|
|
462
|
+
|
|
463
|
+
[0.3.0]: https://github.com/KijitoAI/kijito-inbox-monitor/releases/tag/v0.3.0
|
|
464
|
+
[0.2.0]: https://github.com/KijitoAI/kijito-inbox-monitor/releases/tag/v0.2.0
|
|
465
|
+
[0.1.0]: https://github.com/KijitoAI/kijito-inbox-monitor/releases/tag/v0.1.0
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# providers/monitor — import provenance (P0-F29/A29 monitor-foundation)
|
|
2
|
+
|
|
3
|
+
**Imported by:** argus (monitor steward) · **Date:** 2026-08-17
|
|
4
|
+
**Source:** `KijitoAI/kijito-inbox-monitor` → **Destination:** `KijitoAI/kijito-tools` `providers/monitor/`
|
|
5
|
+
**Authorization:** steward prep `P0B-MONITOR-FOUNDATION-PREP.md` §4.2 · river CONCUR (hive msg 7986,
|
|
6
|
+
2026-08-17: F29 foundation/release/host acceptance) · loom informed (P0 context). Resolver of record:
|
|
7
|
+
Kijito memory 28999 + the committed sole-author commit maps.
|
|
8
|
+
|
|
9
|
+
This manifest is the audit trail because the pre-rewrite lineage was scrubbed by the 2026-08-15
|
|
10
|
+
sole-author window and now lives only in the external remap overlay. Where a fact below could not be
|
|
11
|
+
re-derived from public history, it is recorded here so tree-equality is auditable post-hoc rather than
|
|
12
|
+
asserted.
|
|
13
|
+
|
|
14
|
+
## Selection (P0-F29 "exact selected source")
|
|
15
|
+
|
|
16
|
+
| role | SHA | tree |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Frozen pin (pre-rewrite, contract of record) | `bc9110807dc274da8bb4f84e08c111bc33d055dd` | `135417e5d3d821bf29884e4f2ac9f4e19211c8b0` |
|
|
19
|
+
| Overlay-resolved (imported selection) | `01a70bc6b26a7450cf38b051a4e6aa32992b1763` | `135417e5d3d821bf29884e4f2ac9f4e19211c8b0` |
|
|
20
|
+
|
|
21
|
+
**Tree-equality proof (measured independently by argus, 2026-08-17):** the frozen pin and the
|
|
22
|
+
overlay-resolved SHA share the **identical tree object** `135417e5d3d821bf29884e4f2ac9f4e19211c8b0`;
|
|
23
|
+
`git diff bc9110807dc274da8bb4f84e08c111bc33d055dd 01a70bc6b26a7450cf38b051a4e6aa32992b1763` is empty.
|
|
24
|
+
(bc91108 was still resolvable in the local `kijito-inbox-monitor` object store, so this diff was
|
|
25
|
+
re-run first-hand rather than cited from the 2026-08-15 measurement.) Importing the literal frozen
|
|
26
|
+
SHA was rejected — it would resurrect the authorship the sole-author window exists to have scrubbed.
|
|
27
|
+
|
|
28
|
+
The contract's "public `3d595914…` strict ancestor" clause refers to the **pre-rewrite** public
|
|
29
|
+
lineage; the rewrite destroyed that ancestry in the current object graph (its post-rewrite form is
|
|
30
|
+
`d24c8c8ced9be7e2817bcdf52bbbb1377a16d4e9` per the commit map), so it is recorded here, not asserted
|
|
31
|
+
as git-verifiable against `01a70bc`.
|
|
32
|
+
|
|
33
|
+
## Post-import delta (journaled fast-forward, no selection change)
|
|
34
|
+
|
|
35
|
+
`01a70bc` → `902c21dbd2e93e91b7a6bd801104394577681b5e` (d1_clocks fix, PR #1, assay zero-findings
|
|
36
|
+
certification, hive 7715) → `bd04b28616d9a9558a4a6fa3bb5004db08fbf249` (`release: v0.5.0`, npm + PyPI
|
|
37
|
+
independently confirmed, fleet deployed).
|
|
38
|
+
|
|
39
|
+
| role | SHA | tree |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| FF target (imported HEAD content) | `bd04b28616d9a9558a4a6fa3bb5004db08fbf249` | `e46c189b0a1192406dc12c2039ef1dbe6500a96a` |
|
|
42
|
+
|
|
43
|
+
## Landed in kijito-tools (byte-exact, verified via content-addressed subtree hash)
|
|
44
|
+
|
|
45
|
+
| import step | commit | `HEAD:providers/monitor` tree | matches source tree |
|
|
46
|
+
|---|---|---|---|
|
|
47
|
+
| import overlay-resolved 01a70bc | `d9be542` | `135417e5d3d821bf29884e4f2ac9f4e19211c8b0` | ✅ = tree(01a70bc) |
|
|
48
|
+
| ff to v0.5.0 bd04b28 | `e4a87da` | `e46c189b0a1192406dc12c2039ef1dbe6500a96a` | ✅ = tree(bd04b28) |
|
|
49
|
+
|
|
50
|
+
Re-verify at any time: `git -C <kijito-tools> rev-parse d9be542:providers/monitor` = the 01a70bc tree,
|
|
51
|
+
`git -C <kijito-tools> rev-parse e4a87da:providers/monitor` = the bd04b28 tree. This manifest commit
|
|
52
|
+
adds IMPORT-PROVENANCE.md, so the current `providers/monitor` tree differs from `e46c189b…` by exactly
|
|
53
|
+
this file.
|
|
54
|
+
|
|
55
|
+
## Referenced artifacts (hashes)
|
|
56
|
+
|
|
57
|
+
- Commit map: `evidence/sole-author-remap-20260814/commit-map.inbox-monitor.txt`
|
|
58
|
+
— sha256 `0b1fb8a67165f3bb1146e80e4cc2eeabcf648649cd5fef15fafeacf9edcefc12`
|
|
59
|
+
(contains the line `bc91108… 01a70bc…`).
|
|
60
|
+
- Sole-author remap overlay: `Agents/OpenCode/SurfaceArea/SOLE-AUTHOR-SHA-REMAP-OVERLAY.md`
|
|
61
|
+
— sha256 `028199f2d81f14c7e145e744bba21cf593eb3b52819c289156f2b511837deb20`.
|
|
62
|
+
|
|
63
|
+
## Not done by this import (later P0B sequence)
|
|
64
|
+
|
|
65
|
+
Old-source transition (kijito-inbox-monitor stays the publish/pin home until fleet producers re-pin —
|
|
66
|
+
P0-C7 terminal), `_shared` doorbell + authenticated consumer lease (§4.4), and F29/A29 certify+enforce
|
|
67
|
+
of opaque `--no-content` output in both service templates plus the live Mac-producer gap (§4.6) are
|
|
68
|
+
tracked separately and not claimed here.
|