DOCS CANONICAL · GENERATED FROM REPO RELEASE v0.2.0-beta.1 SOURCE github.com/cubby-network/platform

Contributing

Project · Source: CONTRIBUTING.md

Contributing to cubby.network

Thanks for your interest in contributing. This project is an open-source, deterministic NetOps control plane with an LLM-driven agent layer on top. Because the harness eventually controls real production networks, code quality and safety bars are high. This document explains how to work in the repo and what we expect from a pull request.

TL;DR

  1. Fork the repo and create a feature branch off main.
  2. Run pytest locally — the suite must be green before you push.
  3. Add tests for everything you change. Adapters and validators must ship with golden fixture coverage.
  4. Open a PR that explains the why, not just the what. Link any related issue or architecture decision record (ADR).
  5. Sign off every commit with a DCO line (git commit -s).

Community operations

Start with COMMUNITY.md if you are not sure where a question belongs. Beta support expectations live in SUPPORT.md, issue routing lives in docs/community/TRIAGE.md, and the path from contributor to maintainer lives in docs/community/CONTRIBUTOR_LADDER.md.

Discord is for coordination and quick help once the invite is published. GitHub remains the source of truth for decisions, reproducible issues, accepted designs, PR review, and release history.

Branching, versioning, and releases

We use GitHub Flow with semantic-version tags:

  • main is always deployable. CI gates every merge; nothing lands on main that isn't green.
  • Feature branches are short-lived and topic-named: feat/<what>, fix/<what>, docs/<what>, chore/<what>. Rebase against main before opening the PR so the history stays linear.
  • One logical change per PR. Tests ride with the change. Infrastructure churn (formatting, imports) lands in its own PR so reviewers can see the real diff.
  • Versioning is SemVer. Pre-1.0, breaking changes can land in 0.MAJOR.MINOR bumps. At 1.0 the HTTP + CLI surface freezes and subsequent breaks require a major.
  • Pre-release chain for beta:
    • v0.x.y-alpha.N — internal / friendly testers
    • v0.x.y-beta.N — external beta
    • v0.x.y-rc.N — release candidate
    • v0.x.y — stable milestone
  • Releases are cut from tags on main. See docs/RELEASE.md.
  • Hotfixes branch from the tag being patched → PR to main → tag a patch release → cherry-pick forward as needed.

Developer Certificate of Origin (DCO)

By contributing you certify that your contribution is made under the terms of the Developer Certificate of Origin 1.1. Sign off every commit:

git commit -s -m "feat: …"

This appends Signed-off-by: Your Name <[email protected]> to the commit message. The DCO bot on GitHub checks every PR.

Project layout

PathWhat lives there
apps/api, apps/cli, apps/worker, apps/schedulerProcess entry points.
packages/domainTyped entities, enums, dataclasses. The single source of truth for shapes.
packages/sdkPlugin contracts (ABCs), the registry, and SDK errors. Every plugin must inherit from the matching ABC and set simulated.
packages/orchestratorWorkflow state machine, evidence service, signing.
packages/policy, packages/validation, packages/execution, packages/verificationThe deterministic core.
packages/stateAuthoritative intended/observed state, topology graph store.
packages/discoveryBoot-crawl: BFS LLDP/CDP/SNMP probing.
packages/transportOne interface for SSH/NETCONF/RESTCONF/gNMI/SNMP/REST. Adapters compose transports.
packages/knowledgeDocument store, embeddings, retrieval.
packages/agentsLLM-backed reasoning workers + safety gate. Agents are read-only.
packages/autonomyDigital twin, forecasting, multi-agent coordinator, self-improvement.
plugins/device, plugins/auth, plugins/inventory, plugins/telemetry, plugins/ticketing, plugins/validators, plugins/knowledge_ingestors, plugins/workflow_packsConcrete adapters and packs. Add new vendors here.
tests/unit, tests/contract, tests/integrationTest tiers. Unit tests must run offline.

Hard rules

