fix: apply pass 1 review verdicts to schema library v2 - #80
Merged
Conversation
…ces keep an MTU Packet: DCIM-06 (reject, redesign), keeps DCIM-07. Moving mtu to InterfacePhysical left InterfaceLag and InterfaceVirtual with no MTU at all, which is where a non-default MTU is most often needed (bundles, SVIs, tunnels, loopbacks). The fix for a bad default (1514 was frame size) is the default, so mtu returns to the DcimInterface generic with default_value 1500. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ufacturer Packet: ORG-N1 (change). CC-2 wants plural names on cardinality-many relationships, and both declarations are PR-touched. The rename also removes the clash with DcimGenericDevice.device_type and .platform, which are cardinality one and mean something else. Identifiers stay singular on the object part, so the forward sides in base/dcim.yml are untouched. Checklist: CC-2 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet: LOC-02 (change). The human_friendly_id was silently deriving the uniqueness constraint, which CC-4 forbids; unique: true states what Infrahub already enforces, so there is no behaviour change and no migration. optional and order_weight are written on the same three lines, and display_label plus order_by give the generic parity with LocationGeneric (it is a relationship peer in seven places and had a null display label). Checklist: CC-4, CC-6, CC-9 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…order Packet: IPAM-05 (change, substance accepted). The mandatory plus default_value plus base palette work is exactly what CC-5 and CC-6 ask for; the one defect is that this node listed reserved after deprecated while DcimDevice, GenericVLAN, LocationRack and IpamIPAddress all put reserved first (4:1). Two nodes in the same file ordered the same dropdown differently. Checklist: CC-5 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet: IPAM-04 (color half only, the rest is a question to the author). backbone and management both used #AEC6CF, so the two roles were indistinguishable in the UI and the color carried no information. #9678B6 is freed by dropping the v1 technical role. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packets: AGG-01, VLAN-01 (both change). CC-4 forbids relying on the constraint Infrahub derives from the human_friendly_id. IpamAggregate resolved to a derived [[prefix__value]] (namespace-less is correct here) and IpamVLANGroup.name to a derived [[name__value]]; both are now stated in the file. Checklist: CC-4 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packets: AGG-02, PSU-03, DCIM-CC2 (all change; DCIM-CC2 confirmed by Matej as a rename). CC-2 says Booleans are bare snake_case with no is_ prefix (is_private -> private, 7:1 in the tree) and no run-together words (hotswappable -> hot_swappable, named by the checklist). DCIM-CC2 follows the CC-2 majority count, serial_number 3 against serial 2, so base DcimPhysicalDevice and DcimGenericSFP move to serial_number. The matching object files are renamed in the same commit so the data still loads. Checklist: CC-2 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packets: IPAM-07 (change), base_ipam consistency note 3, ruling R5 (IpamCVLAN part deferred to the QinQ scoping change). The namespace-first human_friendly_id flip is accepted, but the declared constraints still read value-first, so Infrahub no longer recognises them as the same constraint and adds its derived one on top: the live schema carried two functionally identical constraints on IpamPrefix, IpamIPAddress and DcimCrossConnect. Reordering restores one explicit constraint and no implicit one. Checklist: CC-4 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…unique label Packet: CABLE-02 (change). order_by and display_label both point at label, which was optional and non-unique, so unlabelled cables rendered an empty display label and sorted arbitrarily. Making label the node's declared identity fixes both and covers CC-4's HFID-less-node list. Checklist: CC-4 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet: CLU-01 (change). ClusterGeneric.location is cardinality many with a singular name, and CC-2 makes plural the rule. No identifier is declared on either side and the reverse LocationGeneric.clusters is named independently, so the rename does not have to be mirrored. Checklist: CC-2 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e ComputeGenericUnit
Packets: COMP-03, HOST-03 (both escalated, decision Q6-B).
CC-3 says menu placement follows the node's own domain: a Compute server filed under
DcimDevice ("Network Device") and a Cluster filed under VirtualizationVirtualMachine both
broke that, the latter also inverting containment. ComputeGenericUnit becomes the visible
anchor, which is the library's own idiom and yields a real "all compute units" list.
CLU-01 stands as the author wrote it: ClusterGeneric stays hidden.
Checklist: CC-3, CC-9
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tte convention Packet: XCON-CC6 (change, no packet owns the file). CC-6 names cross_connect as one of the status deviants: status is mandatory with a default_value everywhere else. planned is the safe default, since a cross-connect has to be ordered from a carrier before it is connected. The three colors were still the v1 palette. Checklist: CC-5, CC-6 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet: MOD-04 (change).
Text is the wrong kind: the description documented NetBox's {module} token substitution,
which nothing here implements, the sibling DcimPhysicalDevice.position in the same
namespace is a Number, and the author's own first version (98a730f) was Number with
min_value 1. No uniqueness constraint restored: position is optional.
Checklist: CC-2
Note: objects/extensions/device_module/device_module.yml still quotes the three values;
unquoting them is the paired objects-file fix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nja2 template Packet: MOD-06 (change). The generic declared computed_name as read_only + mandatory with no computed_attribute, so it could be neither set by a client nor computed by the server, and any node inheriting the generic without overriding it could not be created. The generic can compute from module_bay, which it owns; concrete kinds keep their own override. The stale "Overriten at node level" comment goes with it. Checklist: CC-4, CC-8 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet: PSU-02 (change).
CC-2 requires units in parentheses in the label, which base does uniformly ("Height (U)",
"Weight (kg)"), and wattage carried no label at all. Commit 8eae840 in this PR set the
min_value precedent; without it a wattage accepts 0 and negatives.
Checklist: CC-2
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… stale 4096-char FIXME Packets: CONN-01 (change), CONN-02 (ruling R6). The FIXME's premise is false on 1.11.0: the full 28-choice list loads at roughly 3.3 KB against a 4096-char limit. CONN-02 keeps the two lists duplicated, so each now carries a sync comment pointing at the other; a shared generic in a common ancestor is the real deduplication and is a base-tier change for a separate release. The three restored choice names keep the surrounding UPPERCASE form; the case sweep owns all 28 values at once. Checklist: CC-5, CC-8 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…re-type enum in the domain Packet: DWDM-03 (change). The flip to optional was unstated and left one identical 25-choice list with two optionalities inside one domain. DcimGenericSFP.sfp_type, form_factor and DcimGenericPatchPanelInterface.connector_type are all mandatory; CC-6's "no sensible default" exception covers a mandatory connector type, and CONN-01 removed the one argument for optional by restoring the missing choices. Checklist: CC-6 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…precated vocabulary Packets: LMIN-03, RACK-02 (both change). CC-2 resolves split vocabulary by majority. Counted across base/ and extensions/, the choice name deprecated wins 5:2 and the label Deprecated 4:3, so both Sites rename. The Rack already used name: deprecated with label: Retired, exactly the name/label split CC-2 exists to close. Checklist: CC-2 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ntifiers Packet: PP-01 (identifier half; the dependency and description halves belong to the metadata commit). CC-7 wants identifiers in subject__object form. DWDM-01 fixed the dwdm half of the v1 collision and left these two bare; v2.0 is the last release that may rename a stored relationship identifier. Checklist: CC-7 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ent/Component Packet: RACK-01 (change). Two defects rode in on the split. CC-4: name was globally unique, so two sites could not each have a rack called R01, and rack names repeat across sites in every real estate; identity is now scoped by site with the uniqueness declared explicitly. CC-7: LocationRack.site was Attribute while its reverse LocationSite.racks is Component, and Parent is what pairs with Component. Deletion behaviour is unchanged, the site side stays Component and site stays mandatory. Checklist: CC-4, CC-7 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet: RACK-04 (change). CC-2's explicit call: facility beats facility_id 2:1, and the rack either aligns or documents that it is a different concept. The attribute carries no description at all, so nothing in the file distinguishes a rack's local identifier from a site's. The objects file moves with it or the load breaks. Checklist: CC-2 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet: SNMP-03 (change). Adding the link is right, the peer was not: concrete DcimDevice silently excludes ComputePhysicalServer, VirtualizationVirtualMachine and DcimPatchPanel, and servers and VMs are the most SNMP-polled things in an estate. TEN-02 in this same diff peers the base generic, and DcimGenericDevice is base, so the change costs no dependency edge. CC-4's explicit uniqueness behind the HFID name folds in here. Checklist: CC-1, CC-4, CC-7 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet: SNMP-04 (change). CC-2 makes plural the rule on cardinality-many relationships and routes untouched singulars to their own packet; SnmpClient had two, the new ip_address and the pre-existing community. CC-4 lists SnmpClient among the nodes whose HFID name is not explicitly unique. community also had no kind, declared here as Attribute rather than left to the kind sweep. Checklist: CC-2, CC-4 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ce it under Organization Packets: TEN-01, TEN-04 (both change). CC-3 names this node explicitly: a single-node Tenancy namespace whose only node inherits OrganizationGeneric violates one-namespace-per-domain, and the folder keeps the feature name. The missing label and menu_placement ride along, matching the two namespace siblings OrganizationManufacturer and OrganizationProvider. TEN-04: tenant__ipaddress runs two words together where every sibling identifier in the file separates them. Checklist: CC-3, CC-7 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet: IPAM-03 (change). The bare stub in base is forced: base loads standalone and first, so it cannot declare vlan_groups without dangling on IpamVLANGroup. What was wrong is the mechanism the extension used to fill it: re-declaring the whole generic under generics: replaces base's definition, duplicates a kind across two files, and hides who fills the stub from a reader of base. extensions: nodes: - kind: <GenericKind> is the documented and repo-established mechanism, already used on LocationHosting by three extensions in this tree. The stub also gains the label and description CC-2/CC-8 require. Checklist: CC-1, CC-2, CC-8 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ss per kind Packets: VLAN-03, QINQ-02 (both change; decision Q4-A). CC-2 rejects a literal Generic namespace: the namespace is prepended to form the kind and "Generic" is not a domain, so the convention is <Ns>Generic<Thing>, exactly the DcimGenericDevice shape. Separately, [vlan_id__value, vlan_group] on the generic made IpamVLAN, IpamSVLAN and IpamCVLAN share one flat VLAN-ID space per group, so two C-VLANs with the same C-tag under different S-VLANs were rejected at create time. That is the central Q-in-Q case. The constraint moves down to IpamVLAN and IpamSVLAN; IpamCVLAN keeps only its own [vlan_id__value, svlan]. Checklist: CC-2, CC-4 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packets: IPAM-07 cross-reference, base_ipam consistency note 3, ruling R5. Third and last of the duplicate-constraint fixes; IpamPrefix, IpamIPAddress and DcimCrossConnect landed earlier. IpamCVLAN's HFID reads [svlan__name__value, vlan_id__value] while the declared constraint read [vlan_id__value, svlan], so Infrahub no longer recognised them as the same constraint and added its derived one on top. Sequenced after the QinQ scoping change so the reorder applies to the final constraint set. Checklist: CC-4 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet: VRRP-02 (change, admitted under ruling R3). Measured: deleting a single virtual IP address deleted the VRRP group and the group's other virtual IP. The cause is kind: Component on IpamIPAddress.vrrp, which implies on_delete: cascade and declares that an IP address owns a VRRP group. Every other Component relationship in the library points container to children. The group to IPs cascade stays: a VIP exists only for its group. A kind change does not touch the derived identifier, so no data migration. Checklist: CC-7 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…onnection Packet: IXP-02 (change). Live-verified: PeeringIXPConnection.bgp_sessions resolves to Component, which defaults to on_delete: cascade, while RoutingBGPSession.ixp_connection is optional. So deleting an IXP connection silently deleted its BGP sessions with their policies and AS links, even though the schema says a session need not have a connection. This is the only Component pair in the repo whose child side is optional by necessity. kind stays Component for the separate-tab UI; on_delete, not the kind, drives deletion. Checklist: CC-7 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packets: SNMP-03, SNMP-04 (objects half). The SNMP-04 verdict states the objects file populates neither renamed relationship; it does populate community on SnmpClient three times, so the load would break on communities. The header comment also still named DcimDevice and ip_address. Split out rather than folded because both schema commits had already landed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Parked objects finding (review brief item c): the circuit mock data re-declared the Americas -> United States -> NYC1/SJC1 hierarchy that objects/extensions/location_minimal already owns, and re-upserting a hierarchical root fails. The endpoints now reference the existing sites by LocationHosting's name__value human_friendly_id instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Output of `uv run invoke docs.generate` after the 56 schema and metadata commits on this branch. Covers the renames (device_types/platforms, OrganizationTenant, serial_number, private, facility, IpamGenericVLAN), the new human_friendly_id and dependency declarations, and the .metadata.yml description rewrites, plus the sweeps' order_weight/optional/include_in_menu. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ider descriptions M-TEXT leftovers: the device_psu_module header announced three PSU bays while the file creates two (PSU-01); circuit.yml re-upserts Lumen and Colt with descriptions that drifted from objects/base/01_organization.yml, so whichever file loaded last won. Checklist: CC-8. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Infrahub caps `description` at 128 characters. Three descriptions added by review commit 351f655 were over the cap, and the first one aborted the whole base load on a fresh 1.11.0 instance before any node was created: base/dcim.yml generic DcimConnector 212 chars extensions/circuit node DcimCircuitEndpoint 208 chars extensions/vrf attribute enforce_unique 146 chars Each description keeps its factual core; the UI-behaviour half moves to a YAML `#` comment above the field, which has no length limit and stays next to the definition it explains. A scan of every `description` and `label` in base/, extensions/ and experimental/ (recursive walk of the parsed YAML) reports zero remaining values over 128 characters, so these three were the only offenders. Verified on a fresh infrahub-testcontainers 1.11.0 instance: `infrahubctl schema load base` now reports "4 schemas processed in 7.106 seconds" where it previously failed with "Unable to load the schema". Reference pages regenerated with `invoke docs.generate`. The three hand-written mutual-exclusion notes in extensions/*/README.md are restored afterwards because the generator overwrites them with the pointer template. Checklist: CC-8. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review commit 0ffcd60 gave DcimPhysicalDisk a human_friendly_id of [device__name__value, name__value] to match its uniqueness_constraints, but left the peer generic DcimDeviceWithPhysicalDisks with a plain `name`. Infrahub only accepts an HFID that walks a peer when the referenced attribute combination on that peer is unique, so a fresh 1.11.0 instance rejected the extension outright: HFID of DcimPhysicalDisk refers to peer DcimDeviceWithPhysicalDisks with a non-unique combination of attributes ['name__value'] `unique: true` on that attribute closes the gap. The library ships no implementer of this generic (it is documented in .metadata.yml as one you apply to your own device kind), and every device kind here inherits DcimGenericDevice, which already declares name unique, so no shipped node changes behaviour. The existing `allow_override: any` still lets an implementer restate the attribute. This mirrors DcimPhysicalDevice.name in base/dcim.yml, which carries the same unique + allow_override pair and the same explanatory comment. Verified on a fresh infrahub-testcontainers 1.11.0 instance: `infrahubctl schema load extensions/physical_disk` reports "1 schema processed in 4.891 seconds"; it previously aborted on the HFID error above. Checklist: CC-4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
objects/extensions/circuit loads at schema-dependency position 2 but referenced
LocationSite NYC1 and SJC1, which objects/extensions/location_minimal only
creates at position 7. On a fresh 1.11.0 instance the file aborted with:
Unable to find the node NYC1 / LocationHosting
DcimCircuitEndpoint.location is mandatory and its peer LocationHosting is a base
generic that base itself never implements, so the fixture has to bring its own
sites. They are created with name and description only, without a parent:
extensions/location_site has no tier above Site and extensions/location_minimal
puts sites under Americas -> United States, so restating either tree would tie
this file to one of the two mutually exclusive location extensions. Restating
location_minimal's is not possible in any case, re-upserting the hierarchy root
fails with "Not supported to assign a value to parent for LocationRegion".
Declaring extensions/location_site as a dependency in .metadata.yml (the way
extensions/rack does, which would move circuit behind the location fixture) was
rejected: CC-1 dropped circuit -> location_minimal as a declared-but-unused
edge, and circuit.yml still references no LocationSite kind, only the base
LocationHosting generic.
On the parent question the brief raised: a partial LocationSite upsert does
clear `parent`, confirmed directly (load location_minimal, upsert
{name: NYC1, description: ...}, parent goes from "United States" to null). It is
not a consequence of this change. objects/extensions/sfp, cross_connect, vlan,
compute, dwdm and hosting_cluster already carry fifteen nested
{kind: LocationSite, data: {name: ...}} blocks that do exactly the same thing,
several of them after location_minimal in the load order, so NYC1 and SJC1 end
a full ordered load without a parent with or without this commit. This file
adds no new exposure: at position 2 it runs before every other file that
touches a site. Cleaning up the fifteen pre-existing blocks (a plain HFID
scalar resolves against LocationHosting and does not re-save the node) is a
separate sweep.
Verified on a fresh infrahub-testcontainers 1.11.0 instance: objects/base plus
all 34 objects/extensions directories load in resolver order with zero errors,
35/35, where objects/extensions/circuit previously failed. GraphQL then reports
DcimCircuit 3 and DcimCircuitEndpoint 4.
Checklist: CC-1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mechanism, read out of infrahub-sdk 1.17.0 and then confirmed on a live
instance. `infrahubctl object load` builds every record from scratch and saves
it with allow_upsert. In InfrahubNodeBase._generate_input_data
(node/node.py:213) a relationship that the document leaves out is treated three
different ways: cardinality-many is skipped, mandatory cardinality-one is
skipped, and OPTIONAL cardinality-one is written into the payload as an
explicit None. A partial re-upsert therefore clears exactly the optional
cardinality-one links, and `create()` sends that payload unfiltered because
allow_upsert never passes exclude_unmodified.
DcimDevice.mlag_domain is optional cardinality one and shares the identifier
device__mlag_domain with MlagDomain.peers, which is min_count/max_count 2.
objects/extensions/mlag wired nyc1-rtr01 and nyc1-rtr02 into NYC1-MLAG-01 at
resolver position 16; objects/extensions/rack (17), device_psu_module (18),
dwdm (22), routing_ospf (28) and routing_pim (29) each re-upsert nyc1-rtr01
afterwards without it, and all five failed with:
['DcimDeviceUpsert'] Node <id> has 0 peers for device__mlag_domain,
no fewer than 2 allowed
Restating mlag_domain in those five files was rejected: objects/extensions/rack
and four others would then fail whenever extensions/mlag is not loaded, which
inverts the layering and contradicts the self-sufficiency rule applied to the
circuit fixture. Referencing the device instead of re-declaring it is not
available either, DcimModuleBay.device peers with DcimPhysicalDevice, which
declares no human_friendly_id, so the nested {kind, data} block that re-saves
the device is the only way to express that link.
So the fixture owns its own endpoints. The domain is now built on nyc1-rtr02
and a new nyc1-rtr03, and this file is the only one anywhere under objects/
that ever saves either of them; nyc1-rtr01 is left to the files that own it.
objects/extensions/routing_bgp and peering_ixp are unaffected, they reference
nyc1-rtr02 by human_friendly_id, which does not re-save it. Both peers moved
into a single document so the LocationRack block can be shared through a YAML
anchor, which does not cross a `---` boundary.
The upsert rule is written out once, in this file's header, with the failing
error text, so a future file re-declaring a linked node has somewhere to look.
objects/extensions/circuit and objects/extensions/vrrp point at it.
Verified on a fresh infrahub-testcontainers 1.11.0 instance: all 35 object
directories load in resolver order with zero errors, and GraphQL reports
NYC1-MLAG-01 with peers nyc1-rtr02 and nyc1-rtr03, two peer_links and
MlagInterface 2.
Checklist: CC-1, CC-7.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same defect family as the MLAG one, uncovered once that fix let objects/extensions/rack get past its device upsert. rack then failed on the nested interface instead: ['InterfacePhysicalUpsert'] Node <id> has -1 peers for interfacelayer3__networkvrrpinterface, no fewer than 1 allowed extensions/vrrp adds InterfaceLayer3.vrrp as an optional cardinality-one relationship, and its counterpart NetworkVRRPInterface.interface is mandatory. objects/extensions/vrrp (resolver position 14) put NYC1-CORE-VRRP on GigabitEthernet0/0/0/0, which objects/extensions/rack (17) owns and re-declares afterwards; the re-declaration nulls the optional side and would orphan the NetworkVRRPInterface, so the server refuses the mutation. The group moves to GigabitEthernet0/0/0/3, which nothing else creates or touches, so all three VRRP groups now sit on interfaces this file creates itself (/1, /2, /3). The header records why /0 is off limits and points at the upsert rule in objects/extensions/mlag/mlag.yml. The stale claim that GigabitEthernet0/0/0/0 "already exists (from extensions/rack)" goes with it: rack loads after this file, sfp is what creates that interface earlier. A sweep of the live 1.11.0 schema for the same shape (an optional cardinality-one relationship whose counterpart is mandatory cardinality-one) found only two others: InterfaceLag.vrrp, which no fixture uses, and DcimModuleBay.installed_module, where objects/extensions/device_psu_module creates fresh bays instead of reusing the occupied ones. Verified on a fresh infrahub-testcontainers 1.11.0 instance: objects/base plus all 34 objects/extensions directories load in resolver order with zero errors, 35/35. GraphQL reports three NetworkVRRPInterface records on GigabitEthernet0/0/0/1, /2 and /3. Checklist: CC-7. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deploying schema-library with
|
| Latest commit: |
233a727
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d7b537e9.schema-library.pages.dev |
| Branch Preview URL: | https://review-schema-library-v2.schema-library.pages.dev |
The markdown lint CI job failed with a single MD047 violation on docs/docs/home.mdx, which is generated from docs/_templates/home_page.j2 by _generate_home_page_documentation(). The template ends with a newline, but the renderer built its environment as jinja2.Environment(trim_blocks=True), and keep_trailing_newline defaults to False, so Jinja stripped the template's final newline from the rendered output. Regenerating the docs therefore reproducibly emits a home page that markdownlint rejects. Set keep_trailing_newline=True and regenerate. The flag only suppresses the lexer's end-of-source newline strip, so the output is the previous output plus one newline, and nothing else changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lancamat1
marked this pull request as ready for review
September 1, 2026 08:47
extensions/vrf/vrf.yml -> #82 tasks/schemas.py (schema-less extension dir) -> #83 tasks/schemas.py (loader return value) -> #84 tasks/docs.py (hardcoded base link, trailing comment only) -> #85 extensions/routing/routing.yml -> #86 Drafts 1 (#81), 7 (#87) and 8 (#88) have no marker to delete. Checklist: CC-8 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BaptisteGi
approved these changes
Sep 2, 2026
| display_label: prefix__value | ||
| uniqueness_constraints: | ||
| - [prefix__value, ip_namespace] | ||
| - [ip_namespace, prefix__value] |
Contributor
There was a problem hiding this comment.
Order uniqueness constraint elements from most selective (returning fewer matches) to least selective (returning more matches) to optimize database query performance.
Nitpicking but to me the prefix value is more selective than the namespace value. (e.g. you will have less 10.0.0.0/8 across the board than prefix in a given namespace). So I guess it should be prefix -> namespace.
ref: https://docs.infrahub.app/schema/nodes-and-attributes#optimizing-uniqueness-constraints
Contributor
There was a problem hiding this comment.
Tho perhaps a good candidate for the infrahub skill (if not already the case)
Contributor
|
Nice one @lancamat1 ! Couple of nitpicking issues but overall I think it's a nice step forward 🙌 |
BaptisteGi
reviewed
Sep 2, 2026
…eview suggestion) Review threads: base/dcim.yml:169-170 and base/ipam.yml:53-54 (BaptisteGi). The note claimed Infrahub 1.11 treats an attribute with a default_value as optional at runtime, which is not documented in any release note, and it sat at the top of the attributes block rather than on the attribute it described. The explicit `optional: false` lines it was meant to justify stay as they are (CC-6). Both occurrences in the repository are removed; a grep for the same claim finds no others. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tion) Review threads: base/ipam.yml:51, base/ipam.yml:123 and extensions/cross_connect/cross_connect.yml:26 (BaptisteGi), citing https://docs.infrahub.app/schema/nodes-and-attributes#optimizing-uniqueness-constraints "Order uniqueness constraint elements from most selective (returning fewer matches) to least selective (returning more matches) to optimize database query performance." A prefix value, an IP address and a provider circuit identifier each return far fewer matches than the namespace or the provider they hang off, so they come first. This reverts the ordering criterion used in fc52a80, which ordered the declared constraint to match the namespace-first human_friendly_id instead. Consequence, kept deliberately: because the declared order no longer matches the HFID, Infrahub adds its HFID-derived constraint on top, so the live schema carries two functionally identical constraints on IpamPrefix, IpamIPAddress and DcimCrossConnect. The HFIDs themselves are unchanged (IPAM-07). Packets: IPAM-07, base_ipam consistency note 3. Checklist: CC-4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s (generalizing review suggestion) Generalizes the review thread on base/ipam.yml:51 (BaptisteGi) to every uniqueness_constraints entry in base/ and extensions/: 24 entries reviewed, 1 more changed. IpamCVLAN was flipped to svlan-first in b26063a under the same rejected criterion (match the human_friendly_id). QinQ has few S-VLANs each encapsulating many C-VLANs, so a C-VLAN id returns fewer matches than an S-VLAN does, and value-first also matches its siblings IpamVLAN and IpamSVLAN, which both read [vlan_id__value, vlan_group]. The remaining parent-first entries stay as they are: they scope a child to a parent that is itself numerous (interfaces per device, ports per patch panel, endpoints per circuit, disks per device), where the parent is the more selective side and the child name repeats across parents. Packets: QINQ-02, base_ipam consistency note 3. Checklist: CC-4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ew suggestion) Review thread: extensions/circuit/circuit.yml:106 (BaptisteGi), "In some places we have `>-` for long YAML string, sometime we don't". Rule adopted, which is the one the repository already follows almost everywhere and the one yamllint can back: a description stays on a single line while it fits inside the 120-character limit from .yamllint.yml, and uses the folded block scalar `>-` when it does not. Swept all 44 schema files in base/ and extensions/ plus objects/ and the .metadata.yml files: 496 descriptions, 492 single-line (longest 119 characters, none over the limit), 4 wrapped. The file Baptiste commented on is already correct, and two deviants needed fixing: - base/ipam.yml:17 wrapped as a plain multi-line scalar (implicit folding) instead of >-. - extensions/dwdm/dwdm.yml:122 used the literal block `|`, which kept a newline inside a prose description that has no meaningful line breaks. Checklist: CC-8. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…view suggestion) Review thread: extensions/cable/cable.yml:221-225 (BaptisteGi), "I think forcing a label on every cable is quite optimistic, especially if it has to be entered manually by someone. I agree on the other hand not having a HFID is quite annoying when playing with the API". 26280ad made the label mandatory to back the new human_friendly_id. The mandatory half is not needed: the HFID validator requires the attribute to be unique, not mandatory (only relationship components of an HFID must be mandatory, per docs.infrahub.app/schema/nodes-and-attributes#human-friendly-identifier-hfid). So the label goes back to optional, keeps unique: true, and the HFID stays. A cable with no label simply has no HFID until someone labels it. Packets: CABLE-02. Checklist: CC-4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ir own node (review suggestion) Review thread: base/ipam.yml:89-92 (BaptisteGi), suggestion block deleting the choice, "As discussed we now have a dedicated aggregate extension". extensions/aggregate models an RIR-assigned top-level prefix as IpamAggregate, so representing the same thing as a role on a generic prefix is a second way to say it. No objects file uses role: supernet, so no fixture changes. Swept the other four choice lists in base/ (device status and role, interface status and role, prefix and address status, address role): no other choice value duplicates a node kind shipped by an extension. Packets: IPAM-04 (prefix roles), ext_ipam digest Q2. Checklist: CC-5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Follows the review fixes in this branch: `uv run invoke docs.generate`, with the three hand-edited extension READMEs (rack, location_minimal, location_site) restored afterwards because the generator overwrites them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet MOD-07 flagged that v2's DcimGenericModule lost the status the v1 extension carried, and that with module_bay mandatory there is no way to record a module that has failed other than deleting the object. Baptiste's answer: "Indeed, could be nice having a status field". Restores status on DcimGenericModule, following the conventions: - kind Dropdown, optional written explicitly, mandatory with default_value active (CC-6) - choices snake_case with a label, a description and a color; colors are base-palette values, taken verbatim from DcimDevice.status in base/dcim.yml where the state matches (provisioning #f0ad4e, active #00d25b, maintenance #ff9800) and from the same palette for the two new states (#e04040, #6c757d) (CC-5) - order_weight 1200, the base scheme's slot for status (CC-9) Choice set, against the v1 baseline (extensions/modules/modules.yml on main: provisioning, active, maintenance, disabled, outage): the first three are kept as-is. v1's disabled and outage are replaced by failed and rma, which is the case the packet was actually about: a module that is still recorded against its last known bay while it is out of service or away for replacement. deprecated is deliberately not included, as end-of-life belongs to the module type rather than to an installed instance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Packet USER-01 asked what UserAccount is for, given that Infrahub already ships builtin accounts and groups: the extension models device login credentials (password, ssh_key, mfa_enabled, a permissions dropdown) under a name that reads like Infrahub's own user management, and nothing in the library consumes it. Baptiste's answer: "I agree it's confusing, perhaps we could just remove it and provide a new implementation when someone face it on the field (would need to try if reusing builtin account/group is doable/wanted)". Scope executed: the whole extensions/users extension, not UserAccount alone. UserGroup exists only to group accounts (its idle_timeout and permissions describe an account's session on a device), and the OrganizationGeneric.accounts extension block has UserAccount as its peer, so neither has a purpose once the account model is gone. Removing UserAccount alone would leave a group node grouping nothing and a dangling extension block. Nothing else references the two kinds: base/, extensions/ and experimental/ are clean, so there is no cross-extension breakage and no experimental extension left pointing at a missing peer. Removed: - extensions/users/users.yml and its generated README - the extensions/users entry in .metadata.yml - objects/extensions/users/users.yml (its mock data) The generated docs page and home page listing are regenerated separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Runs `invoke docs.generate` for the two schema changes in this series: - device_module.mdx picks up the new DcimGenericModule.status attribute - home.mdx drops the Users row, following the removal of the extensions/users entry from .metadata.yml The generator never prunes, so docs/docs/reference/users.mdx is deleted by hand. The hand-written notes in the rack, location_minimal and location_site READMEs are restored: docs.generate overwrites every extension README with the three-line pointer template, and those three carry exclusivity notes that CC-1 requires. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…data Fallout from removing extensions/users, caught by a full object load: objects/extensions/users/users.yml was the only file creating the OrganizationProvider "Acme Networks", as a side effect of upserting the organization its three accounts pointed at. objects/extensions/routing_bgp/routing_bgp.yml uses that record for AS 65000, our own backbone AS, and its header comment already documents it as coming from base. Without it the load fails with "Unable to find the node Acme Networks / OrganizationGeneric in the database", and objects/extensions/peering_ixp fails after it because it needs the AS that never got created. Adds the record to objects/base/01_organization.yml, where routing_bgp already says it lives. OrganizationProvider is the kind extensions/users used and the only concrete OrganizationGeneric kind base ships, so base mock data cannot express it any other way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Review output for #75. I went through the v2 diff decision by decision (142 decisions extracted from the net diff, each judged against a checklist agreed before judging) and this branch carries every "change" verdict as one commit with the reasoning in the commit message. 55 commits.
Key changes
Review context
Blind pass: rationale was taken only from the PR body, commit messages and YAML comments, not from existing review threads. The 76 accept and 11 question verdicts follow as inline threads on #75.
Test plan
uv run invoke schemas.load-all-schemason a fresh 1.11.0: zero errors, location_site skipped as mutually exclusive with location_minimalTEST_EXPERIMENTAL=1pass: everything loads except experimental/security, which still carries the Infra* kinds already fixed on main in d5ae01b, so it resolves once main is merged back🤖 Generated with Claude Code