scout-essentials 1.8.7 → 1.9.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +26 -12
  3. data/README.md +83 -112
  4. data/VERSION +1 -1
  5. data/doc/Improvements.md +226 -0
  6. data/doc/StartHere.md +122 -0
  7. data/doc/developer/AnnotationSystem.md +184 -0
  8. data/doc/developer/Architecture.md +147 -0
  9. data/doc/developer/Configuration.md +238 -0
  10. data/doc/developer/CoreUtilities.md +265 -0
  11. data/doc/developer/DesignPrinciples.md +129 -0
  12. data/doc/developer/ErrorHandling.md +203 -0
  13. data/doc/developer/LockingAndConcurrency.md +157 -0
  14. data/doc/developer/PathResolution.md +200 -0
  15. data/doc/developer/PersistenceAndResources.md +119 -0
  16. data/doc/developer/StreamingModel.md +236 -0
  17. data/doc/user/AnnotatingData.md +202 -0
  18. data/doc/user/CachingResults.md +183 -0
  19. data/doc/user/CommandLineOptions.md +189 -0
  20. data/doc/user/Cookbook.md +211 -0
  21. data/doc/user/HandlingStreams.md +236 -0
  22. data/doc/user/LoggingAndProgress.md +158 -0
  23. data/doc/user/ProducingResources.md +177 -0
  24. data/doc/user/RemoteData.md +157 -0
  25. data/doc/user/RunningCommands.md +218 -0
  26. data/doc/user/WorkingWithFiles.md +217 -0
  27. data/lib/scout/cmd.rb +343 -40
  28. data/lib/scout/concurrent_stream.rb +14 -1
  29. data/lib/scout/indiferent_hash.rb +1 -1
  30. data/lib/scout/log/fingerprint.rb +13 -8
  31. data/lib/scout/log/progress/report.rb +1 -1
  32. data/lib/scout/log.rb +4 -1
  33. data/lib/scout/misc/digest.rb +6 -5
  34. data/lib/scout/misc/format.rb +24 -0
  35. data/lib/scout/named_array.rb +1 -1
  36. data/lib/scout/open/stream.rb +2 -2
  37. data/lib/scout/open/util.rb +8 -4
  38. data/lib/scout/open.rb +3 -3
  39. data/lib/scout/path/find.rb +3 -2
  40. data/lib/scout/persist.rb +14 -10
  41. data/lib/scout/resource/produce.rb +9 -1
  42. data/research/annotations-data-analysis.md +206 -0
  43. data/research/behavior-probes.md +1925 -0
  44. data/research/commands-streaming-analysis.md +272 -0
  45. data/research/design-philosophy-analysis.md +383 -0
  46. data/research/doc-audit-findings.md +294 -0
  47. data/research/ecosystem-attribution.md +118 -0
  48. data/research/implementation-inventory-core.md +1029 -0
  49. data/research/implementation-inventory-open.md +417 -0
  50. data/research/implementation-inventory-path-persist-resource.md +774 -0
  51. data/research/io-paths-analysis.md +228 -0
  52. data/research/persistence-resources-analysis.md +244 -0
  53. data/research/synthesis-report.md +80 -0
  54. data/scout-essentials.gemspec +37 -15
  55. data/test/scout/open/test_remote.rb +1 -2
  56. data/test/scout/test_cmd.rb +411 -0
  57. metadata +36 -14
  58. data/doc/Annotation.md +0 -352
  59. data/doc/CMD.md +0 -363
  60. data/doc/ConcurrentStream.md +0 -163
  61. data/doc/IndiferentHash.md +0 -240
  62. data/doc/Log.md +0 -235
  63. data/doc/NamedArray.md +0 -174
  64. data/doc/Open.md +0 -331
  65. data/doc/Path.md +0 -217
  66. data/doc/Persist.md +0 -214
  67. data/doc/Resource.md +0 -229
  68. data/doc/SimpleOPT.md +0 -236
  69. data/doc/TmpFile.md +0 -154
