mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-08 02:25:36 +08:00
feat(live-atc): add simulated AI background traffic on the tuned frequency
Implements the ai-traffic roadmap item per docs/plans/2026-07-14-ai-traffic-architecture-design.md. Simulated other aircraft on the user's frequency — callsigns, ATC instructions, readbacks in their own stable voice, handovers — as pure scenery. It never touches radioBackend: the Python backend keeps owning the dialogue *with* the user, useAiTraffic owns the radio *around* the user. The two share only the speech queue (arbitration) and the log. Rules live as pure, seeded, framework-free modules under shared/utils/aiTraffic/ so they run in tsx --test without a browser: callsign collision rules, wake/in-trail separation, runway slots, the speed ladder, direct validation, the §3 decision table, and the gating chain. app/composables/useAiTraffic.ts wires them to Vue (1 Hz tick, spawner, scheduler). Gating is evaluated twice — before enqueue and again at playback, since seconds pass in between. Traffic never keys up while the user holds PTT, while their transmission is out at the backend, or inside the fresh readback window. Off by default; the toggle surfaces the feature's v1 limitations rather than burying them in a doc. Zero LLM calls: variance comes from seeded RNG over template variants. Deviations from the design, both documented in the design doc: - Adds SimAircraft.quietUntilSec. The design's rule table says "first matching row per tick" but never says an instruction must be allowed to take effect before the next one. Without it the planner re-derives the same unresolved condition every second and nags one aircraft with the same vector: 624 calls/30min measured, vs 90 with the cooldown. - Airline pool limited to the 14 designators DEFAULT_AIRLINE_TELEPHONY already knows; UAE/AUA/WZZ from the design would be spelled out letter by letter instead of spoken as airline names. Verified: 406 tests pass (176 new), no new typecheck errors, /live-atc compiles and serves. The manual in-session walkthrough (audible traffic, toggle mid-session) is NOT verified — it needs a login and the Python backend. The 30-minute deterministic integration run stands in for it and caught two of the three bugs found during development. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,20 @@ const learningMode = defineModel<boolean>('learningMode', { required: true })
|
||||
const debugMode = defineModel<boolean>('debugMode', { required: true })
|
||||
const prerecEnabled = defineModel<boolean>('prerecEnabled', { required: true })
|
||||
const prerecSeconds = defineModel<number>('prerecSeconds', { required: true })
|
||||
const aiTrafficEnabled = defineModel<boolean>('aiTrafficEnabled', { required: true })
|
||||
|
||||
/**
|
||||
* Shown while AI traffic is on. These are deliberate v1 boundaries from the
|
||||
* architecture design, not bugs — stating them up front is cheaper than having
|
||||
* someone discover them mid-session and file them as defects.
|
||||
*/
|
||||
const AI_TRAFFIC_LIMITS = [
|
||||
'Background scenery only — it never affects how your own radio work is scored.',
|
||||
'Audible on the tuned frequency only. Other frequencies stay busy, just silent.',
|
||||
'Never transmits while you hold PTT, while ATC is answering you, or in the ~12s after an instruction.',
|
||||
'Fixed phraseology, no AI text generation. Invented waypoint names, not real SIDs/STARs.',
|
||||
'Traffic never conflicts with you: it always yields, and you get no extra instructions from it.',
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -116,6 +130,34 @@ const prerecSeconds = defineModel<number>('prerecSeconds', { required: true })
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pt-2 border-t border-white/10 space-y-3">
|
||||
<div>
|
||||
<p class="text-xs uppercase tracking-[0.3em] text-white/40">Frequency</p>
|
||||
<p class="text-[11px] text-white/50 mt-1">
|
||||
Simulated other aircraft on your frequency — callsigns, readbacks, handovers — so the
|
||||
band sounds alive while you fly.
|
||||
</p>
|
||||
</div>
|
||||
<v-switch
|
||||
v-model="aiTrafficEnabled"
|
||||
color="cyan"
|
||||
inset
|
||||
label="AI traffic (background chatter)"
|
||||
hide-details
|
||||
/>
|
||||
<!-- Surfaced rather than buried in a doc: every one of these is a
|
||||
deliberate v1 boundary, and knowing them up front is what stops
|
||||
them from reading as broken behaviour. -->
|
||||
<v-expand-transition>
|
||||
<ul v-if="aiTrafficEnabled" class="pm-ai-limits">
|
||||
<li v-for="limit in AI_TRAFFIC_LIMITS" :key="limit">
|
||||
<v-icon size="13" class="pm-ai-limits__icon">mdi-information-outline</v-icon>
|
||||
<span>{{ limit }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</v-expand-transition>
|
||||
</div>
|
||||
|
||||
<div class="pt-2 border-t border-white/10 space-y-3">
|
||||
<div>
|
||||
<p class="text-xs uppercase tracking-[0.3em] text-white/40">Voice input</p>
|
||||
@@ -155,6 +197,33 @@ const prerecSeconds = defineModel<number>('prerecSeconds', { required: true })
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* The AI-traffic caveats. Quiet by design — they inform, they don't warn. */
|
||||
.pm-ai-limits {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid color-mix(in srgb, #22d3ee 22%, transparent);
|
||||
background: color-mix(in srgb, #22d3ee 7%, transparent);
|
||||
list-style: none;
|
||||
}
|
||||
.pm-ai-limits li {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 7px;
|
||||
font-size: 11px;
|
||||
line-height: 1.45;
|
||||
color: var(--t3);
|
||||
}
|
||||
.pm-ai-limits__icon {
|
||||
flex: none;
|
||||
margin-top: 1px;
|
||||
color: #22d3ee;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
/* Mirrors the page's segmented control so the sheet doesn't depend on
|
||||
live-atc.vue's scoped styles reaching a teleported dialog. */
|
||||
.pm-seg {
|
||||
|
||||
Reference in New Issue
Block a user