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