From 35b802fc33bae6a679eeabdface6c33ce0cfc23b Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Wed, 1 Jul 2026 17:24:52 +0000 Subject: [PATCH 1/2] docs(contributing): add contributor guide and PR template Write down the conventions that previously lived only in git history and RELEASE.md, after an external PR tripped over three of them (the Conventional-Commit title format, ADR numbering, and the no-AI-attribution rule). CONTRIBUTING.md covers the offline/deterministic/keyless core invariant (WF-ADR-0001), dev setup, the ruff/mypy/pytest gate (including the `python -m` interpreter gotcha that silently skips the gateway/ui/tui tests), the commit/PR conventions, the ADR/design/roadmap numbering rule (take the next free number), and a pre-PR checklist. `.github/pull_request_template.md` pre-fills new PRs with the same summary/scope/verification/checklist so the rules arrive up front. Docs only; no code or behaviour change. --- .github/pull_request_template.md | 22 ++++++++ CONTRIBUTING.md | 91 ++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 .github/pull_request_template.md create mode 100644 CONTRIBUTING.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..8dcc52dc --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,22 @@ + + +## Summary + + + +## Scope + + + +## Verification + +- [ ] `ruff check .` +- [ ] `python -m mypy wayfinder_router` +- [ ] `python -m pytest -q` — full suite, run via `python -m` (a bare `pytest` skips the gateway/ui/tui tests) + +## Checklist + +- [ ] Conventional, single-scope title (`type(scope): imperative summary`) with a descriptive body +- [ ] No AI attribution in the commits or this PR +- [ ] Behaviour change → an ADR/design/roadmap doc added with the **next free** number, plus a `CHANGELOG.md` `## Unreleased` entry +- [ ] The scored decision path stays offline, deterministic, and keyless (WF-ADR-0001) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..9c6bc53e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,91 @@ +# Contributing to Wayfinder + +Thanks for helping out. Wayfinder is a small, deterministic tool, and a few conventions keep it that +way. Everything you need is on this page; none of it is onerous. + +## The one invariant + +Wayfinder's **scored decision path is offline, deterministic, and keyless** — it never calls a model, +touches the network, or reads a credential to make a routing decision, and the same prompt always scores +the same (WF-ADR-0001). The core is stdlib-only and imports no web or SDK code; `import rac` must fail. +Anything that needs the network or a key lives in the optional `gateway` / `ui` layers and is imported +lazily. Keep new work on the right side of that line: if it touches the scored decision, it stays pure. + +Python **3.11+**, Apache-2.0. + +## Getting set up + +```bash +pip install -e ".[dev]" +``` + +`[dev]` pulls the test runner plus `fastapi` / `httpx` (which exercise the gateway and UI); `rich` and +`textual` are core dependencies, so this one install runs the whole suite. + +## The gate — run before every push + +```bash +ruff check . +python -m mypy wayfinder_router +python -m pytest -q # or: make test +``` + +All three must be clean, and `pytest` should collect the **full** suite (600+ tests). If you see only a +few hundred, read the next paragraph. + +> **Run pytest and mypy as `python -m …`, not bare `pytest` / `mypy`.** A `pytest` installed on a +> different interpreter won't see `fastapi` / `textual`, so the gateway/ui/tui tests quietly +> `importorskip` and disappear (and mypy prints spurious `textual.*` "missing stub" errors). That's the +> wrong interpreter, not a real failure — `python -m` uses the one where you installed the package. + +## Commits and pull requests + +Commit subjects follow **Conventional Commits**: + +``` +type(scope): imperative summary +``` + +- **type** — one of `feat`, `fix`, `docs`, `chore`, `test`. +- **scope** — required, single, lowercase: the area you touched (`gateway`, `cli`, `tui`, `ui`, + `adapter`, `pricing`, `service`, `calibrate`, `suite`, `release`, …). One scope, not `fix(ui,cli)`. +- **summary** — imperative mood, lowercase after the colon, no trailing period. + +Every commit needs a **descriptive body** — what changed and why, not a restatement of the subject. + +Reference decisions from the body: bracket trailers `[roadmap:WF-ROADMAP-XXXX]`, `[design:WF-DESIGN-XXXX]`, +`[release:X]`, and ADRs inline in prose as `(WF-ADR-XXXX)`. + +**No AI attribution** anywhere in commits or PRs — no "Generated with …" footers, no `Co-Authored-By:` bot +trailers, no session links. Use whatever tools you like; just don't sign the bot into the history. + +PRs are **squash-merged**, and a maintainer writes the final squash subject (with its `(#NN)`). So your PR +title should already be a clean conventional subject, and the description should explain the change. + +## Decision records + +Anything that changes behaviour gets a short decision doc alongside the code: + +| Kind | Directory | Filename | +|---|---|---| +| Architecture decision | `decisions/` | `WF-ADR-NNNN-slug.md` | +| Design note | `designs/` | `WF-DESIGN-NNNN-slug.md` | +| Roadmap | `roadmaps/` | `WF-ROADMAP-NNNN-slug.md` | + +**Numbers are unique and monotonic — take the next free one, never reuse an existing number.** ADRs are at +`0039`, so the next is `0040`. And add a `## Unreleased` entry to [`CHANGELOG.md`](CHANGELOG.md) for +anything users would notice. + +## Releases + +Releases are cut by maintainers — see [`RELEASE.md`](RELEASE.md). Don't bump `__version__` in a feature +PR; the release commit is the only place it changes. + +## Before you open a PR + +- [ ] Conventional, single-scope title; descriptive body. +- [ ] `ruff check .`, `python -m mypy wayfinder_router`, `python -m pytest -q` all green. +- [ ] No AI attribution in commits or the PR. +- [ ] Behaviour change → an ADR/design/roadmap doc with the next free number, and a `CHANGELOG.md` + `## Unreleased` entry. +- [ ] The scored decision path stays offline, deterministic, and keyless (WF-ADR-0001). From 95d5b1e9dc83b7f34d37ee1d11dc563adb200383 Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Wed, 1 Jul 2026 17:37:08 +0000 Subject: [PATCH 2/2] docs(community): add issue templates, security policy, code of conduct, and CODEOWNERS Complete the contributor scaffolding alongside CONTRIBUTING.md and the PR template: - .github/ISSUE_TEMPLATE/: bug-report and feature-request YAML forms (with a surface dropdown and a "does this touch the scored decision path?" gate) plus a config.yml that disables blank issues and links the docs and private security reporting. - SECURITY.md: private vulnerability reporting via GitHub advisories, supported version, and the keys-never-stored / offline-decision posture (WF-ADR-0001/0004). - CODE_OF_CONDUCT.md: Contributor Covenant 2.1. - .github/CODEOWNERS: default owner for the repo. Docs/config only; no code or behaviour change. --- .github/CODEOWNERS | 3 + .github/ISSUE_TEMPLATE/bug_report.yml | 57 +++++++++ .github/ISSUE_TEMPLATE/config.yml | 8 ++ .github/ISSUE_TEMPLATE/feature_request.yml | 32 +++++ CODE_OF_CONDUCT.md | 132 +++++++++++++++++++++ SECURITY.md | 29 +++++ 6 files changed, 261 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 SECURITY.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..7abc11be --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# Default owner for the whole repository. +# Takes effect on PRs when "Require review from Code Owners" is enabled in branch protection. +* @tcballard diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..4de153e4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,57 @@ +name: Bug report +description: Report a problem with Wayfinder +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for the report. One thing to check first: the **scored decision path is offline and + deterministic** — routing never depends on the network or a key — so if the problem is a provider + call failing, it's most likely in the gateway/delivery layer rather than the decision itself. + - type: dropdown + id: surface + attributes: + label: Which surface? + options: + - Scored core (route / library / calibrate) + - Gateway (serve / proxy) + - Terminal chat (TUI) + - Local UI (ui) + - Other / not sure + validations: + required: true + - type: input + id: version + attributes: + label: Version + description: From `pip show wayfinder-router`, or the release tag. + placeholder: "2026.7.0" + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: What happened? + description: What did you expect, and what happened instead? + validations: + required: true + - type: textarea + id: repro + attributes: + label: Steps to reproduce + description: Minimal steps plus the smallest prompt/config that triggers it. Redact any keys. + render: shell + validations: + required: true + - type: input + id: env + attributes: + label: Environment + description: OS, Python version, and install extras (gateway / ui / …). + placeholder: "macOS 15, Python 3.11, [gateway]" + - type: checkboxes + id: confirm + attributes: + label: Before submitting + options: + - label: I'm on the latest version, and this isn't a provider key/network problem outside the offline scored path. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..2cdef9af --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Documentation + url: https://github.com/itsthelore/wayfinder-router#readme + about: Setup, gateway config, and integration recipes live in the README and docs/. + - name: Report a security issue + url: https://github.com/itsthelore/wayfinder-router/security/advisories/new + about: Please report vulnerabilities privately, not as a public issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..2c9b1a23 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,32 @@ +name: Feature request +description: Suggest an improvement or a new capability +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: What's the problem or use case? + description: What are you trying to do that Wayfinder doesn't do well today? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed behaviour + validations: + required: true + - type: dropdown + id: decision-path + attributes: + label: Does this touch the scored decision path? + description: The scored decision must stay offline, deterministic, and keyless (WF-ADR-0001). + options: + - "No — it's in the gateway / ui / tooling layer" + - "Yes — and it keeps the decision offline and deterministic" + - "Not sure" + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..ac05c99f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +tom.ballard08@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..78fa876a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,29 @@ +# Security Policy + +## Reporting a vulnerability + +Please report security issues **privately** — don't open a public issue or pull request. + +Use GitHub's private vulnerability reporting: +**[Report a vulnerability](https://github.com/itsthelore/wayfinder-router/security/advisories/new)** +(the repo's *Security → Advisories → Report a vulnerability*). We'll acknowledge within a few days and +keep you posted as we work on a fix. + +## Supported versions + +Wayfinder follows CalVer and ships fixes on the latest release. Please reproduce against the most recent +version before reporting. + +## Scope & design posture + +A few properties are load-bearing, and reports that undermine them are especially welcome: + +- **The scored decision path is offline, deterministic, and keyless** (WF-ADR-0001) — it makes no model + call, opens no network connection, and reads no credential to route a prompt. +- **Provider keys are read from the environment at request time** and are never written to config, logs, + or disk (WF-ADR-0004). Virtual gateway keys are stored only as SHA-256 hashes, never in plaintext. +- Prompt text is never logged or persisted by the decision or metrics paths. + +Out of scope: problems that require a deployment you control to be misconfigured (for example, exposing +the gateway to an untrusted network with no auth), and vulnerabilities in the upstream providers you point +the gateway at.