|
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. |
4 | 43 |
|
5 | 44 | ## Core Principles |
6 | 45 |
|
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. |
8 | 79 |
|
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 |
12 | 81 |
|
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()`). |
14 | 88 |
|
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`. |
18 | 93 |
|
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. |
20 | 96 |
|
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 |
24 | 98 |
|
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. |
26 | 102 |
|
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. |
30 | 108 |
|
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. |
32 | 111 |
|
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 |
36 | 113 |
|
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. |
38 | 121 |
|
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. |
40 | 124 |
|
41 | | -[SECTION_2_CONTENT] |
42 | | -<!-- Example: Technology stack requirements, compliance standards, deployment policies, etc. --> |
| 125 | +### VI. Format & Lint Before Commit |
43 | 126 |
|
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). |
45 | 132 |
|
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`. |
47 | 135 |
|
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). |
50 | 177 |
|
51 | 178 | ## Governance |
52 | 179 |
|
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: |
54 | 192 |
|
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. |
57 | 196 |
|
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