@@ -16,6 +16,19 @@ class TestCmd < Test::Unit::TestCase
16
16
  assert(CMD.process_cmd_options("--user-agent" => "firefox", "-q" => true) =~ /-q/)
17
17
  end
18
18
 
19
+ def test_cmd_option_array
20
+ assert_equal(["--user-agent", "firefox"], CMD.process_cmd_options_array("--user-agent" => "firefox"))
21
+ assert_equal(["--user-agent=firefox"], CMD.process_cmd_options_array("--user-agent=" => "firefox"))
22
+ assert_equal(["-q"], CMD.process_cmd_options_array("-q" => true))
23
+ assert_equal([], CMD.process_cmd_options_array("-q" => nil))
24
+ assert_equal([], CMD.process_cmd_options_array("-q" => false))
25
+
26
+ result = CMD.process_cmd_options_array("--user-agent" => "firefox", "-q" => true)
27
+ assert_include result, "--user-agent"
28
+ assert_include result, "firefox"
29
+ assert_include result, "-q"
30
+ end
31
+
19
32
  def test_cmd
20
33
  assert_equal("test\n", CMD.cmd("echo '{opt}' test").read)
21
34
  assert_equal("test", CMD.cmd("echo '{opt}' test", "-n" => true).read)
@@ -96,4 +109,402 @@ line33
96
109
  assert_equal "TEST", CMD.bash("echo TEST").read.strip
97
110
  assert_equal ENV["HOME"], CMD.bash("echo $HOME").read.strip
98
111
  end
