kairos-chain 3.42.0 → 3.42.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07f782a3ef998bf91435a2e35cde051338c6f1c2fa8f2e037fbba4427c400ccf
4
- data.tar.gz: 8cd549860f69e91fa94bf50a41121cb3b7625686f2438c2c665c83192f081f27
3
+ metadata.gz: 45fd1079aa951da3ac40d0dcd9763e02c2a15413753d5452dab4fc07e928a3cb
4
+ data.tar.gz: 93e3969307c08b26782c81804c9b5b10c475407e4770962a04ec29ab227a7951
5
5
  SHA512:
6
- metadata.gz: e1ec23c12c08cff2ce324b34715d8eedd0277863a4e561b439c7161bd6e1b08eb10936c36db603ba621544e424953fe24247fe553917d3d4fcb6e844867d7f42
7
- data.tar.gz: 738519ef9bc54544c058f59661d08288b22d60904527dbfb7f99f501f89b35d1a80c095b2b5090750f9aff69d592acc46357fe4069064302db344659b68b5642
6
+ metadata.gz: e0d95027c4457cb89a1dee063b3637dc568fce57735e2be211119ace92d684b42451b03b176887202bc2eb797d71cb49af9e3ca49f0e0e9e00689c88ff74c177
7
+ data.tar.gz: fd1ac48f63e671ce7df974bab3efd35e2f3af8b626aeba655f56d3bffa933903d269f3d5775ae877203f9ef3050c66c5a3d5cbf69811ec113b9f5235b35d6574
data/CHANGELOG.md CHANGED
@@ -4,6 +4,49 @@ 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.42.2] - 2026-07-14
8
+
9
+ ### Knowledge — loop_engineering_patterns §B: Model/Effort budget allocation
10
+
11
+ Extended the `loop_engineering_patterns` L1 entry (0.3 → 0.4) with a §B
12
+ design-craft note distilled from Anthropic's *Model and Effort in Claude Code*
13
+ post:
14
+
15
+ - Model (intelligence ceiling) and effort (per-request thoroughness) are
16
+ distinct levers; raise model when logic/knowledge is the wall, raise effort
17
+ when the loop skips steps.
18
+ - Because a loop compounds turns, uniform high effort multiplies latency —
19
+ concentrate high effort on the verify/judgment/decision gates, keep mechanical
20
+ dispatch turns low.
21
+ - The lever is the *orchestrator's*; a worker in a separate process (hermes body
22
+ subprocess, `agent`-skillset step) carries its own config and is unaffected.
23
+ - KairosChain delta: the non-bypassable gates (Prop 10 consent/audit floor,
24
+ INV-5 layer guard, verdict track) are where effort must not be cut — a stop
25
+ there is an irreversible Kairos moment (Prop 5), not a throughput step.
26
+
27
+ Added the `effort-allocation` tag and a "high/low effort for a long run?"
28
+ entry-point line. No code or behavior change.
29
+
30
+ ## [3.42.1] - 2026-07-14
31
+
32
+ ### Fixed — SectionWriter auto-chunk completeness & duplicate tail
33
+
34
+ Two follow-ups to the v3.42.0 auto-chunk on output-uncapped providers
35
+ (e.g. the `claude_code` CLI):
36
+
37
+ - **Completeness.** v3.42.0 added a per-chunk "rewrite ONLY these paragraphs"
38
+ bound to avoid a trailing duplicate. On `claude_code` that bound backfired —
39
+ the model under-produced and dropped the section tail, leaving long sections
40
+ incomplete. Removed the bound.
41
+ - **Duplicate tail (provider-agnostic).** Some providers "finish the section"
42
+ from the first chunk; the remaining passes then appended duplicate content.
43
+ `SectionWriter` now stops chunking once the assembled output already covers
44
+ ~the whole source (`section_complete_fraction`, default 0.85). Providers that
45
+ rewrite each chunk faithfully (e.g. API adapters, where `max_tokens` is
46
+ honoured) never trip this and still run every chunk. Net: a single clean,
47
+ complete section on both provider kinds. `chunks` in the result now reports
48
+ the number of passes actually run.
49
+
7
50
  ## [3.42.0] - 2026-07-14
