elecdef: rules
Every implementation and every later change follows these rules. Each rule is one sentence and
its GATE: the mechanical check that fails a breach. "Review" is the gate only where no mechanism
can see the breach, and the reason is given. The family rules F1-F17 are shared with refdef
and carried verbatim; the rules E1-E19 are elecdef's own and repeat no family rule.
Family rules
These rules bind elecdef and refdef alike, and symdef where it can (symdef has one runtime dependency, so F1 does not bind it). Both repositories carry this text verbatim in RULES.md,
and tests/test_family_rules.py in each compares it to the other's last release.
- F1. The library declares no runtime dependency and imports only the standard library and
itself; libraries of the family never import each other.
GATE:
tests/test_deps.pyassertsdependencies = []inpyproject.tomland walks every module's imports withast, allowing onlysys.stdlib_module_namesand the package. - F2. Every value is a frozen dataclass or an enum; the library performs no file, network,
clock or random access.
GATE:
tests/test_pure.pyfails a@dataclasswithoutfrozen=Trueand any import or name fromos,pathlib,io,socket,time,datetime,random,secrets,uuidor a call toopenundersrc/. - F3. Validation returns issues as values and never raises for content; only a malformed
call (a wrong type, a wrong kind of argument, or a shape the library's
RULES.mdnames as malformed) raisesTypeError. GATE:tests/test_check.pyrunscheckover every malformed fixture intests/fixtures/and asserts a non-empty issue tuple and no exception;tests/test_malformed.pyassertsTypeErrorfor each malformed-call fixture. - F4. A closed vocabulary is an enum or a typed constant, never a string.
GATE:
tests/test_vocab.pyfails any dataclass field annotatedstrthat is not in the allowlist that the test names andRULES.mdlists for that library. - F5. A fact has one home across the family:
symdefdraws,elecdefsays what a thing is,refdefsays what it is called, a consumer connects and lays out. GATE:tests/test_boundary.pyfails a field or function named for another home, from a list each library keeps in the test (for elecdef:designation,tag,svg,x,y,page; for refdef:rating,voltage,svg,x,y,page); the rest is review, because a duplicated meaning under a new name is invisible to a test. - F6. A caller's convention (a padding, an ordering, a default, a project class) arrives as an
argument or a value; no general function carries a caller's default. A fact the library itself
defines (a block's default terminal markings, the shipped scheme's signs) is not a convention.
GATE: the tests of every option-taking function pass the option explicitly, and
tests/test_boundary.pyfails a module-level default that names a consumer; what counts as a convention is review, because a test cannot tell a convention from a fact. - F7. No paid standard is cited and no standard's wording is used; open sources are used
only with the credit block in
README.md. GATE:tests/test_wording.pyfails a standards-body abbreviation followed by digits, an edition year beside a table or scheme name, or a clause reference (the pattern list lives in the test) anywhere undersrc/,docs/,README.mdorCHANGELOG.mdoutside the README credit block, and asserts the credit block names every open source atables/module header declares. - F8. The public surface is the names in
__all__of the top level and of each door, read bypython -m <library> api, and it changes only with the snapshot. GATE:tests/test_surface.pycomparespython -m <library> apitodocs/api-snapshot.txtbyte for byte, and fails an import of a non-surface name indocs/andtests/guide/. - F9. Every surface name has a docstring and a runnable example on the docs site.
GATE:
tests/test_docs.pyasserts a non-empty docstring for every surface name, one fenced Python block naming it underdocs/guide/, and runs every fenced block; the strict Zensical docs build runs in CI. - F10. Code stays lean: a function has at most 15 statements, complexity 8 and 12 branches,
no
definside adef; a module has at most 300 code lines; a docstring is one summary line plus at most two lines of why. GATE: ruffPLR0915=15,C901=8,PLR0912=12inpyproject.toml;tests/test_lean.pyfor nested defs, module lines and docstring shape; everynoqaandty: ignorenames its reason on its line, checked by the same test. - F11. Versions are semantic and every surface change carries a changelog line.
GATE:
scripts/release_guard.pyin CI fails whendocs/api-snapshot.txtdiffers from the last tag's copy andCHANGELOG.mdhas no line under the unreleased heading, and when a name disappeared without a major bump (minor before 1.0). - F12. A pull request that edits
DESIGN.mdorRULES.md, or changesdocs/api-snapshot.txt, or changes a shipped table undersrc/<library>/tables/, carries aDesign-review:trailer naming an approved review. The maintainer approves a change toDESIGN.mdorRULES.md; the design reviewer approves a surface or table change, and the maintainer sees it at the next review. GATE:scripts/design_guard.pyin CI inspects every commit in the range touching those paths and fails one without the trailer; branch protection requires the job. - F13. A consumer's missing need is met by a reviewed surface extension, never by a
workaround in the consumer, a private import, a copy of the rule or a consumer-specific
default in the library.
GATE:
tests/test_boundary.pyfails an import or a name of another family library undersrc/, andtests/test_standalone.py(F17) fails an application's name; the consumer side is review, because the library cannot see the consumer's tree. - F14. The library is MIT licensed, and the licence covers its code and its shipped tables.
GATE:
tests/test_licence.pyassertsLICENSEholds the MIT text andpyproject.tomldeclareslicense = "MIT", and fails atables/module header that declares an open source whose licence is share-alike or non-commercial. - F15. One public spelling per task: a second way to build, read or print the same value is
not added, and no operator is overloaded to build a value; arithmetic belongs only to the
numeric value types the library's
RULES.mdnames. GATE:tests/test_surface.pyfails an arithmetic or bitwise dunder (__add__,__or__,__truediv__and their kin) on a surface class thatRULES.mddoes not name as numeric; a second spelling under a new name is review, because a test cannot see that two names do one task. - F16. An agent finds everything from one place:
AGENTS.mdin the repository andllms.txtat the docs site root name the docs site, the surface command (python -m <library> api), the guide,DESIGN.mdandRULES.md, and say that a change followsRULES.md. GATE:tests/test_agents.pyasserts both files exist, name each of those five targets, and that every link in them resolves in the built site (the strict Zensical docs build). - F17. The library stands alone: no file in the repository names an application that uses it.
It is written for every electrical engineer, and its examples are general.
GATE:
tests/test_standalone.pylowercases every word in the tracked files and fails one whose SHA-256 is in the test's list of application-name hashes, so the test spells no name itself.
elecdef rules
- E1. The F4 allowlist for elecdef, covering fields annotated
strortuple[str, ...], ismarking,alternates,markings,common,mpn,manufacturer,name,key,text,message,subject. GATE:tests/test_rules_lists.pyparses this rule and asserts the list equals the onetests/test_vocab.pycarries. - E2. A measured figure is a
Decimalinside aQuantity, a count is anint, a flag is abool, a derived ratio (tolerance) is aFraction, andfloatappears nowhere. GATE:tests/test_quantity.pyassertsTypeErrorfor a float value;tests/test_pure.pyfails anast.Constantof typefloatand afloatannotation undersrc/. - E3. A
Unitis an enum member with oneKindand an exact factor; conversion and comparison refuse a second kind. GATE:tests/test_quantity.pyparametrised over every unit pair of different kinds assertsTypeErrorfromto,sameand ordering; every factor is aDecimalwith a finite representation. - E4. Every quantity field of a block declares its kind, and a value of another kind is
refused at construction.
GATE:
tests/test_kinds.pyintrospects every dataclass undersrc/, fails aQuantity,RangeorRangesfield without kind metadata, and constructs each with a wrong-kind value assertingTypeError. - E5. A
Rangeis written asRange(Role.X, nom=, min=, max=, at=)with an optionaltyp;roleis positional and required, tolerance is a derived property, and there is no second constructor or helper spelling. GATE:tests/test_range.pyassertsRange(nom=U.V(24))raisesTypeError, fails aclassmethodorstaticmethodonRangeand a module-level function returning aRangeoutsidepick, and assertstolerancereturns exactFractions. - E6. Current kind, frequency, utilisation code, coil state, conductor kind and ambient are
conditions in
At, never fields of a block; two conditions of one kind in anAtis the malformed shape F3 names. GATE:tests/test_kinds.pyfails a block field annotated with a condition enum (Current,Utilisation,CoilState,ConductorKind) or whose name, split on_, containsac,dcorhz;tests/test_malformed.pyassertsTypeErrorfor two conditions of one kind. - E7.
pickreturns one covering range or an issue; it never returns the first of several. GATE:tests/test_pick.pycovers zero, one and two matches, inclusion by aRangecondition, equality by aQuantitycondition, identity by an enum, and an ask naming a kind the rows lack. - E8. The library answers which stated figure applies and never whether a figure is
acceptable: the only surface callables that return a truth value are
Quantity.same,Quantityordering,Range.contains,Range.coversandAt.covers. GATE:tests/test_surface.pyasserts the set of surface callables annotated-> boolequals that list; a verdict hidden behind another return type is review, because a test cannot read intent. - E9. Blocks compose by typed fields; an application never subclasses a block, and there is one
base class,
Block, for the kind check only. GATE:tests/test_blocks.pyasserts every block's MRO is(cls, Block, object)and thatcheckreports a subclass defined in the test. - E10. A composite's
pinsandlinksare computed from its sub-blocks and stored nowhere else; a leaf's are stored once. GATE:tests/test_pins.pyfails a composite with a storedpinsorlinksfield and asserts the union equals the concatenation of its sub-blocks'. - E11. Pin markings, their alternates and marking schemes are data in
elecdef.pins; aCoilorContactstores only the pins its author gives (own_pins, may be empty) and computespinson every read from its kind, form and position, and a composite numbers its unnumbered contacts on every read, soreplacenever leaves a stale marking; a marking is a non-empty string of letters, digits and/ + - . '. GATE:tests/test_pins.pygolden-tests every scheme (aux, main, overload, timer, plain) for each form and index 1-9 and the relay, contactor, contact block, overload and card numbering, and assertscheckreportsBAD_MARKINGfor every generated string outside the set. - E12. A
Connectionbelongs to the pins a block owns itself:Coil,Contact,SupplyInput,ChannelGroup,Fuse,FuseHolder,Breaker,Overload,PowerSupply,ConnectorandDevicehave oneconnectionfield,TerminalBlockhassides, and every other block has neither. GATE:tests/test_blocks.pyasserts that exact partition over every class undersrc/. - E13.
elecdef.connectionsships technology-only leaves with every range unfilled; vendor figures (torque, strip length, conductor ranges) never ship. GATE:tests/test_connections.pyasserts every shipped leaf hastechnologyset and every other field at its default. - E14. Data tables are generated Python modules whose rows carry no origin, and a row ships
only when the generator's input file, which records each row's origin (an open source, a
maker's datasheet or a measurement) and its confirmation status, marks it confirmed.
GATE:
tests/test_tables.pyfails atables/module without the generated header, a row string containinghttp,sourceorverified, and any row listed intables/HELD.txt; the generator refuses an unconfirmed row, and the status itself is review. - E15.
Product(mpn, manufacturer, block, name)is the one identity envelope; the library holds no supplier, price, footprint, class letter or description field. GATE:tests/test_blocks.pyfails a dataclass field named exactlysupplier,price,footprint,class_letterordescriptionanywhere undersrc/. - E16.
checkreports exactly the content issues the design names: an end below another, a repeated marking, a marking outside the character set, a link naming no pin, a figure a block kind needs and lacks, a class outside the catalogue;IssueCodehas these six codes (END_ORDER,DUPLICATE_MARKING,BAD_MARKING,DANGLING_LINK,MISSING_FIGURE,UNKNOWN_CLASS) and the two thatpickreturns (NO_RATING,AMBIGUOUS_RATING), eight in all. GATE:tests/test_check.pyasserts theIssueCodemembers equal these eight, has one fixture per member that produces it, and asserts thatcheckemits only the six. - E17. A block names its symbol by
Symbol(key, version)and holds nothing else of it. GATE:tests/test_blocks.pyfails a dataclass field namedsymbolwhose annotation is notSymbol | Noneand any dataclass field named exactlygeometry,port,outlineorglyph. - E18. For F15,
Quantityis the only numeric value type, and in v1 it defines comparison dunders only; no surface class defines an arithmetic or bitwise dunder. GATE:tests/test_surface.pyasserts no arithmetic or bitwise dunder on any surface class and comparison dunders onQuantityalone. - E19. Coverage never falls below 95 % per module, and
ty checkpasses. GATE:pytest --cov=elecdef --cov-fail-under=95andty checkin CI.