Skip to content

Commit d7f8d12

Browse files
authored
Merge pull request #1163 from opsmill/dga/feat-constitution-tzh0i
docs: ratify project constitution v1.0.0 (#1144)
2 parents e0b67a2 + 90e2327 commit d7f8d12

1 file changed

Lines changed: 174 additions & 36 deletions

File tree

dev/constitution.md

Lines changed: 174 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,197 @@
1-
# [PROJECT_NAME] Constitution
2-
3-
<!-- Example: Spec Constitution, TaskFlow Constitution, etc. -->
1+
<!--
2+
Sync Impact Report
3+
==================
4+
Version change: (template placeholder) → 1.0.0
5+
Rationale: Initial ratification. The file previously contained only unfilled
6+
template tokens ([PRINCIPLE_1_NAME], [GOVERNANCE_RULES], ...); this is the first
7+
concrete constitution, so it starts at 1.0.0 (MINOR/PATCH bumps do not apply to a
8+
first ratification).
9+
10+
Principles defined (all new):
11+
- I. Async/Sync Dual API Parity
12+
- II. Backward Compatibility & Public API Stability
13+
- III. Layered Architecture
14+
- IV. Type Safety & Typed Errors
15+
- V. Test-First Development
16+
- VI. Format & Lint Before Commit
17+
- VII. Documentation Accuracy (NON-NEGOTIABLE)
18+
19+
Sections added:
20+
- Core Principles (7 articles)
21+
- Additional Constraints (tech stack + boundaries)
22+
- Development Workflow & Quality Gates
23+
- Governance
24+
25+
Templates / artifacts reviewed for consistency:
26+
- .specify/templates/plan-template.md ...... ✅ no change needed
27+
(Constitution Check gate uses generic "[Gates determined based on
28+
constitution file]"; it reads this file at plan time, no hardcoded
29+
principle list to update)
30+
- .specify/templates/spec-template.md ...... ✅ no change needed (no constitution refs)
31+
- .specify/templates/tasks-template.md ..... ✅ no change needed (no constitution refs)
32+
- AGENTS.md / dev/knowledge/* .............. ✅ referenced as operational how-to; not duplicated
33+
34+
Deferred TODOs: none.
35+
-->
36+
37+
# Infrahub Python SDK Constitution
38+
39+
The Infrahub Python SDK is a foundational library that abstracts the Infrahub API so
40+
developers work with infrastructure data as native Python objects. It is consumed by
41+
`infrahubctl`, the Infrahub Ansible collection, and external Python applications. Because
42+
so much depends on it, the principles below are non-negotiable defaults, not aspirations.
443

544
## Core Principles
645

7-
### [PRINCIPLE_1_NAME]
46+
### I. Async/Sync Dual API Parity
47+
48+
Every public feature MUST ship on both `InfrahubClient` (async) and `InfrahubClientSync`
49+
(sync) with matching method names, signatures, and behavior. The only sanctioned
50+
asymmetry is functionality that is physically meaningless in one mode (e.g. a
51+
concurrency/streaming helper with no coherent sync analog); such an exception MUST be
52+
justified in the pull request and documented in the method's docstring. Internal helpers
53+
(leading-underscore, not exported) are exempt.
54+
55+
Enforcement: surface parity is asserted by `tests/unit/sdk/test_client.py`
56+
(`test_validate_method_signature`, `test_method_count`). New behavior MUST be exercised
57+
by tests on **both** the async and sync path.
58+
59+
Rationale: the dual client is the SDK's signature contract. Divergence silently breaks
60+
sync consumers and is the single most likely regression when new surface is added.
61+
62+
### II. Backward Compatibility & Public API Stability
63+
64+
The SDK makes a tiered stability promise:
65+
66+
- **Guaranteed**: names exported from `infrahub_sdk/__init__.py` (`__all__`), the public
67+
(non-underscore) methods of those classes, and documented `Config` fields.
68+
- **Not guaranteed**: underscore-prefixed names and modules reached by deep imports that
69+
are not re-exported at top level. These may change in a MINOR release.
70+
71+
Breaking or removing a *guaranteed* surface MUST follow a deprecation path: first ship a
72+
release that emits `DeprecationWarning` naming the replacement, keep the deprecated path
73+
working for **at least one MINOR release**, and remove it only in a **MAJOR** release.
74+
Breaking changes without this path require explicit maintainer approval (see the
75+
`AGENTS.md` "ask first" gate on changing public API signatures).
76+
77+
Rationale: `infrahubctl`, the Ansible collection, and external applications pin and import
78+
this library; an unannounced break is a break for all of them at once.
879

9-
<!-- Example: I. Library-First -->
10-
[PRINCIPLE_1_DESCRIPTION]
11-
<!-- Example: Every feature starts as a standalone library; Libraries must be self-contained, independently testable, documented; Clear purpose required - no organizational-only libraries -->
80+
### III. Layered Architecture
1281

13-
### [PRINCIPLE_2_NAME]
82+
Reusable logic — API interaction, data transformation, and domain rules — MUST live in
83+
the SDK (`infrahub_sdk/`). Every first-party consumer (the CLI, the Ansible collection,
84+
external apps) is a thin layer over it. Concretely, the CLI (`infrahub_sdk/ctl/`) is
85+
limited to: argument parsing and local-only input validation, calls into SDK methods,
86+
presentation via Rich, and error-to-exit-code handling via `@catch_exception`. It MUST NOT
87+
use plain `print()` or instantiate `InfrahubClient` directly (use `initialize_client()`).
1488

15-
<!-- Example: II. CLI Interface -->
16-
[PRINCIPLE_2_DESCRIPTION]
17-
<!-- Example: Every library exposes functionality via CLI; Text in/out protocol: stdin/args → stdout, errors → stderr; Support JSON + human-readable formats -->
89+
Test for a violation: *"Would a non-CLI consumer have to duplicate this logic to get the
90+
same behavior?"* If yes, it belongs in the SDK, not the CLI. Detailed CLI rules — including
91+
"don't pre-validate what the server validates" — live in
92+
`dev/knowledge/cli-design-principles.md` and `dev/knowledge/cli-architecture.md`.
1893

19-
### [PRINCIPLE_3_NAME]
94+
Rationale: logic stranded in the CLI is invisible to every other consumer and drifts from
95+
the server it duplicates.
2096

21-
<!-- Example: III. Test-First (NON-NEGOTIABLE) -->
22-
[PRINCIPLE_3_DESCRIPTION]
23-
<!-- Example: TDD mandatory: Tests written → User approved → Tests fail → Then implement; Red-Green-Refactor cycle strictly enforced -->
97+
### IV. Type Safety & Typed Errors
2498

25-
### [PRINCIPLE_4_NAME]
99+
All function signatures MUST carry type hints; both `ty` and `mypy` MUST pass clean.
100+
Pydantic v2 models are used at configuration, API, and data boundaries. A type-check
101+
suppression (`# type: ignore`, override) requires an inline justification.
26102

27-
<!-- Example: IV. Integration Testing -->
28-
[PRINCIPLE_4_DESCRIPTION]
29-
<!-- Example: Focus areas requiring integration tests: New library contract tests, Contract changes, Inter-service communication, Shared schemas -->
103+
Errors that a consumer could reasonably catch MUST be raised from the
104+
`infrahub_sdk.exceptions` hierarchy (rooted at `Error`), using a **specific** subclass; a
105+
new failure mode gets a new subclass rather than a bare `Exception`, `RuntimeError`, or a
106+
generic reuse. Standard-library exceptions (`ValueError`, `TypeError`) are acceptable only
107+
for local/programming errors a consumer would never be expected to catch.
30108

31-
### [PRINCIPLE_5_NAME]
109+
Rationale: precise types and a typed exception hierarchy are the contract that lets
110+
consumers handle failures deterministically instead of string-matching messages.
32111

33-
<!-- Example: V. Observability, VI. Versioning & Breaking Changes, VII. Simplicity -->
34-
[PRINCIPLE_5_DESCRIPTION]
35-
<!-- Example: Text I/O ensures debuggability; Structured logging required; Or: MAJOR.MINOR.BUILD format; Or: Start simple, YAGNI principles -->
112+
### V. Test-First Development
36113

37-
## [SECTION_2_NAME]
114+
Every feature and bug fix MUST ship with tests in the same change; tests are never
115+
deferred to a follow-up. A bug fix MUST include a test that reproduces the bug (fails
116+
before the fix, passes after). New public surface MUST test both the async and sync paths
117+
(see Principle I). Unit tests MUST be fast, mocked, and free of external dependencies;
118+
behavior that needs a real server belongs in integration tests (testcontainers). Tests
119+
MUST assert concrete expected values, not mere truthiness or non-null — an assertion that
120+
cannot fail for the right reason is not evidence.
38121

39-
<!-- Example: Additional Constraints, Security Requirements, Performance Standards, etc. -->
122+
Rationale: tests written with the change, and reproduction tests for bugs, are the only
123+
evidence a reviewer or agent can check that behavior is actually pinned.
40124

41-
[SECTION_2_CONTENT]
42-
<!-- Example: Technology stack requirements, compliance standards, deployment policies, etc. -->
125+
### VI. Format & Lint Before Commit
43126

44-
## [SECTION_3_NAME]
127+
Python code and documentation MUST pass the project's format and lint pipeline before
128+
being committed — `uv run invoke format lint-code` for code and `uv run invoke lint-docs`
129+
for documentation — and CI enforces the same pipeline. Nothing merges while these checks
130+
are red, and linters or type-checkers MUST NOT be silenced without an inline
131+
justification (see Principle IV).
45132

46-
<!-- Example: Development Workflow, Review Process, Quality Gates, etc. -->
133+
The concrete tool list is intentionally kept out of this document (it changes over time);
134+
it is defined by the invoke tasks and `AGENTS.md`.
47135

48-
[SECTION_3_CONTENT]
49-
<!-- Example: Code review requirements, testing gates, deployment approval process, etc. -->
136+
Rationale: a green, uniformly formatted baseline keeps diffs about behavior, not style,
137+
and keeps the review signal trustworthy.
138+
139+
### VII. Documentation Accuracy (NON-NEGOTIABLE)
140+
141+
Documentation MUST describe behavior that actually exists.
142+
143+
- **Generated docs**: whenever CLI commands, SDK configuration, or Python docstrings
144+
change, `uv run invoke docs-generate` MUST be run and the result committed;
145+
`docs-validate` MUST pass in CI. Generated artifacts — `protocols.py` and generated
146+
docs — are NEVER hand-edited.
147+
- **Hand-written docs & examples**: MUST describe only behavior that exists (no
148+
aspirational or planned behavior presented as working), and MUST be updated in the
149+
**same pull request** as the behavior change they document.
150+
151+
Rationale: this is a public SDK for network automation engineers; an inaccurate example
152+
does not merely confuse — it ships broken automation downstream. This principle is
153+
non-negotiable and is not waived for schedule pressure.
154+
155+
## Additional Constraints
156+
157+
- **Tech stack**: Python 3.10–3.13, UV for dependency management, pydantic >= 2.0, httpx,
158+
graphql-core. Adding a new runtime dependency is an "ask first" decision (`AGENTS.md`).
159+
- **Generated code**: `protocols.py` and generated documentation are produced by tooling
160+
and MUST NOT be edited by hand (reinforces Principles III and VII).
161+
- **Boundaries of record**: `AGENTS.md` and the subdirectory guides
162+
(`infrahub_sdk/ctl/AGENTS.md`, `infrahub_sdk/pytest_plugin/AGENTS.md`,
163+
`tests/AGENTS.md`) hold the operational Always/Ask-first/Never lists that implement
164+
these principles.
165+
166+
## Development Workflow & Quality Gates
167+
168+
- Run `uv run invoke format lint-code` before committing Python code (Principle VI).
169+
- Run `uv run invoke docs-generate` after creating, modifying, or deleting CLI commands,
170+
SDK config, or Python docstrings; run `uv run invoke lint-docs` before committing
171+
markdown (Principle VII).
172+
- New features follow the async/sync dual pattern and ship with both-path tests
173+
(Principles I, V).
174+
- Pull requests — whether authored by a human or a Spec Kit agent — MUST be reviewable
175+
against each principle above. A deviation MUST be called out and justified in the PR
176+
description (and, for complexity, in the plan's Complexity Tracking table).
50177

51178
## Governance
52179

53-
<!-- Example: Constitution supersedes all other practices; Amendments require documentation, approval, migration plan -->
180+
This constitution supersedes conflicting practices. Where it and `AGENTS.md` /
181+
`dev/knowledge/*` disagree, this constitution wins; otherwise the constitution holds the
182+
non-negotiable principles and those documents hold the operational how-to — they
183+
cross-reference rather than duplicate.
184+
185+
**Compliance**: every pull request and review (including automated Spec Kit reviews of
186+
specs, plans, and implementations) verifies compliance with these principles. Unjustified
187+
deviations block merge.
188+
189+
**Amendments**: changes require a written proposal, maintainer approval, and an adoption
190+
note describing any migration impact. The constitution is versioned with semantic
191+
versioning:
54192

55-
[GOVERNANCE_RULES]
56-
<!-- Example: All PRs/reviews must verify compliance; Complexity must be justified; Use [GUIDANCE_FILE] for runtime development guidance -->
193+
- **MAJOR**: a principle is removed or redefined in a backward-incompatible way.
194+
- **MINOR**: a new principle or section is added, or guidance is materially expanded.
195+
- **PATCH**: clarifications, wording, and non-semantic refinements.
57196

58-
**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
59-
<!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 -->
197+
**Version**: 1.0.0 | **Ratified**: 2026-07-10 | **Last Amended**: 2026-07-10

0 commit comments

Comments
 (0)