8
51
 
9
52
  ### Fixed / Improved — agent SkillSet self-drive on long document sections
@@ -1,4 +1,4 @@
1
1
  module KairosMcp
2
- VERSION = "3.42.0"
2
+ VERSION = "3.42.2"
3
3
  CHANGELOG_URL = "https://github.com/masaomi/KairosChain_2026/blob/main/CHANGELOG.md"
4
4
  end
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: loop_engineering_patterns
3
3
  description: Use before designing or classifying an agent loop in KairosChain — to pick a loop type, apply loop-design craft, and place each loop at the right layer under the Prop 10 floor. Claude Code's four loop types + design principles, extended with the governance constraint Anthropic omits.
4
- version: "0.3"
5
- tags: [loops, agent, autonomy, taxonomy, layer-placement, provenance]
4
+ version: "0.4"
5
+ tags: [loops, agent, autonomy, taxonomy, layer-placement, provenance, effort-allocation]
6
6
  ---
7
7
 
8
8
  # Loop Engineering Patterns
@@ -14,6 +14,7 @@ Open *before* designing or classifying an agent loop — not a runtime tool:
14
14
  - New agent loop / autonomous cycle → pick a §A type, apply §B craft, then place it via §C.
15
15
  - "Which layer owns this loop?" (harness vs gem/SkillSet, plus the cross-cutting L0 governance floor) → §C table + the L0 note below it.
16
16
  - Strengthening the `agent` OODA loop (self-verification, semantic stop) → §B + §C evaluator/verifier notes.
