Natural bridge orders and 3D heading
Policies that keep the org coherent
name: Natural bridge orders overview: Fix the four real Bridge Commander failures by adding natural Star Trek verb phrases, punctuation-tolerant tokenization, filler-aware argument parsing, and two-axis helm heading (MARK/BY) — then sync registry, help, processor, and scene tests. todos:
- id: tokenizer-punctuation
content: Strip punctuation from tokens in CommandTokenizer + tests status: completed
- id: heading-parser
content: Add HeadingArgumentParser (MARK decimal, BY second axis) and wire into CommandParser for helm.set-heading status: completed
- id: registry-phrases
content: Add natural verb phrases + helm.come-about / helm.all-ahead-full in Bridge + test registries status: completed
- id: bridge-state-processor
content: Extend BridgeState (heading + headingBy), processor cases, FormatStatus status: completed
- id: help-scenes-tests
content: Update BridgeHelp, BridgeScenes with exact log lines; engine + simulation tests status: completed isProject: false
What failed (from your log)
| Spoken order | Root cause today |
|---|---|
| `helm come about` | No verb phrase `come about` in registry |
| `helm all ahead full` | No phrase `all ahead full` (only `warp N` / `full stop`) |
| `weaps target the closest enemy` | No phrase `target … enemy`; only `lock target` |
| `helm, set heading to 122 by 180` | First token is `helm,` (comma) → **UnknownContext**; even if fixed, parser cannot handle `to` fillers or **BY** second axis |
flowchart TD
prompt[User prompt] --> norm[Trim + lowercase]
norm --> tok[CommandTokenizer]
tok --> ctx[Resolve station prefix]
ctx -->|helm, fails| failCtx[UnknownContext]
ctx -->|helm OK| verb[Longest verb phrase]
verb -->|no match| failCmd[Unknown command]
verb -->|match| args[Argument parser]
args --> env[CommandEnvelope]Design
1. Punctuation-tolerant tokens (engine)
File: `CommandTokenizer.cs`
After splitting on whitespace, trim trailing/leading punctuation on each token (helm, → helm, 180. → 180) using a small SearchValues<char> set (,;:.!?).
- Fixes
helm, set heading…without special-casing the first token only. - Add tokenizer tests for comma/colon variants.
2. Natural verb phrases (registry + dogfood)
Files:
| Phrase(s) | Maps to | Notes |
|---|---|---|
| `come about` | **new** `helm.come-about` | No args; processor turns **180°** from current heading |
| `all ahead full`, `ahead full` | **new** `helm.all-ahead-full` | No args; processor sets warp **9** (existing clamp) |
| `target the closest enemy`, `target closest enemy` | `tactical.lock-target` | Same handler; still locks KR-12 |
| `set heading to`, `set heading`, `heading` | `helm.set-heading` | Keep existing; heading args parsed by new parser |
Processor: `BridgeCommandProcessor.cs`
helm.come-about:Heading = (Heading + 180) % 360helm.all-ahead-full:SpeedWarp = 9, status “all ahead full”
3. Two-axis helm heading (MARK / BY) — your 3D convention
Semantics (per your clarification):
BYintroduces the second axis (not maritime “mark”).MARKintroduces the decimal part of the primary axis.- Example:
122 MARK 6 BY 180→ primary 122.6°, secondary 180.0° - Example:
122 BY 180→ primary 122°, secondary 180° - Single number only → set primary; leave secondary unchanged (or 0 if never set — pick one and document in help).
State: `BridgeState.cs`
- Add
double HeadingPrimary(or keepHeadingint +HeadingFraction— prefer one `double Heading` + `double HeadingBy` for clean 122.6 display). - Update
FormatStatus()e.g.HDG 122.6 BY 180° | …
Engine: new internal `HeadingArgumentParser.cs`
- Called from `CommandParser.BuildSuccess` when command is
helm.set-heading. - Skips filler tokens:
to,at,the,degrees. - Grammar (token scan after verb phrase):
{int}→ primary{int} MARK {int} [BY {int}]→ primary = int + mark/10, optional by{int} BY {int}→ primary, by- Envelope arguments:
heading(double),headingBy(double, optional — omit key if absent).
Extend `CommandArgumentDefinition` with CommandArgumentKind.Double or keep ints in envelope and use custom parser only for set-heading (minimal surface: two optional doubles on envelope).
Processor helm.set-heading: read heading + optional headingBy, update state, status reflects both axes.
4. Help and reference panel
File: `BridgeHelp.cs`
Add natural examples matching what players type:
helm come about
helm all ahead full
weaps target the closest enemy
helm set heading to 122 by 180
helm heading 122 mark 6 by 180Note that commas after the station name are OK.
5. Tests (library + scenes)
Engine
- Tokenizer:
helm,/tactical,punctuation cases - Parser: each of the four failed log lines as success cases
- Heading parser unit tests:
122 by 180,122 mark 6 by 180,set heading to 122, single270
Bridge scenes: extend `BridgeScenes.cs` with a short “Natural orders” beat using the exact transcript lines; simulation asserts heading/by/warp/lock after the KR-12 scene.
Simulator: `BridgeSimulator.cs` — mirror new helm commands + two-axis heading for scene tests.
6. Optional follow-up (out of scope unless you want it in same PR)
IValidateOptionson registry build (duplicate phrases, unknown contexts) — separate small PR- Smarter failure hints (“Did you mean
lock target?”) when verb phrase is close — not required for these four fixes
Files touched (summary)
| Area | Files |
|---|---|
| Engine | `CommandTokenizer.cs`, `CommandParser.cs`, `HeadingArgumentParser.cs`, `CommandArgumentDefinition.cs` (if Double added) |
| Dogfood | `BridgeCommandRegistry.cs`, `BridgeCommandProcessor.cs`, `BridgeState.cs`, `BridgeHelp.cs` |
| Tests | `CommandTokenizerTests.cs`, `CommandEngineTestCases` / new `NaturalOrderTests.cs`, `BridgeScenes.cs`, `BridgeSimulator.cs`, `CommandEngineTestRegistry.cs` |
Verification
dotnet test --project d:\novolis\novolis-commands\tests\Novolis.Commands.Engine.Tests\Novolis.Commands.Engine.Tests.csproj
dotnet run --project d:\novolis\novolis-dogfooding\apps\BridgeCommanderManual checklist — all four log lines should parse and execute:
helm come abouthelm all ahead fullweaps target the closest enemyhelm, set heading to 122 by 180