112
+
113
+ def test_cmd_array_basic
114
+ assert_equal("hello\n", CMD.cmd(["echo", "hello"]).read)
115
+ assert_equal("hello world\n", CMD.cmd(["echo", "hello world"]).read)
116
+ end
117
+
118
+ def test_cmd_array_special_chars
119
+ # Special characters should be treated literally, NOT interpreted by shell
120
+ assert_equal("hello; rm -rf /\n", CMD.cmd(["echo", "hello; rm -rf /"]).read)
121
+ assert_equal("$HOME\n", CMD.cmd(["echo", "$HOME"]).read)
122
+ assert_equal("hello | cat\n", CMD.cmd(["echo", "hello | cat"]).read)
123
+ end
124
+
125
+ def test_cmd_array_with_options
126
+ # Hash options with boolean flag add the flag to the command array
127
+ # Options hash with flag => value are appended as separate args
128
+ assert_equal("two\n", CMD.cmd(["cut"], "-f" => 2, "-d" => " ", :in => "one two three").read)
129
+ # String second argument is appended to the command array
130
+ assert_equal("world", CMD.cmd(["echo", "-n"], "world").read)
131
+ end
132
+
133
+ def test_cmd_array_with_stdin
134
+ assert_equal("data", CMD.cmd(["cat"], :in => "data").read)
135
+ assert_equal("TRANSFORMED\n", CMD.cmd(["tr", "a-z", "A-Z"], :in => "transformed\n").read)
136
+ end
137
+
138
+ def test_cmd_array_pipe
139
+ assert_equal("test\n", CMD.cmd(["echo", "test"], :pipe => true).read)
140
+
141
+ text = "line1\nline2\nline3\n"
142
+ io = CMD.cmd(["cat"], :in => text, :pipe => true)
143
+ assert_equal(text, io.read)
144
+ io.join
145
+ end
146
+
147
+ def test_cmd_array_pipe_chain
148
+ text = "line1\nline2\nline3\nline4\n"
149
+ io1 = CMD.cmd(["cat"], :in => text, :pipe => true)
150
+ io2 = CMD.cmd(["grep", "line"], :in => io1, :pipe => true)
151
+ assert_equal(4, io2.read.split(/\n/).length)
152
+ io2.join
153
+ end
154
+
155
+ def test_cmd_array_error
156
+ Log.with_severity 6 do
157
+ assert_raise ProcessFailed do CMD.cmd(["fake-command"]) end
158
+ assert_raise ProcessFailed do CMD.cmd(["false"]) end
159
+
160
+ assert_nothing_raised ProcessFailed do CMD.cmd(["fake-command"], :no_fail => true, :pipe => true) end
161
+ assert_nothing_raised ProcessFailed do CMD.cmd(["false"], :no_fail => true, :pipe => true) end
162
+ end
163
+ end
164
+
165
+ def test_cmd_array_save_stderr
166
+ # save_stderr captures stderr onto the result's std_err
167
+ result = CMD.cmd(["ls", "/nonexistent_dir_12345"], :no_fail => true, :save_stderr => true)
168
+ assert_match(/No such file/, result.std_err)
169
+ end
170
+
171
+ def test_cmd_array_no_fail_nonpipe
172
+ # no_fail in non-pipe mode should not raise
173
+ Log.with_severity 6 do
174
+ assert_nothing_raised ProcessFailed do CMD.cmd(["false"], :no_fail => true) end
175
+ assert_nothing_raised ProcessFailed do CMD.cmd(["fake-command"], :no_fail => true) end
176
+ end
177
+ end
178
+
179
+ ### :timeout option
180
+
181
+ def test_cmd_timeout_nonpipe_raises
182
+ Log.with_severity 6 do
183
+ e = assert_raise(CMD::Timeout) do
184
+ CMD.cmd("sleep 5", :timeout => 0.3)
185
+ end
186
+ assert_match(/sleep 5/, e.message)
187
+ assert_match(/0\.3/, e.message)
188
+ assert_match(/sleep 5/, e.command)
189
+ assert_equal(0.3, e.timeout)
190
+ end
191
+ end
192
+
193
+ def test_cmd_timeout_nonpipe_no_zombie
194
+ Log.with_severity 6 do
195
+ e = assert_raise(CMD::Timeout) do
196
+ CMD.cmd("sleep 30", :timeout => 0.3)
197
+ end
198
+ pid = e.pid
199
+
200
+ # The pid is reaped by the watchdog: once reaped, waitpid raises
201
+ # Errno::ECHILD. Retry for a while to allow for the kill grace period
202
+ reaped = false
203
+ 40.times do
204
+ begin
205
+ Process.waitpid(pid, Process::WNOHANG)
206
+ sleep 0.05
207
+ rescue Errno::ECHILD
208
+ reaped = true
209
+ break
210
+ end
211
+ end
212
+ assert(reaped, "pid #{pid} was not reaped after the timeout")
213
+ end
214
+ end
215
+
216
+ def test_cmd_timeout_nonpipe_completes
217
+ # A command that finishes before the timeout works as if no timeout was
218
+ # given, and the option does not leak into the command line
219
+ assert_equal("test\n", CMD.cmd("echo test", :timeout => 5).read)
220
+ end
221
+
222
+ def test_cmd_timeout_pipe_read_raises
223
+ Log.with_severity 6 do
224
+ assert_raise(CMD::Timeout) do
225
+ CMD.cmd("sleep 5", :pipe => true, :timeout => 0.3).read
226
+ end
227
+ end
228
+ end
229
+
230
+ def test_cmd_timeout_pipe_join_raises
231
+ Log.with_severity 6 do
232
+ io = CMD.cmd("sleep 5", :pipe => true, :timeout => 0.3)
233
+ assert_raise(CMD::Timeout){ io.join }
234
+ end
235
+ end
236
+
237
+ def test_cmd_timeout_pipe_partial_output
238
+ Log.with_severity 6 do
239
+ io = CMD.cmd("bash -c 'echo START; sleep 5'", :pipe => true, :timeout => 0.5)
240
+ line = nil
241
+ begin
242
+ line = io.gets
243
+ rescue IOError
244
+ # closing the stream may interrupt the read first
245
+ end
246
+ assert_equal("START\n", line)
247
+ assert_raise(CMD::Timeout){ io.join }
248
+ end
249
+ end
250
+
251
+ def test_cmd_timeout_pipe_completes
252
+ io = CMD.cmd("echo test", :pipe => true, :timeout => 5)
253
+ assert_equal("test\n", io.read)
254
+ assert_nothing_raised{ io.join }
255
+ end
256
+
257
+ def test_cmd_timeout_pipe_stderr_thread
258
+ # Exercises the stderr consumer thread cleanup on timeout
259
+ Log.with_severity 6 do
260
+ assert_raise(CMD::Timeout) do
261
+ CMD.cmd("bash -c 'echo OUT; echo ERR >&2; sleep 5'", :pipe => true, :timeout => 0.3, :stderr => 0).read
262
+ end
263
+ end
264
+ end
265
+
266
+ def test_cmd_timeout_pipe_blocked_input
267
+ # The stdin feeder thread blocks writing to the process; it must be
268
+ # interrupted when the timeout aborts the stream
269
+ Log.with_severity 6 do
270
+ content = "line\n" * 100_000
271
+ assert_raise(CMD::Timeout) do
272
+ CMD.cmd("bash -c 'read line; sleep 5'", :in => content, :pipe => true, :timeout => 0.5).read
273
+ end
274
+ end
275
+ end
276
+
277
+ def test_cmd_timeout_is_process_failed
278
+ Log.with_severity 6 do
279
+ caught = nil
280
+ begin
281
+ CMD.cmd("sleep 5", :timeout => 0.3)
282
+ rescue ProcessFailed
283
+ caught = $!
284
+ end
285
+ assert(CMD::Timeout === caught)
286
+ end
287
+ end
288
+
289
+ def test_cmd_failure_without_timeout_is_not_timeout
290
+ Log.with_severity 6 do
291
+ e = assert_raise(ProcessFailed){ CMD.cmd("false") }
292
+ assert(! (CMD::Timeout === e))
293
+ end
294
+ end
295
+
296
+ def test_cmd_no_timeout_option_long_command
297
+ # Without :timeout the behaviour is unchanged: the caller waits
298
+ assert_equal("done\n", CMD.cmd("bash -c 'sleep 0.2; echo done'").read)
299
+ end
300
+
301
+ def stderr_emitting_command(lines = 3, sleep_time = 0.3, pause_after = 0)
302
+ "bash -c '#{(1..lines).map{|i| "sleep #{sleep_time}; echo error#{i} >&2"}.join('; ')}; sleep #{pause_after}'"
303
+ end
304
+
305
+ def test_save_stderr_pipe_string_file_lives_tails
306
+ Log.with_severity 6 do
307
+ TmpFile.with_file(".log", false) do |tmp|
308
+ # The child also prints stdout lines that the parent consumes slowly,
309
+ # so the pipe is genuinely being consumed while stderr reaches the log
310
+ cmd = "bash -c 'sleep 0.2; echo error1 >&2; echo out1; sleep 0.4; echo error2 >&2; echo out2; sleep 0.4; echo error3 >&2; echo out3'"
311
+ io = CMD.cmd(cmd, :pipe => true, :stderr => 0, :save_stderr => tmp)
312
+ assert ! io.closed?
313
+
314
+ # Poll the log size while reading stdout one line at a time: the file
315
+ # must grow before the command ends, proving incremental flushing
316
+ sizes = []
317
+ lines = []
318
+ while line = io.gets
319
+ lines << line
320
+ sleep 0.1
321
+ sizes << File.size(tmp) if File.exist?(tmp)
322
+ end
323
+ io.join
324
+
325
+ assert_equal %w(out1 out2 out3), lines.map(&:chomp)
326
+ assert(sizes.any?{|s| s > 0}, "log file should grow while the command runs: #{sizes.inspect}")
327
+ # Sizes must be non-decreasing, and more than one positive size means
328
+ # the file was already being filled before the command finished
329
+ assert_equal(sizes.sort, sizes, "log file size must not shrink: #{sizes.inspect}")
330
+ assert(sizes.count{|s| s > 0} >= 2, "log file should have been flushed several times while running: #{sizes.inspect}")
331
+ assert io.closed?
332
+ content = File.read(tmp)
333
+ assert_equal "error1\nerror2\nerror3\n", content
334
+ assert_equal "error1\nerror2\nerror3\n", io.std_err
335
+ end
336
+ end
337
+ end
338
+
339
+ def test_save_stderr_pipe_scout_path
340
+ Log.with_severity 6 do
341
+ tmp = Path.setup(File.join(TmpFile.tmpdir, 'scout_path_stderr.log'))
342
+ CMD.cmd("bash -c 'echo error1 >&2; echo error2 >&2'", :pipe => true, :stderr => 0, :save_stderr => tmp).read
343
+ assert_equal "error1\nerror2\n", File.read(tmp.find)
344
+ assert(File.exist?(tmp.find))
345
+ end
346
+ end
347
+
348
+ def test_save_stderr_pipe_pathname
349
+ Log.with_severity 6 do
350
+ TmpFile.with_file(".log", false) do |tmp|
351
+ pathname = Pathname.new(tmp)
352
+ CMD.cmd("bash -c 'echo error1 >&2; echo error2 >&2'", :pipe => true, :stderr => 0, :save_stderr => pathname).read
353
+ assert_equal "error1\nerror2\n", File.read(tmp)
354
+ end
355
+ end
356
+ end
357
+
358
+ def test_save_stderr_pipe_io_is_not_closed
359
+ Log.with_severity 6 do
360
+ TmpFile.with_file(".log", false) do |tmp|
361
+ io_dst = File.open(tmp, 'w')
362
+ CMD.cmd("bash -c 'echo error1 >&2; echo error2 >&2'", :pipe => true, :stderr => 0, :save_stderr => io_dst).read
363
+ assert ! io_dst.closed?
364
+ io_dst.close
365
+ assert_equal "error1\nerror2\n", File.read(tmp)
366
+ end
367
+
368
+ string_io = StringIO.new
369
+ CMD.cmd("bash -c 'echo error1 >&2'", :pipe => true, :stderr => 0, :save_stderr => string_io).read
370
+ assert ! string_io.closed?
371
+ assert_equal "error1\n", string_io.string
372
+ end
373
+ end
374
+
375
+ def test_save_stderr_pipe_file_closed_by_cmd
376
+ Log.with_severity 6 do
377
+ TmpFile.with_file(".log", false) do |tmp|
378
+ fds_before = Dir["/proc/self/fd/*"].length
379
+ CMD.cmd("bash -c 'echo error1 >&2; echo error2 >&2'", :pipe => true, :stderr => 0, :save_stderr => tmp).read
380
+ Misc.insist(10, 0.05) do
381
+ raise "fd leaked" unless Dir["/proc/self/fd/*"].length <= fds_before + 1
382
+ end
383
+ fds_after = Dir["/proc/self/fd/*"].length
384
+ assert(fds_after <= fds_before + 1, "CMD should close the file it opened: #{fds_before} -> #{fds_after}")
385
+
386
+ # A second run truncates correctly: proves the previous run closed it
387
+ CMD.cmd("bash -c 'echo only >&2'", :pipe => true, :stderr => 0, :save_stderr => tmp).read
388
+ assert_equal "only\n", File.read(tmp)
389
+ end
390
+ end
391
+ end
392
+
393
+ def test_save_stderr_nonpipe_path
394
+ Log.with_severity 6 do
395
+ TmpFile.with_file(".log", false) do |tmp|
396
+ CMD.cmd("bash -c 'echo error1 >&2; echo error2 >&2'", :save_stderr => tmp, :stderr => 0)
397
+ assert_equal "error1\nerror2\n", File.read(tmp)
398
+ end
399
+ end
400
+ end
401
+
402
+ def test_save_stderr_nonpipe_io
403
+ Log.with_severity 6 do
404
+ string_io = StringIO.new
405
+ CMD.cmd("bash -c 'echo error1 >&2'", :save_stderr => string_io, :stderr => 0)
406
+ assert_equal "error1\n", string_io.string
407
+ assert ! string_io.closed?
408
+ end
409
+ end
410
+
411
+ def test_save_stderr_boolean_still_populates_std_err
412
+ Log.with_severity 6 do
413
+ io = CMD.cmd("bash -c 'echo error1 >&2'", :pipe => true, :stderr => 0, :save_stderr => true)
414
+ io.join
415
+ assert_equal "error1\n", io.std_err
416
+
417
+ out = CMD.cmd("bash -c 'echo error1 >&2'", :save_stderr => true, :stderr => 0)
418
+ assert_equal "error1\n", out.std_err
419
+
420
+ out = CMD.cmd("bash -c 'echo error1 >&2'", :stderr => 0)
421
+ assert ! out.respond_to?(:std_err) || out.std_err.nil? || out.std_err.empty?
422
+ end
423
+ end
424
+
425
+ def test_save_stderr_timeout_keeps_file
426
+ Log.with_severity 6 do
427
+ TmpFile.with_file(".log", false) do |tmp|
428
+ assert_raise(CMD::Timeout) do
429
+ CMD.cmd("bash -c 'echo error1 >&2; sleep 5'", :pipe => true, :stderr => 0, :save_stderr => tmp, :timeout => 0.5).read
430
+ end
431
+ Misc.insist(3, 0.2, 'timeout log file to be flushed') do
432
+ raise "missing #{tmp}" unless File.exist?(tmp)
433
+ end
434
+ assert(File.exist?(tmp))
435
+ assert_equal "error1\n", File.read(tmp)
436
+
437
+ # The destination was closed even though the command timed out
438
+ fds_before = Dir["/proc/self/fd/*"].length
439
+ assert_equal(fds_before, Dir["/proc/self/fd/*"].length)
440
+ end
441
+ end
442
+ end
443
+
444
+ def test_save_stderr_nonpipe_failure_writes_log
445
+ Log.with_severity 6 do
446
+ TmpFile.with_file(".log", false) do |tmp|
447
+ assert_raise(ProcessFailed) do
448
+ CMD.cmd("bash -c 'echo error1 >&2; exit 3'", :save_stderr => tmp, :stderr => 0)
449
+ end
450
+ # The ProcessFailed message embeds stderr and the destination keeps it
451
+ # even though the command failed
452
+ assert(File.exist?(tmp))
453
+ assert_equal "error1\n", File.read(tmp)
454
+ end
455
+ end
456
+ end
457
+
458
+ def test_save_stderr_spawn_failure_closes_destination
459
+ Log.with_severity 6 do
460
+ TmpFile.with_file(".log", false) do |tmp|
461
+ 3.times do |i|
462
+ assert_raise(ProcessFailed) do
463
+ CMD.cmd(['/no/such/binary_xyz'], :save_stderr => tmp, :stderr => 0)
464
+ end
465
+ # No fd still points at the destination file: CMD closed what it
466
+ # opened even though the command never started (the popen3 pipes
467
+ # themselves are pre-existing noise here, they are not related to
468
+ # the destination)
469
+ leaked = Dir["/proc/self/fd/*"].select do |fd|
470
+ (File.readlink(fd) rescue nil) == tmp
471
+ end
472
+ assert(leaked.empty?, "destination fd leaked on spawn failure: #{leaked.inspect}")
473
+ end
474
+
475
+ # And the path is reusable: a later write is not clobbered by a
476
+ # buffered handle left behind
477
+ File.open(tmp, 'w') { |f| f.write "marker\n" }
478
+ assert_equal "marker\n", File.read(tmp)
479
+ end
480
+ end
481
+ end
482
+
483
+ def test_save_stderr_creates_parent_dirs
484
+ Log.with_severity 6 do
485
+ subdir = File.join(TmpFile.tmpdir, 'missing1', 'missing2')
486
+ tmp = File.join(subdir, 'log.txt')
487
+ assert ! File.exist?(subdir)
488
+ CMD.cmd("bash -c 'echo error1 >&2'", :save_stderr => tmp, :stderr => 0)
489
+ assert File.exist?(tmp)
490
+ assert_equal "error1\n", File.read(tmp)
491
+ end
492
+ end
493
+
494
+ def test_save_stderr_unwritable_path_raises
495
+ Log.with_severity 6 do
496
+ TmpFile.with_file("blocked", false) do |blocked|
497
+ File.open(blocked, 'w') do |f| f.puts 'not a dir' end
498
+ bad = File.join(blocked, 'sub', 'log.txt')
499
+
500
+ raised = false
501
+ begin
502
+ CMD.cmd("echo x", :save_stderr => bad, :stderr => 0)
503
+ rescue Exception
504
+ raised = true
505
+ end
506
+ assert raised, "an unwritable path must raise, not be silently ignored"
507
+ end
508
+ end
509
+ end
99
510
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout-essentials
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.7
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Vazquez
@@ -150,18 +150,28 @@ files:
150
150
  - README.md
