kairos-chain 3.62.0 → 3.64.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +78 -0
- data/lib/kairos_mcp/admin/router.rb +20 -8
- data/lib/kairos_mcp/admin/views/chain.erb +7 -2
- data/lib/kairos_mcp/admin/views/dashboard.erb +7 -2
- data/lib/kairos_mcp/admin/views/partials/_chain_blocks.erb +9 -1
- data/lib/kairos_mcp/drift_detection/correspondence_checker.rb +18 -3
- data/lib/kairos_mcp/kairos_chain/chain.rb +327 -59
- data/lib/kairos_mcp/storage/backend.rb +31 -2
- data/lib/kairos_mcp/storage/file_backend.rb +43 -7
- data/lib/kairos_mcp/tools/chain_history.rb +13 -4
- data/lib/kairos_mcp/tools/chain_status.rb +5 -1
- data/lib/kairos_mcp/tools/chain_verify.rb +8 -4
- data/lib/kairos_mcp/tools/formalization_history.rb +14 -0
- data/lib/kairos_mcp/version.rb +1 -1
- data/templates/knowledge/multi_llm_review_workflow/multi_llm_review_workflow.md +144 -2
- data/templates/skillsets/introspection/lib/introspection/safety_inspector.rb +5 -1
- data/templates/skillsets/introspection/tools/introspection_check.rb +28 -7
- data/templates/skillsets/multi_llm_review/bin/dispatch_worker.rb +27 -1
- data/templates/skillsets/multi_llm_review/lib/multi_llm_review/consensus.rb +92 -18
- data/templates/skillsets/multi_llm_review/lib/multi_llm_review/dispatcher.rb +26 -2
- data/templates/skillsets/multi_llm_review/lib/multi_llm_review/pending_state.rb +8 -0
- data/templates/skillsets/multi_llm_review/lib/multi_llm_review/prompt_builder.rb +19 -5
- data/templates/skillsets/multi_llm_review/lib/multi_llm_review/sanitizer.rb +11 -1
- data/templates/skillsets/multi_llm_review/skillset.json +2 -2
- data/templates/skillsets/multi_llm_review/test/test_dispatcher_usage.rb +66 -0
- data/templates/skillsets/multi_llm_review/test/test_evidence_fidelity.rb +14 -7
- data/templates/skillsets/multi_llm_review/test/test_multi_llm_review.rb +338 -3
- data/templates/skillsets/multi_llm_review/test/test_mutation_survivors.rb +5 -2
- data/templates/skillsets/multi_llm_review/tools/multi_llm_review_collect.rb +141 -13
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ba66e678675e9b4e7776676832765d509f0ac7bb49feed159ee1513ba4b7318
|
|
4
|
+
data.tar.gz: 28af1484af3a0c98529603bfa30fd9ff188e5b18895c06c11a313bcf3a95d8b2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 67e98820708350d674970e3db49f3583efc01491ba8d58f5ecb702b988f64a5ad1efeb083637f45e77897c44dbb30a84569400431e8f669020fb13a2dae7b4aa
|
|
7
|
+
data.tar.gz: 27fef53ab90cdb4a7745955b49d41e3834d8313f0a0417605a96e21e84b0ab44fcb5c0e3ad27d9ff2cf698bda2a75c0dc7712869ab5e686daeb8cc468da77cdf
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,84 @@ All notable changes to the `kairos-chain` gem will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
This project follows [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [3.64.0] - 2026-08-06
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **An append can no longer erase the chain history on disk.** Two paths
|
|
12
|
+
erased history before this release, both measured, neither raising an
|
|
13
|
+
exception: two holders of the chain at the same length both saved and the
|
|
14
|
+
second save replaced the disk sequence with its own; and a ledger that
|
|
15
|
+
existed but could not be read collapsed to nil, read as a fresh install,
|
|
16
|
+
and the next save rebuilt from genesis over it — 687 blocks became 2. An
|
|
17
|
+
append now takes a key file beside the ledger, re-reads and classifies the
|
|
18
|
+
disk under that key (absent / unreadable / empty / corrupt / readable),
|
|
19
|
+
refuses every state but `:readable` and `:absent`, and always builds on
|
|
20
|
+
the tail that is on disk at that moment. Failures on the read and write
|
|
21
|
+
side raise `Storage::Error` instead of flattening into nil or false, and
|
|
22
|
+
every reader consults `Chain#load_state` before trusting the sequence.
|
|
23
|
+
Includes the 3.62.1 locale fix, and one more of the same family: while
|
|
24
|
+
the ledger does not yet exist, a symlinked name resolves to its target
|
|
25
|
+
before the lock key is derived, so a fresh install reached through two
|
|
26
|
+
names is written under one key (measured pre-fix: 51 blocks shrank to
|
|
27
|
+
35–49 under concurrent two-name appends). Verified by 149 assertions and
|
|
28
|
+
a 34-row falsification harness in which each protective mechanism is
|
|
29
|
+
removed in isolation and its named check confirmed to go red.
|
|
30
|
+
|
|
31
|
+
## [3.62.1] - 2026-08-06
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **A non-UTF-8 locale no longer erases the ledger on the next append.**
|
|
36
|
+
Emergency hotfix cut from the shipped 3.62.0 (branch
|
|
37
|
+
`hotfix/utf8-ledger-read`): `FileBackend#load_blocks` read
|
|
38
|
+
`blockchain.json` with the locale-derived default encoding, so a
|
|
39
|
+
LANG-unset start (launchd, cron, plain containers) made a non-ASCII
|
|
40
|
+
ledger unparseable, the failure was flattened into "no ledger", and one
|
|
41
|
+
ordinary append overwrote 705 blocks with 2 (measured on a copy of a
|
|
42
|
+
production ledger). The ledger is now read as UTF-8 explicitly. This is
|
|
43
|
+
the only change over 3.62.0; 3.64.0 contains the full erasure fix.
|
|
44
|
+
|
|
45
|
+
## [3.63.0] - 2026-08-06
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **`multi_llm_review` findings gain a weight axis, and a worker death no
|
|
50
|
+
longer discards completed seats** (SkillSet 0.9.1 → 0.10.0, frozen after one
|
|
51
|
+
review round; every deployment-grounded finding fixed in-round).
|
|
52
|
+
|
|
53
|
+
The severity of a finding said what kind of defect it was, never what it
|
|
54
|
+
costs: measured on a three-persona panel, 3 of one round's 7 P0s were
|
|
55
|
+
factually correct findings that cost nobody anything, landing at the same
|
|
56
|
+
severity as a defect that silently corrupts published output. The reviewer
|
|
57
|
+
prompt contract now requires a `[consequence: who is harmed, and how]`
|
|
58
|
+
clause on every P0, and aggregation records an unclaused P0 at P2 with the
|
|
59
|
+
stated severity and the demotion reason kept beside it. Only presence is
|
|
60
|
+
checked, mechanically; whether a stated consequence is real or trivial
|
|
61
|
+
stays the orchestrator's call. The clause is read before the byte bound
|
|
62
|
+
cuts the tail, the first non-empty clause counts, deduplication compares
|
|
63
|
+
issues without their clauses, and the demotion mark survives merging in
|
|
64
|
+
any member order. Reviewer prompts also stop naming the round number, and
|
|
65
|
+
the L1 workflow knowledge (v3.10.0) adds the matching orchestrator-side
|
|
66
|
+
rule: never tell a reviewer its finding count, the round number, or prior
|
|
67
|
+
verdicts — counts a reviewer performs select for finding-production over
|
|
68
|
+
finding-weight.
|
|
69
|
+
|
|
70
|
+
Separately, one stuck seat used to cost a whole round: a stale heartbeat
|
|
71
|
+
killed the detached worker and every completed seat's reply died with it,
|
|
72
|
+
because nothing left the worker's memory until all seats were done. The
|
|
73
|
+
worker now persists each seat's outcome the moment it is decided
|
|
74
|
+
(`partial_results.json`, atomic, single writer — including the
|
|
75
|
+
dispatcher's own deadline skips, so recovery cannot relabel a reached
|
|
76
|
+
seat as lost). The collect tool's crash and timeout branches recover the
|
|
77
|
+
completed seats, but only after the reaper CONFIRMS the worker's death: a
|
|
78
|
+
stale heartbeat is not a death certificate, and a record sealed over a
|
|
79
|
+
live worker would be permanently false once idempotent replay pins it.
|
|
80
|
+
Unconfirmed death falls back to the previous retryable total-loss report.
|
|
81
|
+
Seats the worker never reached enter the denominator as skip rows named
|
|
82
|
+
`worker_crashed_seat_lost`, and the payload carries `worker_failure`
|
|
83
|
+
naming the death and the recovered and lost seat labels.
|
|
84
|
+
|
|
7
85
|
## [3.62.0] - 2026-08-06
|
|
8
86
|
|
|
9
87
|
### Changed
|
|
@@ -346,6 +346,7 @@ module KairosMcp
|
|
|
346
346
|
|
|
347
347
|
html_response(200, render_partial('_chain_blocks',
|
|
348
348
|
blocks: paged_blocks,
|
|
349
|
+
load_state: chain.load_state,
|
|
349
350
|
total: total,
|
|
350
351
|
limit: limit,
|
|
351
352
|
offset: offset))
|
|
@@ -353,6 +354,10 @@ module KairosMcp
|
|
|
353
354
|
|
|
354
355
|
def handle_chain_block_detail_partial(index)
|
|
355
356
|
chain = KairosChain::Chain.new
|
|
357
|
+
unless chain.valid?
|
|
358
|
+
return html_response(200, "<p>Block ##{index} unavailable: ledger is #{chain.load_state}.</p>")
|
|
359
|
+
end
|
|
360
|
+
|
|
356
361
|
block = chain.chain.find { |b| b.respond_to?(:index) ? b.index == index : b['index'] == index }
|
|
357
362
|
|
|
358
363
|
if block
|
|
@@ -364,13 +369,17 @@ module KairosMcp
|
|
|
364
369
|
|
|
365
370
|
def handle_chain_verify_partial
|
|
366
371
|
chain = KairosChain::Chain.new
|
|
367
|
-
valid = chain.valid?
|
|
368
|
-
length = chain.chain.length
|
|
369
372
|
|
|
370
|
-
|
|
371
|
-
|
|
373
|
+
# Three outcomes: a ledger that does not exist yet is not a failure.
|
|
374
|
+
result = case chain.load_state
|
|
375
|
+
when :readable
|
|
376
|
+
"<div class='flash flash-success'>Chain is valid. " \
|
|
377
|
+
"#{chain.chain.length} blocks verified.</div>"
|
|
378
|
+
when :absent
|
|
379
|
+
"<div class='flash'>Chain not created yet. Nothing to verify.</div>"
|
|
372
380
|
else
|
|
373
|
-
"<div class='flash flash-error'>Chain integrity check FAILED
|
|
381
|
+
"<div class='flash flash-error'>Chain integrity check FAILED! " \
|
|
382
|
+
"(state: #{chain.load_state})</div>"
|
|
374
383
|
end
|
|
375
384
|
html_response(200, result)
|
|
376
385
|
end
|
|
@@ -475,17 +484,20 @@ module KairosMcp
|
|
|
475
484
|
backend = SkillsConfig.storage_backend
|
|
476
485
|
|
|
477
486
|
latest = chain.latest_block
|
|
478
|
-
latest_h = latest.respond_to?(:to_h) ? latest
|
|
487
|
+
latest_h = latest.respond_to?(:to_h) ? latest&.to_h : latest
|
|
479
488
|
|
|
480
489
|
{
|
|
481
490
|
valid: chain.valid?,
|
|
491
|
+
state: chain.load_state,
|
|
482
492
|
length: chain.chain.length,
|
|
483
493
|
storage: { backend: backend },
|
|
484
494
|
latest_block: latest_h
|
|
485
495
|
}
|
|
486
496
|
rescue StandardError => e
|
|
487
|
-
|
|
488
|
-
|
|
497
|
+
# Chain.new does not raise; this branch is reachable only for failures
|
|
498
|
+
# outside the ledger (config, rendering). It must not fabricate a state.
|
|
499
|
+
{ valid: false, state: :error, length: 0, storage: { backend: 'unknown' },
|
|
500
|
+
latest_block: nil, error: e.message }
|
|
489
501
|
end
|
|
490
502
|
|
|
491
503
|
def fetch_knowledge_list(search: nil)
|
|
@@ -11,13 +11,18 @@
|
|
|
11
11
|
<div>
|
|
12
12
|
<p>
|
|
13
13
|
<strong>Status:</strong>
|
|
14
|
+
<%# Three displays, not two: an absent ledger is a fresh install. %>
|
|
14
15
|
<% if chain[:valid] %>
|
|
15
16
|
<span class="badge badge-success">Valid</span>
|
|
17
|
+
<% elsif chain[:state] == :absent %>
|
|
18
|
+
<span class="badge">Not created yet</span>
|
|
16
19
|
<% else %>
|
|
17
|
-
<span class="badge badge-error">Invalid</span>
|
|
20
|
+
<span class="badge badge-error">Invalid (<%= h(chain[:state].to_s) %>)</span>
|
|
18
21
|
<% end %>
|
|
19
22
|
</p>
|
|
20
|
-
|
|
23
|
+
<% if chain[:valid] %>
|
|
24
|
+
<p><strong>Length:</strong> <%= chain[:length] %> blocks</p>
|
|
25
|
+
<% end %>
|
|
21
26
|
</div>
|
|
22
27
|
<div>
|
|
23
28
|
<p><strong>Backend:</strong> <%= h(chain.dig(:storage, :backend) || 'unknown') %></p>
|
|
@@ -9,13 +9,18 @@
|
|
|
9
9
|
<article>
|
|
10
10
|
<header>Blockchain</header>
|
|
11
11
|
<p>
|
|
12
|
+
<%# Three displays, not two: an absent ledger is a fresh install. %>
|
|
12
13
|
<% if chain[:valid] %>
|
|
13
14
|
<span class="badge badge-success">Valid</span>
|
|
15
|
+
<% elsif chain[:state] == :absent %>
|
|
16
|
+
<span class="badge">Not created yet</span>
|
|
14
17
|
<% else %>
|
|
15
|
-
<span class="badge badge-error">Invalid</span>
|
|
18
|
+
<span class="badge badge-error">Invalid (<%= h(chain[:state].to_s) %>)</span>
|
|
16
19
|
<% end %>
|
|
17
20
|
</p>
|
|
18
|
-
|
|
21
|
+
<% if chain[:valid] %>
|
|
22
|
+
<p><strong><%= chain[:length] %></strong> blocks</p>
|
|
23
|
+
<% end %>
|
|
19
24
|
<p><small>Backend: <%= h(chain.dig(:storage, :backend) || 'unknown') %></small></p>
|
|
20
25
|
<footer><a href="/admin/chain">View Chain →</a></footer>
|
|
21
26
|
</article>
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
<% if blocks.empty? %>
|
|
2
|
-
|
|
2
|
+
<%# Three displays, not two: an absent ledger is a fresh install, and an
|
|
3
|
+
unreadable one must not be reported as an empty history. %>
|
|
4
|
+
<% if load_state == :readable %>
|
|
5
|
+
<p>No blocks found.</p>
|
|
6
|
+
<% elsif load_state == :absent %>
|
|
7
|
+
<p>Chain not created yet.</p>
|
|
8
|
+
<% else %>
|
|
9
|
+
<p>History unavailable: ledger is <%= h(load_state.to_s) %>.</p>
|
|
10
|
+
<% end %>
|
|
3
11
|
<% else %>
|
|
4
12
|
<div class="overflow-auto">
|
|
5
13
|
<table>
|
|
@@ -64,7 +64,23 @@ module KairosMcp
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
active = Digest::SHA256.hexdigest(File.read(md_file_path))
|
|
67
|
-
|
|
67
|
+
|
|
68
|
+
chain = KairosChain::Chain.new(storage_backend: storage_backend)
|
|
69
|
+
# :absent completes normally: a fresh install has no ledger, the scan
|
|
70
|
+
# over zero blocks finds nothing, and :missing_record is the honest
|
|
71
|
+
# verdict. The other failed states mean provenance is UNAVAILABLE,
|
|
72
|
+
# which is not the same claim as "never recorded" — reporting
|
|
73
|
+
# :missing_record there would accuse every artifact whenever the
|
|
74
|
+
# ledger cannot be read.
|
|
75
|
+
unless chain.valid? || chain.load_state == :absent
|
|
76
|
+
return Result.new(
|
|
77
|
+
status: :error, name: name,
|
|
78
|
+
active_digest: active, recorded_digest: nil,
|
|
79
|
+
message: "L1 '#{name}': provenance unavailable — ledger state is #{chain.load_state}"
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
recorded = recorded_digest_for(name, chain)
|
|
68
84
|
|
|
69
85
|
if recorded.nil?
|
|
70
86
|
return Result.new(
|
|
@@ -101,8 +117,7 @@ module KairosMcp
|
|
|
101
117
|
# the most recent knowledge_update record, scanning the chain from head
|
|
102
118
|
# backward. Returns nil when the most recent relevant record removed the
|
|
103
119
|
# artifact (next_hash nil — delete/archive) or when none exists.
|
|
104
|
-
def recorded_digest_for(name,
|
|
105
|
-
chain = KairosChain::Chain.new(storage_backend: storage_backend)
|
|
120
|
+
def recorded_digest_for(name, chain)
|
|
106
121
|
chain.chain.reverse_each do |block|
|
|
107
122
|
Array(block.data).each do |entry|
|
|
108
123
|
record = parse_entry(entry)
|