2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	The bridges are drawn straight.

	* breadboard_lib.py: jumper() takes straight=False; True draws a line between the two points (same end dots, same registration for the labels), for a wire whose ends are in one column, whose arc overshoots its
	first end. Every existing caller's shape is unchanged.
	* render.py: the bridges between rails use straight=True (and no longer draw their own end dots twice).
	* pptest_regression: check_rail_bridges, a sixth assertion that reads the drawn SVG (each bridge a line from rail to rail, no curve starting on its rails), proved to fail on the old drawing.
	* FutureResearch.org reviewed at this version; SessionHandoff.org; ResearchJournal.org (continued 68).
	make check reports 682 passing lines (681 in 0.72.76): the new assertion.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	The rail bridges are staggered.

	* render.py: each bridge gets its own column (a column whose hole on either rail is taken by a rail wire of the net or by an earlier bridge end is skipped); p.rail_attach_xs and p.rail_bridge_ends are recorded.
	* pptest_regression: check_rail_bridges, a fifth assertion (no rail hole holds two leads, with negative controls).
	* FutureResearch.org: the item on the connector labels loses the "chained bridges" half, reviewed at this version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 67).
	make check reports 681 passing lines (680 in 0.72.75): the new assertion.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	The jumpers that join a rail's physical rails are instructed.

	* render.py: the bridges drawn between a rail net's physical rails are recorded in p.rail_bridges; the off-board connector (stub and label) is drawn on the top layer at size 8 with its area registered, not
	under the board rectangle; the bridge between two boards is drawn as a complete jumper, like one within a board.
	* build_instructions.py: a "rail_bridge" step per recorded bridge (north, middle, south; "of board N" on several boards).
	* parse_instructions.py: the single-board sentence is read as a rail_bridge step (informational); the multi-board one is refused like every other multi-board step.
	* breadboard_config.py: MultiboardConfig's docstring corrected (it said nothing consumes it).
	* pptest_regression: check_rail_bridges (four assertions; the measured counts apply to the default layout only, the invariants to every layout). ppbuild_instructions.1, ppparse_instructions.1: RAIL BRIDGES. FutureResearch.org: two items done and removed (multi-board rail ties; MultiboardConfig),
	two new (the parser and multi-board text; the bridge labels), reviewed at this version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 66).
	make check reports 680 passing lines (676 in 0.72.74): the four assertions of check_rail_bridges.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	The KiCad version of the files written: default 10, option --kicad-version.

	* kicad_gen.py: DEFAULT_KICAD_VERSION (10), NATIVE_KICAD_VERSION (7), KicadVersionError, parse_kicad_version(), installed_kicad_major(), check_kicad_version() and convert_format() ('kicad-cli sch upgrade
	--force', one call per file).
	* ppkicad_layout, ppresolve_components: --kicad-version N, validated right after the arguments, the files converted after the write.
	* pptest_regression: check_kicad_version_option (five assertions); check_hierarchy_instance_data asks for the 7 format.
	* ppkicad_layout.1, ppresolve_components.1. FutureResearch.org: the item on the KiCad 7 file format is done and leaves the list, reviewed at this version; SessionHandoff.org mirrors it. ResearchJournal.org
	(continued 64).
	* ppkicad_layout, ppresolve_components: PACKPAY_KICAD_VERSION is the default of --kicad-version when set. pptest_regression sets it to 7 for its own subprocesses (several checks read the output as text in
	the 7 layout, and the conversion costs one kicad-cli call per file); check_kicad_version_option clears it to test the true default, and includes a loop in the 10 format (the altimeter resolved).
	* DevelopmentStandard.org: step 4 of the Delivery Checklist asks for the opt-in GUI check on a delivery that changes how a schematic is written.
	make check reports 676 passing lines (671 in 0.72.73): the five assertions of check_kicad_version_option.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	Multi-sheet output carries the instance data the KiCad GUI needs.

	* kicad_gen.py: Schematic.write() takes sheet_path and project and writes (instances (project ..) (path ..) (page N)) inside each (sheet ...) block; SchematicSet.write() always passes the instance paths
	of a child file (it did so only for a file used by several sheet symbols), so every symbol carries one (instances ...) entry per path.
	* pptest_regression: check_hierarchy_instance_data (four assertions: knight-roller, sheet reuse, KiCad's complex_hierarchy demo, a negative control) and check_hierarchy_loads_in_gui (opt-in, PACKPAY_GUI_CHECK=1:
	opens the output in the KiCad GUI headless and must not show the repair dialog an instance-less control shows).
	* pptest_regression.1: the environment variable. FutureResearch.org: the item is done and leaves the list, a new small one (the KiCad 7 file format banner), reviewed at this version; SessionHandoff.org keeps the
	headless GUI procedure and mirrors the list. ResearchJournal.org (continued 63).
	make check reports 671 passing lines (667 in 0.72.72): the four static assertions; the GUI check skips itself unless asked.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	The KiCad GUI is opened for the first time. No code changes.

	* FutureResearch.org: the multi-sheet item loses its GUI point (done: the single-sheet output loads cleanly, the hierarchical one is repaired on load) and its rail-test point (dismissed on a measurement:
	typed power rule against degree rule on the coldfire demo); a new first item, "Hierarchical output: KiCad's GUI repairs something on load", with what is known, leads, the headless procedure and its traps;
	the list is reviewed at this version. SessionHandoff.org mirrors it and records the environment (xdotool installed, disk space, the scaled screenshots). ResearchJournal.org (continued 61).
	make check reports 667 passing lines, as in 0.72.71.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	The anchor order is refined.

	* auto_layout.py: refine_anchor_order() improves the greedy walk of order_anchors_by_connectivity() by local search (best single insertion, repeated on the weighted distance between anchors that share a
	non-rail net); anchor_order_refined / PACKPAY_ANCHOR_REFINE=0, ANCHOR_REFINE_LIMIT (80). layout() is a wrapper over _layout_pass(): when the refined layout leaves a component unplaced it is laid out once
	more with the plain walk and the one that places more is kept.
	* pptest_regression: _plain_anchor_order() pins the old order in the left-bias detector and the bend-wire tripwire; check_rail_wires derives J1's holes from its pin list; check_render_sweep has a ceiling of
	two marginal label overlaps for rp2040 (MARGINAL_LABEL_OVERLAPS).
	* pprender.1, ppbuild_instructions.1, ppkicad_layout.1 document the variable. FutureResearch.org: the anchor order item is done and leaves the list, the left-bias item becomes WAIT (knight-roller, its only
	case, is at 30%), two small items are new, the list is reviewed at this version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 60).
	make check reports 667 passing lines, as in 0.72.69: the assertions that changed were replaced one for one.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	The IC orientation item is closed by measurement. No code changes.

	* FutureResearch.org: the item becomes WAIT with the two cases no example design has (anchor rows for a DIP wider than 7.62 mm; the shorts note for other row spacings); a header's half is dismissed on
	a measurement (identical results with every header in either half), the label overlaps of the synthetic circuit are moot since the diagram fallback; the list is sorted by the rule again; the remark on
	multi-rail boards is recorded in the rail-per-supply item. SessionHandoff.org mirrors it. ResearchJournal.org (continued 59).
	* Concept.org: the best practice on IC orientation says what is done and what waits.
	make check reports 667 passing lines, as in 0.72.69: nothing that is tested changed.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	One supply is the rail; any other is wired with jumpers.

	* auto_layout.py: supply_selection() and demoted_supply_nets(): of the supplies (nets that are not GND and have a rail-like name, or are typed power nets above the threshold) the one with the most members is
	the rail, then the name; breadboard_rail_nets() drops the others; the rail-direct source test ignores a demoted supply's name.
	* build_instructions.py: the three rail-like-name clauses (an anchor's rail sides and rail pins, a rail-direct source's pins) exclude a demoted supply.
	* pptest_regression: check_one_supply_rail (three assertions: the selection on hand-built circuits, rp2040's rails, the instructions of rp2040).
	* ppbuild_instructions.1 follows. FutureResearch.org: the item becomes a WAIT item at the end (a rail per supply, with the measurements), reviewed at this version; SessionHandoff.org mirrors it.
	ResearchJournal.org (continued 58).
	make check now reports 667 passing lines (664 in 0.72.68), as the sum of its thirteen phases, with the variables unset and with PACKPAY_PHYSICAL_ANCHORS=0, PACKPAY_FAR_LEAD_SIDES=0 and
	PACKPAY_PHYSICAL_HEADERS=0 together: the three assertions of check_one_supply_rail. Three expectations about rp2040 follow the change: the narrow-board sweep (3 unplaced -> 0), the same-column
	assertion of check_rail_attached_far_leg_no_short (now the invariant) and check_rail_wires' power group of U1 (one of nine pins).

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	A diagram part's rail wire names its pins.

	* build_instructions.py: Step.join_pins and Step.join_hole; the rail wire of an anchor laid out as a diagram carries the pins of that anchor on that rail NET and the hole of the column (row 0);
	describe_step() words it ("U1's ground (-) pins 21, 3 go in this column: hole f14.").
	* parse_instructions.py: _RAIL_WIRE reads the sentence; reconstruct() registers the pins at that hole; Placement.rail_wire_unchecked.
	* pptest_regression: check_rail_wires gains three assertions (wording and parser; knight-roller's sentences and rp2040's voltage split; no unchecked wire) and a larger weight.
	* ppbuild_instructions.1 and ppparse_instructions.1 follow. FutureResearch.org: the item on IC orientation loses a bullet, reviewed at this version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 57).
	make check now reports 664 passing lines (661 in 0.72.67), as the sum of its thirteen phases, with the variables unset and with PACKPAY_PHYSICAL_ANCHORS=0, PACKPAY_FAR_LEAD_SIDES=0 and
	PACKPAY_PHYSICAL_HEADERS=0 together: the three added assertions of check_rail_wires.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	reconstruct() reads the board the text states.

	* parse_instructions.py: ParsedSteps, the list parse_instructions() returns, carries rows_per_half from the text's Board line; reconstruct() applies it (_config_for_stated_rows(), the rule
	board_config_for() already had, factored out); parse_hole() refuses a hole letter past the last row of the default board.
	* pptest_regression: check_reconstruct_reads_the_board (four assertions, including the real rp2040 on a 15-row board rebuilt with no board argument).
	* ppparse_instructions.1 follows. FutureResearch.org: the first item leaves the list, reviewed at this version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 56).
	make check now reports 661 passing lines (657 in 0.72.66), as the sum of its thirteen phases, with the variables unset and with PACKPAY_PHYSICAL_ANCHORS=0, PACKPAY_FAR_LEAD_SIDES=0 and
	PACKPAY_PHYSICAL_HEADERS=0 together: the four assertions of check_reconstruct_reads_the_board.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	Rail wires are instructed; three real shorts through a rail column are closed.

	* build_instructions.py: add_rail_wires() adds a rail_wire step (one per hole and polarity) right after the step of an anchor with a rail column, a passive with a rail-attached near lead and one with a
	rail-attached far lead; outer_hole_name(); describe_step() words it for an adult and a child.
	* parse_instructions.py: _RAIL_WIRE, kind rail_wire; reconstruct() checks each rail wire against the netlist it rebuilds (p.rail_wires, p.rail_wire_problems).
	* auto_layout.py: verify_connectivity() treats the representative rail column of an anchor as a point of its rail net. render.py: column_net is seeded with those columns, so a far lead of another net is no
	longer put in the strip of a rail column (knight-roller, solar-roller, rp2040 shorted in every earlier version).
	* pptest_regression: check_rail_wires (five assertions); check_rail_attached_far_leg_no_short rebuilds with its own board. pptest_build_instructions: the ordering assertions ignore rail wires.
	* ppbuild_instructions.1 and ppparse_instructions.1 follow. FutureResearch.org: the first item leaves the list, a new one (reconstruct()'s default rows per half), reviewed at this version;
	SessionHandoff.org mirrors it. ResearchJournal.org (continued 55).
	make check now reports 657 passing lines (652 in 0.72.65), as the sum of its thirteen phases, with the variables unset and with PACKPAY_PHYSICAL_ANCHORS=0, PACKPAY_FAR_LEAD_SIDES=0 and
	PACKPAY_PHYSICAL_HEADERS=0 together: the five assertions of check_rail_wires.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	The pin list of a physical part holds every rail pin.

	* auto_layout.py: Placement.anchor_phys_geometry, the footprint geometry (pad: column offset, half) of each physical anchor, rail pins included.
	* build_instructions.py: for a physical anchor the pin list adds the hole of every pin that is on a rail (the placement registered a column only for pins on non-rail nets), in column order.
	* pptest_regression: check_rail_pin_holes (two assertions: knight-roller's three headers list all their pins in consecutive columns; the invariant that every rail pin of a physical part is in its pin list);
	check_rail_pin_strip_is_seen (two assertions, by mutation: a lead moved into the strip of a rail pin is reported as an accidental short, and is not when the geometry is withheld);
	check_synthetic_dip_circuit follows (the dense circuit falls back to the diagram layout, loudly, in both commands; the DIP footprints as geometry; the strict physical layout cannot be drawn).
	* auto_layout.py: the placement owns every physical rail pin's half-column; verify_connectivity() knows every pin of every physical anchor; diagram_fallback(). render.py: column_net is seeded with every
	physical pin's half-column. breadboard_lib.py: NoFreeColumnError (a ValueError) for leg()'s no-free-column failure. pprender and ppbuild_instructions: on that error, or an accidental short after the render,
	they say so on stderr and re-execute in the diagram layout; pprender also prints the connectivity check after the render.
	* ppbuild_instructions.1 follows. FutureResearch.org: a new first item (an anchor's rail wires are drawn but never instructed), the IC item loses a bullet and gains a narrower one, the list is reviewed at this
	version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 54).
	make check now reports 652 passing lines (647 in 0.72.64), as the sum of its thirteen phases, with the variables unset and with PACKPAY_PHYSICAL_ANCHORS=0, PACKPAY_FAR_LEAD_SIDES=0 and
	PACKPAY_PHYSICAL_HEADERS=0 together: two assertions of check_rail_pin_holes, two of check_rail_pin_strip_is_seen and one more in check_synthetic_dip_circuit.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	A passive whose footprint is known to be unpluggable says so.

	* build_instructions.py: a passive's Step carries fit and fit_note when footprint_fit() says smd, shorts or offgrid (not for plug-in, nor for a part with no footprint); describe_step() appends the
	sentence to every wording of the passive step; parts_note() names such passives with the anchors.
	* parse_instructions.py: the two passive patterns read the optional sentence at their end and record the class as the step's fit.
	* pptest_regression: check_passive_fit (four assertions: the parser, the exact wording and header line, the measured sets of flagged passives on the real roots of five designs, the header naming them).
	* ppbuild_instructions.1 and ppparse_instructions.1 follow. DevelopmentStandard.org: compare versions on each design's ROOT schematic. FutureResearch.org: the item on IC orientation loses its first
	piece, the list is reviewed at this version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 53).
	make check now reports 647 passing lines (643 in 0.72.63), as the sum of its thirteen phases, with the variables unset and with PACKPAY_PHYSICAL_ANCHORS=0, PACKPAY_FAR_LEAD_SIDES=0 and
	PACKPAY_PHYSICAL_HEADERS=0 together: the four assertions of check_passive_fit.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	A missing easyeda2kicad is a clean error.

	* lcsc_import.py: fetch_lcsc_symbol() catches the OSError of the missing tool and raises LCSCImportError naming the part and the remedy (its docstring promised that for any failure).
	* pptest_regression: check_lcsc_fetch_without_the_tool (in process and through the real CLI with the tool hidden from PATH; and the live fetch of one part, skipped unless the tool is installed and the
	host answers).
	* ppresolve_components.1: the lcsc entry no longer says it uses --kicad-cli; says what happens without the tool. README: how to install it.
	* FutureResearch.org: the first item of the ordered list is done and leaves it, the list is reviewed at this version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 52).
	make check now reports 643 passing lines (640 in 0.72.62), as the sum of its thirteen phases with the variables unset: the three assertions of check_lcsc_fetch_without_the_tool (the third, the live
	fetch, only where easyeda2kicad is installed and easyeda.com answers: 642 elsewhere).

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	make pdf fails on a LaTeX error; the Devanagari font is declared by the one document that needs it.

	* batch-init.el.in: orgdoc/latex-errors and orgdoc/export-pdf: org-latex-export-to-pdf, then the TeX errors of Org's compile-log buffer; any error fails the export (exit status 1) with a message
	naming the file and the errors. Makefile.am: the .org.pdf rule calls it.
	* Preamble.tex: no Devanagari font (it was loaded for all ten documents). Concept.org: declares it (\newfontfamily and \DeclareTextFontCommand as LATEX_HEADER lines, with a comment). README: the font is
	needed for Concept.pdf only.
	* ResearchJournal.org: a regular expression of an old entry protected with code markup (it was an undefined control sequence).
	* pptest_regression: check_pdf_export_fails_on_latex_errors (two assertions: the sources, and two small documents exported for real, a clean one and one with an undefined control sequence).
	* DevelopmentStandard.org: the checklist step that says make pdf html builds clean says what that now means. FutureResearch.org: the first item of the ordered list is done and leaves it, the list is
	reviewed at this version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 51).
	make check now reports 640 passing lines (638 in 0.72.61), as the sum of its thirteen phases with the variables unset: the two assertions of check_pdf_export_fails_on_latex_errors.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	ppresolve_components gets --paper.

	* ppresolve_components: --paper {A4,A3,A2,A1,A0} (default: none, which starts at A4), passed to generate_schematic_auto(). ppresolve_components.1 follows.
	* pptest_regression: check_ppresolve_components_paper (two assertions: the altimeter on A4 by default and on A3 with the option, with every part of the source; an invalid size refused with exit
	status 2, no traceback, and --help listing the choices).
	* FutureResearch.org: the first item of the ordered list is done and leaves it (its anchor ORDER was measured and left alphabetical); an item born of the measurement arrives, the list is reviewed at
	this version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 50).
	make check now reports 638 passing lines (636 in 0.72.60), as the sum of its thirteen phases with the variables unset: the two assertions of check_ppresolve_components_paper.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	Pin identities for the parts that are not anchors: the instructions name them, and the loop needs no pin_map for camera-trap and solar-roller.

	* build_instructions.py: Step.near_pin and far_pin; a lead names its pin ("(pin S)") when the pins are not 1 and 2, or when the symbol names its two pins differently and the near lead is not pin 1
	(never for a generic passive, a part whose lead roles are stated, or a part with unnamed pins); a rail-direct source's step names its pins on each rail (Step.rail_pins, rail_pin_sentences()).
	* parse_instructions.py: the passive patterns read an optional "(pin X)" after each hole, the rail-direct source pattern its rail-pin sentences; reconstruct() registers the stated pins as real ones
	(the leads' pins, GND and VBUS), and keeps the synthetic pins for the older wording.
	* pptest_regression: check_anchor_pin_identities: the assertion that pinned the limitation (camera-trap with no pin_map names J2 and J3) now asserts that camera-trap and solar-roller come back with none,
	and a new one that the text of 0.72.59 and before still works; check_pin_identities_non_anchors (four assertions); one expected sentence of check_far_lead_sides; a weight.
	* ppbuild_instructions.1, ppparse_instructions.1, ppresolve_components.1, Concept.org. FutureResearch.org: the first item of the ordered list is done and leaves it, a blocked item (audio-jack's loop) is
	born of it, the list is reviewed at this version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 49).
	make check now reports 636 passing lines (631 in 0.72.59), as the sum of its thirteen phases, with the variables unset and with PACKPAY_PHYSICAL_ANCHORS=0, PACKPAY_FAR_LEAD_SIDES=0 and
	PACKPAY_PHYSICAL_HEADERS=0 together.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	ppextract_sources puts its default symbols directory next to its output; make-archive refuses a stray top-level entry.

	* ppextract_sources: the default --symbol-dir is STEM-symbols in the directory of the sources file (it was resolved against the current directory); an output file in a directory that does
	not exist is refused before anything is written. ppextract_sources.1 follows.
	* make-archive: after writing the archive it lists the top-level names and refuses (exit 1, no archive left) any that Makefile.am does not name, configure.ac excepted.
	* pptest_regression: check_ppextract_sources gains three assertions (the default directory beside the output with a different working directory; no -o; a missing output directory);
	check_make_archive_refuses_strays (two assertions: the shippable set of the tree archives; a stray directory and a stray file are refused, named, and no archive is left).
	* DevelopmentStandard.org: the Delivery Checklist mentions the guard. FutureResearch.org: the first item of the ordered list is closed, the list is renumbered and reviewed at this
	version; SessionHandoff.org mirrors it. ResearchJournal.org (continued 48).
	make check now reports 631 passing lines (626 in 0.72.58), as the sum of its thirteen phases with the variables unset: three more assertions of check_ppextract_sources and two of
	check_make_archive_refuses_strays.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	The ordered, maintained list of open issues; the breadboard configuration tests back in make check.

	* FutureResearch.org: rewritten from 62 headings to 24 open items, each with a ranking line (urgency, value, effort, blocked), under a list ordered by a stated rule; the closed items
	removed (all documented in ResearchJournal.org, continued 47), the duplicates merged, the long items condensed, the forgotten ones listed.
	* pptest_regression: _future_research_problems() and check_future_research_list (three assertions: the list is well formed, ordered and reviewed at the tree's version; the check fails
	on five deliberately broken copies; unblocked items come before blocked ones); check_test_phases gains one assertion (check1 runs pptest_breadboard_config and the Makefile no longer says
	it is skipped); the nested-hierarchy test no longer needs anchor_spacing=60 for three copies of ppexample_circuit2.
	* pptest_breadboard_config: --extreme runs the three extreme-scale checks (1300, 1600 and 3200 passives); without it the other fifteen run (85 assertions, two seconds). Makefile.am:
	check1 runs it. pptest_breadboard_config.1 and DeveloperDebugging.org follow.
	* DevelopmentStandard.org: the Delivery Checklist reviews and presents the ordered list; the rule of looking at and sharing the rendered result of a change that can move a layout, migrated
	from FutureResearch.org. SessionHandoff.org: Next Steps mirrors the list. ResearchJournal.org (continued 47).
	make check now reports 626 passing lines (537 in 0.72.57), as the sum of its thirteen phases with the variables unset: the 85 restored checks of pptest_breadboard_config, three of
	check_future_research_list and one of check_test_phases.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	The step of a part whose pins would share a column no longer contradicts itself.

	* build_instructions.py: the note of the class "shorts" reads "Its two rows of pins are only one hole apart, ... so mount it on a breakout board or use a jumper lead for each pin."
	(it no longer says "straddle the trench"), and the base sentence of such a step says "one pin per column" instead of "straddling the trench"; the comment states that the sentence assumes
	two rows one hole apart, as every part of the class so far. parse_instructions.py: the new note and the new base wording, the latter beside the two it already reads.
	* pptest_regression: check_part_fit follows (the note, the parse of a step in the new wording, rp2040's J3) and gains one assertion: a step of that class says "one pin per column" and
	contains no "straddl", while a surface-mount part's step still says "straddling the trench".
	* Concept.org and ppbuild_instructions.1 quote the new sentence. FutureResearch.org: the status. ResearchJournal.org (continued 46); SessionHandoff.org.
	make check now reports 537 passing lines (536 in 0.72.56), as the sum of its thirteen phases, with the variables unset and with PACKPAY_PHYSICAL_ANCHORS=0,
	PACKPAY_FAR_LEAD_SIDES=0 and PACKPAY_PHYSICAL_HEADERS=0 together.

2026-10-11  Péter Felecan  <pfelecan@gmail.com>

	Two-row sockets: they cannot be plugged into a breadboard, and the instructions say so.

	* build_instructions.py: pins_share_a_strip(), true when no way of plugging a through-hole part in (each axis along the strips, every place the trench can fall between rows at
	least three pitches apart, five rows to a half) keeps its pins in strips of their own; footprint_fit() gives such a part the class "shorts" and the sentence "Its pins are too close
	together to straddle the trench, so two of them would share a column of holes and be shorted: ...", where it had said "plug-in" and nothing; parts_note() has a group for it, after
	the surface-mount one. The header line on pin holes names a DIP wider than 7.62 mm, no longer a two-row socket, as the package that is not placed by its footprint.
	* parse_instructions.py: the sentence is an alternative of the optional fit suffix of an anchor step, recorded as fit "shorts".
	* pptest_regression: check_part_fit follows (the socket and a two-row header are "shorts", a 28-pin DIP of 15.24 mm is plug-in, the tally of the 30 anchors is 8 plug-in, 1 shorts, 4
	offgrid, 7 smd, 10 unknown) and gains three assertions: the rule on hole patterns, the sentence and its parse and its group in the line under the title, and rp2040's socket end to end.
	* Concept.org: the best practice "Never let two pins of one part share a column of holes", and the status of the orientation practice. ppbuild_instructions.1, ppparse_instructions.1.
	FutureResearch.org: the status, which also corrects the plan for sockets. ResearchJournal.org (continued 45); SessionHandoff.org.
	make check now reports 536 passing lines (533 in 0.72.55), as the sum of its thirteen phases, with the variables unset and with PACKPAY_PHYSICAL_ANCHORS=0,
	PACKPAY_FAR_LEAD_SIDES=0 and PACKPAY_PHYSICAL_HEADERS=0 together.

2026-10-10  Péter Felecan  <pfelecan@gmail.com>

	The physical layout of single-row headers is the default.

	* auto_layout.py: physical_headers_enabled is on unless PACKPAY_PHYSICAL_HEADERS=0 (it was on only for 1); the comment says what it is now.
	* build_instructions.py: the header line on pin holes names a two-row socket, no longer a pin header, as the package that does not follow the diagram.
	* pptest_regression: check_physical_headers reads the default in a fresh process with the variable removed and asserts that 0 turns it off (one assertion more); its comments say
	that the headers are on by default.
	* ppbuild_instructions.1 and pprender.1: PACKPAY_PHYSICAL_HEADERS is on unless 0. Concept.org: the best practice and the glossary entry for pin 1 describe the header as done.
	* FutureResearch.org: the status. ResearchJournal.org (continued 44); SessionHandoff.org.
	make check now reports 533 passing lines (532 in 0.72.54), as the sum of its thirteen phases, with the variables unset and with PACKPAY_PHYSICAL_ANCHORS=0,
	PACKPAY_FAR_LEAD_SIDES=0 and PACKPAY_PHYSICAL_HEADERS=0 together.

2026-10-10  Péter Felecan  <pfelecan@gmail.com>

	Single-row headers in one row (experimental), and the rails of a physical part on its own pins.

	* auto_layout.py: physical_headers_enabled (on only for PACKPAY_PHYSICAL_HEADERS=1); physical_row_geometry(), a single line of through-hole pads on the 2.54 mm grid, in
	the same shape as a DIP's geometry; the anchor placement takes a header through the physical branch, in the upper half, and records it in Placement.anchor_single_half; its rail
	columns are in that half. For EVERY physical anchor a rail's column is now the column of its first rail pin, in that pin's half, and the block of columns counts only the
	rails that have no pin in the part (it was col + width + j, beyond the part).
	* breadboard_lib.py: ic_row(), a thin body around the row-0 holes of one half with a square at pin 1. render.py draws a header with it.
	* build_instructions.py: Step.single_row; "standing in one row" for "straddling the trench" and "Its pin 1 is at the left." for the notch sentence. parse_instructions.py: both wordings.
	* pptest_regression: new check_physical_headers (6 assertions, registered, weighted): the geometry, camera-trap's J1 in one half in consecutive columns (and unchanged with
	the experiment off), the rail columns of knight-roller's three headers on their own pins, the wording and its parse (and no header line on pin holes when every anchor is
	physical), the loops of camera-trap and knight-roller with the experiment on, and the altimeter untouched. check_physical_dip_layout forces the experiment off in its in-process
	layouts. check_synthetic_dip_circuit states the measured ceiling of label overlaps for the physical layout (at most 2, with the reason).
	* ppbuild_instructions.1 and pprender.1: PACKPAY_PHYSICAL_HEADERS. Concept.org: the best practice. FutureResearch.org: the status. ResearchJournal.org (continued 43); SessionHandoff.org.
	make check now reports 532 passing lines (526 in 0.72.53), as the sum of its thirteen phases, with the variables unset, with PACKPAY_PHYSICAL_HEADERS=1, and with
	PACKPAY_PHYSICAL_ANCHORS=0 and PACKPAY_FAR_LEAD_SIDES=0 together.

2026-10-10  Péter Felecan  <pfelecan@gmail.com>

	A far-lead search that tries both sides, and a far lead that stays on its zone.

	* breadboard_lib.py: leg() takes preferred_cols, the columns of the lead's half that already hold its own net, and tries them first, nearest first on either side, right
	first at equal distance, within the same four columns, before the right-first sweep; far_lead_prefers_own_net is on unless PACKPAY_FAR_LEAD_SIDES=0; resistor(), capacitor(),
	inductor() and diode() pass it through. _at_least_1(), the guard that keeps the lead on the board, now requires the candidate column to be in the near column's own sector
	(it was "at least 1" on the global column, which protects only the first zone: next to column 1 of a later zone the lead could land in "hole e100000").
	* render.py: the preferred columns are those of this half that hold the far net, for a far net that is not a rail.
	* pptest_regression: new check_far_lead_sides (4 assertions, registered, weighted): leg() on its own (no preference is the old sweep; either side; right first at equal
	distance; nearest first; four columns and no further; never over a forbidden column; the flag off), the sector guard (it fails on the old guard, which puts the lead in
	column 100000), camera-trap's audio jack in pin 4's own column with no jumper and the jumper back with the variable at 0, and rp2040 with no hole of five digits. The far-lead
	count of camera-trap is two only with the physical DIP and without the preference. check_instructions_far_lead_links follows the preference too: its dual op-amp circuit
	loses the jumper for OUT2 (R4's far lead goes into pin 7's own column), so 10 steps and four jumpered nets instead of 11 and five, as before with the variable at 0.
	* ppbuild_instructions.1 and pprender.1: PACKPAY_FAR_LEAD_SIDES. FutureResearch.org: the status and the measurements. ResearchJournal.org (continued 42); SessionHandoff.org.
	make check now reports 526 passing lines (522 in 0.72.52), as the sum of its thirteen phases, with the variables unset, with PACKPAY_FAR_LEAD_SIDES=0, and with that and
	PACKPAY_PHYSICAL_ANCHORS=0 together.

2026-10-09  Péter Felecan  <pfelecan@gmail.com>

	The physical DIP layout is the default.

	* auto_layout.py: physical_anchors_enabled is on unless PACKPAY_PHYSICAL_ANCHORS=0 (it was on only for 1). The comment block says what it is now.
	* build_instructions.py: the header line on pin holes gives a pin header and a two-row socket, not a DIP, as the packages that do not follow the diagram.
	* pptest_regression: the far-lead count of camera-trap follows the variable (two unless it is 0); the two tripwires of check_ic_orientation_gap say that they pin the
	diagram layout; check_physical_dip_layout reads the default in a fresh process with the variable removed and asserts that 0 turns it off; check_synthetic_dip_circuit
	names its two layouts "diagram" and "physical"; the assertion on the pin-holes line follows its wording. The suite passes with the variable unset and with it set to 0.
	* Concept.org: the best practice and the glossary entry for pin 1 describe the DIP as done. ppbuild_instructions.1 and pprender.1: PACKPAY_PHYSICAL_ANCHORS is on unless 0.
	* FutureResearch.org: the status. ResearchJournal.org (continued 41); SessionHandoff.org.
	make check now reports 522 passing lines (the same as in 0.72.51), as the sum of its thirteen phases, with the variable unset and again with PACKPAY_PHYSICAL_ANCHORS=0.

2026-10-09  Péter Felecan  <pfelecan@gmail.com>

	A synthetic circuit with two real DIPs through both layouts (no change to the tools).

	* pptest_regression: _synthetic_dip_circuit(), a helper that builds, from a specification, an NE555 astable (DIP-8) clocking a 74HC595 (DIP-16) that drives eight
	LEDs, with a polarized capacitor and a net between two pins of one DIP (24 components, 23 nets), and writes it as a schematic that carries its footprints (the
	auto-layout writer passes none, so they are supplied for this circuit only, while it is generated); check_synthetic_dip_circuit (4 assertions, registered, weighted):
	the circuit is what its specification says; in the default layout the structural checks report 0 failures and it comes back from its own instructions with an empty
	pin map, every net of the specification equal; in the physical layout the same, with 0 label overlaps, two notch sentences, and the register a real DIP-16 of 8
	columns, the oscillator a DIP-8 of 4, sharing no column.
	* FutureResearch.org: the status, and the finding that the default layout leaves 4 label overlaps in this dense circuit (the physical one 0).
	* ResearchJournal.org (continued 40); SessionHandoff.org.
	make check now reports 522 passing lines (518 in 0.72.50), as the sum of its thirteen phases.

