fiber_audit 0.1.0 → 0.2.1

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.fiber-audit.example.yml +19 -0
  3. data/ARCHITECTURE.md +726 -0
  4. data/CHANGELOG.md +41 -0
  5. data/LICENSE +201 -0
  6. data/README.md +66 -8
  7. data/lib/fiber_audit/cli.rb +87 -2
  8. data/lib/fiber_audit/configuration.rb +106 -6
  9. data/lib/fiber_audit/errors.rb +2 -0
  10. data/lib/fiber_audit/operation_vocabulary.rb +42 -0
  11. data/lib/fiber_audit/reporters/text.rb +1 -1
  12. data/lib/fiber_audit/runtime/active_operations.rb +146 -0
  13. data/lib/fiber_audit/runtime/boot.rb +83 -0
  14. data/lib/fiber_audit/runtime/clock.rb +35 -0
  15. data/lib/fiber_audit/runtime/environment.rb +289 -0
  16. data/lib/fiber_audit/runtime/event.rb +86 -0
  17. data/lib/fiber_audit/runtime/execution_context.rb +89 -0
  18. data/lib/fiber_audit/runtime/heartbeat.rb +113 -0
  19. data/lib/fiber_audit/runtime/jsonl/schema.rb +312 -0
  20. data/lib/fiber_audit/runtime/jsonl/writer.rb +122 -0
  21. data/lib/fiber_audit/runtime/lifecycle.rb +343 -0
  22. data/lib/fiber_audit/runtime/limits.rb +102 -0
  23. data/lib/fiber_audit/runtime/location.rb +44 -0
  24. data/lib/fiber_audit/runtime/policy.rb +121 -0
  25. data/lib/fiber_audit/runtime/probes/base.rb +333 -0
  26. data/lib/fiber_audit/runtime/probes/http.rb +80 -0
  27. data/lib/fiber_audit/runtime/probes/io_select.rb +50 -0
  28. data/lib/fiber_audit/runtime/probes/registry.rb +156 -0
  29. data/lib/fiber_audit/runtime/probes/socket.rb +76 -0
  30. data/lib/fiber_audit/runtime/probes/subprocess.rb +83 -0
  31. data/lib/fiber_audit/runtime/probes/synchronization.rb +58 -0
  32. data/lib/fiber_audit/runtime/probes/thread_state.rb +39 -0
  33. data/lib/fiber_audit/runtime/probes/thread_wait.rb +25 -0
  34. data/lib/fiber_audit/runtime/rails_integration.rb +320 -0
  35. data/lib/fiber_audit/runtime/recorder.rb +333 -0
  36. data/lib/fiber_audit/runtime/redactor.rb +102 -0
  37. data/lib/fiber_audit/runtime/sampler.rb +26 -0
  38. data/lib/fiber_audit/runtime/scheduler_observer.rb +128 -0
  39. data/lib/fiber_audit/runtime/session.rb +112 -0
  40. data/lib/fiber_audit/runtime/supervisor.rb +114 -0
  41. data/lib/fiber_audit/runtime/validation.rb +68 -0
  42. data/lib/fiber_audit/runtime/watchdog.rb +479 -0
  43. data/lib/fiber_audit/runtime/watchdog_policy.rb +64 -0
  44. data/lib/fiber_audit/runtime.rb +37 -0
  45. data/lib/fiber_audit/static/rules/blocking_subprocess.rb +3 -8
  46. data/lib/fiber_audit/static/rules/direct_socket.rb +2 -3
  47. data/lib/fiber_audit/static/rules/io_select.rb +2 -4
  48. data/lib/fiber_audit/static/rules/net_http_in_request.rb +3 -5
  49. data/lib/fiber_audit/static/rules/synchronization.rb +2 -6
  50. data/lib/fiber_audit/static/rules/thread_current_state.rb +3 -2
  51. data/lib/fiber_audit/static/rules/thread_join.rb +3 -2
  52. data/lib/fiber_audit/version.rb +1 -1
  53. data/lib/fiber_audit.rb +1 -0
  54. metadata +38 -2
