rubik 0.4.2 → 0.5.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/ATTRIBUTION.md +1 -1
- data/CHANGELOG.md +48 -1
- data/lib/rubik/ast/cache_store.rb +33 -0
- data/lib/rubik/ast/graph.rb +2 -1
- data/lib/rubik/config/defaults.rb +7 -1
- data/lib/rubik/orchestrator/budget_manager.rb +2 -0
- data/lib/rubik/orchestrator/loop.rb +1 -0
- data/lib/rubik/orchestrator/shell_deps.rb +4 -0
- data/lib/rubik/orchestrator/turn_executor/user_channel.rb +2 -0
- data/lib/rubik/prompt/doctrine.rb +10 -15
- data/lib/rubik/prompt/tool_teachings.rb +29 -35
- data/lib/rubik/repl/commands/agent_context.rb +1 -0
- data/lib/rubik/repl/commands/system.rb +2 -0
- data/lib/rubik/repl/turn_recorder.rb +3 -0
- data/lib/rubik/security/redactor.rb +13 -9
- data/lib/rubik/storage/vakanuvis/gate.rb +13 -8
- data/lib/rubik/storage/vakanuvis/harness.rb +115 -112
- data/lib/rubik/storage/vakanuvis/orientation.rb +277 -0
- data/lib/rubik/storage/vakanuvis/service.rb +5 -1
- data/lib/rubik/storage/vakanuvis.rb +8 -1
- data/lib/rubik/tools/builtins/vakanuvis.rb +13 -15
- data/lib/rubik/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9a55adca9a39a813ea7620e63018dc98ae7c902142e9a1fa1dc5afe92637ef88
|
|
4
|
+
data.tar.gz: 72d7ca21a839051017fd515fe63a0cc99573054ec3a23c5fd179a6e37c623460
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f4b426b159392a724d6e66d96dfd09db13cee8882eec7f3dcf0afaaf3d683325d3a74731f0e34abf43e77c1f1655d850cecd2912e324e16da9ad84bd74c1eb71
|
|
7
|
+
data.tar.gz: 36090da4466990229e789cf5912cf880bf4193eb300f8823abc7a1cefed4bf6253aff44774ff9d18e19ebbfb3f874d7c70434d7ab72692ba474bd9e6ba337abf
|
data/ATTRIBUTION.md
CHANGED
|
@@ -121,7 +121,7 @@ SOFTWARE.
|
|
|
121
121
|
| **`strings-ansi`** | Piotr Murach | MIT | ANSI-aware string truncation and alignment for REPL formatters. |
|
|
122
122
|
| **`reline`** | Hiroshi SHIBATA & Ruby Team | Ruby License / BSD-2-Clause | Line-editing, history navigation, autocompletion hooks, and headless terminal adaptation. |
|
|
123
123
|
| **`prism`** | Kevin Newton & Ruby Core Team | MIT | High-performance Ruby parsing and AST extraction ([`Rubik::AST::RubyExtractor`](lib/rubik/ast/ruby_extractor.rb)). |
|
|
124
|
-
| **`minitest`** | Ryan Davis & seattlerb | MIT | Fast, modular test suite powering Rubik's
|
|
124
|
+
| **`minitest`** | Ryan Davis & seattlerb | MIT | Fast, modular test suite powering Rubik's 4,000+ unit, integration, and contract tests. |
|
|
125
125
|
| **`rubocop`** | Bozhidar Batsov & contributors | MIT | Static code analysis, style enforcement, and architectural hygiene gates. |
|
|
126
126
|
| **`benchmark`, `logger`, `pstore`, `reline`, `fiddle`** | Ruby core / rubygems teams | Ruby License / BSD-2-Clause | Ruby stdlib gems pinned explicitly in `rubik.gemspec` as they are unbundled from the default set in Ruby 3.4/3.5. |
|
|
127
127
|
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,52 @@ commits, 2026-08-23 → 2026-10-09, see `git log`).
|
|
|
6
6
|
|
|
7
7
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
8
8
|
|
|
9
|
+
## [0.5.0] - 2026-10-31
|
|
10
|
+
|
|
11
|
+
A vakanüvis minor: the memory system gains a harness-owned auto-digest
|
|
12
|
+
loop and a deterministic topics orientation injected at session start
|
|
13
|
+
and after compaction, the prompt doctrine's recall/record contradiction
|
|
14
|
+
is closed with a single storage write path, duck-typed AST cache stores
|
|
15
|
+
stop breaking `save_to_cache`, and the security redactor's clean-text
|
|
16
|
+
path skips its intermediate allocations.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Harness-owned auto-digest of touched topics (#346, #348):** topics
|
|
21
|
+
recorded or updated since the last digest are tracked in the Harness
|
|
22
|
+
and auto-digested at goal completion and queued (spawn-free) at
|
|
23
|
+
shutdown, gated by the `subagents.vakanuvis.auto_digest` knob
|
|
24
|
+
(default on); per-topic error isolation re-arms failed or refused
|
|
25
|
+
topics for the next flush instead of losing them, the drain window
|
|
26
|
+
between store write and touched-topic tracking is closed with one
|
|
27
|
+
monitor section, and shutdown always joins/kills the digest worker
|
|
28
|
+
with an honest dmesg listing parked topics.
|
|
29
|
+
- **Deterministic topics orientation (#343):** session start and budget
|
|
30
|
+
compaction inject a compact, deterministic overview of the memory
|
|
31
|
+
topics so the model is oriented without a recall round-trip.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **Keywords-in-query contradiction closed (#342):** the vakanüvis
|
|
36
|
+
teaching now separates the recall and record directions, stops
|
|
37
|
+
telling the model to add Keywords to recall queries, renders a single
|
|
38
|
+
write-path storage variant when vakanüvis is active, and folds the
|
|
39
|
+
doctrine down to two core duties with the duplicated
|
|
40
|
+
`goal_storage_rule` wording removed (SSOT).
|
|
41
|
+
- **Duck-typed AST cache stores (#347):** `save_to_cache` probes
|
|
42
|
+
`store.method(:save).parameters` once and dispatches deterministically
|
|
43
|
+
instead of rescuing `ArgumentError` (which swallowed genuine errors
|
|
44
|
+
and double-fired side-effecting stores); 1-arity stores get the
|
|
45
|
+
snapshot without statuses plus a warn naming the store class, while
|
|
46
|
+
stores declaring the canonical kwarg signature keep the full-map
|
|
47
|
+
delivery — including in the clean-graph case.
|
|
48
|
+
|
|
49
|
+
### Performance
|
|
50
|
+
|
|
51
|
+
- **Redactor clean-text fast path (#337):** `Redactor.redact` no longer
|
|
52
|
+
allocates an intermediate scrub/dup string before testing
|
|
53
|
+
`QUICK_CHECK_REGEX` on valid-encoding clean input.
|
|
54
|
+
|
|
9
55
|
## [0.4.2] - 2026-10-29
|
|
10
56
|
|
|
11
57
|
A reliability patch: plugin file loads become transactional with
|
|
@@ -961,7 +1007,8 @@ installation section.
|
|
|
961
1007
|
- Context: serialized index mutations, snapshot-outside-lock reconcile,
|
|
962
1008
|
streaming-first truncation.
|
|
963
1009
|
|
|
964
|
-
[Unreleased]: https://github.com/ozgurulukir/rubik/compare/v0.
|
|
1010
|
+
[Unreleased]: https://github.com/ozgurulukir/rubik/compare/v0.5.0...HEAD
|
|
1011
|
+
[0.5.0]: https://github.com/ozgurulukir/rubik/compare/v0.4.2...v0.5.0
|
|
965
1012
|
[0.4.2]: https://github.com/ozgurulukir/rubik/compare/v0.4.1...v0.4.2
|
|
966
1013
|
[0.4.1]: https://github.com/ozgurulukir/rubik/compare/v0.4.0...v0.4.1
|
|
967
1014
|
[0.1.0]: https://github.com/ozgurulukir/rubik/releases/tag/v0.1.0
|
|
@@ -110,6 +110,39 @@ module Rubik
|
|
|
110
110
|
false
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
+
# Dispatch a cache save across duck-typed stores. The canonical #save
|
|
114
|
+
# contract is (hash, statuses: {}) with a FULL statuses map (every
|
|
115
|
+
# snapshot file present, :ok or not), but gem consumers may hand the
|
|
116
|
+
# graph a narrower store. The signature is probed instead of rescuing
|
|
117
|
+
# ArgumentError around the call — a genuine ArgumentError raised inside
|
|
118
|
+
# a store's save must surface, and a failing save must never be
|
|
119
|
+
# silently retried single-arg (double side effects).
|
|
120
|
+
#
|
|
121
|
+
# Classification: a `statuses:` keyword (or **kwargs) means the kwarg
|
|
122
|
+
# call; otherwise the store is treated as 1-arity and non-ok statuses
|
|
123
|
+
# are dropped with a warn (such entries reload as :ok). When the
|
|
124
|
+
# signature cannot be read at all — save resolves through
|
|
125
|
+
# method_missing, or the object has no save — the canonical kwarg call
|
|
126
|
+
# is kept: the call itself surfaces any problem, exactly as the
|
|
127
|
+
# pre-probe code did. respond_to_missing?-backed proxies report
|
|
128
|
+
# [[:rest]] and are therefore treated as 1-arity: behavioral probing
|
|
129
|
+
# would risk double invocation.
|
|
130
|
+
def self.save_via(store, snapshot, statuses)
|
|
131
|
+
accepts_statuses = begin
|
|
132
|
+
store.method(:save).parameters.any? do |param|
|
|
133
|
+
kind, name = param
|
|
134
|
+
kind == :keyrest || ([:key, :keyreq].include?(kind) && name == :statuses)
|
|
135
|
+
end
|
|
136
|
+
rescue NameError
|
|
137
|
+
nil
|
|
138
|
+
end
|
|
139
|
+
return store.save(snapshot, statuses: statuses) if accepts_statuses != false
|
|
140
|
+
|
|
141
|
+
non_ok = statuses.count { |_, status| status != :ok }
|
|
142
|
+
Rubik::Log.warn(:ast, "#{store.class}#save cannot carry extraction statuses; #{non_ok} file(s) will reload as :ok") if non_ok.positive?
|
|
143
|
+
store.save(snapshot)
|
|
144
|
+
end
|
|
145
|
+
|
|
113
146
|
def load
|
|
114
147
|
load_with_statuses[0]
|
|
115
148
|
end
|
data/lib/rubik/ast/graph.rb
CHANGED
|
@@ -139,7 +139,8 @@ module Rubik
|
|
|
139
139
|
end
|
|
140
140
|
skipped = "cache snapshot excluded #{skipped_failed} file(s) with failed extractions; they re-extract on next boot"
|
|
141
141
|
Rubik::Log.warn(:ast, skipped) if skipped_failed.positive?
|
|
142
|
-
|
|
142
|
+
|
|
143
|
+
CacheStore.save_via(cache_store, snapshot, statuses)
|
|
143
144
|
end
|
|
144
145
|
|
|
145
146
|
def smart_context_for(symbol_name)
|
|
@@ -166,15 +166,21 @@ module Rubik
|
|
|
166
166
|
# annal + wiki derleyicisi. enabled: harness kur/kurulama; distiller:
|
|
167
167
|
# Düzlem 2 LLM doğumu (bütçe: flush_events x max_steps); model: nil =
|
|
168
168
|
# root provider; retention: supersede|purge; root_turns: kök-turn annal'ı.
|
|
169
|
+
# auto_digest: goal bitişinde/kapanışta dokunulan konuların hedefli damıtımı (issue #341).
|
|
169
170
|
"vakanuvis" => {
|
|
170
171
|
"enabled" => true,
|
|
171
172
|
"distiller" => true,
|
|
173
|
+
"auto_digest" => true,
|
|
172
174
|
"max_steps" => 8,
|
|
173
175
|
"flush_events" => 10,
|
|
174
176
|
"max_digest_jobs" => 100,
|
|
175
177
|
"model" => nil,
|
|
176
178
|
"retention" => "supersede",
|
|
177
|
-
"root_turns" => true
|
|
179
|
+
"root_turns" => true,
|
|
180
|
+
# Soğuk-başlangıç ve sıkıştırma sonrası yönelim enjeksiyonu (issue #340):
|
|
181
|
+
# sistem istemi şişirilmeden kök oturuma kısa mem.* özeti eklenir.
|
|
182
|
+
"orientation" => true,
|
|
183
|
+
"orientation_max_topics" => 10
|
|
178
184
|
},
|
|
179
185
|
# Pruner (repl/pruner): yarı deterministik KÖK pencere temizleyicisi,
|
|
180
186
|
# DEFAULT ON (kullanıcı kararı 2026-10-10; kill-switch: enabled=false).
|
|
@@ -57,6 +57,8 @@ module Rubik
|
|
|
57
57
|
max_output_tokens: @deps.max_output_tokens, config: context_cfg,
|
|
58
58
|
absorb: GoalLedger::ObservationProjection.directive_for(deps: @deps)
|
|
59
59
|
))
|
|
60
|
+
manager = @deps.runtime_manager if @deps.respond_to?(:runtime_manager)
|
|
61
|
+
Rubik::Storage::Vakanuvis::Orientation.inject_after_compaction!(@deps.history, manager, config: @deps.config)
|
|
60
62
|
end
|
|
61
63
|
|
|
62
64
|
def track_token_usage!(res)
|
|
@@ -177,6 +177,7 @@ module Rubik
|
|
|
177
177
|
end
|
|
178
178
|
end
|
|
179
179
|
|
|
180
|
+
Rubik::Storage::Vakanuvis::Orientation.inject_session_start!(@history, @runtime_manager, config: @config)
|
|
180
181
|
@history << { role: "user", content: formatted_task }
|
|
181
182
|
|
|
182
183
|
execute_steps(opts: opts, plan_only: plan_only)
|
|
@@ -71,6 +71,10 @@ module Rubik
|
|
|
71
71
|
@loop.emergency_compact_and_notify!(reason)
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
def runtime_manager
|
|
75
|
+
@loop.runtime_manager if @loop.respond_to?(:runtime_manager)
|
|
76
|
+
end
|
|
77
|
+
|
|
74
78
|
def respond_to_missing?(name, include_private = false)
|
|
75
79
|
(SURFACE.include?(name) && @loop.respond_to?(name, include_private)) || super
|
|
76
80
|
end
|
|
@@ -103,6 +103,8 @@ module Rubik
|
|
|
103
103
|
|
|
104
104
|
# ── announce ──
|
|
105
105
|
def announce_compaction(reason, layer)
|
|
106
|
+
manager = @deps.runtime_manager if @deps.respond_to?(:runtime_manager)
|
|
107
|
+
Rubik::Storage::Vakanuvis::Orientation.inject_after_compaction!(@deps.history, manager, config: @deps.config)
|
|
106
108
|
@deps.on_step&.call(type: :emergency_compact, reason: reason, step: @deps.step_count,
|
|
107
109
|
max_steps: @deps.max_steps, layer: layer)
|
|
108
110
|
end
|
|
@@ -221,14 +221,15 @@ module Rubik
|
|
|
221
221
|
def rule_nine_body(active_tools: nil, plan_mode: false)
|
|
222
222
|
return "Keep the repository worktree free of agent workfiles, scratch notes, and temporary artifacts." if plan_mode
|
|
223
223
|
|
|
224
|
+
vakanuvis_on = tool_active?("vakanuvis", active_tools)
|
|
224
225
|
storage_part = if tool_active?("storage", active_tools)
|
|
225
|
-
Rubik::Prompt::ToolTeachings.teaching_for("storage")
|
|
226
|
+
Rubik::Prompt::ToolTeachings.teaching_for("storage", vakanuvis_active: vakanuvis_on)
|
|
226
227
|
else
|
|
227
228
|
"Keep the repository worktree free of scattered agent workfiles; read any existing " \
|
|
228
229
|
"`rubik://storage/<type>/<id>` entities via `read_file` when a store is wired."
|
|
229
230
|
end
|
|
230
231
|
parts = [storage_part]
|
|
231
|
-
parts << Rubik::Prompt::ToolTeachings.teaching_for("vakanuvis") if
|
|
232
|
+
parts << Rubik::Prompt::ToolTeachings.teaching_for("vakanuvis") if vakanuvis_on
|
|
232
233
|
parts << Rubik::Prompt::ToolTeachings.teaching_for("show_file") if tool_active?("show_file", active_tools)
|
|
233
234
|
parts << "Subagent transcripts are indexed automatically as `transcript` entities (alias `subagent-<storage-id>`) " \
|
|
234
235
|
"and are readable live or persisted via the canonical `rubik://subagent/<storage-id>/transcript` reference — " \
|
|
@@ -244,14 +245,10 @@ module Rubik
|
|
|
244
245
|
if storage_on && vakanuvis_on
|
|
245
246
|
"Persist pipeline knowledge in the central storage (`storage` tool): the decomposed plan, review findings, " \
|
|
246
247
|
"and the final goal record — link stages with entity ids (`links: {\"goal\": \"goal.N.<hash16>\"}`) so the " \
|
|
247
|
-
"audit trail survives the session.
|
|
248
|
-
"
|
|
249
|
-
"
|
|
250
|
-
"
|
|
251
|
-
"`vakanuvis(action: 'recall', query: '...')` (also after compaction). Keep the repository worktree free of " \
|
|
252
|
-
"agent workfiles. Transcripts of finished " \
|
|
253
|
-
"subagents are indexed automatically (searchable via `storage` action 'search') and persist after the " \
|
|
254
|
-
"subagent is reaped."
|
|
248
|
+
"audit trail survives the session. Route durable memory through the chronicler (`vakanuvis` record/recall; " \
|
|
249
|
+
"see rule 9), never through raw storage puts. Keep the repository worktree free of agent workfiles. " \
|
|
250
|
+
"Transcripts of finished subagents are indexed automatically (searchable via `storage` action 'search') " \
|
|
251
|
+
"and persist after the subagent is reaped."
|
|
255
252
|
elsif storage_on
|
|
256
253
|
"Persist pipeline knowledge in the central storage (`storage` tool): the decomposed plan, review findings, " \
|
|
257
254
|
"and the final goal record — link stages with entity ids (`links: {\"goal\": \"goal.N.<hash16>\"}`) so the " \
|
|
@@ -259,11 +256,9 @@ module Rubik
|
|
|
259
256
|
"finished subagents are indexed automatically (searchable via `storage` action 'search') and persist after " \
|
|
260
257
|
"the subagent is reaped."
|
|
261
258
|
elsif vakanuvis_on
|
|
262
|
-
"
|
|
263
|
-
"
|
|
264
|
-
"`
|
|
265
|
-
"agent workfiles. Transcripts of finished subagents remain readable via `rubik://subagent/<storage-id>/transcript` " \
|
|
266
|
-
"after the subagent is reaped."
|
|
259
|
+
"Route durable memory through the chronicler (`vakanuvis` record/recall; see rule 9). Keep the repository " \
|
|
260
|
+
"worktree free of agent workfiles. Transcripts of finished subagents remain readable via " \
|
|
261
|
+
"`rubik://subagent/<storage-id>/transcript` after the subagent is reaped."
|
|
267
262
|
else
|
|
268
263
|
"Keep the repository worktree free of agent workfiles. Transcripts of finished subagents remain readable via " \
|
|
269
264
|
"`rubik://subagent/<storage-id>/transcript` after the subagent is reaped."
|
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
module Rubik
|
|
4
4
|
module Prompt
|
|
5
|
-
# Tool teachings keyed by canonical tool name (architecture review
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# its Tools::Declaration; prose (the presentation) lives here — a prompt
|
|
10
|
-
# layer concern. test_tool_teachings welds the two: every key must
|
|
11
|
-
# resolve to a declared (Catalog) tool, so a rename that forgets the
|
|
12
|
-
# prompt fails the suite.
|
|
13
|
-
#
|
|
14
|
-
# The strings are interpolated into the operational rules HEREDOC at the
|
|
15
|
-
# same positions they originally occupied — the rendered rules are
|
|
16
|
-
# byte-identical (pinned by the substring tests in
|
|
17
|
-
# test_structured_context_builder, test_agent_command_contract,
|
|
18
|
-
# test_buffer_apply_conflict, test_repl_tool_separation_and_guardrails).
|
|
5
|
+
# Tool teachings keyed by canonical tool name (architecture review 2026-10-01, candidate 1):
|
|
6
|
+
# the sentences that teach HOW a specific tool is used live here, one key per tool.
|
|
7
|
+
# Identity lives in its Tools::Declaration; prose lives here — a prompt layer concern.
|
|
8
|
+
# test_tool_teachings welds the two: every key must resolve to a declared Catalog tool.
|
|
19
9
|
module ToolTeachings
|
|
10
|
+
STORAGE_BASE =
|
|
11
|
+
"Persist agent workfiles — goals, plans, reviews, and work notes — in the central " \
|
|
12
|
+
"`storage` database (`storage` tool: put/get/list/search/set_ref/delete), NOT as scattered files in " \
|
|
13
|
+
"the repository worktree. Read, list, and search all entity types (`goal`, `plan`, `memory`, `wiki`, " \
|
|
14
|
+
"`transcript`, `review`, `note`) via `storage` or `read_file(path: 'rubik://storage/<type>/<id>')` " \
|
|
15
|
+
"(browse with `rubik://storage/index` and `rubik://storage/<type>` listings)."
|
|
16
|
+
|
|
17
|
+
STORAGE_FALLBACK =
|
|
18
|
+
"#{STORAGE_BASE[0...-1]}; write durable `memory` cards and compiled `wiki` pages through direct " \
|
|
19
|
+
"`storage` `put` on `memory`/`wiki` (reserving direct `storage` `put` on `memory`/`wiki` for sessions " \
|
|
20
|
+
"where `vakanuvis` is unavailable).".freeze
|
|
21
|
+
|
|
20
22
|
TEACHINGS = {
|
|
21
23
|
"replace_file_content" =>
|
|
22
24
|
"For modifying existing files, prefer `replace_file_content` with `path`, exact verbatim `target_content`, " \
|
|
@@ -35,15 +37,7 @@ module Rubik
|
|
|
35
37
|
"params_schema: { type: 'object', properties: { ... }, required: [...] }, code: '->(args) { ... }')` " \
|
|
36
38
|
"(always provide `params_schema` when the handler reads arguments from `args`; omit it or pass an empty " \
|
|
37
39
|
"`{ type: 'object', properties: {} }` schema only for tools that take no arguments).",
|
|
38
|
-
"storage" =>
|
|
39
|
-
"Persist agent workfiles — goals, plans, reviews, and work notes — in the central " \
|
|
40
|
-
"`storage` database (`storage` tool: put/get/list/search/set_ref/delete), NOT as scattered files in " \
|
|
41
|
-
"the repository worktree. Read, list, and search all entity types (`goal`, `plan`, `memory`, `wiki`, " \
|
|
42
|
-
"`transcript`, `review`, `note`) via `storage` or `read_file(path: 'rubik://storage/<type>/<id>')` " \
|
|
43
|
-
"(browse with `rubik://storage/index` and `rubik://storage/<type>` listings); write durable `memory` " \
|
|
44
|
-
"cards and compiled `wiki` pages through `vakanuvis` (`record` / `digest`) whenever `vakanuvis` is in " \
|
|
45
|
-
"your toolbelt, reserving direct `storage` `put` on `memory`/`wiki` for sessions where `vakanuvis` is " \
|
|
46
|
-
"unavailable.",
|
|
40
|
+
"storage" => STORAGE_BASE,
|
|
47
41
|
"run_command" =>
|
|
48
42
|
"GitHub facts must be verified, not assumed: after `gh pr merge` (which can succeed with EMPTY output) " \
|
|
49
43
|
"confirm the merge with `gh pr view <number> --json state`; `mergeable: UNKNOWN` right after a push means " \
|
|
@@ -57,17 +51,13 @@ module Rubik
|
|
|
57
51
|
"while your context stays lean; only fall back to read_file when YOU must reason about the content.",
|
|
58
52
|
"vakanuvis" =>
|
|
59
53
|
"The background chronicler keeps compiled knowledge (memory cards, wiki pages) in rubik://storage. " \
|
|
60
|
-
"At task start
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"body: '...')` — memory writes go
|
|
65
|
-
"through
|
|
66
|
-
"
|
|
67
|
-
"synonyms in BOTH working languages (e.g. TR + EN) — a Turkish query cannot otherwise find an English page. " \
|
|
68
|
-
"If recall is too shallow, queue `vakanuvis(action: 'digest', query: '...')`: it returns a deterministic " \
|
|
69
|
-
"receipt and compiles the wiki page asynchronously. Make the session close a standard digest point: review " \
|
|
70
|
-
"the session's new cards, queue one digest per open topic, and let the home index refresh.",
|
|
54
|
+
"At task start, ask it with `vakanuvis(action: 'recall', query: '...')`: it returns a deterministic " \
|
|
55
|
+
"digest plus rubik:// source links. Recall is keyword-AND over card/page text: use 2-3 core terms. " \
|
|
56
|
+
"If the result is empty, retry once with other-language synonyms. When dispatching a subagent, paste " \
|
|
57
|
+
"the relevant recall digest (or its rubik:// link) into the task brief. At the end of a task, persist " \
|
|
58
|
+
"durable conclusions with `vakanuvis(action: 'record', topic: '...', body: '...')` — memory writes go " \
|
|
59
|
+
"through record (ref minting, upsert, supersede come free), never through raw storage puts. End the body " \
|
|
60
|
+
"with a `Keywords:` line (TR+EN) if you can; it is auto-extracted otherwise.",
|
|
71
61
|
"prune_context" =>
|
|
72
62
|
"When long sessions leave stale exploration and superseded tool output weighing on the ROOT window, " \
|
|
73
63
|
"check `prune_context(action: 'status')` (window estimate vs ceiling, staged plan state) and force a " \
|
|
@@ -79,7 +69,11 @@ module Rubik
|
|
|
79
69
|
|
|
80
70
|
module_function
|
|
81
71
|
|
|
82
|
-
def teaching_for(tool_name
|
|
72
|
+
def teaching_for(tool_name, vakanuvis_active: true)
|
|
73
|
+
return vakanuvis_active ? STORAGE_BASE : STORAGE_FALLBACK if tool_name.to_s == "storage"
|
|
74
|
+
|
|
75
|
+
TEACHINGS[tool_name.to_s]
|
|
76
|
+
end
|
|
83
77
|
end
|
|
84
78
|
end
|
|
85
79
|
end
|
|
@@ -97,6 +97,7 @@ module Rubik
|
|
|
97
97
|
config: context_cfg,
|
|
98
98
|
force: true
|
|
99
99
|
)
|
|
100
|
+
Rubik::Storage::Vakanuvis::Orientation.inject_after_compaction!(ctx.conversation_history, ctx.runtime_manager, config: ctx.config)
|
|
100
101
|
after_tokens = Rubik::Prompt::TokenEstimator.estimate_history(ctx.conversation_history)
|
|
101
102
|
delta = before_tokens - after_tokens
|
|
102
103
|
ctx.out.puts "Context compaction completed: #{before_tokens} -> #{after_tokens} tokens (reduced #{delta} tokens)."
|
|
@@ -57,11 +57,13 @@ module Rubik
|
|
|
57
57
|
|
|
58
58
|
shell.register_command("clear", description: "Clear conversation history / dialogue turns") do |_args, ctx|
|
|
59
59
|
ctx.conversation_history.clear
|
|
60
|
+
Rubik::Storage::Vakanuvis::Orientation.reset_session!(ctx.runtime_manager)
|
|
60
61
|
ctx.out.puts "Conversation context cleared. Starting fresh topic."
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
shell.register_command("reset", description: "Reset conversation history and in-memory dialogue turns") do |_args, ctx|
|
|
64
65
|
ctx.conversation_history.clear
|
|
66
|
+
Rubik::Storage::Vakanuvis::Orientation.reset_session!(ctx.runtime_manager)
|
|
65
67
|
ctx.out.puts "Conversation context reset. Starting fresh topic."
|
|
66
68
|
end
|
|
67
69
|
|
|
@@ -191,6 +191,8 @@ module Rubik
|
|
|
191
191
|
def apply_staged_prune_plan!
|
|
192
192
|
return if @replaying
|
|
193
193
|
|
|
194
|
+
Rubik::Storage::Vakanuvis::Orientation.inject_session_start!(@conversation_history, @runtime_manager, config: @config) if @conversation_history.empty?
|
|
195
|
+
|
|
194
196
|
harness = Rubik::Coeffects.get(@runtime_manager&.context, Rubik::Coeffects::PRUNER)
|
|
195
197
|
return unless harness
|
|
196
198
|
|
|
@@ -198,6 +200,7 @@ module Rubik
|
|
|
198
200
|
return unless new_window
|
|
199
201
|
|
|
200
202
|
@conversation_history = new_window
|
|
203
|
+
Rubik::Storage::Vakanuvis::Orientation.inject_after_compaction!(@conversation_history, @runtime_manager, config: @config)
|
|
201
204
|
line = "[Pruner] Applied staged plan: dropped #{receipt[:dropped]} unit(s), " \
|
|
202
205
|
"summarized #{receipt[:summarized]} (#{receipt[:skipped_stale]} stale op(s) skipped) — " \
|
|
203
206
|
"window ~#{receipt[:before_tokens]} -> ~#{receipt[:after_tokens]} tokens."
|
|
@@ -38,16 +38,20 @@ module Rubik
|
|
|
38
38
|
return text unless text.is_a?(String)
|
|
39
39
|
return text if text.empty?
|
|
40
40
|
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
|
|
41
|
+
# PERF: Fast-path for valid encoding strings: test QUICK_CHECK_REGEX
|
|
42
|
+
# directly on input text before calling dup or scrub. This short-circuits
|
|
43
|
+
# clean strings without allocating extra intermediate string copies or
|
|
44
|
+
# running scrub/gsub passes (~2.7x speedup, ~50% fewer allocations).
|
|
45
|
+
if text.valid_encoding?
|
|
46
|
+
return text.dup unless text.match?(QUICK_CHECK_REGEX)
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
result = text.dup
|
|
49
|
+
else
|
|
50
|
+
# Fall back safely for invalid UTF-8 / binary strings: scrub first so
|
|
51
|
+
# match? and gsub! do not raise ArgumentError.
|
|
52
|
+
result = text.scrub
|
|
53
|
+
return result unless result.match?(QUICK_CHECK_REGEX)
|
|
54
|
+
end
|
|
51
55
|
|
|
52
56
|
# Keep replacement order: masking one credential can expose a word
|
|
53
57
|
# boundary needed to recognize the next (for example, a bearer token).
|
|
@@ -54,8 +54,11 @@ module Rubik
|
|
|
54
54
|
# hemen ardından doğum denemesi AYRI bir @lock penceresinde
|
|
55
55
|
# (iki fazlı — tek büyük kritik bölüm İÇİNE ALMA). Doğum
|
|
56
56
|
# başarısızsa iş kuyrukta kalır: yarım durum birikmez.
|
|
57
|
-
# Dönen makbuz dizgesi bayt-bayt eski Harness#digest çıktısıdır
|
|
58
|
-
|
|
57
|
+
# Dönen makbuz dizgesi bayt-bayt eski Harness#digest çıktısıdır;
|
|
58
|
+
# submit_with_status aynı kabulü [makbuz, durum] ikilisi olarak
|
|
59
|
+
# verir (:accepted|:duplicate|:full) — otomatik damıtım red'i
|
|
60
|
+
# makbuz dizgesinden ayırt edebilsin diye (review #346).
|
|
61
|
+
def submit_with_status(query, spawn: true)
|
|
59
62
|
target = Vocabulary.wiki_ref(query)
|
|
60
63
|
# v2 §4-V5: aynı hedefli iş zaten kuyrukta ya da uçuştaysa makbuz
|
|
61
64
|
# 'already queued' varyantına düşer — çift doğum yok.
|
|
@@ -68,18 +71,20 @@ module Rubik
|
|
|
68
71
|
:accepted
|
|
69
72
|
end
|
|
70
73
|
if admission == :duplicate
|
|
71
|
-
return "Digest already queued for '#{target}' (#{AnnalWriter.one_line(query, 60)}): the chronicler is " \
|
|
72
|
-
|
|
74
|
+
return ["Digest already queued for '#{target}' (#{AnnalWriter.one_line(query, 60)}): the chronicler is " \
|
|
75
|
+
"compiling it — recall it later with vakanuvis(action: 'recall', query: '...').", admission]
|
|
73
76
|
end
|
|
74
77
|
if admission == :full
|
|
75
|
-
return "Digest not accepted for '#{target}': capacity #{@max_digest_jobs} is full. " \
|
|
76
|
-
|
|
78
|
+
return ["Digest not accepted for '#{target}': capacity #{@max_digest_jobs} is full. " \
|
|
79
|
+
"Existing jobs are preserved; retry after a digest finishes.", admission]
|
|
77
80
|
end
|
|
78
81
|
|
|
79
|
-
spawned = spawn_job!(batch: false)
|
|
80
|
-
DistillGate.receipt(query: query, target: target, spawned: !spawned.nil?)
|
|
82
|
+
spawned = spawn ? spawn_job!(batch: false) : nil
|
|
83
|
+
[DistillGate.receipt(query: query, target: target, spawned: !spawned.nil?), admission]
|
|
81
84
|
end
|
|
82
85
|
|
|
86
|
+
def submit(query, spawn: true) = submit_with_status(query, spawn: spawn)[0]
|
|
87
|
+
|
|
83
88
|
# ---- Seam 2: worker thread'i (Harness#handle_subagent çağırır) ---
|
|
84
89
|
|
|
85
90
|
# Ham subagent tamamlanma olayı. İş sicilden @lock altında
|