2026-10-09  Péter Felecan  <pfelecan@gmail.com>

	IC orientation, step C: a physical layout for a dual-row DIP, behind PACKPAY_PHYSICAL_ANCHORS=1 (off by default; with it off nothing changes).

	* auto_layout.py: physical_anchors_enabled; physical_dip_geometry() (a dual-row through-hole footprint with its rows 7.62 mm apart gives each pad a column
	offset and a half: notch left, pin 1 in the lower row at the left, counter-clockwise); in the anchor placement a physical branch (pins at their footprint
	columns, the rail columns after the physical width); Placement.anchor_physical. The placement bookkeeping learns SPLIT columns, the ones both halves of a
	physical anchor hold: ColumnOwners (one owner net per half), half_depth (the next free row of each half, with the row reserved for a wire on a net of three or
	more members kept in the pin's own half), and the attach site reading and writing it; col_depth stays the envelope for the later phases. With no physical anchor
	the set of split columns is empty and every key is what it was.
	* build_instructions.py: Step.physical; the sentence "Its notch faces left: pin 1 is in the lower row, at the left."; the header line on pin holes is not
	written for a physical part. parse_instructions.py: the optional notch sentence.
	* pptest_regression: new check_physical_dip_layout (7 assertions): the geometry of a DIP-4 and a DIP-14, the layout with the switch off and on, the
	instructions and their parse, the loop closing with the PC817, ColumnOwners, the re-key (a hub net in one half no longer pushes the other half down), and a second
	DIP (an LM358 in a DIP-8, resolved with an empty pin map). The assertions that pin the default layout (the two of check_ic_orientation_gap, the default being off)
	and the number of far-lead links (camera-trap has two with the DIP) hold whichever the environment of the suite; the loop fixtures name J3's nets in both forms; the
	zones fixture is 6 columns (a DIP takes 2 columns, so 8 now fits one zone). The suite passes with the variable set and unset.
	* ppbuild_instructions.1 and pprender.1: an ENVIRONMENT section. Concept.org: the best practice records it. FutureResearch.org: the status.
	* ResearchJournal.org (continued 38 and 39); SessionHandoff.org.
	make check now reports 518 passing lines (511 in 0.72.49), as the sum of its thirteen phases, with the variable unset and again with PACKPAY_PHYSICAL_ANCHORS=1.

2026-10-09  Péter Felecan  <pfelecan@gmail.com>

	IC orientation, step A: the anchor steps state their pins' identities.

	* build_instructions.py: an anchor step lists every pin with its hole, from the left ("Its pins, from the left: 1 in a5, 2 in a6, ..."), after the
	leftmost-pin sentence, and names the pins on each rail after the rail sentence ("Its pins on the ground (-) rail: 2."): pin_listing(),
	rail_pin_sentences(); Step gains pins and rail_pins (also in __slots__); pins_note() is the header line that says the holes follow the diagram,
	shown only when a part that plugs in has its pins listed. A pin id with a comma, a space or a period keeps the older wording for that part.
	Unconnected nets are no longer counted as rails (an unconnected GND pin put a part on the power rail's side).
	* parse_instructions.py: the pin list and the rail pins are optional parts of the anchor sentence, recorded as real pins at their holes
	(no opaque "0" or "_pin@<column>" for them) and on the rail nets (no "_rail_-"); the header line is dropped like the Board line.
	* pptest_regression: new check_anchor_pin_identities (6 assertions, written first, red): the sentences and their order, the parser, reconstruct()
	registering real pins, the header line, and, end to end, the altimeter and knight-roller with NO pin map and camera-trap with a pin map naming
	only its two non-anchors. check_ppresolve_components_same_pin_merge runs its refusals on an older-format text and gains an assertion that such a
	text still resolves. check_ic_orientation_gap's third tripwire says what is true now.
	* Concept.org, ppbuild_instructions.1, ppparse_instructions.1, ppresolve_components.1, FutureResearch.org.
	* ResearchJournal.org (continued 37); SessionHandoff.org.
	make check now reports 511 passing lines (504 in 0.72.48), as the sum of its thirteen phases.

2026-10-09  Péter Felecan  <pfelecan@gmail.com>

	IC orientation, step B: the instructions say which multi-pin parts do not plug into a breadboard.

	* build_instructions.py: footprint_fit() reads the part's KiCad footprint (the pad number is quoted in some library files and not in others)
	and classifies it plug-in, smd, offgrid or unknown; parts_note() names the parts that do not plug in; Step gains fit and fit_note (also in
	__slots__); the anchor sentence ends with the note, after the rail and power-role sentences; the four formatters put the line right after
	the title; the line is wrapped like a step, never at a hyphen (_wrap_note). A plug-in part is worded exactly as before.
	* parse_instructions.py: an optional third suffix of the anchor sentence, recorded as fit; the header line is dropped like the Board line.
	* pptest_regression: new check_part_fit (7 assertions, written first, red): the classifier on real footprints, the notes, the sentence and
	its place, the parser on every wording, the header in all four formats, camera-trap (nothing said) and knight-roller (U1 surface-mount, U2
	unknown) end to end, and the measured finding over the seven designs (30 anchors: 9 plug-in, 4 offgrid, 7 smd, 10 unknown).
	* Concept.org: a glossary entry for breakout board; the best practice on orienting an IC records step B. ppbuild_instructions.1 and
	ppparse_instructions.1 gain a section. FutureResearch.org: the item's status.
	* ResearchJournal.org (continued 36); SessionHandoff.org.
	make check now reports 504 passing lines (497 in 0.72.47), as the sum of its thirteen phases.

2026-10-09  Péter Felecan  <pfelecan@gmail.com>

	IC orientation: the gap measured, documented and pinned (no change to the instructions).

	* pptest_regression: new check_ic_orientation_gap (3 assertions, registered): the PC817 of camera-trap is a real breadboard IC (DIP-4, two rows
	7.62 mm apart, two columns, read from KiCad's footprint); a tripwire that the layout gives its 4 pins 4 distinct columns (5 to 8, two per half)
	where the package has 2; a tripwire that its anchor sentence names only the hole of the leftmost pin and says nothing of pin 1 or the notch.
	* Concept.org: the best practice "Put an IC in with its pin 1 where the mark says" (sources S17 to S19), status open, and the glossary entries DIP
	and pin 1. FutureResearch.org: the item "IC orientation and physical fidelity" with the measurements on the seven designs (30 anchors: 13
	through-hole, 7 surface-mount or mixed, 10 without a footprint file) and three options.
	* ResearchJournal.org (continued 35); SessionHandoff.org.
	make check now reports 497 passing lines (494 in 0.72.46), as the sum of its thirteen phases.

2026-10-09  Péter Felecan  <pfelecan@gmail.com>

	The step wording no longer repeats the type of a part; the reader-facing documents name nobody.

	* build_instructions.py: new value_and_type(): "{value} {type}" without the type twice when the value already ends with its words (an
	LED whose value is "LED", a photodiode, a battery whose value carries its part name); both passive sentences use it. The anchor
	sentence already had the same guard. parse_instructions.py is unchanged: it reads the old wording and the new.
	* pptest_regression: new check_step_wording_does_not_repeat_the_type (4 assertions, written first, red): the function on ten cases,
	the two sentences and the spoken wording, the parser on both wordings, and knight-roller's seven diode and LED steps. New
	check_reader_facing_documents_name_nobody (3 assertions, written first, red on 94 lines of the ChangeLog and 4 of NEWS): the
	ChangeLog (entry headers excepted), NEWS and the newer sections of Concept.org name nobody. Both registered in CHECK_NAMES.
	* ChangeLog and NEWS: every earlier entry reworded so that it states the fact or the decision and names nobody (98 lines in all); the
	changed paragraphs were re-wrapped to the file's width. ppbuild_instructions.1: the example uses the new wording.
	* pptest_regression: check_far_lead_on_rail_fed_net matches the wrapped instruction text with its whitespace normalised (it depended on
	where a line happened to wrap, which the shorter sentence moved).
	* FutureResearch.org: the repeated-type item DONE. ResearchJournal.org (continued 34); SessionHandoff.org.
	make check now reports 494 passing lines (487 in 0.72.45), as the sum of its thirteen phases.

2026-10-09  Péter Felecan  <pfelecan@gmail.com>

	LED polarity, stage 2: the resolver uses the lead roles; polarised capacitors by the library's convention.

	* resolve_components.py: new apply_lead_roles(nl, sym_dir): for each part whose leads carry a recorded role it reads the symbol's pin
	NAMES (parse_kicad_sym_pin_names) and renumbers the part's two leads onto the pins whose names say the same thing, only where the
	symbol is clearly polar and the roles are consistent (one role given: the other lead is its opposite); the netlist's nodes are
	rewritten in place and its cached pin index refreshed. ppresolve_components calls it right after the symbols are resolved.
	* netlist.py: polar_pins() (the symbol's positive and negative pin numbers: by name, else by the library's convention for the
	polarised capacitors, whose pins are both named "~": pin 1 positive, checked against the drawing of C_Polarized and C_Polarized_Small);
	lead_polarity() falls back to it. In 0.72.44 polarised capacitors were therefore never recognised; they are now.
	* pptest_regression: new check_resolver_uses_lead_roles (5 assertions, written first, red): an LED renumbered onto the symbol's pins,
	one already right left untouched, a polarised capacitor by role, one given role completing the other, and the cases left alone (a
	resistor, contradictory roles, an unresolved part). check_knight_roller_loop compares D1 to D7 EXACTLY (its tripwire on the resolver is
	gone: the gap is closed); a control with the one call removed fails that comparison. check_lead_polarity gains the capacitor cases.
	* Concept.org (the best practice names the convention), ppbuild_instructions.1, FutureResearch.org (the item DONE).
	* ChangeLog: entries written since 0.72.39 no longer carry attributions.
	* ResearchJournal.org (continued 33); SessionHandoff.org.
	make check now reports 487 passing lines (483 in 0.72.44), as the sum of its thirteen phases.

2026-10-09  Péter Felecan  <pfelecan@gmail.com>

	LED polarity in the build instructions (the best-practice gap "say which way round the LEDs, diodes and electrolytic capacitors go").

	* netlist.py: polarity_word() (A, K, anode, cathode, +, -, plus, minus read from a pin name) and lead_polarity() (the roles of a part's
	two leads, or None unless one is positive and one negative).
	* build_instructions.py: role_phrase() with the physical hints (LED anode = the longer lead, cathode = the shorter lead on the flat side;
	diode cathode = the banded end; capacitor positive = the one marked +); Step gains near_role and far_role (also in __slots__); the
	passive sentence names them: "one lead into hole g18 (the cathode, ...), the other into hole j20 (the anode, ...)", in both wordings.
	The docstring's disclaimer is replaced: polarity is described, the orientation of an IC or a connector is not.
	* parse_instructions.py: the two passive patterns accept the role annotation, the step dict carries near_role and far_role, and
	reconstruct() records a named role as the lead's pin name in the rebuilt netlist (near lead = pin 1, far lead = pin 2).
	* pptest_regression: new check_lead_polarity (6 assertions, written first, red): the pin-name reader, the roles, the phrases, the parser
	on four sentences, knight-roller's seven polar parts, and an ORACLE independent of the resolver (the netlist rebuilt from the text,
	each lead identified by its stated role, partitions the LED leads into nets as the original does with its pin names).
	check_knight_roller_loop: the old tripwire ("the text never says anode") becomes a positive check plus a new tripwire on the resolver.
	check_best_practices: the polarity gap leaves the tripwire. Registered in CHECK_NAMES.
	* Concept.org: the best practice "say which way round..." is now guarded by check_lead_polarity; a glossary entry "polarity".
	ppbuild_instructions.1 and ppparse_instructions.1 gain a POLARITY section.
	* FutureResearch.org: the polarity item DONE; new items: the resolver using the roles (stage 2), the step wording repeating the type
	("the LED LED"), the orientation of ICs and connectors.
	* ResearchJournal.org (continued 32); SessionHandoff.org.
	make check now reports 483 passing lines (476 in 0.72.43), as the sum of its thirteen phases.

2026-10-08  Péter Felecan  <pfelecan@gmail.com>

	Split rails are read (the exploration of the taxonomy's findings F4 and F5; the first best-practice gap closed).

	* build_instructions.py: board_note() reads RailConfig.continuous (parsed and written back since the rail model was written,
	read by nothing until now): a board declared not continuous gets a header that says its rails are split, to be checked with a
	multimeter and bridged with a jumper; a board that is not declared split says nothing (every existing text is unchanged).
	* pptest_regression: new check_split_rail_note (3 assertions, written first, red on two): the note and its wording, the default says
	nothing, the parser still reads the header. check_best_practices: the split-rail tripwire is retired (the gap is closed).
	* Concept.org: the taxonomy's F4 and F5 explored (sources T9 to T12), the best practice "bridge the split power rails" is now
	guarded by check_split_rail_note (sources S15, S16), a glossary entry "split rail". ppbuild_instructions.1, breadboard_config.5.
	* FutureResearch.org: the exploration item DONE; a new item: MultiboardConfig's stale docstring and the renderer's multi-board
	output, unverified.
	* make check in phases: Makefile.am: check-local depends on check1 ... check13 (check1 the netlist suite, check2 the example circuits
	and their ERC, check3 the build-instructions suite, check4 to check13 the ten slices of pptest_regression, each depending on check2);
	pptest_regression: the registry CHECK_NAMES (59 checks, in run order), CHECK_WEIGHTS (measured seconds), phase_slices(), and the options
	--phases N --phase K, --only, --list, --timings; new check_test_phases (4 assertions, written first, red): every check function is
	registered, the slices are a partition, the phases are balanced, the Makefile runs every slice. DeveloperDebugging.org (a section on the
	phases), pptest_regression.1, DevelopmentStandard.org.
	* Concept.org: the openers of the Taxonomy, Best practices and Glossary sections trimmed, and the findings of the taxonomy state facts
	without attributions; NEWS.
	* ResearchJournal.org (continued 29, 30, 31); SessionHandoff.org (the operational and documentation rules).
	make check now reports 476 passing lines (469 in 0.72.42), as the sum of its thirteen phases.

2026-10-08  Péter Felecan  <pfelecan@gmail.com>

	The classic board is 63 columns (the taxonomy's finding F1), and the decisions on F2 to F5 are recorded.

	* auto_layout.py: CLASSIC_COLUMNS 65 -> 63, with its comments. Measured first: all seven shipped designs get the same tier and the
	same placement on 63 as on 65, rp2040 (two zones) included. SectorConfig's default and breadboard1.conf, breadboard2.conf stay at
	65: they describe boards of 65 strips, and a layout for 63 fits them.
	* pptest_regression: check_escalation_policy pins 63 (red against 65 first); the E2 test's words no longer call its explicit
	65-column board "the classic board". check_taxonomy needed no change: it forced Concept.org to follow the constant.
	* Concept.org: the taxonomy's findings F1 to F5 now record the decisions; the other statements of the width follow. pprender.1,
	ppbuild_instructions.1.
	* FutureResearch.org: the answers; new items "Perfboard and stripboard" ([#B]) and "Explore split rails and joined boards".
	* ResearchJournal.org (continued 28); SessionHandoff.org.
	make check now reports 469 passing lines (469 in 0.72.41: no assertion was added).

2026-10-08  Péter Felecan  <pfelecan@gmail.com>

	The taxonomy of breadboards (the third [#A] research item).

	* Concept.org: new section "Taxonomy of breadboards" before the Best practices: scope, five axes, a table of four classes,
	the project's mapping, five findings (F1 to F5), 8 sources; Workflow.org and Index.org point to it.
	* pptest_regression: new check_taxonomy (6 assertions, written first, red on all six): the section order, every class of the
	table adds up (zones x strips x ten + rail points = tie points), the numbers quoted from the code (CLASSIC_COLUMNS, the rows
	ladder) are the code's own, a tripwire on the model's shared rail between zones, every code name and source exists, the
	references and the findings item.
	* FutureResearch.org: the taxonomy item DONE as a first version; a new item "Taxonomy findings" (63 columns,
	rows beyond 5, rails between zones, split rails, how boards join); the glossary item records the confirmation of all four
	naming decisions.
	* ChangeLog: the 0.72.40 entry below now lists the journal's (continued 25, 26), which it had omitted.
	* ResearchJournal.org (continued 27); SessionHandoff.org.
	make check now reports 469 passing lines (463 in 0.72.40).

2026-10-08  Péter Felecan  <pfelecan@gmail.com>

	The glossary and the best practices (the first two [#A] research items).

	* Concept.org: new last real section "Glossary" (naming decisions, breadboard terms, scheme terms, project terms,
	sources), 51 entries; Workflow.org: a pointer paragraph in its first section; Index.org: the concept entry mentions
	the glossary.
	* pptest_regression: new check_glossary (6 assertions, written first): the 37 required terms, every ~code name~ cited
	exists in the source, the zone/sector and column/row decisions, the Workflow and Index references, and every
	file:X.org::*Heading link in the documents resolves.
	* Concept.org: new section "Best practices" before the Glossary: 12 practices for the breadboard, 9 for the scheme, each with Why,
	Sources (14) and In packpay (10 guarded by named tests, 1 encoded, 7 open, 3 outside the tool); Workflow.org and Index.org point to it.
	* pptest_regression: check_best_practices (6 assertions, written first, red with 5 failures): the section precedes the Glossary,
	every practice has its three lines, every source cited is defined, every status is TRUE of the project (tests and code names
	exist, open items name a research item that exists), the OPEN gaps are tripwires, the Workflow and Index references.
	* FutureResearch.org (the glossary item DONE as a first version; the best-practices item DONE as a first version; a new item,
	"Gaps the best-practices research found");
	ResearchJournal.org (continued 25, 26); SessionHandoff.org.
	make check now reports 463 passing lines (451 in 0.72.39).

2026-10-08  Péter Felecan  <pfelecan@gmail.com>

	The escalation policy: a classic board, 2 zones, boards one at a time, rows last; zones named in the instructions.

	* auto_layout.py: classic_board(), escalation_board_tiers(), walk_escalation() (rows outer, every board
	arrangement tried at a row count: NO stall rule), left_bias_share(), is_left_biased() (LEFT_BIAS_THRESHOLD
	0.70), CLASSIC_COLUMNS/MAX_ZONES_PER_BOARD/MAX_BOARDS. compute_optimal_config(): no base_config means classic_board()
	(it was unbounded in width); max_sectors=None (when given: the old single-board ladder); new max_zones, max_boards;
	every returned layout has p.left_bias_share.
	* build_instructions.py: hole_name() writes "a5 (zone 2)" from zone 2 on (a hole name is zone-relative: U1 and J1 were
	both told "hole a5" on rp2040); board_note() also states the number of zones. parse_instructions.py: the eight hole
	groups accept "(zone N)", parse_hole() reads it. pprender.1, ppbuild_instructions.1, ppparse_instructions.1.
	* pptest_regression: check_escalation_policy (9 assertions), check_left_bias_detector (2),
	check_zones_are_named_in_the_instructions (3, incl. the two-zone round trip on camera-trap at 8 columns).
	* FutureResearch.org (three [#A] research items: glossary, best practices, taxonomy); ResearchJournal.org
	(continued 23, 24); SessionHandoff.org.
	make check now reports 451 passing lines (437 in 0.72.38).

2026-10-07  Péter Felecan  <pfelecan@gmail.com>

	E1 + E2 of the left-side-bias research: knight-roller 10 -> 5 rows, rp2040 7 -> 5 rows.

	* auto_layout.py: free_rows(col) -- the free holes of a column (row 0 counts where there is no anchor pin);
	wire_row() uses it when its counter has run past the strip (E1, flag free_hole_landing_enabled); landed_rows
	is function-level (it was per net: two nets could get the same hole); a net's columns are joined as a CHAIN
	(nearest joined column with a free hole) only when the hub has none left; relocate_overflowing_passives()
	(E2, flag spread_overflow_enabled) runs AFTER the relief loop and spreads a passive whose body would end past
	the strip to the nearest column with room, repointing placed_pin_col and releasing the old depth.
	* render.py: reserved far columns (p.bridged_far_col) are seeded into column_net (setdefault).
	* breadboard_lib.py: _label_obstacle_depth() also forbids the space outside the board's vertical limits.
	* pptest_regression: check_wire_landing_uses_free_holes (E1 isolated, 6 -> 0), check_overflowing_passives_
	spread_to_other_columns (the 2x2: 10 / 6 / 4 / 0 problems, plus "it renders"), check_part_labels_stay_on_
	the_board (red on MTR2 first); the push_bridged_dependents control now runs on the OLD layout.
	A/B, 7 designs: 5 byte-identical; knight-roller 10 -> 5 rows, rp2040 7 -> 5 rows, 0 collisions. The placement
	table: 7 of 35 entries (example-circuit1 x2, rp2040 x2, knight-roller x3) went from overflowing to fitting,
	each measured with the switches off (== the old pin, problem count included) and on; the other 28 unchanged.
	* auto_layout.py: strip_rows(board_config, col); take_free_row(taken, col, half, rows) -- rows 1..rows-1, then
	row 0, then past the end only if the strip is full (it returned rows past a 5-row strip: ppbuild_instructions
	crashed in row_letter() on knight-roller); render.py and build_instructions.py pass the strip's size.
	* FutureResearch.org; ResearchJournal.org (continued 22); SessionHandoff.org.
	make check now reports 437 passing lines (428 in 0.72.37).

2026-10-07  Péter Felecan  <pfelecan@gmail.com>

	Jumper arcs inside the board, part labels off the rails.

	* breadboard_lib.py: prepare_board_limits(board_config) -- the outline's vertical limits and the y of every
	rail, from the config with the chrome's own formulas (_arc_limits, _label_obstacles; both cleared by
	reset_module_state()). jumper() acts ONLY when the arc about to be drawn would leave the limits: a shorter
	bulge, else the other way, choosing the first candidate inside that clears everything drawn, else the least
	bad. flush_labels() scores a candidate as max(_overlap_depth, _label_obstacle_depth).
	* render.py: render() calls prepare_board_limits() right after its own reset. (Labels are flushed BEFORE the
	chrome is drawn, which is why the rails come from the config and not from the drawing.)
	* breadboard_lib.py: flush_labels() searches two further rings (130 and 180 px) AFTER the old last one (90):
	with the rails refused a label could have no clear spot within 90 px and fell back to one overlapping a
	jumper arc (knight-roller, caught by check_render_sweep). A label that already found a clear spot is
	untouched. Cost: knight-roller's D6 label is ~130 px from its part.
	* pptest_regression: new check_rendered_svgs_stay_on_the_board (3 assertions: arcs past the outline, labels
	on a rail, a switched-off control), the SVG TEXT as the oracle, over seven designs incl. rp2040. Red first.
	A/B, 7 designs: solar-roller, camera-trap, audio-jack identical; altimeter, dudhya, knight-roller, rp2040
	changed, each looked at.
	* FutureResearch.org (arcs and labels DONE); ResearchJournal.org (continued 18); SessionHandoff.org.
	make check now reports 428 passing lines (425 in 0.72.36).

2026-10-07  Péter Felecan  <pfelecan@gmail.com>

	The knight-roller loop: the board stated, a pin_map list, power-only breadboard rails.

	* build_instructions.py: board_note(board_config); the four formatters take board_note.
	ppbuild_instructions passes it. States "Board: N rows per half (...)" for a single board
	whose rows_per_half is not the default; a default board's text is unchanged.
	* parse_instructions.py: board_rows_per_half(text), board_config_for(text, board_config)
	(a contradicting config is a ParseError); parse_instructions() skips the line.
	ppparse_instructions and ppresolve_components use board_config_for().
	* resolve_components.py: resolve_pin_identities() takes a LIST of real pins as a pin_map value.
	* auto_layout.py: breadboard_rail_nets(nl, threshold) -- more than `threshold` members AND a
	rail-like name or a power pin, when the netlist has pin types (else the degree rule). Used by
	layout() and the two call sites in build_instructions.py. high_degree_nets() is unchanged and
	still used by the schematic generator. Before: knight-roller's two LED lines were "rails" and
	the instructions put D6 and D7 on the + rail at both ends. Now 10 rows per half, 32 steps.
	* ppbuild_instructions.1, ppparse_instructions.1, ppresolve_components.1: "THE BOARD";
	ppresolve_components.1: pin_map arrays. Workflow.org.
	* pptest_regression: check_instructions_board_geometry (4), check_breadboard_rails_are_power
	(2), check_knight_roller_loop (4: the list semantics, the whole loop, every net with the
	polarized parts compared lead-agnostically, and a TRIPWIRE for the polarity gap). Written first.
	* pptest_regression: check_far_leg_stays_within_board_edges' real-circuit confirmation moved from
	knight-roller's D4 to example-circuit1's L10: knight-roller no longer puts D4 at column 1 at any
	rows_per_half 6-13 (its layout became correct), L10 does at 6 (measured on every shipped design).
	* auto_layout.py: layout()'s nested push_bridged_dependents(), called after run_declutter() moves a
	passive: any passive in the moved passive's bridged far column whose extent now contains its far lead
	is pushed to the row just past it (cascading). Found by LOOKING at knight-roller's render (R2's far lead
	on D2's wire run; run_declutter had moved R2 from row 1 to 4). Switch bridged_dependents_push_enabled.
	A/B, 7 designs: 6 byte-identical, knight-roller differs. pptest_regression: new
	check_rendered_layouts_have_no_hole_collisions (2 assertions, red first, with a switched-off control).
	make check now reports 425 passing lines (409 in 0.72.35).
	* pptest_regression: check_render_sweep's KNOWN_NEAR_LEG_BEND_WIRE_COLLISIONS is now EMPTY (knight-roller
	renders clean in the sweep's board config: measured, fix off = R2/D2 collision, fix on = clean; the old
	instance D3/D5 went away with the rail correction), and a TRIPWIRE reproduces the still-open CLASS with
	the previous rail rule put back (D3 far lead on D5 bend run): it fails the day leg() checks a bend run.
	* breadboard_lib.py: leg()'s last resort no longer chooses a far-leg neighbour by clearance
	alone: a forbidden column (one holding a different net) is never an option, and when nothing
	remains it raises a ValueError. It could draw R113's GND lead into U102's pin 6 column (coldfire):
	a short. Contradicts the branch's old "rather than raise" comment, replaced and said so.
	* pptest_regression: new check_far_leg_last_resort_respects_forbidden (2 assertions, red first);
	check_ppresolve_components_config_flag's "fails without --config" control flipped (it succeeds:
	the instructions state their board) plus a contradicting-config refusal.
	* pptest_regression: EXPECTED_RESULTS knight-roller / small-rows (True, True, 0) -> (False,
	True, 0), reason recorded: the LED lines are signal nets and need jumper rows a 6-row board
	lacks (measured against all 35 combinations, old rule as the comparison: the only change).
	* FutureResearch.org (polarity TODO; the loop DONE); ResearchJournal.org (continued 17);
	SessionHandoff.org.

2026-10-07  Péter Felecan  <pfelecan@gmail.com>

	Embedded symbols for the instructions route: ppextract_sources.

	* ppextract_sources: new command. Writes the symbols a schematic embeds (via
	embedded_symbol_libs(), sub-sheets included) to a directory and a sources file whose
	sym_path entries point at them; an empty pin_map; -o, --symbol-dir, --kicad-cli,
	--quiet; exit 1 and one message for a netlist, a missing file or an unreadable schematic.
	* resolve_components.py: sources_from_schematic(nl, embedded, installed_dir) -- the
	design's own symbol wins over an installed one; a part found in neither is reported.
	The 0.72.33 bad-sources message names ppextract_sources(1).
	* ppextract_sources.1 (new); Makefile.am (the page twice, the script once);
	make-archive (executable check); ppresolve_components.1 (SEE ALSO); Workflow.org.
	* pptest_regression: new check_ppextract_sources (6 assertions, written before the
	function existed): the pure logic, camera-trap, the two-sheet knight-roller, the refusals,
	and the tool's output driving the real camera-trap loop.
	* FutureResearch.org (the item DONE); ResearchJournal.org (continued 16);
	SessionHandoff.org.
	make check now reports 409 passing lines (403 in 0.72.34).

2026-10-07  Péter Felecan  <pfelecan@gmail.com>

	The "R resistor" value: every standard-library passive in the instructions route.

	* resolve_components.py: _strip_redundant_type_suffix() also removes exactly ONE
	trailing " {type_word}" for a part build_instructions.TYPE_NAMES knows (Device:R
	"resistor", Device:C "ceramic capacitor", ...), the exact inverse of the "{value}
	{type_name}" the instruction generator writes; before it removed only the part name, so
	"10k" came back as "10k resistor". Lazy import (build_instructions -> auto_layout ->
	power_sources circularity). Residual risk, documented: an anchor of such a part whose own
	value ends with the type word.
	* pptest_regression: new check_instruction_value_type_word (3 assertions, no kicad-cli);
	a VALUES assertion in check_ppresolve_components_multi_unit (4 passives) and in
	check_camera_trap_loop_rail_fed_far_lead (R160). Written first; 4 failed on the old code.
	* FutureResearch.org (the item DONE); ResearchJournal.org (continued 15);
	SessionHandoff.org.
	make check now reports 403 passing lines (398 in 0.72.33).

2026-10-07  Péter Felecan  <pfelecan@gmail.com>

	The raw traceback for an unusable sources entry.

	* resolve_components.py: the kind "kicad" branch of resolve_components() collects every
	unusable entry (a missing key; a sym_path that cannot be read; a file without the symbol)
	and raises ONE UnresolvedComponentError after the loop naming all of them, with a hint.
	They used to escape as KeyError, FileNotFoundError and LCSCImportError (a traceback from
	ppresolve_components), one part at a time.
	* pptest_regression: new check_ppresolve_components_bad_sources_clean_error (2
	assertions, written first and seen to FAIL on the old code): camera-trap's J1 and J2
	against the installed Connector library, and all three failure kinds in one call.
	* FutureResearch.org (edge (a) DONE, (b) open); ResearchJournal.org (continued 14);
	SessionHandoff.org.
	make check now reports 398 passing lines (396 in 0.72.32).

2026-10-06  Péter Felecan  <pfelecan@gmail.com>

	The SW1 / J2 case: a far lead on a rail-fed net reaches the rail.

	* auto_layout.py: far_lead_rail_side(nl, p, rails, net) -- "+", "-" or None. A net in
	`rails` is a rail for a far lead as before (GND "-", else "+"); a net a rail-direct
	power source feeds is one too, provided no other member has a column strip (an anchor
	pin in p.anchor_pin_col, or a passive attached on that net).
	* render.py, build_instructions.py: the far-lead rail decision (to_rail() and
	Step.far_rail_side) uses it. A/B on identical inputs: altimeter (both configs),
	solar-roller and dudhya byte-identical; camera-trap one step ("(on the nearby power
	(+) rail)" for SW1's far lead).
	* pptest_regression: new check_far_lead_on_rail_fed_net (4 assertions, the previous
	rule put back as the control) and check_camera_trap_loop_rail_fed_far_lead (3
	assertions: the REAL camera-trap schematic -> ppbuild_instructions -> ppresolve_components
	against the source's own netlist, with the previous rule as the control). The rendered
	SVG was checked by diff (exactly the new wire) and by eye (it lands on the + rail).
	* FutureResearch.org: the item DONE. ResearchJournal.org: (continued 13).
	SessionHandoff.org: this delivery. make check now reports 396 passing lines
	(389 in 0.72.31).

2026-10-06  Péter Felecan  <pfelecan@gmail.com>

	The two loose ends and the power-source exclusion.

	* render.py: render() calls breadboard_lib.reset_module_state() first. The module
	keeps its drawing state at module level and assumed one render per process; a
	second render in one process inherited the first's occupied regions, shifting
	leg()'s far-lead search (camera-trap gained a link after the altimeter).
	* power_sources.py: is_generic_passive(nl, ref) -- at most 4 pins AND (reference
	prefix in R, RV, RN, RP, RT, C, CP, L, FB, D, LED, Y, X, Q OR the part name's first
	token in R, C, CP, L, D, LED, DIODE, INDUCTOR, CAPACITOR, RESISTOR, CRYSTAL, FERRITE,
	BEAD, POT); classify_power_sources() excludes it besides the (library, part) lookup.
	"VR" is deliberately not a prefix (a voltage regulator as often as a variable
	resistor). coldfire 104 -> 13 power sources, complex_hierarchy 25 -> 8; solar-roller's
	LDR07 sensors and knight-roller's D_Photo no longer; the other shipped designs
	identical.
	* pptest_regression: new check_render_is_order_independent (2 assertions, the control
	with the reset off) and check_power_source_generic_passives (5, with the rule off as
	the control); the link-completion control now disables the reset on purpose.
	* FutureResearch.org: the loose ends DONE; two TODOs (a far lead on a rail-like net
	with rail-attached anchors; verdicts on switches, jumpers, connectors).
	ResearchJournal.org: (continued 12). SessionHandoff.org: this delivery.
	make check now reports 389 passing lines (382 in 0.72.30).

2026-10-06  Péter Felecan  <pfelecan@gmail.com>

	The incomplete build instructions fixed (the second lossy case of the loop).

	* auto_layout.py: Placement.far_leg_links -- [(ref, far_col, half, far_row,
	target_col, target_half, net)]. verify_connectivity() counts them as adjacency
	and, new, counts a passive's FAR lead in its connectivity loop: far leads were
	registered under a fake pin name ("R3.far") no real node matches, so they were
	checked for accidental shorts and never for opens.
	* render.py: records the far-lead links before its second verify_connectivity(),
	with the same target as the purple jumper it draws (the drawing is untouched),
	deduplicated per unordered pair of column strips and against extra_links; imports
	far_leg_position.
	* build_instructions.py: far_leg_link_tuples(p) -- each link an extra_link step
	(offboard across boards), each end in a free hole of its column and half.
	A/B on identical inputs: altimeter (both configs) and solar-roller byte-identical;
	camera-trap +1 step, dudhya battery-free +1.
	* render.py, auto_layout.py: link COMPLETION -- after the pairwise far-lead links,
	any net still on separate column strips gets one link per piece to the first,
	appended to p.extra_links (drawn, turned into steps, counted); found when the
	strict verifier refused a three-strip layout in the full suite. Helpers
	net_strips(), strip_occupancy(), take_free_row() in auto_layout.
	* resolve_components.py: merge_nets_sharing_pins(nl, rail_names) -- one rail plus
	one ordinary name merge (the ordinary name is kept), two ordinary names or two
	rails raise NetConflictError. ppresolve_components captures the rail net names
	(synthetic "_rail_+/-" pins) before resolve_pin_identities().
	* pptest_regression: new check_instructions_far_lead_links (5 assertions, each
	shipped circuit measured in a fresh process, and the link completion with its
	control); check_ppresolve_components_multi_unit
	and check_ppresolve_components_same_pin_merge updated to the more faithful
	instructions (new pin_maps; the oracle is the whole source, passive leads
	collapsed) and a rail-vs-ordinary-name unit test.
	* ppresolve_components.1; FutureResearch.org (the item DONE, two loose ends TODO);
	ResearchJournal.org (continued 11); SessionHandoff.org.
	make check now reports 382 passing lines (376 in 0.72.29).

2026-10-06  Péter Felecan  <pfelecan@gmail.com>

	Same-pin merge in the instructions route; the incomplete instructions
	root-caused.

	* resolve_components.py: NetConflictError and merge_nets_sharing_pins(nl) --
	nets sharing a (ref, pin) node become one net (the single real name wins over
	synthetic "_..." names, nodes deduplicated in order; the same netlist object
	comes back when nothing is shared); two or more different REAL names on one
	pin raise NetConflictError naming both and the pin.
	* ppresolve_components: generate() merges before expanding multi-unit parts.
	* pptest_regression: new check_ppresolve_components_same_pin_merge, 7
	assertions (the function, the route with a bypass capacitor, a control with
	the merge off, a wrong pin_map refused).
	* ppresolve_components.1: the merge and the refusal.
	* FutureResearch.org: the lossy-loop item STARTED -- case 1 DONE; case 2
	root-caused (far leads invisible to the layout-time link pass and, under the
	fake pin name "R3.far", to verify_connectivity()'s connectivity loop), scale
	measured, fix designed and NOT built. ResearchJournal.org: (continued 10).
	SessionHandoff.org: this delivery. make check now reports 376 passing lines
	(369 in 0.72.28).

2026-10-06  Péter Felecan  <pfelecan@gmail.com>

	Multi-unit parts in the instructions route (ppresolve_components).

	* ppresolve_components: a local generate() calls resolve_components.
	expand_multi_unit() at the last moment, from the REAL netlist, at each
	call -- resolve_pin_identities() and resolve_implicit_anchor_connections()
	rewrite the netlist and pair refs with the parsed instructions' anchor
	columns keyed by the real reference, so a virtual key must never reach
	them; the fallback gets the real refs (unplaced virtual ones mapped
	back). With no multi-unit part the very same objects come back and the
	call is the one always made. Any other RuntimeError is now a message and
	exit status 1 (a multi-unit part used to escape as a traceback).
	* pptest_regression: new check_ppresolve_components_multi_unit, 5
	assertions: a real schematic with an LM358 -> ppbuild_instructions ->
	ppresolve_components; one U1 and exactly the five connections the
	instructions state across units 1 and 2; (unit 1/2/3) under U1, no ERC
	error; an in-process control with the expansion off, refused cleanly.
	* ppresolve_components.1, Workflow.org: the unit paragraph, corrected
	("refused cleanly" had not been run).
	* FutureResearch.org: the item DONE; a new TODO for two lossy cases of the
	instructions loop itself, not traced. ResearchJournal.org: (continued 9).
	SessionHandoff.org: this delivery. make check now reports 369 passing lines
	(364 in 0.72.27).

2026-10-06  Péter Felecan  <pfelecan@gmail.com>

	The BT1/U2 item (a detached ref's dedicated row overlapping an ordinary
	anchor), re-checked: the reported instance is no longer reproducible but
	the class was a real hole, now closed.

	* kicad_layout.py: _extra_drop() in generate_schematic() -- the first
	dedicated row (rail-only parts, island clusters) is dropped in 2.54mm
	steps until every part of it clears everything placed by the final
	overlap check's own circle rule and by stubs to 1mm; the row sat below
	the anchors' lowest PIN while the check measures CIRCLES about their
	centres, which disagree for a wide, shallow anchor. Dedicated parts are
	recorded in placed_geom/placed_stubs. dedicated_row_drop_enabled, a
	module switch for the test's control only. Byte-identical on 24 example
	outputs.
	* pptest_regression: new check_dedicated_row_clears_anchors (2
	assertions: the control, refused at every paper size without the drop, and
	the same design generating and round-tripping through kicad-cli).
	* FutureResearch.org: the item DONE. ResearchJournal.org: (continued 8).
	SessionHandoff.org: this delivery; the instructions route is next.
	make check now reports 364 passing lines (362 in 0.72.26). The
	label-collision check now also switches the row drop off to provoke its
	collision.

2026-10-06  Péter Felecan  <pfelecan@gmail.com>

	Multi-unit symbols, after the dense-circuit item.

	* lcsc_import.py: parse_kicad_sym_pin_units(sym_path, symbol_name) --
	{pin number: [units]}, following (extends); a pin may sit in several
	units (the LM358N's power pins 4 and 8 are drawn in every unit).
	* resolve_components.py: expand_multi_unit(nl, pins, sym_dir) -- one
	virtual component per USED unit (key "U3@u2", part "LM358N@u2", only
	its own pins, suffixed tstamp); the used units are decided by pins in
	exactly one unit; a pin in several units gets a net node in each.
	* kicad_gen.py: use_symbol() strips "@uN" (the library holds one symbol
	with all its units); place() writes the real reference and (unit N);
	the @@INST: expansion matches non-greedily (a reference may contain "@")
	and writes the real reference and unit, and the writer REFUSES to write
	an unexpanded placeholder (the first pattern excluded "@" and kicad-cli
	silently dropped a whole sheet).
	* ppkicad_layout: calls expand_multi_unit() after symbol resolution.
	ppresolve_components is NOT covered (see FutureResearch.org).
	* pptest_regression: new check_multi_unit_symbols, 10 assertions: the
	pin-to-unit parser, a two-stage LM358 amplifier against kicad-cli, a
	negative control, a power pin drawn in both units, and the real
	complex_hierarchy demo (68 parts, 50 nets, one shared child, 0 ERC
	errors) when installed.
	* ppkicad_layout.1, ppresolve_components.1, Workflow.org: the unit
	paragraphs and the limit.
	* version.org.in (and its generated version.org), bootstrap, make-archive,
	fetch-kicad-demos: the assisting model is now Claude Sonnet 5.5; AUTHORS
	lists both models. PackpayFieldReport-dudhya.org keeps its dated credit.
	* FutureResearch.org: the dense-circuit item DONE (coldfire's Root and
	inout_user were multi-unit too); a TODO for the instructions route.
	make check now reports 362 passing lines (352 in 0.72.25).
	ResearchJournal.org: (continued 7). SessionHandoff.org: this delivery.

2026-10-05  Péter Felecan  <pfelecan@gmail.com>

	The dense-circuit blocker (FutureResearch.org's own item): six real
	causes found, measured against kicad-cli and fixed, and a geometric
	short check added. Delivered versions had written electrically WRONG
	schematics in some cases (see the third and sixth fixes).

	* kicad_layout.py: LabelOverflowError (left_shortfall,
	other_violations); generate_schematic_auto() widens start_x by the
	measured left shortfall (a label past the left edge fails at EVERY
	paper tier), at most 3 tries per tier.
	* kicad_layout.py: placement-time collision avoidance for passives --
	a parent's first two children used to land on IDENTICAL coordinates
	(step = slot // 2 + 1, the computed side never used); a candidate is
	now tested against everything placed by the final check's own
	criterion and against label stubs (5.08mm, clearance 2.54mm), moved
	one 15.24mm column right until clear.
	* kicad_layout.py: the anchor row spaces anchors by what the final
	overlap check demands of each pair (2-D centre distance), never below
	anchor_spacing, rounded up to the grid.
	* kicad_layout.py: label_direction(dx, dy, extent) -- a pin's label
	direction by its EDGE of the symbol's pin extent, not by its distance
	from the origin (a left-column pin of a tall symbol got an UP stub
	along the column; four nets of rp2040-basic-m1 merged).
	* kicad_layout.py: a direct wire is refused if it would pass over a
	pin other than its two (KiCad connects a pin lying anywhere on a wire):
	ppexample_circuit1 in 0.72.24 AS DELIVERED had C10 shorted, 8 nets in
	kicad-cli's netlist for 9 in the input, flat and split.
	* kicad_layout.py: dedicated_row_gaps() -- the first gap and the pitch
	of wrapped rail-only / island rows derived from the tallest part's
	extent plus two 5.08mm stubs plus 2.54mm (never below row_gap); with
	the old fixed 15mm, 40 rail-only capacitors on A4 shorted VCC into GND.
	* kicad_layout.py: geometric_shorts() and a check in generate_schematic()
	that raises when drawn geometry joins two different nets (pins, wire
	segments, label anchors, PWR_FLAG pins; labels of one text join).
	* ppkicad_layout, ppresolve_components, both manual pages: --row-gap and
	--anchor-spacing described as MINIMUMS.
	* pptest_regression: new check_dense_circuit_fixes (16 assertions,
	~2 min, with two negative controls: the old label-direction rule and
	the old row pitch put back must be refused / pushed off A4);
	check_label_collision_detection now puts the old fixed row gap back to
	provoke its collision (its circuit no longer collides by default) and
	asserts the converse. make check now reports 352 passing lines
	(336 in 0.72.24).
	* FutureResearch.org: the dense-circuit item STARTED, with two of my own
	misreadings corrected in place; open: multi-unit symbols (confirmed:
	U3, an LM358N, has three pin pairs at identical coordinates), coldfire
	Root and inout_user. ResearchJournal.org: (continued 6).
	SessionHandoff.org: this delivery and its lessons.

2026-10-05  Péter Felecan  <pfelecan@gmail.com>

	Multi-sheet follow-ups, step 4 of 4: sheet reuse, option A (decision:
	SOURCE-DECLARED reuse only). A sheet file the source
	uses more than once is written as one shared child file with several
	sheet symbols and per-instance references.

	* netlist.py: Component.tstamp (the component's own (tstamps),
	identical across the instances of a shared sheet).
	* sheet_partition.py: source_sheet_reuse(nl, groups) -- leaf groups
	sharing a source file, verified instance against instance (tstamps,
	lib/part/value/footprint, net partition, pins reaching outside);
	returns slots, per-instance ref maps and pin maps; instances failing
	any test are left out.
	* kicad_gen.py: place() writes an @@INST:ref@@ placeholder, expanded
	by Schematic.write(path, uuid, instance_paths, project) into one
	(instances (project ...) (path ...) (reference ...)) entry per
	instance, removed otherwise (byte-identical to the previous output,
	checked); label() local labels (text relative to the sheet);
	add_sheet_symbol() takes (pin name, parent net) pairs and a uuid;
	make_root() takes uuids; SchematicSet gains reuse and file_stems and
	allocates every uuid up front, building instance paths top-down.
	* kicad_layout.py: generate_schematic()/_auto() take local_labels;
	_assemble_set() takes reuse, its child call is now build(); reuse
	sets dropped if a PWR_FLAG is homed on one, or if the first instance
	does not fit one sheet; only at the top level.
	* pptest_regression: new check_sheet_reuse, 14 assertions (~75 s): a
	hand-written reused-sheet source judged by kicad-cli, the output
	(one shared file, per-instance references, identical connectivity,
	0 ERC errors, full instance paths), three negative controls, and the
	real complex_hierarchy demo's detection. 17+14 assertions in the two
	newest checks; pptest_regression now runs 234 checks.
	* ppkicad_layout.1, Workflow.org: the reuse paragraph.
	* FutureResearch.org: the multi-sheet item records step 4 option A
	DONE with its limits; option B and grouping for a flat source not
	built. ResearchJournal.org: (continued 4) research with sources,
	(continued 5) the build. SessionHandoff.org: this delivery.

2026-10-05  Péter Felecan  <pfelecan@gmail.com>

	Multi-sheet follow-ups, step 3 of 4: functional grouping from the
	source design's own sheets (decisions: extend the
	loader; a hierarchical source stays hierarchical by default,
	--flatten to opt out), and ppkicad_layout reading the input
	schematic's own embedded symbols. A pre-existing derived-symbol
	embedding bug found and fixed.

	* netlist.py: Component.sheet (the source sheet path, "" if none)
	and Netlist.sheets ([(path, source file), ...]); load_netlist()
	fills both from (sheetpath (names ...)) and (design (sheet ...)).
	Additive: netlists without sheet information are unchanged.
	* sheet_partition.py: source_sheet_groups(nl, refs) (pure: the
	common path prefix is stripped, so a nested source recurses; "Root"
	for the top level's own parts; names made unique and quote-free),
	partition_from_groups(), and _crossing_nets() (the crossing-net
	loop of partition_netlist(), extracted unchanged and shared).
	* kicad_layout.py: _assemble_set() (the tail of
	_generate_hierarchical(), extracted, so a mechanical cut and a
	source-following cut build the same set) and
	_generate_by_source_sheets(); generate_schematic_auto() gains
	group_by_source (default False: every existing caller unchanged).
	A source sheet is held to the per-sheet limit, so one above it is
	cut mechanically inside itself.
	* kicad_gen.py: SchematicSet gains names (sheet symbol names; child
	files <stem>-<name>.kicad_sch, file-safe and unique, _file_slugs());
	extract_and_qualify() now embeds a DERIVED symbol, (extends
	"Parent"), flattened -- the parent's text, recursively, with the
	child's name on the symbol and its unit sub-symbols and the child's
	own properties -- where it used to embed the child verbatim, so
	KiCad saw a symbol with no pins and every pin of the part came out
	unwired (found on knight-roller's ATtiny1616-M; any derived symbol
	from any library was affected).
	* resolve_components.py: embedded_symbol_libs(sch_path, out_dir) --
	one .kicad_sym per library nickname out of a schematic's
	(lib_symbols), following every (sheet)'s Sheetfile property into
	child sheet files, recursively; for resolve_components()'s
	extra_libs.
	* ppkicad_layout, ppkicad_layout.1: --flatten; a .kicad_sch input's
	embedded symbols are used, precedence explicit --symbol-lib, then
	embedded, then $KICAD_SYM_DIR; the "split a circuit under the
	limit" note no longer fires for a split that follows the source.
	* pptest_regression: new check_source_sheet_grouping, 17
	assertions (~150 s): the pure functions, the loader on the shipped
	knight-roller, the derived symbol, the CLI end to end (default,
	--flatten, a limit below the source sheets, a source nested one
	level deeper made by editing the exported .net -- every result equal
	to the original, 0 ERC errors), no --symbol-lib needed for a
	.kicad_sch, an explicit one winning, and rp2040-basic-m1 resolved
	only through embedded symbols (Memory_Flash found only through a
	sub-sheet's Sheetfile). pptest_regression now runs 220 checks.
	* FutureResearch.org: the multi-sheet item records step 3 DONE
	(grouping for a FLAT source, a heuristic, not built; sheet reuse
	left); the dense-circuit item gains a real design,
	rp2040-basic-m1, that fits on no paper tier in any mode, with the
	start_x experiment that did not cure it. Workflow.org, README
	untouched beyond Stage 3b.
	* ResearchJournal.org: 2026-10-05 (continued 3), with the
	embedded-symbols decision and my own refactor slip recorded.
	* SessionHandoff.org: this delivery.

2026-10-05  Péter Felecan  <pfelecan@gmail.com>

	Multi-sheet follow-ups, step 2 of 4: ppkicad_layout, the
	netlist-to-KiCad CLI (Workflow.org's own Stage 3b), and a silent
	skip in resolve_components() closed.

	* ppkicad_layout (new, executable), ppkicad_layout.1 (new, groff
	-w all clean): an existing .kicad_sch (exported with kicad-cli,
	like ppauto_layout) or a .net (like ppnetlist), chosen by
	extension, in; a new laid-out .kicad_sch out, split across
	OUTPUT-sheetN.kicad_sch (nested: -sheetN-sheetM) above
	--max-components, every file reported. --symbol-lib
	[NICKNAME=]FILE (repeatable) for project-local libraries,
	--paper, --kicad-cli, --cache-dir, title-block options,
	--anchor-spacing/--row-gap/--row-spacing, --debug, --quiet,
	--version, --help. Every user-provided value validated: usage
	errors exit 2, runtime errors exit 1, each naming its cause, no
	traceback; an output equal to the input is refused. Anchors in
	connectivity order (order_anchors_by_connectivity), NOT the
	alphabetical order ppresolve_components passes: alphabetical failed
	at every paper tier on ppexample_circuit2 and the 0.72.20 split
	ladder then split an 11-part circuit; a note on stderr now says so
	whenever a circuit under the limit still had to be split. No
	--sources file: a netlist carries every component's library/part
	and every pin's number.
	* resolve_components.py: resolve_components() gains extra_libs
	({nickname: .kicad_sym path}, searched before $KICAD_SYM_DIR) and
	checks UP FRONT that every library a component names can be found,
	reporting all that cannot as one UnresolvedComponentError, grouped
	by library; a symbol missing from a library that was found is
	reported naming the ref. Closes a silent skip (no pins, no library
	copied, no message) that surfaced later as a bare KeyError in
	kicad_layout.py -- confirmed on KiCad's own coldfire demo.
	* pptest_regression: new check_ppkicad_layout_cli, 20 assertions
	(~85 s), registered after check_nested_hierarchy_kicad_cli: both
	input kinds (kicad-cli's netlist of the result equals the input's,
	0 ERC errors), a split design with every file reported, --paper and
	--quiet, a project-local library not found / found / found without
	the symbol, and the whole error matrix. pptest_regression now runs
	203 checks.
	* Makefile.am: ppkicad_layout and ppkicad_layout.1 in EXTRA_DIST,
	ppkicad_layout.1 in MAN_PAGES.
	* make-archive: its executable check now also covers
	ppparse_instructions, ppresolve_components, ppkicad_layout and
	pptest_regression (it listed only some of the pp* scripts).
	* ppnetlist, ppnetlist.1: the help text no longer points at the
	nonexistent ppkicad_layout(1) for how to export a .net; it gives
	the kicad-cli command. SEE ALSO gains ppkicad_layout(1).
	* README, Workflow.org: ppkicad_layout in the tool list; Stage 3b's
	diagram and text now describe the tool ("no direct CLI yet" closed).
	* FutureResearch.org: the netlist-to-KiCad CLI bullet removed
	(closed); the multi-sheet item records step 2 DONE; the loop item
	gains the ppresolve_components finding (alphabetical anchor order;
	no --paper), recorded, not changed.
	* ResearchJournal.org: a 2026-10-05 (continued 2) entry, with the
	decisions made without asking listed for review.
	* SessionHandoff.org: this delivery, steps 1 and 2 of 4 done.

2026-10-05  Péter Felecan  <pfelecan@gmail.com>

	Multi-sheet follow-ups restarted (FutureResearch.org's own
	"Multi-sheet KiCad output" item), step 1 of 4: the nested
	hierarchy 0.72.20's recursion produces, written to disk and read
	back by kicad-cli for the first time -- a real defect found and
	fixed. Also: delivery-discipline corrections after a review of
	0.72.20's archive.

	* kicad_gen.py: add_sheet_symbol() and make_root() gain `upward`
	(default empty: every existing caller unchanged). A net named in
	it ends its sheet-pin stub in a hierarchical_label instead of a
	global_label. SchematicSet gains upward_nets (default empty) and
	passes it to make_root(). Closes kicad-cli ERC's own
	hier_label_mismatch ("Sheet pin X has no matching hierarchical
	label inside the sheet"): a nested set's root drew sheet pins down
	to its own children but nothing inside its file answered its
	parent's sheet pins. The netlist oracle alone had NOT seen it
	(global labels join nets by name even when the hierarchy is
	broken).
	* kicad_layout.py: _generate_hierarchical() sets the returned
	SchematicSet's upward_nets to its parent_crossing_nets restricted
	to the nets its own sub-circuit touches (empty at the top level).
	* pptest_regression: new check_nested_hierarchy_kicad_cli, 11
	assertions, ~75 s, registered after check_hierarchical_generation:
	a nested split reached by simulating the one condition that
	triggers it for real (a sheet that fits no paper tier) over
	ppexample_circuit2 (depth 2) and three renumbered copies of it
	(33 parts, depth 3); every file reported and present, kicad-cli
	netlist equal to the flat export, 0 ERC errors, PWR_FLAG count
	equal to the flat design's (4 = 4, 12 = 12: confirms 0.72.20's
	argument that it needs no threading), and a negative control
	(clearing upward_nets must fail ERC while the netlist still
	compares equal). pptest_regression now runs 183 checks.
	* .tar-exclude: *-erc.rpt, *-drc.rpt (kicad-cli sch erc with no
	-o writes its report into the current directory; a stray one,
	altimeter-resolved-erc.rpt, shipped in 0.72.7 through 0.72.19,
	checked archive by archive). The stray file removed.
	* Makefile.am: EXTRA_DIST completed -- build_netlist.py (imported
	by both example programs and the regression test), make-archive,
	.tar-exclude, and the whole TestSchemes/PoorManAltimeter fixture
	set with its real directory prefix (altimeter-sources.json had
	been listed without it, making `make dist-xz` fail outright).
	instructions.txt and instructions-no-rail.txt, deleted in 0.72.20's
	session as "scratch" on thin evidence, restored unchanged from the
	0.72.19 archive (same 2026-09-16 timestamp as the rest of the
	fixture set). Comments corrected accordingly.
	* DevelopmentStandard.org: new "Scratch Files and Tool Output",
	"Delivery Archive: ./make-archive Only, Never make dist" (0.72.20's
	archive was built with make dist-xz, the second time this slip
	occurred; the pristine check had also been run on that tarball
	rather than the delivered archive), and a numbered "Delivery
	Checklist" whose layout check diffs the sorted file names against
	the previous delivery's.
	* SessionHandoff.org: brought current from 0.68.0 to this
	delivery -- a new "Start Here", a one-line-per-delivery catch-up
	from NEWS, Next Steps mirroring FutureResearch.org's Current
	Status. Updating it is now step 3 of the Delivery Checklist.
	* FutureResearch.org: the multi-sheet item RESTARTED, with the
	agreed order (nested verification DONE; ppkicad_layout; functional
	grouping with the Netlist loader extended to keep the source's
	sheet structure; sheet reuse); a dangling
	internal link to the retitled dense-circuit item fixed; that item
	gains a small reproducible case (33 parts fail flat at every paper
	tier, succeed with anchor_spacing=60).
	* ResearchJournal.org: two 2026-10-05 entries.

2026-09-29  Péter Felecan  <pfelecan@gmail.com>

	"A dense circuit's own label/paper-size overflow" (item 3):
	splitting itself CLOSED, across geometric back-off and recursive
	escalation to child sheets, plus three separate structural bugs
	found and fixed making it actually work. DevelopmentStandard.org's
	own new "cite every source" standard recorded first. A new,
	separate finding (a detached ref's own dedicated row overlapping
	an ordinary anchor) left open, its own new item.

	* DevelopmentStandard.org: new "Critical: Cite Every Source, for
	ANY Research, Not Just the Research Tool" subsection, generalizing
	the existing "Research Tool Workflow" citation rule to every kind
	of research, by whatever means, once its own findings genuinely
	inform a real decision.
	* kicad_layout.py: _force_split()'s own halving ladder now computes
	this circuit's own single largest anchor cluster's own real size
	directly (mirroring partition_netlist()'s own owner-assignment
	exactly) and never tries a limit below it, trying that real floor
	once, as its own final, most aggressive, still-safe attempt --
	closing a real tension a first, plain-halving version of the same
	ladder found directly (overshooting past the real, narrow window
	where a limit avoids both geometric overflow and stranding a
	cluster partition_netlist() itself already had to split), chosen over
	a finer arithmetic step.
	* kicad_layout.py: _generate_hierarchical()'s own child-sheet
	generation now passes hierarchical=True (was False), so a child
	sheet that itself still does not fit on any paper tier genuinely
	splits further, recursively, via its own _force_split(). Confirmed
	directly safe against runaway
	immediate re-splitting (that child's own max_components already
	equals its own exact size).
	* kicad_gen.py: SchematicSet.write()'s own self.paths now extends
	with a nested child's own real paths (hasattr(child, "paths")
	distinguishes a further SchematicSet from a plain Schematic)
	rather than appending only the one child_path -- closing a real
	gap a flag flip alone would have left silently broken: a nested
	split's own real files were never reaching the top-level caller.
	* kicad_layout.py: _generate_hierarchical()/_force_split() gain a
	new parent_crossing_nets parameter, threaded from generate_
	schematic_auto()'s own existing crossing_nets at both of its own
	real call sites, merged by union with each level's own, separately
	computed crossing set -- closing a real gap where a net already
	assigned to cross at the PARENT level had no way to reach a
	further, nested split's own child sheets at all. sheet_nets
	(_generate_hierarchical()) corrected the same way, resolving a
	parent-only net's own real sheet directly via part.sheet_index_of().
	pwr_flag_nets needed no equivalent: nl.nets_needing_pwr_flag()
	already, correctly, re-derives the identical answer at every
	level independently.
	* sheet_partition.py: partition_netlist() gains an optional rails=
	parameter (None preserves its own prior behavior exactly); when
	given, used directly instead of recomputing via high_degree_nets()
	internally -- closing a real, genuinely subtle bug found verifying
	the recursion: that internal recompute is scope-dependent (a
	widely-shared net can have enough members to count as a rail
	across a whole circuit, yet fall below that same threshold within
	a narrower, real sub-circuit), silently "demoting" an already-
	correctly-detachable ref to a wrongly-"owned," falsely-stranded
	one at a nested level (confirmed directly: CLKOUT101, kit-dev-
	coldfire-xilinx_5213). Instruction: "no need
	to recompute each time." _generate_hierarchical() passes its own,
	already-correctly-threaded rails straight through.
	* kicad_layout.py: generate_schematic()'s own rail_only_refs/
	island_clusters dedicated-row placement (two sites) now defaults
	max(xy[1] for ...) over positions to anchor_y when positions is
	empty, instead of letting max() raise outright -- a real, pre-
	existing bug newly exposed by recursive splitting (a child sheet
	can now genuinely have zero anchors and zero ordinary passives at
	all, every one of its own refs rail-only/island, the same day's
	own, earlier, separate detached-ref capacity-awareness fix opening
	a whole new sheet just for overflow detached refs).
	* pptest_regression: check_hierarchical_generation's own real
	altimeter case corrected -- --max-components 3 (was expecting ONE
	sheet) now, correctly, produces a real split instead (that
	circuit's own 3 anchors already, exactly, fill a max_components=3
	sheet, so BT1 no longer silently overflows it "one part over," the
	exact slop the same day's own, earlier, separate capacity-
	awareness fix was built to close); corrected to 4, where BT1 still
	fits with real room to spare. The split-case assertion moved from
	--max-components 2 to 3, avoiding a separate, newly found overlap
	(see FutureResearch.org) not yet understood or fixed.
	* FutureResearch.org: item 3 ("a dense circuit's own label/paper-
	size overflow") updated -- splitting itself closed across the four
	pieces above; the root density cause (why even the single largest
	real anchor cluster, alone, on one sheet, still exhausts every
	paper tier) remains genuinely open. A new item added: a detached
	ref's own dedicated-row placement can land close enough to an
	ordinary anchor, sharing the same sheet, to genuinely overlap
	(confirmed directly: BT1/U2, the real altimeter circuit, at the
	old --max-components 2).
	* ResearchJournal.org: two new, same-day entries recording the
	full, real account -- sources consulted for the trade/ergonomic
	research (now also Development
	Standard.org's own new, standing rule); the geometric back-off
	design and its own build; the recursive-escalation design,
	presented before building once real structural complexity was
	found, and its own three closed gaps; the fourth, pre-existing bug;
	and the full regression run's own two further, honest corrections.

	Multi-sheet KiCad output (item 2): the large-circuit end-to-end
	run attempted for real, the first time, against KiCad's own
	coldfire demo (160 components). Two real gaps found and CLOSED;
	two real, harder findings left open, each its own new item.

	* kicad_layout.py: sheet_partition.py's own partition_netlist()
	call now builds its own "detachable" set from the SAME, direct,
	topological test rail_only_refs already uses (every one of a
	passive's own real nets rail-like), not classify_power_sources()
	alone -- the identical sibling gap the already-closed "rail-only
	passives on the KiCad side" item's own fix already closed on the
	single-sheet path, never generalized to the multi-sheet one.
	Confirmed directly: CLKOUT101 (a real, single-pin connector, its
	own one pin on a rail, but never a "power source") now correctly
	detachable, instead of a misleading orphan.
	* kicad_layout.py: a real, genuinely new, third class of passive
	-- island_refs/island_clusters, a second, genuinely separate
	dedicated area (south of rail_only_refs' own row), for two or
	more passives sharing a real, non-rail net ONLY with each other,
	every other real pin on a rail, never reaching any anchor at all
	(confirmed directly: kit-dev-coldfire-xilinx_5213's own L102/
	VDDA101, LED205/R207). Three real options presented; chosen: a second
	dedicated area (not simply folding these
	into rail_only_refs' own row), keeping each real cluster's own
	members visually adjacent to each other, reusing sheet_
	partition.py's own _adjacency()/_orphan_clusters() for the
	grouping itself. Verified directly, first, on a small, real,
	controlled synthetic circuit, before building: simply sharing the
	SAME label-based connectivity mechanism rail_only_refs already
	uses is already sufficient for correctness, no drawn connecting
	wire needed. A real bug in the first version, caught by the full
	regression suite immediately: island_refs never excluded a
	passive with NO real net connection at all (the same guard rail_
	only_refs already has) -- fixed directly.
	* pptest_regression: full suite (172 checks) confirmed clean
	throughout.
	* FutureResearch.org: item 2's own account updated; two real,
	harder findings recorded as their own, new, open items, not
	solved this session -- a dense circuit's own label/paper-size
	overflow with no escape at any paper tier (this same coldfire
	demo still does not generate cleanly end to end); and classify_
	power_sources()'s own real exclusion for a known generic-passive
	category, which only ever matches the standard Device:* library
	(confirmed directly: 100 of 131 of this real circuit's own
	passives misclassified as "primary" power sources, this project's
	own already-acknowledged harder problem). ResearchJournal.org:
	full account.

2026-09-29  Péter Felecan  <pfelecan@gmail.com>

	An attentive FutureResearch.org review, ordering every remaining
	real, open item and cleaning up what was stale; then "a net
	needing more than one wire at its own hub column" (item 5): fix 1
	CLOSED, fix 2 confirmed still genuinely open.

	* FutureResearch.org: two fully-closed items removed entirely
	(rail-only passives on the KiCad side; two labels landing at the
	same point), both already fully journaled, matching this
	document's own stated maintenance rule. One stale forward-
	reference corrected (an item referenced as "parked... below"
	that never got its own heading, closed the same day it was
	parked, under a different name). One item's own text brought
	current: multi-sheet KiCad output's own "no large circuit has
	gone through this" bullet named a blocker (KiCad's own coldfire
	demo's 41 unplaceable rail-only passives) that the rail-only-
	passives fix had just closed -- noted as a now-tractable
	opportunity, not yet tried. The ten remaining open items
	presented in order; tackled next: item 5, then item 2.
	* breadboard_lib.py: leg()'s own real, final fallback
	(_valid_direction()) now also checks forbidden_cols, mirroring
	every earlier search loop in the same function -- the SAME gap-
	class as 2026-09-17's own item 3 and this same day's own earlier
	knight-roller finding 1 (a check present everywhere except one
	loop). Confirmed directly, precisely, on KiCad's own real kit-
	dev-coldfire-xilinx_5213 demo (rows_per_half=17): R113's own far
	leg, needing GND, used to fall all the way through to this exact
	fallback and land on U102's own pin 6 -- already, correctly,
	forbidden (owned by /UCTS0, a genuinely different real net), but
	never checked there.
	* pptest_regression: new check_far_leg_final_fallback_respects_
	forbidden_cols, against this same real, external KiCad demo
	circuit directly, confirming the exact, original accidental
	short is gone. Full suite confirmed clean (172 checks, up from
	170).
	* FutureResearch.org: re-testing the fix above against this same,
	dense, real circuit surfaced a SECOND, structurally similar
	short, confirmed, by direct tracing, to be a genuinely different
	remainder -- it falls through leg()'s own even more extreme
	"neither direction is valid" fallback, where both candidate
	directions are genuinely forbidden simultaneously, so no safe
	column exists there regardless of what is checked. Traces to the
	item's own original, deeper question: relieve_overflow()'s own
	fix reserves only one row per QUALIFYING net, never scaling with
	how many separate dependents that specific net genuinely has.
	Recorded explicitly as still open -- not solved this iteration.
	ResearchJournal.org:
	full account of both the review and both fixes.

2026-09-29  Péter Felecan  <pfelecan@gmail.com>

	The standing schematic <-> instructions loop goal
	(FutureResearch.org, item 1): finding 2 CLOSED -- a "bridging"
	far-leg column/row reservation never coordinated with leg()'s
	own, separate search. A real, third layer found re-verifying the
	fix, closed the same day; compute_optimal_config() gains a real
	rows_per_half escalation.

	* auto_layout.py: new Placement.bridged_far_col field, {ref:
	(col, half)}, populated at the "bridging" fallback's own commit
	site -- recording the real column it already reserved for a
	passive's own far leg, before that passive is otherwise placed
	at all, for some OTHER, dependent passive to attach to. Traced
	directly: this reservation and render.py's own, entirely
	separate leg() search for the SAME far leg never coordinated at
	all, each computing its own, independently correct, but
	genuinely DIFFERENT column (knight-roller's own real D2:
	reserved column 2, leg() drew column 28). Two real options
	presented; chosen: make leg() honor the
	reservation (via its own, already-existing x_far override),
	rather than reordering the pipeline so extra_links could read
	leg()'s own position instead.
	* Also auto_layout.py: col_depth.setdefault(far_col, 1) corrected
	to LEG_ROWS + 1 -- a real, third, genuinely separate layer found
	re-verifying the above fix against this project's own full
	regression suite: the reservation claimed only row 1 ("the same
	convention an anchor's own pin uses"), but a far leg does NOT
	land at row 0 at all; leg() always places it LEG_ROWS (4) rows
	away, a genuinely different, real convention. Confirmed directly:
	packpay-example-circuit1's own L10(near)/C10(far) collided at the
	identical real pixel point once the column fix made leg() land
	where the reservation claimed.
	* breadboard_lib.py: resistor()/capacitor()/diode()/inductor()
	gain a new x_far=None parameter, threaded straight through to
	leg()'s own existing override.
	* render.py: the dispatch site forces x_far to p.bridged_far_col's
	own value whenever the ref being drawn has one, instead of
	letting leg() search independently.
	* auto_layout.py: new ROWS_PER_HALF_LADDER = [5, 7, 10, 15, 20],
	compute_optimal_config() now escalating rows_per_half too, nested
	OUTER to its own existing sectors escalation (every sector count
	at the real, common 5-row depth exhausted first) -- closing a
	real, honest regression the row fix itself, correctly, surfaced:
	knight-roller's own default rows_per_half (5) no longer fit this
	real circuit at all once the far leg's own real position was
	accounted for correctly. Instruction: most
	real breadboards he has genuinely encountered have 5 rows per
	half, worth a real auto-retry here.
	* ppbuild_instructions: wired directly to compute_optimal_config()
	by default, replacing a bare layout() call -- any explicit
	--config still fully honored, as the real, starting tier, never
	bypassed. Verified directly, end to end: knight-roller now
	generates cleanly via the real CLI.
	* pptest_regression: EXPECTED_RESULTS updated for knight-roller's
	own "default"/"breadboard1" entries (bounds_ok now False at these
	two, real, fixed configs -- an honest, direct consequence of the
	row fix). check_far_leg_stays_within_board_edges updated to use
	D4, not D5 -- this project's own real, sequential, stateful
	breadboard placement means the row fix legitimately shifted which
	ref lands at the board's own absolute left edge at all. A real,
	separate, already-open, previously-theoretical item (a near-leg
	colliding with a different component's own bend wire) is
	confirmed on knight-roller too, the SAME placement shift's own
	honest side effect -- named explicitly in check_render_sweep's
	own new KNOWN_NEAR_LEG_BEND_WIRE_COLLISIONS, not silently
	skipped. Full pptest_regression (172 checks, up from 170) and
	full make check (272 checks, up from 270) confirmed clean.
	* FutureResearch.org: finding 2's own full account closed; item
	1's own Current Status summary updated; the near-leg/bend-wire
	collision item's own body updated, confirmed now on a real
	circuit. A real, separate, pre-existing escaping bug (literal
	backslash-quote sequences) caught and fixed along the way.
	ResearchJournal.org: full account.

2026-09-29  Péter Felecan  <pfelecan@gmail.com>

	The standing schematic <-> instructions loop goal
	(FutureResearch.org, item 1): knight-roller, a genuinely new real
	circuit, run end to end. Finding 1 CLOSED; finding 2 deliberately
	deferred to a future session.

	* breadboard_lib.py: leg()'s own new, shared _at_least_1() helper,
	mirroring the final fallback's own already-correct column >= 1
	check (2026-09-17, item 3), applied to its own two, earlier,
	preferred search loops and the last-resort, board-crossing loop
	too -- all three previously lacked this check entirely, sharing
	the exact weakness item 3's own fix only closed for the final
	fallback. Confirmed directly: knight-roller's own real D5 (a near
	leg at the board's own absolute left edge, with its own
	preferred "right" candidate already occupied) had its own far
	leg land at column 0, producing "hole e100000" -- the same real
	symptom item 3 already documented, on a different real circuit.
	* pptest_regression: check_far_leg_stays_within_board_edges
	extended with a real, end-to-end demonstration against knight-
	roller's own real D5 -- the existing, direct unit-level test
	never actually exercised this gap, since its own empty canvas
	always let the "right" candidate succeed first. Full make check
	confirmed clean (271 checks, up from 270).
	* FutureResearch.org: a new subsection recorded under item 1's
	own, standing body; Current Status summary updated. A second,
	genuinely separate finding from this same investigation (an
	extra_links hub-column selection gap) is recorded precisely but
	deliberately left open. ResearchJournal.org: full account.

2026-09-29  Péter Felecan  <pfelecan@gmail.com>

	The parked direct-wire/USB-C connector gap (FutureResearch.org):
	CLOSED. Taken next, as
	the most reasonable of the remaining open ones.

	* kicad_layout.py: the no_connect-marker generation loop now
	tracks each ref's own real, USED positions, not merely used pin
	KEYS, and skips a no_connect marker whenever the current key's
	own real position already has a real connection via any other
	key. Root cause: a connector symbol can have two different pin
	names sharing the exact same real geometry (confirmed directly,
	USB_C_Receptacle_PowerOnly_6P's own real A9/B9, both VBUS); when
	only one was used, a no_connect marker for the other landed
	directly on top of the already-correct wire, and kicad-cli's own
	netlist export read the shared point as deliberately
	disconnected, silently overriding it.
	* Two other hypotheses (a multi-unit symbol mismatch; the pin's
	own "length" field) were directly checked and ruled out before
	finding the real cause; full account, ResearchJournal.org's own
	entry.
	* Verified directly: R20/J1's own real VBUSIN connection
	(packpay-example-circuit2) no longer reads as unconnected; 0 real
	ERC errors. The negative case verified too: a genuinely, singly
	unused pin still gets its own real no_connect marker.
	* pptest_regression: new check_no_connect_shared_pin_position, a
	permanent regression test. EXPECTED_RESULTS baseline for
	circuit2 updated again (unplaced=0, matching this real, now-
	fixed state). Full make check confirmed clean (270 checks, up
	from 265).
	* FutureResearch.org: item closed and removed entirely (fully
	resolved, no remainder). ResearchJournal.org: full account.

2026-09-29  Péter Felecan  <pfelecan@gmail.com>

	Rail-only passives on the KiCad side (FutureResearch.org, item 2):
	the placement decision CLOSED. A real, separate label-collision
	bug found and closed along the way, its own item; a real,
	pre-existing bug found fixing this item's own test fixture,
	deliberately parked as its own, still-open item.

	* kicad_layout.py: generate_schematic() gains rail_only_refs, a
	direct, topological test (every real net rail-like) replacing
	the former, single-primary-source special case entirely --
	deliberately not gated by classify_power_sources(), which
	excludes plain, real capacitors. Placed in a dedicated row, south
	of everything else, wrapping across more than one row as needed.
	New, fatal verification check: two labels for different real nets
	at the exact same point now raise, with a context-aware message
	(row_gap/row_spacing, or anchor_spacing, depending which is
	actually relevant) -- the suggested remedy itself verified to
	work, not merely plausible-sounding.
	* row_spacing/row_gap: real, tunable parameters now (default
	20.0/15.0mm), threaded through generate_schematic_auto(),
	_generate_hierarchical(), and ppresolve_components' own new
	--row-gap/--row-spacing flags; man page updated.
	* DevelopmentStandard.org: new standing principle in the Error
	Handling section -- an error or warning message should state the
	solution alongside the problem wherever a real, adequate one
	exists, with this exact fix as the worked example.
	* ppexample_circuit2: its own "MISC" tie-up net (a real,
	high-degree net by pure member count, no real power-distribution
	significance) replaced with five separate, single-member nets,
	closing a real classification false-positive this same fixture's
	own shape caused. Doing so surfaced a real, separate,
	pre-existing bug (a component's own connection to a USB-C
	connector silently unconnected in generated output) -- traced
	precisely, not yet resolved, deliberately parked as its own,
	separate FutureResearch.org item.
	* pptest_regression: new check_rail_only_passives_dedicated_row
	and check_label_collision_detection, permanent regression tests.
	EXPECTED_RESULTS baseline for circuit2 updated to match the
	fixed fixture's own real, measured, current state. Full make
	check confirmed clean (265 checks, up from 252).
	* FutureResearch.org: item 2 closed and removed; a new, open item
	added for the parked USB-C connector question; Current Status
	renumbered. ResearchJournal.org: full account.

2026-09-29  Péter Felecan  <pfelecan@gmail.com>

	The rail-attached-column electrical short (FutureResearch.org):
	CLOSED. Decision: fix this before item 2's
	own placement decision, given it fails silently in already-
	shipped functionality, where item 2 fails loudly.

	* render.py: far_net's own computation corrected -- ALWAYS
	other_net_of(), never a rail-attached near leg's own rail used
	directly for the far leg too (wrong for any real part touching
	two different rails, e.g. a decoupling capacitor).
	* auto_layout.py: the SAME correction, independently, in
	verify_connectivity() -- this project's own dedicated short-
	detection mechanism had the identical wrong assumption, so it
	never caught this real short at all.
	* Verified directly on rp2040-basic-m1 (the real circuit that
	exposed this): C8's own far leg no longer lands in its own
	near-leg column; verify_connectivity() now reports the circuit
	clean.
	* pptest_build_instructions: its own round-trip check reverted
	back to rp2040-basic-m1 (briefly switched to a synthetic circuit
	while this defect was open) -- the real circuit is genuinely
	clean again.
	* pptest_regression: new check_rail_attached_far_leg_no_short, a
	permanent regression test. Two real mistakes of my own caught
	along the way: a missing shutil import, and a missing
	breadboard_lib.reset_module_state() call (an already-established
	requirement before any render() call in this file) that made the
	new test pass in isolation but fail as part of the full suite.
	Full make check confirmed clean (252 checks, up from 242).
	* FutureResearch.org: item removed entirely (fully closed, no
	remainder); item 2's own text updated; Current Status
	renumbered. ResearchJournal.org: full account, including a real,
	significant, unplanned mid-session environment reset and its own
	direct recovery (DevelopmentStandard.org's own "Obtaining and
	running KiCad" section).

2026-09-29  Péter Felecan  <pfelecan@gmail.com>

	Rail-only passives on the KiCad side (FutureResearch.org, item 2):
	design question presented, decisions taken, and a genuinely
	separate, PRE-EXISTING correctness gap in the reverse-parsing
	loop found and closed as a real prerequisite. A real, separate,
	PRE-EXISTING placement defect
	found along the way kept as its own, separate item, not fixed
	here.

	* auto_layout.py: new Placement.passive_attach_net -- the net a
	passive's own near leg attached through, set at the single,
	existing, shared attach-commit site.
	* build_instructions.py: new Step.far_rail_side, computed via
	other_net_of() (already used by render.py's own far-leg-jumper
	drawing); describe_step() states it in the generated prose,
	mirroring the existing near-leg rail_side wording.
	* parse_instructions.py: _PASSIVE's own grammar extended to read
	the far-leg rail annotation back; reconstruct() joins the far leg
	directly onto the real, named rail net.
	* Verified directly: a synthetic, both-pins-on-rails circuit now
	correctly merges every far leg into one real VBUS net (previously
	four isolated, wrong synthetic nets); the existing rail_direct_
	power_source (BT1-style) mechanism confirmed unaffected.
	* Found verifying against a real circuit (rp2040-basic-m1): a
	genuine, PRE-EXISTING electrical-short defect in layout()/
	render() itself (two different rails' own components able to
	land in the same real breadboard column) -- newly visible, not
	caused, by this fix; recorded as its own, separate
	FutureResearch.org item.
	* pptest_build_instructions: its own round-trip check switched
	from rp2040-basic-m1 (which genuinely hits the new-found defect)
	to a clean, purpose-built synthetic circuit, with a direct
	comment explaining why.
	* pptest_regression: fixed a genuinely PRE-EXISTING bug in
	check_near_far_anchor_pin_annotation's own netlist-matching logic
	(a naive substring check that couldn't tell whether two matches
	belonged to the same real node), newly exposed by this same fix.
	New check_far_leg_rail_membership, a permanent regression test
	for the fix itself. Full make check confirmed clean (242 checks).
	* FutureResearch.org: item 2's own text updated with the full
	account, including a direct correction to this same item's own
	original framing (option 2 does not, after all, reuse
	rail_power_source -- a structurally different, single-source
	mechanism); a new item recorded for the placement defect found
	along the way; Current Status renumbered. ResearchJournal.org:
	full account.

2026-09-28  Péter Felecan  <pfelecan@gmail.com>

	Hierarchical multi-sheet KiCad schematics (FutureResearch.org),
	increment 3 of 3: the item FINISHED. Decisions: hierarchical pins
	included, the KiCad-side
	TooManyComponentsError becomes a split, library-level entry
	first, reachable through ppresolve_components.

	* kicad_layout.py: generate_schematic_auto() gains
	hierarchical=True -- above the per-sheet limit it partitions and
	generates every sheet (_generate_hierarchical(), new), returning
	a SchematicSet; hierarchical=False keeps the refusal.
	generate_schematic() gains crossing_nets (hierarchical labels, no
	direct-wire shortcut) and pwr_flag_nets (override).
	* kicad_gen.py: new SchematicSet, the same write() interface as
	Schematic (returns the root uuid), writing the root and
	<stem>-sheet<N>.kicad_sch children.
	* sheet_partition.py: partition_netlist() gains anchors/passives
	(the caller's own classification), detachable (a rail-only
	primary power source, attached afterwards to the sheet it feeds
	most) and Partition.orphans/.detached.
	* PWR_FLAG decided ONCE on the whole netlist, one sheet per
	flagged net -- a per-sheet decision demonstrated to fail (5 flags
	for 3 needed, ERC pin_to_pin errors).
	* ppresolve_components (+ man page): --max-components is the
	per-sheet limit; every file written is reported.
	* pptest_regression: new check_hierarchical_generation, 20
	assertions -- split netlist equal to the flat one and 0 ERC
	errors on both example circuits and, through the real CLI, the
	altimeter with its rail-direct BT1; PWR_FLAG count; refusals
	(oversize cluster, rail-only passive, both with precise
	messages); hierarchical=False; a single-sheet fallback.
	* Found and recorded: a rail-only passive is refused on a single
	sheet too (pre-existing, confirmed by a permanent check).
	* FutureResearch.org: the item removed (journal entries of
	2026-09-28 are its record); a WAIT item for what remains and a
	TODO for the rail-only limit; Current Status renumbered.

2026-09-28  Péter Felecan  <pfelecan@gmail.com>

	Hierarchical multi-sheet KiCad schematics (FutureResearch.org),
	increment 2 of 3: the mechanical, capacity-driven partitioners.

	* sheet_partition.py (new, registered in Makefile.am):
	partition_netlist() and sheet_netlists(), a pure library module,
	nothing calling it yet. Reuses classify(), high_degree_nets() and
	order_anchors_by_connectivity() unchanged; each passive gets an
	owner anchor (layered BFS: hops, shared-net weight, anchor
	order); clusters cut at cluster boundaries against a balanced
	target; a cluster splits only if it alone exceeds a sheet.
	* pptest_regression: new check_sheet_partition, 16 assertions --
	synthetic netlists for specific semantics, and on KiCad's own
	160-component coldfire demo: exact coverage, capacity, balance,
	cluster integrity (checked independently), per-sheet netlist
	round trip, quality against a naive split (20 vs 76 crossing
	nets at cap 150), and determinism across three hash seeds.
	* Known limits, verified on that circuit and recorded in
	FutureResearch.org: 45 of 131 passives ownerless (33 capacitors);
	11 nets treated as rails by the engine's own existing test,
	some real signals; splits not minimal.
	* ResearchJournal.org: full account.

2026-09-28  Péter Felecan  <pfelecan@gmail.com>

	Hierarchical multi-sheet KiCad schematics (FutureResearch.org),
	increment 1 of 3: STARTED. The item's own named upstream risk
	(kicad-cli failing to resolve hierarchical sheet instances)
	probed first, directly, and found NOT to apply to a design
	written in this generator's own format. Decisions: real hierarchical
	pins included (net names kept exact
	via a parent-side global_label), mechanical capacity-driven
	partitioning next, library-level entry point first.

	* kicad_gen.py: new make_root() and Schematic.add_sheet_symbol()
	(a real (sheet ...) symbol with one real sheet pin per crossing
	net, each with its own stub and global_label on the root);
	Schematic.label() gains hierarchical=True (a real
	hierarchical_label for the child sheet's own side); write()
	emits sheet symbols and their sheet_instances paths. Every
	existing caller unaffected (the new parameter defaults off).
	* pptest_regression: new check_multisheet_hierarchy_kicad_cli --
	four real assertions: components and exact net names across the
	real hierarchy, 0 ERC errors with the report listing every
	sheet, and a negative case (an unmatched sheet pin is flagged
	hier_label_mismatch) so a clean result is not a vacuous pass.
	Also this project's own permanent guard against the upstream
	risk appearing in a future KiCad.
	* FutureResearch.org: the item moved TODO -> STARTED, with the
	findings, the decisions, the remaining increments (partitioner;
	generator integration and ppresolve_components' multi-file
	output) and one known, unverified hazard for the last (PWR_FLAG
	must be decided once on the whole netlist). ResearchJournal.org:
	full account.

2026-09-24  Péter Felecan  <pfelecan@gmail.com>

	Documentation only, no code changes. Refined FutureResearch.org's
	own removal criterion: an
	item marked "CLOSED" in its own prose, not only one literally
	tagged DONE, is removed once its own real account already stands
	in ResearchJournal.org AND its own text shows no real follow-up
	still open. Applied directly: two more real items removed this
	way (render()'s own performance at extreme scale;
	run_declutter()'s own real O(n^2) cost), alongside the
	already-identified "Build-instruction grammar extended" item --
	three more items removed, on top of the rail-direct item closed
	earlier this same session. FutureResearch.org's own "Current
	Status" list renumbered to match.

	Written down, for the first time, in DevelopmentStandard.org's
	own "Future Research Document" section: the full, real state-
	machine account this project's documents already declare via
	#+SEQ_TODO (TODO/STARTED/WAIT/RESTARTED/DONE/DISMISSED) but had
	never explained. Clarification: an item
	whose own real, remaining work is set aside for a later stage --
	not abandoned, not currently worked, but genuinely not finished
	-- is marked WAIT explicitly, never left untagged or folded into
	a closing note, so it can be unambiguously RESTARTED later.

	* DevelopmentStandard.org: "Future Research Document" section
	extended with the full state-machine account and the WAIT rule.
	* FutureResearch.org: four items marked WAIT, each checked
	directly against its own body, not guessed from its own title
	alone (run_declutter()/relieve_overflow()'s own decision-logic
	item; make check's own intermittent slowness; the rows-dependent
	placement anomaly's own hub-column residual; Anchor-group sector
	packing) -- and its own local Document Maintenance Rule section
	updated with a direct pointer to DevelopmentStandard.org's own
	full account.
	* Full make pdf confirmed clean throughout, including no broken
	links from any heading-text change. Full make check confirmed
	clean (195 checks, unaffected -- a real, pure documentation
	change).

2026-09-24  Péter Felecan  <pfelecan@gmail.com>

	Closed "a rail-direct power source's own real KiCad-side
	equivalent" (FutureResearch.org), by a combined approach: automatic
	pin-polarity classification
	first, --sources' own existing pin_map as a real, honest
	fallback, in that order.

	* power_sources.py: new classify_pin_polarity() -- a small,
	EXACT-match vocabulary ("+"/"vcc"/"vbat"/... vs
	"-"/"gnd"/"vss"/...), deliberately never a substring match.
	* lcsc_import.py: new parse_kicad_sym_pin_names() -- reads a
	real symbol's own real per-pin NAMES, matching the existing
	parse_kicad_sym_pin_types() pattern.
	* resolve_components.py: resolve_pin_identities() extended with
	rail_direct_sources/sym_dir parameters and a new pre-pass;
	fills pin_map via .setdefault() so a caller's own explicit
	entry, whole or partial, is never overridden -- real option 2
	is the SAME, already-established mechanism, completed
	automatically where genuinely missing, not a separate path.
	* ppresolve_components: the 2026-09-16 early refusal removed
	entirely; its own real call site updated to pass both new
	parameters through.
	* TestSchemes/PoorManAltimeter/altimeter-with-rail.conf: new,
	since the existing altimeter-no-rail.conf deliberately has
	every rail disabled.
	* Verified directly, end to end, against this project's own
	real altimeter circuit (BT1 = Device:Battery_Cell): the
	automatic path alone resolves BT1's own real polarity with
	ZERO pin_map entries needed; the resulting schematic passes
	kicad-cli ERC with 0 violations; BT1's own real pin 1 ("+")
	lands on VBUS, pin 2 ("-") on GND; round-trip regeneration
	reproduces identical instruction text. The fallback path
	verified directly too, at the unit level, against a real,
	synthetic, deliberately-ambiguous symbol.
	* pptest_regression: two new real checks
	(check_rail_direct_power_source_kicad_side,
	check_rail_direct_power_source_pin_map_fallback) -- ten new
	real assertions total, all passing; full make check confirmed
	clean (195 checks).
	* Workflow.org: Stage 4's own "NOT yet supported" paragraph
	replaced with a real, working example.
	* FutureResearch.org / ResearchJournal.org: this item removed
	entirely from FutureResearch.org (its own real account now
	standing permanently in ResearchJournal.org's own 2026-09-24
	entry) --, same
	session: an item that is genuinely, fully complete, and already
	documented in ResearchJournal, is REMOVED from FutureResearch.org
	rather than left there marked DONE. Applied consistently, this
	same session, to three other, already-fully-closed items found
	the same way (the "Build-instruction grammar" item, and the two
	older render()/run_declutter() performance items, both already
	recording their own real follow-up as a separate, distinct item
	rather than a remainder of themselves) -- all four removed
	entirely. The rows-dependent placement anomaly item (closed
	2026-09-23) re-scoped rather than removed, since it retains a
	real, deliberately-deferred, genuinely open remainder (a net
	needing more than one wire at its own hub column) -- its own
	closed history moved to ResearchJournal.org, only that open
	remainder kept in FutureResearch.org.

2026-09-23  Péter Felecan  <pfelecan@gmail.com>

	Closed the rows-dependent placement anomaly's own broader
	residual, real option 2:
	reactively relocate a component to a roomier column and bridge
	with a wire, only once it's confirmed it left no room for its
	own net's future wire -- never reserve anything proactively, the
	real, structural difference from two, earlier, reverted
	attempts at this same item (2026-09-03) that regressed an
	independent circuit by reserving margin up front.

	* auto_layout.py: relieve_overflow()'s own real trigger condition
	now folds in _wire_margin(ref) directly (computed once, moved
	earlier), not only a component's own body size.
	* pptest_regression: EXPECTED_RESULTS updated for seven real
	pairs across the standard 40-combination placement sweep -- all
	seven genuine improvements, zero regressions, confirmed directly
	against known, already-recorded ground truth (including the
	exact rp2040-basic-m1 "3sector-tight" case both prior attempts
	regressed on). Full make check clean (185 checks).
	* Verified directly, beyond this project's own test circuits:
	kit-dev-coldfire-xilinx_5213
	(this item's own original, motivating circuit) is available
	directly in KiCad's own installed demo package. Confirmed this
	fix also closes that circuit's own real failure, at both
	rows_per_half=17 and 20, via a direct, precise comparison
	against a fresh, pre-fix extraction of the same circuit.
	* FutureResearch.org / ResearchJournal.org: item marked DONE,
	with the full, real account; one, real, deliberately-deferred
	piece (a net needing more than one wire at its own hub) noted
	for a future attempt at extending the margin itself.

2026-09-23  Péter Felecan  <pfelecan@gmail.com>

	Documentation only, no code changes. Closed the real,
	navigation-breaking documentation gap noted at the end of last
	session: FutureResearch.org's own "schematic <-> instructions
	loop" item had grown, entry by entry over a week, to 573 lines --
	roughly a third of the whole document -- under a single, real
	heading with zero sub-headings at all, making it genuinely
	impossible to find any one finding inside it via the PDF's own
	table of contents (confirmed directly this was why three options
	already written up there could not be found).

	* FutureResearch.org: ten real, descriptive sub-headings added,
	splitting this item into its own overview, each of the original
	six items, the --config follow-up, and each of the three
	dudhya-pixel3a-power findings recorded there -- confirmed
	directly, the real, generated PDF's own table of contents now
	lists each one individually and separately. No content itself
	changed at all, only its own real structure.

2026-09-23  Péter Felecan  <pfelecan@gmail.com>

	Closed the fourth, and last, finding from dudhya-pixel3a-power's
	own investigation -- real option 2 and reasoning (a power input
	genuinely needs to be
	driven regardless of the original schematic; the round trip
	itself is a testing exercise, not this project's own primary,
	common use). Implementing it surfaced this project's own
	already-existing PWR_FLAG mechanism had, itself, apparently
	never once actually worked at all.

	* lcsc_import.py: new parse_kicad_sym_pin_types(), a real,
	dedicated function (deliberately separate from
	parse_kicad_sym_pins() itself) reading each real symbol's own
	electrical pin types directly from its own .kicad_sym file.
	* resolve_components.py: now populates nl.pin_types from it, for
	every real ref resolved -- closing the real root cause
	(nets_needing_pwr_flag() already existed but always read an
	empty field on a reconstructed netlist). Two further, real,
	separate bugs found and fixed immediately after: power.kicad_sym
	was never copied into sym_dir on this reverse path at all; and
	generate_schematic() itself (kicad_layout.py) hard-coded a
	literal "pwr" key that never matched the real PWR_FLAG symbol's
	own real pin (empty name, number "1") -- only ever matching
	example_pins.py's own hand-typed test fixture, itself corrected
	the same day. Fixed to use resolve_pin_key(), the same real
	helper every other pin lookup in that module already uses.
	* pptest_regression: new check_pwr_flag_on_reverse_loop, asserting
	0 ERC violations on dudhya-pixel3a-power's own full, real loop for
	the first time. Full make check clean (185 checks).
	* FutureResearch.org: finding 4 marked done, with the full,
	honest account of all three layered bugs; the standing loop goal's
	own summary updated -- all four findings closed, the goal itself
	kept open as an ongoing commitment.
	* ResearchJournal.org: full entry recorded, including the reasoning
	for choosing option 2 over option 1, and the
	honest account of why FutureResearch.org's own findings had been
	hard to find (a single, 501-line section with no
	sub-headings at all) -- not yet fixed, offered separately.

2026-09-22  Péter Felecan  <pfelecan@gmail.com>

	Closed the third finding from the same day's own dudhya-pixel3a-
	power investigation that a design discussion is always worth having
	first. Real options 1 and 3, together.

	* build_instructions.py: Step gained near_anchor_pin/far_anchor_pin;
	generate_steps() looks these up directly from
	p.anchor_pin_col/p.anchor_pin_half (layout()'s own, already-
	existing, forward-side data, for every one of an anchor's own
	placed pins); describe_step() states the connection directly in
	the generated prose.
	* parse_instructions.py: _PASSIVE/_PASSIVE_NO_FAR extended to
	read this new annotation back; reconstruct() links the real
	connection immediately when present.
	* resolve_components.py: resolve_implicit_anchor_connections()'s
	own real safety net narrowed -- only trusts its own offset-to-pin
	mapping when an anchor's own full symbol pin count exactly
	equals its own already-known relevant pin count plus every real,
	distinct unplaced column grouped to it. Two real mistakes made
	and caught closing this same narrowing, not glossed over: a
	first version compared relevant_pins() alone, refusing every
	real case this fallback is ever needed for at all; a second
	grouped candidates per ref rather than per anchor, double-
	counting a single passive's own two legs. Corrected directly,
	verified against both dudhya-pixel3a-power and solar-roller.
	* pptest_regression: two new checks
	(check_near_far_anchor_pin_annotation,
	check_implicit_anchor_connections_safety_net). Full make check
	clean (182 checks).
	* FutureResearch.org / ResearchJournal.org: finding 2 (shipped in
	0.72.2 but not yet recorded there) and finding 3 both marked
	done, with full accounts; a real, separate, fourth finding (a
	missing PWR_FLAG-style ERC annotation, unrelated to findings 1-3)
	recorded for future investigation.

2026-09-22  Péter Felecan  <pfelecan@gmail.com>

	Tackled FutureResearch.org's own first open item (the current
	"Current Status" order): ran the full schematic<->instructions
	loop against a genuinely new real circuit (dudhya-pixel3a-power)
	for the first time. Two real findings surfaced and reported
	before any code was touched; finding 2 fixed, finding 1 closed as a
	scope-widening (not a new fix).

	* power_sources.py: is_rail_like_name()'s own voltage-value
	regex now anchored to the START of a net name (re.match(), not
	re.search()) -- closes a real, confirmed false-positive
	(matching a voltage-value substring anywhere in a descriptive
	signal name, e.g. "VOUT_4V2", not only a bare rail name).
	Confirmed directly this preserves every already-documented
	intended match.
	* FutureResearch.org: the rows-dependent placement anomaly item
	retitled and widened -- confirmed the same, already-documented,
	deliberately-accepted trade-off also triggers for a net needing
	only a single extra_link wire, on a small board, not only 4+-
	member nets on a large one. No new fix attempted; both prior,
	real fix attempts already demonstrated a real regression from
	scaling the reserved margin at all.
	* A real, THIRD, genuinely new finding surfaced verifying
	finding 2's own fix via the full loop, reported, not yet investigated
	to root cause: item 5's own
	resolve_implicit_anchor_connections() (2026-09-18) misfires on
	this same circuit, linking a passive to the wrong real anchor
	pin -- confirmed NOT caused by an unconnected anchor pin, that
	function's own already-documented caveat. Left open.

2026-09-21  Péter Felecan  <pfelecan@gmail.com>

	Closed the follow-up finding from 0.72.0's own release, after a
	real, direct discussion first: why a board config matters at all
	for a tool whose own output is a KiCad schematic, and whether
	sheet count was the real, relevant detail instead. Traced
	precisely: reconstruct()'s own real hole decoding uses exactly
	one field from a board config (rows_per_half); sheet count is a
	genuinely separate, already-open, unrelated item. Decision: keep this
	project's own established --config
	convention consistent across its own CLI tools.

	* ppresolve_components: new --config option, same real
	resolution as ppbuild_instructions (checked next to
	INSTRUCTIONS-FILE instead of a schematic), plumbed into
	reconstruct()'s own board_config parameter.
	* ppresolve_components.1: documented the new option.
	* pptest_regression: new check_ppresolve_components_config_flag,
	including a direct control confirming the real mismatch this
	closes is real, not hypothetical.
	* A real, separate oversight found and fixed the same day:
	check_implicit_anchor_connections (0.72.0's own new test) had
	never actually been wired into main()'s own dispatch list, so it
	never once ran as part of a real, full make check despite being
	reported done. Both checks now run as part of the real suite.
	* FutureResearch.org: the --config finding marked done, with the
	full account of this discussion; a new, standing "Current
	Status" section added near the top, listing every genuinely
	still-open item in a considered order.
	* DevelopmentStandard.org: a new, standing rule recorded --
	FutureResearch.org's own status summary must be kept current at
	every delivery, not only checked right before one.

2026-09-18  Péter Felecan  <pfelecan@gmail.com>

	Closed items 4 and 5 of FutureResearch.org's own issue set --
	closing that whole set entirely. Item 5 discussed directly first,
	given the real risk of an over-broad fix; two real,
	separate, additional bugs found and fixed along the way.

	* lcsc_import.py: parse_kicad_sym_pins() now follows a real
	"(extends ...)" reference recursively, matching kicad-cli's own
	real symbol resolution, when a symbol's own pin search comes up
	empty.
	* kicad_layout.py: UnplaceableError gained a new, structured
	unplaced_refs attribute.
	* resolve_components.py: new resolve_implicit_anchor_connections()
	-- a deliberate, last-resort retry, tried only once
	generate_schematic() has already failed, for exactly the refs it
	names. Checks each such ref's own synthesized column net against
	every known anchor's own real span, merging the connection when
	found.
	* ppresolve_components: gained the matching catch-and-retry
	around generate_schematic_auto().
	* netlist.py: new Netlist.invalidate_ref_index() -- a real,
	deliberate, explicit exception to this class's own established
	caching invariant, closing a real, separate bug found the same
	day: a correctly-applied repair to nl.nets was still being
	missed by the very next placement attempt, since the cached
	ref/net index was never rebuilt.
	* pptest_regression: two new checks
	(check_kicad_symbol_inheritance, check_implicit_anchor_connections),
	the latter run via this project's own real CLI tools directly.
	* FutureResearch.org / ResearchJournal.org: both items marked
	done; a new, separate, real finding recorded for a future item
	(ppresolve_components has no --config option of its own at all,
	unlike ppbuild_instructions, and silently assumes the default
	board config when reconstructing).

2026-09-17  Péter Felecan  <pfelecan@gmail.com>

	Closed item 3 of FutureResearch.org's own issue set: a passive's
	own far leg, placed right at a board's own left edge, could
	resolve to a genuinely nonsensical column (at or below the
	board's own real edge) instead of a valid one.

	* breadboard_lib.py: leg()'s own final, relative-clearance
	fallback now also requires a genuinely valid column (>= 1) and
	the same real board, for whichever direction it picks -- exactly
	like every earlier search loop in this same function already
	does. Confirmed directly this is symmetric: the left edge always
	has a fixed boundary; the right edge is only ever bounded within
	a real multi-board/multi-sector assembly, already caught the
	same way.
	* pptest_regression: new check_far_leg_stays_within_board_edges,
	a direct, unit-level test plus the full, real altimeter
	demonstration.
	* FutureResearch.org / ResearchJournal.org: item 3 marked done,
	with the full, real account of the root cause and the fix.

2026-09-17  Péter Felecan  <pfelecan@gmail.com>

	Closed items 1 and 2 of FutureResearch.org's own new issue set,
	in the order it committed to. Found and fixed a real, separate,
	more consequential bug along the way: a missing state-reset call
	in this project's own test harness, which had been quietly
	corrupting item 1's own original test evidence.

	* build_instructions.py: describe_step() now says so honestly
	when a passive's own far leg genuinely can't be placed, instead
	of silently repeating its near leg's hole a second time.
	* parse_instructions.py: new _PASSIVE_NO_FAR pattern reads that
	honest sentence back correctly, registering only the real near
	pin.
	* auto_layout.py: layout()'s own rail_direct_power_source now
	requires EVERY one of a ref's own nets to be rail-like, not just
	one -- closing the real harm of a motor or a light sensor
	getting wired directly to a rail like a battery, silently
	dropping its own other, real connection.
	* pptest_build_instructions: _load() now calls
	breadboard_lib.reset_module_state() before every render(),
	matching pptest_regression/pptest_breadboard_config's own
	already-established practice -- its absence was producing
	confusing, seemingly random test failures depending on which
	other checks had already run. check_rail_direct_power_source and
	check_far_leg_unresolvable_is_honest both rewritten against
	genuinely reliable, reachable test cases as a result.
	* pptest_regression: two EXPECTED_RESULTS values correctly
	reverted (knight-roller's own D1/MTR1/MTR2 no longer qualify for
	rail-direct placement at all, structurally identical to
	solar-roller's own M1/M2).
	* FutureResearch.org / ResearchJournal.org: both items marked
	done, with the full, real account of what was found and fixed.

2026-09-17  Péter Felecan  <pfelecan@gmail.com>

	Ran the full schematic <-> instructions loop against a second,
	genuinely different real circuit (solar-roller), deliberately, to
	find what the altimeter's own single, clean example couldn't.
	Found and traced six separate, real bugs and gaps to their own
	root cause -- none fixed in this release. Documentation only.

	* FutureResearch.org: new issue set, six items, in a deliberate,
	reasonable fix order -- a misleading generated-instructions
	fallback; the new rail-direct power-source placement trusting
	classification with no sanity check of its own; a far-leg search
	with no awareness of board edges; a KiCad symbol-inheritance
	format this project can't read yet; and a reverse parser that
	can't recover an implicit, same-column connection. This project
	is not considered complete until this loop runs flawlessly, in
	both directions -- the standing goal.
	* ResearchJournal.org: the full account of this session's own
	investigation, including two direct workarounds used along the
	way (neither a real fix) to keep pushing the loop to its actual
	failure point.
	* LibraryReference.org deliberately left untouched this session --
	likely to change again once
	the six items above are addressed.

2026-09-17  Péter Felecan  <pfelecan@gmail.com>

	Rewrote Workflow.org to use this project's own real command-line
	tools throughout, no raw Python function calls at all. Also found and
	closed a real oversight:
	three scratch directories had shipped in the 0.69.0 archive
	without being asked for.

	* DevelopmentStandard.org: new "Directory Creation" subsection
	under File Naming, naming the concrete incident and the general
	rule -- no new directory without prior agreement, ever, for any
	reason; /tmp/ remains available for scratch
	work without asking.
	* Removed altimeter-demo/, altimeter-cli-demo/, workflow-demo/,
	none of which were needed by anything shipped.
	* Workflow.org: every stage now uses a real command-line tool
	(ppnetlist, ppauto_layout, pprender, ppbuild_instructions,
	ppparse_instructions, ppresolve_components), on one consistent
	circuit (the altimeter) throughout. Stage 3b's own direct
	schematic-to-schematic path, found to have no command-line tool
	behind it at all, reframed honestly as a documented gap, pointing
	to the instructions-based path (Stage 4) as the real, complete
	command-line way to reach the same destination.
	* TestSchemes/PoorManAltimeter/: new subdirectory, holding the
	altimeter's own schematic, both
	board configs, both sets of build instructions, and its sources
	file (moved from this project's own root, and its own missing
	pin_map entries re-applied). This project's own real TestSchemes
	count is now six, corrected in Workflow.org accordingly.

2026-09-16  Péter Felecan  <pfelecan@gmail.com>

	Fixed a real, genuine gap found by running this
	project's own actual command-line tools end-to-end (not
	internal functions): the default build-instructions format
	could never be read back by the reverse tools at all. Also
	closed a real-world placement observation: a
	genuine, simple power source is now wired directly to a board's
	rail system, the way a real person actually does it, instead of
	sitting on the main grid next to another component.

	* parse_instructions.py: parse_instructions() now recognizes
	format_plain()'s own default title header, scoped narrowly to
	the very start of a document only. New _RAIL_POWER_SOURCE/
	_RAIL_JUMPER regexes and matching reconstruct() handling.
	* auto_layout.py: layout() gained a new pre-processing step --
	any passive that's also a power source is placed directly on
	the rail system (Placement.rail_direct_power_source), preferring
	a vertical rail over horizontal when both are configured;
	falls back to ordinary placement when no rail is configured at
	all.
	* build_instructions.py: two new Step kinds (rail_power_source,
	rail_jumper) and matching prose, deduplicated across sources
	sharing the same rail.
	* ppresolve_components: detects a rail-direct power source
	directly and refuses with a clear, explicit error, rather than
	a confusing crash -- generating a new KiCad schematic for one
	isn't supported yet (see FutureResearch.org).
	* pptest_build_instructions: two new checks
	(check_plain_format_title_round_trips,
	check_rail_direct_power_source), 12 new assertions total.
	* pptest_regression: four EXPECTED_RESULTS values updated, a
	real, confirmed improvement (freed room now lets two circuits
	fit within bounds that previously didn't).
	* Workflow.org: new "Stage 4" section documenting the full,
	verified, command-line workflow from build instructions back to
	a breadboard or a new KiCad schematic.

2026-09-15  Péter Felecan  <pfelecan@gmail.com>

	Closed FutureResearch.org's own 2026-09-10 "Option A" item,
	deliberately deferred that day, revisited and expanded: build
	instructions can now
	state an anchor's own rail membership and power-source role
	explicitly, in both directions.

	* build_instructions.py: Step gained anchor_rail_sides/
	power_role; generate_steps() computes both, reusing
	high_degree_nets() and power_sources.classify_power_sources();
	describe_step() appends up to two new, optional sentences to
	an anchor's own step.
	* parse_instructions.py: _ANCHOR_WITH_TYPE/_ANCHOR_PLAIN
	extended with a shared, optional trailing suffix; reconstruct()
	adds each stated rail directly onto the same real "GND"/"VBUS"
	net name the passive branch already uses -- closing
	resolve_components.py's own resolve_components() docstring's
	own, previously-honest, documented limit (an anchor's own rail
	pins used to come back from a round trip on no real net at
	all). forced_anchors left completely untouched, a deliberate,
	pure addition.
	* pptest_build_instructions: new check_anchor_rail_and_power_role
	(13 assertions, both directions plus the no-rail-connection
	edge case) -- parse_instructions.py had no dedicated, automated
	test coverage at all before this.

2026-09-15  Péter Felecan  <pfelecan@gmail.com>

	Traced pptest_breadboard_config's own long-standing, severe
	performance pathology on very large circuits to its complete,
	precise mechanism, and closed it with a deliberate policy
	default rather than an algorithm change -- researched directly:
	real breadboard capacity and this project's own real circuits
	(6-50 components) both ground a sensible default ceiling.

	* breadboard_config.py: new DEFAULT_MAX_COMPONENTS constant
	(150), with the full, direct rationale in its own docstring.
	* auto_layout.py: new TooManyComponentsError; layout() takes a
	new max_components parameter, checked immediately, before any
	placement work begins.
	* kicad_layout.py: the same check, reusing the same constant,
	added to generate_schematic()/generate_schematic_auto() (the
	separate KiCad-schematic path, which never called layout() at
	all and so had no ceiling whatsoever) -- TooManyComponentsError
	now propagates immediately from the paper-size retry loop,
	matching UnplaceableError's own existing behavior.
	* ppauto_layout, pprender, ppbuild_instructions,
	ppresolve_components: new --max-components option, with clear
	error handling and updated manual pages.
	* pptest_breadboard_config: its own three deliberately-extreme-
	scale checks now explicitly opt into a higher limit, rather
	than hitting the new default by accident.
	* Makefile.am: new clean-local target, closing a real, genuine
	gap -- __pycache__ was never removed by
	either `make clean` or `make maintainer-clean` at all, since it
	is a Python-interpreter artifact, not an autotools byproduct,
	and CLEANFILES only ever handles files, never directories.
	* FutureResearch.org: new item recording a related, deliberately
	NOT-yet-started direction -- real, hierarchical, multi-sheet
	KiCad schematics, which this project does not support in any
	form today.

2026-09-14  Péter Felecan  <pfelecan@gmail.com>

	Reverted the delivery archive back to this project's own
	original, top-level layout, after it was noticed that it had quietly
	changed: every file used to sit at the archive's own
	root, but the last three releases (0.63.0-0.65.0) nested
	everything one folder down instead, a side effect of an
	unrelated packaging fix that was never itself flagged as a
	structural change at the time.

	* make-archive: confirmed, with a direct note in its own header,
	as this project's own primary delivery mechanism again, not
	automake's own `make dist-xz` (which is what introduced the
	nesting).
	* Makefile.am: added a matching note, so this doesn't drift back
	a second time for the same reason (an incomplete file list) that
	caused it the first time.

2026-09-11  Péter Felecan  <pfelecan@gmail.com>

	Every step of turning build instructions back into a working
	KiCad schematic can now be run as its own, standalone
	command-line tool, matching the convention every other tool in
	this project already follows. Also updated the README's own
	module list, which had fallen behind over several earlier
	releases.

	* ppparse_instructions (new): loads a build-instruction text
	file and prints the netlist and placement recovered from it --
	no resolution against real component data, mirroring ppnetlist's
	own role for the reverse direction.
	* ppresolve_components (new): the full pipeline -- parses
	instructions, resolves every component against real component
	data, and writes a working KiCad schematic. Real, external
	per-component data (which library a part comes from, or which
	design file to pull its pin layout from) is supplied via a new,
	real JSON file format (--sources).
	* altimeter-sources.json (new): a real, worked example of that
	file format, built from the altimeter circuit.
	* ppparse_instructions.1, ppresolve_components.1 (new): manual
	pages for both tools above.
	* README: brought the module list back up to date -- six
	existing modules from earlier releases had never been added to
	it, alongside the two new ones above.

2026-09-11  Péter Felecan  <pfelecan@gmail.com>

	Automatic power-source handling is now complete: when a circuit
	has more than one power source, the most-used one is correctly
	preferred for direct rail attachment, and any other source is
	handled the same, general way any ordinary component would be.
	Verified end to end with a real, two-source example circuit, on
	both the breadboard and schematic sides. Two real, separate
	placement gaps found and fixed while finishing this: a
	component's far connection point could, in rare cases, land on
	the exact same physical spot as a different component's own
	connection, undetected by the existing collision check; and a
	component needing to attach directly to a power rail could be
	left unplaced entirely, even with room genuinely available
	further along the board. As a welcome side effect, the second
	fix also resolved several already-existing components that were
	silently going unplaced on two of this project's own standard,
	more complex test circuits.

	* breadboard_lib.py (leg): now registers its own chosen far
	connection point directly, closing the first gap above.
	* auto_layout.py: the rail-attachment fallback now retries with
	a wider search on failure, closing the second gap above; the
	most-used-power-source-first placement ordering (previously
	shipped disabled, pending the above) is now permanently enabled.
	* build_netlist_multisource.py (new): a real, two-source example
	circuit, demonstrating the above end to end.
	* pptest_regression, pptest_build_instructions: expected test
	values updated to reflect the real, confirmed placement
	improvements above, not a regression; one test's own reliance on
	a specific circuit/board-size combination to exercise its
	"component genuinely can't be placed" case replaced with a
	small, dedicated example immune to any future placement
	improvement, closing a maintenance cost that had already
	recurred once before.

2026-09-10  Péter Felecan  <pfelecan@gmail.com>

	The complete loop -- build instructions read back into a working
	circuit description, resolved against real component data,
	turned into a real KiCad schematic, and carried back through to
	build instructions again -- now works end to end, verified
	against a real, three-part example circuit with zero electrical-
	rule errors. New: automatic identification of a circuit's own
	power source, so it gets placed and connected sensibly even when
	nothing else in the circuit happens to connect to it directly.
	One known item deliberately left disabled for this release: a
	related refinement (preferring the more-used source when a
	circuit has more than one) was found, once built, to expose a
	separate, pre-existing weakness in the placement search on one
	specific, complex circuit -- kept in the source, disabled, rather
	than risk that regression; recorded precisely for direct pickup
	next session.

	* parse_instructions.py (new): reads a generated build-
	instruction file back into a working circuit description.
	* lcsc_import.py (new), eagle_import.py (new): resolve a
	component named only by description in build instructions back
	into a real part, from KiCad's own installed libraries or from
	Eagle-format design files (the common case for suppliers who
	don't publish KiCad-native data at all).
	* resolve_components.py (new): ties the above together, plus
	handles a circuit whose reconstructed connectivity is
	necessarily incomplete (a real component can have real
	electrical connections a generated instruction set never had
	reason to state explicitly).
	* power_sources.py (new): identifies which component in a
	circuit is genuinely supplying power to it, using several,
	independent signals in order of directness (the part's own name,
	the nets it connects to, its own physical shape, and KiCad's own
	internal pin typing as a last resort) rather than trusting any
	one of them alone, since each was found, checked directly, to be
	unreliable in isolation.
	* kicad_layout.py: a circuit's own identified power source is
	now placed early and connected directly to the appropriate rail,
	rather than requiring it to already share a connection with
	something else already placed.
	* auto_layout.py: carries the same power-source information
	through to the breadboard side; the ordering refinement mentioned
	above lives here, currently disabled by default.

2026-09-10  Péter Felecan  <pfelecan@gmail.com>

	Found and closed a genuine, project-wide electrical-short blind
	spot: a component's own second connection point (its "far leg")
	was never checked against anything by this project's own
	short-detection code, only its first. Found while building a
	new feature that reads generated build instructions back into a
	circuit description, then confirmed directly against all eight
	of this project's own standard circuits, every one of which
	could produce a genuine, undetected short under a valid,
	previously-untested layout size. Fixed at the actual root cause
	in the placement search itself, not merely detected after the
	fact -- confirmed clean on all eight circuits afterward, and no
	change in behavior on any of this project's own existing,
	already-passing tests.

	* auto_layout.py: the short-detection check now also verifies a
	component's own second connection point, not just its first;
	moved one small, shared helper function here from a
	higher-level module to avoid a backwards dependency while doing
	so.
	* breadboard_lib.py: the low-level connection-drawing code can
	now be told which positions are already claimed by a different
	circuit connection, and will search around them the same way it
	already searches around anything visually in the way.
	* render.py: now tells the drawing code which positions are
	already claimed, built up correctly regardless of which order
	components happen to be drawn in -- an earlier version of this
	same fix got that ordering wrong and was corrected before
	release, confirmed empirically, not just re-reasoned about.
	* pptest_regression: the full-circuit render check no longer
	crashes outright the moment one circuit hits this class of
	problem; it now reports that one circuit as failed and keeps
	checking the rest.
	* parse_instructions.py (new): reads a generated build
	instruction file back into a working circuit description --
	the reverse of the existing feature that generates them. This
	is what surfaced the short-detection gap above in the first
	place.

2026-09-09  Péter Felecan  <pfelecan@gmail.com>

	Fixed two genuine performance problems in this project's own
	component-placement code, found directly from an independent live
	profile of an actual, slow, in-progress run on a separate computer --
	the single most valuable, concrete
	piece of information this whole investigation received. A
	genuine, one-line gap in this project's own available tooling
	was found and corrected along the way.

	* auto_layout.py: fixed two internal steps that were each
	re-checking far more of a circuit's own components than they
	ever actually needed to, every single time they ran, growing
	worse the larger a circuit is. Confirmed each fix produces
	byte-for-byte identical real results before and after.
	* A live profiling tool this project's own environment turned
	out not to have installed at all, despite an earlier check
	incorrectly suggesting otherwise -- installed and used directly
	to confirm the two fixes above.

2026-09-09  Péter Felecan  <pfelecan@gmail.com>

	Found and fixed three more genuine performance problems in this
	project's own component-placement code, on top of the two fixed
	earlier the same day -- each one found the same way: attaching a
	live profiler to an actual, slow, in-progress run, rather than
	guessing. Traced one of them all the way to its own, precise
	root cause in a completely different part of the code than
	expected. Directly tested this project's own largest real
	circuit and confirmed it isn't affected by any of this. A
	deeper, intermittent slowness in this project's own automated
	testing was investigated as far as this specific environment
	allows, isolated precisely, and left honestly open, with
	concrete next steps recorded for anyone with a different kind of
	machine to try.

	* auto_layout.py: fixed three more internal steps that were each
	re-checking far more of a circuit's own components than they
	ever actually needed to, every single time they ran, growing
	worse the larger a circuit is -- the same underlying pattern
	already fixed twice earlier the same day. Confirmed each fix
	produces byte-for-byte identical real results before and after,
	including against the one real, contributed circuit that
	specifically exercises one of these three code paths in
	practice.
	* Directly confirmed, with real, direct measurements rather than
	assuming: this project's own largest real, contributed circuit
	lays out in a few seconds, consistently, and is unaffected by
	the deeper, intermittent issue described below.
	* Investigated a deeper, harder, intermittent slowness in this
	project's own automated test suite specifically -- ruled out,
	directly and specifically, every explanation this project could
	test for itself (including a sharp, direct hypothesis about
	Python's own internal randomization, checked and ruled out).
	Recorded exactly what remains genuinely open, and what a
	different kind of computer could specifically try next, rather
	than leaving it as a vague, unresolved question.

2026-09-08  Péter Felecan  <pfelecan@gmail.com>

	Fixed the real slowdown recorded but deliberately left open in
	the previous delivery to
	pursue it. Found, along the way, that this specific slowdown
	was smaller than it first appeared, and that a separate, larger
	one exists in a different, deliberately untouched part of this
	project's own code -- both found and recorded honestly, not
	glossed over. Also confirmed, and removed, two files from this
	project's own test material that turned out not to be needed.

	* auto_layout.py: a specific, internal step in laying out a
	circuit could spend unnecessary time re-checking components it
	had already, correctly placed, growing worse the more
	components a circuit had. Fixed the same way as a related,
	previous fix, taking advantage of the fact that this
	project's own internal bookkeeping was already sorted, and
	confirmed, precisely, to produce identical real results,
	only faster.
	* Investigated further, and found honestly, not silently: two
	separate, careful attempts at
	speeding this same step up even further, beyond what was
	requested, did not actually help, and this project's own
	profiling tools showed exactly why. That same investigation
	also revealed a separate, much larger source of slowness
	elsewhere in this project's own code, deliberately left
	untouched given that specific area's own long, documented
	history of past attempts causing more problems than they
	solved. Recorded precisely, for a future, dedicated attempt,
	rather than rushed.
	* Investigated a real, intermittent slowness in this project's
	own verification process itself, following a sharp, direct
	question about whether it might share a root cause with a
	previously-fixed issue. Confirmed, directly, that it does not
	share that cause, but the actual, underlying reason remains
	unresolved -- recorded honestly as a real, open question rather
	than worked around silently.
	* Confirmed, directly and carefully, that two files bundled
	with one of this project's own contributed, real-world test
	circuits were never actually needed by anything, and removed
	them.

2026-09-08  Péter Felecan  <pfelecan@gmail.com>

	Fixed the real slowdown found but deliberately left open in the
	previous delivery, on the principle that a known performance problem
	should not be left in place. Found,
	but did not fix, a separate, real, related slowdown along the
	way.

	* netlist.py: added a real, shared, one-time lookup built the
	first time it's needed, so this project's own internal netlist
	representation no longer has to search the entire circuit from
	scratch every time it needs to answer a simple, real question
	like "which other connection does this specific component
	have". Applied consistently to every place in this project's
	own code that asked this same kind of question, not just the
	one originally reported, since leaving two other, identical
	slowdowns sitting right next to the fixed one would go against
	the same real principle. Confirmed, not assumed, to produce the
	exact same real results as before, only faster -- roughly
	twelvefold faster on the real circuit shape that first exposed
	this, and over twentyfold faster on a real, full-scale test.
	* Added new, permanent, automatic tests confirming this stays
	fixed going forward. Two real, separate lessons learned while
	building them, both corrected before trusting the tests: a
	simple, fixed time limit turned out to be an unreliable way to
	test for this, since this real environment's own speed can
	genuinely vary depending on what else is running at the same
	time -- replaced with a test that checks proportional
	slowdown as the circuit grows, which stays meaningful
	regardless. And an early version of that same test also
	revealed a real, different, unrelated slowdown getting in its
	way, described next.
	* Found, while building those same tests, a separate, real,
	still-open performance problem in an unrelated part of this
	project's own placement logic, made worse rather than better by
	simply giving a circuit more room -- a real, useful, counter-
	intuitive clue about its actual real cause. Recorded precisely,
	with a concrete direction for a real future fix, in this
	project's own list of open work, deliberately left unfixed here
	as it falls outside what was actually asked this time.

2026-09-08  Péter Felecan  <pfelecan@gmail.com>

	Fixed a real crash that could occur when laying out a large,
	dense circuit, found but deliberately left open in the previous
	delivery. Found and recorded, but did not fix, a separate, real
	performance issue along the way.

	* auto_layout.py: a specific, internal step in laying out a
	circuit could exceed a real, built-in limit on how deep one
	piece of internal bookkeeping was allowed to recurse into
	itself, on a circuit with enough individual small components.
	Rewritten to do the exact same real work as a straightforward,
	repeated pass instead, removing the limit entirely rather than
	simply raising it and risking the same real problem again on a
	still-larger future circuit. Confirmed, not assumed, to produce
	the exact same real layout result as before, only without the
	real risk of crashing.
	* Added a new, permanent, automatic test confirming a real
	circuit with more of these small components than the one that
	originally crashed now lays out cleanly, with no special
	adjustment needed at all.
	* Found, while building that same test, a separate, real,
	unrelated slowdown: a specific internal lookup that becomes
	needlessly slow when many small components are all connected to
	one, shared point in the circuit. Recorded precisely, with a
	concrete direction for a real future fix, in this project's own
	list of open work, rather than addressed now.

2026-09-07  Péter Felecan  <pfelecan@gmail.com>

	Investigated and fixed three separate, real causes behind this
	project's own real, currently-largest circuit (a real, 1507-
	component demo) being far too slow to lay out and draw --
	found to all share the same real
	underlying mistake: a function repeating work proportional to
	everything already processed, on every single call, rather
	than reusing what it already knew. Along the way, found and
	fixed a genuine, real correctness bug in the first attempt at
	one of the three fixes, and found (but did not fix) a separate,
	real crash at this same large scale.

	* Component ordering, part of preparing a layout, sped up
	roughly fourfold on this project's own largest real circuit --
	from recomputing the same real distances repeatedly to
	remembering them once each. Confirmed, not assumed, to produce
	the exact same real result as before, only faster.
	* A real, internal drawing-overlap check, part of producing the
	final diagram, sped up from never finishing within an extended,
	four-minute real limit to about twenty real seconds, by
	checking only genuinely nearby drawn elements instead of every
	element drawn so far. A first attempt at this real fix had its
	own real, genuine bug (confirmed via direct testing, not
	assumed correct), caught and fixed before being kept: it could
	miss the actual worst-positioned candidate on a nearby but
	non-overlapping placement, though this is confirmed to never
	have affected the actual, final overlap check the whole tool
	relies on. Two new, permanent, automatic tests specifically
	guard against this same class of gap recurring.
	* A separate, real, internal room-search routine sped up by
	replacing repeated, real lookups with direct, equivalent
	arithmetic wherever a specific position was already known to
	fall within the same section of the board.
	* Found, but deliberately left open rather than patched around
	informally, a separate, real crash: laying out a circuit this
	large and dense can exceed a real, built-in limit on how deep
	one specific piece of internal bookkeeping is allowed to
	recurse into itself. Recorded precisely, with real, candidate
	fix directions, in this project's own list of open work.

2026-09-06  Péter Felecan  <pfelecan@gmail.com>

	Tackled the open item on grouped-component sector placement,
	and found the real problem was more fundamental than its own
	description suggested: the specific outcome for a circuit that
	doesn't fit in one section of the board could genuinely differ
	from one run to the next, on the exact same input, with nothing
	else changed.

	* Confirmed first that a board genuinely too small for a
	circuit is already handled correctly elsewhere (this project
	already either respects a given board description honestly,
	including telling the user it won't fit, or works out and
	proposes a real, sensible board size that will) -- not this
	item's own real concern at all.
	* auto_layout.py: found and fixed the actual, previously
	unnoticed problem: when several components were equally good
	candidates for placement, which one actually got picked could
	depend on incidental, run-to-run randomness in how Python
	itself orders things internally, rather than on anything
	meaningful about the circuit. The same general kind of issue
	had already been found and partly fixed once before for a
	different pair of components; this closes the general case
	that earlier fix didn't reach. Fixed with a small, explicit,
	deterministic tie-breaking rule, changing nothing else about
	how placement decisions are made.
	* Added a new, permanent check confirming this specific
	scenario now produces identical results every time, run
	repeatedly as entirely separate program runs -- necessary
	because the randomness in question only shows up between
	separate runs, not within one.
	* FutureResearch.org: rewrote the corresponding item to reflect
	what was actually found and fixed, and narrowed what remains
	open to a real, optional, lower-priority refinement, revisited
	only if it turns out to matter in practice.

2026-09-06  Péter Felecan  <pfelecan@gmail.com>

	Added two new documents closing a real, previously named gap:
	guidance for a user whose real circuit doesn't lay out well,
	and guidance for a developer investigating this project's own
	code, both grounded in this project's own real, already-
	demonstrated practice rather than general advice.

	* Troubleshooting.org: covers the three real situations a user
	can run into -- placement failing outright, a technically valid
	but visually crowded result, and a clean but non-optimal one --
	each with concrete, current commands and real, actual example
	output, including a real, documented cautionary example of a
	tuning change that passed its own automated check while making
	the actual result worse to look at.
	* DeveloperDebugging.org: a working methodology for debugging
	this project's own code, illustrated throughout with real,
	specific examples already lived through in this project's own
	recent history rather than invented advice -- verifying a
	claim directly before acting on it, confirming genuine
	randomness before trusting a non-deterministic result, proving
	a fix's own real effect by reverting it and watching the
	original problem return, a state-leak pattern that has already
	recurred more than once in this project's own real test code,
	and the pristine-extraction check this project performs before
	every real delivery.
	* Added a real field report received from an external user
	(previously only read, never actually kept as part of this
	project) into this project's own real source tree, since one
	of the new documents above references it directly as a worked
	example.
	* Both new documents wired into this project's own real
	documentation index, cross-referenced from the concept
	document, and confirmed to build cleanly.

2026-09-06  Péter Felecan  <pfelecan@gmail.com>

	Investigated a previously recorded, intermittent crash and
	confirmed it no longer occurs, then proved exactly why rather
	than simply noting the improvement. Also recorded, directly in
	this project's own list of open work, the answers to two
	follow-up questions about an earlier, related investigation.

	* Confirmed a previously reported, occasional crash (an
	internal error with no useful information, happening roughly
	one time in three under one specific, unusual configuration)
	no longer happens, by running the exact original reproduction
	steps repeatedly. Went further than simply confirming this:
	temporarily undid last session's own defensive check one more
	time to prove, directly, that this check is what closed the
	crash, rather than it happening to go away for some unrelated
	reason. Added a permanent, automatic check confirming this
	specific crash cannot recur.
	* FutureResearch.org: closed the item recording this crash, and
	added the answers to two direct follow-up questions -- what a
	real circuit would need to look like to test a related, still-
	open question, and a more detailed explanation, with concrete
	examples, of the two conditions that would make that question
	worth revisiting -- directly into the corresponding open item,
	not left only in conversation.

2026-09-06  Péter Felecan  <pfelecan@gmail.com>

	Made this project's own component-mounting policy explicit and
	enforced it automatically, and thoroughly investigated a real,
	previously-recorded open question, concluding it isn't
	currently actionable and explaining exactly why.

	* auto_layout.py: documented, directly and explicitly, that
	this project's own default component footprints are meant to
	be through-hole only, with surface-mount parts used only as a
	last resort and only when a real, known breadboard adapter
	exists for that specific part -- never a bare surface-mount
	footprint on its own, since surface-mount parts have no leads
	to plug into a breadboard's own holes. This was already true
	of every one of this project's own current defaults, but had
	never been written down, leaving no guardrail against a future
	change accidentally breaking it.
	* Backed this with a new, permanent, automatic check rather
	than relying on the written policy alone, confirmed to
	genuinely catch a real violation before being trusted.
	* Investigated, in full, an open question about whether this
	project's own layout engine ever tries reorienting an
	oversized component before giving up and reporting it couldn't
	be placed. Confirmed directly that it does not. However, after
	checking every real component type this project currently
	supports, found that none of them currently has an unused,
	smaller real alternative to switch to -- for one type, the
	only real alternative is actually larger, not smaller.
	Concluded that building a general fix for this now would mean
	building something with no real, current case to test it
	against, and documented this conclusion precisely, along with
	the exact conditions that would make it worth revisiting later.

2026-09-06  Péter Felecan  <pfelecan@gmail.com>

	Acted on a real, external field report from dudhya (this
	project's own extraction origin, still using it in practice),
	along with the real circuit that motivated it. Verified every
	one of the report's five findings directly against this
	project's own current code before acting on any of them; two
	were confirmed, real bugs, now fixed; one was a process gap,
	now recorded; two were lower-confidence or design-sensitive
	observations, recorded honestly rather than acted on
	unilaterally.

	* auto_layout.py: fixed a real bug where a component's own
	physical size, for the two categories most likely to be
	mounted standing up on a breadboard (resistors and diodes),
	was measured using its own factory, flat-mounted dimension
	regardless of how it would actually be built -- silently
	overestimating the real space needed, and, in the reported
	circuit, causing several components to be placed outside the
	board's own real limits. Corrected to use each part's own
	real, standing-up dimension instead. Deliberately did not
	apply the same correction blindly to every other kind of
	component, since not all of them have a confirmed, real
	equivalent to standing a part up -- doing so without checking
	first would have traded one wrong assumption for another.
	Added a documented, optional way for a caller with different,
	confirmed real data to override this project's own default
	directly.
	* Also added a documented, optional way to find out, after the
	fact, exactly which components (and why) had to fall back to
	an older, less precise sizing estimate instead of using real,
	measured data -- closing a real, reported source of wasted
	debugging time, where the previous version gave no way to
	answer that question directly.
	* render.py: fixed a real bug where asking this project's own
	renderer to draw a layout that hadn't actually been checked
	first could crash with an obscure, unhelpful internal error
	instead of a clear one. This project's own renderer now checks
	the layout itself before drawing anything, and reports a
	clear, specific problem if it's not valid.
	* Found and fixed a real, unrelated regression this same
	sizing fix caused in one of this project's own existing tests,
	by finding the real, exact point at which the test's own
	original assumption stopped holding, rather than simply
	loosening the test to accept the new behavior.
	* DevelopmentStandard.org: recorded a real, general lesson from
	the report's own account of a past change that unexpectedly
	broke dudhya, this project's own real external user, even
	though it looked entirely safe from inside this project's own
	tree alone.
	* FutureResearch.org: added a new, honestly lower-confidence
	item from the report's own final observation, which the
	report's own author was careful to flag as worth investigating
	rather than already confirmed.
	* TestSchemes: added the real circuit dudhya contributed
	alongside their own report, and folded it fully into this
	project's own existing, automatic regression sweep, not left
	as a one-off, manual check.

2026-09-03  Péter Felecan  <pfelecan@gmail.com>

	Audited this project's own documentation set for freshness
	(aside from the two files that are meant to accumulate history
	rather than stay current), removed narrative references to the
	author and unnecessary date entries throughout, and brought the
	concept document's own goal section up to date. No code
	changed; this delivery is documentation only.

	* Concept.org: the goal section was missing two real,
	significant capabilities entirely (the build-guide generator,
	and multi-board assembly support) -- added. Cross-references
	to this project's own other, user-facing documents added,
	replacing the single existing cross-reference to the research
	journal; a development-standards cross-reference was
	deliberately left out, on direct correction, since that
	document's own audience is this project's own contributors,
	not its users, and it is already referenced from the top-level
	index instead.
	* SessionHandoff.org: found entirely stale (still describing
	this project at its very first, unreleased version) and
	rewritten to reflect the current, real state and this
	project's own genuinely current open items.
	* Index.org: a real, concrete gap found and fixed -- last
	session's own new verification tool was missing from the
	manual-page listing entirely.
	* Workflow.org: a worked example still invoked a command by its
	own old, pre-rename name, and a separate passage flatly and
	incorrectly claimed this project's own placement engine still
	doesn't use per-board configuration at all -- both corrected
	against real, freshly re-verified behavior, not merely edited
	and trusted.
	* LibraryReference.org: several real, confirmed inaccuracies
	corrected, each checked directly against the actual, current
	code first rather than assumed stale: a function's documented
	signature was missing a real, current parameter; a real
	capability (a component connecting only through shared rails)
	was described as unsupported when it has worked for some time;
	a real, existing function was described as still unbuilt;
	three long-deleted functions were still listed as present; and
	a closing note claiming an entire class of connection "isn't
	drawn at all yet" was significantly out of date.
	* breadboard_config.5: real manual-page content, but six
	passages narrated as direct quotes or requests from the
	author, rewritten as plain, impersonal statements without
	losing the substance of what each one said.
	* pprender.1: three real, current command-line options had no
	documentation at all -- added, along with a new section
	explaining the real configuration-resolution strategy those
	options control.
	* ppbreadboard_config.1: found and fixed a real, internal
	contradiction -- one part of the page claimed two tools both
	consume a configuration file, while another part of the very
	same page said neither one does yet. Confirmed directly which
	claim was actually true before fixing both to agree.
	* A systematic check of every remaining manual page's own
	documented options against its own program's real, current
	command line, plus a narrower sweep for the same author-
	narration and stray-date issues -- one real instance of each
	found and fixed; one apparent gap, found by this same check,
	turned out to be a false alarm caused by the check's own method
	and was corrected before any real edit was made, once directly
	confirmed against the actual file.
	* FutureResearch.org: one real, previously untracked, already-
	documented-in-code limitation (the right vertical rail's own
	real connection support) added as a new, precisely scoped open
	item, per direct instruction that anything found this way could
	be recorded rather than silently corrected.
	* DevelopmentStandard.org was deliberately left untouched this
	round: many of its own author references are the direct record
	of a real development decision, not incidental narration, and
	whether the same standard should apply there is left as a real,
	open question rather than guessed at.

2026-09-03  Péter Felecan  <pfelecan@gmail.com>

	Turned this project's own broad, multi-circuit verification
	sweep -- previously run by hand, from scratch, every single
	development session -- into a genuine, permanent, automated
	part of "make check". Every regression this project has
	actually found so far was only ever caught by running this
	exact sweep manually; it now runs on its own, every time.

	* pptest_regression: a new verification tool. Checks placement
	across all seven of this project's own standard test circuits
	against five standard board configurations (35 combinations),
	and checks the drawn output itself across the same seven
	circuits, all against precisely re-derived, confirmed-stable
	expected results -- not a saved snapshot that would need
	manual re-approval every time a genuine improvement legitimately
	changes an outcome.
	* breadboard_lib.py: found and fixed a real bug while building
	this new tool, before it shipped: the drawing routine's own
	internal state was never being reset between separate drawings
	within the same run, so testing several circuits back-to-back
	in one process caused later circuits to be incorrectly flagged
	as clashing with earlier, entirely unrelated ones. Added a
	proper way to reset this state, confirmed to fix the problem
	precisely.
	* Confirmed the new tool actually works as intended, not just
	that it runs: temporarily undid the fix above and confirmed
	the new checks correctly caught it, failing in exactly the way
	the real bug did, before restoring the fix.
	* Makefile.am: wired the new tool into the build system
	properly, including a manual page matching this project's own
	established style, and a correction to run it at the right
	point -- after, not before, both of this project's own example
	circuits have been generated, since it depends on both already
	existing.

2026-09-03  Péter Felecan  <pfelecan@gmail.com>

	Investigated whether an open placement issue could be improved
	using the same "plan ahead rather than decide one at a time"
	idea behind the previous release's own board-assignment work.
	It could not, in a way worth shipping -- and a different,
	genuine fix was found and completed instead.

	* auto_layout.py: Tried two variations of giving certain
	shared connections more accurately-sized room in advance,
	rather than a fixed, uniform amount. Both resolved the two
	specific cases that motivated this, but both also caused three
	previously-working circuits to newly fail under one particular
	configuration. Reverted cleanly back to the existing, safe
	behavior -- confirmed to exactly match, byte for byte -- since
	avoiding new failures on real circuits takes priority over
	fixing a narrower, already-documented case. This negative
	result is recorded precisely so the same two approaches aren't
	tried again without new information.
	* breadboard_lib.py: Separately, found and fixed a real,
	previously-unexercised bug: when a design spans several
	physical boards and falls back to the board's own edge
	connector as a last resort, that fallback's own internal
	counter was shared across every board at once, rather than
	kept separate per board. A board processed later could
	inherit an already-advanced count from an earlier one, causing
	its own connections to silently drift past where they should
	have started -- not a crash, just a quietly wrong result,
	which is likely why nothing had caught it before now.
	* Verified this fix matters, not just that it's safe: since
	the existing regression suite never exercises this specific
	code path, direct inspection of the routine's own internal
	state confirmed the two boards' own counts now advance fully
	independently, exactly as intended.
	* Found, while testing, a separate, unrelated, pre-existing,
	intermittent crash on one specific real circuit under an
	unusual configuration. Confirmed directly this happens whether
	or not the new fix is present, so it isn't connected to this
	work -- recorded precisely, with an exact way to reproduce it,
	for whoever looks into it next, rather than investigated further
	here.
	* pptest_breadboard_config: a dedicated, permanent, reliably
	repeatable test added for the real fix.
	* FutureResearch.org: updated to reflect all of this precisely
	-- one item closed, one negative result recorded so it isn't
	repeated, and one new, separate finding added.

2026-09-03  Péter Felecan  <pfelecan@gmail.com>

	Improved how larger, highly-connected components are assigned
	to a board when a circuit is split across several, closing a
	real limitation named in the previous entry: the earlier
	approach only ever considered components in the order they
	were being placed, and could never revisit an earlier decision
	even when doing so would have produced a better overall
	result.

	* auto_layout.py: Board assignment for these components now
	looks at all of them together first, before placing anything,
	and assigns the largest, most space-demanding groups to a
	board before the smaller ones -- allowing a small, later
	group to make use of room left over from an earlier
	assignment, something the previous, one-at-a-time approach
	could never do.
	* Found and fixed a real, serious bug while verifying this
	change, before it was ever shipped: an early version could,
	for a component group too large to fit any single board at
	all, cause several unrelated components to land on the exact
	same physical connection point -- an electrically incorrect
	result, not just a suboptimal one. Traced to its precise root
	cause and fixed directly. Confirmed corrected on the exact
	circuit that revealed it, then reconfirmed safe with the same
	full, repeated comparison used throughout this work: zero
	change to any existing circuit's own result.
	* Confirmed this change achieves a real, better outcome, not
	just a theoretical one, using a circuit specifically built to
	require it: fitting on two boards where the earlier approach
	would have needed three. A direct, side-by-side comparison
	against the previous version was attempted but abandoned when
	the reconstruction used for comparison purposes turned out to
	be unreliable -- rather than trust a shaky comparison, this
	result was verified on its own, independent merits instead.
	* pptest_breadboard_config: a dedicated, permanent test added
	for this real improvement.
	* Following a direct conversation about why an even more
	thorough approach (reconsidering already-made decisions, not
	just planning better upfront) was set aside as too risky for
	now, FutureResearch.org records that reasoning and sets out
	clear, concrete conditions for when it would become worth
	pursuing -- not a vague "maybe later."

2026-09-02  Péter Felecan  <pfelecan@gmail.com>

	Completed the placement-partitioning work from the previous
	entry: the harder, riskier piece deliberately deferred there is
	now addressed, with a genuine, repeatedly-confirmed improvement
	to show for it, not another partial result.

	* auto_layout.py: Components with a lot of internal connections
	(rather than the simpler, smaller ones) are assigned to a
	board through a different, simpler process than the one fixed
	previously -- one that only ever moves forward, never
	reconsidering an earlier decision. Rather than a bigger, riskier
	rewrite of that core process, found a safer way to improve it:
	group these larger components by their own real, existing
	connections to each other first, then decide board placement
	one whole group at a time instead of one component at a time --
	keeping a closely related group together when it would fit on
	a fresh board, even if it wouldn't fit in what's left of the
	current one.
	* Verified safe the same rigorous way as every change this
	session: compared against a clean, unmodified baseline three
	separate times, with zero change to any existing circuit's own
	result.
	* This time, confirmed the improvement directly and repeatedly,
	not just in principle: built a real, carefully constructed test
	circuit designed to hit exactly the situation this fix
	addresses. Without the fix, two closely related components end
	up on two different boards; with it, they land on the same one.
	Confirmed consistently across five separate runs.
	* pptest_breadboard_config: a dedicated, permanent test added
	for this, not left as a one-off check.
	* FutureResearch.org: the item's own specifically-identified,
	dominant gap is closed. Two narrower, honestly-named
	limitations remain and are recorded as their own, separate,
	precisely-scoped item -- a component group too large to fit any
	single board at all still cannot be helped by this fix, and
	the current approach only ever looks at one group at a time,
	never reconsidering an earlier group to make room for a later
	one.

2026-09-02  Péter Felecan  <pfelecan@gmail.com>

	Investigated placement partitioning for assemblies of several
	boards -- how to decide which component goes on which board
	to minimize the connections needed between them, rather than
	just whether everything fits at all. This entry reports the
	result honestly, including where it fell short, rather than
	overstating what was actually achieved.

	* auto_layout.py: Found and fixed a real, specific gap: when a
	component has to move to a different board because its
	preferred one is full, the routine deciding which other board
	to try used to check them in a fixed, arbitrary order, with no
	regard for which board already held more of that component's
	own related connections. This is now preferred correctly.
	Verified safe with no change to any existing circuit's own
	result, checked repeatedly against a clean baseline.
	* Built a direct, quantitative test on a real, densely
	connected circuit to confirm this actually helps -- and it did
	not measurably reduce the number of connections needed between
	boards, confirmed by reverting the fix and re-measuring. Traced
	why rather than accepting that at face value: in the circuit
	tested, the great majority of cross-board connections came
	from a completely different part of the process -- how larger
	components (rather than smaller, simpler ones) are assigned to
	a board in the first place, which uses an entirely separate,
	simpler rule this fix does not touch.
	* Kept the fix, since it is correct and verified safe on its
	own terms, even though its real-world benefit could not be
	shown on the circuits available for testing. Properly
	addressing the larger, dominant part of this question would
	require a more significant change to how larger components are
	assigned to boards -- not attempted here, given the risk of
	rushing a change to that core part of the process without
	adequate time to verify it properly.
	* FutureResearch.org: narrowed to reflect this honestly -- not
	closed on an overstated claim.

2026-09-02  Péter Felecan  <pfelecan@gmail.com>

	Added a new way to order build instructions, grouping steps
	by which specific part of a component they connect to, so
	related components (a crystal and its own supporting
	capacitors, for example) are described together rather than
	scattered throughout the instructions.

	* build_instructions.py: A previous attempt at this had been
	deliberately deferred, since the simplest version of the idea
	turns out to fail badly on microcontroller-based circuits: a
	microcontroller connects to almost everything, so grouping by
	"shares a connection with the same chip" collapses nearly
	every other component into one, unhelpful group. This version
	groups by the specific pin instead, which avoids that problem.
	* Found and corrected the same kind of collapse in a second,
	related place during testing, before it shipped: components
	sharing a common ground connection were also being lumped
	together for the same underlying reason. Fixed the same way,
	reusing this project's own existing definition of which
	connections are too common to be a useful grouping signal.
	* Confirmed this works as intended against a real,
	microcontroller-based circuit: related parts (a crystal and
	its own capacitor, a matched pair of signal resistors) are now
	grouped together correctly, and no single group ends up
	holding more than half of the circuit's own components.
	* pptest_build_instructions: dedicated, permanent tests added
	for this new ordering option.
	* FutureResearch.org: this item closed.

2026-09-02  Péter Felecan  <pfelecan@gmail.com>

	Fixed a genuine, confirmed placement bug where, in specific
	cases, giving the layout more room to work with could
	paradoxically make the result worse rather than better.

	* auto_layout.py: Traced this to a real interaction between
	two separate parts of the layout process: a component
	successfully sharing a column with a connection point could
	crowd that column so much that a separate wire -- needed for
	a different connection on the same electrical net -- no
	longer had anywhere to go, even though the board itself had
	grown taller. Fixed with two complementary changes: the
	routine that draws these connecting wires now chooses its own
	starting point based on which column actually has room left,
	rather than an arbitrary numbering; and a small amount of room
	is now set aside in advance for columns that a connection is
	known in advance to need extra space for.
	* Found and corrected a real misstep during this work, before
	it went any further: an earlier version of the second change
	reserved too much room in one specific circumstance -- when
	the same component appears more than once on the same
	electrical net -- which caused a different, previously-working
	circuit to fail. Caught this by comparing results against a
	genuinely clean baseline before trusting the fix, not by
	assuming it was correct.
	* Confirmed this resolves both specific, real-world cases that
	originally revealed the problem, verified repeatedly rather
	than on a single run, with zero change to any other existing
	circuit's own result, checked three separate times.
	* A narrower version of the same underlying problem remains,
	honestly identified rather than hidden: connections involving
	four or more shared points can still occasionally be affected
	in a similar way, at specific board heights. This is recorded
	precisely as its own, smaller, separate item rather than
	folded into a claim that everything is now resolved.
	* FutureResearch.org: original item closed; the narrower
	remaining case recorded as its own new item.

2026-09-02  Péter Felecan  <pfelecan@gmail.com>

	Fixed a real, precisely-diagnosed drawing bug affecting
	assemblies of several different breadboards: a component's
	second connection point could be chosen without any awareness
	of which physical board it needed to stay on.

	* breadboard_lib.py: The routine that decides where a
	component's second lead lands now knows, when given the
	necessary information, which physical board that lead's own
	first connection point is on, and will not choose a position
	that would cross onto a different board. Confirmed to behave
	identically to before for every existing use that doesn't
	provide this information.
	* Found and fixed a second, related bug while verifying the
	first, this time by running the same test repeatedly rather
	than trusting a single result (an earlier, single-run check
	had wrongly suggested this was already fully resolved): one
	of a component's own connecting wires was never recorded as
	occupied space at all, so a different, later-drawn
	component's own connection could be placed directly on top of
	it undetected. Fixed by recording that wire's own position
	the same way a component's visible body already was.
	Confirmed against every standard test circuit and several
	realistic circuits tested against a genuine multi-board
	assembly, each run repeatedly, with consistent results.
	* Found, and left honestly open rather than rushed, a real,
	structurally distinct limitation that only a deliberately
	extreme, artificial test case exposes: a component's own
	FIRST connection point is decided earlier in the process,
	before any drawing happens at all, and cannot be reconsidered
	at drawing time no matter how the second point's own search
	is improved. Confirmed this does not affect realistic
	circuits and is recorded as its own new, separate item in
	FutureResearch.org rather than folded into a claim of
	complete resolution.
	* FutureResearch.org: the original board-crossing item
	closed; the newly-found, structurally distinct limitation
	recorded as its own precisely-scoped item.

2026-09-01  Péter Felecan  <pfelecan@gmail.com>

	A real, previously-undiscovered layout bug found and fixed by
	testing the three largest untested KiCad demo circuits,
	completing this project's own wider-test-surface item.

	* breadboard_config.py: Found and fixed a genuine bug in how
	very wide circuits are handled: an internal limit on how many
	columns a single board section could use (1000, based on an
	assumption drawn only from this project's own two hand-measured
	boards) turned out to be too small for a real, external
	circuit -- KiCad's own official "video" demo genuinely needs
	over 1400 columns of room within a single section. This caused
	an outright crash rather than a wrong result. The limit has
	been raised well past any real circuit this project has
	encountered, confirmed to have no effect on any existing,
	smaller circuit.
	* Confirmed this same fix also allows a second, even larger
	demo circuit (over 1100 components) to render correctly.
	* Found, but not yet resolved, a separate, genuine limitation
	at this project's own largest scale so far: its single largest
	test circuit (over 1500 components) takes so long to process
	that it did not complete within an extended time limit. This
	is confirmed to be a performance question, unrelated to the
	correctness bug above, and is recorded precisely for future
	investigation rather than worked around or ignored.
	* FutureResearch.org: the demo-testing item is now fully
	complete and closed; its own remaining, genuinely open pieces
	recorded as their own separate items.

2026-08-31  Péter Felecan  <pfelecan@gmail.com>

	Substantial cleanup of the project's own future-research
	document, and a genuine dead-code finding in the rendering
	library.

	* FutureResearch.org: removed 20 already-completed items whose
	full accounts already exist in ResearchJournal.org, plus three
	older sections that had explicitly described themselves as
	temporary snapshots but had never been removed. One item that
	mixed already-finished work with a genuinely still-open
	question was split, keeping only the open part. Verified every
	removed item's content was safely preserved elsewhere before
	deleting it. The document goes from 2374 lines to under 300,
	with five genuinely open items remaining.
	* breadboard_lib.py: Found and removed three functions
	(trimmer_straddle, usb_c, mosfet) that had no callers anywhere
	in the codebase -- confirmed dead code, something their own
	comments had already noted without it being acted on. This
	also corrects an overstated claim in a recent changelog entry,
	which described a bug in these same functions as affecting
	real, shipped output; that was only true for one of the four
	functions involved, not all of them.

2026-08-28  Péter Felecan  <pfelecan@gmail.com>

	Rendering and build-instruction generation for assemblies of
	several genuinely different breadboards, completing the
	integration begun the previous day. Testing this genuinely end
	to end, rather than only checking it compiled, surfaced a long
	chain of real bugs -- several of them pre-existing, predating
	this integration entirely.

	* breadboard_lib.py: The board-drawing logic is restructured so
	the exact same drawing code runs once per physical board,
	whether those boards are identical copies of one configuration
	or each genuinely different.
	* Found and fixed a real bug already present in the previously
	shipped, same-shaped-boards version of this feature: four
	component-drawing routines (integrated circuits, trimmers,
	USB-C connectors, MOSFETs) never actually accounted for which
	board a component was on when computing its vertical position.
	Any such component placed on a board other than the first would
	already have rendered in the wrong place, silently -- this was
	never visible as an error, only as an incorrect drawing, until
	now.
	* Found and fixed a second, similarly pre-existing bug in the
	same area: a position-checking routine converted a component's
	column number incorrectly whenever more than one sector was in
	use, which could have hidden genuine overlapping-position
	errors or reported false ones.
	* render.py: every remaining place that assumed a single,
	shared board configuration -- the main position-calculation
	routine, canvas sizing, the legend, and the routine that draws
	every component's own connection to power or ground -- updated
	to work correctly for a real assembly of different boards, each
	confirmed to behave exactly as before for existing use.
	* Found and fixed a genuine placement bug, not just a drawing
	one: when a component needed to be moved to make room, the
	code validated the new position against the smallest board in
	the whole assembly rather than the specific board the new
	position was actually on. This could cause two different
	components to be placed in the exact same physical location.
	Verified against the full regression suite with zero change to
	any existing result.
	* build_instructions.py: the same class of fix applied here as
	well.
	* A narrow, specific remaining limitation found and precisely
	documented rather than left as a mystery or silently patched
	over: extremely dense circuits placed very close to a board
	boundary can still occasionally have two connections computed
	to land in the same spot. This is confirmed to only happen at a
	board boundary, not as a general limitation. As an interim
	measure, build-instruction generation for such a case now
	reports the affected connection honestly as unresolved rather
	than crashing outright.
	* FutureResearch.org: recorded in full, including the remaining
	limitation as its own precisely-scoped item.

2026-08-27  Péter Felecan  <pfelecan@gmail.com>

	The deeper heterogeneous multi-board integration: the layout
	engine itself now places components across several genuinely
	different boards, not just computes correct geometry for
	them.

	* auto_layout.py: _find_room() and verify_within_board_bounds()
	made genuinely board-aware -- each candidate position and each
	final placement is now checked against the specific board it
	actually belongs to, rather than one shared row/column limit.
	Confirmed to behave identically to before for every existing
	single-board or identical-boards configuration.
	* Found row/column limits used in roughly twenty further
	places throughout the placement engine's own internal logic,
	all of it heuristic guidance rather than final correctness
	checks. Rather than updating each individually (real, high
	risk to extensively-verified machinery), added a single, safe
	stand-in configuration -- the smallest row and column count
	across every board in the assembly -- for that internal logic
	to use, while the two correctness-critical checks above
	continue consulting each component's own actual board. This
	keeps every heuristic decision safely conservative without
	risking a wrong result.
	* Confirmed working with a real, controlled test circuit
	deliberately built to overflow one small board on its own,
	which then places completely cleanly once a taller second
	board is added to the assembly.
	* A genuine, precisely-diagnosed limitation found and recorded
	while confirming this: which board a given component's main
	body lands on considers available width but not height, so a
	component's own power/ground connection can still end up
	needing more room than its own board provides. The underlying
	reason is a deliberate, existing design rule (a wire's
	connection point can never be moved once assigned, since doing
	so would silently disconnect it) -- a real fix would need
	placement itself to consider board height earlier, not yet
	attempted.
	* pptest_breadboard_config: three new tests added.
	* FutureResearch.org: full account recorded, including what
	remains open -- the rendering and build-instructions side of
	this same integration haven't been touched at all yet.

2026-08-27  Péter Felecan  <pfelecan@gmail.com>

	Confirmed empirically that multi-sector boards cannot resolve
	a row-depth overflow at any sector count; began the
	heterogeneous multi-board work this motivates.

	* Verified directly, not assumed, that the previous session's
	finding (no test circuit fits the corrected, smaller default
	row count) already accounted for multi-sector escalation:
	pprender's own default path already tries this automatically.
	Re-ran all seven standard test circuits with a physically
	realistic, bounded board width across one to four sectors
	explicitly, confirming the same result holds even outside the
	unbounded-width case the earlier test happened to use: no
	number of sectors resolves a row-depth overflow, since every
	sector of one board configuration shares the same row count by
	design. Sectors can still help place individual components
	that need more horizontal room, but not this specific problem.
	* breadboard_config.py: Added the foundational geometry layer
	for assemblies built from multiple, genuinely different boards
	(distinct row counts, widths, or other configuration per
	board) rather than several identical ones -- six new
	functions, each built on the existing single-board geometry
	entirely unchanged, adding only the correct cumulative
	position for each board in sequence. This is a deliberately
	narrow first step: it does not yet change how components are
	actually placed or rendered across such an assembly, matching
	how this project's own multi-board file format itself was
	built and proven before anything used it.
	* pptest_breadboard_config: Twelve new tests added, checked
	against a real two-board assembly with genuinely different
	configurations on each board.
	* FutureResearch.org: both findings recorded in full, including
	a clear account of what remains open for the next step -- using
	this new geometry when actually deciding where components go,
	not just when drawing the result.

2026-08-27  Péter Felecan  <pfelecan@gmail.com>

	Default row count corrected to match a real, physical
	breadboard, exposing and fixing two latent bugs along the way.

	* breadboard_lib.py, breadboard_config.py: The default number
	of rows per breadboard half-sector is now 5, matching a real,
	physical breadboard's own terminal-strip layout. The previous
	value (10) traced back to this project's own earliest history
	with no record of ever having been checked against a real
	board -- and a real, second confirmation was already sitting
	in this project's own files: breadboard1.conf's own comment
	has said the real, measured value is 5 since much earlier in
	this project's history, something the old default had quietly
	disagreed with the whole time.
	* render.py: verify_no_hole_collisions() -- part of every
	render's own core verification -- was found to have always
	used a fixed, module-level row count directly, silently
	ignoring any board configuration's own, different value. This
	was invisible for as long as the two happened to agree by
	coincidence, and surfaced immediately once they diverged.
	Fixed to use the actual board configuration throughout.
	* build_instructions.py: hole_name() had the identical bug,
	silently falling back to the same fixed row count instead of
	the real board's own value. Fixed the same way.
	* A significant, expected consequence: every existing test
	circuit, including the deliberately simple synthetic examples,
	now needs an explicit, taller board configuration to place at
	all, since the smaller, more realistic default genuinely
	doesn't fit them. Confirmed this fails cleanly and honestly
	rather than crashing, and that every circuit renders correctly
	again once given a suitable, explicit configuration.
	* Verified: all seven standard test circuits pass every check,
	including the newly-fixed one, given an explicit, sufficient
	configuration. The full regression suite shows every
	explicitly-configured result unchanged, with only the default
	case affected as intended. make check passes clean.
	* FutureResearch.org: recorded in full, including the two
	bugs found while confirming the change.

2026-08-27  Péter Felecan  <pfelecan@gmail.com>

	The apt-method demo set tested for the first time; a
	documentation correction; a genuine, reproducible placement
	anomaly confirmed on two independent circuits.

	* fetch-kicad-demos: Corrected the header comment and --help
	text, which had characterized the apt method's older KiCad
	version as an inherent property of the method itself. It's
	actually a property of this project's own aging development
	sandbox (an Ubuntu LTS release that, like all Ubuntu LTS
	releases, never receives newer upstream package versions after
	release) -- on a more current system, both packages would be
	version-matched with no discrepancy at all.
	* Analyzed all 19 real schematics in the apt-method demo set.
	13 of these turned out to have component and net counts
	identical to their same-named git-method counterparts,
	confirming they're the same underlying circuits under an older
	file-format stamp rather than genuinely different content;
	testing effort was focused on the 6 apt-only demos instead of
	redundantly re-testing the 13 overlaps. 5 of those 6 passed
	cleanly; one (flat_hierarchy) failed with the same
	already-understood row-overflow pattern seen in earlier
	circuits.
	* A methodological correction made while investigating that
	failure: an apparent pattern across a range of board heights
	turned out, on closer inspection, to include at least one
	genuinely non-deterministic result (the same run producing
	different outcomes on separate invocations, a previously-known
	characteristic of this project's own tooling) mixed in with
	otherwise real, reproducible ones. This prompted re-checking a
	similar-looking finding from the previous session the same,
	more rigorous way -- confirming it, unlike the noisy one, is a
	genuine, fully reproducible anomaly: a taller board consistently
	failing where a shorter one consistently succeeds. The same
	anomaly was then confirmed on this session's newly-found
	circuit too, independently, making it a real pattern on two
	unrelated circuits rather than an isolated curiosity.
	* FutureResearch.org: recorded as its own, precisely-scoped
	item, given it's now confirmed reproducible rather than a
	one-off, though not yet investigated further.

2026-08-27  Péter Felecan  <pfelecan@gmail.com>

	fetch-kicad-demos: a persistent cache, a second, alternate
	fetch method, and a thorough investigation of the four
	previously-found placement failures.

	* fetch-kicad-demos: Redesigned around a persistent, XDG-
	standard cache directory, not touched by this project's own
	clean mechanisms. A re-run with no flags now skips fetching
	entirely and reports what's already cached; --update forces a
	refresh; --clean removes it, either for one method or the
	whole cache. The prior -o DIR option is kept, now explicitly
	documented as bypassing the cache for a one-off copy.
	* fetch-kicad-demos: New --method apt, using the "kicad-demos"
	Debian/Ubuntu package as a second, genuinely different source
	(an older KiCad release's own demo set, including several
	real, older projects the current git-based set no longer
	carries). Installable without root: downloaded and extracted
	directly, never touching the system's own package database.
	* Investigated the four previously-found failures thoroughly
	rather than just re-confirming them. Two resolve cleanly
	through the existing compute_optimal_config(). The other two
	do not, for a real, already-documented reason found by
	re-reading that function's own docstring before reaching for a
	fix: it only escalates sector count, and these two circuits
	overflow the board's own row depth instead, a separate,
	physical property no amount of extra sectors can address. Both
	resolve correctly once given a taller board instead, confirming
	the existing design was correct rather than incomplete.
	* Confirmed, at real scale, a previously-documented placement
	limitation (components connected only to power/ground rails,
	with no other net to place them by) accounts for the large
	majority of one circuit's remaining unplaced components.
	* Continued climbing the complexity ladder through two more
	tiers; two further failures found, both matching the same,
	already-understood cause. One genuinely unexplained anomaly
	noted for future investigation: one circuit resolves with a
	moderately taller board but fails again on an even taller one.
	* FutureResearch.org: the wider test surface item updated with
	the full account of this session's own findings.

2026-08-26  Péter Felecan  <pfelecan@gmail.com>

	KiCad's own official demo/example package, as a wider test
	surface: a new fetch-kicad-demos script, a complete, empirical
	complexity table across all 33 real demos, and a first,
	bottom-up test batch.

	* make-archive: Fixed a real, confirmed regression from the
	previous session's own rename: the executable-bit check list
	still named every script by its old, pre-rename filename, so
	none of the thirteen new pp-prefixed files were ever checked.
	* fetch-kicad-demos: New. Fetches KiCad's own official demo
	schematics via a sparse, shallow clone of an actively synced
	GitHub mirror (the primary repository, on GitLab, is
	unreachable from this project's own development environment).
	Auto-detects which git tag to fetch from the installed
	kicad-cli's own version, after finding that the mirror's
	default branch tracks live development and produces schematic
	files in a format newer than a released KiCad can read.
	Idempotent; kept external by design, never copied into
	TestSchemes/ or shipped in this project's own distribution.
	* Investigated and recorded: a complete, empirical complexity
	table across all 33 real demo circuits (component, net,
	anchor, passive, and rail counts for each, loaded directly
	rather than estimated), spanning 3 to 1507 components.
	Component count adopted as the primary ordering criterion.
	* A direct spot-check across a handful of these already
	surfaced genuine placement failures on four real demos,
	worth investigating as this testing continues.
	* A first, bottom-up batch (the six smallest demos, 3-8
	components each) all passed cleanly, with one render visually
	confirmed correct -- including a positive finding that SPICE
	simulation-only symbols are already handled gracefully with no
	special-casing needed.
	* FutureResearch.org: the "wider test surface" item updated
	from TODO to STARTED, with the full account of what's been
	investigated, built, and tested so far, and what remains open.

2026-08-26  Péter Felecan  <pfelecan@gmail.com>

	Four direct fixes, then a large, pervasive rename: every
	executable script drops its own file extension and gains a
	project-specific "pp" prefix, avoiding collisions with common
	tool names (render.py -> pprender).

	* man2htmldoc: Fixed the documentation index back-link, which
	pointed one directory level too high (../Index.html instead
	of Index.html).
	* Index.org: Added the missing breadboard_config(5) link, plus
	three more man pages found unlinked the same way
	(test_breadboard_config.1, build_instructions.1,
	test_build_instructions.1). Updated all thirteen command
	man-page links to their new, renamed targets.
	* breadboard_config.5: New "MULTI-BOARD ASSEMBLIES" section
	covering both real mechanisms (sectors_per_board for identical
	boards under a limit; the separate [multiboard] file format
	for boards that genuinely differ), with a complete, three-file
	worked example -- verified by actually loading it through the
	real parser, not just written to read plausibly.
	* Investigated before executing the rename: five scripts
	(netlist, footprint, breadboard_config, auto_layout, render)
	are both real CLI tools and densely cross-imported libraries,
	a real tension since Python's import statement needs a ".py"
	extension. Resolved by splitting library code from CLI code
	for each (the original file keeps its own name and loses only
	its CLI glue; a new, separate, extensionless pp-prefixed file
	gets that glue) -- confirmed before executing, along
	with a naming convention. A sixth script,
	build_instructions.py, was found to need the same treatment on
	a second, more careful dependency sweep, after being
	mistakenly bucketed as pure-CLI on the first pass.
	* Nine further scripts, genuinely pure CLI tools with no
	library role, renamed directly. All thirteen command man pages
	renamed to match; breadboard_config.5 kept its own name, since
	it documents a file format, not a command.
	* Two functionally critical bugs found and fixed during
	testing: two subprocess calls each referenced a now-
	nonexistent old filename by name, which would have crashed
	outright -- confirmed fixed by actually running both affected
	tools.
	* Makefile.am rewritten (EXTRA_DIST, MAN_PAGES, check-local)
	and verified by a full bootstrap/configure/make-check cycle
	from the renamed tree.
	* Documentation sweep across the project's own org files:
	live reference documentation updated to the new names;
	historical narrative (ResearchJournal.org, closed
	FutureResearch.org items, SessionHandoff.org's own dated
	account) deliberately left unchanged, since rewriting a
	historical record to use a name that didn't exist at the time
	would make it inaccurate, not more current.
	* Verified: every renamed or split file compiled and was
	functionally tested (CLI invocation and, where applicable,
	library import) individually. Full 35-combination
	placement/rendering stress test: zero difference from the
	confirmed baseline. Every standard test circuit rendered
	cleanly through the new pprender. make check passed clean from
	a fresh bootstrap.

2026-08-26  Péter Felecan  <pfelecan@gmail.com>

	New feature: build_instructions.py, converting a breadboard
	layout into a textual, step-by-step build instruction list --
	the first script in this project that produces prose, not a
	picture. Closes FutureResearch.org's own "build instructions"
	item, following a design session that settled every open
	question with real, worked-example comparisons before any
	implementation began.

	* build_instructions.py: New. Component descriptions built
	from structural netlist data (lib/part), not guessed from the
	value string. Two ordering strategies (position, the default,
	confirmed to reduce backtracking by a wide margin on a dense
	real circuit; netlist, a low-cost alternative), both
	implemented as small, separate, genuinely pluggable functions.
	Four output formats (plain, markdown, HTML, org-mode).
	--child-level for simpler phrasing. Links default to fully
	offline (a static Wikipedia table for generic passives, a
	reliable search-engine query link otherwise); --search-
	datasheets opts into a real web request for a more direct
	result -- this project's first genuine network access anywhere,
	designed to degrade gracefully on any failure.
	* render.py: passive_far -- the far leg position render()
	already computes internally via a stateful, order-dependent
	collision search -- is now also exposed on p.passive_far_xy.
	Confirmed necessary, not a convenience: an independent search
	for the same position could genuinely land on a different
	column than the one actually drawn, silently producing
	instructions that don't match the diagram. Confirmed
	byte-identical SVG output after this change.
	* Two real bugs found and fixed during testing: render()'s own
	diagnostic prints polluting this tool's own text output when
	called internally (suppressed locally); both ordering
	strategies crashing on a circuit with genuinely unplaced
	components, since sorting itself looked up placement data for
	a component that was never placed.
	* test_build_instructions.py, build_instructions.1,
	test_build_instructions.1: New, matching this project's own
	established testing and documentation conventions. Wired into
	check-local and MAN_PAGES.
	* Verified: a dedicated test suite; a full 35-combination
	placement/rendering stress test showing zero difference from
	the confirmed baseline; both new man pages read via their own
	rendered HTML output, not just written and trusted.
	* FutureResearch.org: the build-instructions item closed, with
	a new, separately-scoped item recording net-grouped step
	ordering as a deliberately deferred follow-up.

2026-08-26  Péter Felecan  <pfelecan@gmail.com>

	Multi-board assemblies: the same board-awareness bug, found
	one more time, in the one function that actually matters most.
	Prompted directly by a request to check that every column
	needing power or ground uses the rail on its own sector or
	board.

	* breadboard_lib.py: to_rail() -- the one function that draws
	every component's own actual power/ground connection -- had
	never been made board-aware, despite the same bug class
	already being found and fixed three separate times elsewhere
	in the previous session (render.py's own _y(), rail-bridge
	code, and extra_notes). All three of its own tiers (nearest
	rail, left rail, no-rail fallback) were still computing
	positions via the plain, non-board-aware geometry functions
	directly. Fixed by reusing the already-built, already-verified
	row_y_multi_boardaware()/rail_y_for_global_sector() directly,
	rather than re-deriving the same fix a fourth time.
	* A narrower, remaining gap found and recorded, not fixed: the
	left-rail tier's own connection-spacing counter does not reset
	per board, so a connection on a later board could in principle
	drift past that board's own limited left-rail extent if enough
	earlier connections, on an earlier board, already advanced it.
	Left open since this tier is a genuine last resort, never
	exercised by any real circuit in this project's own test suite
	in combination with a multi-board split.
	* Verified: byte-identical for the single-board case across
	the test suite (with two circuits' own already-known,
	unrelated, intermittent non-determinism in wire-routing order
	re-confirmed as the sole source of any diff, not a real
	change). A genuine four-board split visually confirmed:
	every component's own power/ground connection now correctly
	aligns with its own board's own rail. Full 35-combination
	stress test: zero difference from the confirmed baseline.
	make check confirmed clean.
	* FutureResearch.org: the fix and the narrower, remaining gap
	both recorded precisely.

2026-08-25  Péter Felecan  <pfelecan@gmail.com>

	Multi-board assemblies, step three (rendering) completed. Real
	rail continuity across sectors AND boards, drawn as genuine
	jumpers between real holes -- not decorative lines -- plus a
	real per-board legend on the side. Closes two direct caveats
	on the previous session's own visible progress: a real
	vertical-alignment bug for components on boards after the
	first, and a long-standing rail-continuity gap the existing
	code's own comments had already named but never closed.

	* render.py: New board-aware Y-position helpers used
	throughout (row_y_multi_boardaware(), rail_y_for_global_sector()
	in breadboard_config.py) -- fixes components on any board
	after the first appearing "slightly offbeat on the vertical
	axis," since _y() and the rail-bridge code were still calling
	the plain, non-board-aware geometry functions directly.
	* render.py: The "cross-rail jumper" mechanism generalized
	from same-sector N/S only to every real, distinct physical
	rail a net touches, from every real source (anchors, passives'
	near-pin AND far-leg rail attachment -- the old code only ever
	checked anchors). A subtler issue found along the way: a
	global rail index alone isn't enough to identify one physical
	rail once boards exist, since the same index can mean two
	separate rails once a board boundary falls between the sectors
	that would otherwise share it -- fixed with a
	(board_index, local_rail_index) key.
	* render.py: Rail bridges now drawn as genuine jumper() calls
	between two confirmed rail-hole columns (skipping the real,
	deliberate gap columns group_size clustering leaves empty),
	not a plain line at an off-hole pixel offset. Inter-board wire
	connectors added: when a direct wire or rail bridge would need
	to cross a board boundary, a short, dashed off-board connector
	stub is drawn at each end instead of a physically-impossible
	continuous line.
	* render.py: Per-board legend moved to the right of each
	board, not underneath -- avoids making the vertical gap
	between boards depend on each board's own component count.
	For a single board, the legend stays exactly where it has
	always been, confirmed byte-identical.
	* Verified: full 35-combination stress test shows zero
	difference from the confirmed baseline throughout (every
	change here is additive to rendering, touching nothing in
	auto_layout.py). make check confirmed clean repeatedly. A
	genuine 4-board split of rp2040-basic-m1 visually confirmed:
	correct per-board legends, rail bridges landing on real holes,
	correctly-paired off-board connectors.
	* FutureResearch.org: The multi-board item's own rendering step
	(step three) closed; only placement partitioning (deciding
	which components land on which board to minimize inter-board
	connections) remains open.

2026-08-25  Péter Felecan  <pfelecan@gmail.com>

	Multi-board assemblies, step two: the geometry foundation for
	TRUE splitting, per two direct design decisions -- a
	real, per-board sector limit; a real visual boundary and
	inter-board wire connectors. Clarified the harder-vs-simpler
	design fork via a direct question before committing to a
	substantial implementation, then built and thoroughly verified
	the foundation, deliberately stopping short of the rendering
	restructure itself given its own scale and risk.

	* breadboard_config.py: New BoardConfig.sectors_per_board (0,
	the default, means "no limit" -- reduces every new function to
	today's existing, single-board behavior exactly). New
	board_of()/sector_within_board() (the sector-to-board grouping
	math, confirmed against a real "4 sectors per board, 10 total"
	example: correctly produces boards of [4, 4, 2] sectors). New
	board_y_shift()/board_y_shift_for_board(), letting every
	EXISTING, unmodified geometry function (row_y_multi(),
	rail_y_for_sector(), rail_y()) produce board-aware output --
	confirmed by direct algebraic reasoning before writing any
	code that this works, since every one of those functions
	already reduces to "a sector offset plus a fixed within-sector
	amount." New num_boards()/sectors_on_board()/
	total_multiboard_height_px().
	* Found a genuinely lower-risk architecture than the obvious
	one: rather than a new, independent column-space encoding for
	multiple boards (which would touch the placement algorithm
	directly, a real risk given its own eight-attempt
	stabilization history), multiple boards are modeled as a
	grouping of the SAME, existing, continuous sector sequence
	placement already works across. The placement algorithm itself
	needs zero changes; only rendering needs to know where a board
	boundary falls.
	* test_breadboard_config.py: Extended with a dedicated set of
	checks for all of the above, including the [4, 4, 2] grouping
	example and a direct, numeric confirmation that boards don't
	visually overlap, with exactly the configured gap between
	them.
	* Verified: full 35-combination placement/rendering stress
	test shows zero difference from the confirmed baseline
	(expected -- no placement or rendering code changed this
	session, only new, additive geometry functions). make check
	confirmed clean.
	* Deliberately NOT attempted: draw_board_background()'s own
	restructuring (per-board outer rectangles, a per-board legend,
	a board-aware rail loop) and inter-board wire connectors (a
	genuinely new drawing mechanism) -- both real, substantial,
	risky changes to central, heavily-used code, left for a
	dedicated next step rather than rushed under this session's
	own remaining time.
	* FutureResearch.org: The open design questions this item
	previously left unanswered are now answered and recorded
	precisely; the item's own remaining scope (rendering,
	placement partitioning) is recorded with the same precision.

2026-08-25  Péter Felecan  <pfelecan@gmail.com>

	Multi-board assemblies, step one: MultiboardConfig, a real,
	deliberately narrow SCHEMA-ONLY step, matching exactly how
	breadboard_config.py itself began -- a real file format and a
	real, tested parser/writer, before any integration into
	placement or rendering. Closing a real, pre-existing gap along
	the way: breadboard_config.py itself had no automated test at
	all before this.

	* breadboard_config.py: New MultiboardConfig dataclass, plus
	load_multiboard_config()/write_multiboard_config(). A
	multi-board file lists real board-config file paths
	(comma-separated, in a real [multiboard] section), each
	parsed via the EXISTING, unchanged load_config() -- reusing
	the proven single-board parser directly, not a new INI syntax
	for repeated sections. Named MultiboardConfig, not
	"AssemblyConfig" -- this module's
	own existing terminology already uses "assembly" extensively
	to mean one board's own whole structure; reusing it for a
	different, higher-level concept would have collided with that
	established meaning.
	* test_breadboard_config.py: New. Closes a real, confirmed gap
	found while building this -- breadboard_config.py had never
	had an automated test, despite extensive manual, in-session
	verification across many sessions. Covers both the existing
	single-board functions (default_config(), load_config()/
	write_config(), resolve_config_path()) and the new
	MultiboardConfig ones, verified against two real boards
	(breadboard1.conf, breadboard2.conf), not synthetic
	fixtures. Its own first run caught a mistake in the TEST
	itself, not the code (a wrong assumption about
	default_config()'s own sector.columns default) -- fixed the
	assertion after confirming the code's own behavior was
	correct and already documented.
	* test_breadboard_config.1: New manual page, matching
	test_netlist.1's own established style.
	* Makefile.am: Wired the new test into check-local (make
	check now covers breadboard_config.py directly) and the new
	man page into the existing MAN_PAGES/EXTRA_DIST machinery.
	* Verified: full 35-combination stress test against the
	placement algorithm shows zero difference from the confirmed
	baseline (expected -- this session's own changes are
	schema/test/doc only). make check confirmed clean.
	* FutureResearch.org: The multi-board item's own first,
	config-file step recorded as closed; placement and rendering
	integration remain explicitly, deliberately open, with the
	same real design questions as before still unanswered.

2026-08-24  Péter Felecan  <pfelecan@gmail.com>

	The breadboard config file format now has its own real
	documentation, closing the earlier request for it, now that the format
	has gone unchanged since the
	2026-08-21 structural correction.

	* breadboard_config.5: New man(5) page for the file format
	itself -- the physical rail-sector-rail model, every real
	[section]/key with its own meaning and default, a real, worked
	example, and a precisely-documented subtlety: omitting a
	[rail]-style section entirely defaults to absent, but
	INCLUDING an empty one defaults to present, a real, deliberate
	asymmetry.
	* breadboard_config.1: Found and fixed significant staleness
	while writing the new page -- this page still described the
	pre-correction schema (own_rails, [rail.bottom], fields that
	no longer exist) and falsely claimed no other program consumed
	a config file, when auto_layout.py and render.py both
	genuinely do throughout. The duplicated, stale schema section
	is now a cross-reference to breadboard_config(5) instead.
	* breadboard_config.py: Two related stale docstrings fixed --
	resolve_config_path()'s own docstring and the whole module
	header both still described a pre-integration state from
	2026-08-19, predating all the placement/rendering integration
	work since.
	* Makefile.am: A real, distinct build path for section-5 pages
	added (MAN5_PAGES, a real, explicit HTML rule) -- a real,
	confirmed collision was caught and fixed before this shipped:
	a naive suffix-based rule would have produced the SAME output
	filename as breadboard_config.1's own page, silently
	overwriting one with the other.
	* Verified: both HTML pages build cleanly and independently
	(confirmed by direct content inspection, not just a clean
	exit), make check unaffected, both key circuits' own placement
	confirmed unaffected by the docstring changes.

2026-08-24  Péter Felecan  <pfelecan@gmail.com>

	The eighth real attempt, and the one that finally works:
	solving the ping-pong problem together with the room-
	visibility gap, as directly instructed. Both
	example_circuit1's own long-standing "Z" net issue and
	rp2040-basic-m1's own sector-count bias -- the circuit that
	started this whole multi-session investigation -- now resolve
	on a SINGLE sector. Zero regressions across the full
	35-combination stress test.

	* auto_layout.py: _adjacency_adjusted_row() predicts
	settle()'s own real formula PROACTIVELY, before a candidate is
	ever committed to, in both real directions (would the
	candidate itself be pushed; would accepting it push an
	existing, different neighbor into overflow instead). Applied
	to both push-aside and relieve_overflow(), combined with
	relief_history (structurally preventing the per-ref
	oscillation) and a search radius wide enough to cover the
	whole sector (fixing the sector-count bias itself), and
	_wire_margin()/col_wire_margin (reserving real room for future
	direct wires, kept separate from col_depth so settle() never
	misinterprets it).
	* Three real, distinct bugs found and fixed during this same
	session's own careful, incremental testing, each caught by
	direct verification before being trusted: (1) an initial,
	one-directional adjacency check fixed circuit1 but caused a
	confirmed regression (solar-roller), fixed by making the check
	bidirectional; (2) the wire-margin mechanism's own "fold into
	col_depth before extra_links runs" step was itself wrong --
	it made the room-assignment pass skip the reserved rows
	instead of using them; removed entirely, since col_depth
	already reflects real occupancy and the room-assignment pass
	naturally finds what was kept clear; (3) the adjacency check's
	own reverse-direction overflow check didn't account for a
	pushed neighbor's own separate wire-margin need on top of its
	body size -- fixed by including it.
	* Verified: example_circuit1 resolves at sectors=1 (previously
	sectors=2, and entirely unable to resolve for the past two
	sessions). rp2040-basic-m1 resolves at sectors=1 (previously
	4-5, or entirely unable to resolve). The full 35-combination
	stress test: six differences from the confirmed baseline,
	every one an improvement, zero regressions in either
	direction. render.py's own full pipeline and both circuits'
	own real, final renders visually inspected and confirmed
	clean. make check confirmed clean.
	* FutureResearch.org: The sector-count-bias item finally
	closed, with the full, precise account of all eight attempts
	across four sessions.

2026-08-24  Péter Felecan  <pfelecan@gmail.com>

	Solving the ping-pong problem together with the room-
	visibility gap, as directly instructed. A real, precise
	wire-margin mechanism designed, built, and verified correct on
	its own terms -- but a THIRD, previously-unknown factor
	(settle()'s own separate, genuine adjacency-based row push)
	defeated it too, revealing this is a genuine three-way
	interaction, not a two-way one. Reverted a third time, with
	the most precise account yet. No functional code change from
	0.25.1; this entry documents the investigation itself.

	* auto_layout.py: No net change (confirmed byte-for-byte
	against the shipped 0.25.1 implementation after reverting). A
	real, precise wire-margin mechanism (_wire_margin(), counting
	every distinct non-rail net a component genuinely participates
	in) was designed and confirmed correct for its own stated
	purpose. Its first implementation (reserving margin directly
	in col_depth) was found wrong by direct testing: settle()
	treats col_depth as a lower bound for component placement, so
	the reservation was misinterpreted as real occupancy, pushing
	a component away from its own correctly-reserved room. A
	redesign (a separate col_wire_margin dict, folded into
	col_depth only in a final pass right before wire assignment)
	correctly solved that specific conflict -- but testing
	revealed a third, previously-unexamined factor: settle() has
	its own separate, genuine reason to push the same component
	deep into its column (a real, visual overlap with an adjacent,
	taller neighbor), which the room search has no visibility into
	either.
	* Reverted a third time rather than ship a regression.
	Confirmed the revert restores the exact known-good state: full
	35-combination stress test, zero difference from the 0.25.1
	baseline; example_circuit1 and rp2040-basic-m1 both
	re-confirmed resolving exactly as before this session's own
	work. make check confirmed unaffected.
	* FutureResearch.org, ResearchJournal.org: Full, precise
	account of this sixth attempt across three sessions -- what
	now works correctly on its own (the ping-pong fix, the
	wire-margin computation), and the real, remaining three-way
	interaction any future attempt needs to solve together, with
	two concrete directions recorded.

2026-08-22  Péter Felecan  <pfelecan@gmail.com>

	Designed and implemented a real solution to the sector-count
	bias, as directly requested. A genuinely effective mechanism
	found (relief_history, structurally preventing the per-ref
	ping-pong two earlier sessions couldn't catch), but its own
	wider search radius surfaced a real, DIFFERENT regression in
	example_circuit1's own direct-wire mechanism that two further,
	careful attempts couldn't safely resolve same-day. Reverted a
	second time, with a much more precise account for whoever
	continues this. No functional code change from 0.25.0; this
	entry documents the investigation itself.

	* auto_layout.py: No net change (confirmed byte-for-byte
	against the shipped 0.25.0 implementation after reverting). A
	real, working solution to the narrow ping-pong problem was
	designed, built, and verified (relief_history: a real, per-ref
	memory forbidding a component from ever being relocated back
	to a column it has already been at, combined with widening the
	room search to cover the whole current sector) -- confirmed
	genuinely effective for its own stated purpose:
	rp2040-basic-m1 resolved at sectors=1 (previously 4-5), and
	the real ping-pong itself became structurally impossible, not
	merely unlikely.
	* But the FULL 35-combination stress test -- not the narrower,
	targeted test that motivated the fix -- caught a real,
	DIFFERENT regression: example_circuit1's own
	compute_optimal_config() started failing at every sector tier,
	with a new kind of error (a direct wire landing out of range,
	not a row-overflow). Traced directly to a real, structural gap:
	this project's own direct-wire mechanism (extra_links) runs
	AFTER all placement decisions and has no visibility into a
	wire's own future room needs -- the wider, more effective
	packing left one real component with only one spare row in its
	own column, but it needs two separate direct wires from that
	SAME column.
	* Two further, careful attempts at this specific regression,
	both insufficient: a real, one-row margin added to
	relieve_overflow()'s own room search (confirmed, by direct
	tracing, this didn't even touch the actual mechanism at fault
	-- a SEPARATE function, run_declutter()'s own push-aside
	search, turned out to be responsible); the same margin applied
	there too -- still didn't resolve it, traced only as far as a
	real, separate constant (DEEP_COLUMN_THRESHOLD) before
	responsible time constraints stopped further tracing through
	that function's own additional recursion, not yet examined.
	* Reverted a second time rather than ship either the original
	fix or its own patches. Confirmed the revert restores the
	exact known-good state: full 35-combination stress test, zero
	difference from the 0.25.0 baseline; example_circuit1 and
	rp2040-basic-m1 both re-confirmed resolving exactly as before
	this session's own work. make check confirmed unaffected.
	* FutureResearch.org, ResearchJournal.org: Full, precise
	account of all four real attempts across two real sessions,
	what each one got right and wrong, and the two real,
	interacting problems (the ping-pong, now solved in principle,
	and the extra_links room-visibility gap, not yet solved) any
	future attempt needs to address together.

2026-08-22  Péter Felecan  <pfelecan@gmail.com>

	Two real observations from a review of a rendered rp2040-basic-m1: an
	"unpowered sectors" appearance
	(traced to a test-config artifact plus one genuine, now-fixed,
	narrow gap in to_rail()'s own final fallback tier), and a
	real, confirmed sector-count bias (still open -- two attempted
	fixes both caught and reverted after real regressions).

	* breadboard_lib.py: to_rail()'s own final fallback tier (used
	when col is known but no rail is configured at all) is now
	sector-aware, reusing rail_y() as a virtual position -- the
	same class of bug, and the same fix, already applied to a
	different tier of this function on 2026-08-20, now found to
	have had an overlooked sibling. Confirmed byte-identical for
	the true default case.
	* auto_layout.py: Two real, careful attempts at fixing the
	sector-count bias in relieve_overflow()'s own sector-preference
	logic, both reverted after direct testing against the real
	circuit that exposed the original issue caught real
	regressions -- a threshold gate left three real components
	permanently stuck overflowing; widening the search radius
	instead re-introduced a real ping-pong that the existing
	exact-state cycle detector didn't catch. Restored the original,
	confirmed-working (if imperfect) logic. Both real, precise
	failure modes recorded for whoever picks this up next, rather
	than left as a vague "still needs work."
	* Verified: full 35-combination stress test, run after the
	revert and again after the fallback-tier fix -- zero
	difference from the known-good baseline both times. make check
	confirmed unaffected.
	* FutureResearch.org: The fallback-tier gap closed. A new,
	precisely-scoped item opened for the sector-count bias, with
	both real attempts' own exact failure modes recorded.

2026-08-22  Péter Felecan  <pfelecan@gmail.com>

	rp2040-basic-m1's own real render closed: a real, deeper bug
	found while re-verifying last session's own precisely-scoped
	hole-collision item -- run_declutter()'s own push-aside search
	could wrap a component backward across a sector boundary onto
	an extreme, out-of-bounds column. Fixed, verified rigorously,
	and the original registration gap closed too.

	* auto_layout.py: run_declutter()'s own "push aside" candidate
	search gains a real, targeted check -- a candidate column must
	stay within the same real sector as the one being relieved,
	and within that sector's own real, configured width. Without
	this, raw arithmetic on the global column number (col+1,
	col-1, col+2, col-2) could wrap backward across a sector
	boundary onto the previous sector's own extreme final column.
	Confirmed directly: one real component (C2) had been pushed to
	local column 1000 on a board configured for 65 -- traced by
	instrumenting col_x() to catch the exact call site.
	* breadboard_lib.py: to_rail() and jumper() (confirmed, by
	direct inspection, to have the same real gap) now both call
	_register() for their own real landing spot, closing the
	original hole-collision finding directly -- byte-identical SVG
	output for the default case, confirmed directly, since this
	only affects collision-avoidance bookkeeping.
	* Verified: rp2040-basic-m1's own real render dropped from a
	real, visibly wrong 16211px canvas to a sensible 1603px, still
	0 hole collisions, still fully resolved, now reaching one
	fewer sector on a realistic base config (a genuinely more
	efficient placement, no longer wasting an invalid "solution").
	The full 35-combination stress test, run before and after both
	fixes: zero difference in any already-verified real placement
	outcome. A real, momentary false alarm (an apparent regression
	after the _register() addition) traced directly to a test
	script calling render() twice within one process, not a real
	code issue -- re-tested in isolation and confirmed clean.  make
	check confirmed unaffected throughout.
	* FutureResearch.org: Item closed with the full, precise
	account of both real fixes.

2026-08-22  Péter Felecan  <pfelecan@gmail.com>

	A foundational structural correction: a multi-sector board's
	own real rails are SHARED between adjacent sectors, not
	duplicated. The entire geometry model revised, verified
	extensively, and shipped without disturbing a single already-
	verified placement outcome.

	* breadboard_config.py: SectorConfig's own former own_rails/
	own_rails_continuous/own_rail_groups/own_rail_group_size
	REMOVED -- a sector never genuinely had its own rail.
	BoardConfig's own former rail_bottom REPLACED by a single,
	unified `rail` field, the shared template for every one of
	the assembly's own N+1 real horizontal rails. `rail_right`
	ADDED alongside `rail_left` (schema support only; connection
	tier deliberately deferred). sector_y_offset() rewritten so
	adjacent sectors' own real origins land exactly far enough
	apart that their own shared rail coincides at one real
	position -- verified both algebraically and numerically. New
	rail_y(config, rail_index, plus), confirmed to reproduce the
	old, single-sector module-level rail constants exactly for the
	default config. load_config()/write_config() updated to the
	new [rail]/[rail.right] sections, round-trip tested.
	* breadboard_lib.py: to_rail() redesigned to a real, three-tier
	priority (the nearest of the assembly's own N+1 rails; the
	left rail, a genuine last resort; the single-sector fallback).
	draw_board_background() restructured: per-sector dot grid/
	trench/row-letters, plus one separate loop drawing exactly
	sectors+1 real rails.
	* auto_layout.py, render.py: Three real, leftover call sites
	still referencing the removed rail_bottom field or the old
	per-sector rail_y_for_sector() convention found (by direct
	grep after the main rewrite) and fixed -- compute_optimal_config()'s
	own candidate construction, and render.py's own cross-half
	rail-bridge and extra_notes paths.
	* breadboard1.conf, breadboard2.conf: Updated to the new
	[rail] section format; header comments corrected to describe
	the real, shared-rail structure.
	* Verified: the full 35-combination stress test (every real
	circuit x 5 real board shapes) run before and after, diffed
	line by line -- zero difference. rp2040-basic-m1 re-confirmed
	resolving at sectors=5. A real 3-sector board rendered and
	visually inspected -- exactly 4 rail lines for 3 sectors,
	confirmed both by counting SVG elements and by a direct visual
	crop showing a single, clean rail pair. make check unaffected
	throughout.
	* FutureResearch.org: Item closed. Two new items recorded: a real,
	higher-level "multiple
	separate boards" concept, and the config-file-format
	documentation, explicitly deferred until the format
	stabilizes.

2026-08-21  Péter Felecan  <pfelecan@gmail.com>

	rp2040-basic-m1's own sector-preference question closed: a
	real, substantial win, resolving TWO previously-documented
	"genuinely unfixable by any sector count" cases. A new,
	narrow, separate rendering gap found and precisely scoped.

	* auto_layout.py: A small, real safety gap in the sixth
	attempt's own cycle-detection loop closed first (the
	iteration-cap exit path never restored the best state seen,
	unlike the cycle-detected exit) -- fixed with a real
	`for...else` clause, re-verified with zero difference against
	the sixth attempt's own saved stress-test results.
	relieve_overflow() now checks, before relocating an
	overflowing component near its own current position, whether
	a genuinely less-crowded real sector exists and tries landing
	there directly first -- protected by the now-complete safety
	net (the loop can only ever end at the best state it actually
	reaches).
	* Verified: the default (single-sector) baseline completely
	unaffected. Full 35-combination stress test, with and without
	this change, diffed line by line -- exactly one difference
	across all 35, a real, unambiguous improvement. Confirmed
	directly: example_circuit1's own real "Z" net (documented
	since 2026-08-20 as unfixable by any sector count) now
	resolves at sectors=2, rendered and visually confirmed.
	rp2040-basic-m1 itself resolves for the first time ever, at
	sectors=5. render.py's own full CLI pipeline confirms both
	circuits now render cleanly with no explicit config needed.
	make check unaffected.
	* A new, honest, narrow rendering gap found and precisely
	scoped, not fixed here: rp2040-basic-m1's own real render at
	sectors=5 shows three pairs of far-leg positions landing at
	the exact same pixel -- to_rail() never registers its own
	landing spot when a passive's far pin reaches a rail directly,
	so two nearby components reaching for the same rail can
	independently converge on an identical position. Confirmed a
	real, pre-existing gap (this circuit never reached a fully-
	resolved state before to exercise this path) and confirmed
	narrow (every other real circuit renders completely cleanly).
	* FutureResearch.org: The sector-preference item closed. A new
	item added for the far-leg registration gap, with a concrete
	direction (to_rail() should call _register() like every other
	drawn shape).

2026-08-21  Péter Felecan  <pfelecan@gmail.com>

	rp2040-basic-m1's own row-depth ceiling: a sixth, materially
	different attempt at the shared relocate-on-overflow ping-pong
	-- exact-state cycle detection, not a total-overflow heuristic
	-- verified safe by the most rigorous testing this item has
	had across six attempts, and finally shipped.

	* auto_layout.py: The relieve_overflow()/run_declutter() caller
	loop now detects a genuine cycle via an exact, hashable
	fingerprint of the full relevant placement state (every
	passive's own column and row, plus col_depth), stopping and
	restoring the best real state seen only once an identical full
	state is confirmed to repeat -- structurally cannot mistake a
	genuine plateau for a ping-pong the way the fifth attempt did,
	since a plateau (unlike a true cycle) generally reaches a
	different concrete state each round.
	* Verified: confirmed the real ping-pong is genuinely detected
	and broken (relocation counts: 3, not ~30). A real, direct,
	35-combination stress test (every real circuit x 5 real board
	shapes) run both with and without this fix and diffed line by
	line -- zero difference. The exact scenario known to ping-pong
	(rp2040-basic-m1 at sectors=2) re-verified with and without the
	fix side by side: identical final overflow state either way,
	confirming this fix changes nothing about the real outcome,
	only how efficiently and provably it's reached. Full regression
	sweep, every real circuit, byte-identical to the documented
	baseline. make check unaffected.
	* FutureResearch.org: Item updated -- the ping-pong itself
	closed and shipped; the separate, deeper sector-preference
	question (rp2040-basic-m1's own remaining capacity ceiling)
	remains open, now on genuinely solid, ping-pong-free ground.

2026-08-21  Péter Felecan  <pfelecan@gmail.com>

	Rail nets checked for a real, genuine power source: a
	genuinely additive, non-blocking advisory. A fifth real
	attempt at rp2040-basic-m1's own row-depth ceiling (fixing the
	shared relocate-on-overflow ping-pong directly), also reverted
	after the full regression sweep caught a second real
	regression.

	* auto_layout.py: New rails_without_power_source(nl, rails),
	reusing netlist.py's own already-tested pin_types -- flags a
	rail net with neither a real power_out pin anywhere nor a
	power_in pin (the latter already earns a real PWR_FLAG via
	this project's own existing mechanism). Verified against every
	real circuit: zero false positives on properly-sourced rails;
	genuinely flags the already-known charlieplex/rail-
	classification cases from a different angle; a real, honest
	limitation found and documented (a generic connector symbol
	standing in for a real power source, common in hobbyist
	circuits, looks the same to this check).
	* render.py: Wired in as a real, non-blocking advisory
	alongside the existing connectivity/bounds checks, respecting
	--quiet, never blocking rendering.
	* No net change in auto_layout.py's own relieve_overflow()
	caller loop: a fifth real attempt (a quantitative progress
	measure replacing the bare "did anything move" boolean,
	tracking and restoring the best state seen) genuinely
	eliminated the real ping-pong itself (confirmed: relocation
	counts dropped from ~30 to 3) but caused a second real,
	confirmed regression (example_circuit2 and solar-roller, both
	previously clean, broke) -- traced to a "stop at first non-
	improvement" rule that can't distinguish a genuine ping-pong
	from a real plateau. Reverted immediately, confirmed byte-for-
	byte restored.
	* FutureResearch.org: The power-source item closed, moved to
	Closed Items. The row-depth item updated with the fifth
	attempt's own full, precise account.
	* Verified: full regression sweep, every real circuit,
	byte-identical to the documented baseline throughout
	(including knight-roller's own continued resolution via
	compute_optimal_config()). make check unaffected.

2026-08-21  Péter Felecan  <pfelecan@gmail.com>

	rp2040-basic-m1's own row-depth ceiling: two further, real
	attempts, each yielding genuine new understanding, neither a
	full fix. The second caught as a real regression against
	knight-roller during the full regression sweep and reverted
	before it ever shipped -- no functional code change from
	0.19.0; this entry documents the investigation itself.

	* auto_layout.py: No net change (confirmed byte-for-byte
	against the shipped 0.19.0 implementation after reverting).
	Two real attempts tried and precisely documented instead: a
	targeted cursor jump near a sector's own edge (real, harmless,
	but didn't move rp2040-basic-m1's own real result, since
	_find_room()'s own bidirectional search could still reuse an
	earlier column via its own backward search regardless of where
	the cursor had advanced to); a dedicated, forward-only,
	always-fresh search enforcing this fallback's own original,
	never-actually-enforced "never shared" intent (a real
	improvement on rp2040-basic-m1 alone, but a real, confirmed
	regression on knight-roller, caught by the full regression
	sweep and reverted immediately).
	* A real, separate, deeper bug found while diagnosing that
	regression, not yet fixed: the shared relocate-on-overflow
	code (used by every real attach path, not specific to rail-
	attachment) can ping-pong between two overflowing columns
	roughly 30 times before accepting a still-overflowing result --
	plausibly the real, common cause behind both this item's own
	remaining gap and the regression the fourth attempt exposed.
	* FutureResearch.org: Item updated with the full, precise
	account of all four real attempts (two shipped in 0.19.0, two
	from this entry), the regression catch, and two concrete
	directions for whoever picks this up next -- fix the shared
	ping-pong bug first, then reconsider a forward-only or
	headroom-preferring search, re-tested against both
	rp2040-basic-m1 and knight-roller together.
	* Verified: full regression sweep, every real circuit, re-run
	after the revert, confirmed byte-for-byte matching the
	documented 0.19.0 baseline, including knight-roller's own
	resolution via compute_optimal_config() at sectors=2. make
	check unaffected.

2026-08-21  Péter Felecan  <pfelecan@gmail.com>

	Three remaining FutureResearch.org items closed: rp2040-
	basic-m1's own row-depth competition (real, measurable
	improvement; deeper root cause traced precisely), real CLI
	plumbing for the breadboard-configuration resolution strategy,
	and the rail-connection priority finalized. Two new items recorded for
	future research.

	* auto_layout.py: layout()'s own rail-attachment fallback gains
	a real, persistent, advancing starting column
	(rail_attach_cursor) and uses the real, estimated component
	size (not a hardcoded placeholder) in its own initial room
	search -- fixes a real bug where same-net components were
	funneled into the same column before their real size was ever
	checked. A real, measurable improvement for rp2040-basic-m1,
	though not a full resolution -- the actual deeper cause (
	_find_room()'s own sector-spill only tries another sector when
	nothing fits at all, never preferring a less-crowded one) traced
	precisely and recorded, not attempted further.
	* render.py: New --config/--use-computed/--require-config CLI
	flags, wiring resolve_config_path()/compute_optimal_config()/
	load_config()/write_config() -- every one already independently
	tested -- into main()'s own real resolution logic, exactly per
	the documented pseudocode. A real crash bug found and fixed:
	an unhandled RuntimeError from compute_optimal_config() now
	reports cleanly instead of an ugly traceback. Every real
	scenario tested directly (default, auto-escalation, both
	explicit and default-lookup config paths, --use-computed,
	--require-config, the propose-an-alternative behavior, --quiet,
	a malformed file, a nonexistent path).
	* breadboard_lib.py: to_rail()'s own rail-connection priority
	corrected after review -- an initial, real,
	working "nearest rail" implementation (comparing real distances
	between the assembly's bottom and left rails) was confirmed
	backwards: the left/vertical rail's own real role is a LAST
	RESORT for a direct component connection (no room on the
	horizontal rails, or a genuine secondary power source), never a
	proximity comparison. Corrected to horizontal-first, always,
	left only when no horizontal rail is configured at all.
	Re-verified the exact scenario that first exposed the (wrong)
	distance-based behavior, now correctly routing to the bottom
	rail.
	* FutureResearch.org: All three items closed, each with its own
	real, precise account (including the "nearest rail" item's own
	honest sequence: implemented, verified, corrected). Two new
	items recorded: rail nets aren't
	checked for a real, genuine power source; a real, new feature
	idea (converting a rendered breadboard into a textual, step-by-
	step build instruction list).
	* Verified: full regression sweep, every real circuit, re-run
	after every change in this entry, byte-identical to the
	documented baseline throughout. make check unaffected.

2026-08-21  Péter Felecan  <pfelecan@gmail.com>

	The charlieplex/rail-classification gap closed: passives can
	now attach directly to a rail, the same real option anchors
	already had. Finalized "Real breadboard configuration"
	(rp2040-basic-m1's own capacity item closed via
	compute_optimal_config(), two stale sub-items corrected). A
	real, second capacity discovery in rp2040-basic-m1, and two
	genuinely new bugs found and fixed by actually looking at
	rendered output.

	* auto_layout.py: layout() gains a real, third fallback
	(tried after the primary search and the far-leg fallback both
	fail): a passive touching a rail net directly can now attach
	to it, reusing rail_nets_touched() (confirmed already ref-
	generic) and the existing attach-commit code unchanged. New
	Placement.passive_rail_net dict.
	* render.py: New rendering support for rail-attached passives
	-- draws a real to_rail() connection from the passive's own
	near-pin position, mirroring the existing anchor pattern.
	* breadboard_lib.py: Fixed a real, confirmed bug in to_rail()'s
	own final fallback tier -- was not sector-aware, always
	computing sector 0's own real position regardless of which
	sector a component actually occupies. Found by looking at an
	actual rendered image, not assumed correct from placement
	numbers alone. Fixed by reusing rail_y_for_sector() even when
	own_rails is False.
	* FutureResearch.org: The charlieplex item closed, moved to
	Closed Items. The rp2040-basic-m1 row-capacity item closed
	(resolved by compute_optimal_config(), not separately fixed).
	Two stale sub-items under "Real breadboard configuration"
	corrected (compute_optimal_config() no longer described as
	unbuilt). A new, honestly-reported item opened: rp2040-basic-m1
	now has a real, second capacity ceiling (row-depth competition,
	confirmed not a column-width issue) exposed by the charlieplex
	fix's own success, not a bug in it.
	* Verified: full regression sweep, every real circuit, both
	the default layout() path and compute_optimal_config() where
	relevant -- confirmed against the real, originally-diagnosed
	case (knight-roller's own D2/D4/D6/D7, now placed correctly,
	connectivity_ok=True) and every other circuit's own real,
	expected behavior. make check unaffected. Re-verified from a
	full maintainer-clean rebuild after an interrupted session.

2026-08-20  Péter Felecan  <pfelecan@gmail.com>

	Correction: the left rail's real, primary
	role is feeding the horizontal rails, not individual
	components -- a real, structural connection added. A real,
	confirmed bug (every anchor sharing one rail hole) found and
	fixed in the same review.

	* breadboard_lib.py: New _left_rail_depth module-level tracker
	(the same "next free position" role col_depth already plays)
	-- fixes a real, confirmed bug: to_rail()'s own fourth tier
	used the same Y for every real connection, since every anchor
	shares one outer row, so multiple components wiring to the
	left rail landed at the exact same hole. draw_board_background()
	gains a real, structural connection: for every sector with
	own_rails=True, four short, direct connections from the
	assembly's own left rail to that sector's own local rail
	(top+/top-/bottom+/bottom-) -- the left rail's own real,
	primary "common power distribution" role, previously entirely
	unaddressed.
	* Verified: full regression sweep, all 7 real circuits,
	byte-identical to the documented baseline. The bug fix
	verified by re-rendering the exact circuit that exposed it,
	confirming genuinely distinct landing points. The structural
	connection verified visually (a real render, cropped and
	inspected). make check unaffected.
	* FutureResearch.org: Item updated with the real correction
	and the real bug fix, both stated directly.

2026-08-20  Péter Felecan  <pfelecan@gmail.com>

	The assembly's own left rail gets a real electrical
	connection: a genuinely different kind of tier, reusing
	jumper()'s own real curve-and-collision-avoidance machinery
	rather than forcing it into to_rail()'s own straight-line
	model.

	* breadboard_lib.py: to_rail() gains a real fourth tier -- the
	assembly's own left rail, tried when neither a sector-local
	rail nor the assembly's bottom rail applies but
	rail_left.present is True. Drawn via jumper() (bulge matching
	the component's own half, avoid=True), not a straight line --
	a connection reaching the board's own side edge is far more
	likely to cross something than a short, local, vertical one.
	* Verified: full regression sweep, all 7 real circuits,
	byte-identical to the documented baseline; the new tier
	verified both numerically and visually (a real render,
	converted to PNG, showing two real, long, genuinely obstacle-
	avoiding curves correctly reaching the left rail). make check
	unaffected.
	* FutureResearch.org: Item updated -- the rail-connection
	priority is now real and tested for all three real rail types;
	only a fully general "nearest real rail" strategy (comparing
	real distances across every option, rather than a fixed
	priority order) remains genuinely open.

2026-08-20  Péter Felecan  <pfelecan@gmail.com>

	compute_optimal_config(): the last real piece of the resolution
	strategy, built and verified against real
	circuits, with two genuine, confirmed findings about its own
	real limits.

	* auto_layout.py: New compute_optimal_config(nl,
	base_config=None, max_sectors=8, ...) -- modeled directly on
	kicad_layout.py's own generate_schematic_auto(), escalating
	SECTOR COUNT (the physically correct parameter: a real board's
	own rows_per_half is fixed, you add another real board, you
	don't get more rows on the one you have). Verified against
	rp2040-basic-m1 (resolves at sectors=2, confirmed by rendering
	the real result) and example_circuit1 (correctly exhausts at
	every sector count 1-8, since its own real bottleneck --
	the "Z" net's row overflow -- is a single-sector row-capacity
	issue no number of additional sectors can fix). Docstring and
	verbose output both state plainly that "succeeded" means
	bounds_ok/connectivity_ok hold for whatever was placed, not
	that every component found a position.
	* FutureResearch.org: compute_optimal_config() marked DONE;
	the parent "Real breadboard configuration" item's own heading
	updated -- format, full geometry integration, and this
	function are all done; only the left rail's own connection and
	a general "nearest rail" strategy remain.
	* Verified: full regression sweep against layout() itself (not
	just the new function), all real circuits, byte-identical to
	the documented baseline. make check unaffected.

2026-08-20  Péter Felecan  <pfelecan@gmail.com>

	to_rail()'s own real "which rail" question gets its first
	real answer: a genuine, tiered fallback to the assembly's own
	bottom rail. A real, distinct gap found and fixed in
	row_letter() along the way.

	* breadboard_lib.py: to_rail() gains a real, three-tier
	priority -- a sector's own local rail (unchanged); the
	assembly's own bottom rail, when the sector has none of its
	own but rail_bottom.present is True (new); the single,
	module-level default, unchanged, otherwise. row_letter()
	gains an optional rows_per_half parameter (was hardcoded to
	the module's own ROWS_PER_HALF, crashing for a genuinely
	different sector depth) -- every existing caller unaffected;
	draw_board_background()'s own two real call sites now pass
	the sector's own real value.
	* Verified: full regression sweep, all 7 real circuits,
	byte-identical to the documented baseline; the new bottom-
	rail-fallback path verified both numerically (bounds_ok/
	connectivity_ok both True) and visually (a real render,
	converted to PNG and inspected, showing real, correct
	connections to the assembly's own bottom rail). make check
	unaffected.
	* FutureResearch.org: Item updated -- the bottom-rail
	connection is real and tested; the left rail's own
	connection, and a fully general "nearest real rail" strategy,
	remain genuinely open, stated plainly.

2026-08-20  Péter Felecan  <pfelecan@gmail.com>

	Assembly-level left/bottom rails: real, drawn, and verified --
	implemented without touching a single existing coordinate.

	* breadboard_config.py: New LEFT_RAIL_MARGIN_PX (a real,
	fixed 45px extra canvas width, reserved only when
	rail_left.present), left_rail_x(), bottom_rail_y_for_assembly()
	(reusing total_board_height_px()'s own already-reserved
	margin directly).
	* breadboard_lib.py: draw_board_background() gains a real
	min_x parameter (0 for every existing caller) and draws the
	assembly's own left rail (two vertical lines, real hole
	grouping, full assembly height) and bottom rail (two
	horizontal lines) when present.
	* render.py: Computes min_x from board_config.rail_left.present
	and passes it through; the SVG's own viewBox gains a real,
	negative min-x only when a left rail is present -- confirmed
	exactly 0, and every drawn position byte-identical to before,
	for every existing config.
	* Verified: full regression sweep, all 7 real circuits,
	byte-identical to the documented baseline; a real render with
	both assembly-level rails present, converted to PNG and
	visually inspected -- a real, visible double vertical rail
	spanning the full assembly, a real double horizontal bottom
	rail, both distinct from each sector's own local rail lines.
	make check unaffected.
	* FutureResearch.org: Item marked DONE for the real visual
	chrome; the electrical connection (to_rail() reaching an
	assembly-level rail) remains a real, separate, still-open
	item, stated plainly, not implied closed.

2026-08-20  Péter Felecan  <pfelecan@gmail.com>

	Geometry integration, phase 2 fully closed: the four
	remaining shape functions, the cross-half rail bridge, and
	the extra_notes path all made sector-aware and genuinely
	tested. The assembly-level left/bottom rails scoped as a
	real, distinct next feature.

	* breadboard_lib.py: trimmer_straddle()/usb_c()/mosfet()/
	bus() gain an optional board_config parameter, sector-aware,
	defaulting to today's existing single-sector geometry
	byte-identically -- confirmed to have no real caller anywhere
	in render.py today (genuine dead code), verified in isolation
	against col_x()/row_y()'s own ground-truth pixel values
	directly.
	* render.py: The cross-half rail-bridging path (a rail net
	needing a vertical bridge between an anchor on the north half
	and one on the south) and the extra_notes path both made
	sector-aware and genuinely exercised for the first time (a
	real call for extra_notes; a deliberate, forced mutation for
	the cross-half bridge), not merely edited and trusted.
	* Verified: full render.py regression sweep, all 7 real
	circuits, re-run after every change, byte-identical to the
	documented baseline throughout; make check unaffected.
	* FutureResearch.org, LibraryReference.org: Geometry
	integration phase 2 marked CLOSED. A new, distinct item added
	for the assembly-level left/bottom rails' own drawing (present
	on both real boards, not yet drawn at all) --
	scoped precisely (needs real canvas-coordinate-system changes,
	not the same mechanical pattern that closed everything else),
	deliberately not started in the same sitting.

2026-08-20  Péter Felecan  <pfelecan@gmail.com>

	Geometry integration, phase 2: render.py's own drawing side
	made real, sector-aware, and verified with an actual, visually
	inspected multi-sector render. Two more real formula errors
	found and fixed along the way, the same discipline that caught
	phase 1's own bugs.

	* render.py: New _x()/_y() sector-aware wrappers; every direct
	col_x()/row_y() call site (passives, member_position(),
	extra_links, board sizing) updated to use them. Board sizing
	(H) now uses breadboard_config.total_board_height_px() instead
	of the single-sector RAIL_MINUS_BOT constant directly.
	* breadboard_lib.py: ic()/to_rail()/draw_board_background()
	gain an optional board_config parameter, sector-aware,
	defaulting to today's existing single-sector geometry
	byte-identically (verified against the real generated SVG's
	own rail/trench coordinates, not just in isolation).
	to_rail() connects to a sector's own local rail when one
	exists (a real design decision for FutureResearch.org's own
	"which rail" question), the single global rail otherwise.
	TRENCH_Y's own bare "70" literal named (TRENCH_Y_MARGIN,
	phase 1) reused directly rather than re-derived a third time.
	* breadboard_config.py: row_y_multi() gains a real bounds
	assertion (was silently accepting an out-of-range row -- a
	real gap in the same defense-in-depth breadboard_lib.py's own
	row_y() already has). New trench_y_for_sector()/
	rail_y_for_sector()/total_board_height_px(). sector_height_px()
	corrected twice, in sequence: a first, naive symmetric formula
	was missing TRENCH_Y_MARGIN entirely (a real, traceable 41px
	discrepancy against the known-correct RAIL_MINUS_BOT); the fix
	then wrongly made the rail margin conditional on own_rails,
	when today's real rendering reserves it unconditionally --
	both found by checking a known value, not by trusting a
	re-derivation.
	* Verified: full render.py regression sweep, all 7 real
	circuits, byte-identical to the documented baseline; a real,
	visually-inspected multi-sector render (including genuine
	anchor spillover across two sectors, with real cross-sector
	jumper wires); make check unaffected.
	* FutureResearch.org, LibraryReference.org: Phase 2 marked
	done for the real, exercised core (ic()/to_rail()/
	draw_board_background(), and render.py's own ~15 call sites);
	the real, bounded remaining gap (trimmer_straddle()/usb_c()/
	mosfet()/bus(), the cross-half rail bridge, the assembly-level
	left/bottom rails' own drawing) stated plainly, not implied
	complete.

2026-08-20  Péter Felecan  <pfelecan@gmail.com>

	Geometry integration, phase 1: auto_layout.py's own placement
	engine made real, tested, sector-aware. A critical, self-
	introduced connectivity bug found and reverted along the way.
	Phase 2 (render.py/breadboard_lib.py's own drawing side)
	scoped precisely; deliberately not implemented in the same
	sitting, given the real regression risk of touching the core
	drawing library on the heels of a bug already found from
	insufficient testing under time pressure.

	* breadboard_config.py: SECTOR_COL_STRIDE, sector_of()/
	local_col_of()/global_col() (a global-column encoding letting
	every existing placement function keep working unchanged),
	sector_height_px()/sector_y_offset()/row_y_multi() (real
	multi-sector geometry, verified byte-identical to
	breadboard_lib.py's own row_y() for sector 0 on
	default_config()). default_config()'s own sector.columns
	fixed from a "realistic" 63 to SECTOR_COL_STRIDE-1, after
	confirming directly that TestSchemes' own rp2040-basic-m1
	already legitimately uses columns up to 89 under the previous,
	genuinely unbounded default -- a real regression caught before
	shipping.
	* breadboard_lib.py: TRENCH_Y's own bare "70" literal named
	(TRENCH_Y_MARGIN) so row_y_multi() could reuse the exact real
	constant rather than a second, re-derived one.
	* auto_layout.py: _find_room() gains a real board_config
	parameter -- a genuine per-sector column bound (none existed
	before) and real sector-spilling into another sector when the
	starting one has no room. layout() threads a real, config-
	derived rows_per_half through every site that used to reach
	for the bare ROWS_PER_HALF module constant. Anchor placement
	restructured to be sector-aware, confirmed necessary (not just
	thorough) by checking rp2040-basic-m1's own anchors alone
	already exceed a real 65-column sector. Placement gains
	board_config; verify_within_board_bounds(p) reads it from
	p.board_config rather than a separately-passed, driftable
	copy.
	* auto_layout.py: Reverted a same-day (2026-08-19, earlier)
	wire_row() "room check" that relocated a bridging wire to an
	unrelated column when the original ran out of room -- found,
	by testing a genuinely different rows_per_half for the first
	time, to silently disconnect real net members (PWRA's own
	members bridged to a column nothing real occupied). Confirmed
	not sector-related (reproduced with sectors=1) before
	reverting. Restores the correct trade-off: an honest bounds-
	check failure over a silent, incorrect connectivity pass.
	* FutureResearch.org: Breadboard-configuration item split into
	phase 1 (closed, real, tested) and phase 2 (scoped precisely,
	not implemented) plus compute_optimal_config() (not built);
	each of phase 2's own seven real functions and ~15 call sites
	named directly, not left as a general "needs geometry
	integration."

2026-08-19  Péter Felecan  <pfelecan@gmail.com>

	A physically-grounded rail-grouping formula
	supersedes the earlier curve fit; the bottom rail's own
	grouping confirmed; kicad_layout.py's own section written in
	LibraryReference.org.

	* breadboard_config.py: estimate_rail_groups() replaced --
	the earlier "columns / 6.5" curve fit superseded by a real,
	physically-grounded derivation (start/end margins,
	inter-group gaps): floor((columns - 4) / 5 - 2). Verified
	against the one known data point and checked to diverge
	meaningfully from the superseded version elsewhere, confirming
	it's a real improvement, not a cosmetic rewrite.
	* breadboard1.conf, breadboard2.conf: [rail.bottom] updated
	from "not specified" to groups=10/group_size=4, matching a sector's
	own local
	rail.
	* FutureResearch.org: Rail-grouping item updated with the full
	account -- the superseded formula's own real history kept, not
	erased; the new formula's own real limits stated plainly.
	* LibraryReference.org: kicad_layout.py's own section written
	in full (UnplaceableError, resolve_pin_key, label_direction,
	title_block_rect, avoid_label_reach, generate_schematic/
	generate_schematic_auto, including the real PAPER_LADDER/
	NORTH_EXTRA_LADDER retry strategy). A duplicate heading (a
	leftover stub) caught and removed before shipping.

2026-08-19  Péter Felecan  <pfelecan@gmail.com>

	Rail-grouping hypothesis formula, $Id$ headers, footprint.py's
	own section in LibraryReference.org, and Workflow.org adapted
	for breadboard_config.py's own existence.

	* breadboard_config.py: New estimate_rail_groups(columns,
	group_size=4) -- a real, explicitly-hypothesized formula
	(columns / 6.5), fit to the one real data point available,
	never called automatically. SectorConfig gains
	own_rail_groups/own_rail_group_size (a sector's own local
	rail can now record its own grouping, confirmed to match the
	left rail's own). write_config() now writes a real "# $Id$"
	header line.
	* breadboard1.conf, breadboard2.conf: $Id$ header added;
	own_rail_groups/own_rail_group_size set to 10/4.
	* breadboard_config.1: Updated for the new fields.
	* FutureResearch.org: Breadboard-configuration item gains a
	real account of the rail-grouping confirmation and the
	hypothesis formula, with its own real limits stated plainly.
	* LibraryReference.org: footprint.py's own section written in
	full (Pad/Footprint, all three real methods, both loaders);
	section order corrected to match Index.org's own established
	listing.
	* Workflow.org: Stage 3a gains a real, honest paragraph on
	breadboard_config.py's own existence and its own real,
	current scope (the file format and lookup logic; NOT yet
	consumed by layout()/render()).

2026-08-19  Péter Felecan  <pfelecan@gmail.com>

	A real breadboard configuration format designed, implemented,
	and tested (the "Unix style" proposal),
	plus a real design direction for the far-leg/rails item and a
	documentation-policy correction acted on the same day.

	* breadboard_config.py: New. Real Unix-style INI config
	format (stdlib configparser, no new dependency):
	BoardConfig/SectorConfig/RailConfig dataclasses, a real
	parser and writer, round-trip tested. default_config()
	reproduces breadboard_lib.py's own existing single-sector
	geometry byte-for-byte.
	* breadboard_config.1: New. This project's own tenth real
	man(1) page.
	* breadboard1.conf, breadboard2.conf: New. Two real worked
	examples, from two real boards, round-trip
	verified; structural facts (sector count, rail presence/
	continuity) marked confirmed, exact column/row counts marked
	as an honest placeholder pending a real count.
	* FutureResearch.org: Real breadboard configuration item
	expanded with the full design -- the format (implemented),
	the resolution strategy (formalized
	with concrete flag names, not yet implemented), and the two
	real, separate gaps this depends on (geometry integration;
	compute_optimal_config(), proposed to reuse
	generate_schematic_auto()'s own escalating-retry-ladder
	pattern). Charlieplex/rails item gains a remark on
	a component standing in for a rail-jumper wire, mapped onto
	the real code (rail_nets_touched(), already ref-generic).
	* Makefile.am, make-archive, Index.org: breadboard_config.py/
	.1 and both .conf files wired into the real build.

2026-08-19  Péter Felecan  <pfelecan@gmail.com>

	Real measurements applied to breadboard_config.py's own worked
	examples; the config lookup order implemented and tested
	(a refined priority: env var, schematic-adjacent,
	cwd); LibraryReference.org, a new single reference document
	for the shared libraries, with a complete netlist.py section.

	* breadboard_config.py: RailConfig gains real groups/
	group_size fields (a rail's own hole clustering, independent
	of electrical continuity). Class-level columns default
	updated 63 -> 65 (now a real measurement). New
	resolve_config_path(): $PACKPAY_BREADBOARD_CONFIG, then a
	schematic-adjacent breadboard.conf, then ./breadboard.conf --
	verified with 7 real scenarios. New --resolve-for flag to
	inspect the lookup directly.
	* breadboard1.conf, breadboard2.conf: Corrected with real measurements
	(5x65 per sector; left rail 10 groups
	of 4); re-verified round-trip clean.
	* breadboard_config.1: Updated for the new fields and
	--resolve-for.
	* FutureResearch.org: Breadboard-configuration item updated
	throughout -- CONFIRMED where genuinely confirmed, the
	resolution-strategy pseudocode showing resolve_config_path()
	as real/implemented with the rest still honestly open.
	* LibraryReference.org: New. A single reference document for
	netlist.py/auto_layout.py/kicad_layout.py/footprint.py/
	breadboard_lib.py, deliberately kept separate from
	KicadGen.org (a real scope/naming mismatch, not a stylistic
	preference -- explained in the document's own Purpose
	section). netlist.py's own section is complete; the other
	four are honest, explicit stubs, not rushed.
	* Makefile.am, Index.org: LibraryReference.org added to
	ORG_DOCS and the "Reference" section.

2026-08-19  Péter Felecan  <pfelecan@gmail.com>

	Documentation-policy correction, accepted and acted on same
	day: a script taking no options is still real, workflow-
	relevant tooling. Design direction added for the charlieplex/
	rails open item.

	* example_circuit1.py, example_circuit2.py: Minimal
	--version/--help CLI added, functionally unchanged otherwise
	(verified: test_netlist.py's own subprocess dependency on
	example_circuit1.py still works).
	* render_footprints.py: Real -o/--output (was hardcoded) and
	--quiet added, plus --version/--help.
	* example_circuit1.1, example_circuit2.1, render_footprints.1:
	New. This project's own seventh, eighth, and ninth real man(1)
	pages, each with an honest OPTIONS section.
	* Makefile.am, Index.org: All three added to MAN_PAGES/
	EXTRA_DIST and the "Manual pages" section.
	* FutureResearch.org: The "remaining scripts" item's own
	closing reasoning corrected in place (the original "no varying
	option" exclusion was wrong; measure_labels.py/
	measure_value_text.py remain excluded on the correct,
	different grounds -- explicitly not part of the delivered
	tool). The charlieplex/rails item gains a real, concrete
	design direction: a passive attaching directly to a rail, the
	same option anchors already have (rail_nets_touched()) -- mapped onto
	the actual code before being
	recorded, not left abstract.

2026-08-19  Péter Felecan  <pfelecan@gmail.com>

	The far-leg placement fix implemented and verified
	(FutureResearch.org's own first open item, designed
	2026-08-18). Real scope confirmed by actually running it:
	fixes the originally-diagnosed case plus an unplanned bonus
	(example_circuit1.py's own C10/L10, unplaced since this
	project's creation); exposes two further, genuinely separate
	findings, both deferred with real reasoning rather than
	rushed or hidden.

	* auto_layout.py: other_net_of() moved here from render.py
	(a single, shared definition, not a second copy). New
	fallback-only attach search: when the primary search fails, a
	passive can now attach through another already-placed
	passive's own FAR pin, not just its near one -- reserved a
	real, room-checked column via the existing _find_room()
	helper. Tried only as a fallback; every existing circuit
	finds a primary attach every time, so this never runs for
	them.
	* auto_layout.py: wire_row() (extra_links wire placement)
	given the same room check every other row-assignment site
	already has -- a real, previously-undetected gap (already
	named in verify_within_board_bounds()'s own docstring)
	exposed the moment C10/L10 started placing successfully.
	* render.py: Import other_net_of() from auto_layout.py
	instead of defining a second copy.
	* FutureResearch.org, ResearchJournal.org: The far-leg item
	closed with its real, confirmed scope (fixes knight-roller's
	own D3/D5; D2/D4/D6/D7 remain unplaced for a genuinely
	different reason). Two new items opened: a member-count-based
	"rail" misclassification blocking D2/D4/D6/D7 (a real, deeper,
	separate design question), and a genuine row-capacity finding
	on rp2040-basic-m1 (35 components) once more of its own real
	components correctly place -- both honestly reported
	(bounds_ok=False, not a crash, not silent wrongness), neither
	blocking this delivery.

2026-08-19  Péter Felecan  <pfelecan@gmail.com>

	auto_layout.py and visualize.py brought up to the same real
	CLI-plus-man-page bar as the four scripts before them.
	Every remaining script's own real status accounted for and
	the item closed, not left open-ended.

	* auto_layout.py: Real CLI added (argparse-based):
	--kicad-cli, --version, --debug (every anchor's own real
	pin-to-column mapping), --quiet.
	* auto_layout.1: New. This project's own fifth real man(1)
	page.
	* visualize.py: Real CLI added: -o/--output (was hardcoded),
	--kicad-cli, --version, --debug (the same placement-decision
	text auto_layout.py(1) prints), --quiet.
	* visualize.1: New. This project's own sixth real man(1)
	page.
	* Makefile.am, Index.org: auto_layout.1 and visualize.1 added
	to MAN_PAGES/EXTRA_DIST; "Manual pages" section gains both
	entries.
	* FutureResearch.org: The "remaining scripts" item closed
	with a full accounting of every script checked, not just the
	two done today -- which ones have no __main__ at all (real
	new-capability design work) and which are fixed-content demos
	or explicitly internal harnesses (no real varying option to
	document), neither category attempted here.

2026-08-18  Péter Felecan  <pfelecan@gmail.com>

	A real groff warning fixed on footprint.1; Workflow.org's own
	Purpose section rewritten to state what the document is,
	rather than why it was created; author-reference phrasing
	removed from every document outside DevelopmentStandard.org,
	ResearchJournal.org, and FutureResearch.org; test_netlist.py
	brought up to the same real CLI-plus-man-page bar, this
	project's own fourth worked example.

	* footprint.1: Fixed the real "cannot adjust line" warning --
	a long, unbroken example string in a filled block (now .nf/
	.fi, matching render.1's own precedent) and the same string
	repeated inline in DESCRIPTION prose (now removed, pointing
	at EXAMPLES instead). Confirmed all four man pages now
	convert with zero groff warnings.
	* Workflow.org: "Purpose of this document" rewritten to
	describe the document's own actual content and scope, not its
	creation history (which belongs in, and is already recorded
	in, ResearchJournal.org).
	* Workflow.org, README, NEWS: Removed author-reference phrasing of the
	form "the author's own X" (three real instances found and
	fixed); ChangeLog's own entry authorship left untouched
	(standard GNU convention, a different thing).
	* test_netlist.py: Real CLI added (argparse-based):
	--kicad-cli (replacing the previous positional argument, now
	also reading $KICAD_CLI by default), --version, --debug
	(prints every component/net loaded), --quiet. Real error
	handling added for both real failure points (regenerating the
	example circuit, loading it back).
	* test_netlist.1: New. This project's own fourth real man(1)
	page.
	* Makefile.am: test_netlist.1 added to MAN_PAGES/EXTRA_DIST;
	check-local updated to call test_netlist.py with the new
	--kicad-cli flag explicitly.
	* Index.org: "Manual pages" section gains the test_netlist(1)
	entry.

2026-08-18  Péter Felecan  <pfelecan@gmail.com>

	footprint.py brought up to the same real CLI-plus-man-page
	bar as netlist.py and render.py, this project's own third
	worked example. kicad_layout.py (the natural next candidate)
	has no __main__ at all -- not a like-for-like case, deferred
	with reasoning in FutureResearch.org.

	* footprint.py: Real CLI added (argparse-based): --version,
	--debug (resolved file path, courtyard bbox coordinates),
	--quiet (summary/courtyard only). Real error handling for a
	malformed reference or a footprint not found.
	* footprint.1: New. This project's own third real man(1) page.
	* Makefile.am, Index.org: footprint.1 added to MAN_PAGES/
	EXTRA_DIST; Index.org's own "Manual pages" section gains the
	footprint(1) entry.
	* auto_layout.py, visualize.py: Fixed a real, separate bug
	found while surveying candidates -- both scripts' own demo
	__main__ blocks still defaulted to a dudhya-specific path
	("../dudhya-pixel3a-battery-free-power.kicad_sch") left over
	from the 2026-08-14 extraction. The schematic argument is now
	required; no full CLI treatment yet for these two.

2026-08-18  Péter Felecan  <pfelecan@gmail.com>

	KicadGen.html linked from Index.html (a real gap, same
	shape as the earlier "Manual pages" one); new Workflow.org
	written to answer a real, previously-unmet question -- where
	is the actual pipeline documented, from a KiCad schematic to
	a breadboard diagram, and from an imported schematic
	(TestSchemes/) to a laid-out one ("the loop")?

	* Index.org: New "Reference" section linking KicadGen.html
	(was already generated, never linked). New Workflow.org entry
	under "Concept and planning".
	* Workflow.org: New. The full pipeline, module by module, with
	every command actually run and verified before being
	documented as working -- including "the loop" itself
	(packpay's own already-generated schematic re-imported through
	load_from_schematic(), the same path TestSchemes' own files
	use, then regenerated via generate_schematic_auto(): 0 ERC
	violations on the result).
	* Makefile.am: Workflow.org added to ORG_DOCS.

2026-08-18  Péter Felecan  <pfelecan@gmail.com>

	Version 0.1.0: a real minor bump per Versioning's own rule
	(new functionality shipped today, not only fixes), not a
	default incremented merely because a delivery archive was
	being cut.

	* VERSION: 0.0.0 to 0.1.0.
	* NEWS: New "Version 0.1.0 (2026-08-18)" entry, summarizing
	today's real, shipped changes.
	* README: Fixed a real, now-stale claim ("packpay ships no
	manual pages at 0.0.0") left behind by today's own earlier
	man2htmldoc work; points at Index.org's own real "Manual
	pages" section instead.

2026-08-18  Péter Felecan  <pfelecan@gmail.com>

	render.py brought up to the same real CLI-plus-man-page bar
	as netlist.py, this project's own second worked example
	(FutureResearch.org's own "bring the remaining scripts up..."
	item). A real, general-purpose tool this time (any real
	.kicad_sch in, a breadboard SVG out), not a second small
	utility.

	* render.py: Real CLI added (argparse-based): -o/--output,
	--kicad-cli (now also reads $KICAD_CLI by default, matching
	this project's own Makefile convention), --version, --debug
	(prints every placed passive's own real placement reason and
	the unplaced list), --quiet. The schematic argument is now
	REQUIRED, a real, deliberate behavior change: the previous
	silent fallback to "packpay-example-circuit1.kicad_sch" (a
	demo-specific default) has no place in a genuinely general
	tool. Fixed a real bug found by testing the missing-file
	error path: the new exception handler referenced
	subprocess.CalledProcessError without subprocess being
	imported.
	* render.1: New. This project's own second real man(1) page.
	* Makefile.am, Index.org: render.1 added to MAN_PAGES/
	EXTRA_DIST; Index.org's own "Manual pages" section gains the
	render(1) entry alongside netlist(1).

2026-08-18  Péter Felecan  <pfelecan@gmail.com>

	Four real build/tooling bugs found by actually running this
	project's own build, all fixed and verified (including,
	for the Index.org fix, installing Emacs specifically to
	verify through the real publish pipeline rather than by
	reasoning about Makefile rules on paper).

	* configure.ac: AM_INIT_AUTOMAKE gains -Wno-portability,
	silencing automake's own "not portable"/"GNU make extension"
	warning against Makefile.am:150's own $(shell ...) := use --
	confirmed a false positive against a requirement this
	project's own configure.ac already hard-enforces (GNU make),
	not a real portability gap.
	* VERSION: $Revision$ keyword de-expanded back to its raw,
	unexpanded form (provided pre-expanded
	by mistake). Confirmed configure.ac's own AC_INIT version
	computation already handles the raw form as its normal,
	by-design case, not a special case needing a separate fix.
	* Makefile.am: MAINTAINERCLEANFILES gains configure~
	(autoconf's own backup-before-overwrite byproduct, confirmed
	reproducible on a second ./bootstrap; make maintainer-clean
	previously left it behind).
	* Index.org: "Manual pages" section given real content (a
	link to netlist.html), previously present as a header only.
	Root cause confirmed simpler than first suspected: html-local
	already correctly depends on man-html; the section was empty
	because Index.org's own text never linked to the generated
	page, not because the HTML conversion was being skipped.

2026-08-18  Péter Felecan  <pfelecan@gmail.com>

	First working session on packpay itself: the row-assignment
	room-check gap actually closed, the dependency question
	settled, man2htmldoc wired in with netlist.py as this
	project's own first real CLI-plus-man-page, and TestSchemes
	evaluated and trimmed. Real KiCad 10.0.5 obtained fresh for
	this session per DevelopmentStandard.org's own documented
	procedure (still works exactly as written).

	* auto_layout.py: Fixed. Row-assignment (the BFS-style
	placement pass, run_declutter()'s own row-start-only cap, and
	push-aside's own narrower-threshold blind spot -- three
	separate sites, traced directly) could each independently
	push a component's own row past ROWS_PER_HALF without
	checking first. A single room-search helper (_find_room()) is
	now reused at BFS placement time and in a new, final,
	bounded relieve_overflow() pass, both using the SAME relocate-
	and-let-extra-links-bridge idiom push-aside already
	established -- no new wiring mechanism. Verified against the
	real, originally-documented failure (example_circuit1.py's own
	D10/R10 overflow, reproduced byte-for-byte against a pristine
	0.0.0 extraction with real footprint data, then confirmed
	resolved), a synthetic stress case, a deliberately over-
	capacity synthetic case (terminates and reports honestly), and
	the full real pipeline (test_netlist.py, both generators' own
	0-ERC output, render.py's own full four-check demo path).
	* FutureResearch.org, ResearchJournal.org: Row-assignment item
	closed; the dependency-on-packpay question settled (keep
	PACKPAY_DIR, no pip package -- rationale recorded); man2htmldoc and
	TestSchemes items closed;
	two new items opened (the remaining scripts' own CLI/man-page
	work; a charlieplexed-LED placement-reach gap found evaluating
	TestSchemes; KiCad's own demo package as a future test-surface
	venue).
	* man2htmldoc: Fixed a real bug found converting this
	project's own first real man page: groff mis-renders any real
	UTF-8 character without -Kutf8 (confirmed directly against the
	author's name in netlist.1's own AUTHOR section); the generated
	HTML's own charset meta corrected from US-ASCII to UTF-8 to
	match.
	* netlist.py: This project's own first script brought up to a
	real getopt_long-equivalent CLI (argparse-based --version/
	--help/--debug/--quiet, matching DevelopmentStandard.org's own
	"Options and Arguments" minimal set), replacing the previous
	positional-only argv[1] check. Real error handling added
	(missing/unreadable file, unparseable netlist).
	* netlist.1: New. This project's own first real man(1) page,
	written only once netlist.py's own CLI actually had real
	options to document.
	* Makefile.am: MAN_PAGES = netlist.1 (was empty); the .1.html
	rule now actually calls man2htmldoc instead of its own
	previous "no man2htmldoc yet" stub; man2htmldoc and netlist.1
	added to EXTRA_DIST (both were real gaps: man2htmldoc was
	physically shipped in the 0.0.0 archive but untracked).
	* make-archive: man2htmldoc added to scripts_to_check (the
	same real gap as above, on the executable-bit side).
	* TestSchemes/: Trimmed, all five projects, to *.kicad_sch
	only -- confirmed unneeded by direct isolation testing:
	*.kicad_pcb/*.kicad_prl/*.kicad_pro/sym-lib-table/
	fp-lib-table/every real footprint file/Camera-trap's own
	legacy pre-upgrade files. Two of the five (knight-roller,
	rp2040-basic-m1) turned out to be genuinely hierarchical,
	multi-sheet KiCad projects (confirmed via each one's own
	.kicad_pro "sheets" field) and keep all of their own real
	sheet files together, not just the root -- a real,
	previously-unknown kicad-cli behavior found along the way
	(a missing sibling sheet file is silently dropped from the
	exported netlist, exit code 0, no warning). "jck-Camera-trap
	r1" renamed to "jck-camera-trap-r1" (a literal space in a
	directory name, a real shell-portability annoyance, matching
	the other four projects' own naming). Archive size 2.1M to
	500K; every one of the five re-verified, in place, through the
	real pipeline after trimming.

2026-08-14  Péter Felecan  <pfelecan@gmail.com>

	First delivery: packpay created by surgical extraction from dudhya (0.0.0).

	Assessed directly, before doing any work, whether this
	extraction was premature: not found premature (the generic
	engine had, by this point, been tested against three
	genuinely different circuits with real, automated verification
	at every step). Named packpay, a romanized abbreviation of a Sanskrit
	phrase meaning
	"transformation engine for flowing fire diagram."

	* netlist.py, kicad_gen.py, auto_layout.py, kicad_layout.py,
	footprint.py, render.py, render_footprints.py, visualize.py,
	breadboard_lib.py, measure_labels.py, measure_value_text.py:
	New. Extracted from dudhya's own Power/ subdirectory tree into
	a flat structure (no subdirectories at all, by requirement), every
	cross-file import fixed for the
	new layout.
	* example_pins.py: New. Real, verified KiCad symbol pin
	geometry (STUSB4500QTR, XL4015, USB-C receptacle, standard
	Device parts), extracted from dudhya's own real, hand-verified
	data so this project's own example circuits have zero
	dependency on any downstream project.
	* example_circuit1.py, example_circuit2.py: New (renamed from
	dudhya's own build_test_circuit_autolayout.py/
	build_test_circuit2_autolayout.py -- the old "_autolayout"
	suffix referenced a dudhya-specific hand-placed-vs-autolayout
	distinction that doesn't exist here). Both re-verified: 0 ERC
	errors, same placement behavior as their dudhya-era runs.
	* test_netlist.py: Rewritten. Previously checked netlist.py
	against dudhya's own real circuit facts (not portable); now
	generates example_circuit1.py's own output fresh and checks
	the same real kicad-cli pipeline against that self-authored
	ground truth instead.
	* render.py: Fixed two real bugs found while re-verifying this
	project's own example circuits through the breadboard-side
	algorithm for the first time (neither had ever been run
	through it before, only through the KiCad side) -- both
	independently triggered a genuine row-capacity overflow
	(auto_layout.py's own row-assignment doesn't check available
	room before stacking components in a column), which used to
	crash with an unhandled AssertionError instead of a clean
	failure report. Fixed the crash risk in both the demo's own
	control flow and verify_no_hole_collisions() itself (protecting
	every caller of render(), not just the demo). The deeper root
	cause (row-assignment itself) is deliberately deferred, honestly
	documented in FutureResearch.org, not fixed hastily mid-
	extraction.
	* configure.ac, Makefile.am, bootstrap, make-archive,
	.tar-exclude, README, AUTHORS, COPYING, VERSION, Preamble.tex,
	version.org.in: New. Adapted from dudhya's own conventions,
	scoped for a flat, Python-only project. configure.ac adds one
	real check dudhya's own configure.ac is missing: a
	kicad-footprints presence check.
	* batch-init.el: Copied from dudhya, with a real, unrelated bug
	fixed along the way (a corrupted word, "idudhyately", almost
	certainly from an earlier project rename gone wrong -- dudhya's
	own copy still has it).
	* Constitution.org, Concept.org, DevelopmentStandard.org,
	ResearchJournal.org, FutureResearch.org, Index.org,
	SessionHandoff.org: New. DevelopmentStandard.org's own
	Electronics section is, almost word for word, dudhya's own --
	it was already written, from 2026-08-03 onward, to be "reusable
	across ANY future project involving electronics, not just
	dudhya." ResearchJournal.org reproduces the relevant history
	(2026-08-03 through 2026-08-14) from dudhya's own journal
	verbatim, with a new entry documenting the extraction itself.
	* VERSION, NEWS: 0.0.0.
