packet-tracer-skill 0.2.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +491 -73
  2. package/README.md +575 -442
  3. package/SKILL.md +513 -262
  4. package/bin/packet-tracer-skill.js +29 -2
  5. package/docs/github-launch-ops-0.2.3.md +37 -0
  6. package/docs/github-metadata.md +6 -4
  7. package/docs/hero-demo-plan.md +1 -1
  8. package/docs/home-iot-donor-proof.md +4 -4
  9. package/docs/l2-security-qos-proof.md +1 -1
  10. package/docs/packet-tracer-feature-gap-atlas.md +4 -4
  11. package/docs/post-launch-follow-up.md +9 -5
  12. package/docs/proof-readiness-dashboard.md +69 -0
  13. package/docs/publish-preview-roadmap.md +6 -5
  14. package/docs/release-checklist.md +17 -8
  15. package/docs/release-notes-0.2.4.md +20 -0
  16. package/docs/runtime-truth.md +33 -8
  17. package/docs/security-edge-deepening-proof.md +1 -1
  18. package/examples/README.md +98 -69
  19. package/examples/complex_campus_master_edit_v4.inventory.json +12 -2
  20. package/examples/gallery.md +94 -6
  21. package/examples/home_iot_cli_edit_v1.inventory.json +11 -2
  22. package/examples/index.json +932 -4
  23. package/examples/local-sample-evidence.json +24 -0
  24. package/examples/proof-cards.json +117 -0
  25. package/examples/service_heavy_cli_edit_v1.inventory.json +11 -2
  26. package/package.json +61 -53
  27. package/pytest.ini +9 -0
  28. package/references/packettracer-sample-catalog.json +45287 -4525
  29. package/references/packettracer-sample-catalog.md +599 -259
  30. package/references/proof-readiness-candidates.json +352 -0
  31. package/scripts/build_examples_index.py +228 -35
  32. package/scripts/build_sample_catalog.py +62 -40
  33. package/scripts/corpus_runner.py +430 -0
  34. package/scripts/coverage_matrix.py +1842 -1812
  35. package/scripts/donor_cache.py +354 -0
  36. package/scripts/donor_diagnostics.py +3 -1
  37. package/scripts/generate_pkt.py +12738 -4228
  38. package/scripts/intent_parser.py +2269 -1657
  39. package/scripts/lab_coherence.py +455 -0
  40. package/scripts/local_donors.py +340 -0
  41. package/scripts/packet_tracer_env.py +846 -391
  42. package/scripts/pkt_annotate.py +218 -0
  43. package/scripts/pkt_codec.py +420 -181
  44. package/scripts/pkt_editor.py +2487 -1703
  45. package/scripts/pkt_transformer.py +1170 -727
  46. package/scripts/pkt_verify.py +461 -0
  47. package/scripts/runtime_doctor.py +80 -29
  48. package/scripts/sample_catalog.py +1425 -1250
  49. package/scripts/twofish_diagnostics.py +48 -31
  50. package/scripts/usage_ledger.py +218 -0
  51. package/scripts/vendor/README.md +44 -37
  52. package/scripts/vendor/twofish_pure.py +321 -0
  53. package/scripts/workspace_repair.py +548 -508
  54. package/templates/pt900/donors/README.md +15 -0
package/CHANGELOG.md CHANGED
@@ -2,78 +2,496 @@
2
2
 
3
3
  All notable changes to this project should be recorded in this file.
4
4
 
