fiber_audit 0.2.1 → 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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.fiber-audit.example.yml +7 -8
  3. data/ARCHITECTURE.md +97 -673
  4. data/CHANGELOG.md +52 -0
  5. data/README.md +163 -86
  6. data/lib/fiber_audit/cli.rb +19 -5
  7. data/lib/fiber_audit/configuration.rb +31 -6
  8. data/lib/fiber_audit/operation_semantics.rb +172 -0
  9. data/lib/fiber_audit/operation_vocabulary.rb +2 -1
  10. data/lib/fiber_audit/runtime/active_operations.rb +39 -9
  11. data/lib/fiber_audit/runtime/boot.rb +4 -0
  12. data/lib/fiber_audit/runtime/environment.rb +58 -0
  13. data/lib/fiber_audit/runtime/execution_context.rb +55 -43
  14. data/lib/fiber_audit/runtime/lifecycle.rb +91 -50
  15. data/lib/fiber_audit/runtime/operation_liveness_monitor.rb +282 -0
  16. data/lib/fiber_audit/runtime/operation_liveness_policy.rb +47 -0
  17. data/lib/fiber_audit/runtime/probes/base.rb +27 -4
  18. data/lib/fiber_audit/runtime/probes/subprocess.rb +89 -8
  19. data/lib/fiber_audit/runtime/probes/thread_state.rb +0 -14
  20. data/lib/fiber_audit/runtime/scheduler_evidence_classifier.rb +60 -0
  21. data/lib/fiber_audit/runtime/scheduler_observer.rb +54 -18
  22. data/lib/fiber_audit/runtime/scheduler_snapshot.rb +95 -0
  23. data/lib/fiber_audit/runtime/watchdog.rb +54 -4
  24. data/lib/fiber_audit/runtime.rb +5 -0
  25. data/lib/fiber_audit/static/call_site_extractor.rb +1 -0
  26. data/lib/fiber_audit/static/rules/base.rb +19 -0
  27. data/lib/fiber_audit/static/rules/blocking_subprocess.rb +67 -10
  28. data/lib/fiber_audit/static/rules/direct_socket.rb +54 -19
  29. data/lib/fiber_audit/static/rules/io_select.rb +6 -6
  30. data/lib/fiber_audit/static/rules/net_http_in_request.rb +11 -8
  31. data/lib/fiber_audit/static/rules/synchronization.rb +13 -8
  32. data/lib/fiber_audit/static/rules/thread_current_state.rb +20 -30
  33. data/lib/fiber_audit/static/rules/thread_join.rb +9 -7
  34. data/lib/fiber_audit/version.rb +1 -1
  35. metadata +6 -1
data/ARCHITECTURE.md CHANGED
@@ -1,726 +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.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
9
+ ## Component status
150
10
 
151
11
  | Component | Responsibility | Status |
152
12
  |---|---|---|
153
13
  | Gem packaging and loader | Ruby baseline, executable, public surface | Implemented |
154
14
  | CLI and project discovery | Commands, root/config resolution, exit codes | Implemented |
155
15
  | 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
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 |
255
24
 
256
- **Current file:** `lib/fiber_audit/configuration.rb`
257
-
258
- Recognized static configuration shape:
25
+ ## Configuration boundary
259
26
 
260
27
  ```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:
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:
274
39
  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
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
347
48
  ```
348
49
 
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
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.
360
51
 
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:
52
+ ## Static rules
367
53
 
368
54
  | ID | Concern | Default severity |
369
55
  |---|---|---:|
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
56
+ | FA1001 | Subprocess lifecycle and process-wait cooperation | info/medium |
57
+ | FA1002 | Thread-wait scheduler coordination | low |
58
+ | FA1003 | Synchronization scheduler coordination | low/info |
59
+ | FA1004 | True Thread-variable access shared across Fibers | medium |
60
+ | FA1005 | `IO.select` scheduler capability requirement | medium |
61
+ | FA1006 | Socket allocation and constructor endpoint semantics | low |
62
+ | FA1007 | HTTP scheduler cooperation in request-like contexts | medium |
382
63
 
383
- **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.
384
65
 
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`
66
+ ## Runtime architecture
392
67
 
393
- Severity order:
68
+ Runtime instrumentation is activated only by the explicit runtime command:
394
69
 
395
70
  ```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
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
443
81
  ```
444
82
 
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`
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.
462
84
 
463
- The coordinator owns pipeline sequencing, not component internals. Its result
464
- must contain enough information for all reporters:
85
+ ### Targeted observations and shared semantics
465
86
 
466
- - active findings;
467
- - suppressed findings;
468
- - parse/analysis errors;
469
- - derived status;
470
- - 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.
471
88
 
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:
89
+ `SchedulerEvidenceClassifier` adds only Boolean/nil measurements:
489
90
 
490
91
  ```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
92
+ operation_wait_possible
93
+ operation_inventory_only
94
+ operation_scheduler_capability_required
95
+ operation_scheduler_capability_supported
96
+ operation_scheduler_cooperation_available
513
97
  ```
514
98
 
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
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.
524
100
 
525
- ### 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.
526
102
 
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 |
103
+ ### Scheduler watchdog
533
104
 
534
- 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.
535
106
 
536
- > This is a static-only audit. PASS cannot be granted without runtime coverage.
107
+ ### Operation-liveness monitor
537
108
 
538
- ### 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.
539
110
 
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 |
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.
546
112
 
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.
113
+ ### Bounds, privacy, and lifecycle
549
114
 
550
- ## 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.
551
116
 
552
- 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.
553
118
 
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.
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.
558
120
 
559
- External exceptions should be translated at their adapter boundary. Downstream
560
- 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.
561
122
 
562
- Shared error-class placement remains an R1 design decision; the remediation
563
- plan recommends a dedicated `lib/fiber_audit/errors.rb`.
123
+ ### Verification and performance
564
124
 
565
- ## 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.
566
126
 
567
- 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.
568
128
 
569
- ### Current coverage
129
+ Remaining runtime work is combined static/runtime correlation and any separately approved coverage contract that could support stronger status semantics.
570
130
 
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:
131
+ ## Repository map
604
132
 
605
133
  ```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.
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.