17
+ - "High/low effort for a long run?" → §B budget-allocation note (concentrate effort on §C's non-bypassable gates).
17
18
  - Explaining why KairosChain's autonomous mode keeps human checkpoints → §C Prop 10 floor.
18
19
  - Not for: skill lifecycle/maturation → [[agent_skill_evolution_guide]]; general layer rules → [[layer_placement_guide]].
19
20
 
@@ -29,6 +30,8 @@ Micro (small) and macro (broad) loops compose; match loop complexity to problem
29
30
  ## B. Design craft (universal — adopt as-is)
30
31
  Output quality tracks the surrounding harness, not the model alone. Keep the codebase clean so the loop follows existing patterns. **Encode verification as a skill with quantitative checks** so the loop measures its own work. Keep docs accessible/current. **Use a second, independent agent for review** (fresh context, separate session/model). Set explicit success/stop criteria and max-turn caps to bound cost; script deterministic steps instead of re-reasoning them; monitor token spend.
31
32
 
33
+ **Allocate reasoning budget by phase, not uniformly.** Model and effort are distinct levers ([Model and Effort in Claude Code](https://claude.com/blog/claude-model-and-effort-level-in-claude-code), Anthropic, 2026): model sets the intelligence ceiling — raise it when logic or knowledge is the wall; effort sets per-request thoroughness (files read, tests run, verification passes) — raise it when the loop skips steps, not when it lacks intelligence. Because a loop compounds turns, uniform high effort multiplies latency across every cycle: concentrate high effort on the verify / judgment / decision gates and keep mechanical dispatch turns low. Note this is the *orchestrator's* lever — it governs how carefully the loop monitors, verifies, and decides; it does not raise the quality of a worker running in a separate process (e.g. a hermes body subprocess or an `agent`-skillset step), which carries its own model/effort config. In KairosChain the non-bypassable gates — the Prop 10 consent/audit floor, the INV-5 layer guard, the verdict track — are exactly where effort must not be cut, since a stop there is an irreversible Kairos moment (§C, Prop 5), not a throughput step.
34
+
32
35
  ## C. KairosChain reading (layer placement + the floor Anthropic omits)
33
36
  The four types are harness-framed (`/goal`, `/loop`, `/schedule`, `/usage`, auto mode are Claude Code mechanisms). KairosChain already implements most of the substance across layers — so the work is mostly *placement*, not adoption; one item is still design-stage (flagged in the table):
34
37
 
@@ -23,20 +23,27 @@ module KairosMcp
23
23
  # paragraph boundaries and generate each chunk in its own pass, appending the
24
24
  # results. Short contexts stay a single pass (chunks == [context_text]).
25
25
  chunks = split_context(context_text, chunk_target)
26
+ source_chars = context_text.to_s.gsub(/\s+/, '').length
27
+ complete_at = (source_chars * complete_fraction).ceil
26
28
 
27
29
  total_units = 0
28
30
  total_chars = 0
31
+ chunks_written = 0
29
32
  chunks.each_with_index do |chunk, idx|
30
33
  first = idx.zero?
34
+ # Provider-agnostic de-duplication: some output-uncapped providers (e.g. the
35
+ # claude_code CLI) "finish the section" from the first chunk. Once the assembled
36
+ # output already covers ~the whole source, stop — further passes would only
37
+ # append duplicate content. Providers that rewrite each chunk faithfully never
38
+ # trip this (their per-chunk output tracks per-chunk input), so all chunks run.
39
+ break if !first && total_chars >= complete_at
40
+
41
+ # Continuation chunks must not repeat the section heading. (A stronger
42
+ # "rewrite only these paragraphs" bound was tried but backfired on the
43
+ # claude_code CLI — it then under-produced and dropped the section tail.)
31
44
  chunk_instructions =
32
- if chunks.length > 1
33
- # Bound EVERY chunk to its own paragraphs. Without this, providers that
34
- # "finish the section" (e.g. claude_code) regenerate the whole section from
35
- # the first chunk, so later chunks duplicate content. Continuation chunks
36
- # also must not repeat the heading.
37
- notes = [bounded_note(language)]
38
- notes << continuation_note(language) unless first
39
- "#{instructions}\n\n#{notes.join("\n")}"
45
+ if chunks.length > 1 && !first
46
+ "#{instructions}\n\n#{continuation_note(language)}"
40
47
  else
41
48
  instructions
42
49
  end
@@ -49,6 +56,7 @@ module KairosMcp
49
56
  write_chunk(output_file, gen['content'], first ? append_mode : true)
50
57
  total_units += count_units(gen['content'], language)
51
58
  total_chars += gen['content'].gsub(/\s+/, '').length
59
+ chunks_written += 1
52
60
  end
53
61
 
54
62
  {
@@ -60,7 +68,7 @@ module KairosMcp
60
68
  # non-whitespace char count so callers never size output from a bogus metric.
61
69
  'word_count' => total_units,
62
70
  'char_count' => total_chars,
63
- 'chunks' => chunks.length
71
+ 'chunks' => chunks_written
64
72
  }
65
73
  end
66
74
 
@@ -167,24 +175,20 @@ module KairosMcp
167
175
  end
168
176
 
169
177
  # Auto-chunk threshold. Sized so each pass is small enough that output-uncapped
170
- # providers (e.g. claude_code CLI) emit the whole chunk without stopping early —
178
+ # providers (e.g. claude_code CLI) emit a full section without stopping early —
171
179
  # empirically a larger single pass truncates, while ~3000-char passes complete.
172
- # The bounded_note keeps chunks from overlapping. Overridable via config.
180
+ # On such providers the first chunk may "finish the section", so the output can
181
+ # carry a trailing duplicate fragment; completeness is favoured over that cosmetic
182
+ # cost. API providers (with max_tokens honoured) produce a clean single section.
183
+ # Overridable via config.
173
184
  def chunk_target
174
185
  (@config['section_chunk_target_chars'] || 3000).to_i
175
186
  end
176
187
 
177
- # Appended to EVERY chunk (when chunking) so the model rewrites only the paragraphs
178
- # it was given and does not "finish the section" which would make chunks overlap.
179
- def bounded_note(language)
180
- if %w[ja zh ko].include?(language.to_s)
181
- '重要: 以下に与えた段落だけを推敲せよ。節はいくつかに分けて処理される。' \
182
- '与えられていない段落を書き足したり、節の続きをここで完成させたりしてはならない。'
183
- else
184
- 'IMPORTANT: Rewrite ONLY the paragraphs given below. The section is processed ' \
185
- 'in several passes; do NOT add paragraphs that are not in this input or ' \
186
- 'finish the rest of the section here.'
187
- end
188
+ # Fraction of the source (by non-whitespace chars) at which the assembled output is
189
+ # considered to already cover the whole section, so remaining chunks are skipped.
190
+ def complete_fraction
191
+ (@config['section_complete_fraction'] || 0.85).to_f
188
192
  end
189
193
 
190
194
  # Appended to a continuation chunk's instructions so the model does not repeat the
@@ -468,6 +468,40 @@ assert("T18g: append_mode applies only to the first chunk; later chunks append")
468
468
  content.start_with?('PRE') && content.include?("c1\n\nc2\n\nc3")
469
469
  end
470
470
 
471
+ assert("T18h: stops chunking once output already covers the whole source (no duplicate tail)") do
472
+ caller = MockCallerTool.new
473
+ calls = 0
474
+ caller.define_singleton_method(:invoke_tool) do |tool_name, arguments = {}, context: nil|
475
+ calls += 1
476
+ # First pass "finishes the section" (output >= source); a later pass would be a dup.
477
+ body = calls == 1 ? ('な' * 500) : 'DUPLICATE'
478
+ [{ text: JSON.generate({ 'status' => 'ok', 'response' => { 'content' => body }, 'snapshot' => {} }) }]
479
+ end
480
+ writer = SW.new(caller, { 'section_chunk_target_chars' => 100 })
481
+ ctx = (1..5).map { ('あ' * 80) }.join("\n\n") # ~400 non-ws source chars, would be 5 chunks
482
+ out = File.join(TMPDIR, 'earlystop.md')
483
+ r = writer.write(section_name: 's', instructions: 'i', context_text: ctx,
484
+ output_file: out, max_words: 100, language: 'ja', append_mode: false)
485
+ # chunk1 output (500) >= 0.85 * 400 = 340 → stop after the first pass
486
+ r['chunks'] == 1 && calls == 1 && !File.read(out).include?('DUPLICATE')
487
+ end
488
+
489
+ assert("T18i: faithful per-chunk provider still runs all chunks (no premature stop)") do
490
+ caller = MockCallerTool.new
491
+ calls = 0
492
+ caller.define_singleton_method(:invoke_tool) do |tool_name, arguments = {}, context: nil|
493
+ calls += 1
494
+ [{ text: JSON.generate({ 'status' => 'ok', 'response' => { 'content' => "p#{calls}" }, 'snapshot' => {} }) }]
495
+ end
496
+ writer = SW.new(caller, { 'section_chunk_target_chars' => 100 })
497
+ ctx = (1..4).map { ('あ' * 80) }.join("\n\n")
498
+ out = File.join(TMPDIR, 'faithful.md')
499
+ r = writer.write(section_name: 's', instructions: 'i', context_text: ctx,
500
+ output_file: out, max_words: 100, language: 'ja', append_mode: false)
501
+ # short per-chunk output never reaches the completeness threshold → all chunks run
502
+ r['chunks'] > 1 && calls == r['chunks'] && File.read(out).include?("p#{calls}")
503
+ end
504
+
471
505
  assert("T18d: max_tokens respects config ceiling override") do
472
506
  caller = MockCallerTool.new
473
507
  captured = {}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kairos-chain
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.42.0
4
+ version: 3.42.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masaomi Hatakeyama