Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
af84190
Viewer setup: spec 006 and the SRD amendments from Annex A §B.4
claude Oct 17, 2026
a12814f
Viewer: @tma/demos core and React viewer, browser-safe subpaths
claude Oct 17, 2026
85afe73
Viewer: site page, catalogue from gate experiments, bookmarks, blog p…
claude Oct 17, 2026
90f4873
Iteration 3 setup: generator 0.3.0 (second zig, across-LOS), schema 0…
claude Oct 17, 2026
5d4e273
Iteration 3 plan configs/iter3/a (seed 204, 2c configuration); reserv…
claude Oct 17, 2026
a22a049
Iteration 3 plan: pin seed-204 content digests
claude Oct 17, 2026
c804757
G0 checks the second zig and the across-LOS rule; viewer shows seed-2…
claude Oct 17, 2026
1068831
Slices: zig bands up to 180 degrees for across-LOS turns (spec 007)
claude Oct 17, 2026
eb19125
G0 on generator 0.3.0 (seed 42, iteration-3 ranges, noise-free): mach…
claude Oct 17, 2026
007c6be
Viewer: round the second-zig delay in the scenario panel
claude Oct 17, 2026
7d860de
G0 guard test: the committed 0.3.0 G0 is the latest, accepted only af…
claude Oct 17, 2026
5989b3e
G0 panel round 1 fixes: kinematic and second-arc checks, decision bin…
claude Oct 17, 2026
04c1844
G0 round 2 at 5989b3e (checks after panel round 1); tma-g2 writes pai…
claude Oct 17, 2026
c648455
G0 panel round 1 record and responses (review.md)
claude Oct 17, 2026
3df0750
G0 panel round 2 fixes: step lower bound, turn sums, decision binding…
claude Oct 17, 2026
928995a
G0 panel round 2 responses: classical reference in paired.json, FR-00…
claude Oct 17, 2026
b1e36fc
G0 round 3 at 928995a (checks after panel round 2); review.md moves t…
claude Oct 17, 2026
0df0df2
tma-g2: a decision binds by the test data's own seed too (G0 red team…
claude Oct 17, 2026
491ee1b
G0 on generator 0.3.0 passes: agent review panel round 3 (no rejectio…
claude Oct 17, 2026
3128a3a
Iteration 3 decision record (pre-registered before training): plan co…
claude Oct 17, 2026
e3a8522
Validation compares with 2c on the same rows: compare_models on valid…
claude Oct 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,6 @@ site/docs/data-dictionary.md
site/src/generated/
/models/
.claude/worktrees/

# Viewer model copies (site prebuild, from experiments/)
site/static/viewer/
21 changes: 17 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ packages/
classical/ naive baseline, least-squares solver; CLIs tma-naive, tma-solve
eval/ metrics, slices, curves, gates, bootstrap; CLIs tma-eval, tma-gate (g0, g1), tma-g0
models/ tfjs-node models, training, predictions; CLIs tma-train, tma-model-predict, tma-g1
demos/ browser engagement and model viewer (spec 006); site page /viewer
configs/ generator ranges (iter1-*.json) and training configs (iter1/<model>.json)
experiments/<id>/ committed run outputs: manifest, metrics, curves, verdict, seeds
site/ Docusaurus site (docs, blog); data dictionary generated at build
Expand All @@ -59,7 +60,8 @@ pnpm test # vitest: unit, golden, e2e (~30 s)
UPDATE_GOLDEN=1 pnpm test # regenerate golden files; review the diff, say why in the PR
pnpm schema:gen # after editing schema/tma.yaml; bump its version
pnpm schema:check # fails if generated types are stale
pnpm site:build # data dictionary + gate status + site; fails on broken links
pnpm site:build # tsc -b, then data dictionary + gate status + viewer catalogue + site
node scripts/viewer-smoke.mjs [url] [dir] # Playwright smoke of a served /viewer (PLAYWRIGHT=<pkg dir>)
pnpm tma-generate --seed 42 --count 100000 --noise-free --out data/g0
pnpm tma-g0 --data data/g0 --experiment experiments/<id>
pnpm tma-train --config configs/iter1/<model>.json --train <dir>/train.features.bin \
Expand Down Expand Up @@ -166,6 +168,9 @@ sessions (containers are reclaimed when idle).
- A seeded continuation that changes the loss re-scores the best epoch under the new loss and
persists it as `best_val_loss` (schema 0.8.2); a chained job resumes against that value. Without
it the old best, on the old scale, is never beaten and the old weights are kept silently.
- The viewer page imports `@tma/demos` from `dist/`, so `site:build` runs `tsc -b` first. The
site's Babel compiles `[...iterable]` to `[].concat(iterable)`: in browser code spread arrays
only, and use `Array.from` for NodeLists, Maps and Sets.
- Gate checks: pass needs the whole 95% CI and the point below the threshold; fail needs the CI's
lower bound at or above it; anything else is inconclusive (FR-015).

Expand All @@ -188,9 +193,17 @@ scoring) to allow one more iteration, 2c (spec 005, the last under G2): 2 × 128
layers with `loss_weight_speed` 4, from scratch, judged on fresh seed 203: plan `configs/iter2/d`,
decision `experiments/20261015-i2d-decision/`. **G2 failed a third time** (panel round 2,
`experiments/20261016-i2d-g2/`): range 13.5% / 12.1% and course 7.4° / 6.1° pass, speed 1.47 /
1.53 kt fails (classical 0.61 kt). **The project stops at G2: no-go** on speed, pass on range and
course (write-up: `site/blog/2026-10-17-iteration-2c.md`). Seeds 201–203 are reserved; no further
training is planned. Tooling follow-ups are recorded in that experiment's `review.md`.
1.53 kt fails (classical 0.61 kt), no-go on speed and pass on range and course (write-up:
`site/blog/2026-10-17-iteration-2c.md`). Seeds 201–203 are reserved. **SRD Annex A** (accepted
2026-10-17) lifts the effort budget. The engagement and model viewer (spec 006, `packages/demos`,
site page `/viewer`) is built, and the owner's realism check is recorded (Annex A §A.7). Part B
levers are pre-registered one at a time, each with a decision record committed before training
and a fresh test seed (204 onward). **Iteration 3** (spec 007, decision 22) is lever B.2.1:
generator 0.3.0 adds a second zig 3–5 min into leg 2, and both turns cross the line of sight
(`zig_rule: across_los`). Its dataset is seed 204. G0 passed on 0.3.0 (panel round 3,
`experiments/20261017-g0-i3-r3/`). From iteration 3, `tma-g2 --split test` needs the committed
decision that names its plan and test seed (schema 0.9.1 `ChangeDecision.plan`, `test_seed`). The classical forecast is 0.36 kt at zig + 600 s, and the model forecast is 1.1–1.3 kt
(spec 007 research R2).