151
151
  - Rakefile
152
152
  - VERSION
153
- - doc/Annotation.md
154
- - doc/CMD.md
155
- - doc/ConcurrentStream.md
156
- - doc/IndiferentHash.md
157
- - doc/Log.md
158
- - doc/NamedArray.md
159
- - doc/Open.md
160
- - doc/Path.md
161
- - doc/Persist.md
162
- - doc/Resource.md
163
- - doc/SimpleOPT.md
164
- - doc/TmpFile.md
153
+ - doc/Improvements.md
154
+ - doc/StartHere.md
155
+ - doc/developer/AnnotationSystem.md
156
+ - doc/developer/Architecture.md
157
+ - doc/developer/Configuration.md
158
+ - doc/developer/CoreUtilities.md
159
+ - doc/developer/DesignPrinciples.md
160
+ - doc/developer/ErrorHandling.md
161
+ - doc/developer/LockingAndConcurrency.md
162
+ - doc/developer/PathResolution.md
163
+ - doc/developer/PersistenceAndResources.md
164
+ - doc/developer/StreamingModel.md
165
+ - doc/user/AnnotatingData.md
166
+ - doc/user/CachingResults.md
167
+ - doc/user/CommandLineOptions.md
168
+ - doc/user/Cookbook.md
169
+ - doc/user/HandlingStreams.md
170
+ - doc/user/LoggingAndProgress.md
171
+ - doc/user/ProducingResources.md
172
+ - doc/user/RemoteData.md
173
+ - doc/user/RunningCommands.md
174
+ - doc/user/WorkingWithFiles.md
165
175
  - lib/scout-essentials.rb
