TiGa-RCE commited on
Commit
63c56ce
·
verified ·
1 Parent(s): ed1475b

Prepare public beta: in-page confirmations, feedback and safe GPU errors

Browse files

Keep existing Space access settings. Add recipe-import recovery, capability gates, GitHub issue drafts and bounded CUDA reference instructions; preserve simulator binaries.

Files changed (12) hide show
  1. BETA_TESTING.md +61 -94
  2. CUDA_REFERENCE.md +67 -0
  3. DATA_POLICY.md +2 -2
  4. FEEDBACK.md +4 -3
  5. FRONTEND_SOURCE.json +3 -3
  6. README.md +3 -1
  7. RELEASE_MANIFEST.json +11 -10
  8. TRACK_LIBRARY.md +2 -1
  9. app.py +11 -2
  10. client.js +17 -5
  11. hf-auth.js +1 -1
  12. index.html +36 -10
BETA_TESTING.md CHANGED
@@ -1,94 +1,61 @@
1
- # Small-group beta entry gate
2
-
3
- Current status: local UI/CPU testing is useful now. The refreshed runner interface
4
- requires hosted CPU and GPU qualification before inviting a broad audience. Do not describe it as a
5
- stable release or invite a broad audience to an unqualified build.
6
-
7
- ## Protected beta address
8
-
9
- The Space must remain Protected. Share https://tiga-rce-ald-tao-gpu.hf.space/
10
- with testers. Do not share https://huggingface.co/spaces/TiGa-RCE/ald-tao-gpu
11
- as the public app entry: the repository page is restricted and can display 404
12
- to non-collaborators. HF documents the `.hf.space` address as the public embed
13
- URL for Protected Spaces. Source access is not required for anonymous CPU use.
14
-
15
- CPU processing and complete browser usability are separate acceptance checks.
16
- The interface permits GPU on the direct entry when the server reports it available;
17
- HF still enforces quota. Direct GPU may use the limited anonymous allowance.
18
- Optional HF sign-in identifies the visitor through an official PKCE flow using
19
- openid/profile only. Tokens stay in page memory; no personal-token field or app
20
- password is introduced. Verify HF identity — no GPU checks the same queue/client
21
- credential path before a run. Hosted OAuth/account-quota acceptance must still
22
- be qualified for each build; a completed anonymous run is not account attribution.
23
-
24
- Before remote testers receive the expanded interface:
25
-
26
- 1. Freeze an allow-listed clean package and verify its manifest.
27
- 2. Deploy it to the existing Protected test Space without changing visibility.
28
- 3. Verify hosted CPU and one bounded GPU upload → run → download → deletion path
29
- using disposable, user-owned/generated files. Check HF allowance before GPU.
30
- 4. Confirm recipe persistence/import/export and batch completion/stop in both
31
- Chrome and the in-app browser. Safari/Firefox/Edge and mobile widths then form
32
- the human browser-zoo matrix, rather than a prerequisite for first local feedback.
33
- 5. Freeze the beta build ID in each report and keep the previous clean build for
34
- rollback. No invitation or repository permission change is part of this file.
35
-
36
- ## Tester workflow
37
-
38
- - Use CPU first; it does not acquire a GPU lease. GPU is explicit and has no
39
- automatic CPU fallback. A failed/quota-limited GPU call is not a successful run.
40
- - When HF rejects a staged GPU run for exhausted allowance, confirm the page
41
- gives CPU/account guidance without a personal-token prompt, reports that the
42
- ticket was deleted or is no longer pending, and allows another visitor to
43
- prepare a request immediately.
44
- - Check that a loaded recipe changes the echoed effective settings and generation
45
- results, not just the form. Reload and load again to check browser persistence.
46
- - Export/import a recipe backup. Imported data must not contain tracks, networks,
47
- tokens or arbitrary fields. Invalid JSON must leave the existing library intact.
48
- - Run a two-step carry-forward batch and a reset-to-original batch. Export the
49
- receipt; carry-forward input hash should match the preceding output hash.
50
- - Stop a longer batch. The current step may finish; later steps must not start.
51
- A stop/error must remain visibly different from a complete batch.
52
- - Create/edit/export/re-import a small network. Inspect or edit a known weight
53
- and verify the exported value. Modified artifacts must not inherit old scores.
54
- - Exercise default deletion, opt-in repeat download, manual delete, browser-only re-save,
55
- result expiry, invalid files, missing files and connection failure.
56
-
57
- Current supported worker contract: Formula steering/acceleration, speed/raycast
58
- sensors, 2–4 layers with at most 16 nodes per layer, and bounded execution.
59
- Inspection can show larger networks, but that is not permission to train them.
60
- The track map previews selected tile layouts and terrain, including custom uploads, but does not show collision geometry. Star challenges are explicitly
61
- not evaluated. No driving improvement or real-game/leaderboard parity follows
62
- from a successful artifact lifecycle test. A bucket and broad network support
63
- remain separate work.
64
-
65
- ## Reporting through GitHub
66
-
67
- Prepare read access plus issue filing, without write/merge/deployment privileges.
68
- The local issue-form draft is under `.github/ISSUE_TEMPLATE/runner_beta.yml`.
69
- It is not live until reviewed and committed/pushed to the intended repository.
70
- Repository invitations and collaborator permissions require named recipients;
71
- no invitations have been sent by this work.
72
-
73
- Reports should include browser/version, OS, build ID, CPU/GPU, reproduction steps,
74
- expected/actual behavior and a redacted screenshot or receipt. Do not attach a
75
- private trained network, a proprietary game-extracted track, tokens, job access
76
- keys, or full unrelated logs to an ordinary bug report. Use public/generated
77
- fixtures or describe the network shape and sensor/output contract.
78
-
79
- ## Local 2026-10-13 candidate (not deployed)
80
-
81
- Content-addressed build IDs now appear in capabilities and exported run/batch receipts;
82
- reports include upstream simulator revision and packaged binary SHA-256 values.
83
- A single run has its own receipt export. `/feedback` supplies a private-feedback
84
- template; no public issue destination or invitation is activated.
85
-
86
- The native train-scratch adapter requires positive mutation even when continuing an
87
- uploaded network. The form and server now reject zero before native dispatch.
88
- Any GPU dispatch error attempts to discard its pending ticket; a missing ticket
89
- is not evidence that an already dispatched run stopped.
90
-
91
- This candidate requires an explicitly approved deployment followed by hosted
92
- CPU/GPU and real-account checks before tester invitations. Local checks and
93
- training experiments against the previous live build are recorded outside this
94
- package in the sibling sprint receipt. They do not qualify this candidate as deployed.
 