Gate decisions come from an **agent review panel**, never from waiting on humans (constitution
1.2.0). For each gate experiment:
Expand Down
47 changes: 47 additions & 0 deletions configs/iter3-heldout-ranges.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"window_s": {
"min": 600,
"max": 2400
},
"dt_s": 10,
"ownship_speed_mps": {
"min": 2.5722222222222224,
"max": 7.716666666666667
},
"zig_angle_abs_rad": {
"min": 0.5235987755982988,
"max": 1.5707963267948966
},
"zig_fraction": {
"min": 0.4,
"max": 0.6
},
"target_range0_m": {
"min": 20000,
"max": 30000
},
"range_distribution": "log_uniform",
"target_speed_mps": {
"min": 1.5433333333333334,
"max": 10.28888888888889
},
"noise_sigma_rad_values": [0.017453292519943295, 0.03490658503988659],
"dropout_fraction": {
"min": 0,
"max": 0.1
},
"ownship_turn_rate_rad_s": {
"min": 0.008726646259971648,
"max": 0.05235987755982989
},
"min_range_m": 100,
"zig_rule": "across_los",
"los_offset_rad": {
"min": 0.5235987755982988,
"max": 1.5707963267948966
},
"zig2_delay_s": {
"min": 180,
"max": 300
}
}
47 changes: 47 additions & 0 deletions configs/iter3-ranges.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"window_s": {
"min": 600,
"max": 2400
},
"dt_s": 10,
"ownship_speed_mps": {
"min": 2.5722222222222224,
"max": 7.716666666666667
},
"zig_angle_abs_rad": {
"min": 0.5235987755982988,
"max": 1.5707963267948966
},
"zig_fraction": {
"min": 0.4,
"max": 0.6
},
"target_range0_m": {
"min": 2000,
"max": 20000
},
"range_distribution": "log_uniform",
"target_speed_mps": {
"min": 1.5433333333333334,
"max": 10.28888888888889
},
"noise_sigma_rad_values": [0.017453292519943295, 0.03490658503988659],
"dropout_fraction": {
"min": 0,
"max": 0.1
},
"ownship_turn_rate_rad_s": {
"min": 0.008726646259971648,
"max": 0.05235987755982989
},
"min_range_m": 100,
"zig_rule": "across_los",
"los_offset_rad": {
"min": 0.5235987755982988,
"max": 1.5707963267948966
},
"zig2_delay_s": {
"min": 180,
"max": 300
}
}
20 changes: 20 additions & 0 deletions configs/iter3/a/causal-cnn.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"learning_rate": 0.001,
"batch_size": 256,
"max_epochs": 150,
"patience": 6,
"max_wall_s": 172800,
"loss_weight_range": 1,
"loss_weight_course": 1,
"loss_weight_speed": 4,
"shuffle_labels": false,
"id": "cnn-causal-control",
"model_kind": "causal_cnn",
"units": 64,
"kernel_size": 15,
"layers": 3,
"train_seed": "1003",
"length_bucketing": true,
"tfjs_threads": 1,
"feature_set": "raw"
}
19 changes: 19 additions & 0 deletions configs/iter3/a/instantaneous-shuffled.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"learning_rate": 0.001,
"batch_size": 256,
"max_epochs": 150,
"patience": 6,
"max_wall_s": 172800,
"loss_weight_range": 1,
"loss_weight_course": 1,
"loss_weight_speed": 4,
"shuffle_labels": true,
"id": "lstm-instantaneous-shuffled",
"model_kind": "instantaneous",
"units": 128,
"train_seed": "1005",
"length_bucketing": true,
"tfjs_threads": 1,
"feature_set": "raw",
"layers": 2
}
19 changes: 19 additions & 0 deletions configs/iter3/a/instantaneous.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"learning_rate": 0.001,
"batch_size": 256,
"max_epochs": 150,
"patience": 6,
"max_wall_s": 172800,
"loss_weight_range": 1,
"loss_weight_course": 1,
"loss_weight_speed": 4,
"shuffle_labels": false,
"id": "lstm-instantaneous",
"model_kind": "instantaneous",
"units": 128,
"train_seed": "1001",
"length_bucketing": true,
"tfjs_threads": 1,
"feature_set": "raw",
"layers": 2
}
18 changes: 18 additions & 0 deletions configs/iter3/a/mlp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"learning_rate": 0.001,
"batch_size": 256,
"max_epochs": 150,
"patience": 6,
"max_wall_s": 172800,
"loss_weight_range": 1,
"loss_weight_course": 1,
"loss_weight_speed": 4,
"shuffle_labels": false,
"id": "mlp-control",
"model_kind": "mlp",
"units": 256,
"train_seed": "1004",
"length_bucketing": false,
"tfjs_threads": 1,
"feature_set": "raw"
}
24 changes: 24 additions & 0 deletions configs/iter3/a/plan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"id": "i3a",
"description": "Spec 007 (iteration 3, Annex A lever B.2.1, decision 22): the single change is the problem. Generator 0.3.0 with a second zig 180-300 s after leg 2 starts, and both turns across the line of sight (target 30-90 deg on the other bow); first-zig timing, ranges, noise and dropouts as iteration 2 (configs/iter3-ranges.json). Models: the 2c configuration from scratch (2 x 128 stacked recurrent layers, loss_weight_speed 4, max_epochs 150, patience 6, raw features, same seeds). Dataset seed 204: its own test split decides G2. Scored on validation first (diagnostic; validation rule FR-006).",
"dataset": {
"seed": "204",
"count": 100000,
"ranges": "configs/iter3-ranges.json",
"heldout_ranges": "configs/iter3-heldout-ranges.json",
"digests": {
"train.f32pack": "f8f0629b7db7333c9f079aefadaf02c254ce0f83402e787cadb429e6cc7b32f8",
"validation.f32pack": "01fe0295953e175309c1fe00a6d8a81dcbeb461f237bb2c0e361ea8d464651d5",
"test.f32pack": "947776b48136e31317b5ebd84be80fc8dd64377aab5740400b6c6ef686d9733f",
"heldout.f32pack": "425f4f0df90edddf9392a92de578ece680a78040ffef5742fbcb3caf65b4ee6b"
}
},
"feature_set": "raw",
"configs": "configs/iter3/a",
"models": ["instantaneous", "time-late", "causal-cnn", "mlp", "instantaneous-shuffled"],
"evaluate": {
"command": "tma-g2",
"split": "validation",
"compare_models": "experiments/20261016-i2d-g2/models"
}
}
19 changes: 19 additions & 0 deletions configs/iter3/a/time-late.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"learning_rate": 0.001,
"batch_size": 256,
"max_epochs": 150,
"patience": 6,
"max_wall_s": 172800,
"loss_weight_range": 1,
"loss_weight_course": 1,
"loss_weight_speed": 4,
"shuffle_labels": false,
"id": "bilstm-time-late",
"model_kind": "time_late",
"units": 128,
"train_seed": "1002",
"length_bucketing": true,
"tfjs_threads": 1,
"feature_set": "raw",
"layers": 2
}
29 changes: 26 additions & 3 deletions docs/SRD-annex-A.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,30 @@ training-progress demo (§9, PR preview apps) remains a separate item.