166
176
  - lib/scout/annotation.rb
167
177
  - lib/scout/annotation/annotated_object.rb
@@ -228,6 +238,18 @@ files:
228
238
  - lib/scout/simple_opt/parse.rb
229
239
  - lib/scout/simple_opt/setup.rb
230
240
  - lib/scout/tmpfile.rb
241
+ - research/annotations-data-analysis.md
242
+ - research/behavior-probes.md
243
+ - research/commands-streaming-analysis.md
244
+ - research/design-philosophy-analysis.md
245
+ - research/doc-audit-findings.md
246
+ - research/ecosystem-attribution.md
247
+ - research/implementation-inventory-core.md
248
+ - research/implementation-inventory-open.md
249
+ - research/implementation-inventory-path-persist-resource.md
250
+ - research/io-paths-analysis.md
251
+ - research/persistence-resources-analysis.md
252
+ - research/synthesis-report.md
231
253
  - scout-essentials.gemspec
232
254
  - share/color/color_names
233
255
  - share/color/diverging_colors.hex
@@ -298,7 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
298
320
  - !ruby/object:Gem::Version
299
321
  version: '0'
300
322
  requirements: []
301
- rubygems_version: 3.7.0.dev
323
+ rubygems_version: 3.7.2
302
324
  specification_version: 4
303
325
  summary: Scout essential tools
304
326
  test_files: []