Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions dev/guidelines/backend/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,22 @@ async def set(self, key: str, value: str, expires: KVTTL | int | None = None) ->

To branch on or read from a typed object, use `isinstance` so the type checker can narrow it; reaching for `getattr(obj, "attr", default)` defeats type analysis. When guarding a schema object, cover the whole family that carries the attribute — `isinstance(schema, (NodeSchema, ProfileSchema, TemplateSchema))` — since profiles and templates inherit node behavior and a `NodeSchema`-only check silently drops them.

### `str` satisfies `Sequence` — exclude it before narrowing

When a parameter accepts `T | Sequence[T] | ...` and `isinstance(data, Sequence)` (or `Iterable`/`Collection`) is how the code tells "one item" from "many", check `str` first whenever `T` includes `str`. A bare string satisfies `Sequence` in its own right, so without the carve-out it falls into the "many" branch and gets iterated character-by-character instead of treated as a single item — silently, if the single-item branch also accepts `str`.

```python
# ❌ Bad - a bare id like "abc-123" satisfies Sequence and gets shredded into one item per character
if not isinstance(data, Sequence):
data = [data]

# ✅ Good - str is excluded first, so a single id stays a single item
if isinstance(data, str) or not isinstance(data, Sequence):
data = [data]
```

This carve-out is easy to lose exactly when it matters most: widening a parameter from an invariant `list[T]` to a covariant `Sequence[T]` (e.g. to drop a call-site `# type: ignore[arg-type]`, see [When a wrong-type bug slips through](#when-a-wrong-type-bug-slips-through)) means widening this runtime check in step — an annotation that newly accepts `str` as a `Sequence` while the `isinstance` check still assumes only `list` reaches it will misroute every bare string. Add a test for both the bare-`str` case and the newly-accepted non-`list` sequence (e.g. a `tuple`).

### Deterministic serialization for hashes and cache keys

When a JSON string feeds a hash, fingerprint, or cache key, its output must be deterministic. Do **not** pass `default=str` to `json.dumps` there: it silently serializes unexpected types via `str()`, which can embed run-specific data (memory addresses) and break determinism. Serialize an explicit, canonical shape (sorted keys, known field types) and let unknown types raise instead of being coerced.
Expand Down
8 changes: 8 additions & 0 deletions dev/guidelines/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ uv run towncrier create -c "Added breadcrumb navigation for hierarchical schemas
uv run towncrier create -c "Updated dependencies to latest versions" +deps-update.housekeeping.md
```

## When to Skip

Skip the fragment entirely when the change has **no user-facing effect** — an internal

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This docs-only change targets develop, but per the base-branch rules, changes that can't affect a running product (including docs, tooling, CI) should target stable. Consider retargeting to stable so the guidance ships sooner rather than waiting for the next minor release train.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At dev/guidelines/changelog.md, line 55:

<comment>This docs-only change targets develop, but per the base-branch rules, changes that can't affect a running product (including docs, tooling, CI) should target stable. Consider retargeting to stable so the guidance ships sooner rather than waiting for the next minor release train.</comment>

<file context>
@@ -50,6 +50,14 @@ uv run towncrier create -c "Added breadcrumb navigation for hierarchical schemas
 
+## When to Skip
+
+Skip the fragment entirely when the change has **no user-facing effect** — an internal
+type-annotation correction, a refactor with no behavior change, cleanup of internal docs or
+spec-kit scaffolding. `housekeeping` is for internal changes a user could still plausibly notice
</file context>

type-annotation correction, a refactor with no behavior change, cleanup of internal docs or
spec-kit scaffolding. `housekeeping` is for internal changes a user could still plausibly notice
(a dependency bump, a tooling change) — it is not a catch-all for anything code-adjacent. If it's
unclear whether a change is user-facing, ask rather than defaulting to adding a fragment.

## Writing Good Changelog Messages

- Write from the user's perspective
Expand Down
7 changes: 7 additions & 0 deletions dev/guidelines/git-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ Add changelog fragments to `changelog/` using Towncrier. See [Changelog Guidelin
- Any notable implementation details
- Testing performed

**Scope to match the change:** for a trivial, code-only fix with no behavior change (e.g. a
type-annotation correction), ship just the code diff. Don't carry the spec-kit design record
(`dev/specs/<feature>/`, see [Repository Organization](repository-organization.md)) or a
changelog fragment with no user-facing effect (see [When to Skip](changelog.md#when-to-skip))
into the PR — trim them before opening it if the workflow that produced the change generated them
by default.

## Critical Rules

- Never force push to `stable` or `develop`
Expand Down
6 changes: 6 additions & 0 deletions dev/guidelines/repository-organization.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ Each directory in `dev/` serves a specific purpose and follows a content lifecyc
- Moved to `knowledge/` (if describing how something works)
- Moved to `guidelines/` (if describing how to use something)

**Proportionality**: Not every change needs a spec. If the actual code change is small enough to
be self-explanatory (e.g. an annotation-only fix, a one-line correction with no behavior change),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The cross-reference link says 'Git Workflow → Pull Requests' but points only to the file root, not the Pull Requests section. Append #pull-requests to the link so the text and target match, consistent with how the git-workflow.md change correctly uses changelog.md#when-to-skip with a section anchor.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At dev/guidelines/repository-organization.md, line 89:

<comment>The cross-reference link says 'Git Workflow → Pull Requests' but points only to the file root, not the Pull Requests section. Append `#pull-requests` to the link so the text and target match, consistent with how the git-workflow.md change correctly uses `changelog.md#when-to-skip` with a section anchor.</comment>

<file context>
@@ -85,6 +85,12 @@ Each directory in `dev/` serves a specific purpose and follows a content lifecyc
 - Moved to `guidelines/` (if describing how to use something)
 
+**Proportionality**: Not every change needs a spec. If the actual code change is small enough to
+be self-explanatory (e.g. an annotation-only fix, a one-line correction with no behavior change),
+skip the spec-kit scaffolding entirely or trim `dev/specs/<feature>/` from the PR before it goes
+to review — the design record should be proportional to the change it documents, not a fixed-cost
</file context>

skip the spec-kit scaffolding entirely or trim `dev/specs/<feature>/` from the PR before it goes
to review — the design record should be proportional to the change it documents, not a fixed-cost
byproduct of running the workflow. See [Git Workflow → Pull Requests](git-workflow.md).

### guidelines/

**Purpose**: "What rules should I follow?"
Expand Down