## A.7 Record of the realism check

To be filled in when A.5 has been done: date, datasets and indices looked at, findings, and the
decision whether Part B starts on the current generator or after generator changes.
**2026-10-17, the owner, in the viewer's PR preview (PR #51).** The owner looked at engagements
in the viewer; the record does not list the indices viewed.

**Findings.**

- **Platform motion looks right.** Tracks, turns and bearings are realistic.
- **The zig comes too late.** With both platforms on steady legs there is not enough information
for TMA, unless there is other context such as a predicted initial detection range or a speed
estimate from acoustic cues.
- **Operational practice.** An operator with no steady solution zigs again, and turning towards
the contact traditionally gives a better solution.

**What the gate data says.** Spec 007 research R1 tested these ideas against the seed-203 test
predictions.

- **Shorter first legs are worse at a fixed time after the zig.** The gates and the training loss
already exclude the pre-zig period.
- **Turning towards the contact helps only when the turn crosses the line of sight.**
- **Larger turns help every estimator.**

**Decision (decision 22, D4).** Part B starts after a generator change: lever B.2.1. There is a
second zig, both turns cross the line of sight, and the first zig keeps its timing. This is
iteration 3, spec 007, judged on fresh seed 204. The new engagements are shown in the viewer
before training.

## B. Phase 2: a further attempt at G2

Expand Down Expand Up @@ -261,4 +283,5 @@ Each is a separate, pre-registered experiment (B.3). None is chosen until A.7 is
- **D3** Which model folders ship by default (the 2c pair, or every gate's carried pair).
_Decided 2026-10-17: every gate's carried pair (iterations 1, 2, 2b and 2c), so one engagement
can be stepped through the project's history; controls remain selectable by path._
- **D4** After A.7: the first lever of B.2.
- **D4** After A.7: the first lever of B.2. _Decided 2026-10-17: B.2.1, a second zig with both
turns across the line of sight (spec 007)._
Loading
Loading