data/ARCHITECTURE.md ADDED
@@ -0,0 +1,726 @@
1
+ # FiberAudit Architecture
2
+
3
+ ## 1. Purpose
4
+
5
+ FiberAudit is a risk-based compatibility auditor for Ruby and Rails applications
6
+ running in a fiber-scheduled environment, such as a Rails application served by
7
+ Falcon.
8
+
9
+ It is designed to answer:
10
+
11
+ 1. Which operations may block a scheduler thread?
12
+ 2. Where do those operations occur?
13
+ 3. Do they appear in request, middleware, callback, job, WebSocket, boot, or
14
+ other execution contexts?
15
+ 4. How strong is the evidence for each finding?
16
+ 5. What should an application owner review or remediate?
17
+
18
+ FiberAudit does **not** prove that an application is fiber-safe. Static analysis
19
+ produces hypotheses, while observational runtime sessions provide bounded evidence
20
+ without establishing complete coverage. FiberAudit never claims unconditional
21
+ `PASS`.
22
+
23
+ > **Repository status:** v0.2.1 includes the v0.1.0 static pipeline end to end:
24
+ > project discovery, configuration, semantic and syntax analysis, execution
25
+ > contexts, FA1001–FA1007, suppressions, status derivation, text/JSON reports,
26
+ > and the CLI. The v0.2 runtime contracts, bounded JSONL recorder, explicit
27
+ > child-process boot, lifecycle, supervising command, bounded scheduler
28
+ > watchdog, targeted FA1001–FA1007 operation probes, and Rails runtime execution
29
+ > contexts are implemented; static/runtime correlation remains future work.
30
+
31
+ ## 2. Scope
32
+
33
+ ### v0.1.0
34
+
35
+ The v0.1.0 architecture is static-only:
36
+
37
+ - inspect Ruby source with Rubydex and Prism;
38
+ - identify high-signal operations that may block or misuse thread-local state;
39
+ - classify findings by execution context where possible;
40
+ - attach severity, confidence, evidence, remediation, and a stable fingerprint;
41
+ - apply inline and YAML suppressions;
42
+ - emit text and versioned JSON reports;
43
+ - return CI-friendly exit codes.
44
+
45
+ ### Explicitly outside v0.1.0
46
+
47
+ The following belong to later releases:
48
+
49
+ - runtime instrumentation and scheduler watchdogs;
50
+ - Rails Railties and middleware;
51
+ - Falcon process orchestration;
52
+ - static/runtime correlation beyond stable fingerprint generation;
53
+ - dependency compatibility intelligence;
54
+ - SARIF and HTML reporters;
55
+ - runtime-backed `PASS` certification.
56
+
57
+ ## 3. Sources of Truth
58
+
59
+ Current code and specs define implementation truth. This document records the
60
+ supported architecture and dependency boundaries; README and CHANGELOG record
61
+ the user-facing release contract.
62
+
63
+ The corrected platform target is Ruby `>= 3.3` with CI configured for Ruby 3.3
64
+ and 3.4. Ruby 3.2 is excluded because it is end-of-life.
65
+
66
+ ## 4. Architectural Principles
67
+
68
+ ### 4.1 FiberAudit owns its public contracts
69
+
70
+ Rubydex and Prism are implementation dependencies. Their objects must not leak
71
+ into rules, findings, suppressions, or reporters. Adapters translate external
72
+ library data into FiberAudit-owned value objects.
73
+
74
+ ### 4.2 Severity and confidence are separate
75
+
76
+ - **Severity** represents impact if the finding is real.
77
+ - **Confidence** represents the strength of the evidence.
78
+
79
+ A high-impact heuristic can therefore be `severity: :high` and
80
+ `confidence: :low` without conflating the two dimensions.
81
+
82
+ ### 4.3 Findings are the integration boundary
83
+
84
+ Static rules—and future runtime correlation—use the same `Finding` model.
85
+ Suppressions, status derivation, reporters, and future correlation operate on
86
+ findings rather than AST or semantic-index objects.
87
+
88
+ ### 4.4 Analysis degrades gracefully
89
+
90
+ An unresolved constant, unknown execution context, or unsupported Rubydex query
91
+ should lower confidence or produce explicit gap metadata. It should not crash an
92
+ entire project audit unless analysis cannot continue safely.
93
+
94
+ ### 4.5 Suppression is post-analysis filtering
95
+
96
+ Rules produce findings independently of suppression policy. The suppression
97
+ store partitions findings into active and suppressed sets. Suppressed findings
98
+ remain available for reporting and audit history, but do not determine the
99
+ active result or exit status.
100
+
101
+ ### 4.6 Static analysis cannot grant `PASS`
102
+
103
+ The strongest static-only outcomes are `NO_FINDINGS` or
104
+ `PASS_WITH_WARNINGS`, accompanied by a static-only disclaimer. A plain `PASS`
105
+ requires sufficient runtime coverage and is outside v0.1.0.
106
+
107
+ ## 5. System Context
108
+
109
+ ```text
110
+ Ruby/Rails project
111
+ source files
112
+ configuration
113
+ suppressions
114
+ |
115
+ v
116
+ +--------------------------+
117
+ | FiberAudit static audit |
118
+ | |
119
+ | Rubydex semantic data |
120
+ | Prism syntax data |
121
+ | Context classification |
122
+ | Static rules |
123
+ | Findings + suppressions |
124
+ +-------------+------------+
125
+ |
126
+ +--> Text report
127
+ +--> JSON report
128
+ +--> Process exit code
129
+ ```
130
+
131
+ FiberAudit does not load Rails at the gem entry point. Rails-shaped semantics
132
+ are inferred from source structure, inheritance, paths, and callbacks.
133
+
134
+ ## 6. Current Repository Architecture
135
+
136
+ ```text
137
+ fiber-audit static
138
+ |
139
+ +-- Project + Configuration
140
+ +-- SemanticIndex + CallSiteExtractor
141
+ +-- ExecutionContextResolver
142
+ +-- Built-in registry (FA1001–FA1007)
143
+ +-- Findings + Suppression Store
144
+ +-- Audit::Result
145
+ +-- Text or JSON reporter
146
+ +-- Exit code 0, 1, or 2
147
+ ```
148
+
149
+ ### Current component status
150
+
151
+ | Component | Responsibility | Status |
152
+ |---|---|---|
153
+ | Gem packaging and loader | Ruby baseline, executable, public surface | Implemented |
154
+ | CLI and project discovery | Commands, root/config resolution, exit codes | Implemented |
155
+ | Findings and fingerprints | Evidence-bearing values and stable identity | Implemented |
156
+ | Configuration and suppressions | Validation and post-analysis filtering | Implemented |
157
+ | `SemanticIndex` | Rubydex adapter | Implemented |
158
+ | `CallSiteExtractor` | Prism traversal and conservative inference | Implemented |
159
+ | Context resolver | Rails/request/job/etc. classification | Implemented |
160
+ | Rule system | Registry and FA1001–FA1007 | Implemented |
161
+ | Audit coordinator | End-to-end orchestration and status | Implemented |
162
+ | Reporters | Text and JSON schema 1.0 | Implemented |
163
+ | Runtime engine | Bounded sessions, watchdog, targeted operations | Implemented through Stage 5 |
164
+
165
+ The public loader is `lib/fiber_audit.rb`.
166
+
167
+ ## 7. v0.1.0 Static Pipeline
168
+
169
+ The pipeline below is implemented and covered by unit, fixture, CLI, and golden
170
+ report tests.
171
+
172
+ ```text
173
+ CLI / Project discovery
174
+ |
175
+ v
176
+ Configuration + source glob expansion
177
+ |
178
+ +----------------------+
179
+ | |
180
+ v v
181
+ Rubydex SemanticIndex Prism CallSiteExtractor
182
+ (project-wide meaning) (local syntax and calls)
183
+ | |
184
+ +----------+-----------+
185
+ |
186
+ v
187
+ ExecutionContextResolver
188
+ |
189
+ v
190
+ Enabled rules FA1001–FA1007
191
+ |
192
+ v
193
+ Findings
194
+ |
195
+ v
196
+ Inline/YAML Suppression Store
197
+ |
198
+ +--------+---------+
199
+ | |
200
+ v v
201
+ Active findings Suppressed findings
202
+ | |
203
+ +--------+---------+
204
+ |
205
+ v
206
+ Audit::Result
207
+ |
208
+ +--------+---------+
209
+ | |
210
+ v v
211
+ Text reporter JSON reporter
212
+ | |
213
+ +--------+---------+
214
+ |
215
+ v
216
+ Exit status
217
+ ```
218
+
219
+ ### Pipeline behavior
220
+
221
+ 1. Detect the project root and configuration file.
222
+ 2. Validate configuration before analysis starts.
223
+ 3. Expand included Ruby files and remove excluded paths.
224
+ 4. Build the Rubydex semantic index once for the workspace.
225
+ 5. Parse each selected source file once with Prism.
226
+ 6. Convert call nodes into FiberAudit-owned `CallSite` values.
227
+ 7. Resolve receivers and execution contexts on a best-effort basis.
228
+ 8. Run enabled rules over call sites.
229
+ 9. Publish evidence-bearing findings to a collection.
230
+ 10. Parse and apply inline and YAML suppressions.
231
+ 11. Derive the project status from active findings.
232
+ 12. Render the selected format and return the configured exit status.
233
+
234
+ ## 8. Component Boundaries
235
+
236
+ ### 8.1 CLI and project discovery
237
+
238
+ **Current files**
239
+
240
+ - `lib/fiber_audit/cli.rb`
241
+ - `lib/fiber_audit/project.rb`
242
+
243
+ Responsibilities:
244
+
245
+ - parse commands and flags;
246
+ - discover the project root;
247
+ - locate `.fiber-audit.yml` or honor `--config`;
248
+ - invoke `Audit`;
249
+ - select a reporter;
250
+ - map results and errors to process exit codes.
251
+
252
+ The CLI must not contain AST traversal, rule matching, or report assembly logic.
253
+
254
+ ### 8.2 Configuration
255
+
256
+ **Current file:** `lib/fiber_audit/configuration.rb`
257
+
258
+ Recognized static configuration shape:
259
+
260
+ ```yaml
261
+ static:
262
+ include:
263
+ - app/**/*.rb
264
+ - lib/**/*.rb
265
+ - config/**/*.rb
266
+ exclude:
267
+ - vendor/**/*
268
+ - tmp/**/*
269
+ - db/schema.rb
270
+ suppressions_path: .fiber-audit-suppressions.yml
271
+
272
+ rules:
273
+ FA1001:
274
+ enabled: true
275
+ severity: high
276
+
277
+ report:
278
+ formats:
279
+ - text
280
+ - json
281
+ min_severity: low
282
+ ```
283
+
284
+ The configuration boundary owns defaults, type validation, allowed formats,
285
+ severity coercion, and rule overrides. Configuration failures must become a
286
+ FiberAudit configuration error so the CLI can consistently exit with code 2.
287
+
288
+ ### 8.3 Semantic indexing
289
+
290
+ **Current file:** `lib/fiber_audit/static/semantic_index.rb`
291
+
292
+ Rubydex supplies project-wide information:
293
+
294
+ - declarations;
295
+ - constant resolution;
296
+ - ancestry and descendants;
297
+ - constant references;
298
+ - source locations.
299
+
300
+ `SemanticIndex` is the only component that should directly depend on
301
+ `Rubydex::Graph`. It normalizes workspace paths and coordinates and returns
302
+ FiberAudit-owned values:
303
+
304
+ - `Declaration`
305
+ - `Reference`
306
+ - `Constant`
307
+ - `RubydexGap`
308
+
309
+ Known adapter limitations are exposed as `RubydexGap` values and documented by
310
+ the Rubydex spike fixtures.
311
+
312
+ ### 8.4 Syntax and call-site extraction
313
+
314
+ **Current files:**
315
+
316
+ - `lib/fiber_audit/static/call_site.rb`
317
+ - `lib/fiber_audit/static/call_site_extractor.rb`
318
+
319
+ Prism supplies syntax-level information that Rubydex does not reliably expose,
320
+ including method names, receiver source, arguments, lexical nesting, and
321
+ precise call-site locations.
322
+
323
+ The target `CallSite` contract contains:
324
+
325
+ ```text
326
+ path, line, column
327
+ receiver_source, receiver_constant, method_name
328
+ arguments, enclosing_symbol, nesting
329
+ execution_context, resolution, confidence
330
+ ```
331
+
332
+ The extractor parses each file once and performs conservative receiver
333
+ inference. The obsolete `SourceIndex` placeholder has been removed.
334
+
335
+ ### 8.5 Execution-context resolution
336
+
337
+ **Current files:**
338
+
339
+ - `lib/fiber_audit/execution_context.rb`
340
+ - `lib/fiber_audit/static/execution_context_resolver.rb`
341
+
342
+ Supported contexts are planned as:
343
+
344
+ ```text
345
+ request, middleware, callback, view, job, websocket,
346
+ boot, console, rake_task, test, unknown
347
+ ```
348
+
349
+ Resolution order:
350
+
351
+ 1. semantic inheritance;
352
+ 2. path-based fallback;
353
+ 3. callback/DSL syntax hints;
354
+ 4. `unknown`.
355
+
356
+ The resolver must never invent certainty. Unknown or heuristic contexts remain
357
+ explicit and can lower confidence.
358
+
359
+ ### 8.6 Static rule system
360
+
361
+ **Current files:** `lib/fiber_audit/static/rules/`
362
+
363
+ Rules consume FiberAudit `CallSite` values and emit `Finding` values. They do
364
+ not parse files and do not access Rubydex directly.
365
+
366
+ Shipped v0.1.0 rules:
367
+
368
+ | ID | Concern | Default severity |
369
+ |---|---|---:|
370
+ | FA1001 | Blocking subprocess operations | high |
371
+ | FA1002 | `Thread#join` / `Thread#value` | high |
372
+ | FA1003 | Blocking synchronization | medium |
373
+ | FA1004 | Thread-local request state | medium/high by operation |
374
+ | FA1005 | Explicit `IO.select` | medium |
375
+ | FA1006 | Direct socket creation | medium |
376
+ | FA1007 | `Net::HTTP` in request-like contexts | high |
377
+
378
+ A rule registry owns registration, enumeration, configuration enablement, and
379
+ metadata used by `list-rules` and `explain`.
380
+
381
+ ### 8.7 Findings and fingerprints
382
+
383
+ **Current files:**
384
+
385
+ - `lib/fiber_audit/findings/location.rb`
386
+ - `lib/fiber_audit/findings/evidence.rb`
387
+ - `lib/fiber_audit/findings/finding.rb`
388
+ - `lib/fiber_audit/findings/collection.rb`
389
+ - `lib/fiber_audit/findings/severity.rb`
390
+ - `lib/fiber_audit/findings/confidence.rb`
391
+ - `lib/fiber_audit/correlation/fingerprint.rb`
392
+
393
+ Severity order:
394
+
395
+ ```text
396
+ critical > high > medium > low > info
397
+ ```
398
+
399
+ Confidence order:
400
+
401
+ ```text
402
+ confirmed > high > medium > low > unknown
403
+ ```
404
+
405
+ A finding carries:
406
+
407
+ ```text
408
+ rule identity and title
409
+ category
410
+ severity and confidence
411
+ location and enclosing symbol
412
+ resolved operation and execution context
413
+ message, evidence, and remediation
414
+ stable fingerprint
415
+ ```
416
+
417
+ The fingerprint is SHA-256 over:
418
+
419
+ ```text
420
+ rule_id : normalized_path : enclosing_symbol : operation
421
+ ```
422
+
423
+ Line number is intentionally excluded so a finding remains stable when nearby
424
+ source lines move.
425
+
426
+ `Finding.new` currently permits empty evidence while a finding is assembled.
427
+ Publication through a collection is intended to require at least one evidence
428
+ entry. Constructor and publication paths must enforce the same final invariant.
429
+
430
+ ### 8.8 Suppressions
431
+
432
+ **Current files:**
433
+
434
+ - `lib/fiber_audit/suppressions/parser.rb`
435
+ - `lib/fiber_audit/suppressions/store.rb`
436
+
437
+ Inline form:
438
+
439
+ ```ruby
440
+ # fiber-audit:disable FA1001 -- executed only by an offline migration
441
+ Open3.capture3(command)
442
+ # fiber-audit:enable FA1001
443
+ ```
444
+
445
+ YAML form:
446
+
447
+ ```yaml
448
+ suppressions:
449
+ - rule: FA1001
450
+ symbol: DataMigration#run
451
+ reason: Executed only by an offline task
452
+ ```
453
+
454
+ Every suppression requires a reason. Inline directives must come from actual
455
+ Ruby comments; directive-looking text in strings, heredocs, or regular
456
+ expressions is not a directive. Both disable and enable matching must use
457
+ comment locations rather than unrestricted line scans.
458
+
459
+ ### 8.9 Audit coordinator
460
+
461
+ **Current file:** `lib/fiber_audit/audit.rb`
462
+
463
+ The coordinator owns pipeline sequencing, not component internals. Its result
464
+ must contain enough information for all reporters:
465
+
466
+ - active findings;
467
+ - suppressed findings;
468
+ - parse/analysis errors;
469
+ - derived status;
470
+ - static-only disclaimer and coverage metadata.
471
+
472
+ ### 8.10 Reporters
473
+
474
+ **Current files:** `lib/fiber_audit/reporters/`
475
+
476
+ Reporters consume `Audit::Result`; they do not rerun analysis or apply
477
+ suppressions.
478
+
479
+ - Text output is optimized for humans and CI logs.
480
+ - JSON output is a versioned external contract.
481
+ - The planned initial JSON schema version is `1.0`.
482
+
483
+ `Finding#to_h_for_json` is currently an internal serialization helper. It does
484
+ not by itself constitute the complete versioned report schema.
485
+
486
+ ## 9. Dependency Direction
487
+
488
+ The desired dependency direction is inward toward FiberAudit-owned contracts:
489
+
490
+ ```text
491
+ CLI / Reporters
492
+ |
493
+ v
494
+ Audit coordinator
495
+ |
496
+ +--> Suppression Store
497
+ +--> Rule Registry
498
+ |
499
+ v
500
+ CallSite
501
+ ^
502
+ |
503
+ Context Resolver / Extractor
504
+ ^ ^
505
+ | |
506
+ SemanticIndex Prism syntax
507
+ |
508
+ Rubydex graph
509
+
510
+ Rules --------------------------> Finding
511
+ Suppressions / Reporters -------> Finding
512
+ Finding ------------------------> Fingerprint, Location, Evidence
513
+ ```
514
+
515
+ Forbidden dependencies:
516
+
517
+ - rules must not depend on Rubydex or Prism node classes;
518
+ - reporters must not depend on indexes or rules;
519
+ - suppressions must not mutate rule behavior;
520
+ - the top-level gem loader must not require Rails;
521
+ - v0.1.0 code must not depend on runtime instrumentation modules.
522
+
523
+ ## 10. Status and Exit Contracts
524
+
525
+ ### Planned project statuses
526
+
527
+ | Status | Meaning |
528
+ |---|---|
529
+ | `FAIL` | At least one active critical or high finding |
530
+ | `REVIEW` | Medium risk, or unresolved low/unknown-confidence risk requiring review |
531
+ | `PASS_WITH_WARNINGS` | Only low or informational findings |
532
+ | `NO_FINDINGS` | No active findings |
533
+
534
+ Every v0.1.0 report must include:
535
+
536
+ > This is a static-only audit. PASS cannot be granted without runtime coverage.
537
+
538
+ ### Exit codes
539
+
540
+ | Code | Meaning |
541
+ |---:|---|
542
+ | 0 | No active finding at or above the configured threshold |
543
+ | 1 | At least one active finding at or above the threshold |
544
+ | 2 | Configuration or analysis error |
545
+ | 3 | Reserved; not emitted in v0.1.0 |
546
+
547
+ The `static` command implements these result and exit-code semantics. Source
548
+ parse errors remain report data so analysis can continue on other files.
549
+
550
+ ## 11. Error Handling
551
+
552
+ Errors should be split into two classes of behavior:
553
+
554
+ - **Recoverable analysis gaps:** record an error/gap, lower confidence, and
555
+ continue with other files or references.
556
+ - **Invalid invocation or configuration:** raise a FiberAudit-owned error and
557
+ let the CLI return exit code 2.
558
+
559
+ External exceptions should be translated at their adapter boundary. Downstream
560
+ components should not need to rescue Rubydex-, Prism-, or YAML-specific errors.
561
+
562
+ Shared error-class placement remains an R1 design decision; the remediation
563
+ plan recommends a dedicated `lib/fiber_audit/errors.rb`.
564
+
565
+ ## 12. Testing Architecture
566
+
567
+ Tests mirror `lib/` under `spec/`.
568
+
569
+ ### Current coverage
570
+
571
+ The suite covers value objects, semantic adaptation, call-site extraction,
572
+ context resolution, all built-in rules, configuration, suppressions, project
573
+ discovery, orchestration, reporters, CLI exit paths, and a versioned golden
574
+ report.
575
+
576
+ ### v0.1.0 coverage
577
+
578
+ - exact `CallSite` extraction and receiver inference;
579
+ - execution-context classification;
580
+ - positive and negative fixtures for every rule;
581
+ - shadowed-constant negatives to avoid name-only false positives;
582
+ - stable fingerprints across repeated analysis;
583
+ - suppression behavior, including comment-only enable/disable directives;
584
+ - project-root and configuration discovery;
585
+ - text and JSON reporter contracts;
586
+ - golden JSON output;
587
+ - clean, findings, and invalid-config CLI exit paths.
588
+
589
+ Fixture applications should remain small and deterministic:
590
+
591
+ ```text
592
+ spec/fixtures/apps/poro_clean
593
+ spec/fixtures/apps/rails_blockers
594
+ spec/fixtures/apps/rails_contexts
595
+ spec/fixtures/reports/rails_blockers_v0.1.json
596
+ ```
597
+
598
+ CI is configured to run linting, specs, and gem packaging on Ruby 3.3 and 3.4.
599
+ The workflow configuration does not itself prove that remote CI has passed.
600
+
601
+ ## 13. Runtime Architecture Beyond v0.1.0
602
+
603
+ The long-term architecture adds a runtime branch alongside static analysis:
604
+
605
+ ```text
606
+ Static engine Runtime session
607
+ Rubydex + Prism Instrumentation + watchdog
608
+ | |
609
+ v v
610
+ Static findings Runtime events
611
+ | |
612
+ +---------------+------------------+
613
+ |
614
+ v
615
+ Fingerprint correlation
616
+ |
617
+ v
618
+ Confirmed / static-only / runtime-only findings
619
+ ```
620
+
621
+ The foundational runtime contracts and recorder are implemented under
622
+ `lib/fiber_audit/runtime/`: immutable event/session values, strict redaction and
623
+ resource policy, injected clocks and sampling, bounded JSONL writing, explicit
624
+ drop accounting, and crash-tolerant session recording. The explicit `runtime`
625
+ command supervises a user-supplied command, injects a conditional Ruby boot,
626
+ forwards signals, preserves child status, and creates a distinct session for each
627
+ observed Ruby process.
628
+
629
+ Explicit runtime boot also installs a narrow observer for schedulers configured
630
+ through `Fiber.set_scheduler`. A scheduler-owned heartbeat fiber updates
631
+ monotonic progress; one process-local watchdog thread detects threshold crossings.
632
+ Each stall emits at most one start and one completion event plus a configured,
633
+ bounded set of project-relative frame events. An active-operation registry uses
634
+ only ephemeral thread/fiber identities and process-local sequences so targeted
635
+ operations can overlap scheduler stalls without retaining arguments or request
636
+ identifiers.
637
+
638
+ Watchdog state is explicit: disabled, absent, active, or unsupported. State and
639
+ stall events bypass random sampling but still consume all recorder rate, event,
640
+ record, and session limits. Runtime JSONL schema `1.0` and its `session_start`
641
+ contract remain unchanged; watchdog policy travels only in strict activation
642
+ settings, while state and policy measurements are ordinary bounded events.
643
+ Explicit boot installs narrow, idempotent `Module#prepend` wrappers for the
644
+ operations represented by FA1001–FA1007. One shared probe base owns monotonic
645
+ timing, sampled completion/abortion events, conservative direct project
646
+ callsites, recursion protection, and active-operation registration. Probe events
647
+ retain only canonical operations, duration, project-relative location,
648
+ ephemeral identities, operation sequence, and a small fixed set of Booleans.
649
+ Commands, URLs, addresses, ports, headers, payloads, return data, exception data,
650
+ and thread-local keys or values are never retained.
651
+
652
+ A process-local registry deactivates wrappers without attempting to remove Ruby
653
+ prepends. A narrow `Kernel#require` wrapper rescans only known standard-library
654
+ targets after successful loads; no unrestricted tracing, `load`, `const_missing`,
655
+ or autoload observation is used. Unknown ownership is skipped rather than mapped
656
+ to a project callsite.
657
+
658
+ Stage 5 adds `source: targeted_probe` and the event kinds `operation_started`,
659
+ `operation_completed`, and `operation_aborted`. These values use existing bounded
660
+ identifier fields and are backward-compatible additions to runtime JSONL schema
661
+ `1.0`; the envelope, payload keys, and original golden fixture remain unchanged.
662
+
663
+ Shutdown first deactivates probes, then makes scheduler callbacks inert, requests
664
+ heartbeat/watchdog stop, bounds and joins the watchdog thread, completes any open
665
+ stall, and only then closes the recorder. Fork rebinding discards inherited probe
666
+ and watchdog references before touching their locks and creates process-local
667
+ replacements.
668
+
669
+ Stage 6 adds Rails execution context detection. A bounded, PID-aware fiber-local
670
+ context stack tracks the current execution context during probe observations.
671
+ The fiber-local stack uses `Fiber.current` instance variables to avoid thread-local
672
+ key visibility, validates against `Context::ALL`, enforces `MAX_DEPTH = 32`, and
673
+ resets on fork. A process-local `RailsIntegration` class hooks into Rails boundaries
674
+ via `Module#prepend`: Rack middleware (`:middleware`), `ActionController::Metal#process_action`
675
+ (`:request`), `ActiveJob::Base#perform_now` (`:job`), and `ActionCable::Channel::Base#dispatch_action`
676
+ (`:websocket`). Wrappers consult the active integration before setting context and become
677
+ inert after deactivation or fork, preserving application semantics. The integration
678
+ supports late loading: hooks are installed when Rails components become available,
679
+ even after runtime boot. Probe observations snapshot the context once at start and
680
+ propagate it through active operations and events. Lifecycle wires context store and
681
+ Rails integration ownership, shutdown deactivates Rails integration before probes,
682
+ and fork rebinding resets context and rebuilds integration. JSONL schema 1.0 and
683
+ privacy requirements are preserved; no new schema fields are added.
684
+
685
+ Loading the gem normally performs no instrumentation, fibers, threads, or file
686
+ I/O. The observer is activated only by explicit runtime boot. A native operation
687
+ that retains Ruby's GVL can prevent the watchdog thread from running until the
688
+ operation returns; absence or unsupported monitoring and absence of stalls are
689
+ never clean certification.
690
+
691
+ Remaining runtime concepts include:
692
+
693
+ - static/runtime evidence merging;
694
+ - runtime coverage sufficient to support future `PASS` semantics.
695
+
696
+ Runtime components must continue to emit or enrich the common finding model
697
+ rather than establish a parallel reporting model.
698
+
699
+ ## 14. Repository Map
700
+
701
+ ```text
702
+ lib/fiber_audit.rb public loader
703
+ lib/fiber_audit/cli.rb command and exit-code boundary
704
+ lib/fiber_audit/project.rb root/config discovery
705
+ lib/fiber_audit/audit.rb static coordinator
706
+ lib/fiber_audit/configuration.rb validated static configuration
707
+ lib/fiber_audit/findings/ public result values
708
+ lib/fiber_audit/correlation/fingerprint.rb stable identity
709
+ lib/fiber_audit/suppressions/ suppression parsing and filtering
710
+ lib/fiber_audit/static/ semantic, call-site, context, rules
711
+ lib/fiber_audit/reporters/ text and JSON schema 1.0
712
+ lib/fiber_audit/runtime/ values, lifecycle, watchdog, probes
713
+ ARCHITECTURE.md supported architecture and boundaries
714
+ ```
715
+
716
+ ## 15. Maintaining This Document
717
+
718
+ Update this document when the supported architecture changes:
719
+
720
+ 1. Describe components as implemented only when source and meaningful specs
721
+ exist.
722
+ 2. Update diagrams when dependency direction changes.
723
+ 3. Record external report-schema changes as versioned contract changes.
724
+ 4. Keep future runtime architecture separate from current static behavior.
725
+ 6. Never use the existence of a built gem artifact as evidence that a release or
726
+ architecture stage is complete.