1
+ # Public launch preflight
2
+
3
+ This document defines the hosted qualification checks for the release package.
4
+ Deployment status and build-specific acceptance belong in the release receipt;
5
+ results from one build do not qualify another build.
6
+
7
+ ## Intended launch topology
8
+
9
+ - The GPU Space app is publicly reachable at
10
+ https://tiga-rce-ald-tao-gpu.hf.space/.
11
+ - The GPU Space source repository remains restricted.
12
+ - The standalone CPU Space remains private. The public GPU app also offers an
13
+ anonymous CPU route, which does not acquire a GPU lease.
14
+
15
+ App access and source-repository access are separate. Do not send users to the
16
+ restricted source repository as the app entry point.
17
+
18
+ ## Required qualification for each deployed build
19
+
20
+ 1. Freeze the allow-listed package and verify its file manifest after source
21
+ and documentation changes.
22
+ 2. Deploy the package in the intended topology. Record the exact Space commit,
23
+ visibility, runtime stage, and build ID in the release receipt.
24
+ 3. On the deployed build, verify CPU upload → run → download → receipt
25
+ acknowledgement → server-side 404 using disposable generated files.
26
+ 4. Check included HF allowance before one bounded GPU fixture run. Verify the
27
+ CUDA report, candidate hash, download, and deletion; do not use paid overage.
28
+ A quota rejection must show CPU/account guidance and report whether the
29
+ pending ticket was discarded or already consumed.
30
+ 5. Verify optional HF sign-in and account attribution separately from an
31
+ anonymous GPU run. Do not ask users to paste personal access tokens.
32
+ 6. Exercise result expiry, opt-in repeat download, manual deletion, malformed
33
+ inputs, native failure, and temporary-file cleanup on the deployed build.
34
+ 7. Confirm recipe and batch behavior in supported browsers. A successful
35
+ artifact lifecycle check does not imply training quality or game parity.
36
+
37
+ ## Supported runner contract
38
+
39
+ The worker accepts Formula steering/acceleration networks with two outputs,
40
+ two to four layers, at most 16 nodes per layer, speed and raycast sensors, and
41
+ bounded execution settings. Unsupported network shapes or sensors are not
42
+ accepted for training. The track preview shows tile layout and terrain, not
43
+ collision geometry. Star challenges are not evaluated.
44
+
45
+ ## CUDA reference check
46
+
47
+ Use the deterministic generated network and bundled Replica B01 Oval track
48
+ described in `CUDA_REFERENCE.md`. Keep input hashes, candidate output hash,
49
+ settings, build ID, binary hashes, and deletion receipts together. The
50
+ single fixture cannot establish general CUDA/CPU equivalence, original-game
51
+ parity, or training quality.
52
+
53
+ ## Feedback
54
+
55
+ `/feedback` serves a template only; it does not submit a report. The configured
56
+ public destination is the [driving_sim_rs issue tracker](https://github.com/seeker-cyber-maker/driving_sim_rs/issues/new).
57
+ Reports should identify the app URL and build ID, browser and version, operating
58
+ system, CPU/GPU choice, reproduction steps, expected and observed behavior, and
59
+ a redacted receipt or screenshot. Do not attach tokens, job access keys, private
60
+ networks, proprietary tracks, or unrelated logs. Inspect receipts before
61
+ sharing; batch receipts also include recipe and batch names.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CUDA_REFERENCE.md ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Bounded CUDA reference check
2
+
3
+ This protocol checks that the hosted CUDA path can accept a known small input,
4
+ complete one bounded Formula training run, return a validated artifact, and
5
+ delete the server copy after verified download. It is not a test of broad game
6
+ parity, training quality, or general CPU/GPU equivalence.
7
+
8
+ ## Public-package fixture
9
+
10
+ - Track: `builtin_tracks/tracks/Replica B01 Oval.track`, catalog ID
11
+ `787f73f5-3b8c-5d95-b0e3-0016ed076970`, SHA-256
12
+ `0df5755150c145a35edc6c8693f429e5b9ff4b12210ba67cbfcb932c58faf158`.
13
+ - Network: deterministic, generated Formula network, shape `[6,4,2]`; one
14
+ speed sensor and five raycasts at `-60,-30,0,30,60` degrees with length 800;
15
+ outputs `[0,1]`; for zero-based indices, first-layer weight `W[i][j]` is
16
+ `(i+1)*(j+1)/100`, second-layer `W[i][j]` is `(i+1)*(j-0.5)/10`, and every
17
+ bias is zero. Serialize the JSON with sorted keys and compact separators.
18
+ - Generated network bytes: 554; SHA-256
19
+ `d9851cb1eee40dee78fb8c4492caeac8f130b5543f6d4551bb2175c13c579c3b`.
20
+
21
+ The following standard-library Python snippet is the complete network fixture
22
+ generator. It prints the exact bytes and digest; `Name` is intentionally absent
23
+ because the worker does not require it.
24
+
25
+ ```python
26
+ import hashlib
27
+ import json
28
+
29
+ def generated_network():
30
+ shape = [6, 4, 2]
31
+ first = [[(i + 1) * (j + 1) / 100 for j in range(4)] for i in range(6)]
32
+ second = [[(i + 1) * (j - 0.5) / 10 for j in range(2)] for i in range(4)]
33
+ value = {
34
+ "Type": 0,
35
+ "Outputs": [0, 1],
36
+ "Sensors": [
37
+ {"$type": "speed"},
38
+ *[
39
+ {"$type": "raycast", "Degrees": angle, "Length": 800}
40
+ for angle in (-60, -30, 0, 30, 60)
41
+ ],
42
+ ],
43
+ "NeuralNetwork": {
44
+ "Shape": shape,
45
+ "Weights": [first, second],
46
+ "Biases": [[0.0] * 4, [0.0, 0.0]],
47
+ },
48
+ }
49
+ return json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
50
+
51
+ network = generated_network()
52
+ print(len(network), hashlib.sha256(network).hexdigest())
53
+ ```
54
+
55
+ Pair these network bytes with the unchanged `Replica B01 Oval.track` file from
56
+ the package; do not rewrite the compressed track file.
57
+
58
+ - Bounded run settings: population 1, generation 1, 120 ticks, mutation 0.01,
59
+ seed 42, one worker, default total-score reward. These are a smoke fixture,
60
+ not a quality recommendation.
61
+
62
+ For a live CUDA qualification, first verify the target build ID and included HF
63
+ allowance. Run the fixture once through CPU and once through GPU on the same
64
+ build when CUDA quota permits. Record both reports, output SHA-256 values, binary
65
+ hashes, candidate validation, and deletion receipts. A mismatch is a finding to
66
+ investigate; do not widen the claim from this one track/network/settings tuple.
67
+ Do not spend paid overage. No live GPU job was run to prepare this protocol.
DATA_POLICY.md CHANGED
@@ -1,6 +1,6 @@
1
- # Data handling & deletion policy — private integration test
2
 
3
- Operator: TiGa (seeker-cyber-maker). Version: HF runner refresh 2026-10-12.
4
 
5
  Files are processed only for the requested job, not collected for our own
6
  training, analytics or public examples. Inputs remain in the browser until Run.
 
1
+ # Data handling & deletion policy — public-release candidate
2
 
3
+ Operator: TiGa (seeker-cyber-maker). Candidate version: 2026-10-13. This package is not a receipt that a hosted deployment or publication has occurred.
4
 
5
  Files are processed only for the requested job, not collected for our own
6
  training, analytics or public examples. Inputs remain in the browser until Run.
FEEDBACK.md CHANGED
@@ -1,8 +1,9 @@
1
- # Supervised beta feedback draft
2
 
3
  This form is a local template. It does not submit a message or upload files.
4
- Share a completed copy with the person who invited you, using the agreed private channel.
5
- A public issue destination has not been activated for this candidate.
 
6
 
7
  - App URL:
8
  - Build ID (from an exported run or batch receipt):
 
1
+ # Feedback report template
2
 
3
  This form is a local template. It does not submit a message or upload files.
4
+ Use the public issue form at https://github.com/seeker-cyber-maker/driving_sim_rs/issues/new
5
+ to report a problem. Review every attachment before posting. The issue tracker is
6
+ for product reports; do not include confidential material.
7
 
8
  - App URL:
9
  - Build ID (from an exported run or batch receipt):
FRONTEND_SOURCE.json CHANGED
@@ -1,10 +1,10 @@
1
  {
2
  "source": "Runner r14 single-engine browser UI; selected .track tile renderer added from simulator atlas",
3
  "source_sha256": "65d1e5f2c4e19b4951b828e82510005ab99a5270cf3dcb9f4eaa0fa1af8c40f8",
4
- "output_sha256": "315058a08c31ec512108473904f559087080edb684c02f4d594cbe5032c77791",
5
  "scope": "Frontend only; no engine, private data, track assets, API calls or storage copied",
6
- "status": "Local beta sprint candidate; not deployed",
7
  "track_atlas_sha256": "d97c8900e3e5fb17734b5bd61d1f4f4b196d9be30af9848e2e273ae7e4079ae4",
8
- "client_sha256": "cc18e5ce4405ccc7cecc3ab6cc6d7b2abddcc6cfac69f47825c546b8d7c1bcf1",
9
  "track_map_sha256": "ab5374aa49c7ccec90a787ee8c56cd71beb56b903bf2bbc322783e6f43c009f0"
10
  }
 
1
  {
2
  "source": "Runner r14 single-engine browser UI; selected .track tile renderer added from simulator atlas",
3
  "source_sha256": "65d1e5f2c4e19b4951b828e82510005ab99a5270cf3dcb9f4eaa0fa1af8c40f8",
4
+ "output_sha256": "aeb602565bf2da9d40cb6c3aae0b6e713b89e79a81557fed3e7f3dd8d16f195c",
5
  "scope": "Frontend only; no engine, private data, track assets, API calls or storage copied",
6
+ "status": "Public beta release candidate; local verification pending hosted qualification",
7
  "track_atlas_sha256": "d97c8900e3e5fb17734b5bd61d1f4f4b196d9be30af9848e2e273ae7e4079ae4",
8
+ "client_sha256": "b6c2e0439bc1e4cf55c5e751e497e842e1e911b229f7480d7b1e04dea246536f",
9
  "track_map_sha256": "ab5374aa49c7ccec90a787ee8c56cd71beb56b903bf2bbc322783e6f43c009f0"
10
  }
README.md CHANGED
@@ -8,8 +8,10 @@ hf_oauth: true
8
  hf_oauth_expiration_minutes: 60
9
  ---
10
 
11
- Protected CPU/ZeroGPU training beta. The native Formula runner uses the matched NVIDIA CUDA simulator/library pair from Zorro909/driving_sim_rs at commit d8ab74c25df70b6a86ed101080e33d5fb785ea6f. The newer b90b8af source was checked, but its published library targets AMD HIP and its added five-wheel snowmobile support is outside this Formula-only beta. Open the public app at https://tiga-rce-ald-tao-gpu.hf.space/; the source repository remains restricted. CPU runs anonymously. GPU is explicitly selected and subject to available HF allowance; optional HF sign-in uses the official OAuth flow and needs no personal token entry.
12
 
13
  The selected track map previews tile layout and asphalt/dirt/ice directly from the selected file. Twenty-eight reconstructed editor-format tracks are included. Settings-only recipes and batches save in the visitor's browser. Formula steering/acceleration networks within the current worker limits are supported.
14
 
15
  Application-controlled inputs and intermediate files are temporary. Outputs are held in memory for at most five minutes and deleted sooner after verified download by default; a single-run visitor may explicitly keep the result until that five-minute deadline. This is not a provider-wide zero-data-retention guarantee. Read DATA_POLICY.md and TRACK_LIBRARY.md. Upstream code and license: https://github.com/Zorro909/driving_sim_rs. Open upstream PRs 19 and 21 are not included.
 
 
 
8
  hf_oauth_expiration_minutes: 60
9
  ---
10
 
11
+ Public GPU app package. Its intended topology is a publicly reachable app at https://tiga-rce-ald-tao-gpu.hf.space/ with the GPU Space source repository restricted; the standalone CPU Space remains private. Deployment status and build-specific qualification are recorded in the release receipt. The native Formula runner uses the matched NVIDIA CUDA simulator/library pair from Zorro909/driving_sim_rs at commit d8ab74c25df70b6a86ed101080e33d5fb785ea6f. The newer b90b8af source was checked, but its published library targets AMD HIP and its added five-wheel snowmobile support is outside this Formula-only package. CPU runs anonymously. GPU is explicitly selected and subject to available HF allowance; optional HF sign-in uses the official OAuth flow and needs no personal token entry.
12
 
13
  The selected track map previews tile layout and asphalt/dirt/ice directly from the selected file. Twenty-eight reconstructed editor-format tracks are included. Settings-only recipes and batches save in the visitor's browser. Formula steering/acceleration networks within the current worker limits are supported.
14
 
15
  Application-controlled inputs and intermediate files are temporary. Outputs are held in memory for at most five minutes and deleted sooner after verified download by default; a single-run visitor may explicitly keep the result until that five-minute deadline. This is not a provider-wide zero-data-retention guarantee. Read DATA_POLICY.md and TRACK_LIBRARY.md. Upstream code and license: https://github.com/Zorro909/driving_sim_rs. Open upstream PRs 19 and 21 are not included.
16
+
17
+ Feedback issues: https://github.com/seeker-cyber-maker/driving_sim_rs/issues/new.
RELEASE_MANIFEST.json CHANGED
@@ -2,19 +2,20 @@
2
  "backend": "cpu+zerogpu",
3
  "files": {
4
  ".gitattributes": "c2ad16718fc51c888d32efc88c8f6e23ebe5fcf6f48e2954f896ba9770f24d72",
5
- "BETA_TESTING.md": "9d6447c876a2b2b1d3dbb7637858c79a4c669d8582d8cc2a6a335ea8cc4297c1",
6
  "BINARY_PROVENANCE.json": "1116e1757afc6c8629cb2d957e3d77380fe4f7e3186c3832d16a5a08abce6aa8",
7
- "DATA_POLICY.md": "0daf1b835bf829c0974e6b566cb5a605c6f52dba4517cfd54bed044e8eaf4264",
8
- "FEEDBACK.md": "17127b693eb52cccf1707be24b9bced82b77c0eec174b1f696cc9a3feb33dd67",
9
- "FRONTEND_SOURCE.json": "d9bf9402693f514956ec99432b6da2ccf73f17c7c26dd400123b6b9fb27bf31e",
 
10
  "GODOT_LICENSE": "fb2b3bb82c60f1e76841ba2122eb902f8e39c151476fe21ce10c9a05d1045304",
11
  "IMPORTER_BUILD_RECEIPT.json": "4ba5c533cc617105478e4d85c83f456a7dfa68b1388049624619871ba9e42e9d",
12
  "LICENSE": "53d5851b6440feeae36f72a5f57fcab37858c48b7084765ece4b0c54b9a28ed8",
13
- "README.md": "caf4a2d7ac5ab949f9d9b9f5adb75c598c5669b23a040751041fc0709dc9b79f",
14
  "STORAGE_BOUNDARY.md": "9a1ffb5854230690dba3f0895f9a8e570716de47be2c553de13a6892dd740015",
15
- "TRACK_LIBRARY.md": "7e4829c5e0933bfb317c21d7441ad0ba2ee00ab7f9f1635549452d504aa0777b",
16
  "UPSTREAM.json": "40a2f31fa9abc11e47e247c56909b93b587d1f5be524ab158b24f1f9eed93765",
17
- "app.py": "271c13c152e916c704c9c311a4a2800ef98e3787e6557be9eab8c851de0e1fb4",
18
  "app_release.py": "7482559c667a665fd3cb6aa8af6c78422a822b515e14589db8306f7fd4860a8b",
19
  "assets/formula_template.json": "80f745df82bb7be45af52785fd582460bcddc63c5a4e1d8375d85b3122db4c6b",
20
  "assets/track-atlas.json": "d97c8900e3e5fb17734b5bd61d1f4f4b196d9be30af9848e2e273ae7e4079ae4",
@@ -52,12 +53,12 @@
52
  "builtin_tracks/tracks/Replica R08 Demolition Derby.track": "6f11a9ffbcb477c9d19964566b1b612d2a94675605246e93e21f27bbbdb357ef",
53
  "builtin_tracks/tracks/Replica R09 Eau Noir.track": "3ec0a78478cca1a4f0d2d98ac81f40783928adf271b70f0c32d295a6da2cdaa9",
54
  "builtin_tracks/tracks/Replica The Champions Trial.track": "a3bfb76422affc774b44c739b0636d08acb901fa7615b9adcb52c069f79834e1",
55
- "client.js": "cc18e5ce4405ccc7cecc3ab6cc6d7b2abddcc6cfac69f47825c546b8d7c1bcf1",
56
  "delivery.py": "07453999c3831a16e8b1fcc2b4ae5fb28beb7d3952a3fae8de65d16eb6f11e5b",
57
  "gpu_tickets.py": "b81aadb5ed42cec9212d44da7e7bbb7b413c5bd517383cac0b49ec9b4d659c92",
58
- "hf-auth.js": "b59691ce2c517f193a5d5141538ebe61c961bb163cefa1fcf09b414c388984c1",
59
  "hf_identity.py": "6993d0238ada835606dbdc3275c17a3fe5a5cac5ad2156ab78230acf2d051eba",
60
- "index.html": "315058a08c31ec512108473904f559087080edb684c02f4d594cbe5032c77791",
61
  "requirements.txt": "9b532747e51bd052e82090ece860d6dafe027407f4512dc74272c148ea104c22",
62
  "server.py": "de98db424b96a4fbab7ce5b98ee42a94e846e2b3f317593e2346c5f87489ede8",
63
  "track-library.js": "351ec4c026b49420ce963f4310cb000d9e987fd28e6dac5ecd0176509340f6e6",
 
2
  "backend": "cpu+zerogpu",
3
  "files": {
4
  ".gitattributes": "c2ad16718fc51c888d32efc88c8f6e23ebe5fcf6f48e2954f896ba9770f24d72",
5
+ "BETA_TESTING.md": "c2d4bc2eff0f87144a862282f20585868c95533103221f5d42726242b2eeba21",
6
  "BINARY_PROVENANCE.json": "1116e1757afc6c8629cb2d957e3d77380fe4f7e3186c3832d16a5a08abce6aa8",
7
+ "CUDA_REFERENCE.md": "56a07521fc3d6acd11e3b3c110293d1a5ff710d1b7d7b93115e7b98d5ee7c256",
8
+ "DATA_POLICY.md": "38da17f8528ede1ef1850e98832f75ba35ebdc86a0cde87e27674eb0ff9c7a7f",
9
+ "FEEDBACK.md": "f95b8f6afbf4a360976c0edca5d52f5b3a0b668c9a944d4947b840a7c53a122a",
10
+ "FRONTEND_SOURCE.json": "4a6b79b38053a0db37eb36b6e7209a14ad7f9f505c373003b89f950fd0ff4654",
11
  "GODOT_LICENSE": "fb2b3bb82c60f1e76841ba2122eb902f8e39c151476fe21ce10c9a05d1045304",
12
  "IMPORTER_BUILD_RECEIPT.json": "4ba5c533cc617105478e4d85c83f456a7dfa68b1388049624619871ba9e42e9d",
13
  "LICENSE": "53d5851b6440feeae36f72a5f57fcab37858c48b7084765ece4b0c54b9a28ed8",
14
+ "README.md": "b41c614f81339e6392a018ba7133211a42e0416eb03544a6d54ac34efc7daa81",
15
  "STORAGE_BOUNDARY.md": "9a1ffb5854230690dba3f0895f9a8e570716de47be2c553de13a6892dd740015",
16
+ "TRACK_LIBRARY.md": "66b3656ed84f137d5b6116a0075731cd467818f555cca0969084aff67875319f",
17
  "UPSTREAM.json": "40a2f31fa9abc11e47e247c56909b93b587d1f5be524ab158b24f1f9eed93765",
18
+ "app.py": "162486abd6ac7c9281c8e75c15e33373e0fddba2baf6a799dfd54d333fd575d2",
19
  "app_release.py": "7482559c667a665fd3cb6aa8af6c78422a822b515e14589db8306f7fd4860a8b",
20
  "assets/formula_template.json": "80f745df82bb7be45af52785fd582460bcddc63c5a4e1d8375d85b3122db4c6b",
21
  "assets/track-atlas.json": "d97c8900e3e5fb17734b5bd61d1f4f4b196d9be30af9848e2e273ae7e4079ae4",
 
53
  "builtin_tracks/tracks/Replica R08 Demolition Derby.track": "6f11a9ffbcb477c9d19964566b1b612d2a94675605246e93e21f27bbbdb357ef",
54
  "builtin_tracks/tracks/Replica R09 Eau Noir.track": "3ec0a78478cca1a4f0d2d98ac81f40783928adf271b70f0c32d295a6da2cdaa9",
55
  "builtin_tracks/tracks/Replica The Champions Trial.track": "a3bfb76422affc774b44c739b0636d08acb901fa7615b9adcb52c069f79834e1",
56
+ "client.js": "b6c2e0439bc1e4cf55c5e751e497e842e1e911b229f7480d7b1e04dea246536f",
57
  "delivery.py": "07453999c3831a16e8b1fcc2b4ae5fb28beb7d3952a3fae8de65d16eb6f11e5b",
58
  "gpu_tickets.py": "b81aadb5ed42cec9212d44da7e7bbb7b413c5bd517383cac0b49ec9b4d659c92",
59
+ "hf-auth.js": "d5cf17b961d1f2efa9d4648d33ba86a148eff0d542c74a121710c4df35c92936",
60
  "hf_identity.py": "6993d0238ada835606dbdc3275c17a3fe5a5cac5ad2156ab78230acf2d051eba",
61
+ "index.html": "aeb602565bf2da9d40cb6c3aae0b6e713b89e79a81557fed3e7f3dd8d16f195c",
62
  "requirements.txt": "9b532747e51bd052e82090ece860d6dafe027407f4512dc74272c148ea104c22",
63
  "server.py": "de98db424b96a4fbab7ce5b98ee42a94e846e2b3f317593e2346c5f87489ede8",
64
  "track-library.js": "351ec4c026b49420ce963f4310cb000d9e987fd28e6dac5ecd0176509340f6e6",
TRACK_LIBRARY.md CHANGED
@@ -23,7 +23,8 @@ input. Upload a custom track remains available. Selecting a built-in clears the
23
  custom file selection; choosing an upload switches back to upload mode. Built-in
24
  selection survives completed runs in this page, but is not written to cookies,
25
  localStorage, recipes, or account storage. Network selection is still explicit.
26
- The renderer remains a placeholder.
 
27
 
28
  The public app exposes only catalog-listed IDs and hash-checked replica bytes.
29
  No directory browsing, caller-selected paths, remote track URLs or extraction
 
23
  custom file selection; choosing an upload switches back to upload mode. Built-in
24
  selection survives completed runs in this page, but is not written to cookies,
25
  localStorage, recipes, or account storage. Network selection is still explicit.
26
+ The canvas preview shows tile layout and asphalt, dirt, and ice terrain. It does
27
+ not show collision geometry or prove that the replica matches native physics.
28
 
29
  The public app exposes only catalog-listed IDs and hash-checked replica bytes.
30
  No directory browsing, caller-selected paths, remote track URLs or extraction
app.py CHANGED
@@ -17,6 +17,7 @@ from fastapi.responses import FileResponse,JSONResponse
17
  from app_release import ROOT,cpu_worker,verify_package
18
  from gpu_tickets import InputTickets
19
  from server import create_app
 
20
  from worker import run_native,track_input,network_input
21
  from training_config import validate_config
22
  from build_info import build_identity
@@ -90,8 +91,16 @@ def build_app(cuda_call):
90
  if not admission.acquire(blocking=False):raise ValueError('GPU worker busy')
91
  try:
92
  track,network,automatic,*config=tickets.take(ticket)
93
- payload,report=cuda_call(track,network,*config)
94
- return {**base.state.results.publish(payload,automatic),'backend':'gpu','report':report}
 
 
 
 
 
 
 
 
95
  finally:admission.release()
96
 
97
  @app.api(name='visitor_identity',api_visibility='undocumented')
 
17
  from app_release import ROOT,cpu_worker,verify_package
18
  from gpu_tickets import InputTickets
19
  from server import create_app
20
+ from delivery import CapacityExceeded
21
  from worker import run_native,track_input,network_input
22
  from training_config import validate_config
23
  from build_info import build_identity
 
91
  if not admission.acquire(blocking=False):raise ValueError('GPU worker busy')
92
  try:
93
  track,network,automatic,*config=tickets.take(ticket)
94
+ try:
95
+ payload,report=cuda_call(track,network,*config)
96
+ return {**base.state.results.publish(payload,automatic),'backend':'gpu','report':report}
97
+ except CapacityExceeded:
98
+ raise ValueError('Result capacity reached; no result was published.') from None
99
+ except Exception:
100
+ # Gradio logs callback exceptions. Keep native/provider details out
101
+ # of both logs and the browser-facing error while preserving the
102
+ # UI's separate pending-ticket cleanup check.
103
+ raise ValueError('GPU processing failed; no result was published.') from None
104
  finally:admission.release()
105
 
106
  @app.api(name='visitor_identity',api_visibility='undocumented')
client.js CHANGED
@@ -5,6 +5,7 @@ let busy = false;
5
  let localResult = null;
6
  let capabilitiesReady = false;
7
  let gpuTransport = null;
 
8
  let gradioClient = null;
9
  let stagedNetwork = null;
10
  let batchActive = false;
@@ -29,7 +30,7 @@ function buttons() {
29
  window.HFAuth?.setBusy(busy || !!job);
30
  window.setTrainingBusy?.(busy || !!job);
31
  window.setEditorBusy?.(busy || !!job);
32
- $('run').disabled = busy || !!job || !capabilitiesReady || !networkSource();
33
  $('backend').disabled = busy || !!job || !capabilitiesReady;
34
  $('download').disabled = busy || !job;
35
  $('erase').disabled = busy || !job;
@@ -38,11 +39,20 @@ function buttons() {
38
  $('forgetLocal').disabled = busy || !localResult;
39
  $('track').disabled=busy || !!job;
40
  $('network').disabled=busy || !!job;
41
- if($('batch_start'))$('batch_start').disabled=busy || !!job || !capabilitiesReady;
42
  if($('batch_stop'))$('batch_stop').disabled=!batchActive || batchStopRequested;
43
  if($('run_export'))$('run_export').disabled=busy || !runReceipt;
44
  if($('batch_export'))$('batch_export').disabled=busy || !batchReceipt;
45
  }
 
 
 
 
 
 
 
 
 
46
  function saveLocal() {
47
  if(!localResult)return;
48
  const link=document.createElement('a'),url=URL.createObjectURL(localResult);
@@ -197,7 +207,7 @@ $('download').onclick = async () => {
197
  finally {busy=false; buttons();}
198
  };
199
  $('erase').onclick=async()=>{
200
- if(!confirm('Delete remaining server-side result and revoke download access? Local files are unaffected.'))return;
201
  busy=true; buttons();
202
  try {await request('/api/jobs/'+job.id,{method:'DELETE',headers:headers()}); job=null; say('Server result deleted. Local files are unaffected.');}
203
  catch(error){say(error.message);}
@@ -286,8 +296,10 @@ async function connect(){
286
  $('buildLabel').textContent='Build: '+(appBuild?.build_id?.slice(0,25)??'Unavailable — include the app URL in feedback');
287
  $('buildLabel').title=appBuild?.build_id??'';
288
  }
 
289
  gpuTransport=capabilities.gpu_transport??null;
290
- $('gpuOption').disabled=!capabilities.gpu;
 
291
  $('backendNote').textContent=capabilities.gpu?(hostedStandalone?'CPU is anonymous. GPU may use a limited anonymous allowance or your optional HF account allowance.':'CPU is anonymous. GPU requires an available HF allowance.'):'CPU training is ready; no sign-in or GPU allowance needed.';
292
  $('gpuAccount').hidden=!capabilities.gpu;
293
  $('computeBlock').hidden=!capabilities.gpu;
@@ -295,7 +307,7 @@ async function connect(){
295
  say('Connected. Choose your track and network, then run the test.');
296
  } catch(error) {
297
  $('connectionRecovery').hidden=false;
298
- capabilitiesReady=false; buttons();
299
  $('backendNote').textContent='Connection unavailable — training is disabled.';
300
  say('Could not reach this app. Retry the public beta app address: https://tiga-rce-ald-tao-gpu.hf.space/. The HF repository page is restricted in Protected mode. No run was started.');
301
  } finally {clearTimeout(timeout);$('retry').disabled=false;}
 
5
  let localResult = null;
6
  let capabilitiesReady = false;
7
  let gpuTransport = null;
8
+ let gpuAvailable = false;
9
  let gradioClient = null;
10
  let stagedNetwork = null;
11
  let batchActive = false;
 
30
  window.HFAuth?.setBusy(busy || !!job);
31
  window.setTrainingBusy?.(busy || !!job);
32
  window.setEditorBusy?.(busy || !!job);
33
+ $('run').disabled = busy || !!job || !capabilitiesReady || !networkSource() || ($('backend').value==='gpu'&&!gpuAvailable);
34
  $('backend').disabled = busy || !!job || !capabilitiesReady;
35
  $('download').disabled = busy || !job;
36
  $('erase').disabled = busy || !job;
 
39
  $('forgetLocal').disabled = busy || !localResult;
40
  $('track').disabled=busy || !!job;
41
  $('network').disabled=busy || !!job;
42
+ if($('batch_start'))$('batch_start').disabled=busy || !!job || !capabilitiesReady || ($('backend').value==='gpu'&&!gpuAvailable);
43
  if($('batch_stop'))$('batch_stop').disabled=!batchActive || batchStopRequested;
44
  if($('run_export'))$('run_export').disabled=busy || !runReceipt;
45
  if($('batch_export'))$('batch_export').disabled=busy || !batchReceipt;
46
  }
47
+ $('backend').addEventListener('change',buttons);
48
+ const feedbackLink=$('feedbackLink');
49
+ feedbackLink?.addEventListener('click',()=>{
50
+ const url=new URL('https://github.com/seeker-cyber-maker/driving_sim_rs/issues/new');
51
+ const browser=navigator.userAgent||'Not reported';
52
+ const body=['Build: '+(appBuild?.build_id??'Not reported'),'Browser and OS: '+browser,'Selected backend: '+$('backend').value.toUpperCase(),'','What happened?'].join('\n');
53
+ url.searchParams.set('title','ALD beta problem report');url.searchParams.set('body',body);
54
+ feedbackLink.href=url.href;
55
+ });
56
  function saveLocal() {
57
  if(!localResult)return;
58
  const link=document.createElement('a'),url=URL.createObjectURL(localResult);
 
207
  finally {busy=false; buttons();}
208
  };
209
  $('erase').onclick=async()=>{
210
+ if(!await window.appConfirm('Delete remaining server-side result and revoke download access? Local files are unaffected.',{heading:'Delete server result',confirmLabel:'Delete result',danger:true}))return;
211
  busy=true; buttons();
212
  try {await request('/api/jobs/'+job.id,{method:'DELETE',headers:headers()}); job=null; say('Server result deleted. Local files are unaffected.');}
213
  catch(error){say(error.message);}
 
296
  $('buildLabel').textContent='Build: '+(appBuild?.build_id?.slice(0,25)??'Unavailable — include the app URL in feedback');
297
  $('buildLabel').title=appBuild?.build_id??'';
298
  }
299
+ gpuAvailable=capabilities.gpu===true;
300
  gpuTransport=capabilities.gpu_transport??null;
301
+ $('gpuOption').disabled=!gpuAvailable;
302
+ if(!gpuAvailable)$('backend').value='cpu';
303
  $('backendNote').textContent=capabilities.gpu?(hostedStandalone?'CPU is anonymous. GPU may use a limited anonymous allowance or your optional HF account allowance.':'CPU is anonymous. GPU requires an available HF allowance.'):'CPU training is ready; no sign-in or GPU allowance needed.';
304
  $('gpuAccount').hidden=!capabilities.gpu;
305
  $('computeBlock').hidden=!capabilities.gpu;
 
307
  say('Connected. Choose your track and network, then run the test.');
308
  } catch(error) {
309
  $('connectionRecovery').hidden=false;
310
+ capabilitiesReady=false;gpuAvailable=false;gpuTransport=null;$('backend').value='cpu';$('gpuOption').disabled=true;buttons();
311
  $('backendNote').textContent='Connection unavailable — training is disabled.';
312
  say('Could not reach this app. Retry the public beta app address: https://tiga-rce-ald-tao-gpu.hf.space/. The HF repository page is restricted in Protected mode. No run was started.');
313
  } finally {clearTimeout(timeout);$('retry').disabled=false;}
hf-auth.js CHANGED
@@ -40,7 +40,7 @@
40
  node('hfSignIn').onclick=async()=>{
41
  if(locked||exchanging||!ready)return;
42
  if((node('track').files.length||node('network').files.length) &&
43
- !confirm('HF sign-in navigates away. Selected files and unsaved editor changes are lost; saved recipes remain. Continue, then reselect files?'))return;
44
  exchanging=true;render();
45
  try{
46
  const state={};
 
40
  node('hfSignIn').onclick=async()=>{
41
  if(locked||exchanging||!ready)return;
42
  if((node('track').files.length||node('network').files.length) &&
43
+ !(await window.appConfirm('HF sign-in navigates away. Selected files and unsaved editor changes are lost; saved recipes remain. Continue, then reselect files?',{heading:'Continue to HF sign-in',confirmLabel:'Continue to sign in'})))return;
44
  exchanging=true;render();
45
  try{
46
  const state={};
index.html CHANGED
@@ -1,8 +1,34 @@
1
  <!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>AI Learns To Drive</title>
2
  <style>
3
  :root{color-scheme:dark;font:16px system-ui;background:#0c1420;color:#e5edf5}body{max-width:1400px;margin:auto;padding:22px}h1{font-size:32px;margin:0}h2{font-size:21px;margin:0 0 14px}small,.hint{color:#a2b4c8;line-height:1.6}.grid{display:grid;grid-template-columns:minmax(360px,.9fr) minmax(0,1.25fr);gap:24px}.card{background:#152233;border:1px solid #2a3d52;border-radius:16px;padding:22px;margin-top:20px}label{display:block;margin:12px 0 5px}input,select,button{font:inherit;background:#0c1725;color:#e5edf5;border:1px solid #49627c;border-radius:7px;padding:9px}input[type=number],select{width:100%;box-sizing:border-box}input[type=file]{max-width:100%;box-sizing:border-box}button{cursor:pointer}button.primary{background:#62dfb1;color:#102522;font-weight:700}button:disabled{opacity:.4;cursor:default}.row{display:grid;grid-template-columns:1fr 1fr;gap:15px}.reward{display:grid;grid-template-columns:1.7fr 1fr 70px 30px;gap:5px;margin:8px 0}.reward input{width:70px;box-sizing:border-box}a{color:#7ce4c3}canvas{width:100%;background:#09111b;border-radius:10px}#status{white-space:pre-wrap;line-height:1.7}table{width:100%;text-align:left;border-collapse:collapse;font-size:14px}td,th{padding:9px;border-bottom:1px solid #2a3d52}.pill{display:inline-block;background:#284539;border-radius:20px;padding:5px 12px;color:#91efc6;margin:6px 0}#error{color:#ffb5a2;white-space:pre-wrap}.toolbar{display:flex;gap:12px;align-items:center;flex-wrap:wrap;padding-bottom:16px;border-bottom:1px solid #2a3d52;margin-bottom:15px}.run-card{position:sticky;top:20px}details{margin-top:18px;border-top:1px solid #2a3d52;padding-top:14px}summary{cursor:pointer;font-weight:600;color:#c6d9e9}details[open]>summary{margin-bottom:12px}[hidden]{display:none!important}#map_note{font-size:14px}#runs{max-height:380px;overflow:auto}.table-scroll{overflow:auto}@media(max-width:850px){.grid{grid-template-columns:minmax(0,1fr)}.card{min-width:0}.run-card{position:static}body{padding:16px}}
4
- .tabs{display:flex;flex-wrap:wrap;gap:10px;margin:20px 0}.tabs button[aria-selected=true]{background:#62dfb1;color:#102522}.library{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:20px}.library input[type=text]{width:100%;box-sizing:border-box}.queue-row{display:flex;gap:8px;margin:8px 0}.queue-row select{flex:1}.pane{min-width:0}.map{min-height:300px;background:#09111b;border-radius:12px;padding:10px}.map canvas{display:block;width:100%;height:auto;max-height:320px;object-fit:contain}.map-legend{display:flex;gap:18px;flex-wrap:wrap;margin:10px 0;color:#c6d9e9;font-size:14px}pre{white-space:pre-wrap;overflow-wrap:anywhere}input[type=text]{width:100%;box-sizing:border-box}#libraries .row label>input,#libraries .row label>select{display:block;margin-top:6px}#editor_parameters input{max-width:240px} .eyebrow{color:#62dfb1;font-size:12px;letter-spacing:.18em;font-weight:800}.intro{font-size:19px;color:#b9cddd;max-width:820px;margin:10px 0 0}header{padding:12px 0 4px}header h1{font-size:clamp(30px,4vw,45px);letter-spacing:-.03em;margin-top:7px}.about{max-width:850px;font-size:14px;margin-top:15px}.about p{line-height:1.65}.card{box-shadow:0 10px 35px #050a1280}.tabs{position:sticky;top:0;z-index:3;background:#0c1420eF;padding:12px 0;border-bottom:1px solid #2a3d52}.tabs button{border-radius:12px;padding:11px 15px}.map-status{margin:7px 0;color:#dbe9f1;font-size:14px}#run{background:#62dfb1;color:#102522;font-weight:800;width:100%;margin-top:12px;padding:13px}.privacy-note{font-size:13px;line-height:1.5;color:#a2b4c8}.keep-result{display:flex;gap:10px;align-items:flex-start;padding:12px;border:1px solid #3b566b;border-radius:10px;background:#102030;line-height:1.4}.keep-result input{margin:3px 0 0;flex:none} .terrain{display:inline-block;width:11px;height:11px;border-radius:3px;margin-right:4px;background:#607e96}.terrain.dirt{background:#b89561}.terrain.ice{background:#b9d9ed}.terrain.start{background:#66e5aa}@media(max-width:850px){.map{min-height:0}}</style>
5
- <header><div class="eyebrow">AI LEARNS TO DRIVE · TRAINING LAB</div><h1>Train, inspect, repeat.</h1><p class="intro">Pick a track and network, run a bounded training session, and keep your recipes in this browser.</p><details class="about"><summary>About this beta and its data handling</summary><p>Simulator by <a href="https://github.com/Zorro909/driving_sim_rs">Zorro909</a>; interface and hosting by <a href="https://github.com/seeker-cyber-maker/driving_sim_rs">TiGa</a>. Inspired by the <a href="https://store.steampowered.com/app/3312030/AI_Learns_To_Drive/">game by ActivationFunction</a>. Built-in tracks are reconstructed replicas. Star challenges are not evaluated here.</p><p>Uploaded files are used for the requested run. Results are held briefly in application memory and deleted after verified download or expiry. Browser recipes contain settings only. <a href="/policy" target="_blank" rel="noopener">Read the data policy</a>.</p></details><p><a href="/feedback" target="_blank" rel="noopener">Beta feedback template</a> · Export a run receipt when reporting a problem.</p><p id="buildLabel" class="hint">Build: connecting…</p></header>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  <nav class="tabs" aria-label="Lab sections"><button data-tab="drive" aria-selected="true">1 · Drive & view</button><button data-tab="training" aria-selected="false">2 · Training runs & presets</button><button data-tab="batch" aria-selected="false">3 · Batches</button><button data-tab="editor" aria-selected="false">4 · Network editor</button></nav><div id="drive" class="pane"><div class="grid"><section class="card"><h2>Simulation</h2><button id="run" disabled>Run training</button><p class="hint">Select or create a network below to enable training. Review settings on tab 2 before running.</p><p>Supports Formula steering/acceleration, speed/raycast sensors, and 2–4 layers of up to 16 nodes.</p>
7
  <p id="computeBlock"><label for="backend">Compute</label><select id="backend" disabled><option value="cpu">CPU — no GPU allowance</option><option id="gpuOption" value="gpu" disabled>GPU — uses GPU allowance</option></select></p><p id="backendNote" class="hint">Checking available backends…</p>
8
  <div id="connectionRecovery" hidden><button id="retry" type="button">Reconnect to app</button><p class="hint">Checks server availability. Does not restart training, sign you in, or reset GPU allowance.</p></div>
@@ -249,10 +275,10 @@
249
  window.getBatchPlan=()=>window.BatchTraining.plan(read(),q('batch_slot').value);
250
  const action=fn=>async()=>{try{await fn();}catch(error){message('Not completed: '+error.message);}};
251
  q('recipe_apply_draft').onclick=action(()=>{applySettings(JSON.parse(q('recipe_draft').value));q('preset').value='custom';q('reward_preset').value='custom';message('Draft applied to the training form. No run started.');});
252
- q('recipe_save').onclick=action(()=>{
253
  const book=read(),slot=q('recipe_slot').value;
254
  const next=api.validate({...book,recipes:[...book.recipes.filter(r=>r.slot!==slot),{slot,name:q('recipe_name').value,settings:currentSettings()}]});
255
- if(book.recipes.some(r=>r.slot===slot)&&!confirm('Replace recipe slot '+slot+' on this device?'))return;
256
  store.save(next);message('Saved slot '+slot+' in this browser.');
257
  });
258
  q('recipe_load').onclick=action(()=>{
@@ -266,14 +292,14 @@
266
  message('Recipe export requested. No tracks, networks, results or credentials are included by the application.');
267
  });
268
  q('recipe_import').onchange=action(async()=>{
269
- const file=q('recipe_import').files[0];if(!file)return;if(file.size>32768)throw Error('Recipe file exceeds 32 KB');
270
  const book=api.parse(await file.text());
271
- if(!confirm('Replace this browser’s recipe library with '+book.recipes.length+' imported recipes? Export a backup first if needed.'))return;
272
  if(!store)throw Error('Browser storage unavailable');store.save(book);
273
  message('Imported '+book.recipes.length+' recipes locally. No run was started.');q('recipe_import').value='';
274
  });
275
- q('recipe_clear').onclick=action(()=>{
276
- if(!confirm('Clear saved recipes and batches for this app on this device? Server run data and downloaded files are unaffected.'))return;
277
  if(!store)throw Error('Browser storage unavailable');store.clear();message('Local recipes cleared. Server data and downloads are unaffected.');
278
  });
279
  q('batch_save').disabled=false;q('batch_load').disabled=false;
@@ -282,11 +308,11 @@
282
  q('queue_add').onclick=()=>{
283
  const text=q('batch_recipe_sequence').value.trim();q('batch_recipe_sequence').value=text?text+', '+q('batch_add_recipe').value:q('batch_add_recipe').value;
284
  };
285
- q('batch_save').onclick=action(()=>{
286
  const book=read(),slot=q('batch_slot').value;
287
  const runs=q('batch_recipe_sequence').value.split(',').map(s=>s.trim().toUpperCase());
288
  const next=api.validate({...book,batches:[...book.batches.filter(b=>b.slot!==slot),{slot,name:q('batch_name').value,carry:q('carry').checked,runs}]});
289
- if(book.batches.some(b=>b.slot===slot)&&!confirm('Replace saved batch slot '+slot+'?'))return;
290
  store.save(next);message('Batch '+slot+' saved locally.');
291
  });
292
  q('batch_load').onclick=action(()=>{
 
1
  <!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>AI Learns To Drive</title>
2
  <style>
3
  :root{color-scheme:dark;font:16px system-ui;background:#0c1420;color:#e5edf5}body{max-width:1400px;margin:auto;padding:22px}h1{font-size:32px;margin:0}h2{font-size:21px;margin:0 0 14px}small,.hint{color:#a2b4c8;line-height:1.6}.grid{display:grid;grid-template-columns:minmax(360px,.9fr) minmax(0,1.25fr);gap:24px}.card{background:#152233;border:1px solid #2a3d52;border-radius:16px;padding:22px;margin-top:20px}label{display:block;margin:12px 0 5px}input,select,button{font:inherit;background:#0c1725;color:#e5edf5;border:1px solid #49627c;border-radius:7px;padding:9px}input[type=number],select{width:100%;box-sizing:border-box}input[type=file]{max-width:100%;box-sizing:border-box}button{cursor:pointer}button.primary{background:#62dfb1;color:#102522;font-weight:700}button:disabled{opacity:.4;cursor:default}.row{display:grid;grid-template-columns:1fr 1fr;gap:15px}.reward{display:grid;grid-template-columns:1.7fr 1fr 70px 30px;gap:5px;margin:8px 0}.reward input{width:70px;box-sizing:border-box}a{color:#7ce4c3}canvas{width:100%;background:#09111b;border-radius:10px}#status{white-space:pre-wrap;line-height:1.7}table{width:100%;text-align:left;border-collapse:collapse;font-size:14px}td,th{padding:9px;border-bottom:1px solid #2a3d52}.pill{display:inline-block;background:#284539;border-radius:20px;padding:5px 12px;color:#91efc6;margin:6px 0}#error{color:#ffb5a2;white-space:pre-wrap}.toolbar{display:flex;gap:12px;align-items:center;flex-wrap:wrap;padding-bottom:16px;border-bottom:1px solid #2a3d52;margin-bottom:15px}.run-card{position:sticky;top:20px}details{margin-top:18px;border-top:1px solid #2a3d52;padding-top:14px}summary{cursor:pointer;font-weight:600;color:#c6d9e9}details[open]>summary{margin-bottom:12px}[hidden]{display:none!important}#map_note{font-size:14px}#runs{max-height:380px;overflow:auto}.table-scroll{overflow:auto}@media(max-width:850px){.grid{grid-template-columns:minmax(0,1fr)}.card{min-width:0}.run-card{position:static}body{padding:16px}}
4
+ .tabs{display:flex;flex-wrap:wrap;gap:10px;margin:20px 0}.tabs button[aria-selected=true]{background:#62dfb1;color:#102522}.library{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:20px}.library input[type=text]{width:100%;box-sizing:border-box}.queue-row{display:flex;gap:8px;margin:8px 0}.queue-row select{flex:1}.pane{min-width:0}.map{min-height:300px;background:#09111b;border-radius:12px;padding:10px}.map canvas{display:block;width:100%;height:auto;max-height:320px;object-fit:contain}.map-legend{display:flex;gap:18px;flex-wrap:wrap;margin:10px 0;color:#c6d9e9;font-size:14px}pre{white-space:pre-wrap;overflow-wrap:anywhere}input[type=text]{width:100%;box-sizing:border-box}#libraries .row label>input,#libraries .row label>select{display:block;margin-top:6px}#editor_parameters input{max-width:240px} .eyebrow{color:#62dfb1;font-size:12px;letter-spacing:.18em;font-weight:800}.intro{font-size:19px;color:#b9cddd;max-width:820px;margin:10px 0 0}header{padding:12px 0 4px}header h1{font-size:clamp(30px,4vw,45px);letter-spacing:-.03em;margin-top:7px}.about{max-width:850px;font-size:14px;margin-top:15px}.about p{line-height:1.65}.card{box-shadow:0 10px 35px #050a1280}.tabs{position:sticky;top:0;z-index:3;background:#0c1420eF;padding:12px 0;border-bottom:1px solid #2a3d52}.tabs button{border-radius:12px;padding:11px 15px}.map-status{margin:7px 0;color:#dbe9f1;font-size:14px}#run{background:#62dfb1;color:#102522;font-weight:800;width:100%;margin-top:12px;padding:13px}.privacy-note{font-size:13px;line-height:1.5;color:#a2b4c8}.keep-result{display:flex;gap:10px;align-items:flex-start;padding:12px;border:1px solid #3b566b;border-radius:10px;background:#102030;line-height:1.4}.keep-result input{margin:3px 0 0;flex:none} .terrain{display:inline-block;width:11px;height:11px;border-radius:3px;margin-right:4px;background:#607e96}.terrain.dirt{background:#b89561}.terrain.ice{background:#b9d9ed}.terrain.start{background:#66e5aa}@media(max-width:850px){.map{min-height:0}}dialog#appConfirm{box-sizing:border-box;width:min(92vw,440px);border:1px solid #54718d;border-radius:16px;padding:24px;background:#152233;color:#e5edf5;box-shadow:0 24px 80px #000b}dialog#appConfirm::backdrop{background:#02060bcc}#appConfirm h2{margin:0 0 10px}#appConfirm p{line-height:1.55;color:#c4d3e1;white-space:pre-wrap}#appConfirm .actions{display:flex;justify-content:flex-end;gap:10px;flex-wrap:wrap;margin-top:22px}#appConfirm button{min-width:100px}#appConfirm button.danger{background:#8e3b37;color:#fff;border-color:#d7837e}</style>
5
+ <dialog id="appConfirm" aria-labelledby="appConfirmTitle" aria-describedby="appConfirmMessage">
6
+ <h2 id="appConfirmTitle">Confirm action</h2>
7
+ <p id="appConfirmMessage"></p>
8
+ <div class="actions"><button id="appConfirmCancel" type="button">Cancel</button><button id="appConfirmAccept" type="button">Continue</button></div>
9
+ </dialog>
10
+ <script>
11
+ (() => {
12
+ const dialog=document.getElementById('appConfirm'),title=document.getElementById('appConfirmTitle');
13
+ const message=document.getElementById('appConfirmMessage'),cancel=document.getElementById('appConfirmCancel');
14
+ const accept=document.getElementById('appConfirmAccept');
15
+ let finish=null,returnFocus=null;
16
+ dialog.addEventListener('close',()=>{
17
+ const resolve=finish;finish=null;
18
+ if(resolve)resolve(dialog.returnValue==='confirm');
19
+ if(returnFocus?.isConnected)returnFocus.focus();returnFocus=null;
20
+ });
21
+ cancel.addEventListener('click',()=>dialog.close('cancel'));
22
+ accept.addEventListener('click',()=>dialog.close('confirm'));
23
+ window.appConfirm=(text,{heading='Confirm action',confirmLabel='Continue',cancelLabel='Cancel',danger=false}={})=>{
24
+ if(finish) return Promise.resolve(false);
25
+ returnFocus=document.activeElement;title.textContent=heading;message.textContent=text;
26
+ accept.textContent=confirmLabel;cancel.textContent=cancelLabel;accept.classList.toggle('danger',danger);
27
+ return new Promise(resolve=>{finish=resolve;dialog.showModal();cancel.focus();});
28
+ };
29
+ })();
30
+ </script>
31
+ <header><div class="eyebrow">AI LEARNS TO DRIVE · TRAINING LAB</div><h1>Train, inspect, repeat.</h1><p class="intro">Pick a track and network, run a bounded training session, and keep your recipes in this browser.</p><details class="about"><summary>About this beta and its data handling</summary><p>Simulator by <a href="https://github.com/Zorro909/driving_sim_rs">Zorro909</a>; interface and hosting by <a href="https://github.com/seeker-cyber-maker/driving_sim_rs">TiGa</a>. Inspired by the <a href="https://store.steampowered.com/app/3312030/AI_Learns_To_Drive/">game by ActivationFunction</a>. Built-in tracks are reconstructed replicas. Star challenges are not evaluated here.</p><p>Uploaded files are used for the requested run. Results are held briefly in application memory and deleted after verified download or expiry. Browser recipes contain settings only. <a href="/policy" target="_blank" rel="noopener">Read the data policy</a>.</p></details><p><a id="feedbackLink" href="https://github.com/seeker-cyber-maker/driving_sim_rs/issues/new" target="_blank" rel="noopener">Report a problem on GitHub</a> · Opens an editable draft and never submits it. Review details before posting; export a run receipt separately if it helps explain the issue.</p><p id="buildLabel" class="hint">Build: connecting…</p></header>
32
  <nav class="tabs" aria-label="Lab sections"><button data-tab="drive" aria-selected="true">1 · Drive & view</button><button data-tab="training" aria-selected="false">2 · Training runs & presets</button><button data-tab="batch" aria-selected="false">3 · Batches</button><button data-tab="editor" aria-selected="false">4 · Network editor</button></nav><div id="drive" class="pane"><div class="grid"><section class="card"><h2>Simulation</h2><button id="run" disabled>Run training</button><p class="hint">Select or create a network below to enable training. Review settings on tab 2 before running.</p><p>Supports Formula steering/acceleration, speed/raycast sensors, and 2–4 layers of up to 16 nodes.</p>
33
  <p id="computeBlock"><label for="backend">Compute</label><select id="backend" disabled><option value="cpu">CPU — no GPU allowance</option><option id="gpuOption" value="gpu" disabled>GPU — uses GPU allowance</option></select></p><p id="backendNote" class="hint">Checking available backends…</p>
34
  <div id="connectionRecovery" hidden><button id="retry" type="button">Reconnect to app</button><p class="hint">Checks server availability. Does not restart training, sign you in, or reset GPU allowance.</p></div>
 
275
  window.getBatchPlan=()=>window.BatchTraining.plan(read(),q('batch_slot').value);
276
  const action=fn=>async()=>{try{await fn();}catch(error){message('Not completed: '+error.message);}};
277
  q('recipe_apply_draft').onclick=action(()=>{applySettings(JSON.parse(q('recipe_draft').value));q('preset').value='custom';q('reward_preset').value='custom';message('Draft applied to the training form. No run started.');});
278
+ q('recipe_save').onclick=action(async()=>{
279
  const book=read(),slot=q('recipe_slot').value;
280
  const next=api.validate({...book,recipes:[...book.recipes.filter(r=>r.slot!==slot),{slot,name:q('recipe_name').value,settings:currentSettings()}]});
281
+ if(book.recipes.some(r=>r.slot===slot)&&!(await window.appConfirm('Replace recipe slot '+slot+' on this device?',{heading:'Replace saved recipe',confirmLabel:'Replace'})))return;
282
  store.save(next);message('Saved slot '+slot+' in this browser.');
283
  });
284
  q('recipe_load').onclick=action(()=>{
 
292
  message('Recipe export requested. No tracks, networks, results or credentials are included by the application.');
293
  });
294
  q('recipe_import').onchange=action(async()=>{
295
+ const input=q('recipe_import'),file=input.files[0];if(!file)return;input.value='';if(file.size>32768)throw Error('Recipe file exceeds 32 KB');
296
  const book=api.parse(await file.text());
297
+ if(!(await window.appConfirm('Replace this browser’s recipe library with '+book.recipes.length+' imported recipes? Export a backup first if needed.',{heading:'Import recipe library',confirmLabel:'Replace library'})))return;
298
  if(!store)throw Error('Browser storage unavailable');store.save(book);
299
  message('Imported '+book.recipes.length+' recipes locally. No run was started.');q('recipe_import').value='';
300
  });
301
+ q('recipe_clear').onclick=action(async()=>{
302
+ if(!(await window.appConfirm('Clear saved recipes and batches for this app on this device? Server run data and downloaded files are unaffected.',{heading:'Clear local recipes and batches',confirmLabel:'Clear local data',danger:true})))return;
303
  if(!store)throw Error('Browser storage unavailable');store.clear();message('Local recipes cleared. Server data and downloads are unaffected.');
304
  });
305
  q('batch_save').disabled=false;q('batch_load').disabled=false;
 
308
  q('queue_add').onclick=()=>{
309
  const text=q('batch_recipe_sequence').value.trim();q('batch_recipe_sequence').value=text?text+', '+q('batch_add_recipe').value:q('batch_add_recipe').value;
310
  };
311
+ q('batch_save').onclick=action(async()=>{
312
  const book=read(),slot=q('batch_slot').value;
313
  const runs=q('batch_recipe_sequence').value.split(',').map(s=>s.trim().toUpperCase());
314
  const next=api.validate({...book,batches:[...book.batches.filter(b=>b.slot!==slot),{slot,name:q('batch_name').value,carry:q('carry').checked,runs}]});
315
+ if(book.batches.some(b=>b.slot===slot)&&!(await window.appConfirm('Replace saved batch slot '+slot+'?',{heading:'Replace saved batch',confirmLabel:'Replace'})))return;
316
  store.save(next);message('Batch '+slot+' saved locally.');
317
  });
318
  q('batch_load').onclick=action(()=>{