These are non-negotiable. PRs violating them will be sent back without deeper review.

  • Adapters declare simulated. Every concrete plugin sets simulated to True (in-memory or canned) or False (talks to real systems). The registry refuses to register a plugin without it.
  • Real adapters use the transport layer. Do not hand-roll SSH inside an adapter. Compose a Transport from packages.transport.
  • Agents are read-only. Tools must inherit from packages.agents.tools.Tool and have read_only = True. The runtime's safety gate enforces both allowed_tools membership and read_only at every call.
  • Evidence is signed and chained. Do not bypass EvidenceService. Bundles get a SHA-256, a signature (HMAC by default, Ed25519 in prod), and a prev_sha256 link.
  • API routes require a validated principal. Do not trust requested_by or actor_roles from the request body — stamp_principal overrides them.
  • No new dependencies without an extras group. Heavy libraries (scrapli, ncclient, pgvector, sentence-transformers, hvac, anthropic) live behind optional extras so the base install stays small.
  • Lock file stays in sync with pyproject.toml. Any edit to [project.dependencies] or [project.optional-dependencies] MUST be paired with a regenerated requirements.txt in the same commit. Run ./infra/refresh-lock.sh to rebuild it (uses the same Python 3.11 base image the published Docker artifact runs on, so the lock matches what operators install). The lock-drift CI job fails the build on any divergence.
  • Tests must be deterministic. No real network, no real LLM calls, unless gated by an environment variable (NETOPS_DEVICELAB=1, NETOPS_LLM=1, NETOPS_VAULT=1, NETOPS_PGVECTOR=1).

Adding a new device adapter

  1. Create plugins/device/<vendor>_<os>/. Inherit from packages.sdk.contracts.DeviceAdapter and set:
    • name, vendor, os_family, transports,
    • simulated = False (or True if you can't reach a real device yet),
    • capabilities (frozenset of snapshot, render, precheck, execute, rollback, verify).
  2. Compose a Transport from packages.transport. Don't import scrapli directly — request the right transport from the registry.
  3. Write parsers in plugins/device/<vendor>_<os>/parsers.py. Pure functions, no I/O. Cover them with golden tests under tests/unit/plugins/device/<vendor>_<os>/test_parsers.py using captured real device output stored in tests/fixtures/<vendor>/<os>/.
  4. Wire the adapter into packages/orchestrator/bootstrap.py so the registry knows about it.
  5. Add an integration test under tests/integration/plugins/device/... marked @pytest.mark.devicelab. It must skip cleanly when NETOPS_DEVICELAB is not set.

Adding a new validator

  1. Create plugins/validators/<name>/plugin.py. Inherit from ValidatorPlugin and implement supports() and validate(). Return a dict with findings, risk_score, blast_radius.
  2. If your validator is a wrapper around an external system (Batfish, lab emulator), set simulated accurately and put the heavy code path behind an extras dependency.
  3. Add unit tests under tests/unit/plugins/validators/<name>/.

Adding a workflow pack

Workflow packs are pure metadata. Create plugins/workflow_packs/<name>/pack.py with the required attributes from WorkflowPack. The orchestrator picks up new packs automatically once they are registered in bootstrap.build_demo_harness.

Tests and CI

  • Unit tests (tests/unit/) run offline, fast, deterministic.
  • Contract tests (tests/contract/) verify ABC compliance and cross-implementation behavior (e.g., topology stores).
  • Integration tests (tests/integration/) may touch the network or external systems. They are gated by pytest -m markers and environment variables.

Run pytest -q before pushing. CI runs the same suite plus linters.

Style

  • Format with black and lint with ruff. The pre-commit hook runs both.
  • Type hints are encouraged but not strictly enforced. New code in packages/sdk and packages/transport must be fully typed.
  • Docstrings follow the same style you see in packages/sdk/contracts.py: module-level prose explaining the why, then concise per-symbol notes.

Commit messages

Use imperative present tense ("Add NX-OS adapter", not "Added"). Keep the subject under 72 characters. The body should explain why the change matters.

Reporting security issues

Do not open a public GitHub issue. See SECURITY.md.

Code of conduct

Be kind, be professional, assume good faith. The full text is in CODE_OF_CONDUCT.md.