5
- The format is intentionally simple and release-oriented.
6
-
7
- ## [0.2.3] - 2026-05-03
8
-
9
- ### Added
10
-
11
- - voice/collaboration edit-proven proof for IOS `telephony-service`, `ephone-dn`, `ephone`, and `dial-peer voice` command shapes
12
- - automation/controller edit-proven proof for existing Python, JavaScript, and TCP/UDP script-file replacement
13
- - L2 security/QoS edit-proven proof for explicit dot1x and QoS IOS switch commands
14
- - security-edge deepening proof for explicit router CBAC and ZFW IOS commands
15
- - L2 resiliency + BGP edit-proven proof for explicit BGP, STP/RSTP, EtherChannel/LACP/PAgP, VTP, and DTP IOS commands
16
- - IPv4 routing/NAT/IOS-management edit-proven proof for explicit OSPFv2, EIGRP IPv4, RIPv2, static/default route, DHCP relay, NAT/PAT, SSH, NTP, and syslog IOS commands
17
- - local user-supplied Packet Tracer corpus audit via `--local-sample-audit-root` and ignored `output/local-sample-audit.json`
18
- - donor-backed readiness expansion for dot1x, ZFW, voice/collaboration IOS edits, and automation/controller script-file edits
19
- - donor-backed readiness expansion for explicit OSPFv3, EIGRP IPv6, RIPng, and IPv6 HSRP edit paths
20
- - generate-ready pilot design doc that defines the future acceptance gate without enabling broad generate
21
- - local/cache-only GitHub sample ingestion audit for `.pkt`/`.pka` repositories, license status, decode evidence, and promotion status
22
-
23
- ### Changed
24
-
25
- - feature atlas candidate status now promotes `ospfv3`, `eigrp_ipv6`, `ripng`, `hsrp`, `dot1x`, `qos`, `cbac`, and `zfw` only when editor roundtrip and decode-backed evidence exists
26
- - donor-backed readiness now requires proof-linked sample, decode, parser, and editor roundtrip evidence
27
- - remote samples with unknown license metadata now stay `reference_only`; permissive-license samples still require decode and inventory validation before curated donor eligibility
28
- - local `pkt_examples` evidence is now separated from public curated truth; raw `.pkt/.pka` files remain excluded from git and npm
29
- - README and proof docs now describe `0.2.3` as a capability release, not an unpublished candidate
30
- - ASA service-policy, clientless VPN, Linksys voice, Network Controller GUI, Blockly, VM/IOx, and broad physical/media workflows remain report-only
31
-
32
- ### Notes
33
-
34
- - `0.2.3` is a capability proof/readiness release, not a broad generation release
35
- - `generate_ready=0` remains intentional until donor-backed acceptance evidence exists
36
- - broad NAC, QoS, ASA, and security topology generation remains blocked
37
-
38
- ## [0.2.2]
39
-
40
- ### Added
41
-
42
- - advanced wireless proof surface for WEP and WPA Enterprise/RADIUS edit-proven behavior
43
- - wireless advanced feature atlas coverage for WLC, Meraki, cellular, Bluetooth, beamforming, guest Wi-Fi, WEP, and WPA Enterprise
44
- - runtime README guidance for generic Twofish bridge paths and search-root fallback
45
-
46
- ### Changed
47
-
48
- - package version advanced to `0.2.2` for the README/runtime cleanup and advanced wireless feature wave
49
- - README runtime setup no longer presents a user-specific local path as the default bridge location
50
- - advanced wireless prompts now classify into the `wireless_advanced` family without drifting into `service_heavy`
51
- - WEP and WPA Enterprise/RADIUS are represented as edit-proven where explicit deterministic edit targets exist, while broader WLC/cellular/Bluetooth/Meraki scope remains report-only
52
-
53
- ### Notes
54
-
55
- - `0.2.2` remains conservative: no broad synthetic advanced wireless generation is claimed
56
- - runtime messaging remains Windows-first and explicit about external bridge-assisted validation
57
-
58
- ## [0.2.1]
59
-
60
- ### Added
61
-
62
- - npm tarball hardening for the public package surface
63
- - launch announcement draft aligned with the current public release wording
64
-
65
- ### Changed
66
-
67
- - package version advanced to `0.2.1` because `0.2.0` is already published on npm
68
- - npm package contents now exclude caches, generated previews, and non-essential screenshot payloads
69
- - public release references now consistently point to the `0.2.1` patch release artifacts
70
-
71
- ### Notes
72
-
73
- - `0.2.1` is the publishable patch release for the conservative public preview surface
74
- - runtime messaging remains Windows-first and explicit about the external bridge-assisted validation path
75
-
76
- ## [0.2.0]
5
+ The format is intentionally simple and release-oriented.
6
+
7
+ ## [0.3.1] - 2026-09-06
8
+
9
+ ### Wireless labs that actually carry traffic
10
+
11
+ `0.3.0` shipped wireless topologies that opened and passed every static check
12
+ while no client could reach anything. Measured on a generated lab after this
13
+ release: a laptop takes its lease from the home router and pings the gateway
14
+ and the other laptop 4/4, over Wi-Fi, on an open network and on WPA2.
15
+
16
+ Six facts about one wireless lab were each decided in two places with nothing
17
+ comparing them, and every one of them had to be fixed before a packet moved.
18
+
19
+ ### Fixed
20
+
21
+ - **The access point's key was written where Packet Tracer does not read it.**
22
+ A working WPA2 home router keeps `WIRELESS_COMMON/WEP_PROCESS/KEY` with
23
+ `WEP_PROCESS/ENCRYPTION`, and carries no `WPA_PASSPHRASE` at all -- the field
24
+ names are legacy and WPA2 uses them. Choosing the field by authentication
25
+ type left the access point running WPA2 with no key while its clients had
26
+ one. Both sides looked right in every field anyone was reading.
27
+ - **A home router's DHCP lease records were deleted when its LAN moved.** That
28
+ is how a client gets its address back when the file opens; they are
29
+ renumbered into the new pool now, and a lease naming a client the prune
30
+ removed is dropped.
31
+ - **The client's security, network name and addressing mode never followed the
32
+ access point.** `set_wireless_ssid` wrote the access point;
33
+ `associate_wireless_client` wrote only the SSID. A repair pass now carries
34
+ authentication, encryption, key and network type across, and only into the
35
+ live profile -- every working client leaves its saved `PROFILES` list as the
36
+ donor's boilerplate.
37
+ - **A home router served whatever network its donor had.** The laptops were
38
+ addressed on the planned network and the router on the donor's; the router
39
+ now moves onto the one its own clients point at, and its pool with it.
40
+ - **The layout placed wireless clients out of radio range.** Hosts were laid
41
+ out in rows under the switch they hang off, and a wireless client hangs off
42
+ nothing -- one landed 420 units from a router whose radio reaches 250.
43
+ - **`port_exists` accepted interface names a home router does not have.** It
44
+ checked only the port index, so `FastEthernet0/1` passed on a device whose
45
+ sockets are `Ethernet 1` .. `4`, and with no interfaces in its configuration
46
+ `Ethernet 99` passed too. Both models were read off the live devices; the
47
+ repair pass reads the same list, so a wrong name is renamed rather than the
48
+ cable dropped.
49
+ - **The coherence checker could not see a home router's LAN address.** It walks
50
+ running configs, and that address is a setting, so it reported
51
+ `gateway_answers_for_nobody` against a router holding exactly that address.
52
+
53
+ ### Changed
54
+
55
+ - The committed sample catalogue is limited to the labs it may publish.
56
+ Rebuilding it on a machine with saved labs had staged 350 entries naming
57
+ their owner, with absolute paths under their home directory, into a file
58
+ bound for a public repository. Local labs go to a git-ignored file beside it
59
+ and donor ranking reads both, so nothing about donor choice changed.
60
+ - Comments, docs and fixtures no longer identify their evidence by the filename
61
+ of someone's saved lab.
62
+
63
+ ### Notes on measurement
64
+
65
+ `pt_inspect_ports` immediately after a lab opens shows a wireless client as
66
+ `up`, `linked`, `ip 0.0.0.0`. That is a first reading and means nothing -- a lab
67
+ Packet Tracer saved itself, seconds after it pinged 4/4, reads the same way on
68
+ reopen. Several hypotheses were rejected against that control rather than
69
+ argued: radio bandwidth matching, channel matching, `NETWORK_TYPE`, and the
70
+ belief that only a runtime nudge could make association work.
71
+
72
+ 833 passed, 1 skipped. Corpus: 32 of 33 generated, 31 opened, 0 unexpected.
73
+
74
+ ## [0.3.0] - 2026-08-06
75
+
76
+ ### First verified generation
77
+
78
+ A prompt now produces a `.pkt` that Packet Tracer actually opens. Confirmed on
79
+ 2026-08-02: `1 router 1 switch ve 3 komputer qur` produced a `9.0.0.0810` file
80
+ containing exactly `R1 <-> SW1` and `SW1 <-> PC1/PC2/PC3`, and Packet Tracer
81
+ loaded it in 40 seconds with the window title naming the file.
82
+
83
+ ### Added
84
+
85
+ - `scripts/pkt_verify.py`: two-tier verification. `structural_check` is headless
86
+ and catches dangling link endpoints, duplicate device names, undecodable
87
+ bytes, wrong root elements and incompatible versions. `open_check` launches
88
+ Packet Tracer and waits for the file's own window, reporting
89
+ `opened` / `timeout` / `process_exited` / `packet_tracer_missing`.
90
+ - `scripts/usage_ledger.py`: a local, gitignored record of which donors actually
91
+ worked, fed back into donor ranking so repeat requests try proven donors
92
+ first. Prompts are stored as a non-reversible fingerprint, never verbatim.
93
+ Bounded to 2000 entries, disabled with `PKT_USAGE_LEDGER=off`, and never
94
+ load-bearing — deleting it changes nothing but donor order.
95
+ - `tests/test_pkt_verify.py`, `tests/test_usage_ledger.py`,
96
+ `tests/test_donor_grouping.py`.
97
+
98
+ ### Changed — limitations removed
99
+
100
+ - **The target version is detected, not hardcoded.** Resolution order:
101
+ `PACKET_TRACER_TARGET_VERSION`, the installed Packet Tracer's directory name,
102
+ the compatibility donor's `<VERSION>`, then the default. An 8.2 install now
103
+ targets 8.2 with no configuration. The install-root name yields a three-field
104
+ version deliberately, so a bundled sample carrying `9.0.0.0000` cannot
105
+ outrank the user's own saves by matching a build number that was invented.
106
+ - **The Windows-only restriction is gone.** It existed because the codec needed
107
+ a compiled bridge only ever built for Windows; the pure-Python engine removed
108
+ that. `windows_first_runtime` is no longer raised, and a missing Packet Tracer
109
+ executable is reported as exactly that.
110
+ - **Pruning is no longer an unsafe mutation.** `remove_link` was categorised as
111
+ `port_reassignment` and, with `device_prune`, sat on the blocked list — so the
112
+ safe-open profile forbade the two core operations of donor-prune generation.
113
+ `remove_link` is now `link_prune`, and prune operations are allowed. Inventing
114
+ structure the donor never had stays blocked.
115
+ - **The sample catalogue is version-gated.** Only the compatibility donor was
116
+ checked before, so a 9.0-targeted run could select a 6.1 sample and emit a 6.1
117
+ file. Observed and fixed.
118
+ - Donor groups are aligned to targets by router uplink instead of name order. A
119
+ donor containing `Router <-> Switch` on its second switch was previously
120
+ reported as not containing that link at all.
121
+ - `validate_open` verifies instead of announcing. It ran `subprocess.Popen` and
122
+ printed `{"status": "launched"}` without observing anything, so a corrupt file
123
+ reported the same result as a working one.
124
+ - `validate_external_sample_summary` uses the compatibility ladder rather than
125
+ string equality.
126
+ - Python minimum is 3.10; the 3.14 pin applied only to the optional accelerator.
127
+
128
+ ### Performance
129
+
130
+ Generation went from 200-250 s to **94 s** for a small lab. Profiling showed 340
131
+ of 349 seconds inside `decode_pkt_modern`, and 69 of the 82 calls came from
132
+ `_pkt_version` — full authenticated decrypts of entire multi-megabyte files
133
+ performed only to read `<VERSION>`.
134
+
135
+ - `pkt_codec.peek_pkt_header` decrypts only the front of a file. CTR mode is
136
+ seekable and stage 1 reverses the buffer, so the needed plaintext prefix comes
137
+ from the file's tail: the probe is now O(prefix), not O(file). Measured
138
+ constant ~21 ms regardless of size, against 14.8 s for a full decode of the
139
+ largest lab — 679x on that file, and byte-identical version strings.
140
+ Tag verification is deliberately skipped; this is a read-only probe and
141
+ anything that matters still goes through `decode_pkt_modern`.
142
+ - `_pkt_version` and `decode_pkt_to_root` cache on `(path, size, mtime_ns)`, so
143
+ an edited file is re-read rather than served stale. Only immutable bytes are
144
+ cached; callers still get their own tree to mutate.
145
+
146
+ ### Every corpus case now opens in Packet Tracer
147
+
148
+ 7 generated, 7 opened, 1 correctly refused, 0 unexpected — including topologies
149
+ larger than the donor itself.
150
+
151
+ **The cause of every "not compatible with this version" rejection was invented
152
+ `*_MEM_ADDR` values on newly created links.** Rebuilding an existing, working
153
+ host link with the same devices and ports left exactly five fields different
154
+ from the original: `LENGTH` and the four MEM_ADDRs. Omitting those four makes
155
+ the same link open. They are runtime pointers from the session that saved the
156
+ file — in working donors they resolve to no device at all.
157
+
158
+ That single finding retired an earlier conclusion. Created `Pc <-> Switch` links
159
+ had been refused on the theory that host connections could not be built; the
160
+ endpoint kind was incidental, the invented pointers were the cause.
161
+ `_ensure_link` no longer writes them on a new link, and the restriction is gone.
162
+ The repo's own workspace validator required them too, and so rejected files
163
+ Packet Tracer accepts; it now objects only when one end has a reference and the
164
+ other does not.
165
+
166
+ With that unblocked, three duplication operations close the remaining gaps —
167
+ `duplicate_device`, `duplicate_group`, and `duplicate_host` — each verified
168
+ against a real open. All three must run **after** the rename and prune pass,
169
+ from devices already carrying their final names; emitting them first and
170
+ renaming the copies afterwards produces files Packet Tracer refuses.
171
+
172
+ The donor now constrains which device models are available, not how large a
173
+ topology can be.
174
+
175
+ ### First open-verified generation set, and what it disproved
176
+
177
+ Running the corpus with real Packet Tracer opens produced the first evidence-backed
178
+ readiness numbers — and immediately contradicted two things this repo believed.
179
+
180
+ **Cross-group device borrowing does not work.** A target switch needing more
181
+ hosts than its aligned donor switch carries was allowed to borrow from other
182
+ donor groups. Every corpus case that borrowed failed to open (4, 5 and 7 hosts);
183
+ every case that stayed within its donor group's own hosts opened (2 and 3).
184
+ Moving a device between switch groups leaves state this code does not fix up.
185
+ Borrowing is off by default now; `PACKET_TRACER_CROSS_GROUP_BORROW=1` re-enables
186
+ it for experiments. A refusal beats a file that looks generated and will not open.
187
+
188
+ **Coverage reporting is not base-donor eligibility.** The version policy was
189
+ applied to `_existing_ranked_candidates`, which feeds both. Under the `exact`
190
+ default every bundled sample vanished and campus prompts started refusing with
191
+ "critical capability coverage is still missing" — for coverage sitting in the
192
+ catalogue all along. A sample proves a capability whether or not it can serve as
193
+ a generation base, so the policy now applies only in `_base_donor_candidates`.
194
+ Generation also got 4x faster as a side effect: 6-7 s per case instead of 24-50 s,
195
+ because the base pool is small while coverage still sees everything.
196
+
197
+ Refusal messages no longer suggest loosening the donor policy. Loosening was
198
+ measured to produce files Packet Tracer refuses, so the advice walked users into
199
+ a broken state that looked like progress. When the running build is unknown the
200
+ message asks for the one action that helps: save any lab from Packet Tracer once,
201
+ which is what teaches the skill its build.
202
+
203
+ Corpus now: **4 generate and open, 3 donor-limited, 1 correct refusal, 0
204
+ unexpected.** `refused_donor_limited` is a distinct status so a sound request the
205
+ local donor cannot serve stays countable without being mistaken for a defect.
206
+
207
+ ### A corpus runner, and the five defects it found immediately
208
+
209
+ `scripts/corpus_runner.py` runs a set of prompts end to end and records what
210
+ happened: generated or refused, structural result, and optionally a real Packet
211
+ Tracer open. Its first run found five defects that 377 unit tests had not.
212
+
213
+ - **Device counts attached to the wrong device.** Two phrasings are supported,
214
+ `3 switch` and `switch 3`, and both were pooled through `max`. The trailing
215
+ form swallowed the next device's number, so `4 switch 1 router 8 komputer`
216
+ asked for **eight routers** and `1 router 1 switch 2 komputer` asked for two
217
+ switches. No test had ever asserted a multi-device count.
218
+ - **A prompt with no topology signal produced a lab.** "sebeke haqqinda melumat
219
+ ver" — a request for information — generated a two-device file. Inventing a
220
+ lab is worse than refusing: the user never sees that they were misread.
221
+ - **Host capacity was checked per donor switch group** rather than across the
222
+ donor, so "1 switch and 5 PCs" was refused against a donor holding 11 PCs on
223
+ three switches, every one of which was about to be pruned. Donor devices are
224
+ pooled across groups now.
225
+ - **Two cables on one interface.** Ports are set from two independent places:
226
+ surviving donor links keep their wiring, and `set_link` operations carry
227
+ planner-chosen ports. Neither knew about the other, so `PC1` and `R1` both
228
+ landed on `SW1 FastEthernet0/3`. `_resolve_port_conflicts` reconciles once
229
+ over the links that will actually exist; surviving donor wiring wins because
230
+ it is known-good.
231
+ - **Group alignment dropped donors.** With more targets than donor groups the
232
+ reordering lost entries, and the caller then reported "supports only 0 switch
233
+ groups" for a donor with three switches.
234
+
235
+ Refusals also name the layer that failed. When the intent plan has gaps, donor
236
+ evaluation never runs, so reporting "donor selection" with zero candidate counts
237
+ sent users to fix a donor that had never been consulted. That is
238
+ `blocked_by_intent` now, and donor messages name the donor.
239
+
240
+ Corpus: **6 of 8 generate, 1 refuses correctly, 1 known gap.** `four_switch`
241
+ needs a donor with four switch groups; none of the eligible donors has one, so
242
+ the refusal is real. Generation also got faster — the inflated device counts had
243
+ been driving much larger donor searches, and the minimal case went from 103 s to
244
+ 24 s.
245
+
246
+ ### Every bundled sample is readable now
247
+
248
+ 18 of the 292 samples shipped with Packet Tracer 9.0 failed EAX tag
249
+ verification and were reported as undecodable. They are Packet Tracer 5.x
250
+ saves written before Twofish: qCompress output XORed byte-wise with
251
+ `(length - index)`, no cipher and no tag. `decode_pkt_legacy`,
252
+ `detect_pkt_format` and `decode_pkt_auto` handle both containers.
253
+
254
+ A further 6 decoded but would not parse. Packet Tracer writes raw control bytes
255
+ into element text — a Cisco banner delimiter is literally `banner motd ` —
256
+ which XML 1.0 forbids. `parse_pkt_xml` maps those into the Unicode private use
257
+ area and `serialize_pkt_xml` maps them back, so the round trip stays faithful
258
+ instead of silently dropping banner delimiters.
259
+
260
+ `build_sample_catalog.py` carried its own weaker `summarize_pkt` that omitted
261
+ link endpoints, which is why the committed catalogue had 1051 link records with
262
+ no `from`/`to` and the donor graph-fit filter was comparing empty strings. It
263
+ now calls `sample_catalog._summarize_pkt`.
264
+
265
+ Catalogue rebuild: **292 of 292 readable** (was 274) and **1126 links with
266
+ endpoints** (was 0). Reading those labs promoted `qos`, `cbac`, `real_http` and
267
+ `real_websocket` from report/edit level to `donor_backed_ready`. Those
268
+ capabilities were never missing — the labs proving them were unreadable.
269
+
270
+ ### Topologies are no longer limited to the donor's own
271
+
272
+ Reuse-only wiring meant a chain donor could never satisfy a star request:
273
+ `3 switch, 6 PC, VLAN 10/20/30` was refused with "this donor does not contain
274
+ that device-to-device link". Missing links are now built with the same
275
+ `set_link` operation the edit path uses (`PACKET_TRACER_LINK_STRATEGY=reuse`
276
+ restores the old behaviour).
277
+
278
+ This needed three fixes, and the first attempt was rejected by Packet Tracer
279
+ outright — the two-tier verification caught it as `process_exited` rather than
280
+ reporting a false success:
281
+
282
+ - ports are claimed once. Adopting donor wiring for one link while planning
283
+ another from the blueprint put two cables on `SW1 GigabitEthernet0/2`.
284
+ - alternatives are checked against the device's real interfaces. Incrementing
285
+ the index invented `GigabitEthernet0/3` on a 2960-24TT, and Packet Tracer
286
+ refused the whole file as "not compatible with this version".
287
+ - when gigabit is exhausted the allocator falls back to FastEthernet, which is
288
+ what an engineer would do. A core switch with three uplinks and two gigabit
289
+ ports is a real constraint, not an impossible topology.
290
+
291
+ `port_exists` and `port_capacity` count real interfaces. `_port_address_for_name`
292
+ could not serve as the existence test: it is a MEM_ADDR lookup that returns
293
+ None whenever the donor's port nodes carry no address, which made every port
294
+ look missing.
295
+
296
+ Verified: 3-switch VLAN star on a chain donor opened in 10.1 s; the simple case
297
+ still opens in 10.4 s. `structural_check` now also fails on duplicate port use.
298
+
299
+ ### Host-to-VLAN distribution is defaulted, not refused
300
+
301
+ "3 switch, 6 PC, VLAN 10/20/30" reads as two hosts per VLAN. The planner refused
302
+ it while already defaulting port speeds, cable types, addressing and the VLAN IDs
303
+ themselves — and while the branch directly above already assigned department PCs
304
+ to VLANs by order. Hosts are now spread evenly with the split recorded as an
305
+ assumption. `PACKET_TRACER_STRICT_VLAN_ASSIGNMENT=1` restores the refusal.
306
+
307
+ The rule existed in both `intent_parser` and `generate_pkt`; it now lives only in
308
+ the parser.
309
+
310
+ ### Leftover donor devices are now deleted
311
+
312
+ Spares were renamed `UNUSED-*` / `*-SPARE-*` and moved offscreen rather than
313
+ deleted, so a five-device request produced a twenty-device, 282 KB file. Parking
314
+ was a precaution, not a measured constraint — and with real verification in place
315
+ it could finally be tested instead of assumed.
316
+
317
+ Pruning verified against a real Packet Tracer open: **6 devices, 73 KB, opened in
318
+ 17 s** (the parked equivalent took 40 s). `prune` is now the default;
319
+ `PACKET_TRACER_SPARE_STRATEGY=park` restores the old behaviour.
320
+
321
+ ## [0.3.0-pre] - Runtime and donor gate
322
+
323
+ Removes the two mechanical defects that produced `generate_ready=0` for the
324
+ whole `0.2.x` line. See `docs/improvement-plan-0.3.0.md` for the audit.
325
+
326
+ ### Added
327
+
328
+ - `scripts/vendor/twofish_pure.py`: vendored pure-Python Twofish, verified
329
+ against the official 128/192/256-bit test vectors and cross-checked as
330
+ bit-identical to the compiled bridge
331
+ - donor version compatibility ladder in `packet_tracer_env.py`
332
+ (`exact` / `same_minor` / `same_major` / `upgradeable` / `incompatible`),
333
+ selected by `PACKET_TRACER_DONOR_POLICY`, defaulting to `same_minor`
334
+ - `twofish_backend`, `donor_policy`, and `compatibility_tier` diagnostic fields
335
+ - `tests/test_twofish_pure.py` and `tests/test_donor_compatibility.py`
336
+
337
+ ### Changed
338
+
339
+ - the compiled `_twofish` bridge is now an optional accelerator, not a
340
+ prerequisite: `decode`, `inventory`, `edit`, and `generate` are all `ready` on
341
+ a clean checkout with no binaries and no environment variables
342
+ - `bridge_resolution=external_env` no longer downgrades `runtime_grade` or
343
+ raises a `using_external_bridge_only` blocker
344
+ - donor rejection messages now name the tier, the active policy, and the setting
345
+ that would accept the donor
346
+ - minimum Python relaxed from exactly 3.14 to 3.10+; the 3.14 ABI requirement
347
+ applied only to the compiled accelerator
348
+
349
+ ### Fixed
350
+
351
+ - `tests/test_release_surface.py` referenced an undefined `readme` variable and
352
+ failed under the strict profile; the assertions moved to the README test
353
+
354
+ ### Notes
355
+
356
+ - eligible donors from a stock Packet Tracer 9.0.0 install go from **0 to 48**
357
+ under the default policy (270 under `upgradeable`); none of the 292 bundled
358
+ samples carry the previously-required exact build `9.0.0.0810`
359
+ - one test profile, zero skips: **657 passed, 1 skipped**
360
+ - generation works: the corpus generates **32 of 33** scenarios and Packet
361
+ Tracer opens **32 of 32**, with 0 unexpected outcomes
362
+ - `package.json` moves to `0.3.0` for this release
363
+
364
+ ### Generation, measured against live Packet Tracer
365
+
366
+ Everything below was confirmed by opening the file in Packet Tracer, and the
367
+ connectivity claims by running real pings from the devices.
368
+
369
+ - **A rejected donor can no longer rewrite the request.** Donor adaptation
370
+ edited the caller's blueprint, so the first donor tried -- one that could not
371
+ serve a WAN -- turned `R1 Serial0/0/0 <-> R2 Serial0/0/0 (serial)` into
372
+ `GigabitEthernet0/0 <-> GigabitEthernet0/1 (eCrossOver)`, and nothing after it
373
+ could tell serial had been asked for. Each candidate now adapts a copy; only
374
+ the donor committed to writes back.
375
+ - **Interface names come from the device, not from an assumed model.** A switch
376
+ numbering its ports `FastEthernet0/1, 1/1 ... 9/1` was asked for
377
+ `FastEthernet0/2`, and `port_exists` agreed the name was fine because it only
378
+ compared slot depth. The same lab with the uplink on `FastEthernet2/1` opens.
379
+ The same blindness applied to serial: owning two serial ports made
380
+ `Serial0/0/0` acceptable on a router whose interfaces are `Serial2/0` and
381
+ `Serial3/0`.
382
+ - **Serial cables now declare their clocking end.** `DCEDEV`/`DCEPORT` were
383
+ never written, and a lab with any serial cable was refused. Isolated with a
384
+ six-variant experiment: the same topology opens over copper and is refused
385
+ over serial on every valid port pair.
386
+ - **A serial WAN is built end to end from a prompt.** `iki noqte arasinda
387
+ leased line ile 2 router 4 komputer qur` produces two routers over
388
+ `Serial0/1/1 <-> Serial0/1/0`, the file opens, and `PC1 -> 10.1.1.2` crosses
389
+ the WAN 4/4.
390
+ - **DHCP verified live:** four PCs obtained leases from the router pool and ping
391
+ each other and their gateway 4/4.
392
+
393
+ ### Fixed in the tooling that measures all of this
394
+
395
+ - `open_check` gave false verdicts: one lab checked five times answered
396
+ `opened, timeout, timeout, opened, opened`, and a bisect named a culprit that
397
+ a hand-built copy of the same operations opened fine. Each check now opens a
398
+ uniquely named copy, and a negative verdict must reproduce before it is
399
+ reported. Two of the three defects above were invisible until this was fixed.
400
+ - `--doctor` reported `RUNTIME_GRADE ready` with every capability ready and then
401
+ exited 1 with "Runtime is not fully ready", because an optional checksum line
402
+ printed `MISSING`. The verdict now follows the blocking checks.
403
+
404
+ ## [0.2.4] - Unreleased candidate
405
+
406
+ ### Added
407
+
408
+ - Examples Truth 2.0 proof-card and showcase-example surface for the post-`0.2.3` capability release
409
+ - proof-readiness dashboard for ranking the next donor-backed promotion candidates
410
+ - promotion queue artifact for IPv4 routing/management and L2 resiliency/BGP readiness work
411
+ - local sample evidence board that summarizes user-supplied `.pkt/.pka` audit counts without committing raw samples
412
+ - `0.2.4` release notes draft for the next product-hardening patch
413
+
414
+ ### Changed
415
+
416
+ - examples gallery and examples index now distinguish showcase examples, proof cards, local evidence, and promotion candidates
417
+ - current launch wording is being separated from historical `0.2.1`/`0.2.2` runbooks
418
+
419
+ ### Notes
420
+
421
+ - this candidate does not enable broad generation
422
+ - `generate_ready=0` remains intentional
423
+ - `package.json` stays at `0.2.3` until a publish decision is made
424
+
425
+ ## [0.2.3] - 2026-05-03
426
+
427
+ ### Added
428
+
429
+ - voice/collaboration edit-proven proof for IOS `telephony-service`, `ephone-dn`, `ephone`, and `dial-peer voice` command shapes
430
+ - automation/controller edit-proven proof for existing Python, JavaScript, and TCP/UDP script-file replacement
431
+ - L2 security/QoS edit-proven proof for explicit dot1x and QoS IOS switch commands
432
+ - security-edge deepening proof for explicit router CBAC and ZFW IOS commands
433
+ - L2 resiliency + BGP edit-proven proof for explicit BGP, STP/RSTP, EtherChannel/LACP/PAgP, VTP, and DTP IOS commands
434
+ - IPv4 routing/NAT/IOS-management edit-proven proof for explicit OSPFv2, EIGRP IPv4, RIPv2, static/default route, DHCP relay, NAT/PAT, SSH, NTP, and syslog IOS commands
435
+ - local user-supplied Packet Tracer corpus audit via `--local-sample-audit-root` and ignored `output/local-sample-audit.json`
436
+ - donor-backed readiness expansion for dot1x, ZFW, voice/collaboration IOS edits, and automation/controller script-file edits
437
+ - donor-backed readiness expansion for explicit OSPFv3, EIGRP IPv6, RIPng, and IPv6 HSRP edit paths
438
+ - generate-ready pilot design doc that defines the future acceptance gate without enabling broad generate
439
+ - local/cache-only GitHub sample ingestion audit for `.pkt`/`.pka` repositories, license status, decode evidence, and promotion status
440
+
441
+ ### Changed
442
+
443
+ - feature atlas candidate status now promotes `ospfv3`, `eigrp_ipv6`, `ripng`, `hsrp`, `dot1x`, `qos`, `cbac`, and `zfw` only when editor roundtrip and decode-backed evidence exists
444
+ - donor-backed readiness now requires proof-linked sample, decode, parser, and editor roundtrip evidence
445
+ - remote samples with unknown license metadata now stay `reference_only`; permissive-license samples still require decode and inventory validation before curated donor eligibility
446
+ - local `pkt_examples` evidence is now separated from public curated truth; raw `.pkt/.pka` files remain excluded from git and npm
447
+ - README and proof docs now describe `0.2.3` as a capability release, not an unpublished candidate
448
+ - ASA service-policy, clientless VPN, Linksys voice, Network Controller GUI, Blockly, VM/IOx, and broad physical/media workflows remain report-only
449
+
450
+ ### Notes
451
+
452
+ - `0.2.3` is a capability proof/readiness release, not a broad generation release
453
+ - `generate_ready=0` remains intentional until donor-backed acceptance evidence exists
454
+ - broad NAC, QoS, ASA, and security topology generation remains blocked
455
+
456
+ ## [0.2.2]
457
+
458
+ ### Added
459
+
460
+ - advanced wireless proof surface for WEP and WPA Enterprise/RADIUS edit-proven behavior
461
+ - wireless advanced feature atlas coverage for WLC, Meraki, cellular, Bluetooth, beamforming, guest Wi-Fi, WEP, and WPA Enterprise
462
+ - runtime README guidance for generic Twofish bridge paths and search-root fallback
463
+
464
+ ### Changed
465
+
466
+ - package version advanced to `0.2.2` for the README/runtime cleanup and advanced wireless feature wave
467
+ - README runtime setup no longer presents a user-specific local path as the default bridge location
468
+ - advanced wireless prompts now classify into the `wireless_advanced` family without drifting into `service_heavy`
469
+ - WEP and WPA Enterprise/RADIUS are represented as edit-proven where explicit deterministic edit targets exist, while broader WLC/cellular/Bluetooth/Meraki scope remains report-only
470
+
471
+ ### Notes
472
+
473
+ - `0.2.2` remains conservative: no broad synthetic advanced wireless generation is claimed
474
+ - runtime messaging remains Windows-first and explicit about external bridge-assisted validation
475
+
476
+ ## [0.2.1]
477
+
478
+ ### Added
479
+
480
+ - npm tarball hardening for the public package surface
481
+ - launch announcement draft aligned with the current public release wording
482
+
483
+ ### Changed
484
+
485
+ - package version advanced to `0.2.1` because `0.2.0` is already published on npm
486
+ - npm package contents now exclude caches, generated previews, and non-essential screenshot payloads
487
+ - public release references now consistently point to the `0.2.1` patch release artifacts
488
+
489
+ ### Notes
490
+
491
+ - `0.2.1` is the publishable patch release for the conservative public preview surface
492
+ - runtime messaging remains Windows-first and explicit about the external bridge-assisted validation path
493
+
494
+ ## [0.2.0]
77
495
 
78
496
  ### Added
79
497
 
@@ -83,7 +501,7 @@ The format is intentionally simple and release-oriented.
83
501
  - runtime truth, discovery keyword, GitHub metadata, publish-preview roadmap, and curated donor registry docs
84
502
  - seeded curated donor registry entries derived from known working public example artifacts
85
503
  - checked-in Packet Tracer template fallback assets for hermetic builder coverage
86
- - hero demo plan and `0.2.0` release notes draft artifacts for conservative launch prep
504
+ - hero demo plan and `0.2.0` release notes draft artifacts for conservative launch prep
87
505
 
88
506
  ### Changed
89
507