meerkat-machine-schema 0.6.19

Formal machine schemas and transition definitions for Meerkat
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
use crate::{CompositionSchema, MachineSchema};

use super::{
    compositions::{
        auth_lease_bundle_composition, meerkat_mob_seam_composition, schedule_bundle_composition,
        schedule_mob_bundle_composition, schedule_runtime_bundle_composition,
    },
    dsl::{
        dsl_auth_machine, dsl_meerkat_machine, dsl_mob_machine, dsl_occurrence_lifecycle_machine,
        dsl_schedule_lifecycle_machine, dsl_workgraph_lifecycle_machine,
    },
};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CodeAnchor {
    pub id: String,
    pub path: String,
    pub note: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScenarioCoverage {
    pub id: String,
    pub summary: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SemanticCoverageEntry {
    pub name: String,
    pub anchor_ids: Vec<String>,
    pub scenario_ids: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MachineCoverageManifest {
    pub machine: crate::identity::MachineId,
    pub code_anchors: Vec<CodeAnchor>,
    pub scenarios: Vec<ScenarioCoverage>,
    pub transition_coverage: Vec<SemanticCoverageEntry>,
    pub effect_coverage: Vec<SemanticCoverageEntry>,
    pub invariant_coverage: Vec<SemanticCoverageEntry>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompositionCoverageManifest {
    pub composition: crate::identity::CompositionId,
    pub code_anchors: Vec<CodeAnchor>,
    pub scenarios: Vec<ScenarioCoverage>,
    pub route_coverage: Vec<SemanticCoverageEntry>,
    pub scheduler_rule_coverage: Vec<SemanticCoverageEntry>,
    pub invariant_coverage: Vec<SemanticCoverageEntry>,
}

pub fn canonical_machine_coverage_manifests() -> Vec<MachineCoverageManifest> {
    vec![
        machine_manifest_from_schema(
            &dsl_meerkat_machine(),
            &[
                anchor(
                    "meerkat_machine",
                    "meerkat-runtime/src/meerkat_machine/mod.rs",
                    "authoritative MeerkatMachine command dispatch and state ownership for initialize, recover initializing, register, unregister, reconfigure, stage filters and tools, prepare bindings, drain, interrupt, cancel boundary, cancellation, abort, wait, ingest, publish event, accept input, recover input lifecycle, classify envelope, append/context starts, run preparation, primitive applied conversation/immediate, enter extraction, extraction validation passed/failed retry/exhausted, recoverable/fatal failure, retry requested, budget exhausted, steer accepted, increment attempt count, rollback staged, consume on accept, commit, fail, pending/call/finalize tool surface, retire/retired, reset, stop/stopped executor, destroy/destroyed, ensure executor, runtime notice, silent intents, recycle, realtime binding, MCP server, interaction stream, product turn, live topology, ingress, supervisor, trust reconcile, ops barrier, local endpoint, admission, completion, compaction, submit op event, progress reported op, terminate op, notify op watcher, collect/enqueue, terminal records, model routing status, set model routing baseline, finite switch turn, until changed switch turn, assistant turn admission, image operation begin activate complete restore, routing approval, routing denial, scoped override, sync visibility revisions, and persistent reconfigure",
                ),
                anchor(
                    "meerkat_public_surface",
                    "meerkat/src/meerkat_machine.rs",
                    "MeerkatMachine snapshot/diagnostic facade",
                ),
                anchor(
                    "peer_directory_reachability_authority",
                    "meerkat-comms/src/peer_directory_reachability_authority.rs",
                    "peer directory reachability state now owned as a MeerkatMachine-internal region",
                ),
            ],
            &[
                scenario(
                    "bind-run-boundary-terminal",
                    "runtime binds, runs work, applies a boundary, and reports a terminal outcome",
                ),
                scenario(
                    "retire-reset-destroy",
                    "runtime retires, resets, stops, and destroys without reopening superseded work",
                ),
                scenario(
                    "staged_visibility_apply",
                    "tool visibility staged state promotes into the committed visible revision at a boundary",
                ),
                scenario(
                    "turn_interrupt_and_shutdown",
                    "running work records interrupt and shutdown intent without escaping the Meerkat authority boundary",
                ),
                scenario(
                    "peer_reachability_probe",
                    "resolved peer directory updates and send outcomes mutate Meerkat-owned peer reachability state",
                ),
                scenario(
                    "session_registration_and_binding",
                    "initialize, recover initializing, register, unregister, reconfigure session identity, prepare bindings, ensure executor, attach session ingress, detach ingress, drain exit, and runtime bound/retired/destroyed notices",
                ),
                scenario(
                    "input_admission_and_queueing",
                    "ingest and publish event, accept input with or without completion, classify external envelope or plain event, prepare run work, primitive applied conversation or immediate, enter extraction, extraction validation passed, recoverable or fatal failure, budget exhausted, steer accepted, increment attempt count, consume on accept, enqueue classified entry, resolve admission, submit admitted ingress effect, post admission signal, and input or ingress notices",
                ),
                scenario(
                    "ops_completion_and_waiters",
                    "abort, wait, abort all, request cancellation at boundary, completion produced/resolved, wait all satisfied, collect completed result, submit op event, notify op watcher, reject surface call, retain or evict completed terminal records",
                ),
                scenario(
                    "realtime_connection_projection",
                    "project realtime intent, begin replace detach binding, require reattach, publish signal, reconnect progress, MCP server connect/connected/failed/disconnected/reload, advance session context, interaction stream reserved/attached/completed/expired/closed early, freshness, policy, and binding rotation",
                ),
                scenario(
                    "product_turn_streaming",
                    "product turn in flight, committed, output started, interrupted, terminal, realtime projection advance/refreshed/reset, client input submitted, mid turn activity, and turn terminated classification",
                ),
                scenario(
                    "recycle_and_compaction",
                    "recycle from idle or retired, initiate recycle, check compaction, and re-enter ready runtime ownership without preserving stale completed records",
                ),
                scenario(
                    "model_routing_and_image_operation",
                    "set model routing baseline, request finite switch turn, request until changed switch turn, admit model routing assistant turn, begin image operation, activate image operation override, complete image operation, restore image operation override, project model routing status changed, switch turn denied, switch turn persistent reconfigure requested, switch turn finite override activated/restored, image operation phase changed/denied, and model routing approval terminalized",
                ),
                scenario(
                    "live_topology_and_supervision",
                    "begin live topology reconfigure, mark detached, apply identity or visibility, complete/abort/fail topology, bind/authorize/revoke supervisor, publish/revoke trust edge, comms trust reconcile, and local endpoint publish or clear",
                ),
            ],
        ),
        machine_manifest_from_schema(
            &dsl_mob_machine(),
            &[
                anchor(
                    "mob_handle_surface",
                    "meerkat-mob/src/runtime/handle.rs",
                    "identity-first public MobMachine handle surface for ensure member, reconcile, and member command routing",
                ),
                anchor(
                    "mob_actor_authority",
                    "meerkat-mob/src/runtime/actor.rs",
                    "MobMachine actor authority and command execution for wire, unwire, spawn, ensure member, reconcile, observe runtime, submit work, retire, reset, respawn, complete, mark completed, stop/stopped, resume, force cancel, subscribe events, shutdown, destroy, terminalized member, record operator action provenance, flow, run, create frame seed, create loop seed, project frame phase, project loop state, orchestrator, coordinator, cleanup, append failure ledger, escalate supervisor, peer, progress, notices, kickoff resolve started/callback pending/failed/clear, wiring graph, and session binding",
                ),
            ],
            &[
                scenario(
                    "spawn-work-terminal",
                    "member spawn, ensure member, reconcile, runtime-ready observation, work submission, and terminal work closure",
                ),
                scenario(
                    "retire-respawn-destroy",
                    "member retires, resets, respawns with a new runtime incarnation, stops/stopped, resumes, shuts down, destroys cleanly, and resets to running when reusable",
                ),
                scenario(
                    "wiring-and-session-binding",
                    "wire and unwire members, enforce known identity for session bindings, expose pending spawn, member session binding changed, and wiring lifecycle notices",
                ),
                scenario(
                    "flow-and-run-lifecycle",
                    "run flow, start flow, create run, create frame seed, create loop seed, project frame phase, project loop state, start run, complete flow, finish run, mark completed, kickoff resolve started or failed, kickoff clear, flow terminalized, and force cancel running work",
                ),
                scenario(
                    "event-subscriptions-and-notices",
                    "subscribe agent, all agent, and mob events; emit member, run, flow, progress, terminal, and wiring notices",
                ),
                scenario(
                    "orchestrator-coordinator-cleanup",
                    "initialize, stop, resume, and destroy orchestrator; bind or unbind coordinator; begin and finish cleanup; notify coordinator and escalate supervisor",
                ),
                scenario(
                    "operator-provenance-and-peer-input",
                    "record operator action provenance, trust operation peer, admit peer input, append failure ledger, and surface peer-exposed member inputs",
                ),
            ],
        ),
        machine_manifest_from_schema(
            &dsl_schedule_lifecycle_machine(),
            &[anchor(
                "schedule_lifecycle",
                "meerkat-schedule/src/lifecycle.rs",
                "Schedule::apply domain-facing lifecycle transition seam over create, revise, planning window, pause, resume, delete, supersede pending occurrences, revision, and planning cursor rules",
            )],
            &[
                scenario(
                    "schedule_pause_resume_delete",
                    "schedule transitions through create, pause, resume, and delete while advancing revision",
                ),
                scenario(
                    "schedule_revision_and_planning",
                    "active or paused schedules revise, record planning windows, confirm superseded occurrences, supersede pending occurrences, maintain positive revision, and require occurrence progress for planning cursor",
                ),
            ],
        ),
        machine_manifest_from_schema(
            &dsl_occurrence_lifecycle_machine(),
            &[anchor(
                "occurrence_lifecycle",
                "meerkat-schedule/src/lifecycle.rs",
                "Occurrence::apply domain-facing lifecycle transition seam over claim, claimed, dispatch, await completion, complete, completed, skip, skipped, misfire, misfired, supersede, superseded, delivery failure, lease expiry, live owner, revision, and failure classification",
            )],
            &[
                scenario(
                    "occurrence_start_complete_fail",
                    "occurrence transitions through pending, running, and terminal lifecycle states",
                ),
                scenario(
                    "occurrence_claim_dispatch_completion",
                    "claim pending occurrence, dispatch started from claimed, await completion, complete from dispatching or awaiting, and record claimed/dispatch/awaiting/completed effects",
                ),
                scenario(
                    "occurrence_terminal_classification",
                    "skip/skipped, misfire/misfired, supersede/superseded, delivery failed, occurrences superseded, records revision and explicit failure class for terminal occurrence outcomes",
                ),
                scenario(
                    "occurrence_lease_recovery",
                    "lease expired from claimed, dispatching, or awaiting completion returns live claimed work to owner-aware recovery",
                ),
            ],
        ),
        machine_manifest_from_schema(
            &dsl_auth_machine(),
            &[
                anchor(
                    "auth_lease_handle",
                    "meerkat-runtime/src/handles/auth_lease.rs",
                    "per-binding AuthMachine registry; AuthLeaseHandle trait impl drives acquire, expiring, refresh, reauth, release, lifecycle event, and wake loop DSL transitions through it",
                ),
                anchor(
                    "oauth_flow_handle",
                    "meerkat-runtime/src/handles/oauth_flow.rs",
                    "per-binding AuthMachine-owned OAuth browser and device flow lifecycle authority for admit, verify, begin poll, finish poll, consume, expire, valid, expiring, refreshing, and reauth required phases",
                ),
            ],
            &[
                scenario(
                    "acquire_expire_refresh_complete",
                    "lease transitions through valid, expiring, refreshing, and back to valid on successful refresh",
                ),
                scenario(
                    "reauth_release_and_publication",
                    "reauth required from valid/expiring/refreshing, release lease, emit lifecycle event, and wake refresh loop publication",
                ),
                scenario(
                    "oauth_browser_flow_lifecycle",
                    "OAuth browser flow admit, verify, consume, and expire operations stay under the per-binding AuthMachine lifecycle authority",
                ),
                scenario(
                    "oauth_device_flow_lifecycle",
                    "OAuth device flow admit, verify, begin poll, finish poll, consume, and expire operations stay under the per-binding AuthMachine lifecycle authority",
                ),
            ],
        ),
        machine_manifest_from_schema(
            &dsl_workgraph_lifecycle_machine(),
            &[anchor(
                "workgraph_lifecycle",
                "meerkat-workgraph/src/machine.rs",
                "WorkGraphMachine domain-facing lifecycle transition seam over CreateOpen, CreateBlocked, UpdateOpen, UpdateInProgress, UpdateBlocked, ClaimOpen, ClaimExpiredInProgress, ReleaseInProgress, BlockOpen, BlockInProgress, BlockBlocked, RefreshEligibilityOpen, RefreshEligibilityInProgress, RefreshEligibilityBlocked, ValidateLink, CloseOpenCompleted, CloseInProgressCompleted, CloseBlockedCompleted, CloseOpenCancelled, CloseInProgressCancelled, CloseBlockedCancelled, CloseOpenFailed, CloseInProgressFailed, CloseBlockedFailed, AddEvidenceOpen, AddEvidenceInProgress, AddEvidenceBlocked, AddEvidenceCompleted, AddEvidenceCancelled, AddEvidenceFailed; effects Created, Updated, Claimed, Released, Blocked, LinkValidated, Closed, EvidenceAdded; invariants absent_has_zero_revision, live_has_positive_revision, terminal_has_terminal_time, claim_only_in_progress, blocked_has_no_claim, terminal_has_no_claim; revision, leases, due eligibility, unresolved blockers, and topology legality",
            )],
            &[
                scenario(
                    "workgraph_create_update_ready_claim",
                    "CreateOpen, CreateBlocked, UpdateOpen, UpdateInProgress, UpdateBlocked, RefreshEligibilityOpen, RefreshEligibilityInProgress, RefreshEligibilityBlocked, Created, Updated, ClaimOpen, ClaimExpiredInProgress, Claimed, due eligibility, and CAS revision",
                ),
                scenario(
                    "workgraph_claim_release_recovery",
                    "only one active claim exists, ReleaseInProgress, Released, expired leases become recoverable through machine-approved claim, claim_only_in_progress, blocked_has_no_claim, and terminal_has_no_claim",
                ),
                scenario(
                    "workgraph_block_close_evidence",
                    "BlockOpen, BlockInProgress, BlockBlocked, Blocked, CloseOpenCompleted, CloseInProgressCompleted, CloseBlockedCompleted, CloseOpenCancelled, CloseInProgressCancelled, CloseBlockedCancelled, CloseOpenFailed, CloseInProgressFailed, CloseBlockedFailed, Closed, AddEvidenceOpen, AddEvidenceInProgress, AddEvidenceBlocked, AddEvidenceCompleted, AddEvidenceCancelled, AddEvidenceFailed, EvidenceAdded, absent_has_zero_revision, live_has_positive_revision, and terminal_has_terminal_time",
                ),
                scenario(
                    "workgraph_topology_legality",
                    "ValidateLink and LinkValidated reject missing endpoints, self edges, duplicate edges, and dependency cycles without adding a separate topology machine",
                ),
            ],
        ),
    ]
}

pub fn canonical_composition_coverage_manifests() -> Vec<CompositionCoverageManifest> {
    vec![
        composition_manifest_from_schema(
            &meerkat_mob_seam_composition(),
            &[
                anchor(
                    "mob_meerkat_seam",
                    "meerkat-mob/src/runtime/actor.rs",
                    "MobMachine to MeerkatMachine seam realization for binding requests, work submission, cancellation, lifecycle notices, terminal outcomes, and peer ingress",
                ),
                anchor(
                    "meerkat_runtime_entry",
                    "meerkat-runtime/src/meerkat_machine/mod.rs",
                    "MeerkatMachine command authority consuming runtime binding, admitted work, cancellation, lifecycle, terminal, and peer ingress seam traffic",
                ),
            ],
            &[
                scenario(
                    "binding_round_trip",
                    "mob runtime binding request becomes a Meerkat binding and feeds readiness back to Mob",
                ),
                scenario(
                    "work_round_trip",
                    "mob submits work into Meerkat and observes terminal work outcomes back across the seam",
                ),
                scenario(
                    "peer-ingress-and-cancellation",
                    "peer input admission and cancellation requests cross the MobMachine to MeerkatMachine seam with explicit lifecycle notice feedback",
                ),
            ],
        ),
        composition_manifest_from_schema(
            &schedule_bundle_composition(),
            &[
                anchor(
                    "schedule_service",
                    "meerkat-schedule/src/service.rs",
                    "schedule service precursor for revision supersession, rolling planning, occurrence materialization, pause resume, and delete lifecycle routing",
                ),
                anchor(
                    "schedule_store",
                    "meerkat-schedule/src/store.rs",
                    "schedule store contract precursor for transactional claim, supersede persistence, occurrence progress, and revision-aware planning cursor updates",
                ),
                anchor(
                    "schedule_bundle_schema",
                    "meerkat-machine-schema/src/catalog/compositions.rs",
                    "formal schedule bundle composition",
                ),
            ],
            &[
                scenario(
                    "revision-supersede-route",
                    "revision-affecting schedule updates supersede pending future occurrences through the explicit route",
                ),
                scenario(
                    "pause-resume-without-revision",
                    "pause and resume leave schedule revision unchanged while preserving typed ownership",
                ),
                scenario(
                    "rolling-planning-occurrence-materialization",
                    "rolling planning records a planning window and materializes or supersedes pending occurrences through revision-aware schedule routes",
                ),
            ],
        ),
        composition_manifest_from_schema(
            &schedule_runtime_bundle_composition(),
            &[
                anchor(
                    "schedule_driver",
                    "meerkat-schedule/src/driver.rs",
                    "mechanical scheduler driver precursor for runtime-target claim, revision supersede, handoff, lease expiry, delivery failure, and completion feedback",
                ),
                anchor(
                    "runtime_delivery_precursor",
                    "meerkat-rpc/src/session_runtime.rs",
                    "runtime-owned prompt/event delivery precursor that scheduling must hand off into for dispatch, completion, failure, and lease recovery",
                ),
                anchor(
                    "schedule_runtime_bundle_schema",
                    "meerkat-machine-schema/src/catalog/compositions.rs",
                    "formal schedule runtime bundle composition",
                ),
            ],
            &[
                scenario(
                    "runtime-delivery-feedback",
                    "DispatchToRuntime is realized by runtime-owned delivery and closed by typed completion feedback",
                ),
                scenario(
                    "runtime-lease-expiry",
                    "runtime owner fairness still allows lease expiry to return a stuck occurrence to claimable",
                ),
                scenario(
                    "runtime-revision-supersede",
                    "schedule revision supersede enters occurrence authority before runtime handoff so stale pending work is cancelled explicitly",
                ),
            ],
        ),
        composition_manifest_from_schema(
            &schedule_mob_bundle_composition(),
            &[
                anchor(
                    "schedule_driver",
                    "meerkat-schedule/src/driver.rs",
                    "mechanical scheduler driver precursor for mob-target claim, revision supersede, handoff, lease expiry, delivery failure, and completion feedback",
                ),
                anchor(
                    "mob_delivery_precursor",
                    "meerkat-mob-mcp/src/lib.rs",
                    "mob-owned action delivery precursor that scheduling must hand off into for dispatch, completion, target materialization failure, and lease recovery",
                ),
                anchor(
                    "schedule_mob_bundle_schema",
                    "meerkat-machine-schema/src/catalog/compositions.rs",
                    "formal schedule mob bundle composition",
                ),
            ],
            &[
                scenario(
                    "mob-delivery-feedback",
                    "DispatchToMob is realized by mob-owned delivery and closed by typed completion feedback",
                ),
                scenario(
                    "materialization-failure-classification",
                    "mob-side delivery failure preserves explicit TargetMaterializationFailed classification",
                ),
                scenario(
                    "mob-revision-supersede",
                    "schedule revision supersede enters occurrence authority before mob handoff so stale pending work is cancelled explicitly",
                ),
            ],
        ),
        composition_manifest_from_schema(
            &auth_lease_bundle_composition(),
            &[
                anchor(
                    "auth_lease_handle",
                    "meerkat-runtime/src/handles/auth_lease.rs",
                    "runtime auth lease owner consumes canonical AuthMachine lifecycle acquire, refresh, reauth, release, wake, and publication events",
                ),
                anchor(
                    "auth_lease_bundle_schema",
                    "meerkat-machine-schema/src/catalog/compositions.rs",
                    "formal AuthMachine lifecycle publication handoff composition",
                ),
            ],
            &[scenario(
                "auth-lease-lifecycle-publication",
                "AuthMachine acquire, refresh, reauth, release, wake, and lifecycle transitions publish through the explicit auth lease handoff protocol",
            )],
        ),
    ]
}

fn machine_manifest_from_schema(
    schema: &MachineSchema,
    code_anchors: &[CodeAnchor],
    scenarios: &[ScenarioCoverage],
) -> MachineCoverageManifest {
    MachineCoverageManifest {
        machine: schema.machine.clone(),
        code_anchors: code_anchors.to_vec(),
        scenarios: scenarios.to_vec(),
        transition_coverage: schema
            .transitions
            .iter()
            .map(|transition| SemanticCoverageEntry {
                name: transition.name.as_str().to_owned(),
                anchor_ids: semantic_anchor_ids(transition.name.as_str(), code_anchors),
                scenario_ids: semantic_scenario_ids(transition.name.as_str(), scenarios),
            })
            .collect(),
        effect_coverage: schema
            .effects
            .variants
            .iter()
            .map(|effect| SemanticCoverageEntry {
                name: effect.name.as_str().to_owned(),
                anchor_ids: semantic_anchor_ids(effect.name.as_str(), code_anchors),
                scenario_ids: semantic_scenario_ids(effect.name.as_str(), scenarios),
            })
            .collect(),
        invariant_coverage: schema
            .invariants
            .iter()
            .map(|invariant| SemanticCoverageEntry {
                name: invariant.name.clone(),
                anchor_ids: semantic_anchor_ids(&invariant.name, code_anchors),
                scenario_ids: semantic_scenario_ids(&invariant.name, scenarios),
            })
            .collect(),
    }
}

fn composition_manifest_from_schema(
    schema: &CompositionSchema,
    code_anchors: &[CodeAnchor],
    scenarios: &[ScenarioCoverage],
) -> CompositionCoverageManifest {
    CompositionCoverageManifest {
        composition: schema.name.clone(),
        code_anchors: code_anchors.to_vec(),
        scenarios: scenarios.to_vec(),
        route_coverage: schema
            .routes
            .iter()
            .map(|route| SemanticCoverageEntry {
                name: route.name.as_str().to_owned(),
                anchor_ids: semantic_anchor_ids(route.name.as_str(), code_anchors),
                scenario_ids: semantic_scenario_ids(route.name.as_str(), scenarios),
            })
            .collect(),
        scheduler_rule_coverage: schema
            .scheduler_rules
            .iter()
            .map(|rule| SemanticCoverageEntry {
                name: format!("{rule:?}"),
                anchor_ids: semantic_anchor_ids(&format!("{rule:?}"), code_anchors),
                scenario_ids: semantic_scenario_ids(&format!("{rule:?}"), scenarios),
            })
            .collect(),
        invariant_coverage: schema
            .invariants
            .iter()
            .map(|invariant| SemanticCoverageEntry {
                name: invariant.name.clone(),
                anchor_ids: semantic_anchor_ids(&invariant.name, code_anchors),
                scenario_ids: semantic_scenario_ids(&invariant.name, scenarios),
            })
            .collect(),
    }
}

fn semantic_anchor_ids(name: &str, anchors: &[CodeAnchor]) -> Vec<String> {
    semantic_ids(
        name,
        anchors,
        |anchor| anchor.id.as_str(),
        |anchor| anchor.note.as_str(),
    )
}

fn semantic_scenario_ids(name: &str, scenarios: &[ScenarioCoverage]) -> Vec<String> {
    semantic_ids(
        name,
        scenarios,
        |scenario| scenario.id.as_str(),
        |scenario| scenario.summary.as_str(),
    )
}

fn semantic_ids<T>(
    name: &str,
    items: &[T],
    id: impl Fn(&T) -> &str,
    description: impl Fn(&T) -> &str,
) -> Vec<String> {
    if items.is_empty() {
        return Vec::new();
    }

    let tokens = semantic_tokens(name);
    let scored = items
        .iter()
        .map(|item| {
            let haystack = format!("{} {}", id(item), description(item)).to_ascii_lowercase();
            let score = tokens
                .iter()
                .filter(|token| haystack.contains(token.as_str()))
                .count();
            (item, score)
        })
        .collect::<Vec<_>>();
    let max_score = scored.iter().map(|(_, score)| *score).max().unwrap_or(0);
    if max_score == 0 {
        return Vec::new();
    }

    scored
        .into_iter()
        .filter(|(_, score)| *score == max_score)
        .map(|(item, _)| id(item).to_owned())
        .collect::<Vec<_>>()
}

fn semantic_tokens(name: &str) -> Vec<String> {
    let mut tokens = Vec::new();
    let mut current = String::new();
    let mut previous_lower = false;
    for ch in name.chars() {
        if ch == '_' || ch == '-' || ch == ' ' || ch == '(' || ch == ')' || ch == ',' {
            if current.len() >= 3 {
                tokens.push(current.to_ascii_lowercase());
            }
            current.clear();
            previous_lower = false;
            continue;
        }
        if ch.is_ascii_uppercase() && previous_lower && current.len() >= 3 {
            tokens.push(current.to_ascii_lowercase());
            current.clear();
        }
        previous_lower = ch.is_ascii_lowercase();
        current.push(ch);
    }
    if current.len() >= 3 {
        tokens.push(current.to_ascii_lowercase());
    }
    tokens
}

fn anchor(id: &str, path: &str, note: &str) -> CodeAnchor {
    CodeAnchor {
        id: id.into(),
        path: path.into(),
        note: note.into(),
    }
}

fn scenario(id: &str, summary: &str) -> ScenarioCoverage {
    ScenarioCoverage {
        id: id.into(),
        summary: summary.into(),
    }
}